+/*
+ * Copyright (C) 2022 Jonathan Rajotte<jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+#include <common/defaults.hpp>
+#include <common/error.hpp>
+#include <common/macros.hpp>
+#include <common/optional.hpp>
+#include <common/payload-view.hpp>
+#include <common/payload.hpp>
+#include <common/uri.hpp>
+#include <lttng/session-internal.hpp>
+#include <lttng/session.h>
+#include <memory>
+#include <stdio.h>
+#include <time.h>
+
+struct session_list_element {
+ struct lttng_session *session;
+};
+
+static void session_list_destructor(void *ptr)
+{
+ struct session_list_element *element = (struct session_list_element *) ptr;
+
+ free(element->session);
+ free(element);
+}
+
+int lttng_session_serialize(const struct lttng_session *session, lttng_payload *payload)
+{
+ int ret;
+ size_t name_len, path_len;
+ struct lttng_session_comm session_comm = {};
+ struct lttng_session_extended *extended = (typeof(extended)) session->extended.ptr;
+
+ LTTNG_ASSERT(extended != nullptr);
+
+ name_len = lttng_strnlen(session->name, sizeof(session->name));
+ if (name_len == sizeof(session->name)) {
+ /* Session name is not NULL-terminated. */
+ ret = -1;
+ goto end;
+ }
+
+ /* Add null termination. */
+ name_len += 1;
+
+ path_len = lttng_strnlen(session->path, sizeof(session->path));
+ if (path_len == sizeof(session->path)) {
+ /* Session path is not NULL-terminated. */
+ ret = -1;
+ goto end;
+ }
+
+ /* Add null termination. */
+ path_len += 1;
+
+ session_comm.name_len = (uint32_t) name_len;
+ session_comm.path_len = (uint32_t) path_len;
+ session_comm.enabled = (uint8_t) session->enabled;
+ session_comm.snapshot_mode = session->snapshot_mode;
+ session_comm.live_timer_interval = session->live_timer_interval;
+ if (extended->creation_time.is_set) {
+ LTTNG_OPTIONAL_SET(&session_comm.creation_time,
+ LTTNG_OPTIONAL_GET(extended->creation_time));
+ } else {
+ LTTNG_OPTIONAL_UNSET(&session_comm.creation_time);
+ }
+
+ /* Header */
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &session_comm, sizeof(session_comm));
+ if (ret) {
+ goto end;
+ }
+
+ /* Session name */
+ ret = lttng_dynamic_buffer_append(&payload->buffer, session->name, name_len);
+ if (ret) {
+ goto end;
+ }
+
+ /* Session path */
+ ret = lttng_dynamic_buffer_append(&payload->buffer, session->path, path_len);
+ if (ret) {
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+ssize_t lttng_session_create_from_payload(
+ struct lttng_payload_view *view, struct lttng_session **out_session)
+{
+ ssize_t ret, offset = 0;
+ struct lttng_session *local_session = nullptr;
+ struct lttng_session_extended *local_extended = nullptr;
+ const struct lttng_session_comm *session_comm;
+
+ LTTNG_ASSERT(out_session);
+ LTTNG_ASSERT(view);
+
+ {
+ struct lttng_payload_view comm_view =
+ lttng_payload_view_from_view(view, offset, sizeof(*session_comm));
+
+ if (!lttng_payload_view_is_valid(&comm_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ /* lttng_session_comm header */
+ session_comm = (typeof(session_comm)) comm_view.buffer.data;
+ offset += sizeof(*session_comm);
+ }
+
+ local_session = zmalloc<struct lttng_session>(
+ sizeof(struct lttng_session) + sizeof(struct lttng_session_extended));
+ if (local_session == nullptr) {
+ ret = -1;
+ goto end;
+ }
+
+ local_extended = (struct lttng_session_extended *) ((char *) local_session +
+ sizeof(lttng_session));
+ if (local_extended == nullptr) {
+ ret = -1;
+ goto end;
+ }
+
+ local_session->extended.ptr = local_extended;
+
+ local_session->enabled = session_comm->enabled;
+ local_session->live_timer_interval = session_comm->live_timer_interval;
+ local_session->snapshot_mode = session_comm->snapshot_mode;
+ if (session_comm->creation_time.is_set) {
+ LTTNG_OPTIONAL_SET(&local_extended->creation_time,
+ LTTNG_OPTIONAL_GET(session_comm->creation_time));
+ }
+
+ {
+ const char *name;
+ const struct lttng_buffer_view name_view = lttng_buffer_view_from_view(
+ &view->buffer, offset, session_comm->name_len);
+
+ if (!lttng_buffer_view_is_valid(&name_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ name = (const char *) name_view.data;
+
+ if (!lttng_buffer_view_contains_string(&name_view, name, session_comm->name_len)) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_strncpy(local_session->name, name, sizeof(local_session->name));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += session_comm->name_len;
+ }
+
+ {
+ const char *path;
+ const struct lttng_buffer_view path_view = lttng_buffer_view_from_view(
+ &view->buffer, offset, session_comm->path_len);
+
+ if (!lttng_buffer_view_is_valid(&path_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ path = (const char *) path_view.data;
+
+ if (!lttng_buffer_view_contains_string(&path_view, path, session_comm->path_len)) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_strncpy(local_session->path, path, sizeof(local_session->path));
+ if (ret) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += session_comm->path_len;
+ }
+ /* Transfer ownership to the caller. */
+ *out_session = local_session;
+ local_session = nullptr;
+
+ ret = offset;
+end:
+ free(local_session);
+ return ret;
+}
+
+static enum lttng_error_code compute_flattened_size(
+ struct lttng_dynamic_pointer_array *sessions, size_t *size)
+{
+ enum lttng_error_code ret_code;
+ size_t storage_req, session_count;
+
+ assert(size);
+ assert(sessions);
+
+ session_count = lttng_dynamic_pointer_array_get_count(sessions);
+
+ /* The basic struct lttng_session */
+ storage_req = session_count * sizeof(struct lttng_session);
+
+ /* The struct lttng_session_extended */
+ storage_req += session_count * sizeof(struct lttng_session_extended);
+
+ *size = storage_req;
+ ret_code = LTTNG_OK;
+
+ return ret_code;
+}
+
+static enum lttng_error_code flatten_lttng_sessions(struct lttng_dynamic_pointer_array *sessions,
+ struct lttng_session **flattened_sessions)
+{
+ enum lttng_error_code ret_code;
+ int ret, i;
+ size_t storage_req;
+ struct lttng_dynamic_buffer local_flattened_sessions;
+ int nb_sessions;
+
+ assert(sessions);
+ assert(flattened_sessions);
+
+ lttng_dynamic_buffer_init(&local_flattened_sessions);
+ nb_sessions = lttng_dynamic_pointer_array_get_count(sessions);
+
+ ret_code = compute_flattened_size(sessions, &storage_req);
+ if (ret_code != LTTNG_OK) {
+ goto end;
+ }
+
+ /*
+ * We must ensure that "local_flattened_sessions" is never resized so as
+ * to preserve the validity of the flattened objects.
+ */
+ ret = lttng_dynamic_buffer_set_capacity(&local_flattened_sessions, storage_req);
+ if (ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /* Start by laying the struct lttng_session */
+ for (i = 0; i < nb_sessions; i++) {
+ const struct session_list_element *element = (const struct session_list_element *)
+ lttng_dynamic_pointer_array_get_pointer(sessions, i);
+
+ if (!element) {
+ ret_code = LTTNG_ERR_FATAL;
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(&local_flattened_sessions, element->session,
+ sizeof(struct lttng_session));
+ if (ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ }
+
+ for (i = 0; i < nb_sessions; i++) {
+ const struct session_list_element *element = (const struct session_list_element *)
+ lttng_dynamic_pointer_array_get_pointer(sessions, i);
+ struct lttng_session *session =
+ (struct lttng_session *) (local_flattened_sessions.data +
+ (sizeof(struct lttng_session) * i));
+ struct lttng_session_extended *session_extended =
+ (struct lttng_session_extended *) (local_flattened_sessions.data +
+ local_flattened_sessions.size);
+
+ assert(element);
+
+ /* Insert struct lttng_session_extended. */
+ ret = lttng_dynamic_buffer_set_size(&local_flattened_sessions,
+ local_flattened_sessions.size + sizeof(*session_extended));
+ if (ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+ session->extended.ptr = session_extended;
+
+ memcpy(session_extended, element->session->extended.ptr,
+ sizeof(struct lttng_session_extended));
+ }
+
+ /* Don't reset local_flattened_sessions buffer as we return its content. */
+ *flattened_sessions = (struct lttng_session *) local_flattened_sessions.data;
+ lttng_dynamic_buffer_init(&local_flattened_sessions);
+ ret_code = LTTNG_OK;
+end:
+ lttng_dynamic_buffer_reset(&local_flattened_sessions);
+ return ret_code;
+}
+
+static enum lttng_error_code session_list_create_from_payload(struct lttng_payload_view *view,
+ unsigned int count,
+ struct lttng_dynamic_pointer_array *session_list)
+{
+ enum lttng_error_code ret_code;
+ int ret;
+ unsigned int i;
+ int offset = 0;
+
+ assert(view);
+ assert(session_list);
+
+ for (i = 0; i < count; i++) {
+ ssize_t session_size;
+ struct lttng_payload_view session_view =
+ lttng_payload_view_from_view(view, offset, -1);
+ struct session_list_element *element = zmalloc<session_list_element>();
+
+ if (!element) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ /*
+ * Lifetime and management of the object is now bound to the
+ * array.
+ */
+ ret = lttng_dynamic_pointer_array_add_pointer(session_list, element);
+ if (ret) {
+ session_list_destructor(element);
+ ret_code = LTTNG_ERR_NOMEM;
+ goto end;
+ }
+
+ session_size = lttng_session_create_from_payload(&session_view, &element->session);
+ if (session_size < 0) {
+ ret_code = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ offset += session_size;
+ }
+
+ if (view->buffer.size != offset) {
+ ret_code = LTTNG_ERR_INVALID_PROTOCOL;
+ goto end;
+ }
+
+ ret_code = LTTNG_OK;
+
+end:
+ return ret_code;
+}
+enum lttng_error_code lttng_sessions_create_and_flatten_from_payload(
+ struct lttng_payload_view *payload,
+ unsigned int count,
+ struct lttng_session **sessions)
+{
+ enum lttng_error_code ret = LTTNG_OK;
+ struct lttng_dynamic_pointer_array local_sessions;
+
+ lttng_dynamic_pointer_array_init(&local_sessions, session_list_destructor);
+
+ /* Deserialize the sessions. */
+ {
+ struct lttng_payload_view sessions_view =
+ lttng_payload_view_from_view(payload, 0, -1);
+
+ ret = session_list_create_from_payload(&sessions_view, count, &local_sessions);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+ }
+
+ ret = flatten_lttng_sessions(&local_sessions, sessions);
+ if (ret != LTTNG_OK) {
+ goto end;
+ }
+
+end:
+ lttng_dynamic_pointer_array_reset(&local_sessions);
+ return ret;
+}