#ifndef LTTNG_SESSION_INTERNAL_H
#define LTTNG_SESSION_INTERNAL_H
-#include <lttng/constant.h>
+#include <common/buffer-view.hpp>
#include <common/macros.hpp>
+#include <common/optional.hpp>
+#include <lttng/constant.h>
+#include <lttng/lttng-error.h>
+
+struct lttng_session;
+struct lttng_payload_view;
struct lttng_session_extended {
- struct {
- uint64_t value;
- uint8_t is_set;
- } LTTNG_PACKED creation_time;
+ LTTNG_OPTIONAL(uint64_t) creation_time;
+};
+
+struct lttng_session_comm {
+ uint32_t name_len;
+ uint32_t path_len;
+ uint8_t enabled;
+ uint32_t snapshot_mode;
+ uint32_t live_timer_interval;
+ /* lttng_session_extended data */
+ LTTNG_OPTIONAL(uint64_t) LTTNG_PACKED creation_time;
+ /*
+ * Dynamic payload:
+ * - name[name_len]
+ * - path[path_len]
+ */
+ char payload[];
} LTTNG_PACKED;
+int lttng_session_serialize(const struct lttng_session *session, struct lttng_payload *payload);
+
+ssize_t lttng_session_create_from_payload(
+ struct lttng_payload_view *view, struct lttng_session **out_session);
+
+enum lttng_error_code lttng_sessions_create_and_flatten_from_payload(
+ struct lttng_payload_view *view,
+ unsigned int count,
+ struct lttng_session **sessions);
+
#endif /* LTTNG_SESSION_INTERNAL_H */
return ret;
}
-/*
- * Count number of session permitted by uid/gid.
- */
-static unsigned int lttng_sessions_count(uid_t uid,
- gid_t gid __attribute__((unused)))
-{
- unsigned int i = 0;
- struct ltt_session *session;
- const struct ltt_session_list *session_list = session_get_list();
-
- DBG("Counting number of available session for UID %d", uid);
- cds_list_for_each_entry(session, &session_list->head, list) {
- if (!session_get(session)) {
- continue;
- }
- session_lock(session);
- /* Only count the sessions the user can control. */
- if (session_access_ok(session, uid) &&
- !session->destroyed) {
- i++;
- }
- session_unlock(session);
- session_put(session);
- }
- return i;
-}
-
static enum lttng_error_code receive_lttng_trigger(struct command_ctx *cmd_ctx,
int sock,
int *sock_error,
}
case LTTNG_LIST_SESSIONS:
{
- unsigned int nr_sessions;
- lttng_session *sessions_payload = nullptr;
- size_t payload_len = 0;
+ enum lttng_error_code ret_code;
+ size_t original_payload_size;
+ size_t payload_size;
+ const size_t command_header_size = sizeof(struct lttcomm_list_command_header);
+
+ ret = setup_empty_lttng_msg(cmd_ctx);
+ if (ret) {
+ ret = LTTNG_ERR_NOMEM;
+ goto setup_error;
+ }
+
+ original_payload_size = cmd_ctx->reply_payload.buffer.size;
session_lock_list();
- nr_sessions = lttng_sessions_count(
+ ret_code = cmd_list_lttng_sessions(&cmd_ctx->reply_payload,
LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds),
LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds));
-
- if (nr_sessions > 0) {
- payload_len = (sizeof(struct lttng_session) *
- nr_sessions) +
- (sizeof(struct lttng_session_extended) *
- nr_sessions);
- sessions_payload = zmalloc<lttng_session>(payload_len);
- if (!sessions_payload) {
- session_unlock_list();
- ret = -ENOMEM;
- goto setup_error;
- }
-
- cmd_list_lttng_sessions(sessions_payload, nr_sessions,
- LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds),
- LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds));
- }
-
session_unlock_list();
-
- ret = setup_lttng_msg_no_cmd_header(
- cmd_ctx, sessions_payload, payload_len);
- free(sessions_payload);
-
- if (ret < 0) {
- goto setup_error;
+ if (ret_code != LTTNG_OK) {
+ ret = (int) ret_code;
+ goto error;
}
+ payload_size = cmd_ctx->reply_payload.buffer.size - command_header_size -
+ original_payload_size;
+ update_lttng_msg(cmd_ctx, command_header_size, payload_size);
+
ret = LTTNG_OK;
break;
}
* The session list lock MUST be acquired before calling this function. Use
* session_lock_list() and session_unlock_list().
*/
-void cmd_list_lttng_sessions(struct lttng_session *sessions,
- size_t session_count, uid_t uid, gid_t gid)
+enum lttng_error_code cmd_list_lttng_sessions(
+ struct lttng_payload *reply_payload, uid_t uid, gid_t gid)
{
- int ret;
- unsigned int i = 0;
+ int buffer_resize_ret;
+ enum lttng_error_code ret_code = LTTNG_OK;
+ struct lttcomm_list_command_header reply_command_header = {};
+ size_t reply_command_header_offset;
struct ltt_session *session;
struct ltt_session_list *list = session_get_list();
- struct lttng_session_extended *extended =
- (typeof(extended)) (&sessions[session_count]);
+ int ret;
+ unsigned int i = 0;
+
+ assert(reply_payload);
+
+ /* Reserve space for command reply header. */
+ reply_command_header_offset = reply_payload->buffer.size;
+ buffer_resize_ret = lttng_dynamic_buffer_set_size(&reply_payload->buffer,
+ reply_command_header_offset + sizeof(struct lttcomm_list_command_header));
+ if (buffer_resize_ret) {
+ ret_code = LTTNG_ERR_NOMEM;
+ goto error;
+ }
DBG("Getting all available session for UID %d GID %d",
uid, gid);
- /*
- * Iterate over session list and append data after the control struct in
- * the buffer.
- */
+
cds_list_for_each_entry(session, &list->head, list) {
+ struct lttng_session tmp_session = {};
+ struct lttng_session_extended tmp_extended = {};
+
+ tmp_session.extended.ptr = &tmp_extended;
+
if (!session_get(session)) {
continue;
}
if (session->consumer->type == CONSUMER_DST_NET ||
(ksess && ksess->consumer->type == CONSUMER_DST_NET) ||
(usess && usess->consumer->type == CONSUMER_DST_NET)) {
- ret = build_network_session_path(sessions[i].path,
- sizeof(sessions[i].path), session);
+ ret = build_network_session_path(
+ tmp_session.path, sizeof(tmp_session.path), session);
} else {
- ret = snprintf(sessions[i].path, sizeof(sessions[i].path), "%s",
+ ret = snprintf(tmp_session.path, sizeof(tmp_session.path), "%s",
session->consumer->dst.session_root_path);
}
if (ret < 0) {
continue;
}
- strncpy(sessions[i].name, session->name, NAME_MAX);
- sessions[i].name[NAME_MAX - 1] = '\0';
- sessions[i].enabled = session->active;
- sessions[i].snapshot_mode = session->snapshot_mode;
- sessions[i].live_timer_interval = session->live_timer;
- extended[i].creation_time.value = (uint64_t) session->creation_time;
- extended[i].creation_time.is_set = 1;
+ strncpy(tmp_session.name, session->name, NAME_MAX);
+ tmp_session.name[NAME_MAX - 1] = '\0';
+ tmp_session.enabled = session->active;
+ tmp_session.snapshot_mode = session->snapshot_mode;
+ tmp_session.live_timer_interval = session->live_timer;
+ LTTNG_OPTIONAL_SET(&tmp_extended.creation_time, (uint64_t) session->creation_time);
+ ret = lttng_session_serialize(&tmp_session, reply_payload);
+ if (ret) {
+ ret_code = LTTNG_ERR_FATAL;
+ goto error;
+ }
i++;
session_put(session);
}
+
+ if (i > UINT32_MAX) {
+ ret_code = LTTNG_ERR_OVERFLOW;
+ goto error;
+ }
+
+ /* Update command reply header. */
+ reply_command_header.count = (uint32_t) i;
+ memcpy(reply_payload->buffer.data + reply_command_header_offset, &reply_command_header,
+ sizeof(reply_command_header));
+ ret_code = LTTNG_OK;
+error:
+ return ret_code;
}
/*
enum lttng_error_code cmd_list_channels(enum lttng_domain_type domain,
struct ltt_session *session,
struct lttng_payload *payload);
-void cmd_list_lttng_sessions(struct lttng_session *sessions,
- size_t session_count, uid_t uid, gid_t gid);
+enum lttng_error_code cmd_list_lttng_sessions(struct lttng_payload *payload, uid_t uid, gid_t gid);
enum lttng_error_code cmd_list_tracepoint_fields(enum lttng_domain_type domain,
struct lttng_payload *reply);
enum lttng_error_code cmd_list_tracepoints(enum lttng_domain_type domain,
pthread-lock.hpp \
readwrite.cpp readwrite.hpp \
runas.cpp runas.hpp \
+ session.cpp \
session-descriptor.cpp \
snapshot.cpp snapshot.hpp \
spawn-viewer.cpp spawn-viewer.hpp \
--- /dev/null
+/*
+ * 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;
+}
int lttng_list_sessions(struct lttng_session **out_sessions)
{
int ret;
- struct lttcomm_session_msg lsm;
- const size_t session_size = sizeof(struct lttng_session) +
- sizeof(struct lttng_session_extended);
- size_t session_count, i;
- struct lttng_session_extended *sessions_extended_begin;
- struct lttng_session *sessions = NULL;
+ struct lttcomm_session_msg lsm = {};
+ struct lttng_payload reply;
+ struct lttng_payload_view lsm_view =
+ lttng_payload_view_init_from_buffer((const char *) &lsm, 0, sizeof(lsm));
+ unsigned int nb_sessions = 0;
- memset(&lsm, 0, sizeof(lsm));
+ lttng_payload_init(&reply);
+
+ /* Initialize command parameters. */
lsm.cmd_type = LTTNG_LIST_SESSIONS;
- /*
- * Initialize out_sessions to NULL so it is initialized when
- * lttng_list_sessions returns 0, thus allowing *out_sessions to
- * be subsequently freed.
- */
- *out_sessions = NULL;
- ret = lttng_ctl_ask_sessiond(&lsm, (void**) &sessions);
- if (ret <= 0) {
- goto end;
- }
- if (!sessions) {
- ret = -LTTNG_ERR_FATAL;
+
+ /* Execute command against the session daemon. */
+ ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply);
+ if (ret < 0) {
goto end;
}
- if (ret % session_size) {
- ret = -LTTNG_ERR_UNK;
- free(sessions);
- goto end;
+ {
+ const struct lttcomm_list_command_header *cmd_reply_header = NULL;
+ const lttng_payload_view cmd_reply_header_view = lttng_payload_view_from_payload(
+ &reply, 0, sizeof(*cmd_reply_header));
+
+ if (!lttng_payload_view_is_valid(&cmd_reply_header_view)) {
+ ret = -LTTNG_ERR_INVALID_PROTOCOL;
+ goto end;
+ }
+
+ cmd_reply_header = (const struct lttcomm_list_command_header *)
+ cmd_reply_header_view.buffer.data;
+ if (cmd_reply_header->count > INT_MAX) {
+ ret = -LTTNG_ERR_OVERFLOW;
+ goto end;
+ }
+
+ nb_sessions = (unsigned int) cmd_reply_header->count;
}
- session_count = (size_t) ret / session_size;
- sessions_extended_begin = (struct lttng_session_extended *)
- (&sessions[session_count]);
- /* Set extended session info pointers. */
- for (i = 0; i < session_count; i++) {
- struct lttng_session *session = &sessions[i];
- struct lttng_session_extended *extended =
- &(sessions_extended_begin[i]);
+ {
+ enum lttng_error_code ret_code;
+ lttng_payload_view cmd_reply_payload = lttng_payload_view_from_payload(
+ &reply, sizeof(struct lttcomm_list_command_header), -1);
- session->extended.ptr = extended;
+ ret_code = lttng_sessions_create_and_flatten_from_payload(
+ &cmd_reply_payload, nb_sessions, out_sessions);
+ if (ret_code != LTTNG_OK) {
+ ret = -((int) ret_code);
+ goto end;
+ }
}
- ret = (int) session_count;
- *out_sessions = sessions;
+ ret = (int) nb_sessions;
end:
+ lttng_payload_reset(&reply);
return ret;
}