common: compile libcommon as C++
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 22 Sep 2021 12:21:14 +0000 (08:21 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 18 Nov 2021 00:01:37 +0000 (19:01 -0500)
Change-Id: I5160ef36932d71a4d925018fe0763bbc78b88009
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
144 files changed:
src/common/Makefile.am
src/common/actions/action.c [deleted file]
src/common/actions/action.cpp [new file with mode: 0644]
src/common/actions/list.c [deleted file]
src/common/actions/list.cpp [new file with mode: 0644]
src/common/actions/notify.c [deleted file]
src/common/actions/notify.cpp [new file with mode: 0644]
src/common/actions/path.c [deleted file]
src/common/actions/path.cpp [new file with mode: 0644]
src/common/actions/rate-policy.c [deleted file]
src/common/actions/rate-policy.cpp [new file with mode: 0644]
src/common/actions/rotate-session.c [deleted file]
src/common/actions/rotate-session.cpp [new file with mode: 0644]
src/common/actions/snapshot-session.c [deleted file]
src/common/actions/snapshot-session.cpp [new file with mode: 0644]
src/common/actions/start-session.c [deleted file]
src/common/actions/start-session.cpp [new file with mode: 0644]
src/common/actions/stop-session.c [deleted file]
src/common/actions/stop-session.cpp [new file with mode: 0644]
src/common/buffer-view.c [deleted file]
src/common/buffer-view.cpp [new file with mode: 0644]
src/common/conditions/buffer-usage.c [deleted file]
src/common/conditions/buffer-usage.cpp [new file with mode: 0644]
src/common/conditions/condition.c [deleted file]
src/common/conditions/condition.cpp [new file with mode: 0644]
src/common/conditions/event-rule-matches.c [deleted file]
src/common/conditions/event-rule-matches.cpp [new file with mode: 0644]
src/common/conditions/session-consumed-size.c [deleted file]
src/common/conditions/session-consumed-size.cpp [new file with mode: 0644]
src/common/conditions/session-rotation.c [deleted file]
src/common/conditions/session-rotation.cpp [new file with mode: 0644]
src/common/context.c [deleted file]
src/common/context.cpp [new file with mode: 0644]
src/common/credentials.c [deleted file]
src/common/credentials.cpp [new file with mode: 0644]
src/common/daemonize.c [deleted file]
src/common/daemonize.cpp [new file with mode: 0644]
src/common/defaults.c [deleted file]
src/common/defaults.cpp [new file with mode: 0644]
src/common/domain.c [deleted file]
src/common/domain.cpp [new file with mode: 0644]
src/common/dynamic-array.c [deleted file]
src/common/dynamic-array.cpp [new file with mode: 0644]
src/common/dynamic-buffer.c [deleted file]
src/common/dynamic-buffer.cpp [new file with mode: 0644]
src/common/endpoint.c [deleted file]
src/common/endpoint.cpp [new file with mode: 0644]
src/common/error-query.c [deleted file]
src/common/error-query.cpp [new file with mode: 0644]
src/common/error.c [deleted file]
src/common/error.cpp [new file with mode: 0644]
src/common/evaluation.c [deleted file]
src/common/evaluation.cpp [new file with mode: 0644]
src/common/event-expr/event-expr.c [deleted file]
src/common/event-expr/event-expr.cpp [new file with mode: 0644]
src/common/event-field-value.c [deleted file]
src/common/event-field-value.cpp [new file with mode: 0644]
src/common/event-rule/event-rule.c [deleted file]
src/common/event-rule/event-rule.cpp [new file with mode: 0644]
src/common/event-rule/jul-logging.c [deleted file]
src/common/event-rule/jul-logging.cpp [new file with mode: 0644]
src/common/event-rule/kernel-kprobe.c [deleted file]
src/common/event-rule/kernel-kprobe.cpp [new file with mode: 0644]
src/common/event-rule/kernel-syscall.c [deleted file]
src/common/event-rule/kernel-syscall.cpp [new file with mode: 0644]
src/common/event-rule/kernel-tracepoint.c [deleted file]
src/common/event-rule/kernel-tracepoint.cpp [new file with mode: 0644]
src/common/event-rule/kernel-uprobe.c [deleted file]
src/common/event-rule/kernel-uprobe.cpp [new file with mode: 0644]
src/common/event-rule/log4j-logging.c [deleted file]
src/common/event-rule/log4j-logging.cpp [new file with mode: 0644]
src/common/event-rule/python-logging.c [deleted file]
src/common/event-rule/python-logging.cpp [new file with mode: 0644]
src/common/event-rule/user-tracepoint.c [deleted file]
src/common/event-rule/user-tracepoint.cpp [new file with mode: 0644]
src/common/event.c [deleted file]
src/common/event.cpp [new file with mode: 0644]
src/common/fd-handle.c [deleted file]
src/common/fd-handle.cpp [new file with mode: 0644]
src/common/filter.c [deleted file]
src/common/filter.cpp [new file with mode: 0644]
src/common/fs-handle.c [deleted file]
src/common/fs-handle.cpp [new file with mode: 0644]
src/common/futex.c [deleted file]
src/common/futex.cpp [new file with mode: 0644]
src/common/index-allocator.c [deleted file]
src/common/index-allocator.cpp [new file with mode: 0644]
src/common/kernel-probe.c [deleted file]
src/common/kernel-probe.cpp [new file with mode: 0644]
src/common/location.c [deleted file]
src/common/location.cpp [new file with mode: 0644]
src/common/log-level-rule.c [deleted file]
src/common/log-level-rule.cpp [new file with mode: 0644]
src/common/lttng-elf.c [deleted file]
src/common/lttng-elf.cpp [new file with mode: 0644]
src/common/lttng-elf.h
src/common/mi-lttng.c [deleted file]
src/common/mi-lttng.cpp [new file with mode: 0644]
src/common/notification.c [deleted file]
src/common/notification.cpp [new file with mode: 0644]
src/common/payload-view.c [deleted file]
src/common/payload-view.cpp [new file with mode: 0644]
src/common/payload.c [deleted file]
src/common/payload.cpp [new file with mode: 0644]
src/common/pipe.c [deleted file]
src/common/pipe.cpp [new file with mode: 0644]
src/common/readwrite.c [deleted file]
src/common/readwrite.cpp [new file with mode: 0644]
src/common/runas.c [deleted file]
src/common/runas.cpp [new file with mode: 0644]
src/common/session-descriptor.c [deleted file]
src/common/session-descriptor.cpp [new file with mode: 0644]
src/common/shm.c [deleted file]
src/common/shm.cpp [new file with mode: 0644]
src/common/snapshot.c [deleted file]
src/common/snapshot.cpp [new file with mode: 0644]
src/common/spawn-viewer.c [deleted file]
src/common/spawn-viewer.cpp [new file with mode: 0644]
src/common/thread.c [deleted file]
src/common/thread.cpp [new file with mode: 0644]
src/common/time.c [deleted file]
src/common/time.cpp [new file with mode: 0644]
src/common/trace-chunk.c [deleted file]
src/common/trace-chunk.cpp [new file with mode: 0644]
src/common/tracker.c [deleted file]
src/common/tracker.cpp [new file with mode: 0644]
src/common/trigger.c [deleted file]
src/common/trigger.cpp [new file with mode: 0644]
src/common/unix.c [deleted file]
src/common/unix.cpp [new file with mode: 0644]
src/common/uri.c [deleted file]
src/common/uri.cpp [new file with mode: 0644]
src/common/uri.h
src/common/userspace-probe.c [deleted file]
src/common/userspace-probe.cpp [new file with mode: 0644]
src/common/utils.c [deleted file]
src/common/utils.cpp [new file with mode: 0644]
src/common/uuid.c [deleted file]
src/common/uuid.cpp [new file with mode: 0644]
src/common/waiter.c [deleted file]
src/common/waiter.cpp [new file with mode: 0644]
tests/regression/tools/notification/Makefile.am
tests/regression/tools/notification/default_pipe_size_getter.c [deleted file]
tests/regression/tools/notification/default_pipe_size_getter.cpp [new file with mode: 0644]

index cb8ac7b10ab2190a98adca2bf62dd2e4a6885d61..48e18f311c1f242ae5eff38d6e3767dc2178c39e 100644 (file)
@@ -38,81 +38,81 @@ noinst_LTLIBRARIES = libcommon.la
 EXTRA_DIST = mi-lttng-4.1.xsd
 
 libcommon_la_SOURCES = \
-       actions/action.c \
-       actions/list.c \
-       actions/notify.c \
-       actions/path.c \
-       actions/rotate-session.c \
-       actions/snapshot-session.c \
-       actions/start-session.c \
-       actions/stop-session.c \
-       actions/rate-policy.c \
-       buffer-view.h buffer-view.c \
+       actions/action.cpp \
+       actions/list.cpp \
+       actions/notify.cpp \
+       actions/path.cpp \
+       actions/rotate-session.cpp \
+       actions/snapshot-session.cpp \
+       actions/start-session.cpp \
+       actions/stop-session.cpp \
+       actions/rate-policy.cpp \
+       buffer-view.h buffer-view.cpp \
        common.h \
-       conditions/buffer-usage.c \
-       conditions/condition.c \
-       conditions/event-rule-matches.c \
-       conditions/session-consumed-size.c \
-       conditions/session-rotation.c \
-       context.c context.h \
-       credentials.c credentials.h \
-       daemonize.c daemonize.h \
-       defaults.c \
-       domain.c \
-       dynamic-array.c dynamic-array.h \
-       dynamic-buffer.c dynamic-buffer.h \
-       endpoint.c \
-       error.c error.h \
-       error-query.c \
-       evaluation.c \
-       event.c \
-       event-expr/event-expr.c \
-       event-field-value.c \
-       event-rule/event-rule.c \
-       event-rule/kernel-kprobe.c \
-       event-rule/kernel-syscall.c \
-       event-rule/kernel-uprobe.c \
-       event-rule/kernel-tracepoint.c \
-       event-rule/user-tracepoint.c \
-       event-rule/log4j-logging.c \
-       event-rule/jul-logging.c \
-       event-rule/python-logging.c \
-       filter.c filter.h \
-       fd-handle.c fd-handle.h \
-       fs-handle.c fs-handle.h fs-handle-internal.h \
-       futex.c futex.h \
-       kernel-probe.c \
-       index-allocator.c index-allocator.h \
-       location.c \
-       log-level-rule.c \
-       mi-lttng.c mi-lttng.h \
-       notification.c \
+       conditions/buffer-usage.cpp \
+       conditions/condition.cpp \
+       conditions/event-rule-matches.cpp \
+       conditions/session-consumed-size.cpp \
+       conditions/session-rotation.cpp \
+       context.cpp context.h \
+       credentials.cpp credentials.h \
+       daemonize.cpp daemonize.h \
+       defaults.cpp \
+       domain.cpp \
+       dynamic-array.cpp dynamic-array.h \
+       dynamic-buffer.cpp dynamic-buffer.h \
+       endpoint.cpp \
+       error.cpp error.h \
+       error-query.cpp \
+       evaluation.cpp \
+       event.cpp \
+       event-expr/event-expr.cpp \
+       event-field-value.cpp \
+       event-rule/event-rule.cpp \
+       event-rule/kernel-kprobe.cpp \
+       event-rule/kernel-syscall.cpp \
+       event-rule/kernel-uprobe.cpp \
+       event-rule/kernel-tracepoint.cpp \
+       event-rule/user-tracepoint.cpp \
+       event-rule/log4j-logging.cpp \
+       event-rule/jul-logging.cpp \
+       event-rule/python-logging.cpp \
+       filter.cpp filter.h \
+       fd-handle.cpp fd-handle.h \
+       fs-handle.cpp fs-handle.h fs-handle-internal.h \
+       futex.cpp futex.h \
+       kernel-probe.cpp \
+       index-allocator.cpp index-allocator.h \
+       location.cpp \
+       log-level-rule.cpp \
+       mi-lttng.cpp mi-lttng.h \
+       notification.cpp \
        optional.h \
-       payload.c payload.h \
-       payload-view.c payload-view.h \
-       pipe.c pipe.h \
-       readwrite.c readwrite.h \
-       runas.c runas.h \
-       shm.c shm.h \
-       session-descriptor.c \
-       snapshot.c snapshot.h \
-       spawn-viewer.c spawn-viewer.h \
-       time.c \
-       trace-chunk.c trace-chunk.h \
+       payload.cpp payload.h \
+       payload-view.cpp payload-view.h \
+       pipe.cpp pipe.h \
+       readwrite.cpp readwrite.h \
+       runas.cpp runas.h \
+       shm.cpp shm.h \
+       session-descriptor.cpp \
+       snapshot.cpp snapshot.h \
+       spawn-viewer.cpp spawn-viewer.h \
+       time.cpp \
+       trace-chunk.cpp trace-chunk.h \
        trace-chunk-registry.h \
-       trigger.c \
-       unix.c unix.h \
-       uri.c uri.h \
-       userspace-probe.c \
-       utils.c utils.h \
-       uuid.c uuid.h \
-       thread.c thread.h \
-       tracker.c tracker.h \
-       waiter.c waiter.h
+       trigger.cpp \
+       unix.cpp unix.h \
+       uri.cpp uri.h \
+       userspace-probe.cpp \
+       utils.cpp utils.h \
+       uuid.cpp uuid.h \
+       thread.cpp thread.h \
+       tracker.cpp tracker.h \
+       waiter.cpp waiter.h
 
 if HAVE_ELF_H
 libcommon_la_SOURCES += \
-       lttng-elf.c lttng-elf.h
+       lttng-elf.cpp lttng-elf.h
 endif
 
 libcommon_la_LIBADD = \
diff --git a/src/common/actions/action.c b/src/common/actions/action.c
deleted file mode 100644 (file)
index 21dee73..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/mi-lttng.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/list-internal.h>
-#include <lttng/action/notify-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rotate-session-internal.h>
-#include <lttng/action/snapshot-session-internal.h>
-#include <lttng/action/start-session-internal.h>
-#include <lttng/action/stop-session-internal.h>
-#include <lttng/error-query-internal.h>
-
-const char *lttng_action_type_string(enum lttng_action_type action_type)
-{
-       switch (action_type) {
-       case LTTNG_ACTION_TYPE_UNKNOWN:
-               return "UNKNOWN";
-       case LTTNG_ACTION_TYPE_LIST:
-               return "LIST";
-       case LTTNG_ACTION_TYPE_NOTIFY:
-               return "NOTIFY";
-       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
-               return "ROTATE_SESSION";
-       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
-               return "SNAPSHOT_SESSION";
-       case LTTNG_ACTION_TYPE_START_SESSION:
-               return "START_SESSION";
-       case LTTNG_ACTION_TYPE_STOP_SESSION:
-               return "STOP_SESSION";
-       default:
-               return "???";
-       }
-}
-
-enum lttng_action_type lttng_action_get_type(const struct lttng_action *action)
-{
-       return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
-}
-
-void lttng_action_init(struct lttng_action *action,
-               enum lttng_action_type type,
-               action_validate_cb validate,
-               action_serialize_cb serialize,
-               action_equal_cb equal,
-               action_destroy_cb destroy,
-               action_get_rate_policy_cb get_rate_policy,
-               action_add_error_query_results_cb add_error_query_results,
-               action_mi_serialize_cb mi)
-{
-       urcu_ref_init(&action->ref);
-       action->type = type;
-       action->validate = validate;
-       action->serialize = serialize;
-       action->equal = equal;
-       action->destroy = destroy;
-       action->get_rate_policy = get_rate_policy;
-       action->add_error_query_results = add_error_query_results;
-       action->mi_serialize = mi;
-
-       action->execution_request_counter = 0;
-       action->execution_counter = 0;
-       action->execution_failure_counter = 0;
-}
-
-static
-void action_destroy_ref(struct urcu_ref *ref)
-{
-       struct lttng_action *action =
-                       container_of(ref, struct lttng_action, ref);
-
-       action->destroy(action);
-}
-
-void lttng_action_get(struct lttng_action *action)
-{
-       urcu_ref_get(&action->ref);
-}
-
-void lttng_action_put(struct lttng_action *action)
-{
-       if (!action) {
-               return;
-       }
-
-       LTTNG_ASSERT(action->destroy);
-       urcu_ref_put(&action->ref, action_destroy_ref);
-}
-
-void lttng_action_destroy(struct lttng_action *action)
-{
-       lttng_action_put(action);
-}
-
-bool lttng_action_validate(struct lttng_action *action)
-{
-       bool valid;
-
-       if (!action) {
-               valid = false;
-               goto end;
-       }
-
-       if (!action->validate) {
-               /* Sub-class guarantees that it can never be invalid. */
-               valid = true;
-               goto end;
-       }
-
-       valid = action->validate(action);
-end:
-       return valid;
-}
-
-int lttng_action_serialize(struct lttng_action *action,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_action_comm action_comm = {
-               .action_type = (int8_t) action->type,
-       };
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &action_comm,
-                       sizeof(action_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = action->serialize(action, payload);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view,
-               struct lttng_action **action)
-{
-       ssize_t consumed_len, specific_action_consumed_len;
-       action_create_from_payload_cb create_from_payload_cb;
-       const struct lttng_action_comm *action_comm;
-       const struct lttng_payload_view action_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*action_comm));
-
-       if (!view || !action) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&action_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               consumed_len = -1;
-               goto end;
-       }
-
-       action_comm = (const struct lttng_action_comm *) action_comm_view.buffer.data;
-
-       DBG("Create action from payload: action-type=%s",
-                       lttng_action_type_string(action_comm->action_type));
-
-       switch (action_comm->action_type) {
-       case LTTNG_ACTION_TYPE_NOTIFY:
-               create_from_payload_cb = lttng_action_notify_create_from_payload;
-               break;
-       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
-               create_from_payload_cb =
-                               lttng_action_rotate_session_create_from_payload;
-               break;
-       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
-               create_from_payload_cb =
-                               lttng_action_snapshot_session_create_from_payload;
-               break;
-       case LTTNG_ACTION_TYPE_START_SESSION:
-               create_from_payload_cb =
-                               lttng_action_start_session_create_from_payload;
-               break;
-       case LTTNG_ACTION_TYPE_STOP_SESSION:
-               create_from_payload_cb =
-                               lttng_action_stop_session_create_from_payload;
-               break;
-       case LTTNG_ACTION_TYPE_LIST:
-               create_from_payload_cb = lttng_action_list_create_from_payload;
-               break;
-       default:
-               ERR("Failed to create action from payload, unhandled action type: action-type=%u (%s)",
-                               action_comm->action_type,
-                               lttng_action_type_string(
-                                               action_comm->action_type));
-               consumed_len = -1;
-               goto end;
-       }
-
-       {
-               /* Create buffer view for the action-type-specific data. */
-               struct lttng_payload_view specific_action_view =
-                               lttng_payload_view_from_view(view,
-                                               sizeof(struct lttng_action_comm),
-                                               -1);
-
-               specific_action_consumed_len = create_from_payload_cb(
-                               &specific_action_view, action);
-       }
-       if (specific_action_consumed_len < 0) {
-               ERR("Failed to create specific action from buffer.");
-               consumed_len = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(*action);
-
-       consumed_len = sizeof(struct lttng_action_comm) +
-                      specific_action_consumed_len;
-
-end:
-       return consumed_len;
-}
-
-bool lttng_action_is_equal(const struct lttng_action *a,
-               const struct lttng_action *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->equal);
-       is_equal = a->equal(a, b);
-end:
-       return is_equal;
-}
-
-void lttng_action_increase_execution_request_count(struct lttng_action *action)
-{
-       action->execution_request_counter++;
-}
-
-void lttng_action_increase_execution_count(struct lttng_action *action)
-{
-       action->execution_counter++;
-}
-
-void lttng_action_increase_execution_failure_count(struct lttng_action *action)
-{
-       uatomic_inc(&action->execution_failure_counter);
-}
-
-bool lttng_action_should_execute(const struct lttng_action *action)
-{
-       const struct lttng_rate_policy *policy = NULL;
-       bool execute = false;
-
-       if (action->get_rate_policy == NULL) {
-               execute = true;
-               goto end;
-       }
-
-       policy = action->get_rate_policy(action);
-       if (policy == NULL) {
-               execute = true;
-               goto end;
-       }
-
-       execute = lttng_rate_policy_should_execute(
-                       policy, action->execution_request_counter);
-end:
-       return execute;
-}
-
-enum lttng_action_status lttng_action_add_error_query_results(
-               const struct lttng_action *action,
-               struct lttng_error_query_results *results)
-{
-       return action->add_error_query_results(action, results);
-}
-
-enum lttng_action_status lttng_action_generic_add_error_query_results(
-               const struct lttng_action *action,
-               struct lttng_error_query_results *results)
-{
-       enum lttng_action_status action_status;
-       struct lttng_error_query_result *error_counter = NULL;
-       const uint64_t execution_failure_counter =
-                       uatomic_read(&action->execution_failure_counter);
-
-       error_counter = lttng_error_query_result_counter_create(
-                       "total execution failures",
-                       "Aggregated count of errors encountered when executing the action",
-                       execution_failure_counter);
-       if (!error_counter) {
-               action_status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (lttng_error_query_results_add_result(
-                           results, error_counter)) {
-               action_status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Ownership transferred to the results. */
-       error_counter = NULL;
-       action_status = LTTNG_ACTION_STATUS_OK;
-end:
-       lttng_error_query_result_destroy(error_counter);
-       return action_status;
-}
-
-enum lttng_error_code lttng_action_mi_serialize(const struct lttng_trigger *trigger,
-               const struct lttng_action *action,
-               struct mi_writer *writer,
-               const struct mi_lttng_error_query_callbacks
-                               *error_query_callbacks,
-               struct lttng_dynamic_array *action_path_indexes)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_action_path *action_path = NULL;
-       struct lttng_error_query_results *error_query_results = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(writer);
-
-       /* Open action. */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_action);
-       if (ret) {
-               goto mi_error;
-       }
-
-       if (action->type == LTTNG_ACTION_TYPE_LIST) {
-               /*
-                * Recursion is safe since action lists can't be nested for
-                * the moment.
-                */
-               ret_code = lttng_action_list_mi_serialize(trigger, action, writer,
-                               error_query_callbacks, action_path_indexes);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-
-               /* Nothing else to do. */
-               goto close_action_element;
-       }
-
-       LTTNG_ASSERT(action->mi_serialize);
-       ret_code = action->mi_serialize(action, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Error query for the action. */
-       if (error_query_callbacks && error_query_callbacks->action_cb) {
-               const uint64_t *action_path_indexes_raw_pointer = NULL;
-               const size_t action_path_indexes_size =
-                               lttng_dynamic_array_get_count(
-                                               action_path_indexes);
-
-               if (action_path_indexes_size != 0) {
-                       action_path_indexes_raw_pointer =
-                                       (const uint64_t *) action_path_indexes
-                                                       ->buffer.data;
-               }
-
-               action_path = lttng_action_path_create(
-                               action_path_indexes_raw_pointer,
-                               action_path_indexes_size);
-               LTTNG_ASSERT(action_path);
-
-               ret_code = error_query_callbacks->action_cb(
-                               trigger, action_path, &error_query_results);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-
-               /* Serialize the error query results. */
-               ret_code = lttng_error_query_results_mi_serialize(
-                               error_query_results, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-close_action_element:
-       /* Close action. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       lttng_action_path_destroy(action_path);
-       lttng_error_query_results_destroy(error_query_results);
-       return ret_code;
-}
diff --git a/src/common/actions/action.cpp b/src/common/actions/action.cpp
new file mode 100644 (file)
index 0000000..b71538b
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/mi-lttng.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/list-internal.h>
+#include <lttng/action/notify-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/stop-session-internal.h>
+#include <lttng/error-query-internal.h>
+
+const char *lttng_action_type_string(enum lttng_action_type action_type)
+{
+       switch (action_type) {
+       case LTTNG_ACTION_TYPE_UNKNOWN:
+               return "UNKNOWN";
+       case LTTNG_ACTION_TYPE_LIST:
+               return "LIST";
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               return "NOTIFY";
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               return "ROTATE_SESSION";
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               return "SNAPSHOT_SESSION";
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               return "START_SESSION";
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               return "STOP_SESSION";
+       default:
+               return "???";
+       }
+}
+
+enum lttng_action_type lttng_action_get_type(const struct lttng_action *action)
+{
+       return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN;
+}
+
+void lttng_action_init(struct lttng_action *action,
+               enum lttng_action_type type,
+               action_validate_cb validate,
+               action_serialize_cb serialize,
+               action_equal_cb equal,
+               action_destroy_cb destroy,
+               action_get_rate_policy_cb get_rate_policy,
+               action_add_error_query_results_cb add_error_query_results,
+               action_mi_serialize_cb mi)
+{
+       urcu_ref_init(&action->ref);
+       action->type = type;
+       action->validate = validate;
+       action->serialize = serialize;
+       action->equal = equal;
+       action->destroy = destroy;
+       action->get_rate_policy = get_rate_policy;
+       action->add_error_query_results = add_error_query_results;
+       action->mi_serialize = mi;
+
+       action->execution_request_counter = 0;
+       action->execution_counter = 0;
+       action->execution_failure_counter = 0;
+}
+
+static
+void action_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_action *action =
+                       container_of(ref, struct lttng_action, ref);
+
+       action->destroy(action);
+}
+
+void lttng_action_get(struct lttng_action *action)
+{
+       urcu_ref_get(&action->ref);
+}
+
+void lttng_action_put(struct lttng_action *action)
+{
+       if (!action) {
+               return;
+       }
+
+       LTTNG_ASSERT(action->destroy);
+       urcu_ref_put(&action->ref, action_destroy_ref);
+}
+
+void lttng_action_destroy(struct lttng_action *action)
+{
+       lttng_action_put(action);
+}
+
+bool lttng_action_validate(struct lttng_action *action)
+{
+       bool valid;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       if (!action->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = action->validate(action);
+end:
+       return valid;
+}
+
+int lttng_action_serialize(struct lttng_action *action,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_action_comm action_comm = {
+               .action_type = (int8_t) action->type,
+       };
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &action_comm,
+                       sizeof(action_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = action->serialize(action, payload);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view,
+               struct lttng_action **action)
+{
+       ssize_t consumed_len, specific_action_consumed_len;
+       action_create_from_payload_cb create_from_payload_cb;
+       const struct lttng_action_comm *action_comm;
+       const struct lttng_payload_view action_comm_view =
+                       lttng_payload_view_from_view(
+                                       view, 0, sizeof(*action_comm));
+
+       if (!view || !action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&action_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               consumed_len = -1;
+               goto end;
+       }
+
+       action_comm = (const struct lttng_action_comm *) action_comm_view.buffer.data;
+
+       DBG("Create action from payload: action-type=%s",
+                       lttng_action_type_string((lttng_action_type) action_comm->action_type));
+
+       switch (action_comm->action_type) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               create_from_payload_cb = lttng_action_notify_create_from_payload;
+               break;
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               create_from_payload_cb =
+                               lttng_action_rotate_session_create_from_payload;
+               break;
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               create_from_payload_cb =
+                               lttng_action_snapshot_session_create_from_payload;
+               break;
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               create_from_payload_cb =
+                               lttng_action_start_session_create_from_payload;
+               break;
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               create_from_payload_cb =
+                               lttng_action_stop_session_create_from_payload;
+               break;
+       case LTTNG_ACTION_TYPE_LIST:
+               create_from_payload_cb = lttng_action_list_create_from_payload;
+               break;
+       default:
+               ERR("Failed to create action from payload, unhandled action type: action-type=%u (%s)",
+                               action_comm->action_type,
+                               lttng_action_type_string(
+                                               (lttng_action_type) action_comm->action_type));
+               consumed_len = -1;
+               goto end;
+       }
+
+       {
+               /* Create buffer view for the action-type-specific data. */
+               struct lttng_payload_view specific_action_view =
+                               lttng_payload_view_from_view(view,
+                                               sizeof(struct lttng_action_comm),
+                                               -1);
+
+               specific_action_consumed_len = create_from_payload_cb(
+                               &specific_action_view, action);
+       }
+       if (specific_action_consumed_len < 0) {
+               ERR("Failed to create specific action from buffer.");
+               consumed_len = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(*action);
+
+       consumed_len = sizeof(struct lttng_action_comm) +
+                      specific_action_consumed_len;
+
+end:
+       return consumed_len;
+}
+
+bool lttng_action_is_equal(const struct lttng_action *a,
+               const struct lttng_action *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->equal);
+       is_equal = a->equal(a, b);
+end:
+       return is_equal;
+}
+
+void lttng_action_increase_execution_request_count(struct lttng_action *action)
+{
+       action->execution_request_counter++;
+}
+
+void lttng_action_increase_execution_count(struct lttng_action *action)
+{
+       action->execution_counter++;
+}
+
+void lttng_action_increase_execution_failure_count(struct lttng_action *action)
+{
+       uatomic_inc(&action->execution_failure_counter);
+}
+
+bool lttng_action_should_execute(const struct lttng_action *action)
+{
+       const struct lttng_rate_policy *policy = NULL;
+       bool execute = false;
+
+       if (action->get_rate_policy == NULL) {
+               execute = true;
+               goto end;
+       }
+
+       policy = action->get_rate_policy(action);
+       if (policy == NULL) {
+               execute = true;
+               goto end;
+       }
+
+       execute = lttng_rate_policy_should_execute(
+                       policy, action->execution_request_counter);
+end:
+       return execute;
+}
+
+enum lttng_action_status lttng_action_add_error_query_results(
+               const struct lttng_action *action,
+               struct lttng_error_query_results *results)
+{
+       return action->add_error_query_results(action, results);
+}
+
+enum lttng_action_status lttng_action_generic_add_error_query_results(
+               const struct lttng_action *action,
+               struct lttng_error_query_results *results)
+{
+       enum lttng_action_status action_status;
+       struct lttng_error_query_result *error_counter = NULL;
+       const uint64_t execution_failure_counter =
+                       uatomic_read(&action->execution_failure_counter);
+
+       error_counter = lttng_error_query_result_counter_create(
+                       "total execution failures",
+                       "Aggregated count of errors encountered when executing the action",
+                       execution_failure_counter);
+       if (!error_counter) {
+               action_status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (lttng_error_query_results_add_result(
+                           results, error_counter)) {
+               action_status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Ownership transferred to the results. */
+       error_counter = NULL;
+       action_status = LTTNG_ACTION_STATUS_OK;
+end:
+       lttng_error_query_result_destroy(error_counter);
+       return action_status;
+}
+
+enum lttng_error_code lttng_action_mi_serialize(const struct lttng_trigger *trigger,
+               const struct lttng_action *action,
+               struct mi_writer *writer,
+               const struct mi_lttng_error_query_callbacks
+                               *error_query_callbacks,
+               struct lttng_dynamic_array *action_path_indexes)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_action_path *action_path = NULL;
+       struct lttng_error_query_results *error_query_results = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(writer);
+
+       /* Open action. */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_action);
+       if (ret) {
+               goto mi_error;
+       }
+
+       if (action->type == LTTNG_ACTION_TYPE_LIST) {
+               /*
+                * Recursion is safe since action lists can't be nested for
+                * the moment.
+                */
+               ret_code = lttng_action_list_mi_serialize(trigger, action, writer,
+                               error_query_callbacks, action_path_indexes);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+
+               /* Nothing else to do. */
+               goto close_action_element;
+       }
+
+       LTTNG_ASSERT(action->mi_serialize);
+       ret_code = action->mi_serialize(action, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Error query for the action. */
+       if (error_query_callbacks && error_query_callbacks->action_cb) {
+               const uint64_t *action_path_indexes_raw_pointer = NULL;
+               const size_t action_path_indexes_size =
+                               lttng_dynamic_array_get_count(
+                                               action_path_indexes);
+
+               if (action_path_indexes_size != 0) {
+                       action_path_indexes_raw_pointer =
+                                       (const uint64_t *) action_path_indexes
+                                                       ->buffer.data;
+               }
+
+               action_path = lttng_action_path_create(
+                               action_path_indexes_raw_pointer,
+                               action_path_indexes_size);
+               LTTNG_ASSERT(action_path);
+
+               ret_code = error_query_callbacks->action_cb(
+                               trigger, action_path, &error_query_results);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+
+               /* Serialize the error query results. */
+               ret_code = lttng_error_query_results_mi_serialize(
+                               error_query_results, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+close_action_element:
+       /* Close action. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       lttng_action_path_destroy(action_path);
+       lttng_error_query_results_destroy(error_query_results);
+       return ret_code;
+}
diff --git a/src/common/actions/list.c b/src/common/actions/list.c
deleted file mode 100644 (file)
index 86eabda..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/dynamic-array.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/list-internal.h>
-#include <lttng/action/list.h>
-
-#define IS_LIST_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_LIST)
-
-struct lttng_action_list {
-       struct lttng_action parent;
-
-       /* The array owns the action elements. */
-       struct lttng_dynamic_pointer_array actions;
-};
-
-struct lttng_action_list_comm {
-       uint32_t action_count;
-
-       /*
-        * Variable data: each element serialized sequentially.
-        */
-       char data[];
-} LTTNG_PACKED;
-
-static void destroy_lttng_action_list_element(void *ptr)
-{
-       struct lttng_action *element = (struct lttng_action *) ptr;
-
-       lttng_action_destroy(element);
-}
-
-static struct lttng_action_list *action_list_from_action(
-               const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_list, parent);
-}
-
-static const struct lttng_action_list *action_list_from_action_const(
-               const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_list, parent);
-}
-
-static bool lttng_action_list_validate(struct lttng_action *action)
-{
-       unsigned int i, count;
-       struct lttng_action_list *action_list;
-       bool valid;
-
-       LTTNG_ASSERT(IS_LIST_ACTION(action));
-
-       action_list = action_list_from_action(action);
-
-       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
-
-       for (i = 0; i < count; i++) {
-               struct lttng_action *child =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                               &action_list->actions, i);
-
-               LTTNG_ASSERT(child);
-
-               if (!lttng_action_validate(child)) {
-                       valid = false;
-                       goto end;
-               }
-       }
-
-       valid = true;
-
-end:
-       return valid;
-}
-
-static bool lttng_action_list_is_equal(
-               const struct lttng_action *_a, const struct lttng_action *_b)
-{
-       bool is_equal = false;
-       unsigned int i;
-       unsigned int a_count, b_count;
-
-       if (lttng_action_list_get_count(_a, &a_count) !=
-                       LTTNG_ACTION_STATUS_OK) {
-               goto end;
-       }
-
-       if (lttng_action_list_get_count(_b, &b_count) !=
-                       LTTNG_ACTION_STATUS_OK) {
-               goto end;
-       }
-
-       if (a_count != b_count) {
-               goto end;
-       }
-
-       for (i = 0; i < a_count; i++) {
-               const struct lttng_action *child_a =
-                       lttng_action_list_get_at_index(_a, i);
-               const struct lttng_action *child_b =
-                       lttng_action_list_get_at_index(_b, i);
-
-               LTTNG_ASSERT(child_a);
-               LTTNG_ASSERT(child_b);
-
-               if (!lttng_action_is_equal(child_a, child_b)) {
-                       goto end;
-               }
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static int lttng_action_list_serialize(
-               struct lttng_action *action, struct lttng_payload *payload)
-{
-       struct lttng_action_list *action_list;
-       struct lttng_action_list_comm comm;
-       int ret;
-       unsigned int i, count;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(payload);
-       LTTNG_ASSERT(IS_LIST_ACTION(action));
-
-       action_list = action_list_from_action(action);
-
-       DBG("Serializing action list");
-
-       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
-
-       comm.action_count = count;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       for (i = 0; i < count; i++) {
-               struct lttng_action *child =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                               &action_list->actions, i);
-
-               LTTNG_ASSERT(child);
-
-               ret = lttng_action_serialize(child, payload);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       ret = 0;
-
-end:
-       return ret;
-}
-
-static void lttng_action_list_destroy(struct lttng_action *action)
-{
-       struct lttng_action_list *action_list;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_list = action_list_from_action(action);
-       lttng_dynamic_pointer_array_reset(&action_list->actions);
-       free(action_list);
-
-end:
-       return;
-}
-
-ssize_t lttng_action_list_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **p_action)
-{
-       ssize_t consumed_len;
-       const struct lttng_action_list_comm *comm;
-       struct lttng_action *list;
-       struct lttng_action *child_action = NULL;
-       enum lttng_action_status status;
-       size_t i;
-
-       list = lttng_action_list_create();
-       if (!list) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       comm = (typeof(comm)) view->buffer.data;
-
-       consumed_len = sizeof(struct lttng_action_list_comm);
-
-       for (i = 0; i < comm->action_count; i++) {
-               ssize_t consumed_len_child;
-               struct lttng_payload_view child_view =
-                               lttng_payload_view_from_view(view, consumed_len,
-                                               view->buffer.size - consumed_len);
-
-               if (!lttng_payload_view_is_valid(&child_view)) {
-                       consumed_len = -1;
-                       goto end;
-               }
-
-               consumed_len_child = lttng_action_create_from_payload(
-                               &child_view, &child_action);
-               if (consumed_len_child < 0) {
-                       consumed_len = -1;
-                       goto end;
-               }
-
-               status = lttng_action_list_add_action(list, child_action);
-               if (status != LTTNG_ACTION_STATUS_OK) {
-                       consumed_len = -1;
-                       goto end;
-               }
-
-               /* Transfer ownership to the action list. */
-               lttng_action_put(child_action);
-               child_action = NULL;
-
-               consumed_len += consumed_len_child;
-       }
-
-       *p_action = list;
-       list = NULL;
-
-end:
-       lttng_action_list_destroy(list);
-       return consumed_len;
-}
-
-static enum lttng_action_status lttng_action_list_add_error_query_results(
-               const struct lttng_action *action,
-               struct lttng_error_query_results *results)
-{
-       unsigned int i, count;
-       enum lttng_action_status action_status;
-       const struct lttng_action_list *list =
-                       container_of(action, typeof(*list), parent);
-
-       action_status = lttng_action_list_get_count(action, &count);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               goto end;
-       }
-
-       for (i = 0; i < count; i++) {
-               struct lttng_action *inner_action =
-                               lttng_action_list_borrow_mutable_at_index(action, i);
-
-               action_status = lttng_action_add_error_query_results(
-                               inner_action, results);
-               if (action_status != LTTNG_ACTION_STATUS_OK) {
-                       goto end;
-               }
-       }
-end:
-       return action_status;
-}
-
-enum lttng_error_code lttng_action_list_mi_serialize(
-               const struct lttng_trigger *trigger,
-               const struct lttng_action *action,
-               struct mi_writer *writer,
-               const struct mi_lttng_error_query_callbacks
-                               *error_query_callbacks,
-               struct lttng_dynamic_array *action_path_indexes)
-{
-       int ret;
-       struct lttng_action_list *action_list;
-       unsigned int i, count;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_LIST_ACTION(action));
-       LTTNG_ASSERT(writer);
-
-       /* Open action list. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_list);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize every action of the list. */
-       action_list = action_list_from_action(action);
-       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
-       for (i = 0; i < count; i++) {
-               const struct lttng_action *child =
-                               lttng_action_list_get_at_index(action, i);
-               const uint64_t index = (uint64_t) i;
-
-               LTTNG_ASSERT(child);
-
-               /*
-                * Add the index to the action path.
-                *
-                * This index is replaced on every iteration to walk the action
-                * tree in-order and to re-use the dynamic array instead of
-                * copying it at every level.
-                */
-               ret = lttng_dynamic_array_add_element(
-                               action_path_indexes, &index);
-               if (ret) {
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto end;
-               }
-
-               ret_code = lttng_action_mi_serialize(trigger, child, writer,
-                               error_query_callbacks, action_path_indexes);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-
-               ret = lttng_dynamic_array_remove_element(action_path_indexes,
-                               lttng_dynamic_array_get_count(
-                                               action_path_indexes) -
-                                               1);
-               if (ret) {
-                       ret_code = LTTNG_ERR_UNK;
-                       goto end;
-               }
-       }
-
-       /* Close action_list element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_list_create(void)
-{
-       struct lttng_action_list *action_list;
-       struct lttng_action *action;
-
-       action_list = zmalloc(sizeof(struct lttng_action_list));
-       if (!action_list) {
-               action = NULL;
-               goto end;
-       }
-
-       action = &action_list->parent;
-
-       /*
-        * The mi for the list is handled at the lttng_action_mi level to ease
-        * action path management for error query.
-        */
-       lttng_action_init(action, LTTNG_ACTION_TYPE_LIST,
-                       lttng_action_list_validate, lttng_action_list_serialize,
-                       lttng_action_list_is_equal, lttng_action_list_destroy,
-                       NULL, lttng_action_list_add_error_query_results, NULL);
-
-       lttng_dynamic_pointer_array_init(&action_list->actions,
-                       destroy_lttng_action_list_element);
-
-end:
-       return action;
-}
-
-enum lttng_action_status lttng_action_list_add_action(
-               struct lttng_action *list, struct lttng_action *action)
-{
-       struct lttng_action_list *action_list;
-       enum lttng_action_status status;
-       int ret;
-
-       if (!list || !IS_LIST_ACTION(list) || !action) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       /*
-        * Don't allow adding lists in lists for now, since we're afraid of
-        * cycles.
-        */
-       if (IS_LIST_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_list = action_list_from_action(list);
-
-       ret = lttng_dynamic_pointer_array_add_pointer(&action_list->actions,
-                       action);
-       if (ret < 0) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Take ownership of the object. */
-       lttng_action_get(action);
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_list_get_count(
-               const struct lttng_action *list, unsigned int *count)
-{
-       const struct lttng_action_list *action_list;
-       enum lttng_action_status status = LTTNG_ACTION_STATUS_OK;
-
-       if (!list || !IS_LIST_ACTION(list)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               *count = 0;
-               goto end;
-       }
-
-       action_list = action_list_from_action_const(list);
-       *count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
-end:
-       return status;
-}
-
-const struct lttng_action *lttng_action_list_get_at_index(
-               const struct lttng_action *list, unsigned int index)
-{
-       return lttng_action_list_borrow_mutable_at_index(list, index);
-}
-
-struct lttng_action *lttng_action_list_borrow_mutable_at_index(
-               const struct lttng_action *list, unsigned int index)
-{
-       unsigned int count;
-       const struct lttng_action_list *action_list;
-       struct lttng_action *action = NULL;
-
-       if (lttng_action_list_get_count(list, &count) !=
-                       LTTNG_ACTION_STATUS_OK) {
-               goto end;
-       }
-
-       if (index >= count) {
-               goto end;
-       }
-
-       action_list = action_list_from_action_const(list);
-       action = lttng_dynamic_pointer_array_get_pointer(&action_list->actions,
-                       index);
-end:
-       return action;
-}
diff --git a/src/common/actions/list.cpp b/src/common/actions/list.cpp
new file mode 100644 (file)
index 0000000..42d3d65
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/list-internal.h>
+#include <lttng/action/list.h>
+
+#define IS_LIST_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_LIST)
+
+struct lttng_action_list {
+       struct lttng_action parent;
+
+       /* The array owns the action elements. */
+       struct lttng_dynamic_pointer_array actions;
+};
+
+struct lttng_action_list_comm {
+       uint32_t action_count;
+
+       /*
+        * Variable data: each element serialized sequentially.
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static void destroy_lttng_action_list_element(void *ptr)
+{
+       struct lttng_action *element = (struct lttng_action *) ptr;
+
+       lttng_action_destroy(element);
+}
+
+static struct lttng_action_list *action_list_from_action(
+               const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_list, parent);
+}
+
+static const struct lttng_action_list *action_list_from_action_const(
+               const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_list, parent);
+}
+
+static bool lttng_action_list_validate(struct lttng_action *action)
+{
+       unsigned int i, count;
+       struct lttng_action_list *action_list;
+       bool valid;
+
+       LTTNG_ASSERT(IS_LIST_ACTION(action));
+
+       action_list = action_list_from_action(action);
+
+       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
+
+       for (i = 0; i < count; i++) {
+               struct lttng_action *child =
+                               (lttng_action *) lttng_dynamic_pointer_array_get_pointer(
+                                               &action_list->actions, i);
+
+               LTTNG_ASSERT(child);
+
+               if (!lttng_action_validate(child)) {
+                       valid = false;
+                       goto end;
+               }
+       }
+
+       valid = true;
+
+end:
+       return valid;
+}
+
+static bool lttng_action_list_is_equal(
+               const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       unsigned int i;
+       unsigned int a_count, b_count;
+
+       if (lttng_action_list_get_count(_a, &a_count) !=
+                       LTTNG_ACTION_STATUS_OK) {
+               goto end;
+       }
+
+       if (lttng_action_list_get_count(_b, &b_count) !=
+                       LTTNG_ACTION_STATUS_OK) {
+               goto end;
+       }
+
+       if (a_count != b_count) {
+               goto end;
+       }
+
+       for (i = 0; i < a_count; i++) {
+               const struct lttng_action *child_a =
+                       lttng_action_list_get_at_index(_a, i);
+               const struct lttng_action *child_b =
+                       lttng_action_list_get_at_index(_b, i);
+
+               LTTNG_ASSERT(child_a);
+               LTTNG_ASSERT(child_b);
+
+               if (!lttng_action_is_equal(child_a, child_b)) {
+                       goto end;
+               }
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static int lttng_action_list_serialize(
+               struct lttng_action *action, struct lttng_payload *payload)
+{
+       struct lttng_action_list *action_list;
+       struct lttng_action_list_comm comm;
+       int ret;
+       unsigned int i, count;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(payload);
+       LTTNG_ASSERT(IS_LIST_ACTION(action));
+
+       action_list = action_list_from_action(action);
+
+       DBG("Serializing action list");
+
+       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
+
+       comm.action_count = count;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               struct lttng_action *child =
+                               (lttng_action *) lttng_dynamic_pointer_array_get_pointer(
+                                               &action_list->actions, i);
+
+               LTTNG_ASSERT(child);
+
+               ret = lttng_action_serialize(child, payload);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static void lttng_action_list_destroy(struct lttng_action *action)
+{
+       struct lttng_action_list *action_list;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_list = action_list_from_action(action);
+       lttng_dynamic_pointer_array_reset(&action_list->actions);
+       free(action_list);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_list_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       const struct lttng_action_list_comm *comm;
+       struct lttng_action *list;
+       struct lttng_action *child_action = NULL;
+       enum lttng_action_status status;
+       size_t i;
+
+       list = lttng_action_list_create();
+       if (!list) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (typeof(comm)) view->buffer.data;
+
+       consumed_len = sizeof(struct lttng_action_list_comm);
+
+       for (i = 0; i < comm->action_count; i++) {
+               ssize_t consumed_len_child;
+               struct lttng_payload_view child_view =
+                               lttng_payload_view_from_view(view, consumed_len,
+                                               view->buffer.size - consumed_len);
+
+               if (!lttng_payload_view_is_valid(&child_view)) {
+                       consumed_len = -1;
+                       goto end;
+               }
+
+               consumed_len_child = lttng_action_create_from_payload(
+                               &child_view, &child_action);
+               if (consumed_len_child < 0) {
+                       consumed_len = -1;
+                       goto end;
+               }
+
+               status = lttng_action_list_add_action(list, child_action);
+               if (status != LTTNG_ACTION_STATUS_OK) {
+                       consumed_len = -1;
+                       goto end;
+               }
+
+               /* Transfer ownership to the action list. */
+               lttng_action_put(child_action);
+               child_action = NULL;
+
+               consumed_len += consumed_len_child;
+       }
+
+       *p_action = list;
+       list = NULL;
+
+end:
+       lttng_action_list_destroy(list);
+       return consumed_len;
+}
+
+static enum lttng_action_status lttng_action_list_add_error_query_results(
+               const struct lttng_action *action,
+               struct lttng_error_query_results *results)
+{
+       unsigned int i, count;
+       enum lttng_action_status action_status;
+       const struct lttng_action_list *list =
+                       container_of(action, typeof(*list), parent);
+
+       action_status = lttng_action_list_get_count(action, &count);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               struct lttng_action *inner_action =
+                               lttng_action_list_borrow_mutable_at_index(action, i);
+
+               action_status = lttng_action_add_error_query_results(
+                               inner_action, results);
+               if (action_status != LTTNG_ACTION_STATUS_OK) {
+                       goto end;
+               }
+       }
+end:
+       return action_status;
+}
+
+enum lttng_error_code lttng_action_list_mi_serialize(
+               const struct lttng_trigger *trigger,
+               const struct lttng_action *action,
+               struct mi_writer *writer,
+               const struct mi_lttng_error_query_callbacks
+                               *error_query_callbacks,
+               struct lttng_dynamic_array *action_path_indexes)
+{
+       int ret;
+       struct lttng_action_list *action_list;
+       unsigned int i, count;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_LIST_ACTION(action));
+       LTTNG_ASSERT(writer);
+
+       /* Open action list. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_list);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize every action of the list. */
+       action_list = action_list_from_action(action);
+       count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
+       for (i = 0; i < count; i++) {
+               const struct lttng_action *child =
+                               lttng_action_list_get_at_index(action, i);
+               const uint64_t index = (uint64_t) i;
+
+               LTTNG_ASSERT(child);
+
+               /*
+                * Add the index to the action path.
+                *
+                * This index is replaced on every iteration to walk the action
+                * tree in-order and to re-use the dynamic array instead of
+                * copying it at every level.
+                */
+               ret = lttng_dynamic_array_add_element(
+                               action_path_indexes, &index);
+               if (ret) {
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+
+               ret_code = lttng_action_mi_serialize(trigger, child, writer,
+                               error_query_callbacks, action_path_indexes);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+
+               ret = lttng_dynamic_array_remove_element(action_path_indexes,
+                               lttng_dynamic_array_get_count(
+                                               action_path_indexes) -
+                                               1);
+               if (ret) {
+                       ret_code = LTTNG_ERR_UNK;
+                       goto end;
+               }
+       }
+
+       /* Close action_list element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_list_create(void)
+{
+       struct lttng_action_list *action_list;
+       struct lttng_action *action;
+
+       action_list = (lttng_action_list *) zmalloc(sizeof(struct lttng_action_list));
+       if (!action_list) {
+               action = NULL;
+               goto end;
+       }
+
+       action = &action_list->parent;
+
+       /*
+        * The mi for the list is handled at the lttng_action_mi level to ease
+        * action path management for error query.
+        */
+       lttng_action_init(action, LTTNG_ACTION_TYPE_LIST,
+                       lttng_action_list_validate, lttng_action_list_serialize,
+                       lttng_action_list_is_equal, lttng_action_list_destroy,
+                       NULL, lttng_action_list_add_error_query_results, NULL);
+
+       lttng_dynamic_pointer_array_init(&action_list->actions,
+                       destroy_lttng_action_list_element);
+
+end:
+       return action;
+}
+
+enum lttng_action_status lttng_action_list_add_action(
+               struct lttng_action *list, struct lttng_action *action)
+{
+       struct lttng_action_list *action_list;
+       enum lttng_action_status status;
+       int ret;
+
+       if (!list || !IS_LIST_ACTION(list) || !action) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       /*
+        * Don't allow adding lists in lists for now, since we're afraid of
+        * cycles.
+        */
+       if (IS_LIST_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_list = action_list_from_action(list);
+
+       ret = lttng_dynamic_pointer_array_add_pointer(&action_list->actions,
+                       action);
+       if (ret < 0) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Take ownership of the object. */
+       lttng_action_get(action);
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_list_get_count(
+               const struct lttng_action *list, unsigned int *count)
+{
+       const struct lttng_action_list *action_list;
+       enum lttng_action_status status = LTTNG_ACTION_STATUS_OK;
+
+       if (!list || !IS_LIST_ACTION(list)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               *count = 0;
+               goto end;
+       }
+
+       action_list = action_list_from_action_const(list);
+       *count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
+end:
+       return status;
+}
+
+const struct lttng_action *lttng_action_list_get_at_index(
+               const struct lttng_action *list, unsigned int index)
+{
+       return lttng_action_list_borrow_mutable_at_index(list, index);
+}
+
+struct lttng_action *lttng_action_list_borrow_mutable_at_index(
+               const struct lttng_action *list, unsigned int index)
+{
+       unsigned int count;
+       const struct lttng_action_list *action_list;
+       struct lttng_action *action = NULL;
+
+       if (lttng_action_list_get_count(list, &count) !=
+                       LTTNG_ACTION_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= count) {
+               goto end;
+       }
+
+       action_list = action_list_from_action_const(list);
+       action = (lttng_action *) lttng_dynamic_pointer_array_get_pointer(&action_list->actions,
+                       index);
+end:
+       return action;
+}
diff --git a/src/common/actions/notify.c b/src/common/actions/notify.c
deleted file mode 100644 (file)
index c34f384..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/notify-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/lttng-error.h>
-
-#define IS_NOTIFY_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_NOTIFY)
-
-static struct lttng_action_notify *action_notify_from_action(
-               struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_notify, parent);
-}
-
-static const struct lttng_action_notify *action_notify_from_action_const(
-               const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_notify, parent);
-}
-
-static
-void lttng_action_notify_destroy(struct lttng_action *action)
-{
-       struct lttng_action_notify *notify_action;
-       notify_action = action_notify_from_action(action);
-       lttng_rate_policy_destroy(notify_action->policy);
-       free(notify_action);
-}
-
-static
-int lttng_action_notify_serialize(struct lttng_action *action,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_action_notify *notify_action;
-
-       if (!action || !IS_NOTIFY_ACTION(action) || !payload) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing notify action");
-
-       notify_action = action_notify_from_action(action);
-       DBG("Serializing notify action rate policy");
-       ret = lttng_rate_policy_serialize(notify_action->policy, payload);
-
-end:
-       return ret;
-}
-
-static
-bool lttng_action_notify_is_equal(const struct lttng_action *a,
-               const struct lttng_action *b)
-{
-       const struct lttng_action_notify *_a, *_b;
-
-       _a = action_notify_from_action_const(a);
-       _b = action_notify_from_action_const(b);
-       return lttng_rate_policy_is_equal(_a->policy, _b->policy);
-}
-
-static const struct lttng_rate_policy *
-lttng_action_notify_internal_get_rate_policy(const struct lttng_action *action)
-{
-       const struct lttng_action_notify *_action;
-       _action = action_notify_from_action_const(action);
-
-       return _action->policy;
-}
-
-static enum lttng_error_code lttng_action_notify_mi_serialize(
-               const struct lttng_action *action, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_action_status status;
-       enum lttng_error_code ret_code;
-       const struct lttng_rate_policy *policy = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_NOTIFY_ACTION(action));
-       LTTNG_ASSERT(writer);
-
-       status = lttng_action_notify_get_rate_policy(action, &policy);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(policy != NULL);
-
-       /* Open action notify. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_notify);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close action notify element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_notify_create(void)
-{
-       struct lttng_rate_policy *policy = NULL;
-       struct lttng_action_notify *notify = NULL;
-       struct lttng_action *action = NULL;
-
-       notify = zmalloc(sizeof(struct lttng_action_notify));
-       if (!notify) {
-               goto end;
-       }
-
-       /* Default policy. */
-       policy = lttng_rate_policy_every_n_create(1);
-       if (!policy) {
-               goto end;
-       }
-
-       lttng_action_init(&notify->parent, LTTNG_ACTION_TYPE_NOTIFY, NULL,
-                       lttng_action_notify_serialize,
-                       lttng_action_notify_is_equal,
-                       lttng_action_notify_destroy,
-                       lttng_action_notify_internal_get_rate_policy,
-                       lttng_action_generic_add_error_query_results,
-                       lttng_action_notify_mi_serialize);
-
-       notify->policy = policy;
-       policy = NULL;
-
-       action = &notify->parent;
-       notify = NULL;
-
-end:
-       free(notify);
-       lttng_rate_policy_destroy(policy);
-       return action;
-}
-
-ssize_t lttng_action_notify_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **action)
-{
-       enum lttng_action_status status;
-       ssize_t consumed_length;
-       struct lttng_rate_policy *rate_policy = NULL;
-       struct lttng_action *_action = NULL;
-
-       consumed_length = lttng_rate_policy_create_from_payload(
-                       view, &rate_policy);
-       if (!rate_policy) {
-               consumed_length = -1;
-               goto end;
-       }
-
-       _action = lttng_action_notify_create();
-       if (!_action) {
-               consumed_length = -1;
-               goto end;
-       }
-
-       status = lttng_action_notify_set_rate_policy(_action, rate_policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_length = -1;
-               goto end;
-       }
-
-       *action = _action;
-       _action = NULL;
-
-end:
-       lttng_rate_policy_destroy(rate_policy);
-       lttng_action_destroy(_action);
-       return consumed_length;
-}
-
-enum lttng_action_status lttng_action_notify_set_rate_policy(
-               struct lttng_action *action,
-               const struct lttng_rate_policy *policy)
-{
-       enum lttng_action_status status;
-       struct lttng_action_notify *notify_action;
-       struct lttng_rate_policy *copy = NULL;
-
-       if (!action || !policy || !IS_NOTIFY_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_rate_policy_copy(policy);
-       if (!copy) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       notify_action = action_notify_from_action(action);
-
-       /* Free the previous rate policy .*/
-       lttng_rate_policy_destroy(notify_action->policy);
-
-       /* Assign the policy. */
-       notify_action->policy = copy;
-       status = LTTNG_ACTION_STATUS_OK;
-       copy = NULL;
-
-end:
-       lttng_rate_policy_destroy(copy);
-       return status;
-}
-
-enum lttng_action_status lttng_action_notify_get_rate_policy(
-               const struct lttng_action *action,
-               const struct lttng_rate_policy **policy)
-{
-       enum lttng_action_status status;
-       const struct lttng_action_notify *notify_action;
-
-       if (!action || !policy || !IS_NOTIFY_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       notify_action = action_notify_from_action_const(action);
-
-       *policy = notify_action->policy;
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
diff --git a/src/common/actions/notify.cpp b/src/common/actions/notify.cpp
new file mode 100644 (file)
index 0000000..e3f837a
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/notify-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/lttng-error.h>
+
+#define IS_NOTIFY_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_NOTIFY)
+
+static struct lttng_action_notify *action_notify_from_action(
+               struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_notify, parent);
+}
+
+static const struct lttng_action_notify *action_notify_from_action_const(
+               const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_notify, parent);
+}
+
+static
+void lttng_action_notify_destroy(struct lttng_action *action)
+{
+       struct lttng_action_notify *notify_action;
+       notify_action = action_notify_from_action(action);
+       lttng_rate_policy_destroy(notify_action->policy);
+       free(notify_action);
+}
+
+static
+int lttng_action_notify_serialize(struct lttng_action *action,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_action_notify *notify_action;
+
+       if (!action || !IS_NOTIFY_ACTION(action) || !payload) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing notify action");
+
+       notify_action = action_notify_from_action(action);
+       DBG("Serializing notify action rate policy");
+       ret = lttng_rate_policy_serialize(notify_action->policy, payload);
+
+end:
+       return ret;
+}
+
+static
+bool lttng_action_notify_is_equal(const struct lttng_action *a,
+               const struct lttng_action *b)
+{
+       const struct lttng_action_notify *_a, *_b;
+
+       _a = action_notify_from_action_const(a);
+       _b = action_notify_from_action_const(b);
+       return lttng_rate_policy_is_equal(_a->policy, _b->policy);
+}
+
+static const struct lttng_rate_policy *
+lttng_action_notify_internal_get_rate_policy(const struct lttng_action *action)
+{
+       const struct lttng_action_notify *_action;
+       _action = action_notify_from_action_const(action);
+
+       return _action->policy;
+}
+
+static enum lttng_error_code lttng_action_notify_mi_serialize(
+               const struct lttng_action *action, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_action_status status;
+       enum lttng_error_code ret_code;
+       const struct lttng_rate_policy *policy = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_NOTIFY_ACTION(action));
+       LTTNG_ASSERT(writer);
+
+       status = lttng_action_notify_get_rate_policy(action, &policy);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(policy != NULL);
+
+       /* Open action notify. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_notify);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close action notify element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_notify_create(void)
+{
+       struct lttng_rate_policy *policy = NULL;
+       struct lttng_action_notify *notify = NULL;
+       struct lttng_action *action = NULL;
+
+       notify = (lttng_action_notify *) zmalloc(sizeof(struct lttng_action_notify));
+       if (!notify) {
+               goto end;
+       }
+
+       /* Default policy. */
+       policy = lttng_rate_policy_every_n_create(1);
+       if (!policy) {
+               goto end;
+       }
+
+       lttng_action_init(&notify->parent, LTTNG_ACTION_TYPE_NOTIFY, NULL,
+                       lttng_action_notify_serialize,
+                       lttng_action_notify_is_equal,
+                       lttng_action_notify_destroy,
+                       lttng_action_notify_internal_get_rate_policy,
+                       lttng_action_generic_add_error_query_results,
+                       lttng_action_notify_mi_serialize);
+
+       notify->policy = policy;
+       policy = NULL;
+
+       action = &notify->parent;
+       notify = NULL;
+
+end:
+       free(notify);
+       lttng_rate_policy_destroy(policy);
+       return action;
+}
+
+ssize_t lttng_action_notify_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **action)
+{
+       enum lttng_action_status status;
+       ssize_t consumed_length;
+       struct lttng_rate_policy *rate_policy = NULL;
+       struct lttng_action *_action = NULL;
+
+       consumed_length = lttng_rate_policy_create_from_payload(
+                       view, &rate_policy);
+       if (!rate_policy) {
+               consumed_length = -1;
+               goto end;
+       }
+
+       _action = lttng_action_notify_create();
+       if (!_action) {
+               consumed_length = -1;
+               goto end;
+       }
+
+       status = lttng_action_notify_set_rate_policy(_action, rate_policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_length = -1;
+               goto end;
+       }
+
+       *action = _action;
+       _action = NULL;
+
+end:
+       lttng_rate_policy_destroy(rate_policy);
+       lttng_action_destroy(_action);
+       return consumed_length;
+}
+
+enum lttng_action_status lttng_action_notify_set_rate_policy(
+               struct lttng_action *action,
+               const struct lttng_rate_policy *policy)
+{
+       enum lttng_action_status status;
+       struct lttng_action_notify *notify_action;
+       struct lttng_rate_policy *copy = NULL;
+
+       if (!action || !policy || !IS_NOTIFY_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_rate_policy_copy(policy);
+       if (!copy) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       notify_action = action_notify_from_action(action);
+
+       /* Free the previous rate policy .*/
+       lttng_rate_policy_destroy(notify_action->policy);
+
+       /* Assign the policy. */
+       notify_action->policy = copy;
+       status = LTTNG_ACTION_STATUS_OK;
+       copy = NULL;
+
+end:
+       lttng_rate_policy_destroy(copy);
+       return status;
+}
+
+enum lttng_action_status lttng_action_notify_get_rate_policy(
+               const struct lttng_action *action,
+               const struct lttng_rate_policy **policy)
+{
+       enum lttng_action_status status;
+       const struct lttng_action_notify *notify_action;
+
+       if (!action || !policy || !IS_NOTIFY_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       notify_action = action_notify_from_action_const(action);
+
+       *policy = notify_action->policy;
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
diff --git a/src/common/actions/path.c b/src/common/actions/path.c
deleted file mode 100644 (file)
index 09cf08a..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/action/path-internal.h>
-
-struct lttng_action_path_comm {
-       uint32_t index_count;
-       uint64_t indexes[];
-} LTTNG_PACKED;
-
-struct lttng_action_path *lttng_action_path_create(
-               const uint64_t *indexes, size_t index_count)
-{
-       int ret;
-       size_t i;
-       struct lttng_action_path *path = NULL;
-
-       if (!indexes && index_count > 0) {
-               goto error;
-       }
-
-       path = zmalloc(sizeof(*path));
-       if (!path) {
-               goto error;
-       }
-
-       lttng_dynamic_array_init(&path->indexes, sizeof(uint64_t), NULL);
-
-       for (i = 0; i < index_count; i++) {
-               ret = lttng_dynamic_array_add_element(
-                               &path->indexes, &indexes[i]);
-               if (ret) {
-                       goto error;
-               }
-       }
-
-       goto end;
-error:
-       lttng_action_path_destroy(path);
-       path = NULL;
-end:
-       return path;
-}
-
-enum lttng_action_path_status lttng_action_path_get_index_count(
-               const struct lttng_action_path *path, size_t *index_count)
-{
-       enum lttng_action_path_status status;
-
-       if (!path || !index_count) {
-               status = LTTNG_ACTION_PATH_STATUS_INVALID;
-               goto end;
-       }
-
-       *index_count = lttng_dynamic_array_get_count(&path->indexes);
-       status = LTTNG_ACTION_PATH_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_path_status lttng_action_path_get_index_at_index(
-               const struct lttng_action_path *path,
-               size_t path_index,
-               uint64_t *out_index)
-{
-       enum lttng_action_path_status status;
-
-       if (!path || !out_index ||
-                       path_index >= lttng_dynamic_array_get_count(
-                               &path->indexes)) {
-               status = LTTNG_ACTION_PATH_STATUS_INVALID;
-               goto end;
-       }
-
-       *out_index = *((typeof(out_index)) lttng_dynamic_array_get_element(
-                       &path->indexes, path_index));
-       status = LTTNG_ACTION_PATH_STATUS_OK;
-end:
-       return status;
-}
-
-void lttng_action_path_destroy(struct lttng_action_path *action_path)
-{
-       if (!action_path) {
-               goto end;
-       }
-
-       lttng_dynamic_array_reset(&action_path->indexes);
-       free(action_path);
-end:
-       return;
-}
-
-int lttng_action_path_copy(const struct lttng_action_path *src,
-               struct lttng_action_path *dst)
-{
-       int ret;
-       size_t i, src_count;
-
-       LTTNG_ASSERT(src);
-       LTTNG_ASSERT(dst);
-
-       lttng_dynamic_array_init(&dst->indexes, sizeof(uint64_t), NULL);
-       src_count = lttng_dynamic_array_get_count(&src->indexes);
-
-       for (i = 0; i < src_count; i++) {
-               const void *index = lttng_dynamic_array_get_element(
-                               &src->indexes, i);
-
-               ret = lttng_dynamic_array_add_element(&dst->indexes, index);
-               if (ret) {
-                       goto error;
-               }
-       }
-
-       ret = 0;
-       goto end;
-error:
-       lttng_dynamic_array_reset(&dst->indexes);
-end:
-       return ret;
-}
-
-ssize_t lttng_action_path_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action_path **_action_path)
-{
-       ssize_t consumed_size = 0, ret = -1;
-       const struct lttng_action_path_comm *header;
-       struct lttng_action_path *action_path = NULL;
-       const struct lttng_payload_view header_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*header));
-
-       if (!lttng_payload_view_is_valid(&header_view)) {
-               goto end;
-       }
-
-       header = (typeof(header)) header_view.buffer.data;
-       consumed_size += header_view.buffer.size;
-
-       /*
-        * An action path of size 0 can exist and represents a trigger with a
-        * single non-list action. Handle it differently since a payload view of
-        * size 0 is considered invalid.
-        */
-       if (header->index_count != 0)
-       {
-               const struct lttng_payload_view indexes_view =
-                               lttng_payload_view_from_view(view,
-                                               consumed_size,
-                                               header->index_count *
-                                                               sizeof(uint64_t));
-
-               if (!lttng_payload_view_is_valid(&indexes_view)) {
-                       goto end;
-               }
-
-               consumed_size += indexes_view.buffer.size;
-               action_path = lttng_action_path_create(
-                               (const uint64_t *) indexes_view.buffer.data,
-                               header->index_count);
-               if (!action_path) {
-                       goto end;
-               }
-       } else {
-               action_path = lttng_action_path_create(NULL, 0);
-               if (!action_path) {
-                       goto end;
-               }
-       }
-
-       ret = consumed_size;
-       *_action_path = action_path;
-end:
-       return ret;
-}
-
-int lttng_action_path_serialize(const struct lttng_action_path *action_path,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t index_count, i;
-       enum lttng_action_path_status status;
-
-       status = lttng_action_path_get_index_count(action_path, &index_count);
-       if (status != LTTNG_ACTION_PATH_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       &((struct lttng_action_path_comm) {
-                               .index_count = index_count
-                       }),
-                       sizeof(struct lttng_action_path_comm));
-
-       for (i = 0; i < index_count; i++) {
-               uint64_t path_index;
-
-               status = lttng_action_path_get_index_at_index(
-                               action_path, i, &path_index);
-               if (status != LTTNG_ACTION_PATH_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = lttng_dynamic_buffer_append(&payload->buffer, &path_index,
-                               sizeof(path_index));
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
diff --git a/src/common/actions/path.cpp b/src/common/actions/path.cpp
new file mode 100644 (file)
index 0000000..816f2cd
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/action/path-internal.h>
+
+struct lttng_action_path_comm {
+       uint32_t index_count;
+       uint64_t indexes[];
+} LTTNG_PACKED;
+
+struct lttng_action_path *lttng_action_path_create(
+               const uint64_t *indexes, size_t index_count)
+{
+       int ret;
+       size_t i;
+       struct lttng_action_path *path = NULL;
+
+       if (!indexes && index_count > 0) {
+               goto error;
+       }
+
+       path = (lttng_action_path *) zmalloc(sizeof(*path));
+       if (!path) {
+               goto error;
+       }
+
+       lttng_dynamic_array_init(&path->indexes, sizeof(uint64_t), NULL);
+
+       for (i = 0; i < index_count; i++) {
+               ret = lttng_dynamic_array_add_element(
+                               &path->indexes, &indexes[i]);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       goto end;
+error:
+       lttng_action_path_destroy(path);
+       path = NULL;
+end:
+       return path;
+}
+
+enum lttng_action_path_status lttng_action_path_get_index_count(
+               const struct lttng_action_path *path, size_t *index_count)
+{
+       enum lttng_action_path_status status;
+
+       if (!path || !index_count) {
+               status = LTTNG_ACTION_PATH_STATUS_INVALID;
+               goto end;
+       }
+
+       *index_count = lttng_dynamic_array_get_count(&path->indexes);
+       status = LTTNG_ACTION_PATH_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_path_status lttng_action_path_get_index_at_index(
+               const struct lttng_action_path *path,
+               size_t path_index,
+               uint64_t *out_index)
+{
+       enum lttng_action_path_status status;
+
+       if (!path || !out_index ||
+                       path_index >= lttng_dynamic_array_get_count(
+                               &path->indexes)) {
+               status = LTTNG_ACTION_PATH_STATUS_INVALID;
+               goto end;
+       }
+
+       *out_index = *((typeof(out_index)) lttng_dynamic_array_get_element(
+                       &path->indexes, path_index));
+       status = LTTNG_ACTION_PATH_STATUS_OK;
+end:
+       return status;
+}
+
+void lttng_action_path_destroy(struct lttng_action_path *action_path)
+{
+       if (!action_path) {
+               goto end;
+       }
+
+       lttng_dynamic_array_reset(&action_path->indexes);
+       free(action_path);
+end:
+       return;
+}
+
+int lttng_action_path_copy(const struct lttng_action_path *src,
+               struct lttng_action_path *dst)
+{
+       int ret;
+       size_t i, src_count;
+
+       LTTNG_ASSERT(src);
+       LTTNG_ASSERT(dst);
+
+       lttng_dynamic_array_init(&dst->indexes, sizeof(uint64_t), NULL);
+       src_count = lttng_dynamic_array_get_count(&src->indexes);
+
+       for (i = 0; i < src_count; i++) {
+               const void *index = lttng_dynamic_array_get_element(
+                               &src->indexes, i);
+
+               ret = lttng_dynamic_array_add_element(&dst->indexes, index);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       ret = 0;
+       goto end;
+error:
+       lttng_dynamic_array_reset(&dst->indexes);
+end:
+       return ret;
+}
+
+ssize_t lttng_action_path_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action_path **_action_path)
+{
+       ssize_t consumed_size = 0, ret = -1;
+       const struct lttng_action_path_comm *header;
+       struct lttng_action_path *action_path = NULL;
+       const struct lttng_payload_view header_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*header));
+
+       if (!lttng_payload_view_is_valid(&header_view)) {
+               goto end;
+       }
+
+       header = (typeof(header)) header_view.buffer.data;
+       consumed_size += header_view.buffer.size;
+
+       /*
+        * An action path of size 0 can exist and represents a trigger with a
+        * single non-list action. Handle it differently since a payload view of
+        * size 0 is considered invalid.
+        */
+       if (header->index_count != 0)
+       {
+               const struct lttng_payload_view indexes_view =
+                               lttng_payload_view_from_view(view,
+                                               consumed_size,
+                                               header->index_count *
+                                                               sizeof(uint64_t));
+
+               if (!lttng_payload_view_is_valid(&indexes_view)) {
+                       goto end;
+               }
+
+               consumed_size += indexes_view.buffer.size;
+               action_path = lttng_action_path_create(
+                               (const uint64_t *) indexes_view.buffer.data,
+                               header->index_count);
+               if (!action_path) {
+                       goto end;
+               }
+       } else {
+               action_path = lttng_action_path_create(NULL, 0);
+               if (!action_path) {
+                       goto end;
+               }
+       }
+
+       ret = consumed_size;
+       *_action_path = action_path;
+end:
+       return ret;
+}
+
+int lttng_action_path_serialize(const struct lttng_action_path *action_path,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t index_count, i;
+       enum lttng_action_path_status status;
+       lttng_action_path_comm comm;
+
+       status = lttng_action_path_get_index_count(action_path, &index_count);
+       if (status != LTTNG_ACTION_PATH_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       comm = {
+               .index_count = (uint32_t) index_count,
+       };
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       &comm,
+                       sizeof(struct lttng_action_path_comm));
+
+       for (i = 0; i < index_count; i++) {
+               uint64_t path_index;
+
+               status = lttng_action_path_get_index_at_index(
+                               action_path, i, &path_index);
+               if (status != LTTNG_ACTION_PATH_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = lttng_dynamic_buffer_append(&payload->buffer, &path_index,
+                               sizeof(path_index));
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
diff --git a/src/common/actions/rate-policy.c b/src/common/actions/rate-policy.c
deleted file mode 100644 (file)
index c47e170..0000000
+++ /dev/null
@@ -1,812 +0,0 @@
-/*
- * Copyright (C) 2021 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/buffer-view.h>
-#include <common/dynamic-buffer.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <limits.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rate-policy.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#define IS_EVERY_N_RATE_POLICY(policy) \
-       (lttng_rate_policy_get_type(policy) == LTTNG_RATE_POLICY_TYPE_EVERY_N)
-
-#define IS_ONCE_AFTER_N_RATE_POLICY(policy)    \
-       (lttng_rate_policy_get_type(policy) == \
-                       LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N)
-
-typedef void (*rate_policy_destroy_cb)(struct lttng_rate_policy *rate_policy);
-typedef int (*rate_policy_serialize_cb)(struct lttng_rate_policy *rate_policy,
-               struct lttng_payload *payload);
-typedef bool (*rate_policy_equal_cb)(const struct lttng_rate_policy *a,
-               const struct lttng_rate_policy *b);
-typedef ssize_t (*rate_policy_create_from_payload_cb)(
-               struct lttng_payload_view *view,
-               struct lttng_rate_policy **rate_policy);
-typedef struct lttng_rate_policy *(*rate_policy_copy_cb)(
-               const struct lttng_rate_policy *source);
-typedef enum lttng_error_code (*rate_policy_mi_serialize_cb)(
-               const struct lttng_rate_policy *rate_policy,
-               struct mi_writer *writer);
-
-struct lttng_rate_policy {
-       enum lttng_rate_policy_type type;
-       rate_policy_serialize_cb serialize;
-       rate_policy_equal_cb equal;
-       rate_policy_destroy_cb destroy;
-       rate_policy_copy_cb copy;
-       rate_policy_mi_serialize_cb mi_serialize;
-};
-
-struct lttng_rate_policy_every_n {
-       struct lttng_rate_policy parent;
-       uint64_t interval;
-};
-
-struct lttng_rate_policy_once_after_n {
-       struct lttng_rate_policy parent;
-       uint64_t threshold;
-};
-
-struct lttng_rate_policy_comm {
-       /* enum lttng_rate_policy_type */
-       int8_t rate_policy_type;
-} LTTNG_PACKED;
-
-struct lttng_rate_policy_once_after_n_comm {
-       uint64_t threshold;
-} LTTNG_PACKED;
-
-struct lttng_rate_policy_every_n_comm {
-       uint64_t interval;
-} LTTNG_PACKED;
-
-/* Forward declaration. */
-static void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy,
-               enum lttng_rate_policy_type type,
-               rate_policy_serialize_cb serialize,
-               rate_policy_equal_cb equal,
-               rate_policy_destroy_cb destroy,
-               rate_policy_copy_cb copy,
-               rate_policy_mi_serialize_cb mi);
-
-/* Forward declaration. Every n */
-static bool lttng_rate_policy_every_n_should_execute(
-               const struct lttng_rate_policy *policy, uint64_t counter);
-
-/* Forward declaration. Once after N */
-static bool lttng_rate_policy_once_after_n_should_execute(
-               const struct lttng_rate_policy *policy, uint64_t counter);
-
-const char *lttng_rate_policy_type_string(
-               enum lttng_rate_policy_type rate_policy_type)
-{
-       switch (rate_policy_type) {
-       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
-               return "EVERY-N";
-       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
-               return "ONCE-AFTER-N";
-       default:
-               return "???";
-       }
-}
-
-enum lttng_rate_policy_type lttng_rate_policy_get_type(
-               const struct lttng_rate_policy *policy)
-{
-       return policy ? policy->type : LTTNG_RATE_POLICY_TYPE_UNKNOWN;
-}
-
-void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy,
-               enum lttng_rate_policy_type type,
-               rate_policy_serialize_cb serialize,
-               rate_policy_equal_cb equal,
-               rate_policy_destroy_cb destroy,
-               rate_policy_copy_cb copy,
-               rate_policy_mi_serialize_cb mi)
-{
-       rate_policy->type = type;
-       rate_policy->serialize = serialize;
-       rate_policy->equal = equal;
-       rate_policy->destroy = destroy;
-       rate_policy->copy = copy;
-       rate_policy->mi_serialize = mi;
-}
-
-void lttng_rate_policy_destroy(struct lttng_rate_policy *rate_policy)
-{
-       if (!rate_policy) {
-               return;
-       }
-
-       rate_policy->destroy(rate_policy);
-}
-
-int lttng_rate_policy_serialize(struct lttng_rate_policy *rate_policy,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_rate_policy_comm rate_policy_comm = {
-                       .rate_policy_type = (int8_t) rate_policy->type,
-       };
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &rate_policy_comm,
-                       sizeof(rate_policy_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = rate_policy->serialize(rate_policy, payload);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static ssize_t lttng_rate_policy_once_after_n_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_rate_policy **rate_policy)
-{
-       ssize_t consumed_len = -1;
-       struct lttng_rate_policy *policy = NULL;
-       const struct lttng_rate_policy_once_after_n_comm *comm;
-       const struct lttng_payload_view comm_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*comm));
-
-       if (!view || !rate_policy) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&comm_view)) {
-               /* Payload not large enough to contain the header. */
-               consumed_len = -1;
-               goto end;
-       }
-
-       comm = (const struct lttng_rate_policy_once_after_n_comm *)
-                              comm_view.buffer.data;
-
-       policy = lttng_rate_policy_once_after_n_create(comm->threshold);
-       if (policy == NULL) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       *rate_policy = policy;
-       consumed_len = sizeof(*comm);
-
-end:
-       return consumed_len;
-}
-
-static ssize_t lttng_rate_policy_every_n_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_rate_policy **rate_policy)
-{
-       ssize_t consumed_len = -1;
-       struct lttng_rate_policy *policy = NULL;
-       const struct lttng_rate_policy_every_n_comm *comm;
-       const struct lttng_payload_view comm_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*comm));
-
-       if (!view || !rate_policy) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&comm_view)) {
-               /* Payload not large enough to contain the header. */
-               consumed_len = -1;
-               goto end;
-       }
-
-       comm = (const struct lttng_rate_policy_every_n_comm *)
-                              comm_view.buffer.data;
-
-       policy = lttng_rate_policy_every_n_create(comm->interval);
-       if (policy == NULL) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       *rate_policy = policy;
-       consumed_len = sizeof(*comm);
-
-end:
-       return consumed_len;
-}
-
-ssize_t lttng_rate_policy_create_from_payload(struct lttng_payload_view *view,
-               struct lttng_rate_policy **rate_policy)
-{
-       ssize_t consumed_len, specific_rate_policy_consumed_len;
-       rate_policy_create_from_payload_cb create_from_payload_cb;
-       const struct lttng_rate_policy_comm *rate_policy_comm;
-       const struct lttng_payload_view rate_policy_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*rate_policy_comm));
-
-       if (!view || !rate_policy) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&rate_policy_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               consumed_len = -1;
-               goto end;
-       }
-
-       rate_policy_comm = (const struct lttng_rate_policy_comm *)
-                                          rate_policy_comm_view.buffer.data;
-
-       DBG("Create rate_policy from payload: rate-policy-type=%s",
-                       lttng_rate_policy_type_string(
-                                       rate_policy_comm->rate_policy_type));
-
-       switch (rate_policy_comm->rate_policy_type) {
-       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
-               create_from_payload_cb =
-                               lttng_rate_policy_every_n_create_from_payload;
-               break;
-       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
-               create_from_payload_cb =
-                               lttng_rate_policy_once_after_n_create_from_payload;
-               break;
-       default:
-               ERR("Failed to create rate-policy from payload, unhandled rate-policy type: rate-policy-type=%u (%s)",
-                               rate_policy_comm->rate_policy_type,
-                               lttng_rate_policy_type_string(
-                                               rate_policy_comm->rate_policy_type));
-               consumed_len = -1;
-               goto end;
-       }
-
-       {
-               /* Create buffer view for the rate_policy-type-specific data.
-                */
-               struct lttng_payload_view specific_rate_policy_view =
-                               lttng_payload_view_from_view(view,
-                                               sizeof(struct lttng_rate_policy_comm),
-                                               -1);
-
-               specific_rate_policy_consumed_len = create_from_payload_cb(
-                               &specific_rate_policy_view, rate_policy);
-       }
-       if (specific_rate_policy_consumed_len < 0) {
-               ERR("Failed to create specific rate_policy from buffer.");
-               consumed_len = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(*rate_policy);
-
-       consumed_len = sizeof(struct lttng_rate_policy_comm) +
-                       specific_rate_policy_consumed_len;
-
-end:
-       return consumed_len;
-}
-
-bool lttng_rate_policy_is_equal(const struct lttng_rate_policy *a,
-               const struct lttng_rate_policy *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->equal);
-       is_equal = a->equal(a, b);
-end:
-       return is_equal;
-}
-
-bool lttng_rate_policy_should_execute(
-               const struct lttng_rate_policy *policy, uint64_t counter)
-{
-       switch (policy->type) {
-       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
-               return lttng_rate_policy_every_n_should_execute(
-                               policy, counter);
-       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
-               return lttng_rate_policy_once_after_n_should_execute(
-                               policy, counter);
-       default:
-               abort();
-               break;
-       }
-}
-
-/* Every N */
-static struct lttng_rate_policy_every_n *rate_policy_every_n_from_rate_policy(
-               struct lttng_rate_policy *policy)
-{
-       LTTNG_ASSERT(policy);
-
-       return container_of(policy, struct lttng_rate_policy_every_n, parent);
-}
-
-static const struct lttng_rate_policy_every_n *
-rate_policy_every_n_from_rate_policy_const(
-               const struct lttng_rate_policy *policy)
-{
-       LTTNG_ASSERT(policy);
-
-       return container_of(policy, struct lttng_rate_policy_every_n, parent);
-}
-
-static int lttng_rate_policy_every_n_serialize(
-               struct lttng_rate_policy *policy, struct lttng_payload *payload)
-{
-       int ret;
-
-       struct lttng_rate_policy_every_n *every_n_policy;
-       struct lttng_rate_policy_every_n_comm comm = {};
-
-       LTTNG_ASSERT(policy);
-       LTTNG_ASSERT(payload);
-
-       every_n_policy = rate_policy_every_n_from_rate_policy(policy);
-       comm.interval = every_n_policy->interval;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       return ret;
-}
-
-static bool lttng_rate_policy_every_n_is_equal(
-               const struct lttng_rate_policy *_a,
-               const struct lttng_rate_policy *_b)
-{
-       bool is_equal = false;
-       const struct lttng_rate_policy_every_n *a, *b;
-
-       a = rate_policy_every_n_from_rate_policy_const(_a);
-       b = rate_policy_every_n_from_rate_policy_const(_b);
-
-       if (a->interval != b->interval) {
-               goto end;
-       }
-
-       is_equal = true;
-
-end:
-       return is_equal;
-}
-
-static void lttng_rate_policy_every_n_destroy(struct lttng_rate_policy *policy)
-{
-       struct lttng_rate_policy_every_n *every_n_policy;
-
-       if (!policy) {
-               goto end;
-       }
-
-       every_n_policy = rate_policy_every_n_from_rate_policy(policy);
-
-       free(every_n_policy);
-
-end:
-       return;
-}
-
-static struct lttng_rate_policy *lttng_rate_policy_every_n_copy(
-               const struct lttng_rate_policy *source)
-{
-       struct lttng_rate_policy *copy = NULL;
-       const struct lttng_rate_policy_every_n *every_n_policy;
-
-       if (!source) {
-               goto end;
-       }
-
-       every_n_policy = rate_policy_every_n_from_rate_policy_const(source);
-       copy = lttng_rate_policy_every_n_create(every_n_policy->interval);
-
-end:
-       return copy;
-}
-
-static enum lttng_error_code lttng_rate_policy_every_n_mi_serialize(
-               const struct lttng_rate_policy *rate_policy,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const struct lttng_rate_policy_every_n *every_n_policy = NULL;
-
-       LTTNG_ASSERT(rate_policy);
-       LTTNG_ASSERT(IS_EVERY_N_RATE_POLICY(rate_policy));
-       LTTNG_ASSERT(writer);
-
-       every_n_policy = rate_policy_every_n_from_rate_policy_const(
-                       rate_policy);
-
-       /* Open rate_policy_every_n element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_rate_policy_every_n);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Interval. */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_rate_policy_every_n_interval,
-                       every_n_policy->interval);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close rate_policy_every_n element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_rate_policy *lttng_rate_policy_every_n_create(uint64_t interval)
-{
-       struct lttng_rate_policy_every_n *policy = NULL;
-       struct lttng_rate_policy *_policy = NULL;
-
-       if (interval == 0) {
-               /*
-                * An interval of 0 is invalid since it would never be fired.
-                */
-               goto end;
-       }
-
-       policy = zmalloc(sizeof(struct lttng_rate_policy_every_n));
-       if (!policy) {
-               goto end;
-       }
-
-       lttng_rate_policy_init(&policy->parent, LTTNG_RATE_POLICY_TYPE_EVERY_N,
-                       lttng_rate_policy_every_n_serialize,
-                       lttng_rate_policy_every_n_is_equal,
-                       lttng_rate_policy_every_n_destroy,
-                       lttng_rate_policy_every_n_copy,
-                       lttng_rate_policy_every_n_mi_serialize);
-
-       policy->interval = interval;
-
-       _policy = &policy->parent;
-       policy = NULL;
-
-end:
-       free(policy);
-       return _policy;
-}
-
-enum lttng_rate_policy_status lttng_rate_policy_every_n_get_interval(
-               const struct lttng_rate_policy *policy, uint64_t *interval)
-{
-       const struct lttng_rate_policy_every_n *every_n_policy;
-       enum lttng_rate_policy_status status;
-
-       if (!policy || !IS_EVERY_N_RATE_POLICY(policy) || !interval) {
-               status = LTTNG_RATE_POLICY_STATUS_INVALID;
-               goto end;
-       }
-
-       every_n_policy = rate_policy_every_n_from_rate_policy_const(policy);
-       *interval = every_n_policy->interval;
-       status = LTTNG_RATE_POLICY_STATUS_OK;
-end:
-
-       return status;
-}
-
-static bool lttng_rate_policy_every_n_should_execute(
-               const struct lttng_rate_policy *policy, uint64_t counter)
-{
-       const struct lttng_rate_policy_every_n *every_n_policy;
-       LTTNG_ASSERT(policy);
-       bool execute = false;
-
-       every_n_policy = rate_policy_every_n_from_rate_policy_const(policy);
-
-       if (every_n_policy->interval == 0) {
-               abort();
-       }
-
-       execute = (counter % every_n_policy->interval) == 0;
-
-       DBG("Policy every N = %" PRIu64
-           ": execution %s. Execution count: %" PRIu64,
-                       every_n_policy->interval,
-                       execute ? "accepted" : "denied", counter);
-
-       return execute;
-}
-
-/* Once after N */
-
-static struct lttng_rate_policy_once_after_n *
-rate_policy_once_after_n_from_rate_policy(struct lttng_rate_policy *policy)
-{
-       LTTNG_ASSERT(policy);
-
-       return container_of(
-                       policy, struct lttng_rate_policy_once_after_n, parent);
-}
-
-static const struct lttng_rate_policy_once_after_n *
-rate_policy_once_after_n_from_rate_policy_const(
-               const struct lttng_rate_policy *policy)
-{
-       LTTNG_ASSERT(policy);
-
-       return container_of(
-                       policy, struct lttng_rate_policy_once_after_n, parent);
-}
-static int lttng_rate_policy_once_after_n_serialize(
-               struct lttng_rate_policy *policy, struct lttng_payload *payload)
-{
-       int ret;
-
-       struct lttng_rate_policy_once_after_n *once_after_n_policy;
-       struct lttng_rate_policy_once_after_n_comm comm = {};
-
-       LTTNG_ASSERT(policy);
-       LTTNG_ASSERT(payload);
-
-       once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy);
-       comm.threshold = once_after_n_policy->threshold;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       return ret;
-}
-
-static bool lttng_rate_policy_once_after_n_is_equal(
-               const struct lttng_rate_policy *_a,
-               const struct lttng_rate_policy *_b)
-{
-       bool is_equal = false;
-       const struct lttng_rate_policy_once_after_n *a, *b;
-
-       a = rate_policy_once_after_n_from_rate_policy_const(_a);
-       b = rate_policy_once_after_n_from_rate_policy_const(_b);
-
-       if (a->threshold != b->threshold) {
-               goto end;
-       }
-
-       is_equal = true;
-
-end:
-       return is_equal;
-}
-
-static void lttng_rate_policy_once_after_n_destroy(
-               struct lttng_rate_policy *policy)
-{
-       struct lttng_rate_policy_once_after_n *once_after_n_policy;
-
-       if (!policy) {
-               goto end;
-       }
-
-       once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy);
-
-       free(once_after_n_policy);
-
-end:
-       return;
-}
-
-static struct lttng_rate_policy *lttng_rate_policy_once_after_n_copy(
-               const struct lttng_rate_policy *source)
-{
-       struct lttng_rate_policy *copy = NULL;
-       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
-
-       if (!source) {
-               goto end;
-       }
-
-       once_after_n_policy =
-                       rate_policy_once_after_n_from_rate_policy_const(source);
-       copy = lttng_rate_policy_once_after_n_create(
-                       once_after_n_policy->threshold);
-
-end:
-       return copy;
-}
-
-static enum lttng_error_code lttng_rate_policy_once_after_n_mi_serialize(
-               const struct lttng_rate_policy *rate_policy,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const struct lttng_rate_policy_once_after_n *once_after_n_policy = NULL;
-
-       LTTNG_ASSERT(rate_policy);
-       LTTNG_ASSERT(IS_ONCE_AFTER_N_RATE_POLICY(rate_policy));
-       LTTNG_ASSERT(writer);
-
-       once_after_n_policy = rate_policy_once_after_n_from_rate_policy_const(
-                       rate_policy);
-
-       /* Open rate_policy_once_after_n. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_rate_policy_once_after_n);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Threshold. */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_rate_policy_once_after_n_threshold,
-                       once_after_n_policy->threshold);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close rate_policy_once_after_n element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_rate_policy *lttng_rate_policy_once_after_n_create(
-               uint64_t threshold)
-{
-       struct lttng_rate_policy_once_after_n *policy = NULL;
-       struct lttng_rate_policy *_policy = NULL;
-
-       if (threshold == 0) {
-               /* threshold is expected to be > 0 */
-               goto end;
-       }
-
-       policy = zmalloc(sizeof(struct lttng_rate_policy_once_after_n));
-       if (!policy) {
-               goto end;
-       }
-
-       lttng_rate_policy_init(&policy->parent,
-                       LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N,
-                       lttng_rate_policy_once_after_n_serialize,
-                       lttng_rate_policy_once_after_n_is_equal,
-                       lttng_rate_policy_once_after_n_destroy,
-                       lttng_rate_policy_once_after_n_copy,
-                       lttng_rate_policy_once_after_n_mi_serialize);
-
-       policy->threshold = threshold;
-
-       _policy = &policy->parent;
-       policy = NULL;
-
-end:
-       free(policy);
-       return _policy;
-}
-
-enum lttng_rate_policy_status lttng_rate_policy_once_after_n_get_threshold(
-               const struct lttng_rate_policy *policy, uint64_t *threshold)
-{
-       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
-       enum lttng_rate_policy_status status;
-
-       if (!policy || !IS_ONCE_AFTER_N_RATE_POLICY(policy) || !threshold) {
-               status = LTTNG_RATE_POLICY_STATUS_INVALID;
-               goto end;
-       }
-
-       once_after_n_policy =
-                       rate_policy_once_after_n_from_rate_policy_const(policy);
-       *threshold = once_after_n_policy->threshold;
-       status = LTTNG_RATE_POLICY_STATUS_OK;
-end:
-
-       return status;
-}
-
-struct lttng_rate_policy *lttng_rate_policy_copy(
-               const struct lttng_rate_policy *source)
-{
-       LTTNG_ASSERT(source->copy);
-       return source->copy(source);
-}
-
-static bool lttng_rate_policy_once_after_n_should_execute(
-               const struct lttng_rate_policy *policy, uint64_t counter)
-{
-       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
-       bool execute = false;
-       LTTNG_ASSERT(policy);
-
-       once_after_n_policy =
-                       rate_policy_once_after_n_from_rate_policy_const(policy);
-
-       execute = counter == once_after_n_policy->threshold;
-
-       DBG("Policy once after N = %" PRIu64
-           ": execution %s. Execution count: %" PRIu64,
-                       once_after_n_policy->threshold,
-                       execute ? "accepted" : "denied", counter);
-
-       return counter == once_after_n_policy->threshold;
-}
-
-enum lttng_error_code lttng_rate_policy_mi_serialize(
-               const struct lttng_rate_policy *rate_policy,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(rate_policy);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(rate_policy->mi_serialize);
-
-       /* Open rate policy element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_rate_policy);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize underlying rate policy. */
-       ret_code = rate_policy->mi_serialize(rate_policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close rate policy element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/actions/rate-policy.cpp b/src/common/actions/rate-policy.cpp
new file mode 100644 (file)
index 0000000..d0834dd
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * Copyright (C) 2021 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <limits.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rate-policy.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define IS_EVERY_N_RATE_POLICY(policy) \
+       (lttng_rate_policy_get_type(policy) == LTTNG_RATE_POLICY_TYPE_EVERY_N)
+
+#define IS_ONCE_AFTER_N_RATE_POLICY(policy)    \
+       (lttng_rate_policy_get_type(policy) == \
+                       LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N)
+
+typedef void (*rate_policy_destroy_cb)(struct lttng_rate_policy *rate_policy);
+typedef int (*rate_policy_serialize_cb)(struct lttng_rate_policy *rate_policy,
+               struct lttng_payload *payload);
+typedef bool (*rate_policy_equal_cb)(const struct lttng_rate_policy *a,
+               const struct lttng_rate_policy *b);
+typedef ssize_t (*rate_policy_create_from_payload_cb)(
+               struct lttng_payload_view *view,
+               struct lttng_rate_policy **rate_policy);
+typedef struct lttng_rate_policy *(*rate_policy_copy_cb)(
+               const struct lttng_rate_policy *source);
+typedef enum lttng_error_code (*rate_policy_mi_serialize_cb)(
+               const struct lttng_rate_policy *rate_policy,
+               struct mi_writer *writer);
+
+struct lttng_rate_policy {
+       enum lttng_rate_policy_type type;
+       rate_policy_serialize_cb serialize;
+       rate_policy_equal_cb equal;
+       rate_policy_destroy_cb destroy;
+       rate_policy_copy_cb copy;
+       rate_policy_mi_serialize_cb mi_serialize;
+};
+
+struct lttng_rate_policy_every_n {
+       struct lttng_rate_policy parent;
+       uint64_t interval;
+};
+
+struct lttng_rate_policy_once_after_n {
+       struct lttng_rate_policy parent;
+       uint64_t threshold;
+};
+
+struct lttng_rate_policy_comm {
+       /* enum lttng_rate_policy_type */
+       int8_t rate_policy_type;
+} LTTNG_PACKED;
+
+struct lttng_rate_policy_once_after_n_comm {
+       uint64_t threshold;
+} LTTNG_PACKED;
+
+struct lttng_rate_policy_every_n_comm {
+       uint64_t interval;
+} LTTNG_PACKED;
+
+/* Forward declaration. */
+static void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy,
+               enum lttng_rate_policy_type type,
+               rate_policy_serialize_cb serialize,
+               rate_policy_equal_cb equal,
+               rate_policy_destroy_cb destroy,
+               rate_policy_copy_cb copy,
+               rate_policy_mi_serialize_cb mi);
+
+/* Forward declaration. Every n */
+static bool lttng_rate_policy_every_n_should_execute(
+               const struct lttng_rate_policy *policy, uint64_t counter);
+
+/* Forward declaration. Once after N */
+static bool lttng_rate_policy_once_after_n_should_execute(
+               const struct lttng_rate_policy *policy, uint64_t counter);
+
+const char *lttng_rate_policy_type_string(
+               enum lttng_rate_policy_type rate_policy_type)
+{
+       switch (rate_policy_type) {
+       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
+               return "EVERY-N";
+       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
+               return "ONCE-AFTER-N";
+       default:
+               return "???";
+       }
+}
+
+enum lttng_rate_policy_type lttng_rate_policy_get_type(
+               const struct lttng_rate_policy *policy)
+{
+       return policy ? policy->type : LTTNG_RATE_POLICY_TYPE_UNKNOWN;
+}
+
+void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy,
+               enum lttng_rate_policy_type type,
+               rate_policy_serialize_cb serialize,
+               rate_policy_equal_cb equal,
+               rate_policy_destroy_cb destroy,
+               rate_policy_copy_cb copy,
+               rate_policy_mi_serialize_cb mi)
+{
+       rate_policy->type = type;
+       rate_policy->serialize = serialize;
+       rate_policy->equal = equal;
+       rate_policy->destroy = destroy;
+       rate_policy->copy = copy;
+       rate_policy->mi_serialize = mi;
+}
+
+void lttng_rate_policy_destroy(struct lttng_rate_policy *rate_policy)
+{
+       if (!rate_policy) {
+               return;
+       }
+
+       rate_policy->destroy(rate_policy);
+}
+
+int lttng_rate_policy_serialize(struct lttng_rate_policy *rate_policy,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_rate_policy_comm rate_policy_comm = {
+                       .rate_policy_type = (int8_t) rate_policy->type,
+       };
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &rate_policy_comm,
+                       sizeof(rate_policy_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = rate_policy->serialize(rate_policy, payload);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static ssize_t lttng_rate_policy_once_after_n_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_rate_policy **rate_policy)
+{
+       ssize_t consumed_len = -1;
+       struct lttng_rate_policy *policy = NULL;
+       const struct lttng_rate_policy_once_after_n_comm *comm;
+       const struct lttng_payload_view comm_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*comm));
+
+       if (!view || !rate_policy) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&comm_view)) {
+               /* Payload not large enough to contain the header. */
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (const struct lttng_rate_policy_once_after_n_comm *)
+                              comm_view.buffer.data;
+
+       policy = lttng_rate_policy_once_after_n_create(comm->threshold);
+       if (policy == NULL) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       *rate_policy = policy;
+       consumed_len = sizeof(*comm);
+
+end:
+       return consumed_len;
+}
+
+static ssize_t lttng_rate_policy_every_n_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_rate_policy **rate_policy)
+{
+       ssize_t consumed_len = -1;
+       struct lttng_rate_policy *policy = NULL;
+       const struct lttng_rate_policy_every_n_comm *comm;
+       const struct lttng_payload_view comm_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*comm));
+
+       if (!view || !rate_policy) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&comm_view)) {
+               /* Payload not large enough to contain the header. */
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (const struct lttng_rate_policy_every_n_comm *)
+                              comm_view.buffer.data;
+
+       policy = lttng_rate_policy_every_n_create(comm->interval);
+       if (policy == NULL) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       *rate_policy = policy;
+       consumed_len = sizeof(*comm);
+
+end:
+       return consumed_len;
+}
+
+ssize_t lttng_rate_policy_create_from_payload(struct lttng_payload_view *view,
+               struct lttng_rate_policy **rate_policy)
+{
+       ssize_t consumed_len, specific_rate_policy_consumed_len;
+       rate_policy_create_from_payload_cb create_from_payload_cb;
+       const struct lttng_rate_policy_comm *rate_policy_comm;
+       const struct lttng_payload_view rate_policy_comm_view =
+               lttng_payload_view_from_view(
+                       view, 0, sizeof(*rate_policy_comm));
+
+       if (!view || !rate_policy) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&rate_policy_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               consumed_len = -1;
+               goto end;
+       }
+
+       rate_policy_comm = (const struct lttng_rate_policy_comm *)
+                                          rate_policy_comm_view.buffer.data;
+
+       DBG("Create rate_policy from payload: rate-policy-type=%s",
+               lttng_rate_policy_type_string(
+                       (lttng_rate_policy_type) rate_policy_comm->rate_policy_type));
+
+       switch (rate_policy_comm->rate_policy_type) {
+       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
+               create_from_payload_cb =
+                               lttng_rate_policy_every_n_create_from_payload;
+               break;
+       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
+               create_from_payload_cb =
+                               lttng_rate_policy_once_after_n_create_from_payload;
+               break;
+       default:
+               ERR("Failed to create rate-policy from payload, unhandled rate-policy type: rate-policy-type=%u (%s)",
+                               rate_policy_comm->rate_policy_type,
+                               lttng_rate_policy_type_string(
+                                       (lttng_rate_policy_type) rate_policy_comm->rate_policy_type));
+               consumed_len = -1;
+               goto end;
+       }
+
+       {
+               /* Create buffer view for the rate_policy-type-specific data.
+                */
+               struct lttng_payload_view specific_rate_policy_view =
+                               lttng_payload_view_from_view(view,
+                                               sizeof(struct lttng_rate_policy_comm),
+                                               -1);
+
+               specific_rate_policy_consumed_len = create_from_payload_cb(
+                               &specific_rate_policy_view, rate_policy);
+       }
+       if (specific_rate_policy_consumed_len < 0) {
+               ERR("Failed to create specific rate_policy from buffer.");
+               consumed_len = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(*rate_policy);
+
+       consumed_len = sizeof(struct lttng_rate_policy_comm) +
+                       specific_rate_policy_consumed_len;
+
+end:
+       return consumed_len;
+}
+
+bool lttng_rate_policy_is_equal(const struct lttng_rate_policy *a,
+               const struct lttng_rate_policy *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->equal);
+       is_equal = a->equal(a, b);
+end:
+       return is_equal;
+}
+
+bool lttng_rate_policy_should_execute(
+               const struct lttng_rate_policy *policy, uint64_t counter)
+{
+       switch (policy->type) {
+       case LTTNG_RATE_POLICY_TYPE_EVERY_N:
+               return lttng_rate_policy_every_n_should_execute(
+                               policy, counter);
+       case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N:
+               return lttng_rate_policy_once_after_n_should_execute(
+                               policy, counter);
+       default:
+               abort();
+               break;
+       }
+}
+
+/* Every N */
+static struct lttng_rate_policy_every_n *rate_policy_every_n_from_rate_policy(
+               struct lttng_rate_policy *policy)
+{
+       LTTNG_ASSERT(policy);
+
+       return container_of(policy, struct lttng_rate_policy_every_n, parent);
+}
+
+static const struct lttng_rate_policy_every_n *
+rate_policy_every_n_from_rate_policy_const(
+               const struct lttng_rate_policy *policy)
+{
+       LTTNG_ASSERT(policy);
+
+       return container_of(policy, struct lttng_rate_policy_every_n, parent);
+}
+
+static int lttng_rate_policy_every_n_serialize(
+               struct lttng_rate_policy *policy, struct lttng_payload *payload)
+{
+       int ret;
+
+       struct lttng_rate_policy_every_n *every_n_policy;
+       struct lttng_rate_policy_every_n_comm comm = {};
+
+       LTTNG_ASSERT(policy);
+       LTTNG_ASSERT(payload);
+
+       every_n_policy = rate_policy_every_n_from_rate_policy(policy);
+       comm.interval = every_n_policy->interval;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       return ret;
+}
+
+static bool lttng_rate_policy_every_n_is_equal(
+               const struct lttng_rate_policy *_a,
+               const struct lttng_rate_policy *_b)
+{
+       bool is_equal = false;
+       const struct lttng_rate_policy_every_n *a, *b;
+
+       a = rate_policy_every_n_from_rate_policy_const(_a);
+       b = rate_policy_every_n_from_rate_policy_const(_b);
+
+       if (a->interval != b->interval) {
+               goto end;
+       }
+
+       is_equal = true;
+
+end:
+       return is_equal;
+}
+
+static void lttng_rate_policy_every_n_destroy(struct lttng_rate_policy *policy)
+{
+       struct lttng_rate_policy_every_n *every_n_policy;
+
+       if (!policy) {
+               goto end;
+       }
+
+       every_n_policy = rate_policy_every_n_from_rate_policy(policy);
+
+       free(every_n_policy);
+
+end:
+       return;
+}
+
+static struct lttng_rate_policy *lttng_rate_policy_every_n_copy(
+               const struct lttng_rate_policy *source)
+{
+       struct lttng_rate_policy *copy = NULL;
+       const struct lttng_rate_policy_every_n *every_n_policy;
+
+       if (!source) {
+               goto end;
+       }
+
+       every_n_policy = rate_policy_every_n_from_rate_policy_const(source);
+       copy = lttng_rate_policy_every_n_create(every_n_policy->interval);
+
+end:
+       return copy;
+}
+
+static enum lttng_error_code lttng_rate_policy_every_n_mi_serialize(
+               const struct lttng_rate_policy *rate_policy,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const struct lttng_rate_policy_every_n *every_n_policy = NULL;
+
+       LTTNG_ASSERT(rate_policy);
+       LTTNG_ASSERT(IS_EVERY_N_RATE_POLICY(rate_policy));
+       LTTNG_ASSERT(writer);
+
+       every_n_policy = rate_policy_every_n_from_rate_policy_const(
+                       rate_policy);
+
+       /* Open rate_policy_every_n element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_rate_policy_every_n);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Interval. */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_rate_policy_every_n_interval,
+                       every_n_policy->interval);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close rate_policy_every_n element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_rate_policy *lttng_rate_policy_every_n_create(uint64_t interval)
+{
+       struct lttng_rate_policy_every_n *policy = NULL;
+       struct lttng_rate_policy *_policy = NULL;
+
+       if (interval == 0) {
+               /*
+                * An interval of 0 is invalid since it would never be fired.
+                */
+               goto end;
+       }
+
+       policy = (lttng_rate_policy_every_n *) zmalloc(sizeof(struct lttng_rate_policy_every_n));
+       if (!policy) {
+               goto end;
+       }
+
+       lttng_rate_policy_init(&policy->parent, LTTNG_RATE_POLICY_TYPE_EVERY_N,
+                       lttng_rate_policy_every_n_serialize,
+                       lttng_rate_policy_every_n_is_equal,
+                       lttng_rate_policy_every_n_destroy,
+                       lttng_rate_policy_every_n_copy,
+                       lttng_rate_policy_every_n_mi_serialize);
+
+       policy->interval = interval;
+
+       _policy = &policy->parent;
+       policy = NULL;
+
+end:
+       free(policy);
+       return _policy;
+}
+
+enum lttng_rate_policy_status lttng_rate_policy_every_n_get_interval(
+               const struct lttng_rate_policy *policy, uint64_t *interval)
+{
+       const struct lttng_rate_policy_every_n *every_n_policy;
+       enum lttng_rate_policy_status status;
+
+       if (!policy || !IS_EVERY_N_RATE_POLICY(policy) || !interval) {
+               status = LTTNG_RATE_POLICY_STATUS_INVALID;
+               goto end;
+       }
+
+       every_n_policy = rate_policy_every_n_from_rate_policy_const(policy);
+       *interval = every_n_policy->interval;
+       status = LTTNG_RATE_POLICY_STATUS_OK;
+end:
+
+       return status;
+}
+
+static bool lttng_rate_policy_every_n_should_execute(
+               const struct lttng_rate_policy *policy, uint64_t counter)
+{
+       const struct lttng_rate_policy_every_n *every_n_policy;
+       LTTNG_ASSERT(policy);
+       bool execute = false;
+
+       every_n_policy = rate_policy_every_n_from_rate_policy_const(policy);
+
+       if (every_n_policy->interval == 0) {
+               abort();
+       }
+
+       execute = (counter % every_n_policy->interval) == 0;
+
+       DBG("Policy every N = %" PRIu64
+           ": execution %s. Execution count: %" PRIu64,
+                       every_n_policy->interval,
+                       execute ? "accepted" : "denied", counter);
+
+       return execute;
+}
+
+/* Once after N */
+
+static struct lttng_rate_policy_once_after_n *
+rate_policy_once_after_n_from_rate_policy(struct lttng_rate_policy *policy)
+{
+       LTTNG_ASSERT(policy);
+
+       return container_of(
+                       policy, struct lttng_rate_policy_once_after_n, parent);
+}
+
+static const struct lttng_rate_policy_once_after_n *
+rate_policy_once_after_n_from_rate_policy_const(
+               const struct lttng_rate_policy *policy)
+{
+       LTTNG_ASSERT(policy);
+
+       return container_of(
+                       policy, struct lttng_rate_policy_once_after_n, parent);
+}
+static int lttng_rate_policy_once_after_n_serialize(
+               struct lttng_rate_policy *policy, struct lttng_payload *payload)
+{
+       int ret;
+
+       struct lttng_rate_policy_once_after_n *once_after_n_policy;
+       struct lttng_rate_policy_once_after_n_comm comm = {};
+
+       LTTNG_ASSERT(policy);
+       LTTNG_ASSERT(payload);
+
+       once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy);
+       comm.threshold = once_after_n_policy->threshold;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       return ret;
+}
+
+static bool lttng_rate_policy_once_after_n_is_equal(
+               const struct lttng_rate_policy *_a,
+               const struct lttng_rate_policy *_b)
+{
+       bool is_equal = false;
+       const struct lttng_rate_policy_once_after_n *a, *b;
+
+       a = rate_policy_once_after_n_from_rate_policy_const(_a);
+       b = rate_policy_once_after_n_from_rate_policy_const(_b);
+
+       if (a->threshold != b->threshold) {
+               goto end;
+       }
+
+       is_equal = true;
+
+end:
+       return is_equal;
+}
+
+static void lttng_rate_policy_once_after_n_destroy(
+               struct lttng_rate_policy *policy)
+{
+       struct lttng_rate_policy_once_after_n *once_after_n_policy;
+
+       if (!policy) {
+               goto end;
+       }
+
+       once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy);
+
+       free(once_after_n_policy);
+
+end:
+       return;
+}
+
+static struct lttng_rate_policy *lttng_rate_policy_once_after_n_copy(
+               const struct lttng_rate_policy *source)
+{
+       struct lttng_rate_policy *copy = NULL;
+       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
+
+       if (!source) {
+               goto end;
+       }
+
+       once_after_n_policy =
+                       rate_policy_once_after_n_from_rate_policy_const(source);
+       copy = lttng_rate_policy_once_after_n_create(
+                       once_after_n_policy->threshold);
+
+end:
+       return copy;
+}
+
+static enum lttng_error_code lttng_rate_policy_once_after_n_mi_serialize(
+               const struct lttng_rate_policy *rate_policy,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const struct lttng_rate_policy_once_after_n *once_after_n_policy = NULL;
+
+       LTTNG_ASSERT(rate_policy);
+       LTTNG_ASSERT(IS_ONCE_AFTER_N_RATE_POLICY(rate_policy));
+       LTTNG_ASSERT(writer);
+
+       once_after_n_policy = rate_policy_once_after_n_from_rate_policy_const(
+                       rate_policy);
+
+       /* Open rate_policy_once_after_n. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_rate_policy_once_after_n);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Threshold. */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_rate_policy_once_after_n_threshold,
+                       once_after_n_policy->threshold);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close rate_policy_once_after_n element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_rate_policy *lttng_rate_policy_once_after_n_create(
+               uint64_t threshold)
+{
+       struct lttng_rate_policy_once_after_n *policy = NULL;
+       struct lttng_rate_policy *_policy = NULL;
+
+       if (threshold == 0) {
+               /* threshold is expected to be > 0 */
+               goto end;
+       }
+
+       policy = (lttng_rate_policy_once_after_n *) zmalloc(sizeof(struct lttng_rate_policy_once_after_n));
+       if (!policy) {
+               goto end;
+       }
+
+       lttng_rate_policy_init(&policy->parent,
+                       LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N,
+                       lttng_rate_policy_once_after_n_serialize,
+                       lttng_rate_policy_once_after_n_is_equal,
+                       lttng_rate_policy_once_after_n_destroy,
+                       lttng_rate_policy_once_after_n_copy,
+                       lttng_rate_policy_once_after_n_mi_serialize);
+
+       policy->threshold = threshold;
+
+       _policy = &policy->parent;
+       policy = NULL;
+
+end:
+       free(policy);
+       return _policy;
+}
+
+enum lttng_rate_policy_status lttng_rate_policy_once_after_n_get_threshold(
+               const struct lttng_rate_policy *policy, uint64_t *threshold)
+{
+       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
+       enum lttng_rate_policy_status status;
+
+       if (!policy || !IS_ONCE_AFTER_N_RATE_POLICY(policy) || !threshold) {
+               status = LTTNG_RATE_POLICY_STATUS_INVALID;
+               goto end;
+       }
+
+       once_after_n_policy =
+                       rate_policy_once_after_n_from_rate_policy_const(policy);
+       *threshold = once_after_n_policy->threshold;
+       status = LTTNG_RATE_POLICY_STATUS_OK;
+end:
+
+       return status;
+}
+
+struct lttng_rate_policy *lttng_rate_policy_copy(
+               const struct lttng_rate_policy *source)
+{
+       LTTNG_ASSERT(source->copy);
+       return source->copy(source);
+}
+
+static bool lttng_rate_policy_once_after_n_should_execute(
+               const struct lttng_rate_policy *policy, uint64_t counter)
+{
+       const struct lttng_rate_policy_once_after_n *once_after_n_policy;
+       bool execute = false;
+       LTTNG_ASSERT(policy);
+
+       once_after_n_policy =
+                       rate_policy_once_after_n_from_rate_policy_const(policy);
+
+       execute = counter == once_after_n_policy->threshold;
+
+       DBG("Policy once after N = %" PRIu64
+           ": execution %s. Execution count: %" PRIu64,
+                       once_after_n_policy->threshold,
+                       execute ? "accepted" : "denied", counter);
+
+       return counter == once_after_n_policy->threshold;
+}
+
+enum lttng_error_code lttng_rate_policy_mi_serialize(
+               const struct lttng_rate_policy *rate_policy,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(rate_policy);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(rate_policy->mi_serialize);
+
+       /* Open rate policy element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_rate_policy);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize underlying rate policy. */
+       ret_code = rate_policy->mi_serialize(rate_policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close rate policy element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/actions/rotate-session.c b/src/common/actions/rotate-session.c
deleted file mode 100644 (file)
index e519d45..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rate-policy.h>
-#include <lttng/action/rotate-session-internal.h>
-#include <lttng/action/rotate-session.h>
-
-#define IS_ROTATE_SESSION_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION)
-
-struct lttng_action_rotate_session {
-       struct lttng_action parent;
-
-       /* Owned by this. */
-       char *session_name;
-       struct lttng_rate_policy *policy;
-};
-
-struct lttng_action_rotate_session_comm {
-       /* Includes the trailing \0. */
-       uint32_t session_name_len;
-
-       /*
-        * Variable data:
-        *
-        *  - session name (null terminated)
-        *  - policy
-        */
-       char data[];
-} LTTNG_PACKED;
-
-static const struct lttng_rate_policy *
-lttng_action_rotate_session_internal_get_rate_policy(
-               const struct lttng_action *action);
-
-static struct lttng_action_rotate_session *action_rotate_session_from_action(
-               struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_rotate_session, parent);
-}
-
-static const struct lttng_action_rotate_session *
-action_rotate_session_from_action_const(const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_rotate_session, parent);
-}
-
-static bool lttng_action_rotate_session_validate(struct lttng_action *action)
-{
-       bool valid;
-       struct lttng_action_rotate_session *action_rotate_session;
-
-       if (!action) {
-               valid = false;
-               goto end;
-       }
-
-       action_rotate_session = action_rotate_session_from_action(action);
-
-       /* A non-empty session name is mandatory. */
-       if (!action_rotate_session->session_name ||
-                       strlen(action_rotate_session->session_name) == 0) {
-               valid = false;
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static bool lttng_action_rotate_session_is_equal(
-               const struct lttng_action *_a, const struct lttng_action *_b)
-{
-       bool is_equal = false;
-       const struct lttng_action_rotate_session *a, *b;
-
-       a = action_rotate_session_from_action_const(_a);
-       b = action_rotate_session_from_action_const(_b);
-
-       /* Action is not valid if this is not true. */
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
-end:
-       return is_equal;
-}
-static int lttng_action_rotate_session_serialize(
-               struct lttng_action *action, struct lttng_payload *payload)
-{
-       struct lttng_action_rotate_session *action_rotate_session;
-       struct lttng_action_rotate_session_comm comm;
-       size_t session_name_len;
-       int ret;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(payload);
-
-       action_rotate_session = action_rotate_session_from_action(action);
-
-       LTTNG_ASSERT(action_rotate_session->session_name);
-
-       DBG("Serializing rotate session action: session-name: %s",
-                       action_rotate_session->session_name);
-
-       session_name_len = strlen(action_rotate_session->session_name) + 1;
-       comm.session_name_len = session_name_len;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       action_rotate_session->session_name, session_name_len);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_rate_policy_serialize(
-                       action_rotate_session->policy, payload);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static void lttng_action_rotate_session_destroy(struct lttng_action *action)
-{
-       struct lttng_action_rotate_session *action_rotate_session;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_rotate_session = action_rotate_session_from_action(action);
-
-       lttng_rate_policy_destroy(action_rotate_session->policy);
-       free(action_rotate_session->session_name);
-       free(action_rotate_session);
-
-end:
-       return;
-}
-
-ssize_t lttng_action_rotate_session_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **p_action)
-{
-       ssize_t consumed_len, ret;
-       const struct lttng_action_rotate_session_comm *comm;
-       const char *session_name;
-       struct lttng_action *action;
-       enum lttng_action_status status;
-       struct lttng_rate_policy *policy = NULL;
-
-       action = lttng_action_rotate_session_create();
-       if (!action) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       comm = (typeof(comm)) view->buffer.data;
-       session_name = (const char *) &comm->data;
-
-       if (!lttng_buffer_view_contains_string(
-                       &view->buffer, session_name, comm->session_name_len)) {
-               consumed_len = -1;
-               goto end;
-       }
-       consumed_len = sizeof(*comm) + comm->session_name_len;
-
-       /* Rate policy. */
-       {
-               struct lttng_payload_view policy_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed_len, -1);
-               ret = lttng_rate_policy_create_from_payload(
-                               &policy_view, &policy);
-               if (ret < 0) {
-                       consumed_len = -1;
-                       goto end;
-               }
-               consumed_len += ret;
-       }
-
-       status = lttng_action_rotate_session_set_session_name(
-                       action, session_name);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(policy);
-       status = lttng_action_rotate_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       *p_action = action;
-       action = NULL;
-
-end:
-       lttng_rate_policy_destroy(policy);
-       lttng_action_rotate_session_destroy(action);
-
-       return consumed_len;
-}
-
-static enum lttng_error_code lttng_action_rotate_session_mi_serialize(
-               const struct lttng_action *action, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_action_status status;
-       const char *session_name = NULL;
-       const struct lttng_rate_policy *policy = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_ROTATE_SESSION_ACTION(action));
-
-       status = lttng_action_rotate_session_get_session_name(
-                       action, &session_name);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(session_name != NULL);
-
-       status = lttng_action_notify_get_rate_policy(action, &policy);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(policy != NULL);
-
-       /* Open action rotate session element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_rotate_session);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Rate policy. */
-       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close action rotate session element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_rotate_session_create(void)
-{
-       struct lttng_action *action = NULL;
-       struct lttng_rate_policy *policy = NULL;
-       enum lttng_action_status status;
-
-       /* Create a every N = 1 rate policy. */
-       policy = lttng_rate_policy_every_n_create(1);
-       if (!policy) {
-               goto end;
-       }
-
-       action = zmalloc(sizeof(struct lttng_action_rotate_session));
-       if (!action) {
-               goto end;
-       }
-
-       lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION,
-                       lttng_action_rotate_session_validate,
-                       lttng_action_rotate_session_serialize,
-                       lttng_action_rotate_session_is_equal,
-                       lttng_action_rotate_session_destroy,
-                       lttng_action_rotate_session_internal_get_rate_policy,
-                       lttng_action_generic_add_error_query_results,
-                       lttng_action_rotate_session_mi_serialize);
-
-       status = lttng_action_rotate_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               free(action);
-               action = NULL;
-               goto end;
-       }
-
-end:
-       lttng_rate_policy_destroy(policy);
-       return action;
-}
-
-enum lttng_action_status lttng_action_rotate_session_set_session_name(
-               struct lttng_action *action, const char *session_name)
-{
-       struct lttng_action_rotate_session *action_rotate_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_rotate_session = action_rotate_session_from_action(action);
-
-       free(action_rotate_session->session_name);
-
-       action_rotate_session->session_name = strdup(session_name);
-       if (!action_rotate_session->session_name) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_rotate_session_get_session_name(
-               const struct lttng_action *action, const char **session_name)
-{
-       const struct lttng_action_rotate_session *action_rotate_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_rotate_session = action_rotate_session_from_action_const(action);
-
-       *session_name = action_rotate_session->session_name;
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_rotate_session_set_rate_policy(
-               struct lttng_action *action,
-               const struct lttng_rate_policy *policy)
-{
-       enum lttng_action_status status;
-       struct lttng_action_rotate_session *rotate_session_action;
-       struct lttng_rate_policy *copy = NULL;
-
-       if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_rate_policy_copy(policy);
-       if (!copy) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       rotate_session_action = action_rotate_session_from_action(action);
-
-       /* Free the previous rate policy .*/
-       lttng_rate_policy_destroy(rotate_session_action->policy);
-
-       /* Assign the policy. */
-       rotate_session_action->policy = copy;
-       status = LTTNG_ACTION_STATUS_OK;
-       copy = NULL;
-
-end:
-       lttng_rate_policy_destroy(copy);
-       return status;
-}
-
-enum lttng_action_status lttng_action_rotate_session_get_rate_policy(
-               const struct lttng_action *action,
-               const struct lttng_rate_policy **policy)
-{
-       enum lttng_action_status status;
-       const struct lttng_action_rotate_session *rotate_session_action;
-
-       if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotate_session_action = action_rotate_session_from_action_const(action);
-
-       *policy = rotate_session_action->policy;
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-static const struct lttng_rate_policy *
-lttng_action_rotate_session_internal_get_rate_policy(
-               const struct lttng_action *action)
-{
-       const struct lttng_action_rotate_session *_action;
-       _action = action_rotate_session_from_action_const(action);
-
-       return _action->policy;
-}
diff --git a/src/common/actions/rotate-session.cpp b/src/common/actions/rotate-session.cpp
new file mode 100644 (file)
index 0000000..985036a
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rate-policy.h>
+#include <lttng/action/rotate-session-internal.h>
+#include <lttng/action/rotate-session.h>
+
+#define IS_ROTATE_SESSION_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION)
+
+struct lttng_action_rotate_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+       struct lttng_rate_policy *policy;
+};
+
+struct lttng_action_rotate_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        *  - policy
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static const struct lttng_rate_policy *
+lttng_action_rotate_session_internal_get_rate_policy(
+               const struct lttng_action *action);
+
+static struct lttng_action_rotate_session *action_rotate_session_from_action(
+               struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static const struct lttng_action_rotate_session *
+action_rotate_session_from_action_const(const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_rotate_session, parent);
+}
+
+static bool lttng_action_rotate_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_rotate_session *action_rotate_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_rotate_session->session_name ||
+                       strlen(action_rotate_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_rotate_session_is_equal(
+               const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_rotate_session *a, *b;
+
+       a = action_rotate_session_from_action_const(_a);
+       b = action_rotate_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
+end:
+       return is_equal;
+}
+static int lttng_action_rotate_session_serialize(
+               struct lttng_action *action, struct lttng_payload *payload)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+       struct lttng_action_rotate_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(payload);
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       LTTNG_ASSERT(action_rotate_session->session_name);
+
+       DBG("Serializing rotate session action: session-name: %s",
+                       action_rotate_session->session_name);
+
+       session_name_len = strlen(action_rotate_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       action_rotate_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_rate_policy_serialize(
+                       action_rotate_session->policy, payload);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static void lttng_action_rotate_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       lttng_rate_policy_destroy(action_rotate_session->policy);
+       free(action_rotate_session->session_name);
+       free(action_rotate_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_rotate_session_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len, ret;
+       const struct lttng_action_rotate_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+       struct lttng_rate_policy *policy = NULL;
+
+       action = lttng_action_rotate_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       comm = (typeof(comm)) view->buffer.data;
+       session_name = (const char *) &comm->data;
+
+       if (!lttng_buffer_view_contains_string(
+                       &view->buffer, session_name, comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+       consumed_len = sizeof(*comm) + comm->session_name_len;
+
+       /* Rate policy. */
+       {
+               struct lttng_payload_view policy_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed_len, -1);
+               ret = lttng_rate_policy_create_from_payload(
+                               &policy_view, &policy);
+               if (ret < 0) {
+                       consumed_len = -1;
+                       goto end;
+               }
+               consumed_len += ret;
+       }
+
+       status = lttng_action_rotate_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(policy);
+       status = lttng_action_rotate_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_rate_policy_destroy(policy);
+       lttng_action_rotate_session_destroy(action);
+
+       return consumed_len;
+}
+
+static enum lttng_error_code lttng_action_rotate_session_mi_serialize(
+               const struct lttng_action *action, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_action_status status;
+       const char *session_name = NULL;
+       const struct lttng_rate_policy *policy = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_ROTATE_SESSION_ACTION(action));
+
+       status = lttng_action_rotate_session_get_session_name(
+                       action, &session_name);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(session_name != NULL);
+
+       status = lttng_action_notify_get_rate_policy(action, &policy);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(policy != NULL);
+
+       /* Open action rotate session element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_rotate_session);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Rate policy. */
+       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close action rotate session element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_rotate_session_create(void)
+{
+       struct lttng_action *action = NULL;
+       struct lttng_rate_policy *policy = NULL;
+       enum lttng_action_status status;
+
+       /* Create a every N = 1 rate policy. */
+       policy = lttng_rate_policy_every_n_create(1);
+       if (!policy) {
+               goto end;
+       }
+
+       action = (lttng_action *) zmalloc(sizeof(struct lttng_action_rotate_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION,
+                       lttng_action_rotate_session_validate,
+                       lttng_action_rotate_session_serialize,
+                       lttng_action_rotate_session_is_equal,
+                       lttng_action_rotate_session_destroy,
+                       lttng_action_rotate_session_internal_get_rate_policy,
+                       lttng_action_generic_add_error_query_results,
+                       lttng_action_rotate_session_mi_serialize);
+
+       status = lttng_action_rotate_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               free(action);
+               action = NULL;
+               goto end;
+       }
+
+end:
+       lttng_rate_policy_destroy(policy);
+       return action;
+}
+
+enum lttng_action_status lttng_action_rotate_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_rotate_session *action_rotate_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action(action);
+
+       free(action_rotate_session->session_name);
+
+       action_rotate_session->session_name = strdup(session_name);
+       if (!action_rotate_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_rotate_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_rotate_session *action_rotate_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_rotate_session = action_rotate_session_from_action_const(action);
+
+       *session_name = action_rotate_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_rotate_session_set_rate_policy(
+               struct lttng_action *action,
+               const struct lttng_rate_policy *policy)
+{
+       enum lttng_action_status status;
+       struct lttng_action_rotate_session *rotate_session_action;
+       struct lttng_rate_policy *copy = NULL;
+
+       if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_rate_policy_copy(policy);
+       if (!copy) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       rotate_session_action = action_rotate_session_from_action(action);
+
+       /* Free the previous rate policy .*/
+       lttng_rate_policy_destroy(rotate_session_action->policy);
+
+       /* Assign the policy. */
+       rotate_session_action->policy = copy;
+       status = LTTNG_ACTION_STATUS_OK;
+       copy = NULL;
+
+end:
+       lttng_rate_policy_destroy(copy);
+       return status;
+}
+
+enum lttng_action_status lttng_action_rotate_session_get_rate_policy(
+               const struct lttng_action *action,
+               const struct lttng_rate_policy **policy)
+{
+       enum lttng_action_status status;
+       const struct lttng_action_rotate_session *rotate_session_action;
+
+       if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotate_session_action = action_rotate_session_from_action_const(action);
+
+       *policy = rotate_session_action->policy;
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+static const struct lttng_rate_policy *
+lttng_action_rotate_session_internal_get_rate_policy(
+               const struct lttng_action *action)
+{
+       const struct lttng_action_rotate_session *_action;
+       _action = action_rotate_session_from_action_const(action);
+
+       return _action->policy;
+}
diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c
deleted file mode 100644 (file)
index 26ddd1b..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/snapshot.h>
-#include <inttypes.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rate-policy.h>
-#include <lttng/action/snapshot-session-internal.h>
-#include <lttng/action/snapshot-session.h>
-#include <lttng/snapshot-internal.h>
-#include <lttng/snapshot.h>
-
-#define IS_SNAPSHOT_SESSION_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION)
-
-struct lttng_action_snapshot_session {
-       struct lttng_action parent;
-
-       /* Owned by this. */
-       char *session_name;
-
-       /*
-        * When non-NULL, use this custom output when taking the snapshot,
-        * rather than the session's registered snapshot output.
-        *
-        * Owned by this.
-        */
-       struct lttng_snapshot_output *output;
-       struct lttng_rate_policy *policy;
-};
-
-struct lttng_action_snapshot_session_comm {
-       /* All string lengths include the trailing \0. */
-       uint32_t session_name_len;
-       uint32_t snapshot_output_len;
-       uint32_t rate_policy_len;
-
-       /*
-        * Variable data (all strings are null-terminated):
-        *
-        *  - session name string
-        *  - snapshot output object
-        *  - policy object
-        */
-       char data[];
-} LTTNG_PACKED;
-
-static const struct lttng_rate_policy *
-lttng_action_snapshot_session_internal_get_rate_policy(
-               const struct lttng_action *action);
-
-static struct lttng_action_snapshot_session *
-action_snapshot_session_from_action(struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(
-                       action, struct lttng_action_snapshot_session, parent);
-}
-
-static const struct lttng_action_snapshot_session *
-action_snapshot_session_from_action_const(const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(
-                       action, struct lttng_action_snapshot_session, parent);
-}
-
-static bool lttng_action_snapshot_session_validate(struct lttng_action *action)
-{
-       bool valid = false;
-       struct lttng_action_snapshot_session *action_snapshot_session;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action(action);
-
-       /* A non-empty session name is mandatory. */
-       if (!action_snapshot_session->session_name ||
-                       strlen(action_snapshot_session->session_name) == 0) {
-               goto end;
-       }
-
-       if (action_snapshot_session->output &&
-                       !lttng_snapshot_output_validate(action_snapshot_session->output)) {
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static bool lttng_action_snapshot_session_is_equal(
-               const struct lttng_action *_a, const struct lttng_action *_b)
-{
-       bool is_equal = false;
-       const struct lttng_action_snapshot_session *a, *b;
-
-       a = action_snapshot_session_from_action_const(_a);
-       b = action_snapshot_session_from_action_const(_b);
-
-       /* Action is not valid if this is not true. */
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       if (a->output && b->output &&
-                       !lttng_snapshot_output_is_equal(a->output, b->output)) {
-               goto end;
-       } else if (!!a->output != !!b->output) {
-               goto end;
-       }
-
-       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
-end:
-       return is_equal;
-}
-
-static size_t serialize_strlen(const char *str)
-{
-       return str ? strlen(str) + 1 : 0;
-}
-
-static int lttng_action_snapshot_session_serialize(
-               struct lttng_action *action, struct lttng_payload *payload)
-{
-       struct lttng_action_snapshot_session *action_snapshot_session;
-       struct lttng_action_snapshot_session_comm comm = {};
-       int ret;
-       size_t size_before_comm;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(payload);
-
-       size_before_comm = payload->buffer.size;
-
-       action_snapshot_session = action_snapshot_session_from_action(action);
-       comm.session_name_len =
-               serialize_strlen(action_snapshot_session->session_name);
-
-       /* Add header. */
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(action_snapshot_session->session_name);
-       DBG("Serializing snapshot session action: session-name: %s",
-                       action_snapshot_session->session_name);
-
-       /* Add session name. */
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       action_snapshot_session->session_name,
-                       comm.session_name_len);
-       if (ret) {
-               goto end;
-       }
-
-       /* Serialize the snapshot output object, if any. */
-       if (action_snapshot_session->output) {
-               const size_t size_before_output = payload->buffer.size;
-               struct lttng_action_snapshot_session_comm *comm_in_payload;
-
-               ret = lttng_snapshot_output_serialize(
-                               action_snapshot_session->output,
-                               payload);
-               if (ret) {
-                       goto end;
-               }
-
-               comm_in_payload = (typeof(comm_in_payload))(
-                               payload->buffer.data + size_before_comm);
-               /* Adjust action length in header. */
-               comm_in_payload->snapshot_output_len =
-                               payload->buffer.size - size_before_output;
-       }
-
-       /* Serialize the rate policy. */
-       {
-               const size_t size_before_output = payload->buffer.size;
-               struct lttng_action_snapshot_session_comm *comm_in_payload;
-
-               ret = lttng_rate_policy_serialize(
-                               action_snapshot_session->policy, payload);
-               if (ret) {
-                       ret = -1;
-                       goto end;
-               }
-
-               comm_in_payload = (typeof(comm_in_payload))(
-                               payload->buffer.data + size_before_comm);
-               /* Adjust rate policy length in header. */
-               comm_in_payload->rate_policy_len =
-                               payload->buffer.size - size_before_output;
-       }
-
-end:
-       return ret;
-}
-
-static void lttng_action_snapshot_session_destroy(struct lttng_action *action)
-{
-       struct lttng_action_snapshot_session *action_snapshot_session;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action(action);
-
-       free(action_snapshot_session->session_name);
-       lttng_snapshot_output_destroy(action_snapshot_session->output);
-       lttng_rate_policy_destroy(action_snapshot_session->policy);
-       free(action_snapshot_session);
-
-end:
-       return;
-}
-
-ssize_t lttng_action_snapshot_session_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **p_action)
-{
-       ssize_t consumed_len;
-       const char *variable_data;
-       struct lttng_action *action;
-       enum lttng_action_status status;
-       struct lttng_snapshot_output *snapshot_output = NULL;
-       struct lttng_rate_policy *policy = NULL;
-       const struct lttng_action_snapshot_session_comm *comm;
-       const struct lttng_payload_view snapshot_session_comm_view =
-                       lttng_payload_view_from_view(
-                               view, 0, sizeof(*comm));
-
-       action = lttng_action_snapshot_session_create();
-       if (!action) {
-               goto error;
-       }
-
-       if (!lttng_payload_view_is_valid(&snapshot_session_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               goto error;
-       }
-
-       comm = (typeof(comm)) snapshot_session_comm_view.buffer.data;
-       variable_data = (const char *) &comm->data;
-
-       consumed_len = sizeof(struct lttng_action_snapshot_session_comm);
-
-       if (!lttng_buffer_view_contains_string(
-                       &view->buffer, variable_data, comm->session_name_len)) {
-               goto error;
-       }
-
-       status = lttng_action_snapshot_session_set_session_name(
-                       action, variable_data);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               goto error;
-       }
-
-       variable_data += comm->session_name_len;
-       consumed_len += comm->session_name_len;
-
-       /* If there is a snapshot output object, deserialize it. */
-       if (comm->snapshot_output_len > 0) {
-               ssize_t snapshot_output_consumed_len;
-               enum lttng_action_status action_status;
-               struct lttng_payload_view snapshot_output_buffer_view =
-                       lttng_payload_view_from_view(view, consumed_len,
-                               comm->snapshot_output_len);
-
-               if (!lttng_payload_view_is_valid(&snapshot_output_buffer_view)) {
-                       ERR("Failed to create buffer view for snapshot output.");
-                       goto error;
-               }
-
-               snapshot_output_consumed_len =
-                               lttng_snapshot_output_create_from_payload(
-                                               &snapshot_output_buffer_view,
-                                               &snapshot_output);
-               if (snapshot_output_consumed_len != comm->snapshot_output_len) {
-                       ERR("Failed to deserialize snapshot output object: "
-                                       "consumed-len: %zd, expected-len: %" PRIu32,
-                                       snapshot_output_consumed_len,
-                                       comm->snapshot_output_len);
-                       goto error;
-               }
-
-               action_status = lttng_action_snapshot_session_set_output(
-                       action, snapshot_output);
-               if (action_status != LTTNG_ACTION_STATUS_OK) {
-                       goto error;
-               }
-
-               /* Ownership has been transferred to the action. */
-               snapshot_output = NULL;
-       }
-
-       variable_data += comm->snapshot_output_len;
-       consumed_len += comm->snapshot_output_len;
-
-       /* Rate policy. */
-       if (comm->rate_policy_len <= 0) {
-               ERR("Rate policy should be present.");
-               goto error;
-       }
-       {
-               ssize_t rate_policy_consumed_len;
-               struct lttng_payload_view policy_view =
-                               lttng_payload_view_from_view(view, consumed_len,
-                                               comm->rate_policy_len);
-
-               if (!lttng_payload_view_is_valid(&policy_view)) {
-                       ERR("Failed to create buffer view for rate policy.");
-                       goto error;
-               }
-
-               rate_policy_consumed_len =
-                               lttng_rate_policy_create_from_payload(
-                                               &policy_view, &policy);
-               if (rate_policy_consumed_len < 0) {
-                       goto error;
-               }
-
-               if (rate_policy_consumed_len != comm->rate_policy_len) {
-                       ERR("Failed to deserialize rate policy object: "
-                           "consumed-len: %zd, expected-len: %" PRIu32,
-                                       rate_policy_consumed_len,
-                                       comm->rate_policy_len);
-                       goto error;
-               }
-
-               status = lttng_action_snapshot_session_set_rate_policy(
-                               action, policy);
-               if (status != LTTNG_ACTION_STATUS_OK) {
-                       goto error;
-               }
-       }
-
-       variable_data += comm->rate_policy_len;
-       consumed_len += comm->rate_policy_len;
-
-       *p_action = action;
-       action = NULL;
-
-       goto end;
-
-error:
-       consumed_len = -1;
-
-end:
-       lttng_rate_policy_destroy(policy);
-       lttng_action_snapshot_session_destroy(action);
-       lttng_snapshot_output_destroy(snapshot_output);
-
-       return consumed_len;
-}
-
-static enum lttng_error_code lttng_action_snapshot_session_mi_serialize(
-               const struct lttng_action *action, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_action_status status;
-       const char *session_name = NULL;
-       const struct lttng_snapshot_output *output = NULL;
-       const struct lttng_rate_policy *policy = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_SNAPSHOT_SESSION_ACTION(action));
-
-       status = lttng_action_snapshot_session_get_session_name(
-                       action, &session_name);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(session_name != NULL);
-
-       status = lttng_action_snapshot_session_get_rate_policy(action, &policy);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(policy != NULL);
-
-       /* Open action snapshot session element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_snapshot_session);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Output if any. */
-       status = lttng_action_snapshot_session_get_output(action, &output);
-       if (status == LTTNG_ACTION_STATUS_OK) {
-               LTTNG_ASSERT(output != NULL);
-               ret_code = lttng_snapshot_output_mi_serialize(output, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       } else if (status != LTTNG_ACTION_STATUS_UNSET) {
-               /* This should not happen at this point. */
-               abort();
-       }
-
-       /* Rate policy. */
-       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close action_snapshot_session element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_snapshot_session_create(void)
-{
-       struct lttng_action *action = NULL;
-       struct lttng_rate_policy *policy = NULL;
-       enum lttng_action_status status;
-
-       /* Create a every N = 1 rate policy. */
-       policy = lttng_rate_policy_every_n_create(1);
-       if (!policy) {
-               goto end;
-       }
-
-       action = zmalloc(sizeof(struct lttng_action_snapshot_session));
-       if (!action) {
-               goto end;
-       }
-
-       lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION,
-                       lttng_action_snapshot_session_validate,
-                       lttng_action_snapshot_session_serialize,
-                       lttng_action_snapshot_session_is_equal,
-                       lttng_action_snapshot_session_destroy,
-                       lttng_action_snapshot_session_internal_get_rate_policy,
-                       lttng_action_generic_add_error_query_results,
-                       lttng_action_snapshot_session_mi_serialize);
-
-       status = lttng_action_snapshot_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               free(action);
-               action = NULL;
-               goto end;
-       }
-
-end:
-       lttng_rate_policy_destroy(policy);
-       return action;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_set_session_name(
-               struct lttng_action *action, const char *session_name)
-{
-       struct lttng_action_snapshot_session *action_snapshot_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action(action);
-
-       free(action_snapshot_session->session_name);
-
-       action_snapshot_session->session_name = strdup(session_name);
-       if (!action_snapshot_session->session_name) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_get_session_name(
-               const struct lttng_action *action, const char **session_name)
-{
-       const struct lttng_action_snapshot_session *action_snapshot_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action_const(action);
-
-       if (action_snapshot_session->session_name) {
-               *session_name = action_snapshot_session->session_name;
-               status = LTTNG_ACTION_STATUS_OK;
-       } else {
-               status = LTTNG_ACTION_STATUS_UNSET;
-       }
-
-end:
-
-       return status;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_set_output(
-               struct lttng_action *action,
-               struct lttng_snapshot_output *output)
-{
-       struct lttng_action_snapshot_session *action_snapshot_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !output) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action(action);
-
-       lttng_snapshot_output_destroy(action_snapshot_session->output);
-       action_snapshot_session->output = output;
-
-       status = LTTNG_ACTION_STATUS_OK;
-
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_get_output(
-               const struct lttng_action *action,
-               const struct lttng_snapshot_output **output)
-{
-       const struct lttng_action_snapshot_session *action_snapshot_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action)|| !output) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_snapshot_session = action_snapshot_session_from_action_const(action);
-
-       if (action_snapshot_session->output) {
-               *output = action_snapshot_session->output;
-               status = LTTNG_ACTION_STATUS_OK;
-       } else {
-               status = LTTNG_ACTION_STATUS_UNSET;
-       }
-
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_set_rate_policy(
-               struct lttng_action *action,
-               const struct lttng_rate_policy *policy)
-{
-       enum lttng_action_status status;
-       struct lttng_action_snapshot_session *snapshot_session_action;
-       struct lttng_rate_policy *copy = NULL;
-
-       if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_rate_policy_copy(policy);
-       if (!copy) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       snapshot_session_action = action_snapshot_session_from_action(action);
-
-       /* Free the previous rate policy .*/
-       lttng_rate_policy_destroy(snapshot_session_action->policy);
-
-       /* Assign the policy. */
-       snapshot_session_action->policy = copy;
-       status = LTTNG_ACTION_STATUS_OK;
-       copy = NULL;
-
-end:
-       lttng_rate_policy_destroy(copy);
-       return status;
-}
-
-enum lttng_action_status lttng_action_snapshot_session_get_rate_policy(
-               const struct lttng_action *action,
-               const struct lttng_rate_policy **policy)
-{
-       enum lttng_action_status status;
-       const struct lttng_action_snapshot_session *snapshot_session_action;
-
-       if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       snapshot_session_action =
-                       action_snapshot_session_from_action_const(action);
-
-       *policy = snapshot_session_action->policy;
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-static const struct lttng_rate_policy *
-lttng_action_snapshot_session_internal_get_rate_policy(
-               const struct lttng_action *action)
-{
-       const struct lttng_action_snapshot_session *_action;
-       _action = action_snapshot_session_from_action_const(action);
-
-       return _action->policy;
-}
diff --git a/src/common/actions/snapshot-session.cpp b/src/common/actions/snapshot-session.cpp
new file mode 100644 (file)
index 0000000..0f3da78
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/snapshot.h>
+#include <inttypes.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rate-policy.h>
+#include <lttng/action/snapshot-session-internal.h>
+#include <lttng/action/snapshot-session.h>
+#include <lttng/snapshot-internal.h>
+#include <lttng/snapshot.h>
+
+#define IS_SNAPSHOT_SESSION_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION)
+
+struct lttng_action_snapshot_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+
+       /*
+        * When non-NULL, use this custom output when taking the snapshot,
+        * rather than the session's registered snapshot output.
+        *
+        * Owned by this.
+        */
+       struct lttng_snapshot_output *output;
+       struct lttng_rate_policy *policy;
+};
+
+struct lttng_action_snapshot_session_comm {
+       /* All string lengths include the trailing \0. */
+       uint32_t session_name_len;
+       uint32_t snapshot_output_len;
+       uint32_t rate_policy_len;
+
+       /*
+        * Variable data (all strings are null-terminated):
+        *
+        *  - session name string
+        *  - snapshot output object
+        *  - policy object
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static const struct lttng_rate_policy *
+lttng_action_snapshot_session_internal_get_rate_policy(
+               const struct lttng_action *action);
+
+static struct lttng_action_snapshot_session *
+action_snapshot_session_from_action(struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(
+                       action, struct lttng_action_snapshot_session, parent);
+}
+
+static const struct lttng_action_snapshot_session *
+action_snapshot_session_from_action_const(const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(
+                       action, struct lttng_action_snapshot_session, parent);
+}
+
+static bool lttng_action_snapshot_session_validate(struct lttng_action *action)
+{
+       bool valid = false;
+       struct lttng_action_snapshot_session *action_snapshot_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_snapshot_session->session_name ||
+                       strlen(action_snapshot_session->session_name) == 0) {
+               goto end;
+       }
+
+       if (action_snapshot_session->output &&
+                       !lttng_snapshot_output_validate(action_snapshot_session->output)) {
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_snapshot_session_is_equal(
+               const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_snapshot_session *a, *b;
+
+       a = action_snapshot_session_from_action_const(_a);
+       b = action_snapshot_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       if (a->output && b->output &&
+                       !lttng_snapshot_output_is_equal(a->output, b->output)) {
+               goto end;
+       } else if (!!a->output != !!b->output) {
+               goto end;
+       }
+
+       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
+end:
+       return is_equal;
+}
+
+static size_t serialize_strlen(const char *str)
+{
+       return str ? strlen(str) + 1 : 0;
+}
+
+static int lttng_action_snapshot_session_serialize(
+               struct lttng_action *action, struct lttng_payload *payload)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       struct lttng_action_snapshot_session_comm comm = {};
+       int ret;
+       size_t size_before_comm;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(payload);
+
+       size_before_comm = payload->buffer.size;
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+       comm.session_name_len =
+               serialize_strlen(action_snapshot_session->session_name);
+
+       /* Add header. */
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(action_snapshot_session->session_name);
+       DBG("Serializing snapshot session action: session-name: %s",
+                       action_snapshot_session->session_name);
+
+       /* Add session name. */
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       action_snapshot_session->session_name,
+                       comm.session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       /* Serialize the snapshot output object, if any. */
+       if (action_snapshot_session->output) {
+               const size_t size_before_output = payload->buffer.size;
+               struct lttng_action_snapshot_session_comm *comm_in_payload;
+
+               ret = lttng_snapshot_output_serialize(
+                               action_snapshot_session->output,
+                               payload);
+               if (ret) {
+                       goto end;
+               }
+
+               comm_in_payload = (typeof(comm_in_payload))(
+                               payload->buffer.data + size_before_comm);
+               /* Adjust action length in header. */
+               comm_in_payload->snapshot_output_len =
+                               payload->buffer.size - size_before_output;
+       }
+
+       /* Serialize the rate policy. */
+       {
+               const size_t size_before_output = payload->buffer.size;
+               struct lttng_action_snapshot_session_comm *comm_in_payload;
+
+               ret = lttng_rate_policy_serialize(
+                               action_snapshot_session->policy, payload);
+               if (ret) {
+                       ret = -1;
+                       goto end;
+               }
+
+               comm_in_payload = (typeof(comm_in_payload))(
+                               payload->buffer.data + size_before_comm);
+               /* Adjust rate policy length in header. */
+               comm_in_payload->rate_policy_len =
+                               payload->buffer.size - size_before_output;
+       }
+
+end:
+       return ret;
+}
+
+static void lttng_action_snapshot_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       free(action_snapshot_session->session_name);
+       lttng_snapshot_output_destroy(action_snapshot_session->output);
+       lttng_rate_policy_destroy(action_snapshot_session->policy);
+       free(action_snapshot_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_snapshot_session_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len;
+       const char *variable_data;
+       struct lttng_action *action;
+       enum lttng_action_status status;
+       struct lttng_snapshot_output *snapshot_output = NULL;
+       struct lttng_rate_policy *policy = NULL;
+       const struct lttng_action_snapshot_session_comm *comm;
+       const struct lttng_payload_view snapshot_session_comm_view =
+                       lttng_payload_view_from_view(
+                               view, 0, sizeof(*comm));
+
+       action = lttng_action_snapshot_session_create();
+       if (!action) {
+               goto error;
+       }
+
+       if (!lttng_payload_view_is_valid(&snapshot_session_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               goto error;
+       }
+
+       comm = (typeof(comm)) snapshot_session_comm_view.buffer.data;
+       variable_data = (const char *) &comm->data;
+
+       consumed_len = sizeof(struct lttng_action_snapshot_session_comm);
+
+       if (!lttng_buffer_view_contains_string(
+                       &view->buffer, variable_data, comm->session_name_len)) {
+               goto error;
+       }
+
+       status = lttng_action_snapshot_session_set_session_name(
+                       action, variable_data);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               goto error;
+       }
+
+       variable_data += comm->session_name_len;
+       consumed_len += comm->session_name_len;
+
+       /* If there is a snapshot output object, deserialize it. */
+       if (comm->snapshot_output_len > 0) {
+               ssize_t snapshot_output_consumed_len;
+               enum lttng_action_status action_status;
+               struct lttng_payload_view snapshot_output_buffer_view =
+                       lttng_payload_view_from_view(view, consumed_len,
+                               comm->snapshot_output_len);
+
+               if (!lttng_payload_view_is_valid(&snapshot_output_buffer_view)) {
+                       ERR("Failed to create buffer view for snapshot output.");
+                       goto error;
+               }
+
+               snapshot_output_consumed_len =
+                               lttng_snapshot_output_create_from_payload(
+                                               &snapshot_output_buffer_view,
+                                               &snapshot_output);
+               if (snapshot_output_consumed_len != comm->snapshot_output_len) {
+                       ERR("Failed to deserialize snapshot output object: "
+                                       "consumed-len: %zd, expected-len: %" PRIu32,
+                                       snapshot_output_consumed_len,
+                                       comm->snapshot_output_len);
+                       goto error;
+               }
+
+               action_status = lttng_action_snapshot_session_set_output(
+                       action, snapshot_output);
+               if (action_status != LTTNG_ACTION_STATUS_OK) {
+                       goto error;
+               }
+
+               /* Ownership has been transferred to the action. */
+               snapshot_output = NULL;
+       }
+
+       variable_data += comm->snapshot_output_len;
+       consumed_len += comm->snapshot_output_len;
+
+       /* Rate policy. */
+       if (comm->rate_policy_len <= 0) {
+               ERR("Rate policy should be present.");
+               goto error;
+       }
+       {
+               ssize_t rate_policy_consumed_len;
+               struct lttng_payload_view policy_view =
+                               lttng_payload_view_from_view(view, consumed_len,
+                                               comm->rate_policy_len);
+
+               if (!lttng_payload_view_is_valid(&policy_view)) {
+                       ERR("Failed to create buffer view for rate policy.");
+                       goto error;
+               }
+
+               rate_policy_consumed_len =
+                               lttng_rate_policy_create_from_payload(
+                                               &policy_view, &policy);
+               if (rate_policy_consumed_len < 0) {
+                       goto error;
+               }
+
+               if (rate_policy_consumed_len != comm->rate_policy_len) {
+                       ERR("Failed to deserialize rate policy object: "
+                           "consumed-len: %zd, expected-len: %" PRIu32,
+                                       rate_policy_consumed_len,
+                                       comm->rate_policy_len);
+                       goto error;
+               }
+
+               status = lttng_action_snapshot_session_set_rate_policy(
+                               action, policy);
+               if (status != LTTNG_ACTION_STATUS_OK) {
+                       goto error;
+               }
+       }
+
+       variable_data += comm->rate_policy_len;
+       consumed_len += comm->rate_policy_len;
+
+       *p_action = action;
+       action = NULL;
+
+       goto end;
+
+error:
+       consumed_len = -1;
+
+end:
+       lttng_rate_policy_destroy(policy);
+       lttng_action_snapshot_session_destroy(action);
+       lttng_snapshot_output_destroy(snapshot_output);
+
+       return consumed_len;
+}
+
+static enum lttng_error_code lttng_action_snapshot_session_mi_serialize(
+               const struct lttng_action *action, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_action_status status;
+       const char *session_name = NULL;
+       const struct lttng_snapshot_output *output = NULL;
+       const struct lttng_rate_policy *policy = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_SNAPSHOT_SESSION_ACTION(action));
+
+       status = lttng_action_snapshot_session_get_session_name(
+                       action, &session_name);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(session_name != NULL);
+
+       status = lttng_action_snapshot_session_get_rate_policy(action, &policy);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(policy != NULL);
+
+       /* Open action snapshot session element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_snapshot_session);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Output if any. */
+       status = lttng_action_snapshot_session_get_output(action, &output);
+       if (status == LTTNG_ACTION_STATUS_OK) {
+               LTTNG_ASSERT(output != NULL);
+               ret_code = lttng_snapshot_output_mi_serialize(output, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       } else if (status != LTTNG_ACTION_STATUS_UNSET) {
+               /* This should not happen at this point. */
+               abort();
+       }
+
+       /* Rate policy. */
+       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close action_snapshot_session element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_snapshot_session_create(void)
+{
+       struct lttng_action *action = NULL;
+       struct lttng_rate_policy *policy = NULL;
+       enum lttng_action_status status;
+
+       /* Create a every N = 1 rate policy. */
+       policy = lttng_rate_policy_every_n_create(1);
+       if (!policy) {
+               goto end;
+       }
+
+       action = (lttng_action *) zmalloc(sizeof(struct lttng_action_snapshot_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION,
+                       lttng_action_snapshot_session_validate,
+                       lttng_action_snapshot_session_serialize,
+                       lttng_action_snapshot_session_is_equal,
+                       lttng_action_snapshot_session_destroy,
+                       lttng_action_snapshot_session_internal_get_rate_policy,
+                       lttng_action_generic_add_error_query_results,
+                       lttng_action_snapshot_session_mi_serialize);
+
+       status = lttng_action_snapshot_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               free(action);
+               action = NULL;
+               goto end;
+       }
+
+end:
+       lttng_rate_policy_destroy(policy);
+       return action;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       free(action_snapshot_session->session_name);
+
+       action_snapshot_session->session_name = strdup(session_name);
+       if (!action_snapshot_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+       if (action_snapshot_session->session_name) {
+               *session_name = action_snapshot_session->session_name;
+               status = LTTNG_ACTION_STATUS_OK;
+       } else {
+               status = LTTNG_ACTION_STATUS_UNSET;
+       }
+
+end:
+
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_output(
+               struct lttng_action *action,
+               struct lttng_snapshot_output *output)
+{
+       struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !output) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action(action);
+
+       lttng_snapshot_output_destroy(action_snapshot_session->output);
+       action_snapshot_session->output = output;
+
+       status = LTTNG_ACTION_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_output(
+               const struct lttng_action *action,
+               const struct lttng_snapshot_output **output)
+{
+       const struct lttng_action_snapshot_session *action_snapshot_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_SNAPSHOT_SESSION_ACTION(action)|| !output) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_snapshot_session = action_snapshot_session_from_action_const(action);
+
+       if (action_snapshot_session->output) {
+               *output = action_snapshot_session->output;
+               status = LTTNG_ACTION_STATUS_OK;
+       } else {
+               status = LTTNG_ACTION_STATUS_UNSET;
+       }
+
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_set_rate_policy(
+               struct lttng_action *action,
+               const struct lttng_rate_policy *policy)
+{
+       enum lttng_action_status status;
+       struct lttng_action_snapshot_session *snapshot_session_action;
+       struct lttng_rate_policy *copy = NULL;
+
+       if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_rate_policy_copy(policy);
+       if (!copy) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       snapshot_session_action = action_snapshot_session_from_action(action);
+
+       /* Free the previous rate policy .*/
+       lttng_rate_policy_destroy(snapshot_session_action->policy);
+
+       /* Assign the policy. */
+       snapshot_session_action->policy = copy;
+       status = LTTNG_ACTION_STATUS_OK;
+       copy = NULL;
+
+end:
+       lttng_rate_policy_destroy(copy);
+       return status;
+}
+
+enum lttng_action_status lttng_action_snapshot_session_get_rate_policy(
+               const struct lttng_action *action,
+               const struct lttng_rate_policy **policy)
+{
+       enum lttng_action_status status;
+       const struct lttng_action_snapshot_session *snapshot_session_action;
+
+       if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       snapshot_session_action =
+                       action_snapshot_session_from_action_const(action);
+
+       *policy = snapshot_session_action->policy;
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+static const struct lttng_rate_policy *
+lttng_action_snapshot_session_internal_get_rate_policy(
+               const struct lttng_action *action)
+{
+       const struct lttng_action_snapshot_session *_action;
+       _action = action_snapshot_session_from_action_const(action);
+
+       return _action->policy;
+}
diff --git a/src/common/actions/start-session.c b/src/common/actions/start-session.c
deleted file mode 100644 (file)
index da7aad8..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rate-policy.h>
-#include <lttng/action/start-session-internal.h>
-#include <lttng/action/start-session.h>
-
-#define IS_START_SESSION_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION)
-
-struct lttng_action_start_session {
-       struct lttng_action parent;
-
-       /* Owned by this. */
-       char *session_name;
-       struct lttng_rate_policy *policy;
-};
-
-struct lttng_action_start_session_comm {
-       /* Includes the trailing \0. */
-       uint32_t session_name_len;
-
-       /*
-        * Variable data:
-        *
-        *  - session name (null terminated)
-        *  - policy
-        */
-       char data[];
-} LTTNG_PACKED;
-
-static const struct lttng_rate_policy *
-lttng_action_start_session_internal_get_rate_policy(
-               const struct lttng_action *action);
-
-static struct lttng_action_start_session *action_start_session_from_action(
-               struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_start_session, parent);
-}
-
-static const struct lttng_action_start_session *
-action_start_session_from_action_const(const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_start_session, parent);
-}
-
-static bool lttng_action_start_session_validate(struct lttng_action *action)
-{
-       bool valid;
-       struct lttng_action_start_session *action_start_session;
-
-       if (!action) {
-               valid = false;
-               goto end;
-       }
-
-       action_start_session = action_start_session_from_action(action);
-
-       /* A non-empty session name is mandatory. */
-       if (!action_start_session->session_name ||
-                       strlen(action_start_session->session_name) == 0) {
-               valid = false;
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static bool lttng_action_start_session_is_equal(
-               const struct lttng_action *_a, const struct lttng_action *_b)
-{
-       bool is_equal = false;
-       struct lttng_action_start_session *a, *b;
-
-       a = container_of(_a, struct lttng_action_start_session, parent);
-       b = container_of(_b, struct lttng_action_start_session, parent);
-
-       /* Action is not valid if this is not true. */
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
-end:
-       return is_equal;
-}
-
-static int lttng_action_start_session_serialize(
-               struct lttng_action *action, struct lttng_payload *payload)
-{
-       struct lttng_action_start_session *action_start_session;
-       struct lttng_action_start_session_comm comm;
-       size_t session_name_len;
-       int ret;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(payload);
-
-       action_start_session = action_start_session_from_action(action);
-
-       LTTNG_ASSERT(action_start_session->session_name);
-
-       DBG("Serializing start session action: session-name: %s",
-                       action_start_session->session_name);
-
-       session_name_len = strlen(action_start_session->session_name) + 1;
-       comm.session_name_len = session_name_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       action_start_session->session_name, session_name_len);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_rate_policy_serialize(
-                       action_start_session->policy, payload);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-static void lttng_action_start_session_destroy(struct lttng_action *action)
-{
-       struct lttng_action_start_session *action_start_session;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_start_session = action_start_session_from_action(action);
-
-       lttng_rate_policy_destroy(action_start_session->policy);
-       free(action_start_session->session_name);
-       free(action_start_session);
-
-end:
-       return;
-}
-
-ssize_t lttng_action_start_session_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **p_action)
-{
-       ssize_t consumed_len, ret;
-       const struct lttng_action_start_session_comm *comm;
-       const char *session_name;
-       struct lttng_action *action = NULL;
-       enum lttng_action_status status;
-       struct lttng_rate_policy *policy = NULL;
-
-       comm = (typeof(comm)) view->buffer.data;
-       session_name = (const char *) &comm->data;
-
-       /* Session name */
-       if (!lttng_buffer_view_contains_string(&view->buffer, session_name,
-                           comm->session_name_len)) {
-               consumed_len = -1;
-               goto end;
-       }
-       consumed_len = sizeof(*comm) + comm->session_name_len;
-
-       /* Rate policy. */
-       {
-               struct lttng_payload_view policy_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed_len, -1);
-               ret = lttng_rate_policy_create_from_payload(
-                               &policy_view, &policy);
-               if (ret < 0) {
-                       consumed_len = -1;
-                       goto end;
-               }
-               consumed_len += ret;
-       }
-
-       action = lttng_action_start_session_create();
-       if (!action) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       status = lttng_action_start_session_set_session_name(
-                       action, session_name);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(policy);
-       status = lttng_action_start_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       *p_action = action;
-       action = NULL;
-
-end:
-       lttng_rate_policy_destroy(policy);
-       lttng_action_start_session_destroy(action);
-
-       return consumed_len;
-}
-
-static enum lttng_error_code lttng_action_start_session_mi_serialize(
-               const struct lttng_action *action, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_action_status status;
-       const char *session_name = NULL;
-       const struct lttng_rate_policy *policy = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_START_SESSION_ACTION(action));
-
-       status = lttng_action_start_session_get_session_name(
-                       action, &session_name);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(session_name != NULL);
-
-       status = lttng_action_start_session_get_rate_policy(action, &policy);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(policy != NULL);
-
-       /* Open action start session element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_start_session);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Rate policy. */
-       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close action start session element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_start_session_create(void)
-{
-       struct lttng_action *action = NULL;
-       struct lttng_rate_policy *policy = NULL;
-       enum lttng_action_status status;
-
-       /* Create a every N = 1 rate policy. */
-       policy = lttng_rate_policy_every_n_create(1);
-       if (!policy) {
-               goto end;
-       }
-
-       action = zmalloc(sizeof(struct lttng_action_start_session));
-       if (!action) {
-               goto end;
-       }
-
-       lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION,
-                       lttng_action_start_session_validate,
-                       lttng_action_start_session_serialize,
-                       lttng_action_start_session_is_equal,
-                       lttng_action_start_session_destroy,
-                       lttng_action_start_session_internal_get_rate_policy,
-                       lttng_action_generic_add_error_query_results,
-                       lttng_action_start_session_mi_serialize);
-
-       status = lttng_action_start_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               free(action);
-               action = NULL;
-               goto end;
-       }
-
-end:
-       lttng_rate_policy_destroy(policy);
-       return action;
-}
-
-enum lttng_action_status lttng_action_start_session_set_session_name(
-               struct lttng_action *action, const char *session_name)
-{
-       struct lttng_action_start_session *action_start_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_START_SESSION_ACTION(action) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_start_session = action_start_session_from_action(action);
-
-       free(action_start_session->session_name);
-
-       action_start_session->session_name = strdup(session_name);
-       if (!action_start_session->session_name) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_start_session_get_session_name(
-               const struct lttng_action *action, const char **session_name)
-{
-       const struct lttng_action_start_session *action_start_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_START_SESSION_ACTION(action) || !session_name) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_start_session = action_start_session_from_action_const(action);
-
-       *session_name = action_start_session->session_name;
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_start_session_set_rate_policy(
-               struct lttng_action *action,
-               const struct lttng_rate_policy *policy)
-{
-       enum lttng_action_status status;
-       struct lttng_action_start_session *start_session_action;
-       struct lttng_rate_policy *copy = NULL;
-
-       if (!action || !policy || !IS_START_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_rate_policy_copy(policy);
-       if (!copy) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       start_session_action = action_start_session_from_action(action);
-
-       /* Release the previous rate policy .*/
-       lttng_rate_policy_destroy(start_session_action->policy);
-
-       /* Assign the policy. */
-       start_session_action->policy = copy;
-       status = LTTNG_ACTION_STATUS_OK;
-       copy = NULL;
-
-end:
-       lttng_rate_policy_destroy(copy);
-       return status;
-}
-
-enum lttng_action_status lttng_action_start_session_get_rate_policy(
-               const struct lttng_action *action,
-               const struct lttng_rate_policy **policy)
-{
-       enum lttng_action_status status;
-       const struct lttng_action_start_session *start_session_action;
-
-       if (!action || !policy || !IS_START_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       start_session_action = action_start_session_from_action_const(action);
-
-       *policy = start_session_action->policy;
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-static const struct lttng_rate_policy *
-lttng_action_start_session_internal_get_rate_policy(
-               const struct lttng_action *action)
-{
-       const struct lttng_action_start_session *_action;
-       _action = action_start_session_from_action_const(action);
-
-       return _action->policy;
-}
diff --git a/src/common/actions/start-session.cpp b/src/common/actions/start-session.cpp
new file mode 100644 (file)
index 0000000..1312e5e
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rate-policy.h>
+#include <lttng/action/start-session-internal.h>
+#include <lttng/action/start-session.h>
+
+#define IS_START_SESSION_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION)
+
+struct lttng_action_start_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+       struct lttng_rate_policy *policy;
+};
+
+struct lttng_action_start_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        *  - policy
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static const struct lttng_rate_policy *
+lttng_action_start_session_internal_get_rate_policy(
+               const struct lttng_action *action);
+
+static struct lttng_action_start_session *action_start_session_from_action(
+               struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static const struct lttng_action_start_session *
+action_start_session_from_action_const(const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_start_session, parent);
+}
+
+static bool lttng_action_start_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_start_session *action_start_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_start_session->session_name ||
+                       strlen(action_start_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_start_session_is_equal(
+               const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       struct lttng_action_start_session *a, *b;
+
+       a = container_of(_a, struct lttng_action_start_session, parent);
+       b = container_of(_b, struct lttng_action_start_session, parent);
+
+       /* Action is not valid if this is not true. */
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
+end:
+       return is_equal;
+}
+
+static int lttng_action_start_session_serialize(
+               struct lttng_action *action, struct lttng_payload *payload)
+{
+       struct lttng_action_start_session *action_start_session;
+       struct lttng_action_start_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(payload);
+
+       action_start_session = action_start_session_from_action(action);
+
+       LTTNG_ASSERT(action_start_session->session_name);
+
+       DBG("Serializing start session action: session-name: %s",
+                       action_start_session->session_name);
+
+       session_name_len = strlen(action_start_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       action_start_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_rate_policy_serialize(
+                       action_start_session->policy, payload);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+static void lttng_action_start_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_start_session *action_start_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       lttng_rate_policy_destroy(action_start_session->policy);
+       free(action_start_session->session_name);
+       free(action_start_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_start_session_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len, ret;
+       const struct lttng_action_start_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action = NULL;
+       enum lttng_action_status status;
+       struct lttng_rate_policy *policy = NULL;
+
+       comm = (typeof(comm)) view->buffer.data;
+       session_name = (const char *) &comm->data;
+
+       /* Session name */
+       if (!lttng_buffer_view_contains_string(&view->buffer, session_name,
+                           comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+       consumed_len = sizeof(*comm) + comm->session_name_len;
+
+       /* Rate policy. */
+       {
+               struct lttng_payload_view policy_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed_len, -1);
+               ret = lttng_rate_policy_create_from_payload(
+                               &policy_view, &policy);
+               if (ret < 0) {
+                       consumed_len = -1;
+                       goto end;
+               }
+               consumed_len += ret;
+       }
+
+       action = lttng_action_start_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       status = lttng_action_start_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(policy);
+       status = lttng_action_start_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_rate_policy_destroy(policy);
+       lttng_action_start_session_destroy(action);
+
+       return consumed_len;
+}
+
+static enum lttng_error_code lttng_action_start_session_mi_serialize(
+               const struct lttng_action *action, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_action_status status;
+       const char *session_name = NULL;
+       const struct lttng_rate_policy *policy = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_START_SESSION_ACTION(action));
+
+       status = lttng_action_start_session_get_session_name(
+                       action, &session_name);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(session_name != NULL);
+
+       status = lttng_action_start_session_get_rate_policy(action, &policy);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(policy != NULL);
+
+       /* Open action start session element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_start_session);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Rate policy. */
+       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close action start session element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_start_session_create(void)
+{
+       struct lttng_action *action = NULL;
+       struct lttng_rate_policy *policy = NULL;
+       enum lttng_action_status status;
+
+       /* Create a every N = 1 rate policy. */
+       policy = lttng_rate_policy_every_n_create(1);
+       if (!policy) {
+               goto end;
+       }
+
+       action = (lttng_action *) zmalloc(sizeof(struct lttng_action_start_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION,
+                       lttng_action_start_session_validate,
+                       lttng_action_start_session_serialize,
+                       lttng_action_start_session_is_equal,
+                       lttng_action_start_session_destroy,
+                       lttng_action_start_session_internal_get_rate_policy,
+                       lttng_action_generic_add_error_query_results,
+                       lttng_action_start_session_mi_serialize);
+
+       status = lttng_action_start_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               free(action);
+               action = NULL;
+               goto end;
+       }
+
+end:
+       lttng_rate_policy_destroy(policy);
+       return action;
+}
+
+enum lttng_action_status lttng_action_start_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_start_session *action_start_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_START_SESSION_ACTION(action) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action(action);
+
+       free(action_start_session->session_name);
+
+       action_start_session->session_name = strdup(session_name);
+       if (!action_start_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_start_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_start_session *action_start_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_START_SESSION_ACTION(action) || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_start_session = action_start_session_from_action_const(action);
+
+       *session_name = action_start_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_start_session_set_rate_policy(
+               struct lttng_action *action,
+               const struct lttng_rate_policy *policy)
+{
+       enum lttng_action_status status;
+       struct lttng_action_start_session *start_session_action;
+       struct lttng_rate_policy *copy = NULL;
+
+       if (!action || !policy || !IS_START_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_rate_policy_copy(policy);
+       if (!copy) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       start_session_action = action_start_session_from_action(action);
+
+       /* Release the previous rate policy .*/
+       lttng_rate_policy_destroy(start_session_action->policy);
+
+       /* Assign the policy. */
+       start_session_action->policy = copy;
+       status = LTTNG_ACTION_STATUS_OK;
+       copy = NULL;
+
+end:
+       lttng_rate_policy_destroy(copy);
+       return status;
+}
+
+enum lttng_action_status lttng_action_start_session_get_rate_policy(
+               const struct lttng_action *action,
+               const struct lttng_rate_policy **policy)
+{
+       enum lttng_action_status status;
+       const struct lttng_action_start_session *start_session_action;
+
+       if (!action || !policy || !IS_START_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       start_session_action = action_start_session_from_action_const(action);
+
+       *policy = start_session_action->policy;
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+static const struct lttng_rate_policy *
+lttng_action_start_session_internal_get_rate_policy(
+               const struct lttng_action *action)
+{
+       const struct lttng_action_start_session *_action;
+       _action = action_start_session_from_action_const(action);
+
+       return _action->policy;
+}
diff --git a/src/common/actions/stop-session.c b/src/common/actions/stop-session.c
deleted file mode 100644 (file)
index fa2c77b..0000000
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/rate-policy-internal.h>
-#include <lttng/action/rate-policy.h>
-#include <lttng/action/stop-session-internal.h>
-#include <lttng/action/stop-session.h>
-
-#define IS_STOP_SESSION_ACTION(action) \
-       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION)
-
-struct lttng_action_stop_session {
-       struct lttng_action parent;
-
-       /* Owned by this. */
-       char *session_name;
-       struct lttng_rate_policy *policy;
-};
-
-struct lttng_action_stop_session_comm {
-       /* Includes the trailing \0. */
-       uint32_t session_name_len;
-
-       /*
-        * Variable data:
-        *
-        *  - session name (null terminated)
-        *  - policy
-        */
-       char data[];
-} LTTNG_PACKED;
-
-static const struct lttng_rate_policy *
-lttng_action_stop_session_internal_get_rate_policy(
-               const struct lttng_action *action);
-
-static struct lttng_action_stop_session *action_stop_session_from_action(
-               struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_stop_session, parent);
-}
-
-static const struct lttng_action_stop_session *
-action_stop_session_from_action_const(const struct lttng_action *action)
-{
-       LTTNG_ASSERT(action);
-
-       return container_of(action, struct lttng_action_stop_session, parent);
-}
-
-static bool lttng_action_stop_session_validate(struct lttng_action *action)
-{
-       bool valid;
-       struct lttng_action_stop_session *action_stop_session;
-
-       if (!action) {
-               valid = false;
-               goto end;
-       }
-
-       action_stop_session = action_stop_session_from_action(action);
-
-       /* A non-empty session name is mandatory. */
-       if (!action_stop_session->session_name ||
-                       strlen(action_stop_session->session_name) == 0) {
-               valid = false;
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static bool lttng_action_stop_session_is_equal(
-               const struct lttng_action *_a, const struct lttng_action *_b)
-{
-       bool is_equal = false;
-       const struct lttng_action_stop_session *a, *b;
-
-       a = action_stop_session_from_action_const(_a);
-       b = action_stop_session_from_action_const(_b);
-
-       /* Action is not valid if this is not true. */
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
-end:
-       return is_equal;
-}
-
-static int lttng_action_stop_session_serialize(
-               struct lttng_action *action, struct lttng_payload *payload)
-{
-       struct lttng_action_stop_session *action_stop_session;
-       struct lttng_action_stop_session_comm comm;
-       size_t session_name_len;
-       int ret;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(payload);
-
-       action_stop_session = action_stop_session_from_action(action);
-
-       LTTNG_ASSERT(action_stop_session->session_name);
-
-       DBG("Serializing stop session action: session-name: %s",
-                       action_stop_session->session_name);
-
-       session_name_len = strlen(action_stop_session->session_name) + 1;
-       comm.session_name_len = session_name_len;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       action_stop_session->session_name, session_name_len);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_rate_policy_serialize(action_stop_session->policy, payload);
-       if (ret) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-static void lttng_action_stop_session_destroy(struct lttng_action *action)
-{
-       struct lttng_action_stop_session *action_stop_session;
-
-       if (!action) {
-               goto end;
-       }
-
-       action_stop_session = action_stop_session_from_action(action);
-
-       lttng_rate_policy_destroy(action_stop_session->policy);
-       free(action_stop_session->session_name);
-       free(action_stop_session);
-
-end:
-       return;
-}
-
-ssize_t lttng_action_stop_session_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_action **p_action)
-{
-       ssize_t consumed_len, ret;
-       const struct lttng_action_stop_session_comm *comm;
-       const char *session_name;
-       struct lttng_action *action = NULL;
-       enum lttng_action_status status;
-       struct lttng_rate_policy *policy = NULL;
-
-       comm = (typeof(comm)) view->buffer.data;
-       session_name = (const char *) &comm->data;
-
-       /* Session name. */
-       if (!lttng_buffer_view_contains_string(
-                       &view->buffer, session_name, comm->session_name_len)) {
-               consumed_len = -1;
-               goto end;
-       }
-       consumed_len = sizeof(*comm) + comm->session_name_len;
-
-       /* Rate policy. */
-       {
-               struct lttng_payload_view policy_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed_len, -1);
-               ret = lttng_rate_policy_create_from_payload(
-                               &policy_view, &policy);
-               if (ret < 0) {
-                       consumed_len = -1;
-                       goto end;
-               }
-               consumed_len += ret;
-       }
-
-       action = lttng_action_stop_session_create();
-       if (!action) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       status = lttng_action_stop_session_set_session_name(
-                       action, session_name);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(policy);
-       status = lttng_action_stop_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               consumed_len = -1;
-               goto end;
-       }
-
-       *p_action = action;
-       action = NULL;
-
-end:
-       lttng_rate_policy_destroy(policy);
-       lttng_action_stop_session_destroy(action);
-
-       return consumed_len;
-}
-
-static enum lttng_error_code lttng_action_stop_session_mi_serialize(
-               const struct lttng_action *action, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_action_status status;
-       const char *session_name = NULL;
-       const struct lttng_rate_policy *policy = NULL;
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(IS_STOP_SESSION_ACTION(action));
-
-       status = lttng_action_stop_session_get_session_name(
-                       action, &session_name);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(session_name != NULL);
-
-       status = lttng_action_stop_session_get_rate_policy(action, &policy);
-       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-       LTTNG_ASSERT(policy != NULL);
-
-       /* Open action stop session. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_action_start_session);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Rate policy. */
-       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close action stop session element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_action *lttng_action_stop_session_create(void)
-{
-       struct lttng_action *action = NULL;
-       struct lttng_rate_policy *policy = NULL;
-       enum lttng_action_status status;
-
-       /* Create a every N = 1 rate policy. */
-       policy = lttng_rate_policy_every_n_create(1);
-       if (!policy) {
-               goto end;
-       }
-
-       action = zmalloc(sizeof(struct lttng_action_stop_session));
-       if (!action) {
-               goto end;
-       }
-
-       lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION,
-                       lttng_action_stop_session_validate,
-                       lttng_action_stop_session_serialize,
-                       lttng_action_stop_session_is_equal,
-                       lttng_action_stop_session_destroy,
-                       lttng_action_stop_session_internal_get_rate_policy,
-                       lttng_action_generic_add_error_query_results,
-                       lttng_action_stop_session_mi_serialize);
-
-       status = lttng_action_stop_session_set_rate_policy(action, policy);
-       if (status != LTTNG_ACTION_STATUS_OK) {
-               free(action);
-               action = NULL;
-               goto end;
-       }
-
-end:
-       lttng_rate_policy_destroy(policy);
-       return action;
-}
-
-enum lttng_action_status lttng_action_stop_session_set_session_name(
-               struct lttng_action *action, const char *session_name)
-{
-       struct lttng_action_stop_session *action_stop_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_stop_session = action_stop_session_from_action(action);
-
-       free(action_stop_session->session_name);
-
-       action_stop_session->session_name = strdup(session_name);
-       if (!action_stop_session->session_name) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_stop_session_get_session_name(
-               const struct lttng_action *action, const char **session_name)
-{
-       const struct lttng_action_stop_session *action_stop_session;
-       enum lttng_action_status status;
-
-       if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       action_stop_session = action_stop_session_from_action_const(action);
-
-       *session_name = action_stop_session->session_name;
-
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_action_status lttng_action_stop_session_set_rate_policy(
-               struct lttng_action *action,
-               const struct lttng_rate_policy *policy)
-{
-       enum lttng_action_status status;
-       struct lttng_action_stop_session *stop_session_action;
-       struct lttng_rate_policy *copy = NULL;
-
-       if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_rate_policy_copy(policy);
-       if (!copy) {
-               status = LTTNG_ACTION_STATUS_ERROR;
-               goto end;
-       }
-       stop_session_action = action_stop_session_from_action(action);
-
-       /* Free the previous rate policy .*/
-       lttng_rate_policy_destroy(stop_session_action->policy);
-
-       stop_session_action->policy = copy;
-       status = LTTNG_ACTION_STATUS_OK;
-       copy = NULL;
-
-end:
-       lttng_rate_policy_destroy(copy);
-       return status;
-}
-
-enum lttng_action_status lttng_action_stop_session_get_rate_policy(
-               const struct lttng_action *action,
-               const struct lttng_rate_policy **policy)
-{
-       enum lttng_action_status status;
-       const struct lttng_action_stop_session *stop_session_action;
-
-       if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) {
-               status = LTTNG_ACTION_STATUS_INVALID;
-               goto end;
-       }
-
-       stop_session_action = action_stop_session_from_action_const(action);
-
-       *policy = stop_session_action->policy;
-       status = LTTNG_ACTION_STATUS_OK;
-end:
-       return status;
-}
-
-static const struct lttng_rate_policy *
-lttng_action_stop_session_internal_get_rate_policy(
-               const struct lttng_action *action)
-{
-       const struct lttng_action_stop_session *_action;
-       _action = action_stop_session_from_action_const(action);
-
-       return _action->policy;
-}
diff --git a/src/common/actions/stop-session.cpp b/src/common/actions/stop-session.cpp
new file mode 100644 (file)
index 0000000..55f1a55
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/rate-policy-internal.h>
+#include <lttng/action/rate-policy.h>
+#include <lttng/action/stop-session-internal.h>
+#include <lttng/action/stop-session.h>
+
+#define IS_STOP_SESSION_ACTION(action) \
+       (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION)
+
+struct lttng_action_stop_session {
+       struct lttng_action parent;
+
+       /* Owned by this. */
+       char *session_name;
+       struct lttng_rate_policy *policy;
+};
+
+struct lttng_action_stop_session_comm {
+       /* Includes the trailing \0. */
+       uint32_t session_name_len;
+
+       /*
+        * Variable data:
+        *
+        *  - session name (null terminated)
+        *  - policy
+        */
+       char data[];
+} LTTNG_PACKED;
+
+static const struct lttng_rate_policy *
+lttng_action_stop_session_internal_get_rate_policy(
+               const struct lttng_action *action);
+
+static struct lttng_action_stop_session *action_stop_session_from_action(
+               struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static const struct lttng_action_stop_session *
+action_stop_session_from_action_const(const struct lttng_action *action)
+{
+       LTTNG_ASSERT(action);
+
+       return container_of(action, struct lttng_action_stop_session, parent);
+}
+
+static bool lttng_action_stop_session_validate(struct lttng_action *action)
+{
+       bool valid;
+       struct lttng_action_stop_session *action_stop_session;
+
+       if (!action) {
+               valid = false;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       /* A non-empty session name is mandatory. */
+       if (!action_stop_session->session_name ||
+                       strlen(action_stop_session->session_name) == 0) {
+               valid = false;
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static bool lttng_action_stop_session_is_equal(
+               const struct lttng_action *_a, const struct lttng_action *_b)
+{
+       bool is_equal = false;
+       const struct lttng_action_stop_session *a, *b;
+
+       a = action_stop_session_from_action_const(_a);
+       b = action_stop_session_from_action_const(_b);
+
+       /* Action is not valid if this is not true. */
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = lttng_rate_policy_is_equal(a->policy, b->policy);
+end:
+       return is_equal;
+}
+
+static int lttng_action_stop_session_serialize(
+               struct lttng_action *action, struct lttng_payload *payload)
+{
+       struct lttng_action_stop_session *action_stop_session;
+       struct lttng_action_stop_session_comm comm;
+       size_t session_name_len;
+       int ret;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(payload);
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       LTTNG_ASSERT(action_stop_session->session_name);
+
+       DBG("Serializing stop session action: session-name: %s",
+                       action_stop_session->session_name);
+
+       session_name_len = strlen(action_stop_session->session_name) + 1;
+       comm.session_name_len = session_name_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       action_stop_session->session_name, session_name_len);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_rate_policy_serialize(action_stop_session->policy, payload);
+       if (ret) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+static void lttng_action_stop_session_destroy(struct lttng_action *action)
+{
+       struct lttng_action_stop_session *action_stop_session;
+
+       if (!action) {
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       lttng_rate_policy_destroy(action_stop_session->policy);
+       free(action_stop_session->session_name);
+       free(action_stop_session);
+
+end:
+       return;
+}
+
+ssize_t lttng_action_stop_session_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_action **p_action)
+{
+       ssize_t consumed_len, ret;
+       const struct lttng_action_stop_session_comm *comm;
+       const char *session_name;
+       struct lttng_action *action = NULL;
+       enum lttng_action_status status;
+       struct lttng_rate_policy *policy = NULL;
+
+       comm = (typeof(comm)) view->buffer.data;
+       session_name = (const char *) &comm->data;
+
+       /* Session name. */
+       if (!lttng_buffer_view_contains_string(
+                       &view->buffer, session_name, comm->session_name_len)) {
+               consumed_len = -1;
+               goto end;
+       }
+       consumed_len = sizeof(*comm) + comm->session_name_len;
+
+       /* Rate policy. */
+       {
+               struct lttng_payload_view policy_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed_len, -1);
+               ret = lttng_rate_policy_create_from_payload(
+                               &policy_view, &policy);
+               if (ret < 0) {
+                       consumed_len = -1;
+                       goto end;
+               }
+               consumed_len += ret;
+       }
+
+       action = lttng_action_stop_session_create();
+       if (!action) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       status = lttng_action_stop_session_set_session_name(
+                       action, session_name);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(policy);
+       status = lttng_action_stop_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               consumed_len = -1;
+               goto end;
+       }
+
+       *p_action = action;
+       action = NULL;
+
+end:
+       lttng_rate_policy_destroy(policy);
+       lttng_action_stop_session_destroy(action);
+
+       return consumed_len;
+}
+
+static enum lttng_error_code lttng_action_stop_session_mi_serialize(
+               const struct lttng_action *action, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_action_status status;
+       const char *session_name = NULL;
+       const struct lttng_rate_policy *policy = NULL;
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(IS_STOP_SESSION_ACTION(action));
+
+       status = lttng_action_stop_session_get_session_name(
+                       action, &session_name);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(session_name != NULL);
+
+       status = lttng_action_stop_session_get_rate_policy(action, &policy);
+       LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+       LTTNG_ASSERT(policy != NULL);
+
+       /* Open action stop session. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_action_start_session);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Rate policy. */
+       ret_code = lttng_rate_policy_mi_serialize(policy, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close action stop session element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_action *lttng_action_stop_session_create(void)
+{
+       struct lttng_action *action = NULL;
+       struct lttng_rate_policy *policy = NULL;
+       enum lttng_action_status status;
+
+       /* Create a every N = 1 rate policy. */
+       policy = lttng_rate_policy_every_n_create(1);
+       if (!policy) {
+               goto end;
+       }
+
+       action = (lttng_action *) zmalloc(sizeof(struct lttng_action_stop_session));
+       if (!action) {
+               goto end;
+       }
+
+       lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION,
+                       lttng_action_stop_session_validate,
+                       lttng_action_stop_session_serialize,
+                       lttng_action_stop_session_is_equal,
+                       lttng_action_stop_session_destroy,
+                       lttng_action_stop_session_internal_get_rate_policy,
+                       lttng_action_generic_add_error_query_results,
+                       lttng_action_stop_session_mi_serialize);
+
+       status = lttng_action_stop_session_set_rate_policy(action, policy);
+       if (status != LTTNG_ACTION_STATUS_OK) {
+               free(action);
+               action = NULL;
+               goto end;
+       }
+
+end:
+       lttng_rate_policy_destroy(policy);
+       return action;
+}
+
+enum lttng_action_status lttng_action_stop_session_set_session_name(
+               struct lttng_action *action, const char *session_name)
+{
+       struct lttng_action_stop_session *action_stop_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action(action);
+
+       free(action_stop_session->session_name);
+
+       action_stop_session->session_name = strdup(session_name);
+       if (!action_stop_session->session_name) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_stop_session_get_session_name(
+               const struct lttng_action *action, const char **session_name)
+{
+       const struct lttng_action_stop_session *action_stop_session;
+       enum lttng_action_status status;
+
+       if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       action_stop_session = action_stop_session_from_action_const(action);
+
+       *session_name = action_stop_session->session_name;
+
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_action_status lttng_action_stop_session_set_rate_policy(
+               struct lttng_action *action,
+               const struct lttng_rate_policy *policy)
+{
+       enum lttng_action_status status;
+       struct lttng_action_stop_session *stop_session_action;
+       struct lttng_rate_policy *copy = NULL;
+
+       if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_rate_policy_copy(policy);
+       if (!copy) {
+               status = LTTNG_ACTION_STATUS_ERROR;
+               goto end;
+       }
+       stop_session_action = action_stop_session_from_action(action);
+
+       /* Free the previous rate policy .*/
+       lttng_rate_policy_destroy(stop_session_action->policy);
+
+       stop_session_action->policy = copy;
+       status = LTTNG_ACTION_STATUS_OK;
+       copy = NULL;
+
+end:
+       lttng_rate_policy_destroy(copy);
+       return status;
+}
+
+enum lttng_action_status lttng_action_stop_session_get_rate_policy(
+               const struct lttng_action *action,
+               const struct lttng_rate_policy **policy)
+{
+       enum lttng_action_status status;
+       const struct lttng_action_stop_session *stop_session_action;
+
+       if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) {
+               status = LTTNG_ACTION_STATUS_INVALID;
+               goto end;
+       }
+
+       stop_session_action = action_stop_session_from_action_const(action);
+
+       *policy = stop_session_action->policy;
+       status = LTTNG_ACTION_STATUS_OK;
+end:
+       return status;
+}
+
+static const struct lttng_rate_policy *
+lttng_action_stop_session_internal_get_rate_policy(
+               const struct lttng_action *action)
+{
+       const struct lttng_action_stop_session *_action;
+       _action = action_stop_session_from_action_const(action);
+
+       return _action->policy;
+}
diff --git a/src/common/buffer-view.c b/src/common/buffer-view.c
deleted file mode 100644 (file)
index bfd5ac6..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/buffer-view.h>
-#include <common/dynamic-buffer.h>
-#include <common/error.h>
-
-struct lttng_buffer_view lttng_buffer_view_init(
-               const char *src, size_t offset, ptrdiff_t len)
-{
-       struct lttng_buffer_view view = { .data = src + offset, .size = len };
-       return view;
-}
-
-bool lttng_buffer_view_is_valid(const struct lttng_buffer_view *view)
-{
-       return view && view->data && view->size > 0;
-}
-
-struct lttng_buffer_view lttng_buffer_view_from_view(
-               const struct lttng_buffer_view *src, size_t offset,
-               ptrdiff_t len)
-{
-       struct lttng_buffer_view view = { .data = NULL, .size = 0 };
-
-       LTTNG_ASSERT(src);
-
-       if (offset > src->size) {
-               ERR("Attempt to create buffer view from another view with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd",
-                               src->size, offset, len);
-               goto end;
-       }
-
-       if (len != -1 && len > (src->size - offset)) {
-               ERR("Attempt to create buffer view from another view with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd",
-                               src->size, offset, len);
-               goto end;
-       }
-
-       view.data = src->data + offset;
-       view.size = len == -1 ? (src->size - offset) : len;
-end:
-       return view;
-}
-
-struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer(
-               const struct lttng_dynamic_buffer *src, size_t offset,
-               ptrdiff_t len)
-{
-       struct lttng_buffer_view view = { .data = NULL, .size = 0 };
-
-       LTTNG_ASSERT(src);
-
-       if (offset > src->size) {
-               ERR("Attempt to create buffer view from a dynamic buffer with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd",
-                               src->size, offset, len);
-               goto end;
-       }
-
-       if (len != -1 && len > (src->size - offset)) {
-               ERR("Attempt to create buffer view from a dynamic buffer with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd",
-                               src->size, offset, len);
-               goto end;
-       }
-
-       view.data = src->data + offset;
-       view.size = len == -1 ? (src->size - offset) : len;
-end:
-       return view;
-}
-
-bool lttng_buffer_view_contains_string(const struct lttng_buffer_view *buf,
-               const char *str,
-               size_t len_with_null_terminator)
-{
-       const char *past_buf_end;
-       size_t max_str_len_with_null_terminator;
-       size_t str_len;
-       bool ret;
-
-       past_buf_end = buf->data + buf->size;
-
-       /* Is the start of the string in the buffer view? */
-       if (str < buf->data || str >= past_buf_end) {
-               ret = false;
-               goto end;
-       }
-
-       /*
-        * Max length the string could have to fit in the buffer, including
-        * NULL terminator.
-        */
-       max_str_len_with_null_terminator = past_buf_end - str;
-
-       /* Could the string even fit in the buffer? */
-       if (len_with_null_terminator > max_str_len_with_null_terminator) {
-               ret = false;
-               goto end;
-       }
-
-       str_len = lttng_strnlen(str, max_str_len_with_null_terminator);
-       if (str_len != (len_with_null_terminator - 1)) {
-               ret = false;
-               goto end;
-       }
-
-       ret = true;
-
-end:
-       return ret;
-}
diff --git a/src/common/buffer-view.cpp b/src/common/buffer-view.cpp
new file mode 100644 (file)
index 0000000..ebe6869
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+
+struct lttng_buffer_view lttng_buffer_view_init(
+               const char *src, size_t offset, ptrdiff_t len)
+{
+       struct lttng_buffer_view view = { .data = src + offset, .size = (size_t) len };
+       return view;
+}
+
+bool lttng_buffer_view_is_valid(const struct lttng_buffer_view *view)
+{
+       return view && view->data && view->size > 0;
+}
+
+struct lttng_buffer_view lttng_buffer_view_from_view(
+               const struct lttng_buffer_view *src, size_t offset,
+               ptrdiff_t len)
+{
+       struct lttng_buffer_view view = { .data = NULL, .size = 0 };
+
+       LTTNG_ASSERT(src);
+
+       if (offset > src->size) {
+               ERR("Attempt to create buffer view from another view with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd",
+                               src->size, offset, len);
+               goto end;
+       }
+
+       if (len != -1 && len > (src->size - offset)) {
+               ERR("Attempt to create buffer view from another view with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd",
+                               src->size, offset, len);
+               goto end;
+       }
+
+       view.data = src->data + offset;
+       view.size = len == -1 ? (src->size - offset) : len;
+end:
+       return view;
+}
+
+struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer(
+               const struct lttng_dynamic_buffer *src, size_t offset,
+               ptrdiff_t len)
+{
+       struct lttng_buffer_view view = { .data = NULL, .size = 0 };
+
+       LTTNG_ASSERT(src);
+
+       if (offset > src->size) {
+               ERR("Attempt to create buffer view from a dynamic buffer with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd",
+                               src->size, offset, len);
+               goto end;
+       }
+
+       if (len != -1 && len > (src->size - offset)) {
+               ERR("Attempt to create buffer view from a dynamic buffer with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd",
+                               src->size, offset, len);
+               goto end;
+       }
+
+       view.data = src->data + offset;
+       view.size = len == -1 ? (src->size - offset) : len;
+end:
+       return view;
+}
+
+bool lttng_buffer_view_contains_string(const struct lttng_buffer_view *buf,
+               const char *str,
+               size_t len_with_null_terminator)
+{
+       const char *past_buf_end;
+       size_t max_str_len_with_null_terminator;
+       size_t str_len;
+       bool ret;
+
+       past_buf_end = buf->data + buf->size;
+
+       /* Is the start of the string in the buffer view? */
+       if (str < buf->data || str >= past_buf_end) {
+               ret = false;
+               goto end;
+       }
+
+       /*
+        * Max length the string could have to fit in the buffer, including
+        * NULL terminator.
+        */
+       max_str_len_with_null_terminator = past_buf_end - str;
+
+       /* Could the string even fit in the buffer? */
+       if (len_with_null_terminator > max_str_len_with_null_terminator) {
+               ret = false;
+               goto end;
+       }
+
+       str_len = lttng_strnlen(str, max_str_len_with_null_terminator);
+       if (str_len != (len_with_null_terminator - 1)) {
+               ret = false;
+               goto end;
+       }
+
+       ret = true;
+
+end:
+       return ret;
+}
diff --git a/src/common/conditions/buffer-usage.c b/src/common/conditions/buffer-usage.c
deleted file mode 100644 (file)
index 714ba58..0000000
+++ /dev/null
@@ -1,923 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <float.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <lttng/condition/condition-internal.h>
-#include <math.h>
-#include <time.h>
-
-#define IS_USAGE_CONDITION(condition) ( \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH   \
-       )
-
-static
-bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
-{
-       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
-       return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
-                       type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
-}
-
-static
-void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
-{
-       struct lttng_condition_buffer_usage *usage;
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-
-       free(usage->session_name);
-       free(usage->channel_name);
-       free(usage);
-}
-
-static
-bool lttng_condition_buffer_usage_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_buffer_usage *usage;
-
-       if (!condition) {
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->session_name) {
-               ERR("Invalid buffer condition: a target session name must be set.");
-               goto end;
-       }
-       if (!usage->channel_name) {
-               ERR("Invalid buffer condition: a target channel name must be set.");
-               goto end;
-       }
-       if (usage->threshold_ratio.set == usage->threshold_bytes.set) {
-               ERR("Invalid buffer condition: a threshold must be set or both type cannot be used simultaneously.");
-               goto end;
-       }
-       if (!usage->domain.set) {
-               ERR("Invalid buffer usage condition: a domain must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_buffer_usage_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_condition_buffer_usage *usage;
-       size_t session_name_len, channel_name_len;
-       struct lttng_condition_buffer_usage_comm usage_comm = {};
-
-       if (!condition || !IS_USAGE_CONDITION(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing buffer usage condition");
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-
-       session_name_len = strlen(usage->session_name) + 1;
-       channel_name_len = strlen(usage->channel_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX ||
-                       channel_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
-       usage_comm.session_name_len = session_name_len;
-       usage_comm.channel_name_len = channel_name_len;
-       usage_comm.domain_type = (int8_t) usage->domain.type;
-
-       if (usage->threshold_bytes.set) {
-               usage_comm.threshold_bytes = usage->threshold_bytes.value;
-       } else {
-               usage_comm.threshold_ratio = usage->threshold_ratio.value;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &usage_comm,
-                       sizeof(usage_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->channel_name,
-                       channel_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_buffer_usage *a, *b;
-
-       a = container_of(_a, struct lttng_condition_buffer_usage, parent);
-       b = container_of(_b, struct lttng_condition_buffer_usage, parent);
-
-       if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
-                       (a->threshold_bytes.set && !b->threshold_bytes.set)) {
-               goto end;
-       }
-
-       if (a->threshold_ratio.set && b->threshold_ratio.set) {
-               double a_value, b_value, diff;
-
-               a_value = a->threshold_ratio.value;
-               b_value = b->threshold_ratio.value;
-               diff = fabs(a_value - b_value);
-
-               if (diff > DBL_EPSILON) {
-                       goto end;
-               }
-       } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
-               uint64_t a_value, b_value;
-
-               a_value = a->threshold_bytes.value;
-               b_value = b->threshold_bytes.value;
-               if (a_value != b_value) {
-                       goto end;
-               }
-       }
-
-       /* Condition is not valid if this is not true. */
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->channel_name);
-       LTTNG_ASSERT(b->channel_name);
-       if (strcmp(a->channel_name, b->channel_name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->domain.set);
-       LTTNG_ASSERT(b->domain.set);
-       if (a->domain.type != b->domain.type) {
-               goto end;
-       }
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code lttng_condition_buffer_usage_mi_serialize(
-               const struct lttng_condition *condition,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_condition_status status;
-       const char *session_name = NULL, *channel_name = NULL;
-       enum lttng_domain_type domain_type;
-       bool is_threshold_bytes = false;
-       double threshold_ratio;
-       uint64_t threshold_bytes;
-       const char *condition_type_str = NULL;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(IS_USAGE_CONDITION(condition));
-
-       status = lttng_condition_buffer_usage_get_session_name(
-                       condition, &session_name);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       LTTNG_ASSERT(session_name);
-
-       status = lttng_condition_buffer_usage_get_channel_name(
-                       condition, &channel_name);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       LTTNG_ASSERT(session_name);
-
-       status = lttng_condition_buffer_usage_get_domain_type(
-                       condition, &domain_type);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-
-       status = lttng_condition_buffer_usage_get_threshold(
-                       condition, &threshold_bytes);
-       if (status == LTTNG_CONDITION_STATUS_OK) {
-               is_threshold_bytes = true;
-       } else if (status != LTTNG_CONDITION_STATUS_UNSET) {
-               /* Unexpected at this stage. */
-               ret_code = LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (!is_threshold_bytes) {
-               status = lttng_condition_buffer_usage_get_threshold_ratio(
-                               condition, &threshold_ratio);
-               LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       }
-
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               condition_type_str =
-                               mi_lttng_element_condition_buffer_usage_high;
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               condition_type_str =
-                               mi_lttng_element_condition_buffer_usage_low;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       /* Open the sub type condition element. */
-       ret = mi_lttng_writer_open_element(writer, condition_type_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Channel name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_condition_channel_name, channel_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Domain. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_domain,
-                       mi_lttng_domaintype_string(domain_type));
-       if (ret) {
-               goto mi_error;
-       }
-
-       if (is_threshold_bytes) {
-               /* Usage in bytes. */
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               mi_lttng_element_condition_threshold_bytes,
-                               threshold_bytes);
-               if (ret) {
-                       goto mi_error;
-               }
-       } else {
-               /* Ratio. */
-               ret = mi_lttng_writer_write_element_double(writer,
-                               mi_lttng_element_condition_threshold_ratio,
-                               threshold_ratio);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Closing sub type condition element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-struct lttng_condition *lttng_condition_buffer_usage_create(
-               enum lttng_condition_type type)
-{
-       struct lttng_condition_buffer_usage *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_buffer_usage));
-       if (!condition) {
-               return NULL;
-       }
-
-       lttng_condition_init(&condition->parent, type);
-       condition->parent.validate = lttng_condition_buffer_usage_validate;
-       condition->parent.serialize = lttng_condition_buffer_usage_serialize;
-       condition->parent.equal = lttng_condition_buffer_usage_is_equal;
-       condition->parent.destroy = lttng_condition_buffer_usage_destroy;
-       condition->parent.mi_serialize = lttng_condition_buffer_usage_mi_serialize;
-       return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
-{
-       return lttng_condition_buffer_usage_create(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
-}
-
-struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
-{
-       return lttng_condition_buffer_usage_create(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
-}
-
-static
-ssize_t init_condition_from_payload(struct lttng_condition *condition,
-               struct lttng_payload_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       enum lttng_domain_type domain_type;
-       const char *session_name, *channel_name;
-       struct lttng_buffer_view names_view;
-       const struct lttng_condition_buffer_usage_comm *condition_comm;
-       const struct lttng_payload_view condition_comm_view =
-                       lttng_payload_view_from_view(
-                                       src_view, 0, sizeof(*condition_comm));
-
-       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
-       names_view = lttng_buffer_view_from_view(&src_view->buffer,
-                       sizeof(*condition_comm), -1);
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
-                       condition_comm->channel_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       if (names_view.size <
-                       (condition_comm->session_name_len +
-                       condition_comm->channel_name_len)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
-               ret = -1;
-               goto end;
-       }
-
-       if (condition_comm->threshold_set_in_bytes) {
-               status = lttng_condition_buffer_usage_set_threshold(condition,
-                               condition_comm->threshold_bytes);
-       } else {
-               status = lttng_condition_buffer_usage_set_threshold_ratio(
-                               condition, condition_comm->threshold_ratio);
-       }
-
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to initialize buffer usage condition threshold");
-               ret = -1;
-               goto end;
-       }
-
-       if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
-                       condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
-               /* Invalid domain value. */
-               ERR("Invalid domain type value (%i) found in condition buffer",
-                               (int) condition_comm->domain_type);
-               ret = -1;
-               goto end;
-       }
-
-       domain_type = (enum lttng_domain_type) condition_comm->domain_type;
-       status = lttng_condition_buffer_usage_set_domain_type(condition,
-                       domain_type);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage condition domain");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = names_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       channel_name = session_name + condition_comm->session_name_len;
-       if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
-               ERR("Malformed channel name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_buffer_usage_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage session name");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_buffer_usage_set_channel_name(condition,
-                       channel_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer usage channel name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len +
-                       (ssize_t) condition_comm->channel_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-ssize_t lttng_condition_buffer_usage_low_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_buffer_usage_low_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_payload(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-ssize_t lttng_condition_buffer_usage_high_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_buffer_usage_high_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_payload(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_view *view)
-{
-       const struct lttng_evaluation_buffer_usage_comm *comm =
-                       (typeof(comm)) view->buffer.data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->buffer.size < sizeof(*comm)) {
-               goto end;
-       }
-
-       evaluation = lttng_evaluation_buffer_usage_create(type,
-                       comm->buffer_use, comm->buffer_capacity);
-end:
-       return evaluation;
-}
-
-ssize_t lttng_evaluation_buffer_usage_low_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-ssize_t lttng_evaluation_buffer_usage_high_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(
-                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold_ratio(
-               const struct lttng_condition *condition,
-               double *threshold_ratio)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       !threshold_ratio) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->threshold_ratio.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *threshold_ratio = usage->threshold_ratio.value;
-end:
-       return status;
-}
-
-/* threshold_ratio expressed as [0.0, 1.0]. */
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold_ratio(
-               struct lttng_condition *condition, double threshold_ratio)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       threshold_ratio < 0.0 ||
-                       threshold_ratio > 1.0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->threshold_ratio.set = true;
-       usage->threshold_bytes.set = false;
-       usage->threshold_ratio.value = threshold_ratio;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_threshold(
-               const struct lttng_condition *condition,
-               uint64_t *threshold_bytes)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->threshold_bytes.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *threshold_bytes = usage->threshold_bytes.value;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_threshold(
-               struct lttng_condition *condition, uint64_t threshold_bytes)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition)) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->threshold_ratio.set = false;
-       usage->threshold_bytes.set = true;
-       usage->threshold_bytes.value = threshold_bytes;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = usage->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
-                       strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (usage->session_name) {
-               free(usage->session_name);
-       }
-       usage->session_name = session_name_copy;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_channel_name(
-               const struct lttng_condition *condition,
-               const char **channel_name)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->channel_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *channel_name = usage->channel_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_channel_name(
-               struct lttng_condition *condition, const char *channel_name)
-{
-       char *channel_name_copy;
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
-                       strlen(channel_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       channel_name_copy = strdup(channel_name);
-       if (!channel_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (usage->channel_name) {
-               free(usage->channel_name);
-       }
-       usage->channel_name = channel_name_copy;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_get_domain_type(
-               const struct lttng_condition *condition,
-               enum lttng_domain_type *type)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       if (!usage->domain.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *type = usage->domain.type;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_buffer_usage_set_domain_type(
-               struct lttng_condition *condition, enum lttng_domain_type type)
-{
-       struct lttng_condition_buffer_usage *usage;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_USAGE_CONDITION(condition) ||
-                       type == LTTNG_DOMAIN_NONE) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(condition, struct lttng_condition_buffer_usage,
-                       parent);
-       usage->domain.set = true;
-       usage->domain.type = type;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_buffer_usage_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       struct lttng_evaluation_buffer_usage_comm comm;
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       comm.buffer_use = usage->buffer_use;
-       comm.buffer_capacity = usage->buffer_capacity;
-
-       return lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_buffer_usage_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       free(usage);
-}
-
-struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
-               enum lttng_condition_type type, uint64_t use, uint64_t capacity)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-
-       usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
-       if (!usage) {
-               goto end;
-       }
-
-       usage->parent.type = type;
-       usage->buffer_use = use;
-       usage->buffer_capacity = capacity;
-       usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
-       usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
-end:
-       return &usage->parent;
-}
-
-/*
- * Get the sampled buffer usage which caused the associated condition to
- * evaluate to "true".
- */
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage_ratio(
-               const struct lttng_evaluation *evaluation, double *usage_ratio)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       *usage_ratio = (double) usage->buffer_use /
-                       (double) usage->buffer_capacity;
-end:
-       return status;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_buffer_usage_get_usage(
-               const struct lttng_evaluation *evaluation,
-               uint64_t *usage_bytes)
-{
-       struct lttng_evaluation_buffer_usage *usage;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
-                       parent);
-       *usage_bytes = usage->buffer_use;
-end:
-       return status;
-}
diff --git a/src/common/conditions/buffer-usage.cpp b/src/common/conditions/buffer-usage.cpp
new file mode 100644 (file)
index 0000000..fa28db6
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <float.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <lttng/condition/condition-internal.h>
+#include <math.h>
+#include <time.h>
+
+#define IS_USAGE_CONDITION(condition) ( \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH   \
+       )
+
+static
+bool is_usage_evaluation(const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW ||
+                       type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH;
+}
+
+static
+void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition)
+{
+       struct lttng_condition_buffer_usage *usage;
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+
+       free(usage->session_name);
+       free(usage->channel_name);
+       free(usage);
+}
+
+static
+bool lttng_condition_buffer_usage_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_buffer_usage *usage;
+
+       if (!condition) {
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->session_name) {
+               ERR("Invalid buffer condition: a target session name must be set.");
+               goto end;
+       }
+       if (!usage->channel_name) {
+               ERR("Invalid buffer condition: a target channel name must be set.");
+               goto end;
+       }
+       if (usage->threshold_ratio.set == usage->threshold_bytes.set) {
+               ERR("Invalid buffer condition: a threshold must be set or both type cannot be used simultaneously.");
+               goto end;
+       }
+       if (!usage->domain.set) {
+               ERR("Invalid buffer usage condition: a domain must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_buffer_usage_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_condition_buffer_usage *usage;
+       size_t session_name_len, channel_name_len;
+       struct lttng_condition_buffer_usage_comm usage_comm = {};
+
+       if (!condition || !IS_USAGE_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing buffer usage condition");
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+
+       session_name_len = strlen(usage->session_name) + 1;
+       channel_name_len = strlen(usage->channel_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX ||
+                       channel_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set;
+       usage_comm.session_name_len = session_name_len;
+       usage_comm.channel_name_len = channel_name_len;
+       usage_comm.domain_type = (int8_t) usage->domain.type;
+
+       if (usage->threshold_bytes.set) {
+               usage_comm.threshold_bytes = usage->threshold_bytes.value;
+       } else {
+               usage_comm.threshold_ratio = usage->threshold_ratio.value;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &usage_comm,
+                       sizeof(usage_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, usage->channel_name,
+                       channel_name_len);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_buffer_usage *a, *b;
+
+       a = container_of(_a, struct lttng_condition_buffer_usage, parent);
+       b = container_of(_b, struct lttng_condition_buffer_usage, parent);
+
+       if ((a->threshold_ratio.set && !b->threshold_ratio.set) ||
+                       (a->threshold_bytes.set && !b->threshold_bytes.set)) {
+               goto end;
+       }
+
+       if (a->threshold_ratio.set && b->threshold_ratio.set) {
+               double a_value, b_value, diff;
+
+               a_value = a->threshold_ratio.value;
+               b_value = b->threshold_ratio.value;
+               diff = fabs(a_value - b_value);
+
+               if (diff > DBL_EPSILON) {
+                       goto end;
+               }
+       } else if (a->threshold_bytes.set && b->threshold_bytes.set) {
+               uint64_t a_value, b_value;
+
+               a_value = a->threshold_bytes.value;
+               b_value = b->threshold_bytes.value;
+               if (a_value != b_value) {
+                       goto end;
+               }
+       }
+
+       /* Condition is not valid if this is not true. */
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->channel_name);
+       LTTNG_ASSERT(b->channel_name);
+       if (strcmp(a->channel_name, b->channel_name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->domain.set);
+       LTTNG_ASSERT(b->domain.set);
+       if (a->domain.type != b->domain.type) {
+               goto end;
+       }
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code lttng_condition_buffer_usage_mi_serialize(
+               const struct lttng_condition *condition,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_condition_status status;
+       const char *session_name = NULL, *channel_name = NULL;
+       enum lttng_domain_type domain_type;
+       bool is_threshold_bytes = false;
+       double threshold_ratio;
+       uint64_t threshold_bytes;
+       const char *condition_type_str = NULL;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(IS_USAGE_CONDITION(condition));
+
+       status = lttng_condition_buffer_usage_get_session_name(
+                       condition, &session_name);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       LTTNG_ASSERT(session_name);
+
+       status = lttng_condition_buffer_usage_get_channel_name(
+                       condition, &channel_name);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       LTTNG_ASSERT(session_name);
+
+       status = lttng_condition_buffer_usage_get_domain_type(
+                       condition, &domain_type);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+
+       status = lttng_condition_buffer_usage_get_threshold(
+                       condition, &threshold_bytes);
+       if (status == LTTNG_CONDITION_STATUS_OK) {
+               is_threshold_bytes = true;
+       } else if (status != LTTNG_CONDITION_STATUS_UNSET) {
+               /* Unexpected at this stage. */
+               ret_code = LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (!is_threshold_bytes) {
+               status = lttng_condition_buffer_usage_get_threshold_ratio(
+                               condition, &threshold_ratio);
+               LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       }
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               condition_type_str =
+                               mi_lttng_element_condition_buffer_usage_high;
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               condition_type_str =
+                               mi_lttng_element_condition_buffer_usage_low;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       /* Open the sub type condition element. */
+       ret = mi_lttng_writer_open_element(writer, condition_type_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Channel name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_condition_channel_name, channel_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Domain. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_domain,
+                       mi_lttng_domaintype_string(domain_type));
+       if (ret) {
+               goto mi_error;
+       }
+
+       if (is_threshold_bytes) {
+               /* Usage in bytes. */
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               mi_lttng_element_condition_threshold_bytes,
+                               threshold_bytes);
+               if (ret) {
+                       goto mi_error;
+               }
+       } else {
+               /* Ratio. */
+               ret = mi_lttng_writer_write_element_double(writer,
+                               mi_lttng_element_condition_threshold_ratio,
+                               threshold_ratio);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Closing sub type condition element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+struct lttng_condition *lttng_condition_buffer_usage_create(
+               enum lttng_condition_type type)
+{
+       struct lttng_condition_buffer_usage *condition;
+
+       condition = (lttng_condition_buffer_usage *) zmalloc(sizeof(struct lttng_condition_buffer_usage));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent, type);
+       condition->parent.validate = lttng_condition_buffer_usage_validate;
+       condition->parent.serialize = lttng_condition_buffer_usage_serialize;
+       condition->parent.equal = lttng_condition_buffer_usage_is_equal;
+       condition->parent.destroy = lttng_condition_buffer_usage_destroy;
+       condition->parent.mi_serialize = lttng_condition_buffer_usage_mi_serialize;
+       return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_low_create(void)
+{
+       return lttng_condition_buffer_usage_create(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW);
+}
+
+struct lttng_condition *lttng_condition_buffer_usage_high_create(void)
+{
+       return lttng_condition_buffer_usage_create(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH);
+}
+
+static
+ssize_t init_condition_from_payload(struct lttng_condition *condition,
+               struct lttng_payload_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       enum lttng_domain_type domain_type;
+       const char *session_name, *channel_name;
+       struct lttng_buffer_view names_view;
+       const struct lttng_condition_buffer_usage_comm *condition_comm;
+       const struct lttng_payload_view condition_comm_view =
+                       lttng_payload_view_from_view(
+                                       src_view, 0, sizeof(*condition_comm));
+
+       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
+       names_view = lttng_buffer_view_from_view(&src_view->buffer,
+                       sizeof(*condition_comm), -1);
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX ||
+                       condition_comm->channel_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       if (names_view.size <
+                       (condition_comm->session_name_len +
+                       condition_comm->channel_name_len)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+               ret = -1;
+               goto end;
+       }
+
+       if (condition_comm->threshold_set_in_bytes) {
+               status = lttng_condition_buffer_usage_set_threshold(condition,
+                               condition_comm->threshold_bytes);
+       } else {
+               status = lttng_condition_buffer_usage_set_threshold_ratio(
+                               condition, condition_comm->threshold_ratio);
+       }
+
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to initialize buffer usage condition threshold");
+               ret = -1;
+               goto end;
+       }
+
+       if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE ||
+                       condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) {
+               /* Invalid domain value. */
+               ERR("Invalid domain type value (%i) found in condition buffer",
+                               (int) condition_comm->domain_type);
+               ret = -1;
+               goto end;
+       }
+
+       domain_type = (enum lttng_domain_type) condition_comm->domain_type;
+       status = lttng_condition_buffer_usage_set_domain_type(condition,
+                       domain_type);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage condition domain");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = names_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       channel_name = session_name + condition_comm->session_name_len;
+       if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') {
+               ERR("Malformed channel name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_buffer_usage_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage session name");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_buffer_usage_set_channel_name(condition,
+                       channel_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer usage channel name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len +
+                       (ssize_t) condition_comm->channel_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+ssize_t lttng_condition_buffer_usage_low_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_buffer_usage_low_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_payload(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+ssize_t lttng_condition_buffer_usage_high_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_buffer_usage_high_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_payload(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_view *view)
+{
+       const struct lttng_evaluation_buffer_usage_comm *comm =
+                       (typeof(comm)) view->buffer.data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->buffer.size < sizeof(*comm)) {
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_buffer_usage_create(type,
+                       comm->buffer_use, comm->buffer_capacity);
+end:
+       return evaluation;
+}
+
+ssize_t lttng_evaluation_buffer_usage_low_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+ssize_t lttng_evaluation_buffer_usage_high_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(
+                       LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_buffer_usage_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold_ratio(
+               const struct lttng_condition *condition,
+               double *threshold_ratio)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       !threshold_ratio) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->threshold_ratio.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *threshold_ratio = usage->threshold_ratio.value;
+end:
+       return status;
+}
+
+/* threshold_ratio expressed as [0.0, 1.0]. */
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold_ratio(
+               struct lttng_condition *condition, double threshold_ratio)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       threshold_ratio < 0.0 ||
+                       threshold_ratio > 1.0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->threshold_ratio.set = true;
+       usage->threshold_bytes.set = false;
+       usage->threshold_ratio.value = threshold_ratio;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_threshold(
+               const struct lttng_condition *condition,
+               uint64_t *threshold_bytes)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->threshold_bytes.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *threshold_bytes = usage->threshold_bytes.value;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_threshold(
+               struct lttng_condition *condition, uint64_t threshold_bytes)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->threshold_ratio.set = false;
+       usage->threshold_bytes.set = true;
+       usage->threshold_bytes.value = threshold_bytes;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = usage->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !session_name ||
+                       strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (usage->session_name) {
+               free(usage->session_name);
+       }
+       usage->session_name = session_name_copy;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_channel_name(
+               const struct lttng_condition *condition,
+               const char **channel_name)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->channel_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *channel_name = usage->channel_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_channel_name(
+               struct lttng_condition *condition, const char *channel_name)
+{
+       char *channel_name_copy;
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name ||
+                       strlen(channel_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       channel_name_copy = strdup(channel_name);
+       if (!channel_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (usage->channel_name) {
+               free(usage->channel_name);
+       }
+       usage->channel_name = channel_name_copy;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_get_domain_type(
+               const struct lttng_condition *condition,
+               enum lttng_domain_type *type)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) || !type) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       if (!usage->domain.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *type = usage->domain.type;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_buffer_usage_set_domain_type(
+               struct lttng_condition *condition, enum lttng_domain_type type)
+{
+       struct lttng_condition_buffer_usage *usage;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_USAGE_CONDITION(condition) ||
+                       type == LTTNG_DOMAIN_NONE) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(condition, struct lttng_condition_buffer_usage,
+                       parent);
+       usage->domain.set = true;
+       usage->domain.type = type;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_buffer_usage_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       struct lttng_evaluation_buffer_usage_comm comm;
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       comm.buffer_use = usage->buffer_use;
+       comm.buffer_capacity = usage->buffer_capacity;
+
+       return lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_buffer_usage_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       free(usage);
+}
+
+struct lttng_evaluation *lttng_evaluation_buffer_usage_create(
+               enum lttng_condition_type type, uint64_t use, uint64_t capacity)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+
+       usage = (lttng_evaluation_buffer_usage *) zmalloc(sizeof(struct lttng_evaluation_buffer_usage));
+       if (!usage) {
+               goto end;
+       }
+
+       usage->parent.type = type;
+       usage->buffer_use = use;
+       usage->buffer_capacity = capacity;
+       usage->parent.serialize = lttng_evaluation_buffer_usage_serialize;
+       usage->parent.destroy = lttng_evaluation_buffer_usage_destroy;
+end:
+       return &usage->parent;
+}
+
+/*
+ * Get the sampled buffer usage which caused the associated condition to
+ * evaluate to "true".
+ */
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage_ratio(
+               const struct lttng_evaluation *evaluation, double *usage_ratio)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       *usage_ratio = (double) usage->buffer_use /
+                       (double) usage->buffer_capacity;
+end:
+       return status;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_buffer_usage_get_usage(
+               const struct lttng_evaluation *evaluation,
+               uint64_t *usage_bytes)
+{
+       struct lttng_evaluation_buffer_usage *usage;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       usage = container_of(evaluation, struct lttng_evaluation_buffer_usage,
+                       parent);
+       *usage_bytes = usage->buffer_use;
+end:
+       return status;
+}
diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c
deleted file mode 100644 (file)
index 18b756d..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/buffer-view.h>
-#include <common/dynamic-buffer.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/event-rule-matches-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/error-query-internal.h>
-#include <stdbool.h>
-
-enum lttng_condition_type lttng_condition_get_type(
-               const struct lttng_condition *condition)
-{
-       return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
-}
-
-void lttng_condition_destroy(struct lttng_condition *condition)
-{
-       lttng_condition_put(condition);
-}
-
-static void condition_destroy_ref(struct urcu_ref *ref)
-{
-       struct lttng_condition *condition =
-               container_of(ref, struct lttng_condition, ref);
-
-       condition->destroy(condition);
-}
-
-void lttng_condition_get(struct lttng_condition *condition)
-{
-       urcu_ref_get(&condition->ref);
-}
-
-void lttng_condition_put(struct lttng_condition *condition)
-{
-       if (!condition) {
-               return;
-       }
-
-       LTTNG_ASSERT(condition->destroy);
-       urcu_ref_put(&condition->ref, condition_destroy_ref);
-}
-
-
-bool lttng_condition_validate(const struct lttng_condition *condition)
-{
-       bool valid;
-
-       if (!condition) {
-               valid = false;
-               goto end;
-       }
-
-       if (!condition->validate) {
-               /* Sub-class guarantees that it can never be invalid. */
-               valid = true;
-               goto end;
-       }
-
-       valid = condition->validate(condition);
-end:
-       return valid;
-}
-
-int lttng_condition_serialize(const struct lttng_condition *condition,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_condition_comm condition_comm = {};
-
-       if (!condition) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm.condition_type = (int8_t) condition->type;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm,
-                       sizeof(condition_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = condition->serialize(condition, payload);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-bool lttng_condition_is_equal(const struct lttng_condition *a,
-               const struct lttng_condition *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       is_equal = a->equal ? a->equal(a, b) : true;
-end:
-       return is_equal;
-}
-
-ssize_t lttng_condition_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       ssize_t ret, condition_size = 0;
-       condition_create_from_payload_cb create_from_payload = NULL;
-       const struct lttng_condition_comm *condition_comm;
-       const struct lttng_payload_view condition_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*condition_comm));
-
-       if (!view || !condition) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Deserializing condition from buffer");
-       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
-       condition_size += sizeof(*condition_comm);
-
-       switch ((enum lttng_condition_type) condition_comm->condition_type) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               create_from_payload = lttng_condition_buffer_usage_low_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               create_from_payload = lttng_condition_buffer_usage_high_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               create_from_payload = lttng_condition_session_consumed_size_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               create_from_payload = lttng_condition_session_rotation_completed_create_from_payload;
-               break;
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-               create_from_payload =
-                               lttng_condition_event_rule_matches_create_from_payload;
-               break;
-       default:
-               ERR("Attempted to create condition of unknown type (%i)",
-                               (int) condition_comm->condition_type);
-               ret = -1;
-               goto end;
-       }
-
-       if (create_from_payload) {
-               struct lttng_payload_view condition_view =
-                               lttng_payload_view_from_view(view,
-                                       sizeof(*condition_comm), -1);
-
-               ret = create_from_payload(&condition_view, condition);
-               if (ret < 0) {
-                       goto end;
-               }
-               condition_size += ret;
-
-       } else {
-               abort();
-       }
-
-       ret = condition_size;
-end:
-       return ret;
-}
-
-void lttng_condition_init(struct lttng_condition *condition,
-               enum lttng_condition_type type)
-{
-       condition->type = type;
-       urcu_ref_init(&condition->ref);
-}
-
-const char *lttng_condition_type_str(enum lttng_condition_type type)
-{
-       switch (type) {
-       case LTTNG_CONDITION_TYPE_UNKNOWN:
-               return "unknown";
-
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               return "session consumed size";
-
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               return "buffer usage high";
-
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               return "buffer usage low";
-
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               return "session rotation ongoing";
-
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               return "session rotation completed";
-
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-               return "event rule matches";
-
-       default:
-               return "???";
-       }
-}
-
-enum lttng_error_code lttng_condition_mi_serialize(
-               const struct lttng_trigger *trigger,
-               const struct lttng_condition *condition,
-               struct mi_writer *writer,
-               const struct mi_lttng_error_query_callbacks *error_query_callbacks)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_error_query_results *error_query_results = NULL;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(condition->mi_serialize);
-
-       /* Open condition element. */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_condition);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize underlying condition. */
-       ret_code = condition->mi_serialize(condition, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Serialize error query results for the action. */
-       if (error_query_callbacks && error_query_callbacks->action_cb) {
-               ret_code = error_query_callbacks->condition_cb(
-                               trigger, &error_query_results);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-
-               ret_code = lttng_error_query_results_mi_serialize(
-                               error_query_results, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close condition element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       lttng_error_query_results_destroy(error_query_results);
-       return ret_code;
-}
diff --git a/src/common/conditions/condition.cpp b/src/common/conditions/condition.cpp
new file mode 100644 (file)
index 0000000..18b756d
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/buffer-view.h>
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-matches-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/error-query-internal.h>
+#include <stdbool.h>
+
+enum lttng_condition_type lttng_condition_get_type(
+               const struct lttng_condition *condition)
+{
+       return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN;
+}
+
+void lttng_condition_destroy(struct lttng_condition *condition)
+{
+       lttng_condition_put(condition);
+}
+
+static void condition_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_condition *condition =
+               container_of(ref, struct lttng_condition, ref);
+
+       condition->destroy(condition);
+}
+
+void lttng_condition_get(struct lttng_condition *condition)
+{
+       urcu_ref_get(&condition->ref);
+}
+
+void lttng_condition_put(struct lttng_condition *condition)
+{
+       if (!condition) {
+               return;
+       }
+
+       LTTNG_ASSERT(condition->destroy);
+       urcu_ref_put(&condition->ref, condition_destroy_ref);
+}
+
+
+bool lttng_condition_validate(const struct lttng_condition *condition)
+{
+       bool valid;
+
+       if (!condition) {
+               valid = false;
+               goto end;
+       }
+
+       if (!condition->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = condition->validate(condition);
+end:
+       return valid;
+}
+
+int lttng_condition_serialize(const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_condition_comm condition_comm = {};
+
+       if (!condition) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm.condition_type = (int8_t) condition->type;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm,
+                       sizeof(condition_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = condition->serialize(condition, payload);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+bool lttng_condition_is_equal(const struct lttng_condition *a,
+               const struct lttng_condition *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+ssize_t lttng_condition_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       ssize_t ret, condition_size = 0;
+       condition_create_from_payload_cb create_from_payload = NULL;
+       const struct lttng_condition_comm *condition_comm;
+       const struct lttng_payload_view condition_comm_view =
+                       lttng_payload_view_from_view(
+                                       view, 0, sizeof(*condition_comm));
+
+       if (!view || !condition) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Deserializing condition from buffer");
+       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
+       condition_size += sizeof(*condition_comm);
+
+       switch ((enum lttng_condition_type) condition_comm->condition_type) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               create_from_payload = lttng_condition_buffer_usage_low_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               create_from_payload = lttng_condition_buffer_usage_high_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               create_from_payload = lttng_condition_session_consumed_size_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               create_from_payload = lttng_condition_session_rotation_completed_create_from_payload;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+               create_from_payload =
+                               lttng_condition_event_rule_matches_create_from_payload;
+               break;
+       default:
+               ERR("Attempted to create condition of unknown type (%i)",
+                               (int) condition_comm->condition_type);
+               ret = -1;
+               goto end;
+       }
+
+       if (create_from_payload) {
+               struct lttng_payload_view condition_view =
+                               lttng_payload_view_from_view(view,
+                                       sizeof(*condition_comm), -1);
+
+               ret = create_from_payload(&condition_view, condition);
+               if (ret < 0) {
+                       goto end;
+               }
+               condition_size += ret;
+
+       } else {
+               abort();
+       }
+
+       ret = condition_size;
+end:
+       return ret;
+}
+
+void lttng_condition_init(struct lttng_condition *condition,
+               enum lttng_condition_type type)
+{
+       condition->type = type;
+       urcu_ref_init(&condition->ref);
+}
+
+const char *lttng_condition_type_str(enum lttng_condition_type type)
+{
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_UNKNOWN:
+               return "unknown";
+
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               return "session consumed size";
+
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               return "buffer usage high";
+
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               return "buffer usage low";
+
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               return "session rotation ongoing";
+
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               return "session rotation completed";
+
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+               return "event rule matches";
+
+       default:
+               return "???";
+       }
+}
+
+enum lttng_error_code lttng_condition_mi_serialize(
+               const struct lttng_trigger *trigger,
+               const struct lttng_condition *condition,
+               struct mi_writer *writer,
+               const struct mi_lttng_error_query_callbacks *error_query_callbacks)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_error_query_results *error_query_results = NULL;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(condition->mi_serialize);
+
+       /* Open condition element. */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_condition);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize underlying condition. */
+       ret_code = condition->mi_serialize(condition, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Serialize error query results for the action. */
+       if (error_query_callbacks && error_query_callbacks->action_cb) {
+               ret_code = error_query_callbacks->condition_cb(
+                               trigger, &error_query_results);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+
+               ret_code = lttng_error_query_results_mi_serialize(
+                               error_query_results, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close condition element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       lttng_error_query_results_destroy(error_query_results);
+       return ret_code;
+}
diff --git a/src/common/conditions/event-rule-matches.c b/src/common/conditions/event-rule-matches.c
deleted file mode 100644 (file)
index d91a2c3..0000000
+++ /dev/null
@@ -1,1544 +0,0 @@
-/*
- * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/event-rule-matches-internal.h>
-#include <lttng/condition/event-rule-matches.h>
-#include <lttng/event-expr-internal.h>
-#include <lttng/event-expr.h>
-#include <lttng/event-field-value-internal.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/lttng-error.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <vendor/msgpack/msgpack.h>
-
-#define IS_EVENT_RULE_MATCHES_CONDITION(condition) \
-       (lttng_condition_get_type(condition) ==    \
-                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES)
-
-static bool is_event_rule_matches_evaluation(
-               const struct lttng_evaluation *evaluation)
-{
-       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
-       return type == LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES;
-}
-
-static bool lttng_condition_event_rule_matches_validate(
-               const struct lttng_condition *condition);
-static int lttng_condition_event_rule_matches_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload);
-static bool lttng_condition_event_rule_matches_is_equal(
-               const struct lttng_condition *_a,
-               const struct lttng_condition *_b);
-static void lttng_condition_event_rule_matches_destroy(
-               struct lttng_condition *condition);
-
-static bool lttng_condition_event_rule_matches_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_event_rule_matches *event_rule;
-
-       if (!condition) {
-               goto end;
-       }
-
-       event_rule = container_of(condition,
-                       struct lttng_condition_event_rule_matches, parent);
-       if (!event_rule->rule) {
-               ERR("Invalid on event condition: a rule must be set");
-               goto end;
-       }
-
-       valid = lttng_event_rule_validate(event_rule->rule);
-end:
-       return valid;
-}
-
-static const char *msgpack_object_type_str(msgpack_object_type type)
-{
-       const char *name;
-
-       switch (type) {
-       case MSGPACK_OBJECT_NIL:
-               name = "MSGPACK_OBJECT_NIL";
-               break;
-       case MSGPACK_OBJECT_BOOLEAN:
-               name = "MSGPACK_OBJECT_BOOLEAN";
-               break;
-       case MSGPACK_OBJECT_POSITIVE_INTEGER:
-               name = "MSGPACK_OBJECT_POSITIVE_INTEGER";
-               break;
-       case MSGPACK_OBJECT_NEGATIVE_INTEGER:
-               name = "MSGPACK_OBJECT_NEGATIVE_INTEGER";
-               break;
-       case MSGPACK_OBJECT_FLOAT32:
-               name = "MSGPACK_OBJECT_FLOAT32";
-               break;
-       case MSGPACK_OBJECT_FLOAT:
-               /* Same value as MSGPACK_OBJECT_FLOAT64 */
-               name = "MSGPACK_OBJECT_FLOAT(64)";
-               break;
-       case MSGPACK_OBJECT_STR:
-               name = "MSGPACK_OBJECT_STR";
-               break;
-       case MSGPACK_OBJECT_ARRAY:
-               name = "MSGPACK_OBJECT_ARRAY";
-               break;
-       case MSGPACK_OBJECT_MAP:
-               name = "MSGPACK_OBJECT_MAP";
-               break;
-       case MSGPACK_OBJECT_BIN:
-               name = "MSGPACK_OBJECT_BIN";
-               break;
-       case MSGPACK_OBJECT_EXT:
-               name = "MSGPACK_OBJECT_EXT";
-               break;
-       default:
-               abort();
-       }
-
-       return name;
-}
-
-/*
- * Serializes the C string `str` into `buf`.
- *
- * Encoding is the length of `str` plus one (for the null character),
- * and then the string, including its null terminator.
- */
-static
-int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf)
-{
-       int ret;
-       const uint32_t len = strlen(str) + 1;
-
-       /* Serialize the length, including the null terminator. */
-       DBG("Serializing C string's length (including null terminator): "
-                       "%" PRIu32, len);
-       ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len));
-       if (ret) {
-               goto end;
-       }
-
-       /* Serialize the string. */
-       DBG("Serializing C string: '%s'", str);
-       ret = lttng_dynamic_buffer_append(buf, str, len);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Serializes the event expression `expr` into `buf`.
- */
-static
-int serialize_event_expr(const struct lttng_event_expr *expr,
-               struct lttng_payload *payload)
-{
-       const uint8_t type = expr->type;
-       int ret;
-
-       /* Serialize the expression's type. */
-       DBG("Serializing event expression's type: %d", expr->type);
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type));
-       if (ret) {
-               goto end;
-       }
-
-       /* Serialize the expression */
-       switch (expr->type) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-       {
-               const struct lttng_event_expr_field *field_expr =
-                               container_of(expr,
-                                       const struct lttng_event_expr_field,
-                                       parent);
-
-               /* Serialize the field name. */
-               DBG("Serializing field event expression's field name: '%s'",
-                               field_expr->name);
-               ret = serialize_cstr(field_expr->name, &payload->buffer);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-       {
-               const struct lttng_event_expr_app_specific_context_field *field_expr =
-                               container_of(expr,
-                                       const struct lttng_event_expr_app_specific_context_field,
-                                       parent);
-
-               /* Serialize the provider name. */
-               DBG("Serializing app-specific context field event expression's "
-                               "provider name: '%s'",
-                               field_expr->provider_name);
-               ret = serialize_cstr(field_expr->provider_name, &payload->buffer);
-               if (ret) {
-                       goto end;
-               }
-
-               /* Serialize the type name. */
-               DBG("Serializing app-specific context field event expression's "
-                               "type name: '%s'",
-                               field_expr->provider_name);
-               ret = serialize_cstr(field_expr->type_name, &payload->buffer);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-       {
-               const struct lttng_event_expr_array_field_element *elem_expr =
-                               container_of(expr,
-                                       const struct lttng_event_expr_array_field_element,
-                                       parent);
-               const uint32_t index = elem_expr->index;
-
-               /* Serialize the index. */
-               DBG("Serializing array field element event expression's "
-                               "index: %u", elem_expr->index);
-               ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index));
-               if (ret) {
-                       goto end;
-               }
-
-               /* Serialize the parent array field expression. */
-               DBG("Serializing array field element event expression's "
-                               "parent array field event expression");
-               ret = serialize_event_expr(elem_expr->array_field_expr, payload);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-static struct lttng_capture_descriptor *
-lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
-               const struct lttng_condition *condition, unsigned int index)
-{
-       const struct lttng_condition_event_rule_matches
-                       *event_rule_matches_cond = container_of(condition,
-                                       const struct lttng_condition_event_rule_matches,
-                                       parent);
-       struct lttng_capture_descriptor *desc = NULL;
-       unsigned int count;
-       enum lttng_condition_status status;
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
-               goto end;
-       }
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition, &count);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto end;
-       }
-
-       if (index >= count) {
-               goto end;
-       }
-
-       desc = lttng_dynamic_pointer_array_get_pointer(
-                       &event_rule_matches_cond->capture_descriptors, index);
-end:
-       return desc;
-}
-
-static int lttng_condition_event_rule_matches_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_condition_event_rule_matches *event_rule_matches_condition;
-       enum lttng_condition_status status;
-       /* Used for iteration and communication (size matters). */
-       uint32_t i, capture_descr_count;
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing on event condition");
-       event_rule_matches_condition = container_of(condition,
-                       struct lttng_condition_event_rule_matches, parent);
-
-       DBG("Serializing on event condition's event rule");
-       ret = lttng_event_rule_serialize(
-                       event_rule_matches_condition->rule, payload);
-       if (ret) {
-               goto end;
-       }
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition, &capture_descr_count);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ret = -1;
-               goto end;
-       };
-
-       DBG("Serializing on event condition's capture descriptor count: %" PRIu32,
-                       capture_descr_count);
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count,
-                       sizeof(capture_descr_count));
-       if (ret) {
-               goto end;
-       }
-
-       for (i = 0; i < capture_descr_count; i++) {
-               const struct lttng_capture_descriptor *desc =
-                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
-                                               condition, i);
-
-               DBG("Serializing on event condition's capture descriptor %" PRIu32,
-                               i);
-               ret = serialize_event_expr(desc->event_expression, payload);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static
-bool capture_descriptors_are_equal(
-               const struct lttng_condition *condition_a,
-               const struct lttng_condition *condition_b)
-{
-       bool is_equal = true;
-       unsigned int capture_descr_count_a;
-       unsigned int capture_descr_count_b;
-       size_t i;
-       enum lttng_condition_status status;
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition_a, &capture_descr_count_a);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto not_equal;
-       }
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition_b, &capture_descr_count_b);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto not_equal;
-       }
-
-       if (capture_descr_count_a != capture_descr_count_b) {
-               goto not_equal;
-       }
-
-       for (i = 0; i < capture_descr_count_a; i++) {
-               const struct lttng_event_expr *expr_a =
-                               lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
-                                               condition_a, i);
-               const struct lttng_event_expr *expr_b =
-                               lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
-                                               condition_b, i);
-
-               if (!lttng_event_expr_is_equal(expr_a, expr_b)) {
-                       goto not_equal;
-               }
-       }
-
-       goto end;
-
-not_equal:
-       is_equal = false;
-
-end:
-       return is_equal;
-}
-
-static bool lttng_condition_event_rule_matches_is_equal(
-               const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_event_rule_matches *a, *b;
-
-       a = container_of(_a, struct lttng_condition_event_rule_matches, parent);
-       b = container_of(_b, struct lttng_condition_event_rule_matches, parent);
-
-       /* Both event rules must be set or both must be unset. */
-       if ((a->rule && !b->rule) || (!a->rule && b->rule)) {
-               WARN("Comparing event_rule conditions with uninitialized rule");
-               goto end;
-       }
-
-       is_equal = lttng_event_rule_is_equal(a->rule, b->rule);
-       if (!is_equal) {
-               goto end;
-       }
-
-       is_equal = capture_descriptors_are_equal(_a, _b);
-
-end:
-       return is_equal;
-}
-
-static void lttng_condition_event_rule_matches_destroy(
-               struct lttng_condition *condition)
-{
-       struct lttng_condition_event_rule_matches *event_rule_matches_condition;
-
-       event_rule_matches_condition = container_of(condition,
-                       struct lttng_condition_event_rule_matches, parent);
-
-       lttng_event_rule_put(event_rule_matches_condition->rule);
-       lttng_dynamic_pointer_array_reset(
-                       &event_rule_matches_condition->capture_descriptors);
-       free(event_rule_matches_condition);
-}
-
-static
-void destroy_capture_descriptor(void *ptr)
-{
-       struct lttng_capture_descriptor *desc =
-                       (struct lttng_capture_descriptor *) ptr;
-
-       lttng_event_expr_destroy(desc->event_expression);
-       free(desc->bytecode);
-       free(desc);
-}
-
-static enum lttng_error_code lttng_condition_event_rule_matches_mi_serialize(
-               const struct lttng_condition *condition,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_condition_status status;
-       const struct lttng_event_rule *rule = NULL;
-       unsigned int capture_descriptor_count, i;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_EVENT_RULE_MATCHES_CONDITION(condition));
-
-       status = lttng_condition_event_rule_matches_get_rule(condition, &rule);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       LTTNG_ASSERT(rule != NULL);
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition, &capture_descriptor_count);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-
-       /* Open condition event rule matches element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_condition_event_rule_matches);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize the event rule. */
-       ret_code = lttng_event_rule_mi_serialize(rule, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Open the capture descriptors element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_capture_descriptors);
-       if (ret) {
-               goto mi_error;
-       }
-
-       for (i = 0; i < capture_descriptor_count; i++) {
-               const struct lttng_event_expr *descriptor = NULL;
-
-               descriptor = lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
-                               condition, i);
-               LTTNG_ASSERT(descriptor);
-
-               ret_code = lttng_event_expr_mi_serialize(descriptor, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close capture descriptors element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close condition_event_rule_matches. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_condition *lttng_condition_event_rule_matches_create(
-               struct lttng_event_rule *rule)
-{
-       struct lttng_condition *parent = NULL;
-       struct lttng_condition_event_rule_matches *condition = NULL;
-
-       if (!rule) {
-               goto end;
-       }
-
-       condition = zmalloc(sizeof(struct lttng_condition_event_rule_matches));
-       if (!condition) {
-               return NULL;
-       }
-
-       lttng_condition_init(&condition->parent,
-                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
-       condition->parent.validate =
-                       lttng_condition_event_rule_matches_validate,
-       condition->parent.serialize =
-                       lttng_condition_event_rule_matches_serialize,
-       condition->parent.equal = lttng_condition_event_rule_matches_is_equal,
-       condition->parent.destroy = lttng_condition_event_rule_matches_destroy,
-       condition->parent.mi_serialize = lttng_condition_event_rule_matches_mi_serialize,
-
-       lttng_event_rule_get(rule);
-       condition->rule = rule;
-       rule = NULL;
-
-       lttng_dynamic_pointer_array_init(&condition->capture_descriptors,
-                       destroy_capture_descriptor);
-
-       parent = &condition->parent;
-end:
-       return parent;
-}
-
-static
-uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size,
-               size_t *offset)
-{
-       uint64_t ret;
-       const struct lttng_buffer_view uint_view =
-                       lttng_buffer_view_from_view(view, *offset, size);
-
-       if (!lttng_buffer_view_is_valid(&uint_view)) {
-               ret = UINT64_C(-1);
-               goto end;
-       }
-
-       switch (size) {
-       case 1:
-               ret = (uint64_t) *uint_view.data;
-               break;
-       case sizeof(uint32_t):
-       {
-               uint32_t u32;
-
-               memcpy(&u32, uint_view.data, sizeof(u32));
-               ret = (uint64_t) u32;
-               break;
-       }
-       case sizeof(ret):
-               memcpy(&ret, uint_view.data, sizeof(ret));
-               break;
-       default:
-               abort();
-       }
-
-       *offset += size;
-
-end:
-       return ret;
-}
-
-static
-const char *str_from_buffer(const struct lttng_buffer_view *view,
-               size_t *offset)
-{
-       uint64_t len;
-       const char *ret;
-
-       len = uint_from_buffer(view, sizeof(uint32_t), offset);
-       if (len == UINT64_C(-1)) {
-               goto error;
-       }
-
-       ret = &view->data[*offset];
-
-       if (!lttng_buffer_view_contains_string(view, ret, len)) {
-               goto error;
-       }
-
-       *offset += len;
-       goto end;
-
-error:
-       ret = NULL;
-
-end:
-       return ret;
-}
-
-static
-struct lttng_event_expr *event_expr_from_payload(
-               struct lttng_payload_view *view, size_t *offset)
-{
-       struct lttng_event_expr *expr = NULL;
-       const char *str;
-       uint64_t type;
-
-       type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset);
-       if (type == UINT64_C(-1)) {
-               goto error;
-       }
-
-       switch (type) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-               str = str_from_buffer(&view->buffer, offset);
-               if (!str) {
-                       goto error;
-               }
-
-               expr = lttng_event_expr_event_payload_field_create(str);
-               break;
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-               str = str_from_buffer(&view->buffer, offset);
-               if (!str) {
-                       goto error;
-               }
-
-               expr = lttng_event_expr_channel_context_field_create(str);
-               break;
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-       {
-               const char *provider_name;
-               const char *type_name;
-
-               provider_name = str_from_buffer(&view->buffer, offset);
-               if (!provider_name) {
-                       goto error;
-               }
-
-               type_name = str_from_buffer(&view->buffer, offset);
-               if (!type_name) {
-                       goto error;
-               }
-
-               expr = lttng_event_expr_app_specific_context_field_create(
-                               provider_name, type_name);
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-       {
-               struct lttng_event_expr *array_field_expr;
-               const uint64_t index = uint_from_buffer(
-                               &view->buffer, sizeof(uint32_t), offset);
-
-               if (index == UINT64_C(-1)) {
-                       goto error;
-               }
-
-               /* Array field expression is the encoded after this. */
-               array_field_expr = event_expr_from_payload(view, offset);
-               if (!array_field_expr) {
-                       goto error;
-               }
-
-               /* Move ownership of `array_field_expr` to new expression. */
-               expr = lttng_event_expr_array_field_element_create(
-                               array_field_expr, (unsigned int) index);
-               if (!expr) {
-                       /* `array_field_expr` not moved: destroy it. */
-                       lttng_event_expr_destroy(array_field_expr);
-               }
-
-               break;
-       }
-       default:
-               ERR("Invalid event expression type encoutered while deserializing event expression: type = %" PRIu64,
-                               type);
-               goto error;
-       }
-
-       goto end;
-
-error:
-       lttng_event_expr_destroy(expr);
-       expr = NULL;
-
-end:
-       return expr;
-}
-
-ssize_t lttng_condition_event_rule_matches_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t consumed_length;
-       size_t offset = 0;
-       ssize_t event_rule_length;
-       uint32_t i, capture_descr_count;
-       struct lttng_condition *condition = NULL;
-       struct lttng_event_rule *event_rule = NULL;
-
-       if (!view || !_condition) {
-               goto error;
-       }
-
-       /* Struct lttng_event_rule. */
-       {
-               struct lttng_payload_view event_rule_view =
-                               lttng_payload_view_from_view(view, offset, -1);
-
-               event_rule_length = lttng_event_rule_create_from_payload(
-                               &event_rule_view, &event_rule);
-       }
-
-       if (event_rule_length < 0 || !event_rule) {
-               goto error;
-       }
-
-       offset += event_rule_length;
-
-       /* Create condition (no capture descriptors yet) at this point */
-       condition = lttng_condition_event_rule_matches_create(event_rule);
-       if (!condition) {
-               goto error;
-       }
-
-       /* Capture descriptor count. */
-       LTTNG_ASSERT(event_rule_length >= 0);
-       capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset);
-       if (capture_descr_count == UINT32_C(-1)) {
-               goto error;
-       }
-
-       /* Capture descriptors. */
-       for (i = 0; i < capture_descr_count; i++) {
-               enum lttng_condition_status status;
-               struct lttng_event_expr *expr = event_expr_from_payload(
-                               view, &offset);
-
-               if (!expr) {
-                       goto error;
-               }
-
-               /* Move ownership of `expr` to `condition`. */
-               status = lttng_condition_event_rule_matches_append_capture_descriptor(
-                               condition, expr);
-               if (status != LTTNG_CONDITION_STATUS_OK) {
-                       /* `expr` not moved: destroy it. */
-                       lttng_event_expr_destroy(expr);
-                       goto error;
-               }
-       }
-
-       consumed_length = (ssize_t) offset;
-       *_condition = condition;
-       condition = NULL;
-       goto end;
-
-error:
-       consumed_length = -1;
-
-end:
-       lttng_event_rule_put(event_rule);
-       lttng_condition_put(condition);
-       return consumed_length;
-}
-
-enum lttng_condition_status
-lttng_condition_event_rule_matches_borrow_rule_mutable(
-               const struct lttng_condition *condition,
-               struct lttng_event_rule **rule)
-{
-       struct lttng_condition_event_rule_matches *event_rule;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
-                       !rule) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       event_rule = container_of(condition,
-                       struct lttng_condition_event_rule_matches, parent);
-       if (!event_rule->rule) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-
-       *rule = event_rule->rule;
-end:
-       return status;
-}
-
-enum lttng_condition_status lttng_condition_event_rule_matches_get_rule(
-               const struct lttng_condition *condition,
-               const struct lttng_event_rule **rule)
-{
-       struct lttng_event_rule *mutable_rule = NULL;
-       const enum lttng_condition_status status =
-                       lttng_condition_event_rule_matches_borrow_rule_mutable(
-                                       condition, &mutable_rule);
-
-       *rule = mutable_rule;
-       return status;
-}
-
-void lttng_condition_event_rule_matches_set_error_counter_index(
-               struct lttng_condition *condition, uint64_t error_counter_index)
-{
-       struct lttng_condition_event_rule_matches *event_rule_matches_cond =
-                       container_of(condition,
-                                       struct lttng_condition_event_rule_matches,
-                                       parent);
-
-       LTTNG_OPTIONAL_SET(&event_rule_matches_cond->error_counter_index,
-                       error_counter_index);
-}
-
-uint64_t lttng_condition_event_rule_matches_get_error_counter_index(
-               const struct lttng_condition *condition)
-{
-       const struct lttng_condition_event_rule_matches
-                       *event_rule_matches_cond = container_of(condition,
-                                       const struct lttng_condition_event_rule_matches,
-                                       parent);
-
-       return LTTNG_OPTIONAL_GET(event_rule_matches_cond->error_counter_index);
-}
-
-enum lttng_condition_status
-lttng_condition_event_rule_matches_append_capture_descriptor(
-               struct lttng_condition *condition,
-               struct lttng_event_expr *expr)
-{
-       int ret;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-       struct lttng_condition_event_rule_matches *event_rule_matches_cond =
-                       container_of(condition,
-                                       struct lttng_condition_event_rule_matches,
-                                       parent);
-       struct lttng_capture_descriptor *descriptor = NULL;
-       const struct lttng_event_rule *rule = NULL;
-
-       /* Only accept l-values. */
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
-                       !expr || !lttng_event_expr_is_lvalue(expr)) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       status = lttng_condition_event_rule_matches_get_rule(condition, &rule);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto end;
-       }
-
-       switch(lttng_event_rule_get_type(rule)) {
-       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
-       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
-       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
-       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
-               /* Supported. */
-               status = LTTNG_CONDITION_STATUS_OK;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               break;
-       default:
-               status = LTTNG_CONDITION_STATUS_UNSUPPORTED;
-               break;
-       }
-
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto end;
-       }
-
-       descriptor = malloc(sizeof(*descriptor));
-       if (descriptor == NULL) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       descriptor->event_expression = expr;
-       descriptor->bytecode = NULL;
-
-       ret = lttng_dynamic_pointer_array_add_pointer(
-                       &event_rule_matches_cond->capture_descriptors,
-                       descriptor);
-       if (ret) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Ownership is transfered to the internal capture_descriptors array */
-       descriptor = NULL;
-end:
-       free(descriptor);
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_event_rule_matches_get_capture_descriptor_count(
-               const struct lttng_condition *condition, unsigned int *count)
-{
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-       const struct lttng_condition_event_rule_matches
-                       *event_rule_matches_condition = container_of(condition,
-                                       const struct lttng_condition_event_rule_matches,
-                                       parent);
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
-                       !count) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       *count = lttng_dynamic_pointer_array_get_count(
-                       &event_rule_matches_condition->capture_descriptors);
-
-end:
-       return status;
-}
-
-const struct lttng_event_expr *
-lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
-               const struct lttng_condition *condition, unsigned int index)
-{
-       const struct lttng_event_expr *expr = NULL;
-       const struct lttng_capture_descriptor *desc = NULL;
-
-       desc = lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
-                       condition, index);
-       if (desc == NULL) {
-               goto end;
-       }
-       expr = desc->event_expression;
-
-end:
-       return expr;
-}
-
-ssize_t lttng_evaluation_event_rule_matches_create_from_payload(
-               const struct lttng_condition_event_rule_matches *condition,
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret, offset = 0;
-       struct lttng_evaluation *evaluation = NULL;
-       uint32_t capture_payload_size;
-       const char *capture_payload = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       {
-               const struct lttng_payload_view current_view =
-                               lttng_payload_view_from_view(view, offset, -1);
-
-               if (current_view.buffer.size < sizeof(capture_payload_size)) {
-                       ret = -1;
-                       goto error;
-               }
-
-               memcpy(&capture_payload_size, current_view.buffer.data,
-                               sizeof(capture_payload_size));
-       }
-       offset += sizeof(capture_payload_size);
-
-       if (capture_payload_size > 0) {
-               const struct lttng_payload_view current_view =
-                               lttng_payload_view_from_view(view, offset, -1);
-
-               if (current_view.buffer.size < capture_payload_size) {
-                       ret = -1;
-                       goto error;
-               }
-
-               capture_payload = current_view.buffer.data;
-       }
-
-       evaluation = lttng_evaluation_event_rule_matches_create(
-                       condition, capture_payload, capture_payload_size, true);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       offset += capture_payload_size;
-       *_evaluation = evaluation;
-       evaluation = NULL;
-       ret = offset;
-
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-static int lttng_evaluation_event_rule_matches_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload)
-{
-       int ret = 0;
-       struct lttng_evaluation_event_rule_matches *hit;
-       uint32_t capture_payload_size;
-
-       hit = container_of(evaluation,
-                       struct lttng_evaluation_event_rule_matches, parent);
-
-       capture_payload_size = (uint32_t) hit->capture_payload.size;
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size,
-                       sizeof(capture_payload_size));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data,
-                       hit->capture_payload.size);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str)
-{
-       bool is_equal = true;
-
-       LTTNG_ASSERT(obj->type == MSGPACK_OBJECT_STR);
-
-       if (obj->via.str.size != strlen(str)) {
-               is_equal = false;
-               goto end;
-       }
-
-       if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) {
-               is_equal = false;
-               goto end;
-       }
-
-end:
-       return is_equal;
-}
-
-static
-const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj,
-               const char *name)
-{
-       const msgpack_object *ret = NULL;
-       size_t i;
-
-       LTTNG_ASSERT(map_obj->type == MSGPACK_OBJECT_MAP);
-
-       for (i = 0; i < map_obj->via.map.size; i++) {
-               const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i];
-
-               LTTNG_ASSERT(kv->key.type == MSGPACK_OBJECT_STR);
-
-               if (msgpack_str_is_equal(&kv->key, name)) {
-                       ret = &kv->val;
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static void lttng_evaluation_event_rule_matches_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_event_rule_matches *hit;
-
-       hit = container_of(evaluation,
-                       struct lttng_evaluation_event_rule_matches, parent);
-       lttng_dynamic_buffer_reset(&hit->capture_payload);
-       lttng_event_field_value_destroy(hit->captured_values);
-       free(hit);
-}
-
-static
-int event_field_value_from_obj(const msgpack_object *obj,
-               struct lttng_event_field_value **field_val)
-{
-       int ret = 0;
-
-       LTTNG_ASSERT(obj);
-       LTTNG_ASSERT(field_val);
-
-       switch (obj->type) {
-       case MSGPACK_OBJECT_NIL:
-               /* Unavailable. */
-               *field_val = NULL;
-               goto end;
-       case MSGPACK_OBJECT_POSITIVE_INTEGER:
-               *field_val = lttng_event_field_value_uint_create(
-                               obj->via.u64);
-               break;
-       case MSGPACK_OBJECT_NEGATIVE_INTEGER:
-               *field_val = lttng_event_field_value_int_create(
-                               obj->via.i64);
-               break;
-       case MSGPACK_OBJECT_FLOAT32:
-       case MSGPACK_OBJECT_FLOAT64:
-               *field_val = lttng_event_field_value_real_create(
-                               obj->via.f64);
-               break;
-       case MSGPACK_OBJECT_STR:
-               *field_val = lttng_event_field_value_string_create_with_size(
-                               obj->via.str.ptr, obj->via.str.size);
-               break;
-       case MSGPACK_OBJECT_ARRAY:
-       {
-               size_t i;
-
-               *field_val = lttng_event_field_value_array_create();
-               if (!*field_val) {
-                       goto error;
-               }
-
-               for (i = 0; i < obj->via.array.size; i++) {
-                       const msgpack_object *elem_obj = &obj->via.array.ptr[i];
-                       struct lttng_event_field_value *elem_field_val;
-
-                       ret = event_field_value_from_obj(elem_obj,
-                                       &elem_field_val);
-                       if (ret) {
-                               goto error;
-                       }
-
-                       if (elem_field_val) {
-                               ret = lttng_event_field_value_array_append(
-                                               *field_val, elem_field_val);
-                       } else {
-                               ret = lttng_event_field_value_array_append_unavailable(
-                                               *field_val);
-                       }
-
-                       if (ret) {
-                               lttng_event_field_value_destroy(elem_field_val);
-                               goto error;
-                       }
-               }
-
-               break;
-       }
-       case MSGPACK_OBJECT_MAP:
-       {
-               /*
-                * As of this version, the only valid map object is
-                * for an enumeration value, for example:
-                *
-                *     type: enum
-                *     value: 177
-                *     labels:
-                *     - Labatt 50
-                *     - Molson Dry
-                *     - Carling Black Label
-                */
-               const msgpack_object *inner_obj;
-               size_t label_i;
-
-               inner_obj = get_msgpack_map_obj(obj, "type");
-               if (!inner_obj) {
-                       ERR("Missing `type` entry in map object");
-                       goto error;
-               }
-
-               if (inner_obj->type != MSGPACK_OBJECT_STR) {
-                       ERR("Map object's `type` entry is not a string: type = %s",
-                                       msgpack_object_type_str(inner_obj->type));
-                       goto error;
-               }
-
-               if (!msgpack_str_is_equal(inner_obj, "enum")) {
-                       ERR("Map object's `type` entry: expecting `enum`");
-                       goto error;
-               }
-
-               inner_obj = get_msgpack_map_obj(obj, "value");
-               if (!inner_obj) {
-                       ERR("Missing `value` entry in map object");
-                       goto error;
-               }
-
-               if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
-                       *field_val = lttng_event_field_value_enum_uint_create(
-                                       inner_obj->via.u64);
-               } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) {
-                       *field_val = lttng_event_field_value_enum_int_create(
-                                       inner_obj->via.i64);
-               } else {
-                       ERR("Map object's `value` entry is not an integer: type = %s",
-                                       msgpack_object_type_str(inner_obj->type));
-                       goto error;
-               }
-
-               if (!*field_val) {
-                       goto error;
-               }
-
-               inner_obj = get_msgpack_map_obj(obj, "labels");
-               if (!inner_obj) {
-                       /* No labels */
-                       goto end;
-               }
-
-               if (inner_obj->type != MSGPACK_OBJECT_ARRAY) {
-                       ERR("Map object's `labels` entry is not an array: type = %s",
-                                       msgpack_object_type_str(inner_obj->type));
-                       goto error;
-               }
-
-               for (label_i = 0; label_i < inner_obj->via.array.size;
-                               label_i++) {
-                       int iret;
-                       const msgpack_object *elem_obj =
-                                       &inner_obj->via.array.ptr[label_i];
-
-                       if (elem_obj->type != MSGPACK_OBJECT_STR) {
-                               ERR("Map object's `labels` entry's type is not a string: type = %s",
-                                               msgpack_object_type_str(elem_obj->type));
-                               goto error;
-                       }
-
-                       iret = lttng_event_field_value_enum_append_label_with_size(
-                                       *field_val, elem_obj->via.str.ptr,
-                                       elem_obj->via.str.size);
-                       if (iret) {
-                               goto error;
-                       }
-               }
-
-               break;
-       }
-       default:
-               ERR("Unexpected object type: type = %s",
-                               msgpack_object_type_str(obj->type));
-               goto error;
-       }
-
-       if (!*field_val) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(*field_val);
-       *field_val = NULL;
-       ret = -1;
-
-end:
-       return ret;
-}
-
-static struct lttng_event_field_value *event_field_value_from_capture_payload(
-               const struct lttng_condition_event_rule_matches *condition,
-               const char *capture_payload,
-               size_t capture_payload_size)
-{
-       struct lttng_event_field_value *ret = NULL;
-       msgpack_unpacked unpacked;
-       msgpack_unpack_return unpack_return;
-       const msgpack_object *root_obj;
-       const msgpack_object_array *root_array_obj;
-       size_t i;
-       size_t count;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(capture_payload);
-
-       /* Initialize value. */
-       msgpack_unpacked_init(&unpacked);
-
-       /* Decode. */
-       unpack_return = msgpack_unpack_next(&unpacked, capture_payload,
-                       capture_payload_size, NULL);
-       if (unpack_return != MSGPACK_UNPACK_SUCCESS) {
-               ERR("msgpack_unpack_next() failed to decode the "
-                               "MessagePack-encoded capture payload: "
-                               "size = %zu, ret = %d",
-                               capture_payload_size, unpack_return);
-               goto error;
-       }
-
-       /* Get root array. */
-       root_obj = &unpacked.data;
-
-       if (root_obj->type != MSGPACK_OBJECT_ARRAY) {
-               ERR("Expecting an array as the root object: type = %s",
-                               msgpack_object_type_str(root_obj->type));
-               goto error;
-       }
-
-       root_array_obj = &root_obj->via.array;
-
-       /* Create an empty root array event field value. */
-       ret = lttng_event_field_value_array_create();
-       if (!ret) {
-               goto error;
-       }
-
-       /*
-        * For each capture descriptor in the condition object:
-        *
-        * 1. Get its corresponding captured field value MessagePack
-        *    object.
-        *
-        * 2. Create a corresponding event field value.
-        *
-        * 3. Append it to `ret` (the root array event field value).
-        */
-       count = lttng_dynamic_pointer_array_get_count(
-                       &condition->capture_descriptors);
-       LTTNG_ASSERT(count > 0);
-
-       for (i = 0; i < count; i++) {
-               const struct lttng_capture_descriptor *capture_descriptor =
-                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
-                                               &condition->parent, i);
-               const msgpack_object *elem_obj;
-               struct lttng_event_field_value *elem_field_val;
-               int iret;
-
-               LTTNG_ASSERT(capture_descriptor);
-
-               elem_obj = &root_array_obj->ptr[i];
-               iret = event_field_value_from_obj(elem_obj,
-                               &elem_field_val);
-               if (iret) {
-                       goto error;
-               }
-
-               if (elem_field_val) {
-                       iret = lttng_event_field_value_array_append(ret,
-                                       elem_field_val);
-               } else {
-                       iret = lttng_event_field_value_array_append_unavailable(
-                                       ret);
-               }
-
-               if (iret) {
-                       lttng_event_field_value_destroy(elem_field_val);
-                       goto error;
-               }
-       }
-
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(ret);
-       ret = NULL;
-
-end:
-       msgpack_unpacked_destroy(&unpacked);
-       return ret;
-}
-
-struct lttng_evaluation *lttng_evaluation_event_rule_matches_create(
-               const struct lttng_condition_event_rule_matches *condition,
-               const char *capture_payload,
-               size_t capture_payload_size,
-               bool decode_capture_payload)
-{
-       struct lttng_evaluation_event_rule_matches *hit;
-       struct lttng_evaluation *evaluation = NULL;
-
-       hit = zmalloc(sizeof(struct lttng_evaluation_event_rule_matches));
-       if (!hit) {
-               goto error;
-       }
-
-       lttng_dynamic_buffer_init(&hit->capture_payload);
-
-       if (capture_payload) {
-               const int ret = lttng_dynamic_buffer_append(
-                               &hit->capture_payload, capture_payload,
-                               capture_payload_size);
-               if (ret) {
-                       ERR("Failed to initialize capture payload of event rule evaluation");
-                       goto error;
-               }
-
-               if (decode_capture_payload) {
-                       hit->captured_values =
-                                       event_field_value_from_capture_payload(
-                                               condition,
-                                               capture_payload,
-                                               capture_payload_size);
-                       if (!hit->captured_values) {
-                               ERR("Failed to decode the capture payload: size = %zu",
-                                               capture_payload_size);
-                               goto error;
-                       }
-               }
-       }
-
-       hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES;
-       hit->parent.serialize = lttng_evaluation_event_rule_matches_serialize;
-       hit->parent.destroy = lttng_evaluation_event_rule_matches_destroy;
-
-       evaluation = &hit->parent;
-       hit = NULL;
-
-error:
-       if (hit) {
-               lttng_evaluation_event_rule_matches_destroy(&hit->parent);
-       }
-
-       return evaluation;
-}
-
-enum lttng_evaluation_event_rule_matches_status
-lttng_evaluation_event_rule_matches_get_captured_values(
-               const struct lttng_evaluation *evaluation,
-               const struct lttng_event_field_value **field_val)
-{
-       struct lttng_evaluation_event_rule_matches *hit;
-       enum lttng_evaluation_event_rule_matches_status status =
-                       LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK;
-
-       if (!evaluation || !is_event_rule_matches_evaluation(evaluation) ||
-                       !field_val) {
-               status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_INVALID;
-               goto end;
-       }
-
-       hit = container_of(evaluation,
-                       struct lttng_evaluation_event_rule_matches, parent);
-       if (!hit->captured_values) {
-               status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_NONE;
-               goto end;
-       }
-
-       *field_val = hit->captured_values;
-
-end:
-       return status;
-}
-
-enum lttng_error_code
-lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode(
-               struct lttng_condition *condition)
-{
-       enum lttng_error_code ret;
-       enum lttng_condition_status status;
-       unsigned int capture_count, i;
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
-               ret = LTTNG_ERR_FATAL;
-               goto end;
-       }
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition, &capture_count);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ret = LTTNG_ERR_FATAL;
-               goto end;
-       }
-
-       for (i = 0; i < capture_count; i++) {
-               struct lttng_capture_descriptor *local_capture_desc =
-                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
-                                               condition, i);
-
-               if (local_capture_desc == NULL) {
-                       ret = LTTNG_ERR_FATAL;
-                       goto end;
-               }
-
-               /* Generate the bytecode. */
-               status = lttng_event_expr_to_bytecode(
-                               local_capture_desc->event_expression,
-                               &local_capture_desc->bytecode);
-               if (status < 0 || local_capture_desc->bytecode == NULL) {
-                       ret = LTTNG_ERR_INVALID_CAPTURE_EXPRESSION;
-                       goto end;
-               }
-       }
-
-       /* Everything went better than expected */
-       ret = LTTNG_OK;
-
-end:
-       return ret;
-}
-
-const struct lttng_bytecode *
-lttng_condition_event_rule_matches_get_capture_bytecode_at_index(
-               const struct lttng_condition *condition, unsigned int index)
-{
-       const struct lttng_condition_event_rule_matches
-                       *event_rule_matches_cond = container_of(condition,
-                                       const struct lttng_condition_event_rule_matches,
-                                       parent);
-       struct lttng_capture_descriptor *desc = NULL;
-       struct lttng_bytecode *bytecode = NULL;
-       unsigned int count;
-       enum lttng_condition_status status;
-
-       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
-               goto end;
-       }
-
-       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
-                       condition, &count);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               goto end;
-       }
-
-       if (index >= count) {
-               goto end;
-       }
-
-       desc = lttng_dynamic_pointer_array_get_pointer(
-                       &event_rule_matches_cond->capture_descriptors, index);
-       if (desc == NULL) {
-               goto end;
-       }
-
-       bytecode = desc->bytecode;
-end:
-       return bytecode;
-}
diff --git a/src/common/conditions/event-rule-matches.cpp b/src/common/conditions/event-rule-matches.cpp
new file mode 100644 (file)
index 0000000..3e7776f
--- /dev/null
@@ -0,0 +1,1545 @@
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-matches-internal.h>
+#include <lttng/condition/event-rule-matches.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-expr.h>
+#include <lttng/event-field-value-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/lttng-error.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <vendor/msgpack/msgpack.h>
+
+#define IS_EVENT_RULE_MATCHES_CONDITION(condition) \
+       (lttng_condition_get_type(condition) ==    \
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES)
+
+static bool is_event_rule_matches_evaluation(
+               const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES;
+}
+
+static bool lttng_condition_event_rule_matches_validate(
+               const struct lttng_condition *condition);
+static int lttng_condition_event_rule_matches_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload);
+static bool lttng_condition_event_rule_matches_is_equal(
+               const struct lttng_condition *_a,
+               const struct lttng_condition *_b);
+static void lttng_condition_event_rule_matches_destroy(
+               struct lttng_condition *condition);
+
+static bool lttng_condition_event_rule_matches_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_event_rule_matches *event_rule;
+
+       if (!condition) {
+               goto end;
+       }
+
+       event_rule = container_of(condition,
+                       struct lttng_condition_event_rule_matches, parent);
+       if (!event_rule->rule) {
+               ERR("Invalid on event condition: a rule must be set");
+               goto end;
+       }
+
+       valid = lttng_event_rule_validate(event_rule->rule);
+end:
+       return valid;
+}
+
+static const char *msgpack_object_type_str(msgpack_object_type type)
+{
+       const char *name;
+
+       switch (type) {
+       case MSGPACK_OBJECT_NIL:
+               name = "MSGPACK_OBJECT_NIL";
+               break;
+       case MSGPACK_OBJECT_BOOLEAN:
+               name = "MSGPACK_OBJECT_BOOLEAN";
+               break;
+       case MSGPACK_OBJECT_POSITIVE_INTEGER:
+               name = "MSGPACK_OBJECT_POSITIVE_INTEGER";
+               break;
+       case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+               name = "MSGPACK_OBJECT_NEGATIVE_INTEGER";
+               break;
+       case MSGPACK_OBJECT_FLOAT32:
+               name = "MSGPACK_OBJECT_FLOAT32";
+               break;
+       case MSGPACK_OBJECT_FLOAT:
+               /* Same value as MSGPACK_OBJECT_FLOAT64 */
+               name = "MSGPACK_OBJECT_FLOAT(64)";
+               break;
+       case MSGPACK_OBJECT_STR:
+               name = "MSGPACK_OBJECT_STR";
+               break;
+       case MSGPACK_OBJECT_ARRAY:
+               name = "MSGPACK_OBJECT_ARRAY";
+               break;
+       case MSGPACK_OBJECT_MAP:
+               name = "MSGPACK_OBJECT_MAP";
+               break;
+       case MSGPACK_OBJECT_BIN:
+               name = "MSGPACK_OBJECT_BIN";
+               break;
+       case MSGPACK_OBJECT_EXT:
+               name = "MSGPACK_OBJECT_EXT";
+               break;
+       default:
+               abort();
+       }
+
+       return name;
+}
+
+/*
+ * Serializes the C string `str` into `buf`.
+ *
+ * Encoding is the length of `str` plus one (for the null character),
+ * and then the string, including its null terminator.
+ */
+static
+int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf)
+{
+       int ret;
+       const uint32_t len = strlen(str) + 1;
+
+       /* Serialize the length, including the null terminator. */
+       DBG("Serializing C string's length (including null terminator): "
+                       "%" PRIu32, len);
+       ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len));
+       if (ret) {
+               goto end;
+       }
+
+       /* Serialize the string. */
+       DBG("Serializing C string: '%s'", str);
+       ret = lttng_dynamic_buffer_append(buf, str, len);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Serializes the event expression `expr` into `buf`.
+ */
+static
+int serialize_event_expr(const struct lttng_event_expr *expr,
+               struct lttng_payload *payload)
+{
+       const uint8_t type = expr->type;
+       int ret;
+
+       /* Serialize the expression's type. */
+       DBG("Serializing event expression's type: %d", expr->type);
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type));
+       if (ret) {
+               goto end;
+       }
+
+       /* Serialize the expression */
+       switch (expr->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_field *field_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+
+               /* Serialize the field name. */
+               DBG("Serializing field event expression's field name: '%s'",
+                               field_expr->name);
+               ret = serialize_cstr(field_expr->name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_app_specific_context_field *field_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               /* Serialize the provider name. */
+               DBG("Serializing app-specific context field event expression's "
+                               "provider name: '%s'",
+                               field_expr->provider_name);
+               ret = serialize_cstr(field_expr->provider_name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               /* Serialize the type name. */
+               DBG("Serializing app-specific context field event expression's "
+                               "type name: '%s'",
+                               field_expr->provider_name);
+               ret = serialize_cstr(field_expr->type_name, &payload->buffer);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               const struct lttng_event_expr_array_field_element *elem_expr =
+                               container_of(expr,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+               const uint32_t index = elem_expr->index;
+
+               /* Serialize the index. */
+               DBG("Serializing array field element event expression's "
+                               "index: %u", elem_expr->index);
+               ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index));
+               if (ret) {
+                       goto end;
+               }
+
+               /* Serialize the parent array field expression. */
+               DBG("Serializing array field element event expression's "
+                               "parent array field event expression");
+               ret = serialize_event_expr(elem_expr->array_field_expr, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+static struct lttng_capture_descriptor *
+lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index)
+{
+       const struct lttng_condition_event_rule_matches
+                       *event_rule_matches_cond = container_of(condition,
+                                       const struct lttng_condition_event_rule_matches,
+                                       parent);
+       struct lttng_capture_descriptor *desc = NULL;
+       unsigned int count;
+       enum lttng_condition_status status;
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition, &count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= count) {
+               goto end;
+       }
+
+       desc = (lttng_capture_descriptor *) lttng_dynamic_pointer_array_get_pointer(
+                       &event_rule_matches_cond->capture_descriptors, index);
+end:
+       return desc;
+}
+
+static int lttng_condition_event_rule_matches_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_condition_event_rule_matches *event_rule_matches_condition;
+       enum lttng_condition_status status;
+       /* Used for iteration and communication (size matters). */
+       uint32_t i, capture_descr_count;
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing on event condition");
+       event_rule_matches_condition = container_of(condition,
+                       struct lttng_condition_event_rule_matches, parent);
+
+       DBG("Serializing on event condition's event rule");
+       ret = lttng_event_rule_serialize(
+                       event_rule_matches_condition->rule, payload);
+       if (ret) {
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition, &capture_descr_count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ret = -1;
+               goto end;
+       };
+
+       DBG("Serializing on event condition's capture descriptor count: %" PRIu32,
+                       capture_descr_count);
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count,
+                       sizeof(capture_descr_count));
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < capture_descr_count; i++) {
+               const struct lttng_capture_descriptor *desc =
+                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
+                                               condition, i);
+
+               DBG("Serializing on event condition's capture descriptor %" PRIu32,
+                               i);
+               ret = serialize_event_expr(desc->event_expression, payload);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+bool capture_descriptors_are_equal(
+               const struct lttng_condition *condition_a,
+               const struct lttng_condition *condition_b)
+{
+       bool is_equal = true;
+       unsigned int capture_descr_count_a;
+       unsigned int capture_descr_count_b;
+       size_t i;
+       enum lttng_condition_status status;
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition_a, &capture_descr_count_a);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto not_equal;
+       }
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition_b, &capture_descr_count_b);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto not_equal;
+       }
+
+       if (capture_descr_count_a != capture_descr_count_b) {
+               goto not_equal;
+       }
+
+       for (i = 0; i < capture_descr_count_a; i++) {
+               const struct lttng_event_expr *expr_a =
+                               lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
+                                               condition_a, i);
+               const struct lttng_event_expr *expr_b =
+                               lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
+                                               condition_b, i);
+
+               if (!lttng_event_expr_is_equal(expr_a, expr_b)) {
+                       goto not_equal;
+               }
+       }
+
+       goto end;
+
+not_equal:
+       is_equal = false;
+
+end:
+       return is_equal;
+}
+
+static bool lttng_condition_event_rule_matches_is_equal(
+               const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_event_rule_matches *a, *b;
+
+       a = container_of(_a, struct lttng_condition_event_rule_matches, parent);
+       b = container_of(_b, struct lttng_condition_event_rule_matches, parent);
+
+       /* Both event rules must be set or both must be unset. */
+       if ((a->rule && !b->rule) || (!a->rule && b->rule)) {
+               WARN("Comparing event_rule conditions with uninitialized rule");
+               goto end;
+       }
+
+       is_equal = lttng_event_rule_is_equal(a->rule, b->rule);
+       if (!is_equal) {
+               goto end;
+       }
+
+       is_equal = capture_descriptors_are_equal(_a, _b);
+
+end:
+       return is_equal;
+}
+
+static void lttng_condition_event_rule_matches_destroy(
+               struct lttng_condition *condition)
+{
+       struct lttng_condition_event_rule_matches *event_rule_matches_condition;
+
+       event_rule_matches_condition = container_of(condition,
+                       struct lttng_condition_event_rule_matches, parent);
+
+       lttng_event_rule_put(event_rule_matches_condition->rule);
+       lttng_dynamic_pointer_array_reset(
+                       &event_rule_matches_condition->capture_descriptors);
+       free(event_rule_matches_condition);
+}
+
+static
+void destroy_capture_descriptor(void *ptr)
+{
+       struct lttng_capture_descriptor *desc =
+                       (struct lttng_capture_descriptor *) ptr;
+
+       lttng_event_expr_destroy(desc->event_expression);
+       free(desc->bytecode);
+       free(desc);
+}
+
+static enum lttng_error_code lttng_condition_event_rule_matches_mi_serialize(
+               const struct lttng_condition *condition,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_condition_status status;
+       const struct lttng_event_rule *rule = NULL;
+       unsigned int capture_descriptor_count, i;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_EVENT_RULE_MATCHES_CONDITION(condition));
+
+       status = lttng_condition_event_rule_matches_get_rule(condition, &rule);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       LTTNG_ASSERT(rule != NULL);
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition, &capture_descriptor_count);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+
+       /* Open condition event rule matches element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_condition_event_rule_matches);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize the event rule. */
+       ret_code = lttng_event_rule_mi_serialize(rule, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Open the capture descriptors element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_capture_descriptors);
+       if (ret) {
+               goto mi_error;
+       }
+
+       for (i = 0; i < capture_descriptor_count; i++) {
+               const struct lttng_event_expr *descriptor = NULL;
+
+               descriptor = lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
+                               condition, i);
+               LTTNG_ASSERT(descriptor);
+
+               ret_code = lttng_event_expr_mi_serialize(descriptor, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close capture descriptors element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close condition_event_rule_matches. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_condition *lttng_condition_event_rule_matches_create(
+               struct lttng_event_rule *rule)
+{
+       struct lttng_condition *parent = NULL;
+       struct lttng_condition_event_rule_matches *condition = NULL;
+
+       if (!rule) {
+               goto end;
+       }
+
+       condition = (lttng_condition_event_rule_matches *) zmalloc(sizeof(struct lttng_condition_event_rule_matches));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent,
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
+       condition->parent.validate =
+                       lttng_condition_event_rule_matches_validate,
+       condition->parent.serialize =
+                       lttng_condition_event_rule_matches_serialize,
+       condition->parent.equal = lttng_condition_event_rule_matches_is_equal,
+       condition->parent.destroy = lttng_condition_event_rule_matches_destroy,
+       condition->parent.mi_serialize = lttng_condition_event_rule_matches_mi_serialize,
+
+       lttng_event_rule_get(rule);
+       condition->rule = rule;
+       rule = NULL;
+
+       lttng_dynamic_pointer_array_init(&condition->capture_descriptors,
+                       destroy_capture_descriptor);
+
+       parent = &condition->parent;
+end:
+       return parent;
+}
+
+static
+uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size,
+               size_t *offset)
+{
+       uint64_t ret;
+       const struct lttng_buffer_view uint_view =
+                       lttng_buffer_view_from_view(view, *offset, size);
+
+       if (!lttng_buffer_view_is_valid(&uint_view)) {
+               ret = UINT64_C(-1);
+               goto end;
+       }
+
+       switch (size) {
+       case 1:
+               ret = (uint64_t) *uint_view.data;
+               break;
+       case sizeof(uint32_t):
+       {
+               uint32_t u32;
+
+               memcpy(&u32, uint_view.data, sizeof(u32));
+               ret = (uint64_t) u32;
+               break;
+       }
+       case sizeof(ret):
+               memcpy(&ret, uint_view.data, sizeof(ret));
+               break;
+       default:
+               abort();
+       }
+
+       *offset += size;
+
+end:
+       return ret;
+}
+
+static
+const char *str_from_buffer(const struct lttng_buffer_view *view,
+               size_t *offset)
+{
+       uint64_t len;
+       const char *ret;
+
+       len = uint_from_buffer(view, sizeof(uint32_t), offset);
+       if (len == UINT64_C(-1)) {
+               goto error;
+       }
+
+       ret = &view->data[*offset];
+
+       if (!lttng_buffer_view_contains_string(view, ret, len)) {
+               goto error;
+       }
+
+       *offset += len;
+       goto end;
+
+error:
+       ret = NULL;
+
+end:
+       return ret;
+}
+
+static
+struct lttng_event_expr *event_expr_from_payload(
+               struct lttng_payload_view *view, size_t *offset)
+{
+       struct lttng_event_expr *expr = NULL;
+       const char *str;
+       uint64_t type;
+
+       type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset);
+       if (type == UINT64_C(-1)) {
+               goto error;
+       }
+
+       switch (type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+               str = str_from_buffer(&view->buffer, offset);
+               if (!str) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_event_payload_field_create(str);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+               str = str_from_buffer(&view->buffer, offset);
+               if (!str) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_channel_context_field_create(str);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const char *provider_name;
+               const char *type_name;
+
+               provider_name = str_from_buffer(&view->buffer, offset);
+               if (!provider_name) {
+                       goto error;
+               }
+
+               type_name = str_from_buffer(&view->buffer, offset);
+               if (!type_name) {
+                       goto error;
+               }
+
+               expr = lttng_event_expr_app_specific_context_field_create(
+                               provider_name, type_name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               struct lttng_event_expr *array_field_expr;
+               const uint64_t index = uint_from_buffer(
+                               &view->buffer, sizeof(uint32_t), offset);
+
+               if (index == UINT64_C(-1)) {
+                       goto error;
+               }
+
+               /* Array field expression is the encoded after this. */
+               array_field_expr = event_expr_from_payload(view, offset);
+               if (!array_field_expr) {
+                       goto error;
+               }
+
+               /* Move ownership of `array_field_expr` to new expression. */
+               expr = lttng_event_expr_array_field_element_create(
+                               array_field_expr, (unsigned int) index);
+               if (!expr) {
+                       /* `array_field_expr` not moved: destroy it. */
+                       lttng_event_expr_destroy(array_field_expr);
+               }
+
+               break;
+       }
+       default:
+               ERR("Invalid event expression type encoutered while deserializing event expression: type = %" PRIu64,
+                               type);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_expr_destroy(expr);
+       expr = NULL;
+
+end:
+       return expr;
+}
+
+ssize_t lttng_condition_event_rule_matches_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t consumed_length;
+       size_t offset = 0;
+       ssize_t event_rule_length;
+       uint32_t i, capture_descr_count;
+       struct lttng_condition *condition = NULL;
+       struct lttng_event_rule *event_rule = NULL;
+
+       if (!view || !_condition) {
+               goto error;
+       }
+
+       /* Struct lttng_event_rule. */
+       {
+               struct lttng_payload_view event_rule_view =
+                               lttng_payload_view_from_view(view, offset, -1);
+
+               event_rule_length = lttng_event_rule_create_from_payload(
+                               &event_rule_view, &event_rule);
+       }
+
+       if (event_rule_length < 0 || !event_rule) {
+               goto error;
+       }
+
+       offset += event_rule_length;
+
+       /* Create condition (no capture descriptors yet) at this point */
+       condition = lttng_condition_event_rule_matches_create(event_rule);
+       if (!condition) {
+               goto error;
+       }
+
+       /* Capture descriptor count. */
+       LTTNG_ASSERT(event_rule_length >= 0);
+       capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset);
+       if (capture_descr_count == UINT32_C(-1)) {
+               goto error;
+       }
+
+       /* Capture descriptors. */
+       for (i = 0; i < capture_descr_count; i++) {
+               enum lttng_condition_status status;
+               struct lttng_event_expr *expr = event_expr_from_payload(
+                               view, &offset);
+
+               if (!expr) {
+                       goto error;
+               }
+
+               /* Move ownership of `expr` to `condition`. */
+               status = lttng_condition_event_rule_matches_append_capture_descriptor(
+                               condition, expr);
+               if (status != LTTNG_CONDITION_STATUS_OK) {
+                       /* `expr` not moved: destroy it. */
+                       lttng_event_expr_destroy(expr);
+                       goto error;
+               }
+       }
+
+       consumed_length = (ssize_t) offset;
+       *_condition = condition;
+       condition = NULL;
+       goto end;
+
+error:
+       consumed_length = -1;
+
+end:
+       lttng_event_rule_put(event_rule);
+       lttng_condition_put(condition);
+       return consumed_length;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_matches_borrow_rule_mutable(
+               const struct lttng_condition *condition,
+               struct lttng_event_rule **rule)
+{
+       struct lttng_condition_event_rule_matches *event_rule;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
+                       !rule) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       event_rule = container_of(condition,
+                       struct lttng_condition_event_rule_matches, parent);
+       if (!event_rule->rule) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+
+       *rule = event_rule->rule;
+end:
+       return status;
+}
+
+enum lttng_condition_status lttng_condition_event_rule_matches_get_rule(
+               const struct lttng_condition *condition,
+               const struct lttng_event_rule **rule)
+{
+       struct lttng_event_rule *mutable_rule = NULL;
+       const enum lttng_condition_status status =
+                       lttng_condition_event_rule_matches_borrow_rule_mutable(
+                                       condition, &mutable_rule);
+
+       *rule = mutable_rule;
+       return status;
+}
+
+void lttng_condition_event_rule_matches_set_error_counter_index(
+               struct lttng_condition *condition, uint64_t error_counter_index)
+{
+       struct lttng_condition_event_rule_matches *event_rule_matches_cond =
+                       container_of(condition,
+                                       struct lttng_condition_event_rule_matches,
+                                       parent);
+
+       LTTNG_OPTIONAL_SET(&event_rule_matches_cond->error_counter_index,
+                       error_counter_index);
+}
+
+uint64_t lttng_condition_event_rule_matches_get_error_counter_index(
+               const struct lttng_condition *condition)
+{
+       const struct lttng_condition_event_rule_matches
+                       *event_rule_matches_cond = container_of(condition,
+                                       const struct lttng_condition_event_rule_matches,
+                                       parent);
+
+       return LTTNG_OPTIONAL_GET(event_rule_matches_cond->error_counter_index);
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_matches_append_capture_descriptor(
+               struct lttng_condition *condition,
+               struct lttng_event_expr *expr)
+{
+       int ret;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+       struct lttng_condition_event_rule_matches *event_rule_matches_cond =
+                       container_of(condition,
+                                       struct lttng_condition_event_rule_matches,
+                                       parent);
+       struct lttng_capture_descriptor *descriptor = NULL;
+       const struct lttng_event_rule *rule = NULL;
+
+       /* Only accept l-values. */
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
+                       !expr || !lttng_event_expr_is_lvalue(expr)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_matches_get_rule(condition, &rule);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       switch(lttng_event_rule_get_type(rule)) {
+       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
+       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
+       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
+       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
+               /* Supported. */
+               status = LTTNG_CONDITION_STATUS_OK;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               break;
+       default:
+               status = LTTNG_CONDITION_STATUS_UNSUPPORTED;
+               break;
+       }
+
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       descriptor = (lttng_capture_descriptor *) malloc(sizeof(*descriptor));
+       if (descriptor == NULL) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       descriptor->event_expression = expr;
+       descriptor->bytecode = NULL;
+
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &event_rule_matches_cond->capture_descriptors,
+                       descriptor);
+       if (ret) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Ownership is transfered to the internal capture_descriptors array */
+       descriptor = NULL;
+end:
+       free(descriptor);
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_event_rule_matches_get_capture_descriptor_count(
+               const struct lttng_condition *condition, unsigned int *count)
+{
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+       const struct lttng_condition_event_rule_matches
+                       *event_rule_matches_condition = container_of(condition,
+                                       const struct lttng_condition_event_rule_matches,
+                                       parent);
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) ||
+                       !count) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = lttng_dynamic_pointer_array_get_count(
+                       &event_rule_matches_condition->capture_descriptors);
+
+end:
+       return status;
+}
+
+const struct lttng_event_expr *
+lttng_condition_event_rule_matches_get_capture_descriptor_at_index(
+               const struct lttng_condition *condition, unsigned int index)
+{
+       const struct lttng_event_expr *expr = NULL;
+       const struct lttng_capture_descriptor *desc = NULL;
+
+       desc = lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
+                       condition, index);
+       if (desc == NULL) {
+               goto end;
+       }
+       expr = desc->event_expression;
+
+end:
+       return expr;
+}
+
+ssize_t lttng_evaluation_event_rule_matches_create_from_payload(
+               const struct lttng_condition_event_rule_matches *condition,
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret, offset = 0;
+       struct lttng_evaluation *evaluation = NULL;
+       uint32_t capture_payload_size;
+       const char *capture_payload = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       {
+               const struct lttng_payload_view current_view =
+                               lttng_payload_view_from_view(view, offset, -1);
+
+               if (current_view.buffer.size < sizeof(capture_payload_size)) {
+                       ret = -1;
+                       goto error;
+               }
+
+               memcpy(&capture_payload_size, current_view.buffer.data,
+                               sizeof(capture_payload_size));
+       }
+       offset += sizeof(capture_payload_size);
+
+       if (capture_payload_size > 0) {
+               const struct lttng_payload_view current_view =
+                               lttng_payload_view_from_view(view, offset, -1);
+
+               if (current_view.buffer.size < capture_payload_size) {
+                       ret = -1;
+                       goto error;
+               }
+
+               capture_payload = current_view.buffer.data;
+       }
+
+       evaluation = lttng_evaluation_event_rule_matches_create(
+                       condition, capture_payload, capture_payload_size, true);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       offset += capture_payload_size;
+       *_evaluation = evaluation;
+       evaluation = NULL;
+       ret = offset;
+
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+static int lttng_evaluation_event_rule_matches_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload)
+{
+       int ret = 0;
+       struct lttng_evaluation_event_rule_matches *hit;
+       uint32_t capture_payload_size;
+
+       hit = container_of(evaluation,
+                       struct lttng_evaluation_event_rule_matches, parent);
+
+       capture_payload_size = (uint32_t) hit->capture_payload.size;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size,
+                       sizeof(capture_payload_size));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data,
+                       hit->capture_payload.size);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str)
+{
+       bool is_equal = true;
+
+       LTTNG_ASSERT(obj->type == MSGPACK_OBJECT_STR);
+
+       if (obj->via.str.size != strlen(str)) {
+               is_equal = false;
+               goto end;
+       }
+
+       if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) {
+               is_equal = false;
+               goto end;
+       }
+
+end:
+       return is_equal;
+}
+
+static
+const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj,
+               const char *name)
+{
+       const msgpack_object *ret = NULL;
+       size_t i;
+
+       LTTNG_ASSERT(map_obj->type == MSGPACK_OBJECT_MAP);
+
+       for (i = 0; i < map_obj->via.map.size; i++) {
+               const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i];
+
+               LTTNG_ASSERT(kv->key.type == MSGPACK_OBJECT_STR);
+
+               if (msgpack_str_is_equal(&kv->key, name)) {
+                       ret = &kv->val;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static void lttng_evaluation_event_rule_matches_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_event_rule_matches *hit;
+
+       hit = container_of(evaluation,
+                       struct lttng_evaluation_event_rule_matches, parent);
+       lttng_dynamic_buffer_reset(&hit->capture_payload);
+       lttng_event_field_value_destroy(hit->captured_values);
+       free(hit);
+}
+
+static
+int event_field_value_from_obj(const msgpack_object *obj,
+               struct lttng_event_field_value **field_val)
+{
+       int ret = 0;
+
+       LTTNG_ASSERT(obj);
+       LTTNG_ASSERT(field_val);
+
+       switch (obj->type) {
+       case MSGPACK_OBJECT_NIL:
+               /* Unavailable. */
+               *field_val = NULL;
+               goto end;
+       case MSGPACK_OBJECT_POSITIVE_INTEGER:
+               *field_val = lttng_event_field_value_uint_create(
+                               obj->via.u64);
+               break;
+       case MSGPACK_OBJECT_NEGATIVE_INTEGER:
+               *field_val = lttng_event_field_value_int_create(
+                               obj->via.i64);
+               break;
+       case MSGPACK_OBJECT_FLOAT32:
+       case MSGPACK_OBJECT_FLOAT64:
+               *field_val = lttng_event_field_value_real_create(
+                               obj->via.f64);
+               break;
+       case MSGPACK_OBJECT_STR:
+               *field_val = lttng_event_field_value_string_create_with_size(
+                               obj->via.str.ptr, obj->via.str.size);
+               break;
+       case MSGPACK_OBJECT_ARRAY:
+       {
+               size_t i;
+
+               *field_val = lttng_event_field_value_array_create();
+               if (!*field_val) {
+                       goto error;
+               }
+
+               for (i = 0; i < obj->via.array.size; i++) {
+                       const msgpack_object *elem_obj = &obj->via.array.ptr[i];
+                       struct lttng_event_field_value *elem_field_val;
+
+                       ret = event_field_value_from_obj(elem_obj,
+                                       &elem_field_val);
+                       if (ret) {
+                               goto error;
+                       }
+
+                       if (elem_field_val) {
+                               ret = lttng_event_field_value_array_append(
+                                               *field_val, elem_field_val);
+                       } else {
+                               ret = lttng_event_field_value_array_append_unavailable(
+                                               *field_val);
+                       }
+
+                       if (ret) {
+                               lttng_event_field_value_destroy(elem_field_val);
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+       case MSGPACK_OBJECT_MAP:
+       {
+               /*
+                * As of this version, the only valid map object is
+                * for an enumeration value, for example:
+                *
+                *     type: enum
+                *     value: 177
+                *     labels:
+                *     - Labatt 50
+                *     - Molson Dry
+                *     - Carling Black Label
+                */
+               const msgpack_object *inner_obj;
+               size_t label_i;
+
+               inner_obj = get_msgpack_map_obj(obj, "type");
+               if (!inner_obj) {
+                       ERR("Missing `type` entry in map object");
+                       goto error;
+               }
+
+               if (inner_obj->type != MSGPACK_OBJECT_STR) {
+                       ERR("Map object's `type` entry is not a string: type = %s",
+                                       msgpack_object_type_str(inner_obj->type));
+                       goto error;
+               }
+
+               if (!msgpack_str_is_equal(inner_obj, "enum")) {
+                       ERR("Map object's `type` entry: expecting `enum`");
+                       goto error;
+               }
+
+               inner_obj = get_msgpack_map_obj(obj, "value");
+               if (!inner_obj) {
+                       ERR("Missing `value` entry in map object");
+                       goto error;
+               }
+
+               if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
+                       *field_val = lttng_event_field_value_enum_uint_create(
+                                       inner_obj->via.u64);
+               } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) {
+                       *field_val = lttng_event_field_value_enum_int_create(
+                                       inner_obj->via.i64);
+               } else {
+                       ERR("Map object's `value` entry is not an integer: type = %s",
+                                       msgpack_object_type_str(inner_obj->type));
+                       goto error;
+               }
+
+               if (!*field_val) {
+                       goto error;
+               }
+
+               inner_obj = get_msgpack_map_obj(obj, "labels");
+               if (!inner_obj) {
+                       /* No labels */
+                       goto end;
+               }
+
+               if (inner_obj->type != MSGPACK_OBJECT_ARRAY) {
+                       ERR("Map object's `labels` entry is not an array: type = %s",
+                                       msgpack_object_type_str(inner_obj->type));
+                       goto error;
+               }
+
+               for (label_i = 0; label_i < inner_obj->via.array.size;
+                               label_i++) {
+                       int iret;
+                       const msgpack_object *elem_obj =
+                                       &inner_obj->via.array.ptr[label_i];
+
+                       if (elem_obj->type != MSGPACK_OBJECT_STR) {
+                               ERR("Map object's `labels` entry's type is not a string: type = %s",
+                                               msgpack_object_type_str(elem_obj->type));
+                               goto error;
+                       }
+
+                       iret = lttng_event_field_value_enum_append_label_with_size(
+                                       *field_val, elem_obj->via.str.ptr,
+                                       elem_obj->via.str.size);
+                       if (iret) {
+                               goto error;
+                       }
+               }
+
+               break;
+       }
+       default:
+               ERR("Unexpected object type: type = %s",
+                               msgpack_object_type_str(obj->type));
+               goto error;
+       }
+
+       if (!*field_val) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(*field_val);
+       *field_val = NULL;
+       ret = -1;
+
+end:
+       return ret;
+}
+
+static struct lttng_event_field_value *event_field_value_from_capture_payload(
+               const struct lttng_condition_event_rule_matches *condition,
+               const char *capture_payload,
+               size_t capture_payload_size)
+{
+       struct lttng_event_field_value *ret = NULL;
+       msgpack_unpacked unpacked;
+       msgpack_unpack_return unpack_return;
+       const msgpack_object *root_obj;
+       const msgpack_object_array *root_array_obj;
+       size_t i;
+       size_t count;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(capture_payload);
+
+       /* Initialize value. */
+       msgpack_unpacked_init(&unpacked);
+
+       /* Decode. */
+       unpack_return = msgpack_unpack_next(&unpacked, capture_payload,
+                       capture_payload_size, NULL);
+       if (unpack_return != MSGPACK_UNPACK_SUCCESS) {
+               ERR("msgpack_unpack_next() failed to decode the "
+                               "MessagePack-encoded capture payload: "
+                               "size = %zu, ret = %d",
+                               capture_payload_size, unpack_return);
+               goto error;
+       }
+
+       /* Get root array. */
+       root_obj = &unpacked.data;
+
+       if (root_obj->type != MSGPACK_OBJECT_ARRAY) {
+               ERR("Expecting an array as the root object: type = %s",
+                               msgpack_object_type_str(root_obj->type));
+               goto error;
+       }
+
+       root_array_obj = &root_obj->via.array;
+
+       /* Create an empty root array event field value. */
+       ret = lttng_event_field_value_array_create();
+       if (!ret) {
+               goto error;
+       }
+
+       /*
+        * For each capture descriptor in the condition object:
+        *
+        * 1. Get its corresponding captured field value MessagePack
+        *    object.
+        *
+        * 2. Create a corresponding event field value.
+        *
+        * 3. Append it to `ret` (the root array event field value).
+        */
+       count = lttng_dynamic_pointer_array_get_count(
+                       &condition->capture_descriptors);
+       LTTNG_ASSERT(count > 0);
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_capture_descriptor *capture_descriptor =
+                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
+                                               &condition->parent, i);
+               const msgpack_object *elem_obj;
+               struct lttng_event_field_value *elem_field_val;
+               int iret;
+
+               LTTNG_ASSERT(capture_descriptor);
+
+               elem_obj = &root_array_obj->ptr[i];
+               iret = event_field_value_from_obj(elem_obj,
+                               &elem_field_val);
+               if (iret) {
+                       goto error;
+               }
+
+               if (elem_field_val) {
+                       iret = lttng_event_field_value_array_append(ret,
+                                       elem_field_val);
+               } else {
+                       iret = lttng_event_field_value_array_append_unavailable(
+                                       ret);
+               }
+
+               if (iret) {
+                       lttng_event_field_value_destroy(elem_field_val);
+                       goto error;
+               }
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(ret);
+       ret = NULL;
+
+end:
+       msgpack_unpacked_destroy(&unpacked);
+       return ret;
+}
+
+struct lttng_evaluation *lttng_evaluation_event_rule_matches_create(
+               const struct lttng_condition_event_rule_matches *condition,
+               const char *capture_payload,
+               size_t capture_payload_size,
+               bool decode_capture_payload)
+{
+       struct lttng_evaluation_event_rule_matches *hit;
+       struct lttng_evaluation *evaluation = NULL;
+
+       hit = (lttng_evaluation_event_rule_matches *) zmalloc(sizeof(struct lttng_evaluation_event_rule_matches));
+       if (!hit) {
+               goto error;
+       }
+
+       lttng_dynamic_buffer_init(&hit->capture_payload);
+
+       if (capture_payload) {
+               const int ret = lttng_dynamic_buffer_append(
+                               &hit->capture_payload, capture_payload,
+                               capture_payload_size);
+               if (ret) {
+                       ERR("Failed to initialize capture payload of event rule evaluation");
+                       goto error;
+               }
+
+               if (decode_capture_payload) {
+                       hit->captured_values =
+                                       event_field_value_from_capture_payload(
+                                               condition,
+                                               capture_payload,
+                                               capture_payload_size);
+                       if (!hit->captured_values) {
+                               ERR("Failed to decode the capture payload: size = %zu",
+                                               capture_payload_size);
+                               goto error;
+                       }
+               }
+       }
+
+       hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES;
+       hit->parent.serialize = lttng_evaluation_event_rule_matches_serialize;
+       hit->parent.destroy = lttng_evaluation_event_rule_matches_destroy;
+
+       evaluation = &hit->parent;
+       hit = NULL;
+
+error:
+       if (hit) {
+               lttng_evaluation_event_rule_matches_destroy(&hit->parent);
+       }
+
+       return evaluation;
+}
+
+enum lttng_evaluation_event_rule_matches_status
+lttng_evaluation_event_rule_matches_get_captured_values(
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_event_field_value **field_val)
+{
+       struct lttng_evaluation_event_rule_matches *hit;
+       enum lttng_evaluation_event_rule_matches_status status =
+                       LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK;
+
+       if (!evaluation || !is_event_rule_matches_evaluation(evaluation) ||
+                       !field_val) {
+               status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_INVALID;
+               goto end;
+       }
+
+       hit = container_of(evaluation,
+                       struct lttng_evaluation_event_rule_matches, parent);
+       if (!hit->captured_values) {
+               status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_NONE;
+               goto end;
+       }
+
+       *field_val = hit->captured_values;
+
+end:
+       return status;
+}
+
+enum lttng_error_code
+lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode(
+               struct lttng_condition *condition)
+{
+       enum lttng_error_code ret;
+       enum lttng_condition_status status;
+       unsigned int capture_count, i;
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
+               ret = LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition, &capture_count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ret = LTTNG_ERR_FATAL;
+               goto end;
+       }
+
+       for (i = 0; i < capture_count; i++) {
+               struct lttng_capture_descriptor *local_capture_desc =
+                               lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index(
+                                               condition, i);
+               int bytecode_ret;
+
+               if (local_capture_desc == NULL) {
+                       ret = LTTNG_ERR_FATAL;
+                       goto end;
+               }
+
+               /* Generate the bytecode. */
+               bytecode_ret = lttng_event_expr_to_bytecode(
+                               local_capture_desc->event_expression,
+                               &local_capture_desc->bytecode);
+               if (bytecode_ret < 0 || local_capture_desc->bytecode == NULL) {
+                       ret = LTTNG_ERR_INVALID_CAPTURE_EXPRESSION;
+                       goto end;
+               }
+       }
+
+       /* Everything went better than expected */
+       ret = LTTNG_OK;
+
+end:
+       return ret;
+}
+
+const struct lttng_bytecode *
+lttng_condition_event_rule_matches_get_capture_bytecode_at_index(
+               const struct lttng_condition *condition, unsigned int index)
+{
+       const struct lttng_condition_event_rule_matches
+                       *event_rule_matches_cond = container_of(condition,
+                                       const struct lttng_condition_event_rule_matches,
+                                       parent);
+       struct lttng_capture_descriptor *desc = NULL;
+       struct lttng_bytecode *bytecode = NULL;
+       unsigned int count;
+       enum lttng_condition_status status;
+
+       if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) {
+               goto end;
+       }
+
+       status = lttng_condition_event_rule_matches_get_capture_descriptor_count(
+                       condition, &count);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= count) {
+               goto end;
+       }
+
+       desc = (lttng_capture_descriptor *) lttng_dynamic_pointer_array_get_pointer(
+                       &event_rule_matches_cond->capture_descriptors, index);
+       if (desc == NULL) {
+               goto end;
+       }
+
+       bytecode = desc->bytecode;
+end:
+       return bytecode;
+}
diff --git a/src/common/conditions/session-consumed-size.c b/src/common/conditions/session-consumed-size.c
deleted file mode 100644 (file)
index 2f95bd8..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <float.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/constant.h>
-#include <math.h>
-#include <time.h>
-
-#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
-       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
-       )
-
-#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
-       lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
-       )
-
-static
-void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
-{
-       struct lttng_condition_session_consumed_size *consumed_size;
-
-       consumed_size = container_of(condition,
-                       struct lttng_condition_session_consumed_size, parent);
-
-       free(consumed_size->session_name);
-       free(consumed_size);
-}
-
-static
-bool lttng_condition_session_consumed_size_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_session_consumed_size *consumed;
-
-       if (!condition) {
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->session_name) {
-               ERR("Invalid session consumed size condition: a target session name must be set.");
-               goto end;
-       }
-       if (!consumed->consumed_threshold_bytes.set) {
-               ERR("Invalid session consumed size condition: a threshold must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_session_consumed_size_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t session_name_len;
-       struct lttng_condition_session_consumed_size *consumed;
-       struct lttng_condition_session_consumed_size_comm consumed_comm;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing session consumed size condition");
-       consumed = container_of(condition,
-                       struct lttng_condition_session_consumed_size,
-                       parent);
-
-       session_name_len = strlen(consumed->session_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       consumed_comm.consumed_threshold_bytes =
-                       consumed->consumed_threshold_bytes.value;
-       consumed_comm.session_name_len = (uint32_t) session_name_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &consumed_comm,
-                       sizeof(consumed_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, consumed->session_name,
-                       session_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_session_consumed_size *a, *b;
-
-       a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
-       b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
-
-       if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
-               uint64_t a_value, b_value;
-
-               a_value = a->consumed_threshold_bytes.value;
-               b_value = b->consumed_threshold_bytes.value;
-               if (a_value != b_value) {
-                       goto end;
-               }
-       }
-
-       LTTNG_ASSERT(a->session_name);
-       LTTNG_ASSERT(b->session_name);
-       if (strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static
-enum lttng_error_code lttng_condition_session_consumed_size_mi_serialize(
-               const struct lttng_condition *condition,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_condition_status status;
-       const char *session_name = NULL;
-       uint64_t threshold_bytes;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_CONSUMED_SIZE_CONDITION(condition));
-
-       status = lttng_condition_session_consumed_size_get_session_name(
-                       condition, &session_name);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       LTTNG_ASSERT(session_name);
-
-       status = lttng_condition_session_consumed_size_get_threshold(
-                       condition, &threshold_bytes);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-
-       /* Open condition session consumed size element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_condition_session_consumed_size);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Threshold in bytes. */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_condition_threshold_bytes,
-                       threshold_bytes);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close condition session consumed size element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_condition *lttng_condition_session_consumed_size_create(void)
-{
-       struct lttng_condition_session_consumed_size *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size));
-       if (!condition) {
-               return NULL;
-       }
-
-       lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
-       condition->parent.validate = lttng_condition_session_consumed_size_validate;
-       condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
-       condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
-       condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
-       condition->parent.mi_serialize = lttng_condition_session_consumed_size_mi_serialize;
-       return &condition->parent;
-}
-
-static
-ssize_t init_condition_from_payload(struct lttng_condition *condition,
-               struct lttng_payload_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       const char *session_name;
-       struct lttng_buffer_view session_name_view;
-       const struct lttng_condition_session_consumed_size_comm *condition_comm;
-       struct lttng_payload_view condition_comm_view = lttng_payload_view_from_view(
-                       src_view, 0, sizeof(*condition_comm));
-
-       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
-       session_name_view = lttng_buffer_view_from_view(&src_view->buffer,
-                       sizeof(*condition_comm), condition_comm->session_name_len);
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_buffer_view_is_valid(&session_name_view)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_consumed_size_set_threshold(condition,
-                       condition_comm->consumed_threshold_bytes);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to initialize session consumed size condition threshold");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = session_name_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_consumed_size_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set session consumed size condition's session name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-ssize_t lttng_condition_session_consumed_size_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **_condition)
-{
-       ssize_t ret;
-       struct lttng_condition *condition =
-                       lttng_condition_session_consumed_size_create();
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_payload(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-static
-struct lttng_evaluation *create_evaluation_from_payload(
-               const struct lttng_payload_view *view)
-{
-       const struct lttng_evaluation_session_consumed_size_comm *comm =
-                       (typeof(comm)) view->buffer.data;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (view->buffer.size < sizeof(*comm)) {
-               goto end;
-       }
-
-       evaluation = lttng_evaluation_session_consumed_size_create(
-                       comm->session_consumed);
-end:
-       return evaluation;
-}
-
-ssize_t lttng_evaluation_session_consumed_size_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       evaluation = create_evaluation_from_payload(view);
-       if (!evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_threshold(
-               const struct lttng_condition *condition,
-               uint64_t *consumed_threshold_bytes)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->consumed_threshold_bytes.set) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_threshold(
-               struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       consumed->consumed_threshold_bytes.set = true;
-       consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       if (!consumed->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = consumed->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_consumed_size_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_session_consumed_size *consumed;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
-                       !session_name || strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
-                       parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       if (consumed->session_name) {
-               free(consumed->session_name);
-       }
-       consumed->session_name = session_name_copy;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_session_consumed_size_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-       struct lttng_evaluation_session_consumed_size_comm comm;
-
-       consumed = container_of(evaluation,
-                       struct lttng_evaluation_session_consumed_size, parent);
-       comm.session_consumed = consumed->session_consumed;
-       return lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-}
-
-static
-void lttng_evaluation_session_consumed_size_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-
-       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
-                       parent);
-       free(consumed);
-}
-
-struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
-               uint64_t consumed)
-{
-       struct lttng_evaluation_session_consumed_size *consumed_eval;
-
-       consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
-       if (!consumed_eval) {
-               goto end;
-       }
-
-       consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
-       consumed_eval->session_consumed = consumed;
-       consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
-       consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
-end:
-       return &consumed_eval->parent;
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_consumed_size_get_consumed_size(
-               const struct lttng_evaluation *evaluation,
-               uint64_t *session_consumed)
-{
-       struct lttng_evaluation_session_consumed_size *consumed;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
-                       !session_consumed) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
-                       parent);
-       *session_consumed = consumed->session_consumed;
-end:
-       return status;
-}
diff --git a/src/common/conditions/session-consumed-size.cpp b/src/common/conditions/session-consumed-size.cpp
new file mode 100644 (file)
index 0000000..86a7f33
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <float.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/constant.h>
+#include <math.h>
+#include <time.h>
+
+#define IS_CONSUMED_SIZE_CONDITION(condition) ( \
+       lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+       )
+
+#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \
+       lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \
+       )
+
+static
+void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition)
+{
+       struct lttng_condition_session_consumed_size *consumed_size;
+
+       consumed_size = container_of(condition,
+                       struct lttng_condition_session_consumed_size, parent);
+
+       free(consumed_size->session_name);
+       free(consumed_size);
+}
+
+static
+bool lttng_condition_session_consumed_size_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_session_consumed_size *consumed;
+
+       if (!condition) {
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->session_name) {
+               ERR("Invalid session consumed size condition: a target session name must be set.");
+               goto end;
+       }
+       if (!consumed->consumed_threshold_bytes.set) {
+               ERR("Invalid session consumed size condition: a threshold must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_session_consumed_size_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t session_name_len;
+       struct lttng_condition_session_consumed_size *consumed;
+       struct lttng_condition_session_consumed_size_comm consumed_comm;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing session consumed size condition");
+       consumed = container_of(condition,
+                       struct lttng_condition_session_consumed_size,
+                       parent);
+
+       session_name_len = strlen(consumed->session_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       consumed_comm.consumed_threshold_bytes =
+                       consumed->consumed_threshold_bytes.value;
+       consumed_comm.session_name_len = (uint32_t) session_name_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &consumed_comm,
+                       sizeof(consumed_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, consumed->session_name,
+                       session_name_len);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_session_consumed_size *a, *b;
+
+       a = container_of(_a, struct lttng_condition_session_consumed_size, parent);
+       b = container_of(_b, struct lttng_condition_session_consumed_size, parent);
+
+       if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) {
+               uint64_t a_value, b_value;
+
+               a_value = a->consumed_threshold_bytes.value;
+               b_value = b->consumed_threshold_bytes.value;
+               if (a_value != b_value) {
+                       goto end;
+               }
+       }
+
+       LTTNG_ASSERT(a->session_name);
+       LTTNG_ASSERT(b->session_name);
+       if (strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+enum lttng_error_code lttng_condition_session_consumed_size_mi_serialize(
+               const struct lttng_condition *condition,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_condition_status status;
+       const char *session_name = NULL;
+       uint64_t threshold_bytes;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_CONSUMED_SIZE_CONDITION(condition));
+
+       status = lttng_condition_session_consumed_size_get_session_name(
+                       condition, &session_name);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       LTTNG_ASSERT(session_name);
+
+       status = lttng_condition_session_consumed_size_get_threshold(
+                       condition, &threshold_bytes);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+
+       /* Open condition session consumed size element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_condition_session_consumed_size);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Threshold in bytes. */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_condition_threshold_bytes,
+                       threshold_bytes);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close condition session consumed size element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_condition *lttng_condition_session_consumed_size_create(void)
+{
+       struct lttng_condition_session_consumed_size *condition;
+
+       condition = (lttng_condition_session_consumed_size *) zmalloc(sizeof(struct lttng_condition_session_consumed_size));
+       if (!condition) {
+               return NULL;
+       }
+
+       lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE);
+       condition->parent.validate = lttng_condition_session_consumed_size_validate;
+       condition->parent.serialize = lttng_condition_session_consumed_size_serialize;
+       condition->parent.equal = lttng_condition_session_consumed_size_is_equal;
+       condition->parent.destroy = lttng_condition_session_consumed_size_destroy;
+       condition->parent.mi_serialize = lttng_condition_session_consumed_size_mi_serialize;
+       return &condition->parent;
+}
+
+static
+ssize_t init_condition_from_payload(struct lttng_condition *condition,
+               struct lttng_payload_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       const char *session_name;
+       struct lttng_buffer_view session_name_view;
+       const struct lttng_condition_session_consumed_size_comm *condition_comm;
+       struct lttng_payload_view condition_comm_view = lttng_payload_view_from_view(
+                       src_view, 0, sizeof(*condition_comm));
+
+       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data;
+       session_name_view = lttng_buffer_view_from_view(&src_view->buffer,
+                       sizeof(*condition_comm), condition_comm->session_name_len);
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_buffer_view_is_valid(&session_name_view)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_consumed_size_set_threshold(condition,
+                       condition_comm->consumed_threshold_bytes);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to initialize session consumed size condition threshold");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = session_name_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_consumed_size_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set session consumed size condition's session name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+ssize_t lttng_condition_session_consumed_size_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition)
+{
+       ssize_t ret;
+       struct lttng_condition *condition =
+                       lttng_condition_session_consumed_size_create();
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_payload(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+static
+struct lttng_evaluation *create_evaluation_from_payload(
+               const struct lttng_payload_view *view)
+{
+       const struct lttng_evaluation_session_consumed_size_comm *comm =
+                       (typeof(comm)) view->buffer.data;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (view->buffer.size < sizeof(*comm)) {
+               goto end;
+       }
+
+       evaluation = lttng_evaluation_session_consumed_size_create(
+                       comm->session_consumed);
+end:
+       return evaluation;
+}
+
+ssize_t lttng_evaluation_session_consumed_size_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       evaluation = create_evaluation_from_payload(view);
+       if (!evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       ret = sizeof(struct lttng_evaluation_session_consumed_size_comm);
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_threshold(
+               const struct lttng_condition *condition,
+               uint64_t *consumed_threshold_bytes)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->consumed_threshold_bytes.set) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_threshold(
+               struct lttng_condition *condition, uint64_t consumed_threshold_bytes)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       consumed->consumed_threshold_bytes.set = true;
+       consumed->consumed_threshold_bytes.value = consumed_threshold_bytes;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       if (!consumed->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = consumed->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_consumed_size_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_session_consumed_size *consumed;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) ||
+                       !session_name || strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(condition, struct lttng_condition_session_consumed_size,
+                       parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       if (consumed->session_name) {
+               free(consumed->session_name);
+       }
+       consumed->session_name = session_name_copy;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_session_consumed_size_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+       struct lttng_evaluation_session_consumed_size_comm comm;
+
+       consumed = container_of(evaluation,
+                       struct lttng_evaluation_session_consumed_size, parent);
+       comm.session_consumed = consumed->session_consumed;
+       return lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+}
+
+static
+void lttng_evaluation_session_consumed_size_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+
+       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+                       parent);
+       free(consumed);
+}
+
+struct lttng_evaluation *lttng_evaluation_session_consumed_size_create(
+               uint64_t consumed)
+{
+       struct lttng_evaluation_session_consumed_size *consumed_eval;
+
+       consumed_eval = (lttng_evaluation_session_consumed_size *) zmalloc(sizeof(struct lttng_evaluation_session_consumed_size));
+       if (!consumed_eval) {
+               goto end;
+       }
+
+       consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE;
+       consumed_eval->session_consumed = consumed;
+       consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize;
+       consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy;
+end:
+       return &consumed_eval->parent;
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_consumed_size_get_consumed_size(
+               const struct lttng_evaluation *evaluation,
+               uint64_t *session_consumed)
+{
+       struct lttng_evaluation_session_consumed_size *consumed;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) ||
+                       !session_consumed) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size,
+                       parent);
+       *session_consumed = consumed->session_consumed;
+end:
+       return status;
+}
diff --git a/src/common/conditions/session-rotation.c b/src/common/conditions/session-rotation.c
deleted file mode 100644 (file)
index d352718..0000000
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/location-internal.h>
-#include <stdbool.h>
-
-static
-bool lttng_condition_session_rotation_validate(
-               const struct lttng_condition *condition);
-static
-int lttng_condition_session_rotation_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload);
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b);
-static
-void lttng_condition_session_rotation_destroy(
-               struct lttng_condition *condition);
-
-static
-enum lttng_error_code lttng_condition_session_rotation_mi_serialize(
-               const struct lttng_condition *condition,
-               struct mi_writer *writer);
-
-static const
-struct lttng_condition rotation_condition_template = {
-       /* .type omitted; shall be set on creation. */
-       .validate = lttng_condition_session_rotation_validate,
-       .serialize = lttng_condition_session_rotation_serialize,
-       .equal = lttng_condition_session_rotation_is_equal,
-       .destroy = lttng_condition_session_rotation_destroy,
-       .mi_serialize = lttng_condition_session_rotation_mi_serialize,
-};
-
-static
-int lttng_evaluation_session_rotation_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload);
-static
-void lttng_evaluation_session_rotation_destroy(
-               struct lttng_evaluation *evaluation);
-
-static const
-struct lttng_evaluation rotation_evaluation_template = {
-       /* .type omitted; shall be set on creation. */
-       .serialize = lttng_evaluation_session_rotation_serialize,
-       .destroy = lttng_evaluation_session_rotation_destroy,
-};
-
-static
-bool is_rotation_condition(const struct lttng_condition *condition)
-{
-       enum lttng_condition_type type = lttng_condition_get_type(condition);
-
-       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
-                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
-{
-       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
-
-       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
-                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
-}
-
-static
-bool lttng_condition_session_rotation_validate(
-               const struct lttng_condition *condition)
-{
-       bool valid = false;
-       struct lttng_condition_session_rotation *rotation;
-
-       if (!condition) {
-               goto end;
-       }
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-       if (!rotation->session_name) {
-               ERR("Invalid session rotation condition: a target session name must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static
-int lttng_condition_session_rotation_serialize(
-               const struct lttng_condition *condition,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t session_name_len;
-       struct lttng_condition_session_rotation *rotation;
-       struct lttng_condition_session_rotation_comm rotation_comm;
-
-       if (!condition || !is_rotation_condition(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing session rotation condition");
-       rotation = container_of(condition, struct lttng_condition_session_rotation,
-                       parent);
-
-       session_name_len = strlen(rotation->session_name) + 1;
-       if (session_name_len > LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-
-       rotation_comm.session_name_len = session_name_len;
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &rotation_comm,
-                       sizeof(rotation_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       rotation->session_name, session_name_len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
-               const struct lttng_condition *_b)
-{
-       bool is_equal = false;
-       struct lttng_condition_session_rotation *a, *b;
-
-       a = container_of(_a, struct lttng_condition_session_rotation, parent);
-       b = container_of(_b, struct lttng_condition_session_rotation, parent);
-
-       /* Both session names must be set or both must be unset. */
-       if ((a->session_name && !b->session_name) ||
-                       (!a->session_name && b->session_name)) {
-               WARN("Comparing session rotation conditions with uninitialized session names.");
-               goto end;
-       }
-
-       if (a->session_name && b->session_name &&
-                       strcmp(a->session_name, b->session_name)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static
-void lttng_condition_session_rotation_destroy(
-               struct lttng_condition *condition)
-{
-       struct lttng_condition_session_rotation *rotation;
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-
-       free(rotation->session_name);
-       free(rotation);
-}
-
-static
-struct lttng_condition *lttng_condition_session_rotation_create(
-               enum lttng_condition_type type)
-{
-       struct lttng_condition_session_rotation *condition;
-
-       condition = zmalloc(sizeof(struct lttng_condition_session_rotation));
-       if (!condition) {
-               return NULL;
-       }
-
-       memcpy(&condition->parent, &rotation_condition_template,
-                       sizeof(condition->parent));
-       lttng_condition_init(&condition->parent, type);
-       return &condition->parent;
-}
-
-struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
-{
-       return lttng_condition_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
-{
-       return lttng_condition_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-ssize_t init_condition_from_payload(struct lttng_condition *condition,
-               struct lttng_payload_view *src_view)
-{
-       ssize_t ret, condition_size;
-       enum lttng_condition_status status;
-       const char *session_name;
-       struct lttng_buffer_view name_view;
-       const struct lttng_condition_session_rotation_comm *condition_comm;
-       struct lttng_payload_view condition_comm_view =
-                       lttng_payload_view_from_view(
-                                       src_view, 0, sizeof(*condition_comm));
-
-       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
-       name_view = lttng_buffer_view_from_view(&src_view->buffer,
-                                               sizeof(*condition_comm), condition_comm->session_name_len);
-
-       if (!lttng_buffer_view_is_valid(&name_view)) {
-               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
-               ret = -1;
-               goto end;
-       }
-
-       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
-               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
-               ret = -1;
-               goto end;
-       }
-
-       session_name = name_view.data;
-       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
-               ERR("Malformed session name encountered in condition buffer");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_condition_session_rotation_set_session_name(condition,
-                       session_name);
-       if (status != LTTNG_CONDITION_STATUS_OK) {
-               ERR("Failed to set buffer consumed session name");
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_condition_validate(condition)) {
-               ret = -1;
-               goto end;
-       }
-
-       condition_size = sizeof(*condition_comm) +
-                       (ssize_t) condition_comm->session_name_len;
-       ret = condition_size;
-end:
-       return ret;
-}
-
-static
-ssize_t lttng_condition_session_rotation_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **_condition,
-               enum lttng_condition_type type)
-{
-       ssize_t ret;
-       struct lttng_condition *condition = NULL;
-
-       switch (type) {
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               condition = lttng_condition_session_rotation_ongoing_create();
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               condition = lttng_condition_session_rotation_completed_create();
-               break;
-       default:
-               ret = -1;
-               goto error;
-       }
-
-       if (!_condition || !condition) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = init_condition_from_payload(condition, view);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_condition = condition;
-       return ret;
-error:
-       lttng_condition_destroy(condition);
-       return ret;
-}
-
-ssize_t lttng_condition_session_rotation_ongoing_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_payload(view,
-                       condition,
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
-}
-
-ssize_t lttng_condition_session_rotation_completed_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_condition **condition)
-{
-       return lttng_condition_session_rotation_create_from_payload(view,
-                       condition,
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
-}
-
-static
-struct lttng_evaluation *lttng_evaluation_session_rotation_create(
-               enum lttng_condition_type type, uint64_t id,
-               struct lttng_trace_archive_location *location)
-{
-       struct lttng_evaluation_session_rotation *evaluation;
-
-       evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation));
-       if (!evaluation) {
-               return NULL;
-       }
-
-       memcpy(&evaluation->parent, &rotation_evaluation_template,
-                       sizeof(evaluation->parent));
-       lttng_evaluation_init(&evaluation->parent, type);
-       evaluation->id = id;
-       if (location) {
-               lttng_trace_archive_location_get(location);
-       }
-       evaluation->location = location;
-       return &evaluation->parent;
-}
-
-static
-ssize_t create_evaluation_from_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret, size;
-       struct lttng_evaluation *evaluation = NULL;
-       struct lttng_trace_archive_location *location = NULL;
-       const struct lttng_evaluation_session_rotation_comm *comm;
-       struct lttng_payload_view comm_view = lttng_payload_view_from_view(
-                       view, 0, sizeof(*comm));
-
-       if (!lttng_payload_view_is_valid(&comm_view)) {
-               goto error;
-       }
-
-       comm = (typeof(comm)) comm_view.buffer.data;
-       size = sizeof(*comm);
-       if (comm->has_location) {
-               const struct lttng_buffer_view location_view =
-                               lttng_buffer_view_from_view(
-                                               &view->buffer, sizeof(*comm), -1);
-
-               if (!lttng_buffer_view_is_valid(&location_view)) {
-                       goto error;
-               }
-
-               ret = lttng_trace_archive_location_create_from_buffer(
-                               &location_view, &location);
-               if (ret < 0) {
-                       goto error;
-               }
-               size += ret;
-       }
-
-       evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
-                       location);
-       if (!evaluation) {
-               goto error;
-       }
-
-       lttng_trace_archive_location_put(location);
-       ret = size;
-       *_evaluation = evaluation;
-       return ret;
-error:
-       lttng_trace_archive_location_put(location);
-       evaluation = NULL;
-       return -1;
-}
-
-static
-ssize_t lttng_evaluation_session_rotation_create_from_payload(
-               enum lttng_condition_type type,
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **_evaluation)
-{
-       ssize_t ret;
-       struct lttng_evaluation *evaluation = NULL;
-
-       if (!_evaluation) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = create_evaluation_from_payload(type, view, &evaluation);
-       if (ret < 0) {
-               goto error;
-       }
-
-       *_evaluation = evaluation;
-       return ret;
-error:
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-ssize_t lttng_evaluation_session_rotation_ongoing_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_payload(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
-                       view, evaluation);
-}
-
-ssize_t lttng_evaluation_session_rotation_completed_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_evaluation **evaluation)
-{
-       return lttng_evaluation_session_rotation_create_from_payload(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
-                       view, evaluation);
-}
-
-struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
-               uint64_t id)
-{
-       return lttng_evaluation_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
-                       NULL);
-}
-
-struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
-               uint64_t id, struct lttng_trace_archive_location *location)
-{
-       return lttng_evaluation_session_rotation_create(
-                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
-                       location);
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_get_session_name(
-               const struct lttng_condition *condition,
-               const char **session_name)
-{
-       struct lttng_condition_session_rotation *rotation;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !is_rotation_condition(condition) || !session_name) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(condition, struct lttng_condition_session_rotation,
-                       parent);
-       if (!rotation->session_name) {
-               status = LTTNG_CONDITION_STATUS_UNSET;
-               goto end;
-       }
-       *session_name = rotation->session_name;
-end:
-       return status;
-}
-
-enum lttng_condition_status
-lttng_condition_session_rotation_set_session_name(
-               struct lttng_condition *condition, const char *session_name)
-{
-       char *session_name_copy;
-       struct lttng_condition_session_rotation *rotation;
-       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
-
-       if (!condition || !is_rotation_condition(condition) ||
-                       !session_name || strlen(session_name) == 0) {
-               status = LTTNG_CONDITION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(condition,
-                       struct lttng_condition_session_rotation, parent);
-       session_name_copy = strdup(session_name);
-       if (!session_name_copy) {
-               status = LTTNG_CONDITION_STATUS_ERROR;
-               goto end;
-       }
-
-       free(rotation->session_name);
-       rotation->session_name = session_name_copy;
-end:
-       return status;
-}
-
-static
-int lttng_evaluation_session_rotation_serialize(
-               const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_evaluation_session_rotation *rotation;
-       struct lttng_evaluation_session_rotation_comm comm = { 0 };
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       comm.id = rotation->id;
-       comm.has_location = !!rotation->location;
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-       if (!rotation->location) {
-               goto end;
-       }
-       ret = lttng_trace_archive_location_serialize(rotation->location,
-                       &payload->buffer);
-end:
-       return ret;
-}
-
-static
-void lttng_evaluation_session_rotation_destroy(
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_evaluation_session_rotation *rotation;
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       lttng_trace_archive_location_put(rotation->location);
-       free(rotation);
-}
-
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_get_id(
-               const struct lttng_evaluation *evaluation, uint64_t *id)
-{
-       const struct lttng_evaluation_session_rotation *rotation;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       *id = rotation->id;
-end:
-       return status;
-}
-
-/*
- * The public API assumes that trace archive locations are always provided as
- * "constant". This means that the user of liblttng-ctl never has to destroy a
- * trace archive location. Hence, users of liblttng-ctl have no visibility of
- * the reference counting of archive locations.
- */
-enum lttng_evaluation_status
-lttng_evaluation_session_rotation_completed_get_location(
-               const struct lttng_evaluation *evaluation,
-               const struct lttng_trace_archive_location **location)
-{
-       const struct lttng_evaluation_session_rotation *rotation;
-       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
-
-       if (!evaluation || !location ||
-                       evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
-               status = LTTNG_EVALUATION_STATUS_INVALID;
-               goto end;
-       }
-
-       rotation = container_of(evaluation,
-                       struct lttng_evaluation_session_rotation, parent);
-       *location = rotation->location;
-end:
-       return status;
-}
-
-static
-enum lttng_error_code lttng_condition_session_rotation_mi_serialize(
-               const struct lttng_condition *condition,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_condition_status status;
-       const char *session_name = NULL;
-       const char *type_element_str = NULL;
-
-       LTTNG_ASSERT(condition);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(is_rotation_condition(condition));
-
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               type_element_str =
-                               mi_lttng_element_condition_session_rotation_completed;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               type_element_str =
-                               mi_lttng_element_condition_session_rotation_ongoing;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       status = lttng_condition_session_rotation_get_session_name(
-                       condition, &session_name);
-       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
-       LTTNG_ASSERT(session_name);
-
-       /* Open condition session rotation_* element. */
-       ret = mi_lttng_writer_open_element(writer, type_element_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Session name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_session_name, session_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close condition session rotation element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/conditions/session-rotation.cpp b/src/common/conditions/session-rotation.cpp
new file mode 100644 (file)
index 0000000..7e59895
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/location-internal.h>
+#include <stdbool.h>
+
+static
+bool lttng_condition_session_rotation_validate(
+               const struct lttng_condition *condition);
+static
+int lttng_condition_session_rotation_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload);
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b);
+static
+void lttng_condition_session_rotation_destroy(
+               struct lttng_condition *condition);
+
+static
+enum lttng_error_code lttng_condition_session_rotation_mi_serialize(
+               const struct lttng_condition *condition,
+               struct mi_writer *writer);
+
+static const
+struct lttng_condition rotation_condition_template = {
+       {},
+       LTTNG_CONDITION_TYPE_UNKNOWN, /* type unset, shall be set on creation. */
+       lttng_condition_session_rotation_validate,
+       lttng_condition_session_rotation_serialize,
+       lttng_condition_session_rotation_is_equal,
+       lttng_condition_session_rotation_destroy,
+       lttng_condition_session_rotation_mi_serialize,
+};
+
+static
+int lttng_evaluation_session_rotation_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload);
+static
+void lttng_evaluation_session_rotation_destroy(
+               struct lttng_evaluation *evaluation);
+
+static const
+struct lttng_evaluation rotation_evaluation_template = {
+       LTTNG_CONDITION_TYPE_UNKNOWN, /* type unset, shall be set on creation. */
+       lttng_evaluation_session_rotation_serialize,
+       lttng_evaluation_session_rotation_destroy,
+};
+
+static
+bool is_rotation_condition(const struct lttng_condition *condition)
+{
+       enum lttng_condition_type type = lttng_condition_get_type(condition);
+
+       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool is_rotation_evaluation(const struct lttng_evaluation *evaluation)
+{
+       enum lttng_condition_type type = lttng_evaluation_get_type(evaluation);
+
+       return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING ||
+                       type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED;
+}
+
+static
+bool lttng_condition_session_rotation_validate(
+               const struct lttng_condition *condition)
+{
+       bool valid = false;
+       struct lttng_condition_session_rotation *rotation;
+
+       if (!condition) {
+               goto end;
+       }
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+       if (!rotation->session_name) {
+               ERR("Invalid session rotation condition: a target session name must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static
+int lttng_condition_session_rotation_serialize(
+               const struct lttng_condition *condition,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t session_name_len;
+       struct lttng_condition_session_rotation *rotation;
+       struct lttng_condition_session_rotation_comm rotation_comm;
+
+       if (!condition || !is_rotation_condition(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing session rotation condition");
+       rotation = container_of(condition, struct lttng_condition_session_rotation,
+                       parent);
+
+       session_name_len = strlen(rotation->session_name) + 1;
+       if (session_name_len > LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+
+       rotation_comm.session_name_len = session_name_len;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &rotation_comm,
+                       sizeof(rotation_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       rotation->session_name, session_name_len);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static
+bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a,
+               const struct lttng_condition *_b)
+{
+       bool is_equal = false;
+       struct lttng_condition_session_rotation *a, *b;
+
+       a = container_of(_a, struct lttng_condition_session_rotation, parent);
+       b = container_of(_b, struct lttng_condition_session_rotation, parent);
+
+       /* Both session names must be set or both must be unset. */
+       if ((a->session_name && !b->session_name) ||
+                       (!a->session_name && b->session_name)) {
+               WARN("Comparing session rotation conditions with uninitialized session names.");
+               goto end;
+       }
+
+       if (a->session_name && b->session_name &&
+                       strcmp(a->session_name, b->session_name)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static
+void lttng_condition_session_rotation_destroy(
+               struct lttng_condition *condition)
+{
+       struct lttng_condition_session_rotation *rotation;
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+
+       free(rotation->session_name);
+       free(rotation);
+}
+
+static
+struct lttng_condition *lttng_condition_session_rotation_create(
+               enum lttng_condition_type type)
+{
+       struct lttng_condition_session_rotation *condition;
+
+       condition = (lttng_condition_session_rotation *) zmalloc(sizeof(struct lttng_condition_session_rotation));
+       if (!condition) {
+               return NULL;
+       }
+
+       memcpy(&condition->parent, &rotation_condition_template,
+                       sizeof(condition->parent));
+       lttng_condition_init(&condition->parent, type);
+       return &condition->parent;
+}
+
+struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void)
+{
+       return lttng_condition_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+struct lttng_condition *lttng_condition_session_rotation_completed_create(void)
+{
+       return lttng_condition_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+ssize_t init_condition_from_payload(struct lttng_condition *condition,
+               struct lttng_payload_view *src_view)
+{
+       ssize_t ret, condition_size;
+       enum lttng_condition_status status;
+       const char *session_name;
+       struct lttng_buffer_view name_view;
+       const struct lttng_condition_session_rotation_comm *condition_comm;
+       struct lttng_payload_view condition_comm_view =
+                       lttng_payload_view_from_view(
+                                       src_view, 0, sizeof(*condition_comm));
+
+       if (!lttng_payload_view_is_valid(&condition_comm_view)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       condition_comm = (typeof(condition_comm)) src_view->buffer.data;
+       name_view = lttng_buffer_view_from_view(&src_view->buffer,
+                                               sizeof(*condition_comm), condition_comm->session_name_len);
+
+       if (!lttng_buffer_view_is_valid(&name_view)) {
+               ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name");
+               ret = -1;
+               goto end;
+       }
+
+       if (condition_comm->session_name_len > LTTNG_NAME_MAX) {
+               ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME");
+               ret = -1;
+               goto end;
+       }
+
+       session_name = name_view.data;
+       if (*(session_name + condition_comm->session_name_len - 1) != '\0') {
+               ERR("Malformed session name encountered in condition buffer");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_condition_session_rotation_set_session_name(condition,
+                       session_name);
+       if (status != LTTNG_CONDITION_STATUS_OK) {
+               ERR("Failed to set buffer consumed session name");
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_condition_validate(condition)) {
+               ret = -1;
+               goto end;
+       }
+
+       condition_size = sizeof(*condition_comm) +
+                       (ssize_t) condition_comm->session_name_len;
+       ret = condition_size;
+end:
+       return ret;
+}
+
+static
+ssize_t lttng_condition_session_rotation_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **_condition,
+               enum lttng_condition_type type)
+{
+       ssize_t ret;
+       struct lttng_condition *condition = NULL;
+
+       switch (type) {
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               condition = lttng_condition_session_rotation_ongoing_create();
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               condition = lttng_condition_session_rotation_completed_create();
+               break;
+       default:
+               ret = -1;
+               goto error;
+       }
+
+       if (!_condition || !condition) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = init_condition_from_payload(condition, view);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_condition = condition;
+       return ret;
+error:
+       lttng_condition_destroy(condition);
+       return ret;
+}
+
+ssize_t lttng_condition_session_rotation_ongoing_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_payload(view,
+                       condition,
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING);
+}
+
+ssize_t lttng_condition_session_rotation_completed_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_condition **condition)
+{
+       return lttng_condition_session_rotation_create_from_payload(view,
+                       condition,
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED);
+}
+
+static
+struct lttng_evaluation *lttng_evaluation_session_rotation_create(
+               enum lttng_condition_type type, uint64_t id,
+               struct lttng_trace_archive_location *location)
+{
+       struct lttng_evaluation_session_rotation *evaluation;
+
+       evaluation = (lttng_evaluation_session_rotation *) zmalloc(sizeof(struct lttng_evaluation_session_rotation));
+       if (!evaluation) {
+               return NULL;
+       }
+
+       memcpy(&evaluation->parent, &rotation_evaluation_template,
+                       sizeof(evaluation->parent));
+       lttng_evaluation_init(&evaluation->parent, type);
+       evaluation->id = id;
+       if (location) {
+               lttng_trace_archive_location_get(location);
+       }
+       evaluation->location = location;
+       return &evaluation->parent;
+}
+
+static
+ssize_t create_evaluation_from_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret, size;
+       struct lttng_evaluation *evaluation = NULL;
+       struct lttng_trace_archive_location *location = NULL;
+       const struct lttng_evaluation_session_rotation_comm *comm;
+       struct lttng_payload_view comm_view = lttng_payload_view_from_view(
+                       view, 0, sizeof(*comm));
+
+       if (!lttng_payload_view_is_valid(&comm_view)) {
+               goto error;
+       }
+
+       comm = (typeof(comm)) comm_view.buffer.data;
+       size = sizeof(*comm);
+       if (comm->has_location) {
+               const struct lttng_buffer_view location_view =
+                               lttng_buffer_view_from_view(
+                                               &view->buffer, sizeof(*comm), -1);
+
+               if (!lttng_buffer_view_is_valid(&location_view)) {
+                       goto error;
+               }
+
+               ret = lttng_trace_archive_location_create_from_buffer(
+                               &location_view, &location);
+               if (ret < 0) {
+                       goto error;
+               }
+               size += ret;
+       }
+
+       evaluation = lttng_evaluation_session_rotation_create(type, comm->id,
+                       location);
+       if (!evaluation) {
+               goto error;
+       }
+
+       lttng_trace_archive_location_put(location);
+       ret = size;
+       *_evaluation = evaluation;
+       return ret;
+error:
+       lttng_trace_archive_location_put(location);
+       evaluation = NULL;
+       return -1;
+}
+
+static
+ssize_t lttng_evaluation_session_rotation_create_from_payload(
+               enum lttng_condition_type type,
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **_evaluation)
+{
+       ssize_t ret;
+       struct lttng_evaluation *evaluation = NULL;
+
+       if (!_evaluation) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = create_evaluation_from_payload(type, view, &evaluation);
+       if (ret < 0) {
+               goto error;
+       }
+
+       *_evaluation = evaluation;
+       return ret;
+error:
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+ssize_t lttng_evaluation_session_rotation_ongoing_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_payload(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING,
+                       view, evaluation);
+}
+
+ssize_t lttng_evaluation_session_rotation_completed_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_evaluation **evaluation)
+{
+       return lttng_evaluation_session_rotation_create_from_payload(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED,
+                       view, evaluation);
+}
+
+struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create(
+               uint64_t id)
+{
+       return lttng_evaluation_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id,
+                       NULL);
+}
+
+struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create(
+               uint64_t id, struct lttng_trace_archive_location *location)
+{
+       return lttng_evaluation_session_rotation_create(
+                       LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id,
+                       location);
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_get_session_name(
+               const struct lttng_condition *condition,
+               const char **session_name)
+{
+       struct lttng_condition_session_rotation *rotation;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !is_rotation_condition(condition) || !session_name) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(condition, struct lttng_condition_session_rotation,
+                       parent);
+       if (!rotation->session_name) {
+               status = LTTNG_CONDITION_STATUS_UNSET;
+               goto end;
+       }
+       *session_name = rotation->session_name;
+end:
+       return status;
+}
+
+enum lttng_condition_status
+lttng_condition_session_rotation_set_session_name(
+               struct lttng_condition *condition, const char *session_name)
+{
+       char *session_name_copy;
+       struct lttng_condition_session_rotation *rotation;
+       enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK;
+
+       if (!condition || !is_rotation_condition(condition) ||
+                       !session_name || strlen(session_name) == 0) {
+               status = LTTNG_CONDITION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(condition,
+                       struct lttng_condition_session_rotation, parent);
+       session_name_copy = strdup(session_name);
+       if (!session_name_copy) {
+               status = LTTNG_CONDITION_STATUS_ERROR;
+               goto end;
+       }
+
+       free(rotation->session_name);
+       rotation->session_name = session_name_copy;
+end:
+       return status;
+}
+
+static
+int lttng_evaluation_session_rotation_serialize(
+               const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_evaluation_session_rotation *rotation;
+       struct lttng_evaluation_session_rotation_comm comm = { 0 };
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       comm.id = rotation->id;
+       comm.has_location = !!rotation->location;
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+       if (!rotation->location) {
+               goto end;
+       }
+       ret = lttng_trace_archive_location_serialize(rotation->location,
+                       &payload->buffer);
+end:
+       return ret;
+}
+
+static
+void lttng_evaluation_session_rotation_destroy(
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_evaluation_session_rotation *rotation;
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       lttng_trace_archive_location_put(rotation->location);
+       free(rotation);
+}
+
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_get_id(
+               const struct lttng_evaluation *evaluation, uint64_t *id)
+{
+       const struct lttng_evaluation_session_rotation *rotation;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !id || !is_rotation_evaluation(evaluation)) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       *id = rotation->id;
+end:
+       return status;
+}
+
+/*
+ * The public API assumes that trace archive locations are always provided as
+ * "constant". This means that the user of liblttng-ctl never has to destroy a
+ * trace archive location. Hence, users of liblttng-ctl have no visibility of
+ * the reference counting of archive locations.
+ */
+enum lttng_evaluation_status
+lttng_evaluation_session_rotation_completed_get_location(
+               const struct lttng_evaluation *evaluation,
+               const struct lttng_trace_archive_location **location)
+{
+       const struct lttng_evaluation_session_rotation *rotation;
+       enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK;
+
+       if (!evaluation || !location ||
+                       evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) {
+               status = LTTNG_EVALUATION_STATUS_INVALID;
+               goto end;
+       }
+
+       rotation = container_of(evaluation,
+                       struct lttng_evaluation_session_rotation, parent);
+       *location = rotation->location;
+end:
+       return status;
+}
+
+static
+enum lttng_error_code lttng_condition_session_rotation_mi_serialize(
+               const struct lttng_condition *condition,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_condition_status status;
+       const char *session_name = NULL;
+       const char *type_element_str = NULL;
+
+       LTTNG_ASSERT(condition);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(is_rotation_condition(condition));
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               type_element_str =
+                               mi_lttng_element_condition_session_rotation_completed;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               type_element_str =
+                               mi_lttng_element_condition_session_rotation_ongoing;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       status = lttng_condition_session_rotation_get_session_name(
+                       condition, &session_name);
+       LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK);
+       LTTNG_ASSERT(session_name);
+
+       /* Open condition session rotation_* element. */
+       ret = mi_lttng_writer_open_element(writer, type_element_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Session name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_session_name, session_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close condition session rotation element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/context.c b/src/common/context.c
deleted file mode 100644 (file)
index e2e2b60..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "context.h"
-#include <stddef.h>
-#include <string.h>
-#include <common/error.h>
-#include <common/macros.h>
-
-int parse_application_context(const char *str, char **out_provider_name,
-               char **out_ctx_name)
-{
-       const char app_ctx_prefix[] = "$app.";
-       char *provider_name = NULL, *ctx_name = NULL;
-       size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len;
-
-       if (!str || !out_provider_name || !out_ctx_name) {
-               goto not_found;
-       }
-
-       len = strlen(str);
-       if (len <= sizeof(app_ctx_prefix) - 1) {
-               goto not_found;
-       }
-
-       /* String starts with $app. */
-       if (strncmp(str, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) {
-               goto not_found;
-       }
-
-       /* Validate that the ':' separator is present. */
-       for (i = sizeof(app_ctx_prefix); i < len; i++) {
-               const char c = str[i];
-
-               if (c == ':') {
-                       colon_pos = i;
-                       break;
-               }
-       }
-
-       /*
-        * No colon found or no ctx name ("$app.provider:") or no provider name
-        * given ("$app.:..."), which is invalid.
-        */
-       if (!colon_pos || colon_pos == len ||
-                       colon_pos == sizeof(app_ctx_prefix)) {
-               goto not_found;
-       }
-
-       provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2;
-       provider_name = zmalloc(provider_name_len);
-       if (!provider_name) {
-               PERROR("malloc provider_name");
-               goto not_found;
-       }
-       strncpy(provider_name, str + sizeof(app_ctx_prefix) - 1,
-                       provider_name_len - 1);
-
-       ctx_name_len = len - colon_pos;
-       ctx_name = zmalloc(ctx_name_len);
-       if (!ctx_name) {
-               PERROR("malloc ctx_name");
-               goto not_found;
-       }
-       strncpy(ctx_name, str + colon_pos + 1, ctx_name_len - 1);
-
-       *out_provider_name = provider_name;
-       *out_ctx_name = ctx_name;
-       return 0;
-not_found:
-       free(provider_name);
-       free(ctx_name);
-       return -1;
-}
-
diff --git a/src/common/context.cpp b/src/common/context.cpp
new file mode 100644 (file)
index 0000000..ab61a81
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "context.h"
+#include <stddef.h>
+#include <string.h>
+#include <common/error.h>
+#include <common/macros.h>
+
+int parse_application_context(const char *str, char **out_provider_name,
+               char **out_ctx_name)
+{
+       const char app_ctx_prefix[] = "$app.";
+       char *provider_name = NULL, *ctx_name = NULL;
+       size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len;
+
+       if (!str || !out_provider_name || !out_ctx_name) {
+               goto not_found;
+       }
+
+       len = strlen(str);
+       if (len <= sizeof(app_ctx_prefix) - 1) {
+               goto not_found;
+       }
+
+       /* String starts with $app. */
+       if (strncmp(str, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) {
+               goto not_found;
+       }
+
+       /* Validate that the ':' separator is present. */
+       for (i = sizeof(app_ctx_prefix); i < len; i++) {
+               const char c = str[i];
+
+               if (c == ':') {
+                       colon_pos = i;
+                       break;
+               }
+       }
+
+       /*
+        * No colon found or no ctx name ("$app.provider:") or no provider name
+        * given ("$app.:..."), which is invalid.
+        */
+       if (!colon_pos || colon_pos == len ||
+                       colon_pos == sizeof(app_ctx_prefix)) {
+               goto not_found;
+       }
+
+       provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2;
+       provider_name = (char *) zmalloc(provider_name_len);
+       if (!provider_name) {
+               PERROR("malloc provider_name");
+               goto not_found;
+       }
+       strncpy(provider_name, str + sizeof(app_ctx_prefix) - 1,
+                       provider_name_len - 1);
+
+       ctx_name_len = len - colon_pos;
+       ctx_name = (char *) zmalloc(ctx_name_len);
+       if (!ctx_name) {
+               PERROR("malloc ctx_name");
+               goto not_found;
+       }
+       strncpy(ctx_name, str + colon_pos + 1, ctx_name_len - 1);
+
+       *out_provider_name = provider_name;
+       *out_ctx_name = ctx_name;
+       return 0;
+not_found:
+       free(provider_name);
+       free(ctx_name);
+       return -1;
+}
+
diff --git a/src/common/credentials.c b/src/common/credentials.c
deleted file mode 100644 (file)
index fd8b447..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <stdbool.h>
-#include "credentials.h"
-
-uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds)
-{
-       return LTTNG_OPTIONAL_GET(creds->uid);
-}
-
-gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds)
-{
-       return LTTNG_OPTIONAL_GET(creds->gid);
-}
-
-bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a,
-               const struct lttng_credentials *b)
-{
-       LTTNG_ASSERT(a);
-       LTTNG_ASSERT(b);
-
-       /* XOR on the is_set value */
-       if (!!a->uid.is_set != !!b->uid.is_set) {
-               return false;
-       }
-
-       if (!a->uid.is_set && !b->uid.is_set) {
-               return true;
-       }
-
-       /* Both a and b are set. */
-       return a->uid.value == b->uid.value;
-}
-
-bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a,
-               const struct lttng_credentials *b)
-{
-       LTTNG_ASSERT(a);
-       LTTNG_ASSERT(b);
-
-       /* XOR on the is_set value */
-       if (!!a->gid.is_set != !!b->gid.is_set) {
-               return false;
-       }
-
-       if (!a->gid.is_set && !b->gid.is_set) {
-               return true;
-       }
-
-       /* Both a and b are set. */
-       return a->gid.value == b->gid.value;
-}
-
-bool lttng_credentials_is_equal(const struct lttng_credentials *a,
-               const struct lttng_credentials *b)
-{
-       LTTNG_ASSERT(a);
-       LTTNG_ASSERT(b);
-
-       return lttng_credentials_is_equal_uid(a, b) &&
-                       lttng_credentials_is_equal_gid(a, b);
-}
diff --git a/src/common/credentials.cpp b/src/common/credentials.cpp
new file mode 100644 (file)
index 0000000..fd8b447
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <stdbool.h>
+#include "credentials.h"
+
+uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds)
+{
+       return LTTNG_OPTIONAL_GET(creds->uid);
+}
+
+gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds)
+{
+       return LTTNG_OPTIONAL_GET(creds->gid);
+}
+
+bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       LTTNG_ASSERT(a);
+       LTTNG_ASSERT(b);
+
+       /* XOR on the is_set value */
+       if (!!a->uid.is_set != !!b->uid.is_set) {
+               return false;
+       }
+
+       if (!a->uid.is_set && !b->uid.is_set) {
+               return true;
+       }
+
+       /* Both a and b are set. */
+       return a->uid.value == b->uid.value;
+}
+
+bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       LTTNG_ASSERT(a);
+       LTTNG_ASSERT(b);
+
+       /* XOR on the is_set value */
+       if (!!a->gid.is_set != !!b->gid.is_set) {
+               return false;
+       }
+
+       if (!a->gid.is_set && !b->gid.is_set) {
+               return true;
+       }
+
+       /* Both a and b are set. */
+       return a->gid.value == b->gid.value;
+}
+
+bool lttng_credentials_is_equal(const struct lttng_credentials *a,
+               const struct lttng_credentials *b)
+{
+       LTTNG_ASSERT(a);
+       LTTNG_ASSERT(b);
+
+       return lttng_credentials_is_equal_uid(a, b) &&
+                       lttng_credentials_is_equal_gid(a, b);
+}
diff --git a/src/common/daemonize.c b/src/common/daemonize.c
deleted file mode 100644 (file)
index 16af5a3..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <unistd.h>
-#include <common/compat/paths.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-
-#include <urcu/system.h>
-
-#include <common/daemonize.h>
-#include <common/error.h>
-
-int lttng_daemonize(pid_t *child_ppid, int *completion_flag,
-               int close_fds)
-{
-       pid_t pid;
-
-       /* Get parent pid of this process. */
-       *child_ppid = getppid();
-
-       pid = fork();
-       if (pid < 0) {
-               PERROR("fork");
-               goto error;
-       } else if (pid == 0) {
-               int fd;
-               pid_t sid;
-               int ret;
-
-               /* Child */
-
-               /*
-                * Get the newly created parent pid so we can signal
-                * that process when we are ready to operate.
-                */
-               *child_ppid = getppid();
-
-               sid = setsid();
-               if (sid < 0) {
-                       PERROR("setsid");
-                       goto error;
-               }
-
-               /*
-                * Try to change directory to /. If we can't well at
-                * least notify.
-                */
-               ret = chdir("/");
-               if (ret < 0) {
-                       PERROR("chdir");
-               }
-
-               if (close_fds) {
-                       fd = open(_PATH_DEVNULL, O_RDWR, 0);
-                       if (fd < 0) {
-                               PERROR("open %s", _PATH_DEVNULL);
-                               /*
-                                * Let 0, 1 and 2 open since we can't
-                                * bind them to /dev/null.
-                                */
-                       } else {
-                               (void) dup2(fd, STDIN_FILENO);
-                               (void) dup2(fd, STDOUT_FILENO);
-                               (void) dup2(fd, STDERR_FILENO);
-                               if (fd > 2) {
-                                       ret = close(fd);
-                                       if (ret < 0) {
-                                               PERROR("close");
-                                       }
-                               }
-                       }
-               }
-               goto end;
-       } else {
-               /* Parent */
-
-               /*
-                * Waiting for child to notify this parent that it can
-                * exit. Note that sleep() is interrupted before the 1
-                * second delay as soon as the signal is received, so it
-                * will not cause visible delay for the user.
-                */
-               while (!CMM_LOAD_SHARED(*completion_flag)) {
-                       int status;
-                       pid_t ret;
-
-                       /*
-                        * Check if child exists without blocking. If
-                        * so, we have to stop this parent process and
-                        * return an error.
-                        */
-                       ret = waitpid(pid, &status, WNOHANG);
-                       if (ret < 0 || (ret != 0 && WIFEXITED(status))) {
-                               /* The child exited somehow or was not valid. */
-                               goto error;
-                       }
-                       sleep(1);
-               }
-
-               /*
-                * From this point on, the parent can exit and the child
-                * is now an operational session daemon ready to serve
-                * clients and applications.
-                */
-               exit(EXIT_SUCCESS);
-       }
-
-end:
-       return 0;
-
-error:
-       return -1;
-}
diff --git a/src/common/daemonize.cpp b/src/common/daemonize.cpp
new file mode 100644 (file)
index 0000000..16af5a3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <unistd.h>
+#include <common/compat/paths.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+#include <urcu/system.h>
+
+#include <common/daemonize.h>
+#include <common/error.h>
+
+int lttng_daemonize(pid_t *child_ppid, int *completion_flag,
+               int close_fds)
+{
+       pid_t pid;
+
+       /* Get parent pid of this process. */
+       *child_ppid = getppid();
+
+       pid = fork();
+       if (pid < 0) {
+               PERROR("fork");
+               goto error;
+       } else if (pid == 0) {
+               int fd;
+               pid_t sid;
+               int ret;
+
+               /* Child */
+
+               /*
+                * Get the newly created parent pid so we can signal
+                * that process when we are ready to operate.
+                */
+               *child_ppid = getppid();
+
+               sid = setsid();
+               if (sid < 0) {
+                       PERROR("setsid");
+                       goto error;
+               }
+
+               /*
+                * Try to change directory to /. If we can't well at
+                * least notify.
+                */
+               ret = chdir("/");
+               if (ret < 0) {
+                       PERROR("chdir");
+               }
+
+               if (close_fds) {
+                       fd = open(_PATH_DEVNULL, O_RDWR, 0);
+                       if (fd < 0) {
+                               PERROR("open %s", _PATH_DEVNULL);
+                               /*
+                                * Let 0, 1 and 2 open since we can't
+                                * bind them to /dev/null.
+                                */
+                       } else {
+                               (void) dup2(fd, STDIN_FILENO);
+                               (void) dup2(fd, STDOUT_FILENO);
+                               (void) dup2(fd, STDERR_FILENO);
+                               if (fd > 2) {
+                                       ret = close(fd);
+                                       if (ret < 0) {
+                                               PERROR("close");
+                                       }
+                               }
+                       }
+               }
+               goto end;
+       } else {
+               /* Parent */
+
+               /*
+                * Waiting for child to notify this parent that it can
+                * exit. Note that sleep() is interrupted before the 1
+                * second delay as soon as the signal is received, so it
+                * will not cause visible delay for the user.
+                */
+               while (!CMM_LOAD_SHARED(*completion_flag)) {
+                       int status;
+                       pid_t ret;
+
+                       /*
+                        * Check if child exists without blocking. If
+                        * so, we have to stop this parent process and
+                        * return an error.
+                        */
+                       ret = waitpid(pid, &status, WNOHANG);
+                       if (ret < 0 || (ret != 0 && WIFEXITED(status))) {
+                               /* The child exited somehow or was not valid. */
+                               goto error;
+                       }
+                       sleep(1);
+               }
+
+               /*
+                * From this point on, the parent can exit and the child
+                * is now an operational session daemon ready to serve
+                * clients and applications.
+                */
+               exit(EXIT_SUCCESS);
+       }
+
+end:
+       return 0;
+
+error:
+       return -1;
+}
diff --git a/src/common/defaults.c b/src/common/defaults.c
deleted file mode 100644 (file)
index 2550784..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2012 Simon Marchi <simon.marchi@polymtl.ca>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <stddef.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <sys/resource.h>
-#include <pthread.h>
-
-#include "defaults.h"
-#include "macros.h"
-#include "error.h"
-
-static int pthread_attr_init_done;
-static pthread_attr_t tattr;
-
-static size_t get_page_size(void)
-{
-       const long ret = sysconf(_SC_PAGE_SIZE);
-
-       if (ret < 0) {
-               /*
-                * Fatal error since there is no safe way to recover from this.
-                */
-               PERROR("Failed to get system page size using sysconf(_SC_PAGE_SIZE)");
-               abort();
-       }
-
-       return (size_t) ret;
-}
-
-size_t default_get_channel_subbuf_size(void)
-{
-       return max(_DEFAULT_CHANNEL_SUBBUF_SIZE, get_page_size());
-}
-
-size_t default_get_metadata_subbuf_size(void)
-{
-       return max(DEFAULT_METADATA_SUBBUF_SIZE, get_page_size());
-}
-
-size_t default_get_kernel_channel_subbuf_size(void)
-{
-       return max(DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE, get_page_size());
-}
-
-size_t default_get_ust_pid_channel_subbuf_size(void)
-{
-       return max(DEFAULT_UST_PID_CHANNEL_SUBBUF_SIZE, get_page_size());
-}
-
-size_t default_get_ust_uid_channel_subbuf_size(void)
-{
-       return max(DEFAULT_UST_UID_CHANNEL_SUBBUF_SIZE, get_page_size());
-}
-
-pthread_attr_t *default_pthread_attr(void)
-{
-       if (pthread_attr_init_done) {
-               return &tattr;
-       }
-
-       WARN("Uninitialized pthread attributes, using libc defaults.");
-       return NULL;
-}
-
-static void __attribute__((constructor)) init_default_pthread_attr(void)
-{
-       int ret;
-       struct rlimit rlim;
-       size_t pthread_ss, system_ss, selected_ss;
-
-       ret = pthread_attr_init(&tattr);
-       if (ret) {
-               errno = ret;
-               PERROR("pthread_attr_init");
-               goto error;
-       }
-
-       /* Get system stack size limits. */
-       ret = getrlimit(RLIMIT_STACK, &rlim);
-       if (ret < 0) {
-               PERROR("getrlimit");
-               goto error_destroy;
-       }
-       DBG("Stack size limits: soft %lld, hard %lld bytes",
-                       (long long) rlim.rlim_cur,
-                       (long long) rlim.rlim_max);
-
-       /*
-        * getrlimit() may return a stack size of "-1", meaning "unlimited".
-        * In this case, we impose a known-good default minimum value which will
-        * override the libc's default stack size if it is smaller.
-        */
-       system_ss = rlim.rlim_cur != -1 ? rlim.rlim_cur :
-                       DEFAULT_LTTNG_THREAD_STACK_SIZE;
-
-       /* Get pthread default thread stack size. */
-       ret = pthread_attr_getstacksize(&tattr, &pthread_ss);
-       if (ret < 0) {
-               PERROR("pthread_attr_getstacksize");
-               goto error_destroy;
-       }
-       DBG("Default pthread stack size is %zu bytes", pthread_ss);
-
-       selected_ss = max_t(size_t, pthread_ss, system_ss);
-       if (selected_ss < DEFAULT_LTTNG_THREAD_STACK_SIZE) {
-               DBG("Default stack size is too small, setting it to %zu bytes",
-                       (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE);
-               selected_ss = DEFAULT_LTTNG_THREAD_STACK_SIZE;
-       }
-
-       if (rlim.rlim_max > 0 && selected_ss > rlim.rlim_max) {
-               WARN("Your system's stack size restrictions (%zu bytes) may be too low for the LTTng daemons to function properly, please set the stack size limit to at least %zu bytes to ensure reliable operation",
-                       (size_t) rlim.rlim_max, (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE);
-               selected_ss = (size_t) rlim.rlim_max;
-       }
-
-       ret = pthread_attr_setstacksize(&tattr, selected_ss);
-       if (ret < 0) {
-               PERROR("pthread_attr_setstacksize");
-               goto error_destroy;
-       }
-       pthread_attr_init_done = 1;
-error:
-       return;
-error_destroy:
-       ret = pthread_attr_destroy(&tattr);
-       if (ret) {
-               errno = ret;
-               PERROR("pthread_attr_destroy");
-       }
-}
-
-static void __attribute__((destructor)) fini_default_pthread_attr(void)
-{
-       int ret;
-
-       if (!pthread_attr_init_done) {
-               return;
-       }
-
-       ret = pthread_attr_destroy(&tattr);
-       if (ret) {
-               errno = ret;
-               PERROR("pthread_attr_destroy");
-       }
-}
diff --git a/src/common/defaults.cpp b/src/common/defaults.cpp
new file mode 100644 (file)
index 0000000..5ecb435
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 Simon Marchi <simon.marchi@polymtl.ca>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <stddef.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <algorithm>
+
+#include "defaults.h"
+#include "macros.h"
+#include "error.h"
+
+static int pthread_attr_init_done;
+static pthread_attr_t tattr;
+
+static size_t get_page_size(void)
+{
+       const long ret = sysconf(_SC_PAGE_SIZE);
+
+       if (ret < 0) {
+               /*
+                * Fatal error since there is no safe way to recover from this.
+                */
+               PERROR("Failed to get system page size using sysconf(_SC_PAGE_SIZE)");
+               abort();
+       }
+
+       return (size_t) ret;
+}
+
+size_t default_get_channel_subbuf_size(void)
+{
+       return std::max<size_t>(_DEFAULT_CHANNEL_SUBBUF_SIZE, get_page_size());
+}
+
+size_t default_get_metadata_subbuf_size(void)
+{
+       return std::max<size_t>(DEFAULT_METADATA_SUBBUF_SIZE, get_page_size());
+}
+
+size_t default_get_kernel_channel_subbuf_size(void)
+{
+       return std::max<size_t>(DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE, get_page_size());
+}
+
+size_t default_get_ust_pid_channel_subbuf_size(void)
+{
+       return std::max<size_t>(DEFAULT_UST_PID_CHANNEL_SUBBUF_SIZE, get_page_size());
+}
+
+size_t default_get_ust_uid_channel_subbuf_size(void)
+{
+       return std::max<size_t>(DEFAULT_UST_UID_CHANNEL_SUBBUF_SIZE, get_page_size());
+}
+
+pthread_attr_t *default_pthread_attr(void)
+{
+       if (pthread_attr_init_done) {
+               return &tattr;
+       }
+
+       WARN("Uninitialized pthread attributes, using libc defaults.");
+       return NULL;
+}
+
+static void __attribute__((constructor)) init_default_pthread_attr(void)
+{
+       int ret;
+       struct rlimit rlim;
+       size_t pthread_ss, system_ss, selected_ss;
+
+       ret = pthread_attr_init(&tattr);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_attr_init");
+               goto error;
+       }
+
+       /* Get system stack size limits. */
+       ret = getrlimit(RLIMIT_STACK, &rlim);
+       if (ret < 0) {
+               PERROR("getrlimit");
+               goto error_destroy;
+       }
+       DBG("Stack size limits: soft %lld, hard %lld bytes",
+                       (long long) rlim.rlim_cur,
+                       (long long) rlim.rlim_max);
+
+       /*
+        * getrlimit() may return a stack size of "-1", meaning "unlimited".
+        * In this case, we impose a known-good default minimum value which will
+        * override the libc's default stack size if it is smaller.
+        */
+       system_ss = rlim.rlim_cur != -1 ? rlim.rlim_cur :
+                       DEFAULT_LTTNG_THREAD_STACK_SIZE;
+
+       /* Get pthread default thread stack size. */
+       ret = pthread_attr_getstacksize(&tattr, &pthread_ss);
+       if (ret < 0) {
+               PERROR("pthread_attr_getstacksize");
+               goto error_destroy;
+       }
+       DBG("Default pthread stack size is %zu bytes", pthread_ss);
+
+       selected_ss = std::max(pthread_ss, system_ss);
+       if (selected_ss < DEFAULT_LTTNG_THREAD_STACK_SIZE) {
+               DBG("Default stack size is too small, setting it to %zu bytes",
+                       (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE);
+               selected_ss = DEFAULT_LTTNG_THREAD_STACK_SIZE;
+       }
+
+       if (rlim.rlim_max > 0 && selected_ss > rlim.rlim_max) {
+               WARN("Your system's stack size restrictions (%zu bytes) may be too low for the LTTng daemons to function properly, please set the stack size limit to at least %zu bytes to ensure reliable operation",
+                       (size_t) rlim.rlim_max, (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE);
+               selected_ss = (size_t) rlim.rlim_max;
+       }
+
+       ret = pthread_attr_setstacksize(&tattr, selected_ss);
+       if (ret < 0) {
+               PERROR("pthread_attr_setstacksize");
+               goto error_destroy;
+       }
+       pthread_attr_init_done = 1;
+error:
+       return;
+error_destroy:
+       ret = pthread_attr_destroy(&tattr);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_attr_destroy");
+       }
+}
+
+static void __attribute__((destructor)) fini_default_pthread_attr(void)
+{
+       int ret;
+
+       if (!pthread_attr_init_done) {
+               return;
+       }
+
+       ret = pthread_attr_destroy(&tattr);
+       if (ret) {
+               errno = ret;
+               PERROR("pthread_attr_destroy");
+       }
+}
diff --git a/src/common/domain.c b/src/common/domain.c
deleted file mode 100644 (file)
index fb088fa..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "lttng/domain-internal.h"
-#include "common/macros.h"
-
-const char *lttng_domain_type_str(enum lttng_domain_type domain_type)
-{
-       switch (domain_type) {
-       case LTTNG_DOMAIN_NONE:
-               return "none";
-       case LTTNG_DOMAIN_KERNEL:
-               return "kernel";
-       case LTTNG_DOMAIN_UST:
-               return "ust";
-       case LTTNG_DOMAIN_JUL:
-               return "jul";
-       case LTTNG_DOMAIN_LOG4J:
-               return "log4j";
-       case LTTNG_DOMAIN_PYTHON:
-               return "python";
-       default:
-               return "???";
-       }
-}
diff --git a/src/common/domain.cpp b/src/common/domain.cpp
new file mode 100644 (file)
index 0000000..fb088fa
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "lttng/domain-internal.h"
+#include "common/macros.h"
+
+const char *lttng_domain_type_str(enum lttng_domain_type domain_type)
+{
+       switch (domain_type) {
+       case LTTNG_DOMAIN_NONE:
+               return "none";
+       case LTTNG_DOMAIN_KERNEL:
+               return "kernel";
+       case LTTNG_DOMAIN_UST:
+               return "ust";
+       case LTTNG_DOMAIN_JUL:
+               return "jul";
+       case LTTNG_DOMAIN_LOG4J:
+               return "log4j";
+       case LTTNG_DOMAIN_PYTHON:
+               return "python";
+       default:
+               return "???";
+       }
+}
diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c
deleted file mode 100644 (file)
index d723ffa..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/dynamic-array.h>
-
-void lttng_dynamic_array_init(struct lttng_dynamic_array *array,
-               size_t element_size,
-               lttng_dynamic_array_element_destructor destructor)
-{
-       lttng_dynamic_buffer_init(&array->buffer);
-       array->element_size = element_size;
-       array->size = 0;
-       array->destructor = destructor;
-}
-
-int lttng_dynamic_array_set_count(struct lttng_dynamic_array *array,
-               size_t new_element_count)
-{
-       int ret;
-
-       if (!array) {
-               ret = -1;
-               goto end;
-       }
-
-       if (array->destructor) {
-               size_t i;
-
-               for (i = new_element_count; i < array->size; i++) {
-                       void *element = lttng_dynamic_array_get_element(
-                                       array, i);
-
-                       array->destructor(element);
-               }
-       }
-
-       array->size = new_element_count;
-       ret = lttng_dynamic_buffer_set_size(&array->buffer,
-                       new_element_count * array->element_size);
-end:
-       return ret;
-}
-
-int lttng_dynamic_array_add_element(struct lttng_dynamic_array *array,
-               const void *element)
-{
-       int ret;
-
-       if (!array || !element) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&array->buffer, element,
-                       array->element_size);
-       if (ret) {
-               goto end;
-       }
-       array->size++;
-end:
-       return ret;
-}
-
-int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array,
-               size_t element_index)
-{
-       void *element = lttng_dynamic_array_get_element(array,
-                       element_index);
-
-       if (array->destructor) {
-               array->destructor(element);
-       }
-       if (element_index != lttng_dynamic_array_get_count(array) - 1) {
-               void *next_element = lttng_dynamic_array_get_element(array,
-                               element_index + 1);
-
-               memmove(element, next_element,
-                               (array->size - element_index - 1) * array->element_size);
-       }
-       array->size--;
-       return lttng_dynamic_buffer_set_size(&array->buffer,
-                       array->buffer.size - array->element_size);
-}
-
-void lttng_dynamic_array_reset(struct lttng_dynamic_array *array)
-{
-       if (array->destructor) {
-               size_t i;
-
-               for (i = 0; i < lttng_dynamic_array_get_count(array); i++) {
-                       array->destructor(lttng_dynamic_array_get_element(array,
-                                       i));
-               }
-       }
-
-       lttng_dynamic_buffer_reset(&array->buffer);
-       array->size = 0;
-}
-
-void lttng_dynamic_array_clear(struct lttng_dynamic_array *array)
-{
-       if (array->destructor) {
-               size_t i;
-
-               for (i = 0; i < lttng_dynamic_array_get_count(array); i++) {
-                       array->destructor(lttng_dynamic_array_get_element(array,
-                                       i));
-               }
-       }
-
-       (void) lttng_dynamic_buffer_set_size(&array->buffer, 0);
-       array->size = 0;
-}
-
-void lttng_dynamic_pointer_array_init(
-               struct lttng_dynamic_pointer_array *array,
-               lttng_dynamic_pointer_array_destructor destructor)
-{
-       lttng_dynamic_array_init(&array->array, sizeof(void *), destructor);
-}      
-
-int lttng_dynamic_pointer_array_remove_pointer(
-               struct lttng_dynamic_pointer_array *array, size_t index)
-{
-       int ret;
-       const lttng_dynamic_array_element_destructor destructor =
-                       array->array.destructor;
-
-       /*
-        * Prevent the destructor from being used by the underlying
-        * dynamic array.
-        */
-       array->array.destructor = NULL;
-       if (destructor) {
-               destructor(lttng_dynamic_pointer_array_get_pointer(array,
-                               index));
-       }
-       ret = lttng_dynamic_array_remove_element(&array->array, index);
-       array->array.destructor = destructor;
-       return ret;
-}
-
-/* Release any memory used by the dynamic array. */
-void lttng_dynamic_pointer_array_reset(
-               struct lttng_dynamic_pointer_array *array)
-{
-       if (array->array.destructor) {
-               size_t i, count = lttng_dynamic_pointer_array_get_count(array);
-
-               for (i = 0; i < count; i++) {
-                       void *ptr = lttng_dynamic_pointer_array_get_pointer(
-                                       array, i);
-                       array->array.destructor(ptr);
-               }
-               /*
-                * Prevent the destructor from being used by the underlying
-                * dynamic array.
-                */
-               array->array.destructor = NULL;
-       }
-       lttng_dynamic_array_reset(&array->array);
-}
-
-void lttng_dynamic_pointer_array_clear(
-               struct lttng_dynamic_pointer_array *array)
-{
-       const lttng_dynamic_array_element_destructor destructor =
-                       array->array.destructor;
-
-       /*
-        * Prevent the destructor from being used by the underlying
-        * dynamic array.
-        */
-       array->array.destructor = NULL;
-       if (destructor) {
-               size_t i, count = lttng_dynamic_pointer_array_get_count(array);
-
-               for (i = 0; i < count; i++) {
-                       void *ptr = lttng_dynamic_pointer_array_get_pointer(
-                                       array, i);
-                       destructor(ptr);
-               }
-       }
-       lttng_dynamic_array_clear(&array->array);
-       array->array.destructor = destructor;
-}
diff --git a/src/common/dynamic-array.cpp b/src/common/dynamic-array.cpp
new file mode 100644 (file)
index 0000000..d723ffa
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-array.h>
+
+void lttng_dynamic_array_init(struct lttng_dynamic_array *array,
+               size_t element_size,
+               lttng_dynamic_array_element_destructor destructor)
+{
+       lttng_dynamic_buffer_init(&array->buffer);
+       array->element_size = element_size;
+       array->size = 0;
+       array->destructor = destructor;
+}
+
+int lttng_dynamic_array_set_count(struct lttng_dynamic_array *array,
+               size_t new_element_count)
+{
+       int ret;
+
+       if (!array) {
+               ret = -1;
+               goto end;
+       }
+
+       if (array->destructor) {
+               size_t i;
+
+               for (i = new_element_count; i < array->size; i++) {
+                       void *element = lttng_dynamic_array_get_element(
+                                       array, i);
+
+                       array->destructor(element);
+               }
+       }
+
+       array->size = new_element_count;
+       ret = lttng_dynamic_buffer_set_size(&array->buffer,
+                       new_element_count * array->element_size);
+end:
+       return ret;
+}
+
+int lttng_dynamic_array_add_element(struct lttng_dynamic_array *array,
+               const void *element)
+{
+       int ret;
+
+       if (!array || !element) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&array->buffer, element,
+                       array->element_size);
+       if (ret) {
+               goto end;
+       }
+       array->size++;
+end:
+       return ret;
+}
+
+int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array,
+               size_t element_index)
+{
+       void *element = lttng_dynamic_array_get_element(array,
+                       element_index);
+
+       if (array->destructor) {
+               array->destructor(element);
+       }
+       if (element_index != lttng_dynamic_array_get_count(array) - 1) {
+               void *next_element = lttng_dynamic_array_get_element(array,
+                               element_index + 1);
+
+               memmove(element, next_element,
+                               (array->size - element_index - 1) * array->element_size);
+       }
+       array->size--;
+       return lttng_dynamic_buffer_set_size(&array->buffer,
+                       array->buffer.size - array->element_size);
+}
+
+void lttng_dynamic_array_reset(struct lttng_dynamic_array *array)
+{
+       if (array->destructor) {
+               size_t i;
+
+               for (i = 0; i < lttng_dynamic_array_get_count(array); i++) {
+                       array->destructor(lttng_dynamic_array_get_element(array,
+                                       i));
+               }
+       }
+
+       lttng_dynamic_buffer_reset(&array->buffer);
+       array->size = 0;
+}
+
+void lttng_dynamic_array_clear(struct lttng_dynamic_array *array)
+{
+       if (array->destructor) {
+               size_t i;
+
+               for (i = 0; i < lttng_dynamic_array_get_count(array); i++) {
+                       array->destructor(lttng_dynamic_array_get_element(array,
+                                       i));
+               }
+       }
+
+       (void) lttng_dynamic_buffer_set_size(&array->buffer, 0);
+       array->size = 0;
+}
+
+void lttng_dynamic_pointer_array_init(
+               struct lttng_dynamic_pointer_array *array,
+               lttng_dynamic_pointer_array_destructor destructor)
+{
+       lttng_dynamic_array_init(&array->array, sizeof(void *), destructor);
+}      
+
+int lttng_dynamic_pointer_array_remove_pointer(
+               struct lttng_dynamic_pointer_array *array, size_t index)
+{
+       int ret;
+       const lttng_dynamic_array_element_destructor destructor =
+                       array->array.destructor;
+
+       /*
+        * Prevent the destructor from being used by the underlying
+        * dynamic array.
+        */
+       array->array.destructor = NULL;
+       if (destructor) {
+               destructor(lttng_dynamic_pointer_array_get_pointer(array,
+                               index));
+       }
+       ret = lttng_dynamic_array_remove_element(&array->array, index);
+       array->array.destructor = destructor;
+       return ret;
+}
+
+/* Release any memory used by the dynamic array. */
+void lttng_dynamic_pointer_array_reset(
+               struct lttng_dynamic_pointer_array *array)
+{
+       if (array->array.destructor) {
+               size_t i, count = lttng_dynamic_pointer_array_get_count(array);
+
+               for (i = 0; i < count; i++) {
+                       void *ptr = lttng_dynamic_pointer_array_get_pointer(
+                                       array, i);
+                       array->array.destructor(ptr);
+               }
+               /*
+                * Prevent the destructor from being used by the underlying
+                * dynamic array.
+                */
+               array->array.destructor = NULL;
+       }
+       lttng_dynamic_array_reset(&array->array);
+}
+
+void lttng_dynamic_pointer_array_clear(
+               struct lttng_dynamic_pointer_array *array)
+{
+       const lttng_dynamic_array_element_destructor destructor =
+                       array->array.destructor;
+
+       /*
+        * Prevent the destructor from being used by the underlying
+        * dynamic array.
+        */
+       array->array.destructor = NULL;
+       if (destructor) {
+               size_t i, count = lttng_dynamic_pointer_array_get_count(array);
+
+               for (i = 0; i < count; i++) {
+                       void *ptr = lttng_dynamic_pointer_array_get_pointer(
+                                       array, i);
+                       destructor(ptr);
+               }
+       }
+       lttng_dynamic_array_clear(&array->array);
+       array->array.destructor = destructor;
+}
diff --git a/src/common/dynamic-buffer.c b/src/common/dynamic-buffer.c
deleted file mode 100644 (file)
index d6ad674..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/dynamic-buffer.h>
-#include <common/buffer-view.h>
-#include <common/utils.h>
-
-/*
- * Round to (upper) power of two, val is returned if it already is a power of
- * two.
- */
-static
-size_t round_to_power_of_2(size_t val)
-{
-       size_t rounded;
-       const int order = utils_get_count_order_u64(val);
-
-       LTTNG_ASSERT(order >= 0);
-       rounded = (1ULL << order);
-       LTTNG_ASSERT(rounded >= val);
-
-       return rounded;
-}
-
-void lttng_dynamic_buffer_init(struct lttng_dynamic_buffer *buffer)
-{
-       LTTNG_ASSERT(buffer);
-       memset(buffer, 0, sizeof(*buffer));
-}
-
-int lttng_dynamic_buffer_append(struct lttng_dynamic_buffer *buffer,
-               const void *buf, size_t len)
-{
-       int ret = 0;
-
-       if (!buffer || (!buf && len)) {
-               ret = -1;
-               goto end;
-       }
-
-       if (len == 0) {
-               /* Not an error, no-op. */
-               goto end;
-       }
-
-       LTTNG_ASSERT(buffer->_capacity >= buffer->size);
-       if (buffer->_capacity < (len + buffer->size)) {
-               ret = lttng_dynamic_buffer_set_capacity(buffer,
-                               buffer->_capacity +
-                               (len - (buffer->_capacity - buffer->size)));
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       memcpy(buffer->data + buffer->size, buf, len);
-       buffer->size += len;
-end:
-       return ret;
-}
-
-int lttng_dynamic_buffer_append_buffer(struct lttng_dynamic_buffer *dst_buffer,
-               const struct lttng_dynamic_buffer *src_buffer)
-{
-       int ret;
-
-       if (!dst_buffer || !src_buffer) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(dst_buffer, src_buffer->data,
-                       src_buffer->size);
-end:
-       return ret;
-}
-
-int lttng_dynamic_buffer_append_view(struct lttng_dynamic_buffer *buffer,
-               const struct lttng_buffer_view *src)
-{
-       int ret;
-
-       if (!buffer || !src) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(buffer, src->data,
-                       src->size);
-end:
-       return ret;
-}
-
-int lttng_dynamic_buffer_set_size(struct lttng_dynamic_buffer *buffer,
-               size_t new_size)
-{
-       int ret = 0;
-
-       if (!buffer) {
-               goto end;
-       }
-
-       if (new_size == buffer->size) {
-               goto end;
-       }
-
-       if (new_size > buffer->_capacity) {
-               ret = lttng_dynamic_buffer_set_capacity(buffer, new_size);
-               if (ret) {
-                       goto end;
-               }
-
-               memset(buffer->data + buffer->size, 0, new_size - buffer->size);
-       } else if (new_size > buffer->size) {
-               memset(buffer->data + buffer->size, 0, new_size - buffer->size);
-       } else {
-               /*
-                * Shrinking size. There is no need to zero-out the newly
-                * released memory as it will either be:
-                *   - overwritten by lttng_dynamic_buffer_append,
-                *   - expanded later, which will zero-out the memory
-                *
-                * Users of external APIs are encouraged to set the buffer's
-                * size _before_ making such calls.
-                */
-       }
-
-       buffer->size = new_size;
-end:
-       return ret;
-}
-
-int lttng_dynamic_buffer_set_capacity(struct lttng_dynamic_buffer *buffer,
-               size_t demanded_capacity)
-{
-       int ret = 0;
-       void *new_buf;
-       size_t new_capacity = demanded_capacity ?
-                       round_to_power_of_2(demanded_capacity) : 0;
-
-       if (!buffer || demanded_capacity < buffer->size) {
-               /*
-                * Shrinking a buffer's size by changing its capacity is
-                * unsupported.
-                */
-               ret = -1;
-               goto end;
-       }
-
-       if (new_capacity == buffer->_capacity) {
-               goto end;
-       }
-
-       /* Memory is initialized by the size increases. */
-       new_buf = realloc(buffer->data, new_capacity);
-       if (!new_buf) {
-               ret = -1;
-               goto end;
-       }
-
-       buffer->data = new_buf;
-       buffer->_capacity = new_capacity;
-end:
-       return ret;
-}
-
-/* Release any memory used by the dynamic buffer. */
-void lttng_dynamic_buffer_reset(struct lttng_dynamic_buffer *buffer)
-{
-       if (!buffer) {
-               return;
-       }
-
-       buffer->size = 0;
-       buffer->_capacity = 0;
-       free(buffer->data);
-       buffer->data = NULL;
-}
-
-size_t lttng_dynamic_buffer_get_capacity_left(
-               struct lttng_dynamic_buffer *buffer)
-{
-       if (!buffer) {
-               return 0;
-       }
-
-       return buffer->_capacity - buffer->size;
-}
diff --git a/src/common/dynamic-buffer.cpp b/src/common/dynamic-buffer.cpp
new file mode 100644 (file)
index 0000000..945434c
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-buffer.h>
+#include <common/buffer-view.h>
+#include <common/utils.h>
+
+/*
+ * Round to (upper) power of two, val is returned if it already is a power of
+ * two.
+ */
+static
+size_t round_to_power_of_2(size_t val)
+{
+       size_t rounded;
+       const int order = utils_get_count_order_u64(val);
+
+       LTTNG_ASSERT(order >= 0);
+       rounded = (1ULL << order);
+       LTTNG_ASSERT(rounded >= val);
+
+       return rounded;
+}
+
+void lttng_dynamic_buffer_init(struct lttng_dynamic_buffer *buffer)
+{
+       LTTNG_ASSERT(buffer);
+       memset(buffer, 0, sizeof(*buffer));
+}
+
+int lttng_dynamic_buffer_append(struct lttng_dynamic_buffer *buffer,
+               const void *buf, size_t len)
+{
+       int ret = 0;
+
+       if (!buffer || (!buf && len)) {
+               ret = -1;
+               goto end;
+       }
+
+       if (len == 0) {
+               /* Not an error, no-op. */
+               goto end;
+       }
+
+       LTTNG_ASSERT(buffer->_capacity >= buffer->size);
+       if (buffer->_capacity < (len + buffer->size)) {
+               ret = lttng_dynamic_buffer_set_capacity(buffer,
+                               buffer->_capacity +
+                               (len - (buffer->_capacity - buffer->size)));
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       memcpy(buffer->data + buffer->size, buf, len);
+       buffer->size += len;
+end:
+       return ret;
+}
+
+int lttng_dynamic_buffer_append_buffer(struct lttng_dynamic_buffer *dst_buffer,
+               const struct lttng_dynamic_buffer *src_buffer)
+{
+       int ret;
+
+       if (!dst_buffer || !src_buffer) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(dst_buffer, src_buffer->data,
+                       src_buffer->size);
+end:
+       return ret;
+}
+
+int lttng_dynamic_buffer_append_view(struct lttng_dynamic_buffer *buffer,
+               const struct lttng_buffer_view *src)
+{
+       int ret;
+
+       if (!buffer || !src) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buffer, src->data,
+                       src->size);
+end:
+       return ret;
+}
+
+int lttng_dynamic_buffer_set_size(struct lttng_dynamic_buffer *buffer,
+               size_t new_size)
+{
+       int ret = 0;
+
+       if (!buffer) {
+               goto end;
+       }
+
+       if (new_size == buffer->size) {
+               goto end;
+       }
+
+       if (new_size > buffer->_capacity) {
+               ret = lttng_dynamic_buffer_set_capacity(buffer, new_size);
+               if (ret) {
+                       goto end;
+               }
+
+               memset(buffer->data + buffer->size, 0, new_size - buffer->size);
+       } else if (new_size > buffer->size) {
+               memset(buffer->data + buffer->size, 0, new_size - buffer->size);
+       } else {
+               /*
+                * Shrinking size. There is no need to zero-out the newly
+                * released memory as it will either be:
+                *   - overwritten by lttng_dynamic_buffer_append,
+                *   - expanded later, which will zero-out the memory
+                *
+                * Users of external APIs are encouraged to set the buffer's
+                * size _before_ making such calls.
+                */
+       }
+
+       buffer->size = new_size;
+end:
+       return ret;
+}
+
+int lttng_dynamic_buffer_set_capacity(struct lttng_dynamic_buffer *buffer,
+               size_t demanded_capacity)
+{
+       int ret = 0;
+       void *new_buf;
+       size_t new_capacity = demanded_capacity ?
+                       round_to_power_of_2(demanded_capacity) : 0;
+
+       if (!buffer || demanded_capacity < buffer->size) {
+               /*
+                * Shrinking a buffer's size by changing its capacity is
+                * unsupported.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       if (new_capacity == buffer->_capacity) {
+               goto end;
+       }
+
+       /* Memory is initialized by the size increases. */
+       new_buf = realloc(buffer->data, new_capacity);
+       if (!new_buf) {
+               ret = -1;
+               goto end;
+       }
+
+       buffer->data = (char *) new_buf;
+       buffer->_capacity = new_capacity;
+end:
+       return ret;
+}
+
+/* Release any memory used by the dynamic buffer. */
+void lttng_dynamic_buffer_reset(struct lttng_dynamic_buffer *buffer)
+{
+       if (!buffer) {
+               return;
+       }
+
+       buffer->size = 0;
+       buffer->_capacity = 0;
+       free(buffer->data);
+       buffer->data = NULL;
+}
+
+size_t lttng_dynamic_buffer_get_capacity_left(
+               struct lttng_dynamic_buffer *buffer)
+{
+       if (!buffer) {
+               return 0;
+       }
+
+       return buffer->_capacity - buffer->size;
+}
diff --git a/src/common/endpoint.c b/src/common/endpoint.c
deleted file mode 100644 (file)
index 3a0bbad..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/endpoint-internal.h>
-
-static
-struct lttng_endpoint lttng_session_daemon_notification_endpoint_instance = {
-       .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION,
-};
-
-static
-struct lttng_endpoint lttng_session_daemon_command_endpoint_instance = {
-       .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_COMMAND,
-};
-
-struct lttng_endpoint *lttng_session_daemon_notification_endpoint =
-               &lttng_session_daemon_notification_endpoint_instance;
-
-struct lttng_endpoint *lttng_session_daemon_command_endpoint =
-               &lttng_session_daemon_command_endpoint_instance;
diff --git a/src/common/endpoint.cpp b/src/common/endpoint.cpp
new file mode 100644 (file)
index 0000000..3a0bbad
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/endpoint-internal.h>
+
+static
+struct lttng_endpoint lttng_session_daemon_notification_endpoint_instance = {
+       .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION,
+};
+
+static
+struct lttng_endpoint lttng_session_daemon_command_endpoint_instance = {
+       .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_COMMAND,
+};
+
+struct lttng_endpoint *lttng_session_daemon_notification_endpoint =
+               &lttng_session_daemon_notification_endpoint_instance;
+
+struct lttng_endpoint *lttng_session_daemon_command_endpoint =
+               &lttng_session_daemon_command_endpoint_instance;
diff --git a/src/common/error-query.c b/src/common/error-query.c
deleted file mode 100644 (file)
index d6bca73..0000000
+++ /dev/null
@@ -1,1218 +0,0 @@
-/*
- * error-query.c
- *
- * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.1-only
- *
- */
-
-#include <common/dynamic-array.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/list-internal.h>
-#include <lttng/action/path-internal.h>
-#include <lttng/error-query-internal.h>
-#include <lttng/error-query.h>
-#include <lttng/trigger/trigger-internal.h>
-#include <stddef.h>
-
-struct lttng_error_query {
-       enum lttng_error_query_target_type target_type;
-};
-
-struct lttng_error_query_comm {
-       /* enum lttng_error_query_target_type */
-       int8_t target_type;
-       /* Target-specific payload. */
-       char payload[];
-};
-
-struct lttng_error_query_trigger {
-       struct lttng_error_query parent;
-       /* Mutable only because of the reference count. */
-       struct lttng_trigger *trigger;
-};
-
-struct lttng_error_query_condition {
-       struct lttng_error_query parent;
-       /* Mutable only because of the reference count. */
-       struct lttng_trigger *trigger;
-};
-
-struct lttng_error_query_action {
-       struct lttng_error_query parent;
-       /* Mutable only because of the reference count. */
-       struct lttng_trigger *trigger;
-       struct lttng_action_path action_path;
-};
-
-struct lttng_error_query_result {
-       enum lttng_error_query_result_type type;
-       char *name;
-       char *description;
-};
-
-struct lttng_error_query_result_comm {
-       /* enum lttng_error_query_result_type */
-       uint8_t type;
-       /* Length of name (including null-terminator). */
-       uint32_t name_len;
-       /* Length of description (including null-terminator). */
-       uint32_t description_len;
-       /* Name, description, and type-specific payload follow. */
-       char payload[];
-} LTTNG_PACKED;
-
-struct lttng_error_query_result_counter_comm {
-       uint64_t value;
-} LTTNG_PACKED;
-
-struct lttng_error_query_result_counter {
-       struct lttng_error_query_result parent;
-       uint64_t value;
-};
-
-struct lttng_error_query_results_comm {
-       uint32_t count;
-       /* `count` instances of `struct lttng_error_query_result` follow. */
-       char payload[];
-} LTTNG_PACKED;
-
-struct lttng_error_query_results {
-       struct lttng_dynamic_pointer_array results;
-};
-
-static
-enum lttng_error_code lttng_error_query_result_mi_serialize(
-               const struct lttng_error_query_result *result,
-               struct mi_writer *writer);
-
-static
-enum lttng_error_code lttng_error_query_result_counter_mi_serialize(
-               const struct lttng_error_query_result *result,
-               struct mi_writer *writer);
-
-struct lttng_error_query *lttng_error_query_trigger_create(
-               const struct lttng_trigger *trigger)
-{
-       struct lttng_error_query_trigger *query = NULL;
-       struct lttng_trigger *trigger_copy = NULL;
-
-       if (!trigger) {
-               goto end;
-       }
-
-       trigger_copy = lttng_trigger_copy(trigger);
-       if (!trigger_copy) {
-               goto end;
-       }
-
-       query = zmalloc(sizeof(*query));
-       if (!query) {
-               PERROR("Failed to allocate trigger error query");
-               goto error;
-       }
-
-       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER;
-       query->trigger = trigger_copy;
-       trigger_copy = NULL;
-
-error:
-       lttng_trigger_put(trigger_copy);
-end:
-       return query ? &query->parent : NULL;
-}
-
-struct lttng_error_query *lttng_error_query_condition_create(
-               const struct lttng_trigger *trigger)
-{
-       struct lttng_error_query_condition *query = NULL;
-       struct lttng_trigger *trigger_copy = NULL;
-
-       if (!trigger) {
-               goto end;
-       }
-
-       trigger_copy = lttng_trigger_copy(trigger);
-       if (!trigger_copy) {
-               goto end;
-       }
-
-       query = zmalloc(sizeof(*query));
-       if (!query) {
-               PERROR("Failed to allocate condition error query");
-               goto error;
-       }
-
-       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION;
-       query->trigger = trigger_copy;
-       trigger_copy = NULL;
-
-error:
-       lttng_trigger_put(trigger_copy);
-end:
-       return query ? &query->parent : NULL;
-}
-
-static
-struct lttng_action *get_trigger_action_from_path(
-               struct lttng_trigger *trigger,
-               const struct lttng_action_path *action_path)
-{
-       size_t index_count, i;
-       enum lttng_action_path_status path_status;
-       struct lttng_action *current_action = NULL;
-
-       path_status = lttng_action_path_get_index_count(
-                       action_path, &index_count);
-       if (path_status != LTTNG_ACTION_PATH_STATUS_OK) {
-               goto end;
-       }
-
-       current_action = lttng_trigger_get_action(trigger);
-       for (i = 0; i < index_count; i++) {
-               uint64_t path_index;
-
-               path_status = lttng_action_path_get_index_at_index(
-                               action_path, i, &path_index);
-               current_action = lttng_action_list_borrow_mutable_at_index(
-                               current_action, path_index);
-               if (!current_action) {
-                       /* Invalid action path. */
-                       goto end;
-               }
-       }
-
-end:
-       return current_action;
-}
-
-static
-bool is_valid_action_path(const struct lttng_trigger *trigger,
-               const struct lttng_action_path *action_path)
-{
-       /*
-        * While 'trigger's constness is casted-away, the trigger and resulting
-        * action are not modified; we merely check for the action's existence.
-        */
-       return !!get_trigger_action_from_path(
-                       (struct lttng_trigger *) trigger, action_path);
-}
-
-struct lttng_error_query *lttng_error_query_action_create(
-               const struct lttng_trigger *trigger,
-               const struct lttng_action_path *action_path)
-{
-       struct lttng_error_query_action *query = NULL;
-       struct lttng_trigger *trigger_copy = NULL;
-       int ret_copy;
-
-       if (!trigger || !action_path ||
-                       !is_valid_action_path(trigger, action_path)) {
-               goto end;
-       }
-
-       trigger_copy = lttng_trigger_copy(trigger);
-       if (!trigger_copy) {
-               goto end;
-       }
-
-       query = zmalloc(sizeof(*query));
-       if (!query) {
-               PERROR("Failed to allocate action error query");
-               goto error;
-       }
-
-       ret_copy = lttng_action_path_copy(action_path, &query->action_path);
-       if (ret_copy) {
-               goto error;
-       }
-
-       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION;
-       query->trigger = trigger_copy;
-       trigger_copy = NULL;
-       goto end;
-
-error:
-       lttng_trigger_put(trigger_copy);
-       lttng_error_query_destroy(query ? &query->parent : NULL);
-end:
-       return query ? &query->parent : NULL;
-}
-
-void lttng_error_query_destroy(struct lttng_error_query *query)
-{
-       struct lttng_error_query_trigger *trigger_query;
-
-       if (!query) {
-               return;
-       }
-
-       trigger_query = container_of(query, typeof(*trigger_query), parent);
-       lttng_trigger_put(trigger_query->trigger);
-       free(trigger_query);
-}
-
-static
-int lttng_error_query_result_counter_serialize(
-               const struct lttng_error_query_result *result,
-               struct lttng_payload *payload)
-{
-       const struct lttng_error_query_result_counter *counter_result;
-
-       LTTNG_ASSERT(result->type == LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER);
-       counter_result = container_of(result, typeof(*counter_result), parent);
-
-       return lttng_dynamic_buffer_append(&payload->buffer,
-                       &(struct lttng_error_query_result_counter_comm) {
-                                       .value = counter_result->value
-                       },
-                       sizeof(struct lttng_error_query_result_counter_comm));
-}
-
-int lttng_error_query_result_serialize(
-               const struct lttng_error_query_result *result,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_error_query_result_comm header = {
-               .type = (uint8_t) result->type,
-               .name_len = (typeof(header.name_len)) strlen(result->name) + 1,
-               .description_len = (typeof(header.name_len)) strlen(result->description) + 1,
-       };
-
-       /* Header. */
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &header, sizeof(header));
-       if (ret) {
-               ERR("Failed to append error query result communication header to payload");
-               goto end;
-       }
-
-       /* Name. */
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, result->name, header.name_len);
-       if (ret) {
-               ERR("Failed to append error query result name to payload");
-               goto end;
-       }
-
-       /* Description. */
-       ret = lttng_dynamic_buffer_append(&payload->buffer, result->description,
-                       header.description_len);
-       if (ret) {
-               ERR("Failed to append error query result description to payload");
-               goto end;
-       }
-
-       /* Type-specific payload. */
-       switch (result->type) {
-       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
-               ret = lttng_error_query_result_counter_serialize(
-                               result, payload);
-               if (ret) {
-                       ERR("Failed to serialize counter error query result");
-                       goto end;
-               }
-               break;
-       default:
-               abort();
-       }
-
-end:
-       return ret;
-}
-
-static
-int lttng_error_query_result_init(
-               struct lttng_error_query_result *result,
-               enum lttng_error_query_result_type result_type,
-               const char *name,
-               const char *description)
-{
-       int ret;
-
-       LTTNG_ASSERT(name);
-       LTTNG_ASSERT(description);
-
-       result->type = result_type;
-
-       result->name = strdup(name);
-       if (!result->name) {
-               PERROR("Failed to copy error query result name");
-               ret = -1;
-               goto end;
-       }
-
-       result->description = strdup(description);
-       if (!result->description) {
-               PERROR("Failed to copy error query result description");
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-void lttng_error_query_result_destroy(struct lttng_error_query_result *counter)
-{
-       if (!counter) {
-               return;
-       }
-
-       switch (counter->type) {
-       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
-               /* Nothing to tear down. */
-               break;
-       default:
-               abort();
-       }
-
-       free(counter->name);
-       free(counter->description);
-       free(counter);
-}
-
-struct lttng_error_query_result *
-lttng_error_query_result_counter_create(
-               const char *name, const char *description, uint64_t value)
-{
-       int init_ret;
-       struct lttng_error_query_result_counter *counter;
-
-       counter = zmalloc(sizeof(*counter));
-       if (!counter) {
-               PERROR("Failed to allocate error query counter result");
-               goto end;
-       }
-
-       init_ret = lttng_error_query_result_init(&counter->parent,
-                       LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER, name,
-                       description);
-       if (init_ret) {
-               goto error;
-       }
-
-       counter->value = value;
-       goto end;
-error:
-       lttng_error_query_result_destroy(&counter->parent);
-end:
-       return counter ? &counter->parent : NULL;
-}
-
-static
-void destroy_result(void *ptr)
-{
-       struct lttng_error_query_result *result = (typeof(result)) ptr;
-
-       lttng_error_query_result_destroy(result);
-}
-
-struct lttng_error_query_results *lttng_error_query_results_create(void)
-{
-       struct lttng_error_query_results *set = zmalloc(sizeof(*set));
-
-       if (!set) {
-               PERROR("Failed to allocate an error query result set");
-               goto end;
-       }
-
-       lttng_dynamic_pointer_array_init(&set->results, destroy_result);
-end:
-       return set;
-}
-
-int lttng_error_query_results_add_result(
-               struct lttng_error_query_results *results,
-               struct lttng_error_query_result *result)
-{
-       return lttng_dynamic_pointer_array_add_pointer(
-                       &results->results, result);
-}
-
-ssize_t lttng_error_query_result_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_error_query_result **result)
-{
-       ssize_t used_size = 0;
-       struct lttng_error_query_result_comm *header;
-       struct lttng_payload_view header_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*header));
-       const char *name;
-       const char *description;
-
-       if (!lttng_payload_view_is_valid(&header_view)) {
-               used_size = -1;
-               goto end;
-       }
-
-       header = (typeof(header)) header_view.buffer.data;
-       used_size += sizeof(*header);
-
-       {
-               struct lttng_payload_view name_view =
-                               lttng_payload_view_from_view(view, used_size,
-                                               header->name_len);
-
-               if (!lttng_payload_view_is_valid(&name_view) ||
-                               !lttng_buffer_view_contains_string(
-                                               &name_view.buffer,
-                                               name_view.buffer.data,
-                                               header->name_len)) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               name = name_view.buffer.data;
-               used_size += header->name_len;
-       }
-
-       {
-               struct lttng_payload_view description_view =
-                               lttng_payload_view_from_view(view, used_size,
-                                               header->description_len);
-
-               if (!lttng_payload_view_is_valid(&description_view) ||
-                               !lttng_buffer_view_contains_string(
-                                               &description_view.buffer,
-                                               description_view.buffer.data,
-                                               header->description_len)) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               description = description_view.buffer.data;
-               used_size += header->description_len;
-       }
-
-       switch (header->type) {
-       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
-       {
-               struct lttng_error_query_result_counter_comm *counter;
-               struct lttng_payload_view counter_payload_view =
-                               lttng_payload_view_from_view(view, used_size,
-                                               sizeof(*counter));
-
-               if (!lttng_payload_view_is_valid(&counter_payload_view)) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               counter = (typeof(counter)) counter_payload_view.buffer.data;
-               *result = lttng_error_query_result_counter_create(
-                               name, description, counter->value);
-               if (!*result) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               used_size += sizeof(*counter);
-               break;
-       }
-       default:
-               used_size = -1;
-               goto end;
-       }
-
-end:
-       return used_size;
-}
-
-int lttng_error_query_results_serialize(
-               const struct lttng_error_query_results *results,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t result_index;
-       const size_t result_count = lttng_dynamic_pointer_array_get_count(
-                       &results->results);
-       const struct lttng_error_query_results_comm header = {
-               .count = (typeof(header.count)) result_count,
-       };
-
-       /* Header. */
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &header, sizeof(header));
-       if (ret) {
-               ERR("Failed to append error query result set header to payload");
-               goto end;
-       }
-
-       /* Results. */
-       for (result_index = 0; result_index < result_count; result_index++) {
-               const struct lttng_error_query_result *result = (typeof(result))
-                               lttng_dynamic_pointer_array_get_pointer(
-                                               &results->results,
-                                               result_index);
-
-               ret = lttng_error_query_result_serialize(result, payload);
-               if (ret) {
-                       ERR("Failed to append error query result to payload");
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-ssize_t lttng_error_query_results_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_error_query_results **_results)
-{
-       size_t result_index;
-       ssize_t total_used_size = 0;
-       struct lttng_error_query_results_comm *header;
-       struct lttng_payload_view header_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*header));
-       struct lttng_error_query_results *results = NULL;
-
-       if (!lttng_payload_view_is_valid(&header_view)) {
-               ERR("Failed to map view to error query result set header");
-               total_used_size = -1;
-               goto end;
-       }
-
-       header = (typeof(header)) header_view.buffer.data;
-       total_used_size += sizeof(*header);
-       results = lttng_error_query_results_create();
-       if (!results) {
-               total_used_size = -1;
-               goto end;
-       }
-
-       for (result_index = 0; result_index < header->count; result_index++) {
-               ssize_t used_size;
-               struct lttng_error_query_result *result;
-               struct lttng_payload_view result_view =
-                               lttng_payload_view_from_view(
-                                               view, total_used_size, -1);
-
-               if (!lttng_payload_view_is_valid(&result_view)) {
-                       total_used_size = -1;
-                       goto end;
-               }
-
-               used_size = lttng_error_query_result_create_from_payload(
-                               &result_view, &result);
-               if (used_size < 0) {
-                       total_used_size = -1;
-                       goto end;
-               }
-
-               total_used_size += used_size;
-
-               if (lttng_dynamic_pointer_array_add_pointer(
-                                   &results->results, result)) {
-                       lttng_error_query_result_destroy(result);
-                       total_used_size = -1;
-                       goto end;
-               }
-       }
-
-       *_results = results;
-       results = NULL;
-end:
-       lttng_error_query_results_destroy(results);
-       return total_used_size;
-}
-
-static
-int lttng_error_query_trigger_serialize(const struct lttng_error_query *query,
-               struct lttng_payload *payload)
-{
-       int ret;
-       const struct lttng_error_query_trigger *query_trigger =
-                       container_of(query, typeof(*query_trigger), parent);
-
-       if (!lttng_trigger_validate(query_trigger->trigger)) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_trigger_serialize(query_trigger->trigger, payload);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int lttng_error_query_condition_serialize(const struct lttng_error_query *query,
-               struct lttng_payload *payload)
-{
-       int ret;
-       const struct lttng_error_query_condition *query_trigger =
-                       container_of(query, typeof(*query_trigger), parent);
-
-       if (!lttng_trigger_validate(query_trigger->trigger)) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_trigger_serialize(query_trigger->trigger, payload);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int lttng_error_query_action_serialize(const struct lttng_error_query *query,
-               struct lttng_payload *payload)
-{
-       int ret;
-       const struct lttng_error_query_action *query_action =
-                       container_of(query, typeof(*query_action), parent);
-
-       if (!lttng_trigger_validate(query_action->trigger)) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_trigger_serialize(query_action->trigger, payload);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_action_path_serialize(&query_action->action_path, payload);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-enum lttng_error_query_target_type lttng_error_query_get_target_type(
-               const struct lttng_error_query *query)
-{
-       return query->target_type;
-}
-
-const struct lttng_trigger *lttng_error_query_trigger_borrow_target(
-               const struct lttng_error_query *query)
-{
-       const struct lttng_error_query_trigger *query_trigger =
-                       container_of(query, typeof(*query_trigger), parent);
-
-       return query_trigger->trigger;
-}
-
-const struct lttng_trigger *lttng_error_query_condition_borrow_target(
-               const struct lttng_error_query *query)
-{
-       const struct lttng_error_query_condition *query_trigger =
-                       container_of(query, typeof(*query_trigger), parent);
-
-       return query_trigger->trigger;
-}
-
-const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target(
-               const struct lttng_error_query *query)
-{
-       const struct lttng_error_query_action *query_action =
-                       container_of(query, typeof(*query_action), parent);
-
-       return query_action->trigger;
-}
-
-struct lttng_action *lttng_error_query_action_borrow_action_target(
-       const struct lttng_error_query *query,
-       struct lttng_trigger *trigger)
-{
-       const struct lttng_error_query_action *query_action =
-                       container_of(query, typeof(*query_action), parent);
-
-       return get_trigger_action_from_path(
-                       trigger, &query_action->action_path);
-}
-
-int lttng_error_query_serialize(const struct lttng_error_query *query,
-               struct lttng_payload *payload)
-{
-       int ret;
-       const struct lttng_error_query_comm header = {
-               .target_type = (typeof(header.target_type)) query->target_type,
-       };
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &header, sizeof(header));
-       if (ret) {
-               ERR("Failed to append error query header to payload");
-               goto end;
-       }
-
-       switch (query->target_type) {
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
-               ret = lttng_error_query_trigger_serialize(query, payload);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
-               ret = lttng_error_query_condition_serialize(query, payload);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
-               ret = lttng_error_query_action_serialize(query, payload);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       default:
-               abort();
-       }
-end:
-       return ret;
-}
-
-ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view,
-               struct lttng_error_query **query)
-{
-       ssize_t used_size = 0;
-       struct lttng_error_query_comm *header;
-       struct lttng_trigger *trigger = NULL;
-       struct lttng_payload_view header_view =
-                       lttng_payload_view_from_view(view, 0, sizeof(*header));
-
-       if (!lttng_payload_view_is_valid(&header_view)) {
-               ERR("Failed to map error query header");
-               used_size = -1;
-               goto end;
-       }
-
-       used_size = sizeof(*header);
-
-       header = (typeof(header)) header_view.buffer.data;
-       switch ((enum lttng_error_query_target_type) header->target_type) {
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
-       {
-               ssize_t trigger_used_size;
-               struct lttng_payload_view trigger_view =
-                               lttng_payload_view_from_view(
-                                               view, used_size, -1);
-
-               if (!lttng_payload_view_is_valid(&trigger_view)) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               trigger_used_size = lttng_trigger_create_from_payload(
-                               &trigger_view, &trigger);
-               if (trigger_used_size < 0) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               used_size += trigger_used_size;
-
-               *query = lttng_error_query_trigger_create(trigger);
-               if (!*query) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
-       {
-               ssize_t trigger_used_size;
-               struct lttng_payload_view trigger_view =
-                               lttng_payload_view_from_view(
-                                               view, used_size, -1);
-
-               if (!lttng_payload_view_is_valid(&trigger_view)) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               trigger_used_size = lttng_trigger_create_from_payload(
-                               &trigger_view, &trigger);
-               if (trigger_used_size < 0) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               used_size += trigger_used_size;
-
-               *query = lttng_error_query_condition_create(trigger);
-               if (!*query) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
-       {
-               struct lttng_action_path *action_path = NULL;
-
-               {
-                       ssize_t trigger_used_size;
-                       struct lttng_payload_view trigger_view =
-                                       lttng_payload_view_from_view(
-                                                       view, used_size, -1);
-
-                       if (!lttng_payload_view_is_valid(&trigger_view)) {
-                               used_size = -1;
-                               goto end;
-                       }
-
-                       trigger_used_size = lttng_trigger_create_from_payload(
-                                       &trigger_view, &trigger);
-                       if (trigger_used_size < 0) {
-                               used_size = -1;
-                               goto end;
-                       }
-
-                       used_size += trigger_used_size;
-               }
-
-               {
-                       ssize_t action_path_used_size;
-                       struct lttng_payload_view action_path_view =
-                                       lttng_payload_view_from_view(
-                                                       view, used_size, -1);
-
-                       if (!lttng_payload_view_is_valid(&action_path_view)) {
-                               used_size = -1;
-                               goto end;
-                       }
-
-                       action_path_used_size = lttng_action_path_create_from_payload(
-                                       &action_path_view, &action_path);
-                       if (action_path_used_size < 0) {
-                               used_size = -1;
-                               goto end;
-                       }
-
-                       used_size += action_path_used_size;
-               }
-
-               *query = lttng_error_query_action_create(
-                               trigger, action_path);
-               lttng_action_path_destroy(action_path);
-               if (!*query) {
-                       used_size = -1;
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               used_size = -1;
-               goto end;
-       }
-
-end:
-       lttng_trigger_put(trigger);
-       return used_size;
-}
-
-enum lttng_error_query_results_status lttng_error_query_results_get_count(
-               const struct lttng_error_query_results *results,
-               unsigned int *count)
-{
-       enum lttng_error_query_results_status status;
-
-       if (!results || !count) {
-               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       *count = lttng_dynamic_pointer_array_get_count(&results->results);
-       status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_error_query_results_status
-lttng_error_query_results_get_result(
-               const struct lttng_error_query_results *results,
-               const struct lttng_error_query_result **result,
-               unsigned int index)
-{
-       unsigned int result_count;
-       enum lttng_error_query_results_status status;
-
-       if (!results || !result) {
-               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       status = lttng_error_query_results_get_count(results, &result_count);
-       if (status != LTTNG_ERROR_QUERY_RESULTS_STATUS_OK) {
-               goto end;
-       }
-
-       if (index >= result_count) {
-               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       *result = (typeof(*result)) lttng_dynamic_pointer_array_get_pointer(
-                       &results->results, index);
-       LTTNG_ASSERT(*result);
-       status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK;
-end:
-       return status;
-}
-
-void lttng_error_query_results_destroy(
-               struct lttng_error_query_results *results)
-{
-       if (!results) {
-               return;
-       }
-
-       lttng_dynamic_pointer_array_reset(&results->results);
-       free(results);
-}
-
-enum lttng_error_query_result_type
-lttng_error_query_result_get_type(const struct lttng_error_query_result *result)
-{
-       return result ? result->type : LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN;
-}
-
-enum lttng_error_query_result_status lttng_error_query_result_get_name(
-               const struct lttng_error_query_result *result,
-               const char **name)
-{
-       enum lttng_error_query_result_status status;
-
-       if (!result || !name) {
-               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       *name = result->name;
-       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_error_query_result_status lttng_error_query_result_get_description(
-               const struct lttng_error_query_result *result,
-               const char **description)
-{
-       enum lttng_error_query_result_status status;
-
-       if (!result || !description) {
-               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       *description = result->description;
-       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
-end:
-       return status;
-}
-
-enum lttng_error_query_result_status lttng_error_query_result_counter_get_value(
-               const struct lttng_error_query_result *result,
-               uint64_t *value)
-{
-       enum lttng_error_query_result_status status;
-       const struct lttng_error_query_result_counter *counter_result;
-
-       if (!result || !value ||
-                       result->type != LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) {
-               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
-               goto end;
-       }
-
-       counter_result = container_of(result, typeof(*counter_result), parent);
-
-       *value = counter_result->value;
-       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
-end:
-       return status;
-}
-
-static
-enum lttng_error_code lttng_error_query_result_counter_mi_serialize(
-               const struct lttng_error_query_result *result,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_error_query_result_status status;
-       uint64_t value;
-
-       LTTNG_ASSERT(result);
-       LTTNG_ASSERT(writer);
-
-       status = lttng_error_query_result_counter_get_value(result, &value);
-       LTTNG_ASSERT(status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
-
-       /* Open error query result counter element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_error_query_result_counter);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Value. */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_error_query_result_counter_value,
-                       value);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close error query result counter element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-enum lttng_error_code lttng_error_query_result_mi_serialize(
-               const struct lttng_error_query_result *result,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_error_query_result_status result_status;
-       enum lttng_error_query_result_type type;
-       const char *name = NULL;
-       const char *description = NULL;
-
-       LTTNG_ASSERT(result);
-       LTTNG_ASSERT(writer);
-
-       type = lttng_error_query_result_get_type(result);
-
-       result_status = lttng_error_query_result_get_name(result, &name);
-       LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
-
-       result_status = lttng_error_query_result_get_description(
-                       result, &description);
-       LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
-
-       /* Open error query result element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_error_query_result);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, mi_lttng_element_error_query_result_name, name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Description. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_error_query_result_description,
-                       description);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize the result according to its sub type. */
-       switch (type) {
-       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
-               ret_code = lttng_error_query_result_counter_mi_serialize(
-                               result, writer);
-               break;
-       default:
-               abort();
-       }
-
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close error query result element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-enum lttng_error_code lttng_error_query_results_mi_serialize(
-               const struct lttng_error_query_results *results,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       unsigned int i, count;
-       enum lttng_error_query_results_status results_status;
-
-       LTTNG_ASSERT(results);
-       LTTNG_ASSERT(writer);
-
-       /* Open error query results element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_error_query_results);
-       if (ret) {
-               goto mi_error;
-       }
-
-       results_status = lttng_error_query_results_get_count(results, &count);
-       LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK);
-
-       for (i = 0; i < count; i++) {
-               const struct lttng_error_query_result *result;
-
-               results_status = lttng_error_query_results_get_result(
-                               results, &result, i);
-               LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK);
-
-               /* A single error query result. */
-               ret_code = lttng_error_query_result_mi_serialize(result, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close error query results. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/error-query.cpp b/src/common/error-query.cpp
new file mode 100644 (file)
index 0000000..5d369c9
--- /dev/null
@@ -0,0 +1,1220 @@
+/*
+ * error-query.c
+ *
+ * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/action/list-internal.h>
+#include <lttng/action/path-internal.h>
+#include <lttng/error-query-internal.h>
+#include <lttng/error-query.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <stddef.h>
+
+struct lttng_error_query {
+       enum lttng_error_query_target_type target_type;
+};
+
+struct lttng_error_query_comm {
+       /* enum lttng_error_query_target_type */
+       int8_t target_type;
+       /* Target-specific payload. */
+       char payload[];
+};
+
+struct lttng_error_query_trigger {
+       struct lttng_error_query parent;
+       /* Mutable only because of the reference count. */
+       struct lttng_trigger *trigger;
+};
+
+struct lttng_error_query_condition {
+       struct lttng_error_query parent;
+       /* Mutable only because of the reference count. */
+       struct lttng_trigger *trigger;
+};
+
+struct lttng_error_query_action {
+       struct lttng_error_query parent;
+       /* Mutable only because of the reference count. */
+       struct lttng_trigger *trigger;
+       struct lttng_action_path action_path;
+};
+
+struct lttng_error_query_result {
+       enum lttng_error_query_result_type type;
+       char *name;
+       char *description;
+};
+
+struct lttng_error_query_result_comm {
+       /* enum lttng_error_query_result_type */
+       uint8_t type;
+       /* Length of name (including null-terminator). */
+       uint32_t name_len;
+       /* Length of description (including null-terminator). */
+       uint32_t description_len;
+       /* Name, description, and type-specific payload follow. */
+       char payload[];
+} LTTNG_PACKED;
+
+struct lttng_error_query_result_counter_comm {
+       uint64_t value;
+} LTTNG_PACKED;
+
+struct lttng_error_query_result_counter {
+       struct lttng_error_query_result parent;
+       uint64_t value;
+};
+
+struct lttng_error_query_results_comm {
+       uint32_t count;
+       /* `count` instances of `struct lttng_error_query_result` follow. */
+       char payload[];
+} LTTNG_PACKED;
+
+struct lttng_error_query_results {
+       struct lttng_dynamic_pointer_array results;
+};
+
+static
+enum lttng_error_code lttng_error_query_result_mi_serialize(
+               const struct lttng_error_query_result *result,
+               struct mi_writer *writer);
+
+static
+enum lttng_error_code lttng_error_query_result_counter_mi_serialize(
+               const struct lttng_error_query_result *result,
+               struct mi_writer *writer);
+
+struct lttng_error_query *lttng_error_query_trigger_create(
+               const struct lttng_trigger *trigger)
+{
+       struct lttng_error_query_trigger *query = NULL;
+       struct lttng_trigger *trigger_copy = NULL;
+
+       if (!trigger) {
+               goto end;
+       }
+
+       trigger_copy = lttng_trigger_copy(trigger);
+       if (!trigger_copy) {
+               goto end;
+       }
+
+       query = (lttng_error_query_trigger *) zmalloc(sizeof(*query));
+       if (!query) {
+               PERROR("Failed to allocate trigger error query");
+               goto error;
+       }
+
+       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER;
+       query->trigger = trigger_copy;
+       trigger_copy = NULL;
+
+error:
+       lttng_trigger_put(trigger_copy);
+end:
+       return query ? &query->parent : NULL;
+}
+
+struct lttng_error_query *lttng_error_query_condition_create(
+               const struct lttng_trigger *trigger)
+{
+       struct lttng_error_query_condition *query = NULL;
+       struct lttng_trigger *trigger_copy = NULL;
+
+       if (!trigger) {
+               goto end;
+       }
+
+       trigger_copy = lttng_trigger_copy(trigger);
+       if (!trigger_copy) {
+               goto end;
+       }
+
+       query = (lttng_error_query_condition *) zmalloc(sizeof(*query));
+       if (!query) {
+               PERROR("Failed to allocate condition error query");
+               goto error;
+       }
+
+       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION;
+       query->trigger = trigger_copy;
+       trigger_copy = NULL;
+
+error:
+       lttng_trigger_put(trigger_copy);
+end:
+       return query ? &query->parent : NULL;
+}
+
+static
+struct lttng_action *get_trigger_action_from_path(
+               struct lttng_trigger *trigger,
+               const struct lttng_action_path *action_path)
+{
+       size_t index_count, i;
+       enum lttng_action_path_status path_status;
+       struct lttng_action *current_action = NULL;
+
+       path_status = lttng_action_path_get_index_count(
+                       action_path, &index_count);
+       if (path_status != LTTNG_ACTION_PATH_STATUS_OK) {
+               goto end;
+       }
+
+       current_action = lttng_trigger_get_action(trigger);
+       for (i = 0; i < index_count; i++) {
+               uint64_t path_index;
+
+               path_status = lttng_action_path_get_index_at_index(
+                               action_path, i, &path_index);
+               current_action = lttng_action_list_borrow_mutable_at_index(
+                               current_action, path_index);
+               if (!current_action) {
+                       /* Invalid action path. */
+                       goto end;
+               }
+       }
+
+end:
+       return current_action;
+}
+
+static
+bool is_valid_action_path(const struct lttng_trigger *trigger,
+               const struct lttng_action_path *action_path)
+{
+       /*
+        * While 'trigger's constness is casted-away, the trigger and resulting
+        * action are not modified; we merely check for the action's existence.
+        */
+       return !!get_trigger_action_from_path(
+                       (struct lttng_trigger *) trigger, action_path);
+}
+
+struct lttng_error_query *lttng_error_query_action_create(
+               const struct lttng_trigger *trigger,
+               const struct lttng_action_path *action_path)
+{
+       struct lttng_error_query_action *query = NULL;
+       struct lttng_trigger *trigger_copy = NULL;
+       int ret_copy;
+
+       if (!trigger || !action_path ||
+                       !is_valid_action_path(trigger, action_path)) {
+               goto end;
+       }
+
+       trigger_copy = lttng_trigger_copy(trigger);
+       if (!trigger_copy) {
+               goto end;
+       }
+
+       query = (lttng_error_query_action *) zmalloc(sizeof(*query));
+       if (!query) {
+               PERROR("Failed to allocate action error query");
+               goto error;
+       }
+
+       ret_copy = lttng_action_path_copy(action_path, &query->action_path);
+       if (ret_copy) {
+               goto error;
+       }
+
+       query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION;
+       query->trigger = trigger_copy;
+       trigger_copy = NULL;
+       goto end;
+
+error:
+       lttng_trigger_put(trigger_copy);
+       lttng_error_query_destroy(query ? &query->parent : NULL);
+end:
+       return query ? &query->parent : NULL;
+}
+
+void lttng_error_query_destroy(struct lttng_error_query *query)
+{
+       struct lttng_error_query_trigger *trigger_query;
+
+       if (!query) {
+               return;
+       }
+
+       trigger_query = container_of(query, typeof(*trigger_query), parent);
+       lttng_trigger_put(trigger_query->trigger);
+       free(trigger_query);
+}
+
+static
+int lttng_error_query_result_counter_serialize(
+               const struct lttng_error_query_result *result,
+               struct lttng_payload *payload)
+{
+       const struct lttng_error_query_result_counter *counter_result;
+
+       LTTNG_ASSERT(result->type == LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER);
+       counter_result = container_of(result, typeof(*counter_result), parent);
+
+       lttng_error_query_result_counter_comm comm = {
+               .value = counter_result->value,
+       };
+
+       return lttng_dynamic_buffer_append(&payload->buffer,
+                       &comm,
+                       sizeof(struct lttng_error_query_result_counter_comm));
+}
+
+int lttng_error_query_result_serialize(
+               const struct lttng_error_query_result *result,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_error_query_result_comm header = {
+               .type = (uint8_t) result->type,
+               .name_len = (typeof(header.name_len)) strlen(result->name) + 1,
+               .description_len = (typeof(header.name_len)) strlen(result->description) + 1,
+       };
+
+       /* Header. */
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &header, sizeof(header));
+       if (ret) {
+               ERR("Failed to append error query result communication header to payload");
+               goto end;
+       }
+
+       /* Name. */
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, result->name, header.name_len);
+       if (ret) {
+               ERR("Failed to append error query result name to payload");
+               goto end;
+       }
+
+       /* Description. */
+       ret = lttng_dynamic_buffer_append(&payload->buffer, result->description,
+                       header.description_len);
+       if (ret) {
+               ERR("Failed to append error query result description to payload");
+               goto end;
+       }
+
+       /* Type-specific payload. */
+       switch (result->type) {
+       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
+               ret = lttng_error_query_result_counter_serialize(
+                               result, payload);
+               if (ret) {
+                       ERR("Failed to serialize counter error query result");
+                       goto end;
+               }
+               break;
+       default:
+               abort();
+       }
+
+end:
+       return ret;
+}
+
+static
+int lttng_error_query_result_init(
+               struct lttng_error_query_result *result,
+               enum lttng_error_query_result_type result_type,
+               const char *name,
+               const char *description)
+{
+       int ret;
+
+       LTTNG_ASSERT(name);
+       LTTNG_ASSERT(description);
+
+       result->type = result_type;
+
+       result->name = strdup(name);
+       if (!result->name) {
+               PERROR("Failed to copy error query result name");
+               ret = -1;
+               goto end;
+       }
+
+       result->description = strdup(description);
+       if (!result->description) {
+               PERROR("Failed to copy error query result description");
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+void lttng_error_query_result_destroy(struct lttng_error_query_result *counter)
+{
+       if (!counter) {
+               return;
+       }
+
+       switch (counter->type) {
+       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
+               /* Nothing to tear down. */
+               break;
+       default:
+               abort();
+       }
+
+       free(counter->name);
+       free(counter->description);
+       free(counter);
+}
+
+struct lttng_error_query_result *
+lttng_error_query_result_counter_create(
+               const char *name, const char *description, uint64_t value)
+{
+       int init_ret;
+       struct lttng_error_query_result_counter *counter;
+
+       counter = (lttng_error_query_result_counter *) zmalloc(sizeof(*counter));
+       if (!counter) {
+               PERROR("Failed to allocate error query counter result");
+               goto end;
+       }
+
+       init_ret = lttng_error_query_result_init(&counter->parent,
+                       LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER, name,
+                       description);
+       if (init_ret) {
+               goto error;
+       }
+
+       counter->value = value;
+       goto end;
+error:
+       lttng_error_query_result_destroy(&counter->parent);
+end:
+       return counter ? &counter->parent : NULL;
+}
+
+static
+void destroy_result(void *ptr)
+{
+       struct lttng_error_query_result *result = (typeof(result)) ptr;
+
+       lttng_error_query_result_destroy(result);
+}
+
+struct lttng_error_query_results *lttng_error_query_results_create(void)
+{
+       struct lttng_error_query_results *set = (lttng_error_query_results *) zmalloc(sizeof(*set));
+
+       if (!set) {
+               PERROR("Failed to allocate an error query result set");
+               goto end;
+       }
+
+       lttng_dynamic_pointer_array_init(&set->results, destroy_result);
+end:
+       return set;
+}
+
+int lttng_error_query_results_add_result(
+               struct lttng_error_query_results *results,
+               struct lttng_error_query_result *result)
+{
+       return lttng_dynamic_pointer_array_add_pointer(
+                       &results->results, result);
+}
+
+ssize_t lttng_error_query_result_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_error_query_result **result)
+{
+       ssize_t used_size = 0;
+       struct lttng_error_query_result_comm *header;
+       struct lttng_payload_view header_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*header));
+       const char *name;
+       const char *description;
+
+       if (!lttng_payload_view_is_valid(&header_view)) {
+               used_size = -1;
+               goto end;
+       }
+
+       header = (typeof(header)) header_view.buffer.data;
+       used_size += sizeof(*header);
+
+       {
+               struct lttng_payload_view name_view =
+                               lttng_payload_view_from_view(view, used_size,
+                                               header->name_len);
+
+               if (!lttng_payload_view_is_valid(&name_view) ||
+                               !lttng_buffer_view_contains_string(
+                                               &name_view.buffer,
+                                               name_view.buffer.data,
+                                               header->name_len)) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               name = name_view.buffer.data;
+               used_size += header->name_len;
+       }
+
+       {
+               struct lttng_payload_view description_view =
+                               lttng_payload_view_from_view(view, used_size,
+                                               header->description_len);
+
+               if (!lttng_payload_view_is_valid(&description_view) ||
+                               !lttng_buffer_view_contains_string(
+                                               &description_view.buffer,
+                                               description_view.buffer.data,
+                                               header->description_len)) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               description = description_view.buffer.data;
+               used_size += header->description_len;
+       }
+
+       switch (header->type) {
+       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
+       {
+               struct lttng_error_query_result_counter_comm *counter;
+               struct lttng_payload_view counter_payload_view =
+                               lttng_payload_view_from_view(view, used_size,
+                                               sizeof(*counter));
+
+               if (!lttng_payload_view_is_valid(&counter_payload_view)) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               counter = (typeof(counter)) counter_payload_view.buffer.data;
+               *result = lttng_error_query_result_counter_create(
+                               name, description, counter->value);
+               if (!*result) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               used_size += sizeof(*counter);
+               break;
+       }
+       default:
+               used_size = -1;
+               goto end;
+       }
+
+end:
+       return used_size;
+}
+
+int lttng_error_query_results_serialize(
+               const struct lttng_error_query_results *results,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t result_index;
+       const size_t result_count = lttng_dynamic_pointer_array_get_count(
+                       &results->results);
+       const struct lttng_error_query_results_comm header = {
+               .count = (typeof(header.count)) result_count,
+       };
+
+       /* Header. */
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &header, sizeof(header));
+       if (ret) {
+               ERR("Failed to append error query result set header to payload");
+               goto end;
+       }
+
+       /* Results. */
+       for (result_index = 0; result_index < result_count; result_index++) {
+               const struct lttng_error_query_result *result = (typeof(result))
+                               lttng_dynamic_pointer_array_get_pointer(
+                                               &results->results,
+                                               result_index);
+
+               ret = lttng_error_query_result_serialize(result, payload);
+               if (ret) {
+                       ERR("Failed to append error query result to payload");
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+ssize_t lttng_error_query_results_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_error_query_results **_results)
+{
+       size_t result_index;
+       ssize_t total_used_size = 0;
+       struct lttng_error_query_results_comm *header;
+       struct lttng_payload_view header_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*header));
+       struct lttng_error_query_results *results = NULL;
+
+       if (!lttng_payload_view_is_valid(&header_view)) {
+               ERR("Failed to map view to error query result set header");
+               total_used_size = -1;
+               goto end;
+       }
+
+       header = (typeof(header)) header_view.buffer.data;
+       total_used_size += sizeof(*header);
+       results = lttng_error_query_results_create();
+       if (!results) {
+               total_used_size = -1;
+               goto end;
+       }
+
+       for (result_index = 0; result_index < header->count; result_index++) {
+               ssize_t used_size;
+               struct lttng_error_query_result *result;
+               struct lttng_payload_view result_view =
+                               lttng_payload_view_from_view(
+                                               view, total_used_size, -1);
+
+               if (!lttng_payload_view_is_valid(&result_view)) {
+                       total_used_size = -1;
+                       goto end;
+               }
+
+               used_size = lttng_error_query_result_create_from_payload(
+                               &result_view, &result);
+               if (used_size < 0) {
+                       total_used_size = -1;
+                       goto end;
+               }
+
+               total_used_size += used_size;
+
+               if (lttng_dynamic_pointer_array_add_pointer(
+                                   &results->results, result)) {
+                       lttng_error_query_result_destroy(result);
+                       total_used_size = -1;
+                       goto end;
+               }
+       }
+
+       *_results = results;
+       results = NULL;
+end:
+       lttng_error_query_results_destroy(results);
+       return total_used_size;
+}
+
+static
+int lttng_error_query_trigger_serialize(const struct lttng_error_query *query,
+               struct lttng_payload *payload)
+{
+       int ret;
+       const struct lttng_error_query_trigger *query_trigger =
+                       container_of(query, typeof(*query_trigger), parent);
+
+       if (!lttng_trigger_validate(query_trigger->trigger)) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_trigger_serialize(query_trigger->trigger, payload);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int lttng_error_query_condition_serialize(const struct lttng_error_query *query,
+               struct lttng_payload *payload)
+{
+       int ret;
+       const struct lttng_error_query_condition *query_trigger =
+                       container_of(query, typeof(*query_trigger), parent);
+
+       if (!lttng_trigger_validate(query_trigger->trigger)) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_trigger_serialize(query_trigger->trigger, payload);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int lttng_error_query_action_serialize(const struct lttng_error_query *query,
+               struct lttng_payload *payload)
+{
+       int ret;
+       const struct lttng_error_query_action *query_action =
+                       container_of(query, typeof(*query_action), parent);
+
+       if (!lttng_trigger_validate(query_action->trigger)) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_trigger_serialize(query_action->trigger, payload);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_action_path_serialize(&query_action->action_path, payload);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+enum lttng_error_query_target_type lttng_error_query_get_target_type(
+               const struct lttng_error_query *query)
+{
+       return query->target_type;
+}
+
+const struct lttng_trigger *lttng_error_query_trigger_borrow_target(
+               const struct lttng_error_query *query)
+{
+       const struct lttng_error_query_trigger *query_trigger =
+                       container_of(query, typeof(*query_trigger), parent);
+
+       return query_trigger->trigger;
+}
+
+const struct lttng_trigger *lttng_error_query_condition_borrow_target(
+               const struct lttng_error_query *query)
+{
+       const struct lttng_error_query_condition *query_trigger =
+                       container_of(query, typeof(*query_trigger), parent);
+
+       return query_trigger->trigger;
+}
+
+const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target(
+               const struct lttng_error_query *query)
+{
+       const struct lttng_error_query_action *query_action =
+                       container_of(query, typeof(*query_action), parent);
+
+       return query_action->trigger;
+}
+
+struct lttng_action *lttng_error_query_action_borrow_action_target(
+       const struct lttng_error_query *query,
+       struct lttng_trigger *trigger)
+{
+       const struct lttng_error_query_action *query_action =
+                       container_of(query, typeof(*query_action), parent);
+
+       return get_trigger_action_from_path(
+                       trigger, &query_action->action_path);
+}
+
+int lttng_error_query_serialize(const struct lttng_error_query *query,
+               struct lttng_payload *payload)
+{
+       int ret;
+       const struct lttng_error_query_comm header = {
+               .target_type = (typeof(header.target_type)) query->target_type,
+       };
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &header, sizeof(header));
+       if (ret) {
+               ERR("Failed to append error query header to payload");
+               goto end;
+       }
+
+       switch (query->target_type) {
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
+               ret = lttng_error_query_trigger_serialize(query, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
+               ret = lttng_error_query_condition_serialize(query, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
+               ret = lttng_error_query_action_serialize(query, payload);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       default:
+               abort();
+       }
+end:
+       return ret;
+}
+
+ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view,
+               struct lttng_error_query **query)
+{
+       ssize_t used_size = 0;
+       struct lttng_error_query_comm *header;
+       struct lttng_trigger *trigger = NULL;
+       struct lttng_payload_view header_view =
+                       lttng_payload_view_from_view(view, 0, sizeof(*header));
+
+       if (!lttng_payload_view_is_valid(&header_view)) {
+               ERR("Failed to map error query header");
+               used_size = -1;
+               goto end;
+       }
+
+       used_size = sizeof(*header);
+
+       header = (typeof(header)) header_view.buffer.data;
+       switch ((enum lttng_error_query_target_type) header->target_type) {
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER:
+       {
+               ssize_t trigger_used_size;
+               struct lttng_payload_view trigger_view =
+                               lttng_payload_view_from_view(
+                                               view, used_size, -1);
+
+               if (!lttng_payload_view_is_valid(&trigger_view)) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               trigger_used_size = lttng_trigger_create_from_payload(
+                               &trigger_view, &trigger);
+               if (trigger_used_size < 0) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               used_size += trigger_used_size;
+
+               *query = lttng_error_query_trigger_create(trigger);
+               if (!*query) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION:
+       {
+               ssize_t trigger_used_size;
+               struct lttng_payload_view trigger_view =
+                               lttng_payload_view_from_view(
+                                               view, used_size, -1);
+
+               if (!lttng_payload_view_is_valid(&trigger_view)) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               trigger_used_size = lttng_trigger_create_from_payload(
+                               &trigger_view, &trigger);
+               if (trigger_used_size < 0) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               used_size += trigger_used_size;
+
+               *query = lttng_error_query_condition_create(trigger);
+               if (!*query) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION:
+       {
+               struct lttng_action_path *action_path = NULL;
+
+               {
+                       ssize_t trigger_used_size;
+                       struct lttng_payload_view trigger_view =
+                                       lttng_payload_view_from_view(
+                                                       view, used_size, -1);
+
+                       if (!lttng_payload_view_is_valid(&trigger_view)) {
+                               used_size = -1;
+                               goto end;
+                       }
+
+                       trigger_used_size = lttng_trigger_create_from_payload(
+                                       &trigger_view, &trigger);
+                       if (trigger_used_size < 0) {
+                               used_size = -1;
+                               goto end;
+                       }
+
+                       used_size += trigger_used_size;
+               }
+
+               {
+                       ssize_t action_path_used_size;
+                       struct lttng_payload_view action_path_view =
+                                       lttng_payload_view_from_view(
+                                                       view, used_size, -1);
+
+                       if (!lttng_payload_view_is_valid(&action_path_view)) {
+                               used_size = -1;
+                               goto end;
+                       }
+
+                       action_path_used_size = lttng_action_path_create_from_payload(
+                                       &action_path_view, &action_path);
+                       if (action_path_used_size < 0) {
+                               used_size = -1;
+                               goto end;
+                       }
+
+                       used_size += action_path_used_size;
+               }
+
+               *query = lttng_error_query_action_create(
+                               trigger, action_path);
+               lttng_action_path_destroy(action_path);
+               if (!*query) {
+                       used_size = -1;
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               used_size = -1;
+               goto end;
+       }
+
+end:
+       lttng_trigger_put(trigger);
+       return used_size;
+}
+
+enum lttng_error_query_results_status lttng_error_query_results_get_count(
+               const struct lttng_error_query_results *results,
+               unsigned int *count)
+{
+       enum lttng_error_query_results_status status;
+
+       if (!results || !count) {
+               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       *count = lttng_dynamic_pointer_array_get_count(&results->results);
+       status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_error_query_results_status
+lttng_error_query_results_get_result(
+               const struct lttng_error_query_results *results,
+               const struct lttng_error_query_result **result,
+               unsigned int index)
+{
+       unsigned int result_count;
+       enum lttng_error_query_results_status status;
+
+       if (!results || !result) {
+               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       status = lttng_error_query_results_get_count(results, &result_count);
+       if (status != LTTNG_ERROR_QUERY_RESULTS_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= result_count) {
+               status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       *result = (typeof(*result)) lttng_dynamic_pointer_array_get_pointer(
+                       &results->results, index);
+       LTTNG_ASSERT(*result);
+       status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK;
+end:
+       return status;
+}
+
+void lttng_error_query_results_destroy(
+               struct lttng_error_query_results *results)
+{
+       if (!results) {
+               return;
+       }
+
+       lttng_dynamic_pointer_array_reset(&results->results);
+       free(results);
+}
+
+enum lttng_error_query_result_type
+lttng_error_query_result_get_type(const struct lttng_error_query_result *result)
+{
+       return result ? result->type : LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN;
+}
+
+enum lttng_error_query_result_status lttng_error_query_result_get_name(
+               const struct lttng_error_query_result *result,
+               const char **name)
+{
+       enum lttng_error_query_result_status status;
+
+       if (!result || !name) {
+               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       *name = result->name;
+       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_error_query_result_status lttng_error_query_result_get_description(
+               const struct lttng_error_query_result *result,
+               const char **description)
+{
+       enum lttng_error_query_result_status status;
+
+       if (!result || !description) {
+               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       *description = result->description;
+       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
+end:
+       return status;
+}
+
+enum lttng_error_query_result_status lttng_error_query_result_counter_get_value(
+               const struct lttng_error_query_result *result,
+               uint64_t *value)
+{
+       enum lttng_error_query_result_status status;
+       const struct lttng_error_query_result_counter *counter_result;
+
+       if (!result || !value ||
+                       result->type != LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) {
+               status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER;
+               goto end;
+       }
+
+       counter_result = container_of(result, typeof(*counter_result), parent);
+
+       *value = counter_result->value;
+       status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK;
+end:
+       return status;
+}
+
+static
+enum lttng_error_code lttng_error_query_result_counter_mi_serialize(
+               const struct lttng_error_query_result *result,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_error_query_result_status status;
+       uint64_t value;
+
+       LTTNG_ASSERT(result);
+       LTTNG_ASSERT(writer);
+
+       status = lttng_error_query_result_counter_get_value(result, &value);
+       LTTNG_ASSERT(status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
+
+       /* Open error query result counter element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_error_query_result_counter);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Value. */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_error_query_result_counter_value,
+                       value);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close error query result counter element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+enum lttng_error_code lttng_error_query_result_mi_serialize(
+               const struct lttng_error_query_result *result,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_error_query_result_status result_status;
+       enum lttng_error_query_result_type type;
+       const char *name = NULL;
+       const char *description = NULL;
+
+       LTTNG_ASSERT(result);
+       LTTNG_ASSERT(writer);
+
+       type = lttng_error_query_result_get_type(result);
+
+       result_status = lttng_error_query_result_get_name(result, &name);
+       LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
+
+       result_status = lttng_error_query_result_get_description(
+                       result, &description);
+       LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK);
+
+       /* Open error query result element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_error_query_result);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, mi_lttng_element_error_query_result_name, name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Description. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_error_query_result_description,
+                       description);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize the result according to its sub type. */
+       switch (type) {
+       case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER:
+               ret_code = lttng_error_query_result_counter_mi_serialize(
+                               result, writer);
+               break;
+       default:
+               abort();
+       }
+
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close error query result element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code lttng_error_query_results_mi_serialize(
+               const struct lttng_error_query_results *results,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       unsigned int i, count;
+       enum lttng_error_query_results_status results_status;
+
+       LTTNG_ASSERT(results);
+       LTTNG_ASSERT(writer);
+
+       /* Open error query results element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_error_query_results);
+       if (ret) {
+               goto mi_error;
+       }
+
+       results_status = lttng_error_query_results_get_count(results, &count);
+       LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK);
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_error_query_result *result;
+
+               results_status = lttng_error_query_results_get_result(
+                               results, &result, i);
+               LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK);
+
+               /* A single error query result. */
+               ret_code = lttng_error_query_result_mi_serialize(result, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close error query results. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/error.c b/src/common/error.c
deleted file mode 100644 (file)
index b888c26..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <common/common.h>
-#include <common/thread.h>
-#include <common/compat/errno.h>
-#include <common/compat/getenv.h>
-#include <lttng/lttng-error.h>
-
-#include "error.h"
-
-#define ERROR_INDEX(code) (code - LTTNG_OK)
-
-/*
- * lttng_opt_abort_on_error: unset: -1, disabled: 0, enabled: 1.
- * Controlled by the LTTNG_ABORT_ON_ERROR environment variable.
- */
-static int lttng_opt_abort_on_error = -1;
-
-/* TLS variable that contains the time of one single log entry. */
-DEFINE_URCU_TLS(struct log_time, error_log_time);
-DEFINE_URCU_TLS(const char *, logger_thread_name);
-
-const char *log_add_time(void)
-{
-       int ret;
-       struct tm tm, *res;
-       struct timespec tp;
-       time_t now;
-       const int errsv = errno;
-
-       ret = lttng_clock_gettime(CLOCK_REALTIME, &tp);
-       if (ret < 0) {
-               goto error;
-       }
-       now = (time_t) tp.tv_sec;
-
-       res = localtime_r(&now, &tm);
-       if (!res) {
-               goto error;
-       }
-
-       /* Format time in the TLS variable. */
-       ret = snprintf(URCU_TLS(error_log_time).str, sizeof(URCU_TLS(error_log_time).str),
-                       "%02d:%02d:%02d.%09ld",
-                       tm.tm_hour, tm.tm_min, tm.tm_sec, tp.tv_nsec);
-       if (ret < 0) {
-               goto error;
-       }
-
-       errno = errsv;
-       return URCU_TLS(error_log_time).str;
-
-error:
-       /* Return an empty string on error so logging is not affected. */
-       errno = errsv;
-       return "";
-}
-
-void logger_set_thread_name(const char *name, bool set_pthread_name)
-{
-       int ret;
-
-       LTTNG_ASSERT(name);
-       URCU_TLS(logger_thread_name) = name;
-
-       if (set_pthread_name) {
-               ret = lttng_thread_setname(name);
-               if (ret && ret != -ENOSYS) {
-                       /* Don't fail as this is not essential. */
-                       DBG("Failed to set pthread name attribute");
-               }
-       }
-}
-
-/*
- * Human readable error message.
- */
-static const char *error_string_array[] = {
-       /* LTTNG_OK code MUST be at the top for the ERROR_INDEX macro to work */
-       [ ERROR_INDEX(LTTNG_OK) ] = "Success",
-       [ ERROR_INDEX(LTTNG_ERR_UNK) ] = "Unknown error",
-       [ ERROR_INDEX(LTTNG_ERR_UND) ] = "Undefined command",
-       [ ERROR_INDEX(LTTNG_ERR_UNKNOWN_DOMAIN) ] = "Unknown tracing domain",
-       [ ERROR_INDEX(LTTNG_ERR_NO_SESSION) ] = "No session found",
-       [ ERROR_INDEX(LTTNG_ERR_CREATE_DIR_FAIL) ] = "Create directory failed",
-       [ ERROR_INDEX(LTTNG_ERR_SESSION_FAIL) ] = "Create session failed",
-       [ ERROR_INDEX(LTTNG_ERR_SESS_NOT_FOUND) ] = "Session name not found",
-       [ ERROR_INDEX(LTTNG_ERR_FATAL) ] = "Fatal error of the session daemon",
-       [ ERROR_INDEX(LTTNG_ERR_SELECT_SESS) ] = "A session MUST be selected",
-       [ ERROR_INDEX(LTTNG_ERR_EXIST_SESS) ] = "Session name already exists",
-       [ ERROR_INDEX(LTTNG_ERR_NO_EVENT) ] = "Event not found",
-       [ ERROR_INDEX(LTTNG_ERR_CONNECT_FAIL) ] = "Unable to connect to Unix socket",
-       [ ERROR_INDEX(LTTNG_ERR_EPERM) ] = "Permission denied",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_NA) ] = "Kernel tracer not available",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_VERSION) ] = "Kernel tracer version is not compatible",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_EVENT_EXIST) ] = "Kernel event already exists",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_SESS_FAIL) ] = "Kernel create session failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_EXIST) ] = "Kernel channel already exists",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_FAIL) ] = "Kernel create channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_NOT_FOUND) ] = "Kernel channel not found",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_DISABLE_FAIL) ] = "Disable kernel channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_ENABLE_FAIL) ] = "Enable kernel channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CONTEXT_FAIL) ] = "Add kernel context failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_ENABLE_FAIL) ] = "Enable kernel event failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_DISABLE_FAIL) ] = "Disable kernel event failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_META_FAIL) ] = "Opening metadata failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_START_FAIL) ] = "Starting kernel trace failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_STOP_FAIL) ] = "Stopping kernel trace failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CONSUMER_FAIL) ] = "Kernel consumer start failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_STREAM_FAIL) ] = "Kernel create stream failed",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_LIST_FAIL) ] = "Listing kernel events failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CALIBRATE_FAIL) ] = "UST calibration failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_SESS_FAIL) ] = "UST create session failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_FAIL) ] = "UST create channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_EXIST) ] = "UST channel already exist",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_NOT_FOUND) ] = "UST channel not found",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_DISABLE_FAIL) ] = "Disable UST channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_ENABLE_FAIL) ] = "Enable UST channel failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_ENABLE_FAIL) ] = "Enable UST event failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_DISABLE_FAIL) ] = "Disable UST event failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_META_FAIL) ] = "Opening metadata failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_START_FAIL) ] = "Starting UST trace failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_STOP_FAIL) ] = "Stopping UST trace failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CONSUMER64_FAIL) ] = "64-bit UST consumer start failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CONSUMER32_FAIL) ] = "32-bit UST consumer start failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_STREAM_FAIL) ] = "UST create stream failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_LIST_FAIL) ] = "Listing UST events failed",
-       [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_EXIST) ] = "UST event already exist",
-       [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_NOT_FOUND)] = "UST event not found",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_EXIST)] = "UST context already exist",
-       [ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_INVAL)] = "UST invalid context",
-       [ ERROR_INDEX(LTTNG_ERR_NEED_ROOT_SESSIOND) ] = "Tracing the kernel requires a root lttng-sessiond daemon, as well as \"tracing\" group membership or root user ID for the lttng client",
-       [ ERROR_INDEX(LTTNG_ERR_NO_UST) ] = "LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled",
-       [ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STARTED) ] = "Tracing has already been started once",
-       [ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STOPPED) ] = "Tracing has already been stopped",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_EVENT_ENOSYS) ] = "Kernel event type not supported",
-       [ ERROR_INDEX(LTTNG_ERR_NEED_CHANNEL_NAME) ] = "Non-default channel exists within session: channel name needs to be specified with '-c name'",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID) ] = "Invalid parameter",
-       [ ERROR_INDEX(LTTNG_ERR_NO_USTCONSUMERD) ] = "No UST consumer detected",
-       [ ERROR_INDEX(LTTNG_ERR_NO_KERNCONSUMERD) ] = "No kernel consumer detected",
-       [ ERROR_INDEX(LTTNG_ERR_EVENT_EXIST_LOGLEVEL) ] = "Event already enabled with different loglevel",
-       [ ERROR_INDEX(LTTNG_ERR_URL_DATA_MISS) ] = "Missing data path URL",
-       [ ERROR_INDEX(LTTNG_ERR_URL_CTRL_MISS) ] = "Missing control path URL",
-       [ ERROR_INDEX(LTTNG_ERR_ENABLE_CONSUMER_FAIL) ] = "Enabling consumer failed",
-       [ ERROR_INDEX(LTTNG_ERR_RELAYD_CONNECT_FAIL) ] = "Unable to connect to lttng-relayd",
-       [ ERROR_INDEX(LTTNG_ERR_RELAYD_VERSION_FAIL) ] = "Relay daemon not compatible",
-       [ ERROR_INDEX(LTTNG_ERR_FILTER_INVAL) ] = "Invalid filter bytecode",
-       [ ERROR_INDEX(LTTNG_ERR_FILTER_NOMEM) ] = "Not enough memory for filter bytecode",
-       [ ERROR_INDEX(LTTNG_ERR_FILTER_EXIST) ] = "Filter already exist",
-       [ ERROR_INDEX(LTTNG_ERR_NO_CONSUMER) ] = "Consumer not found for recording session",
-       [ ERROR_INDEX(LTTNG_ERR_NO_SESSIOND) ] = "No session daemon is available",
-       [ ERROR_INDEX(LTTNG_ERR_SESSION_STARTED) ] = "Session is running",
-       [ ERROR_INDEX(LTTNG_ERR_NOT_SUPPORTED) ] = "Operation not supported",
-       [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_ENABLED) ] = "UST event already enabled",
-       [ ERROR_INDEX(LTTNG_ERR_SET_URL) ] = "Error setting URL",
-       [ ERROR_INDEX(LTTNG_ERR_URL_EXIST) ] = "URL already exists",
-       [ ERROR_INDEX(LTTNG_ERR_BUFFER_NOT_SUPPORTED)] = "Buffer type not supported",
-       [ ERROR_INDEX(LTTNG_ERR_BUFFER_TYPE_MISMATCH)] = "Buffer type mismatch for session",
-       [ ERROR_INDEX(LTTNG_ERR_NOMEM)] = "Not enough memory",
-       [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST) ] = "Snapshot output already exists",
-       [ ERROR_INDEX(LTTNG_ERR_START_SESSION_ONCE) ] = "Session needs to be started once",
-       [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_FAIL) ] = "Snapshot record failed",
-       [ ERROR_INDEX(LTTNG_ERR_CHAN_EXIST) ] = "Channel already exists",
-       [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_NODATA) ] = "No data available in snapshot",
-       [ ERROR_INDEX(LTTNG_ERR_NO_CHANNEL) ] = "No channel found in the session",
-       [ ERROR_INDEX(LTTNG_ERR_SESSION_INVALID_CHAR) ] = "Invalid character found in session name",
-       [ ERROR_INDEX(LTTNG_ERR_SAVE_FILE_EXIST) ] = "Session file already exists",
-       [ ERROR_INDEX(LTTNG_ERR_SAVE_IO_FAIL) ] = "IO error while writing session configuration",
-       [ ERROR_INDEX(LTTNG_ERR_LOAD_INVALID_CONFIG) ] = "Invalid session configuration",
-       [ ERROR_INDEX(LTTNG_ERR_LOAD_IO_FAIL) ] = "IO error while reading a session configuration",
-       [ ERROR_INDEX(LTTNG_ERR_LOAD_SESSION_NOENT) ] = "Session file not found",
-       [ ERROR_INDEX(LTTNG_ERR_MAX_SIZE_INVALID) ] = "Snapshot max size is invalid",
-       [ ERROR_INDEX(LTTNG_ERR_MI_OUTPUT_TYPE) ] = "Invalid MI output format",
-       [ ERROR_INDEX(LTTNG_ERR_MI_IO_FAIL) ] = "IO error while writing MI output",
-       [ ERROR_INDEX(LTTNG_ERR_MI_NOT_IMPLEMENTED) ] = "Mi feature not implemented",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID_EVENT_NAME) ] = "Invalid event name",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID_CHANNEL_NAME) ] = "Invalid channel name",
-       [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_EXISTS) ] = "Process attribute is already tracked",
-       [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_MISSING) ] = "Process attribute was not tracked",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID_CHANNEL_DOMAIN) ] = "Invalid channel domain",
-       [ ERROR_INDEX(LTTNG_ERR_OVERFLOW) ] = "Overflow occurred",
-       [ ERROR_INDEX(LTTNG_ERR_SESSION_NOT_STARTED) ] = "Session not started",
-       [ ERROR_INDEX(LTTNG_ERR_LIVE_SESSION) ] = "Live sessions are not supported",
-       [ ERROR_INDEX(LTTNG_ERR_PER_PID_SESSION) ] = "Per-PID recording sessions are not supported",
-       [ ERROR_INDEX(LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE) ] = "Context unavailable on this kernel",
-       [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_FAIL) ] = "Failed to regenerate the state dump",
-       [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_NOMEM) ] = "Failed to regenerate the state dump, not enough memory",
-       [ ERROR_INDEX(LTTNG_ERR_NOT_SNAPSHOT_SESSION) ] = "Snapshot command can't be applied to a non-snapshot session",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID_TRIGGER) ] = "Invalid trigger",
-       [ ERROR_INDEX(LTTNG_ERR_TRIGGER_EXISTS) ] = "Trigger already registered",
-       [ ERROR_INDEX(LTTNG_ERR_TRIGGER_NOT_FOUND) ] = "Trigger not found",
-       [ ERROR_INDEX(LTTNG_ERR_COMMAND_CANCELLED) ] = "Command cancelled",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING) ] = "Rotation already pending for this session",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE) ] = "Rotation feature not available for this session's creation mode",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_SCHEDULE_SET) ] = "A session rotation schedule of this type is already set on the session",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET) ] = "No session rotation schedule of this type is set on the session",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP) ] = "Session was already rotated once since it became inactive",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_WRONG_VERSION) ] = "Session rotation is not supported by this kernel tracer version",
-       [ ERROR_INDEX(LTTNG_ERR_NO_SESSION_OUTPUT) ] = "Session has no output",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY) ] = "Rotation feature not available on the relay",
-       [ ERROR_INDEX(LTTNG_ERR_AGENT_TRACING_DISABLED) ] = "Session daemon agent tracing is disabled",
-       [ ERROR_INDEX(LTTNG_ERR_PROBE_LOCATION_INVAL) ] = "Invalid userspace probe location",
-       [ ERROR_INDEX(LTTNG_ERR_ELF_PARSING) ] = "ELF parsing error",
-       [ ERROR_INDEX(LTTNG_ERR_SDT_PROBE_SEMAPHORE) ] = "SDT probe guarded by a semaphore",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_FAIL_CONSUMER) ] = "Rotation failure on consumer",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER) ] = "Rotation rename failure on consumer",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER) ] = "Rotation pending check (local) failure on consumer",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER) ] = "Rotation pending check (relay) failure on consumer",
-       [ ERROR_INDEX(LTTNG_ERR_MKDIR_FAIL_CONSUMER) ] = "Directory creation failure on consumer",
-       [ ERROR_INDEX(LTTNG_ERR_CHAN_NOT_FOUND) ] = "Channel not found",
-       [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_UNSUPPORTED) ] = "Session configuration does not allow the use of snapshots",
-       [ ERROR_INDEX(LTTNG_ERR_SESSION_NOT_EXIST) ] = "Recording session does not exist",
-       [ 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",
-       [ ERROR_INDEX(LTTNG_ERR_FILE_CREATION_ERROR) ] = "Failed to create file",
-       [ ERROR_INDEX(LTTNG_ERR_TIMER_STOP_ERROR) ] = "Failed to stop a timer",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL) ] = "Rotation feature not supported by the kernel tracer",
-       [ ERROR_INDEX(LTTNG_ERR_CLEAR_RELAY_DISALLOWED) ] = "Relayd daemon peer does not allow sessions to be cleared",
-       [ ERROR_INDEX(LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY) ] = "Clearing a session is not supported by the relay daemon",
-       [ ERROR_INDEX(LTTNG_ERR_CLEAR_FAIL_CONSUMER) ] = "Consumer failed to clear the session",
-       [ ERROR_INDEX(LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR) ] = "Session was already cleared since it became inactive",
-       [ ERROR_INDEX(LTTNG_ERR_USER_NOT_FOUND) ] = "User not found",
-       [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found",
-       [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used",
-       [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy",
-       [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor",
-       [ ERROR_INDEX(LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) ] = "Invalid capture expression",
-       [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION) ] = "Failed to create event notifier",
-       [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING) ] = "Failed to initialize event notifier error accounting",
-       [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL) ] = "No index available in event notifier error accounting",
-       [ ERROR_INDEX(LTTNG_ERR_BUFFER_FLUSH_FAILED) ] = "Failed to flush stream buffer",
-
-       /* Last element */
-       [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code"
-};
-
-/*
- * Return ptr to string representing a human readable error code from the
- * lttng_error_code enum.
- *
- * These code MUST be negative in other to treat that as an error value.
- */
-const char *error_get_str(int32_t code)
-{
-       code = -code;
-
-       if (code < LTTNG_OK || code > LTTNG_ERR_NR) {
-               code = LTTNG_ERR_NR;
-       }
-
-       return error_string_array[ERROR_INDEX(code)];
-}
-
-void lttng_abort_on_error(void)
-{
-       if (lttng_opt_abort_on_error < 0) {
-               /* Use lttng_secure_getenv() to query its state. */
-               const char *value;
-
-               value = lttng_secure_getenv("LTTNG_ABORT_ON_ERROR");
-               if (value && !strcmp(value, "1")) {
-                       lttng_opt_abort_on_error = 1;
-               } else {
-                       lttng_opt_abort_on_error = 0;
-               }
-       }
-       if (lttng_opt_abort_on_error > 0) {
-               abort();
-       }
-}
diff --git a/src/common/error.cpp b/src/common/error.cpp
new file mode 100644 (file)
index 0000000..6ca5557
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <common/common.h>
+#include <common/thread.h>
+#include <common/compat/errno.h>
+#include <common/compat/getenv.h>
+#include <lttng/lttng-error.h>
+
+#include "error.h"
+
+/*
+ * lttng_opt_abort_on_error: unset: -1, disabled: 0, enabled: 1.
+ * Controlled by the LTTNG_ABORT_ON_ERROR environment variable.
+ */
+static int lttng_opt_abort_on_error = -1;
+
+/* TLS variable that contains the time of one single log entry. */
+DEFINE_URCU_TLS(struct log_time, error_log_time);
+DEFINE_URCU_TLS(const char *, logger_thread_name);
+
+const char *log_add_time(void)
+{
+       int ret;
+       struct tm tm, *res;
+       struct timespec tp;
+       time_t now;
+       const int errsv = errno;
+
+       ret = lttng_clock_gettime(CLOCK_REALTIME, &tp);
+       if (ret < 0) {
+               goto error;
+       }
+       now = (time_t) tp.tv_sec;
+
+       res = localtime_r(&now, &tm);
+       if (!res) {
+               goto error;
+       }
+
+       /* Format time in the TLS variable. */
+       ret = snprintf(URCU_TLS(error_log_time).str, sizeof(URCU_TLS(error_log_time).str),
+                       "%02d:%02d:%02d.%09ld",
+                       tm.tm_hour, tm.tm_min, tm.tm_sec, tp.tv_nsec);
+       if (ret < 0) {
+               goto error;
+       }
+
+       errno = errsv;
+       return URCU_TLS(error_log_time).str;
+
+error:
+       /* Return an empty string on error so logging is not affected. */
+       errno = errsv;
+       return "";
+}
+
+void logger_set_thread_name(const char *name, bool set_pthread_name)
+{
+       int ret;
+
+       LTTNG_ASSERT(name);
+       URCU_TLS(logger_thread_name) = name;
+
+       if (set_pthread_name) {
+               ret = lttng_thread_setname(name);
+               if (ret && ret != -ENOSYS) {
+                       /* Don't fail as this is not essential. */
+                       DBG("Failed to set pthread name attribute");
+               }
+       }
+}
+
+/*
+ * Human readable error message.
+ */
+static
+const char *lttng_error_code_str(lttng_error_code code)
+{
+       switch (code) {
+       case LTTNG_OK:
+               return "Success";
+       case LTTNG_ERR_UNK:
+               return "Unknown error";
+       case LTTNG_ERR_UND:
+               return "Undefined command";
+       case LTTNG_ERR_UNKNOWN_DOMAIN:
+               return "Unknown tracing domain";
+       case LTTNG_ERR_NO_SESSION:
+               return "No session found";
+       case LTTNG_ERR_CREATE_DIR_FAIL:
+               return "Create directory failed";
+       case LTTNG_ERR_SESSION_FAIL:
+               return "Create session failed";
+       case LTTNG_ERR_SESS_NOT_FOUND:
+               return "Session name not found";
+       case LTTNG_ERR_FATAL:
+               return "Fatal error of the session daemon";
+       case LTTNG_ERR_SELECT_SESS:
+               return "A session MUST be selected";
+       case LTTNG_ERR_EXIST_SESS:
+               return "Session name already exists";
+       case LTTNG_ERR_NO_EVENT:
+               return "Event not found";
+       case LTTNG_ERR_CONNECT_FAIL:
+               return "Unable to connect to Unix socket";
+       case LTTNG_ERR_EPERM:
+               return "Permission denied";
+       case LTTNG_ERR_KERN_NA:
+               return "Kernel tracer not available";
+       case LTTNG_ERR_KERN_VERSION:
+               return "Kernel tracer version is not compatible";
+       case LTTNG_ERR_KERN_EVENT_EXIST:
+               return "Kernel event already exists";
+       case LTTNG_ERR_KERN_SESS_FAIL:
+               return "Kernel create session failed";
+       case LTTNG_ERR_KERN_CHAN_EXIST:
+               return "Kernel channel already exists";
+       case LTTNG_ERR_KERN_CHAN_FAIL:
+               return "Kernel create channel failed";
+       case LTTNG_ERR_KERN_CHAN_NOT_FOUND:
+               return "Kernel channel not found";
+       case LTTNG_ERR_KERN_CHAN_DISABLE_FAIL:
+               return "Disable kernel channel failed";
+       case LTTNG_ERR_KERN_CHAN_ENABLE_FAIL:
+               return "Enable kernel channel failed";
+       case LTTNG_ERR_KERN_CONTEXT_FAIL:
+               return "Add kernel context failed";
+       case LTTNG_ERR_KERN_ENABLE_FAIL:
+               return "Enable kernel event failed";
+       case LTTNG_ERR_KERN_DISABLE_FAIL:
+               return "Disable kernel event failed";
+       case LTTNG_ERR_KERN_META_FAIL:
+               return "Opening metadata failed";
+       case LTTNG_ERR_KERN_START_FAIL:
+               return "Starting kernel trace failed";
+       case LTTNG_ERR_KERN_STOP_FAIL:
+               return "Stopping kernel trace failed";
+       case LTTNG_ERR_KERN_CONSUMER_FAIL:
+               return "Kernel consumer start failed";
+       case LTTNG_ERR_KERN_STREAM_FAIL:
+               return "Kernel create stream failed";
+       case LTTNG_ERR_KERN_LIST_FAIL:
+               return "Listing kernel events failed";
+       case LTTNG_ERR_UST_CALIBRATE_FAIL:
+               return "UST calibration failed";
+       case LTTNG_ERR_UST_SESS_FAIL:
+               return "UST create session failed";
+       case LTTNG_ERR_UST_CHAN_FAIL:
+               return "UST create channel failed";
+       case LTTNG_ERR_UST_CHAN_EXIST:
+               return "UST channel already exist";
+       case LTTNG_ERR_UST_CHAN_NOT_FOUND:
+               return "UST channel not found";
+       case LTTNG_ERR_UST_CHAN_DISABLE_FAIL:
+               return "Disable UST channel failed";
+       case LTTNG_ERR_UST_CHAN_ENABLE_FAIL:
+               return "Enable UST channel failed";
+       case LTTNG_ERR_UST_ENABLE_FAIL:
+               return "Enable UST event failed";
+       case LTTNG_ERR_UST_DISABLE_FAIL:
+               return "Disable UST event failed";
+       case LTTNG_ERR_UST_META_FAIL:
+               return "Opening metadata failed";
+       case LTTNG_ERR_UST_START_FAIL:
+               return "Starting UST trace failed";
+       case LTTNG_ERR_UST_STOP_FAIL:
+               return "Stopping UST trace failed";
+       case LTTNG_ERR_UST_CONSUMER64_FAIL:
+               return "64-bit UST consumer start failed";
+       case LTTNG_ERR_UST_CONSUMER32_FAIL:
+               return "32-bit UST consumer start failed";
+       case LTTNG_ERR_UST_STREAM_FAIL:
+               return "UST create stream failed";
+       case LTTNG_ERR_UST_LIST_FAIL:
+               return "Listing UST events failed";
+       case LTTNG_ERR_UST_EVENT_EXIST:
+               return "UST event already exist";
+       case LTTNG_ERR_UST_EVENT_NOT_FOUND:
+               return "UST event not found";
+       case LTTNG_ERR_UST_CONTEXT_EXIST:
+               return "UST context already exist";
+       case LTTNG_ERR_UST_CONTEXT_INVAL:
+               return "UST invalid context";
+       case LTTNG_ERR_NEED_ROOT_SESSIOND:
+               return "Tracing the kernel requires a root lttng-sessiond daemon, as well as \"tracing\" group membership or root user ID for the lttng client";
+       case LTTNG_ERR_NO_UST:
+               return "LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled";
+       case LTTNG_ERR_TRACE_ALREADY_STARTED:
+               return "Tracing has already been started once";
+       case LTTNG_ERR_TRACE_ALREADY_STOPPED:
+               return "Tracing has already been stopped";
+       case LTTNG_ERR_KERN_EVENT_ENOSYS:
+               return "Kernel event type not supported";
+       case LTTNG_ERR_NEED_CHANNEL_NAME:
+               return "Non-default channel exists within session: channel name needs to be specified with '-c name'";
+       case LTTNG_ERR_INVALID:
+               return "Invalid parameter";
+       case LTTNG_ERR_NO_USTCONSUMERD:
+               return "No UST consumer detected";
+       case LTTNG_ERR_NO_KERNCONSUMERD:
+               return "No kernel consumer detected";
+       case LTTNG_ERR_EVENT_EXIST_LOGLEVEL:
+               return "Event already enabled with different loglevel";
+       case LTTNG_ERR_URL_DATA_MISS:
+               return "Missing data path URL";
+       case LTTNG_ERR_URL_CTRL_MISS:
+               return "Missing control path URL";
+       case LTTNG_ERR_ENABLE_CONSUMER_FAIL:
+               return "Enabling consumer failed";
+       case LTTNG_ERR_RELAYD_CONNECT_FAIL:
+               return "Unable to connect to lttng-relayd";
+       case LTTNG_ERR_RELAYD_VERSION_FAIL:
+               return "Relay daemon not compatible";
+       case LTTNG_ERR_FILTER_INVAL:
+               return "Invalid filter bytecode";
+       case LTTNG_ERR_FILTER_NOMEM:
+               return "Not enough memory for filter bytecode";
+       case LTTNG_ERR_FILTER_EXIST:
+               return "Filter already exist";
+       case LTTNG_ERR_NO_CONSUMER:
+               return "Consumer not found for recording session";
+       case LTTNG_ERR_EXCLUSION_INVAL:
+               return "Invalid event exclusion data";
+       case LTTNG_ERR_EXCLUSION_NOMEM:
+               return "Lack of memory while processing event exclusions";
+       case LTTNG_ERR_NO_SESSIOND:
+               return "No session daemon is available";
+       case LTTNG_ERR_SESSION_STARTED:
+               return "Session is running";
+       case LTTNG_ERR_NOT_SUPPORTED:
+               return "Operation not supported";
+       case LTTNG_ERR_UST_EVENT_ENABLED:
+               return "UST event already enabled";
+       case LTTNG_ERR_SET_URL:
+               return "Error setting URL";
+       case LTTNG_ERR_URL_EXIST:
+               return "URL already exists";
+       case LTTNG_ERR_BUFFER_NOT_SUPPORTED:
+               return "Buffer type not supported";
+       case LTTNG_ERR_BUFFER_TYPE_MISMATCH:
+               return "Buffer type mismatch for session";
+       case LTTNG_ERR_NOMEM:
+               return "Not enough memory";
+       case LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST:
+               return "Snapshot output already exists";
+       case LTTNG_ERR_START_SESSION_ONCE:
+               return "Session needs to be started once";
+       case LTTNG_ERR_SNAPSHOT_FAIL:
+               return "Snapshot record failed";
+       case LTTNG_ERR_NO_STREAM:
+               return "Index without stream on relay";
+       case LTTNG_ERR_CHAN_EXIST:
+               return "Channel already exists";
+       case LTTNG_ERR_SNAPSHOT_NODATA:
+               return "No data available in snapshot";
+       case LTTNG_ERR_NO_CHANNEL:
+               return "No channel found in the session";
+       case LTTNG_ERR_SESSION_INVALID_CHAR:
+               return "Invalid character found in session name";
+       case LTTNG_ERR_SAVE_FILE_EXIST:
+               return "Session file already exists";
+       case LTTNG_ERR_SAVE_IO_FAIL:
+               return "IO error while writing session configuration";
+       case LTTNG_ERR_LOAD_INVALID_CONFIG:
+               return "Invalid session configuration";
+       case LTTNG_ERR_LOAD_IO_FAIL:
+               return "IO error while reading a session configuration";
+       case LTTNG_ERR_LOAD_SESSION_NOENT:
+               return "Session file not found";
+       case LTTNG_ERR_MAX_SIZE_INVALID:
+               return "Snapshot max size is invalid";
+       case LTTNG_ERR_MI_OUTPUT_TYPE:
+               return "Invalid MI output format";
+       case LTTNG_ERR_MI_IO_FAIL:
+               return "IO error while writing MI output";
+       case LTTNG_ERR_MI_NOT_IMPLEMENTED:
+               return "Mi feature not implemented";
+       case LTTNG_ERR_INVALID_EVENT_NAME:
+               return "Invalid event name";
+       case LTTNG_ERR_INVALID_CHANNEL_NAME:
+               return "Invalid channel name";
+       case LTTNG_ERR_PROCESS_ATTR_EXISTS:
+               return "Process attribute is already tracked";
+       case LTTNG_ERR_PROCESS_ATTR_MISSING:
+               return "Process attribute was not tracked";
+       case LTTNG_ERR_INVALID_CHANNEL_DOMAIN:
+               return "Invalid channel domain";
+       case LTTNG_ERR_OVERFLOW:
+               return "Overflow occurred";
+       case LTTNG_ERR_SESSION_NOT_STARTED:
+               return "Session not started";
+       case LTTNG_ERR_LIVE_SESSION:
+               return "Live sessions are not supported";
+       case LTTNG_ERR_PER_PID_SESSION:
+               return "Per-PID recording sessions are not supported";
+       case LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE:
+               return "Context unavailable on this kernel";
+       case LTTNG_ERR_REGEN_STATEDUMP_FAIL:
+               return "Failed to regenerate the state dump";
+       case LTTNG_ERR_REGEN_STATEDUMP_NOMEM:
+               return "Failed to regenerate the state dump, not enough memory";
+       case LTTNG_ERR_NOT_SNAPSHOT_SESSION:
+               return "Snapshot command can't be applied to a non-snapshot session";
+       case LTTNG_ERR_INVALID_TRIGGER:
+               return "Invalid trigger";
+       case LTTNG_ERR_TRIGGER_EXISTS:
+               return "Trigger already registered";
+       case LTTNG_ERR_TRIGGER_NOT_FOUND:
+               return "Trigger not found";
+       case LTTNG_ERR_COMMAND_CANCELLED:
+               return "Command cancelled";
+       case LTTNG_ERR_ROTATION_PENDING:
+               return "Rotation already pending for this session";
+       case LTTNG_ERR_ROTATION_NOT_AVAILABLE:
+               return "Rotation feature not available for this session's creation mode";
+       case LTTNG_ERR_ROTATION_SCHEDULE_SET:
+               return "A session rotation schedule of this type is already set on the session";
+       case LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET:
+               return "No session rotation schedule of this type is set on the session";
+       case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP:
+               return "Session was already rotated once since it became inactive";
+       case LTTNG_ERR_ROTATION_WRONG_VERSION:
+               return "Session rotation is not supported by this kernel tracer version";
+       case LTTNG_ERR_NO_SESSION_OUTPUT:
+               return "Session has no output";
+       case LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY:
+               return "Rotation feature not available on the relay";
+       case LTTNG_ERR_AGENT_TRACING_DISABLED:
+               return "Session daemon agent tracing is disabled";
+       case LTTNG_ERR_PROBE_LOCATION_INVAL:
+               return "Invalid userspace probe location";
+       case LTTNG_ERR_ELF_PARSING:
+               return "ELF parsing error";
+       case LTTNG_ERR_SDT_PROBE_SEMAPHORE:
+               return "SDT probe guarded by a semaphore";
+       case LTTNG_ERR_ROTATION_FAIL_CONSUMER:
+               return "Rotation failure on consumer";
+       case LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER:
+               return "Rotation rename failure on consumer";
+       case LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER:
+               return "Rotation pending check (local) failure on consumer";
+       case LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER:
+               return "Rotation pending check (relay) failure on consumer";
+       case LTTNG_ERR_MKDIR_FAIL_CONSUMER:
+               return "Directory creation failure on consumer";
+       case LTTNG_ERR_CHAN_NOT_FOUND:
+               return "Channel not found";
+       case LTTNG_ERR_SNAPSHOT_UNSUPPORTED:
+               return "Session configuration does not allow the use of snapshots";
+       case LTTNG_ERR_SESSION_NOT_EXIST:
+               return "Recording session does not exist";
+       case LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER:
+               return "Trace chunk creation failed on consumer";
+       case LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER:
+               return "Trace chunk close failed on consumer";
+       case LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER:
+               return "Failed to query consumer for trace chunk existence";
+       case LTTNG_ERR_INVALID_PROTOCOL:
+               return "Protocol error occurred";
+       case LTTNG_ERR_FILE_CREATION_ERROR:
+               return "Failed to create file";
+       case LTTNG_ERR_TIMER_STOP_ERROR:
+               return "Failed to stop a timer";
+       case LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL:
+               return "Rotation feature not supported by the kernel tracer";
+       case LTTNG_ERR_CLEAR_RELAY_DISALLOWED:
+               return "Relayd daemon peer does not allow sessions to be cleared";
+       case LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY:
+               return "Clearing a session is not supported by the relay daemon";
+       case LTTNG_ERR_CLEAR_FAIL_CONSUMER:
+               return "Consumer failed to clear the session";
+       case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR:
+               return "Session was already cleared since it became inactive";
+       case LTTNG_ERR_USER_NOT_FOUND:
+               return "User not found";
+       case LTTNG_ERR_GROUP_NOT_FOUND:
+               return "Group not found";
+       case LTTNG_ERR_UNSUPPORTED_DOMAIN:
+               return "Unsupported domain used";
+       case LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY:
+               return "Operation does not apply to the process attribute tracker's tracking policy";
+       case LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD:
+               return "Failed to create an event notifier group notification file descriptor";
+       case LTTNG_ERR_INVALID_CAPTURE_EXPRESSION:
+               return "Invalid capture expression";
+       case LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION:
+               return "Failed to create event notifier";
+       case LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING:
+               return "Failed to initialize event notifier error accounting";
+       case LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL:
+               return "No index available in event notifier error accounting";
+       case LTTNG_ERR_INVALID_ERROR_QUERY_TARGET:
+               return "Invalid error query target.";
+       case LTTNG_ERR_BUFFER_FLUSH_FAILED:
+               return "Failed to flush stream buffer";
+       case LTTNG_ERR_NR:
+               abort();
+       }
+
+       abort();
+};
+
+/*
+ * Return ptr to string representing a human readable error code from the
+ * lttng_error_code enum.
+ *
+ * These code MUST be negative in other to treat that as an error value.
+ */
+const char *error_get_str(int32_t code)
+{
+       code = -code;
+
+       if (code < LTTNG_OK || code >= LTTNG_ERR_NR) {
+               code = LTTNG_ERR_UNK;
+       }
+
+       return lttng_error_code_str((lttng_error_code) code);
+}
+
+void lttng_abort_on_error(void)
+{
+       if (lttng_opt_abort_on_error < 0) {
+               /* Use lttng_secure_getenv() to query its state. */
+               const char *value;
+
+               value = lttng_secure_getenv("LTTNG_ABORT_ON_ERROR");
+               if (value && !strcmp(value, "1")) {
+                       lttng_opt_abort_on_error = 1;
+               } else {
+                       lttng_opt_abort_on_error = 0;
+               }
+       }
+       if (lttng_opt_abort_on_error > 0) {
+               abort();
+       }
+}
diff --git a/src/common/evaluation.c b/src/common/evaluation.c
deleted file mode 100644 (file)
index 6b3e634..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/evaluation-internal.h>
-#include <lttng/condition/buffer-usage-internal.h>
-#include <lttng/condition/session-consumed-size-internal.h>
-#include <lttng/condition/session-rotation-internal.h>
-#include <lttng/condition/event-rule-matches-internal.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <stdbool.h>
-
-void lttng_evaluation_init(struct lttng_evaluation *evaluation,
-               enum lttng_condition_type type)
-{
-       evaluation->type = type;
-}
-
-int lttng_evaluation_serialize(const struct lttng_evaluation *evaluation,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_evaluation_comm evaluation_comm = {
-               .type = (int8_t) evaluation->type
-       };
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &evaluation_comm,
-                       sizeof(evaluation_comm));
-       if (ret) {
-               goto end;
-       }
-
-       if (evaluation->serialize) {
-               ret = evaluation->serialize(evaluation, payload);
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-ssize_t lttng_evaluation_create_from_payload(
-               const struct lttng_condition *condition,
-               struct lttng_payload_view *src_view,
-               struct lttng_evaluation **evaluation)
-{
-       ssize_t ret, evaluation_size = 0;
-       const struct lttng_evaluation_comm *evaluation_comm;
-       struct lttng_payload_view evaluation_comm_view =
-                       lttng_payload_view_from_view(
-                                       src_view, 0, sizeof(*evaluation_comm));
-       struct lttng_payload_view evaluation_view =
-                       lttng_payload_view_from_view(src_view,
-                                       sizeof(*evaluation_comm), -1);
-
-       if (!src_view || !evaluation) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&evaluation_comm_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       evaluation_comm = (typeof(evaluation_comm)) evaluation_comm_view.buffer.data;
-       evaluation_size += sizeof(*evaluation_comm);
-
-       switch ((enum lttng_condition_type) evaluation_comm->type) {
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               ret = lttng_evaluation_buffer_usage_low_create_from_payload(
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-               ret = lttng_evaluation_buffer_usage_high_create_from_payload(
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-               ret = lttng_evaluation_session_consumed_size_create_from_payload(
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-               ret = lttng_evaluation_session_rotation_ongoing_create_from_payload(
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               ret = lttng_evaluation_session_rotation_completed_create_from_payload(
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-               LTTNG_ASSERT(condition);
-               LTTNG_ASSERT(condition->type ==
-                               LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
-               ret = lttng_evaluation_event_rule_matches_create_from_payload(
-                               container_of(condition,
-                                               const struct lttng_condition_event_rule_matches,
-                                               parent),
-                               &evaluation_view, evaluation);
-               if (ret < 0) {
-                       goto end;
-               }
-               evaluation_size += ret;
-               break;
-       default:
-               ERR("Attempted to create evaluation of unknown type (%i)",
-                               (int) evaluation_comm->type);
-               ret = -1;
-               goto end;
-       }
-
-       ret = evaluation_size;
-end:
-       return ret;
-}
-
-enum lttng_condition_type lttng_evaluation_get_type(
-               const struct lttng_evaluation *evaluation)
-{
-       return evaluation ? evaluation->type : LTTNG_CONDITION_TYPE_UNKNOWN;
-}
-
-void lttng_evaluation_destroy(struct lttng_evaluation *evaluation)
-{
-       if (!evaluation) {
-               return;
-       }
-
-       LTTNG_ASSERT(evaluation->destroy);
-       evaluation->destroy(evaluation);
-}
diff --git a/src/common/evaluation.cpp b/src/common/evaluation.cpp
new file mode 100644 (file)
index 0000000..6b3e634
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/evaluation-internal.h>
+#include <lttng/condition/buffer-usage-internal.h>
+#include <lttng/condition/session-consumed-size-internal.h>
+#include <lttng/condition/session-rotation-internal.h>
+#include <lttng/condition/event-rule-matches-internal.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <stdbool.h>
+
+void lttng_evaluation_init(struct lttng_evaluation *evaluation,
+               enum lttng_condition_type type)
+{
+       evaluation->type = type;
+}
+
+int lttng_evaluation_serialize(const struct lttng_evaluation *evaluation,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_evaluation_comm evaluation_comm = {
+               .type = (int8_t) evaluation->type
+       };
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &evaluation_comm,
+                       sizeof(evaluation_comm));
+       if (ret) {
+               goto end;
+       }
+
+       if (evaluation->serialize) {
+               ret = evaluation->serialize(evaluation, payload);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+ssize_t lttng_evaluation_create_from_payload(
+               const struct lttng_condition *condition,
+               struct lttng_payload_view *src_view,
+               struct lttng_evaluation **evaluation)
+{
+       ssize_t ret, evaluation_size = 0;
+       const struct lttng_evaluation_comm *evaluation_comm;
+       struct lttng_payload_view evaluation_comm_view =
+                       lttng_payload_view_from_view(
+                                       src_view, 0, sizeof(*evaluation_comm));
+       struct lttng_payload_view evaluation_view =
+                       lttng_payload_view_from_view(src_view,
+                                       sizeof(*evaluation_comm), -1);
+
+       if (!src_view || !evaluation) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&evaluation_comm_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       evaluation_comm = (typeof(evaluation_comm)) evaluation_comm_view.buffer.data;
+       evaluation_size += sizeof(*evaluation_comm);
+
+       switch ((enum lttng_condition_type) evaluation_comm->type) {
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               ret = lttng_evaluation_buffer_usage_low_create_from_payload(
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+               ret = lttng_evaluation_buffer_usage_high_create_from_payload(
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+               ret = lttng_evaluation_session_consumed_size_create_from_payload(
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+               ret = lttng_evaluation_session_rotation_ongoing_create_from_payload(
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               ret = lttng_evaluation_session_rotation_completed_create_from_payload(
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+               LTTNG_ASSERT(condition);
+               LTTNG_ASSERT(condition->type ==
+                               LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
+               ret = lttng_evaluation_event_rule_matches_create_from_payload(
+                               container_of(condition,
+                                               const struct lttng_condition_event_rule_matches,
+                                               parent),
+                               &evaluation_view, evaluation);
+               if (ret < 0) {
+                       goto end;
+               }
+               evaluation_size += ret;
+               break;
+       default:
+               ERR("Attempted to create evaluation of unknown type (%i)",
+                               (int) evaluation_comm->type);
+               ret = -1;
+               goto end;
+       }
+
+       ret = evaluation_size;
+end:
+       return ret;
+}
+
+enum lttng_condition_type lttng_evaluation_get_type(
+               const struct lttng_evaluation *evaluation)
+{
+       return evaluation ? evaluation->type : LTTNG_CONDITION_TYPE_UNKNOWN;
+}
+
+void lttng_evaluation_destroy(struct lttng_evaluation *evaluation)
+{
+       if (!evaluation) {
+               return;
+       }
+
+       LTTNG_ASSERT(evaluation->destroy);
+       evaluation->destroy(evaluation);
+}
diff --git a/src/common/event-expr/event-expr.c b/src/common/event-expr/event-expr.c
deleted file mode 100644 (file)
index 7b07312..0000000
+++ /dev/null
@@ -1,918 +0,0 @@
-/*
- * event-expr.c
- *
- * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <stddef.h>
-
-#include <common/bytecode/bytecode.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/event-expr-internal.h>
-#include <lttng/event-expr.h>
-#include <stdio.h>
-
-enum lttng_event_expr_type lttng_event_expr_get_type(
-               const struct lttng_event_expr *expr)
-{
-       enum lttng_event_expr_type type;
-
-       if (!expr) {
-               type = LTTNG_EVENT_EXPR_TYPE_INVALID;
-               goto end;
-       }
-
-       type = expr->type;
-
-end:
-       return type;
-}
-
-static
-struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type,
-               size_t size)
-{
-       struct lttng_event_expr *expr;
-
-       expr = zmalloc(size);
-       if (!expr) {
-               goto end;
-       }
-
-       expr->type = type;
-
-end:
-       return expr;
-}
-
-static
-struct lttng_event_expr_field *create_field_event_expr(
-               enum lttng_event_expr_type type,
-               const char *name)
-{
-       struct lttng_event_expr_field *expr =
-                       container_of(
-                               create_empty_expr(type, sizeof(*expr)),
-                               struct lttng_event_expr_field, parent);
-
-       if (!expr) {
-               goto error;
-       }
-
-       LTTNG_ASSERT(name);
-       expr->name = strdup(name);
-       if (!expr->name) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       if (expr) {
-               lttng_event_expr_destroy(&expr->parent);
-       }
-       expr = NULL;
-
-end:
-       return expr;
-}
-
-struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
-               const char *field_name)
-{
-       struct lttng_event_expr *expr = NULL;
-
-       if (!field_name) {
-               goto end;
-       }
-
-       expr = &create_field_event_expr(
-                       LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD,
-                       field_name)->parent;
-
-end:
-       return expr;
-}
-
-struct lttng_event_expr *lttng_event_expr_channel_context_field_create(
-               const char *field_name)
-{
-       struct lttng_event_expr *expr = NULL;
-
-       if (!field_name) {
-               goto end;
-       }
-
-       expr = &create_field_event_expr(
-                       LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD,
-                       field_name)->parent;
-
-end:
-       return expr;
-}
-
-struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create(
-               const char *provider_name, const char *type_name)
-{
-       struct lttng_event_expr_app_specific_context_field *expr = NULL;
-       struct lttng_event_expr *ret_parent_expr;
-
-       if (!type_name || !provider_name) {
-               goto error;
-       }
-
-       expr = container_of(create_empty_expr(
-                       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD,
-                       sizeof(*expr)),
-                       struct lttng_event_expr_app_specific_context_field,
-                       parent);
-       if (!expr) {
-               goto error;
-       }
-
-       expr->provider_name = strdup(provider_name);
-       if (!expr->provider_name) {
-               goto error;
-       }
-
-       expr->type_name = strdup(type_name);
-       if (!expr->type_name) {
-               goto error;
-       }
-
-       ret_parent_expr = &expr->parent;
-       goto end;
-
-error:
-       if (expr) {
-               lttng_event_expr_destroy(&expr->parent);
-       }
-       ret_parent_expr = NULL;
-
-end:
-       return ret_parent_expr;
-}
-
-struct lttng_event_expr *lttng_event_expr_array_field_element_create(
-               struct lttng_event_expr *array_field_expr,
-               unsigned int index)
-{
-       struct lttng_event_expr_array_field_element *expr = NULL;
-       struct lttng_event_expr *ret_parent_expr;
-
-       /* The parent array field expression must be an l-value */
-       if (!array_field_expr ||
-                       !lttng_event_expr_is_lvalue(array_field_expr)) {
-               goto error;
-       }
-
-       expr = container_of(create_empty_expr(
-                       LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT,
-                       sizeof(*expr)),
-                       struct lttng_event_expr_array_field_element,
-                       parent);
-       if (!expr) {
-               goto error;
-       }
-
-       expr->array_field_expr = array_field_expr;
-       expr->index = index;
-       ret_parent_expr = &expr->parent;
-       goto end;
-
-error:
-       ret_parent_expr = NULL;
-
-end:
-       return ret_parent_expr;
-}
-
-const char *lttng_event_expr_event_payload_field_get_name(
-               const struct lttng_event_expr *expr)
-{
-       const char *ret = NULL;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) {
-               goto end;
-       }
-
-       ret = container_of(expr,
-                       const struct lttng_event_expr_field, parent)->name;
-
-end:
-       return ret;
-}
-
-const char *lttng_event_expr_channel_context_field_get_name(
-               const struct lttng_event_expr *expr)
-{
-       const char *ret = NULL;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) {
-               goto end;
-       }
-
-       ret = container_of(expr,
-                       const struct lttng_event_expr_field, parent)->name;
-
-end:
-       return ret;
-}
-
-const char *lttng_event_expr_app_specific_context_field_get_provider_name(
-               const struct lttng_event_expr *expr)
-{
-       const char *ret = NULL;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
-               goto end;
-       }
-
-       ret = container_of(expr,
-                       const struct lttng_event_expr_app_specific_context_field,
-                       parent)->provider_name;
-
-end:
-       return ret;
-}
-
-const char *lttng_event_expr_app_specific_context_field_get_type_name(
-               const struct lttng_event_expr *expr)
-{
-       const char *ret = NULL;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
-               goto end;
-       }
-
-       ret = container_of(expr,
-                       const struct lttng_event_expr_app_specific_context_field,
-                       parent)->type_name;
-
-end:
-       return ret;
-}
-
-const struct lttng_event_expr *
-lttng_event_expr_array_field_element_get_parent_expr(
-               const struct lttng_event_expr *expr)
-{
-       const struct lttng_event_expr *ret = NULL;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) {
-               goto end;
-       }
-
-       ret = container_of(expr,
-                       const struct lttng_event_expr_array_field_element,
-                       parent)->array_field_expr;
-
-end:
-       return ret;
-}
-
-enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index(
-               const struct lttng_event_expr *expr, unsigned int *index)
-{
-       enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK;
-
-       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT ||
-                       !index) {
-               ret = LTTNG_EVENT_EXPR_STATUS_INVALID;
-               goto end;
-       }
-
-       *index = container_of(expr,
-                       const struct lttng_event_expr_array_field_element,
-                       parent)->index;
-
-end:
-       return ret;
-}
-
-bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
-               const struct lttng_event_expr *expr_b)
-{
-       bool is_equal = true;
-
-       if (!expr_a && !expr_b) {
-               /* Both `NULL`: equal */
-               goto end;
-       }
-
-       if (!expr_a || !expr_b) {
-               /* Only one `NULL`: not equal */
-               goto not_equal;
-       }
-
-       if (expr_a->type != expr_b->type) {
-               /* Different types: not equal */
-               goto not_equal;
-       }
-
-       switch (expr_a->type) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-       {
-               const struct lttng_event_expr_field *field_expr_a =
-                               container_of(expr_a,
-                                       const struct lttng_event_expr_field,
-                                       parent);
-               const struct lttng_event_expr_field *field_expr_b =
-                               container_of(expr_b,
-                                       const struct lttng_event_expr_field,
-                                       parent);
-
-               if (strcmp(field_expr_a->name, field_expr_b->name) != 0) {
-                       goto not_equal;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-       {
-               const struct lttng_event_expr_app_specific_context_field *field_expr_a =
-                               container_of(expr_a,
-                                       const struct lttng_event_expr_app_specific_context_field,
-                                       parent);
-               const struct lttng_event_expr_app_specific_context_field *field_expr_b =
-                               container_of(expr_b,
-                                       const struct lttng_event_expr_app_specific_context_field,
-                                       parent);
-
-               if (strcmp(field_expr_a->provider_name,
-                               field_expr_b->provider_name) != 0) {
-                       goto not_equal;
-               }
-
-               if (strcmp(field_expr_a->type_name,
-                               field_expr_b->type_name) != 0) {
-                       goto not_equal;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-       {
-               const struct lttng_event_expr_array_field_element *elem_expr_a =
-                               container_of(expr_a,
-                                       const struct lttng_event_expr_array_field_element,
-                                       parent);
-               const struct lttng_event_expr_array_field_element *elem_expr_b =
-                               container_of(expr_b,
-                                       const struct lttng_event_expr_array_field_element,
-                                       parent);
-
-               if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr,
-                               elem_expr_b->array_field_expr)) {
-                       goto not_equal;
-               }
-
-               if (elem_expr_a->index != elem_expr_b->index) {
-                       goto not_equal;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-       goto end;
-
-not_equal:
-       is_equal = false;
-
-end:
-       return is_equal;
-}
-
-void lttng_event_expr_destroy(struct lttng_event_expr *expr)
-{
-       if (!expr) {
-               goto end;
-       }
-
-       switch (expr->type) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-       {
-               struct lttng_event_expr_field *field_expr =
-                               container_of(expr,
-                                       struct lttng_event_expr_field, parent);
-
-               free(field_expr->name);
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-       {
-               struct lttng_event_expr_app_specific_context_field *field_expr =
-                               container_of(expr,
-                                       struct lttng_event_expr_app_specific_context_field,
-                                       parent);
-
-               free(field_expr->provider_name);
-               free(field_expr->type_name);
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-       {
-               struct lttng_event_expr_array_field_element *elem_expr =
-                               container_of(expr,
-                                       struct lttng_event_expr_array_field_element,
-                                       parent);
-
-               lttng_event_expr_destroy(elem_expr->array_field_expr);
-               break;
-       }
-       default:
-               break;
-       }
-
-       free(expr);
-
-end:
-       return;
-}
-
-static int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr,
-               struct lttng_bytecode_alloc **bytecode,
-               struct lttng_bytecode_alloc **bytecode_reloc)
-{
-       int status;
-       enum lttng_event_expr_status event_expr_status;
-
-       switch (lttng_event_expr_get_type(expr)) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-       {
-               const char *name;
-
-               status = bytecode_push_get_payload_root(bytecode);
-               if (status) {
-                       ERR("Failed to get payload root from bytecode");
-                       goto end;
-               }
-
-               name = lttng_event_expr_event_payload_field_get_name(expr);
-               if (!name) {
-                       ERR("Failed to get payload field name from event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               status = bytecode_push_get_symbol(
-                               bytecode, bytecode_reloc, name);
-               if (status) {
-                       ERR("Failed to push 'get symbol %s' in bytecode", name);
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-       {
-               const char *name;
-
-               status = bytecode_push_get_context_root(bytecode);
-               if (status) {
-                       ERR("Failed to get context root from bytecode");
-                       goto end;
-               }
-
-               name = lttng_event_expr_channel_context_field_get_name(expr);
-               if (!name) {
-                       ERR("Failed to get channel context field name from event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               status = bytecode_push_get_symbol(
-                               bytecode, bytecode_reloc, name);
-               if (status) {
-                       ERR("Failed to push 'get symbol %s' in bytecode", name);
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-       {
-               int ret;
-               char *name = NULL;
-               const char *provider_name, *type_name;
-
-               status = bytecode_push_get_app_context_root(bytecode);
-               if (status) {
-                       ERR("Failed to get application context root from bytecode");
-                       goto end;
-               }
-
-               provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(
-                               expr);
-               if (!provider_name) {
-                       ERR("Failed to get application context provider name from event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               type_name = lttng_event_expr_app_specific_context_field_get_type_name(
-                               expr);
-               if (!type_name) {
-                       ERR("Failed to get application context type name from event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               /*
-                * Reconstitute the app context field name from its two parts.
-                */
-               ret = asprintf(&name, "%s:%s", provider_name, type_name);
-               if (ret < 0) {
-                       PERROR("Failed to format application specific context: provider_name = '%s', type_name = '%s'",
-                                       provider_name, type_name);
-                       status = -1;
-                       goto end;
-               }
-
-               status = bytecode_push_get_symbol(
-                               bytecode, bytecode_reloc, name);
-               free(name);
-               if (status) {
-                       ERR("Failed to push 'get symbol %s:%s' in bytecode",
-                                       provider_name, type_name);
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-       {
-               unsigned int index;
-               const struct lttng_event_expr *parent;
-
-               parent = lttng_event_expr_array_field_element_get_parent_expr(
-                               expr);
-               if (!parent) {
-                       ERR("Failed to get parent expression from array event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               status = event_expr_to_bytecode_recursive(
-                               parent, bytecode, bytecode_reloc);
-               if (status) {
-                       goto end;
-               }
-
-               event_expr_status =
-                               lttng_event_expr_array_field_element_get_index(
-                                               expr, &index);
-               if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) {
-                       ERR("Failed to get array field element index from event expression");
-                       status = -1;
-                       goto end;
-               }
-
-               status = bytecode_push_get_index_u64(bytecode, index);
-               if (status) {
-                       ERR("Failed to push 'get index %u' in bytecode", index);
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               abort();
-       }
-
-       status = 0;
-end:
-       return status;
-}
-
-int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr,
-               struct lttng_bytecode **bytecode_out)
-{
-       int status;
-       struct return_op ret_insn;
-       struct lttng_bytecode_alloc *bytecode = NULL;
-       struct lttng_bytecode_alloc *bytecode_reloc = NULL;
-
-       status = bytecode_init(&bytecode);
-       if (status) {
-               ERR("Failed to initialize bytecode");
-               goto end;
-       }
-
-       status = bytecode_init(&bytecode_reloc);
-       if (status) {
-               ERR("Failed to initialize relocation bytecode");
-               goto end;
-       }
-
-       status = event_expr_to_bytecode_recursive(
-                       expr, &bytecode, &bytecode_reloc);
-       if (status) {
-               /* Errors already logged. */
-               goto end;
-       }
-
-       ret_insn.op = BYTECODE_OP_RETURN;
-       bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn));
-
-       /* Append symbol table to bytecode. */
-       bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b);
-       status = bytecode_push(&bytecode, bytecode_reloc->b.data, 1,
-                       bytecode_get_len(&bytecode_reloc->b));
-       if (status) {
-               ERR("Failed to push symbol table to bytecode");
-               goto end;
-       }
-
-       /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`.  */
-       *bytecode_out = lttng_bytecode_copy(&bytecode->b);
-       if (!*bytecode_out) {
-               status = -1;
-               goto end;
-       }
-
-end:
-       if (bytecode) {
-               free(bytecode);
-       }
-
-       if (bytecode_reloc) {
-               free(bytecode_reloc);
-       }
-
-       return status;
-}
-
-static
-enum lttng_error_code lttng_event_expr_event_payload_field_mi_serialize(
-               const struct lttng_event_expr *expression,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *name = NULL;
-
-       LTTNG_ASSERT(expression);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD);
-
-       name = lttng_event_expr_event_payload_field_get_name(expression);
-       LTTNG_ASSERT(name);
-
-       /* Open event expr payload field element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_expr_payload_field);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, config_element_name, name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close event expr payload field element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-enum lttng_error_code lttng_event_expr_channel_context_field_mi_serialize(
-               const struct lttng_event_expr *expression,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *name = NULL;
-
-       LTTNG_ASSERT(expression);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD);
-
-       name = lttng_event_expr_channel_context_field_get_name(expression);
-       LTTNG_ASSERT(name);
-
-       /* Open event expr channel context field element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_event_expr_channel_context_field);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, config_element_name, name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close event expr channel context field element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-enum lttng_error_code lttng_event_expr_app_specific_context_field_mi_serialize(
-               const struct lttng_event_expr *expression,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *provider_name = NULL;
-       const char *type_name = NULL;
-
-       LTTNG_ASSERT(expression);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(expression->type ==
-                       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD);
-
-       provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(
-                       expression);
-       LTTNG_ASSERT(provider_name);
-
-       type_name = lttng_event_expr_app_specific_context_field_get_type_name(
-                       expression);
-       LTTNG_ASSERT(provider_name);
-
-       /* Open event expr app specific context field element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_event_expr_app_specific_context_field);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Provider name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_expr_provider_name,
-                       provider_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Type name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_expr_type_name, type_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close event expr app specific context field element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-enum lttng_error_code lttng_event_expr_array_field_element_mi_serialize(
-               const struct lttng_event_expr *expression,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_expr_status status;
-       const struct lttng_event_expr *parent_expr = NULL;
-       unsigned int index;
-
-       LTTNG_ASSERT(expression);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT);
-
-       status = lttng_event_expr_array_field_element_get_index(
-                       expression, &index);
-       LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK);
-
-       parent_expr = lttng_event_expr_array_field_element_get_parent_expr(
-                       expression);
-       LTTNG_ASSERT(parent_expr != NULL);
-
-       /* Open event expr array field element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_event_expr_array_field_element);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Index. */
-       ret = mi_lttng_writer_write_element_unsigned_int(
-                       writer, mi_lttng_element_event_expr_index, index);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Parent expression. */
-       ret_code = lttng_event_expr_mi_serialize(parent_expr, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close event expr array field element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-enum lttng_error_code lttng_event_expr_mi_serialize(
-               const struct lttng_event_expr *expression,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(expression);
-       LTTNG_ASSERT(writer);
-
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_expr);
-       if (ret) {
-               goto mi_error;
-       }
-
-       switch (expression->type) {
-       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
-               ret_code = lttng_event_expr_event_payload_field_mi_serialize(
-                               expression, writer);
-               break;
-       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
-               ret_code = lttng_event_expr_channel_context_field_mi_serialize(
-                               expression, writer);
-               break;
-       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
-               ret_code = lttng_event_expr_app_specific_context_field_mi_serialize(
-                               expression, writer);
-               break;
-       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
-               ret_code = lttng_event_expr_array_field_element_mi_serialize(
-                               expression, writer);
-               break;
-       default:
-               abort();
-       }
-
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-
-end:
-       return ret_code;
-}
diff --git a/src/common/event-expr/event-expr.cpp b/src/common/event-expr/event-expr.cpp
new file mode 100644 (file)
index 0000000..e80fb1d
--- /dev/null
@@ -0,0 +1,918 @@
+/*
+ * event-expr.c
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <stddef.h>
+
+#include <common/bytecode/bytecode.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-expr.h>
+#include <stdio.h>
+
+enum lttng_event_expr_type lttng_event_expr_get_type(
+               const struct lttng_event_expr *expr)
+{
+       enum lttng_event_expr_type type;
+
+       if (!expr) {
+               type = LTTNG_EVENT_EXPR_TYPE_INVALID;
+               goto end;
+       }
+
+       type = expr->type;
+
+end:
+       return type;
+}
+
+static
+struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type,
+               size_t size)
+{
+       struct lttng_event_expr *expr;
+
+       expr = (lttng_event_expr *) zmalloc(size);
+       if (!expr) {
+               goto end;
+       }
+
+       expr->type = type;
+
+end:
+       return expr;
+}
+
+static
+struct lttng_event_expr_field *create_field_event_expr(
+               enum lttng_event_expr_type type,
+               const char *name)
+{
+       struct lttng_event_expr_field *expr =
+                       container_of(
+                               create_empty_expr(type, sizeof(*expr)),
+                               struct lttng_event_expr_field, parent);
+
+       if (!expr) {
+               goto error;
+       }
+
+       LTTNG_ASSERT(name);
+       expr->name = strdup(name);
+       if (!expr->name) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (expr) {
+               lttng_event_expr_destroy(&expr->parent);
+       }
+       expr = NULL;
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_event_payload_field_create(
+               const char *field_name)
+{
+       struct lttng_event_expr *expr = NULL;
+
+       if (!field_name) {
+               goto end;
+       }
+
+       expr = &create_field_event_expr(
+                       LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD,
+                       field_name)->parent;
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_channel_context_field_create(
+               const char *field_name)
+{
+       struct lttng_event_expr *expr = NULL;
+
+       if (!field_name) {
+               goto end;
+       }
+
+       expr = &create_field_event_expr(
+                       LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD,
+                       field_name)->parent;
+
+end:
+       return expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create(
+               const char *provider_name, const char *type_name)
+{
+       struct lttng_event_expr_app_specific_context_field *expr = NULL;
+       struct lttng_event_expr *ret_parent_expr;
+
+       if (!type_name || !provider_name) {
+               goto error;
+       }
+
+       expr = container_of(create_empty_expr(
+                       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD,
+                       sizeof(*expr)),
+                       struct lttng_event_expr_app_specific_context_field,
+                       parent);
+       if (!expr) {
+               goto error;
+       }
+
+       expr->provider_name = strdup(provider_name);
+       if (!expr->provider_name) {
+               goto error;
+       }
+
+       expr->type_name = strdup(type_name);
+       if (!expr->type_name) {
+               goto error;
+       }
+
+       ret_parent_expr = &expr->parent;
+       goto end;
+
+error:
+       if (expr) {
+               lttng_event_expr_destroy(&expr->parent);
+       }
+       ret_parent_expr = NULL;
+
+end:
+       return ret_parent_expr;
+}
+
+struct lttng_event_expr *lttng_event_expr_array_field_element_create(
+               struct lttng_event_expr *array_field_expr,
+               unsigned int index)
+{
+       struct lttng_event_expr_array_field_element *expr = NULL;
+       struct lttng_event_expr *ret_parent_expr;
+
+       /* The parent array field expression must be an l-value */
+       if (!array_field_expr ||
+                       !lttng_event_expr_is_lvalue(array_field_expr)) {
+               goto error;
+       }
+
+       expr = container_of(create_empty_expr(
+                       LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT,
+                       sizeof(*expr)),
+                       struct lttng_event_expr_array_field_element,
+                       parent);
+       if (!expr) {
+               goto error;
+       }
+
+       expr->array_field_expr = array_field_expr;
+       expr->index = index;
+       ret_parent_expr = &expr->parent;
+       goto end;
+
+error:
+       ret_parent_expr = NULL;
+
+end:
+       return ret_parent_expr;
+}
+
+const char *lttng_event_expr_event_payload_field_get_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_field, parent)->name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_channel_context_field_get_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_field, parent)->name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_provider_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_app_specific_context_field,
+                       parent)->provider_name;
+
+end:
+       return ret;
+}
+
+const char *lttng_event_expr_app_specific_context_field_get_type_name(
+               const struct lttng_event_expr *expr)
+{
+       const char *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_app_specific_context_field,
+                       parent)->type_name;
+
+end:
+       return ret;
+}
+
+const struct lttng_event_expr *
+lttng_event_expr_array_field_element_get_parent_expr(
+               const struct lttng_event_expr *expr)
+{
+       const struct lttng_event_expr *ret = NULL;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) {
+               goto end;
+       }
+
+       ret = container_of(expr,
+                       const struct lttng_event_expr_array_field_element,
+                       parent)->array_field_expr;
+
+end:
+       return ret;
+}
+
+enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index(
+               const struct lttng_event_expr *expr, unsigned int *index)
+{
+       enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK;
+
+       if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT ||
+                       !index) {
+               ret = LTTNG_EVENT_EXPR_STATUS_INVALID;
+               goto end;
+       }
+
+       *index = container_of(expr,
+                       const struct lttng_event_expr_array_field_element,
+                       parent)->index;
+
+end:
+       return ret;
+}
+
+bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a,
+               const struct lttng_event_expr *expr_b)
+{
+       bool is_equal = true;
+
+       if (!expr_a && !expr_b) {
+               /* Both `NULL`: equal */
+               goto end;
+       }
+
+       if (!expr_a || !expr_b) {
+               /* Only one `NULL`: not equal */
+               goto not_equal;
+       }
+
+       if (expr_a->type != expr_b->type) {
+               /* Different types: not equal */
+               goto not_equal;
+       }
+
+       switch (expr_a->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_field *field_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+               const struct lttng_event_expr_field *field_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_field,
+                                       parent);
+
+               if (strcmp(field_expr_a->name, field_expr_b->name) != 0) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               const struct lttng_event_expr_app_specific_context_field *field_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+               const struct lttng_event_expr_app_specific_context_field *field_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               if (strcmp(field_expr_a->provider_name,
+                               field_expr_b->provider_name) != 0) {
+                       goto not_equal;
+               }
+
+               if (strcmp(field_expr_a->type_name,
+                               field_expr_b->type_name) != 0) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               const struct lttng_event_expr_array_field_element *elem_expr_a =
+                               container_of(expr_a,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+               const struct lttng_event_expr_array_field_element *elem_expr_b =
+                               container_of(expr_b,
+                                       const struct lttng_event_expr_array_field_element,
+                                       parent);
+
+               if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr,
+                               elem_expr_b->array_field_expr)) {
+                       goto not_equal;
+               }
+
+               if (elem_expr_a->index != elem_expr_b->index) {
+                       goto not_equal;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+       goto end;
+
+not_equal:
+       is_equal = false;
+
+end:
+       return is_equal;
+}
+
+void lttng_event_expr_destroy(struct lttng_event_expr *expr)
+{
+       if (!expr) {
+               goto end;
+       }
+
+       switch (expr->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               struct lttng_event_expr_field *field_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_field, parent);
+
+               free(field_expr->name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               struct lttng_event_expr_app_specific_context_field *field_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_app_specific_context_field,
+                                       parent);
+
+               free(field_expr->provider_name);
+               free(field_expr->type_name);
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               struct lttng_event_expr_array_field_element *elem_expr =
+                               container_of(expr,
+                                       struct lttng_event_expr_array_field_element,
+                                       parent);
+
+               lttng_event_expr_destroy(elem_expr->array_field_expr);
+               break;
+       }
+       default:
+               break;
+       }
+
+       free(expr);
+
+end:
+       return;
+}
+
+static int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr,
+               struct lttng_bytecode_alloc **bytecode,
+               struct lttng_bytecode_alloc **bytecode_reloc)
+{
+       int status;
+       enum lttng_event_expr_status event_expr_status;
+
+       switch (lttng_event_expr_get_type(expr)) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+       {
+               const char *name;
+
+               status = bytecode_push_get_payload_root(bytecode);
+               if (status) {
+                       ERR("Failed to get payload root from bytecode");
+                       goto end;
+               }
+
+               name = lttng_event_expr_event_payload_field_get_name(expr);
+               if (!name) {
+                       ERR("Failed to get payload field name from event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(
+                               bytecode, bytecode_reloc, name);
+               if (status) {
+                       ERR("Failed to push 'get symbol %s' in bytecode", name);
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+       {
+               const char *name;
+
+               status = bytecode_push_get_context_root(bytecode);
+               if (status) {
+                       ERR("Failed to get context root from bytecode");
+                       goto end;
+               }
+
+               name = lttng_event_expr_channel_context_field_get_name(expr);
+               if (!name) {
+                       ERR("Failed to get channel context field name from event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(
+                               bytecode, bytecode_reloc, name);
+               if (status) {
+                       ERR("Failed to push 'get symbol %s' in bytecode", name);
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+       {
+               int ret;
+               char *name = NULL;
+               const char *provider_name, *type_name;
+
+               status = bytecode_push_get_app_context_root(bytecode);
+               if (status) {
+                       ERR("Failed to get application context root from bytecode");
+                       goto end;
+               }
+
+               provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(
+                               expr);
+               if (!provider_name) {
+                       ERR("Failed to get application context provider name from event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               type_name = lttng_event_expr_app_specific_context_field_get_type_name(
+                               expr);
+               if (!type_name) {
+                       ERR("Failed to get application context type name from event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               /*
+                * Reconstitute the app context field name from its two parts.
+                */
+               ret = asprintf(&name, "%s:%s", provider_name, type_name);
+               if (ret < 0) {
+                       PERROR("Failed to format application specific context: provider_name = '%s', type_name = '%s'",
+                                       provider_name, type_name);
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_symbol(
+                               bytecode, bytecode_reloc, name);
+               free(name);
+               if (status) {
+                       ERR("Failed to push 'get symbol %s:%s' in bytecode",
+                                       provider_name, type_name);
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+       {
+               unsigned int index;
+               const struct lttng_event_expr *parent;
+
+               parent = lttng_event_expr_array_field_element_get_parent_expr(
+                               expr);
+               if (!parent) {
+                       ERR("Failed to get parent expression from array event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               status = event_expr_to_bytecode_recursive(
+                               parent, bytecode, bytecode_reloc);
+               if (status) {
+                       goto end;
+               }
+
+               event_expr_status =
+                               lttng_event_expr_array_field_element_get_index(
+                                               expr, &index);
+               if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) {
+                       ERR("Failed to get array field element index from event expression");
+                       status = -1;
+                       goto end;
+               }
+
+               status = bytecode_push_get_index_u64(bytecode, index);
+               if (status) {
+                       ERR("Failed to push 'get index %u' in bytecode", index);
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               abort();
+       }
+
+       status = 0;
+end:
+       return status;
+}
+
+int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr,
+               struct lttng_bytecode **bytecode_out)
+{
+       int status;
+       struct return_op ret_insn;
+       struct lttng_bytecode_alloc *bytecode = NULL;
+       struct lttng_bytecode_alloc *bytecode_reloc = NULL;
+
+       status = bytecode_init(&bytecode);
+       if (status) {
+               ERR("Failed to initialize bytecode");
+               goto end;
+       }
+
+       status = bytecode_init(&bytecode_reloc);
+       if (status) {
+               ERR("Failed to initialize relocation bytecode");
+               goto end;
+       }
+
+       status = event_expr_to_bytecode_recursive(
+                       expr, &bytecode, &bytecode_reloc);
+       if (status) {
+               /* Errors already logged. */
+               goto end;
+       }
+
+       ret_insn.op = BYTECODE_OP_RETURN;
+       bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn));
+
+       /* Append symbol table to bytecode. */
+       bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b);
+       status = bytecode_push(&bytecode, bytecode_reloc->b.data, 1,
+                       bytecode_get_len(&bytecode_reloc->b));
+       if (status) {
+               ERR("Failed to push symbol table to bytecode");
+               goto end;
+       }
+
+       /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`.  */
+       *bytecode_out = lttng_bytecode_copy(&bytecode->b);
+       if (!*bytecode_out) {
+               status = -1;
+               goto end;
+       }
+
+end:
+       if (bytecode) {
+               free(bytecode);
+       }
+
+       if (bytecode_reloc) {
+               free(bytecode_reloc);
+       }
+
+       return status;
+}
+
+static
+enum lttng_error_code lttng_event_expr_event_payload_field_mi_serialize(
+               const struct lttng_event_expr *expression,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *name = NULL;
+
+       LTTNG_ASSERT(expression);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD);
+
+       name = lttng_event_expr_event_payload_field_get_name(expression);
+       LTTNG_ASSERT(name);
+
+       /* Open event expr payload field element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_expr_payload_field);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, config_element_name, name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close event expr payload field element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+enum lttng_error_code lttng_event_expr_channel_context_field_mi_serialize(
+               const struct lttng_event_expr *expression,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *name = NULL;
+
+       LTTNG_ASSERT(expression);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD);
+
+       name = lttng_event_expr_channel_context_field_get_name(expression);
+       LTTNG_ASSERT(name);
+
+       /* Open event expr channel context field element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_event_expr_channel_context_field);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, config_element_name, name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close event expr channel context field element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+enum lttng_error_code lttng_event_expr_app_specific_context_field_mi_serialize(
+               const struct lttng_event_expr *expression,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *provider_name = NULL;
+       const char *type_name = NULL;
+
+       LTTNG_ASSERT(expression);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(expression->type ==
+                       LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD);
+
+       provider_name = lttng_event_expr_app_specific_context_field_get_provider_name(
+                       expression);
+       LTTNG_ASSERT(provider_name);
+
+       type_name = lttng_event_expr_app_specific_context_field_get_type_name(
+                       expression);
+       LTTNG_ASSERT(provider_name);
+
+       /* Open event expr app specific context field element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_event_expr_app_specific_context_field);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Provider name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_expr_provider_name,
+                       provider_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Type name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_expr_type_name, type_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close event expr app specific context field element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+enum lttng_error_code lttng_event_expr_array_field_element_mi_serialize(
+               const struct lttng_event_expr *expression,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_expr_status status;
+       const struct lttng_event_expr *parent_expr = NULL;
+       unsigned int index;
+
+       LTTNG_ASSERT(expression);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT);
+
+       status = lttng_event_expr_array_field_element_get_index(
+                       expression, &index);
+       LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK);
+
+       parent_expr = lttng_event_expr_array_field_element_get_parent_expr(
+                       expression);
+       LTTNG_ASSERT(parent_expr != NULL);
+
+       /* Open event expr array field element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_event_expr_array_field_element);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Index. */
+       ret = mi_lttng_writer_write_element_unsigned_int(
+                       writer, mi_lttng_element_event_expr_index, index);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Parent expression. */
+       ret_code = lttng_event_expr_mi_serialize(parent_expr, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close event expr array field element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code lttng_event_expr_mi_serialize(
+               const struct lttng_event_expr *expression,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(expression);
+       LTTNG_ASSERT(writer);
+
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_expr);
+       if (ret) {
+               goto mi_error;
+       }
+
+       switch (expression->type) {
+       case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD:
+               ret_code = lttng_event_expr_event_payload_field_mi_serialize(
+                               expression, writer);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD:
+               ret_code = lttng_event_expr_channel_context_field_mi_serialize(
+                               expression, writer);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD:
+               ret_code = lttng_event_expr_app_specific_context_field_mi_serialize(
+                               expression, writer);
+               break;
+       case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT:
+               ret_code = lttng_event_expr_array_field_element_mi_serialize(
+                               expression, writer);
+               break;
+       default:
+               abort();
+       }
+
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+
+end:
+       return ret_code;
+}
diff --git a/src/common/event-field-value.c b/src/common/event-field-value.c
deleted file mode 100644 (file)
index 850f9ed..0000000
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * event-field-value.c
- *
- * Linux Trace Toolkit Control Library
- *
- * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <stddef.h>
-#include <stdbool.h>
-
-#include <common/error.h>
-#include <common/macros.h>
-#include <lttng/event-field-value-internal.h>
-
-static
-struct lttng_event_field_value *create_empty_field_val(
-               enum lttng_event_field_value_type type, size_t size)
-{
-       struct lttng_event_field_value *field_val;
-
-       field_val = zmalloc(size);
-       if (!field_val) {
-               goto end;
-       }
-
-       field_val->type = type;
-
-end:
-       return field_val;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_uint_create(
-               uint64_t val)
-{
-       struct lttng_event_field_value_uint *field_val;
-
-       field_val = container_of(create_empty_field_val(
-                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT,
-                       sizeof(*field_val)),
-                       struct lttng_event_field_value_uint, parent);
-       if (!field_val) {
-               goto error;
-       }
-
-       field_val->val = val;
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return &field_val->parent;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_int_create(
-               int64_t val)
-{
-       struct lttng_event_field_value_int *field_val;
-
-       field_val = container_of(create_empty_field_val(
-                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT,
-                       sizeof(*field_val)),
-                       struct lttng_event_field_value_int, parent);
-       if (!field_val) {
-               goto error;
-       }
-
-       field_val->val = val;
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return &field_val->parent;
-}
-
-static
-struct lttng_event_field_value_enum *create_enum_field_val(
-               enum lttng_event_field_value_type type, size_t size)
-{
-       struct lttng_event_field_value_enum *field_val;
-
-       field_val = container_of(create_empty_field_val(type, size),
-                       struct lttng_event_field_value_enum, parent);
-       if (!field_val) {
-               goto error;
-       }
-
-       lttng_dynamic_pointer_array_init(&field_val->labels, free);
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return field_val;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
-               uint64_t val)
-{
-       struct lttng_event_field_value_enum_uint *field_val;
-
-       field_val = container_of(create_enum_field_val(
-                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM,
-                       sizeof(*field_val)),
-                       struct lttng_event_field_value_enum_uint, parent);
-       if (!field_val) {
-               goto error;
-       }
-
-       field_val->val = val;
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent.parent);
-
-end:
-       return &field_val->parent.parent;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
-               int64_t val)
-{
-       struct lttng_event_field_value_enum_int *field_val;
-
-       field_val = container_of(create_enum_field_val(
-                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM,
-                       sizeof(*field_val)),
-                       struct lttng_event_field_value_enum_int, parent);
-       if (!field_val) {
-               goto error;
-       }
-
-       field_val->val = val;
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent.parent);
-
-end:
-       return &field_val->parent.parent;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_real_create(double val)
-{
-       struct lttng_event_field_value_real *field_val = container_of(
-                       create_empty_field_val(
-                               LTTNG_EVENT_FIELD_VALUE_TYPE_REAL,
-                               sizeof(*field_val)),
-                       struct lttng_event_field_value_real, parent);
-
-       if (!field_val) {
-               goto error;
-       }
-
-       field_val->val = val;
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return &field_val->parent;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
-               const char *val, size_t size)
-{
-       struct lttng_event_field_value_string *field_val = container_of(
-                       create_empty_field_val(
-                               LTTNG_EVENT_FIELD_VALUE_TYPE_STRING,
-                               sizeof(*field_val)),
-                       struct lttng_event_field_value_string, parent);
-
-       if (!field_val) {
-               goto error;
-       }
-
-       LTTNG_ASSERT(val);
-       field_val->val = strndup(val, size);
-       if (!field_val->val) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return &field_val->parent;
-}
-
-struct lttng_event_field_value *lttng_event_field_value_string_create(
-               const char *val)
-{
-       LTTNG_ASSERT(val);
-       return lttng_event_field_value_string_create_with_size(val,
-                       strlen(val));
-}
-
-static
-void destroy_field_val(void *field_val)
-{
-       lttng_event_field_value_destroy(field_val);
-}
-
-struct lttng_event_field_value *lttng_event_field_value_array_create(void)
-{
-       struct lttng_event_field_value_array *field_val = container_of(
-                       create_empty_field_val(
-                               LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY,
-                               sizeof(*field_val)),
-                       struct lttng_event_field_value_array, parent);
-
-       if (!field_val) {
-               goto error;
-       }
-
-       lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val);
-       goto end;
-
-error:
-       lttng_event_field_value_destroy(&field_val->parent);
-
-end:
-       return &field_val->parent;
-}
-
-void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val)
-{
-       if (!field_val) {
-               goto end;
-       }
-
-       switch (field_val->type) {
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
-       {
-               struct lttng_event_field_value_enum *enum_field_val =
-                               container_of(field_val,
-                                       struct lttng_event_field_value_enum, parent);
-
-               lttng_dynamic_pointer_array_reset(&enum_field_val->labels);
-               break;
-       }
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
-       {
-               struct lttng_event_field_value_string *str_field_val =
-                               container_of(field_val,
-                                       struct lttng_event_field_value_string, parent);
-
-               free(str_field_val->val);
-               break;
-       }
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
-       {
-               struct lttng_event_field_value_array *array_field_expr =
-                               container_of(field_val,
-                                       struct lttng_event_field_value_array,
-                                       parent);
-
-               lttng_dynamic_pointer_array_reset(&array_field_expr->elems);
-               break;
-       }
-       default:
-               break;
-       }
-
-       free(field_val);
-
-end:
-       return;
-}
-
-int lttng_event_field_value_enum_append_label_with_size(
-               struct lttng_event_field_value *field_val,
-               const char *label, size_t size)
-{
-       int ret;
-       char *new_label;
-
-       LTTNG_ASSERT(field_val);
-       LTTNG_ASSERT(label);
-       new_label = strndup(label, size);
-       if (!new_label) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_pointer_array_add_pointer(
-                       &container_of(field_val,
-                               struct lttng_event_field_value_enum, parent)->labels,
-                       new_label);
-       if (ret == 0) {
-               new_label = NULL;
-       }
-
-end:
-       free(new_label);
-       return ret;
-}
-
-int lttng_event_field_value_enum_append_label(
-               struct lttng_event_field_value *field_val,
-               const char *label)
-{
-       LTTNG_ASSERT(label);
-       return lttng_event_field_value_enum_append_label_with_size(field_val,
-                       label, strlen(label));
-}
-
-int lttng_event_field_value_array_append(
-               struct lttng_event_field_value *array_field_val,
-               struct lttng_event_field_value *field_val)
-{
-       LTTNG_ASSERT(array_field_val);
-       LTTNG_ASSERT(field_val);
-       return lttng_dynamic_pointer_array_add_pointer(
-                       &container_of(array_field_val,
-                               struct lttng_event_field_value_array, parent)->elems,
-                       field_val);
-}
-
-int lttng_event_field_value_array_append_unavailable(
-               struct lttng_event_field_value *array_field_val)
-{
-       LTTNG_ASSERT(array_field_val);
-       return lttng_dynamic_pointer_array_add_pointer(
-                       &container_of(array_field_val,
-                               struct lttng_event_field_value_array, parent)->elems,
-                       NULL);
-}
-
-enum lttng_event_field_value_type lttng_event_field_value_get_type(
-               const struct lttng_event_field_value *field_val)
-{
-       enum lttng_event_field_value_type type;
-
-       if (!field_val) {
-               type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID;
-               goto end;
-       }
-
-       type = field_val->type;
-
-end:
-       return type;
-}
-
-enum lttng_event_field_value_status
-lttng_event_field_value_unsigned_int_get_value(
-               const struct lttng_event_field_value *field_val, uint64_t *val)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || !val) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       switch (field_val->type) {
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
-               *val = container_of(field_val,
-                               const struct lttng_event_field_value_uint,
-                               parent)->val;
-               break;
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
-       {
-               const struct lttng_event_field_value_enum *field_val_enum = container_of(
-                               field_val,
-                               const struct lttng_event_field_value_enum,
-                               parent);
-               const struct lttng_event_field_value_enum_uint
-                               *field_val_enum_uint = container_of(
-                                               field_val_enum,
-                                               const struct lttng_event_field_value_enum_uint,
-                                               parent);
-               *val = field_val_enum_uint->val;
-               break;
-       }
-       default:
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-enum lttng_event_field_value_status
-lttng_event_field_value_signed_int_get_value(
-               const struct lttng_event_field_value *field_val, int64_t *val)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || !val) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       switch (field_val->type) {
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
-               *val = container_of(field_val,
-                               const struct lttng_event_field_value_int,
-                               parent)->val;
-               break;
-       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
-       {
-               const struct lttng_event_field_value_enum *field_val_enum = container_of(
-                               field_val,
-                               const struct lttng_event_field_value_enum,
-                               parent);
-               const struct lttng_event_field_value_enum_int
-                               *field_val_enum_uint = container_of(
-                                               field_val_enum,
-                                               const struct lttng_event_field_value_enum_int,
-                                               parent);
-               *val = field_val_enum_uint->val;
-               break;
-       }
-       default:
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-enum lttng_event_field_value_status
-lttng_event_field_value_real_get_value(
-               const struct lttng_event_field_value *field_val, double *val)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL ||
-                       !val) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       *val = container_of(field_val,
-                       const struct lttng_event_field_value_real, parent)->val;
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-static
-bool is_enum_field_val(const struct lttng_event_field_value *field_val)
-{
-       return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM ||
-               field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM;
-}
-
-enum lttng_event_field_value_status
-lttng_event_field_value_enum_get_label_count(
-               const struct lttng_event_field_value *field_val,
-               unsigned int *count)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || !is_enum_field_val(field_val) || !count) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       *count = (unsigned int) lttng_dynamic_pointer_array_get_count(
-                       &container_of(field_val,
-                               const struct lttng_event_field_value_enum,
-                               parent)->labels);
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-const char *lttng_event_field_value_enum_get_label_at_index(
-               const struct lttng_event_field_value *field_val,
-               unsigned int index)
-{
-       const char *ret;
-       const struct lttng_event_field_value_enum *enum_field_val;
-
-       if (!field_val || !is_enum_field_val(field_val)) {
-               ret = NULL;
-               goto end;
-       }
-
-       enum_field_val = container_of(field_val,
-                       const struct lttng_event_field_value_enum, parent);
-
-       if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) {
-               ret = NULL;
-               goto end;
-       }
-
-       ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels,
-                       index);
-
-end:
-       return ret;
-}
-
-enum lttng_event_field_value_status lttng_event_field_value_string_get_value(
-               const struct lttng_event_field_value *field_val,
-               const char **value)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       *value = container_of(field_val,
-                       const struct lttng_event_field_value_string, parent)->val;
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-enum lttng_event_field_value_status lttng_event_field_value_array_get_length(
-               const struct lttng_event_field_value *field_val,
-               unsigned int *length)
-{
-       enum lttng_event_field_value_status status;
-
-       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
-                       !length) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       *length = (unsigned int) lttng_dynamic_pointer_array_get_count(
-                       &container_of(field_val,
-                               const struct lttng_event_field_value_array,
-                               parent)->elems);
-       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-
-end:
-       return status;
-}
-
-enum lttng_event_field_value_status
-lttng_event_field_value_array_get_element_at_index(
-               const struct lttng_event_field_value *field_val,
-               unsigned int index,
-               const struct lttng_event_field_value **elem_field_val)
-{
-       enum lttng_event_field_value_status status;
-       const struct lttng_event_field_value_array *array_field_val;
-
-       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
-                       !elem_field_val) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       array_field_val = container_of(field_val,
-                       const struct lttng_event_field_value_array, parent);
-
-       if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
-               goto end;
-       }
-
-       *elem_field_val = lttng_dynamic_pointer_array_get_pointer(
-                       &array_field_val->elems, index);
-       if (*elem_field_val) {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
-       } else {
-               status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE;
-       }
-
-end:
-       return status;
-}
diff --git a/src/common/event-field-value.cpp b/src/common/event-field-value.cpp
new file mode 100644 (file)
index 0000000..7572713
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * event-field-value.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2020 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <lttng/event-field-value-internal.h>
+
+static
+struct lttng_event_field_value *create_empty_field_val(
+               enum lttng_event_field_value_type type, size_t size)
+{
+       struct lttng_event_field_value *field_val;
+
+       field_val = (lttng_event_field_value *) zmalloc(size);
+       if (!field_val) {
+               goto end;
+       }
+
+       field_val->type = type;
+
+end:
+       return field_val;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_uint_create(
+               uint64_t val)
+{
+       struct lttng_event_field_value_uint *field_val;
+
+       field_val = container_of(create_empty_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_uint, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_int_create(
+               int64_t val)
+{
+       struct lttng_event_field_value_int *field_val;
+
+       field_val = container_of(create_empty_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_int, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+static
+struct lttng_event_field_value_enum *create_enum_field_val(
+               enum lttng_event_field_value_type type, size_t size)
+{
+       struct lttng_event_field_value_enum *field_val;
+
+       field_val = container_of(create_empty_field_val(type, size),
+                       struct lttng_event_field_value_enum, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       lttng_dynamic_pointer_array_init(&field_val->labels, free);
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return field_val;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_enum_uint_create(
+               uint64_t val)
+{
+       struct lttng_event_field_value_enum_uint *field_val;
+
+       field_val = container_of(create_enum_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_enum_uint, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+       return &field_val->parent.parent;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_enum_int_create(
+               int64_t val)
+{
+       struct lttng_event_field_value_enum_int *field_val;
+
+       field_val = container_of(create_enum_field_val(
+                       LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM,
+                       sizeof(*field_val)),
+                       struct lttng_event_field_value_enum_int, parent);
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent.parent);
+
+end:
+       return &field_val->parent.parent;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_real_create(double val)
+{
+       struct lttng_event_field_value_real *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_REAL,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_real, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       field_val->val = val;
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_string_create_with_size(
+               const char *val, size_t size)
+{
+       struct lttng_event_field_value_string *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_STRING,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_string, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       LTTNG_ASSERT(val);
+       field_val->val = strndup(val, size);
+       if (!field_val->val) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+struct lttng_event_field_value *lttng_event_field_value_string_create(
+               const char *val)
+{
+       LTTNG_ASSERT(val);
+       return lttng_event_field_value_string_create_with_size(val,
+                       strlen(val));
+}
+
+static
+void destroy_field_val(void *field_val)
+{
+       lttng_event_field_value_destroy((lttng_event_field_value *) field_val);
+}
+
+struct lttng_event_field_value *lttng_event_field_value_array_create(void)
+{
+       struct lttng_event_field_value_array *field_val = container_of(
+                       create_empty_field_val(
+                               LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY,
+                               sizeof(*field_val)),
+                       struct lttng_event_field_value_array, parent);
+
+       if (!field_val) {
+               goto error;
+       }
+
+       lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val);
+       goto end;
+
+error:
+       lttng_event_field_value_destroy(&field_val->parent);
+
+end:
+       return &field_val->parent;
+}
+
+void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val)
+{
+       if (!field_val) {
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+       {
+               struct lttng_event_field_value_enum *enum_field_val =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_enum, parent);
+
+               lttng_dynamic_pointer_array_reset(&enum_field_val->labels);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING:
+       {
+               struct lttng_event_field_value_string *str_field_val =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_string, parent);
+
+               free(str_field_val->val);
+               break;
+       }
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY:
+       {
+               struct lttng_event_field_value_array *array_field_expr =
+                               container_of(field_val,
+                                       struct lttng_event_field_value_array,
+                                       parent);
+
+               lttng_dynamic_pointer_array_reset(&array_field_expr->elems);
+               break;
+       }
+       default:
+               break;
+       }
+
+       free(field_val);
+
+end:
+       return;
+}
+
+int lttng_event_field_value_enum_append_label_with_size(
+               struct lttng_event_field_value *field_val,
+               const char *label, size_t size)
+{
+       int ret;
+       char *new_label;
+
+       LTTNG_ASSERT(field_val);
+       LTTNG_ASSERT(label);
+       new_label = strndup(label, size);
+       if (!new_label) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(field_val,
+                               struct lttng_event_field_value_enum, parent)->labels,
+                       new_label);
+       if (ret == 0) {
+               new_label = NULL;
+       }
+
+end:
+       free(new_label);
+       return ret;
+}
+
+int lttng_event_field_value_enum_append_label(
+               struct lttng_event_field_value *field_val,
+               const char *label)
+{
+       LTTNG_ASSERT(label);
+       return lttng_event_field_value_enum_append_label_with_size(field_val,
+                       label, strlen(label));
+}
+
+int lttng_event_field_value_array_append(
+               struct lttng_event_field_value *array_field_val,
+               struct lttng_event_field_value *field_val)
+{
+       LTTNG_ASSERT(array_field_val);
+       LTTNG_ASSERT(field_val);
+       return lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(array_field_val,
+                               struct lttng_event_field_value_array, parent)->elems,
+                       field_val);
+}
+
+int lttng_event_field_value_array_append_unavailable(
+               struct lttng_event_field_value *array_field_val)
+{
+       LTTNG_ASSERT(array_field_val);
+       return lttng_dynamic_pointer_array_add_pointer(
+                       &container_of(array_field_val,
+                               struct lttng_event_field_value_array, parent)->elems,
+                       NULL);
+}
+
+enum lttng_event_field_value_type lttng_event_field_value_get_type(
+               const struct lttng_event_field_value *field_val)
+{
+       enum lttng_event_field_value_type type;
+
+       if (!field_val) {
+               type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID;
+               goto end;
+       }
+
+       type = field_val->type;
+
+end:
+       return type;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_unsigned_int_get_value(
+               const struct lttng_event_field_value *field_val, uint64_t *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT:
+               *val = container_of(field_val,
+                               const struct lttng_event_field_value_uint,
+                               parent)->val;
+               break;
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM:
+       {
+               const struct lttng_event_field_value_enum *field_val_enum = container_of(
+                               field_val,
+                               const struct lttng_event_field_value_enum,
+                               parent);
+               const struct lttng_event_field_value_enum_uint
+                               *field_val_enum_uint = container_of(
+                                               field_val_enum,
+                                               const struct lttng_event_field_value_enum_uint,
+                                               parent);
+               *val = field_val_enum_uint->val;
+               break;
+       }
+       default:
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_signed_int_get_value(
+               const struct lttng_event_field_value *field_val, int64_t *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       switch (field_val->type) {
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT:
+               *val = container_of(field_val,
+                               const struct lttng_event_field_value_int,
+                               parent)->val;
+               break;
+       case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM:
+       {
+               const struct lttng_event_field_value_enum *field_val_enum = container_of(
+                               field_val,
+                               const struct lttng_event_field_value_enum,
+                               parent);
+               const struct lttng_event_field_value_enum_int
+                               *field_val_enum_uint = container_of(
+                                               field_val_enum,
+                                               const struct lttng_event_field_value_enum_int,
+                                               parent);
+               *val = field_val_enum_uint->val;
+               break;
+       }
+       default:
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_real_get_value(
+               const struct lttng_event_field_value *field_val, double *val)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL ||
+                       !val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *val = container_of(field_val,
+                       const struct lttng_event_field_value_real, parent)->val;
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+bool is_enum_field_val(const struct lttng_event_field_value *field_val)
+{
+       return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM ||
+               field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_enum_get_label_count(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *count)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || !is_enum_field_val(field_val) || !count) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = (unsigned int) lttng_dynamic_pointer_array_get_count(
+                       &container_of(field_val,
+                               const struct lttng_event_field_value_enum,
+                               parent)->labels);
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+const char *lttng_event_field_value_enum_get_label_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index)
+{
+       const char *ret;
+       const struct lttng_event_field_value_enum *enum_field_val;
+
+       if (!field_val || !is_enum_field_val(field_val)) {
+               ret = NULL;
+               goto end;
+       }
+
+       enum_field_val = container_of(field_val,
+                       const struct lttng_event_field_value_enum, parent);
+
+       if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) {
+               ret = NULL;
+               goto end;
+       }
+
+       ret = (const char *) lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels,
+                       index);
+
+end:
+       return ret;
+}
+
+enum lttng_event_field_value_status lttng_event_field_value_string_get_value(
+               const struct lttng_event_field_value *field_val,
+               const char **value)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *value = container_of(field_val,
+                       const struct lttng_event_field_value_string, parent)->val;
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status lttng_event_field_value_array_get_length(
+               const struct lttng_event_field_value *field_val,
+               unsigned int *length)
+{
+       enum lttng_event_field_value_status status;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+                       !length) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *length = (unsigned int) lttng_dynamic_pointer_array_get_count(
+                       &container_of(field_val,
+                               const struct lttng_event_field_value_array,
+                               parent)->elems);
+       status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+
+end:
+       return status;
+}
+
+enum lttng_event_field_value_status
+lttng_event_field_value_array_get_element_at_index(
+               const struct lttng_event_field_value *field_val,
+               unsigned int index,
+               const struct lttng_event_field_value **elem_field_val)
+{
+       enum lttng_event_field_value_status status;
+       const struct lttng_event_field_value_array *array_field_val;
+
+       if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY ||
+                       !elem_field_val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       array_field_val = container_of(field_val,
+                       const struct lttng_event_field_value_array, parent);
+
+       if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID;
+               goto end;
+       }
+
+       *elem_field_val = (lttng_event_field_value *) lttng_dynamic_pointer_array_get_pointer(
+                       &array_field_val->elems, index);
+       if (*elem_field_val) {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK;
+       } else {
+               status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE;
+       }
+
+end:
+       return status;
+}
diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c
deleted file mode 100644 (file)
index 7e3f724..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte
- * <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/jul-logging-internal.h>
-#include <lttng/event-rule/kernel-kprobe-internal.h>
-#include <lttng/event-rule/kernel-syscall-internal.h>
-#include <lttng/event-rule/kernel-tracepoint-internal.h>
-#include <lttng/event-rule/kernel-uprobe-internal.h>
-#include <lttng/event-rule/log4j-logging-internal.h>
-#include <lttng/event-rule/python-logging-internal.h>
-#include <lttng/event-rule/user-tracepoint-internal.h>
-#include <stdbool.h>
-
-enum lttng_event_rule_type lttng_event_rule_get_type(
-               const struct lttng_event_rule *event_rule)
-{
-       return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN;
-}
-
-enum lttng_domain_type lttng_event_rule_get_domain_type(
-               const struct lttng_event_rule *event_rule)
-{
-       enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
-
-       switch (lttng_event_rule_get_type(event_rule)) {
-       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
-               domain_type = LTTNG_DOMAIN_UST;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
-               domain_type = LTTNG_DOMAIN_JUL;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
-               domain_type = LTTNG_DOMAIN_LOG4J;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
-               domain_type = LTTNG_DOMAIN_PYTHON;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
-               domain_type = LTTNG_DOMAIN_KERNEL;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
-               domain_type = LTTNG_DOMAIN_NONE;
-               break;
-       }
-
-       return domain_type;
-}
-
-static void lttng_event_rule_release(struct urcu_ref *ref)
-{
-       struct lttng_event_rule *event_rule =
-                       container_of(ref, typeof(*event_rule), ref);
-
-       LTTNG_ASSERT(event_rule->destroy);
-       event_rule->destroy(event_rule);
-}
-
-void lttng_event_rule_destroy(struct lttng_event_rule *event_rule)
-{
-       lttng_event_rule_put(event_rule);
-}
-
-bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule)
-{
-       bool valid;
-
-       if (!event_rule) {
-               valid = false;
-               goto end;
-       }
-
-       if (!event_rule->validate) {
-               /* Sub-class guarantees that it can never be invalid. */
-               valid = true;
-               goto end;
-       }
-
-       valid = event_rule->validate(event_rule);
-end:
-       return valid;
-}
-
-int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_event_rule_comm event_rule_comm = {};
-
-       if (!event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       event_rule_comm.event_rule_type = (int8_t) event_rule->type;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &event_rule_comm, sizeof(event_rule_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = event_rule->serialize(event_rule, payload);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
-               const struct lttng_event_rule *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       is_equal = a->equal ? a->equal(a, b) : true;
-end:
-       return is_equal;
-}
-
-ssize_t lttng_event_rule_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **event_rule)
-{
-       ssize_t ret, consumed = 0;
-       event_rule_create_from_payload_cb create_from_payload = NULL;
-       const struct lttng_event_rule_comm *event_rule_comm;
-       const struct lttng_payload_view event_rule_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*event_rule_comm));
-
-       if (!view || !event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&event_rule_comm_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Deserializing event_rule from payload");
-       event_rule_comm = (const struct lttng_event_rule_comm *) event_rule_comm_view.buffer.data;
-       consumed += sizeof(*event_rule_comm);
-
-       switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) {
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
-               create_from_payload = lttng_event_rule_kernel_kprobe_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
-               create_from_payload = lttng_event_rule_kernel_uprobe_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
-               create_from_payload =
-                               lttng_event_rule_kernel_syscall_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
-               create_from_payload =
-                               lttng_event_rule_kernel_tracepoint_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
-               create_from_payload =
-                               lttng_event_rule_user_tracepoint_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
-               create_from_payload =
-                               lttng_event_rule_jul_logging_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
-               create_from_payload =
-                               lttng_event_rule_log4j_logging_create_from_payload;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
-               create_from_payload =
-                               lttng_event_rule_python_logging_create_from_payload;
-               break;
-       default:
-               ERR("Attempted to create event rule of unknown type (%i)",
-                               (int) event_rule_comm->event_rule_type);
-               ret = -1;
-               goto end;
-       }
-
-       LTTNG_ASSERT(create_from_payload);
-
-       {
-               struct lttng_payload_view child_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed, -1);
-
-               ret = create_from_payload(&child_view, event_rule);
-               if (ret < 0) {
-                       goto end;
-               }
-
-               consumed += ret;
-       }
-
-       if (!lttng_event_rule_validate(*event_rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = consumed;
-end:
-       return ret;
-}
-
-void lttng_event_rule_init(struct lttng_event_rule *event_rule,
-               enum lttng_event_rule_type type)
-{
-       urcu_ref_init(&event_rule->ref);
-       event_rule->type = type;
-}
-
-bool lttng_event_rule_get(struct lttng_event_rule *event_rule)
-{
-       return urcu_ref_get_unless_zero(&event_rule->ref);
-}
-
-void lttng_event_rule_put(struct lttng_event_rule *event_rule)
-{
-       if (!event_rule) {
-               return;
-       }
-
-       LTTNG_ASSERT(event_rule->ref.refcount);
-       urcu_ref_put(&event_rule->ref, lttng_event_rule_release);
-}
-
-enum lttng_error_code lttng_event_rule_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       LTTNG_ASSERT(rule->generate_filter_bytecode);
-       return rule->generate_filter_bytecode(rule, creds);
-}
-
-const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule)
-{
-       LTTNG_ASSERT(rule->get_filter);
-       return rule->get_filter(rule);
-}
-
-const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       LTTNG_ASSERT(rule->get_filter_bytecode);
-       return rule->get_filter_bytecode(rule);
-}
-
-enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_generate_exclusions(const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **exclusions)
-{
-       LTTNG_ASSERT(rule->generate_exclusions);
-       return rule->generate_exclusions(rule, exclusions);
-}
-
-struct lttng_event *lttng_event_rule_generate_lttng_event(
-               const struct lttng_event_rule *rule)
-{
-       LTTNG_ASSERT(rule->generate_lttng_event);
-       return rule->generate_lttng_event(rule);
-}
-
-bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule)
-{
-       bool targets_agent_domain = false;
-       enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule);
-
-       switch (type) {
-       case LTTNG_DOMAIN_JUL:
-       case LTTNG_DOMAIN_LOG4J:
-       case LTTNG_DOMAIN_PYTHON:
-               targets_agent_domain = true;
-               break;
-       case LTTNG_DOMAIN_UST:
-       case LTTNG_DOMAIN_KERNEL:
-               targets_agent_domain = false;
-               break;
-       default:
-               abort();
-       };
-
-       return targets_agent_domain;
-}
-
-const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
-{
-       switch (type) {
-       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
-               return "unknown";
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
-               return "kernel syscall";
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
-               return "kernel kprobe";
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
-               return "kernel uprobe";
-       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
-               return "kernel tracepoint";
-       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
-               return "user tracepoint";
-       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
-               return "jul logging";
-       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
-               return "log4j logging";
-       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
-               return "python logging";
-
-       default:
-               abort();
-       }
-}
-
-unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule)
-{
-       LTTNG_ASSERT(rule->hash);
-       return rule->hash(rule);
-}
-
-enum lttng_error_code lttng_event_rule_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(rule->mi_serialize);
-
-       /* Open event rule element. */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_rule);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize underlying event rule. */
-       ret_code = rule->mi_serialize(rule, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close event rule element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/event-rule/event-rule.cpp b/src/common/event-rule/event-rule.cpp
new file mode 100644 (file)
index 0000000..7e3f724
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte
+ * <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/jul-logging-internal.h>
+#include <lttng/event-rule/kernel-kprobe-internal.h>
+#include <lttng/event-rule/kernel-syscall-internal.h>
+#include <lttng/event-rule/kernel-tracepoint-internal.h>
+#include <lttng/event-rule/kernel-uprobe-internal.h>
+#include <lttng/event-rule/log4j-logging-internal.h>
+#include <lttng/event-rule/python-logging-internal.h>
+#include <lttng/event-rule/user-tracepoint-internal.h>
+#include <stdbool.h>
+
+enum lttng_event_rule_type lttng_event_rule_get_type(
+               const struct lttng_event_rule *event_rule)
+{
+       return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN;
+}
+
+enum lttng_domain_type lttng_event_rule_get_domain_type(
+               const struct lttng_event_rule *event_rule)
+{
+       enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE;
+
+       switch (lttng_event_rule_get_type(event_rule)) {
+       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
+               domain_type = LTTNG_DOMAIN_UST;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
+               domain_type = LTTNG_DOMAIN_JUL;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
+               domain_type = LTTNG_DOMAIN_LOG4J;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
+               domain_type = LTTNG_DOMAIN_PYTHON;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
+               domain_type = LTTNG_DOMAIN_KERNEL;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               domain_type = LTTNG_DOMAIN_NONE;
+               break;
+       }
+
+       return domain_type;
+}
+
+static void lttng_event_rule_release(struct urcu_ref *ref)
+{
+       struct lttng_event_rule *event_rule =
+                       container_of(ref, typeof(*event_rule), ref);
+
+       LTTNG_ASSERT(event_rule->destroy);
+       event_rule->destroy(event_rule);
+}
+
+void lttng_event_rule_destroy(struct lttng_event_rule *event_rule)
+{
+       lttng_event_rule_put(event_rule);
+}
+
+bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule)
+{
+       bool valid;
+
+       if (!event_rule) {
+               valid = false;
+               goto end;
+       }
+
+       if (!event_rule->validate) {
+               /* Sub-class guarantees that it can never be invalid. */
+               valid = true;
+               goto end;
+       }
+
+       valid = event_rule->validate(event_rule);
+end:
+       return valid;
+}
+
+int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_event_rule_comm event_rule_comm = {};
+
+       if (!event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       event_rule_comm.event_rule_type = (int8_t) event_rule->type;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &event_rule_comm, sizeof(event_rule_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = event_rule->serialize(event_rule, payload);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+bool lttng_event_rule_is_equal(const struct lttng_event_rule *a,
+               const struct lttng_event_rule *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+ssize_t lttng_event_rule_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **event_rule)
+{
+       ssize_t ret, consumed = 0;
+       event_rule_create_from_payload_cb create_from_payload = NULL;
+       const struct lttng_event_rule_comm *event_rule_comm;
+       const struct lttng_payload_view event_rule_comm_view =
+                       lttng_payload_view_from_view(
+                                       view, 0, sizeof(*event_rule_comm));
+
+       if (!view || !event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&event_rule_comm_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Deserializing event_rule from payload");
+       event_rule_comm = (const struct lttng_event_rule_comm *) event_rule_comm_view.buffer.data;
+       consumed += sizeof(*event_rule_comm);
+
+       switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) {
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
+               create_from_payload = lttng_event_rule_kernel_kprobe_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
+               create_from_payload = lttng_event_rule_kernel_uprobe_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
+               create_from_payload =
+                               lttng_event_rule_kernel_syscall_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
+               create_from_payload =
+                               lttng_event_rule_kernel_tracepoint_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
+               create_from_payload =
+                               lttng_event_rule_user_tracepoint_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
+               create_from_payload =
+                               lttng_event_rule_jul_logging_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
+               create_from_payload =
+                               lttng_event_rule_log4j_logging_create_from_payload;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
+               create_from_payload =
+                               lttng_event_rule_python_logging_create_from_payload;
+               break;
+       default:
+               ERR("Attempted to create event rule of unknown type (%i)",
+                               (int) event_rule_comm->event_rule_type);
+               ret = -1;
+               goto end;
+       }
+
+       LTTNG_ASSERT(create_from_payload);
+
+       {
+               struct lttng_payload_view child_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed, -1);
+
+               ret = create_from_payload(&child_view, event_rule);
+               if (ret < 0) {
+                       goto end;
+               }
+
+               consumed += ret;
+       }
+
+       if (!lttng_event_rule_validate(*event_rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = consumed;
+end:
+       return ret;
+}
+
+void lttng_event_rule_init(struct lttng_event_rule *event_rule,
+               enum lttng_event_rule_type type)
+{
+       urcu_ref_init(&event_rule->ref);
+       event_rule->type = type;
+}
+
+bool lttng_event_rule_get(struct lttng_event_rule *event_rule)
+{
+       return urcu_ref_get_unless_zero(&event_rule->ref);
+}
+
+void lttng_event_rule_put(struct lttng_event_rule *event_rule)
+{
+       if (!event_rule) {
+               return;
+       }
+
+       LTTNG_ASSERT(event_rule->ref.refcount);
+       urcu_ref_put(&event_rule->ref, lttng_event_rule_release);
+}
+
+enum lttng_error_code lttng_event_rule_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       LTTNG_ASSERT(rule->generate_filter_bytecode);
+       return rule->generate_filter_bytecode(rule, creds);
+}
+
+const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule)
+{
+       LTTNG_ASSERT(rule->get_filter);
+       return rule->get_filter(rule);
+}
+
+const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       LTTNG_ASSERT(rule->get_filter_bytecode);
+       return rule->get_filter_bytecode(rule);
+}
+
+enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_generate_exclusions(const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **exclusions)
+{
+       LTTNG_ASSERT(rule->generate_exclusions);
+       return rule->generate_exclusions(rule, exclusions);
+}
+
+struct lttng_event *lttng_event_rule_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       LTTNG_ASSERT(rule->generate_lttng_event);
+       return rule->generate_lttng_event(rule);
+}
+
+bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule)
+{
+       bool targets_agent_domain = false;
+       enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule);
+
+       switch (type) {
+       case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
+       case LTTNG_DOMAIN_PYTHON:
+               targets_agent_domain = true;
+               break;
+       case LTTNG_DOMAIN_UST:
+       case LTTNG_DOMAIN_KERNEL:
+               targets_agent_domain = false;
+               break;
+       default:
+               abort();
+       };
+
+       return targets_agent_domain;
+}
+
+const char *lttng_event_rule_type_str(enum lttng_event_rule_type type)
+{
+       switch (type) {
+       case LTTNG_EVENT_RULE_TYPE_UNKNOWN:
+               return "unknown";
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL:
+               return "kernel syscall";
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE:
+               return "kernel kprobe";
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE:
+               return "kernel uprobe";
+       case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT:
+               return "kernel tracepoint";
+       case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT:
+               return "user tracepoint";
+       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
+               return "jul logging";
+       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
+               return "log4j logging";
+       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
+               return "python logging";
+
+       default:
+               abort();
+       }
+}
+
+unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule)
+{
+       LTTNG_ASSERT(rule->hash);
+       return rule->hash(rule);
+}
+
+enum lttng_error_code lttng_event_rule_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(rule->mi_serialize);
+
+       /* Open event rule element. */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_rule);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize underlying event rule. */
+       ret_code = rule->mi_serialize(rule, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close event rule element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/event-rule/jul-logging.c b/src/common/event-rule/jul-logging.c
deleted file mode 100644 (file)
index c6c0554..0000000
+++ /dev/null
@@ -1,922 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/jul-logging-internal.h>
-#include <lttng/event.h>
-#include <lttng/log-level-rule.h>
-
-#define IS_JUL_LOGGING_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_JUL_LOGGING)
-
-static void lttng_event_rule_jul_logging_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-
-       lttng_log_level_rule_destroy(jul_logging->log_level_rule);
-       free(jul_logging->pattern);
-       free(jul_logging->filter_expression);
-       free(jul_logging->internal_filter.filter);
-       free(jul_logging->internal_filter.bytecode);
-       free(jul_logging);
-}
-
-static bool lttng_event_rule_jul_logging_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_jul_logging *jul_logging;
-
-       if (!rule) {
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-
-       /* Required field. */
-       if (!jul_logging->pattern) {
-               ERR("Invalid jul_logging event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_jul_logging_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t pattern_len, filter_expression_len, header_offset;
-       size_t size_before_log_level_rule;
-       struct lttng_event_rule_jul_logging *jul_logging;
-       struct lttng_event_rule_jul_logging_comm jul_logging_comm;
-       struct lttng_event_rule_jul_logging_comm *header;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing jul_logging event rule.");
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-
-       pattern_len = strlen(jul_logging->pattern) + 1;
-
-       if (jul_logging->filter_expression != NULL) {
-               filter_expression_len =
-                               strlen(jul_logging->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       jul_logging_comm.pattern_len = pattern_len;
-       jul_logging_comm.filter_expression_len = filter_expression_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &jul_logging_comm,
-                       sizeof(jul_logging_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, jul_logging->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, jul_logging->filter_expression,
-                       filter_expression_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_log_level_rule = payload->buffer.size;
-
-       ret = lttng_log_level_rule_serialize(jul_logging->log_level_rule, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
-       header->log_level_rule_len =
-                       payload->buffer.size - size_before_log_level_rule;
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_jul_logging_is_equal(
-               const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_jul_logging *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_jul_logging, parent);
-       b = container_of(_b, struct lttng_event_rule_jul_logging, parent);
-
-       /* Quick checks. */
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       /* Long check. */
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set; not the other. */
-               goto end;
-       }
-
-       if (!lttng_log_level_rule_is_equal(
-                               a->log_level_rule, b->log_level_rule)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-/*
- * On success ret is 0;
- *
- * On error ret is negative.
- *
- * An event with NO loglevel and the name is * will return NULL.
- */
-static int generate_agent_filter(
-               const struct lttng_event_rule *rule, char **_agent_filter)
-{
-       int err;
-       int ret = 0;
-       char *agent_filter = NULL;
-       const char *pattern;
-       const char *filter;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-       enum lttng_event_rule_status status;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(_agent_filter);
-
-       status = lttng_event_rule_jul_logging_get_name_pattern(rule, &pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-
-       /* Don't add filter for the '*' event. */
-       if (strcmp(pattern, "*") != 0) {
-               if (filter) {
-                       err = asprintf(&agent_filter,
-                                       "(%s) && (logger_name == \"%s\")",
-                                       filter, pattern);
-               } else {
-                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
-                                       pattern);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       status = lttng_event_rule_jul_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-               const char *op;
-               int level;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule))
-               {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &level);
-                       op = "==";
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &level);
-                       op = ">=";
-                       break;
-               default:
-                       abort();
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               if (filter || agent_filter) {
-                       char *new_filter;
-
-                       err = asprintf(&new_filter,
-                                       "(%s) && (int_loglevel %s %d)",
-                                       agent_filter ? agent_filter : filter,
-                                       op, level);
-                       if (agent_filter) {
-                               free(agent_filter);
-                       }
-                       agent_filter = new_filter;
-               } else {
-                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
-                                       level);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_agent_filter = agent_filter;
-       agent_filter = NULL;
-
-end:
-       free(agent_filter);
-       return ret;
-}
-
-static enum lttng_error_code
-lttng_event_rule_jul_logging_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-       char *agent_filter;
-
-       LTTNG_ASSERT(rule);
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-
-       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       ret = generate_agent_filter(rule, &agent_filter);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       jul_logging->internal_filter.filter = agent_filter;
-
-       if (jul_logging->internal_filter.filter == NULL) {
-               ret_code = LTTNG_OK;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       jul_logging->internal_filter.filter, creds,
-                       &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       jul_logging->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-       ret_code = LTTNG_OK;
-
-error:
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_jul_logging_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-
-       LTTNG_ASSERT(rule);
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       return jul_logging->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_jul_logging_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-
-       LTTNG_ASSERT(rule);
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       return jul_logging->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_jul_logging_generate_exclusions(
-               const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **_exclusions)
-{
-       /* Unsupported. */
-       *_exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long lttng_event_rule_jul_logging_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_jul_logging *tp_rule =
-                       container_of(rule, typeof(*tp_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_JUL_LOGGING,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
-
-       if (tp_rule->filter_expression) {
-               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
-       }
-
-       if (tp_rule->log_level_rule) {
-               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
-       }
-
-       return hash;
-}
-
-static struct lttng_event *lttng_event_rule_jul_logging_generate_lttng_event(
-               const struct lttng_event_rule *rule)
-{
-       int ret;
-       const struct lttng_event_rule_jul_logging *jul_logging;
-       struct lttng_event *local_event = NULL;
-       struct lttng_event *event = NULL;
-       enum lttng_loglevel_type loglevel_type;
-       int loglevel_value = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_log_level_rule *log_level_rule;
-
-       jul_logging = container_of(
-                       rule, const struct lttng_event_rule_jul_logging, parent);
-
-       local_event = zmalloc(sizeof(*local_event));
-       if (!local_event) {
-               goto error;
-       }
-
-       local_event->type = LTTNG_EVENT_TRACEPOINT;
-       ret = lttng_strncpy(local_event->name, jul_logging->pattern,
-                           sizeof(local_event->name));
-       if (ret) {
-               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
-                               jul_logging->pattern);
-               goto error;
-       }
-
-
-       /* Map the log level rule to an equivalent lttng_loglevel. */
-       status = lttng_event_rule_jul_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
-               loglevel_value = 0;
-       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule)) {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
-                       break;
-               default:
-                       abort();
-                       break;
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       goto error;
-               }
-       } else {
-               goto error;
-       }
-
-       local_event->loglevel_type = loglevel_type;
-       local_event->loglevel = loglevel_value;
-
-       event = local_event;
-       local_event = NULL;
-error:
-       free(local_event);
-       return event;
-}
-
-static enum lttng_error_code lttng_event_rule_jul_logging_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_JUL_LOGGING_EVENT_RULE(rule));
-
-       status = lttng_event_rule_jul_logging_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       status = lttng_event_rule_jul_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       /* Open event rule jul logging element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_jul_logging);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter expression. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Log level rule. */
-       if (log_level_rule) {
-               ret_code = lttng_log_level_rule_mi_serialize(
-                               log_level_rule, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close event rule jul logging element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_jul_logging_create(void)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_jul_logging *tp_rule;
-       enum lttng_event_rule_status status;
-
-       tp_rule = zmalloc(sizeof(struct lttng_event_rule_jul_logging));
-       if (!tp_rule) {
-               goto end;
-       }
-
-       rule = &tp_rule->parent;
-       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_JUL_LOGGING);
-       tp_rule->parent.validate = lttng_event_rule_jul_logging_validate;
-       tp_rule->parent.serialize = lttng_event_rule_jul_logging_serialize;
-       tp_rule->parent.equal = lttng_event_rule_jul_logging_is_equal;
-       tp_rule->parent.destroy = lttng_event_rule_jul_logging_destroy;
-       tp_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_jul_logging_generate_filter_bytecode;
-       tp_rule->parent.get_filter =
-                       lttng_event_rule_jul_logging_get_internal_filter;
-       tp_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_jul_logging_get_internal_filter_bytecode;
-       tp_rule->parent.generate_exclusions =
-                       lttng_event_rule_jul_logging_generate_exclusions;
-       tp_rule->parent.hash = lttng_event_rule_jul_logging_hash;
-       tp_rule->parent.generate_lttng_event =
-                       lttng_event_rule_jul_logging_generate_lttng_event;
-       tp_rule->parent.mi_serialize = lttng_event_rule_jul_logging_mi_serialize;
-
-       tp_rule->log_level_rule = NULL;
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_jul_logging_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_jul_logging_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_jul_logging_comm *jul_logging_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_log_level_rule *log_level_rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*jul_logging_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule jul_logging: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       jul_logging_comm = (typeof(jul_logging_comm)) current_buffer_view.data;
-
-       rule = lttng_event_rule_jul_logging_create();
-       if (!rule) {
-               ERR("Failed to create event rule jul_logging.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, jul_logging_comm->pattern_len);
-
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       jul_logging_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += jul_logging_comm->pattern_len;
-
-       if (!jul_logging_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       jul_logging_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                       filter_expression,
-                       jul_logging_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += jul_logging_comm->filter_expression_len;
-
-skip_filter_expression:
-       if (!jul_logging_comm->log_level_rule_len) {
-               goto skip_log_level_rule;
-       }
-
-       {
-               /* Map the log level rule. */
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               jul_logging_comm->log_level_rule_len);
-
-               ret = lttng_log_level_rule_create_from_payload(
-                               &current_payload_view, &log_level_rule);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-
-               LTTNG_ASSERT(ret == jul_logging_comm->log_level_rule_len);
-       }
-
-       /* Skip after the log level rule. */
-       offset += jul_logging_comm->log_level_rule_len;
-
-skip_log_level_rule:
-
-       status = lttng_event_rule_jul_logging_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule jul_logging pattern.");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_jul_logging_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule jul_logging pattern.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (log_level_rule) {
-               status = lttng_event_rule_jul_logging_set_log_level_rule(
-                               rule, log_level_rule);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule jul_logging log level rule.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_log_level_rule_destroy(log_level_rule);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Normalize the pattern. */
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(jul_logging->pattern);
-
-       jul_logging->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       if (!jul_logging->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = jul_logging->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               PERROR("Failed to copy filter expression");
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (jul_logging->filter_expression) {
-               free(jul_logging->filter_expression);
-       }
-
-       jul_logging->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       if (!jul_logging->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = jul_logging->filter_expression;
-end:
-       return status;
-}
-
-static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
-{
-       /*
-        * For both JUL and LOG4J custom log level are possible and can
-        * span the entire int32 range.
-        */
-       return true;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_set_log_level_rule(
-               struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule *log_level_rule)
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-       struct lttng_log_level_rule *copy = NULL;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-
-       if (!log_level_rule_valid(log_level_rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_log_level_rule_copy(log_level_rule);
-       if (copy == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (jul_logging->log_level_rule) {
-               lttng_log_level_rule_destroy(jul_logging->log_level_rule);
-       }
-
-       jul_logging->log_level_rule = copy;
-
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_jul_logging_get_log_level_rule(
-               const struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule **log_level_rule
-               )
-{
-       struct lttng_event_rule_jul_logging *jul_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       jul_logging = container_of(
-                       rule, struct lttng_event_rule_jul_logging, parent);
-       if (jul_logging->log_level_rule == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *log_level_rule = jul_logging->log_level_rule;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/jul-logging.cpp b/src/common/event-rule/jul-logging.cpp
new file mode 100644 (file)
index 0000000..77ae380
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/jul-logging-internal.h>
+#include <lttng/event.h>
+#include <lttng/log-level-rule.h>
+
+#define IS_JUL_LOGGING_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_JUL_LOGGING)
+
+static void lttng_event_rule_jul_logging_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+
+       lttng_log_level_rule_destroy(jul_logging->log_level_rule);
+       free(jul_logging->pattern);
+       free(jul_logging->filter_expression);
+       free(jul_logging->internal_filter.filter);
+       free(jul_logging->internal_filter.bytecode);
+       free(jul_logging);
+}
+
+static bool lttng_event_rule_jul_logging_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_jul_logging *jul_logging;
+
+       if (!rule) {
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+
+       /* Required field. */
+       if (!jul_logging->pattern) {
+               ERR("Invalid jul_logging event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_jul_logging_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len, header_offset;
+       size_t size_before_log_level_rule;
+       struct lttng_event_rule_jul_logging *jul_logging;
+       struct lttng_event_rule_jul_logging_comm jul_logging_comm;
+       struct lttng_event_rule_jul_logging_comm *header;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing jul_logging event rule.");
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+
+       pattern_len = strlen(jul_logging->pattern) + 1;
+
+       if (jul_logging->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(jul_logging->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       jul_logging_comm.pattern_len = pattern_len;
+       jul_logging_comm.filter_expression_len = filter_expression_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &jul_logging_comm,
+                       sizeof(jul_logging_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, jul_logging->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, jul_logging->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_log_level_rule = payload->buffer.size;
+
+       ret = lttng_log_level_rule_serialize(jul_logging->log_level_rule, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
+       header->log_level_rule_len =
+                       payload->buffer.size - size_before_log_level_rule;
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_jul_logging_is_equal(
+               const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_jul_logging *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_jul_logging, parent);
+       b = container_of(_b, struct lttng_event_rule_jul_logging, parent);
+
+       /* Quick checks. */
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check. */
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set; not the other. */
+               goto end;
+       }
+
+       if (!lttng_log_level_rule_is_equal(
+                               a->log_level_rule, b->log_level_rule)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+/*
+ * On success ret is 0;
+ *
+ * On error ret is negative.
+ *
+ * An event with NO loglevel and the name is * will return NULL.
+ */
+static int generate_agent_filter(
+               const struct lttng_event_rule *rule, char **_agent_filter)
+{
+       int err;
+       int ret = 0;
+       char *agent_filter = NULL;
+       const char *pattern;
+       const char *filter;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+       enum lttng_event_rule_status status;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(_agent_filter);
+
+       status = lttng_event_rule_jul_logging_get_name_pattern(rule, &pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+
+       /* Don't add filter for the '*' event. */
+       if (strcmp(pattern, "*") != 0) {
+               if (filter) {
+                       err = asprintf(&agent_filter,
+                                       "(%s) && (logger_name == \"%s\")",
+                                       filter, pattern);
+               } else {
+                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
+                                       pattern);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       status = lttng_event_rule_jul_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+               const char *op;
+               int level;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule))
+               {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &level);
+                       op = "==";
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &level);
+                       op = ">=";
+                       break;
+               default:
+                       abort();
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (filter || agent_filter) {
+                       char *new_filter;
+
+                       err = asprintf(&new_filter,
+                                       "(%s) && (int_loglevel %s %d)",
+                                       agent_filter ? agent_filter : filter,
+                                       op, level);
+                       if (agent_filter) {
+                               free(agent_filter);
+                       }
+                       agent_filter = new_filter;
+               } else {
+                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
+                                       level);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_agent_filter = agent_filter;
+       agent_filter = NULL;
+
+end:
+       free(agent_filter);
+       return ret;
+}
+
+static enum lttng_error_code
+lttng_event_rule_jul_logging_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+       char *agent_filter;
+
+       LTTNG_ASSERT(rule);
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+
+       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       ret = generate_agent_filter(rule, &agent_filter);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       jul_logging->internal_filter.filter = agent_filter;
+
+       if (jul_logging->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       jul_logging->internal_filter.filter, creds,
+                       &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       jul_logging->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_jul_logging_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+
+       LTTNG_ASSERT(rule);
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       return jul_logging->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_jul_logging_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+
+       LTTNG_ASSERT(rule);
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       return jul_logging->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_jul_logging_generate_exclusions(
+               const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **_exclusions)
+{
+       /* Unsupported. */
+       *_exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long lttng_event_rule_jul_logging_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_jul_logging *tp_rule =
+                       container_of(rule, typeof(*tp_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_JUL_LOGGING,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+       if (tp_rule->filter_expression) {
+               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+       }
+
+       if (tp_rule->log_level_rule) {
+               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
+       }
+
+       return hash;
+}
+
+static struct lttng_event *lttng_event_rule_jul_logging_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       int ret;
+       const struct lttng_event_rule_jul_logging *jul_logging;
+       struct lttng_event *local_event = NULL;
+       struct lttng_event *event = NULL;
+       enum lttng_loglevel_type loglevel_type;
+       int loglevel_value = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_log_level_rule *log_level_rule;
+
+       jul_logging = container_of(
+                       rule, const struct lttng_event_rule_jul_logging, parent);
+
+       local_event = (lttng_event *) zmalloc(sizeof(*local_event));
+       if (!local_event) {
+               goto error;
+       }
+
+       local_event->type = LTTNG_EVENT_TRACEPOINT;
+       ret = lttng_strncpy(local_event->name, jul_logging->pattern,
+                           sizeof(local_event->name));
+       if (ret) {
+               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
+                               jul_logging->pattern);
+               goto error;
+       }
+
+
+       /* Map the log level rule to an equivalent lttng_loglevel. */
+       status = lttng_event_rule_jul_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+               loglevel_value = 0;
+       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule)) {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
+                       break;
+               default:
+                       abort();
+                       break;
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       goto error;
+               }
+       } else {
+               goto error;
+       }
+
+       local_event->loglevel_type = loglevel_type;
+       local_event->loglevel = loglevel_value;
+
+       event = local_event;
+       local_event = NULL;
+error:
+       free(local_event);
+       return event;
+}
+
+static enum lttng_error_code lttng_event_rule_jul_logging_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_JUL_LOGGING_EVENT_RULE(rule));
+
+       status = lttng_event_rule_jul_logging_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_jul_logging_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       status = lttng_event_rule_jul_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       /* Open event rule jul logging element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_jul_logging);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter expression. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Log level rule. */
+       if (log_level_rule) {
+               ret_code = lttng_log_level_rule_mi_serialize(
+                               log_level_rule, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close event rule jul logging element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_jul_logging_create(void)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_jul_logging *tp_rule;
+       enum lttng_event_rule_status status;
+
+       tp_rule = (lttng_event_rule_jul_logging *) zmalloc(sizeof(struct lttng_event_rule_jul_logging));
+       if (!tp_rule) {
+               goto end;
+       }
+
+       rule = &tp_rule->parent;
+       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_JUL_LOGGING);
+       tp_rule->parent.validate = lttng_event_rule_jul_logging_validate;
+       tp_rule->parent.serialize = lttng_event_rule_jul_logging_serialize;
+       tp_rule->parent.equal = lttng_event_rule_jul_logging_is_equal;
+       tp_rule->parent.destroy = lttng_event_rule_jul_logging_destroy;
+       tp_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_jul_logging_generate_filter_bytecode;
+       tp_rule->parent.get_filter =
+                       lttng_event_rule_jul_logging_get_internal_filter;
+       tp_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_jul_logging_get_internal_filter_bytecode;
+       tp_rule->parent.generate_exclusions =
+                       lttng_event_rule_jul_logging_generate_exclusions;
+       tp_rule->parent.hash = lttng_event_rule_jul_logging_hash;
+       tp_rule->parent.generate_lttng_event =
+                       lttng_event_rule_jul_logging_generate_lttng_event;
+       tp_rule->parent.mi_serialize = lttng_event_rule_jul_logging_mi_serialize;
+
+       tp_rule->log_level_rule = NULL;
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_jul_logging_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_jul_logging_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_jul_logging_comm *jul_logging_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_log_level_rule *log_level_rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*jul_logging_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule jul_logging: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       jul_logging_comm = (typeof(jul_logging_comm)) current_buffer_view.data;
+
+       rule = lttng_event_rule_jul_logging_create();
+       if (!rule) {
+               ERR("Failed to create event rule jul_logging.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, jul_logging_comm->pattern_len);
+
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       jul_logging_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += jul_logging_comm->pattern_len;
+
+       if (!jul_logging_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       jul_logging_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                       filter_expression,
+                       jul_logging_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += jul_logging_comm->filter_expression_len;
+
+skip_filter_expression:
+       if (!jul_logging_comm->log_level_rule_len) {
+               goto skip_log_level_rule;
+       }
+
+       {
+               /* Map the log level rule. */
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               jul_logging_comm->log_level_rule_len);
+
+               ret = lttng_log_level_rule_create_from_payload(
+                               &current_payload_view, &log_level_rule);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+
+               LTTNG_ASSERT(ret == jul_logging_comm->log_level_rule_len);
+       }
+
+       /* Skip after the log level rule. */
+       offset += jul_logging_comm->log_level_rule_len;
+
+skip_log_level_rule:
+
+       status = lttng_event_rule_jul_logging_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule jul_logging pattern.");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_jul_logging_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule jul_logging pattern.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (log_level_rule) {
+               status = lttng_event_rule_jul_logging_set_log_level_rule(
+                               rule, log_level_rule);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule jul_logging log level rule.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_log_level_rule_destroy(log_level_rule);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Normalize the pattern. */
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(jul_logging->pattern);
+
+       jul_logging->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       if (!jul_logging->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = jul_logging->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               PERROR("Failed to copy filter expression");
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (jul_logging->filter_expression) {
+               free(jul_logging->filter_expression);
+       }
+
+       jul_logging->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       if (!jul_logging->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = jul_logging->filter_expression;
+end:
+       return status;
+}
+
+static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
+{
+       /*
+        * For both JUL and LOG4J custom log level are possible and can
+        * span the entire int32 range.
+        */
+       return true;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_set_log_level_rule(
+               struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule *log_level_rule)
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       struct lttng_log_level_rule *copy = NULL;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+
+       if (!log_level_rule_valid(log_level_rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_log_level_rule_copy(log_level_rule);
+       if (copy == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (jul_logging->log_level_rule) {
+               lttng_log_level_rule_destroy(jul_logging->log_level_rule);
+       }
+
+       jul_logging->log_level_rule = copy;
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_jul_logging_get_log_level_rule(
+               const struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule **log_level_rule
+               )
+{
+       struct lttng_event_rule_jul_logging *jul_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_JUL_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       jul_logging = container_of(
+                       rule, struct lttng_event_rule_jul_logging, parent);
+       if (jul_logging->log_level_rule == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *log_level_rule = jul_logging->log_level_rule;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/kernel-kprobe.c b/src/common/event-rule/kernel-kprobe.c
deleted file mode 100644 (file)
index 872d03a..0000000
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <ctype.h>
-#include <lttng/constant.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/event-rule.h>
-#include <lttng/event-rule/kernel-kprobe-internal.h>
-#include <lttng/kernel-probe-internal.h>
-#include <lttng/kernel-probe.h>
-#include <stdio.h>
-
-#define IS_KPROBE_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE)
-
-#if (LTTNG_SYMBOL_NAME_LEN == 256)
-#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
-#endif
-
-static void lttng_event_rule_kernel_kprobe_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-
-       lttng_kernel_probe_location_destroy(kprobe->location);
-       free(kprobe->name);
-       free(kprobe);
-}
-
-static bool lttng_event_rule_kernel_kprobe_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-
-       if (!rule) {
-               goto end;
-       }
-
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-
-       /* Required field. */
-       if (!kprobe->name) {
-               ERR("Invalid name event rule: a name must be set.");
-               goto end;
-       }
-
-       /* Required field. */
-       if(!kprobe->location) {
-               ERR("Invalid name event rule: a location must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_kernel_kprobe_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t name_len, header_offset, size_before_location;
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-       struct lttng_event_rule_kernel_kprobe_comm kprobe_comm;
-       struct lttng_event_rule_kernel_kprobe_comm *header;
-
-       if (!rule || !IS_KPROBE_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing kprobe event rule.");
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-
-       name_len = strlen(kprobe->name) + 1;
-       kprobe_comm.name_len = name_len;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &kprobe_comm, sizeof(kprobe_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, kprobe->name, name_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_location = payload->buffer.size;
-
-       ret = lttng_kernel_probe_location_serialize(kprobe->location, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       /* Update the header regarding the probe size. */
-       header = (struct lttng_event_rule_kernel_kprobe_comm*) (
-                       (char *) payload->buffer.data + header_offset);
-       header->location_len = payload->buffer.size - size_before_location;
-
-       ret = 0;
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_kernel_kprobe_is_equal(const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_kernel_kprobe *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_kernel_kprobe, parent);
-       b = container_of(_b, struct lttng_event_rule_kernel_kprobe, parent);
-
-       /* Quick checks */
-       if (!!a->name != !!b->name) {
-               goto end;
-       }
-
-       /* Long check */
-       LTTNG_ASSERT(a->name);
-       LTTNG_ASSERT(b->name);
-       if (strcmp(a->name, b->name)) {
-               goto end;
-       }
-
-       is_equal = lttng_kernel_probe_location_is_equal(
-                       a->location, b->location);
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_kprobe_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       /* Nothing to do. */
-       return LTTNG_OK;
-}
-
-static const char *lttng_event_rule_kernel_kprobe_get_filter(
-               const struct lttng_event_rule *rule)
-{
-       /* Not supported. */
-       return NULL;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_kernel_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
-{
-       /* Not supported. */
-       return NULL;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_kernel_kprobe_generate_exclusions(const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **exclusions)
-{
-       /* Not supported. */
-       *exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long
-lttng_event_rule_kernel_kprobe_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_kernel_kprobe *krule =
-                       container_of(rule, typeof(*krule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(krule->name, lttng_ht_seed);
-       hash ^= lttng_kernel_probe_location_hash(krule->location);
-
-       return hash;
-}
-
-static
-int kernel_probe_set_location(
-               struct lttng_event_rule_kernel_kprobe *kprobe,
-               const struct lttng_kernel_probe_location *location)
-{
-       int ret;
-       struct lttng_kernel_probe_location *location_copy = NULL;
-
-       if (!kprobe || !location || kprobe->location) {
-               ret = -1;
-               goto end;
-       }
-
-       location_copy = lttng_kernel_probe_location_copy(location);
-       if (!location_copy) {
-               ret = -1;
-               goto end;
-       }
-
-       kprobe->location = location_copy;
-       location_copy = NULL;
-       ret = 0;
-end:
-       lttng_kernel_probe_location_destroy(location_copy);
-       return ret;
-}
-
-static
-enum lttng_error_code lttng_event_rule_kernel_kprobe_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *event_name = NULL;
-       const struct lttng_kernel_probe_location *location = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_KPROBE_EVENT_RULE(rule));
-
-       status = lttng_event_rule_kernel_kprobe_get_event_name(
-                       rule, &event_name);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(event_name);
-
-       status = lttng_event_rule_kernel_kprobe_get_location(rule, &location);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(location);
-
-       /* Open event rule kernel kprobe element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_kernel_kprobe);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_event_name, event_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Probe location. */
-       ret_code = lttng_kernel_probe_location_mi_serialize(location, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close event rule kernel kprobe element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_kernel_kprobe_create(
-               const struct lttng_kernel_probe_location *location)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_kernel_kprobe *krule;
-
-       krule = zmalloc(sizeof(struct lttng_event_rule_kernel_kprobe));
-       if (!krule) {
-               goto end;
-       }
-
-       rule = &krule->parent;
-       lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE);
-       krule->parent.validate = lttng_event_rule_kernel_kprobe_validate;
-       krule->parent.serialize = lttng_event_rule_kernel_kprobe_serialize;
-       krule->parent.equal = lttng_event_rule_kernel_kprobe_is_equal;
-       krule->parent.destroy = lttng_event_rule_kernel_kprobe_destroy;
-       krule->parent.generate_filter_bytecode =
-                       lttng_event_rule_kernel_kprobe_generate_filter_bytecode;
-       krule->parent.get_filter = lttng_event_rule_kernel_kprobe_get_filter;
-       krule->parent.get_filter_bytecode =
-                       lttng_event_rule_kernel_kprobe_get_filter_bytecode;
-       krule->parent.generate_exclusions =
-                       lttng_event_rule_kernel_kprobe_generate_exclusions;
-       krule->parent.hash = lttng_event_rule_kernel_kprobe_hash;
-       krule->parent.mi_serialize = lttng_event_rule_kernel_kprobe_mi_serialize;
-
-       if (kernel_probe_set_location(krule, location)) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_kernel_kprobe_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_kernel_kprobe_comm *kprobe_comm;
-       const char *name;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_kernel_probe_location *location = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*kprobe_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       kprobe_comm = (typeof(kprobe_comm)) current_buffer_view.data;
-
-       /* Skip to payload */
-       offset += current_buffer_view.size;
-
-       {
-               /* Map the name. */
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               kprobe_comm->name_len);
-
-               if (!lttng_payload_view_is_valid(&current_payload_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               name = current_payload_view.buffer.data;
-               if (!lttng_buffer_view_contains_string(
-                               &current_payload_view.buffer, name,
-                               kprobe_comm->name_len)) {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       /* Skip after the name. */
-       offset += kprobe_comm->name_len;
-
-       /* Map the kernel probe location. */
-       {
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               kprobe_comm->location_len);
-
-               if (!lttng_payload_view_is_valid(&current_payload_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = lttng_kernel_probe_location_create_from_payload(
-                               &current_payload_view, &location);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (ret != kprobe_comm->location_len) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the location */
-       offset += kprobe_comm->location_len;
-
-       rule = lttng_event_rule_kernel_kprobe_create(location);
-       if (!rule) {
-               ERR("Failed to create event rule kprobe.");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_kernel_kprobe_set_event_name(rule, name);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule kprobe name.");
-               ret = -1;
-               goto end;
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_kernel_probe_location_destroy(location);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_get_location(
-               const struct lttng_event_rule *rule,
-               const struct lttng_kernel_probe_location **location)
-{
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-
-       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !location) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-       *location = kprobe->location;
-
-       if (!*location) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_set_event_name(
-               struct lttng_event_rule *rule, const char *name)
-{
-       char *name_copy = NULL;
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name ||
-                       strlen(name) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-       name_copy = strdup(name);
-       if (!name_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       free(kprobe->name);
-
-       kprobe->name = name_copy;
-       name_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_get_event_name(
-               const struct lttng_event_rule *rule, const char **name)
-{
-       struct lttng_event_rule_kernel_kprobe *kprobe;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
-       if (!kprobe->name) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *name = kprobe->name;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/kernel-kprobe.cpp b/src/common/event-rule/kernel-kprobe.cpp
new file mode 100644 (file)
index 0000000..d33d446
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <ctype.h>
+#include <lttng/constant.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/kernel-kprobe-internal.h>
+#include <lttng/kernel-probe-internal.h>
+#include <lttng/kernel-probe.h>
+#include <stdio.h>
+
+#define IS_KPROBE_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE)
+
+#if (LTTNG_SYMBOL_NAME_LEN == 256)
+#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255"
+#endif
+
+static void lttng_event_rule_kernel_kprobe_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+
+       lttng_kernel_probe_location_destroy(kprobe->location);
+       free(kprobe->name);
+       free(kprobe);
+}
+
+static bool lttng_event_rule_kernel_kprobe_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+
+       if (!rule) {
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+
+       /* Required field. */
+       if (!kprobe->name) {
+               ERR("Invalid name event rule: a name must be set.");
+               goto end;
+       }
+
+       /* Required field. */
+       if(!kprobe->location) {
+               ERR("Invalid name event rule: a location must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_kernel_kprobe_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t name_len, header_offset, size_before_location;
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+       struct lttng_event_rule_kernel_kprobe_comm kprobe_comm;
+       struct lttng_event_rule_kernel_kprobe_comm *header;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing kprobe event rule.");
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+
+       name_len = strlen(kprobe->name) + 1;
+       kprobe_comm.name_len = name_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &kprobe_comm, sizeof(kprobe_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, kprobe->name, name_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_location = payload->buffer.size;
+
+       ret = lttng_kernel_probe_location_serialize(kprobe->location, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       /* Update the header regarding the probe size. */
+       header = (struct lttng_event_rule_kernel_kprobe_comm*) (
+                       (char *) payload->buffer.data + header_offset);
+       header->location_len = payload->buffer.size - size_before_location;
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_kernel_kprobe_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_kernel_kprobe *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_kernel_kprobe, parent);
+       b = container_of(_b, struct lttng_event_rule_kernel_kprobe, parent);
+
+       /* Quick checks */
+       if (!!a->name != !!b->name) {
+               goto end;
+       }
+
+       /* Long check */
+       LTTNG_ASSERT(a->name);
+       LTTNG_ASSERT(b->name);
+       if (strcmp(a->name, b->name)) {
+               goto end;
+       }
+
+       is_equal = lttng_kernel_probe_location_is_equal(
+                       a->location, b->location);
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_kprobe_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       /* Nothing to do. */
+       return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kernel_kprobe_get_filter(
+               const struct lttng_event_rule *rule)
+{
+       /* Not supported. */
+       return NULL;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kernel_kprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+       /* Not supported. */
+       return NULL;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_kernel_kprobe_generate_exclusions(const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **exclusions)
+{
+       /* Not supported. */
+       *exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long
+lttng_event_rule_kernel_kprobe_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_kernel_kprobe *krule =
+                       container_of(rule, typeof(*krule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(krule->name, lttng_ht_seed);
+       hash ^= lttng_kernel_probe_location_hash(krule->location);
+
+       return hash;
+}
+
+static
+int kernel_probe_set_location(
+               struct lttng_event_rule_kernel_kprobe *kprobe,
+               const struct lttng_kernel_probe_location *location)
+{
+       int ret;
+       struct lttng_kernel_probe_location *location_copy = NULL;
+
+       if (!kprobe || !location || kprobe->location) {
+               ret = -1;
+               goto end;
+       }
+
+       location_copy = lttng_kernel_probe_location_copy(location);
+       if (!location_copy) {
+               ret = -1;
+               goto end;
+       }
+
+       kprobe->location = location_copy;
+       location_copy = NULL;
+       ret = 0;
+end:
+       lttng_kernel_probe_location_destroy(location_copy);
+       return ret;
+}
+
+static
+enum lttng_error_code lttng_event_rule_kernel_kprobe_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *event_name = NULL;
+       const struct lttng_kernel_probe_location *location = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_KPROBE_EVENT_RULE(rule));
+
+       status = lttng_event_rule_kernel_kprobe_get_event_name(
+                       rule, &event_name);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(event_name);
+
+       status = lttng_event_rule_kernel_kprobe_get_location(rule, &location);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(location);
+
+       /* Open event rule kernel kprobe element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_kernel_kprobe);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_event_name, event_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Probe location. */
+       ret_code = lttng_kernel_probe_location_mi_serialize(location, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close event rule kernel kprobe element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_kernel_kprobe_create(
+               const struct lttng_kernel_probe_location *location)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_kernel_kprobe *krule;
+
+       krule = (lttng_event_rule_kernel_kprobe *) zmalloc(sizeof(struct lttng_event_rule_kernel_kprobe));
+       if (!krule) {
+               goto end;
+       }
+
+       rule = &krule->parent;
+       lttng_event_rule_init(&krule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE);
+       krule->parent.validate = lttng_event_rule_kernel_kprobe_validate;
+       krule->parent.serialize = lttng_event_rule_kernel_kprobe_serialize;
+       krule->parent.equal = lttng_event_rule_kernel_kprobe_is_equal;
+       krule->parent.destroy = lttng_event_rule_kernel_kprobe_destroy;
+       krule->parent.generate_filter_bytecode =
+                       lttng_event_rule_kernel_kprobe_generate_filter_bytecode;
+       krule->parent.get_filter = lttng_event_rule_kernel_kprobe_get_filter;
+       krule->parent.get_filter_bytecode =
+                       lttng_event_rule_kernel_kprobe_get_filter_bytecode;
+       krule->parent.generate_exclusions =
+                       lttng_event_rule_kernel_kprobe_generate_exclusions;
+       krule->parent.hash = lttng_event_rule_kernel_kprobe_hash;
+       krule->parent.mi_serialize = lttng_event_rule_kernel_kprobe_mi_serialize;
+
+       if (kernel_probe_set_location(krule, location)) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_kernel_kprobe_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_kernel_kprobe_comm *kprobe_comm;
+       const char *name;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_kernel_probe_location *location = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*kprobe_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule kprobe: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       kprobe_comm = (typeof(kprobe_comm)) current_buffer_view.data;
+
+       /* Skip to payload */
+       offset += current_buffer_view.size;
+
+       {
+               /* Map the name. */
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               kprobe_comm->name_len);
+
+               if (!lttng_payload_view_is_valid(&current_payload_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               name = current_payload_view.buffer.data;
+               if (!lttng_buffer_view_contains_string(
+                               &current_payload_view.buffer, name,
+                               kprobe_comm->name_len)) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       /* Skip after the name. */
+       offset += kprobe_comm->name_len;
+
+       /* Map the kernel probe location. */
+       {
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               kprobe_comm->location_len);
+
+               if (!lttng_payload_view_is_valid(&current_payload_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = lttng_kernel_probe_location_create_from_payload(
+                               &current_payload_view, &location);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (ret != kprobe_comm->location_len) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the location */
+       offset += kprobe_comm->location_len;
+
+       rule = lttng_event_rule_kernel_kprobe_create(location);
+       if (!rule) {
+               ERR("Failed to create event rule kprobe.");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_kernel_kprobe_set_event_name(rule, name);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule kprobe name.");
+               ret = -1;
+               goto end;
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_kernel_probe_location_destroy(location);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_get_location(
+               const struct lttng_event_rule *rule,
+               const struct lttng_kernel_probe_location **location)
+{
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !location) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+       *location = kprobe->location;
+
+       if (!*location) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_set_event_name(
+               struct lttng_event_rule *rule, const char *name)
+{
+       char *name_copy = NULL;
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name ||
+                       strlen(name) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+       name_copy = strdup(name);
+       if (!name_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       free(kprobe->name);
+
+       kprobe->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_kprobe_get_event_name(
+               const struct lttng_event_rule *rule, const char **name)
+{
+       struct lttng_event_rule_kernel_kprobe *kprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KPROBE_EVENT_RULE(rule) || !name) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       kprobe = container_of(rule, struct lttng_event_rule_kernel_kprobe, parent);
+       if (!kprobe->name) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *name = kprobe->name;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/kernel-syscall.c b/src/common/event-rule/kernel-syscall.c
deleted file mode 100644 (file)
index 21aa1e2..0000000
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/kernel-syscall-internal.h>
-
-#define IS_SYSCALL_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL)
-
-static void lttng_event_rule_kernel_syscall_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_syscall *syscall;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       free(syscall->pattern);
-       free(syscall->filter_expression);
-       free(syscall->internal_filter.filter);
-       free(syscall->internal_filter.bytecode);
-       free(syscall);
-}
-
-static bool lttng_event_rule_kernel_syscall_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_kernel_syscall *syscall;
-
-       if (!rule) {
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       /* Required field. */
-       if (!syscall->pattern) {
-               ERR("Invalid syscall event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_kernel_syscall_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t pattern_len, filter_expression_len;
-       struct lttng_event_rule_kernel_syscall *syscall;
-       struct lttng_event_rule_kernel_syscall_comm syscall_comm;
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing syscall event rule");
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       pattern_len = strlen(syscall->pattern) + 1;
-
-       if (syscall->filter_expression != NULL) {
-               filter_expression_len = strlen(syscall->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       syscall_comm.pattern_len = pattern_len;
-       syscall_comm.filter_expression_len = filter_expression_len;
-       syscall_comm.emission_site = syscall->emission_site;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &syscall_comm, sizeof(syscall_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, syscall->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       syscall->filter_expression, filter_expression_len);
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_kernel_syscall_is_equal(const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_kernel_syscall *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_kernel_syscall, parent);
-       b = container_of(_b, struct lttng_event_rule_kernel_syscall, parent);
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set and not the other. */
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_syscall_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code = LTTNG_OK;
-       struct lttng_event_rule_kernel_syscall *syscall;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-
-       LTTNG_ASSERT(rule);
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       /* Generate the filter bytecode. */
-       status = lttng_event_rule_kernel_syscall_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter == NULL) {
-               /* Nothing to do. */
-               ret = LTTNG_OK;
-               goto end;
-       }
-
-       syscall->internal_filter.filter = strdup(filter);
-       if (syscall->internal_filter.filter == NULL) {
-               ret_code = LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       syscall->internal_filter.filter, creds, &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-       }
-
-       syscall->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_kernel_syscall_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_syscall *syscall;
-
-       LTTNG_ASSERT(rule);
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       return syscall->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_kernel_syscall_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_syscall *syscall;
-
-       LTTNG_ASSERT(rule);
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-
-       return syscall->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_kernel_syscall_generate_exclusions(const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **exclusions)
-{
-       /* Unsupported. */
-       *exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long
-lttng_event_rule_kernel_syscall_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_kernel_syscall *syscall_rule =
-                       container_of(rule, typeof(*syscall_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(syscall_rule->pattern, lttng_ht_seed);
-       if (syscall_rule->filter_expression) {
-               hash ^= hash_key_str(syscall_rule->filter_expression,
-                               lttng_ht_seed);
-       }
-
-       return hash;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_syscall_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-
-       enum lttng_event_rule_kernel_syscall_emission_site site_type;
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-       const char *site_type_str = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_SYSCALL_EVENT_RULE(rule));
-
-       status = lttng_event_rule_kernel_syscall_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_kernel_syscall_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       site_type = lttng_event_rule_kernel_syscall_get_emission_site(rule);
-
-       switch (site_type) {
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
-               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit;
-               break;
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
-               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_entry;
-               break;
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
-               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_exit;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       /* Open event rule kernel syscall element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_kernel_syscall);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Emission site. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_kernel_syscall_emission_site,
-                       site_type_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Close event rule kernel syscall. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_kernel_syscall_create(
-               enum lttng_event_rule_kernel_syscall_emission_site
-                               emission_site)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_kernel_syscall *syscall_rule;
-       enum lttng_event_rule_status status;
-
-       /* Validate the emission site type */
-       switch (emission_site) {
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
-               break;
-       default:
-               /* Invalid emission type */
-               goto end;
-       }
-
-       syscall_rule = zmalloc(sizeof(struct lttng_event_rule_kernel_syscall));
-       if (!syscall_rule) {
-               goto end;
-       }
-
-       rule = &syscall_rule->parent;
-       lttng_event_rule_init(
-                       &syscall_rule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL);
-       syscall_rule->parent.validate = lttng_event_rule_kernel_syscall_validate;
-       syscall_rule->parent.serialize = lttng_event_rule_kernel_syscall_serialize;
-       syscall_rule->parent.equal = lttng_event_rule_kernel_syscall_is_equal;
-       syscall_rule->parent.destroy = lttng_event_rule_kernel_syscall_destroy;
-       syscall_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_kernel_syscall_generate_filter_bytecode;
-       syscall_rule->parent.get_filter =
-                       lttng_event_rule_kernel_syscall_get_internal_filter;
-       syscall_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_kernel_syscall_get_internal_filter_bytecode;
-       syscall_rule->parent.generate_exclusions =
-                       lttng_event_rule_kernel_syscall_generate_exclusions;
-       syscall_rule->parent.hash = lttng_event_rule_kernel_syscall_hash;
-       syscall_rule->parent.mi_serialize = lttng_event_rule_kernel_syscall_mi_serialize;
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_kernel_syscall_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-       /* Emission site type */
-       syscall_rule->emission_site = emission_site;
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_kernel_syscall_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_kernel_syscall_comm *syscall_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       if (view->buffer.size < sizeof(*syscall_comm)) {
-               ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*syscall_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       syscall_comm = (typeof(syscall_comm)) current_buffer_view.data;
-       rule = lttng_event_rule_kernel_syscall_create(syscall_comm->emission_site);
-       if (!rule) {
-               ERR("Failed to create event rule syscall");
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, syscall_comm->pattern_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       syscall_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += syscall_comm->pattern_len;
-
-       if (!syscall_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       syscall_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                               filter_expression,
-                               syscall_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += syscall_comm->filter_expression_len;
-
-skip_filter_expression:
-
-       status = lttng_event_rule_kernel_syscall_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule syscall pattern");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_kernel_syscall_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule syscall pattern");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_syscall_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_kernel_syscall *syscall;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(syscall->pattern);
-
-       syscall->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_syscall_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_kernel_syscall *syscall;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-       if (!syscall->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = syscall->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_syscall_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_kernel_syscall *syscall;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       /* TODO: validate that the passed expression is valid. */
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (syscall->filter_expression) {
-               free(syscall->filter_expression);
-       }
-
-       syscall->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_syscall_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_kernel_syscall *syscall;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-       if (!syscall->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = syscall->filter_expression;
-end:
-       return status;
-}
-extern enum lttng_event_rule_kernel_syscall_emission_site
-lttng_event_rule_kernel_syscall_get_emission_site(
-               const struct lttng_event_rule *rule)
-{
-       enum lttng_event_rule_kernel_syscall_emission_site emission_site =
-               LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_UNKNOWN;
-       struct lttng_event_rule_kernel_syscall *syscall;
-
-       if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
-               goto end;
-       }
-
-       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
-       emission_site = syscall->emission_site;
-
-end:
-       return emission_site;
-}
-
-const char *lttng_event_rule_kernel_syscall_emission_site_str(
-               enum lttng_event_rule_kernel_syscall_emission_site emission_site)
-{
-       switch (emission_site) {
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
-               return "entry";
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
-               return "entry+exit";
-       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
-               return "exit";
-       default:
-               return "???";
-       }
-}
diff --git a/src/common/event-rule/kernel-syscall.cpp b/src/common/event-rule/kernel-syscall.cpp
new file mode 100644 (file)
index 0000000..3f8e64e
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kernel-syscall-internal.h>
+
+#define IS_SYSCALL_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL)
+
+static void lttng_event_rule_kernel_syscall_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_syscall *syscall;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       free(syscall->pattern);
+       free(syscall->filter_expression);
+       free(syscall->internal_filter.filter);
+       free(syscall->internal_filter.bytecode);
+       free(syscall);
+}
+
+static bool lttng_event_rule_kernel_syscall_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_kernel_syscall *syscall;
+
+       if (!rule) {
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       /* Required field. */
+       if (!syscall->pattern) {
+               ERR("Invalid syscall event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_kernel_syscall_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len;
+       struct lttng_event_rule_kernel_syscall *syscall;
+       struct lttng_event_rule_kernel_syscall_comm syscall_comm;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing syscall event rule");
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       pattern_len = strlen(syscall->pattern) + 1;
+
+       if (syscall->filter_expression != NULL) {
+               filter_expression_len = strlen(syscall->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       syscall_comm.pattern_len = pattern_len;
+       syscall_comm.filter_expression_len = filter_expression_len;
+       syscall_comm.emission_site = syscall->emission_site;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &syscall_comm, sizeof(syscall_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, syscall->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       syscall->filter_expression, filter_expression_len);
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_kernel_syscall_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_kernel_syscall *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_kernel_syscall, parent);
+       b = container_of(_b, struct lttng_event_rule_kernel_syscall, parent);
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set and not the other. */
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_syscall_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code = LTTNG_OK;
+       struct lttng_event_rule_kernel_syscall *syscall;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+
+       LTTNG_ASSERT(rule);
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       /* Generate the filter bytecode. */
+       status = lttng_event_rule_kernel_syscall_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter == NULL) {
+               /* Nothing to do. */
+               ret = LTTNG_OK;
+               goto end;
+       }
+
+       syscall->internal_filter.filter = strdup(filter);
+       if (syscall->internal_filter.filter == NULL) {
+               ret_code = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       syscall->internal_filter.filter, creds, &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+       }
+
+       syscall->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_kernel_syscall_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_syscall *syscall;
+
+       LTTNG_ASSERT(rule);
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       return syscall->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kernel_syscall_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_syscall *syscall;
+
+       LTTNG_ASSERT(rule);
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+
+       return syscall->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_kernel_syscall_generate_exclusions(const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **exclusions)
+{
+       /* Unsupported. */
+       *exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long
+lttng_event_rule_kernel_syscall_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_kernel_syscall *syscall_rule =
+                       container_of(rule, typeof(*syscall_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(syscall_rule->pattern, lttng_ht_seed);
+       if (syscall_rule->filter_expression) {
+               hash ^= hash_key_str(syscall_rule->filter_expression,
+                               lttng_ht_seed);
+       }
+
+       return hash;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_syscall_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+
+       enum lttng_event_rule_kernel_syscall_emission_site site_type;
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+       const char *site_type_str = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_SYSCALL_EVENT_RULE(rule));
+
+       status = lttng_event_rule_kernel_syscall_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_kernel_syscall_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       site_type = lttng_event_rule_kernel_syscall_get_emission_site(rule);
+
+       switch (site_type) {
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
+               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit;
+               break;
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
+               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_entry;
+               break;
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
+               site_type_str = mi_lttng_event_rule_kernel_syscall_emission_site_exit;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       /* Open event rule kernel syscall element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_kernel_syscall);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Emission site. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_kernel_syscall_emission_site,
+                       site_type_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Close event rule kernel syscall. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_kernel_syscall_create(
+               enum lttng_event_rule_kernel_syscall_emission_site
+                               emission_site)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_kernel_syscall *syscall_rule;
+       enum lttng_event_rule_status status;
+
+       /* Validate the emission site type */
+       switch (emission_site) {
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
+               break;
+       default:
+               /* Invalid emission type */
+               goto end;
+       }
+
+       syscall_rule = (lttng_event_rule_kernel_syscall *) zmalloc(sizeof(struct lttng_event_rule_kernel_syscall));
+       if (!syscall_rule) {
+               goto end;
+       }
+
+       rule = &syscall_rule->parent;
+       lttng_event_rule_init(
+                       &syscall_rule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL);
+       syscall_rule->parent.validate = lttng_event_rule_kernel_syscall_validate;
+       syscall_rule->parent.serialize = lttng_event_rule_kernel_syscall_serialize;
+       syscall_rule->parent.equal = lttng_event_rule_kernel_syscall_is_equal;
+       syscall_rule->parent.destroy = lttng_event_rule_kernel_syscall_destroy;
+       syscall_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_kernel_syscall_generate_filter_bytecode;
+       syscall_rule->parent.get_filter =
+                       lttng_event_rule_kernel_syscall_get_internal_filter;
+       syscall_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_kernel_syscall_get_internal_filter_bytecode;
+       syscall_rule->parent.generate_exclusions =
+                       lttng_event_rule_kernel_syscall_generate_exclusions;
+       syscall_rule->parent.hash = lttng_event_rule_kernel_syscall_hash;
+       syscall_rule->parent.mi_serialize = lttng_event_rule_kernel_syscall_mi_serialize;
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_kernel_syscall_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+       /* Emission site type */
+       syscall_rule->emission_site = emission_site;
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_kernel_syscall_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_kernel_syscall_comm *syscall_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->buffer.size < sizeof(*syscall_comm)) {
+               ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*syscall_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       syscall_comm = (typeof(syscall_comm)) current_buffer_view.data;
+       rule = lttng_event_rule_kernel_syscall_create((lttng_event_rule_kernel_syscall_emission_site) syscall_comm->emission_site);
+       if (!rule) {
+               ERR("Failed to create event rule syscall");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, syscall_comm->pattern_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       syscall_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += syscall_comm->pattern_len;
+
+       if (!syscall_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       syscall_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                               filter_expression,
+                               syscall_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += syscall_comm->filter_expression_len;
+
+skip_filter_expression:
+
+       status = lttng_event_rule_kernel_syscall_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule syscall pattern");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_kernel_syscall_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule syscall pattern");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_syscall_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_kernel_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(syscall->pattern);
+
+       syscall->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_syscall_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_kernel_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+       if (!syscall->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = syscall->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_syscall_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_kernel_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       /* TODO: validate that the passed expression is valid. */
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (syscall->filter_expression) {
+               free(syscall->filter_expression);
+       }
+
+       syscall->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_syscall_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_kernel_syscall *syscall;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+       if (!syscall->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = syscall->filter_expression;
+end:
+       return status;
+}
+extern enum lttng_event_rule_kernel_syscall_emission_site
+lttng_event_rule_kernel_syscall_get_emission_site(
+               const struct lttng_event_rule *rule)
+{
+       enum lttng_event_rule_kernel_syscall_emission_site emission_site =
+               LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_UNKNOWN;
+       struct lttng_event_rule_kernel_syscall *syscall;
+
+       if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) {
+               goto end;
+       }
+
+       syscall = container_of(rule, struct lttng_event_rule_kernel_syscall, parent);
+       emission_site = syscall->emission_site;
+
+end:
+       return emission_site;
+}
+
+const char *lttng_event_rule_kernel_syscall_emission_site_str(
+               enum lttng_event_rule_kernel_syscall_emission_site emission_site)
+{
+       switch (emission_site) {
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY:
+               return "entry";
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_ENTRY_EXIT:
+               return "entry+exit";
+       case LTTNG_EVENT_RULE_KERNEL_SYSCALL_EMISSION_SITE_EXIT:
+               return "exit";
+       default:
+               return "???";
+       }
+}
diff --git a/src/common/event-rule/kernel-tracepoint.c b/src/common/event-rule/kernel-tracepoint.c
deleted file mode 100644 (file)
index a1f5f62..0000000
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/kernel-tracepoint-internal.h>
-#include <lttng/event.h>
-
-#define IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT)
-
-static void lttng_event_rule_kernel_tracepoint_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-
-       free(tracepoint->pattern);
-       free(tracepoint->filter_expression);
-       free(tracepoint->internal_filter.filter);
-       free(tracepoint->internal_filter.bytecode);
-       free(tracepoint);
-}
-
-static bool lttng_event_rule_kernel_tracepoint_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-
-       if (!rule) {
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-
-       /* Required field. */
-       if (!tracepoint->pattern) {
-               ERR("Invalid kernel tracepoint event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_kernel_tracepoint_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t pattern_len, filter_expression_len;
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       struct lttng_event_rule_kernel_tracepoint_comm tracepoint_comm;
-
-       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Serializing kernel tracepoint event rule.");
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-
-       pattern_len = strlen(tracepoint->pattern) + 1;
-
-       if (tracepoint->filter_expression != NULL) {
-               filter_expression_len =
-                               strlen(tracepoint->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       tracepoint_comm.pattern_len = pattern_len;
-       tracepoint_comm.filter_expression_len = filter_expression_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &tracepoint_comm,
-                       sizeof(tracepoint_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, tracepoint->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, tracepoint->filter_expression,
-                       filter_expression_len);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_kernel_tracepoint_is_equal(
-               const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_kernel_tracepoint *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_kernel_tracepoint, parent);
-       b = container_of(_b, struct lttng_event_rule_kernel_tracepoint, parent);
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       /* Long check. */
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set; not the other. */
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code
-lttng_event_rule_kernel_tracepoint_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-
-       LTTNG_ASSERT(rule);
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-
-       status = lttng_event_rule_kernel_tracepoint_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       if (filter) {
-               tracepoint->internal_filter.filter = strdup(filter);
-               if (tracepoint->internal_filter.filter == NULL) {
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto error;
-               }
-       } else {
-               tracepoint->internal_filter.filter = NULL;
-       }
-
-       if (tracepoint->internal_filter.filter == NULL) {
-               ret_code = LTTNG_OK;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       tracepoint->internal_filter.filter, creds,
-                       &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       tracepoint->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-       ret_code = LTTNG_OK;
-
-error:
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_kernel_tracepoint_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-
-       LTTNG_ASSERT(rule);
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       return tracepoint->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_kernel_tracepoint_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-
-       LTTNG_ASSERT(rule);
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       return tracepoint->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_kernel_tracepoint_generate_exclusions(
-               const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **_exclusions)
-{
-       /* Unsupported. */
-       *_exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long lttng_event_rule_kernel_tracepoint_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_kernel_tracepoint *tp_rule =
-                       container_of(rule, typeof(*tp_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
-
-       if (tp_rule->filter_expression) {
-               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
-       }
-
-       return hash;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_tracepoint_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_KERNEL_TRACEPOINT_EVENT_RULE(rule));
-
-       status = lttng_event_rule_kernel_tracepoint_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_kernel_tracepoint_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       /* Open event rule kernel tracepoint element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_kernel_tracepoint);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Close event rule kernel tracepoint element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_kernel_tracepoint_create(void)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_kernel_tracepoint *tp_rule;
-       enum lttng_event_rule_status status;
-
-       tp_rule = zmalloc(sizeof(struct lttng_event_rule_kernel_tracepoint));
-       if (!tp_rule) {
-               goto end;
-       }
-
-       rule = &tp_rule->parent;
-       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT);
-       tp_rule->parent.validate = lttng_event_rule_kernel_tracepoint_validate;
-       tp_rule->parent.serialize = lttng_event_rule_kernel_tracepoint_serialize;
-       tp_rule->parent.equal = lttng_event_rule_kernel_tracepoint_is_equal;
-       tp_rule->parent.destroy = lttng_event_rule_kernel_tracepoint_destroy;
-       tp_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_kernel_tracepoint_generate_filter_bytecode;
-       tp_rule->parent.get_filter =
-                       lttng_event_rule_kernel_tracepoint_get_internal_filter;
-       tp_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_kernel_tracepoint_get_internal_filter_bytecode;
-       tp_rule->parent.generate_exclusions =
-                       lttng_event_rule_kernel_tracepoint_generate_exclusions;
-       tp_rule->parent.hash = lttng_event_rule_kernel_tracepoint_hash;
-       tp_rule->parent.mi_serialize = lttng_event_rule_kernel_tracepoint_mi_serialize;
-
-       /* Not necessary for now. */
-       tp_rule->parent.generate_lttng_event = NULL;
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_kernel_tracepoint_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_kernel_tracepoint_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_kernel_tracepoint_comm *tracepoint_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*tracepoint_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule kernel tracepoint: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       tracepoint_comm = (typeof(tracepoint_comm)) current_buffer_view.data;
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, tracepoint_comm->pattern_len);
-
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       tracepoint_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += tracepoint_comm->pattern_len;
-
-       if (!tracepoint_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       tracepoint_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                       filter_expression,
-                       tracepoint_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += tracepoint_comm->filter_expression_len;
-
-skip_filter_expression:
-
-       rule = lttng_event_rule_kernel_tracepoint_create();
-       if (!rule) {
-               ERR("Failed to create event rule kernel tracepoint.");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_kernel_tracepoint_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule kernel tracepoint pattern.");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_kernel_tracepoint_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule kernel tracepoint pattern.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Normalize the pattern. */
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(tracepoint->pattern);
-
-       tracepoint->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       if (!tracepoint->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = tracepoint->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               PERROR("Failed to copy filter expression");
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (tracepoint->filter_expression) {
-               free(tracepoint->filter_expression);
-       }
-
-       tracepoint->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_kernel_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
-       if (!tracepoint->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = tracepoint->filter_expression;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/kernel-tracepoint.cpp b/src/common/event-rule/kernel-tracepoint.cpp
new file mode 100644 (file)
index 0000000..cff3048
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kernel-tracepoint-internal.h>
+#include <lttng/event.h>
+
+#define IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT)
+
+static void lttng_event_rule_kernel_tracepoint_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+
+       free(tracepoint->pattern);
+       free(tracepoint->filter_expression);
+       free(tracepoint->internal_filter.filter);
+       free(tracepoint->internal_filter.bytecode);
+       free(tracepoint);
+}
+
+static bool lttng_event_rule_kernel_tracepoint_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+
+       if (!rule) {
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+
+       /* Required field. */
+       if (!tracepoint->pattern) {
+               ERR("Invalid kernel tracepoint event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_kernel_tracepoint_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len;
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       struct lttng_event_rule_kernel_tracepoint_comm tracepoint_comm;
+
+       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Serializing kernel tracepoint event rule.");
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+
+       pattern_len = strlen(tracepoint->pattern) + 1;
+
+       if (tracepoint->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(tracepoint->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       tracepoint_comm.pattern_len = pattern_len;
+       tracepoint_comm.filter_expression_len = filter_expression_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &tracepoint_comm,
+                       sizeof(tracepoint_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, tracepoint->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, tracepoint->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_kernel_tracepoint_is_equal(
+               const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_kernel_tracepoint *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_kernel_tracepoint, parent);
+       b = container_of(_b, struct lttng_event_rule_kernel_tracepoint, parent);
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check. */
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set; not the other. */
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code
+lttng_event_rule_kernel_tracepoint_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+
+       LTTNG_ASSERT(rule);
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+
+       status = lttng_event_rule_kernel_tracepoint_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       if (filter) {
+               tracepoint->internal_filter.filter = strdup(filter);
+               if (tracepoint->internal_filter.filter == NULL) {
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       } else {
+               tracepoint->internal_filter.filter = NULL;
+       }
+
+       if (tracepoint->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       tracepoint->internal_filter.filter, creds,
+                       &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       tracepoint->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_kernel_tracepoint_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+
+       LTTNG_ASSERT(rule);
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       return tracepoint->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kernel_tracepoint_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+
+       LTTNG_ASSERT(rule);
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       return tracepoint->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_kernel_tracepoint_generate_exclusions(
+               const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **_exclusions)
+{
+       /* Unsupported. */
+       *_exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long lttng_event_rule_kernel_tracepoint_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_kernel_tracepoint *tp_rule =
+                       container_of(rule, typeof(*tp_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+       if (tp_rule->filter_expression) {
+               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+       }
+
+       return hash;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_tracepoint_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_KERNEL_TRACEPOINT_EVENT_RULE(rule));
+
+       status = lttng_event_rule_kernel_tracepoint_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_kernel_tracepoint_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       /* Open event rule kernel tracepoint element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_kernel_tracepoint);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Close event rule kernel tracepoint element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_kernel_tracepoint_create(void)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_kernel_tracepoint *tp_rule;
+       enum lttng_event_rule_status status;
+
+       tp_rule = (lttng_event_rule_kernel_tracepoint *) zmalloc(sizeof(struct lttng_event_rule_kernel_tracepoint));
+       if (!tp_rule) {
+               goto end;
+       }
+
+       rule = &tp_rule->parent;
+       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT);
+       tp_rule->parent.validate = lttng_event_rule_kernel_tracepoint_validate;
+       tp_rule->parent.serialize = lttng_event_rule_kernel_tracepoint_serialize;
+       tp_rule->parent.equal = lttng_event_rule_kernel_tracepoint_is_equal;
+       tp_rule->parent.destroy = lttng_event_rule_kernel_tracepoint_destroy;
+       tp_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_kernel_tracepoint_generate_filter_bytecode;
+       tp_rule->parent.get_filter =
+                       lttng_event_rule_kernel_tracepoint_get_internal_filter;
+       tp_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_kernel_tracepoint_get_internal_filter_bytecode;
+       tp_rule->parent.generate_exclusions =
+                       lttng_event_rule_kernel_tracepoint_generate_exclusions;
+       tp_rule->parent.hash = lttng_event_rule_kernel_tracepoint_hash;
+       tp_rule->parent.mi_serialize = lttng_event_rule_kernel_tracepoint_mi_serialize;
+
+       /* Not necessary for now. */
+       tp_rule->parent.generate_lttng_event = NULL;
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_kernel_tracepoint_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_kernel_tracepoint_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_kernel_tracepoint_comm *tracepoint_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*tracepoint_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule kernel tracepoint: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       tracepoint_comm = (typeof(tracepoint_comm)) current_buffer_view.data;
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, tracepoint_comm->pattern_len);
+
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       tracepoint_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += tracepoint_comm->pattern_len;
+
+       if (!tracepoint_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       tracepoint_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                       filter_expression,
+                       tracepoint_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += tracepoint_comm->filter_expression_len;
+
+skip_filter_expression:
+
+       rule = lttng_event_rule_kernel_tracepoint_create();
+       if (!rule) {
+               ERR("Failed to create event rule kernel tracepoint.");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_kernel_tracepoint_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule kernel tracepoint pattern.");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_kernel_tracepoint_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule kernel tracepoint pattern.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Normalize the pattern. */
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(tracepoint->pattern);
+
+       tracepoint->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       if (!tracepoint->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = tracepoint->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               PERROR("Failed to copy filter expression");
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (tracepoint->filter_expression) {
+               free(tracepoint->filter_expression);
+       }
+
+       tracepoint->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_tracepoint_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_kernel_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_KERNEL_TRACEPOINT_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_kernel_tracepoint, parent);
+       if (!tracepoint->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = tracepoint->filter_expression;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/kernel-uprobe.c b/src/common/event-rule/kernel-uprobe.c
deleted file mode 100644 (file)
index 805f2bc..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/kernel-uprobe-internal.h>
-#include <lttng/userspace-probe-internal.h>
-
-#define IS_UPROBE_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE)
-
-static void lttng_event_rule_kernel_uprobe_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-
-       lttng_userspace_probe_location_destroy(uprobe->location);
-       free(uprobe->name);
-       free(uprobe);
-}
-
-static bool lttng_event_rule_kernel_uprobe_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-
-       if (!rule) {
-               goto end;
-       }
-
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-
-       /* Required field. */
-       if (!uprobe->name) {
-               ERR("Invalid uprobe event rule: a pattern must be set.");
-               goto end;
-       }
-
-       if (!uprobe->location) {
-               ERR("Invalid uprobe event rule: a location must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_kernel_uprobe_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t name_len, header_offset, size_before_probe;
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-       struct lttng_event_rule_kernel_uprobe_comm uprobe_comm = {};
-       struct lttng_event_rule_kernel_uprobe_comm *header;
-
-       if (!rule || !IS_UPROBE_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing uprobe event rule.");
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-
-       name_len = strlen(uprobe->name) + 1;
-
-       uprobe_comm.name_len = name_len;
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &uprobe_comm, sizeof(uprobe_comm));
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, uprobe->name, name_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_probe = payload->buffer.size;
-
-       /* This serialize return the size taken in the buffer. */
-       ret = lttng_userspace_probe_location_serialize(
-                       uprobe->location, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       /* Update the header regarding the probe size. */
-       header = (struct lttng_event_rule_kernel_uprobe_comm
-                                       *) ((char *) payload->buffer.data +
-                       header_offset);
-       header->location_len = payload->buffer.size - size_before_probe;
-
-       ret = 0;
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_kernel_uprobe_is_equal(const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_kernel_uprobe *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_kernel_uprobe, parent);
-       b = container_of(_b, struct lttng_event_rule_kernel_uprobe, parent);
-
-       /* uprobe is invalid if this is not true. */
-       LTTNG_ASSERT(a->name);
-       LTTNG_ASSERT(b->name);
-       if (strcmp(a->name, b->name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->location);
-       LTTNG_ASSERT(b->location);
-       is_equal = lttng_userspace_probe_location_is_equal(
-                       a->location, b->location);
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_uprobe_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       /* Nothing to do. */
-       return LTTNG_OK;
-}
-
-static const char *lttng_event_rule_kernel_uprobe_get_filter(
-               const struct lttng_event_rule *rule)
-{
-       /* Unsupported. */
-       return NULL;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_kernel_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
-{
-       /* Unsupported. */
-       return NULL;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_kernel_uprobe_generate_exclusions(const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **exclusions)
-{
-       /* Unsupported. */
-       *exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long
-lttng_event_rule_kernel_uprobe_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_kernel_uprobe *urule =
-                       container_of(rule, typeof(*urule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(urule->name, lttng_ht_seed);
-       hash ^= lttng_userspace_probe_location_hash(urule->location);
-
-       return hash;
-}
-
-static
-int userspace_probe_set_location(
-               struct lttng_event_rule_kernel_uprobe *uprobe,
-               const struct lttng_userspace_probe_location *location)
-{
-       int ret;
-       struct lttng_userspace_probe_location *location_copy = NULL;
-
-       if (!uprobe || !location || uprobe->location) {
-               ret = -1;
-               goto end;
-       }
-
-       location_copy = lttng_userspace_probe_location_copy(location);
-       if (!location_copy) {
-               ret = -1;
-               goto end;
-       }
-
-       uprobe->location = location_copy;
-       location_copy = NULL;
-       ret = 0;
-end:
-       lttng_userspace_probe_location_destroy(location_copy);
-       return ret;
-}
-
-static enum lttng_error_code lttng_event_rule_kernel_uprobe_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *event_name = NULL;
-       const struct lttng_userspace_probe_location *location = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_UPROBE_EVENT_RULE(rule));
-
-       status = lttng_event_rule_kernel_uprobe_get_event_name(
-                       rule, &event_name);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(event_name);
-
-       status = lttng_event_rule_kernel_uprobe_get_location(rule, &location);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(location);
-
-       /* Open event rule kernel uprobe element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_kernel_uprobe);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Event name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_event_name, event_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Probe location. */
-       ret_code = lttng_userspace_probe_location_mi_serialize(location, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close event rule kernel uprobe element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_kernel_uprobe_create(
-               const struct lttng_userspace_probe_location *location)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_kernel_uprobe *urule;
-
-       urule = zmalloc(sizeof(struct lttng_event_rule_kernel_uprobe));
-       if (!urule) {
-               goto end;
-       }
-
-       rule = &urule->parent;
-       lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE);
-       urule->parent.validate = lttng_event_rule_kernel_uprobe_validate;
-       urule->parent.serialize = lttng_event_rule_kernel_uprobe_serialize;
-       urule->parent.equal = lttng_event_rule_kernel_uprobe_is_equal;
-       urule->parent.destroy = lttng_event_rule_kernel_uprobe_destroy;
-       urule->parent.generate_filter_bytecode =
-                       lttng_event_rule_kernel_uprobe_generate_filter_bytecode;
-       urule->parent.get_filter = lttng_event_rule_kernel_uprobe_get_filter;
-       urule->parent.get_filter_bytecode =
-                       lttng_event_rule_kernel_uprobe_get_filter_bytecode;
-       urule->parent.generate_exclusions =
-                       lttng_event_rule_kernel_uprobe_generate_exclusions;
-       urule->parent.hash = lttng_event_rule_kernel_uprobe_hash;
-       urule->parent.mi_serialize = lttng_event_rule_kernel_uprobe_mi_serialize;
-
-       if (userspace_probe_set_location(urule, location)) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_kernel_uprobe_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       const struct lttng_event_rule_kernel_uprobe_comm *uprobe_comm;
-       const char *name;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_userspace_probe_location *location = NULL;
-       enum lttng_event_rule_status status;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*uprobe_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header");
-               ret = -1;
-               goto end;
-       }
-
-       uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data;
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the name. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, uprobe_comm->name_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       name = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, name,
-                       uprobe_comm->name_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the name. */
-       offset += uprobe_comm->name_len;
-
-       /* Map the location. */
-       {
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               uprobe_comm->location_len);
-
-               if (!lttng_payload_view_is_valid(&current_payload_view)) {
-                       ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain location");
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = lttng_userspace_probe_location_create_from_payload(
-                               &current_payload_view, &location);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       LTTNG_ASSERT(ret == uprobe_comm->location_len);
-
-       /* Skip after the location. */
-       offset += uprobe_comm->location_len;
-
-       rule = lttng_event_rule_kernel_uprobe_create(location);
-       if (!rule) {
-               ERR("Failed to create event rule uprobe.");
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_kernel_uprobe_set_event_name(rule, name);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_event_rule_kernel_uprobe_validate(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_userspace_probe_location_destroy(location);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-
-enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_location(
-               const struct lttng_event_rule *rule,
-               const struct lttng_userspace_probe_location **location)
-{
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       *location = lttng_event_rule_kernel_uprobe_get_location_mutable(rule);
-       if (!*location) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-struct lttng_userspace_probe_location *
-lttng_event_rule_kernel_uprobe_get_location_mutable(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-
-       LTTNG_ASSERT(rule);
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-
-       return uprobe->location;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_set_event_name(
-               struct lttng_event_rule *rule, const char *name)
-{
-       char *name_copy = NULL;
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name ||
-                       strlen(name) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-       name_copy = strdup(name);
-       if (!name_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (uprobe->name) {
-               free(uprobe->name);
-       }
-
-       uprobe->name = name_copy;
-       name_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_event_name(
-               const struct lttng_event_rule *rule, const char **name)
-{
-       struct lttng_event_rule_kernel_uprobe *uprobe;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
-       if (!uprobe->name) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *name = uprobe->name;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/kernel-uprobe.cpp b/src/common/event-rule/kernel-uprobe.cpp
new file mode 100644 (file)
index 0000000..d53958d
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/kernel-uprobe-internal.h>
+#include <lttng/userspace-probe-internal.h>
+
+#define IS_UPROBE_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE)
+
+static void lttng_event_rule_kernel_uprobe_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+
+       lttng_userspace_probe_location_destroy(uprobe->location);
+       free(uprobe->name);
+       free(uprobe);
+}
+
+static bool lttng_event_rule_kernel_uprobe_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+
+       if (!rule) {
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+
+       /* Required field. */
+       if (!uprobe->name) {
+               ERR("Invalid uprobe event rule: a pattern must be set.");
+               goto end;
+       }
+
+       if (!uprobe->location) {
+               ERR("Invalid uprobe event rule: a location must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_kernel_uprobe_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t name_len, header_offset, size_before_probe;
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+       struct lttng_event_rule_kernel_uprobe_comm uprobe_comm = {};
+       struct lttng_event_rule_kernel_uprobe_comm *header;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing uprobe event rule.");
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+
+       name_len = strlen(uprobe->name) + 1;
+
+       uprobe_comm.name_len = name_len;
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &uprobe_comm, sizeof(uprobe_comm));
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, uprobe->name, name_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_probe = payload->buffer.size;
+
+       /* This serialize return the size taken in the buffer. */
+       ret = lttng_userspace_probe_location_serialize(
+                       uprobe->location, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       /* Update the header regarding the probe size. */
+       header = (struct lttng_event_rule_kernel_uprobe_comm
+                                       *) ((char *) payload->buffer.data +
+                       header_offset);
+       header->location_len = payload->buffer.size - size_before_probe;
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_kernel_uprobe_is_equal(const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_kernel_uprobe *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_kernel_uprobe, parent);
+       b = container_of(_b, struct lttng_event_rule_kernel_uprobe, parent);
+
+       /* uprobe is invalid if this is not true. */
+       LTTNG_ASSERT(a->name);
+       LTTNG_ASSERT(b->name);
+       if (strcmp(a->name, b->name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->location);
+       LTTNG_ASSERT(b->location);
+       is_equal = lttng_userspace_probe_location_is_equal(
+                       a->location, b->location);
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_uprobe_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       /* Nothing to do. */
+       return LTTNG_OK;
+}
+
+static const char *lttng_event_rule_kernel_uprobe_get_filter(
+               const struct lttng_event_rule *rule)
+{
+       /* Unsupported. */
+       return NULL;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_kernel_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule)
+{
+       /* Unsupported. */
+       return NULL;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_kernel_uprobe_generate_exclusions(const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **exclusions)
+{
+       /* Unsupported. */
+       *exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long
+lttng_event_rule_kernel_uprobe_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_kernel_uprobe *urule =
+                       container_of(rule, typeof(*urule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(urule->name, lttng_ht_seed);
+       hash ^= lttng_userspace_probe_location_hash(urule->location);
+
+       return hash;
+}
+
+static
+int userspace_probe_set_location(
+               struct lttng_event_rule_kernel_uprobe *uprobe,
+               const struct lttng_userspace_probe_location *location)
+{
+       int ret;
+       struct lttng_userspace_probe_location *location_copy = NULL;
+
+       if (!uprobe || !location || uprobe->location) {
+               ret = -1;
+               goto end;
+       }
+
+       location_copy = lttng_userspace_probe_location_copy(location);
+       if (!location_copy) {
+               ret = -1;
+               goto end;
+       }
+
+       uprobe->location = location_copy;
+       location_copy = NULL;
+       ret = 0;
+end:
+       lttng_userspace_probe_location_destroy(location_copy);
+       return ret;
+}
+
+static enum lttng_error_code lttng_event_rule_kernel_uprobe_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *event_name = NULL;
+       const struct lttng_userspace_probe_location *location = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_UPROBE_EVENT_RULE(rule));
+
+       status = lttng_event_rule_kernel_uprobe_get_event_name(
+                       rule, &event_name);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(event_name);
+
+       status = lttng_event_rule_kernel_uprobe_get_location(rule, &location);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(location);
+
+       /* Open event rule kernel uprobe element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_kernel_uprobe);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Event name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_event_name, event_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Probe location. */
+       ret_code = lttng_userspace_probe_location_mi_serialize(location, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close event rule kernel uprobe element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_kernel_uprobe_create(
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_kernel_uprobe *urule;
+
+       urule = (lttng_event_rule_kernel_uprobe *) zmalloc(sizeof(struct lttng_event_rule_kernel_uprobe));
+       if (!urule) {
+               goto end;
+       }
+
+       rule = &urule->parent;
+       lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE);
+       urule->parent.validate = lttng_event_rule_kernel_uprobe_validate;
+       urule->parent.serialize = lttng_event_rule_kernel_uprobe_serialize;
+       urule->parent.equal = lttng_event_rule_kernel_uprobe_is_equal;
+       urule->parent.destroy = lttng_event_rule_kernel_uprobe_destroy;
+       urule->parent.generate_filter_bytecode =
+                       lttng_event_rule_kernel_uprobe_generate_filter_bytecode;
+       urule->parent.get_filter = lttng_event_rule_kernel_uprobe_get_filter;
+       urule->parent.get_filter_bytecode =
+                       lttng_event_rule_kernel_uprobe_get_filter_bytecode;
+       urule->parent.generate_exclusions =
+                       lttng_event_rule_kernel_uprobe_generate_exclusions;
+       urule->parent.hash = lttng_event_rule_kernel_uprobe_hash;
+       urule->parent.mi_serialize = lttng_event_rule_kernel_uprobe_mi_serialize;
+
+       if (userspace_probe_set_location(urule, location)) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_kernel_uprobe_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       const struct lttng_event_rule_kernel_uprobe_comm *uprobe_comm;
+       const char *name;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_userspace_probe_location *location = NULL;
+       enum lttng_event_rule_status status;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*uprobe_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header");
+               ret = -1;
+               goto end;
+       }
+
+       uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data;
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the name. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, uprobe_comm->name_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       name = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, name,
+                       uprobe_comm->name_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the name. */
+       offset += uprobe_comm->name_len;
+
+       /* Map the location. */
+       {
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               uprobe_comm->location_len);
+
+               if (!lttng_payload_view_is_valid(&current_payload_view)) {
+                       ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain location");
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = lttng_userspace_probe_location_create_from_payload(
+                               &current_payload_view, &location);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       LTTNG_ASSERT(ret == uprobe_comm->location_len);
+
+       /* Skip after the location. */
+       offset += uprobe_comm->location_len;
+
+       rule = lttng_event_rule_kernel_uprobe_create(location);
+       if (!rule) {
+               ERR("Failed to create event rule uprobe.");
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_kernel_uprobe_set_event_name(rule, name);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_event_rule_kernel_uprobe_validate(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_userspace_probe_location_destroy(location);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+
+enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_location(
+               const struct lttng_event_rule *rule,
+               const struct lttng_userspace_probe_location **location)
+{
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       *location = lttng_event_rule_kernel_uprobe_get_location_mutable(rule);
+       if (!*location) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+struct lttng_userspace_probe_location *
+lttng_event_rule_kernel_uprobe_get_location_mutable(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+
+       LTTNG_ASSERT(rule);
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+
+       return uprobe->location;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_set_event_name(
+               struct lttng_event_rule *rule, const char *name)
+{
+       char *name_copy = NULL;
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name ||
+                       strlen(name) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+       name_copy = strdup(name);
+       if (!name_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (uprobe->name) {
+               free(uprobe->name);
+       }
+
+       uprobe->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_event_name(
+               const struct lttng_event_rule *rule, const char **name)
+{
+       struct lttng_event_rule_kernel_uprobe *uprobe;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent);
+       if (!uprobe->name) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *name = uprobe->name;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/log4j-logging.c b/src/common/event-rule/log4j-logging.c
deleted file mode 100644 (file)
index 6fd0552..0000000
+++ /dev/null
@@ -1,921 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/log4j-logging-internal.h>
-#include <lttng/event.h>
-#include <lttng/log-level-rule.h>
-
-#define IS_LOG4J_LOGGING_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING)
-
-static void lttng_event_rule_log4j_logging_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-
-       lttng_log_level_rule_destroy(log4j_logging->log_level_rule);
-       free(log4j_logging->pattern);
-       free(log4j_logging->filter_expression);
-       free(log4j_logging->internal_filter.filter);
-       free(log4j_logging->internal_filter.bytecode);
-       free(log4j_logging);
-}
-
-static bool lttng_event_rule_log4j_logging_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-
-       if (!rule) {
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-
-       /* Required field. */
-       if (!log4j_logging->pattern) {
-               ERR("Invalid log4j_logging event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_log4j_logging_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t pattern_len, filter_expression_len, header_offset;
-       size_t size_before_log_level_rule;
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       struct lttng_event_rule_log4j_logging_comm log4j_logging_comm;
-       struct lttng_event_rule_log4j_logging_comm *header;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing log4j_logging event rule.");
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-
-       pattern_len = strlen(log4j_logging->pattern) + 1;
-
-       if (log4j_logging->filter_expression != NULL) {
-               filter_expression_len =
-                               strlen(log4j_logging->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       log4j_logging_comm.pattern_len = pattern_len;
-       log4j_logging_comm.filter_expression_len = filter_expression_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &log4j_logging_comm,
-                       sizeof(log4j_logging_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, log4j_logging->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, log4j_logging->filter_expression,
-                       filter_expression_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_log_level_rule = payload->buffer.size;
-
-       ret = lttng_log_level_rule_serialize(log4j_logging->log_level_rule, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
-       header->log_level_rule_len =
-                       payload->buffer.size - size_before_log_level_rule;
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_log4j_logging_is_equal(
-               const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_log4j_logging *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_log4j_logging, parent);
-       b = container_of(_b, struct lttng_event_rule_log4j_logging, parent);
-
-       /* Quick checks. */
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       /* Long check. */
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set; not the other. */
-               goto end;
-       }
-
-       if (!lttng_log_level_rule_is_equal(
-                               a->log_level_rule, b->log_level_rule)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-/*
- * On success ret is 0;
- *
- * On error ret is negative.
- *
- * An event with NO loglevel and the name is * will return NULL.
- */
-static int generate_agent_filter(
-               const struct lttng_event_rule *rule, char **_agent_filter)
-{
-       int err;
-       int ret = 0;
-       char *agent_filter = NULL;
-       const char *pattern;
-       const char *filter;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-       enum lttng_event_rule_status status;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(_agent_filter);
-
-       status = lttng_event_rule_log4j_logging_get_name_pattern(rule, &pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-
-       /* Don't add filter for the '*' event. */
-       if (strcmp(pattern, "*") != 0) {
-               if (filter) {
-                       err = asprintf(&agent_filter,
-                                       "(%s) && (logger_name == \"%s\")",
-                                       filter, pattern);
-               } else {
-                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
-                                       pattern);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       status = lttng_event_rule_log4j_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-               const char *op;
-               int level;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule))
-               {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &level);
-                       op = "==";
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &level);
-                       op = ">=";
-                       break;
-               default:
-                       abort();
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               if (filter || agent_filter) {
-                       char *new_filter;
-
-                       err = asprintf(&new_filter,
-                                       "(%s) && (int_loglevel %s %d)",
-                                       agent_filter ? agent_filter : filter,
-                                       op, level);
-                       if (agent_filter) {
-                               free(agent_filter);
-                       }
-                       agent_filter = new_filter;
-               } else {
-                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
-                                       level);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_agent_filter = agent_filter;
-       agent_filter = NULL;
-
-end:
-       free(agent_filter);
-       return ret;
-}
-
-static enum lttng_error_code
-lttng_event_rule_log4j_logging_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-       char *agent_filter;
-
-       LTTNG_ASSERT(rule);
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-
-       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       ret = generate_agent_filter(rule, &agent_filter);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       log4j_logging->internal_filter.filter = agent_filter;
-
-       if (log4j_logging->internal_filter.filter == NULL) {
-               ret_code = LTTNG_OK;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       log4j_logging->internal_filter.filter, creds,
-                       &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       log4j_logging->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-       ret_code = LTTNG_OK;
-
-error:
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_log4j_logging_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-
-       LTTNG_ASSERT(rule);
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       return log4j_logging->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_log4j_logging_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-
-       LTTNG_ASSERT(rule);
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       return log4j_logging->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_log4j_logging_generate_exclusions(
-               const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **_exclusions)
-{
-       /* Unsupported. */
-       *_exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long lttng_event_rule_log4j_logging_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_log4j_logging *tp_rule =
-                       container_of(rule, typeof(*tp_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
-
-       if (tp_rule->filter_expression) {
-               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
-       }
-
-       if (tp_rule->log_level_rule) {
-               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
-       }
-
-       return hash;
-}
-
-static struct lttng_event *lttng_event_rule_log4j_logging_generate_lttng_event(
-               const struct lttng_event_rule *rule)
-{
-       int ret;
-       const struct lttng_event_rule_log4j_logging *log4j_logging;
-       struct lttng_event *local_event = NULL;
-       struct lttng_event *event = NULL;
-       enum lttng_loglevel_type loglevel_type;
-       int loglevel_value = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_log_level_rule *log_level_rule;
-
-       log4j_logging = container_of(
-                       rule, const struct lttng_event_rule_log4j_logging, parent);
-
-       local_event = zmalloc(sizeof(*local_event));
-       if (!local_event) {
-               goto error;
-       }
-
-       local_event->type = LTTNG_EVENT_TRACEPOINT;
-       ret = lttng_strncpy(local_event->name, log4j_logging->pattern,
-                           sizeof(local_event->name));
-       if (ret) {
-               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
-                               log4j_logging->pattern);
-               goto error;
-       }
-
-
-       /* Map the log level rule to an equivalent lttng_loglevel. */
-       status = lttng_event_rule_log4j_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
-               loglevel_value = 0;
-       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule)) {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
-                       break;
-               default:
-                       abort();
-                       break;
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       goto error;
-               }
-       } else {
-               goto error;
-       }
-
-       local_event->loglevel_type = loglevel_type;
-       local_event->loglevel = loglevel_value;
-
-       event = local_event;
-       local_event = NULL;
-error:
-       free(local_event);
-       return event;
-}
-
-static enum lttng_error_code lttng_event_rule_log4j_logging_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_LOG4J_LOGGING_EVENT_RULE(rule));
-
-       status = lttng_event_rule_log4j_logging_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       status = lttng_event_rule_log4j_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       /* Open event rule log4j logging element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_log4j_logging);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter expression. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Log level rule. */
-       if (log_level_rule) {
-               ret_code = lttng_log_level_rule_mi_serialize(
-                               log_level_rule, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close event rule log4j logging element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_log4j_logging_create(void)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_log4j_logging *tp_rule;
-       enum lttng_event_rule_status status;
-
-       tp_rule = zmalloc(sizeof(struct lttng_event_rule_log4j_logging));
-       if (!tp_rule) {
-               goto end;
-       }
-
-       rule = &tp_rule->parent;
-       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING);
-       tp_rule->parent.validate = lttng_event_rule_log4j_logging_validate;
-       tp_rule->parent.serialize = lttng_event_rule_log4j_logging_serialize;
-       tp_rule->parent.equal = lttng_event_rule_log4j_logging_is_equal;
-       tp_rule->parent.destroy = lttng_event_rule_log4j_logging_destroy;
-       tp_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_log4j_logging_generate_filter_bytecode;
-       tp_rule->parent.get_filter =
-                       lttng_event_rule_log4j_logging_get_internal_filter;
-       tp_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_log4j_logging_get_internal_filter_bytecode;
-       tp_rule->parent.generate_exclusions =
-                       lttng_event_rule_log4j_logging_generate_exclusions;
-       tp_rule->parent.hash = lttng_event_rule_log4j_logging_hash;
-       tp_rule->parent.generate_lttng_event =
-                       lttng_event_rule_log4j_logging_generate_lttng_event;
-       tp_rule->parent.mi_serialize = lttng_event_rule_log4j_logging_mi_serialize;
-
-       tp_rule->log_level_rule = NULL;
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_log4j_logging_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_log4j_logging_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_log4j_logging_comm *log4j_logging_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_log_level_rule *log_level_rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*log4j_logging_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule log4j_logging: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       log4j_logging_comm = (typeof(log4j_logging_comm)) current_buffer_view.data;
-
-       rule = lttng_event_rule_log4j_logging_create();
-       if (!rule) {
-               ERR("Failed to create event rule log4j_logging.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, log4j_logging_comm->pattern_len);
-
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       log4j_logging_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += log4j_logging_comm->pattern_len;
-
-       if (!log4j_logging_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       log4j_logging_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                       filter_expression,
-                       log4j_logging_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += log4j_logging_comm->filter_expression_len;
-
-skip_filter_expression:
-       if (!log4j_logging_comm->log_level_rule_len) {
-               goto skip_log_level_rule;
-       }
-
-       {
-               /* Map the log level rule. */
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               log4j_logging_comm->log_level_rule_len);
-
-               ret = lttng_log_level_rule_create_from_payload(
-                               &current_payload_view, &log_level_rule);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-
-               LTTNG_ASSERT(ret == log4j_logging_comm->log_level_rule_len);
-       }
-
-       /* Skip after the log level rule. */
-       offset += log4j_logging_comm->log_level_rule_len;
-
-skip_log_level_rule:
-
-       status = lttng_event_rule_log4j_logging_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule log4j_logging pattern.");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_log4j_logging_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule log4j_logging pattern.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (log_level_rule) {
-               status = lttng_event_rule_log4j_logging_set_log_level_rule(
-                               rule, log_level_rule);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule log4j_logging log level rule.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_log_level_rule_destroy(log_level_rule);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Normalize the pattern. */
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(log4j_logging->pattern);
-
-       log4j_logging->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       if (!log4j_logging->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = log4j_logging->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               PERROR("Failed to copy filter expression");
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (log4j_logging->filter_expression) {
-               free(log4j_logging->filter_expression);
-       }
-
-       log4j_logging->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       if (!log4j_logging->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = log4j_logging->filter_expression;
-end:
-       return status;
-}
-
-static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
-{
-       /*
-        * For both LOG4J custom log level are possible and can
-        * span the entire int32 range.
-        */
-       return true;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_log_level_rule(
-               struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule *log_level_rule)
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-       struct lttng_log_level_rule *copy = NULL;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-
-       if (!log_level_rule_valid(log_level_rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_log_level_rule_copy(log_level_rule);
-       if (copy == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (log4j_logging->log_level_rule) {
-               lttng_log_level_rule_destroy(log4j_logging->log_level_rule);
-       }
-
-       log4j_logging->log_level_rule = copy;
-
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_log_level_rule(
-               const struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule **log_level_rule
-               )
-{
-       struct lttng_event_rule_log4j_logging *log4j_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       log4j_logging = container_of(
-                       rule, struct lttng_event_rule_log4j_logging, parent);
-       if (log4j_logging->log_level_rule == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *log_level_rule = log4j_logging->log_level_rule;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/log4j-logging.cpp b/src/common/event-rule/log4j-logging.cpp
new file mode 100644 (file)
index 0000000..25f2c4c
--- /dev/null
@@ -0,0 +1,921 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/log4j-logging-internal.h>
+#include <lttng/event.h>
+#include <lttng/log-level-rule.h>
+
+#define IS_LOG4J_LOGGING_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING)
+
+static void lttng_event_rule_log4j_logging_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+
+       lttng_log_level_rule_destroy(log4j_logging->log_level_rule);
+       free(log4j_logging->pattern);
+       free(log4j_logging->filter_expression);
+       free(log4j_logging->internal_filter.filter);
+       free(log4j_logging->internal_filter.bytecode);
+       free(log4j_logging);
+}
+
+static bool lttng_event_rule_log4j_logging_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+
+       if (!rule) {
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+
+       /* Required field. */
+       if (!log4j_logging->pattern) {
+               ERR("Invalid log4j_logging event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_log4j_logging_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len, header_offset;
+       size_t size_before_log_level_rule;
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       struct lttng_event_rule_log4j_logging_comm log4j_logging_comm;
+       struct lttng_event_rule_log4j_logging_comm *header;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing log4j_logging event rule.");
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+
+       pattern_len = strlen(log4j_logging->pattern) + 1;
+
+       if (log4j_logging->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(log4j_logging->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       log4j_logging_comm.pattern_len = pattern_len;
+       log4j_logging_comm.filter_expression_len = filter_expression_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &log4j_logging_comm,
+                       sizeof(log4j_logging_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, log4j_logging->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, log4j_logging->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_log_level_rule = payload->buffer.size;
+
+       ret = lttng_log_level_rule_serialize(log4j_logging->log_level_rule, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
+       header->log_level_rule_len =
+                       payload->buffer.size - size_before_log_level_rule;
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_log4j_logging_is_equal(
+               const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_log4j_logging *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_log4j_logging, parent);
+       b = container_of(_b, struct lttng_event_rule_log4j_logging, parent);
+
+       /* Quick checks. */
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check. */
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set; not the other. */
+               goto end;
+       }
+
+       if (!lttng_log_level_rule_is_equal(
+                               a->log_level_rule, b->log_level_rule)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+/*
+ * On success ret is 0;
+ *
+ * On error ret is negative.
+ *
+ * An event with NO loglevel and the name is * will return NULL.
+ */
+static int generate_agent_filter(
+               const struct lttng_event_rule *rule, char **_agent_filter)
+{
+       int err;
+       int ret = 0;
+       char *agent_filter = NULL;
+       const char *pattern;
+       const char *filter;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+       enum lttng_event_rule_status status;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(_agent_filter);
+
+       status = lttng_event_rule_log4j_logging_get_name_pattern(rule, &pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+
+       /* Don't add filter for the '*' event. */
+       if (strcmp(pattern, "*") != 0) {
+               if (filter) {
+                       err = asprintf(&agent_filter,
+                                       "(%s) && (logger_name == \"%s\")",
+                                       filter, pattern);
+               } else {
+                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
+                                       pattern);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       status = lttng_event_rule_log4j_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+               const char *op;
+               int level;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule))
+               {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &level);
+                       op = "==";
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &level);
+                       op = ">=";
+                       break;
+               default:
+                       abort();
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (filter || agent_filter) {
+                       char *new_filter;
+
+                       err = asprintf(&new_filter,
+                                       "(%s) && (int_loglevel %s %d)",
+                                       agent_filter ? agent_filter : filter,
+                                       op, level);
+                       if (agent_filter) {
+                               free(agent_filter);
+                       }
+                       agent_filter = new_filter;
+               } else {
+                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
+                                       level);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_agent_filter = agent_filter;
+       agent_filter = NULL;
+
+end:
+       free(agent_filter);
+       return ret;
+}
+
+static enum lttng_error_code
+lttng_event_rule_log4j_logging_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+       char *agent_filter;
+
+       LTTNG_ASSERT(rule);
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+
+       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       ret = generate_agent_filter(rule, &agent_filter);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       log4j_logging->internal_filter.filter = agent_filter;
+
+       if (log4j_logging->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       log4j_logging->internal_filter.filter, creds,
+                       &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       log4j_logging->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_log4j_logging_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+
+       LTTNG_ASSERT(rule);
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       return log4j_logging->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_log4j_logging_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+
+       LTTNG_ASSERT(rule);
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       return log4j_logging->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_log4j_logging_generate_exclusions(
+               const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **_exclusions)
+{
+       /* Unsupported. */
+       *_exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long lttng_event_rule_log4j_logging_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_log4j_logging *tp_rule =
+                       container_of(rule, typeof(*tp_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+       if (tp_rule->filter_expression) {
+               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+       }
+
+       if (tp_rule->log_level_rule) {
+               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
+       }
+
+       return hash;
+}
+
+static struct lttng_event *lttng_event_rule_log4j_logging_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       int ret;
+       const struct lttng_event_rule_log4j_logging *log4j_logging;
+       struct lttng_event *local_event = NULL;
+       struct lttng_event *event = NULL;
+       enum lttng_loglevel_type loglevel_type;
+       int loglevel_value = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_log_level_rule *log_level_rule;
+
+       log4j_logging = container_of(
+                       rule, const struct lttng_event_rule_log4j_logging, parent);
+
+       local_event = (lttng_event *) zmalloc(sizeof(*local_event));
+       if (!local_event) {
+               goto error;
+       }
+
+       local_event->type = LTTNG_EVENT_TRACEPOINT;
+       ret = lttng_strncpy(local_event->name, log4j_logging->pattern,
+                           sizeof(local_event->name));
+       if (ret) {
+               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
+                               log4j_logging->pattern);
+               goto error;
+       }
+
+
+       /* Map the log level rule to an equivalent lttng_loglevel. */
+       status = lttng_event_rule_log4j_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+               loglevel_value = 0;
+       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule)) {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
+                       break;
+               default:
+                       abort();
+                       break;
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       goto error;
+               }
+       } else {
+               goto error;
+       }
+
+       local_event->loglevel_type = loglevel_type;
+       local_event->loglevel = loglevel_value;
+
+       event = local_event;
+       local_event = NULL;
+error:
+       free(local_event);
+       return event;
+}
+
+static enum lttng_error_code lttng_event_rule_log4j_logging_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_LOG4J_LOGGING_EVENT_RULE(rule));
+
+       status = lttng_event_rule_log4j_logging_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_log4j_logging_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       status = lttng_event_rule_log4j_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       /* Open event rule log4j logging element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_log4j_logging);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter expression. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Log level rule. */
+       if (log_level_rule) {
+               ret_code = lttng_log_level_rule_mi_serialize(
+                               log_level_rule, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close event rule log4j logging element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_log4j_logging_create(void)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_log4j_logging *tp_rule;
+       enum lttng_event_rule_status status;
+
+       tp_rule = (lttng_event_rule_log4j_logging *) zmalloc(sizeof(struct lttng_event_rule_log4j_logging));
+       if (!tp_rule) {
+               goto end;
+       }
+
+       rule = &tp_rule->parent;
+       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING);
+       tp_rule->parent.validate = lttng_event_rule_log4j_logging_validate;
+       tp_rule->parent.serialize = lttng_event_rule_log4j_logging_serialize;
+       tp_rule->parent.equal = lttng_event_rule_log4j_logging_is_equal;
+       tp_rule->parent.destroy = lttng_event_rule_log4j_logging_destroy;
+       tp_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_log4j_logging_generate_filter_bytecode;
+       tp_rule->parent.get_filter =
+                       lttng_event_rule_log4j_logging_get_internal_filter;
+       tp_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_log4j_logging_get_internal_filter_bytecode;
+       tp_rule->parent.generate_exclusions =
+                       lttng_event_rule_log4j_logging_generate_exclusions;
+       tp_rule->parent.hash = lttng_event_rule_log4j_logging_hash;
+       tp_rule->parent.generate_lttng_event =
+                       lttng_event_rule_log4j_logging_generate_lttng_event;
+       tp_rule->parent.mi_serialize = lttng_event_rule_log4j_logging_mi_serialize;
+
+       tp_rule->log_level_rule = NULL;
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_log4j_logging_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_log4j_logging_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_log4j_logging_comm *log4j_logging_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_log_level_rule *log_level_rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*log4j_logging_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule log4j_logging: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       log4j_logging_comm = (typeof(log4j_logging_comm)) current_buffer_view.data;
+
+       rule = lttng_event_rule_log4j_logging_create();
+       if (!rule) {
+               ERR("Failed to create event rule log4j_logging.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, log4j_logging_comm->pattern_len);
+
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       log4j_logging_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += log4j_logging_comm->pattern_len;
+
+       if (!log4j_logging_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       log4j_logging_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                       filter_expression,
+                       log4j_logging_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += log4j_logging_comm->filter_expression_len;
+
+skip_filter_expression:
+       if (!log4j_logging_comm->log_level_rule_len) {
+               goto skip_log_level_rule;
+       }
+
+       {
+               /* Map the log level rule. */
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               log4j_logging_comm->log_level_rule_len);
+
+               ret = lttng_log_level_rule_create_from_payload(
+                               &current_payload_view, &log_level_rule);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+
+               LTTNG_ASSERT(ret == log4j_logging_comm->log_level_rule_len);
+       }
+
+       /* Skip after the log level rule. */
+       offset += log4j_logging_comm->log_level_rule_len;
+
+skip_log_level_rule:
+
+       status = lttng_event_rule_log4j_logging_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule log4j_logging pattern.");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_log4j_logging_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule log4j_logging pattern.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (log_level_rule) {
+               status = lttng_event_rule_log4j_logging_set_log_level_rule(
+                               rule, log_level_rule);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule log4j_logging log level rule.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_log_level_rule_destroy(log_level_rule);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Normalize the pattern. */
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(log4j_logging->pattern);
+
+       log4j_logging->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       if (!log4j_logging->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = log4j_logging->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               PERROR("Failed to copy filter expression");
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (log4j_logging->filter_expression) {
+               free(log4j_logging->filter_expression);
+       }
+
+       log4j_logging->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       if (!log4j_logging->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = log4j_logging->filter_expression;
+end:
+       return status;
+}
+
+static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
+{
+       /*
+        * For both LOG4J custom log level are possible and can
+        * span the entire int32 range.
+        */
+       return true;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_set_log_level_rule(
+               struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule *log_level_rule)
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       struct lttng_log_level_rule *copy = NULL;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+
+       if (!log_level_rule_valid(log_level_rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_log_level_rule_copy(log_level_rule);
+       if (copy == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (log4j_logging->log_level_rule) {
+               lttng_log_level_rule_destroy(log4j_logging->log_level_rule);
+       }
+
+       log4j_logging->log_level_rule = copy;
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_log4j_logging_get_log_level_rule(
+               const struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule **log_level_rule
+               )
+{
+       struct lttng_event_rule_log4j_logging *log4j_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_LOG4J_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       log4j_logging = container_of(
+                       rule, struct lttng_event_rule_log4j_logging, parent);
+       if (log4j_logging->log_level_rule == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *log_level_rule = log4j_logging->log_level_rule;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/python-logging.c b/src/common/event-rule/python-logging.c
deleted file mode 100644 (file)
index 4ec00c7..0000000
+++ /dev/null
@@ -1,923 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/python-logging-internal.h>
-#include <lttng/event.h>
-#include <lttng/log-level-rule.h>
-
-#define IS_PYTHON_LOGGING_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING)
-
-static void lttng_event_rule_python_logging_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-
-       lttng_log_level_rule_destroy(python_logging->log_level_rule);
-       free(python_logging->pattern);
-       free(python_logging->filter_expression);
-       free(python_logging->internal_filter.filter);
-       free(python_logging->internal_filter.bytecode);
-       free(python_logging);
-}
-
-static bool lttng_event_rule_python_logging_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_python_logging *python_logging;
-
-       if (!rule) {
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-
-       /* Required field. */
-       if (!python_logging->pattern) {
-               ERR("Invalid python_logging event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_python_logging_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t pattern_len, filter_expression_len, header_offset;
-       size_t size_before_log_level_rule;
-       struct lttng_event_rule_python_logging *python_logging;
-       struct lttng_event_rule_python_logging_comm python_logging_comm;
-       struct lttng_event_rule_python_logging_comm *header;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing python_logging event rule.");
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-
-       pattern_len = strlen(python_logging->pattern) + 1;
-
-       if (python_logging->filter_expression != NULL) {
-               filter_expression_len =
-                               strlen(python_logging->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       python_logging_comm.pattern_len = pattern_len;
-       python_logging_comm.filter_expression_len = filter_expression_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &python_logging_comm,
-                       sizeof(python_logging_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, python_logging->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, python_logging->filter_expression,
-                       filter_expression_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_log_level_rule = payload->buffer.size;
-
-       ret = lttng_log_level_rule_serialize(python_logging->log_level_rule, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
-       header->log_level_rule_len =
-                       payload->buffer.size - size_before_log_level_rule;
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_python_logging_is_equal(
-               const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       bool is_equal = false;
-       struct lttng_event_rule_python_logging *a, *b;
-
-       a = container_of(_a, struct lttng_event_rule_python_logging, parent);
-       b = container_of(_b, struct lttng_event_rule_python_logging, parent);
-
-       /* Quick checks. */
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       /* Long check. */
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set; not the other. */
-               goto end;
-       }
-
-       if (!lttng_log_level_rule_is_equal(
-                               a->log_level_rule, b->log_level_rule)) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-/*
- * On success ret is 0;
- *
- * On error ret is negative.
- *
- * An event with NO loglevel and the name is * will return NULL.
- */
-static int generate_agent_filter(
-               const struct lttng_event_rule *rule, char **_agent_filter)
-{
-       int err;
-       int ret = 0;
-       char *agent_filter = NULL;
-       const char *pattern;
-       const char *filter;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-       enum lttng_event_rule_status status;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(_agent_filter);
-
-       status = lttng_event_rule_python_logging_get_name_pattern(rule, &pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-
-       /* Don't add filter for the '*' event. */
-       if (strcmp(pattern, "*") != 0) {
-               if (filter) {
-                       err = asprintf(&agent_filter,
-                                       "(%s) && (logger_name == \"%s\")",
-                                       filter, pattern);
-               } else {
-                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
-                                       pattern);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       status = lttng_event_rule_python_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-               const char *op;
-               int level;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule))
-               {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &level);
-                       op = "==";
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &level);
-                       op = ">=";
-                       break;
-               default:
-                       abort();
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               if (filter || agent_filter) {
-                       char *new_filter;
-
-                       err = asprintf(&new_filter,
-                                       "(%s) && (int_loglevel %s %d)",
-                                       agent_filter ? agent_filter : filter,
-                                       op, level);
-                       if (agent_filter) {
-                               free(agent_filter);
-                       }
-                       agent_filter = new_filter;
-               } else {
-                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
-                                       level);
-               }
-
-               if (err < 0) {
-                       PERROR("Failed to format agent filter string");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_agent_filter = agent_filter;
-       agent_filter = NULL;
-
-end:
-       free(agent_filter);
-       return ret;
-}
-
-static enum lttng_error_code
-lttng_event_rule_python_logging_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-       char *agent_filter;
-
-       LTTNG_ASSERT(rule);
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-
-       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       ret = generate_agent_filter(rule, &agent_filter);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       python_logging->internal_filter.filter = agent_filter;
-
-       if (python_logging->internal_filter.filter == NULL) {
-               ret_code = LTTNG_OK;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       python_logging->internal_filter.filter, creds,
-                       &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       python_logging->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-       ret_code = LTTNG_OK;
-
-error:
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_python_logging_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-
-       LTTNG_ASSERT(rule);
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       return python_logging->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_python_logging_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-
-       LTTNG_ASSERT(rule);
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       return python_logging->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_python_logging_generate_exclusions(
-               const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **_exclusions)
-{
-       /* Unsupported. */
-       *_exclusions = NULL;
-       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-}
-
-static unsigned long lttng_event_rule_python_logging_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       struct lttng_event_rule_python_logging *tp_rule =
-                       container_of(rule, typeof(*tp_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
-
-       if (tp_rule->filter_expression) {
-               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
-       }
-
-       if (tp_rule->log_level_rule) {
-               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
-       }
-
-       return hash;
-}
-
-static struct lttng_event *lttng_event_rule_python_logging_generate_lttng_event(
-               const struct lttng_event_rule *rule)
-{
-       int ret;
-       const struct lttng_event_rule_python_logging *python_logging;
-       struct lttng_event *local_event = NULL;
-       struct lttng_event *event = NULL;
-       enum lttng_loglevel_type loglevel_type;
-       int loglevel_value = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_log_level_rule *log_level_rule;
-
-       python_logging = container_of(
-                       rule, const struct lttng_event_rule_python_logging, parent);
-
-       local_event = zmalloc(sizeof(*local_event));
-       if (!local_event) {
-               goto error;
-       }
-
-       local_event->type = LTTNG_EVENT_TRACEPOINT;
-       ret = lttng_strncpy(local_event->name, python_logging->pattern,
-                           sizeof(local_event->name));
-       if (ret) {
-               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
-                               python_logging->pattern);
-               goto error;
-       }
-
-
-       /* Map the log level rule to an equivalent lttng_loglevel. */
-       status = lttng_event_rule_python_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
-               loglevel_value = 0;
-       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
-               enum lttng_log_level_rule_status llr_status;
-
-               switch (lttng_log_level_rule_get_type(log_level_rule)) {
-               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-                       llr_status = lttng_log_level_rule_exactly_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
-                       break;
-               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                                       log_level_rule, &loglevel_value);
-                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
-                       break;
-               default:
-                       abort();
-                       break;
-               }
-
-               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
-                       goto error;
-               }
-       } else {
-               goto error;
-       }
-
-       local_event->loglevel_type = loglevel_type;
-       local_event->loglevel = loglevel_value;
-
-       event = local_event;
-       local_event = NULL;
-error:
-       free(local_event);
-       return event;
-}
-
-static enum lttng_error_code lttng_event_rule_python_logging_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_PYTHON_LOGGING_EVENT_RULE(rule));
-
-       status = lttng_event_rule_python_logging_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       status = lttng_event_rule_python_logging_get_log_level_rule(
-                       rule, &log_level_rule);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       /* Open event rule python logging element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_python_logging);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter expression. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Log level rule. */
-       if (log_level_rule) {
-               ret_code = lttng_log_level_rule_mi_serialize(
-                               log_level_rule, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close event rule python logging element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_python_logging_create(void)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_python_logging *tp_rule;
-       enum lttng_event_rule_status status;
-
-       tp_rule = zmalloc(sizeof(struct lttng_event_rule_python_logging));
-       if (!tp_rule) {
-               goto end;
-       }
-
-       rule = &tp_rule->parent;
-       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING);
-       tp_rule->parent.validate = lttng_event_rule_python_logging_validate;
-       tp_rule->parent.serialize = lttng_event_rule_python_logging_serialize;
-       tp_rule->parent.equal = lttng_event_rule_python_logging_is_equal;
-       tp_rule->parent.destroy = lttng_event_rule_python_logging_destroy;
-       tp_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_python_logging_generate_filter_bytecode;
-       tp_rule->parent.get_filter =
-                       lttng_event_rule_python_logging_get_internal_filter;
-       tp_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_python_logging_get_internal_filter_bytecode;
-       tp_rule->parent.generate_exclusions =
-                       lttng_event_rule_python_logging_generate_exclusions;
-       tp_rule->parent.hash = lttng_event_rule_python_logging_hash;
-       tp_rule->parent.generate_lttng_event =
-                       lttng_event_rule_python_logging_generate_lttng_event;
-       tp_rule->parent.mi_serialize = lttng_event_rule_python_logging_mi_serialize;
-
-       tp_rule->log_level_rule = NULL;
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_python_logging_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_python_logging_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_python_logging_comm *python_logging_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_log_level_rule *log_level_rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*python_logging_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule python_logging: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       python_logging_comm = (typeof(python_logging_comm)) current_buffer_view.data;
-
-       rule = lttng_event_rule_python_logging_create();
-       if (!rule) {
-               ERR("Failed to create event rule python_logging.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, python_logging_comm->pattern_len);
-
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       python_logging_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += python_logging_comm->pattern_len;
-
-       if (!python_logging_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       python_logging_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                       filter_expression,
-                       python_logging_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += python_logging_comm->filter_expression_len;
-
-skip_filter_expression:
-       if (!python_logging_comm->log_level_rule_len) {
-               goto skip_log_level_rule;
-       }
-
-       {
-               /* Map the log level rule. */
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               python_logging_comm->log_level_rule_len);
-
-               ret = lttng_log_level_rule_create_from_payload(
-                               &current_payload_view, &log_level_rule);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-
-               LTTNG_ASSERT(ret == python_logging_comm->log_level_rule_len);
-       }
-
-       /* Skip after the log level rule. */
-       offset += python_logging_comm->log_level_rule_len;
-
-skip_log_level_rule:
-
-       status = lttng_event_rule_python_logging_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule python_logging pattern.");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_python_logging_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule python_logging pattern.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (log_level_rule) {
-               status = lttng_event_rule_python_logging_set_log_level_rule(
-                               rule, log_level_rule);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule python_logging log level rule.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       lttng_log_level_rule_destroy(log_level_rule);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Normalize the pattern. */
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(python_logging->pattern);
-
-       python_logging->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       if (!python_logging->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = python_logging->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               PERROR("Failed to copy filter expression");
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (python_logging->filter_expression) {
-               free(python_logging->filter_expression);
-       }
-
-       python_logging->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       if (!python_logging->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = python_logging->filter_expression;
-end:
-       return status;
-}
-
-static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
-{
-       /*
-        * For python, custom log level are possible, it is not clear if
-        * negative value are accepted (NOTSET == 0) but the source code
-        * validates against the int type implying that negative values
-        * are accepted.
-        */
-       return true;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_set_log_level_rule(
-               struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule *log_level_rule)
-{
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-       struct lttng_log_level_rule *copy = NULL;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-
-       if (!log_level_rule_valid(log_level_rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_log_level_rule_copy(log_level_rule);
-       if (copy == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (python_logging->log_level_rule) {
-               lttng_log_level_rule_destroy(python_logging->log_level_rule);
-       }
-
-       python_logging->log_level_rule = copy;
-
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_python_logging_get_log_level_rule(
-               const struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule **log_level_rule
-               )
-{
-       struct lttng_event_rule_python_logging *python_logging;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       python_logging = container_of(
-                       rule, struct lttng_event_rule_python_logging, parent);
-       if (python_logging->log_level_rule == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *log_level_rule = python_logging->log_level_rule;
-end:
-       return status;
-}
diff --git a/src/common/event-rule/python-logging.cpp b/src/common/event-rule/python-logging.cpp
new file mode 100644 (file)
index 0000000..1ccd495
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/python-logging-internal.h>
+#include <lttng/event.h>
+#include <lttng/log-level-rule.h>
+
+#define IS_PYTHON_LOGGING_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING)
+
+static void lttng_event_rule_python_logging_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+
+       lttng_log_level_rule_destroy(python_logging->log_level_rule);
+       free(python_logging->pattern);
+       free(python_logging->filter_expression);
+       free(python_logging->internal_filter.filter);
+       free(python_logging->internal_filter.bytecode);
+       free(python_logging);
+}
+
+static bool lttng_event_rule_python_logging_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_python_logging *python_logging;
+
+       if (!rule) {
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+
+       /* Required field. */
+       if (!python_logging->pattern) {
+               ERR("Invalid python_logging event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_python_logging_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t pattern_len, filter_expression_len, header_offset;
+       size_t size_before_log_level_rule;
+       struct lttng_event_rule_python_logging *python_logging;
+       struct lttng_event_rule_python_logging_comm python_logging_comm;
+       struct lttng_event_rule_python_logging_comm *header;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing python_logging event rule.");
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+
+       pattern_len = strlen(python_logging->pattern) + 1;
+
+       if (python_logging->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(python_logging->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       python_logging_comm.pattern_len = pattern_len;
+       python_logging_comm.filter_expression_len = filter_expression_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &python_logging_comm,
+                       sizeof(python_logging_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, python_logging->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, python_logging->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_log_level_rule = payload->buffer.size;
+
+       ret = lttng_log_level_rule_serialize(python_logging->log_level_rule, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
+       header->log_level_rule_len =
+                       payload->buffer.size - size_before_log_level_rule;
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_python_logging_is_equal(
+               const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       bool is_equal = false;
+       struct lttng_event_rule_python_logging *a, *b;
+
+       a = container_of(_a, struct lttng_event_rule_python_logging, parent);
+       b = container_of(_b, struct lttng_event_rule_python_logging, parent);
+
+       /* Quick checks. */
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check. */
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set; not the other. */
+               goto end;
+       }
+
+       if (!lttng_log_level_rule_is_equal(
+                               a->log_level_rule, b->log_level_rule)) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+/*
+ * On success ret is 0;
+ *
+ * On error ret is negative.
+ *
+ * An event with NO loglevel and the name is * will return NULL.
+ */
+static int generate_agent_filter(
+               const struct lttng_event_rule *rule, char **_agent_filter)
+{
+       int err;
+       int ret = 0;
+       char *agent_filter = NULL;
+       const char *pattern;
+       const char *filter;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+       enum lttng_event_rule_status status;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(_agent_filter);
+
+       status = lttng_event_rule_python_logging_get_name_pattern(rule, &pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+
+       /* Don't add filter for the '*' event. */
+       if (strcmp(pattern, "*") != 0) {
+               if (filter) {
+                       err = asprintf(&agent_filter,
+                                       "(%s) && (logger_name == \"%s\")",
+                                       filter, pattern);
+               } else {
+                       err = asprintf(&agent_filter, "logger_name == \"%s\"",
+                                       pattern);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       status = lttng_event_rule_python_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+               const char *op;
+               int level;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule))
+               {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &level);
+                       op = "==";
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &level);
+                       op = ">=";
+                       break;
+               default:
+                       abort();
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (filter || agent_filter) {
+                       char *new_filter;
+
+                       err = asprintf(&new_filter,
+                                       "(%s) && (int_loglevel %s %d)",
+                                       agent_filter ? agent_filter : filter,
+                                       op, level);
+                       if (agent_filter) {
+                               free(agent_filter);
+                       }
+                       agent_filter = new_filter;
+               } else {
+                       err = asprintf(&agent_filter, "int_loglevel %s %d", op,
+                                       level);
+               }
+
+               if (err < 0) {
+                       PERROR("Failed to format agent filter string");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_agent_filter = agent_filter;
+       agent_filter = NULL;
+
+end:
+       free(agent_filter);
+       return ret;
+}
+
+static enum lttng_error_code
+lttng_event_rule_python_logging_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+       char *agent_filter;
+
+       LTTNG_ASSERT(rule);
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+
+       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       ret = generate_agent_filter(rule, &agent_filter);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       python_logging->internal_filter.filter = agent_filter;
+
+       if (python_logging->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       python_logging->internal_filter.filter, creds,
+                       &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       python_logging->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_python_logging_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+
+       LTTNG_ASSERT(rule);
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       return python_logging->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_python_logging_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+
+       LTTNG_ASSERT(rule);
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       return python_logging->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_python_logging_generate_exclusions(
+               const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **_exclusions)
+{
+       /* Unsupported. */
+       *_exclusions = NULL;
+       return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+}
+
+static unsigned long lttng_event_rule_python_logging_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       struct lttng_event_rule_python_logging *tp_rule =
+                       container_of(rule, typeof(*tp_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+       if (tp_rule->filter_expression) {
+               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+       }
+
+       if (tp_rule->log_level_rule) {
+               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
+       }
+
+       return hash;
+}
+
+static struct lttng_event *lttng_event_rule_python_logging_generate_lttng_event(
+               const struct lttng_event_rule *rule)
+{
+       int ret;
+       const struct lttng_event_rule_python_logging *python_logging;
+       struct lttng_event *local_event = NULL;
+       struct lttng_event *event = NULL;
+       enum lttng_loglevel_type loglevel_type;
+       int loglevel_value = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_log_level_rule *log_level_rule;
+
+       python_logging = container_of(
+                       rule, const struct lttng_event_rule_python_logging, parent);
+
+       local_event = (lttng_event *) zmalloc(sizeof(*local_event));
+       if (!local_event) {
+               goto error;
+       }
+
+       local_event->type = LTTNG_EVENT_TRACEPOINT;
+       ret = lttng_strncpy(local_event->name, python_logging->pattern,
+                           sizeof(local_event->name));
+       if (ret) {
+               ERR("Truncation occurred when copying event rule pattern to `lttng_event` structure: pattern = '%s'",
+                               python_logging->pattern);
+               goto error;
+       }
+
+
+       /* Map the log level rule to an equivalent lttng_loglevel. */
+       status = lttng_event_rule_python_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+               loglevel_value = 0;
+       } else if (status == LTTNG_EVENT_RULE_STATUS_OK) {
+               enum lttng_log_level_rule_status llr_status;
+
+               switch (lttng_log_level_rule_get_type(log_level_rule)) {
+               case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+                       llr_status = lttng_log_level_rule_exactly_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+                       break;
+               case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+                       llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                                       log_level_rule, &loglevel_value);
+                       loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
+                       break;
+               default:
+                       abort();
+                       break;
+               }
+
+               if (llr_status != LTTNG_LOG_LEVEL_RULE_STATUS_OK) {
+                       goto error;
+               }
+       } else {
+               goto error;
+       }
+
+       local_event->loglevel_type = loglevel_type;
+       local_event->loglevel = loglevel_value;
+
+       event = local_event;
+       local_event = NULL;
+error:
+       free(local_event);
+       return event;
+}
+
+static enum lttng_error_code lttng_event_rule_python_logging_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_PYTHON_LOGGING_EVENT_RULE(rule));
+
+       status = lttng_event_rule_python_logging_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_python_logging_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       status = lttng_event_rule_python_logging_get_log_level_rule(
+                       rule, &log_level_rule);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       /* Open event rule python logging element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_python_logging);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter expression. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Log level rule. */
+       if (log_level_rule) {
+               ret_code = lttng_log_level_rule_mi_serialize(
+                               log_level_rule, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close event rule python logging element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_python_logging_create(void)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_python_logging *tp_rule;
+       enum lttng_event_rule_status status;
+
+       tp_rule = (lttng_event_rule_python_logging *) zmalloc(sizeof(struct lttng_event_rule_python_logging));
+       if (!tp_rule) {
+               goto end;
+       }
+
+       rule = &tp_rule->parent;
+       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING);
+       tp_rule->parent.validate = lttng_event_rule_python_logging_validate;
+       tp_rule->parent.serialize = lttng_event_rule_python_logging_serialize;
+       tp_rule->parent.equal = lttng_event_rule_python_logging_is_equal;
+       tp_rule->parent.destroy = lttng_event_rule_python_logging_destroy;
+       tp_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_python_logging_generate_filter_bytecode;
+       tp_rule->parent.get_filter =
+                       lttng_event_rule_python_logging_get_internal_filter;
+       tp_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_python_logging_get_internal_filter_bytecode;
+       tp_rule->parent.generate_exclusions =
+                       lttng_event_rule_python_logging_generate_exclusions;
+       tp_rule->parent.hash = lttng_event_rule_python_logging_hash;
+       tp_rule->parent.generate_lttng_event =
+                       lttng_event_rule_python_logging_generate_lttng_event;
+       tp_rule->parent.mi_serialize = lttng_event_rule_python_logging_mi_serialize;
+
+       tp_rule->log_level_rule = NULL;
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_python_logging_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_python_logging_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_python_logging_comm *python_logging_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_log_level_rule *log_level_rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*python_logging_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule python_logging: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       python_logging_comm = (typeof(python_logging_comm)) current_buffer_view.data;
+
+       rule = lttng_event_rule_python_logging_create();
+       if (!rule) {
+               ERR("Failed to create event rule python_logging.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, python_logging_comm->pattern_len);
+
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       python_logging_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += python_logging_comm->pattern_len;
+
+       if (!python_logging_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       python_logging_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                       filter_expression,
+                       python_logging_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += python_logging_comm->filter_expression_len;
+
+skip_filter_expression:
+       if (!python_logging_comm->log_level_rule_len) {
+               goto skip_log_level_rule;
+       }
+
+       {
+               /* Map the log level rule. */
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               python_logging_comm->log_level_rule_len);
+
+               ret = lttng_log_level_rule_create_from_payload(
+                               &current_payload_view, &log_level_rule);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+
+               LTTNG_ASSERT(ret == python_logging_comm->log_level_rule_len);
+       }
+
+       /* Skip after the log level rule. */
+       offset += python_logging_comm->log_level_rule_len;
+
+skip_log_level_rule:
+
+       status = lttng_event_rule_python_logging_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule python_logging pattern.");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_python_logging_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule python_logging pattern.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (log_level_rule) {
+               status = lttng_event_rule_python_logging_set_log_level_rule(
+                               rule, log_level_rule);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule python_logging log level rule.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       lttng_log_level_rule_destroy(log_level_rule);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Normalize the pattern. */
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(python_logging->pattern);
+
+       python_logging->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       if (!python_logging->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = python_logging->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               PERROR("Failed to copy filter expression");
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (python_logging->filter_expression) {
+               free(python_logging->filter_expression);
+       }
+
+       python_logging->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       if (!python_logging->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = python_logging->filter_expression;
+end:
+       return status;
+}
+
+static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
+{
+       /*
+        * For python, custom log level are possible, it is not clear if
+        * negative value are accepted (NOTSET == 0) but the source code
+        * validates against the int type implying that negative values
+        * are accepted.
+        */
+       return true;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_set_log_level_rule(
+               struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule *log_level_rule)
+{
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       struct lttng_log_level_rule *copy = NULL;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+
+       if (!log_level_rule_valid(log_level_rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_log_level_rule_copy(log_level_rule);
+       if (copy == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (python_logging->log_level_rule) {
+               lttng_log_level_rule_destroy(python_logging->log_level_rule);
+       }
+
+       python_logging->log_level_rule = copy;
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_python_logging_get_log_level_rule(
+               const struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule **log_level_rule
+               )
+{
+       struct lttng_event_rule_python_logging *python_logging;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_PYTHON_LOGGING_EVENT_RULE(rule) || !log_level_rule) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       python_logging = container_of(
+                       rule, struct lttng_event_rule_python_logging, parent);
+       if (python_logging->log_level_rule == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *log_level_rule = python_logging->log_level_rule;
+end:
+       return status;
+}
diff --git a/src/common/event-rule/user-tracepoint.c b/src/common/event-rule/user-tracepoint.c
deleted file mode 100644 (file)
index cb384f8..0000000
+++ /dev/null
@@ -1,1081 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/runas.h>
-#include <common/string-utils/string-utils.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/user-tracepoint-internal.h>
-#include <lttng/event.h>
-#include <lttng/log-level-rule.h>
-
-#define IS_USER_TRACEPOINT_EVENT_RULE(rule) \
-       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT)
-
-static void lttng_event_rule_user_tracepoint_destroy(struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-
-       if (rule == NULL) {
-               return;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       lttng_log_level_rule_destroy(tracepoint->log_level_rule);
-       lttng_dynamic_pointer_array_reset(&tracepoint->exclusions);
-       free(tracepoint->pattern);
-       free(tracepoint->filter_expression);
-       free(tracepoint->internal_filter.filter);
-       free(tracepoint->internal_filter.bytecode);
-       free(tracepoint);
-}
-
-static bool lttng_event_rule_user_tracepoint_validate(
-               const struct lttng_event_rule *rule)
-{
-       bool valid = false;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-
-       if (!rule) {
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       /* Required field. */
-       if (!tracepoint->pattern) {
-               ERR("Invalid user tracepoint event rule: a pattern must be set.");
-               goto end;
-       }
-
-       valid = true;
-end:
-       return valid;
-}
-
-static int lttng_event_rule_user_tracepoint_serialize(
-               const struct lttng_event_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret, i;
-       size_t pattern_len, filter_expression_len, exclusions_len, header_offset;
-       size_t size_before_log_level_rule;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       struct lttng_event_rule_user_tracepoint_comm tracepoint_comm;
-       enum lttng_event_rule_status status;
-       unsigned int exclusion_count;
-       size_t exclusions_appended_len = 0;
-       struct lttng_event_rule_user_tracepoint_comm *header;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule)) {
-               ret = -1;
-               goto end;
-       }
-
-       header_offset = payload->buffer.size;
-
-       DBG("Serializing user tracepoint event rule.");
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule, &exclusion_count);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-       pattern_len = strlen(tracepoint->pattern) + 1;
-
-       if (tracepoint->filter_expression != NULL) {
-               filter_expression_len =
-                               strlen(tracepoint->filter_expression) + 1;
-       } else {
-               filter_expression_len = 0;
-       }
-
-       exclusions_len = 0;
-       for (i = 0; i < exclusion_count; i++) {
-               const char *exclusion;
-
-               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                               rule, i, &exclusion);
-               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-               /* Length field. */
-               exclusions_len += sizeof(uint32_t);
-               /* Payload (null terminated). */
-               exclusions_len += strlen(exclusion) + 1;
-       }
-
-       tracepoint_comm.pattern_len = pattern_len;
-       tracepoint_comm.filter_expression_len = filter_expression_len;
-       tracepoint_comm.exclusions_count = exclusion_count;
-       tracepoint_comm.exclusions_len = exclusions_len;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &tracepoint_comm,
-                       sizeof(tracepoint_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, tracepoint->pattern, pattern_len);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer, tracepoint->filter_expression,
-                       filter_expression_len);
-       if (ret) {
-               goto end;
-       }
-
-       size_before_log_level_rule = payload->buffer.size;
-
-       ret = lttng_log_level_rule_serialize(tracepoint->log_level_rule, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
-       header->log_level_rule_len =
-                       payload->buffer.size - size_before_log_level_rule;
-
-       for (i = 0; i < exclusion_count; i++) {
-               size_t len;
-               const char *exclusion;
-
-               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                               rule, i, &exclusion);
-               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-               len = strlen(exclusion) + 1;
-               /* Append exclusion length, includes the null terminator. */
-               ret = lttng_dynamic_buffer_append(
-                               &payload->buffer, &len, sizeof(uint32_t));
-               if (ret) {
-                       goto end;
-               }
-
-               exclusions_appended_len += sizeof(uint32_t);
-
-               /* Include the '\0' in the payload. */
-               ret = lttng_dynamic_buffer_append(
-                               &payload->buffer, exclusion, len);
-               if (ret) {
-                       goto end;
-               }
-
-               exclusions_appended_len += len;
-       }
-
-       LTTNG_ASSERT(exclusions_len == exclusions_appended_len);
-
-end:
-       return ret;
-}
-
-static bool lttng_event_rule_user_tracepoint_is_equal(
-               const struct lttng_event_rule *_a,
-               const struct lttng_event_rule *_b)
-{
-       int i;
-       bool is_equal = false;
-       struct lttng_event_rule_user_tracepoint *a, *b;
-       unsigned int count_a, count_b;
-       enum lttng_event_rule_status status;
-
-       a = container_of(_a, struct lttng_event_rule_user_tracepoint, parent);
-       b = container_of(_b, struct lttng_event_rule_user_tracepoint, parent);
-
-       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(_a, &count_a);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(_b, &count_b);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-       /* Quick checks. */
-       if (count_a != count_b) {
-               goto end;
-       }
-
-       if (!!a->filter_expression != !!b->filter_expression) {
-               goto end;
-       }
-
-       /* Long check. */
-       LTTNG_ASSERT(a->pattern);
-       LTTNG_ASSERT(b->pattern);
-       if (strcmp(a->pattern, b->pattern)) {
-               goto end;
-       }
-
-       if (a->filter_expression && b->filter_expression) {
-               if (strcmp(a->filter_expression, b->filter_expression)) {
-                       goto end;
-               }
-       } else if (!!a->filter_expression != !!b->filter_expression) {
-               /* One is set; not the other. */
-               goto end;
-       }
-
-       if (!lttng_log_level_rule_is_equal(
-                               a->log_level_rule, b->log_level_rule)) {
-               goto end;
-       }
-
-       for (i = 0; i < count_a; i++) {
-               const char *exclusion_a, *exclusion_b;
-
-               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                               _a, i, &exclusion_a);
-               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                               _b, i, &exclusion_b);
-               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-               if (strcmp(exclusion_a, exclusion_b)) {
-                       goto end;
-               }
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-static enum lttng_error_code
-lttng_event_rule_user_tracepoint_generate_filter_bytecode(
-               struct lttng_event_rule *rule,
-               const struct lttng_credentials *creds)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status;
-       const char *filter;
-       struct lttng_bytecode *bytecode = NULL;
-
-       LTTNG_ASSERT(rule);
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       status = lttng_event_rule_user_tracepoint_get_filter(rule, &filter);
-       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               filter = NULL;
-       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       if (filter && filter[0] == '\0') {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto error;
-       }
-
-       if (filter) {
-               tracepoint->internal_filter.filter = strdup(filter);
-               if (tracepoint->internal_filter.filter == NULL) {
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto error;
-               }
-       } else {
-               tracepoint->internal_filter.filter = NULL;
-       }
-
-       if (tracepoint->internal_filter.filter == NULL) {
-               ret_code = LTTNG_OK;
-               goto end;
-       }
-
-       ret = run_as_generate_filter_bytecode(
-                       tracepoint->internal_filter.filter, creds,
-                       &bytecode);
-       if (ret) {
-               ret_code = LTTNG_ERR_FILTER_INVAL;
-               goto end;
-       }
-
-       tracepoint->internal_filter.bytecode = bytecode;
-       bytecode = NULL;
-       ret_code = LTTNG_OK;
-
-error:
-end:
-       free(bytecode);
-       return ret_code;
-}
-
-static const char *lttng_event_rule_user_tracepoint_get_internal_filter(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-
-       LTTNG_ASSERT(rule);
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       return tracepoint->internal_filter.filter;
-}
-
-static const struct lttng_bytecode *
-lttng_event_rule_user_tracepoint_get_internal_filter_bytecode(
-               const struct lttng_event_rule *rule)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-
-       LTTNG_ASSERT(rule);
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       return tracepoint->internal_filter.bytecode;
-}
-
-static enum lttng_event_rule_generate_exclusions_status
-lttng_event_rule_user_tracepoint_generate_exclusions(
-               const struct lttng_event_rule *rule,
-               struct lttng_event_exclusion **_exclusions)
-{
-       unsigned int nb_exclusions = 0, i;
-       struct lttng_event_exclusion *exclusions;
-       enum lttng_event_rule_status event_rule_status;
-       enum lttng_event_rule_generate_exclusions_status ret_status;
-
-       LTTNG_ASSERT(_exclusions);
-
-       event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
-                       rule, &nb_exclusions);
-       LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
-       if (nb_exclusions == 0) {
-               /* Nothing to do. */
-               exclusions = NULL;
-               ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
-               goto end;
-       }
-
-       exclusions = zmalloc(sizeof(struct lttng_event_exclusion) +
-                       (LTTNG_SYMBOL_NAME_LEN * nb_exclusions));
-       if (!exclusions) {
-               PERROR("Failed to allocate exclusions buffer");
-               ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_OUT_OF_MEMORY;
-               goto end;
-       }
-
-       exclusions->count = nb_exclusions;
-       for (i = 0; i < nb_exclusions; i++) {
-               int copy_ret;
-               const char *exclusion_str;
-
-               event_rule_status =
-                               lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                                               rule, i, &exclusion_str);
-               LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
-
-               copy_ret = lttng_strncpy(exclusions->names[i], exclusion_str,
-                               LTTNG_SYMBOL_NAME_LEN);
-               if (copy_ret) {
-                       free(exclusions);
-                       exclusions = NULL;
-                       ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_OK;
-
-end:
-       *_exclusions = exclusions;
-       return ret_status;
-}
-
-static void destroy_lttng_exclusions_element(void *ptr)
-{
-       free(ptr);
-}
-
-static unsigned long lttng_event_rule_user_tracepoint_hash(
-               const struct lttng_event_rule *rule)
-{
-       unsigned long hash;
-       unsigned int i, exclusion_count;
-       enum lttng_event_rule_status status;
-       struct lttng_event_rule_user_tracepoint *tp_rule =
-                       container_of(rule, typeof(*tp_rule), parent);
-
-       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT,
-                       lttng_ht_seed);
-       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
-
-       if (tp_rule->filter_expression) {
-               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
-       }
-
-       if (tp_rule->log_level_rule) {
-               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
-       }
-
-       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule,
-                       &exclusion_count);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-       for (i = 0; i < exclusion_count; i++) {
-               const char *exclusion;
-
-               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                               rule, i, &exclusion);
-               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-               hash ^= hash_key_str(exclusion, lttng_ht_seed);
-       }
-
-       return hash;
-}
-
-static enum lttng_error_code lttng_event_rule_user_tracepoint_mi_serialize(
-               const struct lttng_event_rule *rule, struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_event_rule_status status;
-       const char *filter = NULL;
-       const char *name_pattern = NULL;
-       const struct lttng_log_level_rule *log_level_rule = NULL;
-       unsigned int exclusion_count = 0;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(IS_USER_TRACEPOINT_EVENT_RULE(rule));
-
-       status = lttng_event_rule_user_tracepoint_get_name_pattern(
-                       rule, &name_pattern);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-       LTTNG_ASSERT(name_pattern);
-
-       status = lttng_event_rule_user_tracepoint_get_filter(rule, &filter);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       status = lttng_event_rule_user_tracepoint_get_log_level_rule(
-                       rule, &log_level_rule);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
-                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
-
-       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
-                       rule, &exclusion_count);
-       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-       /* Open event rule user tracepoint element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_event_rule_user_tracepoint);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name pattern. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_event_rule_name_pattern, name_pattern);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Filter expression. */
-       if (filter != NULL) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_event_rule_filter_expression,
-                               filter);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Log level rule. */
-       if (log_level_rule) {
-               ret_code = lttng_log_level_rule_mi_serialize(
-                               log_level_rule, writer);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       if (exclusion_count != 0) {
-               int i;
-
-               /* Open the exclusion list. */
-               ret = mi_lttng_writer_open_element(writer,
-                               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions);
-               if (ret) {
-                       goto mi_error;
-               }
-
-               for (i = 0; i < exclusion_count; i++) {
-                       const char *exclusion;
-
-                       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-                                       rule, i, &exclusion);
-                       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
-
-                       ret = mi_lttng_writer_write_element_string(writer,
-                                       mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion,
-                                       exclusion);
-                       if (ret) {
-                               goto mi_error;
-                       }
-               }
-
-               /* Close the list. */
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Close event rule user tracepoint element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-struct lttng_event_rule *lttng_event_rule_user_tracepoint_create(void)
-{
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_event_rule_user_tracepoint *tp_rule;
-       enum lttng_event_rule_status status;
-
-       tp_rule = zmalloc(sizeof(struct lttng_event_rule_user_tracepoint));
-       if (!tp_rule) {
-               goto end;
-       }
-
-       rule = &tp_rule->parent;
-       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT);
-       tp_rule->parent.validate = lttng_event_rule_user_tracepoint_validate;
-       tp_rule->parent.serialize = lttng_event_rule_user_tracepoint_serialize;
-       tp_rule->parent.equal = lttng_event_rule_user_tracepoint_is_equal;
-       tp_rule->parent.destroy = lttng_event_rule_user_tracepoint_destroy;
-       tp_rule->parent.generate_filter_bytecode =
-                       lttng_event_rule_user_tracepoint_generate_filter_bytecode;
-       tp_rule->parent.get_filter =
-                       lttng_event_rule_user_tracepoint_get_internal_filter;
-       tp_rule->parent.get_filter_bytecode =
-                       lttng_event_rule_user_tracepoint_get_internal_filter_bytecode;
-       tp_rule->parent.generate_exclusions =
-                       lttng_event_rule_user_tracepoint_generate_exclusions;
-       tp_rule->parent.hash = lttng_event_rule_user_tracepoint_hash;
-       tp_rule->parent.mi_serialize = lttng_event_rule_user_tracepoint_mi_serialize;
-
-       /* Not necessary for now. */
-       tp_rule->parent.generate_lttng_event = NULL;
-
-       tp_rule->log_level_rule = NULL;
-
-       lttng_dynamic_pointer_array_init(&tp_rule->exclusions,
-                       destroy_lttng_exclusions_element);
-
-       /* Default pattern is '*'. */
-       status = lttng_event_rule_user_tracepoint_set_name_pattern(rule, "*");
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_event_rule_destroy(rule);
-               rule = NULL;
-       }
-
-end:
-       return rule;
-}
-
-ssize_t lttng_event_rule_user_tracepoint_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_event_rule **_event_rule)
-{
-       ssize_t ret, offset = 0;
-       int i;
-       enum lttng_event_rule_status status;
-       const struct lttng_event_rule_user_tracepoint_comm *tracepoint_comm;
-       const char *pattern;
-       const char *filter_expression = NULL;
-       const char **exclusions = NULL;
-       const uint32_t *exclusion_len;
-       const char *exclusion;
-       struct lttng_buffer_view current_buffer_view;
-       struct lttng_event_rule *rule = NULL;
-       struct lttng_log_level_rule *log_level_rule = NULL;
-
-       if (!_event_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, sizeof(*tracepoint_comm));
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header.");
-               ret = -1;
-               goto end;
-       }
-
-       tracepoint_comm = (typeof(tracepoint_comm)) current_buffer_view.data;
-
-       rule = lttng_event_rule_user_tracepoint_create();
-       if (!rule) {
-               ERR("Failed to create event rule user tracepoint.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip to payload. */
-       offset += current_buffer_view.size;
-
-       /* Map the pattern. */
-       current_buffer_view = lttng_buffer_view_from_view(
-                       &view->buffer, offset, tracepoint_comm->pattern_len);
-
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       pattern = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
-                       tracepoint_comm->pattern_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += tracepoint_comm->pattern_len;
-
-       if (!tracepoint_comm->filter_expression_len) {
-               goto skip_filter_expression;
-       }
-
-       /* Map the filter_expression. */
-       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
-                       tracepoint_comm->filter_expression_len);
-       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       filter_expression = current_buffer_view.data;
-       if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                       filter_expression,
-                       tracepoint_comm->filter_expression_len)) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the pattern. */
-       offset += tracepoint_comm->filter_expression_len;
-
-skip_filter_expression:
-       if (!tracepoint_comm->log_level_rule_len) {
-               goto skip_log_level_rule;
-       }
-
-       {
-               /* Map the log level rule. */
-               struct lttng_payload_view current_payload_view =
-                               lttng_payload_view_from_view(view, offset,
-                                               tracepoint_comm->log_level_rule_len);
-
-               ret = lttng_log_level_rule_create_from_payload(
-                               &current_payload_view, &log_level_rule);
-               if (ret < 0) {
-                       ret = -1;
-                       goto end;
-               }
-
-               LTTNG_ASSERT(ret == tracepoint_comm->log_level_rule_len);
-       }
-
-       /* Skip after the log level rule. */
-       offset += tracepoint_comm->log_level_rule_len;
-
-skip_log_level_rule:
-       for (i = 0; i < tracepoint_comm->exclusions_count; i++) {
-               current_buffer_view = lttng_buffer_view_from_view(
-                               &view->buffer, offset, sizeof(*exclusion_len));
-               if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               exclusion_len = (typeof(exclusion_len)) current_buffer_view.data;
-               offset += sizeof(*exclusion_len);
-
-               current_buffer_view = lttng_buffer_view_from_view(
-                               &view->buffer, offset, *exclusion_len);
-               if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               exclusion = current_buffer_view.data;
-               if (!lttng_buffer_view_contains_string(&current_buffer_view,
-                               exclusion, *exclusion_len)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion(rule, exclusion);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to add event rule user tracepoint exclusion \"%s\".",
-                                       exclusion);
-                       ret = -1;
-                       goto end;
-               }
-
-               /* Skip to next exclusion. */
-               offset += *exclusion_len;
-       }
-
-       status = lttng_event_rule_user_tracepoint_set_name_pattern(rule, pattern);
-       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-               ERR("Failed to set event rule user tracepoint pattern.");
-               ret = -1;
-               goto end;
-       }
-
-       if (filter_expression) {
-               status = lttng_event_rule_user_tracepoint_set_filter(
-                               rule, filter_expression);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule user tracepoint pattern.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (log_level_rule) {
-               status = lttng_event_rule_user_tracepoint_set_log_level_rule(
-                               rule, log_level_rule);
-               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
-                       ERR("Failed to set event rule user tracepoint log level rule.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       *_event_rule = rule;
-       rule = NULL;
-       ret = offset;
-end:
-       free(exclusions);
-       lttng_log_level_rule_destroy(log_level_rule);
-       lttng_event_rule_destroy(rule);
-       return ret;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_name_pattern(
-               struct lttng_event_rule *rule, const char *pattern)
-{
-       char *pattern_copy = NULL;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
-                       strlen(pattern) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       pattern_copy = strdup(pattern);
-       if (!pattern_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Normalize the pattern. */
-       strutils_normalize_star_glob_pattern(pattern_copy);
-
-       free(tracepoint->pattern);
-
-       tracepoint->pattern = pattern_copy;
-       pattern_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern(
-               const struct lttng_event_rule *rule, const char **pattern)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       if (!tracepoint->pattern) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *pattern = tracepoint->pattern;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_filter(
-               struct lttng_event_rule *rule, const char *expression)
-{
-       char *expression_copy = NULL;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !expression ||
-                       strlen(expression) == 0) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       expression_copy = strdup(expression);
-       if (!expression_copy) {
-               PERROR("Failed to copy filter expression");
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (tracepoint->filter_expression) {
-               free(tracepoint->filter_expression);
-       }
-
-       tracepoint->filter_expression = expression_copy;
-       expression_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_filter(
-               const struct lttng_event_rule *rule, const char **expression)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !expression) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       if (!tracepoint->filter_expression) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *expression = tracepoint->filter_expression;
-end:
-       return status;
-}
-
-static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
-{
-       bool valid = false;
-       enum lttng_log_level_rule_status status;
-       int level;
-
-       switch (lttng_log_level_rule_get_type(rule)) {
-       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-               status = lttng_log_level_rule_exactly_get_level(rule, &level);
-               break;
-       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-               status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                               rule, &level);
-               break;
-       default:
-               abort();
-       }
-
-       LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
-
-       if (level < LTTNG_LOGLEVEL_EMERG) {
-               /* Invalid. */
-               goto end;
-       }
-       if (level > LTTNG_LOGLEVEL_DEBUG) {
-               /* Invalid. */
-               goto end;
-       }
-
-       valid = true;
-
-end:
-       return valid;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_log_level_rule(
-               struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule *log_level_rule)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-       struct lttng_log_level_rule *copy = NULL;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       if (!log_level_rule_valid(log_level_rule)) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       copy = lttng_log_level_rule_copy(log_level_rule);
-       if (copy == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       if (tracepoint->log_level_rule) {
-               lttng_log_level_rule_destroy(tracepoint->log_level_rule);
-       }
-
-       tracepoint->log_level_rule = copy;
-
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_log_level_rule(
-               const struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule **log_level_rule
-               )
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !log_level_rule) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       if (tracepoint->log_level_rule == NULL) {
-               status = LTTNG_EVENT_RULE_STATUS_UNSET;
-               goto end;
-       }
-
-       *log_level_rule = tracepoint->log_level_rule;
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_add_name_pattern_exclusion(
-               struct lttng_event_rule *rule,
-               const char *exclusion)
-{
-       int ret;
-       char *exclusion_copy = NULL;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) ||
-                       !exclusion) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-
-       if (strlen(exclusion) >= LTTNG_SYMBOL_NAME_LEN) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       exclusion_copy = strdup(exclusion);
-       if (!exclusion_copy) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       ret = lttng_dynamic_pointer_array_add_pointer(&tracepoint->exclusions,
-                       exclusion_copy);
-       if (ret < 0) {
-               status = LTTNG_EVENT_RULE_STATUS_ERROR;
-               goto end;
-       }
-
-       exclusion_copy = NULL;
-end:
-       free(exclusion_copy);
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
-               const struct lttng_event_rule *rule, unsigned int *count)
-{
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !count) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       *count = lttng_dynamic_pointer_array_get_count(&tracepoint->exclusions);
-end:
-       return status;
-}
-
-enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
-               const struct lttng_event_rule *rule,
-               unsigned int index,
-               const char **exclusion)
-{
-       unsigned int count;
-       struct lttng_event_rule_user_tracepoint *tracepoint;
-       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
-
-       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !exclusion) {
-               status = LTTNG_EVENT_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       tracepoint = container_of(
-                       rule, struct lttng_event_rule_user_tracepoint, parent);
-       if (lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule, &count) !=
-                       LTTNG_EVENT_RULE_STATUS_OK) {
-               goto end;
-       }
-
-       if (index >= count) {
-               goto end;
-       }
-
-       *exclusion = lttng_dynamic_pointer_array_get_pointer(
-                       &tracepoint->exclusions, index);
-end:
-       return status;
-}
diff --git a/src/common/event-rule/user-tracepoint.cpp b/src/common/event-rule/user-tracepoint.cpp
new file mode 100644 (file)
index 0000000..5158eea
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/runas.h>
+#include <common/string-utils/string-utils.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/user-tracepoint-internal.h>
+#include <lttng/event.h>
+#include <lttng/log-level-rule.h>
+
+#define IS_USER_TRACEPOINT_EVENT_RULE(rule) \
+       (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT)
+
+static void lttng_event_rule_user_tracepoint_destroy(struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+
+       if (rule == NULL) {
+               return;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       lttng_log_level_rule_destroy(tracepoint->log_level_rule);
+       lttng_dynamic_pointer_array_reset(&tracepoint->exclusions);
+       free(tracepoint->pattern);
+       free(tracepoint->filter_expression);
+       free(tracepoint->internal_filter.filter);
+       free(tracepoint->internal_filter.bytecode);
+       free(tracepoint);
+}
+
+static bool lttng_event_rule_user_tracepoint_validate(
+               const struct lttng_event_rule *rule)
+{
+       bool valid = false;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+
+       if (!rule) {
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       /* Required field. */
+       if (!tracepoint->pattern) {
+               ERR("Invalid user tracepoint event rule: a pattern must be set.");
+               goto end;
+       }
+
+       valid = true;
+end:
+       return valid;
+}
+
+static int lttng_event_rule_user_tracepoint_serialize(
+               const struct lttng_event_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret, i;
+       size_t pattern_len, filter_expression_len, exclusions_len, header_offset;
+       size_t size_before_log_level_rule;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       struct lttng_event_rule_user_tracepoint_comm tracepoint_comm;
+       enum lttng_event_rule_status status;
+       unsigned int exclusion_count;
+       size_t exclusions_appended_len = 0;
+       struct lttng_event_rule_user_tracepoint_comm *header;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule)) {
+               ret = -1;
+               goto end;
+       }
+
+       header_offset = payload->buffer.size;
+
+       DBG("Serializing user tracepoint event rule.");
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule, &exclusion_count);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       pattern_len = strlen(tracepoint->pattern) + 1;
+
+       if (tracepoint->filter_expression != NULL) {
+               filter_expression_len =
+                               strlen(tracepoint->filter_expression) + 1;
+       } else {
+               filter_expression_len = 0;
+       }
+
+       exclusions_len = 0;
+       for (i = 0; i < exclusion_count; i++) {
+               const char *exclusion;
+
+               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                               rule, i, &exclusion);
+               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+               /* Length field. */
+               exclusions_len += sizeof(uint32_t);
+               /* Payload (null terminated). */
+               exclusions_len += strlen(exclusion) + 1;
+       }
+
+       tracepoint_comm.pattern_len = pattern_len;
+       tracepoint_comm.filter_expression_len = filter_expression_len;
+       tracepoint_comm.exclusions_count = exclusion_count;
+       tracepoint_comm.exclusions_len = exclusions_len;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &tracepoint_comm,
+                       sizeof(tracepoint_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, tracepoint->pattern, pattern_len);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer, tracepoint->filter_expression,
+                       filter_expression_len);
+       if (ret) {
+               goto end;
+       }
+
+       size_before_log_level_rule = payload->buffer.size;
+
+       ret = lttng_log_level_rule_serialize(tracepoint->log_level_rule, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       header = (typeof(header)) ((char *) payload->buffer.data + header_offset);
+       header->log_level_rule_len =
+                       payload->buffer.size - size_before_log_level_rule;
+
+       for (i = 0; i < exclusion_count; i++) {
+               size_t len;
+               const char *exclusion;
+
+               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                               rule, i, &exclusion);
+               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+               len = strlen(exclusion) + 1;
+               /* Append exclusion length, includes the null terminator. */
+               ret = lttng_dynamic_buffer_append(
+                               &payload->buffer, &len, sizeof(uint32_t));
+               if (ret) {
+                       goto end;
+               }
+
+               exclusions_appended_len += sizeof(uint32_t);
+
+               /* Include the '\0' in the payload. */
+               ret = lttng_dynamic_buffer_append(
+                               &payload->buffer, exclusion, len);
+               if (ret) {
+                       goto end;
+               }
+
+               exclusions_appended_len += len;
+       }
+
+       LTTNG_ASSERT(exclusions_len == exclusions_appended_len);
+
+end:
+       return ret;
+}
+
+static bool lttng_event_rule_user_tracepoint_is_equal(
+               const struct lttng_event_rule *_a,
+               const struct lttng_event_rule *_b)
+{
+       int i;
+       bool is_equal = false;
+       struct lttng_event_rule_user_tracepoint *a, *b;
+       unsigned int count_a, count_b;
+       enum lttng_event_rule_status status;
+
+       a = container_of(_a, struct lttng_event_rule_user_tracepoint, parent);
+       b = container_of(_b, struct lttng_event_rule_user_tracepoint, parent);
+
+       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(_a, &count_a);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(_b, &count_b);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       /* Quick checks. */
+       if (count_a != count_b) {
+               goto end;
+       }
+
+       if (!!a->filter_expression != !!b->filter_expression) {
+               goto end;
+       }
+
+       /* Long check. */
+       LTTNG_ASSERT(a->pattern);
+       LTTNG_ASSERT(b->pattern);
+       if (strcmp(a->pattern, b->pattern)) {
+               goto end;
+       }
+
+       if (a->filter_expression && b->filter_expression) {
+               if (strcmp(a->filter_expression, b->filter_expression)) {
+                       goto end;
+               }
+       } else if (!!a->filter_expression != !!b->filter_expression) {
+               /* One is set; not the other. */
+               goto end;
+       }
+
+       if (!lttng_log_level_rule_is_equal(
+                               a->log_level_rule, b->log_level_rule)) {
+               goto end;
+       }
+
+       for (i = 0; i < count_a; i++) {
+               const char *exclusion_a, *exclusion_b;
+
+               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                               _a, i, &exclusion_a);
+               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                               _b, i, &exclusion_b);
+               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+               if (strcmp(exclusion_a, exclusion_b)) {
+                       goto end;
+               }
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+static enum lttng_error_code
+lttng_event_rule_user_tracepoint_generate_filter_bytecode(
+               struct lttng_event_rule *rule,
+               const struct lttng_credentials *creds)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status;
+       const char *filter;
+       struct lttng_bytecode *bytecode = NULL;
+
+       LTTNG_ASSERT(rule);
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       status = lttng_event_rule_user_tracepoint_get_filter(rule, &filter);
+       if (status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               filter = NULL;
+       } else if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       if (filter && filter[0] == '\0') {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto error;
+       }
+
+       if (filter) {
+               tracepoint->internal_filter.filter = strdup(filter);
+               if (tracepoint->internal_filter.filter == NULL) {
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       } else {
+               tracepoint->internal_filter.filter = NULL;
+       }
+
+       if (tracepoint->internal_filter.filter == NULL) {
+               ret_code = LTTNG_OK;
+               goto end;
+       }
+
+       ret = run_as_generate_filter_bytecode(
+                       tracepoint->internal_filter.filter, creds,
+                       &bytecode);
+       if (ret) {
+               ret_code = LTTNG_ERR_FILTER_INVAL;
+               goto end;
+       }
+
+       tracepoint->internal_filter.bytecode = bytecode;
+       bytecode = NULL;
+       ret_code = LTTNG_OK;
+
+error:
+end:
+       free(bytecode);
+       return ret_code;
+}
+
+static const char *lttng_event_rule_user_tracepoint_get_internal_filter(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+
+       LTTNG_ASSERT(rule);
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       return tracepoint->internal_filter.filter;
+}
+
+static const struct lttng_bytecode *
+lttng_event_rule_user_tracepoint_get_internal_filter_bytecode(
+               const struct lttng_event_rule *rule)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+
+       LTTNG_ASSERT(rule);
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       return tracepoint->internal_filter.bytecode;
+}
+
+static enum lttng_event_rule_generate_exclusions_status
+lttng_event_rule_user_tracepoint_generate_exclusions(
+               const struct lttng_event_rule *rule,
+               struct lttng_event_exclusion **_exclusions)
+{
+       unsigned int nb_exclusions = 0, i;
+       struct lttng_event_exclusion *exclusions;
+       enum lttng_event_rule_status event_rule_status;
+       enum lttng_event_rule_generate_exclusions_status ret_status;
+
+       LTTNG_ASSERT(_exclusions);
+
+       event_rule_status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
+                       rule, &nb_exclusions);
+       LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+       if (nb_exclusions == 0) {
+               /* Nothing to do. */
+               exclusions = NULL;
+               ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE;
+               goto end;
+       }
+
+       exclusions = (lttng_event_exclusion *) zmalloc(sizeof(struct lttng_event_exclusion) +
+                       (LTTNG_SYMBOL_NAME_LEN * nb_exclusions));
+       if (!exclusions) {
+               PERROR("Failed to allocate exclusions buffer");
+               ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_OUT_OF_MEMORY;
+               goto end;
+       }
+
+       exclusions->count = nb_exclusions;
+       for (i = 0; i < nb_exclusions; i++) {
+               int copy_ret;
+               const char *exclusion_str;
+
+               event_rule_status =
+                               lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                                               rule, i, &exclusion_str);
+               LTTNG_ASSERT(event_rule_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+               copy_ret = lttng_strncpy(exclusions->names[i], exclusion_str,
+                               LTTNG_SYMBOL_NAME_LEN);
+               if (copy_ret) {
+                       free(exclusions);
+                       exclusions = NULL;
+                       ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       ret_status = LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_OK;
+
+end:
+       *_exclusions = exclusions;
+       return ret_status;
+}
+
+static void destroy_lttng_exclusions_element(void *ptr)
+{
+       free(ptr);
+}
+
+static unsigned long lttng_event_rule_user_tracepoint_hash(
+               const struct lttng_event_rule *rule)
+{
+       unsigned long hash;
+       unsigned int i, exclusion_count;
+       enum lttng_event_rule_status status;
+       struct lttng_event_rule_user_tracepoint *tp_rule =
+                       container_of(rule, typeof(*tp_rule), parent);
+
+       hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT,
+                       lttng_ht_seed);
+       hash ^= hash_key_str(tp_rule->pattern, lttng_ht_seed);
+
+       if (tp_rule->filter_expression) {
+               hash ^= hash_key_str(tp_rule->filter_expression, lttng_ht_seed);
+       }
+
+       if (tp_rule->log_level_rule) {
+               hash ^= lttng_log_level_rule_hash(tp_rule->log_level_rule);
+       }
+
+       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule,
+                       &exclusion_count);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       for (i = 0; i < exclusion_count; i++) {
+               const char *exclusion;
+
+               status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                               rule, i, &exclusion);
+               LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+               hash ^= hash_key_str(exclusion, lttng_ht_seed);
+       }
+
+       return hash;
+}
+
+static enum lttng_error_code lttng_event_rule_user_tracepoint_mi_serialize(
+               const struct lttng_event_rule *rule, struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_event_rule_status status;
+       const char *filter = NULL;
+       const char *name_pattern = NULL;
+       const struct lttng_log_level_rule *log_level_rule = NULL;
+       unsigned int exclusion_count = 0;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(IS_USER_TRACEPOINT_EVENT_RULE(rule));
+
+       status = lttng_event_rule_user_tracepoint_get_name_pattern(
+                       rule, &name_pattern);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+       LTTNG_ASSERT(name_pattern);
+
+       status = lttng_event_rule_user_tracepoint_get_filter(rule, &filter);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       status = lttng_event_rule_user_tracepoint_get_log_level_rule(
+                       rule, &log_level_rule);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK ||
+                       status == LTTNG_EVENT_RULE_STATUS_UNSET);
+
+       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
+                       rule, &exclusion_count);
+       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       /* Open event rule user tracepoint element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_event_rule_user_tracepoint);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name pattern. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_event_rule_name_pattern, name_pattern);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Filter expression. */
+       if (filter != NULL) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_event_rule_filter_expression,
+                               filter);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Log level rule. */
+       if (log_level_rule) {
+               ret_code = lttng_log_level_rule_mi_serialize(
+                               log_level_rule, writer);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       if (exclusion_count != 0) {
+               int i;
+
+               /* Open the exclusion list. */
+               ret = mi_lttng_writer_open_element(writer,
+                               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions);
+               if (ret) {
+                       goto mi_error;
+               }
+
+               for (i = 0; i < exclusion_count; i++) {
+                       const char *exclusion;
+
+                       status = lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+                                       rule, i, &exclusion);
+                       LTTNG_ASSERT(status == LTTNG_EVENT_RULE_STATUS_OK);
+
+                       ret = mi_lttng_writer_write_element_string(writer,
+                                       mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion,
+                                       exclusion);
+                       if (ret) {
+                               goto mi_error;
+                       }
+               }
+
+               /* Close the list. */
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Close event rule user tracepoint element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+struct lttng_event_rule *lttng_event_rule_user_tracepoint_create(void)
+{
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_event_rule_user_tracepoint *tp_rule;
+       enum lttng_event_rule_status status;
+
+       tp_rule = (lttng_event_rule_user_tracepoint *) zmalloc(sizeof(struct lttng_event_rule_user_tracepoint));
+       if (!tp_rule) {
+               goto end;
+       }
+
+       rule = &tp_rule->parent;
+       lttng_event_rule_init(&tp_rule->parent, LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT);
+       tp_rule->parent.validate = lttng_event_rule_user_tracepoint_validate;
+       tp_rule->parent.serialize = lttng_event_rule_user_tracepoint_serialize;
+       tp_rule->parent.equal = lttng_event_rule_user_tracepoint_is_equal;
+       tp_rule->parent.destroy = lttng_event_rule_user_tracepoint_destroy;
+       tp_rule->parent.generate_filter_bytecode =
+                       lttng_event_rule_user_tracepoint_generate_filter_bytecode;
+       tp_rule->parent.get_filter =
+                       lttng_event_rule_user_tracepoint_get_internal_filter;
+       tp_rule->parent.get_filter_bytecode =
+                       lttng_event_rule_user_tracepoint_get_internal_filter_bytecode;
+       tp_rule->parent.generate_exclusions =
+                       lttng_event_rule_user_tracepoint_generate_exclusions;
+       tp_rule->parent.hash = lttng_event_rule_user_tracepoint_hash;
+       tp_rule->parent.mi_serialize = lttng_event_rule_user_tracepoint_mi_serialize;
+
+       /* Not necessary for now. */
+       tp_rule->parent.generate_lttng_event = NULL;
+
+       tp_rule->log_level_rule = NULL;
+
+       lttng_dynamic_pointer_array_init(&tp_rule->exclusions,
+                       destroy_lttng_exclusions_element);
+
+       /* Default pattern is '*'. */
+       status = lttng_event_rule_user_tracepoint_set_name_pattern(rule, "*");
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_event_rule_destroy(rule);
+               rule = NULL;
+       }
+
+end:
+       return rule;
+}
+
+ssize_t lttng_event_rule_user_tracepoint_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_event_rule **_event_rule)
+{
+       ssize_t ret, offset = 0;
+       int i;
+       enum lttng_event_rule_status status;
+       const struct lttng_event_rule_user_tracepoint_comm *tracepoint_comm;
+       const char *pattern;
+       const char *filter_expression = NULL;
+       const char **exclusions = NULL;
+       const uint32_t *exclusion_len;
+       const char *exclusion;
+       struct lttng_buffer_view current_buffer_view;
+       struct lttng_event_rule *rule = NULL;
+       struct lttng_log_level_rule *log_level_rule = NULL;
+
+       if (!_event_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, sizeof(*tracepoint_comm));
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ERR("Failed to initialize from malformed event rule tracepoint: buffer too short to contain header.");
+               ret = -1;
+               goto end;
+       }
+
+       tracepoint_comm = (typeof(tracepoint_comm)) current_buffer_view.data;
+
+       rule = lttng_event_rule_user_tracepoint_create();
+       if (!rule) {
+               ERR("Failed to create event rule user tracepoint.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip to payload. */
+       offset += current_buffer_view.size;
+
+       /* Map the pattern. */
+       current_buffer_view = lttng_buffer_view_from_view(
+                       &view->buffer, offset, tracepoint_comm->pattern_len);
+
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       pattern = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view, pattern,
+                       tracepoint_comm->pattern_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += tracepoint_comm->pattern_len;
+
+       if (!tracepoint_comm->filter_expression_len) {
+               goto skip_filter_expression;
+       }
+
+       /* Map the filter_expression. */
+       current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset,
+                       tracepoint_comm->filter_expression_len);
+       if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       filter_expression = current_buffer_view.data;
+       if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                       filter_expression,
+                       tracepoint_comm->filter_expression_len)) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the pattern. */
+       offset += tracepoint_comm->filter_expression_len;
+
+skip_filter_expression:
+       if (!tracepoint_comm->log_level_rule_len) {
+               goto skip_log_level_rule;
+       }
+
+       {
+               /* Map the log level rule. */
+               struct lttng_payload_view current_payload_view =
+                               lttng_payload_view_from_view(view, offset,
+                                               tracepoint_comm->log_level_rule_len);
+
+               ret = lttng_log_level_rule_create_from_payload(
+                               &current_payload_view, &log_level_rule);
+               if (ret < 0) {
+                       ret = -1;
+                       goto end;
+               }
+
+               LTTNG_ASSERT(ret == tracepoint_comm->log_level_rule_len);
+       }
+
+       /* Skip after the log level rule. */
+       offset += tracepoint_comm->log_level_rule_len;
+
+skip_log_level_rule:
+       for (i = 0; i < tracepoint_comm->exclusions_count; i++) {
+               current_buffer_view = lttng_buffer_view_from_view(
+                               &view->buffer, offset, sizeof(*exclusion_len));
+               if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               exclusion_len = (typeof(exclusion_len)) current_buffer_view.data;
+               offset += sizeof(*exclusion_len);
+
+               current_buffer_view = lttng_buffer_view_from_view(
+                               &view->buffer, offset, *exclusion_len);
+               if (!lttng_buffer_view_is_valid(&current_buffer_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               exclusion = current_buffer_view.data;
+               if (!lttng_buffer_view_contains_string(&current_buffer_view,
+                               exclusion, *exclusion_len)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               status = lttng_event_rule_user_tracepoint_add_name_pattern_exclusion(rule, exclusion);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to add event rule user tracepoint exclusion \"%s\".",
+                                       exclusion);
+                       ret = -1;
+                       goto end;
+               }
+
+               /* Skip to next exclusion. */
+               offset += *exclusion_len;
+       }
+
+       status = lttng_event_rule_user_tracepoint_set_name_pattern(rule, pattern);
+       if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+               ERR("Failed to set event rule user tracepoint pattern.");
+               ret = -1;
+               goto end;
+       }
+
+       if (filter_expression) {
+               status = lttng_event_rule_user_tracepoint_set_filter(
+                               rule, filter_expression);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule user tracepoint pattern.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (log_level_rule) {
+               status = lttng_event_rule_user_tracepoint_set_log_level_rule(
+                               rule, log_level_rule);
+               if (status != LTTNG_EVENT_RULE_STATUS_OK) {
+                       ERR("Failed to set event rule user tracepoint log level rule.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       *_event_rule = rule;
+       rule = NULL;
+       ret = offset;
+end:
+       free(exclusions);
+       lttng_log_level_rule_destroy(log_level_rule);
+       lttng_event_rule_destroy(rule);
+       return ret;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_name_pattern(
+               struct lttng_event_rule *rule, const char *pattern)
+{
+       char *pattern_copy = NULL;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !pattern ||
+                       strlen(pattern) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       pattern_copy = strdup(pattern);
+       if (!pattern_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Normalize the pattern. */
+       strutils_normalize_star_glob_pattern(pattern_copy);
+
+       free(tracepoint->pattern);
+
+       tracepoint->pattern = pattern_copy;
+       pattern_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern(
+               const struct lttng_event_rule *rule, const char **pattern)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       if (!tracepoint->pattern) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *pattern = tracepoint->pattern;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_filter(
+               struct lttng_event_rule *rule, const char *expression)
+{
+       char *expression_copy = NULL;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !expression ||
+                       strlen(expression) == 0) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       expression_copy = strdup(expression);
+       if (!expression_copy) {
+               PERROR("Failed to copy filter expression");
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (tracepoint->filter_expression) {
+               free(tracepoint->filter_expression);
+       }
+
+       tracepoint->filter_expression = expression_copy;
+       expression_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_filter(
+               const struct lttng_event_rule *rule, const char **expression)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !expression) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       if (!tracepoint->filter_expression) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *expression = tracepoint->filter_expression;
+end:
+       return status;
+}
+
+static bool log_level_rule_valid(const struct lttng_log_level_rule *rule)
+{
+       bool valid = false;
+       enum lttng_log_level_rule_status status;
+       int level;
+
+       switch (lttng_log_level_rule_get_type(rule)) {
+       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+               status = lttng_log_level_rule_exactly_get_level(rule, &level);
+               break;
+       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+               status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                               rule, &level);
+               break;
+       default:
+               abort();
+       }
+
+       LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
+
+       if (level < LTTNG_LOGLEVEL_EMERG) {
+               /* Invalid. */
+               goto end;
+       }
+       if (level > LTTNG_LOGLEVEL_DEBUG) {
+               /* Invalid. */
+               goto end;
+       }
+
+       valid = true;
+
+end:
+       return valid;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_set_log_level_rule(
+               struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule *log_level_rule)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+       struct lttng_log_level_rule *copy = NULL;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       if (!log_level_rule_valid(log_level_rule)) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       copy = lttng_log_level_rule_copy(log_level_rule);
+       if (copy == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       if (tracepoint->log_level_rule) {
+               lttng_log_level_rule_destroy(tracepoint->log_level_rule);
+       }
+
+       tracepoint->log_level_rule = copy;
+
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_log_level_rule(
+               const struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule **log_level_rule
+               )
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !log_level_rule) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       if (tracepoint->log_level_rule == NULL) {
+               status = LTTNG_EVENT_RULE_STATUS_UNSET;
+               goto end;
+       }
+
+       *log_level_rule = tracepoint->log_level_rule;
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_add_name_pattern_exclusion(
+               struct lttng_event_rule *rule,
+               const char *exclusion)
+{
+       int ret;
+       char *exclusion_copy = NULL;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) ||
+                       !exclusion) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+
+       if (strlen(exclusion) >= LTTNG_SYMBOL_NAME_LEN) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       exclusion_copy = strdup(exclusion);
+       if (!exclusion_copy) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_add_pointer(&tracepoint->exclusions,
+                       exclusion_copy);
+       if (ret < 0) {
+               status = LTTNG_EVENT_RULE_STATUS_ERROR;
+               goto end;
+       }
+
+       exclusion_copy = NULL;
+end:
+       free(exclusion_copy);
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(
+               const struct lttng_event_rule *rule, unsigned int *count)
+{
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !count) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       *count = lttng_dynamic_pointer_array_get_count(&tracepoint->exclusions);
+end:
+       return status;
+}
+
+enum lttng_event_rule_status lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_at_index(
+               const struct lttng_event_rule *rule,
+               unsigned int index,
+               const char **exclusion)
+{
+       unsigned int count;
+       struct lttng_event_rule_user_tracepoint *tracepoint;
+       enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK;
+
+       if (!rule || !IS_USER_TRACEPOINT_EVENT_RULE(rule) || !exclusion) {
+               status = LTTNG_EVENT_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       tracepoint = container_of(
+                       rule, struct lttng_event_rule_user_tracepoint, parent);
+       if (lttng_event_rule_user_tracepoint_get_name_pattern_exclusion_count(rule, &count) !=
+                       LTTNG_EVENT_RULE_STATUS_OK) {
+               goto end;
+       }
+
+       if (index >= count) {
+               goto end;
+       }
+
+       *exclusion = (const char *) lttng_dynamic_pointer_array_get_pointer(
+                       &tracepoint->exclusions, index);
+end:
+       return status;
+}
diff --git a/src/common/event.c b/src/common/event.c
deleted file mode 100644 (file)
index f1792f9..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/event-internal.h>
-#include <common/error.h>
-
-struct lttng_event *lttng_event_copy(const struct lttng_event *event)
-{
-       struct lttng_event *new_event;
-       struct lttng_event_extended *new_event_extended;
-
-       new_event = zmalloc(sizeof(*event));
-       if (!new_event) {
-               PERROR("Error allocating event structure");
-               goto end;
-       }
-
-       /* Copy the content of the old event. */
-       memcpy(new_event, event, sizeof(*event));
-
-       /*
-        * We need to create a new extended since the previous pointer is now
-        * invalid.
-        */
-       new_event_extended = zmalloc(sizeof(*new_event_extended));
-       if (!new_event_extended) {
-               PERROR("Error allocating event extended structure");
-               goto error;
-       }
-
-       new_event->extended.ptr = new_event_extended;
-end:
-       return new_event;
-error:
-       free(new_event);
-       new_event = NULL;
-       goto end;
-}
diff --git a/src/common/event.cpp b/src/common/event.cpp
new file mode 100644 (file)
index 0000000..6b22fec
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/event-internal.h>
+#include <common/error.h>
+
+struct lttng_event *lttng_event_copy(const struct lttng_event *event)
+{
+       struct lttng_event *new_event;
+       struct lttng_event_extended *new_event_extended;
+
+       new_event = (lttng_event *) zmalloc(sizeof(*event));
+       if (!new_event) {
+               PERROR("Error allocating event structure");
+               goto end;
+       }
+
+       /* Copy the content of the old event. */
+       memcpy(new_event, event, sizeof(*event));
+
+       /*
+        * We need to create a new extended since the previous pointer is now
+        * invalid.
+        */
+       new_event_extended = (lttng_event_extended *) zmalloc(sizeof(*new_event_extended));
+       if (!new_event_extended) {
+               PERROR("Error allocating event extended structure");
+               goto error;
+       }
+
+       new_event->extended.ptr = new_event_extended;
+end:
+       return new_event;
+error:
+       free(new_event);
+       new_event = NULL;
+       goto end;
+}
diff --git a/src/common/fd-handle.c b/src/common/fd-handle.c
deleted file mode 100644 (file)
index b2b7efd..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <unistd.h>
-#include <urcu/ref.h>
-
-#include "fd-handle.h"
-#include <common/error.h>
-
-struct fd_handle {
-       struct urcu_ref ref;
-       int fd;
-};
-
-static void fd_handle_release(struct urcu_ref *ref)
-{
-       int ret;
-       struct fd_handle *handle = container_of(ref, struct fd_handle, ref);
-
-       LTTNG_ASSERT(handle->fd >= 0);
-       ret = close(handle->fd);
-       if (ret == -1) {
-               PERROR("Failed to close file descriptor of fd_handle upon release: fd = %d",
-                               handle->fd);
-       }
-
-       free(handle);
-}
-
-struct fd_handle *fd_handle_create(int fd)
-{
-       struct fd_handle *handle = NULL;
-
-       if (fd < 0) {
-               ERR("Attempted to create an fd_handle from an invalid file descriptor: fd = %d",
-                               fd);
-               goto end;
-       }
-
-       handle = zmalloc(sizeof(*handle));
-       if (!handle) {
-               PERROR("Failed to allocate fd_handle");
-               goto end;
-       }
-
-       urcu_ref_init(&handle->ref);
-       handle->fd = fd;
-
-end:
-       return handle;
-}
-
-void fd_handle_get(struct fd_handle *handle)
-{
-       if (!handle) {
-               return;
-       }
-
-       urcu_ref_get(&handle->ref);
-}
-
-void fd_handle_put(struct fd_handle *handle)
-{
-       if (!handle) {
-               return;
-       }
-
-       urcu_ref_put(&handle->ref, fd_handle_release);
-}
-
-int fd_handle_get_fd(struct fd_handle *handle)
-{
-       LTTNG_ASSERT(handle);
-       return handle->fd;
-}
-
-struct fd_handle *fd_handle_copy(const struct fd_handle *handle)
-{
-       struct fd_handle *new_handle = NULL;
-       const int new_fd = dup(handle->fd);
-
-       if (new_fd < 0) {
-               PERROR("Failed to duplicate file descriptor while copying fd_handle: fd = %d", handle->fd);
-               goto end;
-       }
-
-       new_handle = fd_handle_create(new_fd);
-end:
-       return new_handle;
-}
diff --git a/src/common/fd-handle.cpp b/src/common/fd-handle.cpp
new file mode 100644 (file)
index 0000000..d8b5b78
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <unistd.h>
+#include <urcu/ref.h>
+
+#include "fd-handle.h"
+#include <common/error.h>
+
+struct fd_handle {
+       struct urcu_ref ref;
+       int fd;
+};
+
+static void fd_handle_release(struct urcu_ref *ref)
+{
+       int ret;
+       struct fd_handle *handle = container_of(ref, struct fd_handle, ref);
+
+       LTTNG_ASSERT(handle->fd >= 0);
+       ret = close(handle->fd);
+       if (ret == -1) {
+               PERROR("Failed to close file descriptor of fd_handle upon release: fd = %d",
+                               handle->fd);
+       }
+
+       free(handle);
+}
+
+struct fd_handle *fd_handle_create(int fd)
+{
+       struct fd_handle *handle = NULL;
+
+       if (fd < 0) {
+               ERR("Attempted to create an fd_handle from an invalid file descriptor: fd = %d",
+                               fd);
+               goto end;
+       }
+
+       handle = (fd_handle *) zmalloc(sizeof(*handle));
+       if (!handle) {
+               PERROR("Failed to allocate fd_handle");
+               goto end;
+       }
+
+       urcu_ref_init(&handle->ref);
+       handle->fd = fd;
+
+end:
+       return handle;
+}
+
+void fd_handle_get(struct fd_handle *handle)
+{
+       if (!handle) {
+               return;
+       }
+
+       urcu_ref_get(&handle->ref);
+}
+
+void fd_handle_put(struct fd_handle *handle)
+{
+       if (!handle) {
+               return;
+       }
+
+       urcu_ref_put(&handle->ref, fd_handle_release);
+}
+
+int fd_handle_get_fd(struct fd_handle *handle)
+{
+       LTTNG_ASSERT(handle);
+       return handle->fd;
+}
+
+struct fd_handle *fd_handle_copy(const struct fd_handle *handle)
+{
+       struct fd_handle *new_handle = NULL;
+       const int new_fd = dup(handle->fd);
+
+       if (new_fd < 0) {
+               PERROR("Failed to duplicate file descriptor while copying fd_handle: fd = %d", handle->fd);
+               goto end;
+       }
+
+       new_handle = fd_handle_create(new_fd);
+end:
+       return new_handle;
+}
diff --git a/src/common/filter.c b/src/common/filter.c
deleted file mode 100644 (file)
index 9195808..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "filter.h"
-#include <stddef.h>
-
-struct bytecode_symbol_iterator {
-       /* No ownership of bytecode is taken. */
-       char *bytecode;
-       size_t offset, len;
-};
-
-struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
-               struct lttng_bytecode *bytecode)
-{
-       struct bytecode_symbol_iterator *it = NULL;
-
-       if (!bytecode) {
-               goto end;
-       }
-
-       it = zmalloc(sizeof(*it));
-       if (!it) {
-               goto end;
-       }
-
-       it->bytecode = bytecode->data;
-       it->offset = bytecode->reloc_table_offset;
-       it->len = bytecode->len;
-end:
-       return it;
-}
-
-int bytecode_symbol_iterator_next(struct bytecode_symbol_iterator *it)
-{
-       int ret;
-       size_t len;
-
-       if (!it || it->offset >= it->len) {
-               ret = -1;
-               goto end;
-       }
-
-       len = strlen(it->bytecode + it->offset + sizeof(uint16_t)) + 1;
-       it->offset += len + sizeof(uint16_t);
-       ret = it->offset >= it->len ? -1 : 0;
-end:
-       return ret;
-}
-
-int bytecode_symbol_iterator_get_type(struct bytecode_symbol_iterator *it)
-{
-       int ret;
-
-       if (!it) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = *((uint16_t *) (it->bytecode + it->offset));
-end:
-       return ret;
- }
-
-const char *bytecode_symbol_iterator_get_name(
-               struct bytecode_symbol_iterator *it)
-{
-       const char *ret = NULL;
-
-       if (!it) {
-               goto end;
-       }
-
-       ret = it->bytecode + it->offset + sizeof(uint16_t);
-end:
-       return ret;
-}
-
-void bytecode_symbol_iterator_destroy(struct bytecode_symbol_iterator *it)
-{
-       free(it);
-}
diff --git a/src/common/filter.cpp b/src/common/filter.cpp
new file mode 100644 (file)
index 0000000..f74b637
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "filter.h"
+#include <stddef.h>
+
+struct bytecode_symbol_iterator {
+       /* No ownership of bytecode is taken. */
+       char *bytecode;
+       size_t offset, len;
+};
+
+struct bytecode_symbol_iterator *bytecode_symbol_iterator_create(
+               struct lttng_bytecode *bytecode)
+{
+       struct bytecode_symbol_iterator *it = NULL;
+
+       if (!bytecode) {
+               goto end;
+       }
+
+       it = (bytecode_symbol_iterator *) zmalloc(sizeof(*it));
+       if (!it) {
+               goto end;
+       }
+
+       it->bytecode = bytecode->data;
+       it->offset = bytecode->reloc_table_offset;
+       it->len = bytecode->len;
+end:
+       return it;
+}
+
+int bytecode_symbol_iterator_next(struct bytecode_symbol_iterator *it)
+{
+       int ret;
+       size_t len;
+
+       if (!it || it->offset >= it->len) {
+               ret = -1;
+               goto end;
+       }
+
+       len = strlen(it->bytecode + it->offset + sizeof(uint16_t)) + 1;
+       it->offset += len + sizeof(uint16_t);
+       ret = it->offset >= it->len ? -1 : 0;
+end:
+       return ret;
+}
+
+int bytecode_symbol_iterator_get_type(struct bytecode_symbol_iterator *it)
+{
+       int ret;
+
+       if (!it) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = *((uint16_t *) (it->bytecode + it->offset));
+end:
+       return ret;
+ }
+
+const char *bytecode_symbol_iterator_get_name(
+               struct bytecode_symbol_iterator *it)
+{
+       const char *ret = NULL;
+
+       if (!it) {
+               goto end;
+       }
+
+       ret = it->bytecode + it->offset + sizeof(uint16_t);
+end:
+       return ret;
+}
+
+void bytecode_symbol_iterator_destroy(struct bytecode_symbol_iterator *it)
+{
+       free(it);
+}
diff --git a/src/common/fs-handle.c b/src/common/fs-handle.c
deleted file mode 100644 (file)
index bed0010..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/fs-handle-internal.h>
-#include <common/fs-handle.h>
-#include <common/readwrite.h>
-
-int fs_handle_get_fd(struct fs_handle *handle)
-{
-       return handle->get_fd(handle);
-}
-
-void fs_handle_put_fd(struct fs_handle *handle)
-{
-       return handle->put_fd(handle);
-}
-
-int fs_handle_unlink(struct fs_handle *handle)
-{
-       return handle->unlink(handle);
-}
-
-int fs_handle_close(struct fs_handle *handle)
-{
-       return handle->close(handle);
-}
-
-ssize_t fs_handle_read(struct fs_handle *handle, void *buf, size_t count)
-{
-       ssize_t ret;
-       const int fd = fs_handle_get_fd(handle);
-
-       if (fd < 0) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_read(fd, buf, count);
-       fs_handle_put_fd(handle);
-end:
-       return ret;
-}
-
-ssize_t fs_handle_write(struct fs_handle *handle, const void *buf, size_t count)
-{
-       ssize_t ret;
-       const int fd = fs_handle_get_fd(handle);
-
-       if (fd < 0) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_write(fd, buf, count);
-       fs_handle_put_fd(handle);
-end:
-       return ret;
-}
-
-int fs_handle_truncate(struct fs_handle *handle, off_t offset)
-{
-       int ret;
-       const int fd = fs_handle_get_fd(handle);
-
-       if (fd < 0) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = ftruncate(fd, offset);
-       fs_handle_put_fd(handle);
-end:
-       return ret;
-}
-
-off_t fs_handle_seek(struct fs_handle *handle, off_t offset, int whence)
-{
-       off_t ret;
-       const int fd = fs_handle_get_fd(handle);
-
-       if (fd < 0) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lseek(fd, offset, whence);
-       fs_handle_put_fd(handle);
-end:
-       return ret;
-}
diff --git a/src/common/fs-handle.cpp b/src/common/fs-handle.cpp
new file mode 100644 (file)
index 0000000..bed0010
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/fs-handle-internal.h>
+#include <common/fs-handle.h>
+#include <common/readwrite.h>
+
+int fs_handle_get_fd(struct fs_handle *handle)
+{
+       return handle->get_fd(handle);
+}
+
+void fs_handle_put_fd(struct fs_handle *handle)
+{
+       return handle->put_fd(handle);
+}
+
+int fs_handle_unlink(struct fs_handle *handle)
+{
+       return handle->unlink(handle);
+}
+
+int fs_handle_close(struct fs_handle *handle)
+{
+       return handle->close(handle);
+}
+
+ssize_t fs_handle_read(struct fs_handle *handle, void *buf, size_t count)
+{
+       ssize_t ret;
+       const int fd = fs_handle_get_fd(handle);
+
+       if (fd < 0) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_read(fd, buf, count);
+       fs_handle_put_fd(handle);
+end:
+       return ret;
+}
+
+ssize_t fs_handle_write(struct fs_handle *handle, const void *buf, size_t count)
+{
+       ssize_t ret;
+       const int fd = fs_handle_get_fd(handle);
+
+       if (fd < 0) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_write(fd, buf, count);
+       fs_handle_put_fd(handle);
+end:
+       return ret;
+}
+
+int fs_handle_truncate(struct fs_handle *handle, off_t offset)
+{
+       int ret;
+       const int fd = fs_handle_get_fd(handle);
+
+       if (fd < 0) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = ftruncate(fd, offset);
+       fs_handle_put_fd(handle);
+end:
+       return ret;
+}
+
+off_t fs_handle_seek(struct fs_handle *handle, off_t offset, int whence)
+{
+       off_t ret;
+       const int fd = fs_handle_get_fd(handle);
+
+       if (fd < 0) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lseek(fd, offset, whence);
+       fs_handle_put_fd(handle);
+end:
+       return ret;
+}
diff --git a/src/common/futex.c b/src/common/futex.c
deleted file mode 100644 (file)
index d0f95ab..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <limits.h>
-#include <unistd.h>
-#include <urcu.h>
-#include <urcu/futex.h>
-
-#include <common/common.h>
-
-#include "futex.h"
-
-/*
- * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the
- * "nto1" added to all function signature.
- *
- * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu
- * git tree for a detail example of this scheme being used. futex_async() is
- * the urcu wrapper over the futex() sycall.
- *
- * There is also a formal verification available in the git tree.
- *
- *   branch: formal-model
- *   commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a
- *
- * Ref: git://git.lttng.org/userspace-rcu.git
- */
-
-/*
- * Update futex according to active or not. This scheme is used to wake every
- * libust waiting on the shared memory map futex hence the INT_MAX used in the
- * futex() call. If active, we set the value and wake everyone else we indicate
- * that we are gone (cleanup() case).
- */
-void futex_wait_update(int32_t *futex, int active)
-{
-       if (active) {
-               uatomic_set(futex, 1);
-               if (futex_async(futex, FUTEX_WAKE,
-                               INT_MAX, NULL, NULL, 0) < 0) {
-                       PERROR("futex_async");
-                       abort();
-               }
-       } else {
-               uatomic_set(futex, 0);
-       }
-
-       DBG("Futex wait update active %d", active);
-}
-
-/*
- * Prepare futex.
- */
-void futex_nto1_prepare(int32_t *futex)
-{
-       uatomic_set(futex, -1);
-       cmm_smp_mb();
-
-       DBG("Futex n to 1 prepare done");
-}
-
-/*
- * Wait futex.
- */
-void futex_nto1_wait(int32_t *futex)
-{
-       cmm_smp_mb();
-
-       if (uatomic_read(futex) != -1)
-               goto end;
-       while (futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0)) {
-               switch (errno) {
-               case EWOULDBLOCK:
-                       /* Value already changed. */
-                       goto end;
-               case EINTR:
-                       /* Retry if interrupted by signal. */
-                       break;  /* Get out of switch. */
-               default:
-                       /* Unexpected error. */
-                       PERROR("futex_async");
-                       abort();
-               }
-       }
-end:
-       DBG("Futex n to 1 wait done");
-}
-
-/*
- * Wake 1 futex.
- */
-void futex_nto1_wake(int32_t *futex)
-{
-       if (caa_unlikely(uatomic_read(futex) != -1))
-               goto end;
-       uatomic_set(futex, 0);
-       if (futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0) < 0) {
-               PERROR("futex_async");
-               abort();
-       }
-end:
-       DBG("Futex n to 1 wake done");
-}
diff --git a/src/common/futex.cpp b/src/common/futex.cpp
new file mode 100644 (file)
index 0000000..d0f95ab
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <limits.h>
+#include <unistd.h>
+#include <urcu.h>
+#include <urcu/futex.h>
+
+#include <common/common.h>
+
+#include "futex.h"
+
+/*
+ * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the
+ * "nto1" added to all function signature.
+ *
+ * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu
+ * git tree for a detail example of this scheme being used. futex_async() is
+ * the urcu wrapper over the futex() sycall.
+ *
+ * There is also a formal verification available in the git tree.
+ *
+ *   branch: formal-model
+ *   commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a
+ *
+ * Ref: git://git.lttng.org/userspace-rcu.git
+ */
+
+/*
+ * Update futex according to active or not. This scheme is used to wake every
+ * libust waiting on the shared memory map futex hence the INT_MAX used in the
+ * futex() call. If active, we set the value and wake everyone else we indicate
+ * that we are gone (cleanup() case).
+ */
+void futex_wait_update(int32_t *futex, int active)
+{
+       if (active) {
+               uatomic_set(futex, 1);
+               if (futex_async(futex, FUTEX_WAKE,
+                               INT_MAX, NULL, NULL, 0) < 0) {
+                       PERROR("futex_async");
+                       abort();
+               }
+       } else {
+               uatomic_set(futex, 0);
+       }
+
+       DBG("Futex wait update active %d", active);
+}
+
+/*
+ * Prepare futex.
+ */
+void futex_nto1_prepare(int32_t *futex)
+{
+       uatomic_set(futex, -1);
+       cmm_smp_mb();
+
+       DBG("Futex n to 1 prepare done");
+}
+
+/*
+ * Wait futex.
+ */
+void futex_nto1_wait(int32_t *futex)
+{
+       cmm_smp_mb();
+
+       if (uatomic_read(futex) != -1)
+               goto end;
+       while (futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0)) {
+               switch (errno) {
+               case EWOULDBLOCK:
+                       /* Value already changed. */
+                       goto end;
+               case EINTR:
+                       /* Retry if interrupted by signal. */
+                       break;  /* Get out of switch. */
+               default:
+                       /* Unexpected error. */
+                       PERROR("futex_async");
+                       abort();
+               }
+       }
+end:
+       DBG("Futex n to 1 wait done");
+}
+
+/*
+ * Wake 1 futex.
+ */
+void futex_nto1_wake(int32_t *futex)
+{
+       if (caa_unlikely(uatomic_read(futex) != -1))
+               goto end;
+       uatomic_set(futex, 0);
+       if (futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0) < 0) {
+               PERROR("futex_async");
+               abort();
+       }
+end:
+       DBG("Futex n to 1 wake done");
+}
diff --git a/src/common/index-allocator.c b/src/common/index-allocator.c
deleted file mode 100644 (file)
index 8c73218..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include <inttypes.h>
-
-#include <urcu.h>
-#include <urcu/list.h>
-
-#include "macros.h"
-#include "error.h"
-
-#include "index-allocator.h"
-
-struct lttng_index_allocator {
-       struct cds_list_head unused_list;
-       uint64_t size;
-       uint64_t position;
-       uint64_t nb_allocated_indexes;
-};
-
-struct lttng_index {
-       uint64_t index;
-       struct cds_list_head head;
-};
-
-struct lttng_index_allocator *lttng_index_allocator_create(
-               uint64_t index_count)
-{
-       struct lttng_index_allocator *allocator = NULL;
-
-       allocator = zmalloc(sizeof(*allocator));
-       if (!allocator) {
-               PERROR("Failed to allocate index allocator");
-               goto end;
-       }
-
-       allocator->size = index_count;
-       allocator->position = 0;
-       allocator->nb_allocated_indexes = 0;
-
-       CDS_INIT_LIST_HEAD(&allocator->unused_list);
-
-end:
-       return allocator;
-}
-
-uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator)
-{
-       return allocator->nb_allocated_indexes;
-}
-
-enum lttng_index_allocator_status lttng_index_allocator_alloc(
-               struct lttng_index_allocator *allocator,
-               uint64_t *allocated_index)
-{
-       enum lttng_index_allocator_status status =
-                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
-
-       if (cds_list_empty(&allocator->unused_list)) {
-               if (allocator->position >= allocator->size) {
-                       /* No indices left. */
-                       status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY;
-                       goto end;
-               }
-
-               *allocated_index = allocator->position++;
-       } else {
-               struct lttng_index *index;
-
-               index = cds_list_first_entry(&allocator->unused_list,
-                               typeof(*index), head);
-               cds_list_del(&index->head);
-               *allocated_index = index->index;
-               free(index);
-       }
-
-       allocator->nb_allocated_indexes++;
-end:
-       return status;
-}
-
-enum lttng_index_allocator_status lttng_index_allocator_release(
-               struct lttng_index_allocator *allocator, uint64_t idx)
-{
-       struct lttng_index *index = NULL;
-       enum lttng_index_allocator_status status =
-                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
-
-       LTTNG_ASSERT(idx < allocator->size);
-
-       index = zmalloc(sizeof(*index));
-       if (!index) {
-               PERROR("Failed to allocate free index queue");
-               status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       index->index = idx;
-       cds_list_add_tail(&index->head, &allocator->unused_list);
-       allocator->nb_allocated_indexes--;
-
-end:
-       return status;
-}
-
-void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator)
-{
-       struct lttng_index *index = NULL, *tmp_index = NULL;
-
-       if (!allocator) {
-               return;
-       }
-
-       if (lttng_index_allocator_get_index_count(allocator) > 0) {
-               WARN("Destroying index allocator with %" PRIu64
-                               " slot indexes still in use",
-                               lttng_index_allocator_get_index_count(allocator));
-       }
-
-       cds_list_for_each_entry_safe(index, tmp_index,
-                       &allocator->unused_list, head) {
-               cds_list_del(&index->head);
-               free(index);
-       }
-
-       free(allocator);
-}
diff --git a/src/common/index-allocator.cpp b/src/common/index-allocator.cpp
new file mode 100644 (file)
index 0000000..c48d708
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <inttypes.h>
+
+#include <urcu.h>
+#include <urcu/list.h>
+
+#include "macros.h"
+#include "error.h"
+
+#include "index-allocator.h"
+
+struct lttng_index_allocator {
+       struct cds_list_head unused_list;
+       uint64_t size;
+       uint64_t position;
+       uint64_t nb_allocated_indexes;
+};
+
+struct lttng_index {
+       uint64_t index;
+       struct cds_list_head head;
+};
+
+struct lttng_index_allocator *lttng_index_allocator_create(
+               uint64_t index_count)
+{
+       struct lttng_index_allocator *allocator = NULL;
+
+       allocator = (lttng_index_allocator *) zmalloc(sizeof(*allocator));
+       if (!allocator) {
+               PERROR("Failed to allocate index allocator");
+               goto end;
+       }
+
+       allocator->size = index_count;
+       allocator->position = 0;
+       allocator->nb_allocated_indexes = 0;
+
+       CDS_INIT_LIST_HEAD(&allocator->unused_list);
+
+end:
+       return allocator;
+}
+
+uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator)
+{
+       return allocator->nb_allocated_indexes;
+}
+
+enum lttng_index_allocator_status lttng_index_allocator_alloc(
+               struct lttng_index_allocator *allocator,
+               uint64_t *allocated_index)
+{
+       enum lttng_index_allocator_status status =
+                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
+
+       if (cds_list_empty(&allocator->unused_list)) {
+               if (allocator->position >= allocator->size) {
+                       /* No indices left. */
+                       status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY;
+                       goto end;
+               }
+
+               *allocated_index = allocator->position++;
+       } else {
+               struct lttng_index *index;
+
+               index = cds_list_first_entry(&allocator->unused_list,
+                               typeof(*index), head);
+               cds_list_del(&index->head);
+               *allocated_index = index->index;
+               free(index);
+       }
+
+       allocator->nb_allocated_indexes++;
+end:
+       return status;
+}
+
+enum lttng_index_allocator_status lttng_index_allocator_release(
+               struct lttng_index_allocator *allocator, uint64_t idx)
+{
+       struct lttng_index *index = NULL;
+       enum lttng_index_allocator_status status =
+                       LTTNG_INDEX_ALLOCATOR_STATUS_OK;
+
+       LTTNG_ASSERT(idx < allocator->size);
+
+       index = (lttng_index *) zmalloc(sizeof(*index));
+       if (!index) {
+               PERROR("Failed to allocate free index queue");
+               status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       index->index = idx;
+       cds_list_add_tail(&index->head, &allocator->unused_list);
+       allocator->nb_allocated_indexes--;
+
+end:
+       return status;
+}
+
+void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator)
+{
+       struct lttng_index *index = NULL, *tmp_index = NULL;
+
+       if (!allocator) {
+               return;
+       }
+
+       if (lttng_index_allocator_get_index_count(allocator) > 0) {
+               WARN("Destroying index allocator with %" PRIu64
+                               " slot indexes still in use",
+                               lttng_index_allocator_get_index_count(allocator));
+       }
+
+       cds_list_for_each_entry_safe(index, tmp_index,
+                       &allocator->unused_list, head) {
+               cds_list_del(&index->head);
+               free(index);
+       }
+
+       free(allocator);
+}
diff --git a/src/common/kernel-probe.c b/src/common/kernel-probe.c
deleted file mode 100644 (file)
index 33ba00f..0000000
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "lttng/lttng-error.h"
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <fcntl.h>
-#include <lttng/constant.h>
-#include <lttng/kernel-probe-internal.h>
-#include <lttng/kernel-probe.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-static
-int lttng_kernel_probe_location_address_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct lttng_payload *payload);
-
-static
-int lttng_kernel_probe_location_symbol_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct lttng_payload *payload);
-
-static
-bool lttng_kernel_probe_location_address_is_equal(
-               const struct lttng_kernel_probe_location *a,
-               const struct lttng_kernel_probe_location *b);
-
-static
-bool lttng_kernel_probe_location_symbol_is_equal(
-               const struct lttng_kernel_probe_location *a,
-               const struct lttng_kernel_probe_location *b);
-
-static
-unsigned long lttng_kernel_probe_location_address_hash(
-               const struct lttng_kernel_probe_location *location);
-
-static
-unsigned long lttng_kernel_probe_location_symbol_hash(
-               const struct lttng_kernel_probe_location *location);
-
-static
-enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct mi_writer *writer);
-
-static
-enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct mi_writer *writer);
-
-enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type(
-               const struct lttng_kernel_probe_location *location)
-{
-       return location ? location->type :
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN;
-}
-
-static
-void lttng_kernel_probe_location_address_destroy(
-               struct lttng_kernel_probe_location *location)
-{
-       LTTNG_ASSERT(location);
-       free(location);
-}
-
-static
-void lttng_kernel_probe_location_symbol_destroy(
-               struct lttng_kernel_probe_location *location)
-{
-       struct lttng_kernel_probe_location_symbol *location_symbol = NULL;
-
-       LTTNG_ASSERT(location);
-
-       location_symbol = container_of(location,
-                       struct lttng_kernel_probe_location_symbol,
-                       parent);
-
-       LTTNG_ASSERT(location_symbol);
-
-       free(location_symbol->symbol_name);
-       free(location);
-}
-
-void lttng_kernel_probe_location_destroy(
-               struct lttng_kernel_probe_location *location)
-{
-       if (!location) {
-               return;
-       }
-
-       switch (location->type) {
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
-               lttng_kernel_probe_location_address_destroy(location);
-               break;
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
-               lttng_kernel_probe_location_symbol_destroy(location);
-               break;
-       default:
-               abort();
-       }
-}
-
-struct lttng_kernel_probe_location *
-lttng_kernel_probe_location_address_create(uint64_t address)
-{
-       struct lttng_kernel_probe_location *ret = NULL;
-       struct lttng_kernel_probe_location_address *location;
-
-       location = zmalloc(sizeof(*location));
-       if (!location) {
-               PERROR("Error allocating userspace probe location.");
-               goto end;
-       }
-
-       location->address = address;
-
-       ret = &location->parent;
-       ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS;
-       ret->equal = lttng_kernel_probe_location_address_is_equal;
-       ret->serialize = lttng_kernel_probe_location_address_serialize;
-       ret->hash = lttng_kernel_probe_location_address_hash;
-       ret->mi_serialize = lttng_kernel_probe_location_address_mi_serialize;
-
-end:
-       return ret;
-}
-
-struct lttng_kernel_probe_location *
-lttng_kernel_probe_location_symbol_create(const char *symbol_name,
-               uint64_t offset)
-{
-       char *symbol_name_copy = NULL;
-       struct lttng_kernel_probe_location *ret = NULL;
-       struct lttng_kernel_probe_location_symbol *location;
-
-       if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) {
-               goto error;
-       }
-
-       symbol_name_copy = strdup(symbol_name);
-       if (!symbol_name_copy) {
-               PERROR("Failed to copy symbol name '%s'", symbol_name);
-               goto error;
-       }
-
-       location = zmalloc(sizeof(*location));
-       if (!location) {
-               PERROR("Failed to allocate kernel symbol probe location");
-               goto error;
-       }
-
-       location->symbol_name = symbol_name_copy;
-       location->offset = offset;
-
-       ret = &location->parent;
-       ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET;
-       ret->equal = lttng_kernel_probe_location_symbol_is_equal;
-       ret->serialize = lttng_kernel_probe_location_symbol_serialize;
-       ret->hash = lttng_kernel_probe_location_symbol_hash;
-       ret->mi_serialize = lttng_kernel_probe_location_symbol_mi_serialize;
-       goto end;
-
-error:
-       free(symbol_name_copy);
-end:
-       return ret;
-}
-
-enum lttng_kernel_probe_location_status
-lttng_kernel_probe_location_address_get_address(
-               const struct lttng_kernel_probe_location *location,
-               uint64_t *offset)
-{
-       enum lttng_kernel_probe_location_status ret =
-                       LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK;
-       struct lttng_kernel_probe_location_address *address_location;
-
-       LTTNG_ASSERT(offset);
-
-       if (!location || lttng_kernel_probe_location_get_type(location) !=
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       address_location = container_of(location,
-                       struct lttng_kernel_probe_location_address, parent);
-       *offset = address_location->address;
-end:
-       return ret;
-}
-
-const char *lttng_kernel_probe_location_symbol_get_name(
-               const struct lttng_kernel_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_kernel_probe_location_symbol *symbol_location;
-
-       if (!location || lttng_kernel_probe_location_get_type(location) !=
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       symbol_location = container_of(location,
-                       struct lttng_kernel_probe_location_symbol, parent);
-       ret = symbol_location->symbol_name;
-end:
-       return ret;
-}
-
-enum lttng_kernel_probe_location_status
-lttng_kernel_probe_location_symbol_get_offset(
-               const struct lttng_kernel_probe_location *location,
-               uint64_t *offset)
-{
-       enum lttng_kernel_probe_location_status ret =
-                       LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK;
-       struct lttng_kernel_probe_location_symbol *symbol_location;
-
-       LTTNG_ASSERT(offset);
-
-       if (!location || lttng_kernel_probe_location_get_type(location) !=
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       symbol_location = container_of(location,
-                       struct lttng_kernel_probe_location_symbol, parent);
-       *offset = symbol_location->offset;
-end:
-       return ret;
-}
-
-static
-int lttng_kernel_probe_location_symbol_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t symbol_name_len;
-       size_t original_payload_size;
-       struct lttng_kernel_probe_location_symbol *location_symbol;
-       struct lttng_kernel_probe_location_symbol_comm location_symbol_comm;
-
-       if (!location || !payload) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) ==
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
-
-       original_payload_size = payload->buffer.size;
-       location_symbol = container_of(location,
-                       struct lttng_kernel_probe_location_symbol, parent);
-
-       if (!location_symbol->symbol_name) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       symbol_name_len = strlen(location_symbol->symbol_name);
-       if (symbol_name_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_symbol_comm.symbol_len = symbol_name_len + 1;
-       location_symbol_comm.offset = location_symbol->offset;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       &location_symbol_comm, sizeof(location_symbol_comm));
-       if (ret) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       location_symbol->symbol_name,
-                       location_symbol_comm.symbol_len);
-       if (ret) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (int) (payload->buffer.size - original_payload_size);
-end:
-       return ret;
-}
-
-static
-int lttng_kernel_probe_location_address_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t original_payload_size;
-       struct lttng_kernel_probe_location_address *location_address;
-       struct lttng_kernel_probe_location_address_comm location_address_comm;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) ==
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
-
-       original_payload_size = payload->buffer.size;
-       location_address = container_of(location,
-                       struct lttng_kernel_probe_location_address,
-                       parent);
-
-       location_address_comm.address = location_address->address;
-
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       &location_address_comm,
-                       sizeof(location_address_comm));
-       if (ret) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (int) (payload->buffer.size - original_payload_size);
-end:
-       return ret;
-}
-
-int lttng_kernel_probe_location_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t original_payload_size;
-       struct lttng_kernel_probe_location_comm location_generic_comm = {};
-
-       if (!location || !payload) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       original_payload_size = payload->buffer.size;
-       location_generic_comm.type = (int8_t) location->type;
-       ret = lttng_dynamic_buffer_append(&payload->buffer,
-                       &location_generic_comm,
-                       sizeof(location_generic_comm));
-       if (ret) {
-               goto end;
-       }
-
-       ret = location->serialize(location, payload);
-       if (ret < 0) {
-               goto end;
-       }
-
-       ret = (int) (payload->buffer.size - original_payload_size);
-end:
-       return ret;
-}
-
-static
-int lttng_kernel_probe_location_symbol_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_kernel_probe_location **location)
-{
-       struct lttng_kernel_probe_location_symbol_comm *location_symbol_comm;
-       const char *symbol_name_src;
-       ssize_t ret = 0;
-       size_t expected_size;
-
-       LTTNG_ASSERT(location);
-
-       if (view->buffer.size < sizeof(*location_symbol_comm)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_symbol_comm =
-                       (typeof(location_symbol_comm)) view->buffer.data;
-
-       expected_size = sizeof(*location_symbol_comm) +
-                       location_symbol_comm->symbol_len;
-
-       if (view->buffer.size < expected_size) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm);
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src,
-                       location_symbol_comm->symbol_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       *location = lttng_kernel_probe_location_symbol_create(
-                       symbol_name_src, location_symbol_comm->offset);
-       if (!(*location)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (ssize_t) expected_size;
-end:
-       return ret;
-}
-
-static
-ssize_t lttng_kernel_probe_location_address_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_kernel_probe_location **location)
-{
-       struct lttng_kernel_probe_location_address_comm *location_address_comm;
-       ssize_t ret = 0;
-       size_t expected_size;
-
-       LTTNG_ASSERT(location);
-
-       expected_size = sizeof(*location_address_comm);
-
-       if (view->buffer.size < expected_size) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_address_comm =
-                       (typeof(location_address_comm)) view->buffer.data;
-
-       *location = lttng_kernel_probe_location_address_create(location_address_comm->address);
-       if (!(*location)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (size_t) expected_size;
-end:
-       return ret;
-}
-
-ssize_t lttng_kernel_probe_location_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_kernel_probe_location **location)
-{
-       enum lttng_kernel_probe_location_type type;
-       ssize_t consumed = 0;
-       ssize_t ret;
-       const struct lttng_kernel_probe_location_comm *probe_location_comm;
-       const struct lttng_payload_view probe_location_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*probe_location_comm));
-
-       LTTNG_ASSERT(view);
-       LTTNG_ASSERT(location);
-
-       if (!lttng_payload_view_is_valid(&probe_location_comm_view)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data;
-       type = (enum lttng_kernel_probe_location_type) probe_location_comm->type;
-       consumed += sizeof(*probe_location_comm);
-
-       switch (type) {
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
-       {
-               struct lttng_payload_view location_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed, -1);
-
-               ret = lttng_kernel_probe_location_symbol_create_from_payload(
-                               &location_view, location);
-               break;
-       }
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
-       {
-               struct lttng_payload_view location_view =
-                               lttng_payload_view_from_view(view, consumed, -1);
-
-               ret = lttng_kernel_probe_location_address_create_from_payload(
-                               &location_view, location);
-               break;
-       }
-       default:
-               ret = -LTTNG_ERR_INVALID;
-               break;
-       }
-
-       if (ret < 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret += consumed;
-
-end:
-       return ret;
-}
-
-static
-unsigned long lttng_kernel_probe_location_address_hash(
-               const struct lttng_kernel_probe_location *location)
-{
-       unsigned long hash = hash_key_ulong(
-                       (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS,
-                       lttng_ht_seed);
-       struct lttng_kernel_probe_location_address *address_location =
-                       container_of(location, typeof(*address_location),
-                               parent);
-
-       hash ^= hash_key_u64(&address_location->address, lttng_ht_seed);
-
-       return hash;
-}
-
-static
-bool lttng_kernel_probe_location_address_is_equal(
-               const struct lttng_kernel_probe_location *_a,
-               const struct lttng_kernel_probe_location *_b)
-{
-       bool is_equal = false;
-       struct lttng_kernel_probe_location_address *a, *b;
-
-       a = container_of(_a, struct lttng_kernel_probe_location_address,
-                       parent);
-       b = container_of(_b, struct lttng_kernel_probe_location_address,
-                       parent);
-
-       if (a->address != b->address) {
-               goto end;
-       }
-
-       is_equal = true;
-
-end:
-       return is_equal;
-}
-
-static
-unsigned long lttng_kernel_probe_location_symbol_hash(
-               const struct lttng_kernel_probe_location *location)
-{
-       unsigned long hash = hash_key_ulong(
-                       (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET,
-                       lttng_ht_seed);
-       struct lttng_kernel_probe_location_symbol *symbol_location =
-                       container_of(location, typeof(*symbol_location),
-                               parent);
-
-       hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed);
-       hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed);
-
-       return hash;
-}
-
-static
-bool lttng_kernel_probe_location_symbol_is_equal(
-               const struct lttng_kernel_probe_location *_a,
-               const struct lttng_kernel_probe_location *_b)
-{
-       bool is_equal = false;
-       struct lttng_kernel_probe_location_symbol *a, *b;
-
-       a = container_of(_a, struct lttng_kernel_probe_location_symbol,
-                       parent);
-       b = container_of(_b, struct lttng_kernel_probe_location_symbol,
-                       parent);
-
-       LTTNG_ASSERT(a->symbol_name);
-       LTTNG_ASSERT(b->symbol_name);
-       if (strcmp(a->symbol_name, b->symbol_name)) {
-               goto end;
-       }
-
-       if (a->offset != b->offset) {
-               goto end;
-       }
-
-       is_equal = true;
-
-end:
-       return is_equal;
-}
-
-bool lttng_kernel_probe_location_is_equal(
-               const struct lttng_kernel_probe_location *a,
-               const struct lttng_kernel_probe_location *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       is_equal = a->equal ? a->equal(a, b) : true;
-end:
-       return is_equal;
-}
-
-static struct lttng_kernel_probe_location *
-lttng_kernel_probe_location_symbol_copy(
-               const struct lttng_kernel_probe_location *location)
-{
-       struct lttng_kernel_probe_location *new_location = NULL;
-       struct lttng_kernel_probe_location_symbol *symbol_location;
-       enum lttng_kernel_probe_location_status status;
-       const char *symbol_name = NULL;
-       uint64_t offset;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
-       symbol_location = container_of(
-                       location, typeof(*symbol_location), parent);
-
-        /* Get probe location offset */
-       status = lttng_kernel_probe_location_symbol_get_offset(location, &offset);
-       if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
-               ERR("Get kernel probe location offset failed.");
-               goto error;
-       }
-
-       symbol_name = lttng_kernel_probe_location_symbol_get_name(location);
-       if (!symbol_name) {
-               ERR("Kernel probe symbol name is NULL.");
-               goto error;
-       }
-
-       /* Create the probe_location */
-       new_location = lttng_kernel_probe_location_symbol_create(
-                       symbol_name, offset);
-
-       goto end;
-
-error:
-       new_location = NULL;
-end:
-       return new_location;
-}
-static struct lttng_kernel_probe_location *
-lttng_kernel_probe_location_address_copy(
-               const struct lttng_kernel_probe_location *location)
-{
-       struct lttng_kernel_probe_location *new_location = NULL;
-       struct lttng_kernel_probe_location_address *address_location;
-       enum lttng_kernel_probe_location_status status;
-       uint64_t address;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
-       address_location = container_of(
-                       location, typeof(*address_location), parent);
-
-
-        /* Get probe location fields */
-       status = lttng_kernel_probe_location_address_get_address(location, &address);
-       if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
-               ERR("Get kernel probe address failed.");
-               goto error;
-       }
-
-       /* Create the probe_location */
-       new_location = lttng_kernel_probe_location_address_create(address);
-
-       goto end;
-
-error:
-       new_location = NULL;
-end:
-       return new_location;
-}
-
-struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy(
-               const struct lttng_kernel_probe_location *location)
-{
-       struct lttng_kernel_probe_location *new_location = NULL;
-       enum lttng_kernel_probe_location_type type;
-
-       if (!location) {
-               goto err;
-       }
-
-       type = lttng_kernel_probe_location_get_type(location);
-       switch (type) {
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
-               new_location =
-                       lttng_kernel_probe_location_address_copy(location);
-               if (!new_location) {
-                       goto err;
-               }
-               break;
-       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
-               new_location =
-                       lttng_kernel_probe_location_symbol_copy(location);
-               if (!new_location) {
-                       goto err;
-               }
-               break;
-       default:
-               new_location = NULL;
-               goto err;
-       }
-err:
-       return new_location;
-}
-
-unsigned long lttng_kernel_probe_location_hash(
-       const struct lttng_kernel_probe_location *location)
-{
-       return location->hash(location);
-}
-
-static
-enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_kernel_probe_location_status status;
-       uint64_t address;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
-
-       status = lttng_kernel_probe_location_address_get_address(
-                       location, &address);
-       LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK);
-
-       /* Open kernel probe location address element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_kernel_probe_location_address);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_kernel_probe_location_address_address,
-                       address);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close kernel probe location address element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static
-enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_kernel_probe_location_status status;
-       const char *name = NULL;
-       uint64_t offset;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-       LTTNG_ASSERT(location->type ==
-                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
-
-       name = lttng_kernel_probe_location_symbol_get_name(location);
-       LTTNG_ASSERT(name);
-
-       status = lttng_kernel_probe_location_symbol_get_offset(
-                       location, &offset);
-       LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK);
-
-       /* Open kernel probe location symbol offset element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_kernel_probe_location_symbol_offset);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_kernel_probe_location_symbol_offset_name,
-                       name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Offset. */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_kernel_probe_location_symbol_offset_offset,
-                       offset);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close kernel probe location symbol offset element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-enum lttng_error_code lttng_kernel_probe_location_mi_serialize(
-               const struct lttng_kernel_probe_location *location,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-
-       /* Open kernel probe location element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_kernel_probe_location);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Serialize the location sub type. */
-       ret_code = location->mi_serialize(location, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close kernel probe location element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/kernel-probe.cpp b/src/common/kernel-probe.cpp
new file mode 100644 (file)
index 0000000..a05b451
--- /dev/null
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "lttng/lttng-error.h"
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <fcntl.h>
+#include <lttng/constant.h>
+#include <lttng/kernel-probe-internal.h>
+#include <lttng/kernel-probe.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static
+int lttng_kernel_probe_location_address_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct lttng_payload *payload);
+
+static
+int lttng_kernel_probe_location_symbol_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct lttng_payload *payload);
+
+static
+bool lttng_kernel_probe_location_address_is_equal(
+               const struct lttng_kernel_probe_location *a,
+               const struct lttng_kernel_probe_location *b);
+
+static
+bool lttng_kernel_probe_location_symbol_is_equal(
+               const struct lttng_kernel_probe_location *a,
+               const struct lttng_kernel_probe_location *b);
+
+static
+unsigned long lttng_kernel_probe_location_address_hash(
+               const struct lttng_kernel_probe_location *location);
+
+static
+unsigned long lttng_kernel_probe_location_symbol_hash(
+               const struct lttng_kernel_probe_location *location);
+
+static
+enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct mi_writer *writer);
+
+static
+enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct mi_writer *writer);
+
+enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type(
+               const struct lttng_kernel_probe_location *location)
+{
+       return location ? location->type :
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN;
+}
+
+static
+void lttng_kernel_probe_location_address_destroy(
+               struct lttng_kernel_probe_location *location)
+{
+       LTTNG_ASSERT(location);
+       free(location);
+}
+
+static
+void lttng_kernel_probe_location_symbol_destroy(
+               struct lttng_kernel_probe_location *location)
+{
+       struct lttng_kernel_probe_location_symbol *location_symbol = NULL;
+
+       LTTNG_ASSERT(location);
+
+       location_symbol = container_of(location,
+                       struct lttng_kernel_probe_location_symbol,
+                       parent);
+
+       LTTNG_ASSERT(location_symbol);
+
+       free(location_symbol->symbol_name);
+       free(location);
+}
+
+void lttng_kernel_probe_location_destroy(
+               struct lttng_kernel_probe_location *location)
+{
+       if (!location) {
+               return;
+       }
+
+       switch (location->type) {
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
+               lttng_kernel_probe_location_address_destroy(location);
+               break;
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
+               lttng_kernel_probe_location_symbol_destroy(location);
+               break;
+       default:
+               abort();
+       }
+}
+
+struct lttng_kernel_probe_location *
+lttng_kernel_probe_location_address_create(uint64_t address)
+{
+       struct lttng_kernel_probe_location *ret = NULL;
+       struct lttng_kernel_probe_location_address *location;
+
+       location = (lttng_kernel_probe_location_address *) zmalloc(sizeof(*location));
+       if (!location) {
+               PERROR("Error allocating userspace probe location.");
+               goto end;
+       }
+
+       location->address = address;
+
+       ret = &location->parent;
+       ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS;
+       ret->equal = lttng_kernel_probe_location_address_is_equal;
+       ret->serialize = lttng_kernel_probe_location_address_serialize;
+       ret->hash = lttng_kernel_probe_location_address_hash;
+       ret->mi_serialize = lttng_kernel_probe_location_address_mi_serialize;
+
+end:
+       return ret;
+}
+
+struct lttng_kernel_probe_location *
+lttng_kernel_probe_location_symbol_create(const char *symbol_name,
+               uint64_t offset)
+{
+       char *symbol_name_copy = NULL;
+       struct lttng_kernel_probe_location *ret = NULL;
+       struct lttng_kernel_probe_location_symbol *location;
+
+       if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) {
+               goto error;
+       }
+
+       symbol_name_copy = strdup(symbol_name);
+       if (!symbol_name_copy) {
+               PERROR("Failed to copy symbol name '%s'", symbol_name);
+               goto error;
+       }
+
+       location = (lttng_kernel_probe_location_symbol *) zmalloc(sizeof(*location));
+       if (!location) {
+               PERROR("Failed to allocate kernel symbol probe location");
+               goto error;
+       }
+
+       location->symbol_name = symbol_name_copy;
+       location->offset = offset;
+
+       ret = &location->parent;
+       ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET;
+       ret->equal = lttng_kernel_probe_location_symbol_is_equal;
+       ret->serialize = lttng_kernel_probe_location_symbol_serialize;
+       ret->hash = lttng_kernel_probe_location_symbol_hash;
+       ret->mi_serialize = lttng_kernel_probe_location_symbol_mi_serialize;
+       goto end;
+
+error:
+       free(symbol_name_copy);
+end:
+       return ret;
+}
+
+enum lttng_kernel_probe_location_status
+lttng_kernel_probe_location_address_get_address(
+               const struct lttng_kernel_probe_location *location,
+               uint64_t *offset)
+{
+       enum lttng_kernel_probe_location_status ret =
+                       LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK;
+       struct lttng_kernel_probe_location_address *address_location;
+
+       LTTNG_ASSERT(offset);
+
+       if (!location || lttng_kernel_probe_location_get_type(location) !=
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       address_location = container_of(location,
+                       struct lttng_kernel_probe_location_address, parent);
+       *offset = address_location->address;
+end:
+       return ret;
+}
+
+const char *lttng_kernel_probe_location_symbol_get_name(
+               const struct lttng_kernel_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_kernel_probe_location_symbol *symbol_location;
+
+       if (!location || lttng_kernel_probe_location_get_type(location) !=
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       symbol_location = container_of(location,
+                       struct lttng_kernel_probe_location_symbol, parent);
+       ret = symbol_location->symbol_name;
+end:
+       return ret;
+}
+
+enum lttng_kernel_probe_location_status
+lttng_kernel_probe_location_symbol_get_offset(
+               const struct lttng_kernel_probe_location *location,
+               uint64_t *offset)
+{
+       enum lttng_kernel_probe_location_status ret =
+                       LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK;
+       struct lttng_kernel_probe_location_symbol *symbol_location;
+
+       LTTNG_ASSERT(offset);
+
+       if (!location || lttng_kernel_probe_location_get_type(location) !=
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       symbol_location = container_of(location,
+                       struct lttng_kernel_probe_location_symbol, parent);
+       *offset = symbol_location->offset;
+end:
+       return ret;
+}
+
+static
+int lttng_kernel_probe_location_symbol_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t symbol_name_len;
+       size_t original_payload_size;
+       struct lttng_kernel_probe_location_symbol *location_symbol;
+       struct lttng_kernel_probe_location_symbol_comm location_symbol_comm;
+
+       if (!location || !payload) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) ==
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
+
+       original_payload_size = payload->buffer.size;
+       location_symbol = container_of(location,
+                       struct lttng_kernel_probe_location_symbol, parent);
+
+       if (!location_symbol->symbol_name) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       symbol_name_len = strlen(location_symbol->symbol_name);
+       if (symbol_name_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_symbol_comm.symbol_len = symbol_name_len + 1;
+       location_symbol_comm.offset = location_symbol->offset;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       &location_symbol_comm, sizeof(location_symbol_comm));
+       if (ret) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       location_symbol->symbol_name,
+                       location_symbol_comm.symbol_len);
+       if (ret) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (int) (payload->buffer.size - original_payload_size);
+end:
+       return ret;
+}
+
+static
+int lttng_kernel_probe_location_address_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t original_payload_size;
+       struct lttng_kernel_probe_location_address *location_address;
+       struct lttng_kernel_probe_location_address_comm location_address_comm;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) ==
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
+
+       original_payload_size = payload->buffer.size;
+       location_address = container_of(location,
+                       struct lttng_kernel_probe_location_address,
+                       parent);
+
+       location_address_comm.address = location_address->address;
+
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       &location_address_comm,
+                       sizeof(location_address_comm));
+       if (ret) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (int) (payload->buffer.size - original_payload_size);
+end:
+       return ret;
+}
+
+int lttng_kernel_probe_location_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t original_payload_size;
+       struct lttng_kernel_probe_location_comm location_generic_comm = {};
+
+       if (!location || !payload) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       original_payload_size = payload->buffer.size;
+       location_generic_comm.type = (int8_t) location->type;
+       ret = lttng_dynamic_buffer_append(&payload->buffer,
+                       &location_generic_comm,
+                       sizeof(location_generic_comm));
+       if (ret) {
+               goto end;
+       }
+
+       ret = location->serialize(location, payload);
+       if (ret < 0) {
+               goto end;
+       }
+
+       ret = (int) (payload->buffer.size - original_payload_size);
+end:
+       return ret;
+}
+
+static
+int lttng_kernel_probe_location_symbol_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_kernel_probe_location **location)
+{
+       struct lttng_kernel_probe_location_symbol_comm *location_symbol_comm;
+       const char *symbol_name_src;
+       ssize_t ret = 0;
+       size_t expected_size;
+
+       LTTNG_ASSERT(location);
+
+       if (view->buffer.size < sizeof(*location_symbol_comm)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_symbol_comm =
+                       (typeof(location_symbol_comm)) view->buffer.data;
+
+       expected_size = sizeof(*location_symbol_comm) +
+                       location_symbol_comm->symbol_len;
+
+       if (view->buffer.size < expected_size) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm);
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src,
+                       location_symbol_comm->symbol_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       *location = lttng_kernel_probe_location_symbol_create(
+                       symbol_name_src, location_symbol_comm->offset);
+       if (!(*location)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (ssize_t) expected_size;
+end:
+       return ret;
+}
+
+static
+ssize_t lttng_kernel_probe_location_address_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_kernel_probe_location **location)
+{
+       struct lttng_kernel_probe_location_address_comm *location_address_comm;
+       ssize_t ret = 0;
+       size_t expected_size;
+
+       LTTNG_ASSERT(location);
+
+       expected_size = sizeof(*location_address_comm);
+
+       if (view->buffer.size < expected_size) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_address_comm =
+                       (typeof(location_address_comm)) view->buffer.data;
+
+       *location = lttng_kernel_probe_location_address_create(location_address_comm->address);
+       if (!(*location)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (size_t) expected_size;
+end:
+       return ret;
+}
+
+ssize_t lttng_kernel_probe_location_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_kernel_probe_location **location)
+{
+       enum lttng_kernel_probe_location_type type;
+       ssize_t consumed = 0;
+       ssize_t ret;
+       const struct lttng_kernel_probe_location_comm *probe_location_comm;
+       const struct lttng_payload_view probe_location_comm_view =
+                       lttng_payload_view_from_view(
+                                       view, 0, sizeof(*probe_location_comm));
+
+       LTTNG_ASSERT(view);
+       LTTNG_ASSERT(location);
+
+       if (!lttng_payload_view_is_valid(&probe_location_comm_view)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data;
+       type = (enum lttng_kernel_probe_location_type) probe_location_comm->type;
+       consumed += sizeof(*probe_location_comm);
+
+       switch (type) {
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
+       {
+               struct lttng_payload_view location_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed, -1);
+
+               ret = lttng_kernel_probe_location_symbol_create_from_payload(
+                               &location_view, location);
+               break;
+       }
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
+       {
+               struct lttng_payload_view location_view =
+                               lttng_payload_view_from_view(view, consumed, -1);
+
+               ret = lttng_kernel_probe_location_address_create_from_payload(
+                               &location_view, location);
+               break;
+       }
+       default:
+               ret = -LTTNG_ERR_INVALID;
+               break;
+       }
+
+       if (ret < 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret += consumed;
+
+end:
+       return ret;
+}
+
+static
+unsigned long lttng_kernel_probe_location_address_hash(
+               const struct lttng_kernel_probe_location *location)
+{
+       unsigned long hash = hash_key_ulong(
+                       (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS,
+                       lttng_ht_seed);
+       struct lttng_kernel_probe_location_address *address_location =
+                       container_of(location, typeof(*address_location),
+                               parent);
+
+       hash ^= hash_key_u64(&address_location->address, lttng_ht_seed);
+
+       return hash;
+}
+
+static
+bool lttng_kernel_probe_location_address_is_equal(
+               const struct lttng_kernel_probe_location *_a,
+               const struct lttng_kernel_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_kernel_probe_location_address *a, *b;
+
+       a = container_of(_a, struct lttng_kernel_probe_location_address,
+                       parent);
+       b = container_of(_b, struct lttng_kernel_probe_location_address,
+                       parent);
+
+       if (a->address != b->address) {
+               goto end;
+       }
+
+       is_equal = true;
+
+end:
+       return is_equal;
+}
+
+static
+unsigned long lttng_kernel_probe_location_symbol_hash(
+               const struct lttng_kernel_probe_location *location)
+{
+       unsigned long hash = hash_key_ulong(
+                       (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET,
+                       lttng_ht_seed);
+       struct lttng_kernel_probe_location_symbol *symbol_location =
+                       container_of(location, typeof(*symbol_location),
+                               parent);
+
+       hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed);
+       hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed);
+
+       return hash;
+}
+
+static
+bool lttng_kernel_probe_location_symbol_is_equal(
+               const struct lttng_kernel_probe_location *_a,
+               const struct lttng_kernel_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_kernel_probe_location_symbol *a, *b;
+
+       a = container_of(_a, struct lttng_kernel_probe_location_symbol,
+                       parent);
+       b = container_of(_b, struct lttng_kernel_probe_location_symbol,
+                       parent);
+
+       LTTNG_ASSERT(a->symbol_name);
+       LTTNG_ASSERT(b->symbol_name);
+       if (strcmp(a->symbol_name, b->symbol_name)) {
+               goto end;
+       }
+
+       if (a->offset != b->offset) {
+               goto end;
+       }
+
+       is_equal = true;
+
+end:
+       return is_equal;
+}
+
+bool lttng_kernel_probe_location_is_equal(
+               const struct lttng_kernel_probe_location *a,
+               const struct lttng_kernel_probe_location *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+static struct lttng_kernel_probe_location *
+lttng_kernel_probe_location_symbol_copy(
+               const struct lttng_kernel_probe_location *location)
+{
+       struct lttng_kernel_probe_location *new_location = NULL;
+       struct lttng_kernel_probe_location_symbol *symbol_location;
+       enum lttng_kernel_probe_location_status status;
+       const char *symbol_name = NULL;
+       uint64_t offset;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
+       symbol_location = container_of(
+                       location, typeof(*symbol_location), parent);
+
+        /* Get probe location offset */
+       status = lttng_kernel_probe_location_symbol_get_offset(location, &offset);
+       if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+               ERR("Get kernel probe location offset failed.");
+               goto error;
+       }
+
+       symbol_name = lttng_kernel_probe_location_symbol_get_name(location);
+       if (!symbol_name) {
+               ERR("Kernel probe symbol name is NULL.");
+               goto error;
+       }
+
+       /* Create the probe_location */
+       new_location = lttng_kernel_probe_location_symbol_create(
+                       symbol_name, offset);
+
+       goto end;
+
+error:
+       new_location = NULL;
+end:
+       return new_location;
+}
+static struct lttng_kernel_probe_location *
+lttng_kernel_probe_location_address_copy(
+               const struct lttng_kernel_probe_location *location)
+{
+       struct lttng_kernel_probe_location *new_location = NULL;
+       struct lttng_kernel_probe_location_address *address_location;
+       enum lttng_kernel_probe_location_status status;
+       uint64_t address;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
+       address_location = container_of(
+                       location, typeof(*address_location), parent);
+
+
+        /* Get probe location fields */
+       status = lttng_kernel_probe_location_address_get_address(location, &address);
+       if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) {
+               ERR("Get kernel probe address failed.");
+               goto error;
+       }
+
+       /* Create the probe_location */
+       new_location = lttng_kernel_probe_location_address_create(address);
+
+       goto end;
+
+error:
+       new_location = NULL;
+end:
+       return new_location;
+}
+
+struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy(
+               const struct lttng_kernel_probe_location *location)
+{
+       struct lttng_kernel_probe_location *new_location = NULL;
+       enum lttng_kernel_probe_location_type type;
+
+       if (!location) {
+               goto err;
+       }
+
+       type = lttng_kernel_probe_location_get_type(location);
+       switch (type) {
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS:
+               new_location =
+                       lttng_kernel_probe_location_address_copy(location);
+               if (!new_location) {
+                       goto err;
+               }
+               break;
+       case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET:
+               new_location =
+                       lttng_kernel_probe_location_symbol_copy(location);
+               if (!new_location) {
+                       goto err;
+               }
+               break;
+       default:
+               new_location = NULL;
+               goto err;
+       }
+err:
+       return new_location;
+}
+
+unsigned long lttng_kernel_probe_location_hash(
+       const struct lttng_kernel_probe_location *location)
+{
+       return location->hash(location);
+}
+
+static
+enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_kernel_probe_location_status status;
+       uint64_t address;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS);
+
+       status = lttng_kernel_probe_location_address_get_address(
+                       location, &address);
+       LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK);
+
+       /* Open kernel probe location address element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_kernel_probe_location_address);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_kernel_probe_location_address_address,
+                       address);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close kernel probe location address element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static
+enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_kernel_probe_location_status status;
+       const char *name = NULL;
+       uint64_t offset;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+       LTTNG_ASSERT(location->type ==
+                       LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET);
+
+       name = lttng_kernel_probe_location_symbol_get_name(location);
+       LTTNG_ASSERT(name);
+
+       status = lttng_kernel_probe_location_symbol_get_offset(
+                       location, &offset);
+       LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK);
+
+       /* Open kernel probe location symbol offset element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_kernel_probe_location_symbol_offset);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_kernel_probe_location_symbol_offset_name,
+                       name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Offset. */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_kernel_probe_location_symbol_offset_offset,
+                       offset);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close kernel probe location symbol offset element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code lttng_kernel_probe_location_mi_serialize(
+               const struct lttng_kernel_probe_location *location,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+
+       /* Open kernel probe location element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_kernel_probe_location);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Serialize the location sub type. */
+       ret_code = location->mi_serialize(location, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close kernel probe location element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/location.c b/src/common/location.c
deleted file mode 100644 (file)
index e68051e..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/location-internal.h>
-#include <common/macros.h>
-#include <stdlib.h>
-#include <common/error.h>
-
-static
-struct lttng_trace_archive_location *lttng_trace_archive_location_create(
-               enum lttng_trace_archive_location_type type)
-{
-       struct lttng_trace_archive_location *location;
-
-       location = zmalloc(sizeof(*location));
-       if (!location) {
-               goto end;
-       }
-
-       urcu_ref_init(&location->ref);
-       location->type = type;
-end:
-       return location;
-}
-
-static
-void trace_archive_location_destroy_ref(struct urcu_ref *ref)
-{
-       struct lttng_trace_archive_location *location =
-                       container_of(ref, struct lttng_trace_archive_location, ref);
-
-       switch (location->type) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
-               free(location->types.local.absolute_path);
-               break;
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
-               free(location->types.relay.host);
-               free(location->types.relay.relative_path);
-               break;
-       default:
-               abort();
-       }
-
-       free(location);
-}
-
-void lttng_trace_archive_location_get(struct lttng_trace_archive_location *location)
-{
-       urcu_ref_get(&location->ref);
-}
-
-void lttng_trace_archive_location_put(struct lttng_trace_archive_location *location)
-{
-       if (!location) {
-               return;
-       }
-
-       urcu_ref_put(&location->ref, trace_archive_location_destroy_ref);
-}
-
-struct lttng_trace_archive_location *lttng_trace_archive_location_local_create(
-               const char *absolute_path)
-{
-       struct lttng_trace_archive_location *location = NULL;
-
-       if (!absolute_path) {
-               goto end;
-       }
-
-       location = lttng_trace_archive_location_create(
-                       LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL);
-       if (!location) {
-               goto end;
-       }
-
-       location->types.local.absolute_path = strdup(absolute_path);
-       if (!location->types.local.absolute_path) {
-               goto error;
-       }
-
-end:
-       return location;
-error:
-       lttng_trace_archive_location_put(location);
-       return NULL;
-}
-
-struct lttng_trace_archive_location *lttng_trace_archive_location_relay_create(
-               const char *host,
-               enum lttng_trace_archive_location_relay_protocol_type protocol,
-               uint16_t control_port, uint16_t data_port,
-               const char *relative_path)
-{
-       struct lttng_trace_archive_location *location = NULL;
-
-       if (!host || !relative_path) {
-               goto end;
-       }
-
-       location = lttng_trace_archive_location_create(
-                       LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY);
-       if (!location) {
-               goto end;
-       }
-
-       location->types.relay.host = strdup(host);
-       if (!location->types.relay.host) {
-               goto error;
-       }
-       location->types.relay.relative_path = strdup(relative_path);
-       if (!location->types.relay.relative_path) {
-               goto error;
-       }
-
-       location->types.relay.protocol = protocol;
-       location->types.relay.ports.control = control_port;
-       location->types.relay.ports.data = data_port;
-end:
-       return location;
-error:
-       lttng_trace_archive_location_put(location);
-       return NULL;
-}
-
-ssize_t lttng_trace_archive_location_create_from_buffer(
-               const struct lttng_buffer_view *view,
-               struct lttng_trace_archive_location **location)
-{
-       size_t offset = 0;
-       const struct lttng_trace_archive_location_comm *location_comm;
-       struct lttng_buffer_view location_comm_view;
-
-       location_comm_view = lttng_buffer_view_from_view(view, 0,
-                       sizeof(*location_comm));
-       if (!lttng_buffer_view_is_valid(&location_comm_view)) {
-               goto error;
-       }
-
-       offset += location_comm_view.size;
-       location_comm = (const struct lttng_trace_archive_location_comm *) location_comm_view.data;
-
-       switch ((enum lttng_trace_archive_location_type) location_comm->type) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
-       {
-               const struct lttng_buffer_view absolute_path_view =
-                               lttng_buffer_view_from_view(view, offset,
-                               location_comm->types.local.absolute_path_len);
-
-               if (!lttng_buffer_view_is_valid(&absolute_path_view)) {
-                       goto error;
-               }
-
-               if (absolute_path_view.data[absolute_path_view.size - 1] != '\0') {
-                       goto error;
-               }
-               offset += absolute_path_view.size;
-
-               *location = lttng_trace_archive_location_local_create(
-                               absolute_path_view.data);
-               if (!*location) {
-                       goto error;
-               }
-               break;
-       }
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
-       {
-               const struct lttng_buffer_view hostname_view =
-                               lttng_buffer_view_from_view(view, offset,
-                               location_comm->types.relay.hostname_len);
-               const struct lttng_buffer_view relative_path_view =
-                               lttng_buffer_view_from_view(view,
-                               offset + hostname_view.size,
-                               location_comm->types.relay.relative_path_len);
-
-               if (!lttng_buffer_view_is_valid(&hostname_view) ||
-                               !lttng_buffer_view_is_valid(
-                                               &relative_path_view)) {
-                       goto error;
-               }
-
-               if (hostname_view.data[hostname_view.size - 1] != '\0') {
-                       goto error;
-               }
-               if (relative_path_view.data[relative_path_view.size - 1] != '\0') {
-                       goto error;
-               }
-               offset += hostname_view.size + relative_path_view.size;
-
-               *location = lttng_trace_archive_location_relay_create(
-                               hostname_view.data,
-                               (enum lttng_trace_archive_location_relay_protocol_type) location_comm->types.relay.protocol,
-                               location_comm->types.relay.ports.control,
-                               location_comm->types.relay.ports.data,
-                               relative_path_view.data);
-               if (!*location) {
-                       goto error;
-               }
-               break;
-       }
-       default:
-               goto error;
-       }
-
-       return offset;
-error:
-       return -1;
-}
-
-ssize_t lttng_trace_archive_location_serialize(
-               const struct lttng_trace_archive_location *location,
-               struct lttng_dynamic_buffer *buffer)
-{
-       int ret;
-       struct lttng_trace_archive_location_comm location_comm;
-
-       location_comm.type = (int8_t) location->type;
-
-       switch (location->type) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
-               location_comm.types.local.absolute_path_len =
-                               strlen(location->types.local.absolute_path) + 1;
-               break;
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
-               location_comm.types.relay.hostname_len =
-                               strlen(location->types.relay.host) + 1;
-               location_comm.types.relay.protocol =
-                               (int8_t) location->types.relay.protocol;
-               location_comm.types.relay.ports.control =
-                               location->types.relay.ports.control;
-               location_comm.types.relay.ports.data =
-                               location->types.relay.ports.data;
-               location_comm.types.relay.relative_path_len =
-                               strlen(location->types.relay.relative_path) + 1;
-               break;
-       default:
-               abort();
-       }
-
-       ret = lttng_dynamic_buffer_append(buffer, &location_comm,
-                       sizeof(location_comm));
-       if (ret) {
-               goto error;
-       }
-
-       switch (location->type) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
-               ret = lttng_dynamic_buffer_append(buffer,
-                               location->types.local.absolute_path,
-                               location_comm.types.local.absolute_path_len);
-               if (ret) {
-                       goto error;
-               }
-               break;
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
-               ret = lttng_dynamic_buffer_append(buffer,
-                               location->types.relay.host,
-                               location_comm.types.relay.hostname_len);
-               if (ret) {
-                       goto error;
-               }
-               ret = lttng_dynamic_buffer_append(buffer,
-                               location->types.relay.relative_path,
-                               location_comm.types.relay.relative_path_len);
-               if (ret) {
-                       goto error;
-               }
-               break;
-       default:
-               abort();
-       }
-
-       return 0;
-error:
-       return -1;
-}
-
-enum lttng_trace_archive_location_type lttng_trace_archive_location_get_type(
-               const struct lttng_trace_archive_location *location)
-{
-       enum lttng_trace_archive_location_type type;
-
-       if (!location) {
-               type = LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_UNKNOWN;
-               goto end;
-       }
-
-       type = location->type;
-end:
-       return type;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_local_get_absolute_path(
-               const struct lttng_trace_archive_location *location,
-               const char **absolute_path)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !absolute_path ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *absolute_path = location->types.local.absolute_path;
-end:
-       return status;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_relay_get_host(
-               const struct lttng_trace_archive_location *location,
-               const char **relay_host)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !relay_host ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *relay_host = location->types.relay.host;
-end:
-       return status;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_relay_get_relative_path(
-               const struct lttng_trace_archive_location *location,
-               const char **relative_path)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !relative_path ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *relative_path = location->types.relay.relative_path;
-end:
-       return status;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_relay_get_control_port(
-               const struct lttng_trace_archive_location *location,
-               uint16_t *control_port)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !control_port ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *control_port = location->types.relay.ports.control;
-end:
-       return status;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_relay_get_data_port(
-               const struct lttng_trace_archive_location *location,
-               uint16_t *data_port)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !data_port ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *data_port = location->types.relay.ports.data;
-end:
-       return status;
-}
-
-enum lttng_trace_archive_location_status
-lttng_trace_archive_location_relay_get_protocol_type(
-               const struct lttng_trace_archive_location *location,
-               enum lttng_trace_archive_location_relay_protocol_type *protocol)
-{
-       enum lttng_trace_archive_location_status status =
-                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
-
-       if (!location || !protocol ||
-                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
-               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       *protocol = location->types.relay.protocol;
-end:
-       return status;
-}
diff --git a/src/common/location.cpp b/src/common/location.cpp
new file mode 100644 (file)
index 0000000..80c4b4d
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/location-internal.h>
+#include <common/macros.h>
+#include <stdlib.h>
+#include <common/error.h>
+
+static
+struct lttng_trace_archive_location *lttng_trace_archive_location_create(
+               enum lttng_trace_archive_location_type type)
+{
+       struct lttng_trace_archive_location *location;
+
+       location = (lttng_trace_archive_location *) zmalloc(sizeof(*location));
+       if (!location) {
+               goto end;
+       }
+
+       urcu_ref_init(&location->ref);
+       location->type = type;
+end:
+       return location;
+}
+
+static
+void trace_archive_location_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_trace_archive_location *location =
+                       container_of(ref, struct lttng_trace_archive_location, ref);
+
+       switch (location->type) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
+               free(location->types.local.absolute_path);
+               break;
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
+               free(location->types.relay.host);
+               free(location->types.relay.relative_path);
+               break;
+       default:
+               abort();
+       }
+
+       free(location);
+}
+
+void lttng_trace_archive_location_get(struct lttng_trace_archive_location *location)
+{
+       urcu_ref_get(&location->ref);
+}
+
+void lttng_trace_archive_location_put(struct lttng_trace_archive_location *location)
+{
+       if (!location) {
+               return;
+       }
+
+       urcu_ref_put(&location->ref, trace_archive_location_destroy_ref);
+}
+
+struct lttng_trace_archive_location *lttng_trace_archive_location_local_create(
+               const char *absolute_path)
+{
+       struct lttng_trace_archive_location *location = NULL;
+
+       if (!absolute_path) {
+               goto end;
+       }
+
+       location = lttng_trace_archive_location_create(
+                       LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL);
+       if (!location) {
+               goto end;
+       }
+
+       location->types.local.absolute_path = strdup(absolute_path);
+       if (!location->types.local.absolute_path) {
+               goto error;
+       }
+
+end:
+       return location;
+error:
+       lttng_trace_archive_location_put(location);
+       return NULL;
+}
+
+struct lttng_trace_archive_location *lttng_trace_archive_location_relay_create(
+               const char *host,
+               enum lttng_trace_archive_location_relay_protocol_type protocol,
+               uint16_t control_port, uint16_t data_port,
+               const char *relative_path)
+{
+       struct lttng_trace_archive_location *location = NULL;
+
+       if (!host || !relative_path) {
+               goto end;
+       }
+
+       location = lttng_trace_archive_location_create(
+                       LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY);
+       if (!location) {
+               goto end;
+       }
+
+       location->types.relay.host = strdup(host);
+       if (!location->types.relay.host) {
+               goto error;
+       }
+       location->types.relay.relative_path = strdup(relative_path);
+       if (!location->types.relay.relative_path) {
+               goto error;
+       }
+
+       location->types.relay.protocol = protocol;
+       location->types.relay.ports.control = control_port;
+       location->types.relay.ports.data = data_port;
+end:
+       return location;
+error:
+       lttng_trace_archive_location_put(location);
+       return NULL;
+}
+
+ssize_t lttng_trace_archive_location_create_from_buffer(
+               const struct lttng_buffer_view *view,
+               struct lttng_trace_archive_location **location)
+{
+       size_t offset = 0;
+       const struct lttng_trace_archive_location_comm *location_comm;
+       struct lttng_buffer_view location_comm_view;
+
+       location_comm_view = lttng_buffer_view_from_view(view, 0,
+                       sizeof(*location_comm));
+       if (!lttng_buffer_view_is_valid(&location_comm_view)) {
+               goto error;
+       }
+
+       offset += location_comm_view.size;
+       location_comm = (const struct lttng_trace_archive_location_comm *) location_comm_view.data;
+
+       switch ((enum lttng_trace_archive_location_type) location_comm->type) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
+       {
+               const struct lttng_buffer_view absolute_path_view =
+                               lttng_buffer_view_from_view(view, offset,
+                               location_comm->types.local.absolute_path_len);
+
+               if (!lttng_buffer_view_is_valid(&absolute_path_view)) {
+                       goto error;
+               }
+
+               if (absolute_path_view.data[absolute_path_view.size - 1] != '\0') {
+                       goto error;
+               }
+               offset += absolute_path_view.size;
+
+               *location = lttng_trace_archive_location_local_create(
+                               absolute_path_view.data);
+               if (!*location) {
+                       goto error;
+               }
+               break;
+       }
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
+       {
+               const struct lttng_buffer_view hostname_view =
+                               lttng_buffer_view_from_view(view, offset,
+                               location_comm->types.relay.hostname_len);
+               const struct lttng_buffer_view relative_path_view =
+                               lttng_buffer_view_from_view(view,
+                               offset + hostname_view.size,
+                               location_comm->types.relay.relative_path_len);
+
+               if (!lttng_buffer_view_is_valid(&hostname_view) ||
+                               !lttng_buffer_view_is_valid(
+                                               &relative_path_view)) {
+                       goto error;
+               }
+
+               if (hostname_view.data[hostname_view.size - 1] != '\0') {
+                       goto error;
+               }
+               if (relative_path_view.data[relative_path_view.size - 1] != '\0') {
+                       goto error;
+               }
+               offset += hostname_view.size + relative_path_view.size;
+
+               *location = lttng_trace_archive_location_relay_create(
+                               hostname_view.data,
+                               (enum lttng_trace_archive_location_relay_protocol_type) location_comm->types.relay.protocol,
+                               location_comm->types.relay.ports.control,
+                               location_comm->types.relay.ports.data,
+                               relative_path_view.data);
+               if (!*location) {
+                       goto error;
+               }
+               break;
+       }
+       default:
+               goto error;
+       }
+
+       return offset;
+error:
+       return -1;
+}
+
+ssize_t lttng_trace_archive_location_serialize(
+               const struct lttng_trace_archive_location *location,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret;
+       struct lttng_trace_archive_location_comm location_comm;
+
+       location_comm.type = (int8_t) location->type;
+
+       switch (location->type) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
+               location_comm.types.local.absolute_path_len =
+                               strlen(location->types.local.absolute_path) + 1;
+               break;
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
+               location_comm.types.relay.hostname_len =
+                               strlen(location->types.relay.host) + 1;
+               location_comm.types.relay.protocol =
+                               (int8_t) location->types.relay.protocol;
+               location_comm.types.relay.ports.control =
+                               location->types.relay.ports.control;
+               location_comm.types.relay.ports.data =
+                               location->types.relay.ports.data;
+               location_comm.types.relay.relative_path_len =
+                               strlen(location->types.relay.relative_path) + 1;
+               break;
+       default:
+               abort();
+       }
+
+       ret = lttng_dynamic_buffer_append(buffer, &location_comm,
+                       sizeof(location_comm));
+       if (ret) {
+               goto error;
+       }
+
+       switch (location->type) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
+               ret = lttng_dynamic_buffer_append(buffer,
+                               location->types.local.absolute_path,
+                               location_comm.types.local.absolute_path_len);
+               if (ret) {
+                       goto error;
+               }
+               break;
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
+               ret = lttng_dynamic_buffer_append(buffer,
+                               location->types.relay.host,
+                               location_comm.types.relay.hostname_len);
+               if (ret) {
+                       goto error;
+               }
+               ret = lttng_dynamic_buffer_append(buffer,
+                               location->types.relay.relative_path,
+                               location_comm.types.relay.relative_path_len);
+               if (ret) {
+                       goto error;
+               }
+               break;
+       default:
+               abort();
+       }
+
+       return 0;
+error:
+       return -1;
+}
+
+enum lttng_trace_archive_location_type lttng_trace_archive_location_get_type(
+               const struct lttng_trace_archive_location *location)
+{
+       enum lttng_trace_archive_location_type type;
+
+       if (!location) {
+               type = LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_UNKNOWN;
+               goto end;
+       }
+
+       type = location->type;
+end:
+       return type;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_local_get_absolute_path(
+               const struct lttng_trace_archive_location *location,
+               const char **absolute_path)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !absolute_path ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *absolute_path = location->types.local.absolute_path;
+end:
+       return status;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_relay_get_host(
+               const struct lttng_trace_archive_location *location,
+               const char **relay_host)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !relay_host ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *relay_host = location->types.relay.host;
+end:
+       return status;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_relay_get_relative_path(
+               const struct lttng_trace_archive_location *location,
+               const char **relative_path)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !relative_path ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *relative_path = location->types.relay.relative_path;
+end:
+       return status;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_relay_get_control_port(
+               const struct lttng_trace_archive_location *location,
+               uint16_t *control_port)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !control_port ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *control_port = location->types.relay.ports.control;
+end:
+       return status;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_relay_get_data_port(
+               const struct lttng_trace_archive_location *location,
+               uint16_t *data_port)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !data_port ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *data_port = location->types.relay.ports.data;
+end:
+       return status;
+}
+
+enum lttng_trace_archive_location_status
+lttng_trace_archive_location_relay_get_protocol_type(
+               const struct lttng_trace_archive_location *location,
+               enum lttng_trace_archive_location_relay_protocol_type *protocol)
+{
+       enum lttng_trace_archive_location_status status =
+                       LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK;
+
+       if (!location || !protocol ||
+                       location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) {
+               status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       *protocol = location->types.relay.protocol;
+end:
+       return status;
+}
diff --git a/src/common/log-level-rule.c b/src/common/log-level-rule.c
deleted file mode 100644 (file)
index d2bbd41..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/dynamic-buffer.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <lttng/log-level-rule-internal.h>
-#include <lttng/log-level-rule.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule)
-{
-       enum lttng_log_level_rule_type type =
-                       lttng_log_level_rule_get_type(rule);
-
-       return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY;
-}
-
-static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule)
-{
-
-       enum lttng_log_level_rule_type type =
-                       lttng_log_level_rule_get_type(rule);
-
-       return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS;
-}
-
-enum lttng_log_level_rule_type lttng_log_level_rule_get_type(
-               const struct lttng_log_level_rule *rule)
-{
-       return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN;
-}
-
-struct lttng_log_level_rule *lttng_log_level_rule_exactly_create(
-               int level)
-{
-       struct lttng_log_level_rule *rule = NULL;
-
-       rule = zmalloc(sizeof(struct lttng_log_level_rule));
-       if (!rule) {
-               goto end;
-       }
-
-       rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY;
-       rule->level = level;
-
-end:
-       return rule;
-}
-
-enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level(
-               const struct lttng_log_level_rule *rule, int *level)
-{
-       enum lttng_log_level_rule_status status =
-                       LTTNG_LOG_LEVEL_RULE_STATUS_OK;
-
-       if (!rule || !level || !is_log_level_rule_exactly_type(rule)) {
-               status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       *level = rule->level;
-end:
-       return status;
-}
-
-struct lttng_log_level_rule *
-lttng_log_level_rule_at_least_as_severe_as_create(int level)
-{
-       struct lttng_log_level_rule *rule = NULL;
-
-       rule = zmalloc(sizeof(struct lttng_log_level_rule));
-       if (!rule) {
-               goto end;
-       }
-
-       rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS;
-       rule->level = level;
-
-end:
-       return rule;
-}
-
-enum lttng_log_level_rule_status
-lttng_log_level_rule_at_least_as_severe_as_get_level(
-               const struct lttng_log_level_rule *rule, int *level)
-{
-       enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK;
-
-       if (!rule || !level ||
-                       !is_log_level_rule_at_least_as_severe_type(rule)) {
-               status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID;
-               goto end;
-       }
-
-       *level = rule->level;
-end:
-       return status;
-}
-
-void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule)
-{
-       free(log_level_rule);
-}
-
-ssize_t lttng_log_level_rule_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_log_level_rule **_rule)
-{
-       ssize_t ret;
-       size_t offset = 0;
-       struct lttng_log_level_rule *rule = NULL;
-       const struct lttng_log_level_rule_comm *comm =
-                       (const struct lttng_log_level_rule_comm *)
-                                       view->buffer.data;
-
-       offset += sizeof(*comm);
-
-       if (!_rule) {
-               ret = -1;
-               goto end;
-       }
-
-       if (view->buffer.size < sizeof(*comm)) {
-               ret = -1;
-               goto end;
-       }
-
-       switch (comm->type) {
-       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-               rule = lttng_log_level_rule_exactly_create((int) comm->level);
-               break;
-       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-               rule = lttng_log_level_rule_at_least_as_severe_as_create(
-                               (int) comm->level);
-               break;
-       default:
-               abort();
-       }
-
-       if (!rule) {
-               ret = -1;
-               goto end;
-       }
-
-       *_rule = rule;
-       ret = offset;
-
-end:
-       return ret;
-}
-
-int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_log_level_rule_comm comm;
-
-
-       if (!rule) {
-               ret = 0;
-               goto end;
-       }
-
-       comm.type = (int8_t) rule->type;
-       comm.level = (int32_t) rule->level;
-
-       DBG("Serializing log level rule of type %d", rule->type);
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &comm,
-                       sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a,
-               const struct lttng_log_level_rule *b)
-{
-       bool is_equal = false;
-
-       if (a == NULL && b == NULL) {
-               /* Both are null. */
-               is_equal = true;
-               goto end;
-       }
-
-       if (a == NULL || b == NULL) {
-               /* One is NULL.*/
-               goto end;
-       }
-
-       if (a == b) {
-               /* Same object.*/
-               is_equal = true;
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       if (a->level != b->level) {
-               goto end;
-       }
-
-       is_equal = true;
-
-end:
-       return is_equal;
-}
-
-struct lttng_log_level_rule *lttng_log_level_rule_copy(
-               const struct lttng_log_level_rule *source)
-{
-       struct lttng_log_level_rule *copy = NULL;
-
-       LTTNG_ASSERT(source);
-
-       copy = zmalloc(sizeof(struct lttng_log_level_rule));
-       if (!copy) {
-               goto end;
-       }
-
-       copy->type = source->type;
-       copy->level = source->level;
-end:
-       return copy;
-}
-
-void lttng_log_level_rule_to_loglevel(
-               const struct lttng_log_level_rule *log_level_rule,
-               enum lttng_loglevel_type *loglevel_type,
-               int *loglevel_value)
-{
-       LTTNG_ASSERT(log_level_rule);
-
-       switch (log_level_rule->type) {
-       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-               *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
-               break;
-       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-               *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
-               break;
-       default:
-               abort();
-       }
-
-       *loglevel_value = log_level_rule->level;
-}
-
-unsigned long lttng_log_level_rule_hash(
-               const struct lttng_log_level_rule *log_level_rule)
-{
-       unsigned long hash;
-       enum lttng_log_level_rule_status llr_status;
-       int log_level_value;
-       enum lttng_log_level_rule_type type;
-
-       LTTNG_ASSERT(log_level_rule);
-
-       type = lttng_log_level_rule_get_type(log_level_rule);
-
-       switch (type) {
-       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-               llr_status = lttng_log_level_rule_exactly_get_level(
-                               log_level_rule, &log_level_value);
-               break;
-       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-               llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                               log_level_rule, &log_level_value);
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
-
-       hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed);
-
-       hash ^= hash_key_ulong((void *) (unsigned long) log_level_value,
-                       lttng_ht_seed);
-
-       return hash;
-}
-
-enum lttng_error_code lttng_log_level_rule_mi_serialize(
-               const struct lttng_log_level_rule *rule,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_log_level_rule_status status;
-       const char *element_str = NULL;
-       int level;
-
-       LTTNG_ASSERT(rule);
-       LTTNG_ASSERT(writer);
-
-       switch (lttng_log_level_rule_get_type(rule)) {
-       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
-               status = lttng_log_level_rule_exactly_get_level(rule, &level);
-               element_str = mi_lttng_element_log_level_rule_exactly;
-               break;
-       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
-               element_str = mi_lttng_element_log_level_rule_at_least_as_severe_as;
-               status = lttng_log_level_rule_at_least_as_severe_as_get_level(
-                               rule, &level);
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
-
-       /* Open log level rule element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_log_level_rule);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Log level rule type element. */
-       ret = mi_lttng_writer_open_element(writer, element_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Level. */
-       ret = mi_lttng_writer_write_element_signed_int(
-                       writer, mi_lttng_element_log_level_rule_level, level);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close log level rule type element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close log level rule element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/log-level-rule.cpp b/src/common/log-level-rule.cpp
new file mode 100644 (file)
index 0000000..e891222
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2020 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <lttng/log-level-rule-internal.h>
+#include <lttng/log-level-rule.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule)
+{
+       enum lttng_log_level_rule_type type =
+                       lttng_log_level_rule_get_type(rule);
+
+       return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY;
+}
+
+static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule)
+{
+
+       enum lttng_log_level_rule_type type =
+                       lttng_log_level_rule_get_type(rule);
+
+       return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS;
+}
+
+enum lttng_log_level_rule_type lttng_log_level_rule_get_type(
+               const struct lttng_log_level_rule *rule)
+{
+       return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN;
+}
+
+struct lttng_log_level_rule *lttng_log_level_rule_exactly_create(
+               int level)
+{
+       struct lttng_log_level_rule *rule = NULL;
+
+       rule = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule));
+       if (!rule) {
+               goto end;
+       }
+
+       rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY;
+       rule->level = level;
+
+end:
+       return rule;
+}
+
+enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level(
+               const struct lttng_log_level_rule *rule, int *level)
+{
+       enum lttng_log_level_rule_status status =
+                       LTTNG_LOG_LEVEL_RULE_STATUS_OK;
+
+       if (!rule || !level || !is_log_level_rule_exactly_type(rule)) {
+               status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       *level = rule->level;
+end:
+       return status;
+}
+
+struct lttng_log_level_rule *
+lttng_log_level_rule_at_least_as_severe_as_create(int level)
+{
+       struct lttng_log_level_rule *rule = NULL;
+
+       rule = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule));
+       if (!rule) {
+               goto end;
+       }
+
+       rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS;
+       rule->level = level;
+
+end:
+       return rule;
+}
+
+enum lttng_log_level_rule_status
+lttng_log_level_rule_at_least_as_severe_as_get_level(
+               const struct lttng_log_level_rule *rule, int *level)
+{
+       enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK;
+
+       if (!rule || !level ||
+                       !is_log_level_rule_at_least_as_severe_type(rule)) {
+               status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID;
+               goto end;
+       }
+
+       *level = rule->level;
+end:
+       return status;
+}
+
+void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule)
+{
+       free(log_level_rule);
+}
+
+ssize_t lttng_log_level_rule_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_log_level_rule **_rule)
+{
+       ssize_t ret;
+       size_t offset = 0;
+       struct lttng_log_level_rule *rule = NULL;
+       const struct lttng_log_level_rule_comm *comm =
+                       (const struct lttng_log_level_rule_comm *)
+                                       view->buffer.data;
+
+       offset += sizeof(*comm);
+
+       if (!_rule) {
+               ret = -1;
+               goto end;
+       }
+
+       if (view->buffer.size < sizeof(*comm)) {
+               ret = -1;
+               goto end;
+       }
+
+       switch (comm->type) {
+       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+               rule = lttng_log_level_rule_exactly_create((int) comm->level);
+               break;
+       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+               rule = lttng_log_level_rule_at_least_as_severe_as_create(
+                               (int) comm->level);
+               break;
+       default:
+               abort();
+       }
+
+       if (!rule) {
+               ret = -1;
+               goto end;
+       }
+
+       *_rule = rule;
+       ret = offset;
+
+end:
+       return ret;
+}
+
+int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_log_level_rule_comm comm;
+
+
+       if (!rule) {
+               ret = 0;
+               goto end;
+       }
+
+       comm.type = (int8_t) rule->type;
+       comm.level = (int32_t) rule->level;
+
+       DBG("Serializing log level rule of type %d", rule->type);
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &comm,
+                       sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a,
+               const struct lttng_log_level_rule *b)
+{
+       bool is_equal = false;
+
+       if (a == NULL && b == NULL) {
+               /* Both are null. */
+               is_equal = true;
+               goto end;
+       }
+
+       if (a == NULL || b == NULL) {
+               /* One is NULL.*/
+               goto end;
+       }
+
+       if (a == b) {
+               /* Same object.*/
+               is_equal = true;
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       if (a->level != b->level) {
+               goto end;
+       }
+
+       is_equal = true;
+
+end:
+       return is_equal;
+}
+
+struct lttng_log_level_rule *lttng_log_level_rule_copy(
+               const struct lttng_log_level_rule *source)
+{
+       struct lttng_log_level_rule *copy = NULL;
+
+       LTTNG_ASSERT(source);
+
+       copy = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule));
+       if (!copy) {
+               goto end;
+       }
+
+       copy->type = source->type;
+       copy->level = source->level;
+end:
+       return copy;
+}
+
+void lttng_log_level_rule_to_loglevel(
+               const struct lttng_log_level_rule *log_level_rule,
+               enum lttng_loglevel_type *loglevel_type,
+               int *loglevel_value)
+{
+       LTTNG_ASSERT(log_level_rule);
+
+       switch (log_level_rule->type) {
+       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+               *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE;
+               break;
+       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+               *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE;
+               break;
+       default:
+               abort();
+       }
+
+       *loglevel_value = log_level_rule->level;
+}
+
+unsigned long lttng_log_level_rule_hash(
+               const struct lttng_log_level_rule *log_level_rule)
+{
+       unsigned long hash;
+       enum lttng_log_level_rule_status llr_status;
+       int log_level_value;
+       enum lttng_log_level_rule_type type;
+
+       LTTNG_ASSERT(log_level_rule);
+
+       type = lttng_log_level_rule_get_type(log_level_rule);
+
+       switch (type) {
+       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+               llr_status = lttng_log_level_rule_exactly_get_level(
+                               log_level_rule, &log_level_value);
+               break;
+       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+               llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                               log_level_rule, &log_level_value);
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
+
+       hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed);
+
+       hash ^= hash_key_ulong((void *) (unsigned long) log_level_value,
+                       lttng_ht_seed);
+
+       return hash;
+}
+
+enum lttng_error_code lttng_log_level_rule_mi_serialize(
+               const struct lttng_log_level_rule *rule,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_log_level_rule_status status;
+       const char *element_str = NULL;
+       int level;
+
+       LTTNG_ASSERT(rule);
+       LTTNG_ASSERT(writer);
+
+       switch (lttng_log_level_rule_get_type(rule)) {
+       case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY:
+               status = lttng_log_level_rule_exactly_get_level(rule, &level);
+               element_str = mi_lttng_element_log_level_rule_exactly;
+               break;
+       case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS:
+               element_str = mi_lttng_element_log_level_rule_at_least_as_severe_as;
+               status = lttng_log_level_rule_at_least_as_severe_as_get_level(
+                               rule, &level);
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK);
+
+       /* Open log level rule element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_log_level_rule);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Log level rule type element. */
+       ret = mi_lttng_writer_open_element(writer, element_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Level. */
+       ret = mi_lttng_writer_write_element_signed_int(
+                       writer, mi_lttng_element_log_level_rule_level, level);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close log level rule type element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close log level rule element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/lttng-elf.c b/src/common/lttng-elf.c
deleted file mode 100644 (file)
index d6cef2f..0000000
+++ /dev/null
@@ -1,1080 +0,0 @@
-/*
- * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
- * Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright (C) 2017 Erica Bugden <erica.bugden@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- *
- */
-
-#include <common/compat/endian.h>
-#include <common/error.h>
-#include <common/lttng-elf.h>
-#include <common/macros.h>
-#include <common/readwrite.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <elf.h>
-
-#define BUF_LEN        4096
-#define TEXT_SECTION_NAME      ".text"
-#define SYMBOL_TAB_SECTION_NAME ".symtab"
-#define STRING_TAB_SECTION_NAME ".strtab"
-#define DYNAMIC_SYMBOL_TAB_SECTION_NAME ".dynsym"
-#define DYNAMIC_STRING_TAB_SECTION_NAME ".dynstr"
-#define NOTE_STAPSDT_SECTION_NAME ".note.stapsdt"
-#define NOTE_STAPSDT_NAME "stapsdt"
-#define NOTE_STAPSDT_TYPE 3
-#define MAX_SECTION_DATA_SIZE   512 * 1024 * 1024
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define NATIVE_ELF_ENDIANNESS ELFDATA2LSB
-#else
-#define NATIVE_ELF_ENDIANNESS ELFDATA2MSB
-#endif
-
-#define next_4bytes_boundary(x) (typeof(x)) ((((uint64_t)x) + 3) & ~0x03)
-
-#define bswap(x)                               \
-       do {                                    \
-               switch (sizeof(x)) {            \
-               case 8:                         \
-                       x = be64toh((uint64_t)x);               \
-                       break;                  \
-               case 4:                         \
-                       x = be32toh((uint32_t)x);               \
-                       break;                  \
-               case 2:                         \
-                       x = be16toh((uint16_t)x);               \
-                       break;                  \
-               case 1:                         \
-                       break;                  \
-               default:                        \
-                       abort();                \
-               }                               \
-       } while (0)
-
-#define bswap_shdr(shdr)           \
-       do {                                \
-               bswap((shdr).sh_name);      \
-               bswap((shdr).sh_type);      \
-               bswap((shdr).sh_flags);     \
-               bswap((shdr).sh_addr);      \
-               bswap((shdr).sh_offset);    \
-               bswap((shdr).sh_size);      \
-               bswap((shdr).sh_link);      \
-               bswap((shdr).sh_info);      \
-               bswap((shdr).sh_addralign); \
-               bswap((shdr).sh_entsize);   \
-       } while (0)
-
-#define bswap_ehdr(ehdr)                               \
-       do {                                            \
-               bswap((ehdr).e_type);                   \
-               bswap((ehdr).e_machine);                \
-               bswap((ehdr).e_version);                \
-               bswap((ehdr).e_entry);                  \
-               bswap((ehdr).e_phoff);                  \
-               bswap((ehdr).e_shoff);                  \
-               bswap((ehdr).e_flags);                  \
-               bswap((ehdr).e_ehsize);                 \
-               bswap((ehdr).e_phentsize);              \
-               bswap((ehdr).e_phnum);                  \
-               bswap((ehdr).e_shentsize);              \
-               bswap((ehdr).e_shnum);                  \
-               bswap((ehdr).e_shstrndx);               \
-       } while (0)
-
-#define copy_shdr(src_shdr, dst_shdr)                                  \
-       do {                                                            \
-               (dst_shdr).sh_name = (src_shdr).sh_name;                \
-               (dst_shdr).sh_type = (src_shdr).sh_type;                \
-               (dst_shdr).sh_flags = (src_shdr).sh_flags;              \
-               (dst_shdr).sh_addr = (src_shdr).sh_addr;                \
-               (dst_shdr).sh_offset = (src_shdr).sh_offset;            \
-               (dst_shdr).sh_size = (src_shdr).sh_size;                \
-               (dst_shdr).sh_link = (src_shdr).sh_link;                \
-               (dst_shdr).sh_info = (src_shdr).sh_info;                \
-               (dst_shdr).sh_addralign = (src_shdr).sh_addralign;      \
-               (dst_shdr).sh_entsize = (src_shdr).sh_entsize;          \
-       } while (0)
-
-#define copy_ehdr(src_ehdr, dst_ehdr)                                  \
-       do {                                                            \
-               (dst_ehdr).e_type = (src_ehdr).e_type;                  \
-               (dst_ehdr).e_machine = (src_ehdr).e_machine;            \
-               (dst_ehdr).e_version = (src_ehdr).e_version;            \
-               (dst_ehdr).e_entry = (src_ehdr).e_entry;                \
-               (dst_ehdr).e_phoff = (src_ehdr).e_phoff;                \
-               (dst_ehdr).e_shoff = (src_ehdr).e_shoff;                \
-               (dst_ehdr).e_flags = (src_ehdr).e_flags;                \
-               (dst_ehdr).e_ehsize = (src_ehdr).e_ehsize;              \
-               (dst_ehdr).e_phentsize = (src_ehdr).e_phentsize;        \
-               (dst_ehdr).e_phnum = (src_ehdr).e_phnum;                \
-               (dst_ehdr).e_shentsize = (src_ehdr).e_shentsize;        \
-               (dst_ehdr).e_shnum = (src_ehdr).e_shnum;                \
-               (dst_ehdr).e_shstrndx = (src_ehdr).e_shstrndx;          \
-       } while (0)
-
-#define copy_sym(src_sym, dst_sym)                     \
-       do {                                            \
-               dst_sym.st_name = src_sym.st_name;      \
-               dst_sym.st_info = src_sym.st_info;      \
-               dst_sym.st_other = src_sym.st_other;    \
-               dst_sym.st_shndx = src_sym.st_shndx;    \
-               dst_sym.st_value = src_sym.st_value;    \
-               dst_sym.st_size = src_sym.st_size;      \
-       } while (0)
-
-#ifndef ELFCLASSNUM
-#define ELFCLASSNUM 3
-#endif
-
-#ifndef ELFDATANUM
-#define ELFDATANUM 3
-#endif
-
-#ifndef EV_NUM
-#define EV_NUM 2
-#endif
-
-struct lttng_elf_ehdr {
-       uint16_t e_type;
-       uint16_t e_machine;
-       uint32_t e_version;
-       uint64_t e_entry;
-       uint64_t e_phoff;
-       uint64_t e_shoff;
-       uint32_t e_flags;
-       uint16_t e_ehsize;
-       uint16_t e_phentsize;
-       uint16_t e_phnum;
-       uint16_t e_shentsize;
-       uint16_t e_shnum;
-       uint16_t e_shstrndx;
-};
-
-struct lttng_elf_shdr {
-       uint32_t sh_name;
-       uint32_t sh_type;
-       uint64_t sh_flags;
-       uint64_t sh_addr;
-       uint64_t sh_offset;
-       uint64_t sh_size;
-       uint32_t sh_link;
-       uint32_t sh_info;
-       uint64_t sh_addralign;
-       uint64_t sh_entsize;
-};
-
-/*
- * This struct can hold both 32bit and 64bit symbol description. It's used with
- * the copy_sym() macro. Using this abstraction, we can use the same code for
- * both bitness.
- */
-struct lttng_elf_sym {
-       uint32_t st_name;
-       uint8_t  st_info;
-       uint8_t  st_other;
-       uint16_t st_shndx;
-       uint64_t st_value;
-       uint64_t st_size;
-};
-
-struct lttng_elf {
-       int fd;
-       size_t file_size;
-       uint8_t bitness;
-       uint8_t endianness;
-       /* Offset in bytes to start of section names string table. */
-       off_t section_names_offset;
-       /* Size in bytes of section names string table. */
-       size_t section_names_size;
-       struct lttng_elf_ehdr *ehdr;
-};
-
-static inline
-int is_elf_32_bit(struct lttng_elf *elf)
-{
-       return elf->bitness == ELFCLASS32;
-}
-
-static inline
-int is_elf_native_endian(struct lttng_elf *elf)
-{
-       return elf->endianness == NATIVE_ELF_ENDIANNESS;
-}
-
-static
-int populate_section_header(struct lttng_elf * elf, struct lttng_elf_shdr *shdr,
-               uint32_t index)
-{
-       int ret = 0;
-       off_t offset;
-
-       /* Compute the offset of the section in the file */
-       offset = (off_t) elf->ehdr->e_shoff
-                       + (off_t) index * elf->ehdr->e_shentsize;
-
-       if (lseek(elf->fd, offset, SEEK_SET) < 0) {
-               PERROR("Error seeking to the beginning of ELF section header");
-               ret = -1;
-               goto error;
-       }
-
-       if (is_elf_32_bit(elf)) {
-               Elf32_Shdr elf_shdr;
-
-               if (lttng_read(elf->fd, &elf_shdr, sizeof(elf_shdr)) < sizeof(elf_shdr)) {
-                       PERROR("Error reading ELF section header");
-                       ret = -1;
-                       goto error;
-               }
-               if (!is_elf_native_endian(elf)) {
-                       bswap_shdr(elf_shdr);
-               }
-               copy_shdr(elf_shdr, *shdr);
-       } else {
-               Elf64_Shdr elf_shdr;
-
-               if (lttng_read(elf->fd, &elf_shdr, sizeof(elf_shdr)) < sizeof(elf_shdr)) {
-                       PERROR("Error reading ELF section header");
-                       ret = -1;
-                       goto error;
-               }
-               if (!is_elf_native_endian(elf)) {
-                       bswap_shdr(elf_shdr);
-               }
-               copy_shdr(elf_shdr, *shdr);
-       }
-
-error:
-       return ret;
-}
-
-static
-int populate_elf_header(struct lttng_elf *elf)
-{
-       int ret = 0;
-
-       /*
-        * Move the read pointer back to the beginning to read the full header
-        * and copy it in our structure.
-        */
-       if (lseek(elf->fd, 0, SEEK_SET) < 0) {
-               PERROR("Error seeking to the beginning of the file");
-               ret = -1;
-               goto error;
-       }
-
-       /*
-        * Use macros to set fields in the ELF header struct for both 32bit and
-        * 64bit.
-        */
-       if (is_elf_32_bit(elf)) {
-               Elf32_Ehdr elf_ehdr;
-
-               if (lttng_read(elf->fd, &elf_ehdr, sizeof(elf_ehdr)) < sizeof(elf_ehdr)) {
-                       ret = -1;
-                       goto error;
-               }
-               if (!is_elf_native_endian(elf)) {
-                       bswap_ehdr(elf_ehdr);
-               }
-               copy_ehdr(elf_ehdr, *(elf->ehdr));
-       } else {
-               Elf64_Ehdr elf_ehdr;
-
-               if (lttng_read(elf->fd, &elf_ehdr, sizeof(elf_ehdr)) < sizeof(elf_ehdr)) {
-                       ret = -1;
-                       goto error;
-               }
-               if (!is_elf_native_endian(elf)) {
-                       bswap_ehdr(elf_ehdr);
-               }
-               copy_ehdr(elf_ehdr, *(elf->ehdr));
-       }
-error:
-       return ret;
-}
-
-/*
- * Retrieve the nth (where n is the `index` argument) shdr (section
- * header) from the given elf instance.
- *
- * 0 is returned on succes, -1 on failure.
- */
-static
-int lttng_elf_get_section_hdr(struct lttng_elf *elf,
-               uint16_t index, struct lttng_elf_shdr *out_header)
-{
-       int ret = 0;
-
-       if (!elf) {
-               ret = -1;
-               goto error;
-       }
-
-       if (index >= elf->ehdr->e_shnum) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = populate_section_header(elf, out_header, index);
-       if (ret) {
-               DBG("Error populating section header.");
-               goto error;
-       }
-
-error:
-       return ret;
-}
-
-/*
- * Lookup a section's name from a given offset (usually from an shdr's
- * sh_name value) in bytes relative to the beginning of the section
- * names string table.
- *
- * If no name is found, NULL is returned.
- */
-static
-char *lttng_elf_get_section_name(struct lttng_elf *elf, off_t offset)
-{
-       char *name = NULL;
-       size_t name_length = 0, to_read;        /* name_length does not include \0 */
-
-       if (!elf) {
-               goto error;
-       }
-
-       if (offset >= elf->section_names_size) {
-               goto error;
-       }
-
-       if (lseek(elf->fd, elf->section_names_offset + offset, SEEK_SET) < 0) {
-               PERROR("Error seeking to the beginning of ELF string table section");
-               goto error;
-       }
-
-       to_read = elf->section_names_size - offset;
-
-       /* Find first \0 after or at current location, remember name_length. */
-       for (;;) {
-               char buf[BUF_LEN];
-               ssize_t read_len;
-               size_t i;
-
-               if (!to_read) {
-                       goto error;
-               }
-               read_len = lttng_read(elf->fd, buf, min_t(size_t, BUF_LEN, to_read));
-               if (read_len <= 0) {
-                       PERROR("Error reading ELF string table section");
-                       goto error;
-               }
-               for (i = 0; i < read_len; i++) {
-                       if (buf[i] == '\0') {
-                               name_length += i;
-                               goto end;
-                       }
-               }
-               name_length += read_len;
-               to_read -= read_len;
-       }
-end:
-       /*
-        * We found the length of the section name, now seek back to the
-        * beginning of the name and copy it in the newly allocated buffer.
-        */
-       name = zmalloc(sizeof(char) * (name_length + 1));       /* + 1 for \0 */
-       if (!name) {
-               PERROR("Error allocating ELF section name buffer");
-               goto error;
-       }
-       if (lseek(elf->fd, elf->section_names_offset + offset, SEEK_SET) < 0) {
-               PERROR("Error seeking to the offset of the ELF section name");
-               goto error;
-       }
-       if (lttng_read(elf->fd, name, name_length + 1) < name_length + 1) {
-               PERROR("Error reading the ELF section name");
-               goto error;
-       }
-
-       return name;
-
-error:
-       free(name);
-       return NULL;
-}
-
-static
-int lttng_elf_validate_and_populate(struct lttng_elf *elf)
-{
-       uint8_t version;
-       uint8_t e_ident[EI_NIDENT];
-       uint8_t *magic_number = NULL;
-       int ret = 0;
-
-       if (elf->fd == -1) {
-               DBG("fd error");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * First read the magic number, endianness and version to later populate
-        * the ELF header with the correct endianness and bitness.
-        * (see elf.h)
-        */
-
-       if (lseek(elf->fd, 0, SEEK_SET) < 0) {
-               PERROR("Error seeking the beginning of ELF file");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-       ret = lttng_read(elf->fd, e_ident, EI_NIDENT);
-       if (ret < EI_NIDENT) {
-               DBG("Error reading the ELF identification fields");
-               if (ret == -1) {
-                       PERROR("Error reading the ELF identification fields");
-               }
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * Copy fields used to check that the target file is in fact a valid ELF
-        * file.
-        */
-       elf->bitness = e_ident[EI_CLASS];
-       elf->endianness = e_ident[EI_DATA];
-       version = e_ident[EI_VERSION];
-       magic_number = &e_ident[EI_MAG0];
-
-       /*
-        * Check the magic number.
-        */
-       if (memcmp(magic_number, ELFMAG, SELFMAG) != 0) {
-               DBG("Error check ELF magic number.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * Check the bitness is either ELFCLASS32 or ELFCLASS64.
-        */
-       if (elf->bitness <= ELFCLASSNONE || elf->bitness >= ELFCLASSNUM) {
-               DBG("ELF class error.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * Check the endianness is either ELFDATA2LSB or ELFDATA2MSB.
-        */
-       if (elf->endianness <= ELFDATANONE || elf->endianness >= ELFDATANUM) {
-               DBG("ELF endianness error.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * Check the version is ELF_CURRENT.
-        */
-       if (version <= EV_NONE || version >= EV_NUM) {
-               DBG("Wrong ELF version.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       elf->ehdr = zmalloc(sizeof(struct lttng_elf_ehdr));
-       if (!elf->ehdr) {
-               PERROR("Error allocation buffer for ELF header");
-               ret = LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       /*
-        * Copy the content of the elf header.
-        */
-       ret = populate_elf_header(elf);
-       if (ret) {
-               DBG("Error reading ELF header,");
-               goto free_elf_error;
-       }
-
-       goto end;
-
-free_elf_error:
-       free(elf->ehdr);
-       elf->ehdr = NULL;
-end:
-       return ret;
-}
-
-/*
- * Create an instance of lttng_elf for the ELF file located at
- * `path`.
- *
- * Return a pointer to the instance on success, NULL on failure.
- */
-static
-struct lttng_elf *lttng_elf_create(int fd)
-{
-       struct lttng_elf_shdr section_names_shdr;
-       struct lttng_elf *elf = NULL;
-       int ret;
-       struct stat stat_buf;
-
-       if (fd < 0) {
-               goto error;
-       }
-
-       ret = fstat(fd, &stat_buf);
-       if (ret) {
-               PERROR("Failed to determine size of elf file");
-               goto error;
-       }
-       if (!S_ISREG(stat_buf.st_mode)) {
-               ERR("Refusing to initialize lttng_elf from non-regular file");
-               goto error;
-       }
-
-       elf = zmalloc(sizeof(struct lttng_elf));
-       if (!elf) {
-               PERROR("Error allocating struct lttng_elf");
-               goto error;
-       }
-       elf->file_size = (size_t) stat_buf.st_size;
-
-       elf->fd = dup(fd);
-       if (elf->fd < 0) {
-               PERROR("Error duplicating file descriptor to binary");
-               goto error;
-       }
-
-       ret = lttng_elf_validate_and_populate(elf);
-       if (ret) {
-               goto error;
-       }
-
-       ret = lttng_elf_get_section_hdr(
-                       elf, elf->ehdr->e_shstrndx, &section_names_shdr);
-       if (ret) {
-               goto error;
-       }
-
-       elf->section_names_offset = section_names_shdr.sh_offset;
-       elf->section_names_size = section_names_shdr.sh_size;
-       return elf;
-
-error:
-       if (elf) {
-               if (elf->ehdr) {
-                       free(elf->ehdr);
-               }
-               if (elf->fd >= 0) {
-                       if (close(elf->fd)) {
-                               PERROR("Error closing file descriptor in error path");
-                               abort();
-                       }
-               }
-               free(elf);
-       }
-       return NULL;
-}
-
-/*
- * Destroy the given lttng_elf instance.
- */
-static
-void lttng_elf_destroy(struct lttng_elf *elf)
-{
-       if (!elf) {
-               return;
-       }
-
-       free(elf->ehdr);
-       if (close(elf->fd)) {
-               PERROR("Error closing file description in error path");
-               abort();
-       }
-       free(elf);
-}
-
-static
-int lttng_elf_get_section_hdr_by_name(struct lttng_elf *elf,
-               const char *section_name, struct lttng_elf_shdr *section_hdr)
-{
-       int i;
-       char *curr_section_name;
-
-       for (i = 0; i < elf->ehdr->e_shnum; ++i) {
-               bool name_equal;
-               int ret = lttng_elf_get_section_hdr(elf, i, section_hdr);
-
-               if (ret) {
-                       break;
-               }
-               curr_section_name = lttng_elf_get_section_name(elf,
-                               section_hdr->sh_name);
-               if (!curr_section_name) {
-                       continue;
-               }
-               name_equal = strcmp(curr_section_name, section_name) == 0;
-               free(curr_section_name);
-               if (name_equal) {
-                       return 0;
-               }
-       }
-       return LTTNG_ERR_ELF_PARSING;
-}
-
-static
-char *lttng_elf_get_section_data(struct lttng_elf *elf,
-               struct lttng_elf_shdr *shdr)
-{
-       int ret;
-       off_t section_offset;
-       char *data;
-       size_t max_alloc_size;
-
-       if (!elf || !shdr) {
-               goto error;
-       }
-
-       max_alloc_size = min_t(size_t, MAX_SECTION_DATA_SIZE, elf->file_size);
-
-       section_offset = shdr->sh_offset;
-       if (lseek(elf->fd, section_offset, SEEK_SET) < 0) {
-               PERROR("Error seeking to section offset");
-               goto error;
-       }
-
-       if (shdr->sh_size > max_alloc_size) {
-               ERR("ELF section size exceeds maximal allowed size of %zu bytes",
-                               max_alloc_size);
-               goto error;
-       }
-       data = zmalloc(shdr->sh_size);
-       if (!data) {
-               PERROR("Error allocating buffer for ELF section data");
-               goto error;
-       }
-       ret = lttng_read(elf->fd, data, shdr->sh_size);
-       if (ret == -1) {
-               PERROR("Error reading ELF section data");
-               goto free_error;
-       }
-
-       return data;
-
-free_error:
-       free(data);
-error:
-       return NULL;
-}
-
-/*
- * Convert the virtual address in a binary's mapping to the offset of
- * the corresponding instruction in the binary file.
- * This function assumes the address is in the text section.
- *
- * Returns the offset on success or non-zero in case of failure.
- */
-static
-int lttng_elf_convert_addr_in_text_to_offset(struct lttng_elf *elf_handle,
-               size_t addr, uint64_t *offset)
-{
-       int ret = 0;
-       off_t text_section_offset;
-       off_t text_section_addr_beg;
-       off_t text_section_addr_end;
-       off_t offset_in_section;
-       struct lttng_elf_shdr text_section_hdr;
-
-       if (!elf_handle) {
-               DBG("Invalid ELF handle.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto error;
-       }
-
-       /* Get a pointer to the .text section header. */
-       ret = lttng_elf_get_section_hdr_by_name(elf_handle,
-                       TEXT_SECTION_NAME, &text_section_hdr);
-       if (ret) {
-               DBG("Text section not found in binary.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto error;
-       }
-
-       text_section_offset = text_section_hdr.sh_offset;
-       text_section_addr_beg = text_section_hdr.sh_addr;
-       text_section_addr_end =
-                       text_section_addr_beg + text_section_hdr.sh_size;
-
-       /*
-        * Verify that the address is within the .text section boundaries.
-        */
-       if (addr < text_section_addr_beg || addr > text_section_addr_end) {
-               DBG("Address found is outside of the .text section addr=0x%zx, "
-                       ".text section=[0x%jd - 0x%jd].", addr, (intmax_t)text_section_addr_beg,
-                       (intmax_t)text_section_addr_end);
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto error;
-       }
-
-       offset_in_section = addr - text_section_addr_beg;
-
-       /*
-        * Add the target offset in the text section to the offset of this text
-        * section from the beginning of the binary file.
-        */
-       *offset = text_section_offset + offset_in_section;
-
-error:
-       return ret;
-}
-
-/*
- * Compute the offset of a symbol from the begining of the ELF binary.
- *
- * On success, returns 0 offset parameter is set to the computed value
- * On failure, returns -1.
- */
-int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
-{
-       int ret = 0;
-       int sym_found = 0;
-       int sym_count = 0;
-       int sym_idx = 0;
-       uint64_t addr = 0;
-       char *curr_sym_str = NULL;
-       char *symbol_table_data = NULL;
-       char *string_table_data = NULL;
-       const char *string_table_name = NULL;
-       struct lttng_elf_shdr symtab_hdr;
-       struct lttng_elf_shdr strtab_hdr;
-       struct lttng_elf *elf = NULL;
-
-       if (!symbol || !offset ) {
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       elf = lttng_elf_create(fd);
-       if (!elf) {
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto end;
-       }
-
-       /*
-        * The .symtab section might not exist on stripped binaries.
-        * Try to get the symbol table section header first. If it's absent,
-        * try to get the dynamic symbol table. All symbols in the dynamic
-        * symbol tab are in the (normal) symbol table if it exists.
-        */
-       ret = lttng_elf_get_section_hdr_by_name(elf, SYMBOL_TAB_SECTION_NAME,
-                       &symtab_hdr);
-       if (ret) {
-               DBG("Cannot get ELF Symbol Table section. Trying to get ELF Dynamic Symbol Table section.");
-               /* Get the dynamic symbol table section header. */
-               ret = lttng_elf_get_section_hdr_by_name(elf, DYNAMIC_SYMBOL_TAB_SECTION_NAME,
-                               &symtab_hdr);
-               if (ret) {
-                       DBG("Cannot get ELF Symbol Table nor Dynamic Symbol Table sections.");
-                       ret = LTTNG_ERR_ELF_PARSING;
-                       goto destroy_elf;
-               }
-               string_table_name = DYNAMIC_STRING_TAB_SECTION_NAME;
-       } else {
-               string_table_name = STRING_TAB_SECTION_NAME;
-       }
-
-       /* Get the data associated with the symbol table section. */
-       symbol_table_data = lttng_elf_get_section_data(elf, &symtab_hdr);
-       if (symbol_table_data == NULL) {
-               DBG("Cannot get ELF Symbol Table data.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto destroy_elf;
-       }
-
-       /* Get the string table section header. */
-       ret = lttng_elf_get_section_hdr_by_name(elf, string_table_name,
-                       &strtab_hdr);
-       if (ret) {
-               DBG("Cannot get ELF string table section.");
-               goto free_symbol_table_data;
-       }
-
-       /* Get the data associated with the string table section. */
-       string_table_data = lttng_elf_get_section_data(elf, &strtab_hdr);
-       if (string_table_data == NULL) {
-               DBG("Cannot get ELF string table section data.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto free_symbol_table_data;
-       }
-
-       /* Get the number of symbol in the table for the iteration. */
-       sym_count = symtab_hdr.sh_size / symtab_hdr.sh_entsize;
-
-       /* Loop over all symbol. */
-       for (sym_idx = 0; sym_idx < sym_count; sym_idx++) {
-               struct lttng_elf_sym curr_sym;
-
-               /* Get the symbol at the current index. */
-               if (is_elf_32_bit(elf)) {
-                       Elf32_Sym tmp = ((Elf32_Sym *) symbol_table_data)[sym_idx];
-                       copy_sym(tmp, curr_sym);
-               } else {
-                       Elf64_Sym tmp = ((Elf64_Sym *) symbol_table_data)[sym_idx];
-                       copy_sym(tmp, curr_sym);
-               }
-
-               /*
-                * If the st_name field is zero, there is no string name for
-                * this symbol; skip to the next symbol.
-                */
-               if (curr_sym.st_name == 0) {
-                       continue;
-               }
-
-               /*
-                * Use the st_name field in the lttng_elf_sym struct to get offset of
-                * the symbol's name from the beginning of the string table.
-                */
-               curr_sym_str = string_table_data + curr_sym.st_name;
-
-               /*
-                * If the current symbol is not a function; skip to the next symbol.
-                */
-               /* Both 32bit and 64bit use the same 1 byte field for type. (See elf.h) */
-               if (ELF32_ST_TYPE(curr_sym.st_info) != STT_FUNC) {
-                       continue;
-               }
-
-               /*
-                * Compare with the search symbol. If there is a match set the address
-                * output parameter and return success.
-                */
-               if (strcmp(symbol, curr_sym_str) == 0 ) {
-                       sym_found = 1;
-                       addr = curr_sym.st_value;
-                       break;
-               }
-       }
-
-       if (!sym_found) {
-               DBG("Symbol not found.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto free_string_table_data;
-       }
-
-       /*
-        * Use the virtual address of the symbol to compute the offset of this
-        * symbol from the beginning of the executable file.
-        */
-       ret = lttng_elf_convert_addr_in_text_to_offset(elf, addr, offset);
-       if (ret) {
-               DBG("Cannot convert addr to offset.");
-               goto free_string_table_data;
-       }
-
-
-free_string_table_data:
-       free(string_table_data);
-free_symbol_table_data:
-       free(symbol_table_data);
-destroy_elf:
-       lttng_elf_destroy(elf);
-end:
-       return ret;
-}
-
-/*
- * Compute the offsets of SDT probes from the begining of the ELF binary.
- *
- * On success, returns 0 and the nb_probes parameter is set to the number of
- * offsets found and the offsets parameter points to an array of offsets where
- * the SDT probes are.
- * On failure, returns -1.
- */
-int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name,
-               const char *probe_name, uint64_t **offsets, uint32_t *nb_probes)
-{
-       int ret = 0, nb_match = 0;
-       struct lttng_elf_shdr stap_note_section_hdr;
-       struct lttng_elf *elf = NULL;
-       char *stap_note_section_data = NULL;
-       char *curr_note_section_begin, *curr_data_ptr, *curr_probe, *curr_provider;
-       char *next_note_ptr;
-       uint32_t name_size, desc_size, note_type;
-       uint64_t curr_probe_location, curr_probe_offset, curr_semaphore_location;
-       uint64_t *probe_locs = NULL, *new_probe_locs = NULL;
-
-       if (!provider_name || !probe_name || !nb_probes || !offsets) {
-               DBG("Invalid arguments.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto error;
-       }
-
-       elf = lttng_elf_create(fd);
-       if (!elf) {
-               DBG("Error allocation ELF.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto error;
-       }
-
-       /* Get the stap note section header. */
-       ret = lttng_elf_get_section_hdr_by_name(elf, NOTE_STAPSDT_SECTION_NAME,
-                       &stap_note_section_hdr);
-       if (ret) {
-               DBG("Cannot get ELF stap note section.");
-               goto destroy_elf_error;
-       }
-
-       /* Get the data associated with the stap note section. */
-       stap_note_section_data =
-                       lttng_elf_get_section_data(elf, &stap_note_section_hdr);
-       if (stap_note_section_data == NULL) {
-               DBG("Cannot get ELF stap note section data.");
-               ret = LTTNG_ERR_ELF_PARSING;
-               goto destroy_elf_error;
-       }
-
-       next_note_ptr = stap_note_section_data;
-       curr_note_section_begin = stap_note_section_data;
-
-       *offsets = NULL;
-       while (1) {
-               curr_data_ptr = next_note_ptr;
-               /* Check if we have reached the end of the note section. */
-               if (curr_data_ptr >=
-                               curr_note_section_begin +
-                                               stap_note_section_hdr.sh_size) {
-                       *nb_probes = nb_match;
-                       *offsets = probe_locs;
-                       ret = 0;
-                       break;
-               }
-               /* Get name size field. */
-               name_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
-               curr_data_ptr += sizeof(uint32_t);
-
-               /* Sanity check; a zero name_size is reserved. */
-               if (name_size == 0) {
-                       DBG("Invalid name size field in SDT probe descriptions"
-                               "section.");
-                       ret = -1;
-                       goto realloc_error;
-               }
-
-               /* Get description size field. */
-               desc_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
-               curr_data_ptr += sizeof(uint32_t);
-
-               /* Get type field. */
-               note_type = *(uint32_t *) curr_data_ptr;
-               curr_data_ptr += sizeof(uint32_t);
-
-               /*
-                * Move the pointer to the next note to be ready for the next
-                * iteration. The current note is made of 3 unsigned 32bit
-                * integers (name size, descriptor size and note type), the
-                * name and the descriptor. To move to the next note, we move
-                * the pointer according to those values.
-                */
-               next_note_ptr = next_note_ptr +
-                       (3 * sizeof(uint32_t)) + desc_size + name_size;
-
-               /*
-                * Move ptr to the end of the name string (we don't need it)
-                * and go to the next 4 byte alignement.
-                */
-               if (note_type != NOTE_STAPSDT_TYPE ||
-                       strncmp(curr_data_ptr, NOTE_STAPSDT_NAME, name_size) != 0) {
-                       continue;
-               }
-
-               curr_data_ptr += name_size;
-
-               /* Get probe location.  */
-               curr_probe_location = *(uint64_t *) curr_data_ptr;
-               curr_data_ptr += sizeof(uint64_t);
-
-               /* Pass over the base. Not needed. */
-               curr_data_ptr += sizeof(uint64_t);
-
-               /* Get semaphore location. */
-               curr_semaphore_location = *(uint64_t *) curr_data_ptr;
-               curr_data_ptr += sizeof(uint64_t);
-               /* Get provider name. */
-               curr_provider = curr_data_ptr;
-               curr_data_ptr += strlen(curr_provider) + 1;
-
-               /* Get probe name. */
-               curr_probe = curr_data_ptr;
-
-               /* Check if the provider and probe name match */
-               if (strcmp(provider_name, curr_provider) == 0 &&
-                               strcmp(probe_name, curr_probe) == 0) {
-                       int new_size;
-
-                       /*
-                        * We currently don't support SDT probes with semaphores. Return
-                        * success as we found a matching probe but it's guarded by a
-                        * semaphore.
-                        */
-                       if (curr_semaphore_location != 0) {
-                               ret = LTTNG_ERR_SDT_PROBE_SEMAPHORE;
-                               goto realloc_error;
-                       }
-
-                       new_size = (++nb_match) * sizeof(uint64_t);
-
-                       /*
-                        * Found a match with not semaphore, we need to copy the
-                        * probe_location to the output parameter.
-                        */
-                       new_probe_locs = realloc(probe_locs, new_size);
-                       if (!new_probe_locs) {
-                               /* Error allocating a larger buffer */
-                               DBG("Allocation error in SDT.");
-                               ret = LTTNG_ERR_NOMEM;
-                               goto realloc_error;
-                       }
-                       probe_locs = new_probe_locs;
-                       new_probe_locs = NULL;
-
-                       /*
-                        * Use the virtual address of the probe to compute the offset of
-                        * this probe from the beginning of the executable file.
-                        */
-                       ret = lttng_elf_convert_addr_in_text_to_offset(elf,
-                                       curr_probe_location, &curr_probe_offset);
-                       if (ret) {
-                               DBG("Conversion error in SDT.");
-                               goto realloc_error;
-                       }
-
-                       probe_locs[nb_match - 1] = curr_probe_offset;
-               }
-       }
-
-end:
-       free(stap_note_section_data);
-destroy_elf_error:
-       lttng_elf_destroy(elf);
-error:
-       return ret;
-realloc_error:
-       free(probe_locs);
-       goto end;
-}
diff --git a/src/common/lttng-elf.cpp b/src/common/lttng-elf.cpp
new file mode 100644 (file)
index 0000000..992410a
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+ * Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright (C) 2017 Erica Bugden <erica.bugden@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ */
+
+#include <algorithm>
+#include <common/compat/endian.h>
+#include <common/error.h>
+#include <common/lttng-elf.h>
+#include <common/macros.h>
+#include <common/readwrite.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <elf.h>
+
+#define BUF_LEN        4096
+#define TEXT_SECTION_NAME      ".text"
+#define SYMBOL_TAB_SECTION_NAME ".symtab"
+#define STRING_TAB_SECTION_NAME ".strtab"
+#define DYNAMIC_SYMBOL_TAB_SECTION_NAME ".dynsym"
+#define DYNAMIC_STRING_TAB_SECTION_NAME ".dynstr"
+#define NOTE_STAPSDT_SECTION_NAME ".note.stapsdt"
+#define NOTE_STAPSDT_NAME "stapsdt"
+#define NOTE_STAPSDT_TYPE 3
+#define MAX_SECTION_DATA_SIZE   512 * 1024 * 1024
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define NATIVE_ELF_ENDIANNESS ELFDATA2LSB
+#else
+#define NATIVE_ELF_ENDIANNESS ELFDATA2MSB
+#endif
+
+#define next_4bytes_boundary(x) (typeof(x)) ((((uint64_t)x) + 3) & ~0x03)
+
+#define bswap(x)                               \
+       do {                                    \
+               switch (sizeof(x)) {            \
+               case 8:                         \
+                       x = be64toh((uint64_t)x);               \
+                       break;                  \
+               case 4:                         \
+                       x = be32toh((uint32_t)x);               \
+                       break;                  \
+               case 2:                         \
+                       x = be16toh((uint16_t)x);               \
+                       break;                  \
+               case 1:                         \
+                       break;                  \
+               default:                        \
+                       abort();                \
+               }                               \
+       } while (0)
+
+#define bswap_shdr(shdr)           \
+       do {                                \
+               bswap((shdr).sh_name);      \
+               bswap((shdr).sh_type);      \
+               bswap((shdr).sh_flags);     \
+               bswap((shdr).sh_addr);      \
+               bswap((shdr).sh_offset);    \
+               bswap((shdr).sh_size);      \
+               bswap((shdr).sh_link);      \
+               bswap((shdr).sh_info);      \
+               bswap((shdr).sh_addralign); \
+               bswap((shdr).sh_entsize);   \
+       } while (0)
+
+#define bswap_ehdr(ehdr)                               \
+       do {                                            \
+               bswap((ehdr).e_type);                   \
+               bswap((ehdr).e_machine);                \
+               bswap((ehdr).e_version);                \
+               bswap((ehdr).e_entry);                  \
+               bswap((ehdr).e_phoff);                  \
+               bswap((ehdr).e_shoff);                  \
+               bswap((ehdr).e_flags);                  \
+               bswap((ehdr).e_ehsize);                 \
+               bswap((ehdr).e_phentsize);              \
+               bswap((ehdr).e_phnum);                  \
+               bswap((ehdr).e_shentsize);              \
+               bswap((ehdr).e_shnum);                  \
+               bswap((ehdr).e_shstrndx);               \
+       } while (0)
+
+#define copy_shdr(src_shdr, dst_shdr)                                  \
+       do {                                                            \
+               (dst_shdr).sh_name = (src_shdr).sh_name;                \
+               (dst_shdr).sh_type = (src_shdr).sh_type;                \
+               (dst_shdr).sh_flags = (src_shdr).sh_flags;              \
+               (dst_shdr).sh_addr = (src_shdr).sh_addr;                \
+               (dst_shdr).sh_offset = (src_shdr).sh_offset;            \
+               (dst_shdr).sh_size = (src_shdr).sh_size;                \
+               (dst_shdr).sh_link = (src_shdr).sh_link;                \
+               (dst_shdr).sh_info = (src_shdr).sh_info;                \
+               (dst_shdr).sh_addralign = (src_shdr).sh_addralign;      \
+               (dst_shdr).sh_entsize = (src_shdr).sh_entsize;          \
+       } while (0)
+
+#define copy_ehdr(src_ehdr, dst_ehdr)                                  \
+       do {                                                            \
+               (dst_ehdr).e_type = (src_ehdr).e_type;                  \
+               (dst_ehdr).e_machine = (src_ehdr).e_machine;            \
+               (dst_ehdr).e_version = (src_ehdr).e_version;            \
+               (dst_ehdr).e_entry = (src_ehdr).e_entry;                \
+               (dst_ehdr).e_phoff = (src_ehdr).e_phoff;                \
+               (dst_ehdr).e_shoff = (src_ehdr).e_shoff;                \
+               (dst_ehdr).e_flags = (src_ehdr).e_flags;                \
+               (dst_ehdr).e_ehsize = (src_ehdr).e_ehsize;              \
+               (dst_ehdr).e_phentsize = (src_ehdr).e_phentsize;        \
+               (dst_ehdr).e_phnum = (src_ehdr).e_phnum;                \
+               (dst_ehdr).e_shentsize = (src_ehdr).e_shentsize;        \
+               (dst_ehdr).e_shnum = (src_ehdr).e_shnum;                \
+               (dst_ehdr).e_shstrndx = (src_ehdr).e_shstrndx;          \
+       } while (0)
+
+#define copy_sym(src_sym, dst_sym)                     \
+       do {                                            \
+               dst_sym.st_name = src_sym.st_name;      \
+               dst_sym.st_info = src_sym.st_info;      \
+               dst_sym.st_other = src_sym.st_other;    \
+               dst_sym.st_shndx = src_sym.st_shndx;    \
+               dst_sym.st_value = src_sym.st_value;    \
+               dst_sym.st_size = src_sym.st_size;      \
+       } while (0)
+
+#ifndef ELFCLASSNUM
+#define ELFCLASSNUM 3
+#endif
+
+#ifndef ELFDATANUM
+#define ELFDATANUM 3
+#endif
+
+#ifndef EV_NUM
+#define EV_NUM 2
+#endif
+
+struct lttng_elf_ehdr {
+       uint16_t e_type;
+       uint16_t e_machine;
+       uint32_t e_version;
+       uint64_t e_entry;
+       uint64_t e_phoff;
+       uint64_t e_shoff;
+       uint32_t e_flags;
+       uint16_t e_ehsize;
+       uint16_t e_phentsize;
+       uint16_t e_phnum;
+       uint16_t e_shentsize;
+       uint16_t e_shnum;
+       uint16_t e_shstrndx;
+};
+
+struct lttng_elf_shdr {
+       uint32_t sh_name;
+       uint32_t sh_type;
+       uint64_t sh_flags;
+       uint64_t sh_addr;
+       uint64_t sh_offset;
+       uint64_t sh_size;
+       uint32_t sh_link;
+       uint32_t sh_info;
+       uint64_t sh_addralign;
+       uint64_t sh_entsize;
+};
+
+/*
+ * This struct can hold both 32bit and 64bit symbol description. It's used with
+ * the copy_sym() macro. Using this abstraction, we can use the same code for
+ * both bitness.
+ */
+struct lttng_elf_sym {
+       uint32_t st_name;
+       uint8_t  st_info;
+       uint8_t  st_other;
+       uint16_t st_shndx;
+       uint64_t st_value;
+       uint64_t st_size;
+};
+
+struct lttng_elf {
+       int fd;
+       size_t file_size;
+       uint8_t bitness;
+       uint8_t endianness;
+       /* Offset in bytes to start of section names string table. */
+       off_t section_names_offset;
+       /* Size in bytes of section names string table. */
+       size_t section_names_size;
+       struct lttng_elf_ehdr *ehdr;
+};
+
+static inline
+int is_elf_32_bit(struct lttng_elf *elf)
+{
+       return elf->bitness == ELFCLASS32;
+}
+
+static inline
+int is_elf_native_endian(struct lttng_elf *elf)
+{
+       return elf->endianness == NATIVE_ELF_ENDIANNESS;
+}
+
+static
+int populate_section_header(struct lttng_elf * elf, struct lttng_elf_shdr *shdr,
+               uint32_t index)
+{
+       int ret = 0;
+       off_t offset;
+
+       /* Compute the offset of the section in the file */
+       offset = (off_t) elf->ehdr->e_shoff
+                       + (off_t) index * elf->ehdr->e_shentsize;
+
+       if (lseek(elf->fd, offset, SEEK_SET) < 0) {
+               PERROR("Error seeking to the beginning of ELF section header");
+               ret = -1;
+               goto error;
+       }
+
+       if (is_elf_32_bit(elf)) {
+               Elf32_Shdr elf_shdr;
+
+               if (lttng_read(elf->fd, &elf_shdr, sizeof(elf_shdr)) < sizeof(elf_shdr)) {
+                       PERROR("Error reading ELF section header");
+                       ret = -1;
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_shdr(elf_shdr);
+               }
+               copy_shdr(elf_shdr, *shdr);
+       } else {
+               Elf64_Shdr elf_shdr;
+
+               if (lttng_read(elf->fd, &elf_shdr, sizeof(elf_shdr)) < sizeof(elf_shdr)) {
+                       PERROR("Error reading ELF section header");
+                       ret = -1;
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_shdr(elf_shdr);
+               }
+               copy_shdr(elf_shdr, *shdr);
+       }
+
+error:
+       return ret;
+}
+
+static
+int populate_elf_header(struct lttng_elf *elf)
+{
+       int ret = 0;
+
+       /*
+        * Move the read pointer back to the beginning to read the full header
+        * and copy it in our structure.
+        */
+       if (lseek(elf->fd, 0, SEEK_SET) < 0) {
+               PERROR("Error seeking to the beginning of the file");
+               ret = -1;
+               goto error;
+       }
+
+       /*
+        * Use macros to set fields in the ELF header struct for both 32bit and
+        * 64bit.
+        */
+       if (is_elf_32_bit(elf)) {
+               Elf32_Ehdr elf_ehdr;
+
+               if (lttng_read(elf->fd, &elf_ehdr, sizeof(elf_ehdr)) < sizeof(elf_ehdr)) {
+                       ret = -1;
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_ehdr(elf_ehdr);
+               }
+               copy_ehdr(elf_ehdr, *(elf->ehdr));
+       } else {
+               Elf64_Ehdr elf_ehdr;
+
+               if (lttng_read(elf->fd, &elf_ehdr, sizeof(elf_ehdr)) < sizeof(elf_ehdr)) {
+                       ret = -1;
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_ehdr(elf_ehdr);
+               }
+               copy_ehdr(elf_ehdr, *(elf->ehdr));
+       }
+error:
+       return ret;
+}
+
+/*
+ * Retrieve the nth (where n is the `index` argument) shdr (section
+ * header) from the given elf instance.
+ *
+ * 0 is returned on succes, -1 on failure.
+ */
+static
+int lttng_elf_get_section_hdr(struct lttng_elf *elf,
+               uint16_t index, struct lttng_elf_shdr *out_header)
+{
+       int ret = 0;
+
+       if (!elf) {
+               ret = -1;
+               goto error;
+       }
+
+       if (index >= elf->ehdr->e_shnum) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = populate_section_header(elf, out_header, index);
+       if (ret) {
+               DBG("Error populating section header.");
+               goto error;
+       }
+
+error:
+       return ret;
+}
+
+/*
+ * Lookup a section's name from a given offset (usually from an shdr's
+ * sh_name value) in bytes relative to the beginning of the section
+ * names string table.
+ *
+ * If no name is found, NULL is returned.
+ */
+static
+char *lttng_elf_get_section_name(struct lttng_elf *elf, off_t offset)
+{
+       char *name = NULL;
+       size_t name_length = 0, to_read;        /* name_length does not include \0 */
+
+       if (!elf) {
+               goto error;
+       }
+
+       if (offset >= elf->section_names_size) {
+               goto error;
+       }
+
+       if (lseek(elf->fd, elf->section_names_offset + offset, SEEK_SET) < 0) {
+               PERROR("Error seeking to the beginning of ELF string table section");
+               goto error;
+       }
+
+       to_read = elf->section_names_size - offset;
+
+       /* Find first \0 after or at current location, remember name_length. */
+       for (;;) {
+               char buf[BUF_LEN];
+               ssize_t read_len;
+               size_t i;
+
+               if (!to_read) {
+                       goto error;
+               }
+               read_len = lttng_read(elf->fd, buf, std::min<size_t>(BUF_LEN, to_read));
+               if (read_len <= 0) {
+                       PERROR("Error reading ELF string table section");
+                       goto error;
+               }
+               for (i = 0; i < read_len; i++) {
+                       if (buf[i] == '\0') {
+                               name_length += i;
+                               goto end;
+                       }
+               }
+               name_length += read_len;
+               to_read -= read_len;
+       }
+end:
+       /*
+        * We found the length of the section name, now seek back to the
+        * beginning of the name and copy it in the newly allocated buffer.
+        */
+       name = (char *)zmalloc(sizeof(char) * (name_length + 1));       /* + 1 for \0 */
+       if (!name) {
+               PERROR("Error allocating ELF section name buffer");
+               goto error;
+       }
+       if (lseek(elf->fd, elf->section_names_offset + offset, SEEK_SET) < 0) {
+               PERROR("Error seeking to the offset of the ELF section name");
+               goto error;
+       }
+       if (lttng_read(elf->fd, name, name_length + 1) < name_length + 1) {
+               PERROR("Error reading the ELF section name");
+               goto error;
+       }
+
+       return name;
+
+error:
+       free(name);
+       return NULL;
+}
+
+static
+int lttng_elf_validate_and_populate(struct lttng_elf *elf)
+{
+       uint8_t version;
+       uint8_t e_ident[EI_NIDENT];
+       uint8_t *magic_number = NULL;
+       int ret = 0;
+
+       if (elf->fd == -1) {
+               DBG("fd error");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * First read the magic number, endianness and version to later populate
+        * the ELF header with the correct endianness and bitness.
+        * (see elf.h)
+        */
+
+       if (lseek(elf->fd, 0, SEEK_SET) < 0) {
+               PERROR("Error seeking the beginning of ELF file");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+       ret = lttng_read(elf->fd, e_ident, EI_NIDENT);
+       if (ret < EI_NIDENT) {
+               DBG("Error reading the ELF identification fields");
+               if (ret == -1) {
+                       PERROR("Error reading the ELF identification fields");
+               }
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * Copy fields used to check that the target file is in fact a valid ELF
+        * file.
+        */
+       elf->bitness = e_ident[EI_CLASS];
+       elf->endianness = e_ident[EI_DATA];
+       version = e_ident[EI_VERSION];
+       magic_number = &e_ident[EI_MAG0];
+
+       /*
+        * Check the magic number.
+        */
+       if (memcmp(magic_number, ELFMAG, SELFMAG) != 0) {
+               DBG("Error check ELF magic number.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * Check the bitness is either ELFCLASS32 or ELFCLASS64.
+        */
+       if (elf->bitness <= ELFCLASSNONE || elf->bitness >= ELFCLASSNUM) {
+               DBG("ELF class error.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * Check the endianness is either ELFDATA2LSB or ELFDATA2MSB.
+        */
+       if (elf->endianness <= ELFDATANONE || elf->endianness >= ELFDATANUM) {
+               DBG("ELF endianness error.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * Check the version is ELF_CURRENT.
+        */
+       if (version <= EV_NONE || version >= EV_NUM) {
+               DBG("Wrong ELF version.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       elf->ehdr = (lttng_elf_ehdr *) zmalloc(sizeof(struct lttng_elf_ehdr));
+       if (!elf->ehdr) {
+               PERROR("Error allocation buffer for ELF header");
+               ret = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       /*
+        * Copy the content of the elf header.
+        */
+       ret = populate_elf_header(elf);
+       if (ret) {
+               DBG("Error reading ELF header,");
+               goto free_elf_error;
+       }
+
+       goto end;
+
+free_elf_error:
+       free(elf->ehdr);
+       elf->ehdr = NULL;
+end:
+       return ret;
+}
+
+/*
+ * Create an instance of lttng_elf for the ELF file located at
+ * `path`.
+ *
+ * Return a pointer to the instance on success, NULL on failure.
+ */
+static
+struct lttng_elf *lttng_elf_create(int fd)
+{
+       struct lttng_elf_shdr section_names_shdr;
+       struct lttng_elf *elf = NULL;
+       int ret;
+       struct stat stat_buf;
+
+       if (fd < 0) {
+               goto error;
+       }
+
+       ret = fstat(fd, &stat_buf);
+       if (ret) {
+               PERROR("Failed to determine size of elf file");
+               goto error;
+       }
+       if (!S_ISREG(stat_buf.st_mode)) {
+               ERR("Refusing to initialize lttng_elf from non-regular file");
+               goto error;
+       }
+
+       elf = (lttng_elf *) zmalloc(sizeof(struct lttng_elf));
+       if (!elf) {
+               PERROR("Error allocating struct lttng_elf");
+               goto error;
+       }
+       elf->file_size = (size_t) stat_buf.st_size;
+
+       elf->fd = dup(fd);
+       if (elf->fd < 0) {
+               PERROR("Error duplicating file descriptor to binary");
+               goto error;
+       }
+
+       ret = lttng_elf_validate_and_populate(elf);
+       if (ret) {
+               goto error;
+       }
+
+       ret = lttng_elf_get_section_hdr(
+                       elf, elf->ehdr->e_shstrndx, &section_names_shdr);
+       if (ret) {
+               goto error;
+       }
+
+       elf->section_names_offset = section_names_shdr.sh_offset;
+       elf->section_names_size = section_names_shdr.sh_size;
+       return elf;
+
+error:
+       if (elf) {
+               if (elf->ehdr) {
+                       free(elf->ehdr);
+               }
+               if (elf->fd >= 0) {
+                       if (close(elf->fd)) {
+                               PERROR("Error closing file descriptor in error path");
+                               abort();
+                       }
+               }
+               free(elf);
+       }
+       return NULL;
+}
+
+/*
+ * Destroy the given lttng_elf instance.
+ */
+static
+void lttng_elf_destroy(struct lttng_elf *elf)
+{
+       if (!elf) {
+               return;
+       }
+
+       free(elf->ehdr);
+       if (close(elf->fd)) {
+               PERROR("Error closing file description in error path");
+               abort();
+       }
+       free(elf);
+}
+
+static
+int lttng_elf_get_section_hdr_by_name(struct lttng_elf *elf,
+               const char *section_name, struct lttng_elf_shdr *section_hdr)
+{
+       int i;
+       char *curr_section_name;
+
+       for (i = 0; i < elf->ehdr->e_shnum; ++i) {
+               bool name_equal;
+               int ret = lttng_elf_get_section_hdr(elf, i, section_hdr);
+
+               if (ret) {
+                       break;
+               }
+               curr_section_name = lttng_elf_get_section_name(elf,
+                               section_hdr->sh_name);
+               if (!curr_section_name) {
+                       continue;
+               }
+               name_equal = strcmp(curr_section_name, section_name) == 0;
+               free(curr_section_name);
+               if (name_equal) {
+                       return 0;
+               }
+       }
+       return LTTNG_ERR_ELF_PARSING;
+}
+
+static
+char *lttng_elf_get_section_data(struct lttng_elf *elf,
+               struct lttng_elf_shdr *shdr)
+{
+       int ret;
+       off_t section_offset;
+       char *data;
+       size_t max_alloc_size;
+
+       if (!elf || !shdr) {
+               goto error;
+       }
+
+       max_alloc_size = std::min<size_t>(MAX_SECTION_DATA_SIZE, elf->file_size);
+
+       section_offset = shdr->sh_offset;
+       if (lseek(elf->fd, section_offset, SEEK_SET) < 0) {
+               PERROR("Error seeking to section offset");
+               goto error;
+       }
+
+       if (shdr->sh_size > max_alloc_size) {
+               ERR("ELF section size exceeds maximal allowed size of %zu bytes",
+                               max_alloc_size);
+               goto error;
+       }
+       data = (char *) zmalloc(shdr->sh_size);
+       if (!data) {
+               PERROR("Error allocating buffer for ELF section data");
+               goto error;
+       }
+       ret = lttng_read(elf->fd, data, shdr->sh_size);
+       if (ret == -1) {
+               PERROR("Error reading ELF section data");
+               goto free_error;
+       }
+
+       return data;
+
+free_error:
+       free(data);
+error:
+       return NULL;
+}
+
+/*
+ * Convert the virtual address in a binary's mapping to the offset of
+ * the corresponding instruction in the binary file.
+ * This function assumes the address is in the text section.
+ *
+ * Returns the offset on success or non-zero in case of failure.
+ */
+static
+int lttng_elf_convert_addr_in_text_to_offset(struct lttng_elf *elf_handle,
+               size_t addr, uint64_t *offset)
+{
+       int ret = 0;
+       off_t text_section_offset;
+       off_t text_section_addr_beg;
+       off_t text_section_addr_end;
+       off_t offset_in_section;
+       struct lttng_elf_shdr text_section_hdr;
+
+       if (!elf_handle) {
+               DBG("Invalid ELF handle.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto error;
+       }
+
+       /* Get a pointer to the .text section header. */
+       ret = lttng_elf_get_section_hdr_by_name(elf_handle,
+                       TEXT_SECTION_NAME, &text_section_hdr);
+       if (ret) {
+               DBG("Text section not found in binary.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto error;
+       }
+
+       text_section_offset = text_section_hdr.sh_offset;
+       text_section_addr_beg = text_section_hdr.sh_addr;
+       text_section_addr_end =
+                       text_section_addr_beg + text_section_hdr.sh_size;
+
+       /*
+        * Verify that the address is within the .text section boundaries.
+        */
+       if (addr < text_section_addr_beg || addr > text_section_addr_end) {
+               DBG("Address found is outside of the .text section addr=0x%zx, "
+                       ".text section=[0x%jd - 0x%jd].", addr, (intmax_t)text_section_addr_beg,
+                       (intmax_t)text_section_addr_end);
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto error;
+       }
+
+       offset_in_section = addr - text_section_addr_beg;
+
+       /*
+        * Add the target offset in the text section to the offset of this text
+        * section from the beginning of the binary file.
+        */
+       *offset = text_section_offset + offset_in_section;
+
+error:
+       return ret;
+}
+
+/*
+ * Compute the offset of a symbol from the begining of the ELF binary.
+ *
+ * On success, returns 0 offset parameter is set to the computed value
+ * On failure, returns -1.
+ */
+int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
+{
+       int ret = 0;
+       int sym_found = 0;
+       int sym_count = 0;
+       int sym_idx = 0;
+       uint64_t addr = 0;
+       char *curr_sym_str = NULL;
+       char *symbol_table_data = NULL;
+       char *string_table_data = NULL;
+       const char *string_table_name = NULL;
+       struct lttng_elf_shdr symtab_hdr;
+       struct lttng_elf_shdr strtab_hdr;
+       struct lttng_elf *elf = NULL;
+
+       if (!symbol || !offset ) {
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       elf = lttng_elf_create(fd);
+       if (!elf) {
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto end;
+       }
+
+       /*
+        * The .symtab section might not exist on stripped binaries.
+        * Try to get the symbol table section header first. If it's absent,
+        * try to get the dynamic symbol table. All symbols in the dynamic
+        * symbol tab are in the (normal) symbol table if it exists.
+        */
+       ret = lttng_elf_get_section_hdr_by_name(elf, SYMBOL_TAB_SECTION_NAME,
+                       &symtab_hdr);
+       if (ret) {
+               DBG("Cannot get ELF Symbol Table section. Trying to get ELF Dynamic Symbol Table section.");
+               /* Get the dynamic symbol table section header. */
+               ret = lttng_elf_get_section_hdr_by_name(elf, DYNAMIC_SYMBOL_TAB_SECTION_NAME,
+                               &symtab_hdr);
+               if (ret) {
+                       DBG("Cannot get ELF Symbol Table nor Dynamic Symbol Table sections.");
+                       ret = LTTNG_ERR_ELF_PARSING;
+                       goto destroy_elf;
+               }
+               string_table_name = DYNAMIC_STRING_TAB_SECTION_NAME;
+       } else {
+               string_table_name = STRING_TAB_SECTION_NAME;
+       }
+
+       /* Get the data associated with the symbol table section. */
+       symbol_table_data = lttng_elf_get_section_data(elf, &symtab_hdr);
+       if (symbol_table_data == NULL) {
+               DBG("Cannot get ELF Symbol Table data.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto destroy_elf;
+       }
+
+       /* Get the string table section header. */
+       ret = lttng_elf_get_section_hdr_by_name(elf, string_table_name,
+                       &strtab_hdr);
+       if (ret) {
+               DBG("Cannot get ELF string table section.");
+               goto free_symbol_table_data;
+       }
+
+       /* Get the data associated with the string table section. */
+       string_table_data = lttng_elf_get_section_data(elf, &strtab_hdr);
+       if (string_table_data == NULL) {
+               DBG("Cannot get ELF string table section data.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto free_symbol_table_data;
+       }
+
+       /* Get the number of symbol in the table for the iteration. */
+       sym_count = symtab_hdr.sh_size / symtab_hdr.sh_entsize;
+
+       /* Loop over all symbol. */
+       for (sym_idx = 0; sym_idx < sym_count; sym_idx++) {
+               struct lttng_elf_sym curr_sym;
+
+               /* Get the symbol at the current index. */
+               if (is_elf_32_bit(elf)) {
+                       Elf32_Sym tmp = ((Elf32_Sym *) symbol_table_data)[sym_idx];
+                       copy_sym(tmp, curr_sym);
+               } else {
+                       Elf64_Sym tmp = ((Elf64_Sym *) symbol_table_data)[sym_idx];
+                       copy_sym(tmp, curr_sym);
+               }
+
+               /*
+                * If the st_name field is zero, there is no string name for
+                * this symbol; skip to the next symbol.
+                */
+               if (curr_sym.st_name == 0) {
+                       continue;
+               }
+
+               /*
+                * Use the st_name field in the lttng_elf_sym struct to get offset of
+                * the symbol's name from the beginning of the string table.
+                */
+               curr_sym_str = string_table_data + curr_sym.st_name;
+
+               /*
+                * If the current symbol is not a function; skip to the next symbol.
+                */
+               /* Both 32bit and 64bit use the same 1 byte field for type. (See elf.h) */
+               if (ELF32_ST_TYPE(curr_sym.st_info) != STT_FUNC) {
+                       continue;
+               }
+
+               /*
+                * Compare with the search symbol. If there is a match set the address
+                * output parameter and return success.
+                */
+               if (strcmp(symbol, curr_sym_str) == 0 ) {
+                       sym_found = 1;
+                       addr = curr_sym.st_value;
+                       break;
+               }
+       }
+
+       if (!sym_found) {
+               DBG("Symbol not found.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto free_string_table_data;
+       }
+
+       /*
+        * Use the virtual address of the symbol to compute the offset of this
+        * symbol from the beginning of the executable file.
+        */
+       ret = lttng_elf_convert_addr_in_text_to_offset(elf, addr, offset);
+       if (ret) {
+               DBG("Cannot convert addr to offset.");
+               goto free_string_table_data;
+       }
+
+
+free_string_table_data:
+       free(string_table_data);
+free_symbol_table_data:
+       free(symbol_table_data);
+destroy_elf:
+       lttng_elf_destroy(elf);
+end:
+       return ret;
+}
+
+/*
+ * Compute the offsets of SDT probes from the begining of the ELF binary.
+ *
+ * On success, returns 0 and the nb_probes parameter is set to the number of
+ * offsets found and the offsets parameter points to an array of offsets where
+ * the SDT probes are.
+ * On failure, returns -1.
+ */
+int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name,
+               const char *probe_name, uint64_t **offsets, uint32_t *nb_probes)
+{
+       int ret = 0, nb_match = 0;
+       struct lttng_elf_shdr stap_note_section_hdr;
+       struct lttng_elf *elf = NULL;
+       char *stap_note_section_data = NULL;
+       char *curr_note_section_begin, *curr_data_ptr, *curr_probe, *curr_provider;
+       char *next_note_ptr;
+       uint32_t name_size, desc_size, note_type;
+       uint64_t curr_probe_location, curr_probe_offset, curr_semaphore_location;
+       uint64_t *probe_locs = NULL, *new_probe_locs = NULL;
+
+       if (!provider_name || !probe_name || !nb_probes || !offsets) {
+               DBG("Invalid arguments.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto error;
+       }
+
+       elf = lttng_elf_create(fd);
+       if (!elf) {
+               DBG("Error allocation ELF.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto error;
+       }
+
+       /* Get the stap note section header. */
+       ret = lttng_elf_get_section_hdr_by_name(elf, NOTE_STAPSDT_SECTION_NAME,
+                       &stap_note_section_hdr);
+       if (ret) {
+               DBG("Cannot get ELF stap note section.");
+               goto destroy_elf_error;
+       }
+
+       /* Get the data associated with the stap note section. */
+       stap_note_section_data =
+                       lttng_elf_get_section_data(elf, &stap_note_section_hdr);
+       if (stap_note_section_data == NULL) {
+               DBG("Cannot get ELF stap note section data.");
+               ret = LTTNG_ERR_ELF_PARSING;
+               goto destroy_elf_error;
+       }
+
+       next_note_ptr = stap_note_section_data;
+       curr_note_section_begin = stap_note_section_data;
+
+       *offsets = NULL;
+       while (1) {
+               curr_data_ptr = next_note_ptr;
+               /* Check if we have reached the end of the note section. */
+               if (curr_data_ptr >=
+                               curr_note_section_begin +
+                                               stap_note_section_hdr.sh_size) {
+                       *nb_probes = nb_match;
+                       *offsets = probe_locs;
+                       ret = 0;
+                       break;
+               }
+               /* Get name size field. */
+               name_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
+               curr_data_ptr += sizeof(uint32_t);
+
+               /* Sanity check; a zero name_size is reserved. */
+               if (name_size == 0) {
+                       DBG("Invalid name size field in SDT probe descriptions"
+                               "section.");
+                       ret = -1;
+                       goto realloc_error;
+               }
+
+               /* Get description size field. */
+               desc_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
+               curr_data_ptr += sizeof(uint32_t);
+
+               /* Get type field. */
+               note_type = *(uint32_t *) curr_data_ptr;
+               curr_data_ptr += sizeof(uint32_t);
+
+               /*
+                * Move the pointer to the next note to be ready for the next
+                * iteration. The current note is made of 3 unsigned 32bit
+                * integers (name size, descriptor size and note type), the
+                * name and the descriptor. To move to the next note, we move
+                * the pointer according to those values.
+                */
+               next_note_ptr = next_note_ptr +
+                       (3 * sizeof(uint32_t)) + desc_size + name_size;
+
+               /*
+                * Move ptr to the end of the name string (we don't need it)
+                * and go to the next 4 byte alignement.
+                */
+               if (note_type != NOTE_STAPSDT_TYPE ||
+                       strncmp(curr_data_ptr, NOTE_STAPSDT_NAME, name_size) != 0) {
+                       continue;
+               }
+
+               curr_data_ptr += name_size;
+
+               /* Get probe location.  */
+               curr_probe_location = *(uint64_t *) curr_data_ptr;
+               curr_data_ptr += sizeof(uint64_t);
+
+               /* Pass over the base. Not needed. */
+               curr_data_ptr += sizeof(uint64_t);
+
+               /* Get semaphore location. */
+               curr_semaphore_location = *(uint64_t *) curr_data_ptr;
+               curr_data_ptr += sizeof(uint64_t);
+               /* Get provider name. */
+               curr_provider = curr_data_ptr;
+               curr_data_ptr += strlen(curr_provider) + 1;
+
+               /* Get probe name. */
+               curr_probe = curr_data_ptr;
+
+               /* Check if the provider and probe name match */
+               if (strcmp(provider_name, curr_provider) == 0 &&
+                               strcmp(probe_name, curr_probe) == 0) {
+                       int new_size;
+
+                       /*
+                        * We currently don't support SDT probes with semaphores. Return
+                        * success as we found a matching probe but it's guarded by a
+                        * semaphore.
+                        */
+                       if (curr_semaphore_location != 0) {
+                               ret = LTTNG_ERR_SDT_PROBE_SEMAPHORE;
+                               goto realloc_error;
+                       }
+
+                       new_size = (++nb_match) * sizeof(uint64_t);
+
+                       /*
+                        * Found a match with not semaphore, we need to copy the
+                        * probe_location to the output parameter.
+                        */
+                       new_probe_locs = (uint64_t *) realloc(probe_locs, new_size);
+                       if (!new_probe_locs) {
+                               /* Error allocating a larger buffer */
+                               DBG("Allocation error in SDT.");
+                               ret = LTTNG_ERR_NOMEM;
+                               goto realloc_error;
+                       }
+                       probe_locs = new_probe_locs;
+                       new_probe_locs = NULL;
+
+                       /*
+                        * Use the virtual address of the probe to compute the offset of
+                        * this probe from the beginning of the executable file.
+                        */
+                       ret = lttng_elf_convert_addr_in_text_to_offset(elf,
+                                       curr_probe_location, &curr_probe_offset);
+                       if (ret) {
+                               DBG("Conversion error in SDT.");
+                               goto realloc_error;
+                       }
+
+                       probe_locs[nb_match - 1] = curr_probe_offset;
+               }
+       }
+
+end:
+       free(stap_note_section_data);
+destroy_elf_error:
+       lttng_elf_destroy(elf);
+error:
+       return ret;
+realloc_error:
+       free(probe_locs);
+       goto end;
+}
index 524b8784605462a897a30fe75eba05f4799bc4a4..41dd496053bcbc0ac98853d952f536325c059e0d 100644 (file)
 
 #include <lttng/lttng-export.h>
 
-LTTNG_EXPORT int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset);
+extern "C" LTTNG_EXPORT
+int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset);
 
-LTTNG_EXPORT int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name,
+extern "C" LTTNG_EXPORT
+int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name,
                const char *probe_name, uint64_t **offsets, uint32_t *nb_probe);
 
 #endif /* _LTTNG_ELF_H */
diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c
deleted file mode 100644 (file)
index 1ff9f93..0000000
+++ /dev/null
@@ -1,2718 +0,0 @@
-/*
- * Copyright (C) 2014 Jonathan Rajotte <jonathan.r.julien@gmail.com>
- * Copyright (C) 2014 Olivier Cotte <olivier.cotte@polymtl.ca>
- * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include "lttng/tracker.h"
-#define _LGPL_SOURCE
-#include "mi-lttng.h"
-#include <common/config/session-config.h>
-#include <common/defaults.h>
-#include <common/tracker.h>
-#include <lttng/channel.h>
-#include <lttng/snapshot-internal.h>
-
-
-#define MI_SCHEMA_MAJOR_VERSION 4
-#define MI_SCHEMA_MINOR_VERSION 1
-
-/* Machine interface namespace URI */
-const char * const mi_lttng_xmlns = "xmlns";
-const char * const mi_lttng_xmlns_xsi = "xmlns:xsi";
-const char * const mi_lttng_w3_schema_uri = "http://www.w3.org/2001/XMLSchema-instance";
-const char * const mi_lttng_schema_location = "xsi:schemaLocation";
-const char * const mi_lttng_schema_location_uri =
-       DEFAULT_LTTNG_MI_NAMESPACE " "
-       "https://lttng.org/xml/schemas/lttng-mi/" XSTR(MI_SCHEMA_MAJOR_VERSION)
-       "/lttng-mi-" XSTR(MI_SCHEMA_MAJOR_VERSION) "."
-       XSTR(MI_SCHEMA_MINOR_VERSION) ".xsd";
-const char * const mi_lttng_schema_version = "schemaVersion";
-const char * const mi_lttng_schema_version_value = XSTR(MI_SCHEMA_MAJOR_VERSION)
-       "." XSTR(MI_SCHEMA_MINOR_VERSION);
-
-/* Strings related to command */
-const char * const mi_lttng_element_command = "command";
-const char * const mi_lttng_element_command_action = "snapshot_action";
-const char * const mi_lttng_element_command_add_context = "add-context";
-const char *const mi_lttng_element_command_add_trigger = "add-trigger";
-const char * const mi_lttng_element_command_create = "create";
-const char * const mi_lttng_element_command_destroy = "destroy";
-const char * const mi_lttng_element_command_disable_channel = "disable-channel";
-const char * const mi_lttng_element_command_disable_event = "disable-event";
-const char * const mi_lttng_element_command_enable_channels = "enable-channel";
-const char * const mi_lttng_element_command_enable_event = "enable-event";
-const char * const mi_lttng_element_command_list = "list";
-const char *const mi_lttng_element_command_list_trigger = "list-trigger";
-const char * const mi_lttng_element_command_load = "load";
-const char * const mi_lttng_element_command_metadata = "metadata";
-const char * const mi_lttng_element_command_metadata_action = "metadata_action";
-const char * const mi_lttng_element_command_regenerate = "regenerate";
-const char * const mi_lttng_element_command_regenerate_action = "regenerate_action";
-const char * const mi_lttng_element_command_name = "name";
-const char * const mi_lttng_element_command_output = "output";
-const char *const mi_lttng_element_command_remove_trigger = "remove-trigger";
-const char * const mi_lttng_element_command_save = "save";
-const char * const mi_lttng_element_command_set_session = "set-session";
-const char * const mi_lttng_element_command_snapshot = "snapshot";
-const char * const mi_lttng_element_command_snapshot_add = "add_snapshot";
-const char * const mi_lttng_element_command_snapshot_del = "del_snapshot";
-const char * const mi_lttng_element_command_snapshot_list = "list_snapshot";
-const char * const mi_lttng_element_command_snapshot_record = "record_snapshot";
-const char * const mi_lttng_element_command_start = "start";
-const char * const mi_lttng_element_command_stop = "stop";
-const char * const mi_lttng_element_command_success = "success";
-const char * const mi_lttng_element_command_track = "track";
-const char * const mi_lttng_element_command_untrack = "untrack";
-const char * const mi_lttng_element_command_version = "version";
-const char * const mi_lttng_element_command_rotate = "rotate";
-const char * const mi_lttng_element_command_enable_rotation = "enable-rotation";
-const char * const mi_lttng_element_command_disable_rotation = "disable-rotation";
-const char * const mi_lttng_element_command_clear = "clear";
-
-/* Strings related to version command */
-const char * const mi_lttng_element_version = "version";
-const char * const mi_lttng_element_version_commit = "commit";
-const char * const mi_lttng_element_version_description = "description";
-const char * const mi_lttng_element_version_license = "license";
-const char * const mi_lttng_element_version_major = "major";
-const char * const mi_lttng_element_version_minor = "minor";
-const char * const mi_lttng_element_version_patch_level = "patchLevel";
-const char * const mi_lttng_element_version_str = "string";
-const char * const mi_lttng_element_version_web = "url";
-
-/* String related to a lttng_event_field */
-const char * const mi_lttng_element_event_field = "event_field";
-const char * const mi_lttng_element_event_fields = "event_fields";
-
-/* String related to lttng_event_perf_counter_ctx */
-const char * const mi_lttng_element_perf_counter_context = "perf";
-
-/* Strings related to pid */
-const char * const mi_lttng_element_pid_id = "id";
-
-/* Strings related to save command */
-const char * const mi_lttng_element_save = "save";
-
-/* Strings related to load command */
-const char * const mi_lttng_element_load = "load";
-const char * const mi_lttng_element_load_overrides = "overrides";
-const char * const mi_lttng_element_load_override_url = "url";
-
-/* General elements of mi_lttng */
-const char * const mi_lttng_element_empty = "";
-const char * const mi_lttng_element_id = "id";
-const char * const mi_lttng_element_nowrite = "nowrite";
-const char * const mi_lttng_element_success = "success";
-const char * const mi_lttng_element_type_enum = "ENUM";
-const char * const mi_lttng_element_type_float = "FLOAT";
-const char * const mi_lttng_element_type_integer = "INTEGER";
-const char * const mi_lttng_element_type_other = "OTHER";
-const char * const mi_lttng_element_type_string = "STRING";
-
-/* String related to loglevel */
-const char * const mi_lttng_loglevel_str_alert = "TRACE_ALERT";
-const char * const mi_lttng_loglevel_str_crit = "TRACE_CRIT";
-const char * const mi_lttng_loglevel_str_debug = "TRACE_DEBUG";
-const char * const mi_lttng_loglevel_str_debug_function = "TRACE_DEBUG_FUNCTION";
-const char * const mi_lttng_loglevel_str_debug_line = "TRACE_DEBUG_LINE";
-const char * const mi_lttng_loglevel_str_debug_module = "TRACE_DEBUG_MODULE";
-const char * const mi_lttng_loglevel_str_debug_process = "TRACE_DEBUG_PROCESS";
-const char * const mi_lttng_loglevel_str_debug_program = "TRACE_DEBUG_PROGRAM";
-const char * const mi_lttng_loglevel_str_debug_system = "TRACE_DEBUG_SYSTEM";
-const char * const mi_lttng_loglevel_str_debug_unit = "TRACE_DEBUG_UNIT";
-const char * const mi_lttng_loglevel_str_emerg = "TRACE_EMERG";
-const char * const mi_lttng_loglevel_str_err = "TRACE_ERR";
-const char * const mi_lttng_loglevel_str_info = "TRACE_INFO";
-const char * const mi_lttng_loglevel_str_notice = "TRACE_NOTICE";
-const char * const mi_lttng_loglevel_str_unknown = "UNKNOWN";
-const char * const mi_lttng_loglevel_str_warning = "TRACE_WARNING";
-
-/* String related to loglevel JUL */
-const char * const mi_lttng_loglevel_str_jul_all = "JUL_ALL";
-const char * const mi_lttng_loglevel_str_jul_config = "JUL_CONFIG";
-const char * const mi_lttng_loglevel_str_jul_fine = "JUL_FINE";
-const char * const mi_lttng_loglevel_str_jul_finer = "JUL_FINER";
-const char * const mi_lttng_loglevel_str_jul_finest = "JUL_FINEST";
-const char * const mi_lttng_loglevel_str_jul_info = "JUL_INFO";
-const char * const mi_lttng_loglevel_str_jul_off = "JUL_OFF";
-const char * const mi_lttng_loglevel_str_jul_severe = "JUL_SEVERE";
-const char * const mi_lttng_loglevel_str_jul_warning = "JUL_WARNING";
-
-/* String related to loglevel LOG4J */
-const char * const mi_lttng_loglevel_str_log4j_off = "LOG4J_OFF";
-const char * const mi_lttng_loglevel_str_log4j_fatal = "LOG4J_FATAL";
-const char * const mi_lttng_loglevel_str_log4j_error = "LOG4J_ERROR";
-const char * const mi_lttng_loglevel_str_log4j_warn = "LOG4J_WARN";
-const char * const mi_lttng_loglevel_str_log4j_info = "LOG4J_INFO";
-const char * const mi_lttng_loglevel_str_log4j_debug = "LOG4J_DEBUG";
-const char * const mi_lttng_loglevel_str_log4j_trace = "LOG4J_TRACE";
-const char * const mi_lttng_loglevel_str_log4j_all = "LOG4J_ALL";
-
-/* String related to loglevel Python */
-const char * const mi_lttng_loglevel_str_python_critical = "PYTHON_CRITICAL";
-const char * const mi_lttng_loglevel_str_python_error = "PYTHON_ERROR";
-const char * const mi_lttng_loglevel_str_python_warning = "PYTHON_WARNING";
-const char * const mi_lttng_loglevel_str_python_info = "PYTHON_INFO";
-const char * const mi_lttng_loglevel_str_python_debug = "PYTHON_DEBUG";
-const char * const mi_lttng_loglevel_str_python_notset = "PYTHON_NOTSET";
-
-/* String related to loglevel type */
-const char * const mi_lttng_loglevel_type_all = "ALL";
-const char * const mi_lttng_loglevel_type_range = "RANGE";
-const char * const mi_lttng_loglevel_type_single = "SINGLE";
-const char * const mi_lttng_loglevel_type_unknown = "UNKNOWN";
-
-/* String related to a lttng_snapshot_output */
-const char * const mi_lttng_element_snapshot_ctrl_url = "ctrl_url";
-const char * const mi_lttng_element_snapshot_data_url = "data_url";
-const char * const mi_lttng_element_snapshot_max_size = "max_size";
-const char * const mi_lttng_element_snapshot_n_ptr = "n_ptr";
-const char * const mi_lttng_element_snapshot_session_name = "session_name";
-const char * const mi_lttng_element_snapshots = "snapshots";
-
-/* String related to track/untrack command */
-const char * const mi_lttng_element_track_untrack_all_wildcard = "*";
-
-const char * const mi_lttng_element_session_name = "session_name";
-
-/* String related to rotate command */
-const char * const mi_lttng_element_rotation = "rotation";
-const char * const mi_lttng_element_rotate_status = "status";
-const char * const mi_lttng_element_rotation_schedule = "rotation_schedule";
-const char * const mi_lttng_element_rotation_schedules = "rotation_schedules";
-const char * const mi_lttng_element_rotation_schedule_result = "rotation_schedule_result";
-const char * const mi_lttng_element_rotation_schedule_results = "rotation_schedule_results";
-const char * const mi_lttng_element_rotation_schedule_periodic = "periodic";
-const char * const mi_lttng_element_rotation_schedule_periodic_time_us = "time_us";
-const char * const mi_lttng_element_rotation_schedule_size_threshold = "size_threshold";
-const char * const mi_lttng_element_rotation_schedule_size_threshold_bytes = "bytes";
-const char * const mi_lttng_element_rotation_state = "state";
-const char * const mi_lttng_element_rotation_location = "location";
-const char * const mi_lttng_element_rotation_location_local = "local";
-const char * const mi_lttng_element_rotation_location_local_absolute_path = "absolute_path";
-const char * const mi_lttng_element_rotation_location_relay = "relay";
-const char * const mi_lttng_element_rotation_location_relay_host = "host";
-const char * const mi_lttng_element_rotation_location_relay_control_port = "control_port";
-const char * const mi_lttng_element_rotation_location_relay_data_port = "data_port";
-const char * const mi_lttng_element_rotation_location_relay_protocol = "protocol";
-const char * const mi_lttng_element_rotation_location_relay_relative_path = "relative_path";
-
-/* String related to enum lttng_rotation_state */
-const char * const mi_lttng_rotation_state_str_ongoing = "ONGOING";
-const char * const mi_lttng_rotation_state_str_completed = "COMPLETED";
-const char * const mi_lttng_rotation_state_str_expired = "EXPIRED";
-const char * const mi_lttng_rotation_state_str_error = "ERROR";
-
-/* String related to enum lttng_trace_archive_location_relay_protocol_type */
-const char * const mi_lttng_rotation_location_relay_protocol_str_tcp = "TCP";
-
-/* String related to rate_policy elements */
-const char *const mi_lttng_element_rate_policy = "rate_policy";
-const char *const mi_lttng_element_rate_policy_every_n =
-               "rate_policy_every_n";
-const char *const mi_lttng_element_rate_policy_once_after_n =
-               "rate_policy_once_after_n";
-
-const char *const mi_lttng_element_rate_policy_every_n_interval =
-               "interval";
-const char
-               *const mi_lttng_element_rate_policy_once_after_n_threshold =
-                               "threshold";
-
-/* String related to action elements */
-const char *const mi_lttng_element_action = "action";
-const char *const mi_lttng_element_action_list = "action_list";
-const char *const mi_lttng_element_action_notify = "action_notify";
-const char *const mi_lttng_element_action_start_session =
-               "action_start_session";
-const char *const mi_lttng_element_action_stop_session =
-               "action_stop_session";
-const char *const mi_lttng_element_action_rotate_session =
-               "action_rotate_session";
-const char *const mi_lttng_element_action_snapshot_session =
-               "action_snapshot_session";
-const char *const mi_lttng_element_action_snapshot_session_output =
-               "output";
-
-/* String related to condition */
-const char *const mi_lttng_element_condition = "condition";
-const char *const mi_lttng_element_condition_buffer_usage_high =
-               "condition_buffer_usage_high";
-const char *const mi_lttng_element_condition_buffer_usage_low =
-               "condition_buffer_usage_low";
-const char *const mi_lttng_element_condition_event_rule_matches =
-               "condition_event_rule_matches";
-const char *const mi_lttng_element_condition_session_consumed_size =
-               "condition_session_consumed_size";
-const char *const mi_lttng_element_condition_session_rotation =
-               "condition_session_rotation";
-const char
-               *const mi_lttng_element_condition_session_rotation_completed =
-                               "condition_session_rotation_completed";
-const char
-               *const mi_lttng_element_condition_session_rotation_ongoing =
-                               "condition_session_rotation_ongoing";
-
-const char *const mi_lttng_element_condition_channel_name =
-               "channel_name";
-const char *const mi_lttng_element_condition_threshold_bytes =
-               "threshold_bytes";
-const char *const mi_lttng_element_condition_threshold_ratio =
-               "threshold_ratio";
-
-/* String related to capture descriptor */
-const char *const mi_lttng_element_capture_descriptor =
-               "capture_descriptor";
-const char *const mi_lttng_element_capture_descriptors =
-               "capture_descriptors";
-
-/* String related to event expression */
-const char *const mi_lttng_element_event_expr = "event_expr";
-const char *const mi_lttng_element_event_expr_payload_field =
-               "event_expr_payload_field";
-const char *const mi_lttng_element_event_expr_channel_context_field =
-               "event_expr_channel_context_field";
-const char
-               *const mi_lttng_element_event_expr_app_specific_context_field =
-                               "event_expr_app_specific_context_field";
-const char *const mi_lttng_element_event_expr_array_field_element =
-               "event_expr_array_field_element";
-const char *const mi_lttng_element_event_expr_provider_name =
-               "provider_name";
-const char *const mi_lttng_element_event_expr_type_name =
-               "type_name";
-const char *const mi_lttng_element_event_expr_index = "index";
-
-/* String related to event rule */
-const char *const mi_lttng_element_event_rule = "event_rule";
-
-/* String related to lttng_event_rule_type */
-const char *const mi_lttng_element_event_rule_event_name =
-               "event_name";
-const char *const mi_lttng_element_event_rule_name_pattern =
-               "name_pattern";
-const char *const mi_lttng_element_event_rule_filter_expression =
-               "filter_expression";
-
-const char *const mi_lttng_element_event_rule_jul_logging =
-               "event_rule_jul_logging";
-const char *const mi_lttng_element_event_rule_kernel_kprobe =
-               "event_rule_kernel_kprobe";
-const char *const mi_lttng_element_event_rule_kernel_syscall =
-               "event_rule_kernel_syscall";
-const char *const mi_lttng_element_event_rule_kernel_tracepoint =
-               "event_rule_kernel_tracepoint";
-const char *const mi_lttng_element_event_rule_kernel_uprobe =
-               "event_rule_kernel_uprobe";
-const char *const mi_lttng_element_event_rule_log4j_logging =
-               "event_rule_log4j_logging";
-const char *const mi_lttng_element_event_rule_python_logging =
-               "event_rule_python_logging";
-const char *const mi_lttng_element_event_rule_user_tracepoint =
-               "event_rule_user_tracepoint";
-
-/* String related to lttng_event_rule_kernel_syscall. */
-const char *const
-               mi_lttng_element_event_rule_kernel_syscall_emission_site =
-                               "emission_site";
-
-/* String related to enum lttng_event_rule_kernel_syscall_emission_site. */
-const char *const
-               mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit =
-                               "entry+exit";
-const char
-               *const mi_lttng_event_rule_kernel_syscall_emission_site_entry =
-                               "entry";
-const char *const
-               mi_lttng_event_rule_kernel_syscall_emission_site_exit = "exit";
-
-/* String related to lttng_event_rule_user_tracepoint */
-const char *const
-               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions =
-                               "name_pattern_exclusions";
-const char *const
-               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion =
-                               "name_pattern_exclusion";
-
-/* String related to log level rule. */
-const char *const mi_lttng_element_log_level_rule =
-               "log_level_rule";
-const char *const mi_lttng_element_log_level_rule_exactly =
-               "log_level_rule_exactly";
-const char
-               *const mi_lttng_element_log_level_rule_at_least_as_severe_as =
-                               "log_level_rule_at_least_as_severe_as";
-const char *const mi_lttng_element_log_level_rule_level = "level";
-
-/* String related to kernel probe location. */
-const char *const mi_lttng_element_kernel_probe_location =
-               "kernel_probe_location";
-const char
-               *const mi_lttng_element_kernel_probe_location_symbol_offset =
-                               "kernel_probe_location_symbol_offset";
-const char *const
-               mi_lttng_element_kernel_probe_location_symbol_offset_name =
-                               "name";
-const char *const
-               mi_lttng_element_kernel_probe_location_symbol_offset_offset =
-                               "offset";
-
-const char *const mi_lttng_element_kernel_probe_location_address =
-               "kernel_probe_location_address";
-const char
-               *const mi_lttng_element_kernel_probe_location_address_address =
-                               "address";
-
-/* String related to userspace probe location. */
-const char *const mi_lttng_element_userspace_probe_location =
-               "userspace_probe_location";
-const char
-               *const mi_lttng_element_userspace_probe_location_binary_path =
-                               "binary_path";
-const char
-               *const mi_lttng_element_userspace_probe_location_function =
-                               "userspace_probe_location_function";
-const char
-               *const mi_lttng_element_userspace_probe_location_function_name =
-                               "name";
-const char
-               *const mi_lttng_element_userspace_probe_location_lookup_method =
-                               "userspace_probe_location_lookup_method";
-const char *const
-               mi_lttng_element_userspace_probe_location_lookup_method_function_default =
-                               "userspace_probe_location_lookup_method_function_default";
-const char *const
-               mi_lttng_element_userspace_probe_location_lookup_method_function_elf =
-                               "userspace_probe_location_lookup_method_function_elf";
-const char *const
-               mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt =
-                               "userspace_probe_location_lookup_method_tracepoint_sdt";
-const char
-               *const mi_lttng_element_userspace_probe_location_tracepoint =
-                               "userspace_probe_location_tracepoint";
-const char *const
-               mi_lttng_element_userspace_probe_location_tracepoint_probe_name =
-                               "probe_name";
-const char *const
-               mi_lttng_element_userspace_probe_location_tracepoint_provider_name =
-                               "provider_name";
-
-/* String related to enum
- * lttng_userspace_probe_location_function_instrumentation_type */
-const char *const
-               mi_lttng_element_userspace_probe_location_function_instrumentation_type =
-                               "instrumentation_type";
-const char *const
-               mi_lttng_userspace_probe_location_function_instrumentation_type_entry =
-                               "ENTRY";
-
-/* String related to trigger */
-const char *const mi_lttng_element_triggers = "triggers";
-const char *const mi_lttng_element_trigger = "trigger";
-const char *const mi_lttng_element_trigger_owner_uid = "owner_uid";
-
-/* String related to error_query. */
-const char *const mi_lttng_element_error_query_result =
-               "error_query_result";
-const char *const mi_lttng_element_error_query_result_counter =
-               "error_query_result_counter";
-const char *const
-               mi_lttng_element_error_query_result_counter_value = "value";
-const char *const mi_lttng_element_error_query_result_description =
-               "description";
-const char *const mi_lttng_element_error_query_result_name =
-               "name";
-const char *const mi_lttng_element_error_query_result_type =
-               "type";
-const char *const mi_lttng_element_error_query_results =
-               "error_query_results";
-
-/* String related to add-context command */
-const char * const mi_lttng_element_context_symbol = "symbol";
-
-/* Deprecated symbols preserved for ABI compatibility. */
-LTTNG_EXPORT const char * const mi_lttng_context_type_perf_counter;
-LTTNG_EXPORT const char * const mi_lttng_context_type_perf_cpu_counter;
-LTTNG_EXPORT const char * const mi_lttng_context_type_perf_thread_counter;
-LTTNG_EXPORT const char * const mi_lttng_element_track_untrack_pid_target;
-LTTNG_EXPORT const char * const mi_lttng_element_track_untrack_targets;
-LTTNG_EXPORT const char * const mi_lttng_element_calibrate;
-LTTNG_EXPORT const char * const mi_lttng_element_calibrate_function;
-LTTNG_EXPORT const char * const mi_lttng_element_command_calibrate;
-
-/* This is a merge of jul loglevel and regular loglevel
- * Those should never overlap by definition
- * (see struct lttng_event loglevel)
- */
-const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain)
-{
-       switch (domain) {
-       case LTTNG_DOMAIN_KERNEL:
-       case LTTNG_DOMAIN_UST:
-               switch (value) {
-               case -1:
-                       return mi_lttng_element_empty;
-               case LTTNG_LOGLEVEL_EMERG:
-                       return mi_lttng_loglevel_str_emerg;
-               case LTTNG_LOGLEVEL_ALERT:
-                       return mi_lttng_loglevel_str_alert;
-               case LTTNG_LOGLEVEL_CRIT:
-                       return mi_lttng_loglevel_str_crit;
-               case LTTNG_LOGLEVEL_ERR:
-                       return mi_lttng_loglevel_str_err;
-               case LTTNG_LOGLEVEL_WARNING:
-                       return mi_lttng_loglevel_str_warning;
-               case LTTNG_LOGLEVEL_NOTICE:
-                       return mi_lttng_loglevel_str_notice;
-               case LTTNG_LOGLEVEL_INFO:
-                       return mi_lttng_loglevel_str_info;
-               case LTTNG_LOGLEVEL_DEBUG_SYSTEM:
-                       return mi_lttng_loglevel_str_debug_system;
-               case LTTNG_LOGLEVEL_DEBUG_PROGRAM:
-                       return mi_lttng_loglevel_str_debug_program;
-               case LTTNG_LOGLEVEL_DEBUG_PROCESS:
-                       return mi_lttng_loglevel_str_debug_process;
-               case LTTNG_LOGLEVEL_DEBUG_MODULE:
-                       return mi_lttng_loglevel_str_debug_module;
-               case LTTNG_LOGLEVEL_DEBUG_UNIT:
-                       return mi_lttng_loglevel_str_debug_unit;
-               case LTTNG_LOGLEVEL_DEBUG_FUNCTION:
-                       return mi_lttng_loglevel_str_debug_function;
-               case LTTNG_LOGLEVEL_DEBUG_LINE:
-                       return mi_lttng_loglevel_str_debug_line;
-               case LTTNG_LOGLEVEL_DEBUG:
-                       return mi_lttng_loglevel_str_debug;
-               default:
-                       return mi_lttng_loglevel_str_unknown;
-               }
-               break;
-       case LTTNG_DOMAIN_LOG4J:
-               switch (value) {
-               case -1:
-                       return mi_lttng_element_empty;
-               case LTTNG_LOGLEVEL_LOG4J_OFF:
-                       return mi_lttng_loglevel_str_log4j_off;
-               case LTTNG_LOGLEVEL_LOG4J_FATAL:
-                       return mi_lttng_loglevel_str_log4j_fatal;
-               case LTTNG_LOGLEVEL_LOG4J_ERROR:
-                       return mi_lttng_loglevel_str_log4j_error;
-               case LTTNG_LOGLEVEL_LOG4J_WARN:
-                       return mi_lttng_loglevel_str_log4j_warn;
-               case LTTNG_LOGLEVEL_LOG4J_INFO:
-                       return mi_lttng_loglevel_str_log4j_info;
-               case LTTNG_LOGLEVEL_LOG4J_DEBUG:
-                       return mi_lttng_loglevel_str_log4j_debug;
-               case LTTNG_LOGLEVEL_LOG4J_TRACE:
-                       return mi_lttng_loglevel_str_log4j_trace;
-               case LTTNG_LOGLEVEL_LOG4J_ALL:
-                       return mi_lttng_loglevel_str_log4j_all;
-               default:
-                       return mi_lttng_loglevel_str_unknown;
-               }
-               break;
-       case LTTNG_DOMAIN_JUL:
-               switch (value) {
-               case -1:
-                       return mi_lttng_element_empty;
-               case LTTNG_LOGLEVEL_JUL_OFF:
-                       return mi_lttng_loglevel_str_jul_off;
-               case LTTNG_LOGLEVEL_JUL_SEVERE:
-                       return mi_lttng_loglevel_str_jul_severe;
-               case LTTNG_LOGLEVEL_JUL_WARNING:
-                       return mi_lttng_loglevel_str_jul_warning;
-               case LTTNG_LOGLEVEL_JUL_INFO:
-                       return mi_lttng_loglevel_str_jul_info;
-               case LTTNG_LOGLEVEL_JUL_CONFIG:
-                       return mi_lttng_loglevel_str_jul_config;
-               case LTTNG_LOGLEVEL_JUL_FINE:
-                       return mi_lttng_loglevel_str_jul_fine;
-               case LTTNG_LOGLEVEL_JUL_FINER:
-                       return mi_lttng_loglevel_str_jul_finer;
-               case LTTNG_LOGLEVEL_JUL_FINEST:
-                       return mi_lttng_loglevel_str_jul_finest;
-               case LTTNG_LOGLEVEL_JUL_ALL:
-                       return mi_lttng_loglevel_str_jul_all;
-               default:
-                       return mi_lttng_loglevel_str_unknown;
-               }
-               break;
-       case LTTNG_DOMAIN_PYTHON:
-               switch (value) {
-               case LTTNG_LOGLEVEL_PYTHON_CRITICAL:
-                       return mi_lttng_loglevel_str_python_critical;
-               case LTTNG_LOGLEVEL_PYTHON_ERROR:
-                       return mi_lttng_loglevel_str_python_error;
-               case LTTNG_LOGLEVEL_PYTHON_WARNING:
-                       return mi_lttng_loglevel_str_python_warning;
-               case LTTNG_LOGLEVEL_PYTHON_INFO:
-                       return mi_lttng_loglevel_str_python_info;
-               case LTTNG_LOGLEVEL_PYTHON_DEBUG:
-                       return mi_lttng_loglevel_str_python_debug;
-               case LTTNG_LOGLEVEL_PYTHON_NOTSET:
-                       return mi_lttng_loglevel_str_python_notset;
-               default:
-                       return mi_lttng_loglevel_str_unknown;
-               }
-               break;
-       default:
-               return mi_lttng_loglevel_str_unknown;
-       }
-}
-
-const char *mi_lttng_logleveltype_string(enum lttng_loglevel_type value)
-{
-       switch (value) {
-       case LTTNG_EVENT_LOGLEVEL_ALL:
-               return mi_lttng_loglevel_type_all;
-       case LTTNG_EVENT_LOGLEVEL_RANGE:
-               return mi_lttng_loglevel_type_range;
-       case LTTNG_EVENT_LOGLEVEL_SINGLE:
-               return mi_lttng_loglevel_type_single;
-       default:
-               return mi_lttng_loglevel_type_unknown;
-       }
-}
-
-static
-const char *mi_lttng_eventtype_string(enum lttng_event_type value)
-{
-       switch (value) {
-       case LTTNG_EVENT_ALL:
-               return config_event_type_all;
-       case LTTNG_EVENT_TRACEPOINT:
-               return config_event_type_tracepoint;
-       case LTTNG_EVENT_PROBE:
-               return config_event_type_probe;
-       case LTTNG_EVENT_USERSPACE_PROBE:
-               return config_event_type_userspace_probe;
-       case LTTNG_EVENT_FUNCTION:
-               return config_event_type_function;
-       case LTTNG_EVENT_FUNCTION_ENTRY:
-               return config_event_type_function_entry;
-       case LTTNG_EVENT_SYSCALL:
-               return config_event_type_syscall;
-       case LTTNG_EVENT_NOOP:
-               return config_event_type_noop;
-       default:
-               return mi_lttng_element_empty;
-       }
-}
-
-static
-const char *mi_lttng_event_contexttype_string(enum lttng_event_context_type val)
-{
-       switch (val) {
-       case LTTNG_EVENT_CONTEXT_PID:
-               return config_event_context_pid;
-       case LTTNG_EVENT_CONTEXT_PROCNAME:
-               return config_event_context_procname;
-       case LTTNG_EVENT_CONTEXT_PRIO:
-               return config_event_context_prio;
-       case LTTNG_EVENT_CONTEXT_NICE:
-               return config_event_context_nice;
-       case LTTNG_EVENT_CONTEXT_VPID:
-               return config_event_context_vpid;
-       case LTTNG_EVENT_CONTEXT_TID:
-               return config_event_context_tid;
-       case LTTNG_EVENT_CONTEXT_VTID:
-               return config_event_context_vtid;
-       case LTTNG_EVENT_CONTEXT_PPID:
-               return config_event_context_ppid;
-       case LTTNG_EVENT_CONTEXT_VPPID:
-               return config_event_context_vppid;
-       case LTTNG_EVENT_CONTEXT_PTHREAD_ID:
-               return config_event_context_pthread_id;
-       case LTTNG_EVENT_CONTEXT_HOSTNAME:
-               return config_event_context_hostname;
-       case LTTNG_EVENT_CONTEXT_IP:
-               return config_event_context_ip;
-       case LTTNG_EVENT_CONTEXT_INTERRUPTIBLE:
-               return config_event_context_interruptible;
-       case LTTNG_EVENT_CONTEXT_PREEMPTIBLE:
-               return config_event_context_preemptible;
-       case LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE:
-               return config_event_context_need_reschedule;
-       case LTTNG_EVENT_CONTEXT_MIGRATABLE:
-               return config_event_context_migratable;
-       case LTTNG_EVENT_CONTEXT_CALLSTACK_USER:
-               return config_event_context_callstack_user;
-       case LTTNG_EVENT_CONTEXT_CALLSTACK_KERNEL:
-               return config_event_context_callstack_kernel;
-       case LTTNG_EVENT_CONTEXT_CGROUP_NS:
-               return config_event_context_cgroup_ns;
-       case LTTNG_EVENT_CONTEXT_IPC_NS:
-               return config_event_context_ipc_ns;
-       case LTTNG_EVENT_CONTEXT_MNT_NS:
-               return config_event_context_mnt_ns;
-       case LTTNG_EVENT_CONTEXT_NET_NS:
-               return config_event_context_net_ns;
-       case LTTNG_EVENT_CONTEXT_PID_NS:
-               return config_event_context_pid_ns;
-       case LTTNG_EVENT_CONTEXT_TIME_NS:
-               return config_event_context_time_ns;
-       case LTTNG_EVENT_CONTEXT_USER_NS:
-               return config_event_context_user_ns;
-       case LTTNG_EVENT_CONTEXT_UTS_NS:
-               return config_event_context_uts_ns;
-       case LTTNG_EVENT_CONTEXT_UID:
-               return config_event_context_uid;
-       case LTTNG_EVENT_CONTEXT_EUID:
-               return config_event_context_euid;
-       case LTTNG_EVENT_CONTEXT_SUID:
-               return config_event_context_suid;
-       case LTTNG_EVENT_CONTEXT_GID:
-               return config_event_context_gid;
-       case LTTNG_EVENT_CONTEXT_EGID:
-               return config_event_context_egid;
-       case LTTNG_EVENT_CONTEXT_SGID:
-               return config_event_context_sgid;
-       case LTTNG_EVENT_CONTEXT_VUID:
-               return config_event_context_vuid;
-       case LTTNG_EVENT_CONTEXT_VEUID:
-               return config_event_context_veuid;
-       case LTTNG_EVENT_CONTEXT_VSUID:
-               return config_event_context_vsuid;
-       case LTTNG_EVENT_CONTEXT_VGID:
-               return config_event_context_vgid;
-       case LTTNG_EVENT_CONTEXT_VEGID:
-               return config_event_context_vegid;
-       case LTTNG_EVENT_CONTEXT_VSGID:
-               return config_event_context_vsgid;
-       default:
-               return NULL;
-       }
-}
-
-const char *mi_lttng_eventfieldtype_string(enum lttng_event_field_type val)
-{
-       switch (val) {
-       case(LTTNG_EVENT_FIELD_INTEGER):
-               return mi_lttng_element_type_integer;
-       case(LTTNG_EVENT_FIELD_ENUM):
-               return mi_lttng_element_type_enum;
-       case(LTTNG_EVENT_FIELD_FLOAT):
-               return mi_lttng_element_type_float;
-       case(LTTNG_EVENT_FIELD_STRING):
-               return mi_lttng_element_type_string;
-       default:
-               return mi_lttng_element_type_other;
-       }
-}
-
-const char *mi_lttng_domaintype_string(enum lttng_domain_type value)
-{
-       switch (value) {
-       case LTTNG_DOMAIN_KERNEL:
-               return config_domain_type_kernel;
-       case LTTNG_DOMAIN_UST:
-               return config_domain_type_ust;
-       case LTTNG_DOMAIN_JUL:
-               return config_domain_type_jul;
-       case LTTNG_DOMAIN_LOG4J:
-               return config_domain_type_log4j;
-       case LTTNG_DOMAIN_PYTHON:
-               return config_domain_type_python;
-       default:
-               /* Should not have an unknown domain */
-               abort();
-               return NULL;
-       }
-}
-
-const char *mi_lttng_buffertype_string(enum lttng_buffer_type value)
-{
-       switch (value) {
-       case LTTNG_BUFFER_PER_PID:
-               return config_buffer_type_per_pid;
-       case LTTNG_BUFFER_PER_UID:
-               return config_buffer_type_per_uid;
-       case LTTNG_BUFFER_GLOBAL:
-               return config_buffer_type_global;
-       default:
-               /* Should not have an unknow buffer type */
-               abort();
-               return NULL;
-       }
-}
-
-const char *mi_lttng_rotation_state_string(enum lttng_rotation_state value)
-{
-       switch (value) {
-       case LTTNG_ROTATION_STATE_ONGOING:
-               return mi_lttng_rotation_state_str_ongoing;
-       case LTTNG_ROTATION_STATE_COMPLETED:
-               return mi_lttng_rotation_state_str_completed;
-       case LTTNG_ROTATION_STATE_EXPIRED:
-               return mi_lttng_rotation_state_str_expired;
-       case LTTNG_ROTATION_STATE_ERROR:
-               return mi_lttng_rotation_state_str_error;
-       default:
-               /* Should not have an unknow rotation state. */
-               abort();
-               return NULL;
-       }
-}
-
-const char *mi_lttng_trace_archive_location_relay_protocol_type_string(
-               enum lttng_trace_archive_location_relay_protocol_type value)
-{
-       switch (value) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP:
-               return mi_lttng_rotation_location_relay_protocol_str_tcp;
-       default:
-               /* Should not have an unknown relay protocol. */
-               abort();
-               return NULL;
-       }
-}
-
-struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type)
-{
-       struct mi_writer *mi_writer;
-
-       mi_writer = zmalloc(sizeof(struct mi_writer));
-       if (!mi_writer) {
-               PERROR("zmalloc mi_writer_create");
-               goto end;
-       }
-       if (mi_output_type == LTTNG_MI_XML) {
-               mi_writer->writer = config_writer_create(fd_output, 0);
-               if (!mi_writer->writer) {
-                       goto err_destroy;
-               }
-               mi_writer->type = LTTNG_MI_XML;
-       } else {
-               goto err_destroy;
-       }
-
-end:
-       return mi_writer;
-
-err_destroy:
-       free(mi_writer);
-       return NULL;
-}
-
-int mi_lttng_writer_destroy(struct mi_writer *writer)
-{
-       int ret;
-
-       if (!writer) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       ret = config_writer_destroy(writer->writer);
-       if (ret < 0) {
-               goto end;
-       }
-
-       free(writer);
-end:
-       return ret;
-}
-
-int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command)
-{
-       int ret;
-
-       /*
-        * A command is always the MI's root node, it must declare the current
-        * namespace and schema URIs and the schema's version.
-        */
-       ret = config_writer_open_element(writer->writer,
-                       mi_lttng_element_command);
-       if (ret) {
-               goto end;
-       }
-
-       ret = config_writer_write_attribute(writer->writer,
-                       mi_lttng_xmlns, DEFAULT_LTTNG_MI_NAMESPACE);
-       if (ret) {
-               goto end;
-       }
-
-       ret = config_writer_write_attribute(writer->writer,
-                       mi_lttng_xmlns_xsi, mi_lttng_w3_schema_uri);
-       if (ret) {
-               goto end;
-       }
-
-       ret = config_writer_write_attribute(writer->writer,
-                       mi_lttng_schema_location,
-                       mi_lttng_schema_location_uri);
-       if (ret) {
-               goto end;
-       }
-
-       ret = config_writer_write_attribute(writer->writer,
-                       mi_lttng_schema_version,
-                       mi_lttng_schema_version_value);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_command_name, command);
-end:
-       return ret;
-}
-
-int mi_lttng_writer_command_close(struct mi_writer *writer)
-{
-       return mi_lttng_writer_close_element(writer);
-}
-
-int mi_lttng_writer_open_element(struct mi_writer *writer,
-               const char *element_name)
-{
-       return config_writer_open_element(writer->writer, element_name);
-}
-
-int mi_lttng_writer_close_element(struct mi_writer *writer)
-{
-       return config_writer_close_element(writer->writer);
-}
-
-int mi_lttng_close_multi_element(struct mi_writer *writer,
-               unsigned int nb_element)
-{
-       int ret, i;
-
-       if (nb_element < 1) {
-               ret = 0;
-               goto end;
-       }
-       for (i = 0; i < nb_element; i++) {
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer,
-               const char *element_name, uint64_t value)
-{
-       return config_writer_write_element_unsigned_int(writer->writer,
-                       element_name, value);
-}
-
-int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer,
-               const char *element_name, int64_t value)
-{
-       return config_writer_write_element_signed_int(writer->writer,
-                       element_name, value);
-}
-
-int mi_lttng_writer_write_element_bool(struct mi_writer *writer,
-               const char *element_name, int value)
-{
-       return config_writer_write_element_bool(writer->writer,
-                       element_name, value);
-}
-
-int mi_lttng_writer_write_element_string(struct mi_writer *writer,
-               const char *element_name, const char *value)
-{
-       return config_writer_write_element_string(writer->writer,
-                       element_name, value);
-}
-
-int mi_lttng_writer_write_element_double(struct mi_writer *writer,
-               const char *element_name,
-               double value)
-{
-       return config_writer_write_element_double(
-                       writer->writer, element_name, value);
-}
-
-int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version,
-       const char *lttng_description, const char *lttng_license)
-{
-       int ret;
-
-       /* Open version */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_version);
-       if (ret) {
-               goto end;
-       }
-
-       /* Version string (contain info like rc etc.) */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_version_str, version->version);
-       if (ret) {
-               goto end;
-       }
-
-       /* Major version number */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_version_major, version->version_major);
-       if (ret) {
-               goto end;
-       }
-
-       /* Minor version number */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_version_minor, version->version_minor);
-       if (ret) {
-               goto end;
-       }
-
-       /* Commit version number */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_version_commit, version->version_commit);
-       if (ret) {
-               goto end;
-       }
-
-       /* Patch number */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_version_patch_level, version->version_patchlevel);
-       if (ret) {
-               goto end;
-       }
-
-       /* Name of the version */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_name, version->version_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Description mostly related to beer... */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_version_description, lttng_description);
-       if (ret) {
-               goto end;
-       }
-
-       /* url */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_version_web, version->package_url);
-       if (ret) {
-               goto end;
-       }
-
-       /* License: free as in free beer...no...*speech* */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_version_license, lttng_license);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close version element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_sessions_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, config_element_sessions);
-}
-
-int mi_lttng_session(struct mi_writer *writer,
-               struct lttng_session *session, int is_open)
-{
-       int ret;
-
-       LTTNG_ASSERT(session);
-
-       /* Open sessions element */
-       ret = mi_lttng_writer_open_element(writer,
-                       config_element_session);
-       if (ret) {
-               goto end;
-       }
-
-       /* Name of the session */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_name, session->name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Path */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_path, session->path);
-       if (ret) {
-               goto end;
-       }
-
-       /* Enabled ? */
-       ret = mi_lttng_writer_write_element_bool(writer,
-                       config_element_enabled, session->enabled);
-       if (ret) {
-               goto end;
-       }
-
-       /* Snapshot mode */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       config_element_snapshot_mode, session->snapshot_mode);
-       if (ret) {
-               goto end;
-       }
-
-       /* Live timer interval in usec */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       config_element_live_timer_interval,
-                       session->live_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               /* Closing session element */
-               ret = mi_lttng_writer_close_element(writer);
-       }
-end:
-       return ret;
-
-}
-
-int mi_lttng_domains_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, config_element_domains);
-}
-
-int mi_lttng_domain(struct mi_writer *writer,
-               struct lttng_domain *domain, int is_open)
-{
-       int ret = 0;
-       const char *str_domain;
-       const char *str_buffer;
-
-       LTTNG_ASSERT(domain);
-
-       /* Open domain element */
-       ret = mi_lttng_writer_open_element(writer, config_element_domain);
-       if (ret) {
-               goto end;
-       }
-
-       /* Domain Type */
-       str_domain = mi_lttng_domaintype_string(domain->type);
-       ret = mi_lttng_writer_write_element_string(writer, config_element_type,
-                       str_domain);
-       if (ret) {
-               goto end;
-       }
-
-       /* Buffer Type */
-       str_buffer= mi_lttng_buffertype_string(domain->buf_type);
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_buffer_type, str_buffer);
-       if (ret) {
-               goto end;
-       }
-
-       /* TODO: union  attr
-        * This union is not currently used and was added for
-        * future ust domain support.
-        * Date: 25-06-2014
-        * */
-
-       if (!is_open) {
-               /* Closing domain element */
-               ret = mi_lttng_writer_close_element(writer);
-       }
-
-end:
-       return ret;
-
-}
-
-int mi_lttng_channels_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, config_element_channels);
-}
-
-int mi_lttng_channel(struct mi_writer *writer,
-               struct lttng_channel *channel, int is_open)
-{
-       int ret = 0;
-
-       LTTNG_ASSERT(channel);
-
-       /* Opening channel element */
-       ret = mi_lttng_writer_open_element(writer, config_element_channel);
-       if (ret) {
-               goto end;
-       }
-
-       /* Name */
-       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                       channel->name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Enabled ? */
-       ret = mi_lttng_writer_write_element_bool(writer,
-                       config_element_enabled, channel->enabled);
-       if (ret) {
-               goto end;
-       }
-
-       /* Attribute */
-       ret = mi_lttng_channel_attr(writer, &channel->attr);
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               /* Closing channel element */
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-int mi_lttng_channel_attr(struct mi_writer *writer,
-               struct lttng_channel_attr *attr)
-{
-       int ret = 0;
-       struct lttng_channel *chan = caa_container_of(attr,
-                       struct lttng_channel, attr);
-       uint64_t discarded_events, lost_packets, monitor_timer_interval;
-       int64_t blocking_timeout;
-
-       LTTNG_ASSERT(attr);
-
-       ret = lttng_channel_get_discarded_event_count(chan, &discarded_events);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_channel_get_lost_packet_count(chan, &lost_packets);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_channel_get_monitor_timer_interval(chan,
-                       &monitor_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_channel_get_blocking_timeout(chan,
-                       &blocking_timeout);
-       if (ret) {
-               goto end;
-       }
-
-       /* Opening Attributes */
-       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       /* Overwrite */
-       ret = mi_lttng_writer_write_element_string(writer,
-               config_element_overwrite_mode,
-               attr->overwrite ? config_overwrite_mode_overwrite :
-                       config_overwrite_mode_discard);
-       if (ret) {
-               goto end;
-       }
-
-       /* Sub buffer size in byte */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_subbuf_size, attr->subbuf_size);
-       if (ret) {
-               goto end;
-       }
-
-       /* Number of subbuffer (power of two) */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_num_subbuf,
-               attr->num_subbuf);
-       if (ret) {
-               goto end;
-       }
-
-       /* Switch timer interval in usec */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_switch_timer_interval,
-               attr->switch_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       /* Read timer interval in usec */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_read_timer_interval,
-               attr->read_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       /* Monitor timer interval in usec */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_monitor_timer_interval,
-               monitor_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       /* Retry timeout in usec */
-       ret = mi_lttng_writer_write_element_signed_int(writer,
-               config_element_blocking_timeout,
-               blocking_timeout);
-       if (ret) {
-               goto end;
-       }
-
-       /* Event output */
-       ret = mi_lttng_writer_write_element_string(writer,
-               config_element_output_type,
-               attr->output == LTTNG_EVENT_SPLICE ?
-               config_output_type_splice : config_output_type_mmap);
-       if (ret) {
-               goto end;
-       }
-
-       /* Tracefile size in bytes */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_tracefile_size, attr->tracefile_size);
-       if (ret) {
-               goto end;
-       }
-
-       /* Count of tracefiles */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_tracefile_count,
-               attr->tracefile_count);
-       if (ret) {
-               goto end;
-       }
-
-       /* Live timer interval in usec*/
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_live_timer_interval,
-               attr->live_timer_interval);
-       if (ret) {
-               goto end;
-       }
-
-       /* Discarded events */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_discarded_events,
-               discarded_events);
-       if (ret) {
-               goto end;
-       }
-
-       /* Lost packets */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-               config_element_lost_packets,
-               lost_packets);
-       if (ret) {
-               goto end;
-       }
-
-       /* Closing attributes */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-
-}
-
-int mi_lttng_event_common_attributes(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       int ret;
-       const char *filter_expression;
-
-       /* Open event element */
-       ret = mi_lttng_writer_open_element(writer, config_element_event);
-       if (ret) {
-               goto end;
-       }
-
-       /* Event name */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_name, event->name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Event type */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_type, mi_lttng_eventtype_string(event->type));
-       if (ret) {
-               goto end;
-       }
-
-       /* Is event enabled */
-       ret = mi_lttng_writer_write_element_bool(writer,
-                       config_element_enabled, event->enabled);
-       if (ret) {
-               goto end;
-       }
-
-       /* Event filter expression */
-       ret = lttng_event_get_filter_expression(event, &filter_expression);
-       if (ret) {
-               goto end;
-       }
-
-       if (filter_expression) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_filter_expression,
-                               filter_expression);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static int write_event_exclusions(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       int i;
-       int ret;
-       int exclusion_count;
-
-       /* Open event exclusions */
-       ret = mi_lttng_writer_open_element(writer, config_element_exclusions);
-       if (ret) {
-               goto end;
-       }
-
-       exclusion_count = lttng_event_get_exclusion_name_count(event);
-       if (exclusion_count < 0) {
-               ret = exclusion_count;
-               goto end;
-       }
-
-       for (i = 0; i < exclusion_count; i++) {
-               const char *name;
-
-               ret = lttng_event_get_exclusion_name(event, i, &name);
-               if (ret) {
-                       /* Close exclusions */
-                       mi_lttng_writer_close_element(writer);
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_exclusion, name);
-               if (ret) {
-                       /* Close exclusions */
-                       mi_lttng_writer_close_element(writer);
-                       goto end;
-               }
-       }
-
-       /* Close exclusions */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_event_tracepoint_loglevel(struct mi_writer *writer,
-               struct lttng_event *event, enum lttng_domain_type domain)
-{
-       int ret;
-
-       /* Event loglevel */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_loglevel,
-                       mi_lttng_loglevel_string(event->loglevel, domain));
-       if (ret) {
-               goto end;
-       }
-
-       /* Log level type */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_loglevel_type,
-                       mi_lttng_logleveltype_string(event->loglevel_type));
-       if (ret) {
-               goto end;
-       }
-
-       /* Event exclusions */
-       ret = write_event_exclusions(writer, event);
-
-end:
-       return ret;
-}
-
-int mi_lttng_event_tracepoint_no_loglevel(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       /* event exclusion filter */
-       return write_event_exclusions(writer, event);
-}
-
-int mi_lttng_event_function_probe(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       int ret;
-
-       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       if (event->attr.probe.addr != 0) {
-               /* event probe address */
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               config_element_address, event->attr.probe.addr);
-               if (ret) {
-                       goto end;
-               }
-       } else {
-               /* event probe offset */
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               config_element_offset, event->attr.probe.offset);
-               if (ret) {
-                       goto end;
-               }
-
-               /* event probe symbol_name */
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_symbol_name, event->attr.probe.symbol_name);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /* Close probe_attributes and attributes */
-       ret = mi_lttng_close_multi_element(writer, 2);
-end:
-       return ret;
-}
-
-static
-int mi_lttng_event_userspace_probe(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       int ret;
-       const struct lttng_userspace_probe_location *location;
-       const struct lttng_userspace_probe_location_lookup_method *lookup_method;
-       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
-
-       location = lttng_event_get_userspace_probe_location(event);
-       if (!location) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
-       if (!lookup_method) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method);
-
-       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       switch (lttng_userspace_probe_location_get_type(location)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-       {
-               const char *function_name;
-               const char *binary_path;
-
-               ret = mi_lttng_writer_open_element(writer,
-                               config_element_userspace_probe_function_attributes);
-               if (ret) {
-                       goto end;
-               }
-
-               switch (lookup_type) {
-               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-                       ret = mi_lttng_writer_write_element_string(writer,
-                                       config_element_userspace_probe_lookup,
-                                       config_element_userspace_probe_lookup_function_elf);
-                       if (ret) {
-                               goto end;
-                       }
-                       break;
-               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
-                       ret = mi_lttng_writer_write_element_string(writer,
-                                       config_element_userspace_probe_lookup,
-                                       config_element_userspace_probe_lookup_function_default);
-                       if (ret) {
-                               goto end;
-                       }
-                       break;
-               default:
-                       goto end;
-               }
-
-               binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_userspace_probe_location_binary_path, binary_path);
-               if (ret) {
-                       goto end;
-               }
-
-               function_name = lttng_userspace_probe_location_function_get_function_name(location);
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_userspace_probe_function_location_function_name,
-                               function_name);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-       {
-               const char *probe_name, *provider_name;
-               const char *binary_path;
-
-               ret = mi_lttng_writer_open_element(writer,
-                               config_element_userspace_probe_function_attributes);
-               if (ret) {
-                       goto end;
-               }
-
-               switch (lookup_type) {
-               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-                       ret = mi_lttng_writer_write_element_string(writer,
-                                       config_element_userspace_probe_lookup,
-                                       config_element_userspace_probe_lookup_tracepoint_sdt);
-                       if (ret) {
-                               goto end;
-                       }
-                       break;
-               default:
-                       goto end;
-               }
-
-               binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location);
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_userspace_probe_location_binary_path,
-                               binary_path);
-               if (ret) {
-                       goto end;
-               }
-
-               provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location);
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_userspace_probe_tracepoint_location_provider_name,
-                               provider_name);
-               if (ret) {
-                       goto end;
-               }
-
-               probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location);
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_userspace_probe_tracepoint_location_probe_name, probe_name);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       }
-       default:
-               ERR("Invalid probe type encountered");
-       }
-       /* Close probe_attributes and attributes */
-       ret = mi_lttng_close_multi_element(writer, 2);
-end:
-       return ret;
-}
-
-int mi_lttng_event_function_entry(struct mi_writer *writer,
-               struct lttng_event *event)
-{
-       int ret;
-
-       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes);
-       if (ret) {
-               goto end;
-       }
-
-       /* event probe symbol_name */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_symbol_name, event->attr.ftrace.symbol_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close function_attributes and attributes */
-       ret = mi_lttng_close_multi_element(writer, 2);
-end:
-       return ret;
-}
-
-int mi_lttng_events_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, config_element_events);
-}
-
-int mi_lttng_event(struct mi_writer *writer,
-               struct lttng_event *event, int is_open, enum lttng_domain_type domain)
-{
-       int ret;
-
-       ret = mi_lttng_event_common_attributes(writer, event);
-       if (ret) {
-               goto end;
-       }
-
-       switch (event->type) {
-       case LTTNG_EVENT_TRACEPOINT:
-       {
-               if (event->loglevel != -1) {
-                       ret = mi_lttng_event_tracepoint_loglevel(writer, event, domain);
-               } else {
-                       ret = mi_lttng_event_tracepoint_no_loglevel(writer, event);
-               }
-               break;
-       }
-       case LTTNG_EVENT_FUNCTION:
-               /* Fallthrough */
-       case LTTNG_EVENT_PROBE:
-               ret = mi_lttng_event_function_probe(writer, event);
-               break;
-       case LTTNG_EVENT_FUNCTION_ENTRY:
-               ret = mi_lttng_event_function_entry(writer, event);
-               break;
-       case LTTNG_EVENT_USERSPACE_PROBE:
-               ret = mi_lttng_event_userspace_probe(writer, event);
-               break;
-       case LTTNG_EVENT_ALL:
-               /* Fallthrough */
-       default:
-               break;
-       }
-
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               ret = mi_lttng_writer_close_element(writer);
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_trackers_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(
-                       writer, config_element_process_attr_trackers);
-}
-
-static int get_tracker_elements(enum lttng_process_attr process_attr,
-               const char **element_process_attr_tracker,
-               const char **element_process_attr_value)
-{
-       int ret = 0;
-
-       switch (process_attr) {
-       case LTTNG_PROCESS_ATTR_PROCESS_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_pid;
-               *element_process_attr_value =
-                               config_element_process_attr_pid_value;
-               break;
-       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_vpid;
-               *element_process_attr_value =
-                               config_element_process_attr_vpid_value;
-               break;
-       case LTTNG_PROCESS_ATTR_USER_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_uid;
-               *element_process_attr_value =
-                               config_element_process_attr_uid_value;
-               break;
-       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_vuid;
-               *element_process_attr_value =
-                               config_element_process_attr_vuid_value;
-               break;
-       case LTTNG_PROCESS_ATTR_GROUP_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_gid;
-               *element_process_attr_value =
-                               config_element_process_attr_gid_value;
-               break;
-       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
-               *element_process_attr_tracker =
-                               config_element_process_attr_tracker_vgid;
-               *element_process_attr_value =
-                               config_element_process_attr_vgid_value;
-               break;
-       default:
-               ret = LTTNG_ERR_SAVE_IO_FAIL;
-       }
-       return ret;
-}
-
-int mi_lttng_process_attribute_tracker_open(
-               struct mi_writer *writer, enum lttng_process_attr process_attr)
-{
-       int ret;
-       const char *element_tracker, *element_value;
-
-       ret = get_tracker_elements(
-                       process_attr, &element_tracker, &element_value);
-       if (ret) {
-               return ret;
-       }
-
-       /* Open process attribute tracker element */
-       ret = mi_lttng_writer_open_element(writer, element_tracker);
-       if (ret) {
-               goto end;
-       }
-
-       /* Open values element */
-       ret = mi_lttng_process_attr_values_open(writer);
-end:
-       return ret;
-}
-
-int mi_lttng_pids_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, config_element_pids);
-}
-
-/*
- * TODO: move the listing of pid for user agent to process semantic on
- * mi api bump. The use of process element break the mi api.
- */
-int mi_lttng_pid(struct mi_writer *writer,
-               pid_t pid,
-               const char *name,
-               int is_open)
-{
-       int ret;
-
-       /* Open pid process */
-       ret = mi_lttng_writer_open_element(writer, config_element_pid);
-       if (ret) {
-               goto end;
-       }
-
-       /* Writing pid number */
-       ret = mi_lttng_writer_write_element_signed_int(writer,
-                       mi_lttng_element_pid_id, (int)pid);
-       if (ret) {
-               goto end;
-       }
-
-       /* Writing name of the process */
-       if (name) {
-               ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                               name);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       if (!is_open) {
-               /* Closing Pid */
-               ret = mi_lttng_writer_close_element(writer);
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_process_attr_values_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(
-                       writer, config_element_process_attr_values);
-}
-
-int mi_lttng_all_process_attribute_value(struct mi_writer *writer,
-               enum lttng_process_attr process_attr,
-               bool is_open)
-{
-       int ret;
-       const char *element_id_tracker, *element_target_id;
-
-       ret = get_tracker_elements(
-                       process_attr, &element_id_tracker, &element_target_id);
-       if (ret) {
-               return ret;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, element_target_id);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, config_element_type);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_bool(writer, config_element_all, 1);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-int mi_lttng_integral_process_attribute_value(struct mi_writer *writer,
-               enum lttng_process_attr process_attr,
-               int64_t value,
-               bool is_open)
-{
-       int ret;
-       const char *element_id_tracker, *element_target_id;
-
-       ret = get_tracker_elements(
-                       process_attr, &element_id_tracker, &element_target_id);
-       if (ret) {
-               return ret;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, element_target_id);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, config_element_type);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_signed_int(
-                       writer, config_element_process_attr_id, value);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_string_process_attribute_value(struct mi_writer *writer,
-               enum lttng_process_attr process_attr,
-               const char *value,
-               bool is_open)
-
-{
-       int ret;
-       const char *element_id_tracker, *element_target_id;
-
-       ret = get_tracker_elements(
-                       process_attr, &element_id_tracker, &element_target_id);
-       if (ret) {
-               return ret;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, element_target_id);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer, config_element_type);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_string(
-                       writer, config_element_name, value);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-
-       if (!is_open) {
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_event_fields_open(struct mi_writer *writer)
-{
-       return mi_lttng_writer_open_element(writer, mi_lttng_element_event_fields);
-}
-
-int mi_lttng_event_field(struct mi_writer *writer,
-               struct lttng_event_field *field)
-{
-       int ret;
-
-       if (!field->field_name[0]) {
-               ret = 0;
-               goto end;
-       }
-
-       /* Open field */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_field);
-       if (ret) {
-               goto end;
-       }
-
-       if (!field->field_name[0]) {
-               goto close;
-       }
-
-       /* Name */
-       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                       field->field_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Type */
-       ret = mi_lttng_writer_write_element_string(writer, config_element_type,
-                       mi_lttng_eventfieldtype_string(field->type));
-       if (ret) {
-               goto end;
-       }
-
-       /* nowrite  */
-       ret = mi_lttng_writer_write_element_signed_int(writer,
-                       mi_lttng_element_nowrite, field->nowrite);
-       if (ret) {
-               goto end;
-       }
-
-close:
-       /* Close field element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_perf_counter_context(struct mi_writer *writer,
-               struct lttng_event_perf_counter_ctx  *perf_context)
-{
-       int ret;
-
-       /* Open perf_counter_context */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_perf_counter_context);
-       if (ret) {
-               goto end;
-       }
-
-       /* Type */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       config_element_type, perf_context->type);
-       if (ret) {
-               goto end;
-       }
-
-       /* Config */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       config_element_config, perf_context->config);
-       if (ret) {
-               goto end;
-       }
-
-       /* Name of the perf counter */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_name, perf_context->name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close perf_counter_context */
-       ret = mi_lttng_writer_close_element(writer);
-end:
-       return ret;
-}
-
-static
-int mi_lttng_app_context(struct mi_writer *writer,
-               const char *provider_name, const char *ctx_name)
-{
-       int ret;
-
-       /* Open app */
-       ret = mi_lttng_writer_open_element(writer,
-                       config_element_context_app);
-       if (ret) {
-               goto end;
-       }
-
-       /* provider_name */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_context_app_provider_name,
-                       provider_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* ctx_name */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_context_app_ctx_name, ctx_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close app */
-       ret = mi_lttng_writer_close_element(writer);
-end:
-       return ret;
-}
-
-int mi_lttng_context(struct mi_writer *writer,
-               struct lttng_event_context *context, int is_open)
-{
-       int ret;
-
-       /* Open context */
-       ret = mi_lttng_writer_open_element(writer , config_element_context);
-       if (ret) {
-               goto end;
-       }
-
-       /* Special case for PERF_*_COUNTER
-        * print the lttng_event_perf_counter_ctx*/
-       switch (context->ctx) {
-       case LTTNG_EVENT_CONTEXT_PERF_COUNTER:
-       case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER:
-       case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER:
-       {
-               struct lttng_event_perf_counter_ctx *perf_context =
-                               &context->u.perf_counter;
-               ret =  mi_lttng_perf_counter_context(writer, perf_context);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       }
-       case LTTNG_EVENT_CONTEXT_APP_CONTEXT:
-       {
-               ret = mi_lttng_app_context(writer,
-                               context->u.app_ctx.provider_name,
-                               context->u.app_ctx.ctx_name);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       }
-       default:
-       {
-               const char *type_string = mi_lttng_event_contexttype_string(
-                               context->ctx);
-               if (!type_string) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-
-               /* Print context type */
-               ret = mi_lttng_writer_write_element_string(writer,
-                               config_element_type, type_string);
-               break;
-       }
-       }
-
-       /* Close context */
-       if (!is_open) {
-               ret = mi_lttng_writer_close_element(writer);
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_snapshot_output_session_name(struct mi_writer *writer,
-               const char *session_name)
-{
-       int ret;
-
-       /* Open session element */
-       ret = mi_lttng_writer_open_element(writer, config_element_session);
-       if (ret) {
-               goto end;
-       }
-
-       /* Snapshot output list for current session name */
-       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                       session_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Open element snapshots (sequence one snapshot) */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_snapshots);
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-int mi_lttng_snapshot_list_output(struct mi_writer *writer,
-               const struct lttng_snapshot_output *output)
-{
-       int ret;
-
-       /* Open element snapshot output */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_command_snapshot);
-       if (ret) {
-               goto end;
-       }
-
-       /* ID of the snapshot output */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_id, output->id);
-       if (ret) {
-               goto end;
-       }
-
-       /* Name of the output */
-       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                       output->name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Destination of the output (ctrl_url)*/
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
-       if (ret) {
-               goto end;
-       }
-
-       /* Destination of the output (data_url) */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_data_url, output->data_url);
-       if (ret) {
-               goto end;
-       }
-
-       /* total size of all stream combined */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_snapshot_max_size, output->max_size);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close snapshot output element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_snapshot_del_output(struct mi_writer *writer, int id,
-               const char *name, const char *current_session_name)
-{
-       int ret;
-
-       /* Open element del_snapshot */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_command_snapshot);
-       if (ret) {
-               goto end;
-       }
-
-
-       if (id != UINT32_MAX) {
-               /* "Snapshot output "id" successfully deleted
-                * for "current_session_name"
-                * ID of the snapshot output
-                */
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               mi_lttng_element_id, id);
-               if (ret) {
-                       goto end;
-               }
-       } else {
-               /* "Snapshot output "name" successfully deleted
-                * for session "current_session_name"
-                * Name of the output
-                */
-               ret = mi_lttng_writer_write_element_string(writer, config_element_name,
-                               name);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /* Snapshot was deleted for session "current_session_name"*/
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_session_name,
-                       current_session_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close snapshot element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_snapshot_add_output(struct mi_writer *writer,
-               const char *current_session_name, const char *n_ptr,
-               struct lttng_snapshot_output *output)
-{
-       int ret;
-
-       /* Open element snapshot */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_command_snapshot);
-       if (ret) {
-               goto end;
-       }
-
-       /* Snapshot output id */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_id, output->id);
-       if (ret) {
-               goto end;
-       }
-
-       /* Snapshot output names */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       config_element_name, n_ptr);
-       if (ret) {
-               goto end;
-       }
-
-       /* Destination of the output (ctrl_url)*/
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
-       if (ret) {
-               goto end;
-       }
-
-       /* Snapshot added for session "current_session_name"*/
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_session_name, current_session_name);
-       if (ret) {
-               goto end;
-       }
-
-       /* total size of all stream combined */
-       ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                       mi_lttng_element_snapshot_max_size, output->max_size);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close snapshot element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_snapshot_record(struct mi_writer *writer,
-               const char *current_session_name, const char *url,
-               const char *cmdline_ctrl_url, const char *cmdline_data_url)
-{
-       int ret;
-
-       /* Open element snapshot */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_command_snapshot);
-       if (ret) {
-               goto end;
-       }
-
-       /*
-        * If a valid an URL was given, serialize it,
-        * else take the command line data and ctrl urls*/
-       if (url) {
-               /* Destination of the output (ctrl_url)*/
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_snapshot_ctrl_url, url);
-               if (ret) {
-                       goto end;
-               }
-       } else if (cmdline_ctrl_url) {
-               /* Destination of the output (ctrl_url)*/
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_snapshot_ctrl_url, cmdline_ctrl_url);
-               if (ret) {
-                       goto end;
-               }
-
-               /* Destination of the output (data_url) */
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_snapshot_data_url, cmdline_data_url);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /* Close record_snapshot element */
-       ret = mi_lttng_writer_close_element(writer);
-
-end:
-       return ret;
-}
-
-int mi_lttng_rotation_schedule(struct mi_writer *writer,
-               const struct lttng_rotation_schedule *schedule)
-{
-       int ret = 0;
-       enum lttng_rotation_status status;
-       uint64_t value;
-       const char *element_name;
-       const char *value_name;
-       bool empty_schedule = false;
-
-       switch (lttng_rotation_schedule_get_type(schedule)) {
-       case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC:
-               status = lttng_rotation_schedule_periodic_get_period(schedule,
-                               &value);
-               element_name = mi_lttng_element_rotation_schedule_periodic;
-               value_name = mi_lttng_element_rotation_schedule_periodic_time_us;
-               break;
-       case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD:
-               status = lttng_rotation_schedule_size_threshold_get_threshold(
-                               schedule, &value);
-               element_name = mi_lttng_element_rotation_schedule_size_threshold;
-               value_name = mi_lttng_element_rotation_schedule_size_threshold_bytes;
-               break;
-       default:
-               ret = -1;
-               goto end;
-       }
-
-       if (status != LTTNG_ROTATION_STATUS_OK) {
-               if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) {
-                       empty_schedule = true;
-               } else {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       ret = mi_lttng_writer_open_element(writer, element_name);
-       if (ret) {
-               goto end;
-       }
-
-       if (!empty_schedule) {
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               value_name, value);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /* Close schedule descriptor element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-int mi_lttng_rotation_schedule_result(struct mi_writer *writer,
-               const struct lttng_rotation_schedule *schedule,
-               bool success)
-{
-       int ret = 0;
-
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_rotation_schedule_result);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_rotation_schedule);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_rotation_schedule(writer, schedule);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close rotation_schedule element */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_bool(writer,
-                       mi_lttng_element_command_success, success);
-       if (ret) {
-               goto end;
-       }
-
-       /* Close rotation_schedule_result element */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-int mi_lttng_location(struct mi_writer *writer,
-               const struct lttng_trace_archive_location *location)
-{
-       int ret = 0;
-       enum lttng_trace_archive_location_type location_type;
-       enum lttng_trace_archive_location_status status;
-
-       location_type = lttng_trace_archive_location_get_type(location);
-
-       switch (location_type) {
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
-       {
-               const char *absolute_path;
-
-               status = lttng_trace_archive_location_local_get_absolute_path(
-                               location, &absolute_path);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_open_element(writer,
-                               mi_lttng_element_rotation_location_local);
-               if (ret) {
-                       goto end;
-               }
-
-
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_rotation_location_local_absolute_path,
-                               absolute_path);
-               if (ret) {
-                       goto end;
-               }
-
-               /* Close local element */
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       }
-       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
-       {
-               uint16_t control_port, data_port;
-               const char *host, *relative_path;
-               enum lttng_trace_archive_location_relay_protocol_type protocol;
-
-               /* Fetch all relay location parameters. */
-               status = lttng_trace_archive_location_relay_get_protocol_type(
-                               location, &protocol);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               status = lttng_trace_archive_location_relay_get_host(
-                               location, &host);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               status = lttng_trace_archive_location_relay_get_control_port(
-                               location, &control_port);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               status = lttng_trace_archive_location_relay_get_data_port(
-                               location, &data_port);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               status = lttng_trace_archive_location_relay_get_relative_path(
-                               location, &relative_path);
-               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_open_element(writer,
-                               mi_lttng_element_rotation_location_relay);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_rotation_location_relay_host,
-                               host);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               mi_lttng_element_rotation_location_relay_control_port,
-                               control_port);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               mi_lttng_element_rotation_location_relay_data_port,
-                               data_port);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_rotation_location_relay_protocol,
-                               mi_lttng_trace_archive_location_relay_protocol_type_string(protocol));
-               if (ret) {
-                       goto end;
-               }
-
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_rotation_location_relay_relative_path,
-                               relative_path);
-               if (ret) {
-                       goto end;
-               }
-
-               /* Close relay element */
-               ret = mi_lttng_writer_close_element(writer);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       }
-       default:
-               abort();
-       }
-end:
-       return ret;
-}
-
-int mi_lttng_rotate(struct mi_writer *writer,
-               const char *session_name,
-               enum lttng_rotation_state rotation_state,
-               const struct lttng_trace_archive_location *location)
-{
-       int ret;
-
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_rotation);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_session_name,
-                       session_name);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_rotation_state,
-                       mi_lttng_rotation_state_string(rotation_state));
-       if (ret) {
-               goto end;
-       }
-
-       if (!location) {
-               /* Not a serialization error. */
-               goto close_rotation;
-       }
-
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_rotation_location);
-       if (ret) {
-               goto end;
-       }
-
-       ret = mi_lttng_location(writer, location);
-       if (ret) {
-               goto close_location;
-       }
-
-close_location:
-       /* Close location element */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-       
-close_rotation:
-       /* Close rotation element */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
diff --git a/src/common/mi-lttng.cpp b/src/common/mi-lttng.cpp
new file mode 100644 (file)
index 0000000..d41a3ca
--- /dev/null
@@ -0,0 +1,2718 @@
+/*
+ * Copyright (C) 2014 Jonathan Rajotte <jonathan.r.julien@gmail.com>
+ * Copyright (C) 2014 Olivier Cotte <olivier.cotte@polymtl.ca>
+ * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "lttng/tracker.h"
+#define _LGPL_SOURCE
+#include "mi-lttng.h"
+#include <common/config/session-config.h>
+#include <common/defaults.h>
+#include <common/tracker.h>
+#include <lttng/channel.h>
+#include <lttng/snapshot-internal.h>
+
+
+#define MI_SCHEMA_MAJOR_VERSION 4
+#define MI_SCHEMA_MINOR_VERSION 1
+
+/* Machine interface namespace URI */
+const char * const mi_lttng_xmlns = "xmlns";
+const char * const mi_lttng_xmlns_xsi = "xmlns:xsi";
+const char * const mi_lttng_w3_schema_uri = "http://www.w3.org/2001/XMLSchema-instance";
+const char * const mi_lttng_schema_location = "xsi:schemaLocation";
+const char * const mi_lttng_schema_location_uri =
+       DEFAULT_LTTNG_MI_NAMESPACE " "
+       "https://lttng.org/xml/schemas/lttng-mi/" XSTR(MI_SCHEMA_MAJOR_VERSION)
+       "/lttng-mi-" XSTR(MI_SCHEMA_MAJOR_VERSION) "."
+       XSTR(MI_SCHEMA_MINOR_VERSION) ".xsd";
+const char * const mi_lttng_schema_version = "schemaVersion";
+const char * const mi_lttng_schema_version_value = XSTR(MI_SCHEMA_MAJOR_VERSION)
+       "." XSTR(MI_SCHEMA_MINOR_VERSION);
+
+/* Strings related to command */
+const char * const mi_lttng_element_command = "command";
+const char * const mi_lttng_element_command_action = "snapshot_action";
+const char * const mi_lttng_element_command_add_context = "add-context";
+const char *const mi_lttng_element_command_add_trigger = "add-trigger";
+const char * const mi_lttng_element_command_create = "create";
+const char * const mi_lttng_element_command_destroy = "destroy";
+const char * const mi_lttng_element_command_disable_channel = "disable-channel";
+const char * const mi_lttng_element_command_disable_event = "disable-event";
+const char * const mi_lttng_element_command_enable_channels = "enable-channel";
+const char * const mi_lttng_element_command_enable_event = "enable-event";
+const char * const mi_lttng_element_command_list = "list";
+const char *const mi_lttng_element_command_list_trigger = "list-trigger";
+const char * const mi_lttng_element_command_load = "load";
+const char * const mi_lttng_element_command_metadata = "metadata";
+const char * const mi_lttng_element_command_metadata_action = "metadata_action";
+const char * const mi_lttng_element_command_regenerate = "regenerate";
+const char * const mi_lttng_element_command_regenerate_action = "regenerate_action";
+const char * const mi_lttng_element_command_name = "name";
+const char * const mi_lttng_element_command_output = "output";
+const char *const mi_lttng_element_command_remove_trigger = "remove-trigger";
+const char * const mi_lttng_element_command_save = "save";
+const char * const mi_lttng_element_command_set_session = "set-session";
+const char * const mi_lttng_element_command_snapshot = "snapshot";
+const char * const mi_lttng_element_command_snapshot_add = "add_snapshot";
+const char * const mi_lttng_element_command_snapshot_del = "del_snapshot";
+const char * const mi_lttng_element_command_snapshot_list = "list_snapshot";
+const char * const mi_lttng_element_command_snapshot_record = "record_snapshot";
+const char * const mi_lttng_element_command_start = "start";
+const char * const mi_lttng_element_command_stop = "stop";
+const char * const mi_lttng_element_command_success = "success";
+const char * const mi_lttng_element_command_track = "track";
+const char * const mi_lttng_element_command_untrack = "untrack";
+const char * const mi_lttng_element_command_version = "version";
+const char * const mi_lttng_element_command_rotate = "rotate";
+const char * const mi_lttng_element_command_enable_rotation = "enable-rotation";
+const char * const mi_lttng_element_command_disable_rotation = "disable-rotation";
+const char * const mi_lttng_element_command_clear = "clear";
+
+/* Strings related to version command */
+const char * const mi_lttng_element_version = "version";
+const char * const mi_lttng_element_version_commit = "commit";
+const char * const mi_lttng_element_version_description = "description";
+const char * const mi_lttng_element_version_license = "license";
+const char * const mi_lttng_element_version_major = "major";
+const char * const mi_lttng_element_version_minor = "minor";
+const char * const mi_lttng_element_version_patch_level = "patchLevel";
+const char * const mi_lttng_element_version_str = "string";
+const char * const mi_lttng_element_version_web = "url";
+
+/* String related to a lttng_event_field */
+const char * const mi_lttng_element_event_field = "event_field";
+const char * const mi_lttng_element_event_fields = "event_fields";
+
+/* String related to lttng_event_perf_counter_ctx */
+const char * const mi_lttng_element_perf_counter_context = "perf";
+
+/* Strings related to pid */
+const char * const mi_lttng_element_pid_id = "id";
+
+/* Strings related to save command */
+const char * const mi_lttng_element_save = "save";
+
+/* Strings related to load command */
+const char * const mi_lttng_element_load = "load";
+const char * const mi_lttng_element_load_overrides = "overrides";
+const char * const mi_lttng_element_load_override_url = "url";
+
+/* General elements of mi_lttng */
+const char * const mi_lttng_element_empty = "";
+const char * const mi_lttng_element_id = "id";
+const char * const mi_lttng_element_nowrite = "nowrite";
+const char * const mi_lttng_element_success = "success";
+const char * const mi_lttng_element_type_enum = "ENUM";
+const char * const mi_lttng_element_type_float = "FLOAT";
+const char * const mi_lttng_element_type_integer = "INTEGER";
+const char * const mi_lttng_element_type_other = "OTHER";
+const char * const mi_lttng_element_type_string = "STRING";
+
+/* String related to loglevel */
+const char * const mi_lttng_loglevel_str_alert = "TRACE_ALERT";
+const char * const mi_lttng_loglevel_str_crit = "TRACE_CRIT";
+const char * const mi_lttng_loglevel_str_debug = "TRACE_DEBUG";
+const char * const mi_lttng_loglevel_str_debug_function = "TRACE_DEBUG_FUNCTION";
+const char * const mi_lttng_loglevel_str_debug_line = "TRACE_DEBUG_LINE";
+const char * const mi_lttng_loglevel_str_debug_module = "TRACE_DEBUG_MODULE";
+const char * const mi_lttng_loglevel_str_debug_process = "TRACE_DEBUG_PROCESS";
+const char * const mi_lttng_loglevel_str_debug_program = "TRACE_DEBUG_PROGRAM";
+const char * const mi_lttng_loglevel_str_debug_system = "TRACE_DEBUG_SYSTEM";
+const char * const mi_lttng_loglevel_str_debug_unit = "TRACE_DEBUG_UNIT";
+const char * const mi_lttng_loglevel_str_emerg = "TRACE_EMERG";
+const char * const mi_lttng_loglevel_str_err = "TRACE_ERR";
+const char * const mi_lttng_loglevel_str_info = "TRACE_INFO";
+const char * const mi_lttng_loglevel_str_notice = "TRACE_NOTICE";
+const char * const mi_lttng_loglevel_str_unknown = "UNKNOWN";
+const char * const mi_lttng_loglevel_str_warning = "TRACE_WARNING";
+
+/* String related to loglevel JUL */
+const char * const mi_lttng_loglevel_str_jul_all = "JUL_ALL";
+const char * const mi_lttng_loglevel_str_jul_config = "JUL_CONFIG";
+const char * const mi_lttng_loglevel_str_jul_fine = "JUL_FINE";
+const char * const mi_lttng_loglevel_str_jul_finer = "JUL_FINER";
+const char * const mi_lttng_loglevel_str_jul_finest = "JUL_FINEST";
+const char * const mi_lttng_loglevel_str_jul_info = "JUL_INFO";
+const char * const mi_lttng_loglevel_str_jul_off = "JUL_OFF";
+const char * const mi_lttng_loglevel_str_jul_severe = "JUL_SEVERE";
+const char * const mi_lttng_loglevel_str_jul_warning = "JUL_WARNING";
+
+/* String related to loglevel LOG4J */
+const char * const mi_lttng_loglevel_str_log4j_off = "LOG4J_OFF";
+const char * const mi_lttng_loglevel_str_log4j_fatal = "LOG4J_FATAL";
+const char * const mi_lttng_loglevel_str_log4j_error = "LOG4J_ERROR";
+const char * const mi_lttng_loglevel_str_log4j_warn = "LOG4J_WARN";
+const char * const mi_lttng_loglevel_str_log4j_info = "LOG4J_INFO";
+const char * const mi_lttng_loglevel_str_log4j_debug = "LOG4J_DEBUG";
+const char * const mi_lttng_loglevel_str_log4j_trace = "LOG4J_TRACE";
+const char * const mi_lttng_loglevel_str_log4j_all = "LOG4J_ALL";
+
+/* String related to loglevel Python */
+const char * const mi_lttng_loglevel_str_python_critical = "PYTHON_CRITICAL";
+const char * const mi_lttng_loglevel_str_python_error = "PYTHON_ERROR";
+const char * const mi_lttng_loglevel_str_python_warning = "PYTHON_WARNING";
+const char * const mi_lttng_loglevel_str_python_info = "PYTHON_INFO";
+const char * const mi_lttng_loglevel_str_python_debug = "PYTHON_DEBUG";
+const char * const mi_lttng_loglevel_str_python_notset = "PYTHON_NOTSET";
+
+/* String related to loglevel type */
+const char * const mi_lttng_loglevel_type_all = "ALL";
+const char * const mi_lttng_loglevel_type_range = "RANGE";
+const char * const mi_lttng_loglevel_type_single = "SINGLE";
+const char * const mi_lttng_loglevel_type_unknown = "UNKNOWN";
+
+/* String related to a lttng_snapshot_output */
+const char * const mi_lttng_element_snapshot_ctrl_url = "ctrl_url";
+const char * const mi_lttng_element_snapshot_data_url = "data_url";
+const char * const mi_lttng_element_snapshot_max_size = "max_size";
+const char * const mi_lttng_element_snapshot_n_ptr = "n_ptr";
+const char * const mi_lttng_element_snapshot_session_name = "session_name";
+const char * const mi_lttng_element_snapshots = "snapshots";
+
+/* String related to track/untrack command */
+const char * const mi_lttng_element_track_untrack_all_wildcard = "*";
+
+const char * const mi_lttng_element_session_name = "session_name";
+
+/* String related to rotate command */
+const char * const mi_lttng_element_rotation = "rotation";
+const char * const mi_lttng_element_rotate_status = "status";
+const char * const mi_lttng_element_rotation_schedule = "rotation_schedule";
+const char * const mi_lttng_element_rotation_schedules = "rotation_schedules";
+const char * const mi_lttng_element_rotation_schedule_result = "rotation_schedule_result";
+const char * const mi_lttng_element_rotation_schedule_results = "rotation_schedule_results";
+const char * const mi_lttng_element_rotation_schedule_periodic = "periodic";
+const char * const mi_lttng_element_rotation_schedule_periodic_time_us = "time_us";
+const char * const mi_lttng_element_rotation_schedule_size_threshold = "size_threshold";
+const char * const mi_lttng_element_rotation_schedule_size_threshold_bytes = "bytes";
+const char * const mi_lttng_element_rotation_state = "state";
+const char * const mi_lttng_element_rotation_location = "location";
+const char * const mi_lttng_element_rotation_location_local = "local";
+const char * const mi_lttng_element_rotation_location_local_absolute_path = "absolute_path";
+const char * const mi_lttng_element_rotation_location_relay = "relay";
+const char * const mi_lttng_element_rotation_location_relay_host = "host";
+const char * const mi_lttng_element_rotation_location_relay_control_port = "control_port";
+const char * const mi_lttng_element_rotation_location_relay_data_port = "data_port";
+const char * const mi_lttng_element_rotation_location_relay_protocol = "protocol";
+const char * const mi_lttng_element_rotation_location_relay_relative_path = "relative_path";
+
+/* String related to enum lttng_rotation_state */
+const char * const mi_lttng_rotation_state_str_ongoing = "ONGOING";
+const char * const mi_lttng_rotation_state_str_completed = "COMPLETED";
+const char * const mi_lttng_rotation_state_str_expired = "EXPIRED";
+const char * const mi_lttng_rotation_state_str_error = "ERROR";
+
+/* String related to enum lttng_trace_archive_location_relay_protocol_type */
+const char * const mi_lttng_rotation_location_relay_protocol_str_tcp = "TCP";
+
+/* String related to rate_policy elements */
+const char *const mi_lttng_element_rate_policy = "rate_policy";
+const char *const mi_lttng_element_rate_policy_every_n =
+               "rate_policy_every_n";
+const char *const mi_lttng_element_rate_policy_once_after_n =
+               "rate_policy_once_after_n";
+
+const char *const mi_lttng_element_rate_policy_every_n_interval =
+               "interval";
+const char
+               *const mi_lttng_element_rate_policy_once_after_n_threshold =
+                               "threshold";
+
+/* String related to action elements */
+const char *const mi_lttng_element_action = "action";
+const char *const mi_lttng_element_action_list = "action_list";
+const char *const mi_lttng_element_action_notify = "action_notify";
+const char *const mi_lttng_element_action_start_session =
+               "action_start_session";
+const char *const mi_lttng_element_action_stop_session =
+               "action_stop_session";
+const char *const mi_lttng_element_action_rotate_session =
+               "action_rotate_session";
+const char *const mi_lttng_element_action_snapshot_session =
+               "action_snapshot_session";
+const char *const mi_lttng_element_action_snapshot_session_output =
+               "output";
+
+/* String related to condition */
+const char *const mi_lttng_element_condition = "condition";
+const char *const mi_lttng_element_condition_buffer_usage_high =
+               "condition_buffer_usage_high";
+const char *const mi_lttng_element_condition_buffer_usage_low =
+               "condition_buffer_usage_low";
+const char *const mi_lttng_element_condition_event_rule_matches =
+               "condition_event_rule_matches";
+const char *const mi_lttng_element_condition_session_consumed_size =
+               "condition_session_consumed_size";
+const char *const mi_lttng_element_condition_session_rotation =
+               "condition_session_rotation";
+const char
+               *const mi_lttng_element_condition_session_rotation_completed =
+                               "condition_session_rotation_completed";
+const char
+               *const mi_lttng_element_condition_session_rotation_ongoing =
+                               "condition_session_rotation_ongoing";
+
+const char *const mi_lttng_element_condition_channel_name =
+               "channel_name";
+const char *const mi_lttng_element_condition_threshold_bytes =
+               "threshold_bytes";
+const char *const mi_lttng_element_condition_threshold_ratio =
+               "threshold_ratio";
+
+/* String related to capture descriptor */
+const char *const mi_lttng_element_capture_descriptor =
+               "capture_descriptor";
+const char *const mi_lttng_element_capture_descriptors =
+               "capture_descriptors";
+
+/* String related to event expression */
+const char *const mi_lttng_element_event_expr = "event_expr";
+const char *const mi_lttng_element_event_expr_payload_field =
+               "event_expr_payload_field";
+const char *const mi_lttng_element_event_expr_channel_context_field =
+               "event_expr_channel_context_field";
+const char
+               *const mi_lttng_element_event_expr_app_specific_context_field =
+                               "event_expr_app_specific_context_field";
+const char *const mi_lttng_element_event_expr_array_field_element =
+               "event_expr_array_field_element";
+const char *const mi_lttng_element_event_expr_provider_name =
+               "provider_name";
+const char *const mi_lttng_element_event_expr_type_name =
+               "type_name";
+const char *const mi_lttng_element_event_expr_index = "index";
+
+/* String related to event rule */
+const char *const mi_lttng_element_event_rule = "event_rule";
+
+/* String related to lttng_event_rule_type */
+const char *const mi_lttng_element_event_rule_event_name =
+               "event_name";
+const char *const mi_lttng_element_event_rule_name_pattern =
+               "name_pattern";
+const char *const mi_lttng_element_event_rule_filter_expression =
+               "filter_expression";
+
+const char *const mi_lttng_element_event_rule_jul_logging =
+               "event_rule_jul_logging";
+const char *const mi_lttng_element_event_rule_kernel_kprobe =
+               "event_rule_kernel_kprobe";
+const char *const mi_lttng_element_event_rule_kernel_syscall =
+               "event_rule_kernel_syscall";
+const char *const mi_lttng_element_event_rule_kernel_tracepoint =
+               "event_rule_kernel_tracepoint";
+const char *const mi_lttng_element_event_rule_kernel_uprobe =
+               "event_rule_kernel_uprobe";
+const char *const mi_lttng_element_event_rule_log4j_logging =
+               "event_rule_log4j_logging";
+const char *const mi_lttng_element_event_rule_python_logging =
+               "event_rule_python_logging";
+const char *const mi_lttng_element_event_rule_user_tracepoint =
+               "event_rule_user_tracepoint";
+
+/* String related to lttng_event_rule_kernel_syscall. */
+const char *const
+               mi_lttng_element_event_rule_kernel_syscall_emission_site =
+                               "emission_site";
+
+/* String related to enum lttng_event_rule_kernel_syscall_emission_site. */
+const char *const
+               mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit =
+                               "entry+exit";
+const char
+               *const mi_lttng_event_rule_kernel_syscall_emission_site_entry =
+                               "entry";
+const char *const
+               mi_lttng_event_rule_kernel_syscall_emission_site_exit = "exit";
+
+/* String related to lttng_event_rule_user_tracepoint */
+const char *const
+               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions =
+                               "name_pattern_exclusions";
+const char *const
+               mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion =
+                               "name_pattern_exclusion";
+
+/* String related to log level rule. */
+const char *const mi_lttng_element_log_level_rule =
+               "log_level_rule";
+const char *const mi_lttng_element_log_level_rule_exactly =
+               "log_level_rule_exactly";
+const char
+               *const mi_lttng_element_log_level_rule_at_least_as_severe_as =
+                               "log_level_rule_at_least_as_severe_as";
+const char *const mi_lttng_element_log_level_rule_level = "level";
+
+/* String related to kernel probe location. */
+const char *const mi_lttng_element_kernel_probe_location =
+               "kernel_probe_location";
+const char
+               *const mi_lttng_element_kernel_probe_location_symbol_offset =
+                               "kernel_probe_location_symbol_offset";
+const char *const
+               mi_lttng_element_kernel_probe_location_symbol_offset_name =
+                               "name";
+const char *const
+               mi_lttng_element_kernel_probe_location_symbol_offset_offset =
+                               "offset";
+
+const char *const mi_lttng_element_kernel_probe_location_address =
+               "kernel_probe_location_address";
+const char
+               *const mi_lttng_element_kernel_probe_location_address_address =
+                               "address";
+
+/* String related to userspace probe location. */
+const char *const mi_lttng_element_userspace_probe_location =
+               "userspace_probe_location";
+const char
+               *const mi_lttng_element_userspace_probe_location_binary_path =
+                               "binary_path";
+const char
+               *const mi_lttng_element_userspace_probe_location_function =
+                               "userspace_probe_location_function";
+const char
+               *const mi_lttng_element_userspace_probe_location_function_name =
+                               "name";
+const char
+               *const mi_lttng_element_userspace_probe_location_lookup_method =
+                               "userspace_probe_location_lookup_method";
+const char *const
+               mi_lttng_element_userspace_probe_location_lookup_method_function_default =
+                               "userspace_probe_location_lookup_method_function_default";
+const char *const
+               mi_lttng_element_userspace_probe_location_lookup_method_function_elf =
+                               "userspace_probe_location_lookup_method_function_elf";
+const char *const
+               mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt =
+                               "userspace_probe_location_lookup_method_tracepoint_sdt";
+const char
+               *const mi_lttng_element_userspace_probe_location_tracepoint =
+                               "userspace_probe_location_tracepoint";
+const char *const
+               mi_lttng_element_userspace_probe_location_tracepoint_probe_name =
+                               "probe_name";
+const char *const
+               mi_lttng_element_userspace_probe_location_tracepoint_provider_name =
+                               "provider_name";
+
+/* String related to enum
+ * lttng_userspace_probe_location_function_instrumentation_type */
+const char *const
+               mi_lttng_element_userspace_probe_location_function_instrumentation_type =
+                               "instrumentation_type";
+const char *const
+               mi_lttng_userspace_probe_location_function_instrumentation_type_entry =
+                               "ENTRY";
+
+/* String related to trigger */
+const char *const mi_lttng_element_triggers = "triggers";
+const char *const mi_lttng_element_trigger = "trigger";
+const char *const mi_lttng_element_trigger_owner_uid = "owner_uid";
+
+/* String related to error_query. */
+const char *const mi_lttng_element_error_query_result =
+               "error_query_result";
+const char *const mi_lttng_element_error_query_result_counter =
+               "error_query_result_counter";
+const char *const
+               mi_lttng_element_error_query_result_counter_value = "value";
+const char *const mi_lttng_element_error_query_result_description =
+               "description";
+const char *const mi_lttng_element_error_query_result_name =
+               "name";
+const char *const mi_lttng_element_error_query_result_type =
+               "type";
+const char *const mi_lttng_element_error_query_results =
+               "error_query_results";
+
+/* String related to add-context command */
+const char * const mi_lttng_element_context_symbol = "symbol";
+
+/* Deprecated symbols preserved for ABI compatibility. */
+LTTNG_EXPORT const char *mi_lttng_context_type_perf_counter;
+LTTNG_EXPORT const char *mi_lttng_context_type_perf_cpu_counter;
+LTTNG_EXPORT const char *mi_lttng_context_type_perf_thread_counter;
+LTTNG_EXPORT const char *mi_lttng_element_track_untrack_pid_target;
+LTTNG_EXPORT const char *mi_lttng_element_track_untrack_targets;
+LTTNG_EXPORT const char *mi_lttng_element_calibrate;
+LTTNG_EXPORT const char *mi_lttng_element_calibrate_function;
+LTTNG_EXPORT const char *mi_lttng_element_command_calibrate;
+
+/* This is a merge of jul loglevel and regular loglevel
+ * Those should never overlap by definition
+ * (see struct lttng_event loglevel)
+ */
+const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain)
+{
+       switch (domain) {
+       case LTTNG_DOMAIN_KERNEL:
+       case LTTNG_DOMAIN_UST:
+               switch (value) {
+               case -1:
+                       return mi_lttng_element_empty;
+               case LTTNG_LOGLEVEL_EMERG:
+                       return mi_lttng_loglevel_str_emerg;
+               case LTTNG_LOGLEVEL_ALERT:
+                       return mi_lttng_loglevel_str_alert;
+               case LTTNG_LOGLEVEL_CRIT:
+                       return mi_lttng_loglevel_str_crit;
+               case LTTNG_LOGLEVEL_ERR:
+                       return mi_lttng_loglevel_str_err;
+               case LTTNG_LOGLEVEL_WARNING:
+                       return mi_lttng_loglevel_str_warning;
+               case LTTNG_LOGLEVEL_NOTICE:
+                       return mi_lttng_loglevel_str_notice;
+               case LTTNG_LOGLEVEL_INFO:
+                       return mi_lttng_loglevel_str_info;
+               case LTTNG_LOGLEVEL_DEBUG_SYSTEM:
+                       return mi_lttng_loglevel_str_debug_system;
+               case LTTNG_LOGLEVEL_DEBUG_PROGRAM:
+                       return mi_lttng_loglevel_str_debug_program;
+               case LTTNG_LOGLEVEL_DEBUG_PROCESS:
+                       return mi_lttng_loglevel_str_debug_process;
+               case LTTNG_LOGLEVEL_DEBUG_MODULE:
+                       return mi_lttng_loglevel_str_debug_module;
+               case LTTNG_LOGLEVEL_DEBUG_UNIT:
+                       return mi_lttng_loglevel_str_debug_unit;
+               case LTTNG_LOGLEVEL_DEBUG_FUNCTION:
+                       return mi_lttng_loglevel_str_debug_function;
+               case LTTNG_LOGLEVEL_DEBUG_LINE:
+                       return mi_lttng_loglevel_str_debug_line;
+               case LTTNG_LOGLEVEL_DEBUG:
+                       return mi_lttng_loglevel_str_debug;
+               default:
+                       return mi_lttng_loglevel_str_unknown;
+               }
+               break;
+       case LTTNG_DOMAIN_LOG4J:
+               switch (value) {
+               case -1:
+                       return mi_lttng_element_empty;
+               case LTTNG_LOGLEVEL_LOG4J_OFF:
+                       return mi_lttng_loglevel_str_log4j_off;
+               case LTTNG_LOGLEVEL_LOG4J_FATAL:
+                       return mi_lttng_loglevel_str_log4j_fatal;
+               case LTTNG_LOGLEVEL_LOG4J_ERROR:
+                       return mi_lttng_loglevel_str_log4j_error;
+               case LTTNG_LOGLEVEL_LOG4J_WARN:
+                       return mi_lttng_loglevel_str_log4j_warn;
+               case LTTNG_LOGLEVEL_LOG4J_INFO:
+                       return mi_lttng_loglevel_str_log4j_info;
+               case LTTNG_LOGLEVEL_LOG4J_DEBUG:
+                       return mi_lttng_loglevel_str_log4j_debug;
+               case LTTNG_LOGLEVEL_LOG4J_TRACE:
+                       return mi_lttng_loglevel_str_log4j_trace;
+               case LTTNG_LOGLEVEL_LOG4J_ALL:
+                       return mi_lttng_loglevel_str_log4j_all;
+               default:
+                       return mi_lttng_loglevel_str_unknown;
+               }
+               break;
+       case LTTNG_DOMAIN_JUL:
+               switch (value) {
+               case -1:
+                       return mi_lttng_element_empty;
+               case LTTNG_LOGLEVEL_JUL_OFF:
+                       return mi_lttng_loglevel_str_jul_off;
+               case LTTNG_LOGLEVEL_JUL_SEVERE:
+                       return mi_lttng_loglevel_str_jul_severe;
+               case LTTNG_LOGLEVEL_JUL_WARNING:
+                       return mi_lttng_loglevel_str_jul_warning;
+               case LTTNG_LOGLEVEL_JUL_INFO:
+                       return mi_lttng_loglevel_str_jul_info;
+               case LTTNG_LOGLEVEL_JUL_CONFIG:
+                       return mi_lttng_loglevel_str_jul_config;
+               case LTTNG_LOGLEVEL_JUL_FINE:
+                       return mi_lttng_loglevel_str_jul_fine;
+               case LTTNG_LOGLEVEL_JUL_FINER:
+                       return mi_lttng_loglevel_str_jul_finer;
+               case LTTNG_LOGLEVEL_JUL_FINEST:
+                       return mi_lttng_loglevel_str_jul_finest;
+               case LTTNG_LOGLEVEL_JUL_ALL:
+                       return mi_lttng_loglevel_str_jul_all;
+               default:
+                       return mi_lttng_loglevel_str_unknown;
+               }
+               break;
+       case LTTNG_DOMAIN_PYTHON:
+               switch (value) {
+               case LTTNG_LOGLEVEL_PYTHON_CRITICAL:
+                       return mi_lttng_loglevel_str_python_critical;
+               case LTTNG_LOGLEVEL_PYTHON_ERROR:
+                       return mi_lttng_loglevel_str_python_error;
+               case LTTNG_LOGLEVEL_PYTHON_WARNING:
+                       return mi_lttng_loglevel_str_python_warning;
+               case LTTNG_LOGLEVEL_PYTHON_INFO:
+                       return mi_lttng_loglevel_str_python_info;
+               case LTTNG_LOGLEVEL_PYTHON_DEBUG:
+                       return mi_lttng_loglevel_str_python_debug;
+               case LTTNG_LOGLEVEL_PYTHON_NOTSET:
+                       return mi_lttng_loglevel_str_python_notset;
+               default:
+                       return mi_lttng_loglevel_str_unknown;
+               }
+               break;
+       default:
+               return mi_lttng_loglevel_str_unknown;
+       }
+}
+
+const char *mi_lttng_logleveltype_string(enum lttng_loglevel_type value)
+{
+       switch (value) {
+       case LTTNG_EVENT_LOGLEVEL_ALL:
+               return mi_lttng_loglevel_type_all;
+       case LTTNG_EVENT_LOGLEVEL_RANGE:
+               return mi_lttng_loglevel_type_range;
+       case LTTNG_EVENT_LOGLEVEL_SINGLE:
+               return mi_lttng_loglevel_type_single;
+       default:
+               return mi_lttng_loglevel_type_unknown;
+       }
+}
+
+static
+const char *mi_lttng_eventtype_string(enum lttng_event_type value)
+{
+       switch (value) {
+       case LTTNG_EVENT_ALL:
+               return config_event_type_all;
+       case LTTNG_EVENT_TRACEPOINT:
+               return config_event_type_tracepoint;
+       case LTTNG_EVENT_PROBE:
+               return config_event_type_probe;
+       case LTTNG_EVENT_USERSPACE_PROBE:
+               return config_event_type_userspace_probe;
+       case LTTNG_EVENT_FUNCTION:
+               return config_event_type_function;
+       case LTTNG_EVENT_FUNCTION_ENTRY:
+               return config_event_type_function_entry;
+       case LTTNG_EVENT_SYSCALL:
+               return config_event_type_syscall;
+       case LTTNG_EVENT_NOOP:
+               return config_event_type_noop;
+       default:
+               return mi_lttng_element_empty;
+       }
+}
+
+static
+const char *mi_lttng_event_contexttype_string(enum lttng_event_context_type val)
+{
+       switch (val) {
+       case LTTNG_EVENT_CONTEXT_PID:
+               return config_event_context_pid;
+       case LTTNG_EVENT_CONTEXT_PROCNAME:
+               return config_event_context_procname;
+       case LTTNG_EVENT_CONTEXT_PRIO:
+               return config_event_context_prio;
+       case LTTNG_EVENT_CONTEXT_NICE:
+               return config_event_context_nice;
+       case LTTNG_EVENT_CONTEXT_VPID:
+               return config_event_context_vpid;
+       case LTTNG_EVENT_CONTEXT_TID:
+               return config_event_context_tid;
+       case LTTNG_EVENT_CONTEXT_VTID:
+               return config_event_context_vtid;
+       case LTTNG_EVENT_CONTEXT_PPID:
+               return config_event_context_ppid;
+       case LTTNG_EVENT_CONTEXT_VPPID:
+               return config_event_context_vppid;
+       case LTTNG_EVENT_CONTEXT_PTHREAD_ID:
+               return config_event_context_pthread_id;
+       case LTTNG_EVENT_CONTEXT_HOSTNAME:
+               return config_event_context_hostname;
+       case LTTNG_EVENT_CONTEXT_IP:
+               return config_event_context_ip;
+       case LTTNG_EVENT_CONTEXT_INTERRUPTIBLE:
+               return config_event_context_interruptible;
+       case LTTNG_EVENT_CONTEXT_PREEMPTIBLE:
+               return config_event_context_preemptible;
+       case LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE:
+               return config_event_context_need_reschedule;
+       case LTTNG_EVENT_CONTEXT_MIGRATABLE:
+               return config_event_context_migratable;
+       case LTTNG_EVENT_CONTEXT_CALLSTACK_USER:
+               return config_event_context_callstack_user;
+       case LTTNG_EVENT_CONTEXT_CALLSTACK_KERNEL:
+               return config_event_context_callstack_kernel;
+       case LTTNG_EVENT_CONTEXT_CGROUP_NS:
+               return config_event_context_cgroup_ns;
+       case LTTNG_EVENT_CONTEXT_IPC_NS:
+               return config_event_context_ipc_ns;
+       case LTTNG_EVENT_CONTEXT_MNT_NS:
+               return config_event_context_mnt_ns;
+       case LTTNG_EVENT_CONTEXT_NET_NS:
+               return config_event_context_net_ns;
+       case LTTNG_EVENT_CONTEXT_PID_NS:
+               return config_event_context_pid_ns;
+       case LTTNG_EVENT_CONTEXT_TIME_NS:
+               return config_event_context_time_ns;
+       case LTTNG_EVENT_CONTEXT_USER_NS:
+               return config_event_context_user_ns;
+       case LTTNG_EVENT_CONTEXT_UTS_NS:
+               return config_event_context_uts_ns;
+       case LTTNG_EVENT_CONTEXT_UID:
+               return config_event_context_uid;
+       case LTTNG_EVENT_CONTEXT_EUID:
+               return config_event_context_euid;
+       case LTTNG_EVENT_CONTEXT_SUID:
+               return config_event_context_suid;
+       case LTTNG_EVENT_CONTEXT_GID:
+               return config_event_context_gid;
+       case LTTNG_EVENT_CONTEXT_EGID:
+               return config_event_context_egid;
+       case LTTNG_EVENT_CONTEXT_SGID:
+               return config_event_context_sgid;
+       case LTTNG_EVENT_CONTEXT_VUID:
+               return config_event_context_vuid;
+       case LTTNG_EVENT_CONTEXT_VEUID:
+               return config_event_context_veuid;
+       case LTTNG_EVENT_CONTEXT_VSUID:
+               return config_event_context_vsuid;
+       case LTTNG_EVENT_CONTEXT_VGID:
+               return config_event_context_vgid;
+       case LTTNG_EVENT_CONTEXT_VEGID:
+               return config_event_context_vegid;
+       case LTTNG_EVENT_CONTEXT_VSGID:
+               return config_event_context_vsgid;
+       default:
+               return NULL;
+       }
+}
+
+const char *mi_lttng_eventfieldtype_string(enum lttng_event_field_type val)
+{
+       switch (val) {
+       case(LTTNG_EVENT_FIELD_INTEGER):
+               return mi_lttng_element_type_integer;
+       case(LTTNG_EVENT_FIELD_ENUM):
+               return mi_lttng_element_type_enum;
+       case(LTTNG_EVENT_FIELD_FLOAT):
+               return mi_lttng_element_type_float;
+       case(LTTNG_EVENT_FIELD_STRING):
+               return mi_lttng_element_type_string;
+       default:
+               return mi_lttng_element_type_other;
+       }
+}
+
+const char *mi_lttng_domaintype_string(enum lttng_domain_type value)
+{
+       switch (value) {
+       case LTTNG_DOMAIN_KERNEL:
+               return config_domain_type_kernel;
+       case LTTNG_DOMAIN_UST:
+               return config_domain_type_ust;
+       case LTTNG_DOMAIN_JUL:
+               return config_domain_type_jul;
+       case LTTNG_DOMAIN_LOG4J:
+               return config_domain_type_log4j;
+       case LTTNG_DOMAIN_PYTHON:
+               return config_domain_type_python;
+       default:
+               /* Should not have an unknown domain */
+               abort();
+               return NULL;
+       }
+}
+
+const char *mi_lttng_buffertype_string(enum lttng_buffer_type value)
+{
+       switch (value) {
+       case LTTNG_BUFFER_PER_PID:
+               return config_buffer_type_per_pid;
+       case LTTNG_BUFFER_PER_UID:
+               return config_buffer_type_per_uid;
+       case LTTNG_BUFFER_GLOBAL:
+               return config_buffer_type_global;
+       default:
+               /* Should not have an unknow buffer type */
+               abort();
+               return NULL;
+       }
+}
+
+const char *mi_lttng_rotation_state_string(enum lttng_rotation_state value)
+{
+       switch (value) {
+       case LTTNG_ROTATION_STATE_ONGOING:
+               return mi_lttng_rotation_state_str_ongoing;
+       case LTTNG_ROTATION_STATE_COMPLETED:
+               return mi_lttng_rotation_state_str_completed;
+       case LTTNG_ROTATION_STATE_EXPIRED:
+               return mi_lttng_rotation_state_str_expired;
+       case LTTNG_ROTATION_STATE_ERROR:
+               return mi_lttng_rotation_state_str_error;
+       default:
+               /* Should not have an unknow rotation state. */
+               abort();
+               return NULL;
+       }
+}
+
+const char *mi_lttng_trace_archive_location_relay_protocol_type_string(
+               enum lttng_trace_archive_location_relay_protocol_type value)
+{
+       switch (value) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP:
+               return mi_lttng_rotation_location_relay_protocol_str_tcp;
+       default:
+               /* Should not have an unknown relay protocol. */
+               abort();
+               return NULL;
+       }
+}
+
+struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type)
+{
+       struct mi_writer *mi_writer;
+
+       mi_writer = (struct mi_writer *) zmalloc(sizeof(struct mi_writer));
+       if (!mi_writer) {
+               PERROR("zmalloc mi_writer_create");
+               goto end;
+       }
+       if (mi_output_type == LTTNG_MI_XML) {
+               mi_writer->writer = config_writer_create(fd_output, 0);
+               if (!mi_writer->writer) {
+                       goto err_destroy;
+               }
+               mi_writer->type = LTTNG_MI_XML;
+       } else {
+               goto err_destroy;
+       }
+
+end:
+       return mi_writer;
+
+err_destroy:
+       free(mi_writer);
+       return NULL;
+}
+
+int mi_lttng_writer_destroy(struct mi_writer *writer)
+{
+       int ret;
+
+       if (!writer) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       ret = config_writer_destroy(writer->writer);
+       if (ret < 0) {
+               goto end;
+       }
+
+       free(writer);
+end:
+       return ret;
+}
+
+int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command)
+{
+       int ret;
+
+       /*
+        * A command is always the MI's root node, it must declare the current
+        * namespace and schema URIs and the schema's version.
+        */
+       ret = config_writer_open_element(writer->writer,
+                       mi_lttng_element_command);
+       if (ret) {
+               goto end;
+       }
+
+       ret = config_writer_write_attribute(writer->writer,
+                       mi_lttng_xmlns, DEFAULT_LTTNG_MI_NAMESPACE);
+       if (ret) {
+               goto end;
+       }
+
+       ret = config_writer_write_attribute(writer->writer,
+                       mi_lttng_xmlns_xsi, mi_lttng_w3_schema_uri);
+       if (ret) {
+               goto end;
+       }
+
+       ret = config_writer_write_attribute(writer->writer,
+                       mi_lttng_schema_location,
+                       mi_lttng_schema_location_uri);
+       if (ret) {
+               goto end;
+       }
+
+       ret = config_writer_write_attribute(writer->writer,
+                       mi_lttng_schema_version,
+                       mi_lttng_schema_version_value);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_command_name, command);
+end:
+       return ret;
+}
+
+int mi_lttng_writer_command_close(struct mi_writer *writer)
+{
+       return mi_lttng_writer_close_element(writer);
+}
+
+int mi_lttng_writer_open_element(struct mi_writer *writer,
+               const char *element_name)
+{
+       return config_writer_open_element(writer->writer, element_name);
+}
+
+int mi_lttng_writer_close_element(struct mi_writer *writer)
+{
+       return config_writer_close_element(writer->writer);
+}
+
+int mi_lttng_close_multi_element(struct mi_writer *writer,
+               unsigned int nb_element)
+{
+       int ret, i;
+
+       if (nb_element < 1) {
+               ret = 0;
+               goto end;
+       }
+       for (i = 0; i < nb_element; i++) {
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer,
+               const char *element_name, uint64_t value)
+{
+       return config_writer_write_element_unsigned_int(writer->writer,
+                       element_name, value);
+}
+
+int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer,
+               const char *element_name, int64_t value)
+{
+       return config_writer_write_element_signed_int(writer->writer,
+                       element_name, value);
+}
+
+int mi_lttng_writer_write_element_bool(struct mi_writer *writer,
+               const char *element_name, int value)
+{
+       return config_writer_write_element_bool(writer->writer,
+                       element_name, value);
+}
+
+int mi_lttng_writer_write_element_string(struct mi_writer *writer,
+               const char *element_name, const char *value)
+{
+       return config_writer_write_element_string(writer->writer,
+                       element_name, value);
+}
+
+int mi_lttng_writer_write_element_double(struct mi_writer *writer,
+               const char *element_name,
+               double value)
+{
+       return config_writer_write_element_double(
+                       writer->writer, element_name, value);
+}
+
+int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version,
+       const char *lttng_description, const char *lttng_license)
+{
+       int ret;
+
+       /* Open version */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_version);
+       if (ret) {
+               goto end;
+       }
+
+       /* Version string (contain info like rc etc.) */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_version_str, version->version);
+       if (ret) {
+               goto end;
+       }
+
+       /* Major version number */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_version_major, version->version_major);
+       if (ret) {
+               goto end;
+       }
+
+       /* Minor version number */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_version_minor, version->version_minor);
+       if (ret) {
+               goto end;
+       }
+
+       /* Commit version number */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_version_commit, version->version_commit);
+       if (ret) {
+               goto end;
+       }
+
+       /* Patch number */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_version_patch_level, version->version_patchlevel);
+       if (ret) {
+               goto end;
+       }
+
+       /* Name of the version */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_name, version->version_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Description mostly related to beer... */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_version_description, lttng_description);
+       if (ret) {
+               goto end;
+       }
+
+       /* url */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_version_web, version->package_url);
+       if (ret) {
+               goto end;
+       }
+
+       /* License: free as in free beer...no...*speech* */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_version_license, lttng_license);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close version element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_sessions_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, config_element_sessions);
+}
+
+int mi_lttng_session(struct mi_writer *writer,
+               struct lttng_session *session, int is_open)
+{
+       int ret;
+
+       LTTNG_ASSERT(session);
+
+       /* Open sessions element */
+       ret = mi_lttng_writer_open_element(writer,
+                       config_element_session);
+       if (ret) {
+               goto end;
+       }
+
+       /* Name of the session */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_name, session->name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Path */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_path, session->path);
+       if (ret) {
+               goto end;
+       }
+
+       /* Enabled ? */
+       ret = mi_lttng_writer_write_element_bool(writer,
+                       config_element_enabled, session->enabled);
+       if (ret) {
+               goto end;
+       }
+
+       /* Snapshot mode */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       config_element_snapshot_mode, session->snapshot_mode);
+       if (ret) {
+               goto end;
+       }
+
+       /* Live timer interval in usec */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       config_element_live_timer_interval,
+                       session->live_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               /* Closing session element */
+               ret = mi_lttng_writer_close_element(writer);
+       }
+end:
+       return ret;
+
+}
+
+int mi_lttng_domains_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, config_element_domains);
+}
+
+int mi_lttng_domain(struct mi_writer *writer,
+               struct lttng_domain *domain, int is_open)
+{
+       int ret = 0;
+       const char *str_domain;
+       const char *str_buffer;
+
+       LTTNG_ASSERT(domain);
+
+       /* Open domain element */
+       ret = mi_lttng_writer_open_element(writer, config_element_domain);
+       if (ret) {
+               goto end;
+       }
+
+       /* Domain Type */
+       str_domain = mi_lttng_domaintype_string(domain->type);
+       ret = mi_lttng_writer_write_element_string(writer, config_element_type,
+                       str_domain);
+       if (ret) {
+               goto end;
+       }
+
+       /* Buffer Type */
+       str_buffer= mi_lttng_buffertype_string(domain->buf_type);
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_buffer_type, str_buffer);
+       if (ret) {
+               goto end;
+       }
+
+       /* TODO: union  attr
+        * This union is not currently used and was added for
+        * future ust domain support.
+        * Date: 25-06-2014
+        * */
+
+       if (!is_open) {
+               /* Closing domain element */
+               ret = mi_lttng_writer_close_element(writer);
+       }
+
+end:
+       return ret;
+
+}
+
+int mi_lttng_channels_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, config_element_channels);
+}
+
+int mi_lttng_channel(struct mi_writer *writer,
+               struct lttng_channel *channel, int is_open)
+{
+       int ret = 0;
+
+       LTTNG_ASSERT(channel);
+
+       /* Opening channel element */
+       ret = mi_lttng_writer_open_element(writer, config_element_channel);
+       if (ret) {
+               goto end;
+       }
+
+       /* Name */
+       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                       channel->name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Enabled ? */
+       ret = mi_lttng_writer_write_element_bool(writer,
+                       config_element_enabled, channel->enabled);
+       if (ret) {
+               goto end;
+       }
+
+       /* Attribute */
+       ret = mi_lttng_channel_attr(writer, &channel->attr);
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               /* Closing channel element */
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+int mi_lttng_channel_attr(struct mi_writer *writer,
+               struct lttng_channel_attr *attr)
+{
+       int ret = 0;
+       struct lttng_channel *chan = caa_container_of(attr,
+                       struct lttng_channel, attr);
+       uint64_t discarded_events, lost_packets, monitor_timer_interval;
+       int64_t blocking_timeout;
+
+       LTTNG_ASSERT(attr);
+
+       ret = lttng_channel_get_discarded_event_count(chan, &discarded_events);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_channel_get_lost_packet_count(chan, &lost_packets);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_channel_get_monitor_timer_interval(chan,
+                       &monitor_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_channel_get_blocking_timeout(chan,
+                       &blocking_timeout);
+       if (ret) {
+               goto end;
+       }
+
+       /* Opening Attributes */
+       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       /* Overwrite */
+       ret = mi_lttng_writer_write_element_string(writer,
+               config_element_overwrite_mode,
+               attr->overwrite ? config_overwrite_mode_overwrite :
+                       config_overwrite_mode_discard);
+       if (ret) {
+               goto end;
+       }
+
+       /* Sub buffer size in byte */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_subbuf_size, attr->subbuf_size);
+       if (ret) {
+               goto end;
+       }
+
+       /* Number of subbuffer (power of two) */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_num_subbuf,
+               attr->num_subbuf);
+       if (ret) {
+               goto end;
+       }
+
+       /* Switch timer interval in usec */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_switch_timer_interval,
+               attr->switch_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       /* Read timer interval in usec */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_read_timer_interval,
+               attr->read_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       /* Monitor timer interval in usec */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_monitor_timer_interval,
+               monitor_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       /* Retry timeout in usec */
+       ret = mi_lttng_writer_write_element_signed_int(writer,
+               config_element_blocking_timeout,
+               blocking_timeout);
+       if (ret) {
+               goto end;
+       }
+
+       /* Event output */
+       ret = mi_lttng_writer_write_element_string(writer,
+               config_element_output_type,
+               attr->output == LTTNG_EVENT_SPLICE ?
+               config_output_type_splice : config_output_type_mmap);
+       if (ret) {
+               goto end;
+       }
+
+       /* Tracefile size in bytes */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_tracefile_size, attr->tracefile_size);
+       if (ret) {
+               goto end;
+       }
+
+       /* Count of tracefiles */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_tracefile_count,
+               attr->tracefile_count);
+       if (ret) {
+               goto end;
+       }
+
+       /* Live timer interval in usec*/
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_live_timer_interval,
+               attr->live_timer_interval);
+       if (ret) {
+               goto end;
+       }
+
+       /* Discarded events */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_discarded_events,
+               discarded_events);
+       if (ret) {
+               goto end;
+       }
+
+       /* Lost packets */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+               config_element_lost_packets,
+               lost_packets);
+       if (ret) {
+               goto end;
+       }
+
+       /* Closing attributes */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+
+}
+
+int mi_lttng_event_common_attributes(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       int ret;
+       const char *filter_expression;
+
+       /* Open event element */
+       ret = mi_lttng_writer_open_element(writer, config_element_event);
+       if (ret) {
+               goto end;
+       }
+
+       /* Event name */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_name, event->name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Event type */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_type, mi_lttng_eventtype_string(event->type));
+       if (ret) {
+               goto end;
+       }
+
+       /* Is event enabled */
+       ret = mi_lttng_writer_write_element_bool(writer,
+                       config_element_enabled, event->enabled);
+       if (ret) {
+               goto end;
+       }
+
+       /* Event filter expression */
+       ret = lttng_event_get_filter_expression(event, &filter_expression);
+       if (ret) {
+               goto end;
+       }
+
+       if (filter_expression) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_filter_expression,
+                               filter_expression);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static int write_event_exclusions(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       int i;
+       int ret;
+       int exclusion_count;
+
+       /* Open event exclusions */
+       ret = mi_lttng_writer_open_element(writer, config_element_exclusions);
+       if (ret) {
+               goto end;
+       }
+
+       exclusion_count = lttng_event_get_exclusion_name_count(event);
+       if (exclusion_count < 0) {
+               ret = exclusion_count;
+               goto end;
+       }
+
+       for (i = 0; i < exclusion_count; i++) {
+               const char *name;
+
+               ret = lttng_event_get_exclusion_name(event, i, &name);
+               if (ret) {
+                       /* Close exclusions */
+                       mi_lttng_writer_close_element(writer);
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_exclusion, name);
+               if (ret) {
+                       /* Close exclusions */
+                       mi_lttng_writer_close_element(writer);
+                       goto end;
+               }
+       }
+
+       /* Close exclusions */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_event_tracepoint_loglevel(struct mi_writer *writer,
+               struct lttng_event *event, enum lttng_domain_type domain)
+{
+       int ret;
+
+       /* Event loglevel */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_loglevel,
+                       mi_lttng_loglevel_string(event->loglevel, domain));
+       if (ret) {
+               goto end;
+       }
+
+       /* Log level type */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_loglevel_type,
+                       mi_lttng_logleveltype_string(event->loglevel_type));
+       if (ret) {
+               goto end;
+       }
+
+       /* Event exclusions */
+       ret = write_event_exclusions(writer, event);
+
+end:
+       return ret;
+}
+
+int mi_lttng_event_tracepoint_no_loglevel(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       /* event exclusion filter */
+       return write_event_exclusions(writer, event);
+}
+
+int mi_lttng_event_function_probe(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       int ret;
+
+       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       if (event->attr.probe.addr != 0) {
+               /* event probe address */
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               config_element_address, event->attr.probe.addr);
+               if (ret) {
+                       goto end;
+               }
+       } else {
+               /* event probe offset */
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               config_element_offset, event->attr.probe.offset);
+               if (ret) {
+                       goto end;
+               }
+
+               /* event probe symbol_name */
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_symbol_name, event->attr.probe.symbol_name);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Close probe_attributes and attributes */
+       ret = mi_lttng_close_multi_element(writer, 2);
+end:
+       return ret;
+}
+
+static
+int mi_lttng_event_userspace_probe(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       int ret;
+       const struct lttng_userspace_probe_location *location;
+       const struct lttng_userspace_probe_location_lookup_method *lookup_method;
+       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
+
+       location = lttng_event_get_userspace_probe_location(event);
+       if (!location) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
+       if (!lookup_method) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method);
+
+       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       switch (lttng_userspace_probe_location_get_type(location)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+       {
+               const char *function_name;
+               const char *binary_path;
+
+               ret = mi_lttng_writer_open_element(writer,
+                               config_element_userspace_probe_function_attributes);
+               if (ret) {
+                       goto end;
+               }
+
+               switch (lookup_type) {
+               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+                       ret = mi_lttng_writer_write_element_string(writer,
+                                       config_element_userspace_probe_lookup,
+                                       config_element_userspace_probe_lookup_function_elf);
+                       if (ret) {
+                               goto end;
+                       }
+                       break;
+               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
+                       ret = mi_lttng_writer_write_element_string(writer,
+                                       config_element_userspace_probe_lookup,
+                                       config_element_userspace_probe_lookup_function_default);
+                       if (ret) {
+                               goto end;
+                       }
+                       break;
+               default:
+                       goto end;
+               }
+
+               binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_userspace_probe_location_binary_path, binary_path);
+               if (ret) {
+                       goto end;
+               }
+
+               function_name = lttng_userspace_probe_location_function_get_function_name(location);
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_userspace_probe_function_location_function_name,
+                               function_name);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+       {
+               const char *probe_name, *provider_name;
+               const char *binary_path;
+
+               ret = mi_lttng_writer_open_element(writer,
+                               config_element_userspace_probe_function_attributes);
+               if (ret) {
+                       goto end;
+               }
+
+               switch (lookup_type) {
+               case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+                       ret = mi_lttng_writer_write_element_string(writer,
+                                       config_element_userspace_probe_lookup,
+                                       config_element_userspace_probe_lookup_tracepoint_sdt);
+                       if (ret) {
+                               goto end;
+                       }
+                       break;
+               default:
+                       goto end;
+               }
+
+               binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location);
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_userspace_probe_location_binary_path,
+                               binary_path);
+               if (ret) {
+                       goto end;
+               }
+
+               provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location);
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_userspace_probe_tracepoint_location_provider_name,
+                               provider_name);
+               if (ret) {
+                       goto end;
+               }
+
+               probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location);
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_userspace_probe_tracepoint_location_probe_name, probe_name);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       }
+       default:
+               ERR("Invalid probe type encountered");
+       }
+       /* Close probe_attributes and attributes */
+       ret = mi_lttng_close_multi_element(writer, 2);
+end:
+       return ret;
+}
+
+int mi_lttng_event_function_entry(struct mi_writer *writer,
+               struct lttng_event *event)
+{
+       int ret;
+
+       ret = mi_lttng_writer_open_element(writer, config_element_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes);
+       if (ret) {
+               goto end;
+       }
+
+       /* event probe symbol_name */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_symbol_name, event->attr.ftrace.symbol_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close function_attributes and attributes */
+       ret = mi_lttng_close_multi_element(writer, 2);
+end:
+       return ret;
+}
+
+int mi_lttng_events_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, config_element_events);
+}
+
+int mi_lttng_event(struct mi_writer *writer,
+               struct lttng_event *event, int is_open, enum lttng_domain_type domain)
+{
+       int ret;
+
+       ret = mi_lttng_event_common_attributes(writer, event);
+       if (ret) {
+               goto end;
+       }
+
+       switch (event->type) {
+       case LTTNG_EVENT_TRACEPOINT:
+       {
+               if (event->loglevel != -1) {
+                       ret = mi_lttng_event_tracepoint_loglevel(writer, event, domain);
+               } else {
+                       ret = mi_lttng_event_tracepoint_no_loglevel(writer, event);
+               }
+               break;
+       }
+       case LTTNG_EVENT_FUNCTION:
+               /* Fallthrough */
+       case LTTNG_EVENT_PROBE:
+               ret = mi_lttng_event_function_probe(writer, event);
+               break;
+       case LTTNG_EVENT_FUNCTION_ENTRY:
+               ret = mi_lttng_event_function_entry(writer, event);
+               break;
+       case LTTNG_EVENT_USERSPACE_PROBE:
+               ret = mi_lttng_event_userspace_probe(writer, event);
+               break;
+       case LTTNG_EVENT_ALL:
+               /* Fallthrough */
+       default:
+               break;
+       }
+
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               ret = mi_lttng_writer_close_element(writer);
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_trackers_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(
+                       writer, config_element_process_attr_trackers);
+}
+
+static int get_tracker_elements(enum lttng_process_attr process_attr,
+               const char **element_process_attr_tracker,
+               const char **element_process_attr_value)
+{
+       int ret = 0;
+
+       switch (process_attr) {
+       case LTTNG_PROCESS_ATTR_PROCESS_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_pid;
+               *element_process_attr_value =
+                               config_element_process_attr_pid_value;
+               break;
+       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_vpid;
+               *element_process_attr_value =
+                               config_element_process_attr_vpid_value;
+               break;
+       case LTTNG_PROCESS_ATTR_USER_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_uid;
+               *element_process_attr_value =
+                               config_element_process_attr_uid_value;
+               break;
+       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_vuid;
+               *element_process_attr_value =
+                               config_element_process_attr_vuid_value;
+               break;
+       case LTTNG_PROCESS_ATTR_GROUP_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_gid;
+               *element_process_attr_value =
+                               config_element_process_attr_gid_value;
+               break;
+       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
+               *element_process_attr_tracker =
+                               config_element_process_attr_tracker_vgid;
+               *element_process_attr_value =
+                               config_element_process_attr_vgid_value;
+               break;
+       default:
+               ret = LTTNG_ERR_SAVE_IO_FAIL;
+       }
+       return ret;
+}
+
+int mi_lttng_process_attribute_tracker_open(
+               struct mi_writer *writer, enum lttng_process_attr process_attr)
+{
+       int ret;
+       const char *element_tracker, *element_value;
+
+       ret = get_tracker_elements(
+                       process_attr, &element_tracker, &element_value);
+       if (ret) {
+               return ret;
+       }
+
+       /* Open process attribute tracker element */
+       ret = mi_lttng_writer_open_element(writer, element_tracker);
+       if (ret) {
+               goto end;
+       }
+
+       /* Open values element */
+       ret = mi_lttng_process_attr_values_open(writer);
+end:
+       return ret;
+}
+
+int mi_lttng_pids_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, config_element_pids);
+}
+
+/*
+ * TODO: move the listing of pid for user agent to process semantic on
+ * mi api bump. The use of process element break the mi api.
+ */
+int mi_lttng_pid(struct mi_writer *writer,
+               pid_t pid,
+               const char *name,
+               int is_open)
+{
+       int ret;
+
+       /* Open pid process */
+       ret = mi_lttng_writer_open_element(writer, config_element_pid);
+       if (ret) {
+               goto end;
+       }
+
+       /* Writing pid number */
+       ret = mi_lttng_writer_write_element_signed_int(writer,
+                       mi_lttng_element_pid_id, (int)pid);
+       if (ret) {
+               goto end;
+       }
+
+       /* Writing name of the process */
+       if (name) {
+               ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                               name);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       if (!is_open) {
+               /* Closing Pid */
+               ret = mi_lttng_writer_close_element(writer);
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_process_attr_values_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(
+                       writer, config_element_process_attr_values);
+}
+
+int mi_lttng_all_process_attribute_value(struct mi_writer *writer,
+               enum lttng_process_attr process_attr,
+               bool is_open)
+{
+       int ret;
+       const char *element_id_tracker, *element_target_id;
+
+       ret = get_tracker_elements(
+                       process_attr, &element_id_tracker, &element_target_id);
+       if (ret) {
+               return ret;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, element_target_id);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, config_element_type);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_bool(writer, config_element_all, 1);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+int mi_lttng_integral_process_attribute_value(struct mi_writer *writer,
+               enum lttng_process_attr process_attr,
+               int64_t value,
+               bool is_open)
+{
+       int ret;
+       const char *element_id_tracker, *element_target_id;
+
+       ret = get_tracker_elements(
+                       process_attr, &element_id_tracker, &element_target_id);
+       if (ret) {
+               return ret;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, element_target_id);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, config_element_type);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_signed_int(
+                       writer, config_element_process_attr_id, value);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_string_process_attribute_value(struct mi_writer *writer,
+               enum lttng_process_attr process_attr,
+               const char *value,
+               bool is_open)
+
+{
+       int ret;
+       const char *element_id_tracker, *element_target_id;
+
+       ret = get_tracker_elements(
+                       process_attr, &element_id_tracker, &element_target_id);
+       if (ret) {
+               return ret;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, element_target_id);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer, config_element_type);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_string(
+                       writer, config_element_name, value);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+
+       if (!is_open) {
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_event_fields_open(struct mi_writer *writer)
+{
+       return mi_lttng_writer_open_element(writer, mi_lttng_element_event_fields);
+}
+
+int mi_lttng_event_field(struct mi_writer *writer,
+               struct lttng_event_field *field)
+{
+       int ret;
+
+       if (!field->field_name[0]) {
+               ret = 0;
+               goto end;
+       }
+
+       /* Open field */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_field);
+       if (ret) {
+               goto end;
+       }
+
+       if (!field->field_name[0]) {
+               goto close;
+       }
+
+       /* Name */
+       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                       field->field_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Type */
+       ret = mi_lttng_writer_write_element_string(writer, config_element_type,
+                       mi_lttng_eventfieldtype_string(field->type));
+       if (ret) {
+               goto end;
+       }
+
+       /* nowrite  */
+       ret = mi_lttng_writer_write_element_signed_int(writer,
+                       mi_lttng_element_nowrite, field->nowrite);
+       if (ret) {
+               goto end;
+       }
+
+close:
+       /* Close field element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_perf_counter_context(struct mi_writer *writer,
+               struct lttng_event_perf_counter_ctx  *perf_context)
+{
+       int ret;
+
+       /* Open perf_counter_context */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_perf_counter_context);
+       if (ret) {
+               goto end;
+       }
+
+       /* Type */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       config_element_type, perf_context->type);
+       if (ret) {
+               goto end;
+       }
+
+       /* Config */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       config_element_config, perf_context->config);
+       if (ret) {
+               goto end;
+       }
+
+       /* Name of the perf counter */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_name, perf_context->name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close perf_counter_context */
+       ret = mi_lttng_writer_close_element(writer);
+end:
+       return ret;
+}
+
+static
+int mi_lttng_app_context(struct mi_writer *writer,
+               const char *provider_name, const char *ctx_name)
+{
+       int ret;
+
+       /* Open app */
+       ret = mi_lttng_writer_open_element(writer,
+                       config_element_context_app);
+       if (ret) {
+               goto end;
+       }
+
+       /* provider_name */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_context_app_provider_name,
+                       provider_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* ctx_name */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_context_app_ctx_name, ctx_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close app */
+       ret = mi_lttng_writer_close_element(writer);
+end:
+       return ret;
+}
+
+int mi_lttng_context(struct mi_writer *writer,
+               struct lttng_event_context *context, int is_open)
+{
+       int ret;
+
+       /* Open context */
+       ret = mi_lttng_writer_open_element(writer , config_element_context);
+       if (ret) {
+               goto end;
+       }
+
+       /* Special case for PERF_*_COUNTER
+        * print the lttng_event_perf_counter_ctx*/
+       switch (context->ctx) {
+       case LTTNG_EVENT_CONTEXT_PERF_COUNTER:
+       case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER:
+       case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER:
+       {
+               struct lttng_event_perf_counter_ctx *perf_context =
+                               &context->u.perf_counter;
+               ret =  mi_lttng_perf_counter_context(writer, perf_context);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       }
+       case LTTNG_EVENT_CONTEXT_APP_CONTEXT:
+       {
+               ret = mi_lttng_app_context(writer,
+                               context->u.app_ctx.provider_name,
+                               context->u.app_ctx.ctx_name);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       }
+       default:
+       {
+               const char *type_string = mi_lttng_event_contexttype_string(
+                               context->ctx);
+               if (!type_string) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+
+               /* Print context type */
+               ret = mi_lttng_writer_write_element_string(writer,
+                               config_element_type, type_string);
+               break;
+       }
+       }
+
+       /* Close context */
+       if (!is_open) {
+               ret = mi_lttng_writer_close_element(writer);
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_snapshot_output_session_name(struct mi_writer *writer,
+               const char *session_name)
+{
+       int ret;
+
+       /* Open session element */
+       ret = mi_lttng_writer_open_element(writer, config_element_session);
+       if (ret) {
+               goto end;
+       }
+
+       /* Snapshot output list for current session name */
+       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                       session_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Open element snapshots (sequence one snapshot) */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_snapshots);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+int mi_lttng_snapshot_list_output(struct mi_writer *writer,
+               const struct lttng_snapshot_output *output)
+{
+       int ret;
+
+       /* Open element snapshot output */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_command_snapshot);
+       if (ret) {
+               goto end;
+       }
+
+       /* ID of the snapshot output */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_id, output->id);
+       if (ret) {
+               goto end;
+       }
+
+       /* Name of the output */
+       ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                       output->name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Destination of the output (ctrl_url)*/
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
+       if (ret) {
+               goto end;
+       }
+
+       /* Destination of the output (data_url) */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_data_url, output->data_url);
+       if (ret) {
+               goto end;
+       }
+
+       /* total size of all stream combined */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_snapshot_max_size, output->max_size);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close snapshot output element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_snapshot_del_output(struct mi_writer *writer, int id,
+               const char *name, const char *current_session_name)
+{
+       int ret;
+
+       /* Open element del_snapshot */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_command_snapshot);
+       if (ret) {
+               goto end;
+       }
+
+
+       if (id != UINT32_MAX) {
+               /* "Snapshot output "id" successfully deleted
+                * for "current_session_name"
+                * ID of the snapshot output
+                */
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               mi_lttng_element_id, id);
+               if (ret) {
+                       goto end;
+               }
+       } else {
+               /* "Snapshot output "name" successfully deleted
+                * for session "current_session_name"
+                * Name of the output
+                */
+               ret = mi_lttng_writer_write_element_string(writer, config_element_name,
+                               name);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Snapshot was deleted for session "current_session_name"*/
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_session_name,
+                       current_session_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close snapshot element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_snapshot_add_output(struct mi_writer *writer,
+               const char *current_session_name, const char *n_ptr,
+               struct lttng_snapshot_output *output)
+{
+       int ret;
+
+       /* Open element snapshot */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_command_snapshot);
+       if (ret) {
+               goto end;
+       }
+
+       /* Snapshot output id */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_id, output->id);
+       if (ret) {
+               goto end;
+       }
+
+       /* Snapshot output names */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       config_element_name, n_ptr);
+       if (ret) {
+               goto end;
+       }
+
+       /* Destination of the output (ctrl_url)*/
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
+       if (ret) {
+               goto end;
+       }
+
+       /* Snapshot added for session "current_session_name"*/
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_session_name, current_session_name);
+       if (ret) {
+               goto end;
+       }
+
+       /* total size of all stream combined */
+       ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                       mi_lttng_element_snapshot_max_size, output->max_size);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close snapshot element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_snapshot_record(struct mi_writer *writer,
+               const char *current_session_name, const char *url,
+               const char *cmdline_ctrl_url, const char *cmdline_data_url)
+{
+       int ret;
+
+       /* Open element snapshot */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_command_snapshot);
+       if (ret) {
+               goto end;
+       }
+
+       /*
+        * If a valid an URL was given, serialize it,
+        * else take the command line data and ctrl urls*/
+       if (url) {
+               /* Destination of the output (ctrl_url)*/
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_snapshot_ctrl_url, url);
+               if (ret) {
+                       goto end;
+               }
+       } else if (cmdline_ctrl_url) {
+               /* Destination of the output (ctrl_url)*/
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_snapshot_ctrl_url, cmdline_ctrl_url);
+               if (ret) {
+                       goto end;
+               }
+
+               /* Destination of the output (data_url) */
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_snapshot_data_url, cmdline_data_url);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Close record_snapshot element */
+       ret = mi_lttng_writer_close_element(writer);
+
+end:
+       return ret;
+}
+
+int mi_lttng_rotation_schedule(struct mi_writer *writer,
+               const struct lttng_rotation_schedule *schedule)
+{
+       int ret = 0;
+       enum lttng_rotation_status status;
+       uint64_t value;
+       const char *element_name;
+       const char *value_name;
+       bool empty_schedule = false;
+
+       switch (lttng_rotation_schedule_get_type(schedule)) {
+       case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC:
+               status = lttng_rotation_schedule_periodic_get_period(schedule,
+                               &value);
+               element_name = mi_lttng_element_rotation_schedule_periodic;
+               value_name = mi_lttng_element_rotation_schedule_periodic_time_us;
+               break;
+       case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD:
+               status = lttng_rotation_schedule_size_threshold_get_threshold(
+                               schedule, &value);
+               element_name = mi_lttng_element_rotation_schedule_size_threshold;
+               value_name = mi_lttng_element_rotation_schedule_size_threshold_bytes;
+               break;
+       default:
+               ret = -1;
+               goto end;
+       }
+
+       if (status != LTTNG_ROTATION_STATUS_OK) {
+               if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) {
+                       empty_schedule = true;
+               } else {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       ret = mi_lttng_writer_open_element(writer, element_name);
+       if (ret) {
+               goto end;
+       }
+
+       if (!empty_schedule) {
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               value_name, value);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Close schedule descriptor element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+int mi_lttng_rotation_schedule_result(struct mi_writer *writer,
+               const struct lttng_rotation_schedule *schedule,
+               bool success)
+{
+       int ret = 0;
+
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_rotation_schedule_result);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_rotation_schedule);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_rotation_schedule(writer, schedule);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close rotation_schedule element */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_bool(writer,
+                       mi_lttng_element_command_success, success);
+       if (ret) {
+               goto end;
+       }
+
+       /* Close rotation_schedule_result element */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static
+int mi_lttng_location(struct mi_writer *writer,
+               const struct lttng_trace_archive_location *location)
+{
+       int ret = 0;
+       enum lttng_trace_archive_location_type location_type;
+       enum lttng_trace_archive_location_status status;
+
+       location_type = lttng_trace_archive_location_get_type(location);
+
+       switch (location_type) {
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL:
+       {
+               const char *absolute_path;
+
+               status = lttng_trace_archive_location_local_get_absolute_path(
+                               location, &absolute_path);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_open_element(writer,
+                               mi_lttng_element_rotation_location_local);
+               if (ret) {
+                       goto end;
+               }
+
+
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_rotation_location_local_absolute_path,
+                               absolute_path);
+               if (ret) {
+                       goto end;
+               }
+
+               /* Close local element */
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       }
+       case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY:
+       {
+               uint16_t control_port, data_port;
+               const char *host, *relative_path;
+               enum lttng_trace_archive_location_relay_protocol_type protocol;
+
+               /* Fetch all relay location parameters. */
+               status = lttng_trace_archive_location_relay_get_protocol_type(
+                               location, &protocol);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               status = lttng_trace_archive_location_relay_get_host(
+                               location, &host);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               status = lttng_trace_archive_location_relay_get_control_port(
+                               location, &control_port);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               status = lttng_trace_archive_location_relay_get_data_port(
+                               location, &data_port);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               status = lttng_trace_archive_location_relay_get_relative_path(
+                               location, &relative_path);
+               if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_open_element(writer,
+                               mi_lttng_element_rotation_location_relay);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_rotation_location_relay_host,
+                               host);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               mi_lttng_element_rotation_location_relay_control_port,
+                               control_port);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               mi_lttng_element_rotation_location_relay_data_port,
+                               data_port);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_rotation_location_relay_protocol,
+                               mi_lttng_trace_archive_location_relay_protocol_type_string(protocol));
+               if (ret) {
+                       goto end;
+               }
+
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_rotation_location_relay_relative_path,
+                               relative_path);
+               if (ret) {
+                       goto end;
+               }
+
+               /* Close relay element */
+               ret = mi_lttng_writer_close_element(writer);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       }
+       default:
+               abort();
+       }
+end:
+       return ret;
+}
+
+int mi_lttng_rotate(struct mi_writer *writer,
+               const char *session_name,
+               enum lttng_rotation_state rotation_state,
+               const struct lttng_trace_archive_location *location)
+{
+       int ret;
+
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_rotation);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_session_name,
+                       session_name);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_rotation_state,
+                       mi_lttng_rotation_state_string(rotation_state));
+       if (ret) {
+               goto end;
+       }
+
+       if (!location) {
+               /* Not a serialization error. */
+               goto close_rotation;
+       }
+
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_rotation_location);
+       if (ret) {
+               goto end;
+       }
+
+       ret = mi_lttng_location(writer, location);
+       if (ret) {
+               goto close_location;
+       }
+
+close_location:
+       /* Close location element */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+
+close_rotation:
+       /* Close rotation element */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
diff --git a/src/common/notification.c b/src/common/notification.c
deleted file mode 100644 (file)
index d140357..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/notification/notification-internal.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/evaluation-internal.h>
-#include <lttng/condition/condition.h>
-#include <lttng/condition/evaluation.h>
-#include <lttng/trigger/trigger-internal.h>
-#include <common/payload.h>
-#include <common/payload-view.h>
-
-struct lttng_notification *lttng_notification_create(
-               struct lttng_trigger *trigger,
-               struct lttng_evaluation *evaluation)
-{
-       struct lttng_notification *notification = NULL;
-
-       if (!trigger || !evaluation) {
-               goto end;
-       }
-
-       notification = zmalloc(sizeof(struct lttng_notification));
-       if (!notification) {
-               goto end;
-       }
-
-       notification->trigger = trigger;
-       notification->evaluation = evaluation;
-end:
-       return notification;
-}
-
-int lttng_notification_serialize(const struct lttng_notification *notification,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t header_offset, size_before_payload;
-       struct lttng_notification_comm notification_comm = { 0 };
-       struct lttng_notification_comm *header;
-
-       header_offset = payload->buffer.size;
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &notification_comm,
-                       sizeof(notification_comm));
-       if (ret) {
-               goto end;
-       }
-
-       size_before_payload = payload->buffer.size;
-       ret = lttng_trigger_serialize(notification->trigger,
-                       payload);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_evaluation_serialize(notification->evaluation, payload);
-       if (ret) {
-               goto end;
-       }
-
-       /* Update payload size. */
-       header = (typeof(header)) (payload->buffer.data + header_offset);
-       header->length = (uint32_t) (payload->buffer.size - size_before_payload);
-end:
-       return ret;
-
-}
-
-ssize_t lttng_notification_create_from_payload(
-               struct lttng_payload_view *src_view,
-               struct lttng_notification **notification)
-{
-       ssize_t ret, notification_size = 0, trigger_size, evaluation_size;
-       struct lttng_trigger *trigger = NULL;
-       struct lttng_evaluation *evaluation = NULL;
-       const struct lttng_notification_comm *notification_comm;
-       const struct lttng_payload_view notification_comm_view =
-                       lttng_payload_view_from_view(
-                                       src_view, 0, sizeof(*notification_comm));
-
-       if (!src_view || !notification) {
-               ret = -1;
-               goto error;
-       }
-
-       if (!lttng_payload_view_is_valid(&notification_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               ret = -1;
-               goto error;
-       }
-
-       notification_comm = (typeof(notification_comm)) notification_comm_view.buffer.data;
-       notification_size += sizeof(*notification_comm);
-       {
-               /* struct lttng_condition */
-               struct lttng_payload_view condition_view =
-                               lttng_payload_view_from_view(src_view,
-                                               notification_size, -1);
-
-               trigger_size = lttng_trigger_create_from_payload(
-                               &condition_view, &trigger);
-       }
-
-       if (trigger_size < 0) {
-               ret = trigger_size;
-               goto error;
-       }
-
-       notification_size += trigger_size;
-
-       {
-               /* struct lttng_evaluation */
-               struct lttng_payload_view evaluation_view =
-                               lttng_payload_view_from_view(src_view,
-                                               notification_size, -1);
-
-               evaluation_size = lttng_evaluation_create_from_payload(
-                               lttng_trigger_get_const_condition(trigger),
-                               &evaluation_view, &evaluation);
-       }
-
-       if (evaluation_size < 0) {
-               ret = evaluation_size;
-               goto error;
-       }
-
-       notification_size += evaluation_size;
-
-       /* Unexpected size of inner-elements; the buffer is corrupted. */
-       if ((ssize_t) notification_comm->length !=
-                       trigger_size + evaluation_size) {
-               ret = -1;
-               goto error;
-       }
-
-       *notification = lttng_notification_create(trigger, evaluation);
-       if (!*notification) {
-               ret = -1;
-               goto error;
-       }
-
-       ret = notification_size;
-       return ret;
-
-error:
-       lttng_trigger_destroy(trigger);
-       lttng_evaluation_destroy(evaluation);
-       return ret;
-}
-
-void lttng_notification_destroy(struct lttng_notification *notification)
-{
-       if (!notification) {
-               return;
-       }
-
-       lttng_trigger_destroy(notification->trigger);
-       lttng_evaluation_destroy(notification->evaluation);
-       free(notification);
-}
-
-const struct lttng_condition *lttng_notification_get_condition(
-               struct lttng_notification *notification)
-{
-       return notification ? lttng_trigger_get_const_condition(notification->trigger) : NULL;
-}
-
-const struct lttng_evaluation *lttng_notification_get_evaluation(
-               struct lttng_notification *notification)
-{
-       return notification ? notification->evaluation : NULL;
-}
-
-const struct lttng_trigger *lttng_notification_get_trigger(
-               struct lttng_notification *notification)
-{
-       return notification ? notification->trigger : NULL;
-}
diff --git a/src/common/notification.cpp b/src/common/notification.cpp
new file mode 100644 (file)
index 0000000..989c20a
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/notification/notification-internal.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/evaluation-internal.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/evaluation.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <common/payload.h>
+#include <common/payload-view.h>
+
+struct lttng_notification *lttng_notification_create(
+               struct lttng_trigger *trigger,
+               struct lttng_evaluation *evaluation)
+{
+       struct lttng_notification *notification = NULL;
+
+       if (!trigger || !evaluation) {
+               goto end;
+       }
+
+       notification = (lttng_notification *) zmalloc(sizeof(struct lttng_notification));
+       if (!notification) {
+               goto end;
+       }
+
+       notification->trigger = trigger;
+       notification->evaluation = evaluation;
+end:
+       return notification;
+}
+
+int lttng_notification_serialize(const struct lttng_notification *notification,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t header_offset, size_before_payload;
+       struct lttng_notification_comm notification_comm = { 0 };
+       struct lttng_notification_comm *header;
+
+       header_offset = payload->buffer.size;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &notification_comm,
+                       sizeof(notification_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = payload->buffer.size;
+       ret = lttng_trigger_serialize(notification->trigger,
+                       payload);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_evaluation_serialize(notification->evaluation, payload);
+       if (ret) {
+               goto end;
+       }
+
+       /* Update payload size. */
+       header = (typeof(header)) (payload->buffer.data + header_offset);
+       header->length = (uint32_t) (payload->buffer.size - size_before_payload);
+end:
+       return ret;
+
+}
+
+ssize_t lttng_notification_create_from_payload(
+               struct lttng_payload_view *src_view,
+               struct lttng_notification **notification)
+{
+       ssize_t ret, notification_size = 0, trigger_size, evaluation_size;
+       struct lttng_trigger *trigger = NULL;
+       struct lttng_evaluation *evaluation = NULL;
+       const struct lttng_notification_comm *notification_comm;
+       const struct lttng_payload_view notification_comm_view =
+                       lttng_payload_view_from_view(
+                                       src_view, 0, sizeof(*notification_comm));
+
+       if (!src_view || !notification) {
+               ret = -1;
+               goto error;
+       }
+
+       if (!lttng_payload_view_is_valid(&notification_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               ret = -1;
+               goto error;
+       }
+
+       notification_comm = (typeof(notification_comm)) notification_comm_view.buffer.data;
+       notification_size += sizeof(*notification_comm);
+       {
+               /* struct lttng_condition */
+               struct lttng_payload_view condition_view =
+                               lttng_payload_view_from_view(src_view,
+                                               notification_size, -1);
+
+               trigger_size = lttng_trigger_create_from_payload(
+                               &condition_view, &trigger);
+       }
+
+       if (trigger_size < 0) {
+               ret = trigger_size;
+               goto error;
+       }
+
+       notification_size += trigger_size;
+
+       {
+               /* struct lttng_evaluation */
+               struct lttng_payload_view evaluation_view =
+                               lttng_payload_view_from_view(src_view,
+                                               notification_size, -1);
+
+               evaluation_size = lttng_evaluation_create_from_payload(
+                               lttng_trigger_get_const_condition(trigger),
+                               &evaluation_view, &evaluation);
+       }
+
+       if (evaluation_size < 0) {
+               ret = evaluation_size;
+               goto error;
+       }
+
+       notification_size += evaluation_size;
+
+       /* Unexpected size of inner-elements; the buffer is corrupted. */
+       if ((ssize_t) notification_comm->length !=
+                       trigger_size + evaluation_size) {
+               ret = -1;
+               goto error;
+       }
+
+       *notification = lttng_notification_create(trigger, evaluation);
+       if (!*notification) {
+               ret = -1;
+               goto error;
+       }
+
+       ret = notification_size;
+       return ret;
+
+error:
+       lttng_trigger_destroy(trigger);
+       lttng_evaluation_destroy(evaluation);
+       return ret;
+}
+
+void lttng_notification_destroy(struct lttng_notification *notification)
+{
+       if (!notification) {
+               return;
+       }
+
+       lttng_trigger_destroy(notification->trigger);
+       lttng_evaluation_destroy(notification->evaluation);
+       free(notification);
+}
+
+const struct lttng_condition *lttng_notification_get_condition(
+               struct lttng_notification *notification)
+{
+       return notification ? lttng_trigger_get_const_condition(notification->trigger) : NULL;
+}
+
+const struct lttng_evaluation *lttng_notification_get_evaluation(
+               struct lttng_notification *notification)
+{
+       return notification ? notification->evaluation : NULL;
+}
+
+const struct lttng_trigger *lttng_notification_get_trigger(
+               struct lttng_notification *notification)
+{
+       return notification ? notification->trigger : NULL;
+}
diff --git a/src/common/payload-view.c b/src/common/payload-view.c
deleted file mode 100644 (file)
index 2e6581b..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/dynamic-array.h>
-#include <common/buffer-view.h>
-#include "payload-view.h"
-#include "payload.h"
-#include <stddef.h>
-
-bool lttng_payload_view_is_valid(const struct lttng_payload_view *view)
-{
-       return view && lttng_buffer_view_is_valid(&view->buffer);
-}
-
-struct lttng_payload_view lttng_payload_view_from_payload(
-               const struct lttng_payload *payload, size_t offset,
-               ptrdiff_t len)
-{
-       return payload ? (struct lttng_payload_view) {
-               .buffer = lttng_buffer_view_from_dynamic_buffer(
-                       &payload->buffer, offset, len),
-               ._fd_handles = payload->_fd_handles,
-               } : (struct lttng_payload_view) {};
-}
-
-struct lttng_payload_view lttng_payload_view_from_view(
-               struct lttng_payload_view *view, size_t offset,
-               ptrdiff_t len)
-{
-       return view ? (struct lttng_payload_view) {
-               .buffer = lttng_buffer_view_from_view(
-                               &view->buffer, offset, len),
-               ._fd_handles = view->_fd_handles,
-               ._iterator.p_fd_handles_position = view->_iterator.p_fd_handles_position ?:
-                               &view->_iterator.fd_handles_position,
-               } : (struct lttng_payload_view) {};
-}
-
-struct lttng_payload_view lttng_payload_view_from_dynamic_buffer(
-               const struct lttng_dynamic_buffer *buffer, size_t offset,
-               ptrdiff_t len)
-{
-       return buffer ? (struct lttng_payload_view) {
-               .buffer = lttng_buffer_view_from_dynamic_buffer(
-                       buffer, offset, len)
-               } : (struct lttng_payload_view) {};
-}
-
-struct lttng_payload_view lttng_payload_view_from_buffer_view(
-               const struct lttng_buffer_view *view, size_t offset,
-               ptrdiff_t len)
-{
-       return view ? (struct lttng_payload_view) {
-               .buffer = lttng_buffer_view_from_view(
-                       view, offset, len)
-               } : (struct lttng_payload_view) {};
-}
-
-struct lttng_payload_view lttng_payload_view_init_from_buffer(
-       const char *src, size_t offset, ptrdiff_t len)
-{
-       return (struct lttng_payload_view) {
-               .buffer = lttng_buffer_view_init(
-                       src, offset, len)
-       };
-}
-
-int lttng_payload_view_get_fd_handle_count(
-               const struct lttng_payload_view *payload_view)
-{
-       int ret;
-       size_t position;
-
-       if (!payload_view) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_pointer_array_get_count(&payload_view->_fd_handles);
-       if (ret < 0) {
-               goto end;
-       }
-
-       position = payload_view->_iterator.p_fd_handles_position ?
-                       *payload_view->_iterator.p_fd_handles_position :
-                       payload_view->_iterator.fd_handles_position;
-       ret = ret - (int) position;
-end:
-       return ret;
-}
-
-struct fd_handle *lttng_payload_view_pop_fd_handle(
-               struct lttng_payload_view *view)
-{
-       struct fd_handle *handle = NULL;
-       size_t fd_handle_count;
-       size_t *pos;
-
-       if (!view) {
-               goto end;
-       }
-
-       fd_handle_count = lttng_payload_view_get_fd_handle_count(view);
-       if (fd_handle_count == 0) {
-               goto end;
-       }
-
-       pos = view->_iterator.p_fd_handles_position ?
-                       view->_iterator.p_fd_handles_position :
-                       &view->_iterator.fd_handles_position;
-       handle = lttng_dynamic_pointer_array_get_pointer(&view->_fd_handles,
-                       *pos);
-       (*pos)++;
-       fd_handle_get(handle);
-end:
-       return handle;
-}
diff --git a/src/common/payload-view.cpp b/src/common/payload-view.cpp
new file mode 100644 (file)
index 0000000..b1a4436
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/dynamic-array.h>
+#include <common/buffer-view.h>
+#include "payload-view.h"
+#include "payload.h"
+#include <stddef.h>
+
+bool lttng_payload_view_is_valid(const struct lttng_payload_view *view)
+{
+       return view && lttng_buffer_view_is_valid(&view->buffer);
+}
+
+struct lttng_payload_view lttng_payload_view_from_payload(
+               const struct lttng_payload *payload, size_t offset,
+               ptrdiff_t len)
+{
+       return payload ? (struct lttng_payload_view) {
+               .buffer = lttng_buffer_view_from_dynamic_buffer(
+                       &payload->buffer, offset, len),
+               ._fd_handles = payload->_fd_handles,
+               } : (struct lttng_payload_view) {};
+}
+
+struct lttng_payload_view lttng_payload_view_from_view(
+               struct lttng_payload_view *view, size_t offset,
+               ptrdiff_t len)
+{
+       return view ? (struct lttng_payload_view) {
+               .buffer = lttng_buffer_view_from_view(
+                               &view->buffer, offset, len),
+               ._fd_handles = view->_fd_handles,
+               ._iterator = {
+                       .p_fd_handles_position = view->_iterator.p_fd_handles_position ?:
+                               &view->_iterator.fd_handles_position,
+               }
+       } : (struct lttng_payload_view) {};
+}
+
+struct lttng_payload_view lttng_payload_view_from_dynamic_buffer(
+               const struct lttng_dynamic_buffer *buffer, size_t offset,
+               ptrdiff_t len)
+{
+       return buffer ? (struct lttng_payload_view) {
+               .buffer = lttng_buffer_view_from_dynamic_buffer(
+                       buffer, offset, len)
+               } : (struct lttng_payload_view) {};
+}
+
+struct lttng_payload_view lttng_payload_view_from_buffer_view(
+               const struct lttng_buffer_view *view, size_t offset,
+               ptrdiff_t len)
+{
+       return view ? (struct lttng_payload_view) {
+               .buffer = lttng_buffer_view_from_view(
+                       view, offset, len)
+               } : (struct lttng_payload_view) {};
+}
+
+struct lttng_payload_view lttng_payload_view_init_from_buffer(
+       const char *src, size_t offset, ptrdiff_t len)
+{
+       return (struct lttng_payload_view) {
+               .buffer = lttng_buffer_view_init(
+                       src, offset, len)
+       };
+}
+
+int lttng_payload_view_get_fd_handle_count(
+               const struct lttng_payload_view *payload_view)
+{
+       int ret;
+       size_t position;
+
+       if (!payload_view) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_get_count(&payload_view->_fd_handles);
+       if (ret < 0) {
+               goto end;
+       }
+
+       position = payload_view->_iterator.p_fd_handles_position ?
+                       *payload_view->_iterator.p_fd_handles_position :
+                       payload_view->_iterator.fd_handles_position;
+       ret = ret - (int) position;
+end:
+       return ret;
+}
+
+struct fd_handle *lttng_payload_view_pop_fd_handle(
+               struct lttng_payload_view *view)
+{
+       struct fd_handle *handle = NULL;
+       size_t fd_handle_count;
+       size_t *pos;
+
+       if (!view) {
+               goto end;
+       }
+
+       fd_handle_count = lttng_payload_view_get_fd_handle_count(view);
+       if (fd_handle_count == 0) {
+               goto end;
+       }
+
+       pos = view->_iterator.p_fd_handles_position ?
+                       view->_iterator.p_fd_handles_position :
+                       &view->_iterator.fd_handles_position;
+       handle = (fd_handle *) lttng_dynamic_pointer_array_get_pointer(&view->_fd_handles,
+                       *pos);
+       (*pos)++;
+       fd_handle_get(handle);
+end:
+       return handle;
+}
diff --git a/src/common/payload.c b/src/common/payload.c
deleted file mode 100644 (file)
index f26585b..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "payload.h"
-#include <common/dynamic-array.h>
-#include <common/dynamic-buffer.h>
-#include <common/error.h>
-
-static
-void release_fd_handle_ref(void *ptr)
-{
-       struct fd_handle *fd_handle = ptr;
-
-       fd_handle_put(fd_handle);
-}
-
-void lttng_payload_init(struct lttng_payload *payload)
-{
-       LTTNG_ASSERT(payload);
-       lttng_dynamic_buffer_init(&payload->buffer);
-       lttng_dynamic_pointer_array_init(&payload->_fd_handles,
-                       release_fd_handle_ref);
-}
-
-int lttng_payload_copy(const struct lttng_payload *src_payload,
-                      struct lttng_payload *dst_payload)
-{
-       int ret;
-       size_t i;
-
-       ret = lttng_dynamic_buffer_append_buffer(
-                       &dst_payload->buffer, &src_payload->buffer);
-       if (ret) {
-               goto end;
-       }
-
-       for (i = 0; i < lttng_dynamic_pointer_array_get_count(
-                                       &src_payload->_fd_handles);
-                       i++) {
-               struct fd_handle *new_fd_handle;
-               const struct fd_handle *src_fd_handle =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                               &src_payload->_fd_handles, i);
-
-               new_fd_handle = fd_handle_copy(src_fd_handle);
-               if (!new_fd_handle) {
-                       PERROR("Failed to copy fd_handle while copying a payload");
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = lttng_payload_push_fd_handle(dst_payload, new_fd_handle);
-               fd_handle_put(new_fd_handle);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-void lttng_payload_reset(struct lttng_payload *payload)
-{
-       if (!payload) {
-               return;
-       }
-
-       lttng_dynamic_buffer_reset(&payload->buffer);
-       lttng_dynamic_pointer_array_reset(&payload->_fd_handles);
-}
-
-void lttng_payload_clear(struct lttng_payload *payload)
-{
-       (void) lttng_dynamic_buffer_set_size(&payload->buffer, 0);
-       lttng_dynamic_pointer_array_clear(&payload->_fd_handles);
-}
-
-int lttng_payload_push_fd_handle(struct lttng_payload *payload,
-               struct fd_handle *fd_handle)
-{
-       int ret;
-
-       if (!payload) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_dynamic_pointer_array_add_pointer(
-                       &payload->_fd_handles, fd_handle);
-       if (ret) {
-               goto end;
-       }
-
-       fd_handle_get(fd_handle);
-end:
-       return ret;
-}
diff --git a/src/common/payload.cpp b/src/common/payload.cpp
new file mode 100644 (file)
index 0000000..0097e65
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "payload.h"
+#include <common/dynamic-array.h>
+#include <common/dynamic-buffer.h>
+#include <common/error.h>
+
+static
+void release_fd_handle_ref(void *ptr)
+{
+       struct fd_handle *fd_handle = (struct fd_handle *) ptr;
+
+       fd_handle_put(fd_handle);
+}
+
+void lttng_payload_init(struct lttng_payload *payload)
+{
+       LTTNG_ASSERT(payload);
+       lttng_dynamic_buffer_init(&payload->buffer);
+       lttng_dynamic_pointer_array_init(&payload->_fd_handles,
+                       release_fd_handle_ref);
+}
+
+int lttng_payload_copy(const struct lttng_payload *src_payload,
+                      struct lttng_payload *dst_payload)
+{
+       int ret;
+       size_t i;
+
+       ret = lttng_dynamic_buffer_append_buffer(
+                       &dst_payload->buffer, &src_payload->buffer);
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < lttng_dynamic_pointer_array_get_count(
+                                       &src_payload->_fd_handles);
+                       i++) {
+               struct fd_handle *new_fd_handle;
+               const struct fd_handle *src_fd_handle =
+                               (fd_handle *) lttng_dynamic_pointer_array_get_pointer(
+                                               &src_payload->_fd_handles, i);
+
+               new_fd_handle = fd_handle_copy(src_fd_handle);
+               if (!new_fd_handle) {
+                       PERROR("Failed to copy fd_handle while copying a payload");
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = lttng_payload_push_fd_handle(dst_payload, new_fd_handle);
+               fd_handle_put(new_fd_handle);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+void lttng_payload_reset(struct lttng_payload *payload)
+{
+       if (!payload) {
+               return;
+       }
+
+       lttng_dynamic_buffer_reset(&payload->buffer);
+       lttng_dynamic_pointer_array_reset(&payload->_fd_handles);
+}
+
+void lttng_payload_clear(struct lttng_payload *payload)
+{
+       (void) lttng_dynamic_buffer_set_size(&payload->buffer, 0);
+       lttng_dynamic_pointer_array_clear(&payload->_fd_handles);
+}
+
+int lttng_payload_push_fd_handle(struct lttng_payload *payload,
+               struct fd_handle *fd_handle)
+{
+       int ret;
+
+       if (!payload) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &payload->_fd_handles, fd_handle);
+       if (ret) {
+               goto end;
+       }
+
+       fd_handle_get(fd_handle);
+end:
+       return ret;
+}
diff --git a/src/common/pipe.c b/src/common/pipe.c
deleted file mode 100644 (file)
index 45be43f..0000000
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <common/common.h>
-
-#include "pipe.h"
-
-/*
- * Lock read side of a pipe.
- */
-static void lock_read_side(struct lttng_pipe *pipe)
-{
-       pthread_mutex_lock(&pipe->read_mutex);
-}
-
-/*
- * Unlock read side of a pipe.
- */
-static void unlock_read_side(struct lttng_pipe *pipe)
-{
-       pthread_mutex_unlock(&pipe->read_mutex);
-}
-
-/*
- * Lock write side of a pipe.
- */
-static void lock_write_side(struct lttng_pipe *pipe)
-{
-       pthread_mutex_lock(&pipe->write_mutex);
-}
-
-/*
- * Unlock write side of a pipe.
- */
-static void unlock_write_side(struct lttng_pipe *pipe)
-{
-       pthread_mutex_unlock(&pipe->write_mutex);
-}
-
-/*
- * Internal function. Close read side of pipe WITHOUT locking the mutex.
- *
- * Return 0 on success else a negative errno from close(2).
- */
-static int _pipe_read_close(struct lttng_pipe *pipe)
-{
-       int ret, ret_val = 0;
-
-       LTTNG_ASSERT(pipe);
-
-       if (!lttng_pipe_is_read_open(pipe)) {
-               goto end;
-       }
-
-       do {
-               ret = close(pipe->fd[0]);
-       } while (ret < 0 && errno == EINTR);
-       if (ret < 0) {
-               PERROR("close lttng read pipe");
-               ret_val = -errno;
-       }
-       pipe->r_state = LTTNG_PIPE_STATE_CLOSED;
-
-end:
-       return ret_val;
-}
-
-/*
- * Internal function. Close write side of pipe WITHOUT locking the mutex.
- *
- * Return 0 on success else a negative errno from close(2).
- */
-static int _pipe_write_close(struct lttng_pipe *pipe)
-{
-       int ret, ret_val = 0;
-
-       LTTNG_ASSERT(pipe);
-
-       if (!lttng_pipe_is_write_open(pipe)) {
-               goto end;
-       }
-
-       do {
-               ret = close(pipe->fd[1]);
-       } while (ret < 0 && errno == EINTR);
-       if (ret < 0) {
-               PERROR("close lttng write pipe");
-               ret_val = -errno;
-       }
-       pipe->w_state = LTTNG_PIPE_STATE_CLOSED;
-
-end:
-       return ret_val;
-}
-
-static struct lttng_pipe *_pipe_create(void)
-{
-       int ret;
-       struct lttng_pipe *p;
-
-       p = zmalloc(sizeof(*p));
-       if (!p) {
-               PERROR("zmalloc pipe create");
-               goto end;
-       }
-       p->fd[0] = p->fd[1] = -1;
-
-       ret = pthread_mutex_init(&p->read_mutex, NULL);
-       if (ret) {
-               PERROR("pthread_mutex_init read lock pipe create");
-               goto error_destroy;
-       }
-       ret = pthread_mutex_init(&p->write_mutex, NULL);
-       if (ret) {
-               PERROR("pthread_mutex_init write lock pipe create");
-               goto error_destroy_rmutex;
-       }
-end:
-       return p;
-error_destroy_rmutex:
-       (void) pthread_mutex_destroy(&p->read_mutex);
-error_destroy:
-       free(p);
-       return NULL;
-}
-
-static int _pipe_set_flags(struct lttng_pipe *pipe, int flags)
-{
-       int i, ret = 0;
-
-       if (!flags) {
-               goto end;
-       }
-
-       for (i = 0; i < 2; i++) {
-               if (flags & O_NONBLOCK) {
-                       ret = fcntl(pipe->fd[i], F_SETFL, O_NONBLOCK);
-                       if (ret < 0) {
-                               PERROR("fcntl lttng pipe %d", flags);
-                               goto end;
-                       }
-               }
-               if (flags & FD_CLOEXEC) {
-                       ret = fcntl(pipe->fd[i], F_SETFD, FD_CLOEXEC);
-                       if (ret < 0) {
-                               PERROR("fcntl lttng pipe %d", flags);
-                               goto end;
-                       }
-               }
-               /*
-                * We only check for O_NONBLOCK or FD_CLOEXEC, if another flag is
-                * needed, we can add it, but for now just make sure we don't make
-                * mistakes with the parameters we pass.
-                */
-               if (!(flags & O_NONBLOCK) && !(flags & FD_CLOEXEC)) {
-                       fprintf(stderr, "Unsupported flag\n");
-                       ret = -1;
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-/*
- * Open a new lttng pipe and set flags using fcntl().
- *
- * Return a newly allocated lttng pipe on success or else NULL.
- */
-struct lttng_pipe *lttng_pipe_open(int flags)
-{
-       int ret;
-       struct lttng_pipe *p;
-
-       p = _pipe_create();
-       if (!p) {
-               goto error;
-       }
-
-       ret = pipe(p->fd);
-       if (ret < 0) {
-               PERROR("lttng pipe");
-               goto error;
-       }
-       p->r_state = LTTNG_PIPE_STATE_OPENED;
-       p->w_state = LTTNG_PIPE_STATE_OPENED;
-
-       ret = _pipe_set_flags(p, flags);
-       if (ret) {
-               goto error;
-       }
-
-       p->flags = flags;
-
-       return p;
-error:
-       lttng_pipe_destroy(p);
-       return NULL;
-}
-
-/*
- * Open a new lttng pipe at path and set flags using fcntl().
- *
- * Return a newly allocated lttng pipe on success or else NULL.
- */
-struct lttng_pipe *lttng_pipe_named_open(const char *path, mode_t mode,
-               int flags)
-{
-       int ret, fd_r, fd_w;
-       struct lttng_pipe *pipe;
-
-       pipe = _pipe_create();
-       if (!pipe) {
-               goto error;
-       }
-
-       ret = mkfifo(path, mode);
-       if (ret) {
-               PERROR("mkfifo");
-               goto error;
-       }
-
-       fd_r = open(path, O_RDONLY | O_NONBLOCK);
-       if (fd_r < 0) {
-               PERROR("open fifo");
-               goto error;
-       }
-       pipe->fd[0] = fd_r;
-       pipe->r_state = LTTNG_PIPE_STATE_OPENED;
-
-       fd_w = open(path, O_WRONLY | O_NONBLOCK);
-       if (fd_w < 0) {
-               PERROR("open fifo");
-               goto error;
-       }
-       pipe->fd[1] = fd_w;
-       pipe->w_state = LTTNG_PIPE_STATE_OPENED;
-
-       ret = _pipe_set_flags(pipe, flags);
-       if (ret) {
-               goto error;
-       }
-       pipe->flags = flags;
-
-       return pipe;
-error:
-       lttng_pipe_destroy(pipe);
-       return NULL;
-}
-
-/*
- * Close read side of a lttng pipe.
- *
- * Return 0 on success else a negative value.
- */
-int lttng_pipe_read_close(struct lttng_pipe *pipe)
-{
-       int ret;
-
-       LTTNG_ASSERT(pipe);
-
-       /* Handle read side first. */
-       lock_read_side(pipe);
-       ret = _pipe_read_close(pipe);
-       unlock_read_side(pipe);
-
-       return ret;
-}
-
-/*
- * Close write side of a lttng pipe.
- *
- * Return 0 on success else a negative value.
- */
-int lttng_pipe_write_close(struct lttng_pipe *pipe)
-{
-       int ret;
-
-       LTTNG_ASSERT(pipe);
-
-       lock_write_side(pipe);
-       ret = _pipe_write_close(pipe);
-       unlock_write_side(pipe);
-
-       return ret;
-}
-
-/*
- * Close both read and write side of a lttng pipe.
- *
- * Return 0 on success else a negative value.
- */
-int lttng_pipe_close(struct lttng_pipe *pipe)
-{
-       int ret, ret_val = 0;
-
-       LTTNG_ASSERT(pipe);
-
-       ret = lttng_pipe_read_close(pipe);
-       if (ret < 0) {
-               ret_val = ret;
-       }
-
-       ret = lttng_pipe_write_close(pipe);
-       if (ret < 0) {
-               ret_val = ret;
-       }
-
-       return ret_val;
-}
-
-/*
- * Close and destroy a lttng pipe object. Finally, pipe is freed.
- */
-void lttng_pipe_destroy(struct lttng_pipe *pipe)
-{
-       int ret;
-
-       if (!pipe) {
-               return;
-       }
-
-       /*
-        * Destroy should *never* be called with a locked mutex. These must always
-        * succeed so we unlock them after the close pipe below.
-        */
-       ret = pthread_mutex_trylock(&pipe->read_mutex);
-       LTTNG_ASSERT(!ret);
-       ret = pthread_mutex_trylock(&pipe->write_mutex);
-       LTTNG_ASSERT(!ret);
-
-       /* Close pipes WITHOUT trying to lock the pipes. */
-       (void) _pipe_read_close(pipe);
-       (void) _pipe_write_close(pipe);
-
-       unlock_read_side(pipe);
-       unlock_write_side(pipe);
-
-       (void) pthread_mutex_destroy(&pipe->read_mutex);
-       (void) pthread_mutex_destroy(&pipe->write_mutex);
-
-       free(pipe);
-}
-
-/*
- * Read on a lttng pipe and put the data in buf of at least size count.
- *
- * Return "count" on success. Return < count on error. errno can be used
- * to check the actual error.
- */
-ssize_t lttng_pipe_read(struct lttng_pipe *pipe, void *buf, size_t count)
-{
-       ssize_t ret;
-
-       LTTNG_ASSERT(pipe);
-       LTTNG_ASSERT(buf);
-
-       lock_read_side(pipe);
-       if (!lttng_pipe_is_read_open(pipe)) {
-               ret = -1;
-               errno = EBADF;
-               goto error;
-       }
-       ret = lttng_read(pipe->fd[0], buf, count);
-error:
-       unlock_read_side(pipe);
-       return ret;
-}
-
-/*
- * Write on a lttng pipe using the data in buf and size of count.
- *
- * Return "count" on success. Return < count on error. errno can be used
- * to check the actual error.
- */
-ssize_t lttng_pipe_write(struct lttng_pipe *pipe, const void *buf,
-               size_t count)
-{
-       ssize_t ret;
-
-       LTTNG_ASSERT(pipe);
-       LTTNG_ASSERT(buf);
-
-       lock_write_side(pipe);
-       if (!lttng_pipe_is_write_open(pipe)) {
-               ret = -1;
-               errno = EBADF;
-               goto error;
-       }
-       ret = lttng_write(pipe->fd[1], buf, count);
-error:
-       unlock_write_side(pipe);
-       return ret;
-}
-
-/*
- * Return and release the read end of the pipe.
- *
- * This call transfers the ownership of the read fd of the underlying pipe
- * to the caller if it is still open.
- *
- * Returns the fd of the read end of the pipe, or -1 if it was already closed or
- * released.
- */
-int lttng_pipe_release_readfd(struct lttng_pipe *pipe)
-{
-       int ret;
-
-       if (!pipe) {
-               ret = -1;
-               goto end;
-       }
-
-       lock_read_side(pipe);
-       if (!lttng_pipe_is_read_open(pipe)) {
-               ret = -1;
-               goto end_unlock;
-       }
-       ret = pipe->fd[0];
-       pipe->fd[0] = -1;
-       pipe->r_state = LTTNG_PIPE_STATE_CLOSED;
-end_unlock:
-       unlock_read_side(pipe);
-end:
-       return ret;
-}
-
-/*
- * Return and release the write end of the pipe.
- *
- * This call transfers the ownership of the write fd of the underlying pipe
- * to the caller if it is still open.
- *
- * Returns the fd of the write end of the pipe, or -1 if it was alwritey closed
- * or released.
- */
-int lttng_pipe_release_writefd(struct lttng_pipe *pipe)
-{
-       int ret;
-
-       if (!pipe) {
-               ret = -1;
-               goto end;
-       }
-
-       lock_write_side(pipe);
-       if (!lttng_pipe_is_write_open(pipe)) {
-               ret = -1;
-               goto end_unlock;
-       }
-       ret = pipe->fd[1];
-       pipe->fd[1] = -1;
-       pipe->w_state = LTTNG_PIPE_STATE_CLOSED;
-end_unlock:
-       unlock_write_side(pipe);
-end:
-       return ret;
-}
diff --git a/src/common/pipe.cpp b/src/common/pipe.cpp
new file mode 100644 (file)
index 0000000..2de91f5
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <common/common.h>
+
+#include "pipe.h"
+
+/*
+ * Lock read side of a pipe.
+ */
+static void lock_read_side(struct lttng_pipe *pipe)
+{
+       pthread_mutex_lock(&pipe->read_mutex);
+}
+
+/*
+ * Unlock read side of a pipe.
+ */
+static void unlock_read_side(struct lttng_pipe *pipe)
+{
+       pthread_mutex_unlock(&pipe->read_mutex);
+}
+
+/*
+ * Lock write side of a pipe.
+ */
+static void lock_write_side(struct lttng_pipe *pipe)
+{
+       pthread_mutex_lock(&pipe->write_mutex);
+}
+
+/*
+ * Unlock write side of a pipe.
+ */
+static void unlock_write_side(struct lttng_pipe *pipe)
+{
+       pthread_mutex_unlock(&pipe->write_mutex);
+}
+
+/*
+ * Internal function. Close read side of pipe WITHOUT locking the mutex.
+ *
+ * Return 0 on success else a negative errno from close(2).
+ */
+static int _pipe_read_close(struct lttng_pipe *pipe)
+{
+       int ret, ret_val = 0;
+
+       LTTNG_ASSERT(pipe);
+
+       if (!lttng_pipe_is_read_open(pipe)) {
+               goto end;
+       }
+
+       do {
+               ret = close(pipe->fd[0]);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0) {
+               PERROR("close lttng read pipe");
+               ret_val = -errno;
+       }
+       pipe->r_state = LTTNG_PIPE_STATE_CLOSED;
+
+end:
+       return ret_val;
+}
+
+/*
+ * Internal function. Close write side of pipe WITHOUT locking the mutex.
+ *
+ * Return 0 on success else a negative errno from close(2).
+ */
+static int _pipe_write_close(struct lttng_pipe *pipe)
+{
+       int ret, ret_val = 0;
+
+       LTTNG_ASSERT(pipe);
+
+       if (!lttng_pipe_is_write_open(pipe)) {
+               goto end;
+       }
+
+       do {
+               ret = close(pipe->fd[1]);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0) {
+               PERROR("close lttng write pipe");
+               ret_val = -errno;
+       }
+       pipe->w_state = LTTNG_PIPE_STATE_CLOSED;
+
+end:
+       return ret_val;
+}
+
+static struct lttng_pipe *_pipe_create(void)
+{
+       int ret;
+       struct lttng_pipe *p;
+
+       p = (lttng_pipe *) zmalloc(sizeof(*p));
+       if (!p) {
+               PERROR("zmalloc pipe create");
+               goto end;
+       }
+       p->fd[0] = p->fd[1] = -1;
+
+       ret = pthread_mutex_init(&p->read_mutex, NULL);
+       if (ret) {
+               PERROR("pthread_mutex_init read lock pipe create");
+               goto error_destroy;
+       }
+       ret = pthread_mutex_init(&p->write_mutex, NULL);
+       if (ret) {
+               PERROR("pthread_mutex_init write lock pipe create");
+               goto error_destroy_rmutex;
+       }
+end:
+       return p;
+error_destroy_rmutex:
+       (void) pthread_mutex_destroy(&p->read_mutex);
+error_destroy:
+       free(p);
+       return NULL;
+}
+
+static int _pipe_set_flags(struct lttng_pipe *pipe, int flags)
+{
+       int i, ret = 0;
+
+       if (!flags) {
+               goto end;
+       }
+
+       for (i = 0; i < 2; i++) {
+               if (flags & O_NONBLOCK) {
+                       ret = fcntl(pipe->fd[i], F_SETFL, O_NONBLOCK);
+                       if (ret < 0) {
+                               PERROR("fcntl lttng pipe %d", flags);
+                               goto end;
+                       }
+               }
+               if (flags & FD_CLOEXEC) {
+                       ret = fcntl(pipe->fd[i], F_SETFD, FD_CLOEXEC);
+                       if (ret < 0) {
+                               PERROR("fcntl lttng pipe %d", flags);
+                               goto end;
+                       }
+               }
+               /*
+                * We only check for O_NONBLOCK or FD_CLOEXEC, if another flag is
+                * needed, we can add it, but for now just make sure we don't make
+                * mistakes with the parameters we pass.
+                */
+               if (!(flags & O_NONBLOCK) && !(flags & FD_CLOEXEC)) {
+                       fprintf(stderr, "Unsupported flag\n");
+                       ret = -1;
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+/*
+ * Open a new lttng pipe and set flags using fcntl().
+ *
+ * Return a newly allocated lttng pipe on success or else NULL.
+ */
+struct lttng_pipe *lttng_pipe_open(int flags)
+{
+       int ret;
+       struct lttng_pipe *p;
+
+       p = _pipe_create();
+       if (!p) {
+               goto error;
+       }
+
+       ret = pipe(p->fd);
+       if (ret < 0) {
+               PERROR("lttng pipe");
+               goto error;
+       }
+       p->r_state = LTTNG_PIPE_STATE_OPENED;
+       p->w_state = LTTNG_PIPE_STATE_OPENED;
+
+       ret = _pipe_set_flags(p, flags);
+       if (ret) {
+               goto error;
+       }
+
+       p->flags = flags;
+
+       return p;
+error:
+       lttng_pipe_destroy(p);
+       return NULL;
+}
+
+/*
+ * Open a new lttng pipe at path and set flags using fcntl().
+ *
+ * Return a newly allocated lttng pipe on success or else NULL.
+ */
+struct lttng_pipe *lttng_pipe_named_open(const char *path, mode_t mode,
+               int flags)
+{
+       int ret, fd_r, fd_w;
+       struct lttng_pipe *pipe;
+
+       pipe = _pipe_create();
+       if (!pipe) {
+               goto error;
+       }
+
+       ret = mkfifo(path, mode);
+       if (ret) {
+               PERROR("mkfifo");
+               goto error;
+       }
+
+       fd_r = open(path, O_RDONLY | O_NONBLOCK);
+       if (fd_r < 0) {
+               PERROR("open fifo");
+               goto error;
+       }
+       pipe->fd[0] = fd_r;
+       pipe->r_state = LTTNG_PIPE_STATE_OPENED;
+
+       fd_w = open(path, O_WRONLY | O_NONBLOCK);
+       if (fd_w < 0) {
+               PERROR("open fifo");
+               goto error;
+       }
+       pipe->fd[1] = fd_w;
+       pipe->w_state = LTTNG_PIPE_STATE_OPENED;
+
+       ret = _pipe_set_flags(pipe, flags);
+       if (ret) {
+               goto error;
+       }
+       pipe->flags = flags;
+
+       return pipe;
+error:
+       lttng_pipe_destroy(pipe);
+       return NULL;
+}
+
+/*
+ * Close read side of a lttng pipe.
+ *
+ * Return 0 on success else a negative value.
+ */
+int lttng_pipe_read_close(struct lttng_pipe *pipe)
+{
+       int ret;
+
+       LTTNG_ASSERT(pipe);
+
+       /* Handle read side first. */
+       lock_read_side(pipe);
+       ret = _pipe_read_close(pipe);
+       unlock_read_side(pipe);
+
+       return ret;
+}
+
+/*
+ * Close write side of a lttng pipe.
+ *
+ * Return 0 on success else a negative value.
+ */
+int lttng_pipe_write_close(struct lttng_pipe *pipe)
+{
+       int ret;
+
+       LTTNG_ASSERT(pipe);
+
+       lock_write_side(pipe);
+       ret = _pipe_write_close(pipe);
+       unlock_write_side(pipe);
+
+       return ret;
+}
+
+/*
+ * Close both read and write side of a lttng pipe.
+ *
+ * Return 0 on success else a negative value.
+ */
+int lttng_pipe_close(struct lttng_pipe *pipe)
+{
+       int ret, ret_val = 0;
+
+       LTTNG_ASSERT(pipe);
+
+       ret = lttng_pipe_read_close(pipe);
+       if (ret < 0) {
+               ret_val = ret;
+       }
+
+       ret = lttng_pipe_write_close(pipe);
+       if (ret < 0) {
+               ret_val = ret;
+       }
+
+       return ret_val;
+}
+
+/*
+ * Close and destroy a lttng pipe object. Finally, pipe is freed.
+ */
+void lttng_pipe_destroy(struct lttng_pipe *pipe)
+{
+       int ret;
+
+       if (!pipe) {
+               return;
+       }
+
+       /*
+        * Destroy should *never* be called with a locked mutex. These must always
+        * succeed so we unlock them after the close pipe below.
+        */
+       ret = pthread_mutex_trylock(&pipe->read_mutex);
+       LTTNG_ASSERT(!ret);
+       ret = pthread_mutex_trylock(&pipe->write_mutex);
+       LTTNG_ASSERT(!ret);
+
+       /* Close pipes WITHOUT trying to lock the pipes. */
+       (void) _pipe_read_close(pipe);
+       (void) _pipe_write_close(pipe);
+
+       unlock_read_side(pipe);
+       unlock_write_side(pipe);
+
+       (void) pthread_mutex_destroy(&pipe->read_mutex);
+       (void) pthread_mutex_destroy(&pipe->write_mutex);
+
+       free(pipe);
+}
+
+/*
+ * Read on a lttng pipe and put the data in buf of at least size count.
+ *
+ * Return "count" on success. Return < count on error. errno can be used
+ * to check the actual error.
+ */
+ssize_t lttng_pipe_read(struct lttng_pipe *pipe, void *buf, size_t count)
+{
+       ssize_t ret;
+
+       LTTNG_ASSERT(pipe);
+       LTTNG_ASSERT(buf);
+
+       lock_read_side(pipe);
+       if (!lttng_pipe_is_read_open(pipe)) {
+               ret = -1;
+               errno = EBADF;
+               goto error;
+       }
+       ret = lttng_read(pipe->fd[0], buf, count);
+error:
+       unlock_read_side(pipe);
+       return ret;
+}
+
+/*
+ * Write on a lttng pipe using the data in buf and size of count.
+ *
+ * Return "count" on success. Return < count on error. errno can be used
+ * to check the actual error.
+ */
+ssize_t lttng_pipe_write(struct lttng_pipe *pipe, const void *buf,
+               size_t count)
+{
+       ssize_t ret;
+
+       LTTNG_ASSERT(pipe);
+       LTTNG_ASSERT(buf);
+
+       lock_write_side(pipe);
+       if (!lttng_pipe_is_write_open(pipe)) {
+               ret = -1;
+               errno = EBADF;
+               goto error;
+       }
+       ret = lttng_write(pipe->fd[1], buf, count);
+error:
+       unlock_write_side(pipe);
+       return ret;
+}
+
+/*
+ * Return and release the read end of the pipe.
+ *
+ * This call transfers the ownership of the read fd of the underlying pipe
+ * to the caller if it is still open.
+ *
+ * Returns the fd of the read end of the pipe, or -1 if it was already closed or
+ * released.
+ */
+int lttng_pipe_release_readfd(struct lttng_pipe *pipe)
+{
+       int ret;
+
+       if (!pipe) {
+               ret = -1;
+               goto end;
+       }
+
+       lock_read_side(pipe);
+       if (!lttng_pipe_is_read_open(pipe)) {
+               ret = -1;
+               goto end_unlock;
+       }
+       ret = pipe->fd[0];
+       pipe->fd[0] = -1;
+       pipe->r_state = LTTNG_PIPE_STATE_CLOSED;
+end_unlock:
+       unlock_read_side(pipe);
+end:
+       return ret;
+}
+
+/*
+ * Return and release the write end of the pipe.
+ *
+ * This call transfers the ownership of the write fd of the underlying pipe
+ * to the caller if it is still open.
+ *
+ * Returns the fd of the write end of the pipe, or -1 if it was alwritey closed
+ * or released.
+ */
+int lttng_pipe_release_writefd(struct lttng_pipe *pipe)
+{
+       int ret;
+
+       if (!pipe) {
+               ret = -1;
+               goto end;
+       }
+
+       lock_write_side(pipe);
+       if (!lttng_pipe_is_write_open(pipe)) {
+               ret = -1;
+               goto end_unlock;
+       }
+       ret = pipe->fd[1];
+       pipe->fd[1] = -1;
+       pipe->w_state = LTTNG_PIPE_STATE_CLOSED;
+end_unlock:
+       unlock_write_side(pipe);
+end:
+       return ret;
+}
diff --git a/src/common/readwrite.c b/src/common/readwrite.c
deleted file mode 100644 (file)
index 14ec171..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <limits.h>
-#include <unistd.h>
-
-#include <common/compat/errno.h>
-
-#include "readwrite.h"
-
-/*
- * lttng_read and lttng_write take care of EINTR and partial read/write.
- * Upon success, they return the "count" received as parameter.
- * They can return a negative value if an error occurs.
- * If a value lower than the requested "count" is returned, it means an
- * error occurred.
- * The error can be checked by querying errno.
- */
-ssize_t lttng_read(int fd, void *buf, size_t count)
-{
-       size_t i = 0;
-       ssize_t ret;
-
-       LTTNG_ASSERT(buf);
-
-       /*
-        * Deny a read count that can be bigger then the returned value max size.
-        * This makes the function to never return an overflow value.
-        */
-       if (count > SSIZE_MAX) {
-               return -EINVAL;
-       }
-
-       do {
-               ret = read(fd, buf + i, count - i);
-               if (ret < 0) {
-                       if (errno == EINTR) {
-                               continue;       /* retry operation */
-                       } else {
-                               goto error;
-                       }
-               }
-               i += ret;
-               LTTNG_ASSERT(i <= count);
-       } while (count - i > 0 && ret > 0);
-       return i;
-
-error:
-       if (i == 0) {
-               return -1;
-       } else {
-               return i;
-       }
-}
-
-ssize_t lttng_write(int fd, const void *buf, size_t count)
-{
-       size_t i = 0;
-       ssize_t ret;
-
-       LTTNG_ASSERT(buf);
-
-       /*
-        * Deny a write count that can be bigger then the returned value max size.
-        * This makes the function to never return an overflow value.
-        */
-       if (count > SSIZE_MAX) {
-               return -EINVAL;
-       }
-
-       do {
-               ret = write(fd, buf + i, count - i);
-               if (ret < 0) {
-                       if (errno == EINTR) {
-                               continue;       /* retry operation */
-                       } else {
-                               goto error;
-                       }
-               }
-               i += ret;
-               LTTNG_ASSERT(i <= count);
-       } while (count - i > 0 && ret > 0);
-       return i;
-
-error:
-       if (i == 0) {
-               return -1;
-       } else {
-               return i;
-       }
-}
diff --git a/src/common/readwrite.cpp b/src/common/readwrite.cpp
new file mode 100644 (file)
index 0000000..857781c
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <limits.h>
+#include <unistd.h>
+
+#include <common/compat/errno.h>
+
+#include "readwrite.h"
+
+/*
+ * lttng_read and lttng_write take care of EINTR and partial read/write.
+ * Upon success, they return the "count" received as parameter.
+ * They can return a negative value if an error occurs.
+ * If a value lower than the requested "count" is returned, it means an
+ * error occurred.
+ * The error can be checked by querying errno.
+ */
+ssize_t lttng_read(int fd, void *buf, size_t count)
+{
+       size_t i = 0;
+       ssize_t ret;
+
+       LTTNG_ASSERT(buf);
+
+       /*
+        * Deny a read count that can be bigger then the returned value max size.
+        * This makes the function to never return an overflow value.
+        */
+       if (count > SSIZE_MAX) {
+               return -EINVAL;
+       }
+
+       do {
+               ret = read(fd, (char *) buf + i, count - i);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               continue;       /* retry operation */
+                       } else {
+                               goto error;
+                       }
+               }
+               i += ret;
+               LTTNG_ASSERT(i <= count);
+       } while (count - i > 0 && ret > 0);
+       return i;
+
+error:
+       if (i == 0) {
+               return -1;
+       } else {
+               return i;
+       }
+}
+
+ssize_t lttng_write(int fd, const void *buf, size_t count)
+{
+       size_t i = 0;
+       ssize_t ret;
+
+       LTTNG_ASSERT(buf);
+
+       /*
+        * Deny a write count that can be bigger then the returned value max size.
+        * This makes the function to never return an overflow value.
+        */
+       if (count > SSIZE_MAX) {
+               return -EINVAL;
+       }
+
+       do {
+               ret = write(fd, (char *) buf + i, count - i);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               continue;       /* retry operation */
+                       } else {
+                               goto error;
+                       }
+               }
+               i += ret;
+               LTTNG_ASSERT(i <= count);
+       } while (count - i > 0 && ret > 0);
+       return i;
+
+error:
+       if (i == 0) {
+               return -1;
+       } else {
+               return i;
+       }
+}
diff --git a/src/common/runas.c b/src/common/runas.c
deleted file mode 100644 (file)
index 930c973..0000000
+++ /dev/null
@@ -1,2033 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <fcntl.h>
-#include <grp.h>
-#include <limits.h>
-#include <pwd.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <common/bytecode/bytecode.h>
-#include <common/lttng-kernel.h>
-#include <common/common.h>
-#include <common/utils.h>
-#include <common/compat/errno.h>
-#include <common/compat/getenv.h>
-#include <common/compat/string.h>
-#include <common/unix.h>
-#include <common/defaults.h>
-#include <common/lttng-elf.h>
-#include <common/thread.h>
-
-#include <lttng/constant.h>
-
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <common/filter/filter-ast.h>
-
-#include "runas.h"
-
-#define GETPW_BUFFER_FALLBACK_SIZE 4096
-
-struct run_as_data;
-struct run_as_ret;
-typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value);
-
-enum run_as_cmd {
-       RUN_AS_MKDIR,
-       RUN_AS_MKDIRAT,
-       RUN_AS_MKDIR_RECURSIVE,
-       RUN_AS_MKDIRAT_RECURSIVE,
-       RUN_AS_OPEN,
-       RUN_AS_OPENAT,
-       RUN_AS_UNLINK,
-       RUN_AS_UNLINKAT,
-       RUN_AS_RMDIR,
-       RUN_AS_RMDIRAT,
-       RUN_AS_RMDIR_RECURSIVE,
-       RUN_AS_RMDIRAT_RECURSIVE,
-       RUN_AS_RENAME,
-       RUN_AS_RENAMEAT,
-       RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
-       RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
-       RUN_AS_GENERATE_FILTER_BYTECODE,
-};
-
-struct run_as_mkdir_data {
-       int dirfd;
-       char path[LTTNG_PATH_MAX];
-       mode_t mode;
-} LTTNG_PACKED;
-
-struct run_as_open_data {
-       int dirfd;
-       char path[LTTNG_PATH_MAX];
-       int flags;
-       mode_t mode;
-} LTTNG_PACKED;
-
-struct run_as_unlink_data {
-       int dirfd;
-       char path[LTTNG_PATH_MAX];
-} LTTNG_PACKED;
-
-struct run_as_rmdir_data {
-       int dirfd;
-       char path[LTTNG_PATH_MAX];
-       int flags; /* enum lttng_directory_handle_rmdir_recursive_flags. */
-} LTTNG_PACKED;
-
-struct run_as_extract_elf_symbol_offset_data {
-       int fd;
-       char function[LTTNG_SYMBOL_NAME_LEN];
-} LTTNG_PACKED;
-
-struct run_as_extract_sdt_probe_offsets_data {
-       int fd;
-       char probe_name[LTTNG_SYMBOL_NAME_LEN];
-       char provider_name[LTTNG_SYMBOL_NAME_LEN];
-} LTTNG_PACKED;
-
-struct run_as_generate_filter_bytecode_data {
-       char filter_expression[LTTNG_FILTER_MAX_LEN];
-} LTTNG_PACKED;
-
-struct run_as_rename_data {
-       /*
-        * [0] = old_dirfd
-        * [1] = new_dirfd
-        */
-       int dirfds[2];
-       char old_path[LTTNG_PATH_MAX];
-       char new_path[LTTNG_PATH_MAX];
-} LTTNG_PACKED;
-
-struct run_as_open_ret {
-       int fd;
-} LTTNG_PACKED;
-
-struct run_as_extract_elf_symbol_offset_ret {
-       uint64_t offset;
-} LTTNG_PACKED;
-
-struct run_as_extract_sdt_probe_offsets_ret {
-       uint32_t num_offset;
-       uint64_t offsets[LTTNG_KERNEL_ABI_MAX_UPROBE_NUM];
-} LTTNG_PACKED;
-
-struct run_as_generate_filter_bytecode_ret {
-       /* A lttng_bytecode_filter struct with 'dynamic' payload. */
-       char bytecode[LTTNG_FILTER_MAX_LEN];
-} LTTNG_PACKED;
-
-struct run_as_data {
-       enum run_as_cmd cmd;
-       union {
-               struct run_as_mkdir_data mkdir;
-               struct run_as_open_data open;
-               struct run_as_unlink_data unlink;
-               struct run_as_rmdir_data rmdir;
-               struct run_as_rename_data rename;
-               struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset;
-               struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets;
-               struct run_as_generate_filter_bytecode_data generate_filter_bytecode;
-       } u;
-       uid_t uid;
-       gid_t gid;
-} LTTNG_PACKED;
-
-/*
- * The run_as_ret structure holds the returned value and status of the command.
- *
- * The `u` union field holds the return value of the command; in most cases it
- * represents the success or the failure of the command. In more complex
- * commands, it holds a computed value.
- *
- * The _errno field is the errno recorded after the execution of the command.
- *
- * The _error fields is used the signify that return status of the command. For
- * simple commands returning `int` the _error field will be the same as the
- * ret_int field. In complex commands, it signify the success or failure of the
- * command.
- *
- */
-struct run_as_ret {
-       union {
-               int ret;
-               struct run_as_open_ret open;
-               struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset;
-               struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets;
-               struct run_as_generate_filter_bytecode_ret generate_filter_bytecode;
-       } u;
-       int _errno;
-       bool _error;
-} LTTNG_PACKED;
-
-#define COMMAND_IN_FDS(data_ptr) ({                                    \
-       int *fds = NULL;                                                \
-       if (command_properties[data_ptr->cmd].in_fds_offset != -1) {    \
-               fds = (int *) ((char *) data_ptr + command_properties[data_ptr->cmd].in_fds_offset); \
-       }                                                               \
-       fds;                                                            \
-})
-
-#define COMMAND_OUT_FDS(cmd, ret_ptr) ({                               \
-       int *fds = NULL;                                                \
-       if (command_properties[cmd].out_fds_offset != -1) {             \
-               fds = (int *) ((char *) ret_ptr + command_properties[cmd].out_fds_offset); \
-       }                                                               \
-       fds;                                                            \
-})
-
-#define COMMAND_IN_FD_COUNT(data_ptr) ({               \
-       command_properties[data_ptr->cmd].in_fd_count;  \
-})
-
-#define COMMAND_OUT_FD_COUNT(cmd) ({           \
-       command_properties[cmd].out_fd_count;   \
-})
-
-#define COMMAND_USE_CWD_FD(data_ptr) command_properties[data_ptr->cmd].use_cwd_fd
-
-struct run_as_command_properties {
-       /* Set to -1 when not applicable. */
-       ptrdiff_t in_fds_offset, out_fds_offset;
-       unsigned int in_fd_count, out_fd_count;
-       bool use_cwd_fd;
-};
-
-static const struct run_as_command_properties command_properties[] = {
-       [RUN_AS_MKDIR] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_MKDIRAT] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_MKDIR_RECURSIVE] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_MKDIRAT_RECURSIVE] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_OPEN] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = offsetof(struct run_as_ret, u.open.fd),
-               .out_fd_count = 1,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_OPENAT] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = offsetof(struct run_as_ret, u.open.fd),
-               .out_fd_count = 1,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_UNLINK] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_UNLINKAT] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_RMDIR_RECURSIVE] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_RMDIRAT_RECURSIVE] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_RMDIR] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_RMDIRAT] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_RENAME] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds),
-               .in_fd_count = 2,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = true,
-       },
-       [RUN_AS_RENAMEAT] = {
-               .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds),
-               .in_fd_count = 2,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET] = {
-               .in_fds_offset = offsetof(struct run_as_data,
-                               u.extract_elf_symbol_offset.fd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_EXTRACT_SDT_PROBE_OFFSETS] = {
-               .in_fds_offset = offsetof(struct run_as_data,
-                               u.extract_sdt_probe_offsets.fd),
-               .in_fd_count = 1,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-       [RUN_AS_GENERATE_FILTER_BYTECODE] = {
-               .in_fds_offset = -1,
-               .in_fd_count = 0,
-               .out_fds_offset = -1,
-               .out_fd_count = 0,
-               .use_cwd_fd = false,
-       },
-};
-
-struct run_as_worker {
-       pid_t pid;      /* Worker PID. */
-       int sockpair[2];
-       char *procname;
-};
-
-/* Single global worker per process (for now). */
-static struct run_as_worker *global_worker;
-/* Lock protecting the worker. */
-static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
-
-#ifdef VALGRIND
-static
-int use_clone(void)
-{
-       return 0;
-}
-#else
-static
-int use_clone(void)
-{
-       return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
-}
-#endif
-
-/*
- * Create recursively directory using the FULL path.
- */
-static
-int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       const char *path;
-       mode_t mode;
-       struct lttng_directory_handle *handle;
-
-       path = data->u.mkdir.path;
-       mode = data->u.mkdir.mode;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd);
-       if (!handle) {
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               ret_value->u.ret = -1;
-               goto end;
-       }
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.mkdir.dirfd = -1;
-       /* Safe to call as we have transitioned to the requested uid/gid. */
-       ret_value->u.ret = lttng_directory_handle_create_subdirectory_recursive(
-                       handle, path, mode);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       const char *path;
-       mode_t mode;
-       struct lttng_directory_handle *handle;
-
-       path = data->u.mkdir.path;
-       mode = data->u.mkdir.mode;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd);
-       if (!handle) {
-               ret_value->u.ret = -1;
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               goto end;
-       }
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.mkdir.dirfd = -1;
-       /* Safe to call as we have transitioned to the requested uid/gid. */
-       ret_value->u.ret = lttng_directory_handle_create_subdirectory(
-                       handle, path, mode);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _open(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       int fd;
-       struct lttng_directory_handle *handle;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.open.dirfd);
-       if (!handle) {
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               ret_value->u.ret = -1;
-               goto end;
-       }
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.open.dirfd = -1;
-
-       fd = lttng_directory_handle_open_file(handle,
-                       data->u.open.path, data->u.open.flags,
-                       data->u.open.mode);
-       if (fd < 0) {
-               ret_value->u.ret = -1;
-               ret_value->u.open.fd = -1;
-       } else {
-               ret_value->u.ret = 0;
-               ret_value->u.open.fd = fd;
-       }
-
-       ret_value->_errno = errno;
-       ret_value->_error = fd < 0;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _unlink(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       struct lttng_directory_handle *handle;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.unlink.dirfd);
-       if (!handle) {
-               ret_value->u.ret = -1;
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               goto end;
-       }
-
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.unlink.dirfd = -1;
-
-       ret_value->u.ret = lttng_directory_handle_unlink_file(handle,
-                       data->u.unlink.path);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _rmdir(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       struct lttng_directory_handle *handle;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd);
-       if (!handle) {
-               ret_value->u.ret = -1;
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               goto end;
-       }
-
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.rmdir.dirfd = -1;
-
-       ret_value->u.ret = lttng_directory_handle_remove_subdirectory(
-                       handle, data->u.rmdir.path);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       struct lttng_directory_handle *handle;
-
-       handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd);
-       if (!handle) {
-               ret_value->u.ret = -1;
-               ret_value->_errno = errno;
-               ret_value->_error = true;
-               goto end;
-       }
-
-       /* Ownership of dirfd is transferred to the handle. */
-       data->u.rmdir.dirfd = -1;
-
-       ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive(
-                       handle, data->u.rmdir.path, data->u.rmdir.flags);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       lttng_directory_handle_put(handle);
-end:
-       return ret_value->u.ret;
-}
-
-static
-int _rename(struct run_as_data *data, struct run_as_ret *ret_value)
-{
-       const char *old_path, *new_path;
-       struct lttng_directory_handle *old_handle = NULL, *new_handle = NULL;
-
-       old_path = data->u.rename.old_path;
-       new_path = data->u.rename.new_path;
-
-       old_handle = lttng_directory_handle_create_from_dirfd(
-                       data->u.rename.dirfds[0]);
-       if (!old_handle) {
-               ret_value->u.ret = -1;
-               goto end;
-       }
-       new_handle = lttng_directory_handle_create_from_dirfd(
-                       data->u.rename.dirfds[1]);
-       if (!new_handle) {
-               ret_value->u.ret = -1;
-               goto end;
-       }
-
-       /* Ownership of dirfds are transferred to the handles. */
-       data->u.rename.dirfds[0] = data->u.rename.dirfds[1] = -1;
-
-       /* Safe to call as we have transitioned to the requested uid/gid. */
-       ret_value->u.ret = lttng_directory_handle_rename(
-                       old_handle, old_path, new_handle, new_path);
-end:
-       lttng_directory_handle_put(old_handle);
-       lttng_directory_handle_put(new_handle);
-       ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.ret) ? true : false;
-       return ret_value->u.ret;
-}
-
-#ifdef HAVE_ELF_H
-static
-int _extract_elf_symbol_offset(struct run_as_data *data,
-               struct run_as_ret *ret_value)
-{
-       int ret = 0;
-       uint64_t offset;
-
-       ret_value->_error = false;
-       ret = lttng_elf_get_symbol_offset(data->u.extract_elf_symbol_offset.fd,
-                        data->u.extract_elf_symbol_offset.function,
-                        &offset);
-       if (ret) {
-               DBG("Failed to extract ELF function offset");
-               ret_value->_error = true;
-       }
-       ret_value->u.extract_elf_symbol_offset.offset = offset;
-
-       return ret;
-}
-
-static
-int _extract_sdt_probe_offsets(struct run_as_data *data,
-               struct run_as_ret *ret_value)
-{
-       int ret = 0;
-       uint64_t *offsets = NULL;
-       uint32_t num_offset;
-
-       ret_value->_error = false;
-
-       /* On success, this call allocates the offsets paramater. */
-       ret = lttng_elf_get_sdt_probe_offsets(
-                       data->u.extract_sdt_probe_offsets.fd,
-                       data->u.extract_sdt_probe_offsets.provider_name,
-                       data->u.extract_sdt_probe_offsets.probe_name,
-                       &offsets, &num_offset);
-
-       if (ret) {
-               DBG("Failed to extract SDT probe offsets");
-               ret_value->_error = true;
-               goto end;
-       }
-
-       if (num_offset <= 0 || num_offset > LTTNG_KERNEL_ABI_MAX_UPROBE_NUM) {
-               DBG("Wrong number of probes.");
-               ret = -1;
-               ret_value->_error = true;
-               goto free_offset;
-       }
-
-       /* Copy the content of the offsets array to the ret struct. */
-       memcpy(ret_value->u.extract_sdt_probe_offsets.offsets,
-                       offsets, num_offset * sizeof(uint64_t));
-
-       ret_value->u.extract_sdt_probe_offsets.num_offset = num_offset;
-
-free_offset:
-       free(offsets);
-end:
-       return ret;
-}
-#else
-static
-int _extract_elf_symbol_offset(struct run_as_data *data,
-               struct run_as_ret *ret_value)
-{
-       ERR("Unimplemented runas command RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET");
-       return -1;
-}
-
-static
-int _extract_sdt_probe_offsets(struct run_as_data *data,
-               struct run_as_ret *ret_value)
-{
-       ERR("Unimplemented runas command RUN_AS_EXTRACT_SDT_PROBE_OFFSETS");
-       return -1;
-}
-#endif
-
-static
-int _generate_filter_bytecode(struct run_as_data *data,
-               struct run_as_ret *ret_value) {
-       int ret = 0;
-       const char *filter_expression = NULL;
-       struct filter_parser_ctx *ctx = NULL;
-
-       ret_value->_error = false;
-
-       filter_expression = data->u.generate_filter_bytecode.filter_expression;
-
-       if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) {
-               ret_value->_error = true;
-               ret = -1;
-               goto end;
-       }
-
-       ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
-       if (ret < 0) {
-               ret_value->_error = true;
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Size of bytecode generated: %u bytes.",
-                       bytecode_get_len(&ctx->bytecode->b));
-
-       /* Copy the lttng_bytecode_filter object to the return structure. */
-       memcpy(ret_value->u.generate_filter_bytecode.bytecode,
-                       &ctx->bytecode->b,
-                       sizeof(ctx->bytecode->b) +
-                                       bytecode_get_len(&ctx->bytecode->b));
-
-end:
-       if (ctx) {
-               filter_bytecode_free(ctx);
-               filter_ir_free(ctx);
-               filter_parser_ctx_free(ctx);
-       }
-
-       return ret;
-}
-static
-run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
-{
-       switch (cmd) {
-       case RUN_AS_MKDIR:
-       case RUN_AS_MKDIRAT:
-               return _mkdirat;
-       case RUN_AS_MKDIR_RECURSIVE:
-       case RUN_AS_MKDIRAT_RECURSIVE:
-               return _mkdirat_recursive;
-       case RUN_AS_OPEN:
-       case RUN_AS_OPENAT:
-               return _open;
-       case RUN_AS_UNLINK:
-       case RUN_AS_UNLINKAT:
-               return _unlink;
-       case RUN_AS_RMDIR:
-       case RUN_AS_RMDIRAT:
-               return _rmdir;
-       case RUN_AS_RMDIR_RECURSIVE:
-       case RUN_AS_RMDIRAT_RECURSIVE:
-               return _rmdir_recursive;
-       case RUN_AS_RENAME:
-       case RUN_AS_RENAMEAT:
-               return _rename;
-       case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
-               return _extract_elf_symbol_offset;
-       case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
-               return _extract_sdt_probe_offsets;
-       case RUN_AS_GENERATE_FILTER_BYTECODE:
-               return _generate_filter_bytecode;
-       default:
-               ERR("Unknown command %d", (int) cmd);
-               return NULL;
-       }
-}
-
-static
-int do_send_fds(int sock, const int *fds, unsigned int fd_count)
-{
-       ssize_t len;
-       unsigned int i;
-
-       for (i = 0; i < fd_count; i++) {
-               if (fds[i] < 0) {
-                       DBG("Attempt to send invalid file descriptor (fd = %i)",
-                                       fds[i]);
-                       /* Return 0 as this is not a fatal error. */
-                       return 0;
-               }
-       }
-
-       len = lttcomm_send_fds_unix_sock(sock, fds, fd_count);
-       return len < 0 ? -1 : 0;
-}
-
-static
-int do_recv_fds(int sock, int *fds, unsigned int fd_count)
-{
-       int ret = 0;
-       unsigned int i;
-       ssize_t len;
-
-       len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count);
-       if (len == 0) {
-               ret = -1;
-               goto end;
-       } else if (len < 0) {
-               PERROR("Failed to receive file descriptors from socket");
-               ret = -1;
-               goto end;
-       }
-
-       for (i = 0; i < fd_count; i++) {
-               if (fds[i] < 0) {
-                       ERR("Invalid file descriptor received from worker (fd = %i)", fds[i]);
-                       /* Return 0 as this is not a fatal error. */
-               }
-       }
-end:
-       return ret;
-}
-
-static
-int send_fds_to_worker(const struct run_as_worker *worker,
-               const struct run_as_data *data)
-{
-       int ret = 0;
-       unsigned int i;
-
-       if (COMMAND_USE_CWD_FD(data) || COMMAND_IN_FD_COUNT(data) == 0) {
-               goto end;
-       }
-
-       for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
-               if (COMMAND_IN_FDS(data)[i] < 0) {
-                       ERR("Refusing to send invalid fd to worker (fd = %i)",
-                                       COMMAND_IN_FDS(data)[i]);
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       ret = do_send_fds(worker->sockpair[0], COMMAND_IN_FDS(data),
-                       COMMAND_IN_FD_COUNT(data));
-       if (ret < 0) {
-               PERROR("Failed to send file descriptor to run-as worker");
-               ret = -1;
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static
-int send_fds_to_master(struct run_as_worker *worker, enum run_as_cmd cmd,
-               struct run_as_ret *run_as_ret)
-{
-       int ret = 0;
-       unsigned int i;
-
-       if (COMMAND_OUT_FD_COUNT(cmd) == 0) {
-               goto end;
-       }
-
-       ret = do_send_fds(worker->sockpair[1], COMMAND_OUT_FDS(cmd, run_as_ret),
-                       COMMAND_OUT_FD_COUNT(cmd));
-       if (ret < 0) {
-               PERROR("Failed to send file descriptor to master process");
-               goto end;
-       }
-
-       for (i = 0; i < COMMAND_OUT_FD_COUNT(cmd); i++) {
-               int fd = COMMAND_OUT_FDS(cmd, run_as_ret)[i];
-               if (fd >= 0) {
-                       int ret_close = close(fd);
-
-                       if (ret_close < 0) {
-                               PERROR("Failed to close result file descriptor (fd = %i)",
-                                               fd);
-                       }
-               }
-       }
-end:
-       return ret;
-}
-
-static
-int recv_fds_from_worker(const struct run_as_worker *worker, enum run_as_cmd cmd,
-               struct run_as_ret *run_as_ret)
-{
-       int ret = 0;
-
-       if (COMMAND_OUT_FD_COUNT(cmd) == 0) {
-               goto end;
-       }
-
-       ret = do_recv_fds(worker->sockpair[0], COMMAND_OUT_FDS(cmd, run_as_ret),
-                       COMMAND_OUT_FD_COUNT(cmd));
-       if (ret < 0) {
-               PERROR("Failed to receive file descriptor from run-as worker");
-               ret = -1;
-       }
-end:
-       return ret;
-}
-
-static
-int recv_fds_from_master(struct run_as_worker *worker, struct run_as_data *data)
-{
-       int ret = 0;
-
-       if (COMMAND_USE_CWD_FD(data)) {
-               unsigned int i;
-
-               for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
-                       COMMAND_IN_FDS(data)[i] = AT_FDCWD;
-               }
-               goto end;
-       }
-
-       if (COMMAND_IN_FD_COUNT(data) == 0) {
-               goto end;
-       }
-
-       ret = do_recv_fds(worker->sockpair[1], COMMAND_IN_FDS(data),
-                       COMMAND_IN_FD_COUNT(data));
-       if (ret < 0) {
-               PERROR("Failed to receive file descriptors from master process");
-               ret = -1;
-       }
-end:
-       return ret;
-}
-
-static
-int cleanup_received_fds(struct run_as_data *data)
-{
-       int ret = 0, i;
-
-       for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
-               if (COMMAND_IN_FDS(data)[i] == -1) {
-                       continue;
-               }
-               ret = close(COMMAND_IN_FDS(data)[i]);
-               if (ret) {
-                       PERROR("Failed to close file descriptor received fd in run-as worker");
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-static int get_user_infos_from_uid(
-               uid_t uid, char **username, gid_t *primary_gid)
-{
-       int ret;
-       char *buf = NULL;
-       long raw_get_pw_buf_size;
-       size_t get_pw_buf_size;
-       struct passwd pwd;
-       struct passwd *result = NULL;
-
-       /* Fetch the max size for the temporary buffer. */
-       errno = 0;
-       raw_get_pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
-       if (raw_get_pw_buf_size < 0) {
-               if (errno != 0) {
-                       PERROR("Failed to query _SC_GETPW_R_SIZE_MAX");
-                       goto error;
-               }
-
-               /* Limit is indeterminate. */
-               WARN("Failed to query _SC_GETPW_R_SIZE_MAX as it is "
-                       "indeterminate; falling back to default buffer size");
-               raw_get_pw_buf_size = GETPW_BUFFER_FALLBACK_SIZE;
-       }
-
-       get_pw_buf_size = (size_t) raw_get_pw_buf_size;
-
-       buf = zmalloc(get_pw_buf_size);
-       if (buf == NULL) {
-               PERROR("Failed to allocate buffer to get password file entries");
-               goto error;
-       }
-
-       ret = getpwuid_r(uid, &pwd, buf, get_pw_buf_size, &result);
-       if (ret < 0) {
-               PERROR("Failed to get user information for user:  uid = %d",
-                               (int) uid);
-               goto error;
-       }
-
-       if (result == NULL) {
-               ERR("Failed to find user information in password entries: uid = %d",
-                               (int) uid);
-               ret = -1;
-               goto error;
-       }
-
-       *username = strdup(result->pw_name);
-       if (*username == NULL) {
-               PERROR("Failed to copy user name");
-               goto error;
-       }
-
-       *primary_gid = result->pw_gid;
-
-end:
-       free(buf);
-       return ret;
-error:
-       *username = NULL;
-       *primary_gid = -1;
-       ret = -1;
-       goto end;
-}
-
-static int demote_creds(
-               uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid)
-{
-       int ret = 0;
-       gid_t primary_gid;
-       char *username = NULL;
-
-       /* Change the group id. */
-       if (prev_gid != new_gid) {
-               ret = setegid(new_gid);
-               if (ret < 0) {
-                       PERROR("Failed to set effective group id: new_gid = %d",
-                                       (int) new_gid);
-                       goto end;
-               }
-       }
-
-       /* Change the user id. */
-       if (prev_uid != new_uid) {
-               ret = get_user_infos_from_uid(new_uid, &username, &primary_gid);
-               if (ret < 0) {
-                       goto end;
-               }
-
-               /*
-                * Initialize the supplementary group access list.
-                *
-                * This is needed to handle cases where the supplementary groups
-                * of the user the process is demoting-to would give it access
-                * to a given file/folder, but not it's primary group.
-                *
-                * e.g
-                *   username: User1
-                *   Primary Group: User1
-                *   Secondary group: Disk, Network
-                *
-                *   mkdir inside the following directory must work since User1
-                *   is part of the Network group.
-                *
-                *   drwxrwx--- 2 root Network 4096 Jul 23 17:17 /tmp/my_folder/
-                *
-                *
-                * The order of the following initgroups and seteuid calls is
-                * important here;
-                * Only a root process or one with CAP_SETGID capability can
-                * call the the initgroups() function. We must initialize the
-                * supplementary groups before we change the effective
-                * UID to a less-privileged user.
-                */
-               ret = initgroups(username, primary_gid);
-               if (ret < 0) {
-                       PERROR("Failed to init the supplementary group access list: "
-                                       "username = `%s`, primary gid = %d", username,
-                                       (int) primary_gid);
-                       goto end;
-               }
-
-               ret = seteuid(new_uid);
-               if (ret < 0) {
-                       PERROR("Failed to set effective user id: new_uid = %d",
-                                       (int) new_uid);
-                       goto end;
-               }
-       }
-end:
-       free(username);
-       return ret;
-}
-
-static int promote_creds(
-               uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid)
-{
-       int ret = 0;
-       gid_t primary_gid;
-       char *username = NULL;
-
-       /* Change the group id. */
-       if (prev_gid != new_gid) {
-               ret = setegid(new_gid);
-               if (ret < 0) {
-                       PERROR("Failed to set effective group id: new_gid = %d",
-                                       (int) new_gid);
-                       goto end;
-               }
-       }
-
-       /* Change the user id. */
-       if (prev_uid != new_uid) {
-               ret = get_user_infos_from_uid(new_uid, &username, &primary_gid);
-               if (ret < 0) {
-                       goto end;
-               }
-
-               /*
-                * seteuid call must be done before the initgroups call because
-                * we need to be privileged (CAP_SETGID) to call initgroups().
-                */
-               ret = seteuid(new_uid);
-               if (ret < 0) {
-                       PERROR("Failed to set effective user id: new_uid = %d",
-                                       (int) new_uid);
-                       goto end;
-               }
-
-               /*
-                * Initialize the supplementary group access list.
-                *
-                * There is a possibility the groups we set in the following
-                * initgroups() call are not exactly the same as the ones we
-                * had when we originally demoted. This can happen if the
-                * /etc/group file is modified after the runas process is
-                * forked. This is very unlikely.
-                */
-               ret = initgroups(username, primary_gid);
-               if (ret < 0) {
-                       PERROR("Failed to init the supplementary group access "
-                                       "list: username = `%s`, primary gid = %d",
-                                       username, (int) primary_gid)
-                       goto end;
-               }
-       }
-end:
-       free(username);
-       return ret;
-}
-
-/*
- * Return < 0 on error, 0 if OK, 1 on hangup.
- */
-static
-int handle_one_cmd(struct run_as_worker *worker)
-{
-       int ret = 0, promote_ret;
-       struct run_as_data data = {};
-       ssize_t readlen, writelen;
-       struct run_as_ret sendret = {};
-       run_as_fct cmd;
-       const uid_t prev_ruid = getuid();
-       const gid_t prev_rgid = getgid();
-
-       /*
-        * Stage 1: Receive run_as_data struct from the master.
-        * The structure contains the command type and all the parameters needed for
-        * its execution
-        */
-       readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
-                       sizeof(data));
-       if (readlen == 0) {
-               /* hang up */
-               ret = 1;
-               goto end;
-       }
-       if (readlen < sizeof(data)) {
-               PERROR("lttcomm_recv_unix_sock error");
-               ret = -1;
-               goto end;
-       }
-
-       cmd = run_as_enum_to_fct(data.cmd);
-       if (!cmd) {
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Stage 2: Receive file descriptor from master.
-        * Some commands need a file descriptor as input so if it's needed we
-        * receive the fd using the Unix socket.
-        */
-       ret = recv_fds_from_master(worker, &data);
-       if (ret < 0) {
-               PERROR("recv_fd_from_master error");
-               ret = -1;
-               goto end;
-       }
-
-       ret = demote_creds(prev_ruid, prev_rgid, data.uid, data.gid);
-       if (ret < 0) {
-               goto write_return;
-       }
-
-       /*
-        * Also set umask to 0 for mkdir executable bit.
-        */
-       umask(0);
-
-       /*
-        * Stage 3: Execute the command
-        */
-       ret = (*cmd)(&data, &sendret);
-       if (ret < 0) {
-               DBG("Execution of command returned an error");
-       }
-
-write_return:
-       ret = cleanup_received_fds(&data);
-       if (ret < 0) {
-               ERR("Error cleaning up FD");
-               goto promote_back;
-       }
-
-       /*
-        * Stage 4: Send run_as_ret structure to the master.
-        * This structure contain the return value of the command and the errno.
-        */
-       writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
-                       sizeof(sendret));
-       if (writelen < sizeof(sendret)) {
-               PERROR("lttcomm_send_unix_sock error");
-               ret = -1;
-               goto promote_back;
-       }
-
-       /*
-        * Stage 5: Send resulting file descriptors to the master.
-        */
-       ret = send_fds_to_master(worker, data.cmd, &sendret);
-       if (ret < 0) {
-               DBG("Sending FD to master returned an error");
-       }
-
-       ret = 0;
-
-promote_back:
-       /* Return to previous uid/gid. */
-       promote_ret = promote_creds(data.uid, data.gid, prev_ruid, prev_rgid);
-       if (promote_ret < 0) {
-               ERR("Failed to promote back to the initial credentials");
-       }
-
-end:
-       return ret;
-}
-
-static
-int run_as_worker(struct run_as_worker *worker)
-{
-       int ret;
-       ssize_t writelen;
-       struct run_as_ret sendret;
-       size_t proc_orig_len;
-
-       /*
-        * Initialize worker. Set a different process cmdline.
-        */
-       proc_orig_len = strlen(worker->procname);
-       memset(worker->procname, 0, proc_orig_len);
-       strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
-
-       ret = lttng_thread_setname(DEFAULT_RUN_AS_WORKER_NAME);
-       if (ret && ret != -ENOSYS) {
-               /* Don't fail as this is not essential. */
-               DBG("Failed to set pthread name attribute");
-       }
-
-       memset(&sendret, 0, sizeof(sendret));
-
-       writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
-                       sizeof(sendret));
-       if (writelen < sizeof(sendret)) {
-               PERROR("lttcomm_send_unix_sock error");
-               ret = EXIT_FAILURE;
-               goto end;
-       }
-
-       for (;;) {
-               ret = handle_one_cmd(worker);
-               if (ret < 0) {
-                       ret = EXIT_FAILURE;
-                       goto end;
-               } else if (ret > 0) {
-                       break;
-               } else {
-                       continue;       /* Next command. */
-               }
-       }
-       ret = EXIT_SUCCESS;
-end:
-       return ret;
-}
-
-static
-int run_as_cmd(struct run_as_worker *worker,
-               enum run_as_cmd cmd,
-               struct run_as_data *data,
-               struct run_as_ret *ret_value,
-               uid_t uid, gid_t gid)
-{
-       int ret = 0;
-       ssize_t readlen, writelen;
-
-       /*
-        * If we are non-root, we can only deal with our own uid.
-        */
-       if (geteuid() != 0) {
-               if (uid != geteuid()) {
-                       ret = -1;
-                       ret_value->_errno = EPERM;
-                       ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
-                               (int) uid, (int) geteuid());
-                       goto end;
-               }
-       }
-
-       data->cmd = cmd;
-       data->uid = uid;
-       data->gid = gid;
-
-       /*
-        * Stage 1: Send the run_as_data struct to the worker process
-        */
-       writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
-                       sizeof(*data));
-       if (writelen < sizeof(*data)) {
-               PERROR("Error writing message to run_as");
-               ret = -1;
-               ret_value->_errno = EIO;
-               goto end;
-       }
-
-       /*
-        * Stage 2: Send file descriptor to the worker process if needed
-        */
-       ret = send_fds_to_worker(worker, data);
-       if (ret) {
-               PERROR("do_send_fd error");
-               ret = -1;
-               ret_value->_errno = EIO;
-               goto end;
-       }
-
-       /*
-        * Stage 3: Wait for the execution of the command
-        */
-
-       /*
-        * Stage 4: Receive the run_as_ret struct containing the return value and
-        * errno
-        */
-       readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value,
-                       sizeof(*ret_value));
-       if (!readlen) {
-               ERR("Run-as worker has hung-up during run_as_cmd");
-               ret = -1;
-               ret_value->_errno = EIO;
-               goto end;
-       } else if (readlen < sizeof(*ret_value)) {
-               PERROR("Error reading response from run_as");
-               ret = -1;
-               ret_value->_errno = errno;
-               goto end;
-       }
-
-       if (ret_value->_error) {
-               /* Skip stage 5 on error as there will be no fd to receive. */
-               goto end;
-       }
-
-       /*
-        * Stage 5: Receive file descriptor if needed
-        */
-       ret = recv_fds_from_worker(worker, cmd, ret_value);
-       if (ret < 0) {
-               ERR("Error receiving fd");
-               ret = -1;
-               ret_value->_errno = EIO;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * This is for debugging ONLY and should not be considered secure.
- */
-static
-int run_as_noworker(enum run_as_cmd cmd,
-               struct run_as_data *data, struct run_as_ret *ret_value,
-               uid_t uid, gid_t gid)
-{
-       int ret, saved_errno;
-       mode_t old_mask;
-       run_as_fct fct;
-
-       fct = run_as_enum_to_fct(cmd);
-       if (!fct) {
-               errno = -ENOSYS;
-               ret = -1;
-               goto end;
-       }
-       old_mask = umask(0);
-       ret = fct(data, ret_value);
-       saved_errno = ret_value->_errno;
-       umask(old_mask);
-       errno = saved_errno;
-end:
-       return ret;
-}
-
-static
-int reset_sighandler(void)
-{
-       int sig;
-
-       DBG("Resetting run_as worker signal handlers to default");
-       for (sig = 1; sig <= 31; sig++) {
-               (void) signal(sig, SIG_DFL);
-       }
-       return 0;
-}
-
-static
-void worker_sighandler(int sig)
-{
-       const char *signame;
-
-       /*
-        * The worker will inherit its parent's signals since they are part of
-        * the same process group. However, in the case of SIGINT and SIGTERM,
-        * we want to give the worker a chance to teardown gracefully when its
-        * parent closes the command socket.
-        */
-       switch (sig) {
-       case SIGINT:
-               signame = "SIGINT";
-               break;
-       case SIGTERM:
-               signame = "SIGTERM";
-               break;
-       default:
-               signame = NULL;
-       }
-
-       if (signame) {
-               DBG("run_as worker received signal %s", signame);
-       } else {
-               DBG("run_as_worker received signal %d", sig);
-       }
-}
-
-static
-int set_worker_sighandlers(void)
-{
-       int ret = 0;
-       sigset_t sigset;
-       struct sigaction sa;
-
-       if ((ret = sigemptyset(&sigset)) < 0) {
-               PERROR("sigemptyset");
-               goto end;
-       }
-
-       sa.sa_handler = worker_sighandler;
-       sa.sa_mask = sigset;
-       sa.sa_flags = 0;
-       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGINT");
-               goto end;
-       }
-
-       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGTERM");
-               goto end;
-       }
-
-       DBG("run_as signal handler set for SIGTERM and SIGINT");
-end:
-       return ret;
-}
-
-static
-int run_as_create_worker_no_lock(const char *procname,
-               post_fork_cleanup_cb clean_up_func,
-               void *clean_up_user_data)
-{
-       pid_t pid;
-       int i, ret = 0;
-       ssize_t readlen;
-       struct run_as_ret recvret;
-       struct run_as_worker *worker;
-
-       LTTNG_ASSERT(!global_worker);
-       if (!use_clone()) {
-               /*
-                * Don't initialize a worker, all run_as tasks will be performed
-                * in the current process.
-                */
-               ret = 0;
-               goto end;
-       }
-       worker = zmalloc(sizeof(*worker));
-       if (!worker) {
-               ret = -ENOMEM;
-               goto end;
-       }
-       worker->procname = strdup(procname);
-       if (!worker->procname) {
-               ret = -ENOMEM;
-               goto error_procname_alloc;
-       }
-       /* Create unix socket. */
-       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
-               ret = -1;
-               goto error_sock;
-       }
-
-       /* Fork worker. */
-       pid = fork();
-       if (pid < 0) {
-               PERROR("fork");
-               ret = -1;
-               goto error_fork;
-       } else if (pid == 0) {
-               /* Child */
-
-               reset_sighandler();
-
-               set_worker_sighandlers();
-
-               logger_set_thread_name("Run-as worker", true);
-
-               if (clean_up_func) {
-                       if (clean_up_func(clean_up_user_data) < 0) {
-                               ERR("Run-as post-fork clean-up failed, exiting.");
-                               exit(EXIT_FAILURE);
-                       }
-               }
-
-               /* Just close, no shutdown. */
-               if (close(worker->sockpair[0])) {
-                       PERROR("close");
-                       exit(EXIT_FAILURE);
-               }
-
-               /*
-                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
-                * Sockpair[1] is used as a control channel with the master
-                */
-               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
-                       if (i != worker->sockpair[1]) {
-                               (void) close(i);
-                       }
-               }
-
-               worker->sockpair[0] = -1;
-               ret = run_as_worker(worker);
-               if (lttcomm_close_unix_sock(worker->sockpair[1])) {
-                       PERROR("close");
-                       ret = -1;
-               }
-               worker->sockpair[1] = -1;
-               free(worker->procname);
-               free(worker);
-               LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
-               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
-       } else {
-               /* Parent */
-
-               /* Just close, no shutdown. */
-               if (close(worker->sockpair[1])) {
-                       PERROR("close");
-                       ret = -1;
-                       goto error_fork;
-               }
-               worker->sockpair[1] = -1;
-               worker->pid = pid;
-               /* Wait for worker to become ready. */
-               readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
-                               &recvret, sizeof(recvret));
-               if (readlen < sizeof(recvret)) {
-                       ERR("readlen: %zd", readlen);
-                       PERROR("Error reading response from run_as at creation");
-                       ret = -1;
-                       goto error_fork;
-               }
-               global_worker = worker;
-       }
-end:
-       return ret;
-
-       /* Error handling. */
-error_fork:
-       for (i = 0; i < 2; i++) {
-               if (worker->sockpair[i] < 0) {
-                       continue;
-               }
-               if (lttcomm_close_unix_sock(worker->sockpair[i])) {
-                       PERROR("close");
-               }
-               worker->sockpair[i] = -1;
-       }
-error_sock:
-       free(worker->procname);
-error_procname_alloc:
-       free(worker);
-       return ret;
-}
-
-static
-void run_as_destroy_worker_no_lock(void)
-{
-       struct run_as_worker *worker = global_worker;
-
-       DBG("Destroying run_as worker");
-       if (!worker) {
-               return;
-       }
-       /* Close unix socket */
-       DBG("Closing run_as worker socket");
-       if (lttcomm_close_unix_sock(worker->sockpair[0])) {
-               PERROR("close");
-       }
-       worker->sockpair[0] = -1;
-       /* Wait for worker. */
-       for (;;) {
-               int status;
-               pid_t wait_ret;
-
-               wait_ret = waitpid(worker->pid, &status, 0);
-               if (wait_ret < 0) {
-                       if (errno == EINTR) {
-                               continue;
-                       }
-                       PERROR("waitpid");
-                       break;
-               }
-
-               if (WIFEXITED(status)) {
-                       LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
-                                       DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
-                                       WEXITSTATUS(status));
-                       break;
-               } else if (WIFSIGNALED(status)) {
-                       ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
-                                       WTERMSIG(status));
-                       break;
-               }
-       }
-       free(worker->procname);
-       free(worker);
-       global_worker = NULL;
-}
-
-static
-int run_as_restart_worker(struct run_as_worker *worker)
-{
-       int ret = 0;
-       char *procname = NULL;
-
-       procname = worker->procname;
-
-       /* Close socket to run_as worker process and clean up the zombie process */
-       run_as_destroy_worker_no_lock();
-
-       /* Create a new run_as worker process*/
-       ret = run_as_create_worker_no_lock(procname, NULL, NULL);
-       if (ret < 0 ) {
-               ERR("Restarting the worker process failed");
-               ret = -1;
-               goto err;
-       }
-err:
-       return ret;
-}
-
-static
-int run_as(enum run_as_cmd cmd, struct run_as_data *data,
-                  struct run_as_ret *ret_value, uid_t uid, gid_t gid)
-{
-       int ret, saved_errno;
-
-       pthread_mutex_lock(&worker_lock);
-       if (use_clone()) {
-               DBG("Using run_as worker");
-
-               LTTNG_ASSERT(global_worker);
-
-               ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
-               saved_errno = ret_value->_errno;
-
-               /*
-                * If the worker thread crashed the errno is set to EIO. we log
-                * the error and  start a new worker process.
-                */
-               if (ret == -1 && saved_errno == EIO) {
-                       DBG("Socket closed unexpectedly... "
-                                       "Restarting the worker process");
-                       ret = run_as_restart_worker(global_worker);
-                       if (ret == -1) {
-                               ERR("Failed to restart worker process.");
-                               goto err;
-                       }
-               }
-       } else {
-               DBG("Using run_as without worker");
-               ret = run_as_noworker(cmd, data, ret_value, uid, gid);
-       }
-err:
-       pthread_mutex_unlock(&worker_lock);
-       return ret;
-}
-
-int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-       return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid);
-}
-
-int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode,
-               uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, (int) mode, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.mkdir.path, path,
-                       sizeof(data.u.mkdir.path));
-       if (ret) {
-               ERR("Failed to copy path argument of mkdirat recursive command");
-               goto error;
-       }
-       data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0';
-       data.u.mkdir.mode = mode;
-       data.u.mkdir.dirfd = dirfd;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE,
-                       &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-       return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid);
-}
-
-int run_as_mkdirat(int dirfd, const char *path, mode_t mode,
-               uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, (int) mode, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.mkdir.path, path,
-                       sizeof(data.u.mkdir.path));
-       if (ret) {
-               ERR("Failed to copy path argument of mkdirat command");
-               goto error;
-       }
-       data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0';
-       data.u.mkdir.mode = mode;
-       data.u.mkdir.dirfd = dirfd;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT,
-                       &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_open(const char *path, int flags, mode_t mode, uid_t uid,
-               gid_t gid)
-{
-       return run_as_openat(AT_FDCWD, path, flags, mode, uid, gid);
-}
-
-int run_as_openat(int dirfd, const char *path, int flags, mode_t mode,
-               uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("openat() fd = %d%s, path = %s, flags = %X, mode = %d, uid %d, gid %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, flags, (int) mode, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.open.path, path, sizeof(data.u.open.path));
-       if (ret) {
-               ERR("Failed to copy path argument of open command");
-               goto error;
-       }
-       data.u.open.flags = flags;
-       data.u.open.mode = mode;
-       data.u.open.dirfd = dirfd;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_OPEN : RUN_AS_OPENAT,
-                       &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret < 0 ? run_as_ret.u.ret :
-                       run_as_ret.u.open.fd;
-error:
-       return ret;
-}
-
-int run_as_unlink(const char *path, uid_t uid, gid_t gid)
-{
-       return run_as_unlinkat(AT_FDCWD, path, uid, gid);
-}
-
-int run_as_unlinkat(int dirfd, const char *path, uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("unlinkat() fd = %d%s, path = %s, uid = %d, gid = %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.unlink.path, path,
-                       sizeof(data.u.unlink.path));
-       if (ret) {
-               goto error;
-       }
-       data.u.unlink.dirfd = dirfd;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_UNLINK : RUN_AS_UNLINKAT, &data,
-                       &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_rmdir(const char *path, uid_t uid, gid_t gid)
-{
-       return run_as_rmdirat(AT_FDCWD, path, uid, gid);
-}
-
-int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("rmdirat() fd = %d%s, path = %s, uid = %d, gid = %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.rmdir.path, path,
-                       sizeof(data.u.rmdir.path));
-       if (ret) {
-               goto error;
-       }
-       data.u.rmdir.dirfd = dirfd;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR : RUN_AS_RMDIRAT, &data,
-                       &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags)
-{
-       return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags);
-}
-
-int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("rmdirat() recursive fd = %d%s, path = %s, uid = %d, gid = %d",
-                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       path, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.rmdir.path, path,
-                       sizeof(data.u.rmdir.path));
-       if (ret) {
-               goto error;
-       }
-       data.u.rmdir.dirfd = dirfd;
-       data.u.rmdir.flags = flags;
-       run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE,
-                       &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid)
-{
-       return run_as_renameat(AT_FDCWD, old, AT_FDCWD, new, uid, gid);
-}
-
-int run_as_renameat(int old_dirfd, const char *old_name,
-               int new_dirfd, const char *new_name, uid_t uid, gid_t gid)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("renameat() old_dirfd = %d%s, old_name = %s, new_dirfd = %d%s, new_name = %s, uid = %d, gid = %d",
-                       old_dirfd, old_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       old_name,
-                       new_dirfd, new_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
-                       new_name, (int) uid, (int) gid);
-       ret = lttng_strncpy(data.u.rename.old_path, old_name,
-                       sizeof(data.u.rename.old_path));
-       if (ret) {
-               goto error;
-       }
-       ret = lttng_strncpy(data.u.rename.new_path, new_name,
-                       sizeof(data.u.rename.new_path));
-       if (ret) {
-               goto error;
-       }
-
-       data.u.rename.dirfds[0] = old_dirfd;
-       data.u.rename.dirfds[1] = new_dirfd;
-       run_as(old_dirfd == AT_FDCWD && new_dirfd == AT_FDCWD ?
-                       RUN_AS_RENAME : RUN_AS_RENAMEAT,
-                       &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       ret = run_as_ret.u.ret;
-error:
-       return ret;
-}
-
-int run_as_extract_elf_symbol_offset(int fd, const char* function,
-               uid_t uid, gid_t gid, uint64_t *offset)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("extract_elf_symbol_offset() on fd=%d and function=%s "
-                       "with for uid %d and gid %d", fd, function,
-                       (int) uid, (int) gid);
-
-       data.u.extract_elf_symbol_offset.fd = fd;
-
-       strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1);
-       data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
-       ret = lttng_strncpy(data.u.extract_elf_symbol_offset.function,
-                       function,
-                       sizeof(data.u.extract_elf_symbol_offset.function));
-       if (ret) {
-               goto error;
-       }
-
-       run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       if (run_as_ret._error) {
-               ret = -1;
-               goto error;
-       }
-
-       *offset = run_as_ret.u.extract_elf_symbol_offset.offset;
-error:
-       return ret;
-}
-
-int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name,
-               const char* probe_name, uid_t uid, gid_t gid,
-               uint64_t **offsets, uint32_t *num_offset)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-
-       DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and "
-                       "provider_name=%s with for uid %d and gid %d", fd,
-                       probe_name, provider_name, (int) uid, (int) gid);
-
-       data.u.extract_sdt_probe_offsets.fd = fd;
-
-       ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name,
-                       sizeof(data.u.extract_sdt_probe_offsets.probe_name));
-       if (ret) {
-               goto error;
-       }
-       ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.provider_name,
-                       provider_name,
-                       sizeof(data.u.extract_sdt_probe_offsets.provider_name));
-       if (ret) {
-               goto error;
-       }
-
-       run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       if (run_as_ret._error) {
-               ret = -1;
-               goto error;
-       }
-
-       *num_offset = run_as_ret.u.extract_sdt_probe_offsets.num_offset;
-       *offsets = zmalloc(*num_offset * sizeof(uint64_t));
-       if (!*offsets) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       memcpy(*offsets, run_as_ret.u.extract_sdt_probe_offsets.offsets,
-                       *num_offset * sizeof(uint64_t));
-error:
-       return ret;
-}
-
-int run_as_generate_filter_bytecode(const char *filter_expression,
-               const struct lttng_credentials *creds,
-               struct lttng_bytecode **bytecode)
-{
-       int ret;
-       struct run_as_data data = {};
-       struct run_as_ret run_as_ret = {};
-       const struct lttng_bytecode *view_bytecode = NULL;
-       struct lttng_bytecode *local_bytecode = NULL;
-       const uid_t uid = lttng_credentials_get_uid(creds);
-       const gid_t gid = lttng_credentials_get_gid(creds);
-
-       DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
-                       filter_expression, (int) uid, (int) gid);
-
-       ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression,
-                       sizeof(data.u.generate_filter_bytecode.filter_expression));
-       if (ret) {
-               goto error;
-       }
-
-       run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid);
-       errno = run_as_ret._errno;
-       if (run_as_ret._error) {
-               ret = -1;
-               goto error;
-       }
-
-       view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
-
-       local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
-       if (!local_bytecode) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode,
-                       sizeof(*local_bytecode) + view_bytecode->len);
-       *bytecode = local_bytecode;
-error:
-       return ret;
-}
-
-int run_as_create_worker(const char *procname,
-               post_fork_cleanup_cb clean_up_func,
-               void *clean_up_user_data)
-{
-       int ret;
-
-       pthread_mutex_lock(&worker_lock);
-       ret = run_as_create_worker_no_lock(procname, clean_up_func,
-                       clean_up_user_data);
-       pthread_mutex_unlock(&worker_lock);
-       return ret;
-}
-
-void run_as_destroy_worker(void)
-{
-       pthread_mutex_lock(&worker_lock);
-       run_as_destroy_worker_no_lock();
-       pthread_mutex_unlock(&worker_lock);
-}
diff --git a/src/common/runas.cpp b/src/common/runas.cpp
new file mode 100644 (file)
index 0000000..3c9eea8
--- /dev/null
@@ -0,0 +1,2033 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <common/bytecode/bytecode.h>
+#include <common/lttng-kernel.h>
+#include <common/common.h>
+#include <common/utils.h>
+#include <common/compat/errno.h>
+#include <common/compat/getenv.h>
+#include <common/compat/string.h>
+#include <common/unix.h>
+#include <common/defaults.h>
+#include <common/lttng-elf.h>
+#include <common/thread.h>
+
+#include <lttng/constant.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/filter/filter-ast.h>
+
+#include "runas.h"
+
+#define GETPW_BUFFER_FALLBACK_SIZE 4096
+
+struct run_as_data;
+struct run_as_ret;
+typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value);
+
+enum run_as_cmd {
+       RUN_AS_MKDIR,
+       RUN_AS_MKDIRAT,
+       RUN_AS_MKDIR_RECURSIVE,
+       RUN_AS_MKDIRAT_RECURSIVE,
+       RUN_AS_OPEN,
+       RUN_AS_OPENAT,
+       RUN_AS_UNLINK,
+       RUN_AS_UNLINKAT,
+       RUN_AS_RMDIR,
+       RUN_AS_RMDIRAT,
+       RUN_AS_RMDIR_RECURSIVE,
+       RUN_AS_RMDIRAT_RECURSIVE,
+       RUN_AS_RENAME,
+       RUN_AS_RENAMEAT,
+       RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
+       RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
+       RUN_AS_GENERATE_FILTER_BYTECODE,
+};
+
+struct run_as_mkdir_data {
+       int dirfd;
+       char path[LTTNG_PATH_MAX];
+       mode_t mode;
+} LTTNG_PACKED;
+
+struct run_as_open_data {
+       int dirfd;
+       char path[LTTNG_PATH_MAX];
+       int flags;
+       mode_t mode;
+} LTTNG_PACKED;
+
+struct run_as_unlink_data {
+       int dirfd;
+       char path[LTTNG_PATH_MAX];
+} LTTNG_PACKED;
+
+struct run_as_rmdir_data {
+       int dirfd;
+       char path[LTTNG_PATH_MAX];
+       int flags; /* enum lttng_directory_handle_rmdir_recursive_flags. */
+} LTTNG_PACKED;
+
+struct run_as_extract_elf_symbol_offset_data {
+       int fd;
+       char function[LTTNG_SYMBOL_NAME_LEN];
+} LTTNG_PACKED;
+
+struct run_as_extract_sdt_probe_offsets_data {
+       int fd;
+       char probe_name[LTTNG_SYMBOL_NAME_LEN];
+       char provider_name[LTTNG_SYMBOL_NAME_LEN];
+} LTTNG_PACKED;
+
+struct run_as_generate_filter_bytecode_data {
+       char filter_expression[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PACKED;
+
+struct run_as_rename_data {
+       /*
+        * [0] = old_dirfd
+        * [1] = new_dirfd
+        */
+       int dirfds[2];
+       char old_path[LTTNG_PATH_MAX];
+       char new_path[LTTNG_PATH_MAX];
+} LTTNG_PACKED;
+
+struct run_as_open_ret {
+       int fd;
+} LTTNG_PACKED;
+
+struct run_as_extract_elf_symbol_offset_ret {
+       uint64_t offset;
+} LTTNG_PACKED;
+
+struct run_as_extract_sdt_probe_offsets_ret {
+       uint32_t num_offset;
+       uint64_t offsets[LTTNG_KERNEL_ABI_MAX_UPROBE_NUM];
+} LTTNG_PACKED;
+
+struct run_as_generate_filter_bytecode_ret {
+       /* A lttng_bytecode_filter struct with 'dynamic' payload. */
+       char bytecode[LTTNG_FILTER_MAX_LEN];
+} LTTNG_PACKED;
+
+struct run_as_data {
+       enum run_as_cmd cmd;
+       union {
+               struct run_as_mkdir_data mkdir;
+               struct run_as_open_data open;
+               struct run_as_unlink_data unlink;
+               struct run_as_rmdir_data rmdir;
+               struct run_as_rename_data rename;
+               struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset;
+               struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets;
+               struct run_as_generate_filter_bytecode_data generate_filter_bytecode;
+       } u;
+       uid_t uid;
+       gid_t gid;
+} LTTNG_PACKED;
+
+/*
+ * The run_as_ret structure holds the returned value and status of the command.
+ *
+ * The `u` union field holds the return value of the command; in most cases it
+ * represents the success or the failure of the command. In more complex
+ * commands, it holds a computed value.
+ *
+ * The _errno field is the errno recorded after the execution of the command.
+ *
+ * The _error fields is used the signify that return status of the command. For
+ * simple commands returning `int` the _error field will be the same as the
+ * ret_int field. In complex commands, it signify the success or failure of the
+ * command.
+ *
+ */
+struct run_as_ret {
+       union {
+               int ret;
+               struct run_as_open_ret open;
+               struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset;
+               struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets;
+               struct run_as_generate_filter_bytecode_ret generate_filter_bytecode;
+       } u;
+       int _errno;
+       bool _error;
+} LTTNG_PACKED;
+
+#define COMMAND_IN_FDS(data_ptr) ({                                    \
+       int *fds = NULL;                                                \
+       if (command_properties[data_ptr->cmd].in_fds_offset != -1) {    \
+               fds = (int *) ((char *) data_ptr + command_properties[data_ptr->cmd].in_fds_offset); \
+       }                                                               \
+       fds;                                                            \
+})
+
+#define COMMAND_OUT_FDS(cmd, ret_ptr) ({                               \
+       int *fds = NULL;                                                \
+       if (command_properties[cmd].out_fds_offset != -1) {             \
+               fds = (int *) ((char *) ret_ptr + command_properties[cmd].out_fds_offset); \
+       }                                                               \
+       fds;                                                            \
+})
+
+#define COMMAND_IN_FD_COUNT(data_ptr) ({               \
+       command_properties[data_ptr->cmd].in_fd_count;  \
+})
+
+#define COMMAND_OUT_FD_COUNT(cmd) ({           \
+       command_properties[cmd].out_fd_count;   \
+})
+
+#define COMMAND_USE_CWD_FD(data_ptr) command_properties[data_ptr->cmd].use_cwd_fd
+
+struct run_as_command_properties {
+       /* Set to -1 when not applicable. */
+       ptrdiff_t in_fds_offset, out_fds_offset;
+       unsigned int in_fd_count, out_fd_count;
+       bool use_cwd_fd;
+};
+
+static const struct run_as_command_properties command_properties[] = {
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd),
+               .out_fds_offset = offsetof(struct run_as_ret, u.open.fd),
+               .in_fd_count = 1,
+               .out_fd_count = 1,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd),
+               .out_fds_offset = offsetof(struct run_as_ret, u.open.fd),
+               .in_fd_count = 1,
+               .out_fd_count = 1,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds),
+               .out_fds_offset = -1,
+               .in_fd_count = 2,
+               .out_fd_count = 0,
+               .use_cwd_fd = true,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds),
+               .out_fds_offset = -1,
+               .in_fd_count = 2,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data,
+                               u.extract_elf_symbol_offset.fd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = offsetof(struct run_as_data,
+                               u.extract_sdt_probe_offsets.fd),
+               .out_fds_offset = -1,
+               .in_fd_count = 1,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+       {
+               .in_fds_offset = -1,
+               .out_fds_offset = -1,
+               .in_fd_count = 0,
+               .out_fd_count = 0,
+               .use_cwd_fd = false,
+       },
+};
+
+struct run_as_worker_data {
+       pid_t pid;      /* Worker PID. */
+       int sockpair[2];
+       char *procname;
+};
+
+/* Single global worker per process (for now). */
+static run_as_worker_data *global_worker;
+/* Lock protecting the worker. */
+static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#ifdef VALGRIND
+static
+int use_clone(void)
+{
+       return 0;
+}
+#else
+static
+int use_clone(void)
+{
+       return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE");
+}
+#endif
+
+/*
+ * Create recursively directory using the FULL path.
+ */
+static
+int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       const char *path;
+       mode_t mode;
+       struct lttng_directory_handle *handle;
+
+       path = data->u.mkdir.path;
+       mode = data->u.mkdir.mode;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd);
+       if (!handle) {
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               ret_value->u.ret = -1;
+               goto end;
+       }
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.mkdir.dirfd = -1;
+       /* Safe to call as we have transitioned to the requested uid/gid. */
+       ret_value->u.ret = lttng_directory_handle_create_subdirectory_recursive(
+                       handle, path, mode);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       const char *path;
+       mode_t mode;
+       struct lttng_directory_handle *handle;
+
+       path = data->u.mkdir.path;
+       mode = data->u.mkdir.mode;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd);
+       if (!handle) {
+               ret_value->u.ret = -1;
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               goto end;
+       }
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.mkdir.dirfd = -1;
+       /* Safe to call as we have transitioned to the requested uid/gid. */
+       ret_value->u.ret = lttng_directory_handle_create_subdirectory(
+                       handle, path, mode);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _open(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       int fd;
+       struct lttng_directory_handle *handle;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.open.dirfd);
+       if (!handle) {
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               ret_value->u.ret = -1;
+               goto end;
+       }
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.open.dirfd = -1;
+
+       fd = lttng_directory_handle_open_file(handle,
+                       data->u.open.path, data->u.open.flags,
+                       data->u.open.mode);
+       if (fd < 0) {
+               ret_value->u.ret = -1;
+               ret_value->u.open.fd = -1;
+       } else {
+               ret_value->u.ret = 0;
+               ret_value->u.open.fd = fd;
+       }
+
+       ret_value->_errno = errno;
+       ret_value->_error = fd < 0;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _unlink(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       struct lttng_directory_handle *handle;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.unlink.dirfd);
+       if (!handle) {
+               ret_value->u.ret = -1;
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               goto end;
+       }
+
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.unlink.dirfd = -1;
+
+       ret_value->u.ret = lttng_directory_handle_unlink_file(handle,
+                       data->u.unlink.path);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _rmdir(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       struct lttng_directory_handle *handle;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd);
+       if (!handle) {
+               ret_value->u.ret = -1;
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               goto end;
+       }
+
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.rmdir.dirfd = -1;
+
+       ret_value->u.ret = lttng_directory_handle_remove_subdirectory(
+                       handle, data->u.rmdir.path);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       struct lttng_directory_handle *handle;
+
+       handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd);
+       if (!handle) {
+               ret_value->u.ret = -1;
+               ret_value->_errno = errno;
+               ret_value->_error = true;
+               goto end;
+       }
+
+       /* Ownership of dirfd is transferred to the handle. */
+       data->u.rmdir.dirfd = -1;
+
+       ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive(
+                       handle, data->u.rmdir.path, data->u.rmdir.flags);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       lttng_directory_handle_put(handle);
+end:
+       return ret_value->u.ret;
+}
+
+static
+int _rename(struct run_as_data *data, struct run_as_ret *ret_value)
+{
+       const char *old_path, *new_path;
+       struct lttng_directory_handle *old_handle = NULL, *new_handle = NULL;
+
+       old_path = data->u.rename.old_path;
+       new_path = data->u.rename.new_path;
+
+       old_handle = lttng_directory_handle_create_from_dirfd(
+                       data->u.rename.dirfds[0]);
+       if (!old_handle) {
+               ret_value->u.ret = -1;
+               goto end;
+       }
+       new_handle = lttng_directory_handle_create_from_dirfd(
+                       data->u.rename.dirfds[1]);
+       if (!new_handle) {
+               ret_value->u.ret = -1;
+               goto end;
+       }
+
+       /* Ownership of dirfds are transferred to the handles. */
+       data->u.rename.dirfds[0] = data->u.rename.dirfds[1] = -1;
+
+       /* Safe to call as we have transitioned to the requested uid/gid. */
+       ret_value->u.ret = lttng_directory_handle_rename(
+                       old_handle, old_path, new_handle, new_path);
+end:
+       lttng_directory_handle_put(old_handle);
+       lttng_directory_handle_put(new_handle);
+       ret_value->_errno = errno;
+       ret_value->_error = (ret_value->u.ret) ? true : false;
+       return ret_value->u.ret;
+}
+
+#ifdef HAVE_ELF_H
+static
+int _extract_elf_symbol_offset(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       int ret = 0;
+       uint64_t offset;
+
+       ret_value->_error = false;
+       ret = lttng_elf_get_symbol_offset(data->u.extract_elf_symbol_offset.fd,
+                        data->u.extract_elf_symbol_offset.function,
+                        &offset);
+       if (ret) {
+               DBG("Failed to extract ELF function offset");
+               ret_value->_error = true;
+       }
+       ret_value->u.extract_elf_symbol_offset.offset = offset;
+
+       return ret;
+}
+
+static
+int _extract_sdt_probe_offsets(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       int ret = 0;
+       uint64_t *offsets = NULL;
+       uint32_t num_offset;
+
+       ret_value->_error = false;
+
+       /* On success, this call allocates the offsets paramater. */
+       ret = lttng_elf_get_sdt_probe_offsets(
+                       data->u.extract_sdt_probe_offsets.fd,
+                       data->u.extract_sdt_probe_offsets.provider_name,
+                       data->u.extract_sdt_probe_offsets.probe_name,
+                       &offsets, &num_offset);
+
+       if (ret) {
+               DBG("Failed to extract SDT probe offsets");
+               ret_value->_error = true;
+               goto end;
+       }
+
+       if (num_offset <= 0 || num_offset > LTTNG_KERNEL_ABI_MAX_UPROBE_NUM) {
+               DBG("Wrong number of probes.");
+               ret = -1;
+               ret_value->_error = true;
+               goto free_offset;
+       }
+
+       /* Copy the content of the offsets array to the ret struct. */
+       memcpy(ret_value->u.extract_sdt_probe_offsets.offsets,
+                       offsets, num_offset * sizeof(uint64_t));
+
+       ret_value->u.extract_sdt_probe_offsets.num_offset = num_offset;
+
+free_offset:
+       free(offsets);
+end:
+       return ret;
+}
+#else
+static
+int _extract_elf_symbol_offset(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       ERR("Unimplemented runas command RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET");
+       return -1;
+}
+
+static
+int _extract_sdt_probe_offsets(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       ERR("Unimplemented runas command RUN_AS_EXTRACT_SDT_PROBE_OFFSETS");
+       return -1;
+}
+#endif
+
+static
+int _generate_filter_bytecode(struct run_as_data *data,
+               struct run_as_ret *ret_value) {
+       int ret = 0;
+       const char *filter_expression = NULL;
+       struct filter_parser_ctx *ctx = NULL;
+
+       ret_value->_error = false;
+
+       filter_expression = data->u.generate_filter_bytecode.filter_expression;
+
+       if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) {
+               ret_value->_error = true;
+               ret = -1;
+               goto end;
+       }
+
+       ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx);
+       if (ret < 0) {
+               ret_value->_error = true;
+               ret = -1;
+               goto end;
+       }
+
+       DBG("Size of bytecode generated: %u bytes.",
+                       bytecode_get_len(&ctx->bytecode->b));
+
+       /* Copy the lttng_bytecode_filter object to the return structure. */
+       memcpy(ret_value->u.generate_filter_bytecode.bytecode,
+                       &ctx->bytecode->b,
+                       sizeof(ctx->bytecode->b) +
+                                       bytecode_get_len(&ctx->bytecode->b));
+
+end:
+       if (ctx) {
+               filter_bytecode_free(ctx);
+               filter_ir_free(ctx);
+               filter_parser_ctx_free(ctx);
+       }
+
+       return ret;
+}
+static
+run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
+{
+       switch (cmd) {
+       case RUN_AS_MKDIR:
+       case RUN_AS_MKDIRAT:
+               return _mkdirat;
+       case RUN_AS_MKDIR_RECURSIVE:
+       case RUN_AS_MKDIRAT_RECURSIVE:
+               return _mkdirat_recursive;
+       case RUN_AS_OPEN:
+       case RUN_AS_OPENAT:
+               return _open;
+       case RUN_AS_UNLINK:
+       case RUN_AS_UNLINKAT:
+               return _unlink;
+       case RUN_AS_RMDIR:
+       case RUN_AS_RMDIRAT:
+               return _rmdir;
+       case RUN_AS_RMDIR_RECURSIVE:
+       case RUN_AS_RMDIRAT_RECURSIVE:
+               return _rmdir_recursive;
+       case RUN_AS_RENAME:
+       case RUN_AS_RENAMEAT:
+               return _rename;
+       case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
+               return _extract_elf_symbol_offset;
+       case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+               return _extract_sdt_probe_offsets;
+       case RUN_AS_GENERATE_FILTER_BYTECODE:
+               return _generate_filter_bytecode;
+       default:
+               ERR("Unknown command %d", (int) cmd);
+               return NULL;
+       }
+}
+
+static
+int do_send_fds(int sock, const int *fds, unsigned int fd_count)
+{
+       ssize_t len;
+       unsigned int i;
+
+       for (i = 0; i < fd_count; i++) {
+               if (fds[i] < 0) {
+                       DBG("Attempt to send invalid file descriptor (fd = %i)",
+                                       fds[i]);
+                       /* Return 0 as this is not a fatal error. */
+                       return 0;
+               }
+       }
+
+       len = lttcomm_send_fds_unix_sock(sock, fds, fd_count);
+       return len < 0 ? -1 : 0;
+}
+
+static
+int do_recv_fds(int sock, int *fds, unsigned int fd_count)
+{
+       int ret = 0;
+       unsigned int i;
+       ssize_t len;
+
+       len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count);
+       if (len == 0) {
+               ret = -1;
+               goto end;
+       } else if (len < 0) {
+               PERROR("Failed to receive file descriptors from socket");
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < fd_count; i++) {
+               if (fds[i] < 0) {
+                       ERR("Invalid file descriptor received from worker (fd = %i)", fds[i]);
+                       /* Return 0 as this is not a fatal error. */
+               }
+       }
+end:
+       return ret;
+}
+
+static
+int send_fds_to_worker(const run_as_worker_data *worker,
+               const struct run_as_data *data)
+{
+       int ret = 0;
+       unsigned int i;
+
+       if (COMMAND_USE_CWD_FD(data) || COMMAND_IN_FD_COUNT(data) == 0) {
+               goto end;
+       }
+
+       for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
+               if (COMMAND_IN_FDS(data)[i] < 0) {
+                       ERR("Refusing to send invalid fd to worker (fd = %i)",
+                                       COMMAND_IN_FDS(data)[i]);
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       ret = do_send_fds(worker->sockpair[0], COMMAND_IN_FDS(data),
+                       COMMAND_IN_FD_COUNT(data));
+       if (ret < 0) {
+               PERROR("Failed to send file descriptor to run-as worker");
+               ret = -1;
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static
+int send_fds_to_master(run_as_worker_data *worker, enum run_as_cmd cmd,
+               struct run_as_ret *run_as_ret)
+{
+       int ret = 0;
+       unsigned int i;
+
+       if (COMMAND_OUT_FD_COUNT(cmd) == 0) {
+               goto end;
+       }
+
+       ret = do_send_fds(worker->sockpair[1], COMMAND_OUT_FDS(cmd, run_as_ret),
+                       COMMAND_OUT_FD_COUNT(cmd));
+       if (ret < 0) {
+               PERROR("Failed to send file descriptor to master process");
+               goto end;
+       }
+
+       for (i = 0; i < COMMAND_OUT_FD_COUNT(cmd); i++) {
+               int fd = COMMAND_OUT_FDS(cmd, run_as_ret)[i];
+               if (fd >= 0) {
+                       int ret_close = close(fd);
+
+                       if (ret_close < 0) {
+                               PERROR("Failed to close result file descriptor (fd = %i)",
+                                               fd);
+                       }
+               }
+       }
+end:
+       return ret;
+}
+
+static
+int recv_fds_from_worker(const run_as_worker_data *worker, enum run_as_cmd cmd,
+               struct run_as_ret *run_as_ret)
+{
+       int ret = 0;
+
+       if (COMMAND_OUT_FD_COUNT(cmd) == 0) {
+               goto end;
+       }
+
+       ret = do_recv_fds(worker->sockpair[0], COMMAND_OUT_FDS(cmd, run_as_ret),
+                       COMMAND_OUT_FD_COUNT(cmd));
+       if (ret < 0) {
+               PERROR("Failed to receive file descriptor from run-as worker");
+               ret = -1;
+       }
+end:
+       return ret;
+}
+
+static
+int recv_fds_from_master(run_as_worker_data *worker, struct run_as_data *data)
+{
+       int ret = 0;
+
+       if (COMMAND_USE_CWD_FD(data)) {
+               unsigned int i;
+
+               for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
+                       COMMAND_IN_FDS(data)[i] = AT_FDCWD;
+               }
+               goto end;
+       }
+
+       if (COMMAND_IN_FD_COUNT(data) == 0) {
+               goto end;
+       }
+
+       ret = do_recv_fds(worker->sockpair[1], COMMAND_IN_FDS(data),
+                       COMMAND_IN_FD_COUNT(data));
+       if (ret < 0) {
+               PERROR("Failed to receive file descriptors from master process");
+               ret = -1;
+       }
+end:
+       return ret;
+}
+
+static
+int cleanup_received_fds(struct run_as_data *data)
+{
+       int ret = 0, i;
+
+       for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) {
+               if (COMMAND_IN_FDS(data)[i] == -1) {
+                       continue;
+               }
+               ret = close(COMMAND_IN_FDS(data)[i]);
+               if (ret) {
+                       PERROR("Failed to close file descriptor received fd in run-as worker");
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+static int get_user_infos_from_uid(
+               uid_t uid, char **username, gid_t *primary_gid)
+{
+       int ret;
+       char *buf = NULL;
+       long raw_get_pw_buf_size;
+       size_t get_pw_buf_size;
+       struct passwd pwd;
+       struct passwd *result = NULL;
+
+       /* Fetch the max size for the temporary buffer. */
+       errno = 0;
+       raw_get_pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+       if (raw_get_pw_buf_size < 0) {
+               if (errno != 0) {
+                       PERROR("Failed to query _SC_GETPW_R_SIZE_MAX");
+                       goto error;
+               }
+
+               /* Limit is indeterminate. */
+               WARN("Failed to query _SC_GETPW_R_SIZE_MAX as it is "
+                       "indeterminate; falling back to default buffer size");
+               raw_get_pw_buf_size = GETPW_BUFFER_FALLBACK_SIZE;
+       }
+
+       get_pw_buf_size = (size_t) raw_get_pw_buf_size;
+
+       buf = (char *) zmalloc(get_pw_buf_size);
+       if (buf == NULL) {
+               PERROR("Failed to allocate buffer to get password file entries");
+               goto error;
+       }
+
+       ret = getpwuid_r(uid, &pwd, buf, get_pw_buf_size, &result);
+       if (ret < 0) {
+               PERROR("Failed to get user information for user:  uid = %d",
+                               (int) uid);
+               goto error;
+       }
+
+       if (result == NULL) {
+               ERR("Failed to find user information in password entries: uid = %d",
+                               (int) uid);
+               ret = -1;
+               goto error;
+       }
+
+       *username = strdup(result->pw_name);
+       if (*username == NULL) {
+               PERROR("Failed to copy user name");
+               goto error;
+       }
+
+       *primary_gid = result->pw_gid;
+
+end:
+       free(buf);
+       return ret;
+error:
+       *username = NULL;
+       *primary_gid = -1;
+       ret = -1;
+       goto end;
+}
+
+static int demote_creds(
+               uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid)
+{
+       int ret = 0;
+       gid_t primary_gid;
+       char *username = NULL;
+
+       /* Change the group id. */
+       if (prev_gid != new_gid) {
+               ret = setegid(new_gid);
+               if (ret < 0) {
+                       PERROR("Failed to set effective group id: new_gid = %d",
+                                       (int) new_gid);
+                       goto end;
+               }
+       }
+
+       /* Change the user id. */
+       if (prev_uid != new_uid) {
+               ret = get_user_infos_from_uid(new_uid, &username, &primary_gid);
+               if (ret < 0) {
+                       goto end;
+               }
+
+               /*
+                * Initialize the supplementary group access list.
+                *
+                * This is needed to handle cases where the supplementary groups
+                * of the user the process is demoting-to would give it access
+                * to a given file/folder, but not it's primary group.
+                *
+                * e.g
+                *   username: User1
+                *   Primary Group: User1
+                *   Secondary group: Disk, Network
+                *
+                *   mkdir inside the following directory must work since User1
+                *   is part of the Network group.
+                *
+                *   drwxrwx--- 2 root Network 4096 Jul 23 17:17 /tmp/my_folder/
+                *
+                *
+                * The order of the following initgroups and seteuid calls is
+                * important here;
+                * Only a root process or one with CAP_SETGID capability can
+                * call the the initgroups() function. We must initialize the
+                * supplementary groups before we change the effective
+                * UID to a less-privileged user.
+                */
+               ret = initgroups(username, primary_gid);
+               if (ret < 0) {
+                       PERROR("Failed to init the supplementary group access list: "
+                                       "username = `%s`, primary gid = %d", username,
+                                       (int) primary_gid);
+                       goto end;
+               }
+
+               ret = seteuid(new_uid);
+               if (ret < 0) {
+                       PERROR("Failed to set effective user id: new_uid = %d",
+                                       (int) new_uid);
+                       goto end;
+               }
+       }
+end:
+       free(username);
+       return ret;
+}
+
+static int promote_creds(
+               uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid)
+{
+       int ret = 0;
+       gid_t primary_gid;
+       char *username = NULL;
+
+       /* Change the group id. */
+       if (prev_gid != new_gid) {
+               ret = setegid(new_gid);
+               if (ret < 0) {
+                       PERROR("Failed to set effective group id: new_gid = %d",
+                                       (int) new_gid);
+                       goto end;
+               }
+       }
+
+       /* Change the user id. */
+       if (prev_uid != new_uid) {
+               ret = get_user_infos_from_uid(new_uid, &username, &primary_gid);
+               if (ret < 0) {
+                       goto end;
+               }
+
+               /*
+                * seteuid call must be done before the initgroups call because
+                * we need to be privileged (CAP_SETGID) to call initgroups().
+                */
+               ret = seteuid(new_uid);
+               if (ret < 0) {
+                       PERROR("Failed to set effective user id: new_uid = %d",
+                                       (int) new_uid);
+                       goto end;
+               }
+
+               /*
+                * Initialize the supplementary group access list.
+                *
+                * There is a possibility the groups we set in the following
+                * initgroups() call are not exactly the same as the ones we
+                * had when we originally demoted. This can happen if the
+                * /etc/group file is modified after the runas process is
+                * forked. This is very unlikely.
+                */
+               ret = initgroups(username, primary_gid);
+               if (ret < 0) {
+                       PERROR("Failed to init the supplementary group access "
+                                       "list: username = `%s`, primary gid = %d",
+                                       username, (int) primary_gid)
+                       goto end;
+               }
+       }
+end:
+       free(username);
+       return ret;
+}
+
+/*
+ * Return < 0 on error, 0 if OK, 1 on hangup.
+ */
+static
+int handle_one_cmd(run_as_worker_data *worker)
+{
+       int ret = 0, promote_ret;
+       struct run_as_data data = {};
+       ssize_t readlen, writelen;
+       struct run_as_ret sendret = {};
+       run_as_fct cmd;
+       const uid_t prev_ruid = getuid();
+       const gid_t prev_rgid = getgid();
+
+       /*
+        * Stage 1: Receive run_as_data struct from the master.
+        * The structure contains the command type and all the parameters needed for
+        * its execution
+        */
+       readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data,
+                       sizeof(data));
+       if (readlen == 0) {
+               /* hang up */
+               ret = 1;
+               goto end;
+       }
+       if (readlen < sizeof(data)) {
+               PERROR("lttcomm_recv_unix_sock error");
+               ret = -1;
+               goto end;
+       }
+
+       cmd = run_as_enum_to_fct(data.cmd);
+       if (!cmd) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Stage 2: Receive file descriptor from master.
+        * Some commands need a file descriptor as input so if it's needed we
+        * receive the fd using the Unix socket.
+        */
+       ret = recv_fds_from_master(worker, &data);
+       if (ret < 0) {
+               PERROR("recv_fd_from_master error");
+               ret = -1;
+               goto end;
+       }
+
+       ret = demote_creds(prev_ruid, prev_rgid, data.uid, data.gid);
+       if (ret < 0) {
+               goto write_return;
+       }
+
+       /*
+        * Also set umask to 0 for mkdir executable bit.
+        */
+       umask(0);
+
+       /*
+        * Stage 3: Execute the command
+        */
+       ret = (*cmd)(&data, &sendret);
+       if (ret < 0) {
+               DBG("Execution of command returned an error");
+       }
+
+write_return:
+       ret = cleanup_received_fds(&data);
+       if (ret < 0) {
+               ERR("Error cleaning up FD");
+               goto promote_back;
+       }
+
+       /*
+        * Stage 4: Send run_as_ret structure to the master.
+        * This structure contain the return value of the command and the errno.
+        */
+       writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
+                       sizeof(sendret));
+       if (writelen < sizeof(sendret)) {
+               PERROR("lttcomm_send_unix_sock error");
+               ret = -1;
+               goto promote_back;
+       }
+
+       /*
+        * Stage 5: Send resulting file descriptors to the master.
+        */
+       ret = send_fds_to_master(worker, data.cmd, &sendret);
+       if (ret < 0) {
+               DBG("Sending FD to master returned an error");
+       }
+
+       ret = 0;
+
+promote_back:
+       /* Return to previous uid/gid. */
+       promote_ret = promote_creds(data.uid, data.gid, prev_ruid, prev_rgid);
+       if (promote_ret < 0) {
+               ERR("Failed to promote back to the initial credentials");
+       }
+
+end:
+       return ret;
+}
+
+static
+int run_as_worker(run_as_worker_data *worker)
+{
+       int ret;
+       ssize_t writelen;
+       struct run_as_ret sendret;
+       size_t proc_orig_len;
+
+       /*
+        * Initialize worker. Set a different process cmdline.
+        */
+       proc_orig_len = strlen(worker->procname);
+       memset(worker->procname, 0, proc_orig_len);
+       strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len);
+
+       ret = lttng_thread_setname(DEFAULT_RUN_AS_WORKER_NAME);
+       if (ret && ret != -ENOSYS) {
+               /* Don't fail as this is not essential. */
+               DBG("Failed to set pthread name attribute");
+       }
+
+       memset(&sendret, 0, sizeof(sendret));
+
+       writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret,
+                       sizeof(sendret));
+       if (writelen < sizeof(sendret)) {
+               PERROR("lttcomm_send_unix_sock error");
+               ret = EXIT_FAILURE;
+               goto end;
+       }
+
+       for (;;) {
+               ret = handle_one_cmd(worker);
+               if (ret < 0) {
+                       ret = EXIT_FAILURE;
+                       goto end;
+               } else if (ret > 0) {
+                       break;
+               } else {
+                       continue;       /* Next command. */
+               }
+       }
+       ret = EXIT_SUCCESS;
+end:
+       return ret;
+}
+
+static
+int run_as_cmd(run_as_worker_data *worker,
+               enum run_as_cmd cmd,
+               struct run_as_data *data,
+               struct run_as_ret *ret_value,
+               uid_t uid, gid_t gid)
+{
+       int ret = 0;
+       ssize_t readlen, writelen;
+
+       /*
+        * If we are non-root, we can only deal with our own uid.
+        */
+       if (geteuid() != 0) {
+               if (uid != geteuid()) {
+                       ret = -1;
+                       ret_value->_errno = EPERM;
+                       ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
+                               (int) uid, (int) geteuid());
+                       goto end;
+               }
+       }
+
+       data->cmd = cmd;
+       data->uid = uid;
+       data->gid = gid;
+
+       /*
+        * Stage 1: Send the run_as_data struct to the worker process
+        */
+       writelen = lttcomm_send_unix_sock(worker->sockpair[0], data,
+                       sizeof(*data));
+       if (writelen < sizeof(*data)) {
+               PERROR("Error writing message to run_as");
+               ret = -1;
+               ret_value->_errno = EIO;
+               goto end;
+       }
+
+       /*
+        * Stage 2: Send file descriptor to the worker process if needed
+        */
+       ret = send_fds_to_worker(worker, data);
+       if (ret) {
+               PERROR("do_send_fd error");
+               ret = -1;
+               ret_value->_errno = EIO;
+               goto end;
+       }
+
+       /*
+        * Stage 3: Wait for the execution of the command
+        */
+
+       /*
+        * Stage 4: Receive the run_as_ret struct containing the return value and
+        * errno
+        */
+       readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value,
+                       sizeof(*ret_value));
+       if (!readlen) {
+               ERR("Run-as worker has hung-up during run_as_cmd");
+               ret = -1;
+               ret_value->_errno = EIO;
+               goto end;
+       } else if (readlen < sizeof(*ret_value)) {
+               PERROR("Error reading response from run_as");
+               ret = -1;
+               ret_value->_errno = errno;
+               goto end;
+       }
+
+       if (ret_value->_error) {
+               /* Skip stage 5 on error as there will be no fd to receive. */
+               goto end;
+       }
+
+       /*
+        * Stage 5: Receive file descriptor if needed
+        */
+       ret = recv_fds_from_worker(worker, cmd, ret_value);
+       if (ret < 0) {
+               ERR("Error receiving fd");
+               ret = -1;
+               ret_value->_errno = EIO;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * This is for debugging ONLY and should not be considered secure.
+ */
+static
+int run_as_noworker(enum run_as_cmd cmd,
+               struct run_as_data *data, struct run_as_ret *ret_value,
+               uid_t uid, gid_t gid)
+{
+       int ret, saved_errno;
+       mode_t old_mask;
+       run_as_fct fct;
+
+       fct = run_as_enum_to_fct(cmd);
+       if (!fct) {
+               errno = -ENOSYS;
+               ret = -1;
+               goto end;
+       }
+       old_mask = umask(0);
+       ret = fct(data, ret_value);
+       saved_errno = ret_value->_errno;
+       umask(old_mask);
+       errno = saved_errno;
+end:
+       return ret;
+}
+
+static
+int reset_sighandler(void)
+{
+       int sig;
+
+       DBG("Resetting run_as worker signal handlers to default");
+       for (sig = 1; sig <= 31; sig++) {
+               (void) signal(sig, SIG_DFL);
+       }
+       return 0;
+}
+
+static
+void worker_sighandler(int sig)
+{
+       const char *signame;
+
+       /*
+        * The worker will inherit its parent's signals since they are part of
+        * the same process group. However, in the case of SIGINT and SIGTERM,
+        * we want to give the worker a chance to teardown gracefully when its
+        * parent closes the command socket.
+        */
+       switch (sig) {
+       case SIGINT:
+               signame = "SIGINT";
+               break;
+       case SIGTERM:
+               signame = "SIGTERM";
+               break;
+       default:
+               signame = NULL;
+       }
+
+       if (signame) {
+               DBG("run_as worker received signal %s", signame);
+       } else {
+               DBG("run_as_worker received signal %d", sig);
+       }
+}
+
+static
+int set_worker_sighandlers(void)
+{
+       int ret = 0;
+       sigset_t sigset;
+       struct sigaction sa;
+
+       if ((ret = sigemptyset(&sigset)) < 0) {
+               PERROR("sigemptyset");
+               goto end;
+       }
+
+       sa.sa_handler = worker_sighandler;
+       sa.sa_mask = sigset;
+       sa.sa_flags = 0;
+       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGINT");
+               goto end;
+       }
+
+       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGTERM");
+               goto end;
+       }
+
+       DBG("run_as signal handler set for SIGTERM and SIGINT");
+end:
+       return ret;
+}
+
+static
+int run_as_create_worker_no_lock(const char *procname,
+               post_fork_cleanup_cb clean_up_func,
+               void *clean_up_user_data)
+{
+       pid_t pid;
+       int i, ret = 0;
+       ssize_t readlen;
+       struct run_as_ret recvret;
+       run_as_worker_data *worker;
+
+       LTTNG_ASSERT(!global_worker);
+       if (!use_clone()) {
+               /*
+                * Don't initialize a worker, all run_as tasks will be performed
+                * in the current process.
+                */
+               ret = 0;
+               goto end;
+       }
+       worker = (run_as_worker_data *) zmalloc(sizeof(*worker));
+       if (!worker) {
+               ret = -ENOMEM;
+               goto end;
+       }
+       worker->procname = strdup(procname);
+       if (!worker->procname) {
+               ret = -ENOMEM;
+               goto error_procname_alloc;
+       }
+       /* Create unix socket. */
+       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
+               ret = -1;
+               goto error_sock;
+       }
+
+       /* Fork worker. */
+       pid = fork();
+       if (pid < 0) {
+               PERROR("fork");
+               ret = -1;
+               goto error_fork;
+       } else if (pid == 0) {
+               /* Child */
+
+               reset_sighandler();
+
+               set_worker_sighandlers();
+
+               logger_set_thread_name("Run-as worker", true);
+
+               if (clean_up_func) {
+                       if (clean_up_func(clean_up_user_data) < 0) {
+                               ERR("Run-as post-fork clean-up failed, exiting.");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /* Just close, no shutdown. */
+               if (close(worker->sockpair[0])) {
+                       PERROR("close");
+                       exit(EXIT_FAILURE);
+               }
+
+               /*
+                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
+                * Sockpair[1] is used as a control channel with the master
+                */
+               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
+                       if (i != worker->sockpair[1]) {
+                               (void) close(i);
+                       }
+               }
+
+               worker->sockpair[0] = -1;
+               ret = run_as_worker(worker);
+               if (lttcomm_close_unix_sock(worker->sockpair[1])) {
+                       PERROR("close");
+                       ret = -1;
+               }
+               worker->sockpair[1] = -1;
+               free(worker->procname);
+               free(worker);
+               LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
+               exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
+       } else {
+               /* Parent */
+
+               /* Just close, no shutdown. */
+               if (close(worker->sockpair[1])) {
+                       PERROR("close");
+                       ret = -1;
+                       goto error_fork;
+               }
+               worker->sockpair[1] = -1;
+               worker->pid = pid;
+               /* Wait for worker to become ready. */
+               readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
+                               &recvret, sizeof(recvret));
+               if (readlen < sizeof(recvret)) {
+                       ERR("readlen: %zd", readlen);
+                       PERROR("Error reading response from run_as at creation");
+                       ret = -1;
+                       goto error_fork;
+               }
+               global_worker = worker;
+       }
+end:
+       return ret;
+
+       /* Error handling. */
+error_fork:
+       for (i = 0; i < 2; i++) {
+               if (worker->sockpair[i] < 0) {
+                       continue;
+               }
+               if (lttcomm_close_unix_sock(worker->sockpair[i])) {
+                       PERROR("close");
+               }
+               worker->sockpair[i] = -1;
+       }
+error_sock:
+       free(worker->procname);
+error_procname_alloc:
+       free(worker);
+       return ret;
+}
+
+static
+void run_as_destroy_worker_no_lock(void)
+{
+       run_as_worker_data *worker = global_worker;
+
+       DBG("Destroying run_as worker");
+       if (!worker) {
+               return;
+       }
+       /* Close unix socket */
+       DBG("Closing run_as worker socket");
+       if (lttcomm_close_unix_sock(worker->sockpair[0])) {
+               PERROR("close");
+       }
+       worker->sockpair[0] = -1;
+       /* Wait for worker. */
+       for (;;) {
+               int status;
+               pid_t wait_ret;
+
+               wait_ret = waitpid(worker->pid, &status, 0);
+               if (wait_ret < 0) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       PERROR("waitpid");
+                       break;
+               }
+
+               if (WIFEXITED(status)) {
+                       LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR,
+                                       DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d",
+                                       WEXITSTATUS(status));
+                       break;
+               } else if (WIFSIGNALED(status)) {
+                       ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d",
+                                       WTERMSIG(status));
+                       break;
+               }
+       }
+       free(worker->procname);
+       free(worker);
+       global_worker = NULL;
+}
+
+static
+int run_as_restart_worker(run_as_worker_data *worker)
+{
+       int ret = 0;
+       char *procname = NULL;
+
+       procname = worker->procname;
+
+       /* Close socket to run_as worker process and clean up the zombie process */
+       run_as_destroy_worker_no_lock();
+
+       /* Create a new run_as worker process*/
+       ret = run_as_create_worker_no_lock(procname, NULL, NULL);
+       if (ret < 0 ) {
+               ERR("Restarting the worker process failed");
+               ret = -1;
+               goto err;
+       }
+err:
+       return ret;
+}
+
+static
+int run_as(enum run_as_cmd cmd, struct run_as_data *data,
+                  struct run_as_ret *ret_value, uid_t uid, gid_t gid)
+{
+       int ret, saved_errno;
+
+       pthread_mutex_lock(&worker_lock);
+       if (use_clone()) {
+               DBG("Using run_as worker");
+
+               LTTNG_ASSERT(global_worker);
+
+               ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
+               saved_errno = ret_value->_errno;
+
+               /*
+                * If the worker thread crashed the errno is set to EIO. we log
+                * the error and  start a new worker process.
+                */
+               if (ret == -1 && saved_errno == EIO) {
+                       DBG("Socket closed unexpectedly... "
+                                       "Restarting the worker process");
+                       ret = run_as_restart_worker(global_worker);
+                       if (ret == -1) {
+                               ERR("Failed to restart worker process.");
+                               goto err;
+                       }
+               }
+       } else {
+               DBG("Using run_as without worker");
+               ret = run_as_noworker(cmd, data, ret_value, uid, gid);
+       }
+err:
+       pthread_mutex_unlock(&worker_lock);
+       return ret;
+}
+
+int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid);
+}
+
+int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode,
+               uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) mode, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.mkdir.path, path,
+                       sizeof(data.u.mkdir.path));
+       if (ret) {
+               ERR("Failed to copy path argument of mkdirat recursive command");
+               goto error;
+       }
+       data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0';
+       data.u.mkdir.mode = mode;
+       data.u.mkdir.dirfd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid);
+}
+
+int run_as_mkdirat(int dirfd, const char *path, mode_t mode,
+               uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) mode, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.mkdir.path, path,
+                       sizeof(data.u.mkdir.path));
+       if (ret) {
+               ERR("Failed to copy path argument of mkdirat command");
+               goto error;
+       }
+       data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0';
+       data.u.mkdir.mode = mode;
+       data.u.mkdir.dirfd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_open(const char *path, int flags, mode_t mode, uid_t uid,
+               gid_t gid)
+{
+       return run_as_openat(AT_FDCWD, path, flags, mode, uid, gid);
+}
+
+int run_as_openat(int dirfd, const char *path, int flags, mode_t mode,
+               uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("openat() fd = %d%s, path = %s, flags = %X, mode = %d, uid %d, gid %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, flags, (int) mode, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.open.path, path, sizeof(data.u.open.path));
+       if (ret) {
+               ERR("Failed to copy path argument of open command");
+               goto error;
+       }
+       data.u.open.flags = flags;
+       data.u.open.mode = mode;
+       data.u.open.dirfd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_OPEN : RUN_AS_OPENAT,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret < 0 ? run_as_ret.u.ret :
+                       run_as_ret.u.open.fd;
+error:
+       return ret;
+}
+
+int run_as_unlink(const char *path, uid_t uid, gid_t gid)
+{
+       return run_as_unlinkat(AT_FDCWD, path, uid, gid);
+}
+
+int run_as_unlinkat(int dirfd, const char *path, uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("unlinkat() fd = %d%s, path = %s, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.unlink.path, path,
+                       sizeof(data.u.unlink.path));
+       if (ret) {
+               goto error;
+       }
+       data.u.unlink.dirfd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_UNLINK : RUN_AS_UNLINKAT, &data,
+                       &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_rmdir(const char *path, uid_t uid, gid_t gid)
+{
+       return run_as_rmdirat(AT_FDCWD, path, uid, gid);
+}
+
+int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("rmdirat() fd = %d%s, path = %s, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.rmdir.path, path,
+                       sizeof(data.u.rmdir.path));
+       if (ret) {
+               goto error;
+       }
+       data.u.rmdir.dirfd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR : RUN_AS_RMDIRAT, &data,
+                       &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags)
+{
+       return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags);
+}
+
+int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("rmdirat() recursive fd = %d%s, path = %s, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.rmdir.path, path,
+                       sizeof(data.u.rmdir.path));
+       if (ret) {
+               goto error;
+       }
+       data.u.rmdir.dirfd = dirfd;
+       data.u.rmdir.flags = flags;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_rename(const char *old_name, const char *new_name, uid_t uid, gid_t gid)
+{
+       return run_as_renameat(AT_FDCWD, old_name, AT_FDCWD, new_name, uid, gid);
+}
+
+int run_as_renameat(int old_dirfd, const char *old_name,
+               int new_dirfd, const char *new_name, uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("renameat() old_dirfd = %d%s, old_name = %s, new_dirfd = %d%s, new_name = %s, uid = %d, gid = %d",
+                       old_dirfd, old_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       old_name,
+                       new_dirfd, new_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       new_name, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.rename.old_path, old_name,
+                       sizeof(data.u.rename.old_path));
+       if (ret) {
+               goto error;
+       }
+       ret = lttng_strncpy(data.u.rename.new_path, new_name,
+                       sizeof(data.u.rename.new_path));
+       if (ret) {
+               goto error;
+       }
+
+       data.u.rename.dirfds[0] = old_dirfd;
+       data.u.rename.dirfds[1] = new_dirfd;
+       run_as(old_dirfd == AT_FDCWD && new_dirfd == AT_FDCWD ?
+                       RUN_AS_RENAME : RUN_AS_RENAMEAT,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.ret;
+error:
+       return ret;
+}
+
+int run_as_extract_elf_symbol_offset(int fd, const char* function,
+               uid_t uid, gid_t gid, uint64_t *offset)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("extract_elf_symbol_offset() on fd=%d and function=%s "
+                       "with for uid %d and gid %d", fd, function,
+                       (int) uid, (int) gid);
+
+       data.u.extract_elf_symbol_offset.fd = fd;
+
+       strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1);
+       data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
+       ret = lttng_strncpy(data.u.extract_elf_symbol_offset.function,
+                       function,
+                       sizeof(data.u.extract_elf_symbol_offset.function));
+       if (ret) {
+               goto error;
+       }
+
+       run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       if (run_as_ret._error) {
+               ret = -1;
+               goto error;
+       }
+
+       *offset = run_as_ret.u.extract_elf_symbol_offset.offset;
+error:
+       return ret;
+}
+
+int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name,
+               const char* probe_name, uid_t uid, gid_t gid,
+               uint64_t **offsets, uint32_t *num_offset)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+
+       DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and "
+                       "provider_name=%s with for uid %d and gid %d", fd,
+                       probe_name, provider_name, (int) uid, (int) gid);
+
+       data.u.extract_sdt_probe_offsets.fd = fd;
+
+       ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name,
+                       sizeof(data.u.extract_sdt_probe_offsets.probe_name));
+       if (ret) {
+               goto error;
+       }
+       ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.provider_name,
+                       provider_name,
+                       sizeof(data.u.extract_sdt_probe_offsets.provider_name));
+       if (ret) {
+               goto error;
+       }
+
+       run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       if (run_as_ret._error) {
+               ret = -1;
+               goto error;
+       }
+
+       *num_offset = run_as_ret.u.extract_sdt_probe_offsets.num_offset;
+       *offsets = (uint64_t *) zmalloc(*num_offset * sizeof(uint64_t));
+       if (!*offsets) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       memcpy(*offsets, run_as_ret.u.extract_sdt_probe_offsets.offsets,
+                       *num_offset * sizeof(uint64_t));
+error:
+       return ret;
+}
+
+int run_as_generate_filter_bytecode(const char *filter_expression,
+               const struct lttng_credentials *creds,
+               struct lttng_bytecode **bytecode)
+{
+       int ret;
+       struct run_as_data data = {};
+       struct run_as_ret run_as_ret = {};
+       const struct lttng_bytecode *view_bytecode = NULL;
+       struct lttng_bytecode *local_bytecode = NULL;
+       const uid_t uid = lttng_credentials_get_uid(creds);
+       const gid_t gid = lttng_credentials_get_gid(creds);
+
+       DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d",
+                       filter_expression, (int) uid, (int) gid);
+
+       ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression,
+                       sizeof(data.u.generate_filter_bytecode.filter_expression));
+       if (ret) {
+               goto error;
+       }
+
+       run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       if (run_as_ret._error) {
+               ret = -1;
+               goto error;
+       }
+
+       view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode;
+
+       local_bytecode = (lttng_bytecode *) zmalloc(sizeof(*local_bytecode) + view_bytecode->len);
+       if (!local_bytecode) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode,
+                       sizeof(*local_bytecode) + view_bytecode->len);
+       *bytecode = local_bytecode;
+error:
+       return ret;
+}
+
+int run_as_create_worker(const char *procname,
+               post_fork_cleanup_cb clean_up_func,
+               void *clean_up_user_data)
+{
+       int ret;
+
+       pthread_mutex_lock(&worker_lock);
+       ret = run_as_create_worker_no_lock(procname, clean_up_func,
+                       clean_up_user_data);
+       pthread_mutex_unlock(&worker_lock);
+       return ret;
+}
+
+void run_as_destroy_worker(void)
+{
+       pthread_mutex_lock(&worker_lock);
+       run_as_destroy_worker_no_lock();
+       pthread_mutex_unlock(&worker_lock);
+}
diff --git a/src/common/session-descriptor.c b/src/common/session-descriptor.c
deleted file mode 100644 (file)
index 93c7dfa..0000000
+++ /dev/null
@@ -1,1155 +0,0 @@
-/*
- * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- */
-
-#include <lttng/session-descriptor-internal.h>
-#include <common/macros.h>
-#include <common/uri.h>
-#include <common/defaults.h>
-#include <common/error.h>
-#include <time.h>
-#include <stdio.h>
-
-struct lttng_session_descriptor_network_location {
-       struct lttng_uri *control;
-       struct lttng_uri *data;
-};
-
-struct lttng_session_descriptor {
-       enum lttng_session_descriptor_type type;
-       /*
-        * If an output type that is not OUTPUT_TYPE_NONE is specified,
-        * it means that an output of that type must be generated at
-        * session-creation time.
-        */
-       enum lttng_session_descriptor_output_type output_type;
-       char *name;
-       union {
-               struct lttng_session_descriptor_network_location network;
-               struct lttng_uri *local;
-       } output;
-};
-
-struct lttng_session_descriptor_snapshot {
-       struct lttng_session_descriptor base;
-       /*
-        * Assumes at-most one snapshot output is supported. Uses
-        * the output field of the base class.
-        */
-};
-
-struct lttng_session_descriptor_live {
-       struct lttng_session_descriptor base;
-       unsigned long long live_timer_us;
-};
-
-struct lttng_session_descriptor_comm {
-       /* enum lttng_session_descriptor_type */
-       uint8_t type;
-       /* enum lttng_session_descriptor_output_type */
-       uint8_t output_type;
-       /* Includes trailing null. */
-       uint32_t name_len;
-       /* Name follows, followed by URIs */
-       uint8_t uri_count;
-} LTTNG_PACKED;
-
-struct lttng_session_descriptor_live_comm {
-       struct lttng_session_descriptor_comm base;
-       /* Live-specific parameters. */
-       uint64_t live_timer_us;
-} LTTNG_PACKED;
-
-static
-struct lttng_uri *uri_copy(const struct lttng_uri *uri)
-{
-       struct lttng_uri *new_uri = NULL;
-
-       if (!uri) {
-               goto end;
-       }
-
-       new_uri = zmalloc(sizeof(*new_uri));
-       if (!new_uri) {
-               goto end;
-       }
-       memcpy(new_uri, uri, sizeof(*new_uri));
-end:
-       return new_uri;
-}
-
-static
-struct lttng_uri *uri_from_path(const char *path)
-{
-       struct lttng_uri *uris = NULL;
-       ssize_t uri_count;
-       char local_protocol_string[LTTNG_PATH_MAX + sizeof("file://")] =
-                       "file://";
-
-       if (strlen(path) >= LTTNG_PATH_MAX) {
-               goto end;
-       }
-
-       if (path[0] != '/') {
-               /* Not an absolute path. */
-               goto end;
-       }
-
-       strncat(local_protocol_string, path, LTTNG_PATH_MAX);
-       uri_count = uri_parse(local_protocol_string, &uris);
-       if (uri_count != 1) {
-               goto error;
-       }
-       if (uris[0].dtype != LTTNG_DST_PATH) {
-               goto error;
-       }
-
-end:
-       return uris;
-error:
-       free(uris);
-       return NULL;
-}
-
-static
-void network_location_fini(
-               struct lttng_session_descriptor_network_location *location)
-{
-       free(location->control);
-       free(location->data);
-}
-
-/* Assumes ownership of control and data. */
-static
-int network_location_set_from_lttng_uris(
-               struct lttng_session_descriptor_network_location *location,
-               struct lttng_uri *control, struct lttng_uri *data)
-{
-       int ret = 0;
-
-       if (!control && !data) {
-               goto end;
-       }
-
-       if (!(control && data)) {
-               /* None or both must be set. */
-               ret = -1;
-               goto end;
-       }
-
-       if (control->stype != LTTNG_STREAM_CONTROL ||
-                       data->stype != LTTNG_STREAM_DATA) {
-               ret = -1;
-               goto end;
-       }
-
-       free(location->control);
-       free(location->data);
-       location->control = control;
-       location->data = data;
-       control = NULL;
-       data = NULL;
-end:
-       free(control);
-       free(data);
-       return ret;
-}
-
-static
-int network_location_set_from_uri_strings(
-               struct lttng_session_descriptor_network_location *location,
-               const char *control, const char *data)
-{
-       int ret = 0;
-       ssize_t uri_count;
-       struct lttng_uri *parsed_uris = NULL;
-       struct lttng_uri *control_uri = NULL;
-       struct lttng_uri *data_uri = NULL;
-
-       uri_count = uri_parse_str_urls(control, data, &parsed_uris);
-       if (uri_count != 2 && uri_count != 0) {
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * uri_parse_str_urls returns a contiguous array of lttng_uris whereas
-        * session descriptors expect individually allocated lttng_uris.
-        */
-       if (uri_count == 2) {
-               control_uri = zmalloc(sizeof(*control_uri));
-               data_uri = zmalloc(sizeof(*data_uri));
-               if (!control_uri || !data_uri) {
-                       ret = -1;
-                       goto end;
-               }
-               memcpy(control_uri, &parsed_uris[0], sizeof(*control_uri));
-               memcpy(data_uri, &parsed_uris[1], sizeof(*data_uri));
-       }
-
-       /* Ownership of control and data uris is transferred. */
-       ret = network_location_set_from_lttng_uris(
-                       location,
-                       control_uri,
-                       data_uri);
-       control_uri = NULL;
-       data_uri = NULL;
-end:
-       free(parsed_uris);
-       free(control_uri);
-       free(data_uri);
-       return ret;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_create(const char *name)
-{
-       struct lttng_session_descriptor *descriptor;
-
-       descriptor = zmalloc(sizeof(*descriptor));
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
-       descriptor->output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
-       if (lttng_session_descriptor_set_session_name(descriptor, name)) {
-               goto error;
-       }
-       return descriptor;
-error:
-       lttng_session_descriptor_destroy(descriptor);
-       return NULL;
-}
-
-/* Ownership of uri is transferred. */
-static
-struct lttng_session_descriptor *
-_lttng_session_descriptor_local_create(const char *name,
-               struct lttng_uri *uri)
-{
-       struct lttng_session_descriptor *descriptor;
-
-       descriptor = lttng_session_descriptor_create(name);
-       if (!descriptor) {
-               goto error;
-       }
-       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
-       descriptor->output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL;
-       if (uri) {
-               if (uri->dtype != LTTNG_DST_PATH) {
-                       goto error;
-               }
-               descriptor->output.local = uri;
-               uri = NULL;
-       }
-       return descriptor;
-error:
-       free(uri);
-       lttng_session_descriptor_destroy(descriptor);
-       return NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_local_create(const char *name, const char *path)
-{
-       struct lttng_uri *uri = NULL;
-       struct lttng_session_descriptor *descriptor;
-
-       if (path) {
-               uri = uri_from_path(path);
-               if (!uri) {
-                       goto error;
-               }
-       }
-       descriptor = _lttng_session_descriptor_local_create(name, uri);
-       return descriptor;
-error:
-       return NULL;
-}
-
-/* Assumes the ownership of both uris. */
-static
-struct lttng_session_descriptor *
-_lttng_session_descriptor_network_create(const char *name,
-               struct lttng_uri *control, struct lttng_uri *data)
-{
-       int ret;
-       struct lttng_session_descriptor *descriptor;
-
-       descriptor = lttng_session_descriptor_create(name);
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
-       descriptor->output_type = LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
-       /* Assumes the ownership of both uris. */
-       ret = network_location_set_from_lttng_uris(&descriptor->output.network,
-                       control, data);
-       control = NULL;
-       data = NULL;
-       if (ret) {
-               goto error;
-       }
-       return descriptor;
-error:
-       lttng_session_descriptor_destroy(descriptor);
-       free(control);
-       free(data);
-       return NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_network_create(const char *name,
-               const char *control_url, const char *data_url)
-{
-       int ret;
-       struct lttng_session_descriptor *descriptor;
-
-       descriptor = _lttng_session_descriptor_network_create(name,
-                       NULL, NULL);
-       if (!descriptor) {
-               goto error;
-       }
-
-       ret = network_location_set_from_uri_strings(&descriptor->output.network,
-                       control_url, data_url);
-       if (ret) {
-               goto error;
-       }
-       return descriptor;
-error:
-       lttng_session_descriptor_destroy(descriptor);
-       return NULL;
-}
-
-static
-struct lttng_session_descriptor_snapshot *
-_lttng_session_descriptor_snapshot_create(const char *name)
-{
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       descriptor = zmalloc(sizeof(*descriptor));
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT;
-       descriptor->base.output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
-       if (lttng_session_descriptor_set_session_name(&descriptor->base,
-                       name)) {
-               goto error;
-       }
-       return descriptor;
-error:
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-/* Ownership of control and data is transferred. */
-static
-struct lttng_session_descriptor_snapshot *
-_lttng_session_descriptor_snapshot_network_create(const char *name,
-               struct lttng_uri *control, struct lttng_uri *data)
-{
-       int ret;
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       descriptor = _lttng_session_descriptor_snapshot_create(name);
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->base.output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
-       /* Ownership of control and data is transferred. */
-       ret = network_location_set_from_lttng_uris(
-                       &descriptor->base.output.network,
-                       control, data);
-       control = NULL;
-       data = NULL;
-       if (ret) {
-               goto error;
-       }
-       return descriptor;
-error:
-       free(control);
-       free(data);
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_snapshot_create(const char *name)
-{
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       descriptor = _lttng_session_descriptor_snapshot_create(name);
-       return descriptor ? &descriptor->base : NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_snapshot_network_create(const char *name,
-               const char *control_url, const char *data_url)
-{
-       int ret;
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       descriptor = _lttng_session_descriptor_snapshot_network_create(name,
-                       NULL, NULL);
-       if (!descriptor) {
-               goto error;
-       }
-
-       ret = network_location_set_from_uri_strings(
-                       &descriptor->base.output.network,
-                       control_url, data_url);
-       if (ret) {
-               goto error;
-       }
-       return &descriptor->base;
-error:
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-/* Ownership of uri is transferred. */
-static
-struct lttng_session_descriptor_snapshot *
-_lttng_session_descriptor_snapshot_local_create(const char *name,
-               struct lttng_uri *uri)
-{
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       descriptor = _lttng_session_descriptor_snapshot_create(name);
-       if (!descriptor) {
-               goto error;
-       }
-       descriptor->base.output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL;
-       if (uri) {
-               if (uri->dtype != LTTNG_DST_PATH) {
-                       goto error;
-               }
-               descriptor->base.output.local = uri;
-               uri = NULL;
-       }
-       return descriptor;
-error:
-       free(uri);
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_snapshot_local_create(const char *name,
-               const char *path)
-{
-       struct lttng_uri *path_uri = NULL;
-       struct lttng_session_descriptor_snapshot *descriptor;
-
-       if (path) {
-               path_uri = uri_from_path(path);
-               if (!path_uri) {
-                       goto error;
-               }
-       }
-       descriptor = _lttng_session_descriptor_snapshot_local_create(name,
-                       path_uri);
-       return descriptor ? &descriptor->base : NULL;
-error:
-       return NULL;
-}
-
-static
-struct lttng_session_descriptor_live *
-_lttng_session_descriptor_live_create(const char *name,
-               unsigned long long live_timer_interval_us)
-{
-       struct lttng_session_descriptor_live *descriptor = NULL;
-
-       if (live_timer_interval_us == 0) {
-               goto error;
-       }
-       descriptor = zmalloc(sizeof(*descriptor));
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE;
-       descriptor->base.output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
-       descriptor->live_timer_us = live_timer_interval_us;
-       if (lttng_session_descriptor_set_session_name(&descriptor->base,
-                       name)) {
-               goto error;
-       }
-
-       return descriptor;
-error:
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-/* Ownership of control and data is transferred. */
-static
-struct lttng_session_descriptor_live *
-_lttng_session_descriptor_live_network_create(
-               const char *name,
-               struct lttng_uri *control, struct lttng_uri *data,
-               unsigned long long live_timer_interval_us)
-{
-       int ret;
-       struct lttng_session_descriptor_live *descriptor;
-
-       descriptor = _lttng_session_descriptor_live_create(name,
-                       live_timer_interval_us);
-       if (!descriptor) {
-               goto error;
-       }
-
-       descriptor->base.output_type =
-                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
-
-       /* Ownerwhip of control and data is transferred. */
-       ret = network_location_set_from_lttng_uris(
-                       &descriptor->base.output.network,
-                       control, data);
-       control = NULL;
-       data = NULL;
-       if (ret) {
-               goto error;
-       }
-       return descriptor;
-error:
-       free(control);
-       free(data);
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_live_create(
-               const char *name,
-               unsigned long long live_timer_us)
-{
-       struct lttng_session_descriptor_live *descriptor;
-
-       descriptor = _lttng_session_descriptor_live_create(name, live_timer_us);
-
-       return descriptor ? &descriptor->base : NULL;
-}
-
-struct lttng_session_descriptor *
-lttng_session_descriptor_live_network_create(
-               const char *name,
-               const char *control_url, const char *data_url,
-               unsigned long long live_timer_us)
-{
-       int ret;
-       struct lttng_session_descriptor_live *descriptor;
-
-       descriptor = _lttng_session_descriptor_live_network_create(name,
-                       NULL, NULL, live_timer_us);
-       if (!descriptor) {
-               goto error;
-       }
-
-       ret = network_location_set_from_uri_strings(
-                       &descriptor->base.output.network,
-                       control_url, data_url);
-       if (ret) {
-               goto error;
-       }
-       return &descriptor->base;
-error:
-       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
-       return NULL;
-}
-
-void lttng_session_descriptor_destroy(
-               struct lttng_session_descriptor *descriptor)
-{
-       if (!descriptor) {
-               return;
-       }
-
-       switch (descriptor->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               free(descriptor->output.local);
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-               network_location_fini(&descriptor->output.network);
-               break;
-       default:
-               abort();
-       }
-
-       free(descriptor->name);
-       free(descriptor);
-}
-
-ssize_t lttng_session_descriptor_create_from_buffer(
-               const struct lttng_buffer_view *payload,
-               struct lttng_session_descriptor **descriptor)
-{
-       int i;
-       ssize_t offset = 0, ret;
-       struct lttng_buffer_view current_view;
-       const char *name = NULL;
-       const struct lttng_session_descriptor_comm *base_header;
-       size_t max_expected_uri_count;
-       uint64_t live_timer_us = 0;
-       struct lttng_uri *uris[2] = {};
-       enum lttng_session_descriptor_type type;
-       enum lttng_session_descriptor_output_type output_type;
-
-       current_view = lttng_buffer_view_from_view(payload, offset,
-                       sizeof(*base_header));
-       if (!lttng_buffer_view_is_valid(&current_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       base_header = (typeof(base_header)) current_view.data;
-       switch (base_header->type) {
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
-       {
-               const struct lttng_session_descriptor_live_comm *live_header;
-
-               current_view = lttng_buffer_view_from_view(payload, offset,
-                               sizeof(*live_header));
-               if (!lttng_buffer_view_is_valid(&current_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               live_header = (typeof(live_header)) current_view.data;
-               live_timer_us = live_header->live_timer_us;
-               break;
-       }
-       default:
-               ret = -1;
-               goto end;
-       }
-       /* type has been validated. */
-       type = base_header->type;
-
-       switch (base_header->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               max_expected_uri_count = 0;
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               max_expected_uri_count = 1;
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-               max_expected_uri_count = 2;
-               break;
-       default:
-               ret = -1;
-               goto end;
-       }
-       /* output_type has been validated. */
-       output_type = base_header->output_type;
-
-       /* Skip after header. */
-       offset += current_view.size;
-       if (!base_header->name_len) {
-               goto skip_name;
-       }
-
-       /* Map the name. */
-       current_view = lttng_buffer_view_from_view(payload, offset,
-                       base_header->name_len);
-       if (!lttng_buffer_view_is_valid(&current_view)) {
-               ret = -1;
-               goto end;
-       }
-
-       name = current_view.data;
-       if (base_header->name_len == 1 ||
-                       name[base_header->name_len - 1] ||
-                       strlen(name) != base_header->name_len - 1) {
-               /*
-                * Check that the name is not NULL, is NULL-terminated, and
-                * does not contain a NULL before the last byte.
-                */
-               ret = -1;
-               goto end;
-       }
-
-       /* Skip after the name. */
-       offset += base_header->name_len;
-skip_name:
-       if (base_header->uri_count > max_expected_uri_count) {
-               ret = -1;
-               goto end;
-       }
-
-       for (i = 0; i < base_header->uri_count; i++) {
-               struct lttng_uri *uri;
-
-               /* Map a URI. */
-               current_view = lttng_buffer_view_from_view(payload,
-                               offset, sizeof(*uri));
-               if (!lttng_buffer_view_is_valid(&current_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               uri = (typeof(uri)) current_view.data;
-               uris[i] = zmalloc(sizeof(*uri));
-               if (!uris[i]) {
-                       ret = -1;
-                       goto end;
-               }
-               memcpy(uris[i], uri, sizeof(*uri));
-               offset += sizeof(*uri);
-       }
-
-       switch (type) {
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
-               switch (output_type) {
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-                       *descriptor = lttng_session_descriptor_create(name);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-                       *descriptor = _lttng_session_descriptor_local_create(
-                                       name, uris[0]);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-                       *descriptor = _lttng_session_descriptor_network_create(
-                                       name, uris[0], uris[1]);
-                       break;
-               default:
-                       /* Already checked. */
-                       abort();
-               }
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
-       {
-               struct lttng_session_descriptor_snapshot *snapshot;
-               switch (output_type) {
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-                       snapshot = _lttng_session_descriptor_snapshot_create(
-                                       name);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-                       snapshot = _lttng_session_descriptor_snapshot_local_create(
-                                       name, uris[0]);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-                       snapshot = _lttng_session_descriptor_snapshot_network_create(
-                                       name, uris[0], uris[1]);
-                       break;
-               default:
-                       /* Already checked. */
-                       abort();
-               }
-               *descriptor = snapshot ? &snapshot->base : NULL;
-               break;
-       }
-       case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
-       {
-               struct lttng_session_descriptor_live *live;
-
-               switch (output_type) {
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-                       live = _lttng_session_descriptor_live_create(
-                                       name, live_timer_us);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-                       live = _lttng_session_descriptor_live_network_create(
-                                       name, uris[0], uris[1],
-                                       live_timer_us);
-                       break;
-               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-                       ret = -1;
-                       goto end;
-               default:
-                       /* Already checked. */
-                       abort();
-               }
-               *descriptor = live ? &live->base : NULL;
-               break;
-       }
-       default:
-               /* Already checked. */
-               abort();
-       }
-       memset(uris, 0, sizeof(uris));
-       if (!*descriptor) {
-               ret = -1;
-               goto end;
-       }
-
-       ret = offset;
-end:
-       free(uris[0]);
-       free(uris[1]);
-       return ret;
-}
-
-int lttng_session_descriptor_serialize(
-               const struct lttng_session_descriptor *descriptor,
-               struct lttng_dynamic_buffer *buffer)
-{
-       int ret, i;
-       /* There are, at most, two URIs to serialize. */
-       struct lttng_uri *uris[2] = {};
-       size_t uri_count = 0;
-       /* The live header is a superset of all headers. */
-       struct lttng_session_descriptor_live_comm header = {
-               .base.type = (uint8_t) descriptor->type,
-               .base.output_type = (uint8_t) descriptor->output_type,
-               .base.name_len = descriptor->name ?
-                               strlen(descriptor->name) + 1 : 0,
-       };
-       const void *header_ptr = NULL;
-       size_t header_size;
-
-       switch (descriptor->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               uris[0] = descriptor->output.local;
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-               uris[0] = descriptor->output.network.control;
-               uris[1] = descriptor->output.network.data;
-               break;
-       default:
-               ret = -1;
-               goto end;
-       }
-       uri_count += !!uris[0];
-       uri_count += !!uris[1];
-
-       header.base.uri_count = uri_count;
-       if (descriptor->type == LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE) {
-               const struct lttng_session_descriptor_live *live =
-                               container_of(descriptor, typeof(*live),
-                               base);
-
-               header.live_timer_us = live->live_timer_us;
-               header_ptr = &header;
-               header_size = sizeof(header);
-       } else {
-               header_ptr = &header.base;
-               header_size = sizeof(header.base);
-       }
-
-       ret = lttng_dynamic_buffer_append(buffer, header_ptr, header_size);
-       if (ret) {
-               goto end;
-       }
-       if (header.base.name_len) {
-               ret = lttng_dynamic_buffer_append(buffer, descriptor->name,
-                               header.base.name_len);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       for (i = 0; i < uri_count; i++) {
-               ret = lttng_dynamic_buffer_append(buffer, uris[i],
-                               sizeof(struct lttng_uri));
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-enum lttng_session_descriptor_type
-lttng_session_descriptor_get_type(
-               const struct lttng_session_descriptor *descriptor)
-{
-       return descriptor->type;
-}
-
-enum lttng_session_descriptor_output_type
-lttng_session_descriptor_get_output_type(
-               const struct lttng_session_descriptor *descriptor)
-{
-       return descriptor->output_type;
-}
-
-void lttng_session_descriptor_get_local_output_uri(
-               const struct lttng_session_descriptor *descriptor,
-               struct lttng_uri *local_uri)
-{
-       memcpy(local_uri, descriptor->output.local, sizeof(*local_uri));
-}
-
-void lttng_session_descriptor_get_network_output_uris(
-               const struct lttng_session_descriptor *descriptor,
-               struct lttng_uri *control,
-               struct lttng_uri *data)
-{
-       memcpy(control, descriptor->output.network.control, sizeof(*control));
-       memcpy(data, descriptor->output.network.data, sizeof(*data));
-}
-
-unsigned long long
-lttng_session_descriptor_live_get_timer_interval(
-               const struct lttng_session_descriptor *descriptor)
-{
-       struct lttng_session_descriptor_live *live;
-
-       live = container_of(descriptor, typeof(*live), base);
-       return live->live_timer_us;
-}
-
-enum lttng_session_descriptor_status
-lttng_session_descriptor_get_session_name(
-               const struct lttng_session_descriptor *descriptor,
-               const char **session_name)
-{
-       enum lttng_session_descriptor_status status;
-
-       if (!descriptor || !session_name) {
-               status = LTTNG_SESSION_DESCRIPTOR_STATUS_INVALID;
-               goto end;
-       }
-
-       *session_name = descriptor->name;
-       status = descriptor->name ?
-                       LTTNG_SESSION_DESCRIPTOR_STATUS_OK :
-                       LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET;
-end:
-       return status;
-}
-
-int lttng_session_descriptor_set_session_name(
-               struct lttng_session_descriptor *descriptor,
-               const char *name)
-{
-       int ret = 0;
-       char *new_name;
-
-       if (!name) {
-               goto end;
-       }
-       if (strlen(name) >= LTTNG_NAME_MAX) {
-               ret = -1;
-               goto end;
-       }
-       new_name = strdup(name);
-       if (!new_name) {
-               ret = -1;
-               goto end;
-       }
-       free(descriptor->name);
-       descriptor->name = new_name;
-end:
-       return ret;
-}
-
-bool lttng_session_descriptor_is_output_destination_initialized(
-               const struct lttng_session_descriptor *descriptor)
-{
-       switch (descriptor->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               return true;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               return descriptor->output.local;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-               return descriptor->output.network.control;
-       default:
-               abort();
-       }
-}
-
-bool lttng_session_descriptor_has_output_directory(
-               const struct lttng_session_descriptor *descriptor)
-{
-       switch (descriptor->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               if (descriptor->output.local) {
-                       return *descriptor->output.local->dst.path;
-               }
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-               if (descriptor->output.network.control) {
-                       return *descriptor->output.network.control->subdir;
-               }
-               break;
-       default:
-               abort();
-       }
-       return false;
-}
-
-enum lttng_error_code lttng_session_descriptor_set_default_output(
-               struct lttng_session_descriptor *descriptor,
-               time_t *session_creation_time,
-               const char *absolute_home_path)
-{
-       enum lttng_error_code ret_code = LTTNG_OK;
-       struct lttng_uri *uris = NULL;
-
-       switch (descriptor->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               goto end;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-       {
-               int ret;
-               ssize_t uri_ret;
-               char local_uri[LTTNG_PATH_MAX];
-               char creation_datetime_suffix[17] = {};
-
-               if (session_creation_time) {
-                       size_t strftime_ret;
-                       struct tm *timeinfo;
-
-                       timeinfo = localtime(session_creation_time);
-                       if (!timeinfo) {
-                               ret_code = LTTNG_ERR_FATAL;
-                               goto end;
-                       }
-                       strftime_ret = strftime(creation_datetime_suffix,
-                                       sizeof(creation_datetime_suffix),
-                                       "-%Y%m%d-%H%M%S", timeinfo);
-                       if (strftime_ret == 0) {
-                               ERR("Failed to format session creation timestamp while setting default local output destination");
-                               ret_code = LTTNG_ERR_FATAL;
-                               goto end;
-                       }
-               }
-               LTTNG_ASSERT(descriptor->name);
-               ret = snprintf(local_uri, sizeof(local_uri),
-                               "file://%s/%s/%s%s",
-                               absolute_home_path,
-                               DEFAULT_TRACE_DIR_NAME, descriptor->name,
-                               creation_datetime_suffix);
-               if (ret >= sizeof(local_uri)) {
-                       ERR("Truncation occurred while setting default local output destination");
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               } else if (ret < 0) {
-                       PERROR("Failed to format default local output URI");
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               }
-
-               uri_ret = uri_parse(local_uri, &uris);
-               if (uri_ret != 1) {
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               }
-               free(descriptor->output.local);
-               descriptor->output.local = &uris[0];
-               uris = NULL;
-               break;
-       }
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-       {
-               int ret;
-               ssize_t uri_ret;
-               struct lttng_uri *control = NULL, *data = NULL;
-
-               uri_ret = uri_parse_str_urls("net://127.0.0.1", NULL, &uris);
-               if (uri_ret != 2) {
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               }
-
-               control = uri_copy(&uris[0]);
-               data = uri_copy(&uris[1]);
-               if (!control || !data) {
-                       free(control);
-                       free(data);
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               }
-
-               /* Ownership of uris is transferred. */
-               ret = network_location_set_from_lttng_uris(
-                               &descriptor->output.network,
-                               control, data);
-               if (ret) {
-                       abort();
-                       ret_code = LTTNG_ERR_SET_URL;
-                       goto end;
-               }
-               break;
-       }
-       default:
-               abort();
-       }
-end:
-       free(uris);
-       return ret_code;
-}
-
-/*
- * Note that only properties that can be populated by the session daemon
- * (output destination and name) are assigned.
- */
-int lttng_session_descriptor_assign(
-               struct lttng_session_descriptor *dst,
-               const struct lttng_session_descriptor *src)
-{
-       int ret = 0;
-
-       if (dst->type != src->type) {
-               ret = -1;
-               goto end;
-       }
-       if (dst->output_type != src->output_type) {
-               ret = -1;
-               goto end;
-       }
-       ret = lttng_session_descriptor_set_session_name(dst, src->name);
-       if (ret) {
-               goto end;
-       }
-       switch (dst->output_type) {
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
-               free(dst->output.local);
-               dst->output.local = uri_copy(src->output.local);
-               if (!dst->output.local) {
-                       ret = -1;
-                       goto end;
-               }
-               break;
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
-       {
-               struct lttng_uri *control_copy = NULL, *data_copy = NULL;
-
-               control_copy = uri_copy(dst->output.network.control);
-               if (!control_copy && dst->output.network.control) {
-                       ret = -1;
-                       goto end;
-               }
-               data_copy = uri_copy(dst->output.network.data);
-               if (!data_copy && dst->output.network.data) {
-                       free(control_copy);
-                       ret = -1;
-                       goto end;
-               }
-               ret = network_location_set_from_lttng_uris(&dst->output.network,
-                               control_copy, data_copy);
-               break;
-       }
-       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
-               goto end;
-       }
-end:
-       return ret;
-}
diff --git a/src/common/session-descriptor.cpp b/src/common/session-descriptor.cpp
new file mode 100644 (file)
index 0000000..ec510b0
--- /dev/null
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <lttng/session-descriptor-internal.h>
+#include <common/macros.h>
+#include <common/uri.h>
+#include <common/defaults.h>
+#include <common/error.h>
+#include <time.h>
+#include <stdio.h>
+
+struct lttng_session_descriptor_network_location {
+       struct lttng_uri *control;
+       struct lttng_uri *data;
+};
+
+struct lttng_session_descriptor {
+       enum lttng_session_descriptor_type type;
+       /*
+        * If an output type that is not OUTPUT_TYPE_NONE is specified,
+        * it means that an output of that type must be generated at
+        * session-creation time.
+        */
+       enum lttng_session_descriptor_output_type output_type;
+       char *name;
+       union {
+               struct lttng_session_descriptor_network_location network;
+               struct lttng_uri *local;
+       } output;
+};
+
+struct lttng_session_descriptor_snapshot {
+       struct lttng_session_descriptor base;
+       /*
+        * Assumes at-most one snapshot output is supported. Uses
+        * the output field of the base class.
+        */
+};
+
+struct lttng_session_descriptor_live {
+       struct lttng_session_descriptor base;
+       unsigned long long live_timer_us;
+};
+
+struct lttng_session_descriptor_comm {
+       /* enum lttng_session_descriptor_type */
+       uint8_t type;
+       /* enum lttng_session_descriptor_output_type */
+       uint8_t output_type;
+       /* Includes trailing null. */
+       uint32_t name_len;
+       /* Name follows, followed by URIs */
+       uint8_t uri_count;
+} LTTNG_PACKED;
+
+struct lttng_session_descriptor_live_comm {
+       struct lttng_session_descriptor_comm base;
+       /* Live-specific parameters. */
+       uint64_t live_timer_us;
+} LTTNG_PACKED;
+
+static
+struct lttng_uri *uri_copy(const struct lttng_uri *uri)
+{
+       struct lttng_uri *new_uri = NULL;
+
+       if (!uri) {
+               goto end;
+       }
+
+       new_uri = (lttng_uri *) zmalloc(sizeof(*new_uri));
+       if (!new_uri) {
+               goto end;
+       }
+       memcpy(new_uri, uri, sizeof(*new_uri));
+end:
+       return new_uri;
+}
+
+static
+struct lttng_uri *uri_from_path(const char *path)
+{
+       struct lttng_uri *uris = NULL;
+       ssize_t uri_count;
+       char local_protocol_string[LTTNG_PATH_MAX + sizeof("file://")] =
+                       "file://";
+
+       if (strlen(path) >= LTTNG_PATH_MAX) {
+               goto end;
+       }
+
+       if (path[0] != '/') {
+               /* Not an absolute path. */
+               goto end;
+       }
+
+       strncat(local_protocol_string, path, LTTNG_PATH_MAX);
+       uri_count = uri_parse(local_protocol_string, &uris);
+       if (uri_count != 1) {
+               goto error;
+       }
+       if (uris[0].dtype != LTTNG_DST_PATH) {
+               goto error;
+       }
+
+end:
+       return uris;
+error:
+       free(uris);
+       return NULL;
+}
+
+static
+void network_location_fini(
+               struct lttng_session_descriptor_network_location *location)
+{
+       free(location->control);
+       free(location->data);
+}
+
+/* Assumes ownership of control and data. */
+static
+int network_location_set_from_lttng_uris(
+               struct lttng_session_descriptor_network_location *location,
+               struct lttng_uri *control, struct lttng_uri *data)
+{
+       int ret = 0;
+
+       if (!control && !data) {
+               goto end;
+       }
+
+       if (!(control && data)) {
+               /* None or both must be set. */
+               ret = -1;
+               goto end;
+       }
+
+       if (control->stype != LTTNG_STREAM_CONTROL ||
+                       data->stype != LTTNG_STREAM_DATA) {
+               ret = -1;
+               goto end;
+       }
+
+       free(location->control);
+       free(location->data);
+       location->control = control;
+       location->data = data;
+       control = NULL;
+       data = NULL;
+end:
+       free(control);
+       free(data);
+       return ret;
+}
+
+static
+int network_location_set_from_uri_strings(
+               struct lttng_session_descriptor_network_location *location,
+               const char *control, const char *data)
+{
+       int ret = 0;
+       ssize_t uri_count;
+       struct lttng_uri *parsed_uris = NULL;
+       struct lttng_uri *control_uri = NULL;
+       struct lttng_uri *data_uri = NULL;
+
+       uri_count = uri_parse_str_urls(control, data, &parsed_uris);
+       if (uri_count != 2 && uri_count != 0) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * uri_parse_str_urls returns a contiguous array of lttng_uris whereas
+        * session descriptors expect individually allocated lttng_uris.
+        */
+       if (uri_count == 2) {
+               control_uri = (lttng_uri *) zmalloc(sizeof(*control_uri));
+               data_uri = (lttng_uri *) zmalloc(sizeof(*data_uri));
+               if (!control_uri || !data_uri) {
+                       ret = -1;
+                       goto end;
+               }
+               memcpy(control_uri, &parsed_uris[0], sizeof(*control_uri));
+               memcpy(data_uri, &parsed_uris[1], sizeof(*data_uri));
+       }
+
+       /* Ownership of control and data uris is transferred. */
+       ret = network_location_set_from_lttng_uris(
+                       location,
+                       control_uri,
+                       data_uri);
+       control_uri = NULL;
+       data_uri = NULL;
+end:
+       free(parsed_uris);
+       free(control_uri);
+       free(data_uri);
+       return ret;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_create(const char *name)
+{
+       struct lttng_session_descriptor *descriptor;
+
+       descriptor = (lttng_session_descriptor *) zmalloc(sizeof(*descriptor));
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
+       descriptor->output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
+       if (lttng_session_descriptor_set_session_name(descriptor, name)) {
+               goto error;
+       }
+       return descriptor;
+error:
+       lttng_session_descriptor_destroy(descriptor);
+       return NULL;
+}
+
+/* Ownership of uri is transferred. */
+static
+struct lttng_session_descriptor *
+_lttng_session_descriptor_local_create(const char *name,
+               struct lttng_uri *uri)
+{
+       struct lttng_session_descriptor *descriptor;
+
+       descriptor = lttng_session_descriptor_create(name);
+       if (!descriptor) {
+               goto error;
+       }
+       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
+       descriptor->output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL;
+       if (uri) {
+               if (uri->dtype != LTTNG_DST_PATH) {
+                       goto error;
+               }
+               descriptor->output.local = uri;
+               uri = NULL;
+       }
+       return descriptor;
+error:
+       free(uri);
+       lttng_session_descriptor_destroy(descriptor);
+       return NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_local_create(const char *name, const char *path)
+{
+       struct lttng_uri *uri = NULL;
+       struct lttng_session_descriptor *descriptor;
+
+       if (path) {
+               uri = uri_from_path(path);
+               if (!uri) {
+                       goto error;
+               }
+       }
+       descriptor = _lttng_session_descriptor_local_create(name, uri);
+       return descriptor;
+error:
+       return NULL;
+}
+
+/* Assumes the ownership of both uris. */
+static
+struct lttng_session_descriptor *
+_lttng_session_descriptor_network_create(const char *name,
+               struct lttng_uri *control, struct lttng_uri *data)
+{
+       int ret;
+       struct lttng_session_descriptor *descriptor;
+
+       descriptor = lttng_session_descriptor_create(name);
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->type = LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR;
+       descriptor->output_type = LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
+       /* Assumes the ownership of both uris. */
+       ret = network_location_set_from_lttng_uris(&descriptor->output.network,
+                       control, data);
+       control = NULL;
+       data = NULL;
+       if (ret) {
+               goto error;
+       }
+       return descriptor;
+error:
+       lttng_session_descriptor_destroy(descriptor);
+       free(control);
+       free(data);
+       return NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_network_create(const char *name,
+               const char *control_url, const char *data_url)
+{
+       int ret;
+       struct lttng_session_descriptor *descriptor;
+
+       descriptor = _lttng_session_descriptor_network_create(name,
+                       NULL, NULL);
+       if (!descriptor) {
+               goto error;
+       }
+
+       ret = network_location_set_from_uri_strings(&descriptor->output.network,
+                       control_url, data_url);
+       if (ret) {
+               goto error;
+       }
+       return descriptor;
+error:
+       lttng_session_descriptor_destroy(descriptor);
+       return NULL;
+}
+
+static
+struct lttng_session_descriptor_snapshot *
+_lttng_session_descriptor_snapshot_create(const char *name)
+{
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       descriptor = (lttng_session_descriptor_snapshot *) zmalloc(sizeof(*descriptor));
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT;
+       descriptor->base.output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
+       if (lttng_session_descriptor_set_session_name(&descriptor->base,
+                       name)) {
+               goto error;
+       }
+       return descriptor;
+error:
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+/* Ownership of control and data is transferred. */
+static
+struct lttng_session_descriptor_snapshot *
+_lttng_session_descriptor_snapshot_network_create(const char *name,
+               struct lttng_uri *control, struct lttng_uri *data)
+{
+       int ret;
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       descriptor = _lttng_session_descriptor_snapshot_create(name);
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->base.output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
+       /* Ownership of control and data is transferred. */
+       ret = network_location_set_from_lttng_uris(
+                       &descriptor->base.output.network,
+                       control, data);
+       control = NULL;
+       data = NULL;
+       if (ret) {
+               goto error;
+       }
+       return descriptor;
+error:
+       free(control);
+       free(data);
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_snapshot_create(const char *name)
+{
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       descriptor = _lttng_session_descriptor_snapshot_create(name);
+       return descriptor ? &descriptor->base : NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_snapshot_network_create(const char *name,
+               const char *control_url, const char *data_url)
+{
+       int ret;
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       descriptor = _lttng_session_descriptor_snapshot_network_create(name,
+                       NULL, NULL);
+       if (!descriptor) {
+               goto error;
+       }
+
+       ret = network_location_set_from_uri_strings(
+                       &descriptor->base.output.network,
+                       control_url, data_url);
+       if (ret) {
+               goto error;
+       }
+       return &descriptor->base;
+error:
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+/* Ownership of uri is transferred. */
+static
+struct lttng_session_descriptor_snapshot *
+_lttng_session_descriptor_snapshot_local_create(const char *name,
+               struct lttng_uri *uri)
+{
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       descriptor = _lttng_session_descriptor_snapshot_create(name);
+       if (!descriptor) {
+               goto error;
+       }
+       descriptor->base.output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL;
+       if (uri) {
+               if (uri->dtype != LTTNG_DST_PATH) {
+                       goto error;
+               }
+               descriptor->base.output.local = uri;
+               uri = NULL;
+       }
+       return descriptor;
+error:
+       free(uri);
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_snapshot_local_create(const char *name,
+               const char *path)
+{
+       struct lttng_uri *path_uri = NULL;
+       struct lttng_session_descriptor_snapshot *descriptor;
+
+       if (path) {
+               path_uri = uri_from_path(path);
+               if (!path_uri) {
+                       goto error;
+               }
+       }
+       descriptor = _lttng_session_descriptor_snapshot_local_create(name,
+                       path_uri);
+       return descriptor ? &descriptor->base : NULL;
+error:
+       return NULL;
+}
+
+static
+struct lttng_session_descriptor_live *
+_lttng_session_descriptor_live_create(const char *name,
+               unsigned long long live_timer_interval_us)
+{
+       struct lttng_session_descriptor_live *descriptor = NULL;
+
+       if (live_timer_interval_us == 0) {
+               goto error;
+       }
+       descriptor = (lttng_session_descriptor_live *) zmalloc(sizeof(*descriptor));
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->base.type = LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE;
+       descriptor->base.output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE;
+       descriptor->live_timer_us = live_timer_interval_us;
+       if (lttng_session_descriptor_set_session_name(&descriptor->base,
+                       name)) {
+               goto error;
+       }
+
+       return descriptor;
+error:
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+/* Ownership of control and data is transferred. */
+static
+struct lttng_session_descriptor_live *
+_lttng_session_descriptor_live_network_create(
+               const char *name,
+               struct lttng_uri *control, struct lttng_uri *data,
+               unsigned long long live_timer_interval_us)
+{
+       int ret;
+       struct lttng_session_descriptor_live *descriptor;
+
+       descriptor = _lttng_session_descriptor_live_create(name,
+                       live_timer_interval_us);
+       if (!descriptor) {
+               goto error;
+       }
+
+       descriptor->base.output_type =
+                       LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK;
+
+       /* Ownerwhip of control and data is transferred. */
+       ret = network_location_set_from_lttng_uris(
+                       &descriptor->base.output.network,
+                       control, data);
+       control = NULL;
+       data = NULL;
+       if (ret) {
+               goto error;
+       }
+       return descriptor;
+error:
+       free(control);
+       free(data);
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_live_create(
+               const char *name,
+               unsigned long long live_timer_us)
+{
+       struct lttng_session_descriptor_live *descriptor;
+
+       descriptor = _lttng_session_descriptor_live_create(name, live_timer_us);
+
+       return descriptor ? &descriptor->base : NULL;
+}
+
+struct lttng_session_descriptor *
+lttng_session_descriptor_live_network_create(
+               const char *name,
+               const char *control_url, const char *data_url,
+               unsigned long long live_timer_us)
+{
+       int ret;
+       struct lttng_session_descriptor_live *descriptor;
+
+       descriptor = _lttng_session_descriptor_live_network_create(name,
+                       NULL, NULL, live_timer_us);
+       if (!descriptor) {
+               goto error;
+       }
+
+       ret = network_location_set_from_uri_strings(
+                       &descriptor->base.output.network,
+                       control_url, data_url);
+       if (ret) {
+               goto error;
+       }
+       return &descriptor->base;
+error:
+       lttng_session_descriptor_destroy(descriptor ? &descriptor->base : NULL);
+       return NULL;
+}
+
+void lttng_session_descriptor_destroy(
+               struct lttng_session_descriptor *descriptor)
+{
+       if (!descriptor) {
+               return;
+       }
+
+       switch (descriptor->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               free(descriptor->output.local);
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+               network_location_fini(&descriptor->output.network);
+               break;
+       default:
+               abort();
+       }
+
+       free(descriptor->name);
+       free(descriptor);
+}
+
+ssize_t lttng_session_descriptor_create_from_buffer(
+               const struct lttng_buffer_view *payload,
+               struct lttng_session_descriptor **descriptor)
+{
+       int i;
+       ssize_t offset = 0, ret;
+       struct lttng_buffer_view current_view;
+       const char *name = NULL;
+       const struct lttng_session_descriptor_comm *base_header;
+       size_t max_expected_uri_count;
+       uint64_t live_timer_us = 0;
+       struct lttng_uri *uris[2] = {};
+       enum lttng_session_descriptor_type type;
+       enum lttng_session_descriptor_output_type output_type;
+
+       current_view = lttng_buffer_view_from_view(payload, offset,
+                       sizeof(*base_header));
+       if (!lttng_buffer_view_is_valid(&current_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       base_header = (typeof(base_header)) current_view.data;
+       switch (base_header->type) {
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+       {
+               const struct lttng_session_descriptor_live_comm *live_header;
+
+               current_view = lttng_buffer_view_from_view(payload, offset,
+                               sizeof(*live_header));
+               if (!lttng_buffer_view_is_valid(&current_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               live_header = (typeof(live_header)) current_view.data;
+               live_timer_us = live_header->live_timer_us;
+               break;
+       }
+       default:
+               ret = -1;
+               goto end;
+       }
+       /* type has been validated. */
+       type = (lttng_session_descriptor_type) base_header->type;
+
+       switch (base_header->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               max_expected_uri_count = 0;
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               max_expected_uri_count = 1;
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+               max_expected_uri_count = 2;
+               break;
+       default:
+               ret = -1;
+               goto end;
+       }
+       /* output_type has been validated. */
+       output_type = (lttng_session_descriptor_output_type) base_header->output_type;
+
+       /* Skip after header. */
+       offset += current_view.size;
+       if (!base_header->name_len) {
+               goto skip_name;
+       }
+
+       /* Map the name. */
+       current_view = lttng_buffer_view_from_view(payload, offset,
+                       base_header->name_len);
+       if (!lttng_buffer_view_is_valid(&current_view)) {
+               ret = -1;
+               goto end;
+       }
+
+       name = current_view.data;
+       if (base_header->name_len == 1 ||
+                       name[base_header->name_len - 1] ||
+                       strlen(name) != base_header->name_len - 1) {
+               /*
+                * Check that the name is not NULL, is NULL-terminated, and
+                * does not contain a NULL before the last byte.
+                */
+               ret = -1;
+               goto end;
+       }
+
+       /* Skip after the name. */
+       offset += base_header->name_len;
+skip_name:
+       if (base_header->uri_count > max_expected_uri_count) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < base_header->uri_count; i++) {
+               struct lttng_uri *uri;
+
+               /* Map a URI. */
+               current_view = lttng_buffer_view_from_view(payload,
+                               offset, sizeof(*uri));
+               if (!lttng_buffer_view_is_valid(&current_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               uri = (typeof(uri)) current_view.data;
+               uris[i] = (lttng_uri *) zmalloc(sizeof(*uri));
+               if (!uris[i]) {
+                       ret = -1;
+                       goto end;
+               }
+               memcpy(uris[i], uri, sizeof(*uri));
+               offset += sizeof(*uri);
+       }
+
+       switch (type) {
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_REGULAR:
+               switch (output_type) {
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+                       *descriptor = lttng_session_descriptor_create(name);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+                       *descriptor = _lttng_session_descriptor_local_create(
+                                       name, uris[0]);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+                       *descriptor = _lttng_session_descriptor_network_create(
+                                       name, uris[0], uris[1]);
+                       break;
+               default:
+                       /* Already checked. */
+                       abort();
+               }
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_SNAPSHOT:
+       {
+               struct lttng_session_descriptor_snapshot *snapshot;
+               switch (output_type) {
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+                       snapshot = _lttng_session_descriptor_snapshot_create(
+                                       name);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+                       snapshot = _lttng_session_descriptor_snapshot_local_create(
+                                       name, uris[0]);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+                       snapshot = _lttng_session_descriptor_snapshot_network_create(
+                                       name, uris[0], uris[1]);
+                       break;
+               default:
+                       /* Already checked. */
+                       abort();
+               }
+               *descriptor = snapshot ? &snapshot->base : NULL;
+               break;
+       }
+       case LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE:
+       {
+               struct lttng_session_descriptor_live *live;
+
+               switch (output_type) {
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+                       live = _lttng_session_descriptor_live_create(
+                                       name, live_timer_us);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+                       live = _lttng_session_descriptor_live_network_create(
+                                       name, uris[0], uris[1],
+                                       live_timer_us);
+                       break;
+               case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+                       ret = -1;
+                       goto end;
+               default:
+                       /* Already checked. */
+                       abort();
+               }
+               *descriptor = live ? &live->base : NULL;
+               break;
+       }
+       default:
+               /* Already checked. */
+               abort();
+       }
+       memset(uris, 0, sizeof(uris));
+       if (!*descriptor) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = offset;
+end:
+       free(uris[0]);
+       free(uris[1]);
+       return ret;
+}
+
+int lttng_session_descriptor_serialize(
+               const struct lttng_session_descriptor *descriptor,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret, i;
+       /* There are, at most, two URIs to serialize. */
+       struct lttng_uri *uris[2] = {};
+       size_t uri_count = 0;
+       /* The live header is a superset of all headers. */
+       struct lttng_session_descriptor_live_comm header = {
+               .base = {
+                       .type = (uint8_t) descriptor->type,
+                       .output_type = (uint8_t) descriptor->output_type,
+                       .name_len = (uint32_t) (descriptor->name ?
+                               strlen(descriptor->name) + 1 : 0),
+               }
+       };
+       const void *header_ptr = NULL;
+       size_t header_size;
+
+       switch (descriptor->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               uris[0] = descriptor->output.local;
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+               uris[0] = descriptor->output.network.control;
+               uris[1] = descriptor->output.network.data;
+               break;
+       default:
+               ret = -1;
+               goto end;
+       }
+       uri_count += !!uris[0];
+       uri_count += !!uris[1];
+
+       header.base.uri_count = uri_count;
+       if (descriptor->type == LTTNG_SESSION_DESCRIPTOR_TYPE_LIVE) {
+               const struct lttng_session_descriptor_live *live =
+                               container_of(descriptor, typeof(*live),
+                               base);
+
+               header.live_timer_us = live->live_timer_us;
+               header_ptr = &header;
+               header_size = sizeof(header);
+       } else {
+               header_ptr = &header.base;
+               header_size = sizeof(header.base);
+       }
+
+       ret = lttng_dynamic_buffer_append(buffer, header_ptr, header_size);
+       if (ret) {
+               goto end;
+       }
+       if (header.base.name_len) {
+               ret = lttng_dynamic_buffer_append(buffer, descriptor->name,
+                               header.base.name_len);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < uri_count; i++) {
+               ret = lttng_dynamic_buffer_append(buffer, uris[i],
+                               sizeof(struct lttng_uri));
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+enum lttng_session_descriptor_type
+lttng_session_descriptor_get_type(
+               const struct lttng_session_descriptor *descriptor)
+{
+       return descriptor->type;
+}
+
+enum lttng_session_descriptor_output_type
+lttng_session_descriptor_get_output_type(
+               const struct lttng_session_descriptor *descriptor)
+{
+       return descriptor->output_type;
+}
+
+void lttng_session_descriptor_get_local_output_uri(
+               const struct lttng_session_descriptor *descriptor,
+               struct lttng_uri *local_uri)
+{
+       memcpy(local_uri, descriptor->output.local, sizeof(*local_uri));
+}
+
+void lttng_session_descriptor_get_network_output_uris(
+               const struct lttng_session_descriptor *descriptor,
+               struct lttng_uri *control,
+               struct lttng_uri *data)
+{
+       memcpy(control, descriptor->output.network.control, sizeof(*control));
+       memcpy(data, descriptor->output.network.data, sizeof(*data));
+}
+
+unsigned long long
+lttng_session_descriptor_live_get_timer_interval(
+               const struct lttng_session_descriptor *descriptor)
+{
+       struct lttng_session_descriptor_live *live;
+
+       live = container_of(descriptor, typeof(*live), base);
+       return live->live_timer_us;
+}
+
+enum lttng_session_descriptor_status
+lttng_session_descriptor_get_session_name(
+               const struct lttng_session_descriptor *descriptor,
+               const char **session_name)
+{
+       enum lttng_session_descriptor_status status;
+
+       if (!descriptor || !session_name) {
+               status = LTTNG_SESSION_DESCRIPTOR_STATUS_INVALID;
+               goto end;
+       }
+
+       *session_name = descriptor->name;
+       status = descriptor->name ?
+                       LTTNG_SESSION_DESCRIPTOR_STATUS_OK :
+                       LTTNG_SESSION_DESCRIPTOR_STATUS_UNSET;
+end:
+       return status;
+}
+
+int lttng_session_descriptor_set_session_name(
+               struct lttng_session_descriptor *descriptor,
+               const char *name)
+{
+       int ret = 0;
+       char *new_name;
+
+       if (!name) {
+               goto end;
+       }
+       if (strlen(name) >= LTTNG_NAME_MAX) {
+               ret = -1;
+               goto end;
+       }
+       new_name = strdup(name);
+       if (!new_name) {
+               ret = -1;
+               goto end;
+       }
+       free(descriptor->name);
+       descriptor->name = new_name;
+end:
+       return ret;
+}
+
+bool lttng_session_descriptor_is_output_destination_initialized(
+               const struct lttng_session_descriptor *descriptor)
+{
+       switch (descriptor->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               return true;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               return descriptor->output.local;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+               return descriptor->output.network.control;
+       default:
+               abort();
+       }
+}
+
+bool lttng_session_descriptor_has_output_directory(
+               const struct lttng_session_descriptor *descriptor)
+{
+       switch (descriptor->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               if (descriptor->output.local) {
+                       return *descriptor->output.local->dst.path;
+               }
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+               if (descriptor->output.network.control) {
+                       return *descriptor->output.network.control->subdir;
+               }
+               break;
+       default:
+               abort();
+       }
+       return false;
+}
+
+enum lttng_error_code lttng_session_descriptor_set_default_output(
+               struct lttng_session_descriptor *descriptor,
+               time_t *session_creation_time,
+               const char *absolute_home_path)
+{
+       enum lttng_error_code ret_code = LTTNG_OK;
+       struct lttng_uri *uris = NULL;
+
+       switch (descriptor->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               goto end;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+       {
+               int ret;
+               ssize_t uri_ret;
+               char local_uri[LTTNG_PATH_MAX];
+               char creation_datetime_suffix[17] = {};
+
+               if (session_creation_time) {
+                       size_t strftime_ret;
+                       struct tm *timeinfo;
+
+                       timeinfo = localtime(session_creation_time);
+                       if (!timeinfo) {
+                               ret_code = LTTNG_ERR_FATAL;
+                               goto end;
+                       }
+                       strftime_ret = strftime(creation_datetime_suffix,
+                                       sizeof(creation_datetime_suffix),
+                                       "-%Y%m%d-%H%M%S", timeinfo);
+                       if (strftime_ret == 0) {
+                               ERR("Failed to format session creation timestamp while setting default local output destination");
+                               ret_code = LTTNG_ERR_FATAL;
+                               goto end;
+                       }
+               }
+               LTTNG_ASSERT(descriptor->name);
+               ret = snprintf(local_uri, sizeof(local_uri),
+                               "file://%s/%s/%s%s",
+                               absolute_home_path,
+                               DEFAULT_TRACE_DIR_NAME, descriptor->name,
+                               creation_datetime_suffix);
+               if (ret >= sizeof(local_uri)) {
+                       ERR("Truncation occurred while setting default local output destination");
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               } else if (ret < 0) {
+                       PERROR("Failed to format default local output URI");
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               }
+
+               uri_ret = uri_parse(local_uri, &uris);
+               if (uri_ret != 1) {
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               }
+               free(descriptor->output.local);
+               descriptor->output.local = &uris[0];
+               uris = NULL;
+               break;
+       }
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+       {
+               int ret;
+               ssize_t uri_ret;
+               struct lttng_uri *control = NULL, *data = NULL;
+
+               uri_ret = uri_parse_str_urls("net://127.0.0.1", NULL, &uris);
+               if (uri_ret != 2) {
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               }
+
+               control = uri_copy(&uris[0]);
+               data = uri_copy(&uris[1]);
+               if (!control || !data) {
+                       free(control);
+                       free(data);
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               }
+
+               /* Ownership of uris is transferred. */
+               ret = network_location_set_from_lttng_uris(
+                               &descriptor->output.network,
+                               control, data);
+               if (ret) {
+                       abort();
+                       ret_code = LTTNG_ERR_SET_URL;
+                       goto end;
+               }
+               break;
+       }
+       default:
+               abort();
+       }
+end:
+       free(uris);
+       return ret_code;
+}
+
+/*
+ * Note that only properties that can be populated by the session daemon
+ * (output destination and name) are assigned.
+ */
+int lttng_session_descriptor_assign(
+               struct lttng_session_descriptor *dst,
+               const struct lttng_session_descriptor *src)
+{
+       int ret = 0;
+
+       if (dst->type != src->type) {
+               ret = -1;
+               goto end;
+       }
+       if (dst->output_type != src->output_type) {
+               ret = -1;
+               goto end;
+       }
+       ret = lttng_session_descriptor_set_session_name(dst, src->name);
+       if (ret) {
+               goto end;
+       }
+       switch (dst->output_type) {
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_LOCAL:
+               free(dst->output.local);
+               dst->output.local = uri_copy(src->output.local);
+               if (!dst->output.local) {
+                       ret = -1;
+                       goto end;
+               }
+               break;
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NETWORK:
+       {
+               struct lttng_uri *control_copy = NULL, *data_copy = NULL;
+
+               control_copy = uri_copy(dst->output.network.control);
+               if (!control_copy && dst->output.network.control) {
+                       ret = -1;
+                       goto end;
+               }
+               data_copy = uri_copy(dst->output.network.data);
+               if (!data_copy && dst->output.network.data) {
+                       free(control_copy);
+                       ret = -1;
+                       goto end;
+               }
+               ret = network_location_set_from_lttng_uris(&dst->output.network,
+                               control_copy, data_copy);
+               break;
+       }
+       case LTTNG_SESSION_DESCRIPTOR_OUTPUT_TYPE_NONE:
+               goto end;
+       }
+end:
+       return ret;
+}
diff --git a/src/common/shm.c b/src/common/shm.c
deleted file mode 100644 (file)
index c006258..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <urcu.h>
-
-#include <common/error.h>
-
-#include "shm.h"
-
-/*
- * Using fork to set umask in the child process (not multi-thread safe). We
- * deal with the shm_open vs ftruncate race (happening when the sessiond owns
- * the shm and does not let everybody modify it, to ensure safety against
- * shm_unlink) by simply letting the mmap fail and retrying after a few
- * seconds. For global shm, everybody has rw access to it until the sessiond
- * starts.
- */
-static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
-{
-       int wait_shm_fd, ret;
-       mode_t mode;
-
-       LTTNG_ASSERT(shm_path);
-
-       /* Default permissions */
-       mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
-
-       /*
-        * Change owner of the shm path.
-        */
-       if (global) {
-               /*
-                * If global session daemon, any application can
-                * register. Make it initially writeable so applications
-                * registering concurrently can do ftruncate() by
-                * themselves.
-                */
-               mode |= S_IROTH | S_IWOTH;
-       }
-
-       /*
-        * We're alone in a child process, so we can modify the process-wide
-        * umask.
-        */
-       umask(~mode);
-
-       /*
-        * Try creating shm (or get rw access). We don't do an exclusive open,
-        * because we allow other processes to create+ftruncate it concurrently.
-        *
-        * A sysctl, fs.protected_regular may prevent the session daemon from
-        * opening a previously created shm when the O_CREAT flag is provided.
-        * Systemd enables this ABI-breaking change by default since v241.
-        *
-        * First, attempt to use the create-or-open semantic that is
-        * desired here. If this fails with EACCES, work around this broken
-        * behaviour and attempt to open the shm without the O_CREAT flag.
-        *
-        * The two attempts are made in this order since applications are
-        * expected to race with the session daemon to create this shm.
-        * Attempting an shm_open() without the O_CREAT flag first could fail
-        * because the file doesn't exist. It could then be created by an
-        * application, which would cause a second try with the O_CREAT flag to
-        * fail with EACCES.
-        *
-        * Note that this introduces a new failure mode where a user could
-        * launch an application (creating the shm) and unlink the shm while
-        * the session daemon is launching, causing the second attempt
-        * to fail. This is not recovered-from as unlinking the shm will
-        * prevent userspace tracing from succeeding anyhow: the sessiond would
-        * use a now-unlinked shm, while the next application would create
-        * a new named shm.
-        */
-       wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode);
-       if (wait_shm_fd < 0) {
-               if (errno == EACCES) {
-                       /* Work around sysctl fs.protected_regular. */
-                       DBG("shm_open of %s returned EACCES, this may be caused "
-                                       "by the fs.protected_regular sysctl. "
-                                       "Attempting to open the shm without "
-                                       "creating it.", shm_path);
-                       wait_shm_fd = shm_open(shm_path, O_RDWR, mode);
-               }
-               if (wait_shm_fd < 0) {
-                       PERROR("Failed to open \"wait\" shared memory object: path = '%s'", shm_path);
-                       goto error;
-               }
-       }
-
-       ret = ftruncate(wait_shm_fd, mmap_size);
-       if (ret < 0) {
-               PERROR("Failed to truncate \"wait\" shared memory object: fd = %d, size = %zu",
-                               wait_shm_fd, mmap_size);
-               exit(EXIT_FAILURE);
-       }
-
-       if (global) {
-               ret = fchown(wait_shm_fd, 0, 0);
-               if (ret < 0) {
-                       PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = 0, group = 0",
-                                       wait_shm_fd);
-                       exit(EXIT_FAILURE);
-               }
-               /*
-                * If global session daemon, any application can
-                * register so the shm needs to be set in read-only mode
-                * for others.
-                */
-               mode &= ~S_IWOTH;
-               ret = fchmod(wait_shm_fd, mode);
-               if (ret < 0) {
-                       PERROR("Failed to set the mode of the \"wait\" shared memory object: fd = %d, mode = %d",
-                                       wait_shm_fd, mode);
-                       exit(EXIT_FAILURE);
-               }
-       } else {
-               ret = fchown(wait_shm_fd, getuid(), getgid());
-               if (ret < 0) {
-                       PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = %d, group = %d",
-                                       wait_shm_fd, getuid(), getgid());
-                       exit(EXIT_FAILURE);
-               }
-       }
-
-       DBG("Wait shared memory file descriptor created successfully: path = '%s', mmap_size = %zu, global = %s, fd = %d",
-                       shm_path, mmap_size, global ? "true" : "false",
-                       wait_shm_fd);
-
-       return wait_shm_fd;
-
-error:
-       DBG("Failed to open shared memory file descriptor: path = '%s', mmap_size = %zu, global = %s",
-                       shm_path, mmap_size, global ? "true" : "false");
-
-       return -1;
-}
-
-/*
- * Return the wait shm mmap for UST application notification. The global
- * variable is used to indicate if the the session daemon is global
- * (root:tracing) or running with an unprivileged user.
- *
- * This returned value is used by futex_wait_update() in futex.c to WAKE all
- * waiters which are UST application waiting for a session daemon.
- */
-char *shm_ust_get_mmap(char *shm_path, int global)
-{
-       size_t mmap_size;
-       int wait_shm_fd, ret;
-       char *wait_shm_mmap;
-       long sys_page_size;
-
-       LTTNG_ASSERT(shm_path);
-
-       sys_page_size = sysconf(_SC_PAGE_SIZE);
-       if (sys_page_size < 0) {
-               PERROR("Failed to get PAGE_SIZE of system");
-               goto error;
-       }
-       mmap_size = sys_page_size;
-
-       wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
-       if (wait_shm_fd < 0) {
-               goto error;
-       }
-
-       wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ,
-                       MAP_SHARED, wait_shm_fd, 0);
-
-       /* close shm fd immediately after taking the mmap reference */
-       ret = close(wait_shm_fd);
-       if (ret) {
-               PERROR("Failed to close \"wait\" shared memory object file descriptor: fd = %d",
-                               wait_shm_fd);
-       }
-
-       if (wait_shm_mmap == MAP_FAILED) {
-               DBG("Failed to mmap the \"wait\" shareed memory object (can be caused by race with ust): path = '%s', global = %s",
-                               shm_path, global ? "true" : "false");
-               goto error;
-       }
-
-       return wait_shm_mmap;
-
-error:
-       return NULL;
-}
-
-/*
- * shm_create_anonymous is never called concurrently within a process.
- */
-int shm_create_anonymous(const char *owner_name)
-{
-       char tmp_name[NAME_MAX];
-       int shmfd, ret;
-
-       ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid());
-       if (ret < 0) {
-               PERROR("Failed to format shm path: owner_name = '%s', pid = %d",
-                               owner_name, getpid());
-               return -1;
-       }
-
-       /*
-        * Allocate shm, and immediately unlink its shm oject, keeping only the
-        * file descriptor as a reference to the object.
-        */
-       shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700);
-       if (shmfd < 0) {
-               PERROR("Failed to open shared memory object: path = '%s'", tmp_name);
-               goto error_shm_open;
-       }
-
-       ret = shm_unlink(tmp_name);
-       if (ret < 0 && errno != ENOENT) {
-               PERROR("Failed to unlink shared memory object: path = '%s'",
-                               tmp_name);
-               goto error_shm_release;
-       }
-
-       return shmfd;
-
-error_shm_release:
-       ret = close(shmfd);
-       if (ret) {
-               PERROR("Failed to close shared memory object file descriptor: fd = %d, path = '%s'",
-                               shmfd, tmp_name);
-       }
-error_shm_open:
-       return -1;
-}
diff --git a/src/common/shm.cpp b/src/common/shm.cpp
new file mode 100644 (file)
index 0000000..99535c4
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include <common/error.h>
+
+#include "shm.h"
+
+/*
+ * Using fork to set umask in the child process (not multi-thread safe). We
+ * deal with the shm_open vs ftruncate race (happening when the sessiond owns
+ * the shm and does not let everybody modify it, to ensure safety against
+ * shm_unlink) by simply letting the mmap fail and retrying after a few
+ * seconds. For global shm, everybody has rw access to it until the sessiond
+ * starts.
+ */
+static int get_wait_shm(char *shm_path, size_t mmap_size, int global)
+{
+       int wait_shm_fd, ret;
+       mode_t mode;
+
+       LTTNG_ASSERT(shm_path);
+
+       /* Default permissions */
+       mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+       /*
+        * Change owner of the shm path.
+        */
+       if (global) {
+               /*
+                * If global session daemon, any application can
+                * register. Make it initially writeable so applications
+                * registering concurrently can do ftruncate() by
+                * themselves.
+                */
+               mode |= S_IROTH | S_IWOTH;
+       }
+
+       /*
+        * We're alone in a child process, so we can modify the process-wide
+        * umask.
+        */
+       umask(~mode);
+
+       /*
+        * Try creating shm (or get rw access). We don't do an exclusive open,
+        * because we allow other processes to create+ftruncate it concurrently.
+        *
+        * A sysctl, fs.protected_regular may prevent the session daemon from
+        * opening a previously created shm when the O_CREAT flag is provided.
+        * Systemd enables this ABI-breaking change by default since v241.
+        *
+        * First, attempt to use the create-or-open semantic that is
+        * desired here. If this fails with EACCES, work around this broken
+        * behaviour and attempt to open the shm without the O_CREAT flag.
+        *
+        * The two attempts are made in this order since applications are
+        * expected to race with the session daemon to create this shm.
+        * Attempting an shm_open() without the O_CREAT flag first could fail
+        * because the file doesn't exist. It could then be created by an
+        * application, which would cause a second try with the O_CREAT flag to
+        * fail with EACCES.
+        *
+        * Note that this introduces a new failure mode where a user could
+        * launch an application (creating the shm) and unlink the shm while
+        * the session daemon is launching, causing the second attempt
+        * to fail. This is not recovered-from as unlinking the shm will
+        * prevent userspace tracing from succeeding anyhow: the sessiond would
+        * use a now-unlinked shm, while the next application would create
+        * a new named shm.
+        */
+       wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode);
+       if (wait_shm_fd < 0) {
+               if (errno == EACCES) {
+                       /* Work around sysctl fs.protected_regular. */
+                       DBG("shm_open of %s returned EACCES, this may be caused "
+                                       "by the fs.protected_regular sysctl. "
+                                       "Attempting to open the shm without "
+                                       "creating it.", shm_path);
+                       wait_shm_fd = shm_open(shm_path, O_RDWR, mode);
+               }
+               if (wait_shm_fd < 0) {
+                       PERROR("Failed to open \"wait\" shared memory object: path = '%s'", shm_path);
+                       goto error;
+               }
+       }
+
+       ret = ftruncate(wait_shm_fd, mmap_size);
+       if (ret < 0) {
+               PERROR("Failed to truncate \"wait\" shared memory object: fd = %d, size = %zu",
+                               wait_shm_fd, mmap_size);
+               exit(EXIT_FAILURE);
+       }
+
+       if (global) {
+               ret = fchown(wait_shm_fd, 0, 0);
+               if (ret < 0) {
+                       PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = 0, group = 0",
+                                       wait_shm_fd);
+                       exit(EXIT_FAILURE);
+               }
+               /*
+                * If global session daemon, any application can
+                * register so the shm needs to be set in read-only mode
+                * for others.
+                */
+               mode &= ~S_IWOTH;
+               ret = fchmod(wait_shm_fd, mode);
+               if (ret < 0) {
+                       PERROR("Failed to set the mode of the \"wait\" shared memory object: fd = %d, mode = %d",
+                                       wait_shm_fd, mode);
+                       exit(EXIT_FAILURE);
+               }
+       } else {
+               ret = fchown(wait_shm_fd, getuid(), getgid());
+               if (ret < 0) {
+                       PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = %d, group = %d",
+                                       wait_shm_fd, getuid(), getgid());
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       DBG("Wait shared memory file descriptor created successfully: path = '%s', mmap_size = %zu, global = %s, fd = %d",
+                       shm_path, mmap_size, global ? "true" : "false",
+                       wait_shm_fd);
+
+       return wait_shm_fd;
+
+error:
+       DBG("Failed to open shared memory file descriptor: path = '%s', mmap_size = %zu, global = %s",
+                       shm_path, mmap_size, global ? "true" : "false");
+
+       return -1;
+}
+
+/*
+ * Return the wait shm mmap for UST application notification. The global
+ * variable is used to indicate if the the session daemon is global
+ * (root:tracing) or running with an unprivileged user.
+ *
+ * This returned value is used by futex_wait_update() in futex.c to WAKE all
+ * waiters which are UST application waiting for a session daemon.
+ */
+char *shm_ust_get_mmap(char *shm_path, int global)
+{
+       size_t mmap_size;
+       int wait_shm_fd, ret;
+       char *wait_shm_mmap;
+       long sys_page_size;
+
+       LTTNG_ASSERT(shm_path);
+
+       sys_page_size = sysconf(_SC_PAGE_SIZE);
+       if (sys_page_size < 0) {
+               PERROR("Failed to get PAGE_SIZE of system");
+               goto error;
+       }
+       mmap_size = sys_page_size;
+
+       wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
+       if (wait_shm_fd < 0) {
+               goto error;
+       }
+
+       wait_shm_mmap = (char *) mmap(NULL, mmap_size, PROT_WRITE | PROT_READ,
+                       MAP_SHARED, wait_shm_fd, 0);
+
+       /* close shm fd immediately after taking the mmap reference */
+       ret = close(wait_shm_fd);
+       if (ret) {
+               PERROR("Failed to close \"wait\" shared memory object file descriptor: fd = %d",
+                               wait_shm_fd);
+       }
+
+       if (wait_shm_mmap == MAP_FAILED) {
+               DBG("Failed to mmap the \"wait\" shareed memory object (can be caused by race with ust): path = '%s', global = %s",
+                               shm_path, global ? "true" : "false");
+               goto error;
+       }
+
+       return wait_shm_mmap;
+
+error:
+       return NULL;
+}
+
+/*
+ * shm_create_anonymous is never called concurrently within a process.
+ */
+int shm_create_anonymous(const char *owner_name)
+{
+       char tmp_name[NAME_MAX];
+       int shmfd, ret;
+
+       ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid());
+       if (ret < 0) {
+               PERROR("Failed to format shm path: owner_name = '%s', pid = %d",
+                               owner_name, getpid());
+               return -1;
+       }
+
+       /*
+        * Allocate shm, and immediately unlink its shm oject, keeping only the
+        * file descriptor as a reference to the object.
+        */
+       shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700);
+       if (shmfd < 0) {
+               PERROR("Failed to open shared memory object: path = '%s'", tmp_name);
+               goto error_shm_open;
+       }
+
+       ret = shm_unlink(tmp_name);
+       if (ret < 0 && errno != ENOENT) {
+               PERROR("Failed to unlink shared memory object: path = '%s'",
+                               tmp_name);
+               goto error_shm_release;
+       }
+
+       return shmfd;
+
+error_shm_release:
+       ret = close(shmfd);
+       if (ret) {
+               PERROR("Failed to close shared memory object file descriptor: fd = %d, path = '%s'",
+                               shmfd, tmp_name);
+       }
+error_shm_open:
+       return -1;
+}
diff --git a/src/common/snapshot.c b/src/common/snapshot.c
deleted file mode 100644 (file)
index 960240b..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include <common/error.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <common/snapshot.h>
-#include <lttng/snapshot-internal.h>
-#include <lttng/snapshot.h>
-
-#include <stdlib.h>
-
-bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output)
-{
-       bool valid = false;
-       size_t len;
-
-       /*
-        * It is mandatory to have a ctrl_url. If there is only one output
-        * URL (in the net://, net6:// or file:// form), it will be in this
-        * field.
-        */
-       len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url));
-       if (len == 0 || len >= sizeof(output->ctrl_url)) {
-               goto end;
-       }
-
-       len = lttng_strnlen(output->data_url, sizeof(output->data_url));
-       if (len >= sizeof(output->data_url)) {
-               goto end;
-       }
-
-       len = lttng_strnlen(output->name, sizeof(output->name));
-       if (len >= sizeof(output->name)) {
-               goto end;
-       }
-
-       valid = true;
-
-end:
-       return valid;
-}
-
-bool lttng_snapshot_output_is_equal(
-               const struct lttng_snapshot_output *a,
-               const struct lttng_snapshot_output *b)
-{
-       bool equal = false;
-
-       LTTNG_ASSERT(a);
-       LTTNG_ASSERT(b);
-
-       if (a->max_size != b->max_size) {
-               goto end;
-       }
-
-       if (strcmp(a->name, b->name) != 0) {
-               goto end;
-       }
-
-       if (strcmp(a->ctrl_url, b->ctrl_url) != 0) {
-               goto end;
-       }
-
-       if (strcmp(a->data_url, b->data_url) != 0) {
-               goto end;
-       }
-
-       equal = true;
-
-end:
-       return equal;
-}
-
-/*
- * This is essentially the same as `struct lttng_snapshot_output`, but packed.
- */
-struct lttng_snapshot_output_comm {
-       uint32_t id;
-       uint64_t max_size;
-       char name[LTTNG_NAME_MAX];
-       char ctrl_url[PATH_MAX];
-       char data_url[PATH_MAX];
-} LTTNG_PACKED;
-
-int lttng_snapshot_output_serialize(
-               const struct lttng_snapshot_output *output,
-               struct lttng_payload *payload)
-{
-       struct lttng_snapshot_output_comm comm;
-       int ret;
-
-       comm.id = output->id;
-       comm.max_size = output->max_size;
-
-       ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_strncpy(
-                       comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_strncpy(
-                       comm.data_url, output->data_url, sizeof(comm.data_url));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, &comm, sizeof(comm));
-       if (ret) {
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-ssize_t lttng_snapshot_output_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_snapshot_output **output_p)
-{
-       const struct lttng_snapshot_output_comm *comm;
-       struct lttng_snapshot_output *output = NULL;
-       int ret;
-
-       if (view->buffer.size != sizeof(*comm)) {
-               ret = -1;
-               goto end;
-       }
-
-       output = lttng_snapshot_output_create();
-       if (!output) {
-               ret = -1;
-               goto end;
-       }
-
-       comm = (typeof(comm)) view->buffer.data;
-
-       output->id = comm->id;
-       output->max_size = comm->max_size;
-
-       ret = lttng_strncpy(output->name, comm->name, sizeof(output->name));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url,
-                       sizeof(output->ctrl_url));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_strncpy(output->data_url, comm->data_url,
-                       sizeof(output->data_url));
-       if (ret) {
-               goto end;
-       }
-
-       *output_p = output;
-       output = NULL;
-       ret = sizeof(*comm);
-
-end:
-       lttng_snapshot_output_destroy(output);
-       return ret;
-}
-
-enum lttng_error_code lttng_snapshot_output_mi_serialize(
-               const struct lttng_snapshot_output *output,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-
-       LTTNG_ASSERT(output);
-       LTTNG_ASSERT(writer);
-
-       /* Open output element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_action_snapshot_session_output);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Name. */
-       if (strnlen(output->name, LTTNG_NAME_MAX) != 0) {
-               ret = mi_lttng_writer_write_element_string(
-                               writer, config_element_name, output->name);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Control url (always present). */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Data url (optional). */
-       if (strnlen(output->data_url, PATH_MAX) != 0) {
-               ret = mi_lttng_writer_write_element_string(writer,
-                               mi_lttng_element_snapshot_data_url,
-                               output->data_url);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /*
-        * Maximum size in bytes of the snapshot meaning the total size of all
-        * streams combined. A value of 0 means unlimited. The default value is
-        * UINT64_MAX which also means unlimited in practice.
-        *
-        * The value is not serialized when it is set to either of those values
-        * to normalize them to '0'.
-        */
-       if (output->max_size > 0 && output->max_size != UINT64_MAX) {
-               /* Total size of all stream combined. */
-               ret = mi_lttng_writer_write_element_unsigned_int(writer,
-                               mi_lttng_element_snapshot_max_size,
-                               output->max_size);
-               if (ret) {
-                       goto mi_error;
-               }
-       }
-
-       /* Close output element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/snapshot.cpp b/src/common/snapshot.cpp
new file mode 100644 (file)
index 0000000..960240b
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2020 Simon Marchi <simon.marchi@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <common/error.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <common/snapshot.h>
+#include <lttng/snapshot-internal.h>
+#include <lttng/snapshot.h>
+
+#include <stdlib.h>
+
+bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output)
+{
+       bool valid = false;
+       size_t len;
+
+       /*
+        * It is mandatory to have a ctrl_url. If there is only one output
+        * URL (in the net://, net6:// or file:// form), it will be in this
+        * field.
+        */
+       len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url));
+       if (len == 0 || len >= sizeof(output->ctrl_url)) {
+               goto end;
+       }
+
+       len = lttng_strnlen(output->data_url, sizeof(output->data_url));
+       if (len >= sizeof(output->data_url)) {
+               goto end;
+       }
+
+       len = lttng_strnlen(output->name, sizeof(output->name));
+       if (len >= sizeof(output->name)) {
+               goto end;
+       }
+
+       valid = true;
+
+end:
+       return valid;
+}
+
+bool lttng_snapshot_output_is_equal(
+               const struct lttng_snapshot_output *a,
+               const struct lttng_snapshot_output *b)
+{
+       bool equal = false;
+
+       LTTNG_ASSERT(a);
+       LTTNG_ASSERT(b);
+
+       if (a->max_size != b->max_size) {
+               goto end;
+       }
+
+       if (strcmp(a->name, b->name) != 0) {
+               goto end;
+       }
+
+       if (strcmp(a->ctrl_url, b->ctrl_url) != 0) {
+               goto end;
+       }
+
+       if (strcmp(a->data_url, b->data_url) != 0) {
+               goto end;
+       }
+
+       equal = true;
+
+end:
+       return equal;
+}
+
+/*
+ * This is essentially the same as `struct lttng_snapshot_output`, but packed.
+ */
+struct lttng_snapshot_output_comm {
+       uint32_t id;
+       uint64_t max_size;
+       char name[LTTNG_NAME_MAX];
+       char ctrl_url[PATH_MAX];
+       char data_url[PATH_MAX];
+} LTTNG_PACKED;
+
+int lttng_snapshot_output_serialize(
+               const struct lttng_snapshot_output *output,
+               struct lttng_payload *payload)
+{
+       struct lttng_snapshot_output_comm comm;
+       int ret;
+
+       comm.id = output->id;
+       comm.max_size = output->max_size;
+
+       ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(
+                       comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(
+                       comm.data_url, output->data_url, sizeof(comm.data_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, &comm, sizeof(comm));
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+ssize_t lttng_snapshot_output_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_snapshot_output **output_p)
+{
+       const struct lttng_snapshot_output_comm *comm;
+       struct lttng_snapshot_output *output = NULL;
+       int ret;
+
+       if (view->buffer.size != sizeof(*comm)) {
+               ret = -1;
+               goto end;
+       }
+
+       output = lttng_snapshot_output_create();
+       if (!output) {
+               ret = -1;
+               goto end;
+       }
+
+       comm = (typeof(comm)) view->buffer.data;
+
+       output->id = comm->id;
+       output->max_size = comm->max_size;
+
+       ret = lttng_strncpy(output->name, comm->name, sizeof(output->name));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url,
+                       sizeof(output->ctrl_url));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_strncpy(output->data_url, comm->data_url,
+                       sizeof(output->data_url));
+       if (ret) {
+               goto end;
+       }
+
+       *output_p = output;
+       output = NULL;
+       ret = sizeof(*comm);
+
+end:
+       lttng_snapshot_output_destroy(output);
+       return ret;
+}
+
+enum lttng_error_code lttng_snapshot_output_mi_serialize(
+               const struct lttng_snapshot_output *output,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+
+       LTTNG_ASSERT(output);
+       LTTNG_ASSERT(writer);
+
+       /* Open output element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_action_snapshot_session_output);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Name. */
+       if (strnlen(output->name, LTTNG_NAME_MAX) != 0) {
+               ret = mi_lttng_writer_write_element_string(
+                               writer, config_element_name, output->name);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Control url (always present). */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_snapshot_ctrl_url, output->ctrl_url);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Data url (optional). */
+       if (strnlen(output->data_url, PATH_MAX) != 0) {
+               ret = mi_lttng_writer_write_element_string(writer,
+                               mi_lttng_element_snapshot_data_url,
+                               output->data_url);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /*
+        * Maximum size in bytes of the snapshot meaning the total size of all
+        * streams combined. A value of 0 means unlimited. The default value is
+        * UINT64_MAX which also means unlimited in practice.
+        *
+        * The value is not serialized when it is set to either of those values
+        * to normalize them to '0'.
+        */
+       if (output->max_size > 0 && output->max_size != UINT64_MAX) {
+               /* Total size of all stream combined. */
+               ret = mi_lttng_writer_write_element_unsigned_int(writer,
+                               mi_lttng_element_snapshot_max_size,
+                               output->max_size);
+               if (ret) {
+                       goto mi_error;
+               }
+       }
+
+       /* Close output element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/spawn-viewer.c b/src/common/spawn-viewer.c
deleted file mode 100644 (file)
index 52be705..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include <stdbool.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <lttng/constant.h>
-
-#include <common/compat/errno.h>
-#include "error.h"
-#include "macros.h"
-#include "spawn-viewer.h"
-
-
-static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN;
-static const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN;
-
-/*
- * This is needed for each viewer since we are using execvp().
- */
-static const char *babeltrace_opts[] = { "babeltrace" };
-static const char *babeltrace2_opts[] = { "babeltrace2" };
-
-/*
- * Type is also use as the index in the viewers array. So please, make sure
- * your enum value is in the right order in the array below.
- */
-enum viewer_type {
-       VIEWER_BABELTRACE    = 0,
-       VIEWER_BABELTRACE2   = 1,
-       VIEWER_USER_DEFINED  = 2,
-};
-
-static const struct viewer {
-       const char *exec_name;
-       enum viewer_type type;
-} viewers[] = {
-       { "babeltrace", VIEWER_BABELTRACE },
-       { "babeltrace2", VIEWER_BABELTRACE2 },
-       { NULL, VIEWER_USER_DEFINED },
-};
-
-static const struct viewer *parse_viewer_option(const char *opt_viewer)
-{
-       if (opt_viewer == NULL) {
-               /* Default is babeltrace2 */
-               return &(viewers[VIEWER_BABELTRACE2]);
-       }
-
-       return &(viewers[VIEWER_USER_DEFINED]);
-}
-
-/*
- * Alloc an array of string pointer from a simple string having all options
- * seperated by spaces. Also adds the trace path to the arguments.
- *
- * The returning pointer is ready to be passed to execvp().
- */
-static char **alloc_argv_from_user_opts(char *opts, const char *trace_path)
-{
-       int i = 0, ignore_space = 0;
-       unsigned int num_opts = 1;
-       char **argv, *token = opts, *saveptr = NULL;
-
-       /* Count number of arguments. */
-       do {
-               if (*token == ' ') {
-                       /* Use to ignore consecutive spaces */
-                       if (!ignore_space) {
-                               num_opts++;
-                       }
-                       ignore_space = 1;
-               } else {
-                       ignore_space = 0;
-               }
-               token++;
-       } while (*token != '\0');
-
-       /* Add two here for the NULL terminating element and trace path */
-       argv = zmalloc(sizeof(char *) * (num_opts + 2));
-       if (argv == NULL) {
-               goto error;
-       }
-
-       token = strtok_r(opts, " ", &saveptr);
-       while (token != NULL) {
-               argv[i] = strdup(token);
-               if (argv[i] == NULL) {
-                       goto error;
-               }
-               token = strtok_r(NULL, " ", &saveptr);
-               i++;
-       }
-
-       argv[num_opts] = (char *) trace_path;
-       argv[num_opts + 1] = NULL;
-
-       return argv;
-
-error:
-       if (argv) {
-               for (i = 0; i < num_opts + 2; i++) {
-                       free(argv[i]);
-               }
-               free(argv);
-       }
-
-       return NULL;
-}
-
-/*
- * Alloc an array of string pointer from an array of strings. It also adds
- * the trace path to the argv.
- *
- * The returning pointer is ready to be passed to execvp().
- */
-static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len,
-               const char *trace_path, bool opt_live_mode)
-{
-       char **argv;
-       size_t size, mem_len;
-
-       /* Add one for the NULL terminating element. */
-       mem_len = opts_len + 1;
-       if (opt_live_mode) {
-               /* Add 3 option for the live mode being "-i lttng-live URL". */
-               mem_len += 3;
-       } else {
-               /* Add option for the trace path. */
-               mem_len += 1;
-       }
-
-       size = sizeof(char *) * mem_len;
-
-       /* Add two here for the trace_path and the NULL terminating element. */
-       argv = zmalloc(size);
-       if (argv == NULL) {
-               goto error;
-       }
-
-       memcpy(argv, opts, sizeof(char *) * opts_len);
-
-       if (opt_live_mode) {
-               argv[opts_len] = (char *) "-i";
-               argv[opts_len + 1] = (char *) "lttng-live";
-               argv[opts_len + 2] = (char *) trace_path;
-               argv[opts_len + 3] = NULL;
-       } else {
-               argv[opts_len] = (char *) trace_path;
-               argv[opts_len + 1] = NULL;
-       }
-
-error:
-       return argv;
-}
-
-
-/*
- * Spawn viewer with the trace directory path.
- */
-int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode)
-{
-       int ret = 0;
-       struct stat status;
-       const char *viewer_bin = NULL;
-       const struct viewer *viewer;
-       char **argv = NULL;
-
-       /* Check for --viewer option. */
-       viewer = parse_viewer_option(opt_viewer);
-       if (viewer == NULL) {
-               ret = -1;
-               goto error;
-       }
-
-retry_viewer:
-       switch (viewer->type) {
-       case VIEWER_BABELTRACE2:
-               if (stat(babeltrace2_bin, &status) == 0) {
-                       viewer_bin = babeltrace2_bin;
-               } else {
-                       viewer_bin = viewer->exec_name;
-               }
-               argv = alloc_argv_from_local_opts(babeltrace2_opts,
-                               ARRAY_SIZE(babeltrace2_opts), trace_path,
-                               opt_live_mode);
-               break;
-       case VIEWER_BABELTRACE:
-               if (stat(babeltrace_bin, &status) == 0) {
-                       viewer_bin = babeltrace_bin;
-               } else {
-                       viewer_bin = viewer->exec_name;
-               }
-               argv = alloc_argv_from_local_opts(babeltrace_opts,
-                               ARRAY_SIZE(babeltrace_opts), trace_path,
-                               opt_live_mode);
-               break;
-       case VIEWER_USER_DEFINED:
-               argv = alloc_argv_from_user_opts(opt_viewer, trace_path);
-               if (argv) {
-                       viewer_bin = argv[0];
-               }
-               break;
-       default:
-               abort();
-       }
-
-       if (argv == NULL || !viewer_bin) {
-               ret = -1;
-               goto error;
-       }
-
-       DBG("Using %s viewer", viewer_bin);
-
-       ret = execvp(viewer_bin, argv);
-       if (ret) {
-               if (errno == ENOENT && viewer->exec_name) {
-                       if (viewer->type == VIEWER_BABELTRACE2) {
-                               /* Fallback to legacy babeltrace. */
-                               DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"",
-                                               viewers[VIEWER_BABELTRACE2].exec_name,
-                                               viewers[VIEWER_BABELTRACE].exec_name);
-                               viewer = &viewers[VIEWER_BABELTRACE];
-                               free(argv);
-                               argv = NULL;
-                               goto retry_viewer;
-                       } else {
-                               ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system",
-                                               viewers[VIEWER_BABELTRACE2].exec_name,
-                                               viewers[VIEWER_BABELTRACE].exec_name);
-                       }
-               } else {
-                       PERROR("Failed to launch \"%s\" viewer", viewer_bin);
-               }
-               ret = -1;
-               goto error;
-       }
-
-       /*
-        * This function should never return if successfull because `execvp(3)`
-        * onle returns if an error has occurred.
-        */
-       LTTNG_ASSERT(ret != 0);
-error:
-       free(argv);
-       return ret;
-}
diff --git a/src/common/spawn-viewer.cpp b/src/common/spawn-viewer.cpp
new file mode 100644 (file)
index 0000000..bcbc229
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2014 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <lttng/constant.h>
+
+#include <common/compat/errno.h>
+#include "error.h"
+#include "macros.h"
+#include "spawn-viewer.h"
+
+
+static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN;
+static const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN;
+
+/*
+ * This is needed for each viewer since we are using execvp().
+ */
+static const char *babeltrace_opts[] = { "babeltrace" };
+static const char *babeltrace2_opts[] = { "babeltrace2" };
+
+/*
+ * Type is also use as the index in the viewers array. So please, make sure
+ * your enum value is in the right order in the array below.
+ */
+enum viewer_type {
+       VIEWER_BABELTRACE    = 0,
+       VIEWER_BABELTRACE2   = 1,
+       VIEWER_USER_DEFINED  = 2,
+};
+
+static const struct viewer {
+       const char *exec_name;
+       enum viewer_type type;
+} viewers[] = {
+       { "babeltrace", VIEWER_BABELTRACE },
+       { "babeltrace2", VIEWER_BABELTRACE2 },
+       { NULL, VIEWER_USER_DEFINED },
+};
+
+static const struct viewer *parse_viewer_option(const char *opt_viewer)
+{
+       if (opt_viewer == NULL) {
+               /* Default is babeltrace2 */
+               return &(viewers[VIEWER_BABELTRACE2]);
+       }
+
+       return &(viewers[VIEWER_USER_DEFINED]);
+}
+
+/*
+ * Alloc an array of string pointer from a simple string having all options
+ * seperated by spaces. Also adds the trace path to the arguments.
+ *
+ * The returning pointer is ready to be passed to execvp().
+ */
+static char **alloc_argv_from_user_opts(char *opts, const char *trace_path)
+{
+       int i = 0, ignore_space = 0;
+       unsigned int num_opts = 1;
+       char **argv, *token = opts, *saveptr = NULL;
+
+       /* Count number of arguments. */
+       do {
+               if (*token == ' ') {
+                       /* Use to ignore consecutive spaces */
+                       if (!ignore_space) {
+                               num_opts++;
+                       }
+                       ignore_space = 1;
+               } else {
+                       ignore_space = 0;
+               }
+               token++;
+       } while (*token != '\0');
+
+       /* Add two here for the NULL terminating element and trace path */
+       argv = (char **) zmalloc(sizeof(char *) * (num_opts + 2));
+       if (argv == NULL) {
+               goto error;
+       }
+
+       token = strtok_r(opts, " ", &saveptr);
+       while (token != NULL) {
+               argv[i] = strdup(token);
+               if (argv[i] == NULL) {
+                       goto error;
+               }
+               token = strtok_r(NULL, " ", &saveptr);
+               i++;
+       }
+
+       argv[num_opts] = (char *) trace_path;
+       argv[num_opts + 1] = NULL;
+
+       return argv;
+
+error:
+       if (argv) {
+               for (i = 0; i < num_opts + 2; i++) {
+                       free(argv[i]);
+               }
+               free(argv);
+       }
+
+       return NULL;
+}
+
+/*
+ * Alloc an array of string pointer from an array of strings. It also adds
+ * the trace path to the argv.
+ *
+ * The returning pointer is ready to be passed to execvp().
+ */
+static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len,
+               const char *trace_path, bool opt_live_mode)
+{
+       char **argv;
+       size_t size, mem_len;
+
+       /* Add one for the NULL terminating element. */
+       mem_len = opts_len + 1;
+       if (opt_live_mode) {
+               /* Add 3 option for the live mode being "-i lttng-live URL". */
+               mem_len += 3;
+       } else {
+               /* Add option for the trace path. */
+               mem_len += 1;
+       }
+
+       size = sizeof(char *) * mem_len;
+
+       /* Add two here for the trace_path and the NULL terminating element. */
+       argv = (char **) zmalloc(size);
+       if (argv == NULL) {
+               goto error;
+       }
+
+       memcpy(argv, opts, sizeof(char *) * opts_len);
+
+       if (opt_live_mode) {
+               argv[opts_len] = (char *) "-i";
+               argv[opts_len + 1] = (char *) "lttng-live";
+               argv[opts_len + 2] = (char *) trace_path;
+               argv[opts_len + 3] = NULL;
+       } else {
+               argv[opts_len] = (char *) trace_path;
+               argv[opts_len + 1] = NULL;
+       }
+
+error:
+       return argv;
+}
+
+
+/*
+ * Spawn viewer with the trace directory path.
+ */
+int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode)
+{
+       int ret = 0;
+       struct stat status;
+       const char *viewer_bin = NULL;
+       const struct viewer *viewer;
+       char **argv = NULL;
+
+       /* Check for --viewer option. */
+       viewer = parse_viewer_option(opt_viewer);
+       if (viewer == NULL) {
+               ret = -1;
+               goto error;
+       }
+
+retry_viewer:
+       switch (viewer->type) {
+       case VIEWER_BABELTRACE2:
+               if (stat(babeltrace2_bin, &status) == 0) {
+                       viewer_bin = babeltrace2_bin;
+               } else {
+                       viewer_bin = viewer->exec_name;
+               }
+               argv = alloc_argv_from_local_opts(babeltrace2_opts,
+                               ARRAY_SIZE(babeltrace2_opts), trace_path,
+                               opt_live_mode);
+               break;
+       case VIEWER_BABELTRACE:
+               if (stat(babeltrace_bin, &status) == 0) {
+                       viewer_bin = babeltrace_bin;
+               } else {
+                       viewer_bin = viewer->exec_name;
+               }
+               argv = alloc_argv_from_local_opts(babeltrace_opts,
+                               ARRAY_SIZE(babeltrace_opts), trace_path,
+                               opt_live_mode);
+               break;
+       case VIEWER_USER_DEFINED:
+               argv = alloc_argv_from_user_opts(opt_viewer, trace_path);
+               if (argv) {
+                       viewer_bin = argv[0];
+               }
+               break;
+       default:
+               abort();
+       }
+
+       if (argv == NULL || !viewer_bin) {
+               ret = -1;
+               goto error;
+       }
+
+       DBG("Using %s viewer", viewer_bin);
+
+       ret = execvp(viewer_bin, argv);
+       if (ret) {
+               if (errno == ENOENT && viewer->exec_name) {
+                       if (viewer->type == VIEWER_BABELTRACE2) {
+                               /* Fallback to legacy babeltrace. */
+                               DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"",
+                                               viewers[VIEWER_BABELTRACE2].exec_name,
+                                               viewers[VIEWER_BABELTRACE].exec_name);
+                               viewer = &viewers[VIEWER_BABELTRACE];
+                               free(argv);
+                               argv = NULL;
+                               goto retry_viewer;
+                       } else {
+                               ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system",
+                                               viewers[VIEWER_BABELTRACE2].exec_name,
+                                               viewers[VIEWER_BABELTRACE].exec_name);
+                       }
+               } else {
+                       PERROR("Failed to launch \"%s\" viewer", viewer_bin);
+               }
+               ret = -1;
+               goto error;
+       }
+
+       /*
+        * This function should never return if successfull because `execvp(3)`
+        * onle returns if an error has occurred.
+        */
+       LTTNG_ASSERT(ret != 0);
+error:
+       free(argv);
+       return ret;
+}
diff --git a/src/common/thread.c b/src/common/thread.c
deleted file mode 100644 (file)
index 34a6206..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 Michael Jeanson <mjeanson@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <string.h>
-
-#include <common/compat/pthread.h>
-#include "thread.h"
-
-
-int lttng_thread_setname(const char *name)
-{
-       int ret;
-       char pthread_name[LTTNG_PTHREAD_NAMELEN];
-
-       /*
-        * Truncations are expected since pthread limits thread names to
-        * a generous 16 characters.
-        */
-       strncpy(pthread_name, name, sizeof(pthread_name));
-       pthread_name[sizeof(pthread_name) - 1] = '\0';
-
-       ret = lttng_pthread_setname_np(pthread_name);
-
-       return ret;
-}
-
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
new file mode 100644 (file)
index 0000000..34a6206
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 Michael Jeanson <mjeanson@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <string.h>
+
+#include <common/compat/pthread.h>
+#include "thread.h"
+
+
+int lttng_thread_setname(const char *name)
+{
+       int ret;
+       char pthread_name[LTTNG_PTHREAD_NAMELEN];
+
+       /*
+        * Truncations are expected since pthread limits thread names to
+        * a generous 16 characters.
+        */
+       strncpy(pthread_name, name, sizeof(pthread_name));
+       pthread_name[sizeof(pthread_name) - 1] = '\0';
+
+       ret = lttng_pthread_setname_np(pthread_name);
+
+       return ret;
+}
+
diff --git a/src/common/time.c b/src/common/time.c
deleted file mode 100644 (file)
index ccb5874..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include <common/time.h>
-#include <common/error.h>
-#include <common/macros.h>
-#include <common/error.h>
-#include <common/compat/errno.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <limits.h>
-#include <pthread.h>
-#include <locale.h>
-#include <string.h>
-
-static bool utf8_output_supported;
-
-bool locale_supports_utf8(void)
-{
-       return utf8_output_supported;
-}
-
-int timespec_to_ms(struct timespec ts, unsigned long *ms)
-{
-       unsigned long res, remain_ms;
-
-       if (ts.tv_sec > ULONG_MAX / MSEC_PER_SEC) {
-               errno = EOVERFLOW;
-               return -1;      /* multiplication overflow */
-       }
-       res = ts.tv_sec * MSEC_PER_SEC;
-       remain_ms = ULONG_MAX - res;
-       if (ts.tv_nsec / NSEC_PER_MSEC > remain_ms) {
-               errno = EOVERFLOW;
-               return -1;      /* addition overflow */
-       }
-       res += ts.tv_nsec / NSEC_PER_MSEC;
-       *ms = res;
-       return 0;
-}
-
-struct timespec timespec_abs_diff(struct timespec t1, struct timespec t2)
-{
-       uint64_t ts1 = (uint64_t) t1.tv_sec * (uint64_t) NSEC_PER_SEC +
-                       (uint64_t) t1.tv_nsec;
-       uint64_t ts2 = (uint64_t) t2.tv_sec * (uint64_t) NSEC_PER_SEC +
-                       (uint64_t) t2.tv_nsec;
-       uint64_t diff = max(ts1, ts2) - min(ts1, ts2);
-       struct timespec res;
-
-       res.tv_sec = diff / (uint64_t) NSEC_PER_SEC;
-       res.tv_nsec = diff % (uint64_t) NSEC_PER_SEC;
-       return res;
-}
-
-static
-void __attribute__((constructor)) init_locale_utf8_support(void)
-{
-       const char *program_locale = setlocale(LC_ALL, NULL);
-       const char *lang = getenv("LANG");
-
-       if (program_locale && strstr(program_locale, "utf8")) {
-               utf8_output_supported = true;
-       } else if (lang && strstr(lang, "utf8")) {
-               utf8_output_supported = true;
-       }
-}
-
-int time_to_iso8601_str(time_t time, char *str, size_t len)
-{
-       int ret = 0;
-       struct tm *tm_result;
-       struct tm tm_storage;
-       size_t strf_ret;
-
-       if (len < ISO8601_STR_LEN) {
-               ERR("Buffer too short to format ISO 8601 timestamp: %zu bytes provided when at least %zu are needed",
-                               len, ISO8601_STR_LEN);
-               ret = -1;
-               goto end;
-       }
-
-       tm_result = localtime_r(&time, &tm_storage);
-       if (!tm_result) {
-               ret = -1;
-               PERROR("Failed to break down timestamp to tm structure");
-               goto end;
-       }
-
-       strf_ret = strftime(str, len, "%Y%m%dT%H%M%S%z", tm_result);
-       if (strf_ret == 0) {
-               ret = -1;
-               ERR("Failed to format timestamp as local time");
-               goto end;
-       }
-end:
-       return ret;
-}
-
-int time_to_datetime_str(time_t time, char *str, size_t len)
-{
-       int ret = 0;
-       struct tm *tm_result;
-       struct tm tm_storage;
-       size_t strf_ret;
-
-       if (len < DATETIME_STR_LEN) {
-               ERR("Buffer too short to format to datetime: %zu bytes provided when at least %zu are needed",
-                               len, DATETIME_STR_LEN);
-               ret = -1;
-               goto end;
-       }
-
-       tm_result = localtime_r(&time, &tm_storage);
-       if (!tm_result) {
-               ret = -1;
-               PERROR("Failed to break down timestamp to tm structure");
-               goto end;
-       }
-
-       strf_ret = strftime(str, len, "%Y%m%d-%H%M%S", tm_result);
-       if (strf_ret == 0) {
-               ret = -1;
-               ERR("Failed to format timestamp as local time");
-               goto end;
-       }
-end:
-       return ret;
-}
diff --git a/src/common/time.cpp b/src/common/time.cpp
new file mode 100644 (file)
index 0000000..a08358f
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include <common/time.h>
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/error.h>
+#include <common/compat/errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <pthread.h>
+#include <locale.h>
+#include <string.h>
+#include <algorithm>
+
+static bool utf8_output_supported;
+
+bool locale_supports_utf8(void)
+{
+       return utf8_output_supported;
+}
+
+int timespec_to_ms(struct timespec ts, unsigned long *ms)
+{
+       unsigned long res, remain_ms;
+
+       if (ts.tv_sec > ULONG_MAX / MSEC_PER_SEC) {
+               errno = EOVERFLOW;
+               return -1;      /* multiplication overflow */
+       }
+       res = ts.tv_sec * MSEC_PER_SEC;
+       remain_ms = ULONG_MAX - res;
+       if (ts.tv_nsec / NSEC_PER_MSEC > remain_ms) {
+               errno = EOVERFLOW;
+               return -1;      /* addition overflow */
+       }
+       res += ts.tv_nsec / NSEC_PER_MSEC;
+       *ms = res;
+       return 0;
+}
+
+struct timespec timespec_abs_diff(struct timespec t1, struct timespec t2)
+{
+       uint64_t ts1 = (uint64_t) t1.tv_sec * (uint64_t) NSEC_PER_SEC +
+                       (uint64_t) t1.tv_nsec;
+       uint64_t ts2 = (uint64_t) t2.tv_sec * (uint64_t) NSEC_PER_SEC +
+                       (uint64_t) t2.tv_nsec;
+       uint64_t diff = std::max(ts1, ts2) - std::min(ts1, ts2);
+       struct timespec res;
+
+       res.tv_sec = diff / (uint64_t) NSEC_PER_SEC;
+       res.tv_nsec = diff % (uint64_t) NSEC_PER_SEC;
+       return res;
+}
+
+static
+void __attribute__((constructor)) init_locale_utf8_support(void)
+{
+       const char *program_locale = setlocale(LC_ALL, NULL);
+       const char *lang = getenv("LANG");
+
+       if (program_locale && strstr(program_locale, "utf8")) {
+               utf8_output_supported = true;
+       } else if (lang && strstr(lang, "utf8")) {
+               utf8_output_supported = true;
+       }
+}
+
+int time_to_iso8601_str(time_t time, char *str, size_t len)
+{
+       int ret = 0;
+       struct tm *tm_result;
+       struct tm tm_storage;
+       size_t strf_ret;
+
+       if (len < ISO8601_STR_LEN) {
+               ERR("Buffer too short to format ISO 8601 timestamp: %zu bytes provided when at least %zu are needed",
+                               len, ISO8601_STR_LEN);
+               ret = -1;
+               goto end;
+       }
+
+       tm_result = localtime_r(&time, &tm_storage);
+       if (!tm_result) {
+               ret = -1;
+               PERROR("Failed to break down timestamp to tm structure");
+               goto end;
+       }
+
+       strf_ret = strftime(str, len, "%Y%m%dT%H%M%S%z", tm_result);
+       if (strf_ret == 0) {
+               ret = -1;
+               ERR("Failed to format timestamp as local time");
+               goto end;
+       }
+end:
+       return ret;
+}
+
+int time_to_datetime_str(time_t time, char *str, size_t len)
+{
+       int ret = 0;
+       struct tm *tm_result;
+       struct tm tm_storage;
+       size_t strf_ret;
+
+       if (len < DATETIME_STR_LEN) {
+               ERR("Buffer too short to format to datetime: %zu bytes provided when at least %zu are needed",
+                               len, DATETIME_STR_LEN);
+               ret = -1;
+               goto end;
+       }
+
+       tm_result = localtime_r(&time, &tm_storage);
+       if (!tm_result) {
+               ret = -1;
+               PERROR("Failed to break down timestamp to tm structure");
+               goto end;
+       }
+
+       strf_ret = strftime(str, len, "%Y%m%d-%H%M%S", tm_result);
+       if (strf_ret == 0) {
+               ret = -1;
+               ERR("Failed to format timestamp as local time");
+               goto end;
+       }
+end:
+       return ret;
+}
diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c
deleted file mode 100644 (file)
index d04d516..0000000
+++ /dev/null
@@ -1,2191 +0,0 @@
-/*
- * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/compat/directory-handle.h>
-#include <common/credentials.h>
-#include <common/defaults.h>
-#include <common/dynamic-array.h>
-#include <common/error.h>
-#include <common/fd-tracker/fd-tracker.h>
-#include <common/fd-tracker/utils.h>
-#include <common/fs-handle.h>
-#include <common/fs-handle-internal.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/optional.h>
-#include <common/string-utils/format.h>
-#include <common/time.h>
-#include <common/trace-chunk-registry.h>
-#include <common/trace-chunk.h>
-#include <common/utils.h>
-#include <lttng/constant.h>
-
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <urcu/rculfhash.h>
-#include <urcu/ref.h>
-
-/*
- * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
- * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
- */
-#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
-#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
-
-enum trace_chunk_mode {
-       TRACE_CHUNK_MODE_USER,
-       TRACE_CHUNK_MODE_OWNER,
-};
-
-/*
- * Callback to invoke on release of a trace chunk. Note that there is no
- * need to 'lock' the trace chunk during the execution of these callbacks
- * since only one thread may access a chunk during its destruction (the last
- * to release its reference to the chunk).
- */
-typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk);
-
-/* Move a completed trace chunk to the 'completed' trace archive folder. */
-static
-int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
-/* Empty callback. */
-static
-int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
-/* Unlink old chunk files. */
-static
-int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
-static
-enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
-               struct lttng_trace_chunk *chunk, const char *path);
-
-struct chunk_credentials {
-       bool use_current_user;
-       struct lttng_credentials user;
-};
-
-/*
- * NOTE: Make sure to update:
- * - lttng_trace_chunk_copy(),
- * - lttng_trace_chunk_registry_element_create_from_chunk()
- * if you modify this structure.
- */
-struct lttng_trace_chunk {
-       pthread_mutex_t lock;
-       struct urcu_ref ref;
-       LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
-       /*
-        * First-level directories created within the trace chunk.
-        * Elements are of type 'char *'.
-        *
-        * Only used by _owner_ mode chunks.
-        */
-       struct lttng_dynamic_pointer_array top_level_directories;
-       /*
-        * All files contained within the trace chunk.
-        * Array of paths (char *).
-        */
-       struct lttng_dynamic_pointer_array files;
-       /* Is contained within an lttng_trace_chunk_registry_element? */
-       bool in_registry_element;
-       bool name_overridden;
-       char *name;
-       char *path;
-       /* An unset id means the chunk is anonymous. */
-       LTTNG_OPTIONAL(uint64_t) id;
-
-       /*
-        * The creation and close timestamps are NOT monotonic.
-        * They must not be used in context were monotonicity is required.
-        */
-       LTTNG_OPTIONAL(time_t) timestamp_creation;
-       LTTNG_OPTIONAL(time_t) timestamp_close;
-
-       LTTNG_OPTIONAL(struct chunk_credentials) credentials;
-       struct lttng_directory_handle *session_output_directory;
-       struct lttng_directory_handle *chunk_directory;
-       LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
-       /*
-        * fd_tracker instance through which file descriptors should be
-        * created/closed.
-        *
-        * An fd_tracker always outlives any trace chunk; there is no
-        * need to perform any reference counting of that object.
-        */
-       struct fd_tracker *fd_tracker;
-};
-
-/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
-struct lttng_trace_chunk_registry_element {
-       struct lttng_trace_chunk chunk;
-       uint64_t session_id;
-       /* Weak and only set when added. */
-       struct lttng_trace_chunk_registry *registry;
-       struct cds_lfht_node trace_chunk_registry_ht_node;
-       /* call_rcu delayed reclaim. */
-       struct rcu_head rcu_node;
-};
-
-struct lttng_trace_chunk_registry {
-       struct cds_lfht *ht;
-};
-
-struct fs_handle_untracked {
-       struct fs_handle parent;
-       int fd;
-       struct {
-               struct lttng_directory_handle *directory_handle;
-               char *path;
-       } location;
-};
-
-static
-int fs_handle_untracked_get_fd(struct fs_handle *handle);
-static
-void fs_handle_untracked_put_fd(struct fs_handle *handle);
-static
-int fs_handle_untracked_unlink(struct fs_handle *handle);
-static
-int fs_handle_untracked_close(struct fs_handle *handle);
-
-static const
-char *close_command_names[] = {
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
-               "move to completed chunk folder",
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
-               "no operation",
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
-               "delete",
-};
-
-static const
-chunk_command close_command_post_release_funcs[] = {
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] =
-                       lttng_trace_chunk_move_to_completed_post_release,
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] =
-                       lttng_trace_chunk_no_operation,
-       [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] =
-                       lttng_trace_chunk_delete_post_release,
-};
-
-static
-struct fs_handle *fs_handle_untracked_create(
-               struct lttng_directory_handle *directory_handle,
-               const char *path,
-               int fd)
-{
-       struct fs_handle_untracked *handle = NULL;
-       bool reference_acquired;
-       char *path_copy = strdup(path);
-
-       LTTNG_ASSERT(fd >= 0);
-       if (!path_copy) {
-               PERROR("Failed to copy file path while creating untracked filesystem handle");
-               goto end;
-       }
-
-       handle = zmalloc(sizeof(typeof(*handle)));
-       if (!handle) {
-               PERROR("Failed to allocate untracked filesystem handle");
-               goto end;
-       }
-
-       handle->parent = (typeof(handle->parent)) {
-               .get_fd = fs_handle_untracked_get_fd,
-               .put_fd = fs_handle_untracked_put_fd,
-               .unlink = fs_handle_untracked_unlink,
-               .close = fs_handle_untracked_close,
-       };
-
-       handle->fd = fd;
-       reference_acquired = lttng_directory_handle_get(directory_handle);
-       LTTNG_ASSERT(reference_acquired);
-       handle->location.directory_handle = directory_handle;
-       /* Ownership is transferred. */
-       handle->location.path = path_copy;
-       path_copy = NULL;
-end:
-       free(path_copy);
-       return handle ? &handle->parent : NULL;
-}
-
-static
-int fs_handle_untracked_get_fd(struct fs_handle *_handle)
-{
-       struct fs_handle_untracked *handle = container_of(
-                       _handle, struct fs_handle_untracked, parent);
-
-       return handle->fd;
-}
-
-static
-void fs_handle_untracked_put_fd(struct fs_handle *_handle)
-{
-       /* no-op. */
-}
-
-static
-int fs_handle_untracked_unlink(struct fs_handle *_handle)
-{
-       struct fs_handle_untracked *handle = container_of(
-                       _handle, struct fs_handle_untracked, parent);
-
-       return lttng_directory_handle_unlink_file(
-                       handle->location.directory_handle,
-                       handle->location.path);
-}
-
-static
-void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
-{
-       lttng_directory_handle_put(handle->location.directory_handle);
-       free(handle->location.path);
-       free(handle);
-}
-
-static
-int fs_handle_untracked_close(struct fs_handle *_handle)
-{
-       struct fs_handle_untracked *handle = container_of(
-                       _handle, struct fs_handle_untracked, parent);
-       int ret = close(handle->fd);
-
-       fs_handle_untracked_destroy(handle);
-       return ret;
-}
-
-static
-bool lttng_trace_chunk_registry_element_equals(
-               const struct lttng_trace_chunk_registry_element *a,
-               const struct lttng_trace_chunk_registry_element *b)
-{
-       if (a->session_id != b->session_id) {
-               goto not_equal;
-       }
-       if (a->chunk.id.is_set != b->chunk.id.is_set) {
-               goto not_equal;
-       }
-       if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
-               goto not_equal;
-       }
-       return true;
-not_equal:
-       return false;
-}
-
-static
-int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
-               const void *key)
-{
-       const struct lttng_trace_chunk_registry_element *element_a, *element_b;
-
-       element_a = (const struct lttng_trace_chunk_registry_element *) key;
-       element_b = caa_container_of(node, typeof(*element_b),
-                       trace_chunk_registry_ht_node);
-       return lttng_trace_chunk_registry_element_equals(element_a, element_b);
-}
-
-static
-unsigned long lttng_trace_chunk_registry_element_hash(
-               const struct lttng_trace_chunk_registry_element *element)
-{
-       unsigned long hash = hash_key_u64(&element->session_id,
-                       lttng_ht_seed);
-
-       if (element->chunk.id.is_set) {
-               hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
-       }
-
-       return hash;
-}
-
-static
-char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
-               const time_t *close_timestamp)
-{
-       int ret = 0;
-       char *new_name= NULL;
-       char start_datetime[ISO8601_STR_LEN] = {};
-       /* Add 1 for a '-' prefix. */
-       char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
-
-       ret = time_to_iso8601_str(
-                       creation_timestamp,
-                       start_datetime, sizeof(start_datetime));
-       if (ret) {
-               ERR("Failed to format trace chunk start date time");
-               goto error;
-       }
-       if (close_timestamp) {
-               *end_datetime_suffix = '-';
-               ret = time_to_iso8601_str(
-                               *close_timestamp,
-                               end_datetime_suffix + 1,
-                               sizeof(end_datetime_suffix) - 1);
-               if (ret) {
-                       ERR("Failed to format trace chunk end date time");
-                       goto error;
-               }
-       }
-       new_name = zmalloc(GENERATED_CHUNK_NAME_LEN);
-       if (!new_name) {
-               ERR("Failed to allocate buffer for automatically-generated trace chunk name");
-               goto error;
-       }
-       ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
-                       start_datetime, end_datetime_suffix, chunk_id);
-       if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
-               ERR("Failed to format trace chunk name");
-               goto error;
-       }
-
-       return new_name;
-error:
-       free(new_name);
-       return NULL;
-}
-
-static
-void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
-{
-       urcu_ref_init(&chunk->ref);
-       pthread_mutex_init(&chunk->lock, NULL);
-       lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
-       lttng_dynamic_pointer_array_init(&chunk->files, free);
-}
-
-static
-void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
-{
-       if (chunk->session_output_directory) {
-               lttng_directory_handle_put(
-                               chunk->session_output_directory);
-               chunk->session_output_directory = NULL;
-       }
-       if (chunk->chunk_directory) {
-               lttng_directory_handle_put(chunk->chunk_directory);
-               chunk->chunk_directory = NULL;
-       }
-       free(chunk->name);
-       chunk->name = NULL;
-       free(chunk->path);
-       chunk->path = NULL;
-       lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
-       lttng_dynamic_pointer_array_reset(&chunk->files);
-       pthread_mutex_destroy(&chunk->lock);
-}
-
-static
-struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
-{
-       struct lttng_trace_chunk *chunk = NULL;
-
-       chunk = zmalloc(sizeof(*chunk));
-       if (!chunk) {
-               ERR("Failed to allocate trace chunk");
-               goto end;
-       }
-       lttng_trace_chunk_init(chunk);
-end:
-       return chunk;
-}
-
-struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
-{
-       DBG("Creating anonymous trace chunk");
-       return lttng_trace_chunk_allocate();
-}
-
-struct lttng_trace_chunk *lttng_trace_chunk_create(
-               uint64_t chunk_id, time_t chunk_creation_time, const char *path)
-{
-       struct lttng_trace_chunk *chunk;
-       char chunk_creation_datetime_buf[16] = {};
-       const char *chunk_creation_datetime_str = "(formatting error)";
-       struct tm timeinfo_buf, *timeinfo;
-
-       timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
-       if (timeinfo) {
-               size_t strftime_ret;
-
-               /* Don't fail because of this; it is only used for logging. */
-               strftime_ret = strftime(chunk_creation_datetime_buf,
-                               sizeof(chunk_creation_datetime_buf),
-                               "%Y%m%d-%H%M%S", timeinfo);
-               if (strftime_ret) {
-                       chunk_creation_datetime_str =
-                                       chunk_creation_datetime_buf;
-               }
-       }
-
-       DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
-                       chunk_id, chunk_creation_datetime_str);
-       chunk = lttng_trace_chunk_allocate();
-       if (!chunk) {
-               goto end;
-       }
-
-       LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
-       LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
-       if (chunk_id != 0) {
-               chunk->name = generate_chunk_name(chunk_id,
-                               chunk_creation_time, NULL);
-               if (!chunk->name) {
-                       ERR("Failed to allocate trace chunk name storage");
-                       goto error;
-               }
-       }
-       if (path) {
-               chunk->path = strdup(path);
-               if (!chunk->path) {
-                       goto error;
-               }
-       } else {
-               if (chunk->name) {
-                       chunk->path = strdup(chunk->name);
-                       if (!chunk->path) {
-                               goto error;
-                       }
-               }
-       }
-
-       DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
-end:
-       return chunk;
-error:
-       lttng_trace_chunk_put(chunk);
-       return NULL;
-}
-
-void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
-               struct fd_tracker *fd_tracker)
-{
-       LTTNG_ASSERT(!chunk->session_output_directory);
-       LTTNG_ASSERT(!chunk->chunk_directory);
-       LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
-       chunk->fd_tracker = fd_tracker;
-}
-
-struct lttng_trace_chunk *lttng_trace_chunk_copy(
-               struct lttng_trace_chunk *source_chunk)
-{
-       struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
-
-       if (!new_chunk) {
-               goto end;
-       }
-
-       pthread_mutex_lock(&source_chunk->lock);
-       /*
-        * A new chunk is always a user; it shall create no new trace
-        * subdirectories.
-        */
-       new_chunk->mode = (typeof(new_chunk->mode)) {
-               .is_set = true,
-               .value = TRACE_CHUNK_MODE_USER,
-       };
-       /*
-        * top_level_directories is not copied as it is never used
-        * by _user_ mode chunks.
-        */
-       /* The new chunk is not part of a registry (yet, at least). */
-       new_chunk->in_registry_element = false;
-       new_chunk->name_overridden = source_chunk->name_overridden;
-       if (source_chunk->name) {
-               new_chunk->name = strdup(source_chunk->name);
-               if (!new_chunk->name) {
-                       ERR("Failed to copy source trace chunk name in %s()",
-                                       __FUNCTION__);
-                       goto error_unlock;
-               }
-       }
-       if (source_chunk->path) {
-               new_chunk->path = strdup(source_chunk->path);
-               if (!new_chunk->path) {
-                       ERR("Failed to copy source trace chunk path in %s()",
-                                       __FUNCTION__);
-               }
-       }
-       new_chunk->id = source_chunk->id;
-       new_chunk->timestamp_creation = source_chunk->timestamp_creation;
-       new_chunk->timestamp_close = source_chunk->timestamp_close;
-       new_chunk->credentials = source_chunk->credentials;
-       if (source_chunk->session_output_directory) {
-               const bool reference_acquired = lttng_directory_handle_get(
-                               source_chunk->session_output_directory);
-
-               LTTNG_ASSERT(reference_acquired);
-               new_chunk->session_output_directory =
-                               source_chunk->session_output_directory;
-       }
-       if (source_chunk->chunk_directory) {
-               const bool reference_acquired = lttng_directory_handle_get(
-                               source_chunk->chunk_directory);
-
-               LTTNG_ASSERT(reference_acquired);
-               new_chunk->chunk_directory = source_chunk->chunk_directory;
-       }
-       new_chunk->close_command = source_chunk->close_command;
-       new_chunk->fd_tracker = source_chunk->fd_tracker;
-       pthread_mutex_unlock(&source_chunk->lock);
-end:
-       return new_chunk;
-error_unlock:
-       pthread_mutex_unlock(&source_chunk->lock);
-       lttng_trace_chunk_put(new_chunk);
-       return NULL;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
-               struct lttng_trace_chunk *chunk, uint64_t *id)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->id.is_set) {
-               *id = chunk->id.value;
-       } else {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-       }
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
-               struct lttng_trace_chunk *chunk, time_t *creation_ts)
-
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->timestamp_creation.is_set) {
-               *creation_ts = chunk->timestamp_creation.value;
-       } else {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-       }
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
-               struct lttng_trace_chunk *chunk, time_t *close_ts)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->timestamp_close.is_set) {
-               *close_ts = chunk->timestamp_close.value;
-       } else {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-       }
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
-               struct lttng_trace_chunk *chunk, time_t close_ts)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->timestamp_creation.is_set) {
-               ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
-               goto end;
-       }
-
-       /*
-        * Note: we do not enforce that the closing timestamp be greater or
-        * equal to the begin timestamp. These timestamps are used for
-        * generating the chunk name and should only be used in context where
-        * the monotonicity of time is not important. The source of those
-        * timestamps is NOT monotonic and represent the system calendar time,
-        * also know as the wall time.
-        */
-       if (chunk->timestamp_creation.value > close_ts) {
-               WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
-                               chunk->timestamp_creation.value, close_ts);
-       }
-
-       LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
-       if (!chunk->name_overridden) {
-               free(chunk->name);
-               chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
-                               LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
-                               &close_ts);
-               if (!chunk->name) {
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               }
-       }
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
-               struct lttng_trace_chunk *chunk, const char **name,
-               bool *name_overridden)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (name_overridden) {
-               *name_overridden = chunk->name_overridden;
-       }
-       if (!chunk->name) {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-               goto end;
-       }
-       *name = chunk->name;
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
-{
-       bool name_overridden;
-
-       pthread_mutex_lock(&chunk->lock);
-       name_overridden = chunk->name_overridden;
-       pthread_mutex_unlock(&chunk->lock);
-       return name_overridden;
-}
-
-static
-bool is_valid_chunk_name(const char *name)
-{
-       size_t len;
-
-       if (!name) {
-               return false;
-       }
-
-       len = lttng_strnlen(name, LTTNG_NAME_MAX);
-       if (len == 0 || len == LTTNG_NAME_MAX) {
-               return false;
-       }
-
-       if (strchr(name, '/') || strchr(name, '.')) {
-               return false;
-       }
-
-       return true;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
-               struct lttng_trace_chunk *chunk, const char *name)
-
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       char *new_name, *new_path;
-
-       DBG("Override trace chunk name from %s to %s", chunk->name, name);
-       if (!is_valid_chunk_name(name)) {
-               ERR("Attempted to set an invalid name on a trace chunk: name = %s",
-                               name ? : "NULL");
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
-               goto end;
-       }
-
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->id.is_set) {
-               ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
-                               name);
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
-               goto end_unlock;
-       }
-
-       new_name = strdup(name);
-       if (!new_name) {
-               ERR("Failed to allocate new trace chunk name");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end_unlock;
-       }
-       free(chunk->name);
-       chunk->name = new_name;
-
-       new_path = strdup(name);
-       if (!new_path) {
-               ERR("Failed to allocate new trace chunk path");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end_unlock;
-       }
-       free(chunk->path);
-       chunk->path = new_path;
-
-       chunk->name_overridden = true;
-end_unlock:
-       pthread_mutex_unlock(&chunk->lock);
-end:
-       return status;
-}
-
-static
-enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
-               struct lttng_trace_chunk *chunk, const char *path)
-
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       struct lttng_directory_handle *rename_directory = NULL;
-       char *new_path, *old_path;
-       int ret;
-
-       if (chunk->name_overridden) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-
-       old_path = chunk->path;
-       DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
-
-       if ((!old_path && !path) ||
-                       (old_path && path && !strcmp(old_path, path)))  {
-               goto end;
-       }
-       /*
-        * Use chunk name as path if NULL path is specified.
-        */
-       if (!path) {
-               path = chunk->name;
-       }
-
-       /* Renaming from "" to "" is not accepted. */
-       if (path[0] == '\0' && old_path[0] == '\0') {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * If a rename is performed on a chunk for which the chunk_directory
-        * is not set (yet), or the session_output_directory is not set
-        * (interacting with a relay daemon), there is no rename to perform.
-        */
-       if (!chunk->chunk_directory ||
-                       !chunk->session_output_directory) {
-               goto skip_move;
-       }
-
-       if (old_path && old_path[0] != '\0' && path[0] != '\0') {
-               /* Rename chunk directory. */
-               ret = lttng_directory_handle_rename_as_user(
-                       chunk->session_output_directory,
-                       old_path,
-                       chunk->session_output_directory,
-                       path,
-                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
-                               NULL :
-                               &chunk->credentials.value.user);
-               if (ret) {
-                       PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
-                                       old_path, path);
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-               rename_directory = chunk->fd_tracker ?
-                               fd_tracker_create_directory_handle_from_handle(
-                                               chunk->fd_tracker,
-                                               chunk->session_output_directory,
-                                               path) :
-                               lttng_directory_handle_create_from_handle(
-                                               path,
-                                               chunk->session_output_directory);
-               if (!rename_directory) {
-                       ERR("Failed to get handle to trace chunk rename directory");
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-
-               /* Release old handle. */
-               lttng_directory_handle_put(chunk->chunk_directory);
-               /*
-                * Transfer new handle reference to chunk as the current chunk
-                * handle.
-                */
-               chunk->chunk_directory = rename_directory;
-               rename_directory = NULL;
-       } else if (old_path && old_path[0] == '\0') {
-               size_t i, count = lttng_dynamic_pointer_array_get_count(
-                               &chunk->top_level_directories);
-
-               ret = lttng_directory_handle_create_subdirectory_as_user(
-                               chunk->session_output_directory,
-                               path,
-                               DIR_CREATION_MODE,
-                               LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
-                                       NULL :
-                                       &chunk->credentials.value.user);
-               if (ret) {
-                       PERROR("Failed to create trace chunk rename directory \"%s\"",
-                                       path);
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-
-               rename_directory = lttng_directory_handle_create_from_handle(
-                               path, chunk->session_output_directory);
-               if (!rename_directory) {
-                       ERR("Failed to get handle to trace chunk rename directory");
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-
-               /* Move toplevel directories. */
-               for (i = 0; i < count; i++) {
-                       const char *top_level_name =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                       &chunk->top_level_directories, i);
-
-                       ret = lttng_directory_handle_rename_as_user(
-                                       chunk->chunk_directory,
-                                       top_level_name,
-                                       rename_directory,
-                                       top_level_name,
-                                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
-                                               NULL :
-                                               &chunk->credentials.value.user);
-                       if (ret) {
-                               PERROR("Failed to move \"%s\" to trace chunk rename directory",
-                                               top_level_name);
-                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-               /* Release old handle. */
-               lttng_directory_handle_put(chunk->chunk_directory);
-               /*
-                * Transfer new handle reference to chunk as the current chunk
-                * handle.
-                */
-               chunk->chunk_directory = rename_directory;
-               rename_directory = NULL;
-       } else if (old_path) {
-               size_t i, count = lttng_dynamic_pointer_array_get_count(
-                               &chunk->top_level_directories);
-               const bool reference_acquired = lttng_directory_handle_get(
-                               chunk->session_output_directory);
-
-               LTTNG_ASSERT(reference_acquired);
-               rename_directory = chunk->session_output_directory;
-
-               /* Move toplevel directories. */
-               for (i = 0; i < count; i++) {
-                       const char *top_level_name =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                       &chunk->top_level_directories, i);
-
-                       ret = lttng_directory_handle_rename_as_user(
-                                       chunk->chunk_directory,
-                                       top_level_name,
-                                       rename_directory,
-                                       top_level_name,
-                                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
-                                               NULL :
-                                               &chunk->credentials.value.user);
-                       if (ret) {
-                               PERROR("Failed to move \"%s\" to trace chunk rename directory",
-                                               top_level_name);
-                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-               /* Release old handle. */
-               lttng_directory_handle_put(chunk->chunk_directory);
-               /*
-                * Transfer new handle reference to chunk as the current chunk
-                * handle.
-                */
-               chunk->chunk_directory = rename_directory;
-               rename_directory = NULL;
-
-               /* Remove old directory. */
-               status = lttng_directory_handle_remove_subdirectory(
-                               chunk->session_output_directory,
-                               old_path);
-               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-                       ERR("Error removing subdirectory '%s' file when deleting chunk",
-                               old_path);
-                       goto end;
-               }
-       } else {
-               /* Unexpected !old_path && !path. */
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
-               goto end;
-       }
-
-skip_move:
-       new_path = strdup(path);
-       if (!new_path) {
-               ERR("Failed to allocate new trace chunk path");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       free(chunk->path);
-       chunk->path = new_path;
-end:
-       lttng_directory_handle_put(rename_directory);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
-               struct lttng_trace_chunk *chunk, const char *path)
-
-{
-       enum lttng_trace_chunk_status status;
-
-       pthread_mutex_lock(&chunk->lock);
-       status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
-       pthread_mutex_unlock(&chunk->lock);
-
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
-               struct lttng_trace_chunk *chunk,
-               struct lttng_credentials *credentials)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->credentials.is_set) {
-               if (chunk->credentials.value.use_current_user) {
-                       LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
-                       LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
-               } else {
-                       *credentials = chunk->credentials.value.user;
-               }
-       } else {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-       }
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
-               struct lttng_trace_chunk *chunk,
-               const struct lttng_credentials *user_credentials)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       const struct chunk_credentials credentials = {
-               .user = *user_credentials,
-               .use_current_user = false,
-       };
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->credentials.is_set) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
-               struct lttng_trace_chunk *chunk)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       const struct chunk_credentials credentials = {
-               .use_current_user = true,
-       };
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->credentials.is_set) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
-               struct lttng_trace_chunk *chunk,
-               struct lttng_directory_handle *session_output_directory)
-{
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       struct lttng_directory_handle *chunk_directory_handle = NULL;
-       bool reference_acquired;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->mode.is_set) {
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
-               goto end;
-       }
-       if (!chunk->credentials.is_set) {
-               /*
-                * Fatal error, credentials must be set before a
-                * directory is created.
-                */
-               ERR("Credentials of trace chunk are unset: refusing to set session output directory");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (chunk->path && chunk->path[0] != '\0') {
-               ret = lttng_directory_handle_create_subdirectory_as_user(
-                               session_output_directory,
-                               chunk->path,
-                               DIR_CREATION_MODE,
-                               !chunk->credentials.value.use_current_user ?
-                                       &chunk->credentials.value.user : NULL);
-               if (ret) {
-                       PERROR("Failed to create chunk output directory \"%s\"",
-                               chunk->path);
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-               chunk_directory_handle =
-                               chunk->fd_tracker ?
-                                       fd_tracker_create_directory_handle_from_handle(
-                                                       chunk->fd_tracker,
-                                                       session_output_directory,
-                                                       chunk->path) :
-                                       lttng_directory_handle_create_from_handle(
-                                                       chunk->path,
-                                                       session_output_directory);
-               if (!chunk_directory_handle) {
-                       /* The function already logs on all error paths. */
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                       goto end;
-               }
-       } else {
-               /*
-                * A nameless chunk does not need its own output directory.
-                * The session's output directory will be used.
-                */
-               reference_acquired = lttng_directory_handle_get(
-                               session_output_directory);
-
-               LTTNG_ASSERT(reference_acquired);
-               chunk_directory_handle = session_output_directory;
-       }
-       chunk->chunk_directory = chunk_directory_handle;
-       chunk_directory_handle = NULL;
-       reference_acquired = lttng_directory_handle_get(
-                       session_output_directory);
-       LTTNG_ASSERT(reference_acquired);
-       chunk->session_output_directory = session_output_directory;
-       LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
-               struct lttng_trace_chunk *chunk,
-               struct lttng_directory_handle *chunk_directory)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       bool reference_acquired;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->mode.is_set) {
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
-               goto end;
-       }
-       if (!chunk->credentials.is_set) {
-               ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       reference_acquired = lttng_directory_handle_get(chunk_directory);
-       LTTNG_ASSERT(reference_acquired);
-       chunk->chunk_directory = chunk_directory;
-       LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status
-lttng_trace_chunk_get_session_output_directory_handle(
-               struct lttng_trace_chunk *chunk,
-               struct lttng_directory_handle **handle)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->session_output_directory) {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-               *handle = NULL;
-               goto end;
-       } else {
-               const bool reference_acquired = lttng_directory_handle_get(
-                               chunk->session_output_directory);
-
-               LTTNG_ASSERT(reference_acquired);
-               *handle = chunk->session_output_directory;
-       }
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
-               struct lttng_trace_chunk *chunk,
-               const struct lttng_directory_handle **handle)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->chunk_directory) {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-               goto end;
-       }
-
-       *handle = chunk->chunk_directory;
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-/* Add a top-level directory to the trace chunk if it was previously unknown. */
-static
-int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
-               const char *new_path)
-{
-       int ret = 0;
-       bool found = false;
-       size_t i, count = lttng_dynamic_pointer_array_get_count(
-                       &chunk->top_level_directories);
-       const char *new_path_separator_pos = strchr(new_path, '/');
-       const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
-                       new_path_separator_pos - new_path : strlen(new_path);
-
-       for (i = 0; i < count; i++) {
-               const char *path = lttng_dynamic_pointer_array_get_pointer(
-                               &chunk->top_level_directories, i);
-               const ptrdiff_t path_top_level_len = strlen(path);
-
-               if (path_top_level_len != new_path_top_level_len) {
-                       continue;
-               }
-               if (!strncmp(path, new_path, path_top_level_len)) {
-                       found = true;
-                       break;
-               }
-       }
-
-       if (!found) {
-               char *copy = lttng_strndup(new_path, new_path_top_level_len);
-
-               DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
-                               new_path, chunk->name ? : "(unnamed)");
-               if (!copy) {
-                       PERROR("Failed to copy path");
-                       ret = -1;
-                       goto end;
-               }
-               ret = lttng_dynamic_pointer_array_add_pointer(
-                               &chunk->top_level_directories, copy);
-               if (ret) {
-                       ERR("Allocation failure while adding top-level directory entry to a trace chunk");
-                       free(copy);
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
-               struct lttng_trace_chunk *chunk,
-               const char *path)
-{
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       DBG("Creating trace chunk subdirectory \"%s\"", path);
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->credentials.is_set) {
-               /*
-                * Fatal error, credentials must be set before a
-                * directory is created.
-                */
-               ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (!chunk->mode.is_set ||
-                       chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
-               ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
-               goto end;
-       }
-       if (!chunk->chunk_directory) {
-               ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (*path == '/') {
-               ERR("Refusing to create absolute trace chunk directory \"%s\"",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
-               goto end;
-       }
-       ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
-                       chunk->chunk_directory, path,
-                       DIR_CREATION_MODE,
-                       chunk->credentials.value.use_current_user ?
-                                       NULL : &chunk->credentials.value.user);
-       if (ret) {
-               PERROR("Failed to create trace chunk subdirectory \"%s\"",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       ret = add_top_level_directory_unique(chunk, path);
-       if (ret) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-/*
- * TODO: Implement O(1) lookup.
- */
-static
-bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
-               const char *path, size_t *index)
-{
-       size_t i, count;
-
-       count = lttng_dynamic_pointer_array_get_count(&chunk->files);
-       for (i = 0; i < count; i++) {
-               const char *iter_path =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                       &chunk->files, i);
-               if (!strcmp(iter_path, path)) {
-                       if (index) {
-                               *index = i;
-                       }
-                       return true;
-               }
-       }
-       return false;
-}
-
-static
-enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
-               struct lttng_trace_chunk *chunk,
-               const char *path)
-{
-       char *copy;
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
-               return LTTNG_TRACE_CHUNK_STATUS_OK;
-       }
-       DBG("Adding new file \"%s\" to trace chunk \"%s\"",
-                       path, chunk->name ? : "(unnamed)");
-       copy = strdup(path);
-       if (!copy) {
-               PERROR("Failed to copy path");
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       ret = lttng_dynamic_pointer_array_add_pointer(
-                       &chunk->files, copy);
-       if (ret) {
-               ERR("Allocation failure while adding file to a trace chunk");
-               free(copy);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-end:
-       return status;
-}
-
-static
-void lttng_trace_chunk_remove_file(
-               struct lttng_trace_chunk *chunk,
-               const char *path)
-{
-       size_t index;
-       bool found;
-       int ret;
-
-       found = lttng_trace_chunk_find_file(chunk, path, &index);
-       if (!found) {
-               return;
-       }
-       ret = lttng_dynamic_pointer_array_remove_pointer(
-                       &chunk->files, index);
-       LTTNG_ASSERT(!ret);
-}
-
-static
-enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked(
-               struct lttng_trace_chunk *chunk,
-               const char *file_path,
-               int flags,
-               mode_t mode,
-               struct fs_handle **out_handle,
-               bool expect_no_file)
-{
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       DBG("Opening trace chunk file \"%s\"", file_path);
-       if (!chunk->credentials.is_set) {
-               /*
-                * Fatal error, credentials must be set before a
-                * file is created.
-                */
-               ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
-                               file_path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (!chunk->chunk_directory) {
-               ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
-                               file_path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       status = lttng_trace_chunk_add_file(chunk, file_path);
-       if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-               goto end;
-       }
-       if (chunk->fd_tracker) {
-               LTTNG_ASSERT(chunk->credentials.value.use_current_user);
-               *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker,
-                               chunk->chunk_directory, file_path, flags, &mode);
-               ret = *out_handle ? 0 : -1;
-       } else {
-               ret = lttng_directory_handle_open_file_as_user(
-                               chunk->chunk_directory, file_path, flags, mode,
-                               chunk->credentials.value.use_current_user ?
-                                               NULL :
-                                               &chunk->credentials.value.user);
-               if (ret >= 0) {
-                       *out_handle = fs_handle_untracked_create(
-                                       chunk->chunk_directory, file_path, ret);
-                       if (!*out_handle) {
-                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-       }
-       if (ret < 0) {
-               if (errno == ENOENT && expect_no_file) {
-                       status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
-               } else {
-                       PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
-                               file_path, flags, (int) mode);
-                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               }
-               lttng_trace_chunk_remove_file(chunk, file_path);
-               goto end;
-       }
-end:
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(
-               struct lttng_trace_chunk *chunk,
-               const char *file_path,
-               int flags,
-               mode_t mode,
-               struct fs_handle **out_handle,
-               bool expect_no_file)
-{
-       enum lttng_trace_chunk_status status;
-
-       pthread_mutex_lock(&chunk->lock);
-       status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
-                       flags, mode, out_handle, expect_no_file);
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
-               struct lttng_trace_chunk *chunk,
-               const char *file_path,
-               int flags,
-               mode_t mode,
-               int *out_fd,
-               bool expect_no_file)
-{
-       enum lttng_trace_chunk_status status;
-       struct fs_handle *fs_handle;
-
-       pthread_mutex_lock(&chunk->lock);
-       /*
-        * Using this method is never valid when an fd_tracker is being
-        * used since the resulting file descriptor would not be tracked.
-        */
-       LTTNG_ASSERT(!chunk->fd_tracker);
-       status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
-                       flags, mode, &fs_handle, expect_no_file);
-       pthread_mutex_unlock(&chunk->lock);
-
-       if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
-               *out_fd = fs_handle_get_fd(fs_handle);
-               /*
-                * Does not close the fd; we just "unbox" it from the fs_handle.
-                */
-               fs_handle_untracked_destroy(container_of(
-                               fs_handle, struct fs_handle_untracked, parent));
-       }
-
-       return status;
-}
-
-int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
-               const char *file_path)
-{
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       DBG("Unlinking trace chunk file \"%s\"", file_path);
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->credentials.is_set) {
-               /*
-                * Fatal error, credentials must be set before a
-                * file is unlinked.
-                */
-               ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
-                               file_path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (!chunk->chunk_directory) {
-               ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
-                               file_path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       ret = lttng_directory_handle_unlink_file_as_user(
-                       chunk->chunk_directory, file_path,
-                       chunk->credentials.value.use_current_user ?
-                                       NULL : &chunk->credentials.value.user);
-       if (ret < 0) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       lttng_trace_chunk_remove_file(chunk, file_path);
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-static
-int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
-               const char *path)
-{
-       int ret;
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       DBG("Recursively removing trace chunk directory \"%s\"", path);
-       pthread_mutex_lock(&chunk->lock);
-       if (!chunk->credentials.is_set) {
-               /*
-                * Fatal error, credentials must be set before a
-                * directory is removed.
-                */
-               ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       if (!chunk->chunk_directory) {
-               ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
-                               path);
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-       ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
-                       chunk->chunk_directory, path,
-                       chunk->credentials.value.use_current_user ?
-                                       NULL : &chunk->credentials.value.user,
-                       LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
-       if (ret < 0) {
-               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
-               goto end;
-       }
-end:
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-static
-int lttng_trace_chunk_move_to_completed_post_release(
-               struct lttng_trace_chunk *trace_chunk)
-{
-       int ret = 0;
-       char *archived_chunk_name = NULL;
-       const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
-       const time_t creation_timestamp =
-                       LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
-       const time_t close_timestamp =
-                       LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
-       struct lttng_directory_handle *archived_chunks_directory = NULL;
-       enum lttng_trace_chunk_status status;
-
-       if (!trace_chunk->mode.is_set ||
-                       trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
-                       !trace_chunk->session_output_directory) {
-               /*
-                * This command doesn't need to run if the output is remote
-                * or if the trace chunk is not owned by this process.
-                */
-               goto end;
-       }
-
-       LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
-       LTTNG_ASSERT(!trace_chunk->name_overridden);
-       LTTNG_ASSERT(trace_chunk->path);
-
-       archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
-                       &close_timestamp);
-       if (!archived_chunk_name) {
-               ERR("Failed to generate archived trace chunk name while renaming trace chunk");
-               ret = -1;
-               goto end;
-       }
-
-       ret = lttng_directory_handle_create_subdirectory_as_user(
-                       trace_chunk->session_output_directory,
-                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
-                       DIR_CREATION_MODE,
-                       !trace_chunk->credentials.value.use_current_user ?
-                                       &trace_chunk->credentials.value.user :
-                                       NULL);
-       if (ret) {
-               PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
-                               "\" directory for archived trace chunks");
-               goto end;
-       }
-
-       archived_chunks_directory = trace_chunk->fd_tracker ?
-                       fd_tracker_create_directory_handle_from_handle(
-                                       trace_chunk->fd_tracker,
-                                       trace_chunk->session_output_directory,
-                                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
-                       lttng_directory_handle_create_from_handle(
-                                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
-                                       trace_chunk->session_output_directory);
-       if (!archived_chunks_directory) {
-               PERROR("Failed to get handle to archived trace chunks directory");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Make sure chunk is renamed to old directory if not already done by
-        * the creation of the next chunk. This happens if a rotation is
-        * performed while tracing is stopped.
-        */
-       if (!trace_chunk->path || strcmp(trace_chunk->path,
-                       DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
-               status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
-                               DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
-               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-                       ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       ret = lttng_directory_handle_rename_as_user(
-                       trace_chunk->session_output_directory,
-                       trace_chunk->path,
-                       archived_chunks_directory,
-                       archived_chunk_name,
-                       LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
-                               NULL :
-                               &trace_chunk->credentials.value.user);
-       if (ret) {
-               PERROR("Failed to rename folder \"%s\" to \"%s\"",
-                               trace_chunk->path,
-                               archived_chunk_name);
-       }
-
-end:
-       lttng_directory_handle_put(archived_chunks_directory);
-       free(archived_chunk_name);
-       return ret;
-}
-
-static
-int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
-{
-       return 0;
-}
-
-static
-int lttng_trace_chunk_delete_post_release_user(
-               struct lttng_trace_chunk *trace_chunk)
-{
-       int ret = 0;
-
-       DBG("Trace chunk \"delete\" close command post-release (User)");
-
-       /* Unlink all files. */
-       while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
-               enum lttng_trace_chunk_status status;
-               const char *path;
-
-               /* Remove first. */
-               path = lttng_dynamic_pointer_array_get_pointer(
-                               &trace_chunk->files, 0);
-               DBG("Unlink file: %s", path);
-               status = lttng_trace_chunk_unlink_file(trace_chunk, path);
-               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-                       ERR("Error unlinking file '%s' when deleting chunk", path);
-                       ret = -1;
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-static
-int lttng_trace_chunk_delete_post_release_owner(
-               struct lttng_trace_chunk *trace_chunk)
-{
-       enum lttng_trace_chunk_status status;
-       size_t i, count;
-       int ret = 0;
-
-       ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
-       if (ret) {
-               goto end;
-       }
-
-       DBG("Trace chunk \"delete\" close command post-release (Owner)");
-
-       LTTNG_ASSERT(trace_chunk->session_output_directory);
-       LTTNG_ASSERT(trace_chunk->chunk_directory);
-
-       /* Remove empty directories. */
-       count = lttng_dynamic_pointer_array_get_count(
-                       &trace_chunk->top_level_directories);
-
-       for (i = 0; i < count; i++) {
-               const char *top_level_name =
-                               lttng_dynamic_pointer_array_get_pointer(
-                                       &trace_chunk->top_level_directories, i);
-
-               status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
-               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-                       ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
-                                       top_level_name);
-                       ret = -1;
-                       break;
-               }
-       }
-       if (!ret) {
-               lttng_directory_handle_put(trace_chunk->chunk_directory);
-               trace_chunk->chunk_directory = NULL;
-
-               if (trace_chunk->path && trace_chunk->path[0] != '\0') {
-                       status = lttng_directory_handle_remove_subdirectory(
-                                       trace_chunk->session_output_directory,
-                                       trace_chunk->path);
-                       if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
-                               ERR("Error removing subdirectory '%s' file when deleting chunk",
-                                       trace_chunk->path);
-                               ret = -1;
-                       }
-               }
-       }
-       free(trace_chunk->path);
-       trace_chunk->path = NULL;
-end:
-       return ret;
-}
-
-/*
- * For local files, session and consumer daemons all run the delete hook. The
- * consumer daemons have the list of files to unlink, and technically the
- * session daemon is the owner of the chunk. Unlink all files owned by each
- * consumer daemon.
- */
-static
-int lttng_trace_chunk_delete_post_release(
-               struct lttng_trace_chunk *trace_chunk)
-{
-       if (!trace_chunk->chunk_directory) {
-               return 0;
-       }
-
-       if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
-               return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
-       } else {
-               return lttng_trace_chunk_delete_post_release_user(trace_chunk);
-       }
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
-               struct lttng_trace_chunk *chunk,
-               enum lttng_trace_chunk_command_type *command_type)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->close_command.is_set) {
-               *command_type = chunk->close_command.value;
-               status = LTTNG_TRACE_CHUNK_STATUS_OK;
-       } else {
-               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
-       }
-       pthread_mutex_unlock(&chunk->lock);
-       return status;
-}
-
-enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
-               struct lttng_trace_chunk *chunk,
-               enum lttng_trace_chunk_command_type close_command)
-{
-       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
-
-       if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
-                       close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
-               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
-               goto end;
-       }
-
-       pthread_mutex_lock(&chunk->lock);
-       if (chunk->close_command.is_set) {
-               DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
-                               close_command_names[chunk->close_command.value],
-                               close_command_names[close_command]);
-       } else {
-               DBG("Setting trace chunk close command to \"%s\"",
-                               close_command_names[close_command]);
-       }
-       /*
-        * Unset close command for no-op for backward compatibility with relayd
-        * 2.11.
-        */
-       if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
-               LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
-       } else {
-               LTTNG_OPTIONAL_UNSET(&chunk->close_command);
-       }
-       pthread_mutex_unlock(&chunk->lock);
-end:
-       return status;
-}
-
-const char *lttng_trace_chunk_command_type_get_name(
-               enum lttng_trace_chunk_command_type command)
-{
-       switch (command) {
-       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
-               return "move to completed trace chunk folder";
-       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
-               return "no operation";
-       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
-               return "delete";
-       default:
-               abort();
-       }
-}
-
-bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a,
-               const struct lttng_trace_chunk *chunk_b)
-{
-       bool equal = false;
-
-       if (chunk_a == chunk_b) {
-               equal = true;
-               goto end;
-       }
-
-       if (!!chunk_a ^ !!chunk_b) {
-               goto end;
-       }
-
-       if (chunk_a->id.is_set ^ chunk_a->id.is_set) {
-               /* One id is set and not the other, thus they are not equal. */
-               goto end;
-       }
-
-       if (!chunk_a->id.is_set) {
-               /* Both ids are unset. */
-               equal = true;
-       } else {
-               equal = chunk_a->id.value == chunk_b->id.value;
-       }
-
-end:
-       return equal;
-}
-
-bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
-{
-       return urcu_ref_get_unless_zero(&chunk->ref);
-}
-
-static
-void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
-{
-       struct lttng_trace_chunk_registry_element *element =
-                       container_of(node, typeof(*element), rcu_node);
-
-       lttng_trace_chunk_fini(&element->chunk);
-       free(element);
-}
-
-static
-void lttng_trace_chunk_release(struct urcu_ref *ref)
-{
-       struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
-                       ref);
-
-       if (chunk->close_command.is_set) {
-               if (close_command_post_release_funcs[
-                               chunk->close_command.value](chunk)) {
-                       ERR("Trace chunk post-release command %s has failed.",
-                                       close_command_names[chunk->close_command.value]);
-               }
-       }
-
-       if (chunk->in_registry_element) {
-               struct lttng_trace_chunk_registry_element *element;
-
-               element = container_of(chunk, typeof(*element), chunk);
-               if (element->registry) {
-                       rcu_read_lock();
-                       cds_lfht_del(element->registry->ht,
-                                       &element->trace_chunk_registry_ht_node);
-                       rcu_read_unlock();
-                       call_rcu(&element->rcu_node,
-                                       free_lttng_trace_chunk_registry_element);
-               } else {
-                       /* Never published, can be free'd immediately. */
-                       free_lttng_trace_chunk_registry_element(
-                                       &element->rcu_node);
-               }
-       } else {
-               /* Not RCU-protected, free immediately. */
-               lttng_trace_chunk_fini(chunk);
-               free(chunk);
-       }
-}
-
-void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
-{
-       if (!chunk) {
-               return;
-       }
-       LTTNG_ASSERT(chunk->ref.refcount);
-       urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
-}
-
-struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
-{
-       struct lttng_trace_chunk_registry *registry;
-
-       registry = zmalloc(sizeof(*registry));
-       if (!registry) {
-               goto end;
-       }
-
-       registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
-                       CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
-       if (!registry->ht) {
-               goto error;
-       }
-end:
-       return registry;
-error:
-       lttng_trace_chunk_registry_destroy(registry);
-       return NULL;
-}
-
-void lttng_trace_chunk_registry_destroy(
-               struct lttng_trace_chunk_registry *registry)
-{
-       if (!registry) {
-               return;
-       }
-       if (registry->ht) {
-               int ret = cds_lfht_destroy(registry->ht, NULL);
-               LTTNG_ASSERT(!ret);
-       }
-       free(registry);
-}
-
-static
-struct lttng_trace_chunk_registry_element *
-lttng_trace_chunk_registry_element_create_from_chunk(
-               struct lttng_trace_chunk *chunk, uint64_t session_id)
-{
-       struct lttng_trace_chunk_registry_element *element =
-                       zmalloc(sizeof(*element));
-
-       if (!element) {
-               goto end;
-       }
-       cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
-       element->session_id = session_id;
-
-       element->chunk = *chunk;
-       lttng_trace_chunk_init(&element->chunk);
-       if (chunk->session_output_directory) {
-               /* Transferred ownership. */
-               element->chunk.session_output_directory =
-                               chunk->session_output_directory;
-               chunk->session_output_directory = NULL;
-       }
-       if (chunk->chunk_directory) {
-               /* Transferred ownership. */
-               element->chunk.chunk_directory = chunk->chunk_directory;
-               chunk->chunk_directory = NULL;
-       }
-       /*
-        * The original chunk becomes invalid; the name and path attributes are
-        * transferred to the new chunk instance.
-        */
-       chunk->name = NULL;
-       chunk->path = NULL;
-       element->chunk.fd_tracker = chunk->fd_tracker;
-       element->chunk.in_registry_element = true;
-end:
-       return element;
-}
-
-struct lttng_trace_chunk *
-lttng_trace_chunk_registry_publish_chunk(
-               struct lttng_trace_chunk_registry *registry,
-               uint64_t session_id, struct lttng_trace_chunk *chunk)
-{
-       struct lttng_trace_chunk_registry_element *element;
-       unsigned long element_hash;
-
-       pthread_mutex_lock(&chunk->lock);
-       element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
-                       session_id);
-       pthread_mutex_unlock(&chunk->lock);
-       if (!element) {
-               goto end;
-       }
-       /*
-        * chunk is now invalid, the only valid operation is a 'put' from the
-        * caller.
-        */
-       chunk = NULL;
-       element_hash = lttng_trace_chunk_registry_element_hash(element);
-
-       rcu_read_lock();
-       while (1) {
-               struct cds_lfht_node *published_node;
-               struct lttng_trace_chunk *published_chunk;
-               struct lttng_trace_chunk_registry_element *published_element;
-
-               published_node = cds_lfht_add_unique(registry->ht,
-                               element_hash,
-                               lttng_trace_chunk_registry_element_match,
-                               element,
-                               &element->trace_chunk_registry_ht_node);
-               if (published_node == &element->trace_chunk_registry_ht_node) {
-                       /* Successfully published the new element. */
-                       element->registry = registry;
-                       /* Acquire a reference for the caller. */
-                       if (lttng_trace_chunk_get(&element->chunk)) {
-                               break;
-                       } else {
-                               /*
-                                * Another thread concurrently unpublished the
-                                * trace chunk. This is currently unexpected.
-                                *
-                                * Re-attempt to publish.
-                                */
-                               ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
-                               continue;
-                       }
-               }
-
-               /*
-                * An equivalent trace chunk was published before this trace
-                * chunk. Attempt to acquire a reference to the one that was
-                * already published and release the reference to the copy we
-                * created if successful.
-                */
-               published_element = container_of(published_node,
-                               typeof(*published_element),
-                               trace_chunk_registry_ht_node);
-               published_chunk = &published_element->chunk;
-               if (lttng_trace_chunk_get(published_chunk)) {
-                       lttng_trace_chunk_put(&element->chunk);
-                       element = published_element;
-                       break;
-               }
-               /*
-                * A reference to the previously published trace chunk could not
-                * be acquired. Hence, retry to publish our copy of the trace 
-                * chunk.
-                */
-       }
-       rcu_read_unlock();
-end:
-       return element ? &element->chunk : NULL;
-}
-
-/*
- * Note that the caller must be registered as an RCU thread.
- * However, it does not need to hold the RCU read lock. The RCU read lock is
- * acquired to perform the look-up in the registry's hash table and held until
- * after a reference to the "found" trace chunk is acquired.
- *
- * IOW, holding a reference guarantees the existence of the object for the
- * caller.
- */
-static
-struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
-               const struct lttng_trace_chunk_registry *registry,
-               uint64_t session_id, uint64_t *chunk_id)
-{
-       const struct lttng_trace_chunk_registry_element target_element = {
-               .chunk.id.is_set = !!chunk_id,
-               .chunk.id.value = chunk_id ? *chunk_id : 0,
-               .session_id = session_id,
-       };
-       const unsigned long element_hash =
-                       lttng_trace_chunk_registry_element_hash(
-                               &target_element);
-       struct cds_lfht_node *published_node;
-       struct lttng_trace_chunk_registry_element *published_element;
-       struct lttng_trace_chunk *published_chunk = NULL;
-       struct cds_lfht_iter iter;
-
-       rcu_read_lock();
-       cds_lfht_lookup(registry->ht,
-                       element_hash,
-                       lttng_trace_chunk_registry_element_match,
-                       &target_element,
-                       &iter);
-       published_node = cds_lfht_iter_get_node(&iter);
-       if (!published_node) {
-               goto end;
-       }
-
-       published_element = container_of(published_node,
-                       typeof(*published_element),
-                       trace_chunk_registry_ht_node);
-       if (lttng_trace_chunk_get(&published_element->chunk)) {
-               published_chunk = &published_element->chunk;
-       }
-end:
-       rcu_read_unlock();
-       return published_chunk;
-}
-
-struct lttng_trace_chunk *
-lttng_trace_chunk_registry_find_chunk(
-               const struct lttng_trace_chunk_registry *registry,
-               uint64_t session_id, uint64_t chunk_id)
-{
-       return _lttng_trace_chunk_registry_find_chunk(registry,
-                       session_id, &chunk_id);
-}
-
-int lttng_trace_chunk_registry_chunk_exists(
-               const struct lttng_trace_chunk_registry *registry,
-               uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
-{
-       int ret = 0;
-       const struct lttng_trace_chunk_registry_element target_element = {
-               .chunk.id.is_set = true,
-               .chunk.id.value = chunk_id,
-               .session_id = session_id,
-       };
-       const unsigned long element_hash =
-                       lttng_trace_chunk_registry_element_hash(
-                               &target_element);
-       struct cds_lfht_node *published_node;
-       struct cds_lfht_iter iter;
-
-       rcu_read_lock();
-       cds_lfht_lookup(registry->ht,
-                       element_hash,
-                       lttng_trace_chunk_registry_element_match,
-                       &target_element,
-                       &iter);
-       published_node = cds_lfht_iter_get_node(&iter);
-       if (!published_node) {
-               *chunk_exists = false;
-               goto end;
-       }
-
-       *chunk_exists = !cds_lfht_is_node_deleted(published_node);
-end:
-       rcu_read_unlock();
-       return ret;
-}
-
-struct lttng_trace_chunk *
-lttng_trace_chunk_registry_find_anonymous_chunk(
-               const struct lttng_trace_chunk_registry *registry,
-               uint64_t session_id)
-{
-       return _lttng_trace_chunk_registry_find_chunk(registry,
-                       session_id, NULL);
-}
-
-unsigned int lttng_trace_chunk_registry_put_each_chunk(
-               const struct lttng_trace_chunk_registry *registry)
-{
-       struct cds_lfht_iter iter;
-       struct lttng_trace_chunk_registry_element *chunk_element;
-       unsigned int trace_chunks_left = 0;
-
-       DBG("Releasing trace chunk registry to all trace chunks");
-       rcu_read_lock();
-       cds_lfht_for_each_entry(registry->ht,
-                       &iter, chunk_element, trace_chunk_registry_ht_node) {
-               const char *chunk_id_str = "none";
-               char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
-
-               pthread_mutex_lock(&chunk_element->chunk.lock);
-               if (chunk_element->chunk.id.is_set) {
-                       int fmt_ret;
-
-                       fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
-                                       "%" PRIu64,
-                                       chunk_element->chunk.id.value);
-                       if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
-                               chunk_id_str = "formatting error";
-                       } else {
-                               chunk_id_str = chunk_id_buf;
-                       }
-               }
-
-               DBG("Releasing reference to trace chunk: session_id = %" PRIu64
-                               "chunk_id = %s, name = \"%s\", status = %s",
-                               chunk_element->session_id,
-                               chunk_id_str,
-                               chunk_element->chunk.name ? : "none",
-                               chunk_element->chunk.close_command.is_set ?
-                                               "open" : "closed");
-               pthread_mutex_unlock(&chunk_element->chunk.lock);
-               lttng_trace_chunk_put(&chunk_element->chunk);
-               trace_chunks_left++;
-       }
-       rcu_read_unlock();
-       DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
-                       __FUNCTION__);
-
-       return trace_chunks_left;
-}
diff --git a/src/common/trace-chunk.cpp b/src/common/trace-chunk.cpp
new file mode 100644 (file)
index 0000000..eed3d55
--- /dev/null
@@ -0,0 +1,2208 @@
+/*
+ * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/compat/directory-handle.h>
+#include <common/credentials.h>
+#include <common/defaults.h>
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/fd-tracker/fd-tracker.h>
+#include <common/fd-tracker/utils.h>
+#include <common/fs-handle.h>
+#include <common/fs-handle-internal.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/optional.h>
+#include <common/string-utils/format.h>
+#include <common/time.h>
+#include <common/trace-chunk-registry.h>
+#include <common/trace-chunk.h>
+#include <common/utils.h>
+#include <lttng/constant.h>
+
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <urcu/rculfhash.h>
+#include <urcu/ref.h>
+
+/*
+ * Two ISO 8601-compatible timestamps, separated by a hypen, followed an
+ * index, i.e. <start-iso-8601>-<end-iso-8601>-<id-uint64_t>.
+ */
+#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t))
+#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG)
+
+enum trace_chunk_mode {
+       TRACE_CHUNK_MODE_USER,
+       TRACE_CHUNK_MODE_OWNER,
+};
+
+/*
+ * Callback to invoke on release of a trace chunk. Note that there is no
+ * need to 'lock' the trace chunk during the execution of these callbacks
+ * since only one thread may access a chunk during its destruction (the last
+ * to release its reference to the chunk).
+ */
+typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk);
+
+/* Move a completed trace chunk to the 'completed' trace archive folder. */
+static
+int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk);
+/* Empty callback. */
+static
+int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk);
+/* Unlink old chunk files. */
+static
+int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk);
+static
+enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
+               struct lttng_trace_chunk *chunk, const char *path);
+
+struct chunk_credentials {
+       bool use_current_user;
+       struct lttng_credentials user;
+};
+
+/*
+ * NOTE: Make sure to update:
+ * - lttng_trace_chunk_copy(),
+ * - lttng_trace_chunk_registry_element_create_from_chunk()
+ * if you modify this structure.
+ */
+struct lttng_trace_chunk {
+       pthread_mutex_t lock;
+       struct urcu_ref ref;
+       LTTNG_OPTIONAL(enum trace_chunk_mode) mode;
+       /*
+        * First-level directories created within the trace chunk.
+        * Elements are of type 'char *'.
+        *
+        * Only used by _owner_ mode chunks.
+        */
+       struct lttng_dynamic_pointer_array top_level_directories;
+       /*
+        * All files contained within the trace chunk.
+        * Array of paths (char *).
+        */
+       struct lttng_dynamic_pointer_array files;
+       /* Is contained within an lttng_trace_chunk_registry_element? */
+       bool in_registry_element;
+       bool name_overridden;
+       char *name;
+       char *path;
+       /* An unset id means the chunk is anonymous. */
+       LTTNG_OPTIONAL(uint64_t) id;
+
+       /*
+        * The creation and close timestamps are NOT monotonic.
+        * They must not be used in context were monotonicity is required.
+        */
+       LTTNG_OPTIONAL(time_t) timestamp_creation;
+       LTTNG_OPTIONAL(time_t) timestamp_close;
+
+       LTTNG_OPTIONAL(struct chunk_credentials) credentials;
+       struct lttng_directory_handle *session_output_directory;
+       struct lttng_directory_handle *chunk_directory;
+       LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command;
+       /*
+        * fd_tracker instance through which file descriptors should be
+        * created/closed.
+        *
+        * An fd_tracker always outlives any trace chunk; there is no
+        * need to perform any reference counting of that object.
+        */
+       struct fd_tracker *fd_tracker;
+};
+
+/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */
+struct lttng_trace_chunk_registry_element {
+       struct lttng_trace_chunk chunk;
+       uint64_t session_id;
+       /* Weak and only set when added. */
+       struct lttng_trace_chunk_registry *registry;
+       struct cds_lfht_node trace_chunk_registry_ht_node;
+       /* call_rcu delayed reclaim. */
+       struct rcu_head rcu_node;
+};
+
+struct lttng_trace_chunk_registry {
+       struct cds_lfht *ht;
+};
+
+struct fs_handle_untracked {
+       struct fs_handle parent;
+       int fd;
+       struct {
+               struct lttng_directory_handle *directory_handle;
+               char *path;
+       } location;
+};
+
+static
+int fs_handle_untracked_get_fd(struct fs_handle *handle);
+static
+void fs_handle_untracked_put_fd(struct fs_handle *handle);
+static
+int fs_handle_untracked_unlink(struct fs_handle *handle);
+static
+int fs_handle_untracked_close(struct fs_handle *handle);
+
+static
+const char *lttng_trace_chunk_command_type_str(
+               lttng_trace_chunk_command_type type) {
+       switch (type) {
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
+               return "move to completed chunk folder";
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
+               return "no operation";
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
+               return "delete";
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
+               abort();
+       }
+
+       abort();
+};
+
+static
+const chunk_command close_command_get_post_release_func(
+               lttng_trace_chunk_command_type type) {
+       switch (type) {
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
+               return lttng_trace_chunk_move_to_completed_post_release;
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
+               return lttng_trace_chunk_no_operation;
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
+               return lttng_trace_chunk_delete_post_release;
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX:
+               abort();
+       }
+
+       abort();
+};
+
+static
+struct fs_handle *fs_handle_untracked_create(
+               struct lttng_directory_handle *directory_handle,
+               const char *path,
+               int fd)
+{
+       struct fs_handle_untracked *handle = NULL;
+       bool reference_acquired;
+       char *path_copy = strdup(path);
+
+       LTTNG_ASSERT(fd >= 0);
+       if (!path_copy) {
+               PERROR("Failed to copy file path while creating untracked filesystem handle");
+               goto end;
+       }
+
+       handle = (fs_handle_untracked *) zmalloc(sizeof(typeof(*handle)));
+       if (!handle) {
+               PERROR("Failed to allocate untracked filesystem handle");
+               goto end;
+       }
+
+       handle->parent = (typeof(handle->parent)) {
+               .get_fd = fs_handle_untracked_get_fd,
+               .put_fd = fs_handle_untracked_put_fd,
+               .unlink = fs_handle_untracked_unlink,
+               .close = fs_handle_untracked_close,
+       };
+
+       handle->fd = fd;
+       reference_acquired = lttng_directory_handle_get(directory_handle);
+       LTTNG_ASSERT(reference_acquired);
+       handle->location.directory_handle = directory_handle;
+       /* Ownership is transferred. */
+       handle->location.path = path_copy;
+       path_copy = NULL;
+end:
+       free(path_copy);
+       return handle ? &handle->parent : NULL;
+}
+
+static
+int fs_handle_untracked_get_fd(struct fs_handle *_handle)
+{
+       struct fs_handle_untracked *handle = container_of(
+                       _handle, struct fs_handle_untracked, parent);
+
+       return handle->fd;
+}
+
+static
+void fs_handle_untracked_put_fd(struct fs_handle *_handle)
+{
+       /* no-op. */
+}
+
+static
+int fs_handle_untracked_unlink(struct fs_handle *_handle)
+{
+       struct fs_handle_untracked *handle = container_of(
+                       _handle, struct fs_handle_untracked, parent);
+
+       return lttng_directory_handle_unlink_file(
+                       handle->location.directory_handle,
+                       handle->location.path);
+}
+
+static
+void fs_handle_untracked_destroy(struct fs_handle_untracked *handle)
+{
+       lttng_directory_handle_put(handle->location.directory_handle);
+       free(handle->location.path);
+       free(handle);
+}
+
+static
+int fs_handle_untracked_close(struct fs_handle *_handle)
+{
+       struct fs_handle_untracked *handle = container_of(
+                       _handle, struct fs_handle_untracked, parent);
+       int ret = close(handle->fd);
+
+       fs_handle_untracked_destroy(handle);
+       return ret;
+}
+
+static
+bool lttng_trace_chunk_registry_element_equals(
+               const struct lttng_trace_chunk_registry_element *a,
+               const struct lttng_trace_chunk_registry_element *b)
+{
+       if (a->session_id != b->session_id) {
+               goto not_equal;
+       }
+       if (a->chunk.id.is_set != b->chunk.id.is_set) {
+               goto not_equal;
+       }
+       if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) {
+               goto not_equal;
+       }
+       return true;
+not_equal:
+       return false;
+}
+
+static
+int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node,
+               const void *key)
+{
+       const struct lttng_trace_chunk_registry_element *element_a, *element_b;
+
+       element_a = (const struct lttng_trace_chunk_registry_element *) key;
+       element_b = caa_container_of(node, typeof(*element_b),
+                       trace_chunk_registry_ht_node);
+       return lttng_trace_chunk_registry_element_equals(element_a, element_b);
+}
+
+static
+unsigned long lttng_trace_chunk_registry_element_hash(
+               const struct lttng_trace_chunk_registry_element *element)
+{
+       unsigned long hash = hash_key_u64(&element->session_id,
+                       lttng_ht_seed);
+
+       if (element->chunk.id.is_set) {
+               hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed);
+       }
+
+       return hash;
+}
+
+static
+char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp,
+               const time_t *close_timestamp)
+{
+       int ret = 0;
+       char *new_name= NULL;
+       char start_datetime[ISO8601_STR_LEN] = {};
+       /* Add 1 for a '-' prefix. */
+       char end_datetime_suffix[ISO8601_STR_LEN + 1] = {};
+
+       ret = time_to_iso8601_str(
+                       creation_timestamp,
+                       start_datetime, sizeof(start_datetime));
+       if (ret) {
+               ERR("Failed to format trace chunk start date time");
+               goto error;
+       }
+       if (close_timestamp) {
+               *end_datetime_suffix = '-';
+               ret = time_to_iso8601_str(
+                               *close_timestamp,
+                               end_datetime_suffix + 1,
+                               sizeof(end_datetime_suffix) - 1);
+               if (ret) {
+                       ERR("Failed to format trace chunk end date time");
+                       goto error;
+               }
+       }
+       new_name = (char *) zmalloc(GENERATED_CHUNK_NAME_LEN);
+       if (!new_name) {
+               ERR("Failed to allocate buffer for automatically-generated trace chunk name");
+               goto error;
+       }
+       ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64,
+                       start_datetime, end_datetime_suffix, chunk_id);
+       if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) {
+               ERR("Failed to format trace chunk name");
+               goto error;
+       }
+
+       return new_name;
+error:
+       free(new_name);
+       return NULL;
+}
+
+static
+void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk)
+{
+       urcu_ref_init(&chunk->ref);
+       pthread_mutex_init(&chunk->lock, NULL);
+       lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free);
+       lttng_dynamic_pointer_array_init(&chunk->files, free);
+}
+
+static
+void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk)
+{
+       if (chunk->session_output_directory) {
+               lttng_directory_handle_put(
+                               chunk->session_output_directory);
+               chunk->session_output_directory = NULL;
+       }
+       if (chunk->chunk_directory) {
+               lttng_directory_handle_put(chunk->chunk_directory);
+               chunk->chunk_directory = NULL;
+       }
+       free(chunk->name);
+       chunk->name = NULL;
+       free(chunk->path);
+       chunk->path = NULL;
+       lttng_dynamic_pointer_array_reset(&chunk->top_level_directories);
+       lttng_dynamic_pointer_array_reset(&chunk->files);
+       pthread_mutex_destroy(&chunk->lock);
+}
+
+static
+struct lttng_trace_chunk *lttng_trace_chunk_allocate(void)
+{
+       struct lttng_trace_chunk *chunk = NULL;
+
+       chunk = (lttng_trace_chunk *) zmalloc(sizeof(*chunk));
+       if (!chunk) {
+               ERR("Failed to allocate trace chunk");
+               goto end;
+       }
+       lttng_trace_chunk_init(chunk);
+end:
+       return chunk;
+}
+
+struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void)
+{
+       DBG("Creating anonymous trace chunk");
+       return lttng_trace_chunk_allocate();
+}
+
+struct lttng_trace_chunk *lttng_trace_chunk_create(
+               uint64_t chunk_id, time_t chunk_creation_time, const char *path)
+{
+       struct lttng_trace_chunk *chunk;
+       char chunk_creation_datetime_buf[16] = {};
+       const char *chunk_creation_datetime_str = "(formatting error)";
+       struct tm timeinfo_buf, *timeinfo;
+
+       timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf);
+       if (timeinfo) {
+               size_t strftime_ret;
+
+               /* Don't fail because of this; it is only used for logging. */
+               strftime_ret = strftime(chunk_creation_datetime_buf,
+                               sizeof(chunk_creation_datetime_buf),
+                               "%Y%m%d-%H%M%S", timeinfo);
+               if (strftime_ret) {
+                       chunk_creation_datetime_str =
+                                       chunk_creation_datetime_buf;
+               }
+       }
+
+       DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s",
+                       chunk_id, chunk_creation_datetime_str);
+       chunk = lttng_trace_chunk_allocate();
+       if (!chunk) {
+               goto end;
+       }
+
+       LTTNG_OPTIONAL_SET(&chunk->id, chunk_id);
+       LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time);
+       if (chunk_id != 0) {
+               chunk->name = generate_chunk_name(chunk_id,
+                               chunk_creation_time, NULL);
+               if (!chunk->name) {
+                       ERR("Failed to allocate trace chunk name storage");
+                       goto error;
+               }
+       }
+       if (path) {
+               chunk->path = strdup(path);
+               if (!chunk->path) {
+                       goto error;
+               }
+       } else {
+               if (chunk->name) {
+                       chunk->path = strdup(chunk->name);
+                       if (!chunk->path) {
+                               goto error;
+                       }
+               }
+       }
+
+       DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)");
+end:
+       return chunk;
+error:
+       lttng_trace_chunk_put(chunk);
+       return NULL;
+}
+
+void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk,
+               struct fd_tracker *fd_tracker)
+{
+       LTTNG_ASSERT(!chunk->session_output_directory);
+       LTTNG_ASSERT(!chunk->chunk_directory);
+       LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0);
+       chunk->fd_tracker = fd_tracker;
+}
+
+struct lttng_trace_chunk *lttng_trace_chunk_copy(
+               struct lttng_trace_chunk *source_chunk)
+{
+       struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate();
+
+       if (!new_chunk) {
+               goto end;
+       }
+
+       pthread_mutex_lock(&source_chunk->lock);
+       /*
+        * A new chunk is always a user; it shall create no new trace
+        * subdirectories.
+        */
+       new_chunk->mode = (typeof(new_chunk->mode)) {
+               .is_set = true,
+               .value = TRACE_CHUNK_MODE_USER,
+       };
+       /*
+        * top_level_directories is not copied as it is never used
+        * by _user_ mode chunks.
+        */
+       /* The new chunk is not part of a registry (yet, at least). */
+       new_chunk->in_registry_element = false;
+       new_chunk->name_overridden = source_chunk->name_overridden;
+       if (source_chunk->name) {
+               new_chunk->name = strdup(source_chunk->name);
+               if (!new_chunk->name) {
+                       ERR("Failed to copy source trace chunk name in %s()",
+                                       __FUNCTION__);
+                       goto error_unlock;
+               }
+       }
+       if (source_chunk->path) {
+               new_chunk->path = strdup(source_chunk->path);
+               if (!new_chunk->path) {
+                       ERR("Failed to copy source trace chunk path in %s()",
+                                       __FUNCTION__);
+               }
+       }
+       new_chunk->id = source_chunk->id;
+       new_chunk->timestamp_creation = source_chunk->timestamp_creation;
+       new_chunk->timestamp_close = source_chunk->timestamp_close;
+       new_chunk->credentials = source_chunk->credentials;
+       if (source_chunk->session_output_directory) {
+               const bool reference_acquired = lttng_directory_handle_get(
+                               source_chunk->session_output_directory);
+
+               LTTNG_ASSERT(reference_acquired);
+               new_chunk->session_output_directory =
+                               source_chunk->session_output_directory;
+       }
+       if (source_chunk->chunk_directory) {
+               const bool reference_acquired = lttng_directory_handle_get(
+                               source_chunk->chunk_directory);
+
+               LTTNG_ASSERT(reference_acquired);
+               new_chunk->chunk_directory = source_chunk->chunk_directory;
+       }
+       new_chunk->close_command = source_chunk->close_command;
+       new_chunk->fd_tracker = source_chunk->fd_tracker;
+       pthread_mutex_unlock(&source_chunk->lock);
+end:
+       return new_chunk;
+error_unlock:
+       pthread_mutex_unlock(&source_chunk->lock);
+       lttng_trace_chunk_put(new_chunk);
+       return NULL;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_id(
+               struct lttng_trace_chunk *chunk, uint64_t *id)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->id.is_set) {
+               *id = chunk->id.value;
+       } else {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+       }
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp(
+               struct lttng_trace_chunk *chunk, time_t *creation_ts)
+
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->timestamp_creation.is_set) {
+               *creation_ts = chunk->timestamp_creation.value;
+       } else {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+       }
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp(
+               struct lttng_trace_chunk *chunk, time_t *close_ts)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->timestamp_close.is_set) {
+               *close_ts = chunk->timestamp_close.value;
+       } else {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+       }
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp(
+               struct lttng_trace_chunk *chunk, time_t close_ts)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->timestamp_creation.is_set) {
+               ERR("Failed to set trace chunk close timestamp: creation timestamp is unset");
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
+               goto end;
+       }
+
+       /*
+        * Note: we do not enforce that the closing timestamp be greater or
+        * equal to the begin timestamp. These timestamps are used for
+        * generating the chunk name and should only be used in context where
+        * the monotonicity of time is not important. The source of those
+        * timestamps is NOT monotonic and represent the system calendar time,
+        * also know as the wall time.
+        */
+       if (chunk->timestamp_creation.value > close_ts) {
+               WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld",
+                               chunk->timestamp_creation.value, close_ts);
+       }
+
+       LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts);
+       if (!chunk->name_overridden) {
+               free(chunk->name);
+               chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id),
+                               LTTNG_OPTIONAL_GET(chunk->timestamp_creation),
+                               &close_ts);
+               if (!chunk->name) {
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               }
+       }
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_name(
+               struct lttng_trace_chunk *chunk, const char **name,
+               bool *name_overridden)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (name_overridden) {
+               *name_overridden = chunk->name_overridden;
+       }
+       if (!chunk->name) {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+               goto end;
+       }
+       *name = chunk->name;
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk)
+{
+       bool name_overridden;
+
+       pthread_mutex_lock(&chunk->lock);
+       name_overridden = chunk->name_overridden;
+       pthread_mutex_unlock(&chunk->lock);
+       return name_overridden;
+}
+
+static
+bool is_valid_chunk_name(const char *name)
+{
+       size_t len;
+
+       if (!name) {
+               return false;
+       }
+
+       len = lttng_strnlen(name, LTTNG_NAME_MAX);
+       if (len == 0 || len == LTTNG_NAME_MAX) {
+               return false;
+       }
+
+       if (strchr(name, '/') || strchr(name, '.')) {
+               return false;
+       }
+
+       return true;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_override_name(
+               struct lttng_trace_chunk *chunk, const char *name)
+
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       char *new_name, *new_path;
+
+       DBG("Override trace chunk name from %s to %s", chunk->name, name);
+       if (!is_valid_chunk_name(name)) {
+               ERR("Attempted to set an invalid name on a trace chunk: name = %s",
+                               name ? : "NULL");
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
+               goto end;
+       }
+
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->id.is_set) {
+               ERR("Attempted to set an override name on an anonymous trace chunk: name = %s",
+                               name);
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
+               goto end_unlock;
+       }
+
+       new_name = strdup(name);
+       if (!new_name) {
+               ERR("Failed to allocate new trace chunk name");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end_unlock;
+       }
+       free(chunk->name);
+       chunk->name = new_name;
+
+       new_path = strdup(name);
+       if (!new_path) {
+               ERR("Failed to allocate new trace chunk path");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end_unlock;
+       }
+       free(chunk->path);
+       chunk->path = new_path;
+
+       chunk->name_overridden = true;
+end_unlock:
+       pthread_mutex_unlock(&chunk->lock);
+end:
+       return status;
+}
+
+static
+enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock(
+               struct lttng_trace_chunk *chunk, const char *path)
+
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       struct lttng_directory_handle *rename_directory = NULL;
+       char *new_path, *old_path;
+       int ret;
+
+       if (chunk->name_overridden) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+
+       old_path = chunk->path;
+       DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path);
+
+       if ((!old_path && !path) ||
+                       (old_path && path && !strcmp(old_path, path)))  {
+               goto end;
+       }
+       /*
+        * Use chunk name as path if NULL path is specified.
+        */
+       if (!path) {
+               path = chunk->name;
+       }
+
+       /* Renaming from "" to "" is not accepted. */
+       if (path[0] == '\0' && old_path[0] == '\0') {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * If a rename is performed on a chunk for which the chunk_directory
+        * is not set (yet), or the session_output_directory is not set
+        * (interacting with a relay daemon), there is no rename to perform.
+        */
+       if (!chunk->chunk_directory ||
+                       !chunk->session_output_directory) {
+               goto skip_move;
+       }
+
+       if (old_path && old_path[0] != '\0' && path[0] != '\0') {
+               /* Rename chunk directory. */
+               ret = lttng_directory_handle_rename_as_user(
+                       chunk->session_output_directory,
+                       old_path,
+                       chunk->session_output_directory,
+                       path,
+                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+                               NULL :
+                               &chunk->credentials.value.user);
+               if (ret) {
+                       PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"",
+                                       old_path, path);
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+               rename_directory = chunk->fd_tracker ?
+                               fd_tracker_create_directory_handle_from_handle(
+                                               chunk->fd_tracker,
+                                               chunk->session_output_directory,
+                                               path) :
+                               lttng_directory_handle_create_from_handle(
+                                               path,
+                                               chunk->session_output_directory);
+               if (!rename_directory) {
+                       ERR("Failed to get handle to trace chunk rename directory");
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* Release old handle. */
+               lttng_directory_handle_put(chunk->chunk_directory);
+               /*
+                * Transfer new handle reference to chunk as the current chunk
+                * handle.
+                */
+               chunk->chunk_directory = rename_directory;
+               rename_directory = NULL;
+       } else if (old_path && old_path[0] == '\0') {
+               size_t i, count = lttng_dynamic_pointer_array_get_count(
+                               &chunk->top_level_directories);
+
+               ret = lttng_directory_handle_create_subdirectory_as_user(
+                               chunk->session_output_directory,
+                               path,
+                               DIR_CREATION_MODE,
+                               LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+                                       NULL :
+                                       &chunk->credentials.value.user);
+               if (ret) {
+                       PERROR("Failed to create trace chunk rename directory \"%s\"",
+                                       path);
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+
+               rename_directory = lttng_directory_handle_create_from_handle(
+                               path, chunk->session_output_directory);
+               if (!rename_directory) {
+                       ERR("Failed to get handle to trace chunk rename directory");
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* Move toplevel directories. */
+               for (i = 0; i < count; i++) {
+                       const char *top_level_name =
+                               (const char *) lttng_dynamic_pointer_array_get_pointer(
+                                       &chunk->top_level_directories, i);
+
+                       ret = lttng_directory_handle_rename_as_user(
+                                       chunk->chunk_directory,
+                                       top_level_name,
+                                       rename_directory,
+                                       top_level_name,
+                                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+                                               NULL :
+                                               &chunk->credentials.value.user);
+                       if (ret) {
+                               PERROR("Failed to move \"%s\" to trace chunk rename directory",
+                                               top_level_name);
+                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+               /* Release old handle. */
+               lttng_directory_handle_put(chunk->chunk_directory);
+               /*
+                * Transfer new handle reference to chunk as the current chunk
+                * handle.
+                */
+               chunk->chunk_directory = rename_directory;
+               rename_directory = NULL;
+       } else if (old_path) {
+               size_t i, count = lttng_dynamic_pointer_array_get_count(
+                               &chunk->top_level_directories);
+               const bool reference_acquired = lttng_directory_handle_get(
+                               chunk->session_output_directory);
+
+               LTTNG_ASSERT(reference_acquired);
+               rename_directory = chunk->session_output_directory;
+
+               /* Move toplevel directories. */
+               for (i = 0; i < count; i++) {
+                       const char *top_level_name =
+                               (const char *) lttng_dynamic_pointer_array_get_pointer(
+                                       &chunk->top_level_directories, i);
+
+                       ret = lttng_directory_handle_rename_as_user(
+                                       chunk->chunk_directory,
+                                       top_level_name,
+                                       rename_directory,
+                                       top_level_name,
+                                       LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ?
+                                               NULL :
+                                               &chunk->credentials.value.user);
+                       if (ret) {
+                               PERROR("Failed to move \"%s\" to trace chunk rename directory",
+                                               top_level_name);
+                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+               /* Release old handle. */
+               lttng_directory_handle_put(chunk->chunk_directory);
+               /*
+                * Transfer new handle reference to chunk as the current chunk
+                * handle.
+                */
+               chunk->chunk_directory = rename_directory;
+               rename_directory = NULL;
+
+               /* Remove old directory. */
+               status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory(
+                               chunk->session_output_directory,
+                               old_path);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       ERR("Error removing subdirectory '%s' file when deleting chunk",
+                               old_path);
+                       goto end;
+               }
+       } else {
+               /* Unexpected !old_path && !path. */
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
+               goto end;
+       }
+
+skip_move:
+       new_path = strdup(path);
+       if (!new_path) {
+               ERR("Failed to allocate new trace chunk path");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       free(chunk->path);
+       chunk->path = new_path;
+end:
+       lttng_directory_handle_put(rename_directory);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_rename_path(
+               struct lttng_trace_chunk *chunk, const char *path)
+
+{
+       enum lttng_trace_chunk_status status;
+
+       pthread_mutex_lock(&chunk->lock);
+       status = lttng_trace_chunk_rename_path_no_lock(chunk, path);
+       pthread_mutex_unlock(&chunk->lock);
+
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials(
+               struct lttng_trace_chunk *chunk,
+               struct lttng_credentials *credentials)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->credentials.is_set) {
+               if (chunk->credentials.value.use_current_user) {
+                       LTTNG_OPTIONAL_SET(&credentials->uid, geteuid());
+                       LTTNG_OPTIONAL_SET(&credentials->gid, getegid());
+               } else {
+                       *credentials = chunk->credentials.value.user;
+               }
+       } else {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+       }
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials(
+               struct lttng_trace_chunk *chunk,
+               const struct lttng_credentials *user_credentials)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       const struct chunk_credentials credentials = {
+               .use_current_user = false,
+               .user = *user_credentials,
+       };
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->credentials.is_set) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user(
+               struct lttng_trace_chunk *chunk)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       const struct chunk_credentials credentials = {
+               .use_current_user = true,
+       };
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->credentials.is_set) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       LTTNG_OPTIONAL_SET(&chunk->credentials, credentials);
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner(
+               struct lttng_trace_chunk *chunk,
+               struct lttng_directory_handle *session_output_directory)
+{
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       struct lttng_directory_handle *chunk_directory_handle = NULL;
+       bool reference_acquired;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->mode.is_set) {
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
+               goto end;
+       }
+       if (!chunk->credentials.is_set) {
+               /*
+                * Fatal error, credentials must be set before a
+                * directory is created.
+                */
+               ERR("Credentials of trace chunk are unset: refusing to set session output directory");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (chunk->path && chunk->path[0] != '\0') {
+               ret = lttng_directory_handle_create_subdirectory_as_user(
+                               session_output_directory,
+                               chunk->path,
+                               DIR_CREATION_MODE,
+                               !chunk->credentials.value.use_current_user ?
+                                       &chunk->credentials.value.user : NULL);
+               if (ret) {
+                       PERROR("Failed to create chunk output directory \"%s\"",
+                               chunk->path);
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+               chunk_directory_handle =
+                               chunk->fd_tracker ?
+                                       fd_tracker_create_directory_handle_from_handle(
+                                                       chunk->fd_tracker,
+                                                       session_output_directory,
+                                                       chunk->path) :
+                                       lttng_directory_handle_create_from_handle(
+                                                       chunk->path,
+                                                       session_output_directory);
+               if (!chunk_directory_handle) {
+                       /* The function already logs on all error paths. */
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                       goto end;
+               }
+       } else {
+               /*
+                * A nameless chunk does not need its own output directory.
+                * The session's output directory will be used.
+                */
+               reference_acquired = lttng_directory_handle_get(
+                               session_output_directory);
+
+               LTTNG_ASSERT(reference_acquired);
+               chunk_directory_handle = session_output_directory;
+       }
+       chunk->chunk_directory = chunk_directory_handle;
+       chunk_directory_handle = NULL;
+       reference_acquired = lttng_directory_handle_get(
+                       session_output_directory);
+       LTTNG_ASSERT(reference_acquired);
+       chunk->session_output_directory = session_output_directory;
+       LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER);
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user(
+               struct lttng_trace_chunk *chunk,
+               struct lttng_directory_handle *chunk_directory)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       bool reference_acquired;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->mode.is_set) {
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
+               goto end;
+       }
+       if (!chunk->credentials.is_set) {
+               ERR("Credentials of trace chunk are unset: refusing to set chunk output directory");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       reference_acquired = lttng_directory_handle_get(chunk_directory);
+       LTTNG_ASSERT(reference_acquired);
+       chunk->chunk_directory = chunk_directory;
+       LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER);
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status
+lttng_trace_chunk_get_session_output_directory_handle(
+               struct lttng_trace_chunk *chunk,
+               struct lttng_directory_handle **handle)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->session_output_directory) {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+               *handle = NULL;
+               goto end;
+       } else {
+               const bool reference_acquired = lttng_directory_handle_get(
+                               chunk->session_output_directory);
+
+               LTTNG_ASSERT(reference_acquired);
+               *handle = chunk->session_output_directory;
+       }
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle(
+               struct lttng_trace_chunk *chunk,
+               const struct lttng_directory_handle **handle)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->chunk_directory) {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+               goto end;
+       }
+
+       *handle = chunk->chunk_directory;
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+/* Add a top-level directory to the trace chunk if it was previously unknown. */
+static
+int add_top_level_directory_unique(struct lttng_trace_chunk *chunk,
+               const char *new_path)
+{
+       int ret = 0;
+       bool found = false;
+       size_t i, count = lttng_dynamic_pointer_array_get_count(
+                       &chunk->top_level_directories);
+       const char *new_path_separator_pos = strchr(new_path, '/');
+       const ptrdiff_t new_path_top_level_len = new_path_separator_pos ?
+                       new_path_separator_pos - new_path : strlen(new_path);
+
+       for (i = 0; i < count; i++) {
+               const char *path = (const char *) lttng_dynamic_pointer_array_get_pointer(
+                               &chunk->top_level_directories, i);
+               const ptrdiff_t path_top_level_len = strlen(path);
+
+               if (path_top_level_len != new_path_top_level_len) {
+                       continue;
+               }
+               if (!strncmp(path, new_path, path_top_level_len)) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               char *copy = lttng_strndup(new_path, new_path_top_level_len);
+
+               DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"",
+                               new_path, chunk->name ? : "(unnamed)");
+               if (!copy) {
+                       PERROR("Failed to copy path");
+                       ret = -1;
+                       goto end;
+               }
+               ret = lttng_dynamic_pointer_array_add_pointer(
+                               &chunk->top_level_directories, copy);
+               if (ret) {
+                       ERR("Allocation failure while adding top-level directory entry to a trace chunk");
+                       free(copy);
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory(
+               struct lttng_trace_chunk *chunk,
+               const char *path)
+{
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       DBG("Creating trace chunk subdirectory \"%s\"", path);
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->credentials.is_set) {
+               /*
+                * Fatal error, credentials must be set before a
+                * directory is created.
+                */
+               ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (!chunk->mode.is_set ||
+                       chunk->mode.value != TRACE_CHUNK_MODE_OWNER) {
+               ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION;
+               goto end;
+       }
+       if (!chunk->chunk_directory) {
+               ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (*path == '/') {
+               ERR("Refusing to create absolute trace chunk directory \"%s\"",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
+               goto end;
+       }
+       ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
+                       chunk->chunk_directory, path,
+                       DIR_CREATION_MODE,
+                       chunk->credentials.value.use_current_user ?
+                                       NULL : &chunk->credentials.value.user);
+       if (ret) {
+               PERROR("Failed to create trace chunk subdirectory \"%s\"",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       ret = add_top_level_directory_unique(chunk, path);
+       if (ret) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+/*
+ * TODO: Implement O(1) lookup.
+ */
+static
+bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk,
+               const char *path, size_t *index)
+{
+       size_t i, count;
+
+       count = lttng_dynamic_pointer_array_get_count(&chunk->files);
+       for (i = 0; i < count; i++) {
+               const char *iter_path =
+                       (const char *) lttng_dynamic_pointer_array_get_pointer(
+                               &chunk->files, i);
+               if (!strcmp(iter_path, path)) {
+                       if (index) {
+                               *index = i;
+                       }
+                       return true;
+               }
+       }
+       return false;
+}
+
+static
+enum lttng_trace_chunk_status lttng_trace_chunk_add_file(
+               struct lttng_trace_chunk *chunk,
+               const char *path)
+{
+       char *copy;
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       if (lttng_trace_chunk_find_file(chunk, path, NULL)) {
+               return LTTNG_TRACE_CHUNK_STATUS_OK;
+       }
+       DBG("Adding new file \"%s\" to trace chunk \"%s\"",
+                       path, chunk->name ? : "(unnamed)");
+       copy = strdup(path);
+       if (!copy) {
+               PERROR("Failed to copy path");
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       ret = lttng_dynamic_pointer_array_add_pointer(
+                       &chunk->files, copy);
+       if (ret) {
+               ERR("Allocation failure while adding file to a trace chunk");
+               free(copy);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+end:
+       return status;
+}
+
+static
+void lttng_trace_chunk_remove_file(
+               struct lttng_trace_chunk *chunk,
+               const char *path)
+{
+       size_t index;
+       bool found;
+       int ret;
+
+       found = lttng_trace_chunk_find_file(chunk, path, &index);
+       if (!found) {
+               return;
+       }
+       ret = lttng_dynamic_pointer_array_remove_pointer(
+                       &chunk->files, index);
+       LTTNG_ASSERT(!ret);
+}
+
+static
+enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked(
+               struct lttng_trace_chunk *chunk,
+               const char *file_path,
+               int flags,
+               mode_t mode,
+               struct fs_handle **out_handle,
+               bool expect_no_file)
+{
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       DBG("Opening trace chunk file \"%s\"", file_path);
+       if (!chunk->credentials.is_set) {
+               /*
+                * Fatal error, credentials must be set before a
+                * file is created.
+                */
+               ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"",
+                               file_path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (!chunk->chunk_directory) {
+               ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory",
+                               file_path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       status = lttng_trace_chunk_add_file(chunk, file_path);
+       if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+               goto end;
+       }
+       if (chunk->fd_tracker) {
+               LTTNG_ASSERT(chunk->credentials.value.use_current_user);
+               *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker,
+                               chunk->chunk_directory, file_path, flags, &mode);
+               ret = *out_handle ? 0 : -1;
+       } else {
+               ret = lttng_directory_handle_open_file_as_user(
+                               chunk->chunk_directory, file_path, flags, mode,
+                               chunk->credentials.value.use_current_user ?
+                                               NULL :
+                                               &chunk->credentials.value.user);
+               if (ret >= 0) {
+                       *out_handle = fs_handle_untracked_create(
+                                       chunk->chunk_directory, file_path, ret);
+                       if (!*out_handle) {
+                               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+       }
+       if (ret < 0) {
+               if (errno == ENOENT && expect_no_file) {
+                       status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE;
+               } else {
+                       PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d",
+                               file_path, flags, (int) mode);
+                       status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               }
+               lttng_trace_chunk_remove_file(chunk, file_path);
+               goto end;
+       }
+end:
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle(
+               struct lttng_trace_chunk *chunk,
+               const char *file_path,
+               int flags,
+               mode_t mode,
+               struct fs_handle **out_handle,
+               bool expect_no_file)
+{
+       enum lttng_trace_chunk_status status;
+
+       pthread_mutex_lock(&chunk->lock);
+       status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
+                       flags, mode, out_handle, expect_no_file);
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_open_file(
+               struct lttng_trace_chunk *chunk,
+               const char *file_path,
+               int flags,
+               mode_t mode,
+               int *out_fd,
+               bool expect_no_file)
+{
+       enum lttng_trace_chunk_status status;
+       struct fs_handle *fs_handle;
+
+       pthread_mutex_lock(&chunk->lock);
+       /*
+        * Using this method is never valid when an fd_tracker is being
+        * used since the resulting file descriptor would not be tracked.
+        */
+       LTTNG_ASSERT(!chunk->fd_tracker);
+       status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path,
+                       flags, mode, &fs_handle, expect_no_file);
+       pthread_mutex_unlock(&chunk->lock);
+
+       if (status == LTTNG_TRACE_CHUNK_STATUS_OK) {
+               *out_fd = fs_handle_get_fd(fs_handle);
+               /*
+                * Does not close the fd; we just "unbox" it from the fs_handle.
+                */
+               fs_handle_untracked_destroy(container_of(
+                               fs_handle, struct fs_handle_untracked, parent));
+       }
+
+       return status;
+}
+
+int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk,
+               const char *file_path)
+{
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       DBG("Unlinking trace chunk file \"%s\"", file_path);
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->credentials.is_set) {
+               /*
+                * Fatal error, credentials must be set before a
+                * file is unlinked.
+                */
+               ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"",
+                               file_path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (!chunk->chunk_directory) {
+               ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory",
+                               file_path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       ret = lttng_directory_handle_unlink_file_as_user(
+                       chunk->chunk_directory, file_path,
+                       chunk->credentials.value.use_current_user ?
+                                       NULL : &chunk->credentials.value.user);
+       if (ret < 0) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       lttng_trace_chunk_remove_file(chunk, file_path);
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+static
+int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk,
+               const char *path)
+{
+       int ret;
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       DBG("Recursively removing trace chunk directory \"%s\"", path);
+       pthread_mutex_lock(&chunk->lock);
+       if (!chunk->credentials.is_set) {
+               /*
+                * Fatal error, credentials must be set before a
+                * directory is removed.
+                */
+               ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       if (!chunk->chunk_directory) {
+               ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory",
+                               path);
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+       ret = lttng_directory_handle_remove_subdirectory_recursive_as_user(
+                       chunk->chunk_directory, path,
+                       chunk->credentials.value.use_current_user ?
+                                       NULL : &chunk->credentials.value.user,
+                       LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG);
+       if (ret < 0) {
+               status = LTTNG_TRACE_CHUNK_STATUS_ERROR;
+               goto end;
+       }
+end:
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+static
+int lttng_trace_chunk_move_to_completed_post_release(
+               struct lttng_trace_chunk *trace_chunk)
+{
+       int ret = 0;
+       char *archived_chunk_name = NULL;
+       const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id);
+       const time_t creation_timestamp =
+                       LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation);
+       const time_t close_timestamp =
+                       LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close);
+       struct lttng_directory_handle *archived_chunks_directory = NULL;
+       enum lttng_trace_chunk_status status;
+
+       if (!trace_chunk->mode.is_set ||
+                       trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER ||
+                       !trace_chunk->session_output_directory) {
+               /*
+                * This command doesn't need to run if the output is remote
+                * or if the trace chunk is not owned by this process.
+                */
+               goto end;
+       }
+
+       LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER);
+       LTTNG_ASSERT(!trace_chunk->name_overridden);
+       LTTNG_ASSERT(trace_chunk->path);
+
+       archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp,
+                       &close_timestamp);
+       if (!archived_chunk_name) {
+               ERR("Failed to generate archived trace chunk name while renaming trace chunk");
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_directory_handle_create_subdirectory_as_user(
+                       trace_chunk->session_output_directory,
+                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
+                       DIR_CREATION_MODE,
+                       !trace_chunk->credentials.value.use_current_user ?
+                                       &trace_chunk->credentials.value.user :
+                                       NULL);
+       if (ret) {
+               PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY
+                               "\" directory for archived trace chunks");
+               goto end;
+       }
+
+       archived_chunks_directory = trace_chunk->fd_tracker ?
+                       fd_tracker_create_directory_handle_from_handle(
+                                       trace_chunk->fd_tracker,
+                                       trace_chunk->session_output_directory,
+                                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) :
+                       lttng_directory_handle_create_from_handle(
+                                       DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY,
+                                       trace_chunk->session_output_directory);
+       if (!archived_chunks_directory) {
+               PERROR("Failed to get handle to archived trace chunks directory");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Make sure chunk is renamed to old directory if not already done by
+        * the creation of the next chunk. This happens if a rotation is
+        * performed while tracing is stopped.
+        */
+       if (!trace_chunk->path || strcmp(trace_chunk->path,
+                       DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) {
+               status = lttng_trace_chunk_rename_path_no_lock(trace_chunk,
+                               DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY);
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       ret = lttng_directory_handle_rename_as_user(
+                       trace_chunk->session_output_directory,
+                       trace_chunk->path,
+                       archived_chunks_directory,
+                       archived_chunk_name,
+                       LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ?
+                               NULL :
+                               &trace_chunk->credentials.value.user);
+       if (ret) {
+               PERROR("Failed to rename folder \"%s\" to \"%s\"",
+                               trace_chunk->path,
+                               archived_chunk_name);
+       }
+
+end:
+       lttng_directory_handle_put(archived_chunks_directory);
+       free(archived_chunk_name);
+       return ret;
+}
+
+static
+int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk)
+{
+       return 0;
+}
+
+static
+int lttng_trace_chunk_delete_post_release_user(
+               struct lttng_trace_chunk *trace_chunk)
+{
+       int ret = 0;
+
+       DBG("Trace chunk \"delete\" close command post-release (User)");
+
+       /* Unlink all files. */
+       while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) {
+               enum lttng_trace_chunk_status status;
+               const char *path;
+
+               /* Remove first. */
+               path = (const char *) lttng_dynamic_pointer_array_get_pointer(
+                       &trace_chunk->files, 0);
+               DBG("Unlink file: %s", path);
+               status = (lttng_trace_chunk_status) lttng_trace_chunk_unlink_file(trace_chunk, path);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       ERR("Error unlinking file '%s' when deleting chunk", path);
+                       ret = -1;
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+static
+int lttng_trace_chunk_delete_post_release_owner(
+               struct lttng_trace_chunk *trace_chunk)
+{
+       enum lttng_trace_chunk_status status;
+       size_t i, count;
+       int ret = 0;
+
+       ret = lttng_trace_chunk_delete_post_release_user(trace_chunk);
+       if (ret) {
+               goto end;
+       }
+
+       DBG("Trace chunk \"delete\" close command post-release (Owner)");
+
+       LTTNG_ASSERT(trace_chunk->session_output_directory);
+       LTTNG_ASSERT(trace_chunk->chunk_directory);
+
+       /* Remove empty directories. */
+       count = lttng_dynamic_pointer_array_get_count(
+                       &trace_chunk->top_level_directories);
+
+       for (i = 0; i < count; i++) {
+               const char *top_level_name =
+                       (const char *) lttng_dynamic_pointer_array_get_pointer(
+                               &trace_chunk->top_level_directories, i);
+
+               status = (lttng_trace_chunk_status) lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name);
+               if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                       ERR("Error recursively removing subdirectory '%s' file when deleting chunk",
+                                       top_level_name);
+                       ret = -1;
+                       break;
+               }
+       }
+       if (!ret) {
+               lttng_directory_handle_put(trace_chunk->chunk_directory);
+               trace_chunk->chunk_directory = NULL;
+
+               if (trace_chunk->path && trace_chunk->path[0] != '\0') {
+                       status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory(
+                                       trace_chunk->session_output_directory,
+                                       trace_chunk->path);
+                       if (status != LTTNG_TRACE_CHUNK_STATUS_OK) {
+                               ERR("Error removing subdirectory '%s' file when deleting chunk",
+                                       trace_chunk->path);
+                               ret = -1;
+                       }
+               }
+       }
+       free(trace_chunk->path);
+       trace_chunk->path = NULL;
+end:
+       return ret;
+}
+
+/*
+ * For local files, session and consumer daemons all run the delete hook. The
+ * consumer daemons have the list of files to unlink, and technically the
+ * session daemon is the owner of the chunk. Unlink all files owned by each
+ * consumer daemon.
+ */
+static
+int lttng_trace_chunk_delete_post_release(
+               struct lttng_trace_chunk *trace_chunk)
+{
+       if (!trace_chunk->chunk_directory) {
+               return 0;
+       }
+
+       if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) {
+               return lttng_trace_chunk_delete_post_release_owner(trace_chunk);
+       } else {
+               return lttng_trace_chunk_delete_post_release_user(trace_chunk);
+       }
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command(
+               struct lttng_trace_chunk *chunk,
+               enum lttng_trace_chunk_command_type *command_type)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->close_command.is_set) {
+               *command_type = chunk->close_command.value;
+               status = LTTNG_TRACE_CHUNK_STATUS_OK;
+       } else {
+               status = LTTNG_TRACE_CHUNK_STATUS_NONE;
+       }
+       pthread_mutex_unlock(&chunk->lock);
+       return status;
+}
+
+enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command(
+               struct lttng_trace_chunk *chunk,
+               enum lttng_trace_chunk_command_type close_command)
+{
+       enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK;
+
+       if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED ||
+                       close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) {
+               status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT;
+               goto end;
+       }
+
+       pthread_mutex_lock(&chunk->lock);
+       if (chunk->close_command.is_set) {
+               DBG("Overriding trace chunk close command from \"%s\" to \"%s\"",
+                               lttng_trace_chunk_command_type_str(chunk->close_command.value),
+                               lttng_trace_chunk_command_type_str(close_command));
+       } else {
+               DBG("Setting trace chunk close command to \"%s\"",
+                               lttng_trace_chunk_command_type_str(close_command));
+       }
+       /*
+        * Unset close command for no-op for backward compatibility with relayd
+        * 2.11.
+        */
+       if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) {
+               LTTNG_OPTIONAL_SET(&chunk->close_command, close_command);
+       } else {
+               LTTNG_OPTIONAL_UNSET(&chunk->close_command);
+       }
+       pthread_mutex_unlock(&chunk->lock);
+end:
+       return status;
+}
+
+const char *lttng_trace_chunk_command_type_get_name(
+               enum lttng_trace_chunk_command_type command)
+{
+       switch (command) {
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED:
+               return "move to completed trace chunk folder";
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION:
+               return "no operation";
+       case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE:
+               return "delete";
+       default:
+               abort();
+       }
+}
+
+bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a,
+               const struct lttng_trace_chunk *chunk_b)
+{
+       bool equal = false;
+
+       if (chunk_a == chunk_b) {
+               equal = true;
+               goto end;
+       }
+
+       if (!!chunk_a ^ !!chunk_b) {
+               goto end;
+       }
+
+       if (chunk_a->id.is_set ^ chunk_a->id.is_set) {
+               /* One id is set and not the other, thus they are not equal. */
+               goto end;
+       }
+
+       if (!chunk_a->id.is_set) {
+               /* Both ids are unset. */
+               equal = true;
+       } else {
+               equal = chunk_a->id.value == chunk_b->id.value;
+       }
+
+end:
+       return equal;
+}
+
+bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk)
+{
+       return urcu_ref_get_unless_zero(&chunk->ref);
+}
+
+static
+void free_lttng_trace_chunk_registry_element(struct rcu_head *node)
+{
+       struct lttng_trace_chunk_registry_element *element =
+                       container_of(node, typeof(*element), rcu_node);
+
+       lttng_trace_chunk_fini(&element->chunk);
+       free(element);
+}
+
+static
+void lttng_trace_chunk_release(struct urcu_ref *ref)
+{
+       struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk),
+                       ref);
+
+       if (chunk->close_command.is_set) {
+               chunk_command func = close_command_get_post_release_func(chunk->close_command.value);
+
+               if (func(chunk)) {
+                       ERR("Trace chunk post-release command %s has failed.",
+                                       lttng_trace_chunk_command_type_str(chunk->close_command.value));
+               }
+       }
+
+       if (chunk->in_registry_element) {
+               struct lttng_trace_chunk_registry_element *element;
+
+               element = container_of(chunk, typeof(*element), chunk);
+               if (element->registry) {
+                       rcu_read_lock();
+                       cds_lfht_del(element->registry->ht,
+                                       &element->trace_chunk_registry_ht_node);
+                       rcu_read_unlock();
+                       call_rcu(&element->rcu_node,
+                                       free_lttng_trace_chunk_registry_element);
+               } else {
+                       /* Never published, can be free'd immediately. */
+                       free_lttng_trace_chunk_registry_element(
+                                       &element->rcu_node);
+               }
+       } else {
+               /* Not RCU-protected, free immediately. */
+               lttng_trace_chunk_fini(chunk);
+               free(chunk);
+       }
+}
+
+void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk)
+{
+       if (!chunk) {
+               return;
+       }
+       LTTNG_ASSERT(chunk->ref.refcount);
+       urcu_ref_put(&chunk->ref, lttng_trace_chunk_release);
+}
+
+struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void)
+{
+       struct lttng_trace_chunk_registry *registry;
+
+       registry = (lttng_trace_chunk_registry *) zmalloc(sizeof(*registry));
+       if (!registry) {
+               goto end;
+       }
+
+       registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
+                       CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+       if (!registry->ht) {
+               goto error;
+       }
+end:
+       return registry;
+error:
+       lttng_trace_chunk_registry_destroy(registry);
+       return NULL;
+}
+
+void lttng_trace_chunk_registry_destroy(
+               struct lttng_trace_chunk_registry *registry)
+{
+       if (!registry) {
+               return;
+       }
+       if (registry->ht) {
+               int ret = cds_lfht_destroy(registry->ht, NULL);
+               LTTNG_ASSERT(!ret);
+       }
+       free(registry);
+}
+
+static
+struct lttng_trace_chunk_registry_element *
+lttng_trace_chunk_registry_element_create_from_chunk(
+               struct lttng_trace_chunk *chunk, uint64_t session_id)
+{
+       struct lttng_trace_chunk_registry_element *element =
+               (lttng_trace_chunk_registry_element *) zmalloc(sizeof(*element));
+
+       if (!element) {
+               goto end;
+       }
+       cds_lfht_node_init(&element->trace_chunk_registry_ht_node);
+       element->session_id = session_id;
+
+       element->chunk = *chunk;
+       lttng_trace_chunk_init(&element->chunk);
+       if (chunk->session_output_directory) {
+               /* Transferred ownership. */
+               element->chunk.session_output_directory =
+                               chunk->session_output_directory;
+               chunk->session_output_directory = NULL;
+       }
+       if (chunk->chunk_directory) {
+               /* Transferred ownership. */
+               element->chunk.chunk_directory = chunk->chunk_directory;
+               chunk->chunk_directory = NULL;
+       }
+       /*
+        * The original chunk becomes invalid; the name and path attributes are
+        * transferred to the new chunk instance.
+        */
+       chunk->name = NULL;
+       chunk->path = NULL;
+       element->chunk.fd_tracker = chunk->fd_tracker;
+       element->chunk.in_registry_element = true;
+end:
+       return element;
+}
+
+struct lttng_trace_chunk *
+lttng_trace_chunk_registry_publish_chunk(
+               struct lttng_trace_chunk_registry *registry,
+               uint64_t session_id, struct lttng_trace_chunk *chunk)
+{
+       struct lttng_trace_chunk_registry_element *element;
+       unsigned long element_hash;
+
+       pthread_mutex_lock(&chunk->lock);
+       element = lttng_trace_chunk_registry_element_create_from_chunk(chunk,
+                       session_id);
+       pthread_mutex_unlock(&chunk->lock);
+       if (!element) {
+               goto end;
+       }
+       /*
+        * chunk is now invalid, the only valid operation is a 'put' from the
+        * caller.
+        */
+       chunk = NULL;
+       element_hash = lttng_trace_chunk_registry_element_hash(element);
+
+       rcu_read_lock();
+       while (1) {
+               struct cds_lfht_node *published_node;
+               struct lttng_trace_chunk *published_chunk;
+               struct lttng_trace_chunk_registry_element *published_element;
+
+               published_node = cds_lfht_add_unique(registry->ht,
+                               element_hash,
+                               lttng_trace_chunk_registry_element_match,
+                               element,
+                               &element->trace_chunk_registry_ht_node);
+               if (published_node == &element->trace_chunk_registry_ht_node) {
+                       /* Successfully published the new element. */
+                       element->registry = registry;
+                       /* Acquire a reference for the caller. */
+                       if (lttng_trace_chunk_get(&element->chunk)) {
+                               break;
+                       } else {
+                               /*
+                                * Another thread concurrently unpublished the
+                                * trace chunk. This is currently unexpected.
+                                *
+                                * Re-attempt to publish.
+                                */
+                               ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion");
+                               continue;
+                       }
+               }
+
+               /*
+                * An equivalent trace chunk was published before this trace
+                * chunk. Attempt to acquire a reference to the one that was
+                * already published and release the reference to the copy we
+                * created if successful.
+                */
+               published_element = container_of(published_node,
+                               typeof(*published_element),
+                               trace_chunk_registry_ht_node);
+               published_chunk = &published_element->chunk;
+               if (lttng_trace_chunk_get(published_chunk)) {
+                       lttng_trace_chunk_put(&element->chunk);
+                       element = published_element;
+                       break;
+               }
+               /*
+                * A reference to the previously published trace chunk could not
+                * be acquired. Hence, retry to publish our copy of the trace
+                * chunk.
+                */
+       }
+       rcu_read_unlock();
+end:
+       return element ? &element->chunk : NULL;
+}
+
+/*
+ * Note that the caller must be registered as an RCU thread.
+ * However, it does not need to hold the RCU read lock. The RCU read lock is
+ * acquired to perform the look-up in the registry's hash table and held until
+ * after a reference to the "found" trace chunk is acquired.
+ *
+ * IOW, holding a reference guarantees the existence of the object for the
+ * caller.
+ */
+static
+struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk(
+               const struct lttng_trace_chunk_registry *registry,
+               uint64_t session_id, uint64_t *chunk_id)
+{
+       lttng_trace_chunk_registry_element target_element {};
+
+       target_element.chunk.id.is_set = !!chunk_id;
+       target_element.chunk.id.value = chunk_id ? *chunk_id : 0;
+       target_element.session_id = session_id;
+
+       const unsigned long element_hash =
+                       lttng_trace_chunk_registry_element_hash(
+                               &target_element);
+       struct cds_lfht_node *published_node;
+       struct lttng_trace_chunk_registry_element *published_element;
+       struct lttng_trace_chunk *published_chunk = NULL;
+       struct cds_lfht_iter iter;
+
+       rcu_read_lock();
+       cds_lfht_lookup(registry->ht,
+                       element_hash,
+                       lttng_trace_chunk_registry_element_match,
+                       &target_element,
+                       &iter);
+       published_node = cds_lfht_iter_get_node(&iter);
+       if (!published_node) {
+               goto end;
+       }
+
+       published_element = container_of(published_node,
+                       typeof(*published_element),
+                       trace_chunk_registry_ht_node);
+       if (lttng_trace_chunk_get(&published_element->chunk)) {
+               published_chunk = &published_element->chunk;
+       }
+end:
+       rcu_read_unlock();
+       return published_chunk;
+}
+
+struct lttng_trace_chunk *
+lttng_trace_chunk_registry_find_chunk(
+               const struct lttng_trace_chunk_registry *registry,
+               uint64_t session_id, uint64_t chunk_id)
+{
+       return _lttng_trace_chunk_registry_find_chunk(registry,
+                       session_id, &chunk_id);
+}
+
+int lttng_trace_chunk_registry_chunk_exists(
+               const struct lttng_trace_chunk_registry *registry,
+               uint64_t session_id, uint64_t chunk_id, bool *chunk_exists)
+{
+       int ret = 0;
+       lttng_trace_chunk_registry_element target_element;
+
+       target_element.chunk.id.is_set = true;
+       target_element.chunk.id.value = chunk_id;
+       target_element.session_id = session_id;
+
+       const unsigned long element_hash =
+                       lttng_trace_chunk_registry_element_hash(
+                               &target_element);
+       struct cds_lfht_node *published_node;
+       struct cds_lfht_iter iter;
+
+       rcu_read_lock();
+       cds_lfht_lookup(registry->ht,
+                       element_hash,
+                       lttng_trace_chunk_registry_element_match,
+                       &target_element,
+                       &iter);
+       published_node = cds_lfht_iter_get_node(&iter);
+       if (!published_node) {
+               *chunk_exists = false;
+               goto end;
+       }
+
+       *chunk_exists = !cds_lfht_is_node_deleted(published_node);
+end:
+       rcu_read_unlock();
+       return ret;
+}
+
+struct lttng_trace_chunk *
+lttng_trace_chunk_registry_find_anonymous_chunk(
+               const struct lttng_trace_chunk_registry *registry,
+               uint64_t session_id)
+{
+       return _lttng_trace_chunk_registry_find_chunk(registry,
+                       session_id, NULL);
+}
+
+unsigned int lttng_trace_chunk_registry_put_each_chunk(
+               const struct lttng_trace_chunk_registry *registry)
+{
+       struct cds_lfht_iter iter;
+       struct lttng_trace_chunk_registry_element *chunk_element;
+       unsigned int trace_chunks_left = 0;
+
+       DBG("Releasing trace chunk registry to all trace chunks");
+       rcu_read_lock();
+       cds_lfht_for_each_entry(registry->ht,
+                       &iter, chunk_element, trace_chunk_registry_ht_node) {
+               const char *chunk_id_str = "none";
+               char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)];
+
+               pthread_mutex_lock(&chunk_element->chunk.lock);
+               if (chunk_element->chunk.id.is_set) {
+                       int fmt_ret;
+
+                       fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf),
+                                       "%" PRIu64,
+                                       chunk_element->chunk.id.value);
+                       if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) {
+                               chunk_id_str = "formatting error";
+                       } else {
+                               chunk_id_str = chunk_id_buf;
+                       }
+               }
+
+               DBG("Releasing reference to trace chunk: session_id = %" PRIu64
+                               "chunk_id = %s, name = \"%s\", status = %s",
+                               chunk_element->session_id,
+                               chunk_id_str,
+                               chunk_element->chunk.name ? : "none",
+                               chunk_element->chunk.close_command.is_set ?
+                                               "open" : "closed");
+               pthread_mutex_unlock(&chunk_element->chunk.lock);
+               lttng_trace_chunk_put(&chunk_element->chunk);
+               trace_chunks_left++;
+       }
+       rcu_read_unlock();
+       DBG("Released reference to %u trace chunks in %s()", trace_chunks_left,
+                       __FUNCTION__);
+
+       return trace_chunks_left;
+}
diff --git a/src/common/tracker.c b/src/common/tracker.c
deleted file mode 100644 (file)
index f3a9f25..0000000
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <lttng/domain.h>
-#include <lttng/lttng-error.h>
-#include <lttng/tracker.h>
-
-#include <common/dynamic-array.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/tracker.h>
-
-#include <stdbool.h>
-
-struct process_attr_tracker_values_comm_header {
-       uint32_t count;
-};
-
-struct process_attr_tracker_value_comm {
-       /* enum lttng_process_attr_value_type */
-       int32_t type;
-       union {
-               struct process_attr_integral_value_comm integral;
-               /* Includes the '\0' terminator. */
-               uint32_t name_len;
-       } value;
-};
-
-#define GET_INTEGRAL_COMM_VALUE(value_ptr, as_type)              \
-       ((as_type)(is_signed(as_type) ? (value_ptr)->u._signed : \
-                                       (value_ptr)->u._unsigned))
-
-#define SET_INTEGRAL_COMM_VALUE(comm_value, value)                         \
-       if (is_signed(typeof(value))) {                                    \
-               (comm_value)->u._signed =                                  \
-                               (typeof((comm_value)->u._signed)) value;   \
-       } else {                                                           \
-               (comm_value)->u._unsigned =                                \
-                               (typeof((comm_value)->u._unsigned)) value; \
-       }
-
-static inline bool is_virtual_process_attr(enum lttng_process_attr process_attr)
-{
-       return process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID ||
-              process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID ||
-              process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID;
-}
-
-static inline bool is_value_type_name(
-               enum lttng_process_attr_value_type value_type)
-{
-       return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ||
-              value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME;
-}
-
-enum lttng_error_code process_attr_value_from_comm(
-               enum lttng_domain_type domain,
-               enum lttng_process_attr process_attr,
-               enum lttng_process_attr_value_type value_type,
-               const struct process_attr_integral_value_comm *integral_value,
-               const struct lttng_buffer_view *value_view,
-               struct process_attr_value **_value)
-{
-       char *name = NULL;
-       enum lttng_error_code ret = LTTNG_OK;
-       struct process_attr_value *value = zmalloc(sizeof(*value));
-
-       if (!value) {
-               ret = LTTNG_ERR_NOMEM;
-               goto error;
-       }
-
-       if (value_view && value_view->size > 0) {
-               if (value_view->data[value_view->size - 1] != '\0') {
-                       ret = LTTNG_ERR_INVALID;
-                       goto error;
-               }
-               name = strdup(value_view->data);
-               if (!name) {
-                       ret = LTTNG_ERR_NOMEM;
-                       goto error;
-               }
-       }
-
-       if (domain != LTTNG_DOMAIN_UST && domain != LTTNG_DOMAIN_KERNEL) {
-               ERR("Only the user space and kernel space domains may be specified to configure process attribute trackers");
-               ret = LTTNG_ERR_UNSUPPORTED_DOMAIN;
-               goto error;
-       }
-
-       if (!is_virtual_process_attr(process_attr) &&
-                       domain != LTTNG_DOMAIN_KERNEL) {
-               ERR("Non-virtual process attributes can only be used in the kernel domain");
-               ret = LTTNG_ERR_UNSUPPORTED_DOMAIN;
-               goto error;
-       }
-
-       /* Only expect a payload for name value types. */
-       if (is_value_type_name(value_type) &&
-                       (!value_view || value_view->size == 0)) {
-               ret = LTTNG_ERR_INVALID_PROTOCOL;
-               goto error;
-       } else if (!is_value_type_name(value_type) && value_view &&
-                       value_view->size != 0) {
-               ret = LTTNG_ERR_INVALID_PROTOCOL;
-               goto error;
-       }
-
-       value->type = value_type;
-       switch (process_attr) {
-       case LTTNG_PROCESS_ATTR_PROCESS_ID:
-       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
-               if (value_type != LTTNG_PROCESS_ATTR_VALUE_TYPE_PID) {
-                       ERR("Invalid value type used for process ID process attribute");
-                       ret = LTTNG_ERR_INVALID;
-                       goto error;
-               }
-               value->value.pid =
-                               GET_INTEGRAL_COMM_VALUE(integral_value, pid_t);
-               break;
-       case LTTNG_PROCESS_ATTR_USER_ID:
-       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
-               switch (value_type) {
-               case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
-                       value->value.uid = GET_INTEGRAL_COMM_VALUE(
-                                       integral_value, uid_t);
-                       break;
-               case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
-                       if (!name) {
-                               ret = LTTNG_ERR_INVALID;
-                               goto error;
-                       }
-
-                       value->value.user_name = name;
-                       name = NULL;
-                       break;
-               default:
-                       ERR("Invalid value type used for user ID process attribute");
-                       ret = LTTNG_ERR_INVALID;
-                       goto error;
-               }
-               break;
-       case LTTNG_PROCESS_ATTR_GROUP_ID:
-       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
-               switch (value_type) {
-               case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
-                       value->value.gid = GET_INTEGRAL_COMM_VALUE(
-                                       integral_value, gid_t);
-                       break;
-               case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
-                       if (!name) {
-                               ret = LTTNG_ERR_INVALID;
-                               goto error;
-                       }
-
-                       value->value.group_name = name;
-                       name = NULL;
-                       break;
-               default:
-                       ERR("Invalid value type used for group ID process attribute");
-                       ret = LTTNG_ERR_INVALID;
-                       goto error;
-               }
-               break;
-       default:
-               ret = LTTNG_ERR_INVALID_PROTOCOL;
-               goto error;
-       }
-
-       *_value = value;
-       value = NULL;
-       free(name);
-       return LTTNG_OK;
-error:
-       free(name);
-       process_attr_value_destroy(value);
-       return ret;
-}
-
-const char *lttng_process_attr_to_string(enum lttng_process_attr process_attr)
-{
-       switch (process_attr) {
-       case LTTNG_PROCESS_ATTR_PROCESS_ID:
-               return "process ID";
-       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
-               return "virtual process ID";
-       case LTTNG_PROCESS_ATTR_USER_ID:
-               return "user ID";
-       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
-               return "virtual user ID";
-       case LTTNG_PROCESS_ATTR_GROUP_ID:
-               return "group ID";
-       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
-               return "virtual group ID";
-       default:
-               return "unknown process attribute";
-       }
-}
-
-static void process_attr_tracker_value_destructor(void *ptr)
-{
-       struct process_attr_value *value = (typeof(value)) ptr;
-
-       process_attr_value_destroy(value);
-}
-
-struct lttng_process_attr_values *lttng_process_attr_values_create(void)
-{
-       struct lttng_process_attr_values *values = zmalloc(sizeof(*values));
-
-       if (!values) {
-               goto end;
-       }
-
-       lttng_dynamic_pointer_array_init(
-                       &values->array, process_attr_tracker_value_destructor);
-end:
-       return values;
-}
-
-unsigned int _lttng_process_attr_values_get_count(
-               const struct lttng_process_attr_values *values)
-{
-       return (unsigned int) lttng_dynamic_pointer_array_get_count(
-                       &values->array);
-}
-
-const struct process_attr_value *lttng_process_attr_tracker_values_get_at_index(
-               const struct lttng_process_attr_values *values,
-               unsigned int index)
-{
-       return lttng_dynamic_pointer_array_get_pointer(&values->array, index);
-}
-
-static
-int process_attr_tracker_value_serialize(const struct process_attr_value *value,
-               struct lttng_dynamic_buffer *buffer)
-{
-       int ret;
-       struct process_attr_tracker_value_comm value_comm = {
-                       .type = (int32_t) value->type,
-       };
-       const char *name = NULL;
-
-       switch (value->type) {
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
-               SET_INTEGRAL_COMM_VALUE(
-                               &value_comm.value.integral, value->value.pid);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
-               SET_INTEGRAL_COMM_VALUE(
-                               &value_comm.value.integral, value->value.uid);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
-               SET_INTEGRAL_COMM_VALUE(
-                               &value_comm.value.integral, value->value.gid);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
-               name = value->value.user_name;
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
-               name = value->value.group_name;
-               break;
-       default:
-               abort();
-       }
-
-       if (name) {
-               value_comm.value.name_len = strlen(name) + 1;
-       }
-
-       ret = lttng_dynamic_buffer_append(
-                       buffer, &value_comm, sizeof(value_comm));
-       if (ret) {
-               goto end;
-       }
-
-       if (name) {
-               ret = lttng_dynamic_buffer_append(
-                               buffer, name, value_comm.value.name_len);
-       }
-end:
-       return ret;
-}
-
-int lttng_process_attr_values_serialize(
-               const struct lttng_process_attr_values *values,
-               struct lttng_dynamic_buffer *buffer)
-{
-       int ret;
-       unsigned int count, i;
-       struct process_attr_tracker_values_comm_header header = {};
-
-       count = _lttng_process_attr_values_get_count(values);
-       header.count = (uint32_t) count;
-
-       ret = lttng_dynamic_buffer_append(buffer, &header, sizeof(header));
-       if (ret) {
-               goto end;
-       }
-
-       for (i = 0; i < count; i++) {
-               const struct process_attr_value *value =
-                               lttng_process_attr_tracker_values_get_at_index(
-                                               values, i);
-
-               ret = process_attr_tracker_value_serialize(value, buffer);
-               if (ret) {
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-ssize_t lttng_process_attr_values_create_from_buffer(
-               enum lttng_domain_type domain,
-               enum lttng_process_attr process_attr,
-               const struct lttng_buffer_view *buffer_view,
-               struct lttng_process_attr_values **_values)
-{
-       ssize_t offset;
-       unsigned int i;
-       struct lttng_process_attr_values *values;
-       struct lttng_buffer_view header_view;
-       const struct process_attr_tracker_values_comm_header *header;
-
-       values = lttng_process_attr_values_create();
-       if (!values) {
-               goto error;
-       }
-
-       header_view = lttng_buffer_view_from_view(
-                       buffer_view, 0, sizeof(*header));
-       if (!lttng_buffer_view_is_valid(&header_view)) {
-               goto error;
-       }
-
-       offset = header_view.size;
-       header = (typeof(header)) header_view.data;
-
-       /*
-        * Check that the number of values is not absurdly large with respect to
-        * the received buffer's size.
-        */
-       if (buffer_view->size <
-                       header->count * sizeof(struct process_attr_tracker_value_comm)) {
-               goto error;
-       }
-       for (i = 0; i < (unsigned int) header->count; i++) {
-               int ret;
-               enum lttng_error_code ret_code;
-               const struct process_attr_tracker_value_comm *value_comm;
-               struct process_attr_value *value;
-               enum lttng_process_attr_value_type type;
-               struct lttng_buffer_view value_view;
-               struct lttng_buffer_view value_name_view = {};
-
-               value_view = lttng_buffer_view_from_view(
-                               buffer_view, offset, sizeof(*value_comm));
-               if (!lttng_buffer_view_is_valid(&value_view)) {
-                       goto error;
-               }
-
-               offset += value_view.size;
-               value_comm = (typeof(value_comm)) value_view.data;
-               type = (typeof(type)) value_comm->type;
-
-               if (is_value_type_name(type)) {
-                       value_name_view = lttng_buffer_view_from_view(
-                                       buffer_view, offset,
-                                       value_comm->value.name_len);
-                       if (!lttng_buffer_view_is_valid(&value_name_view)) {
-                               goto error;
-                       }
-
-                       offset += value_name_view.size;
-               }
-
-               ret_code = process_attr_value_from_comm(domain, process_attr,
-                               type, &value_comm->value.integral,
-                               &value_name_view, &value);
-               if (ret_code != LTTNG_OK) {
-                       goto error;
-               }
-
-               ret = lttng_dynamic_pointer_array_add_pointer(
-                               &values->array, value);
-               if (ret) {
-                       process_attr_value_destroy(value);
-                       goto error;
-               }
-       }
-
-       *_values = values;
-       return offset;
-error:
-       lttng_process_attr_values_destroy(values);
-       return -1;
-}
-
-void lttng_process_attr_values_destroy(struct lttng_process_attr_values *values)
-{
-       if (!values) {
-               return;
-       }
-       lttng_dynamic_pointer_array_reset(&values->array);
-       free(values);
-}
-
-struct process_attr_value *process_attr_value_copy(
-               const struct process_attr_value *value)
-{
-       struct process_attr_value *new_value = NULL;
-
-       if (!value) {
-               goto end;
-       }
-
-       new_value = zmalloc(sizeof(*new_value));
-       if (!new_value) {
-               goto end;
-       }
-       if (is_value_type_name(value->type)) {
-               const char *src =
-                               value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
-                                               value->value.user_name :
-                                               value->value.group_name;
-               char **dst = value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
-                                            &new_value->value.user_name :
-                                            &new_value->value.group_name;
-
-               new_value->type = value->type;
-               *dst = strdup(src);
-               if (!*dst) {
-                       goto error;
-               }
-       } else {
-               *new_value = *value;
-       }
-end:
-       return new_value;
-error:
-       free(new_value);
-       return NULL;
-}
-
-unsigned long process_attr_value_hash(const struct process_attr_value *a)
-{
-       unsigned long hash = hash_key_ulong((void *) a->type, lttng_ht_seed);
-
-       switch (a->type) {
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
-               hash ^= hash_key_ulong((void *) (unsigned long) a->value.pid,
-                               lttng_ht_seed);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
-               hash ^= hash_key_ulong((void *) (unsigned long) a->value.uid,
-                               lttng_ht_seed);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
-               hash ^= hash_key_ulong((void *) (unsigned long) a->value.gid,
-                               lttng_ht_seed);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
-               hash ^= hash_key_str(a->value.user_name, lttng_ht_seed);
-               break;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
-               hash ^= hash_key_str(a->value.group_name, lttng_ht_seed);
-               break;
-       default:
-               abort();
-       }
-
-       return hash;
-}
-
-bool process_attr_tracker_value_equal(const struct process_attr_value *a,
-               const struct process_attr_value *b)
-{
-       if (a->type != b->type) {
-               return false;
-       }
-       switch (a->type) {
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
-               return a->value.pid == b->value.pid;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
-               return a->value.uid == b->value.uid;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
-               return a->value.gid == b->value.gid;
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
-               return !strcmp(a->value.user_name, b->value.user_name);
-       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
-               return !strcmp(a->value.group_name, b->value.group_name);
-       default:
-               abort();
-       }
-}
-
-void process_attr_value_destroy(struct process_attr_value *value)
-{
-       if (!value) {
-               return;
-       }
-       if (is_value_type_name(value->type)) {
-               free(value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
-                                               value->value.user_name :
-                                               value->value.group_name);
-       }
-       free(value);
-}
diff --git a/src/common/tracker.cpp b/src/common/tracker.cpp
new file mode 100644 (file)
index 0000000..84b6c6b
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <lttng/domain.h>
+#include <lttng/lttng-error.h>
+#include <lttng/tracker.h>
+
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/tracker.h>
+
+#include <stdbool.h>
+
+struct process_attr_tracker_values_comm_header {
+       uint32_t count;
+};
+
+struct process_attr_tracker_value_comm {
+       /* enum lttng_process_attr_value_type */
+       int32_t type;
+       union {
+               struct process_attr_integral_value_comm integral;
+               /* Includes the '\0' terminator. */
+               uint32_t name_len;
+       } value;
+};
+
+#define GET_INTEGRAL_COMM_VALUE(value_ptr, as_type)              \
+       ((as_type)(is_signed(as_type) ? (value_ptr)->u._signed : \
+                                       (value_ptr)->u._unsigned))
+
+#define SET_INTEGRAL_COMM_VALUE(comm_value, value)                         \
+       if (is_signed(typeof(value))) {                                    \
+               (comm_value)->u._signed =                                  \
+                               (typeof((comm_value)->u._signed)) value;   \
+       } else {                                                           \
+               (comm_value)->u._unsigned =                                \
+                               (typeof((comm_value)->u._unsigned)) value; \
+       }
+
+static inline bool is_virtual_process_attr(enum lttng_process_attr process_attr)
+{
+       return process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID ||
+              process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID ||
+              process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID;
+}
+
+static inline bool is_value_type_name(
+               enum lttng_process_attr_value_type value_type)
+{
+       return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ||
+              value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME;
+}
+
+enum lttng_error_code process_attr_value_from_comm(
+               enum lttng_domain_type domain,
+               enum lttng_process_attr process_attr,
+               enum lttng_process_attr_value_type value_type,
+               const struct process_attr_integral_value_comm *integral_value,
+               const struct lttng_buffer_view *value_view,
+               struct process_attr_value **_value)
+{
+       char *name = NULL;
+       enum lttng_error_code ret = LTTNG_OK;
+       struct process_attr_value *value = (process_attr_value *) zmalloc(sizeof(*value));
+
+       if (!value) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       if (value_view && value_view->size > 0) {
+               if (value_view->data[value_view->size - 1] != '\0') {
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+               name = strdup(value_view->data);
+               if (!name) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       }
+
+       if (domain != LTTNG_DOMAIN_UST && domain != LTTNG_DOMAIN_KERNEL) {
+               ERR("Only the user space and kernel space domains may be specified to configure process attribute trackers");
+               ret = LTTNG_ERR_UNSUPPORTED_DOMAIN;
+               goto error;
+       }
+
+       if (!is_virtual_process_attr(process_attr) &&
+                       domain != LTTNG_DOMAIN_KERNEL) {
+               ERR("Non-virtual process attributes can only be used in the kernel domain");
+               ret = LTTNG_ERR_UNSUPPORTED_DOMAIN;
+               goto error;
+       }
+
+       /* Only expect a payload for name value types. */
+       if (is_value_type_name(value_type) &&
+                       (!value_view || value_view->size == 0)) {
+               ret = LTTNG_ERR_INVALID_PROTOCOL;
+               goto error;
+       } else if (!is_value_type_name(value_type) && value_view &&
+                       value_view->size != 0) {
+               ret = LTTNG_ERR_INVALID_PROTOCOL;
+               goto error;
+       }
+
+       value->type = value_type;
+       switch (process_attr) {
+       case LTTNG_PROCESS_ATTR_PROCESS_ID:
+       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
+               if (value_type != LTTNG_PROCESS_ATTR_VALUE_TYPE_PID) {
+                       ERR("Invalid value type used for process ID process attribute");
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+               value->value.pid =
+                               GET_INTEGRAL_COMM_VALUE(integral_value, pid_t);
+               break;
+       case LTTNG_PROCESS_ATTR_USER_ID:
+       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
+               switch (value_type) {
+               case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
+                       value->value.uid = GET_INTEGRAL_COMM_VALUE(
+                                       integral_value, uid_t);
+                       break;
+               case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
+                       if (!name) {
+                               ret = LTTNG_ERR_INVALID;
+                               goto error;
+                       }
+
+                       value->value.user_name = name;
+                       name = NULL;
+                       break;
+               default:
+                       ERR("Invalid value type used for user ID process attribute");
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+               break;
+       case LTTNG_PROCESS_ATTR_GROUP_ID:
+       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
+               switch (value_type) {
+               case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
+                       value->value.gid = GET_INTEGRAL_COMM_VALUE(
+                                       integral_value, gid_t);
+                       break;
+               case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
+                       if (!name) {
+                               ret = LTTNG_ERR_INVALID;
+                               goto error;
+                       }
+
+                       value->value.group_name = name;
+                       name = NULL;
+                       break;
+               default:
+                       ERR("Invalid value type used for group ID process attribute");
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+               break;
+       default:
+               ret = LTTNG_ERR_INVALID_PROTOCOL;
+               goto error;
+       }
+
+       *_value = value;
+       value = NULL;
+       free(name);
+       return LTTNG_OK;
+error:
+       free(name);
+       process_attr_value_destroy(value);
+       return ret;
+}
+
+const char *lttng_process_attr_to_string(enum lttng_process_attr process_attr)
+{
+       switch (process_attr) {
+       case LTTNG_PROCESS_ATTR_PROCESS_ID:
+               return "process ID";
+       case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID:
+               return "virtual process ID";
+       case LTTNG_PROCESS_ATTR_USER_ID:
+               return "user ID";
+       case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID:
+               return "virtual user ID";
+       case LTTNG_PROCESS_ATTR_GROUP_ID:
+               return "group ID";
+       case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID:
+               return "virtual group ID";
+       default:
+               return "unknown process attribute";
+       }
+}
+
+static void process_attr_tracker_value_destructor(void *ptr)
+{
+       struct process_attr_value *value = (typeof(value)) ptr;
+
+       process_attr_value_destroy(value);
+}
+
+struct lttng_process_attr_values *lttng_process_attr_values_create(void)
+{
+       struct lttng_process_attr_values *values = (lttng_process_attr_values *) zmalloc(sizeof(*values));
+
+       if (!values) {
+               goto end;
+       }
+
+       lttng_dynamic_pointer_array_init(
+                       &values->array, process_attr_tracker_value_destructor);
+end:
+       return values;
+}
+
+unsigned int _lttng_process_attr_values_get_count(
+               const struct lttng_process_attr_values *values)
+{
+       return (unsigned int) lttng_dynamic_pointer_array_get_count(
+                       &values->array);
+}
+
+const struct process_attr_value *lttng_process_attr_tracker_values_get_at_index(
+               const struct lttng_process_attr_values *values,
+               unsigned int index)
+{
+       return (process_attr_value *) lttng_dynamic_pointer_array_get_pointer(&values->array, index);
+}
+
+static
+int process_attr_tracker_value_serialize(const struct process_attr_value *value,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret;
+       struct process_attr_tracker_value_comm value_comm = {
+                       .type = (int32_t) value->type,
+       };
+       const char *name = NULL;
+
+       switch (value->type) {
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
+               SET_INTEGRAL_COMM_VALUE(
+                               &value_comm.value.integral, value->value.pid);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
+               SET_INTEGRAL_COMM_VALUE(
+                               &value_comm.value.integral, value->value.uid);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
+               SET_INTEGRAL_COMM_VALUE(
+                               &value_comm.value.integral, value->value.gid);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
+               name = value->value.user_name;
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
+               name = value->value.group_name;
+               break;
+       default:
+               abort();
+       }
+
+       if (name) {
+               value_comm.value.name_len = strlen(name) + 1;
+       }
+
+       ret = lttng_dynamic_buffer_append(
+                       buffer, &value_comm, sizeof(value_comm));
+       if (ret) {
+               goto end;
+       }
+
+       if (name) {
+               ret = lttng_dynamic_buffer_append(
+                               buffer, name, value_comm.value.name_len);
+       }
+end:
+       return ret;
+}
+
+int lttng_process_attr_values_serialize(
+               const struct lttng_process_attr_values *values,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret;
+       unsigned int count, i;
+       struct process_attr_tracker_values_comm_header header = {};
+
+       count = _lttng_process_attr_values_get_count(values);
+       header.count = (uint32_t) count;
+
+       ret = lttng_dynamic_buffer_append(buffer, &header, sizeof(header));
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < count; i++) {
+               const struct process_attr_value *value =
+                               lttng_process_attr_tracker_values_get_at_index(
+                                               values, i);
+
+               ret = process_attr_tracker_value_serialize(value, buffer);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+ssize_t lttng_process_attr_values_create_from_buffer(
+               enum lttng_domain_type domain,
+               enum lttng_process_attr process_attr,
+               const struct lttng_buffer_view *buffer_view,
+               struct lttng_process_attr_values **_values)
+{
+       ssize_t offset;
+       unsigned int i;
+       struct lttng_process_attr_values *values;
+       struct lttng_buffer_view header_view;
+       const struct process_attr_tracker_values_comm_header *header;
+
+       values = lttng_process_attr_values_create();
+       if (!values) {
+               goto error;
+       }
+
+       header_view = lttng_buffer_view_from_view(
+                       buffer_view, 0, sizeof(*header));
+       if (!lttng_buffer_view_is_valid(&header_view)) {
+               goto error;
+       }
+
+       offset = header_view.size;
+       header = (typeof(header)) header_view.data;
+
+       /*
+        * Check that the number of values is not absurdly large with respect to
+        * the received buffer's size.
+        */
+       if (buffer_view->size <
+                       header->count * sizeof(struct process_attr_tracker_value_comm)) {
+               goto error;
+       }
+       for (i = 0; i < (unsigned int) header->count; i++) {
+               int ret;
+               enum lttng_error_code ret_code;
+               const struct process_attr_tracker_value_comm *value_comm;
+               struct process_attr_value *value;
+               enum lttng_process_attr_value_type type;
+               struct lttng_buffer_view value_view;
+               struct lttng_buffer_view value_name_view = {};
+
+               value_view = lttng_buffer_view_from_view(
+                               buffer_view, offset, sizeof(*value_comm));
+               if (!lttng_buffer_view_is_valid(&value_view)) {
+                       goto error;
+               }
+
+               offset += value_view.size;
+               value_comm = (typeof(value_comm)) value_view.data;
+               type = (typeof(type)) value_comm->type;
+
+               if (is_value_type_name(type)) {
+                       value_name_view = lttng_buffer_view_from_view(
+                                       buffer_view, offset,
+                                       value_comm->value.name_len);
+                       if (!lttng_buffer_view_is_valid(&value_name_view)) {
+                               goto error;
+                       }
+
+                       offset += value_name_view.size;
+               }
+
+               ret_code = process_attr_value_from_comm(domain, process_attr,
+                               type, &value_comm->value.integral,
+                               &value_name_view, &value);
+               if (ret_code != LTTNG_OK) {
+                       goto error;
+               }
+
+               ret = lttng_dynamic_pointer_array_add_pointer(
+                               &values->array, value);
+               if (ret) {
+                       process_attr_value_destroy(value);
+                       goto error;
+               }
+       }
+
+       *_values = values;
+       return offset;
+error:
+       lttng_process_attr_values_destroy(values);
+       return -1;
+}
+
+void lttng_process_attr_values_destroy(struct lttng_process_attr_values *values)
+{
+       if (!values) {
+               return;
+       }
+       lttng_dynamic_pointer_array_reset(&values->array);
+       free(values);
+}
+
+struct process_attr_value *process_attr_value_copy(
+               const struct process_attr_value *value)
+{
+       struct process_attr_value *new_value = NULL;
+
+       if (!value) {
+               goto end;
+       }
+
+       new_value = (process_attr_value *) zmalloc(sizeof(*new_value));
+       if (!new_value) {
+               goto end;
+       }
+       if (is_value_type_name(value->type)) {
+               const char *src =
+                               value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
+                                               value->value.user_name :
+                                               value->value.group_name;
+               char **dst = value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
+                                            &new_value->value.user_name :
+                                            &new_value->value.group_name;
+
+               new_value->type = value->type;
+               *dst = strdup(src);
+               if (!*dst) {
+                       goto error;
+               }
+       } else {
+               *new_value = *value;
+       }
+end:
+       return new_value;
+error:
+       free(new_value);
+       return NULL;
+}
+
+unsigned long process_attr_value_hash(const struct process_attr_value *a)
+{
+       unsigned long hash = hash_key_ulong((void *) a->type, lttng_ht_seed);
+
+       switch (a->type) {
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
+               hash ^= hash_key_ulong((void *) (unsigned long) a->value.pid,
+                               lttng_ht_seed);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
+               hash ^= hash_key_ulong((void *) (unsigned long) a->value.uid,
+                               lttng_ht_seed);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
+               hash ^= hash_key_ulong((void *) (unsigned long) a->value.gid,
+                               lttng_ht_seed);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
+               hash ^= hash_key_str(a->value.user_name, lttng_ht_seed);
+               break;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
+               hash ^= hash_key_str(a->value.group_name, lttng_ht_seed);
+               break;
+       default:
+               abort();
+       }
+
+       return hash;
+}
+
+bool process_attr_tracker_value_equal(const struct process_attr_value *a,
+               const struct process_attr_value *b)
+{
+       if (a->type != b->type) {
+               return false;
+       }
+       switch (a->type) {
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID:
+               return a->value.pid == b->value.pid;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID:
+               return a->value.uid == b->value.uid;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID:
+               return a->value.gid == b->value.gid;
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME:
+               return !strcmp(a->value.user_name, b->value.user_name);
+       case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME:
+               return !strcmp(a->value.group_name, b->value.group_name);
+       default:
+               abort();
+       }
+}
+
+void process_attr_value_destroy(struct process_attr_value *value)
+{
+       if (!value) {
+               return;
+       }
+       if (is_value_type_name(value->type)) {
+               free(value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ?
+                                               value->value.user_name :
+                                               value->value.group_name);
+       }
+       free(value);
+}
diff --git a/src/common/trigger.c b/src/common/trigger.c
deleted file mode 100644 (file)
index 76082a0..0000000
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/credentials.h>
-#include <common/dynamic-array.h>
-#include <common/error.h>
-#include <common/mi-lttng.h>
-#include <common/optional.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <inttypes.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/condition/buffer-usage.h>
-#include <lttng/condition/condition-internal.h>
-#include <lttng/condition/event-rule-matches-internal.h>
-#include <lttng/condition/event-rule-matches.h>
-#include <lttng/domain.h>
-#include <lttng/error-query-internal.h>
-#include <lttng/event-expr-internal.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/trigger/trigger-internal.h>
-#include <pthread.h>
-
-bool lttng_trigger_validate(const struct lttng_trigger *trigger)
-{
-       bool valid;
-
-       if (!trigger) {
-               valid = false;
-               goto end;
-       }
-
-       if (!trigger->creds.uid.is_set) {
-               valid = false;
-               goto end;
-       }
-
-       valid = lttng_condition_validate(trigger->condition) &&
-                       lttng_action_validate(trigger->action);
-end:
-       return valid;
-}
-
-struct lttng_trigger *lttng_trigger_create(
-               struct lttng_condition *condition,
-               struct lttng_action *action)
-{
-       struct lttng_trigger *trigger = NULL;
-
-       if (!condition || !action) {
-               goto end;
-       }
-
-       trigger = zmalloc(sizeof(struct lttng_trigger));
-       if (!trigger) {
-               goto end;
-       }
-
-       urcu_ref_init(&trigger->ref);
-
-       lttng_condition_get(condition);
-       trigger->condition = condition;
-
-       lttng_action_get(action);
-       trigger->action = action;
-
-       pthread_mutex_init(&trigger->lock, NULL);
-       trigger->registered = false;
-
-end:
-       return trigger;
-}
-
-/*
- * Note: the lack of reference counting 'get' on the condition object is normal.
- * This API was exposed as such in 2.11. The client is not expected to call
- * lttng_condition_destroy on the returned object.
- */
-struct lttng_condition *lttng_trigger_get_condition(
-               struct lttng_trigger *trigger)
-{
-       return trigger ? trigger->condition : NULL;
-}
-
-const struct lttng_condition *lttng_trigger_get_const_condition(
-               const struct lttng_trigger *trigger)
-{
-       return trigger ? trigger->condition : NULL;
-}
-
-/*
- * Note: the lack of reference counting 'get' on the action object is normal.
- * This API was exposed as such in 2.11. The client is not expected to call
- * lttng_action_destroy on the returned object.
- */
-struct lttng_action *lttng_trigger_get_action(
-               struct lttng_trigger *trigger)
-{
-       return trigger ? trigger->action : NULL;
-}
-
-const struct lttng_action *lttng_trigger_get_const_action(
-               const struct lttng_trigger *trigger)
-{
-       return trigger ? trigger->action : NULL;
-}
-
-static void trigger_destroy_ref(struct urcu_ref *ref)
-{
-       struct lttng_trigger *trigger =
-                       container_of(ref, struct lttng_trigger, ref);
-       struct lttng_action *action = lttng_trigger_get_action(trigger);
-       struct lttng_condition *condition =
-                       lttng_trigger_get_condition(trigger);
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(condition);
-
-       /* Release ownership. */
-       lttng_action_put(action);
-       lttng_condition_put(condition);
-
-       pthread_mutex_destroy(&trigger->lock);
-
-       free(trigger->name);
-       free(trigger);
-}
-
-void lttng_trigger_destroy(struct lttng_trigger *trigger)
-{
-       lttng_trigger_put(trigger);
-}
-
-ssize_t lttng_trigger_create_from_payload(
-               struct lttng_payload_view *src_view,
-               struct lttng_trigger **_trigger)
-{
-       ssize_t ret, offset = 0, condition_size, action_size, name_size = 0;
-       struct lttng_condition *condition = NULL;
-       struct lttng_action *action = NULL;
-       const struct lttng_trigger_comm *trigger_comm;
-       const char *name = NULL;
-       struct lttng_credentials creds = {
-               .uid = LTTNG_OPTIONAL_INIT_UNSET,
-               .gid = LTTNG_OPTIONAL_INIT_UNSET,
-       };
-       struct lttng_trigger *trigger = NULL;
-       const struct lttng_payload_view trigger_comm_view =
-                       lttng_payload_view_from_view(
-                                       src_view, 0, sizeof(*trigger_comm));
-
-       if (!src_view || !_trigger) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_payload_view_is_valid(&trigger_comm_view)) {
-               /* Payload not large enough to contain the header. */
-               ret = -1;
-               goto end;
-       }
-
-       /* lttng_trigger_comm header */
-       trigger_comm = (typeof(trigger_comm)) trigger_comm_view.buffer.data;
-
-       /* Set the trigger's creds. */
-       if (trigger_comm->uid > (uint64_t) ((uid_t) -1)) {
-               /* UID out of range for this platform. */
-               ret = -1;
-               goto end;
-       }
-
-       LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid);
-
-       offset += sizeof(*trigger_comm);
-
-       if (trigger_comm->name_length != 0) {
-               /* Name. */
-               const struct lttng_payload_view name_view =
-                               lttng_payload_view_from_view(
-                                               src_view, offset,
-                                               trigger_comm->name_length);
-
-               if (!lttng_payload_view_is_valid(&name_view)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               name = name_view.buffer.data;
-               if (!lttng_buffer_view_contains_string(&name_view.buffer, name,
-                                   trigger_comm->name_length)) {
-                       ret = -1;
-                       goto end;
-               }
-
-               offset += trigger_comm->name_length;
-               name_size = trigger_comm->name_length;
-       }
-
-       {
-               /* struct lttng_condition */
-               struct lttng_payload_view condition_view =
-                               lttng_payload_view_from_view(
-                                               src_view, offset, -1);
-
-               condition_size = lttng_condition_create_from_payload(&condition_view,
-                               &condition);
-       }
-
-       if (condition_size < 0) {
-               ret = condition_size;
-               goto end;
-       }
-
-       offset += condition_size;
-       {
-               /* struct lttng_action */
-               struct lttng_payload_view action_view =
-                               lttng_payload_view_from_view(
-                                       src_view, offset, -1);
-
-               action_size = lttng_action_create_from_payload(&action_view, &action);
-       }
-
-       if (action_size < 0) {
-               ret = action_size;
-               goto end;
-       }
-       offset += action_size;
-
-       /* Unexpected size of inner-elements; the buffer is corrupted. */
-       if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) {
-               ret = -1;
-               goto error;
-       }
-
-       trigger = lttng_trigger_create(condition, action);
-       if (!trigger) {
-               ret = -1;
-               goto error;
-       }
-
-       lttng_trigger_set_credentials(trigger, &creds);
-
-       /*
-        * The trigger object owns references to the action and condition
-        * objects.
-        */
-       lttng_condition_put(condition);
-       condition = NULL;
-
-       lttng_action_put(action);
-       action = NULL;
-
-       if (name) {
-               const enum lttng_trigger_status status =
-                               lttng_trigger_set_name(trigger, name);
-
-               if (status != LTTNG_TRIGGER_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       ret = offset;
-
-error:
-       lttng_condition_put(condition);
-       lttng_action_put(action);
-end:
-       if (ret >= 0) {
-               *_trigger = trigger;
-       } else {
-               lttng_trigger_put(trigger);
-       }
-
-       return ret;
-}
-
-/*
- * Both elements are stored contiguously, see their "*_comm" structure
- * for the detailed format.
- */
-int lttng_trigger_serialize(const struct lttng_trigger *trigger,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t header_offset, size_before_payload, size_name;
-       struct lttng_trigger_comm trigger_comm = {};
-       struct lttng_trigger_comm *header;
-       const struct lttng_credentials *creds = NULL;
-
-       creds = lttng_trigger_get_credentials(trigger);
-       LTTNG_ASSERT(creds);
-
-       trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid);
-
-       if (trigger->name != NULL) {
-               size_name = strlen(trigger->name) + 1;
-       } else {
-               size_name = 0;
-       }
-
-       trigger_comm.name_length = size_name;
-
-       header_offset = payload->buffer.size;
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm,
-                       sizeof(trigger_comm));
-       if (ret) {
-               goto end;
-       }
-
-       size_before_payload = payload->buffer.size;
-
-       /* Trigger name. */
-       ret = lttng_dynamic_buffer_append(
-                       &payload->buffer, trigger->name, size_name);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_condition_serialize(trigger->condition, payload);
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_action_serialize(trigger->action, payload);
-       if (ret) {
-               goto end;
-       }
-
-       /* Update payload size. */
-       header = (typeof(header)) (payload->buffer.data + header_offset);
-       header->length = payload->buffer.size - size_before_payload;
-end:
-       return ret;
-}
-
-bool lttng_trigger_is_equal(
-               const struct lttng_trigger *a, const struct lttng_trigger *b)
-{
-       if (!!a->name != !!b->name) {
-               /* Both must be either anonymous or named. */
-               return false;
-       }
-
-       if (a->name && strcmp(a->name, b->name) != 0) {
-               return false;
-       }
-
-       if (!lttng_condition_is_equal(a->condition, b->condition)) {
-               return false;
-       }
-
-       if (!lttng_action_is_equal(a->action, b->action)) {
-               return false;
-       }
-
-       if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a),
-                       lttng_trigger_get_credentials(b))) {
-               return false;
-       }
-
-       if (a->is_hidden != b->is_hidden) {
-               return false;
-       }
-
-       return true;
-}
-
-bool lttng_trigger_is_hidden(const struct lttng_trigger *trigger)
-{
-       return trigger->is_hidden;
-}
-
-void lttng_trigger_set_hidden(struct lttng_trigger *trigger)
-{
-       LTTNG_ASSERT(!trigger->is_hidden);
-       trigger->is_hidden = true;
-}
-
-enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger,
-               const char* name)
-{
-       char *name_copy = NULL;
-       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
-
-       if (!trigger) {
-               status = LTTNG_TRIGGER_STATUS_INVALID;
-               goto end;
-       }
-
-       if (name) {
-               name_copy = strdup(name);
-               if (!name_copy) {
-                       status = LTTNG_TRIGGER_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       free(trigger->name);
-
-       trigger->name = name_copy;
-       name_copy = NULL;
-end:
-       return status;
-}
-
-enum lttng_trigger_status lttng_trigger_get_name(
-               const struct lttng_trigger *trigger, const char **name)
-{
-       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
-
-       if (!trigger || !name) {
-               status = LTTNG_TRIGGER_STATUS_INVALID;
-               goto end;
-       }
-
-       if (!trigger->name) {
-               status = LTTNG_TRIGGER_STATUS_UNSET;
-       }
-
-       *name = trigger->name;
-end:
-       return status;
-}
-
-int lttng_trigger_assign_name(struct lttng_trigger *dst,
-               const struct lttng_trigger *src)
-{
-       int ret = 0;
-       enum lttng_trigger_status status;
-
-       status = lttng_trigger_set_name(dst, src->name);
-       if (status != LTTNG_TRIGGER_STATUS_OK) {
-               ret = -1;
-               ERR("Failed to set name for trigger");
-               goto end;
-       }
-end:
-       return ret;
-}
-
-void lttng_trigger_set_tracer_token(struct lttng_trigger *trigger,
-               uint64_t token)
-{
-       LTTNG_ASSERT(trigger);
-       LTTNG_OPTIONAL_SET(&trigger->tracer_token, token);
-}
-
-uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger)
-{
-       LTTNG_ASSERT(trigger);
-
-       return LTTNG_OPTIONAL_GET(trigger->tracer_token);
-}
-
-int lttng_trigger_generate_name(struct lttng_trigger *trigger,
-               uint64_t unique_id)
-{
-       int ret = 0;
-       char *generated_name = NULL;
-
-       ret = asprintf(&generated_name, "trigger%" PRIu64 "", unique_id);
-       if (ret < 0) {
-               ERR("Failed to generate trigger name");
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-       free(trigger->name);
-       trigger->name = generated_name;
-end:
-       return ret;
-}
-
-void lttng_trigger_get(struct lttng_trigger *trigger)
-{
-       urcu_ref_get(&trigger->ref);
-}
-
-void lttng_trigger_put(struct lttng_trigger *trigger)
-{
-       if (!trigger) {
-               return;
-       }
-
-       urcu_ref_put(&trigger->ref , trigger_destroy_ref);
-}
-
-static void delete_trigger_array_element(void *ptr)
-{
-       struct lttng_trigger *trigger = ptr;
-
-       lttng_trigger_put(trigger);
-}
-
-struct lttng_triggers *lttng_triggers_create(void)
-{
-       struct lttng_triggers *triggers = NULL;
-
-       triggers = zmalloc(sizeof(*triggers));
-       if (!triggers) {
-               goto end;
-       }
-
-       lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
-
-end:
-       return triggers;
-}
-
-struct lttng_trigger *lttng_triggers_borrow_mutable_at_index(
-               const struct lttng_triggers *triggers, unsigned int index)
-{
-       struct lttng_trigger *trigger = NULL;
-
-       LTTNG_ASSERT(triggers);
-       if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
-               goto end;
-       }
-
-       trigger = (struct lttng_trigger *)
-                       lttng_dynamic_pointer_array_get_pointer(
-                                       &triggers->array, index);
-end:
-       return trigger;
-}
-
-int lttng_triggers_add(
-               struct lttng_triggers *triggers, struct lttng_trigger *trigger)
-{
-       int ret;
-
-       LTTNG_ASSERT(triggers);
-       LTTNG_ASSERT(trigger);
-
-       lttng_trigger_get(trigger);
-
-       ret = lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
-       if (ret) {
-               lttng_trigger_put(trigger);
-       }
-
-       return ret;
-}
-
-int lttng_triggers_remove_hidden_triggers(struct lttng_triggers *triggers)
-{
-       int ret;
-       unsigned int trigger_count, i = 0;
-       enum lttng_trigger_status trigger_status;
-
-       LTTNG_ASSERT(triggers);
-
-       trigger_status = lttng_triggers_get_count(triggers, &trigger_count);
-       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
-
-       while (i < trigger_count) {
-               const struct lttng_trigger *trigger =
-                               lttng_triggers_get_at_index(triggers, i);
-
-               if (lttng_trigger_is_hidden(trigger)) {
-                       ret = lttng_dynamic_pointer_array_remove_pointer(
-                                       &triggers->array, i);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       trigger_count--;
-               } else {
-                       i++;
-               }
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-const struct lttng_trigger *lttng_triggers_get_at_index(
-               const struct lttng_triggers *triggers, unsigned int index)
-{
-       return lttng_triggers_borrow_mutable_at_index(triggers, index);
-}
-
-enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count)
-{
-       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
-
-       if (!triggers || !count) {
-               status = LTTNG_TRIGGER_STATUS_INVALID;
-               goto end;
-       }
-
-       *count = lttng_dynamic_pointer_array_get_count(&triggers->array);
-end:
-       return status;
-}
-
-void lttng_triggers_destroy(struct lttng_triggers *triggers)
-{
-       if (!triggers) {
-               return;
-       }
-
-       lttng_dynamic_pointer_array_reset(&triggers->array);
-       free(triggers);
-}
-
-int lttng_triggers_serialize(const struct lttng_triggers *triggers,
-               struct lttng_payload *payload)
-{
-       int ret;
-       unsigned int i, count;
-       size_t size_before_payload;
-       struct lttng_triggers_comm triggers_comm = {};
-       struct lttng_triggers_comm *header;
-       enum lttng_trigger_status status;
-       const size_t header_offset = payload->buffer.size;
-
-       status = lttng_triggers_get_count(triggers, &count);
-       if (status != LTTNG_TRIGGER_STATUS_OK) {
-               ret = LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       triggers_comm.count = count;
-
-       /* Placeholder header; updated at the end. */
-       ret = lttng_dynamic_buffer_append(&payload->buffer, &triggers_comm,
-                       sizeof(triggers_comm));
-       if (ret) {
-               goto end;
-       }
-
-       size_before_payload = payload->buffer.size;
-
-       for (i = 0; i < count; i++) {
-               const struct lttng_trigger *trigger =
-                               lttng_triggers_get_at_index(triggers, i);
-
-               LTTNG_ASSERT(trigger);
-
-               ret = lttng_trigger_serialize(trigger, payload);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /* Update payload size. */
-       header = (struct lttng_triggers_comm *) ((char *) payload->buffer.data + header_offset);
-       header->length = payload->buffer.size - size_before_payload;
-end:
-       return ret;
-}
-
-ssize_t lttng_triggers_create_from_payload(
-               struct lttng_payload_view *src_view,
-               struct lttng_triggers **triggers)
-{
-       ssize_t ret, offset = 0, triggers_size = 0;
-       unsigned int i;
-       const struct lttng_triggers_comm *triggers_comm;
-       struct lttng_triggers *local_triggers = NULL;
-
-       if (!src_view || !triggers) {
-               ret = -1;
-               goto error;
-       }
-
-       /* lttng_trigger_comms header */
-       triggers_comm = (const struct lttng_triggers_comm *) src_view->buffer.data;
-       offset += sizeof(*triggers_comm);
-
-       local_triggers = lttng_triggers_create();
-       if (!local_triggers) {
-               ret = -1;
-               goto error;
-       }
-
-       for (i = 0; i < triggers_comm->count; i++) {
-               struct lttng_trigger *trigger = NULL;
-               struct lttng_payload_view trigger_view =
-                               lttng_payload_view_from_view(src_view, offset, -1);
-               ssize_t trigger_size;
-
-               trigger_size = lttng_trigger_create_from_payload(
-                               &trigger_view, &trigger);
-               if (trigger_size < 0) {
-                       ret = trigger_size;
-                       goto error;
-               }
-
-               /* Transfer ownership of the trigger to the collection. */
-               ret = lttng_triggers_add(local_triggers, trigger);
-               lttng_trigger_put(trigger);
-               if (ret < 0) {
-                       ret = -1;
-                       goto error;
-               }
-
-               offset += trigger_size;
-               triggers_size += trigger_size;
-       }
-
-       /* Unexpected size of inner-elements; the buffer is corrupted. */
-       if ((ssize_t) triggers_comm->length != triggers_size) {
-               ret = -1;
-               goto error;
-       }
-
-       /* Pass ownership to caller. */
-       *triggers = local_triggers;
-       local_triggers = NULL;
-
-       ret = offset;
-error:
-
-       lttng_triggers_destroy(local_triggers);
-       return ret;
-}
-
-const struct lttng_credentials *lttng_trigger_get_credentials(
-               const struct lttng_trigger *trigger)
-{
-       return &trigger->creds;
-}
-
-void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
-               const struct lttng_credentials *creds)
-{
-       LTTNG_ASSERT(creds);
-       trigger->creds = *creds;
-}
-
-enum lttng_trigger_status lttng_trigger_set_owner_uid(
-               struct lttng_trigger *trigger, uid_t uid)
-{
-       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
-       const uid_t euid = geteuid();
-       const struct lttng_credentials creds = {
-               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
-               .gid = LTTNG_OPTIONAL_INIT_UNSET,
-       };
-
-       if (!trigger) {
-               ret = LTTNG_TRIGGER_STATUS_INVALID;
-               goto end;
-       }
-
-       /* Client-side validation only to report a clearer error. */
-       if (euid != 0 && euid != uid) {
-               ret = LTTNG_TRIGGER_STATUS_PERMISSION_DENIED;
-               goto end;
-       }
-
-       lttng_trigger_set_credentials(trigger, &creds);
-
-end:
-       return ret;
-}
-
-enum lttng_trigger_status lttng_trigger_get_owner_uid(
-               const struct lttng_trigger *trigger, uid_t *uid)
-{
-       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
-       const struct lttng_credentials *creds = NULL;
-
-       if (!trigger || !uid ) {
-               ret = LTTNG_TRIGGER_STATUS_INVALID;
-               goto end;
-       }
-
-       if (!trigger->creds.uid.is_set ) {
-               ret = LTTNG_TRIGGER_STATUS_UNSET;
-               goto end;
-       }
-
-       creds = lttng_trigger_get_credentials(trigger);
-       *uid = lttng_credentials_get_uid(creds);
-
-end:
-       return ret;
-}
-
-enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
-               const struct lttng_trigger *trigger)
-{
-       enum lttng_domain_type type = LTTNG_DOMAIN_NONE;
-       const struct lttng_event_rule *event_rule;
-       enum lttng_condition_status c_status;
-       enum lttng_condition_type c_type;
-
-       LTTNG_ASSERT(trigger);
-       LTTNG_ASSERT(trigger->condition);
-
-       c_type = lttng_condition_get_type(trigger->condition);
-       assert (c_type != LTTNG_CONDITION_TYPE_UNKNOWN);
-
-       switch (c_type) {
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               /* Apply to any domain. */
-               type = LTTNG_DOMAIN_NONE;
-               break;
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-               /* Return the domain of the event rule. */
-               c_status = lttng_condition_event_rule_matches_get_rule(
-                               trigger->condition, &event_rule);
-               LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
-               type = lttng_event_rule_get_domain_type(event_rule);
-               break;
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-               /* Return the domain of the channel being monitored. */
-               c_status = lttng_condition_buffer_usage_get_domain_type(
-                               trigger->condition, &type);
-               LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
-               break;
-       default:
-               abort();
-       }
-
-       return type;
-}
-
-/*
- * Generate bytecode related to the trigger.
- * On success LTTNG_OK. On error, returns lttng_error code.
- */
-enum lttng_error_code lttng_trigger_generate_bytecode(
-               struct lttng_trigger *trigger,
-               const struct lttng_credentials *creds)
-{
-       enum lttng_error_code ret;
-       struct lttng_condition *condition = NULL;
-
-       condition = lttng_trigger_get_condition(trigger);
-       if (!condition) {
-               ret = LTTNG_ERR_INVALID_TRIGGER;
-               goto end;
-       }
-
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-       {
-               struct lttng_event_rule *event_rule;
-               const enum lttng_condition_status condition_status =
-                               lttng_condition_event_rule_matches_borrow_rule_mutable(
-                                               condition, &event_rule);
-
-               LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK);
-
-               /* Generate the filter bytecode. */
-               ret = lttng_event_rule_generate_filter_bytecode(
-                               event_rule, creds);
-               if (ret != LTTNG_OK) {
-                       goto end;
-               }
-
-               /* Generate the capture bytecode. */
-               ret = lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode(
-                               condition);
-               if (ret != LTTNG_OK) {
-                       goto end;
-               }
-
-               ret = LTTNG_OK;
-               break;
-       }
-       default:
-               ret = LTTNG_OK;
-               break;
-       }
-end:
-       return ret;
-}
-
-struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger)
-{
-       int ret;
-       struct lttng_payload copy_buffer;
-       struct lttng_condition *condition_copy = NULL;
-       struct lttng_action *action_copy = NULL;
-       struct lttng_trigger *copy = NULL;
-       enum lttng_trigger_status trigger_status;
-       const char *trigger_name;
-       uid_t trigger_owner_uid;
-
-       lttng_payload_init(&copy_buffer);
-
-       ret = lttng_condition_serialize(trigger->condition, &copy_buffer);
-       if (ret < 0) {
-               goto end;
-       }
-
-       {
-               struct lttng_payload_view view =
-                               lttng_payload_view_from_payload(
-                                               &copy_buffer, 0, -1);
-
-               ret = lttng_condition_create_from_payload(
-                               &view, &condition_copy);
-               if (ret < 0) {
-                       goto end;
-               }
-       }
-
-       lttng_payload_clear(&copy_buffer);
-
-       ret = lttng_action_serialize(trigger->action, &copy_buffer);
-       if (ret < 0) {
-               goto end;
-       }
-
-       {
-               struct lttng_payload_view view =
-                               lttng_payload_view_from_payload(
-                                               &copy_buffer, 0, -1);
-
-               ret = lttng_action_create_from_payload(
-                               &view, &action_copy);
-               if (ret < 0) {
-                       goto end;
-               }
-       }
-
-       copy = lttng_trigger_create(condition_copy, action_copy);
-       if (!copy) {
-               ERR("Failed to allocate trigger during trigger copy");
-               goto end;
-       }
-
-       trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
-       switch (trigger_status) {
-       case LTTNG_TRIGGER_STATUS_OK:
-               trigger_status = lttng_trigger_set_name(copy, trigger_name);
-               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
-                       ERR("Failed to set name of new trigger during copy");
-                       goto error_cleanup_trigger;
-               }
-               break;
-       case LTTNG_TRIGGER_STATUS_UNSET:
-               break;
-       default:
-               ERR("Failed to get name of original trigger during copy");
-               goto error_cleanup_trigger;
-       }
-
-       trigger_status = lttng_trigger_get_owner_uid(
-                       trigger, &trigger_owner_uid);
-       switch (trigger_status) {
-       case LTTNG_TRIGGER_STATUS_OK:
-               LTTNG_OPTIONAL_SET(&copy->creds.uid, trigger_owner_uid);
-               break;
-       case LTTNG_TRIGGER_STATUS_UNSET:
-               break;
-       default:
-               ERR("Failed to get owner uid of original trigger during copy");
-               goto error_cleanup_trigger;
-       }
-
-       copy->tracer_token = trigger->tracer_token;
-       copy->registered = trigger->registered;
-       copy->is_hidden = trigger->is_hidden;
-       goto end;
-
-error_cleanup_trigger:
-       lttng_trigger_destroy(copy);
-       copy = NULL;
-end:
-       lttng_condition_put(condition_copy);
-       lttng_action_put(action_copy);
-       lttng_payload_reset(&copy_buffer);
-       return copy;
-}
-
-bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger)
-{
-       bool needs_tracer_notifier = false;
-       const struct lttng_condition *condition =
-                       lttng_trigger_get_const_condition(trigger);
-
-       switch (lttng_condition_get_type(condition)) {
-       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
-               needs_tracer_notifier = true;
-               goto end;
-       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
-       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
-       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
-               goto end;
-       case LTTNG_CONDITION_TYPE_UNKNOWN:
-       default:
-               abort();
-       }
-end:
-       return needs_tracer_notifier;
-}
-
-void lttng_trigger_set_as_registered(struct lttng_trigger *trigger)
-{
-       pthread_mutex_lock(&trigger->lock);
-       trigger->registered = true;
-       pthread_mutex_unlock(&trigger->lock);
-}
-
-void lttng_trigger_set_as_unregistered(struct lttng_trigger *trigger)
-{
-       pthread_mutex_lock(&trigger->lock);
-       trigger->registered = false;
-       pthread_mutex_unlock(&trigger->lock);
-}
-
-/*
- * The trigger must be locked before calling lttng_trigger_registered.
- * The lock is necessary since a trigger can be unregistered at anytime.
- * Manipulations requiring that the trigger be registered must always acquire
- * the trigger lock for the duration of the manipulation using
- * `lttng_trigger_lock` and `lttng_trigger_unlock`.
- */
-bool lttng_trigger_is_registered(struct lttng_trigger *trigger)
-{
-       ASSERT_LOCKED(trigger->lock);
-       return trigger->registered;
-}
-
-void lttng_trigger_lock(struct lttng_trigger *trigger)
-{
-       pthread_mutex_lock(&trigger->lock);
-}
-
-void lttng_trigger_unlock(struct lttng_trigger *trigger)
-{
-       pthread_mutex_unlock(&trigger->lock);
-}
-
-enum lttng_error_code lttng_trigger_mi_serialize(const struct lttng_trigger *trigger,
-               struct mi_writer *writer,
-               const struct mi_lttng_error_query_callbacks
-                               *error_query_callbacks)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_trigger_status trigger_status;
-       const struct lttng_condition *condition = NULL;
-       const struct lttng_action *action = NULL;
-       struct lttng_dynamic_array action_path_indexes;
-       uid_t owner_uid;
-
-       LTTNG_ASSERT(trigger);
-       LTTNG_ASSERT(writer);
-
-       lttng_dynamic_array_init(&action_path_indexes, sizeof(uint64_t), NULL);
-
-       /* Open trigger element. */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_trigger);
-       if (ret) {
-               goto mi_error;
-       }
-
-       trigger_status = lttng_trigger_get_owner_uid(trigger, &owner_uid);
-       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
-
-       /* Name. */
-       ret = mi_lttng_writer_write_element_string(
-                       writer, config_element_name, trigger->name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Owner uid. */
-       ret = mi_lttng_writer_write_element_signed_int(writer,
-                       mi_lttng_element_trigger_owner_uid,
-                       (int64_t) owner_uid);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Condition. */
-       condition = lttng_trigger_get_const_condition(trigger);
-       LTTNG_ASSERT(condition);
-       ret_code = lttng_condition_mi_serialize(
-                       trigger, condition, writer, error_query_callbacks);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Action. */
-       action = lttng_trigger_get_const_action(trigger);
-       LTTNG_ASSERT(action);
-       ret_code = lttng_action_mi_serialize(trigger, action, writer,
-                       error_query_callbacks, &action_path_indexes);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       if (error_query_callbacks && error_query_callbacks->trigger_cb) {
-               struct lttng_error_query_results *results = NULL;
-
-               ret_code = error_query_callbacks->trigger_cb(trigger, &results);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-
-               ret_code = lttng_error_query_results_mi_serialize(
-                               results, writer);
-               lttng_error_query_results_destroy(results);
-               if (ret_code != LTTNG_OK) {
-                       goto end;
-               }
-       }
-
-       /* Close trigger element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       lttng_dynamic_array_reset(&action_path_indexes);
-       return ret_code;
-}
-
-/* Used by qsort, which expects the semantics of strcmp(). */
-static int compare_triggers_by_name(const void *a, const void *b)
-{
-       const struct lttng_trigger *trigger_a =
-                       *((const struct lttng_trigger **) a);
-       const struct lttng_trigger *trigger_b =
-                       *((const struct lttng_trigger **) b);
-       const char *name_a, *name_b;
-       enum lttng_trigger_status trigger_status;
-
-       /* Anonymous triggers are not reachable here. */
-       trigger_status = lttng_trigger_get_name(trigger_a, &name_a);
-       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
-
-       trigger_status = lttng_trigger_get_name(trigger_b, &name_b);
-       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
-
-       return strcmp(name_a, name_b);
-}
-
-enum lttng_error_code lttng_triggers_mi_serialize(const struct lttng_triggers *triggers,
-               struct mi_writer *writer,
-               const struct mi_lttng_error_query_callbacks
-                               *error_query_callbacks)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       enum lttng_trigger_status status;
-       unsigned int count, i;
-       struct lttng_dynamic_pointer_array sorted_triggers;
-
-       LTTNG_ASSERT(triggers);
-       LTTNG_ASSERT(writer);
-
-       /*
-        * Sort trigger by name to ensure an order at the MI level and ignore
-        * any anonymous trigger present.
-        */
-       lttng_dynamic_pointer_array_init(&sorted_triggers, NULL);
-
-       status = lttng_triggers_get_count(triggers, &count);
-       LTTNG_ASSERT(status == LTTNG_TRIGGER_STATUS_OK);
-
-       for (i = 0; i < count; i++) {
-               int add_ret;
-               const char *unused_name;
-               const struct lttng_trigger *trigger =
-                               lttng_triggers_get_at_index(triggers, i);
-
-               status = lttng_trigger_get_name(trigger, &unused_name);
-               switch (status) {
-               case LTTNG_TRIGGER_STATUS_OK:
-                       break;
-               case LTTNG_TRIGGER_STATUS_UNSET:
-                       /* Don't list anonymous triggers. */
-                       continue;
-               default:
-                       abort();
-               }
-
-               add_ret = lttng_dynamic_pointer_array_add_pointer(
-                               &sorted_triggers, (void *) trigger);
-
-               if (add_ret) {
-                       ERR("Failed to lttng_trigger to sorting array.");
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto error;
-               }
-       }
-
-       qsort(sorted_triggers.array.buffer.data, count,
-                       sizeof(struct lttng_trigger *),
-                       compare_triggers_by_name);
-
-       /* Open triggers element. */
-       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_triggers);
-       if (ret) {
-               ret_code = LTTNG_ERR_MI_IO_FAIL;
-               goto error;
-       }
-
-       for (i = 0; i < lttng_dynamic_pointer_array_get_count(&sorted_triggers); i++) {
-               const struct lttng_trigger *trigger =
-                               (const struct lttng_trigger *)
-                               lttng_dynamic_pointer_array_get_pointer(
-                                               &sorted_triggers, i);
-
-               lttng_trigger_mi_serialize(trigger, writer, error_query_callbacks);
-       }
-
-       /* Close triggers element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               ret_code = LTTNG_ERR_MI_IO_FAIL;
-               goto error;
-       }
-
-       ret_code = LTTNG_OK;
-
-error:
-       lttng_dynamic_pointer_array_reset(&sorted_triggers);
-       return ret_code;
-}
diff --git a/src/common/trigger.cpp b/src/common/trigger.cpp
new file mode 100644 (file)
index 0000000..3782f49
--- /dev/null
@@ -0,0 +1,1240 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/credentials.h>
+#include <common/dynamic-array.h>
+#include <common/error.h>
+#include <common/mi-lttng.h>
+#include <common/optional.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <inttypes.h>
+#include <lttng/action/action-internal.h>
+#include <lttng/condition/buffer-usage.h>
+#include <lttng/condition/condition-internal.h>
+#include <lttng/condition/event-rule-matches-internal.h>
+#include <lttng/condition/event-rule-matches.h>
+#include <lttng/domain.h>
+#include <lttng/error-query-internal.h>
+#include <lttng/event-expr-internal.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/trigger/trigger-internal.h>
+#include <pthread.h>
+
+bool lttng_trigger_validate(const struct lttng_trigger *trigger)
+{
+       bool valid;
+
+       if (!trigger) {
+               valid = false;
+               goto end;
+       }
+
+       if (!trigger->creds.uid.is_set) {
+               valid = false;
+               goto end;
+       }
+
+       valid = lttng_condition_validate(trigger->condition) &&
+                       lttng_action_validate(trigger->action);
+end:
+       return valid;
+}
+
+struct lttng_trigger *lttng_trigger_create(
+               struct lttng_condition *condition,
+               struct lttng_action *action)
+{
+       struct lttng_trigger *trigger = NULL;
+
+       if (!condition || !action) {
+               goto end;
+       }
+
+       trigger = (lttng_trigger *) zmalloc(sizeof(struct lttng_trigger));
+       if (!trigger) {
+               goto end;
+       }
+
+       urcu_ref_init(&trigger->ref);
+
+       lttng_condition_get(condition);
+       trigger->condition = condition;
+
+       lttng_action_get(action);
+       trigger->action = action;
+
+       pthread_mutex_init(&trigger->lock, NULL);
+       trigger->registered = false;
+
+end:
+       return trigger;
+}
+
+/*
+ * Note: the lack of reference counting 'get' on the condition object is normal.
+ * This API was exposed as such in 2.11. The client is not expected to call
+ * lttng_condition_destroy on the returned object.
+ */
+struct lttng_condition *lttng_trigger_get_condition(
+               struct lttng_trigger *trigger)
+{
+       return trigger ? trigger->condition : NULL;
+}
+
+const struct lttng_condition *lttng_trigger_get_const_condition(
+               const struct lttng_trigger *trigger)
+{
+       return trigger ? trigger->condition : NULL;
+}
+
+/*
+ * Note: the lack of reference counting 'get' on the action object is normal.
+ * This API was exposed as such in 2.11. The client is not expected to call
+ * lttng_action_destroy on the returned object.
+ */
+struct lttng_action *lttng_trigger_get_action(
+               struct lttng_trigger *trigger)
+{
+       return trigger ? trigger->action : NULL;
+}
+
+const struct lttng_action *lttng_trigger_get_const_action(
+               const struct lttng_trigger *trigger)
+{
+       return trigger ? trigger->action : NULL;
+}
+
+static void trigger_destroy_ref(struct urcu_ref *ref)
+{
+       struct lttng_trigger *trigger =
+                       container_of(ref, struct lttng_trigger, ref);
+       struct lttng_action *action = lttng_trigger_get_action(trigger);
+       struct lttng_condition *condition =
+                       lttng_trigger_get_condition(trigger);
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(condition);
+
+       /* Release ownership. */
+       lttng_action_put(action);
+       lttng_condition_put(condition);
+
+       pthread_mutex_destroy(&trigger->lock);
+
+       free(trigger->name);
+       free(trigger);
+}
+
+void lttng_trigger_destroy(struct lttng_trigger *trigger)
+{
+       lttng_trigger_put(trigger);
+}
+
+ssize_t lttng_trigger_create_from_payload(
+               struct lttng_payload_view *src_view,
+               struct lttng_trigger **_trigger)
+{
+       ssize_t ret, offset = 0, condition_size, action_size, name_size = 0;
+       struct lttng_condition *condition = NULL;
+       struct lttng_action *action = NULL;
+       const struct lttng_trigger_comm *trigger_comm;
+       const char *name = NULL;
+       struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_UNSET,
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
+       struct lttng_trigger *trigger = NULL;
+       const struct lttng_payload_view trigger_comm_view =
+                       lttng_payload_view_from_view(
+                                       src_view, 0, sizeof(*trigger_comm));
+
+       if (!src_view || !_trigger) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_payload_view_is_valid(&trigger_comm_view)) {
+               /* Payload not large enough to contain the header. */
+               ret = -1;
+               goto end;
+       }
+
+       /* lttng_trigger_comm header */
+       trigger_comm = (typeof(trigger_comm)) trigger_comm_view.buffer.data;
+
+       /* Set the trigger's creds. */
+       if (trigger_comm->uid > (uint64_t) ((uid_t) -1)) {
+               /* UID out of range for this platform. */
+               ret = -1;
+               goto end;
+       }
+
+       LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid);
+
+       offset += sizeof(*trigger_comm);
+
+       if (trigger_comm->name_length != 0) {
+               /* Name. */
+               const struct lttng_payload_view name_view =
+                               lttng_payload_view_from_view(
+                                               src_view, offset,
+                                               trigger_comm->name_length);
+
+               if (!lttng_payload_view_is_valid(&name_view)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               name = name_view.buffer.data;
+               if (!lttng_buffer_view_contains_string(&name_view.buffer, name,
+                                   trigger_comm->name_length)) {
+                       ret = -1;
+                       goto end;
+               }
+
+               offset += trigger_comm->name_length;
+               name_size = trigger_comm->name_length;
+       }
+
+       {
+               /* struct lttng_condition */
+               struct lttng_payload_view condition_view =
+                               lttng_payload_view_from_view(
+                                               src_view, offset, -1);
+
+               condition_size = lttng_condition_create_from_payload(&condition_view,
+                               &condition);
+       }
+
+       if (condition_size < 0) {
+               ret = condition_size;
+               goto end;
+       }
+
+       offset += condition_size;
+       {
+               /* struct lttng_action */
+               struct lttng_payload_view action_view =
+                               lttng_payload_view_from_view(
+                                       src_view, offset, -1);
+
+               action_size = lttng_action_create_from_payload(&action_view, &action);
+       }
+
+       if (action_size < 0) {
+               ret = action_size;
+               goto end;
+       }
+       offset += action_size;
+
+       /* Unexpected size of inner-elements; the buffer is corrupted. */
+       if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) {
+               ret = -1;
+               goto error;
+       }
+
+       trigger = lttng_trigger_create(condition, action);
+       if (!trigger) {
+               ret = -1;
+               goto error;
+       }
+
+       lttng_trigger_set_credentials(trigger, &creds);
+
+       /*
+        * The trigger object owns references to the action and condition
+        * objects.
+        */
+       lttng_condition_put(condition);
+       condition = NULL;
+
+       lttng_action_put(action);
+       action = NULL;
+
+       if (name) {
+               const enum lttng_trigger_status status =
+                               lttng_trigger_set_name(trigger, name);
+
+               if (status != LTTNG_TRIGGER_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       ret = offset;
+
+error:
+       lttng_condition_put(condition);
+       lttng_action_put(action);
+end:
+       if (ret >= 0) {
+               *_trigger = trigger;
+       } else {
+               lttng_trigger_put(trigger);
+       }
+
+       return ret;
+}
+
+/*
+ * Both elements are stored contiguously, see their "*_comm" structure
+ * for the detailed format.
+ */
+int lttng_trigger_serialize(const struct lttng_trigger *trigger,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t header_offset, size_before_payload, size_name;
+       struct lttng_trigger_comm trigger_comm = {};
+       struct lttng_trigger_comm *header;
+       const struct lttng_credentials *creds = NULL;
+
+       creds = lttng_trigger_get_credentials(trigger);
+       LTTNG_ASSERT(creds);
+
+       trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid);
+
+       if (trigger->name != NULL) {
+               size_name = strlen(trigger->name) + 1;
+       } else {
+               size_name = 0;
+       }
+
+       trigger_comm.name_length = size_name;
+
+       header_offset = payload->buffer.size;
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm,
+                       sizeof(trigger_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = payload->buffer.size;
+
+       /* Trigger name. */
+       ret = lttng_dynamic_buffer_append(
+                       &payload->buffer, trigger->name, size_name);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_condition_serialize(trigger->condition, payload);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_action_serialize(trigger->action, payload);
+       if (ret) {
+               goto end;
+       }
+
+       /* Update payload size. */
+       header = (typeof(header)) (payload->buffer.data + header_offset);
+       header->length = payload->buffer.size - size_before_payload;
+end:
+       return ret;
+}
+
+bool lttng_trigger_is_equal(
+               const struct lttng_trigger *a, const struct lttng_trigger *b)
+{
+       if (!!a->name != !!b->name) {
+               /* Both must be either anonymous or named. */
+               return false;
+       }
+
+       if (a->name && strcmp(a->name, b->name) != 0) {
+               return false;
+       }
+
+       if (!lttng_condition_is_equal(a->condition, b->condition)) {
+               return false;
+       }
+
+       if (!lttng_action_is_equal(a->action, b->action)) {
+               return false;
+       }
+
+       if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a),
+                       lttng_trigger_get_credentials(b))) {
+               return false;
+       }
+
+       if (a->is_hidden != b->is_hidden) {
+               return false;
+       }
+
+       return true;
+}
+
+bool lttng_trigger_is_hidden(const struct lttng_trigger *trigger)
+{
+       return trigger->is_hidden;
+}
+
+void lttng_trigger_set_hidden(struct lttng_trigger *trigger)
+{
+       LTTNG_ASSERT(!trigger->is_hidden);
+       trigger->is_hidden = true;
+}
+
+enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger,
+               const char* name)
+{
+       char *name_copy = NULL;
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!trigger) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (name) {
+               name_copy = strdup(name);
+               if (!name_copy) {
+                       status = LTTNG_TRIGGER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       free(trigger->name);
+
+       trigger->name = name_copy;
+       name_copy = NULL;
+end:
+       return status;
+}
+
+enum lttng_trigger_status lttng_trigger_get_name(
+               const struct lttng_trigger *trigger, const char **name)
+{
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!trigger || !name) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (!trigger->name) {
+               status = LTTNG_TRIGGER_STATUS_UNSET;
+       }
+
+       *name = trigger->name;
+end:
+       return status;
+}
+
+int lttng_trigger_assign_name(struct lttng_trigger *dst,
+               const struct lttng_trigger *src)
+{
+       int ret = 0;
+       enum lttng_trigger_status status;
+
+       status = lttng_trigger_set_name(dst, src->name);
+       if (status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = -1;
+               ERR("Failed to set name for trigger");
+               goto end;
+       }
+end:
+       return ret;
+}
+
+void lttng_trigger_set_tracer_token(struct lttng_trigger *trigger,
+               uint64_t token)
+{
+       LTTNG_ASSERT(trigger);
+       LTTNG_OPTIONAL_SET(&trigger->tracer_token, token);
+}
+
+uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger)
+{
+       LTTNG_ASSERT(trigger);
+
+       return LTTNG_OPTIONAL_GET(trigger->tracer_token);
+}
+
+int lttng_trigger_generate_name(struct lttng_trigger *trigger,
+               uint64_t unique_id)
+{
+       int ret = 0;
+       char *generated_name = NULL;
+
+       ret = asprintf(&generated_name, "trigger%" PRIu64 "", unique_id);
+       if (ret < 0) {
+               ERR("Failed to generate trigger name");
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+       free(trigger->name);
+       trigger->name = generated_name;
+end:
+       return ret;
+}
+
+void lttng_trigger_get(struct lttng_trigger *trigger)
+{
+       urcu_ref_get(&trigger->ref);
+}
+
+void lttng_trigger_put(struct lttng_trigger *trigger)
+{
+       if (!trigger) {
+               return;
+       }
+
+       urcu_ref_put(&trigger->ref , trigger_destroy_ref);
+}
+
+static void delete_trigger_array_element(void *ptr)
+{
+       struct lttng_trigger *trigger = (lttng_trigger *) ptr;
+
+       lttng_trigger_put(trigger);
+}
+
+struct lttng_triggers *lttng_triggers_create(void)
+{
+       struct lttng_triggers *triggers = NULL;
+
+       triggers = (lttng_triggers *) zmalloc(sizeof(*triggers));
+       if (!triggers) {
+               goto end;
+       }
+
+       lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
+
+end:
+       return triggers;
+}
+
+struct lttng_trigger *lttng_triggers_borrow_mutable_at_index(
+               const struct lttng_triggers *triggers, unsigned int index)
+{
+       struct lttng_trigger *trigger = NULL;
+
+       LTTNG_ASSERT(triggers);
+       if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
+               goto end;
+       }
+
+       trigger = (struct lttng_trigger *)
+                       lttng_dynamic_pointer_array_get_pointer(
+                                       &triggers->array, index);
+end:
+       return trigger;
+}
+
+int lttng_triggers_add(
+               struct lttng_triggers *triggers, struct lttng_trigger *trigger)
+{
+       int ret;
+
+       LTTNG_ASSERT(triggers);
+       LTTNG_ASSERT(trigger);
+
+       lttng_trigger_get(trigger);
+
+       ret = lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
+       if (ret) {
+               lttng_trigger_put(trigger);
+       }
+
+       return ret;
+}
+
+int lttng_triggers_remove_hidden_triggers(struct lttng_triggers *triggers)
+{
+       int ret;
+       unsigned int trigger_count, i = 0;
+       enum lttng_trigger_status trigger_status;
+
+       LTTNG_ASSERT(triggers);
+
+       trigger_status = lttng_triggers_get_count(triggers, &trigger_count);
+       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       while (i < trigger_count) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+
+               if (lttng_trigger_is_hidden(trigger)) {
+                       ret = lttng_dynamic_pointer_array_remove_pointer(
+                                       &triggers->array, i);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       trigger_count--;
+               } else {
+                       i++;
+               }
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+const struct lttng_trigger *lttng_triggers_get_at_index(
+               const struct lttng_triggers *triggers, unsigned int index)
+{
+       return lttng_triggers_borrow_mutable_at_index(triggers, index);
+}
+
+enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count)
+{
+       enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
+
+       if (!triggers || !count) {
+               status = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       *count = lttng_dynamic_pointer_array_get_count(&triggers->array);
+end:
+       return status;
+}
+
+void lttng_triggers_destroy(struct lttng_triggers *triggers)
+{
+       if (!triggers) {
+               return;
+       }
+
+       lttng_dynamic_pointer_array_reset(&triggers->array);
+       free(triggers);
+}
+
+int lttng_triggers_serialize(const struct lttng_triggers *triggers,
+               struct lttng_payload *payload)
+{
+       int ret;
+       unsigned int i, count;
+       size_t size_before_payload;
+       struct lttng_triggers_comm triggers_comm = {};
+       struct lttng_triggers_comm *header;
+       enum lttng_trigger_status status;
+       const size_t header_offset = payload->buffer.size;
+
+       status = lttng_triggers_get_count(triggers, &count);
+       if (status != LTTNG_TRIGGER_STATUS_OK) {
+               ret = LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       triggers_comm.count = count;
+
+       /* Placeholder header; updated at the end. */
+       ret = lttng_dynamic_buffer_append(&payload->buffer, &triggers_comm,
+                       sizeof(triggers_comm));
+       if (ret) {
+               goto end;
+       }
+
+       size_before_payload = payload->buffer.size;
+
+       for (i = 0; i < count; i++) {
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+
+               LTTNG_ASSERT(trigger);
+
+               ret = lttng_trigger_serialize(trigger, payload);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /* Update payload size. */
+       header = (struct lttng_triggers_comm *) ((char *) payload->buffer.data + header_offset);
+       header->length = payload->buffer.size - size_before_payload;
+end:
+       return ret;
+}
+
+ssize_t lttng_triggers_create_from_payload(
+               struct lttng_payload_view *src_view,
+               struct lttng_triggers **triggers)
+{
+       ssize_t ret, offset = 0, triggers_size = 0;
+       unsigned int i;
+       const struct lttng_triggers_comm *triggers_comm;
+       struct lttng_triggers *local_triggers = NULL;
+
+       if (!src_view || !triggers) {
+               ret = -1;
+               goto error;
+       }
+
+       /* lttng_trigger_comms header */
+       triggers_comm = (const struct lttng_triggers_comm *) src_view->buffer.data;
+       offset += sizeof(*triggers_comm);
+
+       local_triggers = lttng_triggers_create();
+       if (!local_triggers) {
+               ret = -1;
+               goto error;
+       }
+
+       for (i = 0; i < triggers_comm->count; i++) {
+               struct lttng_trigger *trigger = NULL;
+               struct lttng_payload_view trigger_view =
+                               lttng_payload_view_from_view(src_view, offset, -1);
+               ssize_t trigger_size;
+
+               trigger_size = lttng_trigger_create_from_payload(
+                               &trigger_view, &trigger);
+               if (trigger_size < 0) {
+                       ret = trigger_size;
+                       goto error;
+               }
+
+               /* Transfer ownership of the trigger to the collection. */
+               ret = lttng_triggers_add(local_triggers, trigger);
+               lttng_trigger_put(trigger);
+               if (ret < 0) {
+                       ret = -1;
+                       goto error;
+               }
+
+               offset += trigger_size;
+               triggers_size += trigger_size;
+       }
+
+       /* Unexpected size of inner-elements; the buffer is corrupted. */
+       if ((ssize_t) triggers_comm->length != triggers_size) {
+               ret = -1;
+               goto error;
+       }
+
+       /* Pass ownership to caller. */
+       *triggers = local_triggers;
+       local_triggers = NULL;
+
+       ret = offset;
+error:
+
+       lttng_triggers_destroy(local_triggers);
+       return ret;
+}
+
+const struct lttng_credentials *lttng_trigger_get_credentials(
+               const struct lttng_trigger *trigger)
+{
+       return &trigger->creds;
+}
+
+void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
+               const struct lttng_credentials *creds)
+{
+       LTTNG_ASSERT(creds);
+       trigger->creds = *creds;
+}
+
+enum lttng_trigger_status lttng_trigger_set_owner_uid(
+               struct lttng_trigger *trigger, uid_t uid)
+{
+       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
+       const uid_t euid = geteuid();
+       const struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
+               .gid = LTTNG_OPTIONAL_INIT_UNSET,
+       };
+
+       if (!trigger) {
+               ret = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       /* Client-side validation only to report a clearer error. */
+       if (euid != 0 && euid != uid) {
+               ret = LTTNG_TRIGGER_STATUS_PERMISSION_DENIED;
+               goto end;
+       }
+
+       lttng_trigger_set_credentials(trigger, &creds);
+
+end:
+       return ret;
+}
+
+enum lttng_trigger_status lttng_trigger_get_owner_uid(
+               const struct lttng_trigger *trigger, uid_t *uid)
+{
+       enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
+       const struct lttng_credentials *creds = NULL;
+
+       if (!trigger || !uid ) {
+               ret = LTTNG_TRIGGER_STATUS_INVALID;
+               goto end;
+       }
+
+       if (!trigger->creds.uid.is_set ) {
+               ret = LTTNG_TRIGGER_STATUS_UNSET;
+               goto end;
+       }
+
+       creds = lttng_trigger_get_credentials(trigger);
+       *uid = lttng_credentials_get_uid(creds);
+
+end:
+       return ret;
+}
+
+enum lttng_domain_type lttng_trigger_get_underlying_domain_type_restriction(
+               const struct lttng_trigger *trigger)
+{
+       enum lttng_domain_type type = LTTNG_DOMAIN_NONE;
+       const struct lttng_event_rule *event_rule;
+       enum lttng_condition_status c_status;
+       enum lttng_condition_type c_type;
+
+       LTTNG_ASSERT(trigger);
+       LTTNG_ASSERT(trigger->condition);
+
+       c_type = lttng_condition_get_type(trigger->condition);
+       assert (c_type != LTTNG_CONDITION_TYPE_UNKNOWN);
+
+       switch (c_type) {
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               /* Apply to any domain. */
+               type = LTTNG_DOMAIN_NONE;
+               break;
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+               /* Return the domain of the event rule. */
+               c_status = lttng_condition_event_rule_matches_get_rule(
+                               trigger->condition, &event_rule);
+               LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
+               type = lttng_event_rule_get_domain_type(event_rule);
+               break;
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+               /* Return the domain of the channel being monitored. */
+               c_status = lttng_condition_buffer_usage_get_domain_type(
+                               trigger->condition, &type);
+               LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
+               break;
+       default:
+               abort();
+       }
+
+       return type;
+}
+
+/*
+ * Generate bytecode related to the trigger.
+ * On success LTTNG_OK. On error, returns lttng_error code.
+ */
+enum lttng_error_code lttng_trigger_generate_bytecode(
+               struct lttng_trigger *trigger,
+               const struct lttng_credentials *creds)
+{
+       enum lttng_error_code ret;
+       struct lttng_condition *condition = NULL;
+
+       condition = lttng_trigger_get_condition(trigger);
+       if (!condition) {
+               ret = LTTNG_ERR_INVALID_TRIGGER;
+               goto end;
+       }
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+       {
+               struct lttng_event_rule *event_rule;
+               const enum lttng_condition_status condition_status =
+                               lttng_condition_event_rule_matches_borrow_rule_mutable(
+                                               condition, &event_rule);
+
+               LTTNG_ASSERT(condition_status == LTTNG_CONDITION_STATUS_OK);
+
+               /* Generate the filter bytecode. */
+               ret = lttng_event_rule_generate_filter_bytecode(
+                               event_rule, creds);
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+
+               /* Generate the capture bytecode. */
+               ret = lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode(
+                               condition);
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+
+               ret = LTTNG_OK;
+               break;
+       }
+       default:
+               ret = LTTNG_OK;
+               break;
+       }
+end:
+       return ret;
+}
+
+struct lttng_trigger *lttng_trigger_copy(const struct lttng_trigger *trigger)
+{
+       int ret;
+       struct lttng_payload copy_buffer;
+       struct lttng_condition *condition_copy = NULL;
+       struct lttng_action *action_copy = NULL;
+       struct lttng_trigger *copy = NULL;
+       enum lttng_trigger_status trigger_status;
+       const char *trigger_name;
+       uid_t trigger_owner_uid;
+
+       lttng_payload_init(&copy_buffer);
+
+       ret = lttng_condition_serialize(trigger->condition, &copy_buffer);
+       if (ret < 0) {
+               goto end;
+       }
+
+       {
+               struct lttng_payload_view view =
+                               lttng_payload_view_from_payload(
+                                               &copy_buffer, 0, -1);
+
+               ret = lttng_condition_create_from_payload(
+                               &view, &condition_copy);
+               if (ret < 0) {
+                       goto end;
+               }
+       }
+
+       lttng_payload_clear(&copy_buffer);
+
+       ret = lttng_action_serialize(trigger->action, &copy_buffer);
+       if (ret < 0) {
+               goto end;
+       }
+
+       {
+               struct lttng_payload_view view =
+                               lttng_payload_view_from_payload(
+                                               &copy_buffer, 0, -1);
+
+               ret = lttng_action_create_from_payload(
+                               &view, &action_copy);
+               if (ret < 0) {
+                       goto end;
+               }
+       }
+
+       copy = lttng_trigger_create(condition_copy, action_copy);
+       if (!copy) {
+               ERR("Failed to allocate trigger during trigger copy");
+               goto end;
+       }
+
+       trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+       switch (trigger_status) {
+       case LTTNG_TRIGGER_STATUS_OK:
+               trigger_status = lttng_trigger_set_name(copy, trigger_name);
+               if (trigger_status != LTTNG_TRIGGER_STATUS_OK) {
+                       ERR("Failed to set name of new trigger during copy");
+                       goto error_cleanup_trigger;
+               }
+               break;
+       case LTTNG_TRIGGER_STATUS_UNSET:
+               break;
+       default:
+               ERR("Failed to get name of original trigger during copy");
+               goto error_cleanup_trigger;
+       }
+
+       trigger_status = lttng_trigger_get_owner_uid(
+                       trigger, &trigger_owner_uid);
+       switch (trigger_status) {
+       case LTTNG_TRIGGER_STATUS_OK:
+               LTTNG_OPTIONAL_SET(&copy->creds.uid, trigger_owner_uid);
+               break;
+       case LTTNG_TRIGGER_STATUS_UNSET:
+               break;
+       default:
+               ERR("Failed to get owner uid of original trigger during copy");
+               goto error_cleanup_trigger;
+       }
+
+       copy->tracer_token = trigger->tracer_token;
+       copy->registered = trigger->registered;
+       copy->is_hidden = trigger->is_hidden;
+       goto end;
+
+error_cleanup_trigger:
+       lttng_trigger_destroy(copy);
+       copy = NULL;
+end:
+       lttng_condition_put(condition_copy);
+       lttng_action_put(action_copy);
+       lttng_payload_reset(&copy_buffer);
+       return copy;
+}
+
+bool lttng_trigger_needs_tracer_notifier(const struct lttng_trigger *trigger)
+{
+       bool needs_tracer_notifier = false;
+       const struct lttng_condition *condition =
+                       lttng_trigger_get_const_condition(trigger);
+
+       switch (lttng_condition_get_type(condition)) {
+       case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES:
+               needs_tracer_notifier = true;
+               goto end;
+       case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH:
+       case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING:
+       case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED:
+               goto end;
+       case LTTNG_CONDITION_TYPE_UNKNOWN:
+       default:
+               abort();
+       }
+end:
+       return needs_tracer_notifier;
+}
+
+void lttng_trigger_set_as_registered(struct lttng_trigger *trigger)
+{
+       pthread_mutex_lock(&trigger->lock);
+       trigger->registered = true;
+       pthread_mutex_unlock(&trigger->lock);
+}
+
+void lttng_trigger_set_as_unregistered(struct lttng_trigger *trigger)
+{
+       pthread_mutex_lock(&trigger->lock);
+       trigger->registered = false;
+       pthread_mutex_unlock(&trigger->lock);
+}
+
+/*
+ * The trigger must be locked before calling lttng_trigger_registered.
+ * The lock is necessary since a trigger can be unregistered at anytime.
+ * Manipulations requiring that the trigger be registered must always acquire
+ * the trigger lock for the duration of the manipulation using
+ * `lttng_trigger_lock` and `lttng_trigger_unlock`.
+ */
+bool lttng_trigger_is_registered(struct lttng_trigger *trigger)
+{
+       ASSERT_LOCKED(trigger->lock);
+       return trigger->registered;
+}
+
+void lttng_trigger_lock(struct lttng_trigger *trigger)
+{
+       pthread_mutex_lock(&trigger->lock);
+}
+
+void lttng_trigger_unlock(struct lttng_trigger *trigger)
+{
+       pthread_mutex_unlock(&trigger->lock);
+}
+
+enum lttng_error_code lttng_trigger_mi_serialize(const struct lttng_trigger *trigger,
+               struct mi_writer *writer,
+               const struct mi_lttng_error_query_callbacks
+                               *error_query_callbacks)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_trigger_status trigger_status;
+       const struct lttng_condition *condition = NULL;
+       const struct lttng_action *action = NULL;
+       struct lttng_dynamic_array action_path_indexes;
+       uid_t owner_uid;
+
+       LTTNG_ASSERT(trigger);
+       LTTNG_ASSERT(writer);
+
+       lttng_dynamic_array_init(&action_path_indexes, sizeof(uint64_t), NULL);
+
+       /* Open trigger element. */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_trigger);
+       if (ret) {
+               goto mi_error;
+       }
+
+       trigger_status = lttng_trigger_get_owner_uid(trigger, &owner_uid);
+       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       /* Name. */
+       ret = mi_lttng_writer_write_element_string(
+                       writer, config_element_name, trigger->name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Owner uid. */
+       ret = mi_lttng_writer_write_element_signed_int(writer,
+                       mi_lttng_element_trigger_owner_uid,
+                       (int64_t) owner_uid);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Condition. */
+       condition = lttng_trigger_get_const_condition(trigger);
+       LTTNG_ASSERT(condition);
+       ret_code = lttng_condition_mi_serialize(
+                       trigger, condition, writer, error_query_callbacks);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Action. */
+       action = lttng_trigger_get_const_action(trigger);
+       LTTNG_ASSERT(action);
+       ret_code = lttng_action_mi_serialize(trigger, action, writer,
+                       error_query_callbacks, &action_path_indexes);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       if (error_query_callbacks && error_query_callbacks->trigger_cb) {
+               struct lttng_error_query_results *results = NULL;
+
+               ret_code = error_query_callbacks->trigger_cb(trigger, &results);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+
+               ret_code = lttng_error_query_results_mi_serialize(
+                               results, writer);
+               lttng_error_query_results_destroy(results);
+               if (ret_code != LTTNG_OK) {
+                       goto end;
+               }
+       }
+
+       /* Close trigger element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       lttng_dynamic_array_reset(&action_path_indexes);
+       return ret_code;
+}
+
+/* Used by qsort, which expects the semantics of strcmp(). */
+static int compare_triggers_by_name(const void *a, const void *b)
+{
+       const struct lttng_trigger *trigger_a =
+                       *((const struct lttng_trigger **) a);
+       const struct lttng_trigger *trigger_b =
+                       *((const struct lttng_trigger **) b);
+       const char *name_a, *name_b;
+       enum lttng_trigger_status trigger_status;
+
+       /* Anonymous triggers are not reachable here. */
+       trigger_status = lttng_trigger_get_name(trigger_a, &name_a);
+       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       trigger_status = lttng_trigger_get_name(trigger_b, &name_b);
+       LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
+
+       return strcmp(name_a, name_b);
+}
+
+enum lttng_error_code lttng_triggers_mi_serialize(const struct lttng_triggers *triggers,
+               struct mi_writer *writer,
+               const struct mi_lttng_error_query_callbacks
+                               *error_query_callbacks)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       enum lttng_trigger_status status;
+       unsigned int count, i;
+       struct lttng_dynamic_pointer_array sorted_triggers;
+
+       LTTNG_ASSERT(triggers);
+       LTTNG_ASSERT(writer);
+
+       /*
+        * Sort trigger by name to ensure an order at the MI level and ignore
+        * any anonymous trigger present.
+        */
+       lttng_dynamic_pointer_array_init(&sorted_triggers, NULL);
+
+       status = lttng_triggers_get_count(triggers, &count);
+       LTTNG_ASSERT(status == LTTNG_TRIGGER_STATUS_OK);
+
+       for (i = 0; i < count; i++) {
+               int add_ret;
+               const char *unused_name;
+               const struct lttng_trigger *trigger =
+                               lttng_triggers_get_at_index(triggers, i);
+
+               status = lttng_trigger_get_name(trigger, &unused_name);
+               switch (status) {
+               case LTTNG_TRIGGER_STATUS_OK:
+                       break;
+               case LTTNG_TRIGGER_STATUS_UNSET:
+                       /* Don't list anonymous triggers. */
+                       continue;
+               default:
+                       abort();
+               }
+
+               add_ret = lttng_dynamic_pointer_array_add_pointer(
+                               &sorted_triggers, (void *) trigger);
+
+               if (add_ret) {
+                       ERR("Failed to lttng_trigger to sorting array.");
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto error;
+               }
+       }
+
+       qsort(sorted_triggers.array.buffer.data, count,
+                       sizeof(struct lttng_trigger *),
+                       compare_triggers_by_name);
+
+       /* Open triggers element. */
+       ret = mi_lttng_writer_open_element(writer, mi_lttng_element_triggers);
+       if (ret) {
+               ret_code = LTTNG_ERR_MI_IO_FAIL;
+               goto error;
+       }
+
+       for (i = 0; i < lttng_dynamic_pointer_array_get_count(&sorted_triggers); i++) {
+               const struct lttng_trigger *trigger =
+                               (const struct lttng_trigger *)
+                               lttng_dynamic_pointer_array_get_pointer(
+                                               &sorted_triggers, i);
+
+               lttng_trigger_mi_serialize(trigger, writer, error_query_callbacks);
+       }
+
+       /* Close triggers element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               ret_code = LTTNG_ERR_MI_IO_FAIL;
+               goto error;
+       }
+
+       ret_code = LTTNG_OK;
+
+error:
+       lttng_dynamic_pointer_array_reset(&sorted_triggers);
+       return ret_code;
+}
diff --git a/src/common/unix.c b/src/common/unix.c
deleted file mode 100644 (file)
index 9918db2..0000000
+++ /dev/null
@@ -1,1147 +0,0 @@
-/*
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <common/common.h>
-#include <common/compat/errno.h>
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <common/fd-handle.h>
-
-#include "unix.h"
-
-/*
- * Connect to unix socket using the path name.
- */
-int lttcomm_connect_unix_sock(const char *pathname)
-{
-       struct sockaddr_un s_un;
-       int fd, ret, closeret;
-
-       if (strlen(pathname) >= sizeof(s_un.sun_path)) {
-               ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
-                               pathname, strlen(pathname) + 1,
-                               sizeof(s_un.sun_path));
-               ret = -ENAMETOOLONG;
-               goto error;
-       }
-
-       fd = socket(PF_UNIX, SOCK_STREAM, 0);
-       if (fd < 0) {
-               PERROR("socket");
-               ret = fd;
-               goto error;
-       }
-
-       memset(&s_un, 0, sizeof(s_un));
-       s_un.sun_family = AF_UNIX;
-       strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path));
-       s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0';
-
-       ret = connect(fd, (struct sockaddr *) &s_un, sizeof(s_un));
-       if (ret < 0) {
-               /*
-                * Don't print message on connect error, because connect is used in
-                * normal execution to detect if sessiond is alive.
-                */
-               goto error_connect;
-       }
-
-       return fd;
-
-error_connect:
-       closeret = close(fd);
-       if (closeret) {
-               PERROR("close");
-       }
-error:
-       return ret;
-}
-
-/*
- * Do an accept(2) on the sock and return the new file descriptor. The socket
- * MUST be bind(2) before.
- */
-int lttcomm_accept_unix_sock(int sock)
-{
-       int new_fd;
-       struct sockaddr_un s_un;
-       socklen_t len = sizeof(s_un);
-
-       /* Blocking call */
-       new_fd = accept(sock, (struct sockaddr *) &s_un, &len);
-       if (new_fd < 0) {
-               PERROR("accept");
-       }
-
-       return new_fd;
-}
-
-int lttcomm_create_anon_unix_socketpair(int *fds)
-{
-       if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) < 0) {
-               PERROR("socketpair");
-               return -1;
-       }
-       return 0;
-}
-
-/*
- * Creates a AF_UNIX local socket using pathname bind the socket upon creation
- * and return the fd.
- */
-int lttcomm_create_unix_sock(const char *pathname)
-{
-       struct sockaddr_un s_un;
-       int fd = -1;
-       int ret = -1;
-
-       if (strlen(pathname) >= sizeof(s_un.sun_path)) {
-               ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
-                               pathname, strlen(pathname) + 1,
-                               sizeof(s_un.sun_path));
-               ret = -ENAMETOOLONG;
-               goto error;
-       }
-
-       /* Create server socket */
-       if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
-               PERROR("socket");
-               goto error;
-       }
-
-       memset(&s_un, 0, sizeof(s_un));
-       s_un.sun_family = AF_UNIX;
-       strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path));
-       s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0';
-
-       /* Unlink the old file if present */
-       (void) unlink(pathname);
-       ret = bind(fd, (struct sockaddr *) &s_un, sizeof(s_un));
-       if (ret < 0) {
-               PERROR("bind");
-               goto error;
-       }
-
-       return fd;
-
-error:
-       if (fd >= 0) {
-               if (close(fd) < 0) {
-                       PERROR("close create unix sock");
-               }
-       }
-       return ret;
-}
-
-/*
- * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN.
- */
-int lttcomm_listen_unix_sock(int sock)
-{
-       int ret;
-
-       ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN);
-       if (ret < 0) {
-               PERROR("listen");
-       }
-
-       return ret;
-}
-
-/*
- * Receive data of size len in put that data into the buf param. Using recvmsg
- * API.
- *
- * Return the size of received data.
- */
-ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret = -1;
-       size_t len_last;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-
-       memset(&msg, 0, sizeof(msg));
-
-       iov[0].iov_base = buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-       do {
-               len_last = iov[0].iov_len;
-               ret = lttng_recvmsg_nosigpipe(sock, &msg);
-               if (ret > 0) {
-                       iov[0].iov_base += ret;
-                       iov[0].iov_len -= ret;
-                       LTTNG_ASSERT(ret <= len_last);
-               }
-       } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
-       if (ret < 0) {
-               PERROR("recvmsg");
-       } else if (ret > 0) {
-               ret = len;
-       }
-       /* Else ret = 0 meaning an orderly shutdown. */
-
-       return ret;
-}
-
-/*
- * Receive data of size len in put that data into the buf param. Using recvmsg
- * API. Only use with sockets set in non-blocking mode.
- *
- * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a
- * poll set. The poll loop will handle the EPIPE original cause.
- *
- * Return the size of received data.
- */
-ssize_t lttcomm_recv_unix_sock_non_block(int sock, void *buf, size_t len)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-
-       memset(&msg, 0, sizeof(msg));
-
-       iov[0].iov_base = buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-retry:
-       ret = lttng_recvmsg_nosigpipe(sock, &msg);
-       if (ret < 0) {
-               if (errno == EINTR) {
-                       goto retry;
-               } else {
-                       /*
-                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
-                        */
-                       if (errno == EAGAIN || errno == EWOULDBLOCK ||
-                                       errno == EPIPE) {
-                               /*
-                                * Nothing was recv.
-                                */
-                               ret = 0;
-                               goto end;
-                       }
-
-                       /* Unexpected error */
-                       PERROR("recvmsg");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Send buf data of size len. Using sendmsg API.
- *
- * Return the size of sent data.
- */
-ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t len)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-
-       memset(&msg, 0, sizeof(msg));
-
-       iov[0].iov_base = (void *) buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-       while (iov[0].iov_len) {
-               ret = sendmsg(sock, &msg, 0);
-               if (ret < 0) {
-                       if (errno == EINTR) {
-                               continue;
-                       } else {
-                               /*
-                                * Only warn about EPIPE when quiet mode is
-                                * deactivated.
-                                * We consider EPIPE as expected.
-                                */
-                               if (errno != EPIPE || !lttng_opt_quiet) {
-                                       PERROR("sendmsg");
-                               }
-                               goto end;
-                       }
-               }
-               iov[0].iov_len -= ret;
-               iov[0].iov_base += ret;
-       }
-       ret = len;
-end:
-       return ret;
-}
-
-/*
- * Send buf data of size len. Using sendmsg API.
- * Only use with non-blocking sockets. The difference with the blocking version
- * of the function is that this one does not retry to send on partial sends,
- * except if the interruption was caused by a signal (EINTR).
- *
- * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a
- * poll set. The poll loop will handle the EPIPE original cause.
- *
- * Return the size of sent data.
- */
-ssize_t lttcomm_send_unix_sock_non_block(int sock, const void *buf, size_t len)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-
-       memset(&msg, 0, sizeof(msg));
-
-       iov[0].iov_base = (void *) buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-retry:
-       ret = sendmsg(sock, &msg, 0);
-       if (ret < 0) {
-               if (errno == EINTR) {
-                       goto retry;
-               } else {
-                       /*
-                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
-                        */
-                       if (errno == EAGAIN || errno == EWOULDBLOCK ||
-                                       errno == EPIPE) {
-                               /*
-                                * This can happen in non blocking mode.
-                                * Nothing was sent.
-                                */
-                               ret = 0;
-                               goto end;
-                       }
-
-                       /* Unexpected error */
-                       PERROR("sendmsg");
-                       ret = -1;
-                       goto end;
-               }
-       }
-end:
-       return ret;
-}
-
-/*
- * Shutdown cleanly a unix socket.
- */
-int lttcomm_close_unix_sock(int sock)
-{
-       int ret, closeret;
-
-       /* Shutdown receptions and transmissions */
-       ret = shutdown(sock, SHUT_RDWR);
-       if (ret < 0) {
-               PERROR("shutdown");
-       }
-
-       closeret = close(sock);
-       if (closeret) {
-               PERROR("close");
-       }
-
-       return ret;
-}
-
-/*
- * Send a message accompanied by fd(s) over a unix socket.
- *
- * Returns the size of data sent, or negative error value.
- */
-ssize_t lttcomm_send_fds_unix_sock(int sock, const int *fds, size_t nb_fd)
-{
-       struct msghdr msg;
-       struct cmsghdr *cmptr;
-       struct iovec iov[1];
-       ssize_t ret = -1;
-       unsigned int sizeof_fds = nb_fd * sizeof(int);
-       char tmp[CMSG_SPACE(sizeof_fds)];
-       char dummy = 0;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(fds);
-       LTTNG_ASSERT(nb_fd > 0);
-
-       memset(&msg, 0, sizeof(msg));
-       memset(tmp, 0, sizeof(tmp));
-
-       if (nb_fd > LTTCOMM_MAX_SEND_FDS)
-               return -EINVAL;
-
-       msg.msg_control = (caddr_t)tmp;
-       msg.msg_controllen = CMSG_LEN(sizeof_fds);
-
-       cmptr = CMSG_FIRSTHDR(&msg);
-       if (!cmptr) {
-               return -1;
-       }
-
-       cmptr->cmsg_level = SOL_SOCKET;
-       cmptr->cmsg_type = SCM_RIGHTS;
-       cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
-       memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
-       /* Sum of the length of all control messages in the buffer: */
-       msg.msg_controllen = cmptr->cmsg_len;
-
-       iov[0].iov_base = &dummy;
-       iov[0].iov_len = 1;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-       do {
-               ret = sendmsg(sock, &msg, 0);
-       } while (ret < 0 && errno == EINTR);
-       if (ret < 0) {
-               /*
-                * Only warn about EPIPE when quiet mode is deactivated.
-                * We consider EPIPE as expected.
-                */
-               if (errno != EPIPE || !lttng_opt_quiet) {
-                       PERROR("sendmsg");
-               }
-       }
-       return ret;
-}
-
-/*
- * Send the fd(s) of a payload view over a unix socket.
- *
- * Returns the size of data sent, or negative error value.
- */
-static
-ssize_t _lttcomm_send_payload_view_fds_unix_sock(int sock,
-               struct lttng_payload_view *view,
-               bool blocking)
-{
-       int i;
-       ssize_t ret;
-       struct lttng_dynamic_array raw_fds;
-       const int fd_count = lttng_payload_view_get_fd_handle_count(view);
-
-       lttng_dynamic_array_init(&raw_fds, sizeof(int), NULL);
-
-       if (fd_count < 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       /*
-        * Prepare a contiguous array of file descriptors to send them.
-        *
-        * Note that the reference to each fd is released during the iteration;
-        * we're just getting the numerical value of the fds to conform to the
-        * syscall's interface. We rely on the fact that "view" must remain
-        * valid for the duration of the call and that the underlying payload
-        * owns a reference to the fd_handles.
-        */
-       for (i = 0; i < fd_count; i++) {
-               struct fd_handle *handle =
-                               lttng_payload_view_pop_fd_handle(view);
-               const int raw_fd = fd_handle_get_fd(handle);
-               const int add_ret = lttng_dynamic_array_add_element(
-                               &raw_fds, &raw_fd);
-
-               fd_handle_put(handle);
-               if (add_ret) {
-                       ret = -LTTNG_ERR_NOMEM;
-                       goto end;
-               }
-       }
-
-       if (blocking) {
-               ret = lttcomm_send_fds_unix_sock(sock,
-                               (const int *) raw_fds.buffer.data, fd_count);
-       } else {
-               ret = lttcomm_send_fds_unix_sock_non_block(sock,
-                               (const int *) raw_fds.buffer.data, fd_count);
-       }
-
-end:
-       lttng_dynamic_array_reset(&raw_fds);
-       return ret;
-}
-
-ssize_t lttcomm_send_payload_view_fds_unix_sock(int sock,
-               struct lttng_payload_view *view)
-{
-       return _lttcomm_send_payload_view_fds_unix_sock(sock, view, true);
-}
-
-ssize_t lttcomm_send_payload_view_fds_unix_sock_non_block(int sock,
-               struct lttng_payload_view *view)
-{
-       return _lttcomm_send_payload_view_fds_unix_sock(sock, view, false);
-}
-
-/*
- * Send a message accompanied by fd(s) over a unix socket.
- * Only use for non blocking socket.
- *
- * Returns the size of data sent, or negative error value.
- */
-ssize_t lttcomm_send_fds_unix_sock_non_block(int sock, const int *fds, size_t nb_fd)
-{
-       struct msghdr msg;
-       struct cmsghdr *cmptr;
-       struct iovec iov[1];
-       ssize_t ret = -1;
-       unsigned int sizeof_fds = nb_fd * sizeof(int);
-       char tmp[CMSG_SPACE(sizeof_fds)];
-       char dummy = 0;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(fds);
-       LTTNG_ASSERT(nb_fd > 0);
-
-       memset(&msg, 0, sizeof(msg));
-       memset(tmp, 0, sizeof(tmp));
-
-       if (nb_fd > LTTCOMM_MAX_SEND_FDS)
-               return -EINVAL;
-
-       msg.msg_control = (caddr_t)tmp;
-       msg.msg_controllen = CMSG_LEN(sizeof_fds);
-
-       cmptr = CMSG_FIRSTHDR(&msg);
-       if (!cmptr) {
-               return -1;
-       }
-
-       cmptr->cmsg_level = SOL_SOCKET;
-       cmptr->cmsg_type = SCM_RIGHTS;
-       cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
-       memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
-       /* Sum of the length of all control messages in the buffer: */
-       msg.msg_controllen = cmptr->cmsg_len;
-
-       iov[0].iov_base = &dummy;
-       iov[0].iov_len = 1;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-retry:
-       ret = sendmsg(sock, &msg, 0);
-       if (ret < 0) {
-               if (errno == EINTR) {
-                       goto retry;
-               } else {
-                       /*
-                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
-                        */
-                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
-                               /*
-                                * This can happen in non blocking mode.
-                                * Nothing was sent.
-                                */
-                               ret = 0;
-                               goto end;
-                       }
-
-                       if (errno == EPIPE) {
-                               /* Expected error, pass error to caller */
-                               DBG3("EPIPE on sendmsg");
-                               ret = -1;
-                               goto end;
-                       }
-
-                       /* Unexpected error */
-                       PERROR("sendmsg");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Recv a message accompanied by fd(s) from a unix socket.
- *
- * Returns the size of received data, or negative error value.
- *
- * Expect at most "nb_fd" file descriptors. Returns the number of fd
- * actually received in nb_fd.
- */
-ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
-{
-       struct iovec iov[1];
-       ssize_t ret = 0;
-       struct cmsghdr *cmsg;
-       size_t sizeof_fds = nb_fd * sizeof(int);
-
-#ifdef __linux__
-/* Account for the struct ucred cmsg in the buffer size */
-#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred))
-#else
-#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds)
-#endif /* __linux__ */
-
-       char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE];
-       struct msghdr msg;
-       char dummy;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(fds);
-       LTTNG_ASSERT(nb_fd > 0);
-
-       memset(&msg, 0, sizeof(msg));
-
-       /* Prepare to receive the structures */
-       iov[0].iov_base = &dummy;
-       iov[0].iov_len = 1;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-       cmsg = (struct cmsghdr *) recv_buf;
-       cmsg->cmsg_len = CMSG_LEN(sizeof_fds);
-       cmsg->cmsg_level = SOL_SOCKET;
-       cmsg->cmsg_type = SCM_RIGHTS;
-
-       msg.msg_control = cmsg;
-       msg.msg_controllen = CMSG_LEN(sizeof(recv_buf));
-       msg.msg_flags = 0;
-
-retry:
-       ret = lttng_recvmsg_nosigpipe(sock, &msg);
-       if (ret < 0) {
-               if (errno == EINTR) {
-                       goto retry;
-               } else {
-                       /* We consider EPIPE and EAGAIN as expected. */
-                       if (!lttng_opt_quiet &&
-                                       (errno != EPIPE && errno != EAGAIN)) {
-                               PERROR("recvmsg");
-                       }
-                       goto end;
-               }
-       }
-
-       if (ret != 1) {
-               fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
-                               ret, 1);
-               goto end;
-       }
-
-       if (msg.msg_flags & MSG_CTRUNC) {
-               fprintf(stderr, "Error: Control message truncated.\n");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * If the socket was configured with SO_PASSCRED, the kernel will add a
-        * control message (cmsg) to the ancillary data of the unix socket. We
-        * need to expect a cmsg of the SCM_CREDENTIALS as the first control
-        * message.
-        */
-       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-               if (cmsg->cmsg_level != SOL_SOCKET) {
-                       fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n");
-                       ret = -1;
-                       goto end;
-               }
-               if (cmsg->cmsg_type == SCM_RIGHTS) {
-                       /*
-                        * We found the controle message for file descriptors,
-                        * now copy the fds to the fds ptr and return success.
-                        */
-                       if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
-                               fprintf(stderr, "Error: Received %zu bytes of"
-                                       "ancillary data for FDs, expected %zu\n",
-                                       (size_t) cmsg->cmsg_len,
-                                       (size_t) CMSG_LEN(sizeof_fds));
-                               ret = -1;
-                               goto end;
-                       }
-                       memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
-                       ret = sizeof_fds;
-                       goto end;
-               }
-#ifdef __linux__
-               if (cmsg->cmsg_type == SCM_CREDENTIALS) {
-                       /*
-                        * Expect credentials to be sent when expecting fds even
-                        * if no credential were include in the send(). The
-                        * kernel adds them...
-                        */
-                       ret = -1;
-               }
-#endif /* __linux__ */
-       }
-end:
-       return ret;
-}
-
-static
-void close_raw_fd(void *ptr)
-{
-       const int raw_fd = *((const int *) ptr);
-
-       if (raw_fd >= 0) {
-               const int ret = close(raw_fd);
-
-               if (ret) {
-                       PERROR("Failed to close file descriptor %d", raw_fd);
-               }
-       }
-}
-
-static
-enum lttng_error_code add_fds_to_payload(struct lttng_dynamic_array *raw_fds,
-               struct lttng_payload *payload)
-{
-       int i;
-       enum lttng_error_code ret_code = LTTNG_OK;
-       const int fd_count = lttng_dynamic_array_get_count(raw_fds);
-
-       for (i = 0; i < fd_count; i++) {
-               int ret;
-               struct fd_handle *handle;
-               int *raw_fd = (int *) lttng_dynamic_array_get_element(
-                       raw_fds, i);
-
-               LTTNG_ASSERT(*raw_fd != -1);
-
-               handle = fd_handle_create(*raw_fd);
-               if (!handle) {
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto end;
-               }
-
-               /* FD ownership transferred to the handle. */
-               *raw_fd = -1;
-
-               ret = lttng_payload_push_fd_handle(payload, handle);
-               fd_handle_put(handle);
-               if (ret) {
-                       ret_code = LTTNG_ERR_NOMEM;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_code;
-}
-
-static
-ssize_t _lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd,
-               struct lttng_payload *payload, bool blocking)
-{
-       int i = 0;
-       enum lttng_error_code add_ret;
-       ssize_t ret;
-       int default_value = -1;
-       struct lttng_dynamic_array raw_fds;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(payload);
-       LTTNG_ASSERT(nb_fd > 0);
-
-       lttng_dynamic_array_init(&raw_fds, sizeof(int), close_raw_fd);
-
-       for (i = 0; i < nb_fd; i++) {
-               if (lttng_dynamic_array_add_element(&raw_fds, &default_value)) {
-                       ret = -LTTNG_ERR_NOMEM;
-                       goto end;
-               }
-       }
-
-       if (blocking) {
-               ret = lttcomm_recv_fds_unix_sock(
-                               sock, (int *) raw_fds.buffer.data, nb_fd);
-       } else {
-               ret = lttcomm_recv_fds_unix_sock_non_block(
-                               sock, (int *) raw_fds.buffer.data, nb_fd);
-       }
-
-       if (ret <= 0) {
-               goto end;
-       }
-
-       add_ret = add_fds_to_payload(&raw_fds, payload);
-       if (add_ret != LTTNG_OK) {
-               ret = - (int) add_ret;
-               goto end;
-       }
-
-end:
-       lttng_dynamic_array_reset(&raw_fds);
-       return ret;
-}
-
-ssize_t lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd,
-                          struct lttng_payload *payload)
-{
-       return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, true);
-}
-
-ssize_t lttcomm_recv_payload_fds_unix_sock_non_block(int sock, size_t nb_fd,
-                          struct lttng_payload *payload)
-{
-       return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, false);
-}
-
-/*
- * Recv a message accompanied by fd(s) from a non-blocking unix socket.
- * Only use with non-blocking sockets.
- *
- * Returns the size of received data, or negative error value.
- *
- * Expect at most "nb_fd" file descriptors.
- *
- * Note that based on our comprehension, partial reception of fds is not
- * possible since the FDs are actually in the control message. It is all or
- * nothing, still the sender side can send the wrong number of fds.
- */
-ssize_t lttcomm_recv_fds_unix_sock_non_block(int sock, int *fds, size_t nb_fd)
-{
-       struct iovec iov[1];
-       ssize_t ret = 0;
-       struct cmsghdr *cmsg;
-       size_t sizeof_fds = nb_fd * sizeof(int);
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(fds);
-       LTTNG_ASSERT(nb_fd > 0);
-
-#ifdef __linux__
-/* Account for the struct ucred cmsg in the buffer size */
-#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred))
-#else
-#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds)
-#endif /* __linux__ */
-
-       char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE];
-       struct msghdr msg;
-       char dummy;
-
-       memset(&msg, 0, sizeof(msg));
-
-       /* Prepare to receive the structures */
-       iov[0].iov_base = &dummy;
-       iov[0].iov_len = 1;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-       cmsg = (struct cmsghdr *) recv_buf;
-       cmsg->cmsg_len = CMSG_LEN(sizeof_fds);
-       cmsg->cmsg_level = SOL_SOCKET;
-       cmsg->cmsg_type = SCM_RIGHTS;
-
-       msg.msg_control = cmsg;
-       msg.msg_controllen = CMSG_LEN(sizeof(recv_buf));
-       msg.msg_flags = 0;
-
-retry:
-       ret = lttng_recvmsg_nosigpipe(sock, &msg);
-       if (ret < 0) {
-               if (errno == EINTR) {
-                       goto retry;
-               } else {
-                       /*
-                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
-                        */
-                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
-                               /*
-                                * This can happen in non blocking mode.
-                                * Nothing was recv.
-                                */
-                               ret = 0;
-                               goto end;
-                       }
-
-                       if (errno == EPIPE) {
-                               /* Expected error, pass error to caller */
-                               DBG3("EPIPE on recvmsg");
-                               ret = -1;
-                               goto end;
-                       }
-
-                       /* Unexpected error */
-                       PERROR("recvmsg");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       if (ret != 1) {
-               fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
-                               ret, 1);
-               goto end;
-       }
-
-       if (msg.msg_flags & MSG_CTRUNC) {
-               fprintf(stderr, "Error: Control message truncated.\n");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * If the socket was configured with SO_PASSCRED, the kernel will add a
-        * control message (cmsg) to the ancillary data of the unix socket. We
-        * need to expect a cmsg of the SCM_CREDENTIALS as the first control
-        * message.
-        */
-       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-               if (cmsg->cmsg_level != SOL_SOCKET) {
-                       fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n");
-                       ret = -1;
-                       goto end;
-               }
-               if (cmsg->cmsg_type == SCM_RIGHTS) {
-                       /*
-                        * We found the controle message for file descriptors,
-                        * now copy the fds to the fds ptr and return success.
-                        */
-                       if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
-                               fprintf(stderr, "Error: Received %zu bytes of"
-                                       "ancillary data for FDs, expected %zu\n",
-                                       (size_t) cmsg->cmsg_len,
-                                       (size_t) CMSG_LEN(sizeof_fds));
-                               ret = -1;
-                               goto end;
-                       }
-                       memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
-                       ret = sizeof_fds;
-                       goto end;
-               }
-#ifdef __linux__
-               if (cmsg->cmsg_type == SCM_CREDENTIALS) {
-                       /*
-                        * Expect credentials to be sent when expecting fds even
-                        * if no credential were include in the send(). The
-                        * kernel adds them...
-                        */
-                       ret = -1;
-               }
-#endif /* __linux__ */
-       }
-end:
-       return ret;
-}
-
-/*
- * Send a message with credentials over a unix socket.
- *
- * Returns the size of data sent, or negative error value.
- */
-ssize_t lttcomm_send_creds_unix_sock(int sock, const void *buf, size_t len)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret = -1;
-#if defined(__linux__) || defined(__CYGWIN__)
-       struct cmsghdr *cmptr;
-       size_t sizeof_cred = sizeof(lttng_sock_cred);
-       char anc_buf[CMSG_SPACE(sizeof_cred)];
-       lttng_sock_cred *creds;
-
-       memset(anc_buf, 0, CMSG_SPACE(sizeof_cred) * sizeof(char));
-#endif /* __linux__, __CYGWIN__ */
-
-       memset(&msg, 0, sizeof(msg));
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-
-       iov[0].iov_base = (void *) buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-#if defined(__linux__) || defined(__CYGWIN__)
-       msg.msg_control = (caddr_t) anc_buf;
-       msg.msg_controllen = CMSG_LEN(sizeof_cred);
-
-       cmptr = CMSG_FIRSTHDR(&msg);
-       if (!cmptr) {
-               return -1;
-       }
-       cmptr->cmsg_level = SOL_SOCKET;
-       cmptr->cmsg_type = LTTNG_SOCK_CREDS;
-       cmptr->cmsg_len = CMSG_LEN(sizeof_cred);
-
-       creds = (lttng_sock_cred*) CMSG_DATA(cmptr);
-
-       LTTNG_SOCK_SET_UID_CRED(creds, geteuid());
-       LTTNG_SOCK_SET_GID_CRED(creds, getegid());
-       LTTNG_SOCK_SET_PID_CRED(creds, getpid());
-#endif /* __linux__, __CYGWIN__ */
-
-       do {
-               ret = sendmsg(sock, &msg, 0);
-       } while (ret < 0 && errno == EINTR);
-       if (ret < 0) {
-               /*
-                * Only warn about EPIPE when quiet mode is deactivated.
-                * We consider EPIPE as expected.
-                */
-               if (errno != EPIPE || !lttng_opt_quiet) {
-                       PERROR("sendmsg");
-               }
-       }
-       return ret;
-}
-
-/*
- * Recv a message accompanied with credentials from a unix socket.
- *
- * Returns the size of received data, or negative error value.
- */
-ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
-               lttng_sock_cred *creds)
-{
-       struct msghdr msg;
-       struct iovec iov[1];
-       ssize_t ret;
-       size_t len_last;
-#if defined(__linux__) || defined(__CYGWIN__)
-       struct cmsghdr *cmptr;
-       size_t sizeof_cred = sizeof(lttng_sock_cred);
-       char anc_buf[CMSG_SPACE(sizeof_cred)];
-#endif /* __linux__, __CYGWIN__ */
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-       LTTNG_ASSERT(len > 0);
-       LTTNG_ASSERT(creds);
-
-       memset(&msg, 0, sizeof(msg));
-
-       /* Prepare to receive the structures */
-       iov[0].iov_base = buf;
-       iov[0].iov_len = len;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 1;
-
-#if defined(__linux__) || defined(__CYGWIN__)
-       msg.msg_control = anc_buf;
-       msg.msg_controllen = sizeof(anc_buf);
-#endif /* __linux__, __CYGWIN__ */
-
-       do {
-               len_last = iov[0].iov_len;
-               ret = recvmsg(sock, &msg, 0);
-               if (ret > 0) {
-                       iov[0].iov_base += ret;
-                       iov[0].iov_len -= ret;
-                       LTTNG_ASSERT(ret <= len_last);
-               }
-       } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
-       if (ret < 0) {
-               PERROR("recvmsg fds");
-               goto end;
-       } else if (ret > 0) {
-               ret = len;
-       }
-       /* Else ret = 0 meaning an orderly shutdown. */
-
-#if defined(__linux__) || defined(__CYGWIN__)
-       if (msg.msg_flags & MSG_CTRUNC) {
-               fprintf(stderr, "Error: Control message truncated.\n");
-               ret = -1;
-               goto end;
-       }
-
-       cmptr = CMSG_FIRSTHDR(&msg);
-       if (cmptr == NULL) {
-               fprintf(stderr, "Error: Invalid control message header\n");
-               ret = -1;
-               goto end;
-       }
-
-       if (cmptr->cmsg_level != SOL_SOCKET ||
-                       cmptr->cmsg_type != LTTNG_SOCK_CREDS) {
-               fprintf(stderr, "Didn't received any credentials\n");
-               ret = -1;
-               goto end;
-       }
-
-       if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) {
-               fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
-                               (size_t) cmptr->cmsg_len, (size_t) CMSG_LEN(sizeof_cred));
-               ret = -1;
-               goto end;
-       }
-
-       memcpy(creds, CMSG_DATA(cmptr), sizeof_cred);
-#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__))
-       if (lttng_get_unix_socket_peer_creds(sock, creds)) {
-               fprintf(stderr, "ARG\n");
-               ret = -1;
-               goto end;
-       }
-#else
-#error "Please implement credential support for your OS."
-#endif /* __linux__, __CYGWIN__ */
-
-end:
-       return ret;
-}
-
-/*
- * Set socket option to use credentials passing.
- */
-#if defined(__linux__) || defined(__CYGWIN__)
-int lttcomm_setsockopt_creds_unix_sock(int sock)
-{
-       int ret, on = 1;
-
-       /* Set socket for credentials retrieval */
-       ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
-       if (ret < 0) {
-               PERROR("setsockopt creds unix sock");
-       }
-       return ret;
-}
-#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__))
-int lttcomm_setsockopt_creds_unix_sock(int sock)
-{
-       return 0;
-}
-#else
-#error "Please implement credential support for your OS."
-#endif /* __linux__ */
diff --git a/src/common/unix.cpp b/src/common/unix.cpp
new file mode 100644 (file)
index 0000000..504970e
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <common/common.h>
+#include <common/compat/errno.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/fd-handle.h>
+
+#include "unix.h"
+
+/*
+ * Connect to unix socket using the path name.
+ */
+int lttcomm_connect_unix_sock(const char *pathname)
+{
+       struct sockaddr_un s_un;
+       int fd, ret, closeret;
+
+       if (strlen(pathname) >= sizeof(s_un.sun_path)) {
+               ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
+                               pathname, strlen(pathname) + 1,
+                               sizeof(s_un.sun_path));
+               ret = -ENAMETOOLONG;
+               goto error;
+       }
+
+       fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0) {
+               PERROR("socket");
+               ret = fd;
+               goto error;
+       }
+
+       memset(&s_un, 0, sizeof(s_un));
+       s_un.sun_family = AF_UNIX;
+       strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path));
+       s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0';
+
+       ret = connect(fd, (struct sockaddr *) &s_un, sizeof(s_un));
+       if (ret < 0) {
+               /*
+                * Don't print message on connect error, because connect is used in
+                * normal execution to detect if sessiond is alive.
+                */
+               goto error_connect;
+       }
+
+       return fd;
+
+error_connect:
+       closeret = close(fd);
+       if (closeret) {
+               PERROR("close");
+       }
+error:
+       return ret;
+}
+
+/*
+ * Do an accept(2) on the sock and return the new file descriptor. The socket
+ * MUST be bind(2) before.
+ */
+int lttcomm_accept_unix_sock(int sock)
+{
+       int new_fd;
+       struct sockaddr_un s_un;
+       socklen_t len = sizeof(s_un);
+
+       /* Blocking call */
+       new_fd = accept(sock, (struct sockaddr *) &s_un, &len);
+       if (new_fd < 0) {
+               PERROR("accept");
+       }
+
+       return new_fd;
+}
+
+int lttcomm_create_anon_unix_socketpair(int *fds)
+{
+       if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+               PERROR("socketpair");
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Creates a AF_UNIX local socket using pathname bind the socket upon creation
+ * and return the fd.
+ */
+int lttcomm_create_unix_sock(const char *pathname)
+{
+       struct sockaddr_un s_un;
+       int fd = -1;
+       int ret = -1;
+
+       if (strlen(pathname) >= sizeof(s_un.sun_path)) {
+               ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).",
+                               pathname, strlen(pathname) + 1,
+                               sizeof(s_un.sun_path));
+               ret = -ENAMETOOLONG;
+               goto error;
+       }
+
+       /* Create server socket */
+       if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+               PERROR("socket");
+               goto error;
+       }
+
+       memset(&s_un, 0, sizeof(s_un));
+       s_un.sun_family = AF_UNIX;
+       strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path));
+       s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0';
+
+       /* Unlink the old file if present */
+       (void) unlink(pathname);
+       ret = bind(fd, (struct sockaddr *) &s_un, sizeof(s_un));
+       if (ret < 0) {
+               PERROR("bind");
+               goto error;
+       }
+
+       return fd;
+
+error:
+       if (fd >= 0) {
+               if (close(fd) < 0) {
+                       PERROR("close create unix sock");
+               }
+       }
+       return ret;
+}
+
+/*
+ * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN.
+ */
+int lttcomm_listen_unix_sock(int sock)
+{
+       int ret;
+
+       ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN);
+       if (ret < 0) {
+               PERROR("listen");
+       }
+
+       return ret;
+}
+
+/*
+ * Receive data of size len in put that data into the buf param. Using recvmsg
+ * API.
+ *
+ * Return the size of received data.
+ */
+ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret = -1;
+       size_t len_last;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+
+       memset(&msg, 0, sizeof(msg));
+
+       iov[0].iov_base = buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       do {
+               len_last = iov[0].iov_len;
+               ret = lttng_recvmsg_nosigpipe(sock, &msg);
+               if (ret > 0) {
+                       iov[0].iov_base = (char *) iov[0].iov_base + ret;
+                       iov[0].iov_len -= ret;
+                       LTTNG_ASSERT(ret <= len_last);
+               }
+       } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
+       if (ret < 0) {
+               PERROR("recvmsg");
+       } else if (ret > 0) {
+               ret = len;
+       }
+       /* Else ret = 0 meaning an orderly shutdown. */
+
+       return ret;
+}
+
+/*
+ * Receive data of size len in put that data into the buf param. Using recvmsg
+ * API. Only use with sockets set in non-blocking mode.
+ *
+ * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a
+ * poll set. The poll loop will handle the EPIPE original cause.
+ *
+ * Return the size of received data.
+ */
+ssize_t lttcomm_recv_unix_sock_non_block(int sock, void *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+
+       memset(&msg, 0, sizeof(msg));
+
+       iov[0].iov_base = buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+retry:
+       ret = lttng_recvmsg_nosigpipe(sock, &msg);
+       if (ret < 0) {
+               if (errno == EINTR) {
+                       goto retry;
+               } else {
+                       /*
+                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
+                        */
+                       if (errno == EAGAIN || errno == EWOULDBLOCK ||
+                                       errno == EPIPE) {
+                               /*
+                                * Nothing was recv.
+                                */
+                               ret = 0;
+                               goto end;
+                       }
+
+                       /* Unexpected error */
+                       PERROR("recvmsg");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Send buf data of size len. Using sendmsg API.
+ *
+ * Return the size of sent data.
+ */
+ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+
+       memset(&msg, 0, sizeof(msg));
+
+       iov[0].iov_base = (void *) buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       while (iov[0].iov_len) {
+               ret = sendmsg(sock, &msg, 0);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               continue;
+                       } else {
+                               /*
+                                * Only warn about EPIPE when quiet mode is
+                                * deactivated.
+                                * We consider EPIPE as expected.
+                                */
+                               if (errno != EPIPE || !lttng_opt_quiet) {
+                                       PERROR("sendmsg");
+                               }
+                               goto end;
+                       }
+               }
+               iov[0].iov_len -= ret;
+               iov[0].iov_base = (char *) iov[0].iov_base + ret;
+       }
+       ret = len;
+end:
+       return ret;
+}
+
+/*
+ * Send buf data of size len. Using sendmsg API.
+ * Only use with non-blocking sockets. The difference with the blocking version
+ * of the function is that this one does not retry to send on partial sends,
+ * except if the interruption was caused by a signal (EINTR).
+ *
+ * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a
+ * poll set. The poll loop will handle the EPIPE original cause.
+ *
+ * Return the size of sent data.
+ */
+ssize_t lttcomm_send_unix_sock_non_block(int sock, const void *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+
+       memset(&msg, 0, sizeof(msg));
+
+       iov[0].iov_base = (void *) buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+retry:
+       ret = sendmsg(sock, &msg, 0);
+       if (ret < 0) {
+               if (errno == EINTR) {
+                       goto retry;
+               } else {
+                       /*
+                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
+                        */
+                       if (errno == EAGAIN || errno == EWOULDBLOCK ||
+                                       errno == EPIPE) {
+                               /*
+                                * This can happen in non blocking mode.
+                                * Nothing was sent.
+                                */
+                               ret = 0;
+                               goto end;
+                       }
+
+                       /* Unexpected error */
+                       PERROR("sendmsg");
+                       ret = -1;
+                       goto end;
+               }
+       }
+end:
+       return ret;
+}
+
+/*
+ * Shutdown cleanly a unix socket.
+ */
+int lttcomm_close_unix_sock(int sock)
+{
+       int ret, closeret;
+
+       /* Shutdown receptions and transmissions */
+       ret = shutdown(sock, SHUT_RDWR);
+       if (ret < 0) {
+               PERROR("shutdown");
+       }
+
+       closeret = close(sock);
+       if (closeret) {
+               PERROR("close");
+       }
+
+       return ret;
+}
+
+/*
+ * Send a message accompanied by fd(s) over a unix socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+ssize_t lttcomm_send_fds_unix_sock(int sock, const int *fds, size_t nb_fd)
+{
+       struct msghdr msg;
+       struct cmsghdr *cmptr;
+       struct iovec iov[1];
+       ssize_t ret = -1;
+       unsigned int sizeof_fds = nb_fd * sizeof(int);
+       char tmp[CMSG_SPACE(sizeof_fds)];
+       char dummy = 0;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(fds);
+       LTTNG_ASSERT(nb_fd > 0);
+
+       memset(&msg, 0, sizeof(msg));
+       memset(tmp, 0, sizeof(tmp));
+
+       if (nb_fd > LTTCOMM_MAX_SEND_FDS)
+               return -EINVAL;
+
+       msg.msg_control = (caddr_t)tmp;
+       msg.msg_controllen = CMSG_LEN(sizeof_fds);
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       if (!cmptr) {
+               return -1;
+       }
+
+       cmptr->cmsg_level = SOL_SOCKET;
+       cmptr->cmsg_type = SCM_RIGHTS;
+       cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
+       memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
+       /* Sum of the length of all control messages in the buffer: */
+       msg.msg_controllen = cmptr->cmsg_len;
+
+       iov[0].iov_base = &dummy;
+       iov[0].iov_len = 1;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       do {
+               ret = sendmsg(sock, &msg, 0);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0) {
+               /*
+                * Only warn about EPIPE when quiet mode is deactivated.
+                * We consider EPIPE as expected.
+                */
+               if (errno != EPIPE || !lttng_opt_quiet) {
+                       PERROR("sendmsg");
+               }
+       }
+       return ret;
+}
+
+/*
+ * Send the fd(s) of a payload view over a unix socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+static
+ssize_t _lttcomm_send_payload_view_fds_unix_sock(int sock,
+               struct lttng_payload_view *view,
+               bool blocking)
+{
+       int i;
+       ssize_t ret;
+       struct lttng_dynamic_array raw_fds;
+       const int fd_count = lttng_payload_view_get_fd_handle_count(view);
+
+       lttng_dynamic_array_init(&raw_fds, sizeof(int), NULL);
+
+       if (fd_count < 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       /*
+        * Prepare a contiguous array of file descriptors to send them.
+        *
+        * Note that the reference to each fd is released during the iteration;
+        * we're just getting the numerical value of the fds to conform to the
+        * syscall's interface. We rely on the fact that "view" must remain
+        * valid for the duration of the call and that the underlying payload
+        * owns a reference to the fd_handles.
+        */
+       for (i = 0; i < fd_count; i++) {
+               struct fd_handle *handle =
+                               lttng_payload_view_pop_fd_handle(view);
+               const int raw_fd = fd_handle_get_fd(handle);
+               const int add_ret = lttng_dynamic_array_add_element(
+                               &raw_fds, &raw_fd);
+
+               fd_handle_put(handle);
+               if (add_ret) {
+                       ret = -LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+       }
+
+       if (blocking) {
+               ret = lttcomm_send_fds_unix_sock(sock,
+                               (const int *) raw_fds.buffer.data, fd_count);
+       } else {
+               ret = lttcomm_send_fds_unix_sock_non_block(sock,
+                               (const int *) raw_fds.buffer.data, fd_count);
+       }
+
+end:
+       lttng_dynamic_array_reset(&raw_fds);
+       return ret;
+}
+
+ssize_t lttcomm_send_payload_view_fds_unix_sock(int sock,
+               struct lttng_payload_view *view)
+{
+       return _lttcomm_send_payload_view_fds_unix_sock(sock, view, true);
+}
+
+ssize_t lttcomm_send_payload_view_fds_unix_sock_non_block(int sock,
+               struct lttng_payload_view *view)
+{
+       return _lttcomm_send_payload_view_fds_unix_sock(sock, view, false);
+}
+
+/*
+ * Send a message accompanied by fd(s) over a unix socket.
+ * Only use for non blocking socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+ssize_t lttcomm_send_fds_unix_sock_non_block(int sock, const int *fds, size_t nb_fd)
+{
+       struct msghdr msg;
+       struct cmsghdr *cmptr;
+       struct iovec iov[1];
+       ssize_t ret = -1;
+       unsigned int sizeof_fds = nb_fd * sizeof(int);
+       char tmp[CMSG_SPACE(sizeof_fds)];
+       char dummy = 0;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(fds);
+       LTTNG_ASSERT(nb_fd > 0);
+
+       memset(&msg, 0, sizeof(msg));
+       memset(tmp, 0, sizeof(tmp));
+
+       if (nb_fd > LTTCOMM_MAX_SEND_FDS)
+               return -EINVAL;
+
+       msg.msg_control = (caddr_t)tmp;
+       msg.msg_controllen = CMSG_LEN(sizeof_fds);
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       if (!cmptr) {
+               return -1;
+       }
+
+       cmptr->cmsg_level = SOL_SOCKET;
+       cmptr->cmsg_type = SCM_RIGHTS;
+       cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
+       memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
+       /* Sum of the length of all control messages in the buffer: */
+       msg.msg_controllen = cmptr->cmsg_len;
+
+       iov[0].iov_base = &dummy;
+       iov[0].iov_len = 1;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+retry:
+       ret = sendmsg(sock, &msg, 0);
+       if (ret < 0) {
+               if (errno == EINTR) {
+                       goto retry;
+               } else {
+                       /*
+                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
+                        */
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                               /*
+                                * This can happen in non blocking mode.
+                                * Nothing was sent.
+                                */
+                               ret = 0;
+                               goto end;
+                       }
+
+                       if (errno == EPIPE) {
+                               /* Expected error, pass error to caller */
+                               DBG3("EPIPE on sendmsg");
+                               ret = -1;
+                               goto end;
+                       }
+
+                       /* Unexpected error */
+                       PERROR("sendmsg");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Recv a message accompanied by fd(s) from a unix socket.
+ *
+ * Returns the size of received data, or negative error value.
+ *
+ * Expect at most "nb_fd" file descriptors. Returns the number of fd
+ * actually received in nb_fd.
+ */
+ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
+{
+       struct iovec iov[1];
+       ssize_t ret = 0;
+       struct cmsghdr *cmsg;
+       size_t sizeof_fds = nb_fd * sizeof(int);
+
+#ifdef __linux__
+/* Account for the struct ucred cmsg in the buffer size */
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred))
+#else
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds)
+#endif /* __linux__ */
+
+       char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE];
+       struct msghdr msg;
+       char dummy;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(fds);
+       LTTNG_ASSERT(nb_fd > 0);
+
+       memset(&msg, 0, sizeof(msg));
+
+       /* Prepare to receive the structures */
+       iov[0].iov_base = &dummy;
+       iov[0].iov_len = 1;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       cmsg = (struct cmsghdr *) recv_buf;
+       cmsg->cmsg_len = CMSG_LEN(sizeof_fds);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+
+       msg.msg_control = cmsg;
+       msg.msg_controllen = CMSG_LEN(sizeof(recv_buf));
+       msg.msg_flags = 0;
+
+retry:
+       ret = lttng_recvmsg_nosigpipe(sock, &msg);
+       if (ret < 0) {
+               if (errno == EINTR) {
+                       goto retry;
+               } else {
+                       /* We consider EPIPE and EAGAIN as expected. */
+                       if (!lttng_opt_quiet &&
+                                       (errno != EPIPE && errno != EAGAIN)) {
+                               PERROR("recvmsg");
+                       }
+                       goto end;
+               }
+       }
+
+       if (ret != 1) {
+               fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
+                               ret, 1);
+               goto end;
+       }
+
+       if (msg.msg_flags & MSG_CTRUNC) {
+               fprintf(stderr, "Error: Control message truncated.\n");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * If the socket was configured with SO_PASSCRED, the kernel will add a
+        * control message (cmsg) to the ancillary data of the unix socket. We
+        * need to expect a cmsg of the SCM_CREDENTIALS as the first control
+        * message.
+        */
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level != SOL_SOCKET) {
+                       fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n");
+                       ret = -1;
+                       goto end;
+               }
+               if (cmsg->cmsg_type == SCM_RIGHTS) {
+                       /*
+                        * We found the controle message for file descriptors,
+                        * now copy the fds to the fds ptr and return success.
+                        */
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
+                               fprintf(stderr, "Error: Received %zu bytes of"
+                                       "ancillary data for FDs, expected %zu\n",
+                                       (size_t) cmsg->cmsg_len,
+                                       (size_t) CMSG_LEN(sizeof_fds));
+                               ret = -1;
+                               goto end;
+                       }
+                       memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
+                       ret = sizeof_fds;
+                       goto end;
+               }
+#ifdef __linux__
+               if (cmsg->cmsg_type == SCM_CREDENTIALS) {
+                       /*
+                        * Expect credentials to be sent when expecting fds even
+                        * if no credential were include in the send(). The
+                        * kernel adds them...
+                        */
+                       ret = -1;
+               }
+#endif /* __linux__ */
+       }
+end:
+       return ret;
+}
+
+static
+void close_raw_fd(void *ptr)
+{
+       const int raw_fd = *((const int *) ptr);
+
+       if (raw_fd >= 0) {
+               const int ret = close(raw_fd);
+
+               if (ret) {
+                       PERROR("Failed to close file descriptor %d", raw_fd);
+               }
+       }
+}
+
+static
+enum lttng_error_code add_fds_to_payload(struct lttng_dynamic_array *raw_fds,
+               struct lttng_payload *payload)
+{
+       int i;
+       enum lttng_error_code ret_code = LTTNG_OK;
+       const int fd_count = lttng_dynamic_array_get_count(raw_fds);
+
+       for (i = 0; i < fd_count; i++) {
+               int ret;
+               struct fd_handle *handle;
+               int *raw_fd = (int *) lttng_dynamic_array_get_element(
+                       raw_fds, i);
+
+               LTTNG_ASSERT(*raw_fd != -1);
+
+               handle = fd_handle_create(*raw_fd);
+               if (!handle) {
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+
+               /* FD ownership transferred to the handle. */
+               *raw_fd = -1;
+
+               ret = lttng_payload_push_fd_handle(payload, handle);
+               fd_handle_put(handle);
+               if (ret) {
+                       ret_code = LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_code;
+}
+
+static
+ssize_t _lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd,
+               struct lttng_payload *payload, bool blocking)
+{
+       int i = 0;
+       enum lttng_error_code add_ret;
+       ssize_t ret;
+       int default_value = -1;
+       struct lttng_dynamic_array raw_fds;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(payload);
+       LTTNG_ASSERT(nb_fd > 0);
+
+       lttng_dynamic_array_init(&raw_fds, sizeof(int), close_raw_fd);
+
+       for (i = 0; i < nb_fd; i++) {
+               if (lttng_dynamic_array_add_element(&raw_fds, &default_value)) {
+                       ret = -LTTNG_ERR_NOMEM;
+                       goto end;
+               }
+       }
+
+       if (blocking) {
+               ret = lttcomm_recv_fds_unix_sock(
+                               sock, (int *) raw_fds.buffer.data, nb_fd);
+       } else {
+               ret = lttcomm_recv_fds_unix_sock_non_block(
+                               sock, (int *) raw_fds.buffer.data, nb_fd);
+       }
+
+       if (ret <= 0) {
+               goto end;
+       }
+
+       add_ret = add_fds_to_payload(&raw_fds, payload);
+       if (add_ret != LTTNG_OK) {
+               ret = - (int) add_ret;
+               goto end;
+       }
+
+end:
+       lttng_dynamic_array_reset(&raw_fds);
+       return ret;
+}
+
+ssize_t lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd,
+                          struct lttng_payload *payload)
+{
+       return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, true);
+}
+
+ssize_t lttcomm_recv_payload_fds_unix_sock_non_block(int sock, size_t nb_fd,
+                          struct lttng_payload *payload)
+{
+       return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, false);
+}
+
+/*
+ * Recv a message accompanied by fd(s) from a non-blocking unix socket.
+ * Only use with non-blocking sockets.
+ *
+ * Returns the size of received data, or negative error value.
+ *
+ * Expect at most "nb_fd" file descriptors.
+ *
+ * Note that based on our comprehension, partial reception of fds is not
+ * possible since the FDs are actually in the control message. It is all or
+ * nothing, still the sender side can send the wrong number of fds.
+ */
+ssize_t lttcomm_recv_fds_unix_sock_non_block(int sock, int *fds, size_t nb_fd)
+{
+       struct iovec iov[1];
+       ssize_t ret = 0;
+       struct cmsghdr *cmsg;
+       size_t sizeof_fds = nb_fd * sizeof(int);
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(fds);
+       LTTNG_ASSERT(nb_fd > 0);
+
+#ifdef __linux__
+/* Account for the struct ucred cmsg in the buffer size */
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred))
+#else
+#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds)
+#endif /* __linux__ */
+
+       char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE];
+       struct msghdr msg;
+       char dummy;
+
+       memset(&msg, 0, sizeof(msg));
+
+       /* Prepare to receive the structures */
+       iov[0].iov_base = &dummy;
+       iov[0].iov_len = 1;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+       cmsg = (struct cmsghdr *) recv_buf;
+       cmsg->cmsg_len = CMSG_LEN(sizeof_fds);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+
+       msg.msg_control = cmsg;
+       msg.msg_controllen = CMSG_LEN(sizeof(recv_buf));
+       msg.msg_flags = 0;
+
+retry:
+       ret = lttng_recvmsg_nosigpipe(sock, &msg);
+       if (ret < 0) {
+               if (errno == EINTR) {
+                       goto retry;
+               } else {
+                       /*
+                        * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected.
+                        */
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                               /*
+                                * This can happen in non blocking mode.
+                                * Nothing was recv.
+                                */
+                               ret = 0;
+                               goto end;
+                       }
+
+                       if (errno == EPIPE) {
+                               /* Expected error, pass error to caller */
+                               DBG3("EPIPE on recvmsg");
+                               ret = -1;
+                               goto end;
+                       }
+
+                       /* Unexpected error */
+                       PERROR("recvmsg");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       if (ret != 1) {
+               fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
+                               ret, 1);
+               goto end;
+       }
+
+       if (msg.msg_flags & MSG_CTRUNC) {
+               fprintf(stderr, "Error: Control message truncated.\n");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * If the socket was configured with SO_PASSCRED, the kernel will add a
+        * control message (cmsg) to the ancillary data of the unix socket. We
+        * need to expect a cmsg of the SCM_CREDENTIALS as the first control
+        * message.
+        */
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level != SOL_SOCKET) {
+                       fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n");
+                       ret = -1;
+                       goto end;
+               }
+               if (cmsg->cmsg_type == SCM_RIGHTS) {
+                       /*
+                        * We found the controle message for file descriptors,
+                        * now copy the fds to the fds ptr and return success.
+                        */
+                       if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
+                               fprintf(stderr, "Error: Received %zu bytes of"
+                                       "ancillary data for FDs, expected %zu\n",
+                                       (size_t) cmsg->cmsg_len,
+                                       (size_t) CMSG_LEN(sizeof_fds));
+                               ret = -1;
+                               goto end;
+                       }
+                       memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
+                       ret = sizeof_fds;
+                       goto end;
+               }
+#ifdef __linux__
+               if (cmsg->cmsg_type == SCM_CREDENTIALS) {
+                       /*
+                        * Expect credentials to be sent when expecting fds even
+                        * if no credential were include in the send(). The
+                        * kernel adds them...
+                        */
+                       ret = -1;
+               }
+#endif /* __linux__ */
+       }
+end:
+       return ret;
+}
+
+/*
+ * Send a message with credentials over a unix socket.
+ *
+ * Returns the size of data sent, or negative error value.
+ */
+ssize_t lttcomm_send_creds_unix_sock(int sock, const void *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret = -1;
+#if defined(__linux__) || defined(__CYGWIN__)
+       struct cmsghdr *cmptr;
+       size_t sizeof_cred = sizeof(lttng_sock_cred);
+       char anc_buf[CMSG_SPACE(sizeof_cred)];
+       lttng_sock_cred *creds;
+
+       memset(anc_buf, 0, CMSG_SPACE(sizeof_cred) * sizeof(char));
+#endif /* __linux__, __CYGWIN__ */
+
+       memset(&msg, 0, sizeof(msg));
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+
+       iov[0].iov_base = (void *) buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+#if defined(__linux__) || defined(__CYGWIN__)
+       msg.msg_control = (caddr_t) anc_buf;
+       msg.msg_controllen = CMSG_LEN(sizeof_cred);
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       if (!cmptr) {
+               return -1;
+       }
+       cmptr->cmsg_level = SOL_SOCKET;
+       cmptr->cmsg_type = LTTNG_SOCK_CREDS;
+       cmptr->cmsg_len = CMSG_LEN(sizeof_cred);
+
+       creds = (lttng_sock_cred*) CMSG_DATA(cmptr);
+
+       LTTNG_SOCK_SET_UID_CRED(creds, geteuid());
+       LTTNG_SOCK_SET_GID_CRED(creds, getegid());
+       LTTNG_SOCK_SET_PID_CRED(creds, getpid());
+#endif /* __linux__, __CYGWIN__ */
+
+       do {
+               ret = sendmsg(sock, &msg, 0);
+       } while (ret < 0 && errno == EINTR);
+       if (ret < 0) {
+               /*
+                * Only warn about EPIPE when quiet mode is deactivated.
+                * We consider EPIPE as expected.
+                */
+               if (errno != EPIPE || !lttng_opt_quiet) {
+                       PERROR("sendmsg");
+               }
+       }
+       return ret;
+}
+
+/*
+ * Recv a message accompanied with credentials from a unix socket.
+ *
+ * Returns the size of received data, or negative error value.
+ */
+ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len,
+               lttng_sock_cred *creds)
+{
+       struct msghdr msg;
+       struct iovec iov[1];
+       ssize_t ret;
+       size_t len_last;
+#if defined(__linux__) || defined(__CYGWIN__)
+       struct cmsghdr *cmptr;
+       size_t sizeof_cred = sizeof(lttng_sock_cred);
+       char anc_buf[CMSG_SPACE(sizeof_cred)];
+#endif /* __linux__, __CYGWIN__ */
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+       LTTNG_ASSERT(len > 0);
+       LTTNG_ASSERT(creds);
+
+       memset(&msg, 0, sizeof(msg));
+
+       /* Prepare to receive the structures */
+       iov[0].iov_base = buf;
+       iov[0].iov_len = len;
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 1;
+
+#if defined(__linux__) || defined(__CYGWIN__)
+       msg.msg_control = anc_buf;
+       msg.msg_controllen = sizeof(anc_buf);
+#endif /* __linux__, __CYGWIN__ */
+
+       do {
+               len_last = iov[0].iov_len;
+               ret = recvmsg(sock, &msg, 0);
+               if (ret > 0) {
+                       iov[0].iov_base = (char *) iov[0].iov_base + ret;
+                       iov[0].iov_len -= ret;
+                       LTTNG_ASSERT(ret <= len_last);
+               }
+       } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
+       if (ret < 0) {
+               PERROR("recvmsg fds");
+               goto end;
+       } else if (ret > 0) {
+               ret = len;
+       }
+       /* Else ret = 0 meaning an orderly shutdown. */
+
+#if defined(__linux__) || defined(__CYGWIN__)
+       if (msg.msg_flags & MSG_CTRUNC) {
+               fprintf(stderr, "Error: Control message truncated.\n");
+               ret = -1;
+               goto end;
+       }
+
+       cmptr = CMSG_FIRSTHDR(&msg);
+       if (cmptr == NULL) {
+               fprintf(stderr, "Error: Invalid control message header\n");
+               ret = -1;
+               goto end;
+       }
+
+       if (cmptr->cmsg_level != SOL_SOCKET ||
+                       cmptr->cmsg_type != LTTNG_SOCK_CREDS) {
+               fprintf(stderr, "Didn't received any credentials\n");
+               ret = -1;
+               goto end;
+       }
+
+       if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) {
+               fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
+                               (size_t) cmptr->cmsg_len, (size_t) CMSG_LEN(sizeof_cred));
+               ret = -1;
+               goto end;
+       }
+
+       memcpy(creds, CMSG_DATA(cmptr), sizeof_cred);
+#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__))
+       if (lttng_get_unix_socket_peer_creds(sock, creds)) {
+               fprintf(stderr, "ARG\n");
+               ret = -1;
+               goto end;
+       }
+#else
+#error "Please implement credential support for your OS."
+#endif /* __linux__, __CYGWIN__ */
+
+end:
+       return ret;
+}
+
+/*
+ * Set socket option to use credentials passing.
+ */
+#if defined(__linux__) || defined(__CYGWIN__)
+int lttcomm_setsockopt_creds_unix_sock(int sock)
+{
+       int ret, on = 1;
+
+       /* Set socket for credentials retrieval */
+       ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+       if (ret < 0) {
+               PERROR("setsockopt creds unix sock");
+       }
+       return ret;
+}
+#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__))
+int lttcomm_setsockopt_creds_unix_sock(int sock)
+{
+       return 0;
+}
+#else
+#error "Please implement credential support for your OS."
+#endif /* __linux__ */
diff --git a/src/common/uri.c b/src/common/uri.c
deleted file mode 100644 (file)
index fef21b3..0000000
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <arpa/inet.h>
-#include <common/compat/netdb.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-
-#include <common/common.h>
-#include <common/defaults.h>
-#include <common/utils.h>
-
-#include "uri.h"
-
-#define LOOPBACK_ADDR_IPV4 "127.0.0.1"
-#define LOOPBACK_ADDR_IPV6 "::1"
-
-enum uri_proto_code {
-       P_NET, P_NET6, P_FILE, P_TCP, P_TCP6,
-};
-
-struct uri_proto {
-       const char *name;
-       const char *leading_string;
-       enum uri_proto_code code;
-       enum lttng_proto_type type;
-       enum lttng_dst_type dtype;
-};
-
-/* Supported protocols */
-static const struct uri_proto proto_uri[] = {
-       { .name = "file", .leading_string = "file://", .code = P_FILE, .type = 0, .dtype = LTTNG_DST_PATH },
-       { .name = "net", .leading_string = "net://", .code = P_NET, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
-       { .name = "net4", .leading_string = "net4://", .code = P_NET, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
-       { .name = "net6", .leading_string = "net6://", .code = P_NET6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
-       { .name = "tcp", .leading_string = "tcp://", .code = P_TCP, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
-       { .name = "tcp4", .leading_string = "tcp4://", .code = P_TCP, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
-       { .name = "tcp6", .leading_string = "tcp6://", .code = P_TCP6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
-       /* Invalid proto marking the end of the array. */
-       { NULL, NULL, 0, 0, 0 }
-};
-
-/*
- * Return pointer to the character in s matching one of the characters in
- * accept. If nothing is found, return pointer to the end of string (eos).
- */
-static inline const char *strpbrk_or_eos(const char *s, const char *accept)
-{
-       char *p = strpbrk(s, accept);
-       if (p == NULL) {
-               p = strchr(s, '\0');
-       }
-
-       return p;
-}
-
-/*
- * Validate if proto is a supported protocol from proto_uri array.
- */
-static const struct uri_proto *get_uri_proto(const char *uri_str)
-{
-       const struct uri_proto *supported = NULL;
-
-       /* Safety net */
-       if (uri_str == NULL) {
-               goto end;
-       }
-
-       for (supported = &proto_uri[0];
-                       supported->leading_string != NULL; ++supported) {
-               if (strncasecmp(uri_str, supported->leading_string,
-                                       strlen(supported->leading_string)) == 0) {
-                       goto end;
-               }
-       }
-
-       /* Proto not found */
-       return NULL;
-
-end:
-       return supported;
-}
-
-/*
- * Set network address from string into dst. Supports both IP string and
- * hostname.
- */
-static int set_ip_address(const char *addr, int af, char *dst, size_t size)
-{
-       int ret;
-       unsigned char buf[sizeof(struct in6_addr)];
-       struct hostent *record;
-
-       LTTNG_ASSERT(addr);
-       LTTNG_ASSERT(dst);
-
-       memset(dst, 0, size);
-
-       /* Network protocol */
-       ret = inet_pton(af, addr, buf);
-       if (ret < 1) {
-               /* We consider the dst to be an hostname or an invalid IP char */
-               record = lttng_gethostbyname2(addr, af);
-               if (record) {
-                       /* Translate IP to string */
-                       if (!inet_ntop(af, record->h_addr_list[0], dst, size)) {
-                               PERROR("inet_ntop");
-                               goto error;
-                       }
-               } else if (!strcmp(addr, "localhost") &&
-                               (af == AF_INET || af == AF_INET6)) {
-                       /*
-                        * Some systems may not have "localhost" defined in
-                        * accordance with IETF RFC 6761. According to this RFC,
-                        * applications may recognize "localhost" names as
-                        * special and resolve to the appropriate loopback
-                        * address.
-                        *
-                        * We choose to use the system name resolution API first
-                        * to honor its network configuration. If this fails, we
-                        * resolve to the appropriate loopback address. This is
-                        * done to accommodates systems which may want to start
-                        * tracing before their network configured.
-                        */
-                       const char *loopback_addr = af == AF_INET ?
-                                       LOOPBACK_ADDR_IPV4 : LOOPBACK_ADDR_IPV6;
-                       const size_t loopback_addr_len = af == AF_INET ?
-                                       sizeof(LOOPBACK_ADDR_IPV4) :
-                                       sizeof(LOOPBACK_ADDR_IPV6);
-
-                       DBG2("Could not resolve localhost address, using fallback");
-                       if (loopback_addr_len > size) {
-                               ERR("Could not resolve localhost address; destination string is too short");
-                               goto error;
-                       }
-                       strcpy(dst, loopback_addr);
-               } else {
-                       /* At this point, the IP or the hostname is bad */
-                       goto error;
-               }
-       } else {
-               if (size > 0) {
-                       strncpy(dst, addr, size);
-                       dst[size - 1] = '\0';
-               }
-       }
-
-       DBG2("IP address resolved to %s", dst);
-       return 0;
-
-error:
-       ERR("URI parse bad hostname %s for af %d", addr, af);
-       return -1;
-}
-
-/*
- * Set default URI attribute which is basically the given stream type and the
- * default port if none is set in the URI.
- */
-static void set_default_uri_attr(struct lttng_uri *uri,
-               enum lttng_stream_type stype)
-{
-       uri->stype = stype;
-       if (uri->dtype != LTTNG_DST_PATH && uri->port == 0) {
-               uri->port = (stype == LTTNG_STREAM_CONTROL) ?
-                       DEFAULT_NETWORK_CONTROL_PORT : DEFAULT_NETWORK_DATA_PORT;
-       }
-}
-
-/*
- * Compare two URL destination.
- *
- * Return 0 is equal else is not equal.
- */
-static int compare_destination(struct lttng_uri *ctrl, struct lttng_uri *data)
-{
-       int ret;
-
-       LTTNG_ASSERT(ctrl);
-       LTTNG_ASSERT(data);
-
-       switch (ctrl->dtype) {
-       case LTTNG_DST_IPV4:
-               ret = strncmp(ctrl->dst.ipv4, data->dst.ipv4, sizeof(ctrl->dst.ipv4));
-               break;
-       case LTTNG_DST_IPV6:
-               ret = strncmp(ctrl->dst.ipv6, data->dst.ipv6, sizeof(ctrl->dst.ipv6));
-               break;
-       default:
-               ret = -1;
-               break;
-       }
-
-       return ret;
-}
-
-/*
- * Build a string URL from a lttng_uri object.
- */
-int uri_to_str_url(struct lttng_uri *uri, char *dst, size_t size)
-{
-       int ipver, ret;
-       const char *addr;
-       char proto[5], port[7];
-
-       LTTNG_ASSERT(uri);
-       LTTNG_ASSERT(dst);
-
-       if (uri->dtype == LTTNG_DST_PATH) {
-               ipver = 0;
-               addr = uri->dst.path;
-               (void) snprintf(proto, sizeof(proto), "file");
-               (void) snprintf(port, sizeof(port), "%s", "");
-       } else {
-               ipver = (uri->dtype == LTTNG_DST_IPV4) ? 4 : 6;
-               addr = (ipver == 4) ?  uri->dst.ipv4 : uri->dst.ipv6;
-               (void) snprintf(proto, sizeof(proto), "tcp%d", ipver);
-               (void) snprintf(port, sizeof(port), ":%d", uri->port);
-       }
-
-       ret = snprintf(dst, size, "%s://%s%s%s%s/%s", proto,
-                       (ipver == 6) ? "[" : "", addr, (ipver == 6) ? "]" : "",
-                       port, uri->subdir);
-       if (ret < 0) {
-               PERROR("snprintf uri to url");
-       }
-
-       return ret;
-}
-
-/*
- * Compare two URIs.
- *
- * Return 0 if equal else 1.
- */
-int uri_compare(struct lttng_uri *uri1, struct lttng_uri *uri2)
-{
-       return memcmp(uri1, uri2, sizeof(struct lttng_uri));
-}
-
-/*
- * Free URI memory.
- */
-void uri_free(struct lttng_uri *uri)
-{
-       free(uri);
-}
-
-/*
- * Parses a string URI to a lttng_uri. This function can potentially return
- * more than one URI in uris so the size of the array is returned and uris is
- * allocated and populated. Caller must free(3) the array.
- *
- * This function can not detect the stream type of the URI so the caller has to
- * make sure the correct type (stype) is set on the return URI(s). The default
- * port must also be set by the caller if the returned URI has its port set to
- * zero.
- *
- * NOTE: A good part of the following code was inspired from the "wget" source
- * tree from the src/url.c file and url_parse() function. Also, the
- * strpbrk_or_eos() function found above is also inspired by the same code.
- * This code was originally licensed GPLv2 so we acknolwedge the Free Software
- * Foundation here for the work and to make sure we are compliant with it.
- */
-ssize_t uri_parse(const char *str_uri, struct lttng_uri **uris)
-{
-       int ret, i = 0;
-       /* Size of the uris array. Default is 1 */
-       ssize_t size = 1;
-       char subdir[PATH_MAX];
-       unsigned int ctrl_port = 0;
-       unsigned int data_port = 0;
-       struct lttng_uri *tmp_uris;
-       char *addr_f = NULL;
-       const struct uri_proto *proto;
-       const char *purl, *addr_e, *addr_b, *subdir_b = NULL;
-       const char *seps = ":/\0";
-
-       /*
-        * The first part is the protocol portion of a maximum of 5 bytes for now.
-        * The second part is the hostname or IP address. The 255 bytes size is the
-        * limit found in the RFC 1035 for the total length of a domain name
-        * (https://www.ietf.org/rfc/rfc1035.txt). Finally, for the net://
-        * protocol, two ports CAN be specified.
-        */
-
-       DBG3("URI string: %s", str_uri);
-
-       proto = get_uri_proto(str_uri);
-       if (proto == NULL) {
-               ERR("URI parse unknown protocol %s", str_uri);
-               goto error;
-       }
-
-       purl = str_uri;
-
-       if (proto->code == P_NET || proto->code == P_NET6) {
-               /* Special case for net:// which requires two URI objects */
-               size = 2;
-       }
-
-       /* Allocate URI array */
-       tmp_uris = zmalloc(sizeof(struct lttng_uri) * size);
-       if (tmp_uris == NULL) {
-               PERROR("zmalloc uri");
-               goto error;
-       }
-
-       memset(subdir, 0, sizeof(subdir));
-       purl += strlen(proto->leading_string);
-
-       /* Copy known value to the first URI. */
-       tmp_uris[0].dtype = proto->dtype;
-       tmp_uris[0].proto = proto->type;
-
-       if (proto->code == P_FILE) {
-               if (*purl != '/') {
-                       ERR("Missing destination full path.");
-                       goto free_error;
-               }
-
-               strncpy(tmp_uris[0].dst.path, purl, sizeof(tmp_uris[0].dst.path));
-               tmp_uris[0].dst.path[sizeof(tmp_uris[0].dst.path) - 1] = '\0';
-               DBG3("URI file destination: %s", purl);
-               goto end;
-       }
-
-       /* Assume we are at the beginning of an address or host of some sort. */
-       addr_b = purl;
-
-       /*
-        * Handle IPv6 address inside square brackets as mention by RFC 2732. IPv6
-        * address that does not start AND end with brackets will be rejected even
-        * if valid.
-        *
-        * proto://[<addr>]...
-        *         ^
-        */
-       if (*purl == '[') {
-               /* Address begins after '[' */
-               addr_b = purl + 1;
-               addr_e = strchr(addr_b, ']');
-               if (addr_e == NULL || addr_b == addr_e) {
-                       ERR("Broken IPv6 address %s", addr_b);
-                       goto free_error;
-               }
-
-               /* Moving parsed URL pointer after the final bracket ']' */
-               purl = addr_e + 1;
-
-               /*
-                * The closing bracket must be followed by a seperator or NULL char.
-                */
-               if (strchr(seps, *purl) == NULL) {
-                       ERR("Unknown symbol after IPv6 address: %s", purl);
-                       goto free_error;
-               }
-       } else {
-               purl = strpbrk_or_eos(purl, seps);
-               addr_e = purl;
-       }
-
-       /* Check if we at least have a char for the addr or hostname. */
-       if (addr_b == addr_e) {
-               ERR("No address or hostname detected.");
-               goto free_error;
-       }
-
-       addr_f = utils_strdupdelim(addr_b, addr_e);
-       if (addr_f == NULL) {
-               goto free_error;
-       }
-
-       /*
-        * Detect PORT after address. The net/net6 protocol allows up to two port
-        * so we can define the control and data port.
-        */
-       while (*purl == ':') {
-               const char *port_b, *port_e;
-               char *port_f;
-
-               /* Update pass counter */
-               i++;
-
-               /*
-                * Maximum of two ports is possible if P_NET/NET6. Bigger than that,
-                * two much stuff.
-                */
-               if ((i == 2 && (proto->code != P_NET && proto->code != P_NET6))
-                               || i > 2) {
-                       break;
-               }
-
-               /*
-                * Move parsed URL to port value.
-                * proto://addr_host:PORT1:PORT2/foo/bar
-                *                   ^
-                */
-               ++purl;
-               port_b = purl;
-               purl = strpbrk_or_eos(purl, seps);
-               port_e = purl;
-
-               if (port_b != port_e) {
-                       int port;
-
-                       port_f = utils_strdupdelim(port_b, port_e);
-                       if (port_f == NULL) {
-                               goto free_error;
-                       }
-
-                       port = atoi(port_f);
-                       if (port > 0xffff || port <= 0x0) {
-                               ERR("Invalid port number %d", port);
-                               free(port_f);
-                               goto free_error;
-                       }
-                       free(port_f);
-
-                       if (i == 1) {
-                               ctrl_port = port;
-                       } else {
-                               data_port = port;
-                       }
-               }
-       };
-
-       /* Check for a valid subdir or trailing garbage */
-       if (*purl == '/') {
-               /*
-                * Move to subdir value.
-                * proto://addr_host:PORT1:PORT2/foo/bar
-                *                               ^
-                */
-               ++purl;
-               subdir_b = purl;
-       } else if (*purl != '\0') {
-               ERR("Trailing characters not recognized: %s", purl);
-               goto free_error;
-       }
-
-       /* We have enough valid information to create URI(s) object */
-
-       /* Copy generic information */
-       tmp_uris[0].port = ctrl_port;
-
-       /* Copy subdirectory if one. */
-       if (subdir_b) {
-               strncpy(tmp_uris[0].subdir, subdir_b, sizeof(tmp_uris[0].subdir));
-               tmp_uris[0].subdir[sizeof(tmp_uris[0].subdir) - 1] = '\0';
-       }
-
-       switch (proto->code) {
-       case P_NET:
-               ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4,
-                               sizeof(tmp_uris[0].dst.ipv4));
-               if (ret < 0) {
-                       goto free_error;
-               }
-
-               memcpy(tmp_uris[1].dst.ipv4, tmp_uris[0].dst.ipv4, sizeof(tmp_uris[1].dst.ipv4));
-
-               tmp_uris[1].dtype = proto->dtype;
-               tmp_uris[1].proto = proto->type;
-               tmp_uris[1].port = data_port;
-               break;
-       case P_NET6:
-               ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6,
-                               sizeof(tmp_uris[0].dst.ipv6));
-               if (ret < 0) {
-                       goto free_error;
-               }
-
-               memcpy(tmp_uris[1].dst.ipv6, tmp_uris[0].dst.ipv6, sizeof(tmp_uris[1].dst.ipv6));
-
-               tmp_uris[1].dtype = proto->dtype;
-               tmp_uris[1].proto = proto->type;
-               tmp_uris[1].port = data_port;
-               break;
-       case P_TCP:
-               ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4,
-                               sizeof(tmp_uris[0].dst.ipv4));
-               if (ret < 0) {
-                       goto free_error;
-               }
-               break;
-       case P_TCP6:
-               ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6,
-                               sizeof(tmp_uris[0].dst.ipv6));
-               if (ret < 0) {
-                       goto free_error;
-               }
-               break;
-       default:
-               goto free_error;
-       }
-
-end:
-       DBG3("URI dtype: %d, proto: %d, host: %s, subdir: %s, ctrl: %d, data: %d",
-                       proto->dtype, proto->type, (addr_f == NULL) ? "" : addr_f,
-                       (subdir_b == NULL) ? "" : subdir_b, ctrl_port, data_port);
-
-       free(addr_f);
-
-       *uris = tmp_uris;
-       LTTNG_ASSERT(size == 1 || size == 2);
-       return size;
-
-free_error:
-       free(addr_f);
-       free(tmp_uris);
-error:
-       return -1;
-}
-
-/*
- * Parse a string URL and creates URI(s) returning the size of the populated
- * array.
- */
-ssize_t uri_parse_str_urls(const char *ctrl_url, const char *data_url,
-               struct lttng_uri **uris)
-{
-       unsigned int equal = 1, idx = 0;
-       /* Add the "file://" size to the URL maximum size */
-       char url[PATH_MAX + 7];
-       ssize_t ctrl_uri_count = 0, data_uri_count = 0, uri_count;
-       struct lttng_uri *ctrl_uris = NULL, *data_uris = NULL;
-       struct lttng_uri *tmp_uris = NULL;
-
-       /* No URL(s) is allowed. This means that the consumer will be disabled. */
-       if (ctrl_url == NULL && data_url == NULL) {
-               return 0;
-       }
-
-       /* Check if URLs are equal and if so, only use the control URL */
-       if ((ctrl_url && *ctrl_url != '\0') && (data_url && *data_url != '\0')) {
-               equal = !strcmp(ctrl_url, data_url);
-       }
-
-       /*
-        * Since we allow the str_url to be a full local filesystem path, we are
-        * going to create a valid file:// URL if it's the case.
-        *
-        * Check if first character is a '/' or else reject the URL.
-        */
-       if (ctrl_url && ctrl_url[0] == '/') {
-               int ret;
-
-               ret = snprintf(url, sizeof(url), "file://%s", ctrl_url);
-               if (ret < 0) {
-                       PERROR("snprintf file url");
-                       goto parse_error;
-               } else if (ret >= sizeof(url)) {
-                       PERROR("snprintf file url is too long");
-                       goto parse_error;
-
-               }
-               ctrl_url = url;
-       }
-
-       /* Parse the control URL if there is one */
-       if (ctrl_url && *ctrl_url != '\0') {
-               ctrl_uri_count = uri_parse(ctrl_url, &ctrl_uris);
-               if (ctrl_uri_count < 1) {
-                       ERR("Unable to parse the URL %s", ctrl_url);
-                       goto parse_error;
-               }
-
-               /* 1 and 2 are the only expected values on success. */
-               LTTNG_ASSERT(ctrl_uri_count == 1 || ctrl_uri_count == 2);
-
-               /* At this point, we know there is at least one URI in the array */
-               set_default_uri_attr(&ctrl_uris[0], LTTNG_STREAM_CONTROL);
-
-               if (ctrl_uris[0].dtype == LTTNG_DST_PATH &&
-                               (data_url && *data_url != '\0')) {
-                       ERR("Cannot have a data URL when destination is file://");
-                       goto error;
-               }
-
-               /* URL are not equal but the control URL uses a net:// protocol */
-               if (ctrl_uri_count == 2) {
-                       if (!equal) {
-                               ERR("Control URL uses the net:// protocol and the data URL is "
-                                               "different. Not allowed.");
-                               goto error;
-                       } else {
-                               set_default_uri_attr(&ctrl_uris[1], LTTNG_STREAM_DATA);
-                               /*
-                                * The data_url and ctrl_url are equal and the ctrl_url
-                                * contains a net:// protocol so we just skip the data part.
-                                */
-                               data_url = NULL;
-                       }
-               }
-       }
-
-       if (data_url && *data_url != '\0') {
-               int ret;
-
-               /* We have to parse the data URL in this case */
-               data_uri_count = uri_parse(data_url, &data_uris);
-               if (data_uri_count < 1) {
-                       ERR("Unable to parse the URL %s", data_url);
-                       goto error;
-               } else if (data_uri_count == 2) {
-                       ERR("Data URL can not be set with the net[4|6]:// protocol");
-                       goto error;
-               } else {
-                       /* 1 and 2 are the only expected values on success. */
-                       LTTNG_ASSERT(data_uri_count == 1);
-               }
-
-               set_default_uri_attr(&data_uris[0], LTTNG_STREAM_DATA);
-
-               if (ctrl_uris) {
-                       ret = compare_destination(&ctrl_uris[0], &data_uris[0]);
-                       if (ret != 0) {
-                               ERR("Control and data destination mismatch");
-                               goto error;
-                       }
-               }
-       }
-
-       /* Compute total size. */
-       uri_count = ctrl_uri_count + data_uri_count;
-       if (uri_count <= 0) {
-               goto error;
-       }
-
-       tmp_uris = zmalloc(sizeof(struct lttng_uri) * uri_count);
-       if (tmp_uris == NULL) {
-               PERROR("zmalloc uris");
-               goto error;
-       }
-
-       if (ctrl_uris) {
-               /* It's possible the control URIs array contains more than one URI */
-               memcpy(tmp_uris, ctrl_uris, sizeof(struct lttng_uri) * ctrl_uri_count);
-               ++idx;
-               free(ctrl_uris);
-       }
-
-       if (data_uris) {
-               memcpy(&tmp_uris[idx], data_uris, sizeof(struct lttng_uri));
-               free(data_uris);
-       }
-
-       *uris = tmp_uris;
-
-       return uri_count;
-
-error:
-       free(ctrl_uris);
-       free(data_uris);
-       free(tmp_uris);
-parse_error:
-       return -1;
-}
diff --git a/src/common/uri.cpp b/src/common/uri.cpp
new file mode 100644 (file)
index 0000000..b1c2c63
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <arpa/inet.h>
+#include <common/compat/netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <common/common.h>
+#include <common/defaults.h>
+#include <common/utils.h>
+
+#include "uri.h"
+
+#define LOOPBACK_ADDR_IPV4 "127.0.0.1"
+#define LOOPBACK_ADDR_IPV6 "::1"
+
+enum uri_proto_code {
+       P_NET, P_NET6, P_FILE, P_TCP, P_TCP6,
+};
+
+struct uri_proto {
+       const char *name;
+       const char *leading_string;
+       enum uri_proto_code code;
+       enum lttng_proto_type type;
+       enum lttng_dst_type dtype;
+};
+
+/* Supported protocols */
+static const struct uri_proto proto_uri[] = {
+       { .name = "file", .leading_string = "file://", .code = P_FILE, .type = LTTNG_PROTO_TYPE_NONE, .dtype = LTTNG_DST_PATH },
+       { .name = "net", .leading_string = "net://", .code = P_NET, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
+       { .name = "net4", .leading_string = "net4://", .code = P_NET, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
+       { .name = "net6", .leading_string = "net6://", .code = P_NET6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
+       { .name = "tcp", .leading_string = "tcp://", .code = P_TCP, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
+       { .name = "tcp4", .leading_string = "tcp4://", .code = P_TCP, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
+       { .name = "tcp6", .leading_string = "tcp6://", .code = P_TCP6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
+       /* Invalid proto marking the end of the array. */
+       { 0 }
+};
+
+/*
+ * Return pointer to the character in s matching one of the characters in
+ * accept. If nothing is found, return pointer to the end of string (eos).
+ */
+static inline const char *strpbrk_or_eos(const char *s, const char *accept)
+{
+       char *p = (char *) strpbrk(s, accept);
+       if (p == NULL) {
+               p = (char *) strchr(s, '\0');
+       }
+
+       return p;
+}
+
+/*
+ * Validate if proto is a supported protocol from proto_uri array.
+ */
+static const struct uri_proto *get_uri_proto(const char *uri_str)
+{
+       const struct uri_proto *supported = NULL;
+
+       /* Safety net */
+       if (uri_str == NULL) {
+               goto end;
+       }
+
+       for (supported = &proto_uri[0];
+                       supported->leading_string != NULL; ++supported) {
+               if (strncasecmp(uri_str, supported->leading_string,
+                                       strlen(supported->leading_string)) == 0) {
+                       goto end;
+               }
+       }
+
+       /* Proto not found */
+       return NULL;
+
+end:
+       return supported;
+}
+
+/*
+ * Set network address from string into dst. Supports both IP string and
+ * hostname.
+ */
+static int set_ip_address(const char *addr, int af, char *dst, size_t size)
+{
+       int ret;
+       unsigned char buf[sizeof(struct in6_addr)];
+       struct hostent *record;
+
+       LTTNG_ASSERT(addr);
+       LTTNG_ASSERT(dst);
+
+       memset(dst, 0, size);
+
+       /* Network protocol */
+       ret = inet_pton(af, addr, buf);
+       if (ret < 1) {
+               /* We consider the dst to be an hostname or an invalid IP char */
+               record = lttng_gethostbyname2(addr, af);
+               if (record) {
+                       /* Translate IP to string */
+                       if (!inet_ntop(af, record->h_addr_list[0], dst, size)) {
+                               PERROR("inet_ntop");
+                               goto error;
+                       }
+               } else if (!strcmp(addr, "localhost") &&
+                               (af == AF_INET || af == AF_INET6)) {
+                       /*
+                        * Some systems may not have "localhost" defined in
+                        * accordance with IETF RFC 6761. According to this RFC,
+                        * applications may recognize "localhost" names as
+                        * special and resolve to the appropriate loopback
+                        * address.
+                        *
+                        * We choose to use the system name resolution API first
+                        * to honor its network configuration. If this fails, we
+                        * resolve to the appropriate loopback address. This is
+                        * done to accommodates systems which may want to start
+                        * tracing before their network configured.
+                        */
+                       const char *loopback_addr = af == AF_INET ?
+                                       LOOPBACK_ADDR_IPV4 : LOOPBACK_ADDR_IPV6;
+                       const size_t loopback_addr_len = af == AF_INET ?
+                                       sizeof(LOOPBACK_ADDR_IPV4) :
+                                       sizeof(LOOPBACK_ADDR_IPV6);
+
+                       DBG2("Could not resolve localhost address, using fallback");
+                       if (loopback_addr_len > size) {
+                               ERR("Could not resolve localhost address; destination string is too short");
+                               goto error;
+                       }
+                       strcpy(dst, loopback_addr);
+               } else {
+                       /* At this point, the IP or the hostname is bad */
+                       goto error;
+               }
+       } else {
+               if (size > 0) {
+                       strncpy(dst, addr, size);
+                       dst[size - 1] = '\0';
+               }
+       }
+
+       DBG2("IP address resolved to %s", dst);
+       return 0;
+
+error:
+       ERR("URI parse bad hostname %s for af %d", addr, af);
+       return -1;
+}
+
+/*
+ * Set default URI attribute which is basically the given stream type and the
+ * default port if none is set in the URI.
+ */
+static void set_default_uri_attr(struct lttng_uri *uri,
+               enum lttng_stream_type stype)
+{
+       uri->stype = stype;
+       if (uri->dtype != LTTNG_DST_PATH && uri->port == 0) {
+               uri->port = (stype == LTTNG_STREAM_CONTROL) ?
+                       DEFAULT_NETWORK_CONTROL_PORT : DEFAULT_NETWORK_DATA_PORT;
+       }
+}
+
+/*
+ * Compare two URL destination.
+ *
+ * Return 0 is equal else is not equal.
+ */
+static int compare_destination(struct lttng_uri *ctrl, struct lttng_uri *data)
+{
+       int ret;
+
+       LTTNG_ASSERT(ctrl);
+       LTTNG_ASSERT(data);
+
+       switch (ctrl->dtype) {
+       case LTTNG_DST_IPV4:
+               ret = strncmp(ctrl->dst.ipv4, data->dst.ipv4, sizeof(ctrl->dst.ipv4));
+               break;
+       case LTTNG_DST_IPV6:
+               ret = strncmp(ctrl->dst.ipv6, data->dst.ipv6, sizeof(ctrl->dst.ipv6));
+               break;
+       default:
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Build a string URL from a lttng_uri object.
+ */
+int uri_to_str_url(struct lttng_uri *uri, char *dst, size_t size)
+{
+       int ipver, ret;
+       const char *addr;
+       char proto[5], port[7];
+
+       LTTNG_ASSERT(uri);
+       LTTNG_ASSERT(dst);
+
+       if (uri->dtype == LTTNG_DST_PATH) {
+               ipver = 0;
+               addr = uri->dst.path;
+               (void) snprintf(proto, sizeof(proto), "file");
+               (void) snprintf(port, sizeof(port), "%s", "");
+       } else {
+               ipver = (uri->dtype == LTTNG_DST_IPV4) ? 4 : 6;
+               addr = (ipver == 4) ?  uri->dst.ipv4 : uri->dst.ipv6;
+               (void) snprintf(proto, sizeof(proto), "tcp%d", ipver);
+               (void) snprintf(port, sizeof(port), ":%d", uri->port);
+       }
+
+       ret = snprintf(dst, size, "%s://%s%s%s%s/%s", proto,
+                       (ipver == 6) ? "[" : "", addr, (ipver == 6) ? "]" : "",
+                       port, uri->subdir);
+       if (ret < 0) {
+               PERROR("snprintf uri to url");
+       }
+
+       return ret;
+}
+
+/*
+ * Compare two URIs.
+ *
+ * Return 0 if equal else 1.
+ */
+int uri_compare(struct lttng_uri *uri1, struct lttng_uri *uri2)
+{
+       return memcmp(uri1, uri2, sizeof(struct lttng_uri));
+}
+
+/*
+ * Free URI memory.
+ */
+void uri_free(struct lttng_uri *uri)
+{
+       free(uri);
+}
+
+/*
+ * Parses a string URI to a lttng_uri. This function can potentially return
+ * more than one URI in uris so the size of the array is returned and uris is
+ * allocated and populated. Caller must free(3) the array.
+ *
+ * This function can not detect the stream type of the URI so the caller has to
+ * make sure the correct type (stype) is set on the return URI(s). The default
+ * port must also be set by the caller if the returned URI has its port set to
+ * zero.
+ *
+ * NOTE: A good part of the following code was inspired from the "wget" source
+ * tree from the src/url.c file and url_parse() function. Also, the
+ * strpbrk_or_eos() function found above is also inspired by the same code.
+ * This code was originally licensed GPLv2 so we acknolwedge the Free Software
+ * Foundation here for the work and to make sure we are compliant with it.
+ */
+ssize_t uri_parse(const char *str_uri, struct lttng_uri **uris)
+{
+       int ret, i = 0;
+       /* Size of the uris array. Default is 1 */
+       ssize_t size = 1;
+       char subdir[PATH_MAX];
+       unsigned int ctrl_port = 0;
+       unsigned int data_port = 0;
+       struct lttng_uri *tmp_uris;
+       char *addr_f = NULL;
+       const struct uri_proto *proto;
+       const char *purl, *addr_e, *addr_b, *subdir_b = NULL;
+       const char *seps = ":/\0";
+
+       /*
+        * The first part is the protocol portion of a maximum of 5 bytes for now.
+        * The second part is the hostname or IP address. The 255 bytes size is the
+        * limit found in the RFC 1035 for the total length of a domain name
+        * (https://www.ietf.org/rfc/rfc1035.txt). Finally, for the net://
+        * protocol, two ports CAN be specified.
+        */
+
+       DBG3("URI string: %s", str_uri);
+
+       proto = get_uri_proto(str_uri);
+       if (proto == NULL) {
+               ERR("URI parse unknown protocol %s", str_uri);
+               goto error;
+       }
+
+       purl = str_uri;
+
+       if (proto->code == P_NET || proto->code == P_NET6) {
+               /* Special case for net:// which requires two URI objects */
+               size = 2;
+       }
+
+       /* Allocate URI array */
+       tmp_uris = (lttng_uri *) zmalloc(sizeof(struct lttng_uri) * size);
+       if (tmp_uris == NULL) {
+               PERROR("zmalloc uri");
+               goto error;
+       }
+
+       memset(subdir, 0, sizeof(subdir));
+       purl += strlen(proto->leading_string);
+
+       /* Copy known value to the first URI. */
+       tmp_uris[0].dtype = proto->dtype;
+       tmp_uris[0].proto = proto->type;
+
+       if (proto->code == P_FILE) {
+               if (*purl != '/') {
+                       ERR("Missing destination full path.");
+                       goto free_error;
+               }
+
+               strncpy(tmp_uris[0].dst.path, purl, sizeof(tmp_uris[0].dst.path));
+               tmp_uris[0].dst.path[sizeof(tmp_uris[0].dst.path) - 1] = '\0';
+               DBG3("URI file destination: %s", purl);
+               goto end;
+       }
+
+       /* Assume we are at the beginning of an address or host of some sort. */
+       addr_b = purl;
+
+       /*
+        * Handle IPv6 address inside square brackets as mention by RFC 2732. IPv6
+        * address that does not start AND end with brackets will be rejected even
+        * if valid.
+        *
+        * proto://[<addr>]...
+        *         ^
+        */
+       if (*purl == '[') {
+               /* Address begins after '[' */
+               addr_b = purl + 1;
+               addr_e = strchr(addr_b, ']');
+               if (addr_e == NULL || addr_b == addr_e) {
+                       ERR("Broken IPv6 address %s", addr_b);
+                       goto free_error;
+               }
+
+               /* Moving parsed URL pointer after the final bracket ']' */
+               purl = addr_e + 1;
+
+               /*
+                * The closing bracket must be followed by a seperator or NULL char.
+                */
+               if (strchr(seps, *purl) == NULL) {
+                       ERR("Unknown symbol after IPv6 address: %s", purl);
+                       goto free_error;
+               }
+       } else {
+               purl = strpbrk_or_eos(purl, seps);
+               addr_e = purl;
+       }
+
+       /* Check if we at least have a char for the addr or hostname. */
+       if (addr_b == addr_e) {
+               ERR("No address or hostname detected.");
+               goto free_error;
+       }
+
+       addr_f = utils_strdupdelim(addr_b, addr_e);
+       if (addr_f == NULL) {
+               goto free_error;
+       }
+
+       /*
+        * Detect PORT after address. The net/net6 protocol allows up to two port
+        * so we can define the control and data port.
+        */
+       while (*purl == ':') {
+               const char *port_b, *port_e;
+               char *port_f;
+
+               /* Update pass counter */
+               i++;
+
+               /*
+                * Maximum of two ports is possible if P_NET/NET6. Bigger than that,
+                * two much stuff.
+                */
+               if ((i == 2 && (proto->code != P_NET && proto->code != P_NET6))
+                               || i > 2) {
+                       break;
+               }
+
+               /*
+                * Move parsed URL to port value.
+                * proto://addr_host:PORT1:PORT2/foo/bar
+                *                   ^
+                */
+               ++purl;
+               port_b = purl;
+               purl = strpbrk_or_eos(purl, seps);
+               port_e = purl;
+
+               if (port_b != port_e) {
+                       int port;
+
+                       port_f = utils_strdupdelim(port_b, port_e);
+                       if (port_f == NULL) {
+                               goto free_error;
+                       }
+
+                       port = atoi(port_f);
+                       if (port > 0xffff || port <= 0x0) {
+                               ERR("Invalid port number %d", port);
+                               free(port_f);
+                               goto free_error;
+                       }
+                       free(port_f);
+
+                       if (i == 1) {
+                               ctrl_port = port;
+                       } else {
+                               data_port = port;
+                       }
+               }
+       };
+
+       /* Check for a valid subdir or trailing garbage */
+       if (*purl == '/') {
+               /*
+                * Move to subdir value.
+                * proto://addr_host:PORT1:PORT2/foo/bar
+                *                               ^
+                */
+               ++purl;
+               subdir_b = purl;
+       } else if (*purl != '\0') {
+               ERR("Trailing characters not recognized: %s", purl);
+               goto free_error;
+       }
+
+       /* We have enough valid information to create URI(s) object */
+
+       /* Copy generic information */
+       tmp_uris[0].port = ctrl_port;
+
+       /* Copy subdirectory if one. */
+       if (subdir_b) {
+               strncpy(tmp_uris[0].subdir, subdir_b, sizeof(tmp_uris[0].subdir));
+               tmp_uris[0].subdir[sizeof(tmp_uris[0].subdir) - 1] = '\0';
+       }
+
+       switch (proto->code) {
+       case P_NET:
+               ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4,
+                               sizeof(tmp_uris[0].dst.ipv4));
+               if (ret < 0) {
+                       goto free_error;
+               }
+
+               memcpy(tmp_uris[1].dst.ipv4, tmp_uris[0].dst.ipv4, sizeof(tmp_uris[1].dst.ipv4));
+
+               tmp_uris[1].dtype = proto->dtype;
+               tmp_uris[1].proto = proto->type;
+               tmp_uris[1].port = data_port;
+               break;
+       case P_NET6:
+               ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6,
+                               sizeof(tmp_uris[0].dst.ipv6));
+               if (ret < 0) {
+                       goto free_error;
+               }
+
+               memcpy(tmp_uris[1].dst.ipv6, tmp_uris[0].dst.ipv6, sizeof(tmp_uris[1].dst.ipv6));
+
+               tmp_uris[1].dtype = proto->dtype;
+               tmp_uris[1].proto = proto->type;
+               tmp_uris[1].port = data_port;
+               break;
+       case P_TCP:
+               ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4,
+                               sizeof(tmp_uris[0].dst.ipv4));
+               if (ret < 0) {
+                       goto free_error;
+               }
+               break;
+       case P_TCP6:
+               ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6,
+                               sizeof(tmp_uris[0].dst.ipv6));
+               if (ret < 0) {
+                       goto free_error;
+               }
+               break;
+       default:
+               goto free_error;
+       }
+
+end:
+       DBG3("URI dtype: %d, proto: %d, host: %s, subdir: %s, ctrl: %d, data: %d",
+                       proto->dtype, proto->type, (addr_f == NULL) ? "" : addr_f,
+                       (subdir_b == NULL) ? "" : subdir_b, ctrl_port, data_port);
+
+       free(addr_f);
+
+       *uris = tmp_uris;
+       LTTNG_ASSERT(size == 1 || size == 2);
+       return size;
+
+free_error:
+       free(addr_f);
+       free(tmp_uris);
+error:
+       return -1;
+}
+
+/*
+ * Parse a string URL and creates URI(s) returning the size of the populated
+ * array.
+ */
+ssize_t uri_parse_str_urls(const char *ctrl_url, const char *data_url,
+               struct lttng_uri **uris)
+{
+       unsigned int equal = 1, idx = 0;
+       /* Add the "file://" size to the URL maximum size */
+       char url[PATH_MAX + 7];
+       ssize_t ctrl_uri_count = 0, data_uri_count = 0, uri_count;
+       struct lttng_uri *ctrl_uris = NULL, *data_uris = NULL;
+       struct lttng_uri *tmp_uris = NULL;
+
+       /* No URL(s) is allowed. This means that the consumer will be disabled. */
+       if (ctrl_url == NULL && data_url == NULL) {
+               return 0;
+       }
+
+       /* Check if URLs are equal and if so, only use the control URL */
+       if ((ctrl_url && *ctrl_url != '\0') && (data_url && *data_url != '\0')) {
+               equal = !strcmp(ctrl_url, data_url);
+       }
+
+       /*
+        * Since we allow the str_url to be a full local filesystem path, we are
+        * going to create a valid file:// URL if it's the case.
+        *
+        * Check if first character is a '/' or else reject the URL.
+        */
+       if (ctrl_url && ctrl_url[0] == '/') {
+               int ret;
+
+               ret = snprintf(url, sizeof(url), "file://%s", ctrl_url);
+               if (ret < 0) {
+                       PERROR("snprintf file url");
+                       goto parse_error;
+               } else if (ret >= sizeof(url)) {
+                       PERROR("snprintf file url is too long");
+                       goto parse_error;
+
+               }
+               ctrl_url = url;
+       }
+
+       /* Parse the control URL if there is one */
+       if (ctrl_url && *ctrl_url != '\0') {
+               ctrl_uri_count = uri_parse(ctrl_url, &ctrl_uris);
+               if (ctrl_uri_count < 1) {
+                       ERR("Unable to parse the URL %s", ctrl_url);
+                       goto parse_error;
+               }
+
+               /* 1 and 2 are the only expected values on success. */
+               LTTNG_ASSERT(ctrl_uri_count == 1 || ctrl_uri_count == 2);
+
+               /* At this point, we know there is at least one URI in the array */
+               set_default_uri_attr(&ctrl_uris[0], LTTNG_STREAM_CONTROL);
+
+               if (ctrl_uris[0].dtype == LTTNG_DST_PATH &&
+                               (data_url && *data_url != '\0')) {
+                       ERR("Cannot have a data URL when destination is file://");
+                       goto error;
+               }
+
+               /* URL are not equal but the control URL uses a net:// protocol */
+               if (ctrl_uri_count == 2) {
+                       if (!equal) {
+                               ERR("Control URL uses the net:// protocol and the data URL is "
+                                               "different. Not allowed.");
+                               goto error;
+                       } else {
+                               set_default_uri_attr(&ctrl_uris[1], LTTNG_STREAM_DATA);
+                               /*
+                                * The data_url and ctrl_url are equal and the ctrl_url
+                                * contains a net:// protocol so we just skip the data part.
+                                */
+                               data_url = NULL;
+                       }
+               }
+       }
+
+       if (data_url && *data_url != '\0') {
+               int ret;
+
+               /* We have to parse the data URL in this case */
+               data_uri_count = uri_parse(data_url, &data_uris);
+               if (data_uri_count < 1) {
+                       ERR("Unable to parse the URL %s", data_url);
+                       goto error;
+               } else if (data_uri_count == 2) {
+                       ERR("Data URL can not be set with the net[4|6]:// protocol");
+                       goto error;
+               } else {
+                       /* 1 and 2 are the only expected values on success. */
+                       LTTNG_ASSERT(data_uri_count == 1);
+               }
+
+               set_default_uri_attr(&data_uris[0], LTTNG_STREAM_DATA);
+
+               if (ctrl_uris) {
+                       ret = compare_destination(&ctrl_uris[0], &data_uris[0]);
+                       if (ret != 0) {
+                               ERR("Control and data destination mismatch");
+                               goto error;
+                       }
+               }
+       }
+
+       /* Compute total size. */
+       uri_count = ctrl_uri_count + data_uri_count;
+       if (uri_count <= 0) {
+               goto error;
+       }
+
+       tmp_uris = (lttng_uri *) zmalloc(sizeof(struct lttng_uri) * uri_count);
+       if (tmp_uris == NULL) {
+               PERROR("zmalloc uris");
+               goto error;
+       }
+
+       if (ctrl_uris) {
+               /* It's possible the control URIs array contains more than one URI */
+               memcpy(tmp_uris, ctrl_uris, sizeof(struct lttng_uri) * ctrl_uri_count);
+               ++idx;
+               free(ctrl_uris);
+       }
+
+       if (data_uris) {
+               memcpy(&tmp_uris[idx], data_uris, sizeof(struct lttng_uri));
+               free(data_uris);
+       }
+
+       *uris = tmp_uris;
+
+       return uri_count;
+
+error:
+       free(ctrl_uris);
+       free(data_uris);
+       free(tmp_uris);
+parse_error:
+       return -1;
+}
index a270db9d927c021a84b59723afc8920b2948c536..3a02dda5c228f7b676f5ae966f27991afcb812ec 100644 (file)
@@ -44,6 +44,7 @@ enum lttng_stream_type {
  * should be ignored.
  */
 enum lttng_proto_type {
+       LTTNG_PROTO_TYPE_NONE                 = 0,
        LTTNG_TCP                             = 1,
        /*
         * UDP protocol is not supported for now.
diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c
deleted file mode 100644 (file)
index 084fec5..0000000
+++ /dev/null
@@ -1,2260 +0,0 @@
-/*
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "lttng/lttng-error.h"
-#include <common/compat/string.h>
-#include <common/align.h>
-#include <common/error.h>
-#include <common/hashtable/hashtable.h>
-#include <common/hashtable/utils.h>
-#include <common/macros.h>
-#include <common/mi-lttng.h>
-#include <common/payload-view.h>
-#include <common/payload.h>
-#include <fcntl.h>
-#include <lttng/constant.h>
-#include <lttng/userspace-probe-internal.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-static
-int lttng_userspace_probe_location_function_set_binary_fd_handle(
-               struct lttng_userspace_probe_location *location,
-               struct fd_handle *binary_fd_handle);
-
-static
-int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
-               struct lttng_userspace_probe_location *location,
-               struct fd_handle *binary_fd_handle);
-
-static
-enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize(
-               const struct lttng_userspace_probe_location_lookup_method
-                               *method,
-               struct mi_writer *writer);
-
-static
-enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct mi_writer *writer);
-
-static
-enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct mi_writer *writer);
-
-enum lttng_userspace_probe_location_lookup_method_type
-lttng_userspace_probe_location_lookup_method_get_type(
-               const struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       return lookup_method ? lookup_method->type :
-               LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_UNKNOWN;
-}
-
-void lttng_userspace_probe_location_lookup_method_destroy(
-               struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       if (!lookup_method){
-               return;
-       }
-
-       free(lookup_method);
-}
-
-struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_lookup_method_function_elf_create(void)
-{
-       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
-       struct lttng_userspace_probe_location_lookup_method_elf *elf_method;
-
-       elf_method = zmalloc(sizeof(*elf_method));
-       if (!elf_method) {
-               PERROR("zmalloc");
-               goto end;
-       }
-
-       ret = &elf_method->parent;
-       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF;
-end:
-       return ret;
-}
-
-struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(void)
-{
-       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
-       struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method;
-
-       sdt_method = zmalloc(sizeof(*sdt_method));
-       if (!sdt_method) {
-               PERROR("zmalloc");
-               goto end;
-       }
-
-       ret = &sdt_method->parent;
-       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT;
-end:
-       return ret;
-}
-
-enum lttng_userspace_probe_location_type lttng_userspace_probe_location_get_type(
-               const struct lttng_userspace_probe_location *location)
-{
-       return location ? location->type :
-               LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN;
-}
-
-static
-void lttng_userspace_probe_location_function_destroy(
-               struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location_function *location_function = NULL;
-
-       LTTNG_ASSERT(location);
-
-       location_function = container_of(location,
-                       struct lttng_userspace_probe_location_function, parent);
-
-       LTTNG_ASSERT(location_function);
-
-       free(location_function->function_name);
-       free(location_function->binary_path);
-       fd_handle_put(location_function->binary_fd_handle);
-       free(location);
-}
-
-static
-void lttng_userspace_probe_location_tracepoint_destroy(
-               struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location_tracepoint *location_tracepoint = NULL;
-
-       LTTNG_ASSERT(location);
-
-       location_tracepoint = container_of(location,
-                       struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-
-       LTTNG_ASSERT(location_tracepoint);
-
-       free(location_tracepoint->probe_name);
-       free(location_tracepoint->provider_name);
-       free(location_tracepoint->binary_path);
-       fd_handle_put(location_tracepoint->binary_fd_handle);
-       free(location);
-}
-
-void lttng_userspace_probe_location_destroy(
-               struct lttng_userspace_probe_location *location)
-{
-       if (!location) {
-               return;
-       }
-
-       lttng_userspace_probe_location_lookup_method_destroy(
-                       location->lookup_method);
-
-       switch (location->type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-               lttng_userspace_probe_location_function_destroy(location);
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-               lttng_userspace_probe_location_tracepoint_destroy(location);
-               break;
-       default:
-               abort();
-       }
-}
-
-/* Compare two file descriptors based on their inode and device numbers. */
-static bool fd_is_equal(int a, int b)
-{
-       int ret;
-       bool is_equal = false;
-       struct stat a_stat, b_stat;
-
-       if (a < 0 && b >= 0) {
-               goto end;
-       }
-
-       if (b < 0 && a >= 0) {
-               goto end;
-       }
-
-       if (a < 0 && b < 0) {
-               if (a == -1 && b == -1) {
-                       is_equal = true;
-                       goto end;
-               }
-
-               /* Invalid state, abort. */
-               abort();
-       }
-
-       /* Both are valid file descriptors. */
-       ret = fstat(a, &a_stat);
-       if (ret) {
-               PERROR("Failed to fstat userspace probe location binary fd %d",
-                               a);
-               goto end;
-       }
-
-       ret = fstat(b, &b_stat);
-       if (ret) {
-               PERROR("Failed to fstat userspace probe location binary fd %d",
-                               b);
-               goto end;
-       }
-
-       is_equal = (a_stat.st_ino == b_stat.st_ino) &&
-                       (a_stat.st_dev == b_stat.st_dev);
-
-end:
-       return is_equal;
-}
-
-static unsigned long lttng_userspace_probe_location_function_hash(
-               const struct lttng_userspace_probe_location *location)
-{
-       unsigned long hash = hash_key_ulong(
-                       (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION,
-                       lttng_ht_seed);
-       struct lttng_userspace_probe_location_function *function_location =
-                       container_of(location, typeof(*function_location),
-                                       parent);
-
-       hash ^= hash_key_str(function_location->function_name, lttng_ht_seed);
-       hash ^= hash_key_str(function_location->binary_path, lttng_ht_seed);
-       /*
-        * No need to hash on the fd. Worst comes to worse,
-        * the equal function will discriminate.
-        */
-       return hash;
-}
-
-static bool lttng_userspace_probe_location_function_is_equal(
-               const struct lttng_userspace_probe_location *_a,
-               const struct lttng_userspace_probe_location *_b)
-{
-       bool is_equal = false;
-       struct lttng_userspace_probe_location_function *a, *b;
-
-       a = container_of(_a, struct lttng_userspace_probe_location_function,
-                       parent);
-       b = container_of(_b, struct lttng_userspace_probe_location_function,
-                       parent);
-
-       if (a->instrumentation_type != b->instrumentation_type) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->function_name);
-       LTTNG_ASSERT(b->function_name);
-       if (strcmp(a->function_name, b->function_name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->binary_path);
-       LTTNG_ASSERT(b->binary_path);
-       if (strcmp(a->binary_path, b->binary_path)) {
-               goto end;
-       }
-
-       is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1,
-                       b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1);
-end:
-       return is_equal;
-}
-
-static struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_function_create_no_check(const char *binary_path,
-               const char *function_name,
-               struct lttng_userspace_probe_location_lookup_method *lookup_method,
-               bool open_binary)
-{
-       int binary_fd = -1;
-       struct fd_handle *binary_fd_handle = NULL;
-       char *function_name_copy = NULL, *binary_path_copy = NULL;
-       struct lttng_userspace_probe_location *ret = NULL;
-       struct lttng_userspace_probe_location_function *location;
-
-       if (open_binary) {
-               binary_fd = open(binary_path, O_RDONLY);
-               if (binary_fd < 0) {
-                       PERROR("Error opening the binary");
-                       goto error;
-               }
-
-               binary_fd_handle = fd_handle_create(binary_fd);
-               if (!binary_fd) {
-                       goto error;
-               }
-
-               /* Ownership transferred to fd_handle. */
-               binary_fd = -1;
-       }
-
-       function_name_copy = lttng_strndup(function_name, LTTNG_SYMBOL_NAME_LEN);
-       if (!function_name_copy) {
-               PERROR("Error duplicating the function name");
-               goto error;
-       }
-
-       binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX);
-       if (!binary_path_copy) {
-               PERROR("Error duplicating the function name");
-               goto error;
-       }
-
-       location = zmalloc(sizeof(*location));
-       if (!location) {
-               PERROR("Error allocating userspace probe location");
-               goto error;
-       }
-
-       location->function_name = function_name_copy;
-       location->binary_path = binary_path_copy;
-       location->binary_fd_handle = binary_fd_handle;
-       binary_fd_handle = NULL;
-       location->instrumentation_type =
-                       LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY;
-
-       ret = &location->parent;
-       ret->lookup_method = lookup_method;
-       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION;
-       ret->equal = lttng_userspace_probe_location_function_is_equal;
-       ret->hash = lttng_userspace_probe_location_function_hash;
-       goto end;
-
-error:
-       free(function_name_copy);
-       free(binary_path_copy);
-       if (binary_fd >= 0) {
-               if (close(binary_fd)) {
-                       PERROR("Error closing binary fd in error path");
-               }
-       }
-       fd_handle_put(binary_fd_handle);
-end:
-       return ret;
-}
-
-static unsigned long lttng_userspace_probe_location_tracepoint_hash(
-               const struct lttng_userspace_probe_location *location)
-{
-       unsigned long hash = hash_key_ulong(
-                       (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT,
-                       lttng_ht_seed);
-       struct lttng_userspace_probe_location_tracepoint *tp_location =
-                       container_of(location, typeof(*tp_location), parent);
-
-       hash ^= hash_key_str(tp_location->probe_name, lttng_ht_seed);
-       hash ^= hash_key_str(tp_location->provider_name, lttng_ht_seed);
-       hash ^= hash_key_str(tp_location->binary_path, lttng_ht_seed);
-       /*
-        * No need to hash on the fd. Worst comes to worse,
-        * the equal function will discriminate.
-        */
-       return hash;
-}
-
-static bool lttng_userspace_probe_location_tracepoint_is_equal(
-               const struct lttng_userspace_probe_location *_a,
-               const struct lttng_userspace_probe_location *_b)
-{
-       bool is_equal = false;
-       struct lttng_userspace_probe_location_tracepoint *a, *b;
-
-       a = container_of(_a, struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-       b = container_of(_b, struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-
-       LTTNG_ASSERT(a->probe_name);
-       LTTNG_ASSERT(b->probe_name);
-       if (strcmp(a->probe_name, b->probe_name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->provider_name);
-       LTTNG_ASSERT(b->provider_name);
-       if (strcmp(a->provider_name, b->provider_name)) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(a->binary_path);
-       LTTNG_ASSERT(b->binary_path);
-       if (strcmp(a->binary_path, b->binary_path)) {
-               goto end;
-       }
-
-       is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1,
-                       b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1);
-
-end:
-       return is_equal;
-}
-
-static struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path,
-               const char *provider_name, const char *probe_name,
-               struct lttng_userspace_probe_location_lookup_method *lookup_method,
-               bool open_binary)
-{
-       int binary_fd = -1;
-       struct fd_handle *binary_fd_handle = NULL;
-       char *probe_name_copy = NULL;
-       char *provider_name_copy = NULL;
-       char *binary_path_copy = NULL;
-       struct lttng_userspace_probe_location *ret = NULL;
-       struct lttng_userspace_probe_location_tracepoint *location;
-
-       if (open_binary) {
-               binary_fd = open(binary_path, O_RDONLY);
-               if (binary_fd < 0) {
-                       PERROR("open");
-                       goto error;
-               }
-
-               binary_fd_handle = fd_handle_create(binary_fd);
-               if (!binary_fd) {
-                       goto error;
-               }
-
-               /* Ownership transferred to fd_handle. */
-               binary_fd = -1;
-       }
-
-       probe_name_copy = lttng_strndup(probe_name, LTTNG_SYMBOL_NAME_LEN);
-       if (!probe_name_copy) {
-               PERROR("lttng_strndup");
-               goto error;
-       }
-
-       provider_name_copy = lttng_strndup(provider_name, LTTNG_SYMBOL_NAME_LEN);
-       if (!provider_name_copy) {
-               PERROR("lttng_strndup");
-               goto error;
-       }
-
-       binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX);
-       if (!binary_path_copy) {
-               PERROR("lttng_strndup");
-               goto error;
-       }
-
-       location = zmalloc(sizeof(*location));
-       if (!location) {
-               PERROR("zmalloc");
-               goto error;
-       }
-
-       location->probe_name = probe_name_copy;
-       location->provider_name = provider_name_copy;
-       location->binary_path = binary_path_copy;
-       location->binary_fd_handle = binary_fd_handle;
-       binary_fd_handle = NULL;
-
-       ret = &location->parent;
-       ret->lookup_method = lookup_method;
-       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT;
-       ret->equal = lttng_userspace_probe_location_tracepoint_is_equal;
-       ret->hash = lttng_userspace_probe_location_tracepoint_hash;
-       goto end;
-
-error:
-       free(probe_name_copy);
-       free(provider_name_copy);
-       free(binary_path_copy);
-       if (binary_fd >= 0) {
-               if (close(binary_fd)) {
-                       PERROR("Error closing binary fd in error path");
-               }
-       }
-       fd_handle_put(binary_fd_handle);
-end:
-       return ret;
-}
-
-struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_function_create(const char *binary_path,
-               const char *function_name,
-               struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       struct lttng_userspace_probe_location *ret = NULL;
-
-       if (!binary_path || !function_name) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       switch (lttng_userspace_probe_location_lookup_method_get_type(
-                       lookup_method)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               break;
-       default:
-               /* Invalid probe location lookup method. */
-               goto end;
-       }
-
-       ret = lttng_userspace_probe_location_function_create_no_check(
-                       binary_path, function_name, lookup_method, true);
-end:
-       return ret;
-}
-
-struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_tracepoint_create(const char *binary_path,
-               const char *provider_name, const char *probe_name,
-               struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       struct lttng_userspace_probe_location *ret = NULL;
-
-       if (!binary_path || !probe_name || !provider_name) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       switch (lttng_userspace_probe_location_lookup_method_get_type(
-                       lookup_method)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               break;
-       default:
-               /* Invalid probe location lookup method. */
-               goto end;
-       }
-
-       ret = lttng_userspace_probe_location_tracepoint_create_no_check(
-                       binary_path, provider_name, probe_name, lookup_method, true);
-end:
-       return ret;
-}
-
-static struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_lookup_method_function_elf_copy(
-               const struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       struct lttng_userspace_probe_location_lookup_method *parent = NULL;
-       struct lttng_userspace_probe_location_lookup_method_elf *elf_method;
-
-       LTTNG_ASSERT(lookup_method);
-       LTTNG_ASSERT(lookup_method->type ==
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF);
-
-       elf_method = zmalloc(sizeof(*elf_method));
-       if (!elf_method) {
-               PERROR("Error allocating ELF userspace probe lookup method");
-               goto error;
-       }
-
-       elf_method->parent.type = lookup_method->type;
-       parent = &elf_method->parent;
-
-       goto end;
-error:
-       parent = NULL;
-end:
-       return parent;
-}
-
-static struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy(
-               struct lttng_userspace_probe_location_lookup_method *lookup_method)
-{
-       struct lttng_userspace_probe_location_lookup_method *parent = NULL;
-       struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method;
-
-       LTTNG_ASSERT(lookup_method);
-       LTTNG_ASSERT(lookup_method->type ==
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT);
-
-       sdt_method = zmalloc(sizeof(*sdt_method));
-       if (!sdt_method) {
-               PERROR("zmalloc");
-               goto error;
-       }
-
-       sdt_method->parent.type = lookup_method->type;
-       parent = &sdt_method->parent;
-
-       goto end;
-
-error:
-       parent = NULL;
-end:
-       return parent;
-}
-
-static struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_function_copy(
-               const struct lttng_userspace_probe_location *location)
-{
-       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
-       struct lttng_userspace_probe_location *new_location = NULL;
-       struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
-       const char *binary_path = NULL;
-       const char *function_name = NULL;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
-       function_location = container_of(
-                       location, typeof(*function_location), parent);
-
-        /* Get probe location fields */
-       binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
-       if (!binary_path) {
-               ERR("Userspace probe binary path is NULL");
-               goto error;
-       }
-
-       function_name = lttng_userspace_probe_location_function_get_function_name(location);
-       if (!function_name) {
-               ERR("Userspace probe function name is NULL");
-               goto error;
-       }
-
-       /*
-        * Duplicate probe location method fields
-        */
-       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(
-                       location->lookup_method);
-       switch (lookup_type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               lookup_method =
-                       lttng_userspace_probe_location_lookup_method_function_elf_copy(
-                                       location->lookup_method);
-               if (!lookup_method) {
-                       goto error;
-               }
-               break;
-       default:
-               /* Invalid probe location lookup method. */
-               goto error;
-       }
-
-       /* Create the probe_location */
-       new_location = lttng_userspace_probe_location_function_create_no_check(
-                       binary_path, function_name, lookup_method, false);
-       if (!new_location) {
-               goto destroy_lookup_method;
-       }
-
-       /* Set the duplicated fd to the new probe_location */
-       if (lttng_userspace_probe_location_function_set_binary_fd_handle(new_location,
-                       function_location->binary_fd_handle) < 0) {
-               goto destroy_probe_location;
-       }
-
-       goto end;
-
-destroy_probe_location:
-       lttng_userspace_probe_location_destroy(new_location);
-destroy_lookup_method:
-       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
-error:
-       new_location = NULL;
-end:
-       return new_location;
-}
-
-static struct lttng_userspace_probe_location *
-lttng_userspace_probe_location_tracepoint_copy(
-               const struct lttng_userspace_probe_location *location)
-{
-       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
-       struct lttng_userspace_probe_location *new_location = NULL;
-       struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
-       const char *binary_path = NULL;
-       const char *probe_name = NULL;
-       const char *provider_name = NULL;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
-       tracepoint_location = container_of(
-                       location, typeof(*tracepoint_location), parent);
-
-       /* Get probe location fields */
-       binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location);
-       if (!binary_path) {
-               ERR("Userspace probe binary path is NULL");
-               goto error;
-       }
-
-       probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location);
-       if (!probe_name) {
-               ERR("Userspace probe probe name is NULL");
-               goto error;
-       }
-
-       provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location);
-       if (!provider_name) {
-               ERR("Userspace probe provider name is NULL");
-               goto error;
-       }
-
-       /*
-        * Duplicate probe location method fields
-        */
-       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(
-                       location->lookup_method);
-       switch (lookup_type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               lookup_method =
-                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy(
-                                       location->lookup_method);
-               if (!lookup_method) {
-                       goto error;
-               }
-               break;
-       default:
-               /* Invalid probe location lookup method. */
-               goto error;
-       }
-
-       /* Create the probe_location */
-       new_location = lttng_userspace_probe_location_tracepoint_create_no_check(
-                       binary_path, provider_name, probe_name, lookup_method, false);
-       if (!new_location) {
-               goto destroy_lookup_method;
-       }
-
-       /* Set the duplicated fd to the new probe_location */
-       if (lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(new_location,
-                       tracepoint_location->binary_fd_handle) < 0) {
-               goto destroy_probe_location;
-       }
-
-       goto end;
-
-destroy_probe_location:
-       lttng_userspace_probe_location_destroy(new_location);
-destroy_lookup_method:
-       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
-error:
-       new_location = NULL;
-end:
-       return new_location;
-}
-
-const char *lttng_userspace_probe_location_function_get_binary_path(
-               const struct lttng_userspace_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       function_location = container_of(location,
-               struct lttng_userspace_probe_location_function,
-                       parent);
-       ret = function_location->binary_path;
-end:
-       return ret;
-}
-
-const char *lttng_userspace_probe_location_tracepoint_get_binary_path(
-               const struct lttng_userspace_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       tracepoint_location = container_of(location,
-               struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-       ret = tracepoint_location->binary_path;
-end:
-       return ret;
-}
-
-const char *lttng_userspace_probe_location_function_get_function_name(
-               const struct lttng_userspace_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       function_location = container_of(location,
-               struct lttng_userspace_probe_location_function, parent);
-       ret = function_location->function_name;
-end:
-       return ret;
-}
-
-const char *lttng_userspace_probe_location_tracepoint_get_probe_name(
-               const struct lttng_userspace_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       tracepoint_location = container_of(location,
-               struct lttng_userspace_probe_location_tracepoint, parent);
-       ret = tracepoint_location->probe_name;
-end:
-       return ret;
-}
-
-const char *lttng_userspace_probe_location_tracepoint_get_provider_name(
-               const struct lttng_userspace_probe_location *location)
-{
-       const char *ret = NULL;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       tracepoint_location = container_of(location,
-               struct lttng_userspace_probe_location_tracepoint, parent);
-       ret = tracepoint_location->provider_name;
-end:
-       return ret;
-}
-
-int lttng_userspace_probe_location_function_get_binary_fd(
-               const struct lttng_userspace_probe_location *location)
-{
-       int ret = -1;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       function_location = container_of(location,
-               struct lttng_userspace_probe_location_function, parent);
-       ret = function_location->binary_fd_handle ?
-                       fd_handle_get_fd(function_location->binary_fd_handle) : -1;
-end:
-       return ret;
-}
-
-enum lttng_userspace_probe_location_function_instrumentation_type
-lttng_userspace_probe_location_function_get_instrumentation_type(
-               const struct lttng_userspace_probe_location *location)
-{
-       enum lttng_userspace_probe_location_function_instrumentation_type type;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN;
-               goto end;
-       }
-
-       function_location = container_of(location,
-               struct lttng_userspace_probe_location_function, parent);
-       type = function_location->instrumentation_type;
-end:
-       return type;
-}
-
-enum lttng_userspace_probe_location_status
-lttng_userspace_probe_location_function_set_instrumentation_type(
-               const struct lttng_userspace_probe_location *location,
-               enum lttng_userspace_probe_location_function_instrumentation_type instrumentation_type)
-{
-       enum lttng_userspace_probe_location_status status =
-                       LTTNG_USERSPACE_PROBE_LOCATION_STATUS_OK;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION ||
-                       instrumentation_type !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID;
-               goto end;
-       }
-
-       function_location = container_of(location,
-               struct lttng_userspace_probe_location_function, parent);
-       function_location->instrumentation_type = instrumentation_type;
-end:
-       return status;
-}
-
-int lttng_userspace_probe_location_tracepoint_get_binary_fd(
-               const struct lttng_userspace_probe_location *location)
-{
-       int ret = -1;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       tracepoint_location = container_of(location,
-               struct lttng_userspace_probe_location_tracepoint, parent);
-       ret = tracepoint_location->binary_fd_handle ?
-                       fd_handle_get_fd(tracepoint_location->binary_fd_handle) : -1;
-end:
-       return ret;
-}
-
-static struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_function_get_lookup_method(
-               const struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       ret = location->lookup_method;
-end:
-       return ret;
-}
-
-static struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_tracepoint_get_lookup_method(
-               const struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
-
-       if (!location || lttng_userspace_probe_location_get_type(location) !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               goto end;
-       }
-
-       ret = location->lookup_method;
-end:
-       return ret;
-}
-
-const struct lttng_userspace_probe_location_lookup_method *
-lttng_userspace_probe_location_get_lookup_method(
-               const struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
-
-       LTTNG_ASSERT(location);
-       switch (location->type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-       ret = lttng_userspace_probe_location_function_get_lookup_method(
-                       location);
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-       ret = lttng_userspace_probe_location_tracepoint_get_lookup_method(
-                       location);
-               break;
-       default:
-               ERR("Unknowned lookup method.");
-               break;
-       }
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_lookup_method_serialize(
-               struct lttng_userspace_probe_location_lookup_method *method,
-               struct lttng_payload *payload)
-{
-       int ret;
-       struct lttng_userspace_probe_location_lookup_method_comm
-                       lookup_method_comm;
-
-       lookup_method_comm.type = (int8_t) (method ? method->type :
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT);
-       if (payload) {
-               ret = lttng_dynamic_buffer_append(&payload->buffer, &lookup_method_comm,
-                               sizeof(lookup_method_comm));
-               if (ret) {
-                       goto end;
-               }
-       }
-       ret = sizeof(lookup_method_comm);
-end:
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_function_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t function_name_len, binary_path_len;
-       struct lttng_userspace_probe_location_function *location_function;
-       struct lttng_userspace_probe_location_function_comm location_function_comm;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) ==
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
-
-       location_function = container_of(location,
-                       struct lttng_userspace_probe_location_function,
-                       parent);
-       if (!location_function->function_name || !location_function->binary_path) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (payload && !location_function->binary_fd_handle) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       function_name_len = strlen(location_function->function_name);
-       if (function_name_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-       binary_path_len = strlen(location_function->binary_path);
-       if (binary_path_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_function_comm.function_name_len = function_name_len + 1;
-       location_function_comm.binary_path_len = binary_path_len + 1;
-
-       if (payload) {
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               &location_function_comm,
-                               sizeof(location_function_comm));
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               location_function->function_name,
-                               location_function_comm.function_name_len);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               location_function->binary_path,
-                               location_function_comm.binary_path_len);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_payload_push_fd_handle(
-                               payload, location_function->binary_fd_handle);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-       }
-       ret = sizeof(location_function_comm) +
-                       location_function_comm.function_name_len +
-                       location_function_comm.binary_path_len;
-end:
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_tracepoint_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret;
-       size_t probe_name_len, provider_name_len, binary_path_len;
-       struct lttng_userspace_probe_location_tracepoint *location_tracepoint;
-       struct lttng_userspace_probe_location_tracepoint_comm location_tracepoint_comm;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) ==
-                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
-
-       location_tracepoint = container_of(location,
-                       struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-       if (!location_tracepoint->probe_name ||
-                       !location_tracepoint->provider_name ||
-                       !location_tracepoint->binary_path) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (payload && !location_tracepoint->binary_fd_handle) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_name_len = strlen(location_tracepoint->probe_name);
-       if (probe_name_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       provider_name_len = strlen(location_tracepoint->provider_name);
-       if (provider_name_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       binary_path_len = strlen(location_tracepoint->binary_path);
-       if (binary_path_len == 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_tracepoint_comm.probe_name_len = probe_name_len + 1;
-       location_tracepoint_comm.provider_name_len = provider_name_len + 1;
-       location_tracepoint_comm.binary_path_len = binary_path_len + 1;
-
-       if (payload) {
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               &location_tracepoint_comm,
-                               sizeof(location_tracepoint_comm));
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               location_tracepoint->probe_name,
-                               location_tracepoint_comm.probe_name_len);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               location_tracepoint->provider_name,
-                               location_tracepoint_comm.provider_name_len);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               location_tracepoint->binary_path,
-                               location_tracepoint_comm.binary_path_len);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               ret = lttng_payload_push_fd_handle(
-                               payload, location_tracepoint->binary_fd_handle);
-               if (ret) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-       }
-
-       ret = sizeof(location_tracepoint_comm) +
-                       location_tracepoint_comm.probe_name_len +
-                       location_tracepoint_comm.provider_name_len +
-                       location_tracepoint_comm.binary_path_len;
-end:
-       return ret;
-}
-
-int lttng_userspace_probe_location_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_payload *payload)
-{
-       int ret, buffer_use = 0;
-       struct lttng_userspace_probe_location_comm location_generic_comm;
-
-       if (!location) {
-               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       memset(&location_generic_comm, 0, sizeof(location_generic_comm));
-
-       location_generic_comm.type = (int8_t) location->type;
-       if (payload) {
-               ret = lttng_dynamic_buffer_append(&payload->buffer,
-                               &location_generic_comm,
-                               sizeof(location_generic_comm));
-               if (ret) {
-                       goto end;
-               }
-       }
-       buffer_use += sizeof(location_generic_comm);
-
-       switch (lttng_userspace_probe_location_get_type(location)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-               ret = lttng_userspace_probe_location_function_serialize(
-                               location, payload);
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-               ret = lttng_userspace_probe_location_tracepoint_serialize(
-                               location, payload);
-               break;
-       default:
-               ERR("Unsupported probe location type");
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-       if (ret < 0) {
-               goto end;
-       }
-       buffer_use += ret;
-
-       ret = lttng_userspace_probe_location_lookup_method_serialize(
-                       location->lookup_method, payload);
-       if (ret < 0) {
-               goto end;
-       }
-       ret += buffer_use;
-end:
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_function_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_userspace_probe_location **location)
-{
-       struct lttng_userspace_probe_location_function_comm *location_function_comm;
-       const char *function_name_src, *binary_path_src;
-       char *function_name = NULL, *binary_path = NULL;
-       int ret = 0;
-       size_t expected_size;
-       struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view);
-
-       LTTNG_ASSERT(location);
-
-       if (view->buffer.size < sizeof(*location_function_comm)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_function_comm =
-                       (typeof(location_function_comm)) view->buffer.data;
-
-       expected_size = sizeof(*location_function_comm) +
-                       location_function_comm->function_name_len +
-                       location_function_comm->binary_path_len;
-
-       if (view->buffer.size < expected_size) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       function_name_src = view->buffer.data + sizeof(*location_function_comm);
-       binary_path_src = function_name_src +
-                       location_function_comm->function_name_len;
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, function_name_src,
-                           location_function_comm->function_name_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src,
-                           location_function_comm->binary_path_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       function_name = lttng_strndup(function_name_src, LTTNG_SYMBOL_NAME_LEN);
-       if (!function_name) {
-               PERROR("lttng_strndup");
-               ret = -LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX);
-       if (!binary_path) {
-               PERROR("lttng_strndup");
-               ret = -LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       *location = lttng_userspace_probe_location_function_create_no_check(
-                       binary_path, function_name, NULL, false);
-       if (!(*location)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = lttng_userspace_probe_location_function_set_binary_fd_handle(
-                       *location, binary_fd_handle);
-       if (ret) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (int) expected_size;
-end:
-       fd_handle_put(binary_fd_handle);
-       free(function_name);
-       free(binary_path);
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_tracepoint_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_userspace_probe_location **location)
-{
-       struct lttng_userspace_probe_location_tracepoint_comm *location_tracepoint_comm;
-       const char *probe_name_src, *provider_name_src, *binary_path_src;
-       char *probe_name = NULL, *provider_name = NULL, *binary_path = NULL;
-       int ret = 0;
-       size_t expected_size;
-       struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view);
-
-       LTTNG_ASSERT(location);
-
-       if (!binary_fd_handle) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (view->buffer.size < sizeof(*location_tracepoint_comm)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       location_tracepoint_comm =
-                       (typeof(location_tracepoint_comm)) view->buffer.data;
-
-       expected_size = sizeof(*location_tracepoint_comm) +
-                       location_tracepoint_comm->probe_name_len +
-                       location_tracepoint_comm->provider_name_len +
-                       location_tracepoint_comm->binary_path_len;
-
-       if (view->buffer.size < expected_size) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_name_src = view->buffer.data + sizeof(*location_tracepoint_comm);
-       provider_name_src = probe_name_src +
-                       location_tracepoint_comm->probe_name_len;
-       binary_path_src = provider_name_src +
-                       location_tracepoint_comm->provider_name_len;
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, probe_name_src,
-                           location_tracepoint_comm->probe_name_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, provider_name_src,
-                           location_tracepoint_comm->provider_name_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src,
-                           location_tracepoint_comm->binary_path_len)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_name = lttng_strndup(probe_name_src, LTTNG_SYMBOL_NAME_LEN);
-       if (!probe_name) {
-               PERROR("Failed to allocate probe name");
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-       provider_name = lttng_strndup(provider_name_src, LTTNG_SYMBOL_NAME_LEN);
-       if (!provider_name) {
-               PERROR("Failed to allocate provider name");
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX);
-       if (!binary_path) {
-               PERROR("Failed to allocate binary path");
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       *location = lttng_userspace_probe_location_tracepoint_create_no_check(
-                       binary_path, provider_name, probe_name, NULL, false);
-       if (!(*location)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
-                       *location, binary_fd_handle);
-       if (ret) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = (int) expected_size;
-end:
-       fd_handle_put(binary_fd_handle);
-       free(probe_name);
-       free(provider_name);
-       free(binary_path);
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_lookup_method_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_userspace_probe_location_lookup_method **lookup_method)
-{
-       int ret;
-       struct lttng_userspace_probe_location_lookup_method_comm *lookup_comm;
-       enum lttng_userspace_probe_location_lookup_method_type type;
-
-       LTTNG_ASSERT(view);
-       LTTNG_ASSERT(lookup_method);
-
-       if (view->buffer.size < sizeof(*lookup_comm)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       lookup_comm = (typeof(lookup_comm)) view->buffer.data;
-       type = (enum lttng_userspace_probe_location_lookup_method_type)
-                       lookup_comm->type;
-       switch (type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
-               *lookup_method = NULL;
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               *lookup_method =
-                       lttng_userspace_probe_location_lookup_method_function_elf_create();
-               if (!(*lookup_method)) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               *lookup_method =
-                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
-               if (!(*lookup_method)) {
-                       ret = -LTTNG_ERR_INVALID;
-                       goto end;
-               }
-               break;
-       default:
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       ret = sizeof(*lookup_comm);
-end:
-       return ret;
-}
-
-int lttng_userspace_probe_location_create_from_payload(
-               struct lttng_payload_view *view,
-               struct lttng_userspace_probe_location **location)
-{
-       struct lttng_userspace_probe_location_lookup_method *lookup_method;
-       enum lttng_userspace_probe_location_type type;
-       int consumed = 0;
-       int ret;
-       struct lttng_userspace_probe_location_comm *probe_location_comm;
-       struct lttng_payload_view probe_location_comm_view =
-                       lttng_payload_view_from_view(
-                                       view, 0, sizeof(*probe_location_comm));
-
-       LTTNG_ASSERT(view);
-       LTTNG_ASSERT(location);
-
-       lookup_method = NULL;
-
-       if (!lttng_payload_view_is_valid(&probe_location_comm_view)) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data;
-       type = (enum lttng_userspace_probe_location_type) probe_location_comm->type;
-       consumed += sizeof(*probe_location_comm);
-
-       switch (type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-       {
-               struct lttng_payload_view location_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed, -1);
-
-               ret = lttng_userspace_probe_location_function_create_from_payload(
-                               &location_view, location);
-               if (ret < 0) {
-                       goto end;
-               }
-               break;
-       }
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-       {
-               struct lttng_payload_view location_view =
-                               lttng_payload_view_from_view(view, consumed, -1);
-
-               ret = lttng_userspace_probe_location_tracepoint_create_from_payload(
-                               &location_view, location);
-               if (ret < 0) {
-                       goto end;
-               }
-               break;
-       }
-       default:
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       consumed += ret;
-       if (view->buffer.size <= consumed) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       {
-               struct lttng_payload_view lookup_method_view =
-                               lttng_payload_view_from_view(
-                                               view, consumed, -1);
-
-               ret = lttng_userspace_probe_location_lookup_method_create_from_payload(
-                               &lookup_method_view, &lookup_method);
-       }
-       if (ret < 0) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       LTTNG_ASSERT(lookup_method);
-       (*location)->lookup_method = lookup_method;
-       lookup_method = NULL;
-       ret += consumed;
-end:
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_function_set_binary_fd_handle(
-               struct lttng_userspace_probe_location *location,
-               struct fd_handle *binary_fd)    
-{
-       int ret = 0;
-       struct lttng_userspace_probe_location_function *function_location;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
-
-       function_location = container_of(location,
-                       struct lttng_userspace_probe_location_function, parent);
-       fd_handle_put(function_location->binary_fd_handle);
-       fd_handle_get(binary_fd);
-       function_location->binary_fd_handle = binary_fd;
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
-               struct lttng_userspace_probe_location *location,
-               struct fd_handle *binary_fd)
-{
-       int ret = 0;
-       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
-
-       tracepoint_location = container_of(location,
-                       struct lttng_userspace_probe_location_tracepoint, parent);
-       fd_handle_put(tracepoint_location->binary_fd_handle);
-       fd_handle_get(binary_fd);
-       tracepoint_location->binary_fd_handle = binary_fd;
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_function_flatten(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_dynamic_buffer *buffer)
-{
-       struct lttng_userspace_probe_location_lookup_method_elf flat_lookup_method;
-       struct lttng_userspace_probe_location_function *probe_function;
-       struct lttng_userspace_probe_location_function flat_probe;
-       size_t function_name_len, binary_path_len;
-       size_t padding_needed = 0;
-       char *flat_probe_start;
-       int storage_needed = 0;
-       int ret;
-
-       LTTNG_ASSERT(location);
-
-       if (location->lookup_method && location->lookup_method->type !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       probe_function = container_of(location,
-                       struct lttng_userspace_probe_location_function,
-                       parent);
-       LTTNG_ASSERT(probe_function->function_name);
-       LTTNG_ASSERT(probe_function->binary_path);
-
-       storage_needed +=
-                       sizeof(struct lttng_userspace_probe_location_function);
-       function_name_len = strlen(probe_function->function_name) + 1;
-       binary_path_len = strlen(probe_function->binary_path) + 1;
-       storage_needed += function_name_len + binary_path_len;
-
-       /*
-        * The lookup method is aligned to 64-bit within the buffer.
-        * This is needed even if there is no lookup method since
-        * the next structure in the buffer probably needs to be
-        * aligned too (depending on the arch).
-        */
-       padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed;
-       storage_needed += padding_needed;
-
-       if (location->lookup_method) {
-               /* NOTE: elf look-up method is assumed here. */
-               storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf);
-       }
-
-       if (!buffer) {
-               ret = storage_needed;
-               goto end;
-       }
-
-       if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) {
-               ret = lttng_dynamic_buffer_set_capacity(buffer,
-                               buffer->size + storage_needed);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       memset(&flat_probe, 0, sizeof(flat_probe));
-
-       flat_probe_start = buffer->data + buffer->size;
-       flat_probe.parent.type = location->type;
-       /*
-        * The lookup method, if present, is the last element in the flat
-        * representation of the probe.
-        */
-       if (location->lookup_method) {
-               flat_probe.parent.lookup_method =
-                               (struct lttng_userspace_probe_location_lookup_method *)
-                                       (flat_probe_start + sizeof(flat_probe) +
-                                       function_name_len + binary_path_len + padding_needed);
-       } else {
-               flat_probe.parent.lookup_method = NULL;
-       }
-
-       flat_probe.function_name = flat_probe_start + sizeof(flat_probe);
-       flat_probe.binary_path = flat_probe.function_name + function_name_len;
-       flat_probe.binary_fd_handle = NULL;
-       ret = lttng_dynamic_buffer_append(buffer, &flat_probe,
-                       sizeof(flat_probe));
-       if (ret) {
-               goto end;
-       }
-
-       ret = lttng_dynamic_buffer_append(buffer,
-                       probe_function->function_name, function_name_len);
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buffer,
-                       probe_function->binary_path, binary_path_len);
-       if (ret) {
-               goto end;
-       }
-
-       /* Insert padding before the lookup method. */
-       ret = lttng_dynamic_buffer_set_size(buffer,
-                       buffer->size + padding_needed);
-       if (ret) {
-               goto end;
-       }
-
-       if (!location->lookup_method) {
-               /* Not an error, the default method is used. */
-               ret = storage_needed;
-               goto end;
-       }
-
-       memset(&flat_lookup_method, 0, sizeof(flat_lookup_method));
-       flat_lookup_method.parent.type =
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF;
-       ret = lttng_dynamic_buffer_append(buffer,
-                       &flat_lookup_method, sizeof(flat_lookup_method));
-       if (ret) {
-               goto end;
-       }
-       ret = storage_needed;
-end:
-       return ret;
-}
-
-static
-int lttng_userspace_probe_location_tracepoint_flatten(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_dynamic_buffer *buffer)
-{
-       struct lttng_userspace_probe_location_lookup_method_sdt flat_lookup_method;
-       struct lttng_userspace_probe_location_tracepoint *probe_tracepoint;
-       struct lttng_userspace_probe_location_tracepoint flat_probe;
-       size_t probe_name_len, provider_name_len, binary_path_len;
-       size_t padding_needed = 0;
-       int storage_needed = 0;
-       char *flat_probe_start;
-       int ret = 0;
-
-       LTTNG_ASSERT(location);
-
-       /* Only SDT tracepoints are supported at the moment */
-       if (location->lookup_method && location->lookup_method->type !=
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-       probe_tracepoint = container_of(location,
-                       struct lttng_userspace_probe_location_tracepoint,
-                       parent);
-       LTTNG_ASSERT(probe_tracepoint->probe_name);
-       LTTNG_ASSERT(probe_tracepoint->provider_name);
-       LTTNG_ASSERT(probe_tracepoint->binary_path);
-
-       /* Compute the storage space needed to flatten the probe location */
-       storage_needed += sizeof(struct lttng_userspace_probe_location_tracepoint);
-
-       probe_name_len = strlen(probe_tracepoint->probe_name) + 1;
-       provider_name_len = strlen(probe_tracepoint->provider_name) + 1;
-       binary_path_len = strlen(probe_tracepoint->binary_path) + 1;
-
-       storage_needed += probe_name_len + provider_name_len + binary_path_len;
-
-       /*
-        * The lookup method is aligned to 64-bit within the buffer.
-        * This is needed even if there is no lookup method since
-        * the next structure in the buffer probably needs to be
-        * aligned too (depending on the arch).
-        */
-       padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed;
-       storage_needed += padding_needed;
-
-       if (location->lookup_method) {
-               /* NOTE: elf look-up method is assumed here. */
-               storage_needed +=
-                       sizeof(struct lttng_userspace_probe_location_lookup_method_elf);
-       }
-
-       /*
-        * If the caller set buffer to NULL, return the size of the needed buffer.
-        */
-       if (!buffer) {
-               ret = storage_needed;
-               goto end;
-       }
-
-       if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) {
-               ret = lttng_dynamic_buffer_set_capacity(buffer,
-                               buffer->size + storage_needed);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       memset(&flat_probe, 0, sizeof(flat_probe));
-
-       flat_probe_start = buffer->data + buffer->size;
-       flat_probe.parent.type = location->type;
-
-       /*
-        * The lookup method, if present, is the last element in the flat
-        * representation of the probe.
-        */
-       if (location->lookup_method) {
-               flat_probe.parent.lookup_method =
-                               (struct lttng_userspace_probe_location_lookup_method *)
-                                       (flat_probe_start + sizeof(flat_probe) +
-                                       probe_name_len + provider_name_len +
-                                       binary_path_len + padding_needed);
-       } else {
-               flat_probe.parent.lookup_method = NULL;
-       }
-
-       flat_probe.probe_name = flat_probe_start + sizeof(flat_probe);
-       flat_probe.provider_name = flat_probe.probe_name + probe_name_len;
-       flat_probe.binary_path = flat_probe.provider_name + provider_name_len;
-       flat_probe.binary_fd_handle = NULL;
-       ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe));
-       if (ret) {
-               goto end;
-       }
-
-       /* Append all the fields to the buffer */
-       ret = lttng_dynamic_buffer_append(buffer,
-                       probe_tracepoint->probe_name, probe_name_len);
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buffer,
-                       probe_tracepoint->provider_name, provider_name_len);
-       if (ret) {
-               goto end;
-       }
-       ret = lttng_dynamic_buffer_append(buffer,
-                       probe_tracepoint->binary_path, binary_path_len);
-       if (ret) {
-               goto end;
-       }
-
-       /* Insert padding before the lookup method. */
-       ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed);
-       if (ret) {
-               goto end;
-       }
-
-       if (!location->lookup_method) {
-               /* Not an error, the default method is used. */
-               ret = storage_needed;
-               goto end;
-       }
-
-       memset(&flat_lookup_method, 0, sizeof(flat_lookup_method));
-
-       flat_lookup_method.parent.type =
-                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT;
-       ret = lttng_dynamic_buffer_append(buffer,
-                       &flat_lookup_method, sizeof(flat_lookup_method));
-       if (ret) {
-               goto end;
-       }
-       ret = storage_needed;
-end:
-       return ret;
-}
-
-int lttng_userspace_probe_location_flatten(
-               const struct lttng_userspace_probe_location *location,
-               struct lttng_dynamic_buffer *buffer)
-{
-       int ret;
-       if (!location) {
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-       /* Only types currently supported. */
-       switch (location->type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-               ret = lttng_userspace_probe_location_function_flatten(location, buffer);
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-               ret = lttng_userspace_probe_location_tracepoint_flatten(location, buffer);
-               break;
-       default:
-               ret = -LTTNG_ERR_INVALID;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy(
-               const struct lttng_userspace_probe_location *location)
-{
-       struct lttng_userspace_probe_location *new_location = NULL;
-       enum lttng_userspace_probe_location_type type;
-
-       if (!location) {
-               goto err;
-       }
-
-       type = lttng_userspace_probe_location_get_type(location);
-       switch (type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-               new_location =
-                       lttng_userspace_probe_location_function_copy(location);
-               if (!new_location) {
-                       goto err;
-               }
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-               new_location =
-                       lttng_userspace_probe_location_tracepoint_copy(location);
-               if (!new_location) {
-                       goto err;
-               }
-               break;
-       default:
-               new_location = NULL;
-               goto err;
-       }
-err:
-       return new_location;
-}
-
-bool lttng_userspace_probe_location_lookup_method_is_equal(
-               const struct lttng_userspace_probe_location_lookup_method *a,
-               const struct lttng_userspace_probe_location_lookup_method *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       is_equal = true;
-end:
-       return is_equal;
-}
-
-bool lttng_userspace_probe_location_is_equal(
-               const struct lttng_userspace_probe_location *a,
-               const struct lttng_userspace_probe_location *b)
-{
-       bool is_equal = false;
-
-       if (!a || !b) {
-               goto end;
-       }
-
-       if (a == b) {
-               is_equal = true;
-               goto end;
-       }
-
-       if (!lttng_userspace_probe_location_lookup_method_is_equal(
-                           a->lookup_method, b->lookup_method)) {
-               goto end;
-       }
-
-       if (a->type != b->type) {
-               goto end;
-       }
-
-       is_equal = a->equal ? a->equal(a, b) : true;
-end:
-       return is_equal;
-}
-
-unsigned long lttng_userspace_probe_location_hash(
-               const struct lttng_userspace_probe_location *location)
-{
-       return location->hash(location);
-}
-
-enum lttng_error_code lttng_userspace_probe_location_mi_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct mi_writer *writer)
-{
-       typedef enum lttng_error_code (*mi_fp)(
-                       const struct lttng_userspace_probe_location *,
-                       struct mi_writer *);
-
-       int ret;
-       enum lttng_error_code ret_code;
-       mi_fp mi_function = NULL;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-
-       switch (lttng_userspace_probe_location_get_type(location)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
-               mi_function = lttng_userspace_probe_location_function_mi_serialize;
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
-               mi_function = lttng_userspace_probe_location_tracepoint_mi_serialize;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       /* Open userspace probe location element. */
-       ret = mi_lttng_writer_open_element(
-                       writer, mi_lttng_element_userspace_probe_location);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Underlying user space probe location. */
-       ret_code = mi_function(location, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close userspace probe location element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize(
-               const struct lttng_userspace_probe_location_lookup_method
-                               *method,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *type_element_str;
-
-       LTTNG_ASSERT(method);
-       LTTNG_ASSERT(writer);
-
-       switch (lttng_userspace_probe_location_lookup_method_get_type(method)) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
-               type_element_str =
-                               mi_lttng_element_userspace_probe_location_lookup_method_function_default;
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
-               type_element_str =
-                               mi_lttng_element_userspace_probe_location_lookup_method_function_elf;
-               break;
-       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
-               type_element_str =
-                               mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       /* Open userspace probe location lookup method element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_userspace_probe_location_lookup_method);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* User space probe location lookup method empty element. */
-       ret = mi_lttng_writer_open_element(writer, type_element_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Close userspace probe location lookup method element. */
-       ret = mi_lttng_close_multi_element(writer, 2);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *probe_name = NULL;
-       const char *provider_name = NULL;
-       const char *binary_path = NULL;
-       const struct lttng_userspace_probe_location_lookup_method
-                       *lookup_method = NULL;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-
-       probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(
-                       location);
-       provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(
-                       location);
-       binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(
-                       location);
-       lookup_method = lttng_userspace_probe_location_tracepoint_get_lookup_method(
-                       location);
-
-       /* Open userspace probe location tracepoint element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_userspace_probe_location_tracepoint);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Probe name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_tracepoint_probe_name,
-                       probe_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Provider name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_tracepoint_provider_name,
-                       provider_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Binary path. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_binary_path,
-                       binary_path);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* The lookup method. */
-       ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize(
-                       lookup_method, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close userspace probe location tracepoint. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
-
-static enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize(
-               const struct lttng_userspace_probe_location *location,
-               struct mi_writer *writer)
-{
-       int ret;
-       enum lttng_error_code ret_code;
-       const char *function_name = NULL;
-       const char *binary_path = NULL;
-       const char *instrumentation_type_str = NULL;
-       enum lttng_userspace_probe_location_function_instrumentation_type
-                       instrumentation_type;
-       const struct lttng_userspace_probe_location_lookup_method
-                       *lookup_method = NULL;
-
-       LTTNG_ASSERT(location);
-       LTTNG_ASSERT(writer);
-
-       function_name = lttng_userspace_probe_location_function_get_function_name(
-                       location);
-       binary_path = lttng_userspace_probe_location_function_get_binary_path(
-                       location);
-       instrumentation_type =
-                       lttng_userspace_probe_location_function_get_instrumentation_type(
-                                       location);
-       lookup_method = lttng_userspace_probe_location_function_get_lookup_method(
-                       location);
-
-       switch (instrumentation_type) {
-       case LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY:
-               instrumentation_type_str =
-                               mi_lttng_userspace_probe_location_function_instrumentation_type_entry;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       /* Open userspace probe location function element. */
-       ret = mi_lttng_writer_open_element(writer,
-                       mi_lttng_element_userspace_probe_location_function);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Function name. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_function_name,
-                       function_name);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Binary path. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_binary_path,
-                       binary_path);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* Instrumentation type. */
-       ret = mi_lttng_writer_write_element_string(writer,
-                       mi_lttng_element_userspace_probe_location_function_instrumentation_type,
-                       instrumentation_type_str);
-       if (ret) {
-               goto mi_error;
-       }
-
-       /* The lookup method. */
-       ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize(
-                       lookup_method, writer);
-       if (ret_code != LTTNG_OK) {
-               goto end;
-       }
-
-       /* Close userspace probe location function element. */
-       ret = mi_lttng_writer_close_element(writer);
-       if (ret) {
-               goto mi_error;
-       }
-
-       ret_code = LTTNG_OK;
-       goto end;
-
-mi_error:
-       ret_code = LTTNG_ERR_MI_IO_FAIL;
-end:
-       return ret_code;
-}
diff --git a/src/common/userspace-probe.cpp b/src/common/userspace-probe.cpp
new file mode 100644 (file)
index 0000000..4ade5d8
--- /dev/null
@@ -0,0 +1,2260 @@
+/*
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "lttng/lttng-error.h"
+#include <common/compat/string.h>
+#include <common/align.h>
+#include <common/error.h>
+#include <common/hashtable/hashtable.h>
+#include <common/hashtable/utils.h>
+#include <common/macros.h>
+#include <common/mi-lttng.h>
+#include <common/payload-view.h>
+#include <common/payload.h>
+#include <fcntl.h>
+#include <lttng/constant.h>
+#include <lttng/userspace-probe-internal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static
+int lttng_userspace_probe_location_function_set_binary_fd_handle(
+               struct lttng_userspace_probe_location *location,
+               struct fd_handle *binary_fd_handle);
+
+static
+int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
+               struct lttng_userspace_probe_location *location,
+               struct fd_handle *binary_fd_handle);
+
+static
+enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize(
+               const struct lttng_userspace_probe_location_lookup_method
+                               *method,
+               struct mi_writer *writer);
+
+static
+enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct mi_writer *writer);
+
+static
+enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct mi_writer *writer);
+
+enum lttng_userspace_probe_location_lookup_method_type
+lttng_userspace_probe_location_lookup_method_get_type(
+               const struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       return lookup_method ? lookup_method->type :
+               LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_UNKNOWN;
+}
+
+void lttng_userspace_probe_location_lookup_method_destroy(
+               struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       if (!lookup_method){
+               return;
+       }
+
+       free(lookup_method);
+}
+
+struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_lookup_method_function_elf_create(void)
+{
+       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
+       struct lttng_userspace_probe_location_lookup_method_elf *elf_method;
+
+       elf_method = (lttng_userspace_probe_location_lookup_method_elf *) zmalloc(sizeof(*elf_method));
+       if (!elf_method) {
+               PERROR("zmalloc");
+               goto end;
+       }
+
+       ret = &elf_method->parent;
+       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF;
+end:
+       return ret;
+}
+
+struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(void)
+{
+       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
+       struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method;
+
+       sdt_method = (lttng_userspace_probe_location_lookup_method_sdt *) zmalloc(sizeof(*sdt_method));
+       if (!sdt_method) {
+               PERROR("zmalloc");
+               goto end;
+       }
+
+       ret = &sdt_method->parent;
+       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT;
+end:
+       return ret;
+}
+
+enum lttng_userspace_probe_location_type lttng_userspace_probe_location_get_type(
+               const struct lttng_userspace_probe_location *location)
+{
+       return location ? location->type :
+               LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN;
+}
+
+static
+void lttng_userspace_probe_location_function_destroy(
+               struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location_function *location_function = NULL;
+
+       LTTNG_ASSERT(location);
+
+       location_function = container_of(location,
+                       struct lttng_userspace_probe_location_function, parent);
+
+       LTTNG_ASSERT(location_function);
+
+       free(location_function->function_name);
+       free(location_function->binary_path);
+       fd_handle_put(location_function->binary_fd_handle);
+       free(location);
+}
+
+static
+void lttng_userspace_probe_location_tracepoint_destroy(
+               struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location_tracepoint *location_tracepoint = NULL;
+
+       LTTNG_ASSERT(location);
+
+       location_tracepoint = container_of(location,
+                       struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+
+       LTTNG_ASSERT(location_tracepoint);
+
+       free(location_tracepoint->probe_name);
+       free(location_tracepoint->provider_name);
+       free(location_tracepoint->binary_path);
+       fd_handle_put(location_tracepoint->binary_fd_handle);
+       free(location);
+}
+
+void lttng_userspace_probe_location_destroy(
+               struct lttng_userspace_probe_location *location)
+{
+       if (!location) {
+               return;
+       }
+
+       lttng_userspace_probe_location_lookup_method_destroy(
+                       location->lookup_method);
+
+       switch (location->type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+               lttng_userspace_probe_location_function_destroy(location);
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               lttng_userspace_probe_location_tracepoint_destroy(location);
+               break;
+       default:
+               abort();
+       }
+}
+
+/* Compare two file descriptors based on their inode and device numbers. */
+static bool fd_is_equal(int a, int b)
+{
+       int ret;
+       bool is_equal = false;
+       struct stat a_stat, b_stat;
+
+       if (a < 0 && b >= 0) {
+               goto end;
+       }
+
+       if (b < 0 && a >= 0) {
+               goto end;
+       }
+
+       if (a < 0 && b < 0) {
+               if (a == -1 && b == -1) {
+                       is_equal = true;
+                       goto end;
+               }
+
+               /* Invalid state, abort. */
+               abort();
+       }
+
+       /* Both are valid file descriptors. */
+       ret = fstat(a, &a_stat);
+       if (ret) {
+               PERROR("Failed to fstat userspace probe location binary fd %d",
+                               a);
+               goto end;
+       }
+
+       ret = fstat(b, &b_stat);
+       if (ret) {
+               PERROR("Failed to fstat userspace probe location binary fd %d",
+                               b);
+               goto end;
+       }
+
+       is_equal = (a_stat.st_ino == b_stat.st_ino) &&
+                       (a_stat.st_dev == b_stat.st_dev);
+
+end:
+       return is_equal;
+}
+
+static unsigned long lttng_userspace_probe_location_function_hash(
+               const struct lttng_userspace_probe_location *location)
+{
+       unsigned long hash = hash_key_ulong(
+                       (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION,
+                       lttng_ht_seed);
+       struct lttng_userspace_probe_location_function *function_location =
+                       container_of(location, typeof(*function_location),
+                                       parent);
+
+       hash ^= hash_key_str(function_location->function_name, lttng_ht_seed);
+       hash ^= hash_key_str(function_location->binary_path, lttng_ht_seed);
+       /*
+        * No need to hash on the fd. Worst comes to worse,
+        * the equal function will discriminate.
+        */
+       return hash;
+}
+
+static bool lttng_userspace_probe_location_function_is_equal(
+               const struct lttng_userspace_probe_location *_a,
+               const struct lttng_userspace_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_userspace_probe_location_function *a, *b;
+
+       a = container_of(_a, struct lttng_userspace_probe_location_function,
+                       parent);
+       b = container_of(_b, struct lttng_userspace_probe_location_function,
+                       parent);
+
+       if (a->instrumentation_type != b->instrumentation_type) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->function_name);
+       LTTNG_ASSERT(b->function_name);
+       if (strcmp(a->function_name, b->function_name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->binary_path);
+       LTTNG_ASSERT(b->binary_path);
+       if (strcmp(a->binary_path, b->binary_path)) {
+               goto end;
+       }
+
+       is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1,
+                       b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1);
+end:
+       return is_equal;
+}
+
+static struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_function_create_no_check(const char *binary_path,
+               const char *function_name,
+               struct lttng_userspace_probe_location_lookup_method *lookup_method,
+               bool open_binary)
+{
+       int binary_fd = -1;
+       struct fd_handle *binary_fd_handle = NULL;
+       char *function_name_copy = NULL, *binary_path_copy = NULL;
+       struct lttng_userspace_probe_location *ret = NULL;
+       struct lttng_userspace_probe_location_function *location;
+
+       if (open_binary) {
+               binary_fd = open(binary_path, O_RDONLY);
+               if (binary_fd < 0) {
+                       PERROR("Error opening the binary");
+                       goto error;
+               }
+
+               binary_fd_handle = fd_handle_create(binary_fd);
+               if (!binary_fd) {
+                       goto error;
+               }
+
+               /* Ownership transferred to fd_handle. */
+               binary_fd = -1;
+       }
+
+       function_name_copy = lttng_strndup(function_name, LTTNG_SYMBOL_NAME_LEN);
+       if (!function_name_copy) {
+               PERROR("Error duplicating the function name");
+               goto error;
+       }
+
+       binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX);
+       if (!binary_path_copy) {
+               PERROR("Error duplicating the function name");
+               goto error;
+       }
+
+       location = (lttng_userspace_probe_location_function *) zmalloc(sizeof(*location));
+       if (!location) {
+               PERROR("Error allocating userspace probe location");
+               goto error;
+       }
+
+       location->function_name = function_name_copy;
+       location->binary_path = binary_path_copy;
+       location->binary_fd_handle = binary_fd_handle;
+       binary_fd_handle = NULL;
+       location->instrumentation_type =
+                       LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY;
+
+       ret = &location->parent;
+       ret->lookup_method = lookup_method;
+       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION;
+       ret->equal = lttng_userspace_probe_location_function_is_equal;
+       ret->hash = lttng_userspace_probe_location_function_hash;
+       goto end;
+
+error:
+       free(function_name_copy);
+       free(binary_path_copy);
+       if (binary_fd >= 0) {
+               if (close(binary_fd)) {
+                       PERROR("Error closing binary fd in error path");
+               }
+       }
+       fd_handle_put(binary_fd_handle);
+end:
+       return ret;
+}
+
+static unsigned long lttng_userspace_probe_location_tracepoint_hash(
+               const struct lttng_userspace_probe_location *location)
+{
+       unsigned long hash = hash_key_ulong(
+                       (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT,
+                       lttng_ht_seed);
+       struct lttng_userspace_probe_location_tracepoint *tp_location =
+                       container_of(location, typeof(*tp_location), parent);
+
+       hash ^= hash_key_str(tp_location->probe_name, lttng_ht_seed);
+       hash ^= hash_key_str(tp_location->provider_name, lttng_ht_seed);
+       hash ^= hash_key_str(tp_location->binary_path, lttng_ht_seed);
+       /*
+        * No need to hash on the fd. Worst comes to worse,
+        * the equal function will discriminate.
+        */
+       return hash;
+}
+
+static bool lttng_userspace_probe_location_tracepoint_is_equal(
+               const struct lttng_userspace_probe_location *_a,
+               const struct lttng_userspace_probe_location *_b)
+{
+       bool is_equal = false;
+       struct lttng_userspace_probe_location_tracepoint *a, *b;
+
+       a = container_of(_a, struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+       b = container_of(_b, struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+
+       LTTNG_ASSERT(a->probe_name);
+       LTTNG_ASSERT(b->probe_name);
+       if (strcmp(a->probe_name, b->probe_name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->provider_name);
+       LTTNG_ASSERT(b->provider_name);
+       if (strcmp(a->provider_name, b->provider_name)) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(a->binary_path);
+       LTTNG_ASSERT(b->binary_path);
+       if (strcmp(a->binary_path, b->binary_path)) {
+               goto end;
+       }
+
+       is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1,
+                       b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1);
+
+end:
+       return is_equal;
+}
+
+static struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path,
+               const char *provider_name, const char *probe_name,
+               struct lttng_userspace_probe_location_lookup_method *lookup_method,
+               bool open_binary)
+{
+       int binary_fd = -1;
+       struct fd_handle *binary_fd_handle = NULL;
+       char *probe_name_copy = NULL;
+       char *provider_name_copy = NULL;
+       char *binary_path_copy = NULL;
+       struct lttng_userspace_probe_location *ret = NULL;
+       struct lttng_userspace_probe_location_tracepoint *location;
+
+       if (open_binary) {
+               binary_fd = open(binary_path, O_RDONLY);
+               if (binary_fd < 0) {
+                       PERROR("open");
+                       goto error;
+               }
+
+               binary_fd_handle = fd_handle_create(binary_fd);
+               if (!binary_fd) {
+                       goto error;
+               }
+
+               /* Ownership transferred to fd_handle. */
+               binary_fd = -1;
+       }
+
+       probe_name_copy = lttng_strndup(probe_name, LTTNG_SYMBOL_NAME_LEN);
+       if (!probe_name_copy) {
+               PERROR("lttng_strndup");
+               goto error;
+       }
+
+       provider_name_copy = lttng_strndup(provider_name, LTTNG_SYMBOL_NAME_LEN);
+       if (!provider_name_copy) {
+               PERROR("lttng_strndup");
+               goto error;
+       }
+
+       binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX);
+       if (!binary_path_copy) {
+               PERROR("lttng_strndup");
+               goto error;
+       }
+
+       location = (lttng_userspace_probe_location_tracepoint *) zmalloc(sizeof(*location));
+       if (!location) {
+               PERROR("zmalloc");
+               goto error;
+       }
+
+       location->probe_name = probe_name_copy;
+       location->provider_name = provider_name_copy;
+       location->binary_path = binary_path_copy;
+       location->binary_fd_handle = binary_fd_handle;
+       binary_fd_handle = NULL;
+
+       ret = &location->parent;
+       ret->lookup_method = lookup_method;
+       ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT;
+       ret->equal = lttng_userspace_probe_location_tracepoint_is_equal;
+       ret->hash = lttng_userspace_probe_location_tracepoint_hash;
+       goto end;
+
+error:
+       free(probe_name_copy);
+       free(provider_name_copy);
+       free(binary_path_copy);
+       if (binary_fd >= 0) {
+               if (close(binary_fd)) {
+                       PERROR("Error closing binary fd in error path");
+               }
+       }
+       fd_handle_put(binary_fd_handle);
+end:
+       return ret;
+}
+
+struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_function_create(const char *binary_path,
+               const char *function_name,
+               struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       struct lttng_userspace_probe_location *ret = NULL;
+
+       if (!binary_path || !function_name) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       switch (lttng_userspace_probe_location_lookup_method_get_type(
+                       lookup_method)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               break;
+       default:
+               /* Invalid probe location lookup method. */
+               goto end;
+       }
+
+       ret = lttng_userspace_probe_location_function_create_no_check(
+                       binary_path, function_name, lookup_method, true);
+end:
+       return ret;
+}
+
+struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_tracepoint_create(const char *binary_path,
+               const char *provider_name, const char *probe_name,
+               struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       struct lttng_userspace_probe_location *ret = NULL;
+
+       if (!binary_path || !probe_name || !provider_name) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       switch (lttng_userspace_probe_location_lookup_method_get_type(
+                       lookup_method)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               break;
+       default:
+               /* Invalid probe location lookup method. */
+               goto end;
+       }
+
+       ret = lttng_userspace_probe_location_tracepoint_create_no_check(
+                       binary_path, provider_name, probe_name, lookup_method, true);
+end:
+       return ret;
+}
+
+static struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_lookup_method_function_elf_copy(
+               const struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       struct lttng_userspace_probe_location_lookup_method *parent = NULL;
+       struct lttng_userspace_probe_location_lookup_method_elf *elf_method;
+
+       LTTNG_ASSERT(lookup_method);
+       LTTNG_ASSERT(lookup_method->type ==
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF);
+
+       elf_method = (lttng_userspace_probe_location_lookup_method_elf *) zmalloc(sizeof(*elf_method));
+       if (!elf_method) {
+               PERROR("Error allocating ELF userspace probe lookup method");
+               goto error;
+       }
+
+       elf_method->parent.type = lookup_method->type;
+       parent = &elf_method->parent;
+
+       goto end;
+error:
+       parent = NULL;
+end:
+       return parent;
+}
+
+static struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy(
+               struct lttng_userspace_probe_location_lookup_method *lookup_method)
+{
+       struct lttng_userspace_probe_location_lookup_method *parent = NULL;
+       struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method;
+
+       LTTNG_ASSERT(lookup_method);
+       LTTNG_ASSERT(lookup_method->type ==
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT);
+
+       sdt_method = (lttng_userspace_probe_location_lookup_method_sdt *) zmalloc(sizeof(*sdt_method));
+       if (!sdt_method) {
+               PERROR("zmalloc");
+               goto error;
+       }
+
+       sdt_method->parent.type = lookup_method->type;
+       parent = &sdt_method->parent;
+
+       goto end;
+
+error:
+       parent = NULL;
+end:
+       return parent;
+}
+
+static struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_function_copy(
+               const struct lttng_userspace_probe_location *location)
+{
+       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
+       struct lttng_userspace_probe_location *new_location = NULL;
+       struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
+       const char *binary_path = NULL;
+       const char *function_name = NULL;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
+       function_location = container_of(
+                       location, typeof(*function_location), parent);
+
+        /* Get probe location fields */
+       binary_path = lttng_userspace_probe_location_function_get_binary_path(location);
+       if (!binary_path) {
+               ERR("Userspace probe binary path is NULL");
+               goto error;
+       }
+
+       function_name = lttng_userspace_probe_location_function_get_function_name(location);
+       if (!function_name) {
+               ERR("Userspace probe function name is NULL");
+               goto error;
+       }
+
+       /*
+        * Duplicate probe location method fields
+        */
+       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(
+                       location->lookup_method);
+       switch (lookup_type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               lookup_method =
+                       lttng_userspace_probe_location_lookup_method_function_elf_copy(
+                                       location->lookup_method);
+               if (!lookup_method) {
+                       goto error;
+               }
+               break;
+       default:
+               /* Invalid probe location lookup method. */
+               goto error;
+       }
+
+       /* Create the probe_location */
+       new_location = lttng_userspace_probe_location_function_create_no_check(
+                       binary_path, function_name, lookup_method, false);
+       if (!new_location) {
+               goto destroy_lookup_method;
+       }
+
+       /* Set the duplicated fd to the new probe_location */
+       if (lttng_userspace_probe_location_function_set_binary_fd_handle(new_location,
+                       function_location->binary_fd_handle) < 0) {
+               goto destroy_probe_location;
+       }
+
+       goto end;
+
+destroy_probe_location:
+       lttng_userspace_probe_location_destroy(new_location);
+destroy_lookup_method:
+       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
+error:
+       new_location = NULL;
+end:
+       return new_location;
+}
+
+static struct lttng_userspace_probe_location *
+lttng_userspace_probe_location_tracepoint_copy(
+               const struct lttng_userspace_probe_location *location)
+{
+       enum lttng_userspace_probe_location_lookup_method_type lookup_type;
+       struct lttng_userspace_probe_location *new_location = NULL;
+       struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
+       const char *binary_path = NULL;
+       const char *probe_name = NULL;
+       const char *provider_name = NULL;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
+       tracepoint_location = container_of(
+                       location, typeof(*tracepoint_location), parent);
+
+       /* Get probe location fields */
+       binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location);
+       if (!binary_path) {
+               ERR("Userspace probe binary path is NULL");
+               goto error;
+       }
+
+       probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location);
+       if (!probe_name) {
+               ERR("Userspace probe probe name is NULL");
+               goto error;
+       }
+
+       provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location);
+       if (!provider_name) {
+               ERR("Userspace probe provider name is NULL");
+               goto error;
+       }
+
+       /*
+        * Duplicate probe location method fields
+        */
+       lookup_type = lttng_userspace_probe_location_lookup_method_get_type(
+                       location->lookup_method);
+       switch (lookup_type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               lookup_method =
+                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy(
+                                       location->lookup_method);
+               if (!lookup_method) {
+                       goto error;
+               }
+               break;
+       default:
+               /* Invalid probe location lookup method. */
+               goto error;
+       }
+
+       /* Create the probe_location */
+       new_location = lttng_userspace_probe_location_tracepoint_create_no_check(
+                       binary_path, provider_name, probe_name, lookup_method, false);
+       if (!new_location) {
+               goto destroy_lookup_method;
+       }
+
+       /* Set the duplicated fd to the new probe_location */
+       if (lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(new_location,
+                       tracepoint_location->binary_fd_handle) < 0) {
+               goto destroy_probe_location;
+       }
+
+       goto end;
+
+destroy_probe_location:
+       lttng_userspace_probe_location_destroy(new_location);
+destroy_lookup_method:
+       lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
+error:
+       new_location = NULL;
+end:
+       return new_location;
+}
+
+const char *lttng_userspace_probe_location_function_get_binary_path(
+               const struct lttng_userspace_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       function_location = container_of(location,
+               struct lttng_userspace_probe_location_function,
+                       parent);
+       ret = function_location->binary_path;
+end:
+       return ret;
+}
+
+const char *lttng_userspace_probe_location_tracepoint_get_binary_path(
+               const struct lttng_userspace_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       tracepoint_location = container_of(location,
+               struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+       ret = tracepoint_location->binary_path;
+end:
+       return ret;
+}
+
+const char *lttng_userspace_probe_location_function_get_function_name(
+               const struct lttng_userspace_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       function_location = container_of(location,
+               struct lttng_userspace_probe_location_function, parent);
+       ret = function_location->function_name;
+end:
+       return ret;
+}
+
+const char *lttng_userspace_probe_location_tracepoint_get_probe_name(
+               const struct lttng_userspace_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       tracepoint_location = container_of(location,
+               struct lttng_userspace_probe_location_tracepoint, parent);
+       ret = tracepoint_location->probe_name;
+end:
+       return ret;
+}
+
+const char *lttng_userspace_probe_location_tracepoint_get_provider_name(
+               const struct lttng_userspace_probe_location *location)
+{
+       const char *ret = NULL;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       tracepoint_location = container_of(location,
+               struct lttng_userspace_probe_location_tracepoint, parent);
+       ret = tracepoint_location->provider_name;
+end:
+       return ret;
+}
+
+int lttng_userspace_probe_location_function_get_binary_fd(
+               const struct lttng_userspace_probe_location *location)
+{
+       int ret = -1;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       function_location = container_of(location,
+               struct lttng_userspace_probe_location_function, parent);
+       ret = function_location->binary_fd_handle ?
+                       fd_handle_get_fd(function_location->binary_fd_handle) : -1;
+end:
+       return ret;
+}
+
+enum lttng_userspace_probe_location_function_instrumentation_type
+lttng_userspace_probe_location_function_get_instrumentation_type(
+               const struct lttng_userspace_probe_location *location)
+{
+       enum lttng_userspace_probe_location_function_instrumentation_type type;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN;
+               goto end;
+       }
+
+       function_location = container_of(location,
+               struct lttng_userspace_probe_location_function, parent);
+       type = function_location->instrumentation_type;
+end:
+       return type;
+}
+
+enum lttng_userspace_probe_location_status
+lttng_userspace_probe_location_function_set_instrumentation_type(
+               const struct lttng_userspace_probe_location *location,
+               enum lttng_userspace_probe_location_function_instrumentation_type instrumentation_type)
+{
+       enum lttng_userspace_probe_location_status status =
+                       LTTNG_USERSPACE_PROBE_LOCATION_STATUS_OK;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION ||
+                       instrumentation_type !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID;
+               goto end;
+       }
+
+       function_location = container_of(location,
+               struct lttng_userspace_probe_location_function, parent);
+       function_location->instrumentation_type = instrumentation_type;
+end:
+       return status;
+}
+
+int lttng_userspace_probe_location_tracepoint_get_binary_fd(
+               const struct lttng_userspace_probe_location *location)
+{
+       int ret = -1;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       tracepoint_location = container_of(location,
+               struct lttng_userspace_probe_location_tracepoint, parent);
+       ret = tracepoint_location->binary_fd_handle ?
+                       fd_handle_get_fd(tracepoint_location->binary_fd_handle) : -1;
+end:
+       return ret;
+}
+
+static struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_function_get_lookup_method(
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       ret = location->lookup_method;
+end:
+       return ret;
+}
+
+static struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_tracepoint_get_lookup_method(
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
+
+       if (!location || lttng_userspace_probe_location_get_type(location) !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               goto end;
+       }
+
+       ret = location->lookup_method;
+end:
+       return ret;
+}
+
+const struct lttng_userspace_probe_location_lookup_method *
+lttng_userspace_probe_location_get_lookup_method(
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location_lookup_method *ret = NULL;
+
+       LTTNG_ASSERT(location);
+       switch (location->type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+       ret = lttng_userspace_probe_location_function_get_lookup_method(
+                       location);
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+       ret = lttng_userspace_probe_location_tracepoint_get_lookup_method(
+                       location);
+               break;
+       default:
+               ERR("Unknowned lookup method.");
+               break;
+       }
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_lookup_method_serialize(
+               struct lttng_userspace_probe_location_lookup_method *method,
+               struct lttng_payload *payload)
+{
+       int ret;
+       struct lttng_userspace_probe_location_lookup_method_comm
+                       lookup_method_comm;
+
+       lookup_method_comm.type = (int8_t) (method ? method->type :
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT);
+       if (payload) {
+               ret = lttng_dynamic_buffer_append(&payload->buffer, &lookup_method_comm,
+                               sizeof(lookup_method_comm));
+               if (ret) {
+                       goto end;
+               }
+       }
+       ret = sizeof(lookup_method_comm);
+end:
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_function_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t function_name_len, binary_path_len;
+       struct lttng_userspace_probe_location_function *location_function;
+       struct lttng_userspace_probe_location_function_comm location_function_comm;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) ==
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
+
+       location_function = container_of(location,
+                       struct lttng_userspace_probe_location_function,
+                       parent);
+       if (!location_function->function_name || !location_function->binary_path) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (payload && !location_function->binary_fd_handle) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       function_name_len = strlen(location_function->function_name);
+       if (function_name_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+       binary_path_len = strlen(location_function->binary_path);
+       if (binary_path_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_function_comm.function_name_len = function_name_len + 1;
+       location_function_comm.binary_path_len = binary_path_len + 1;
+
+       if (payload) {
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               &location_function_comm,
+                               sizeof(location_function_comm));
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               location_function->function_name,
+                               location_function_comm.function_name_len);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               location_function->binary_path,
+                               location_function_comm.binary_path_len);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_payload_push_fd_handle(
+                               payload, location_function->binary_fd_handle);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+       }
+       ret = sizeof(location_function_comm) +
+                       location_function_comm.function_name_len +
+                       location_function_comm.binary_path_len;
+end:
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_tracepoint_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret;
+       size_t probe_name_len, provider_name_len, binary_path_len;
+       struct lttng_userspace_probe_location_tracepoint *location_tracepoint;
+       struct lttng_userspace_probe_location_tracepoint_comm location_tracepoint_comm;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) ==
+                       LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
+
+       location_tracepoint = container_of(location,
+                       struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+       if (!location_tracepoint->probe_name ||
+                       !location_tracepoint->provider_name ||
+                       !location_tracepoint->binary_path) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (payload && !location_tracepoint->binary_fd_handle) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_name_len = strlen(location_tracepoint->probe_name);
+       if (probe_name_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       provider_name_len = strlen(location_tracepoint->provider_name);
+       if (provider_name_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       binary_path_len = strlen(location_tracepoint->binary_path);
+       if (binary_path_len == 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_tracepoint_comm.probe_name_len = probe_name_len + 1;
+       location_tracepoint_comm.provider_name_len = provider_name_len + 1;
+       location_tracepoint_comm.binary_path_len = binary_path_len + 1;
+
+       if (payload) {
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               &location_tracepoint_comm,
+                               sizeof(location_tracepoint_comm));
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               location_tracepoint->probe_name,
+                               location_tracepoint_comm.probe_name_len);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               location_tracepoint->provider_name,
+                               location_tracepoint_comm.provider_name_len);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               location_tracepoint->binary_path,
+                               location_tracepoint_comm.binary_path_len);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               ret = lttng_payload_push_fd_handle(
+                               payload, location_tracepoint->binary_fd_handle);
+               if (ret) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+       }
+
+       ret = sizeof(location_tracepoint_comm) +
+                       location_tracepoint_comm.probe_name_len +
+                       location_tracepoint_comm.provider_name_len +
+                       location_tracepoint_comm.binary_path_len;
+end:
+       return ret;
+}
+
+int lttng_userspace_probe_location_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_payload *payload)
+{
+       int ret, buffer_use = 0;
+       struct lttng_userspace_probe_location_comm location_generic_comm;
+
+       if (!location) {
+               ERR("Invalid argument(s) passed to '%s'", __FUNCTION__);
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       memset(&location_generic_comm, 0, sizeof(location_generic_comm));
+
+       location_generic_comm.type = (int8_t) location->type;
+       if (payload) {
+               ret = lttng_dynamic_buffer_append(&payload->buffer,
+                               &location_generic_comm,
+                               sizeof(location_generic_comm));
+               if (ret) {
+                       goto end;
+               }
+       }
+       buffer_use += sizeof(location_generic_comm);
+
+       switch (lttng_userspace_probe_location_get_type(location)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+               ret = lttng_userspace_probe_location_function_serialize(
+                               location, payload);
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               ret = lttng_userspace_probe_location_tracepoint_serialize(
+                               location, payload);
+               break;
+       default:
+               ERR("Unsupported probe location type");
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+       if (ret < 0) {
+               goto end;
+       }
+       buffer_use += ret;
+
+       ret = lttng_userspace_probe_location_lookup_method_serialize(
+                       location->lookup_method, payload);
+       if (ret < 0) {
+               goto end;
+       }
+       ret += buffer_use;
+end:
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_function_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_userspace_probe_location **location)
+{
+       struct lttng_userspace_probe_location_function_comm *location_function_comm;
+       const char *function_name_src, *binary_path_src;
+       char *function_name = NULL, *binary_path = NULL;
+       int ret = 0;
+       size_t expected_size;
+       struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view);
+
+       LTTNG_ASSERT(location);
+
+       if (view->buffer.size < sizeof(*location_function_comm)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_function_comm =
+                       (typeof(location_function_comm)) view->buffer.data;
+
+       expected_size = sizeof(*location_function_comm) +
+                       location_function_comm->function_name_len +
+                       location_function_comm->binary_path_len;
+
+       if (view->buffer.size < expected_size) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       function_name_src = view->buffer.data + sizeof(*location_function_comm);
+       binary_path_src = function_name_src +
+                       location_function_comm->function_name_len;
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, function_name_src,
+                           location_function_comm->function_name_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src,
+                           location_function_comm->binary_path_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       function_name = lttng_strndup(function_name_src, LTTNG_SYMBOL_NAME_LEN);
+       if (!function_name) {
+               PERROR("lttng_strndup");
+               ret = -LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX);
+       if (!binary_path) {
+               PERROR("lttng_strndup");
+               ret = -LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       *location = lttng_userspace_probe_location_function_create_no_check(
+                       binary_path, function_name, NULL, false);
+       if (!(*location)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_userspace_probe_location_function_set_binary_fd_handle(
+                       *location, binary_fd_handle);
+       if (ret) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (int) expected_size;
+end:
+       fd_handle_put(binary_fd_handle);
+       free(function_name);
+       free(binary_path);
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_tracepoint_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_userspace_probe_location **location)
+{
+       struct lttng_userspace_probe_location_tracepoint_comm *location_tracepoint_comm;
+       const char *probe_name_src, *provider_name_src, *binary_path_src;
+       char *probe_name = NULL, *provider_name = NULL, *binary_path = NULL;
+       int ret = 0;
+       size_t expected_size;
+       struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view);
+
+       LTTNG_ASSERT(location);
+
+       if (!binary_fd_handle) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (view->buffer.size < sizeof(*location_tracepoint_comm)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       location_tracepoint_comm =
+                       (typeof(location_tracepoint_comm)) view->buffer.data;
+
+       expected_size = sizeof(*location_tracepoint_comm) +
+                       location_tracepoint_comm->probe_name_len +
+                       location_tracepoint_comm->provider_name_len +
+                       location_tracepoint_comm->binary_path_len;
+
+       if (view->buffer.size < expected_size) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_name_src = view->buffer.data + sizeof(*location_tracepoint_comm);
+       provider_name_src = probe_name_src +
+                       location_tracepoint_comm->probe_name_len;
+       binary_path_src = provider_name_src +
+                       location_tracepoint_comm->provider_name_len;
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, probe_name_src,
+                           location_tracepoint_comm->probe_name_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, provider_name_src,
+                           location_tracepoint_comm->provider_name_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src,
+                           location_tracepoint_comm->binary_path_len)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_name = lttng_strndup(probe_name_src, LTTNG_SYMBOL_NAME_LEN);
+       if (!probe_name) {
+               PERROR("Failed to allocate probe name");
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+       provider_name = lttng_strndup(provider_name_src, LTTNG_SYMBOL_NAME_LEN);
+       if (!provider_name) {
+               PERROR("Failed to allocate provider name");
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX);
+       if (!binary_path) {
+               PERROR("Failed to allocate binary path");
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       *location = lttng_userspace_probe_location_tracepoint_create_no_check(
+                       binary_path, provider_name, probe_name, NULL, false);
+       if (!(*location)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
+                       *location, binary_fd_handle);
+       if (ret) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = (int) expected_size;
+end:
+       fd_handle_put(binary_fd_handle);
+       free(probe_name);
+       free(provider_name);
+       free(binary_path);
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_lookup_method_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_userspace_probe_location_lookup_method **lookup_method)
+{
+       int ret;
+       struct lttng_userspace_probe_location_lookup_method_comm *lookup_comm;
+       enum lttng_userspace_probe_location_lookup_method_type type;
+
+       LTTNG_ASSERT(view);
+       LTTNG_ASSERT(lookup_method);
+
+       if (view->buffer.size < sizeof(*lookup_comm)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       lookup_comm = (typeof(lookup_comm)) view->buffer.data;
+       type = (enum lttng_userspace_probe_location_lookup_method_type)
+                       lookup_comm->type;
+       switch (type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
+               *lookup_method = NULL;
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               *lookup_method =
+                       lttng_userspace_probe_location_lookup_method_function_elf_create();
+               if (!(*lookup_method)) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               *lookup_method =
+                       lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
+               if (!(*lookup_method)) {
+                       ret = -LTTNG_ERR_INVALID;
+                       goto end;
+               }
+               break;
+       default:
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       ret = sizeof(*lookup_comm);
+end:
+       return ret;
+}
+
+int lttng_userspace_probe_location_create_from_payload(
+               struct lttng_payload_view *view,
+               struct lttng_userspace_probe_location **location)
+{
+       struct lttng_userspace_probe_location_lookup_method *lookup_method;
+       enum lttng_userspace_probe_location_type type;
+       int consumed = 0;
+       int ret;
+       struct lttng_userspace_probe_location_comm *probe_location_comm;
+       struct lttng_payload_view probe_location_comm_view =
+                       lttng_payload_view_from_view(
+                                       view, 0, sizeof(*probe_location_comm));
+
+       LTTNG_ASSERT(view);
+       LTTNG_ASSERT(location);
+
+       lookup_method = NULL;
+
+       if (!lttng_payload_view_is_valid(&probe_location_comm_view)) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data;
+       type = (enum lttng_userspace_probe_location_type) probe_location_comm->type;
+       consumed += sizeof(*probe_location_comm);
+
+       switch (type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+       {
+               struct lttng_payload_view location_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed, -1);
+
+               ret = lttng_userspace_probe_location_function_create_from_payload(
+                               &location_view, location);
+               if (ret < 0) {
+                       goto end;
+               }
+               break;
+       }
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+       {
+               struct lttng_payload_view location_view =
+                               lttng_payload_view_from_view(view, consumed, -1);
+
+               ret = lttng_userspace_probe_location_tracepoint_create_from_payload(
+                               &location_view, location);
+               if (ret < 0) {
+                       goto end;
+               }
+               break;
+       }
+       default:
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       consumed += ret;
+       if (view->buffer.size <= consumed) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       {
+               struct lttng_payload_view lookup_method_view =
+                               lttng_payload_view_from_view(
+                                               view, consumed, -1);
+
+               ret = lttng_userspace_probe_location_lookup_method_create_from_payload(
+                               &lookup_method_view, &lookup_method);
+       }
+       if (ret < 0) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       LTTNG_ASSERT(lookup_method);
+       (*location)->lookup_method = lookup_method;
+       lookup_method = NULL;
+       ret += consumed;
+end:
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_function_set_binary_fd_handle(
+               struct lttng_userspace_probe_location *location,
+               struct fd_handle *binary_fd)
+{
+       int ret = 0;
+       struct lttng_userspace_probe_location_function *function_location;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
+
+       function_location = container_of(location,
+                       struct lttng_userspace_probe_location_function, parent);
+       fd_handle_put(function_location->binary_fd_handle);
+       fd_handle_get(binary_fd);
+       function_location->binary_fd_handle = binary_fd;
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(
+               struct lttng_userspace_probe_location *location,
+               struct fd_handle *binary_fd)
+{
+       int ret = 0;
+       struct lttng_userspace_probe_location_tracepoint *tracepoint_location;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
+
+       tracepoint_location = container_of(location,
+                       struct lttng_userspace_probe_location_tracepoint, parent);
+       fd_handle_put(tracepoint_location->binary_fd_handle);
+       fd_handle_get(binary_fd);
+       tracepoint_location->binary_fd_handle = binary_fd;
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_function_flatten(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_dynamic_buffer *buffer)
+{
+       struct lttng_userspace_probe_location_lookup_method_elf flat_lookup_method;
+       struct lttng_userspace_probe_location_function *probe_function;
+       struct lttng_userspace_probe_location_function flat_probe;
+       size_t function_name_len, binary_path_len;
+       size_t padding_needed = 0;
+       char *flat_probe_start;
+       int storage_needed = 0;
+       int ret;
+
+       LTTNG_ASSERT(location);
+
+       if (location->lookup_method && location->lookup_method->type !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       probe_function = container_of(location,
+                       struct lttng_userspace_probe_location_function,
+                       parent);
+       LTTNG_ASSERT(probe_function->function_name);
+       LTTNG_ASSERT(probe_function->binary_path);
+
+       storage_needed +=
+                       sizeof(struct lttng_userspace_probe_location_function);
+       function_name_len = strlen(probe_function->function_name) + 1;
+       binary_path_len = strlen(probe_function->binary_path) + 1;
+       storage_needed += function_name_len + binary_path_len;
+
+       /*
+        * The lookup method is aligned to 64-bit within the buffer.
+        * This is needed even if there is no lookup method since
+        * the next structure in the buffer probably needs to be
+        * aligned too (depending on the arch).
+        */
+       padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed;
+       storage_needed += padding_needed;
+
+       if (location->lookup_method) {
+               /* NOTE: elf look-up method is assumed here. */
+               storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf);
+       }
+
+       if (!buffer) {
+               ret = storage_needed;
+               goto end;
+       }
+
+       if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) {
+               ret = lttng_dynamic_buffer_set_capacity(buffer,
+                               buffer->size + storage_needed);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       memset(&flat_probe, 0, sizeof(flat_probe));
+
+       flat_probe_start = buffer->data + buffer->size;
+       flat_probe.parent.type = location->type;
+       /*
+        * The lookup method, if present, is the last element in the flat
+        * representation of the probe.
+        */
+       if (location->lookup_method) {
+               flat_probe.parent.lookup_method =
+                               (struct lttng_userspace_probe_location_lookup_method *)
+                                       (flat_probe_start + sizeof(flat_probe) +
+                                       function_name_len + binary_path_len + padding_needed);
+       } else {
+               flat_probe.parent.lookup_method = NULL;
+       }
+
+       flat_probe.function_name = flat_probe_start + sizeof(flat_probe);
+       flat_probe.binary_path = flat_probe.function_name + function_name_len;
+       flat_probe.binary_fd_handle = NULL;
+       ret = lttng_dynamic_buffer_append(buffer, &flat_probe,
+                       sizeof(flat_probe));
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_dynamic_buffer_append(buffer,
+                       probe_function->function_name, function_name_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buffer,
+                       probe_function->binary_path, binary_path_len);
+       if (ret) {
+               goto end;
+       }
+
+       /* Insert padding before the lookup method. */
+       ret = lttng_dynamic_buffer_set_size(buffer,
+                       buffer->size + padding_needed);
+       if (ret) {
+               goto end;
+       }
+
+       if (!location->lookup_method) {
+               /* Not an error, the default method is used. */
+               ret = storage_needed;
+               goto end;
+       }
+
+       memset(&flat_lookup_method, 0, sizeof(flat_lookup_method));
+       flat_lookup_method.parent.type =
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF;
+       ret = lttng_dynamic_buffer_append(buffer,
+                       &flat_lookup_method, sizeof(flat_lookup_method));
+       if (ret) {
+               goto end;
+       }
+       ret = storage_needed;
+end:
+       return ret;
+}
+
+static
+int lttng_userspace_probe_location_tracepoint_flatten(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_dynamic_buffer *buffer)
+{
+       struct lttng_userspace_probe_location_lookup_method_sdt flat_lookup_method;
+       struct lttng_userspace_probe_location_tracepoint *probe_tracepoint;
+       struct lttng_userspace_probe_location_tracepoint flat_probe;
+       size_t probe_name_len, provider_name_len, binary_path_len;
+       size_t padding_needed = 0;
+       int storage_needed = 0;
+       char *flat_probe_start;
+       int ret = 0;
+
+       LTTNG_ASSERT(location);
+
+       /* Only SDT tracepoints are supported at the moment */
+       if (location->lookup_method && location->lookup_method->type !=
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+       probe_tracepoint = container_of(location,
+                       struct lttng_userspace_probe_location_tracepoint,
+                       parent);
+       LTTNG_ASSERT(probe_tracepoint->probe_name);
+       LTTNG_ASSERT(probe_tracepoint->provider_name);
+       LTTNG_ASSERT(probe_tracepoint->binary_path);
+
+       /* Compute the storage space needed to flatten the probe location */
+       storage_needed += sizeof(struct lttng_userspace_probe_location_tracepoint);
+
+       probe_name_len = strlen(probe_tracepoint->probe_name) + 1;
+       provider_name_len = strlen(probe_tracepoint->provider_name) + 1;
+       binary_path_len = strlen(probe_tracepoint->binary_path) + 1;
+
+       storage_needed += probe_name_len + provider_name_len + binary_path_len;
+
+       /*
+        * The lookup method is aligned to 64-bit within the buffer.
+        * This is needed even if there is no lookup method since
+        * the next structure in the buffer probably needs to be
+        * aligned too (depending on the arch).
+        */
+       padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed;
+       storage_needed += padding_needed;
+
+       if (location->lookup_method) {
+               /* NOTE: elf look-up method is assumed here. */
+               storage_needed +=
+                       sizeof(struct lttng_userspace_probe_location_lookup_method_elf);
+       }
+
+       /*
+        * If the caller set buffer to NULL, return the size of the needed buffer.
+        */
+       if (!buffer) {
+               ret = storage_needed;
+               goto end;
+       }
+
+       if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) {
+               ret = lttng_dynamic_buffer_set_capacity(buffer,
+                               buffer->size + storage_needed);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       memset(&flat_probe, 0, sizeof(flat_probe));
+
+       flat_probe_start = buffer->data + buffer->size;
+       flat_probe.parent.type = location->type;
+
+       /*
+        * The lookup method, if present, is the last element in the flat
+        * representation of the probe.
+        */
+       if (location->lookup_method) {
+               flat_probe.parent.lookup_method =
+                               (struct lttng_userspace_probe_location_lookup_method *)
+                                       (flat_probe_start + sizeof(flat_probe) +
+                                       probe_name_len + provider_name_len +
+                                       binary_path_len + padding_needed);
+       } else {
+               flat_probe.parent.lookup_method = NULL;
+       }
+
+       flat_probe.probe_name = flat_probe_start + sizeof(flat_probe);
+       flat_probe.provider_name = flat_probe.probe_name + probe_name_len;
+       flat_probe.binary_path = flat_probe.provider_name + provider_name_len;
+       flat_probe.binary_fd_handle = NULL;
+       ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe));
+       if (ret) {
+               goto end;
+       }
+
+       /* Append all the fields to the buffer */
+       ret = lttng_dynamic_buffer_append(buffer,
+                       probe_tracepoint->probe_name, probe_name_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buffer,
+                       probe_tracepoint->provider_name, provider_name_len);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_dynamic_buffer_append(buffer,
+                       probe_tracepoint->binary_path, binary_path_len);
+       if (ret) {
+               goto end;
+       }
+
+       /* Insert padding before the lookup method. */
+       ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed);
+       if (ret) {
+               goto end;
+       }
+
+       if (!location->lookup_method) {
+               /* Not an error, the default method is used. */
+               ret = storage_needed;
+               goto end;
+       }
+
+       memset(&flat_lookup_method, 0, sizeof(flat_lookup_method));
+
+       flat_lookup_method.parent.type =
+                       LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT;
+       ret = lttng_dynamic_buffer_append(buffer,
+                       &flat_lookup_method, sizeof(flat_lookup_method));
+       if (ret) {
+               goto end;
+       }
+       ret = storage_needed;
+end:
+       return ret;
+}
+
+int lttng_userspace_probe_location_flatten(
+               const struct lttng_userspace_probe_location *location,
+               struct lttng_dynamic_buffer *buffer)
+{
+       int ret;
+       if (!location) {
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+       /* Only types currently supported. */
+       switch (location->type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+               ret = lttng_userspace_probe_location_function_flatten(location, buffer);
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               ret = lttng_userspace_probe_location_tracepoint_flatten(location, buffer);
+               break;
+       default:
+               ret = -LTTNG_ERR_INVALID;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy(
+               const struct lttng_userspace_probe_location *location)
+{
+       struct lttng_userspace_probe_location *new_location = NULL;
+       enum lttng_userspace_probe_location_type type;
+
+       if (!location) {
+               goto err;
+       }
+
+       type = lttng_userspace_probe_location_get_type(location);
+       switch (type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+               new_location =
+                       lttng_userspace_probe_location_function_copy(location);
+               if (!new_location) {
+                       goto err;
+               }
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               new_location =
+                       lttng_userspace_probe_location_tracepoint_copy(location);
+               if (!new_location) {
+                       goto err;
+               }
+               break;
+       default:
+               new_location = NULL;
+               goto err;
+       }
+err:
+       return new_location;
+}
+
+bool lttng_userspace_probe_location_lookup_method_is_equal(
+               const struct lttng_userspace_probe_location_lookup_method *a,
+               const struct lttng_userspace_probe_location_lookup_method *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       is_equal = true;
+end:
+       return is_equal;
+}
+
+bool lttng_userspace_probe_location_is_equal(
+               const struct lttng_userspace_probe_location *a,
+               const struct lttng_userspace_probe_location *b)
+{
+       bool is_equal = false;
+
+       if (!a || !b) {
+               goto end;
+       }
+
+       if (a == b) {
+               is_equal = true;
+               goto end;
+       }
+
+       if (!lttng_userspace_probe_location_lookup_method_is_equal(
+                           a->lookup_method, b->lookup_method)) {
+               goto end;
+       }
+
+       if (a->type != b->type) {
+               goto end;
+       }
+
+       is_equal = a->equal ? a->equal(a, b) : true;
+end:
+       return is_equal;
+}
+
+unsigned long lttng_userspace_probe_location_hash(
+               const struct lttng_userspace_probe_location *location)
+{
+       return location->hash(location);
+}
+
+enum lttng_error_code lttng_userspace_probe_location_mi_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct mi_writer *writer)
+{
+       typedef enum lttng_error_code (*mi_fp)(
+                       const struct lttng_userspace_probe_location *,
+                       struct mi_writer *);
+
+       int ret;
+       enum lttng_error_code ret_code;
+       mi_fp mi_function = NULL;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+
+       switch (lttng_userspace_probe_location_get_type(location)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION:
+               mi_function = lttng_userspace_probe_location_function_mi_serialize;
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT:
+               mi_function = lttng_userspace_probe_location_tracepoint_mi_serialize;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       /* Open userspace probe location element. */
+       ret = mi_lttng_writer_open_element(
+                       writer, mi_lttng_element_userspace_probe_location);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Underlying user space probe location. */
+       ret_code = mi_function(location, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close userspace probe location element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize(
+               const struct lttng_userspace_probe_location_lookup_method
+                               *method,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *type_element_str;
+
+       LTTNG_ASSERT(method);
+       LTTNG_ASSERT(writer);
+
+       switch (lttng_userspace_probe_location_lookup_method_get_type(method)) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT:
+               type_element_str =
+                               mi_lttng_element_userspace_probe_location_lookup_method_function_default;
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+               type_element_str =
+                               mi_lttng_element_userspace_probe_location_lookup_method_function_elf;
+               break;
+       case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+               type_element_str =
+                               mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       /* Open userspace probe location lookup method element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_userspace_probe_location_lookup_method);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* User space probe location lookup method empty element. */
+       ret = mi_lttng_writer_open_element(writer, type_element_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Close userspace probe location lookup method element. */
+       ret = mi_lttng_close_multi_element(writer, 2);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *probe_name = NULL;
+       const char *provider_name = NULL;
+       const char *binary_path = NULL;
+       const struct lttng_userspace_probe_location_lookup_method
+                       *lookup_method = NULL;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+
+       probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(
+                       location);
+       provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(
+                       location);
+       binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(
+                       location);
+       lookup_method = lttng_userspace_probe_location_tracepoint_get_lookup_method(
+                       location);
+
+       /* Open userspace probe location tracepoint element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_userspace_probe_location_tracepoint);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Probe name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_tracepoint_probe_name,
+                       probe_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Provider name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_tracepoint_provider_name,
+                       provider_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Binary path. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_binary_path,
+                       binary_path);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* The lookup method. */
+       ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize(
+                       lookup_method, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close userspace probe location tracepoint. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
+
+static enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize(
+               const struct lttng_userspace_probe_location *location,
+               struct mi_writer *writer)
+{
+       int ret;
+       enum lttng_error_code ret_code;
+       const char *function_name = NULL;
+       const char *binary_path = NULL;
+       const char *instrumentation_type_str = NULL;
+       enum lttng_userspace_probe_location_function_instrumentation_type
+                       instrumentation_type;
+       const struct lttng_userspace_probe_location_lookup_method
+                       *lookup_method = NULL;
+
+       LTTNG_ASSERT(location);
+       LTTNG_ASSERT(writer);
+
+       function_name = lttng_userspace_probe_location_function_get_function_name(
+                       location);
+       binary_path = lttng_userspace_probe_location_function_get_binary_path(
+                       location);
+       instrumentation_type =
+                       lttng_userspace_probe_location_function_get_instrumentation_type(
+                                       location);
+       lookup_method = lttng_userspace_probe_location_function_get_lookup_method(
+                       location);
+
+       switch (instrumentation_type) {
+       case LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY:
+               instrumentation_type_str =
+                               mi_lttng_userspace_probe_location_function_instrumentation_type_entry;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       /* Open userspace probe location function element. */
+       ret = mi_lttng_writer_open_element(writer,
+                       mi_lttng_element_userspace_probe_location_function);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Function name. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_function_name,
+                       function_name);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Binary path. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_binary_path,
+                       binary_path);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* Instrumentation type. */
+       ret = mi_lttng_writer_write_element_string(writer,
+                       mi_lttng_element_userspace_probe_location_function_instrumentation_type,
+                       instrumentation_type_str);
+       if (ret) {
+               goto mi_error;
+       }
+
+       /* The lookup method. */
+       ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize(
+                       lookup_method, writer);
+       if (ret_code != LTTNG_OK) {
+               goto end;
+       }
+
+       /* Close userspace probe location function element. */
+       ret = mi_lttng_writer_close_element(writer);
+       if (ret) {
+               goto mi_error;
+       }
+
+       ret_code = LTTNG_OK;
+       goto end;
+
+mi_error:
+       ret_code = LTTNG_ERR_MI_IO_FAIL;
+end:
+       return ret_code;
+}
diff --git a/src/common/utils.c b/src/common/utils.c
deleted file mode 100644 (file)
index eebed2a..0000000
+++ /dev/null
@@ -1,1675 +0,0 @@
-/*
- * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
- * Copyright (C) 2013 Raphaël Beamonte <raphael.beamonte@gmail.com>
- * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#include "common/macros.h"
-#define _LGPL_SOURCE
-#include <ctype.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/file.h>
-#include <unistd.h>
-
-#include <common/common.h>
-#include <common/readwrite.h>
-#include <common/runas.h>
-#include <common/compat/getenv.h>
-#include <common/compat/string.h>
-#include <common/compat/dirent.h>
-#include <common/compat/directory-handle.h>
-#include <common/dynamic-buffer.h>
-#include <common/string-utils/format.h>
-#include <lttng/constant.h>
-
-#include "utils.h"
-#include "defaults.h"
-#include "time.h"
-
-#define PROC_MEMINFO_PATH               "/proc/meminfo"
-#define PROC_MEMINFO_MEMAVAILABLE_LINE  "MemAvailable:"
-#define PROC_MEMINFO_MEMTOTAL_LINE      "MemTotal:"
-
-/* The length of the longest field of `/proc/meminfo`. */
-#define PROC_MEMINFO_FIELD_MAX_NAME_LEN        20
-
-#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20)
-#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19"
-#else
-#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1)
-#endif
-
-#define FALLBACK_USER_BUFLEN 16384
-#define FALLBACK_GROUP_BUFLEN 16384
-
-/*
- * Return a partial realpath(3) of the path even if the full path does not
- * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist
- * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with
- * /test2/test3 then returned. In normal time, realpath(3) fails if the end
- * point directory does not exist.
- *
- * Return a newly-allocated string.
- */
-static
-char *utils_partial_realpath(const char *path)
-{
-       char *cut_path = NULL, *try_path = NULL, *try_path_prev = NULL;
-       const char *next, *prev, *end;
-       char *resolved_path = NULL;
-
-       /* Safety net */
-       if (path == NULL) {
-               goto error;
-       }
-
-       /*
-        * Identify the end of the path, we don't want to treat the
-        * last char if it is a '/', we will just keep it on the side
-        * to be added at the end, and return a value coherent with
-        * the path given as argument
-        */
-       end = path + strlen(path);
-       if (*(end-1) == '/') {
-               end--;
-       }
-
-       /* Initiate the values of the pointers before looping */
-       next = path;
-       prev = next;
-       /* Only to ensure try_path is not NULL to enter the while */
-       try_path = (char *)next;
-
-       /* Resolve the canonical path of the first part of the path */
-       while (try_path != NULL && next != end) {
-               char *try_path_buf = NULL;
-
-               /*
-                * If there is not any '/' left, we want to try with
-                * the full path
-                */
-               next = strpbrk(next + 1, "/");
-               if (next == NULL) {
-                       next = end;
-               }
-
-               /* Cut the part we will be trying to resolve */
-               cut_path = lttng_strndup(path, next - path);
-               if (cut_path == NULL) {
-                       PERROR("lttng_strndup");
-                       goto error;
-               }
-
-               try_path_buf = zmalloc(LTTNG_PATH_MAX);
-               if (!try_path_buf) {
-                       PERROR("zmalloc");
-                       goto error;
-               }
-
-               /* Try to resolve this part */
-               try_path = realpath((char *) cut_path, try_path_buf);
-               if (try_path == NULL) {
-                       free(try_path_buf);
-                       /*
-                        * There was an error, we just want to be assured it
-                        * is linked to an unexistent directory, if it's another
-                        * reason, we spawn an error
-                        */
-                       switch (errno) {
-                       case ENOENT:
-                               /* Ignore the error */
-                               break;
-                       default:
-                               PERROR("realpath (partial_realpath)");
-                               goto error;
-                               break;
-                       }
-               } else {
-                       /* Save the place we are before trying the next step */
-                       try_path_buf = NULL;
-                       free(try_path_prev);
-                       try_path_prev = try_path;
-                       prev = next;
-               }
-
-               /* Free the allocated memory */
-               free(cut_path);
-               cut_path = NULL;
-       }
-
-       /* Allocate memory for the resolved path. */
-       resolved_path = zmalloc(LTTNG_PATH_MAX);
-       if (resolved_path == NULL) {
-               PERROR("zmalloc resolved path");
-               goto error;
-       }
-
-       /*
-        * If we were able to solve at least partially the path, we can concatenate
-        * what worked and what didn't work
-        */
-       if (try_path_prev != NULL) {
-               /* If we risk to concatenate two '/', we remove one of them */
-               if (try_path_prev[strlen(try_path_prev) - 1] == '/' && prev[0] == '/') {
-                       try_path_prev[strlen(try_path_prev) - 1] = '\0';
-               }
-
-               /*
-                * Duplicate the memory used by prev in case resolved_path and
-                * path are pointers for the same memory space
-                */
-               cut_path = strdup(prev);
-               if (cut_path == NULL) {
-                       PERROR("strdup");
-                       goto error;
-               }
-
-               /* Concatenate the strings */
-               snprintf(resolved_path, LTTNG_PATH_MAX, "%s%s",
-                               try_path_prev, cut_path);
-
-               /* Free the allocated memory */
-               free(cut_path);
-               free(try_path_prev);
-               cut_path = NULL;
-               try_path_prev = NULL;
-       /*
-        * Else, we just copy the path in our resolved_path to
-        * return it as is
-        */
-       } else {
-               strncpy(resolved_path, path, LTTNG_PATH_MAX);
-       }
-
-       /* Then we return the 'partially' resolved path */
-       return resolved_path;
-
-error:
-       free(resolved_path);
-       free(cut_path);
-       free(try_path);
-       if (try_path_prev != try_path) {
-               free(try_path_prev);
-       }
-       return NULL;
-}
-
-static
-int expand_double_slashes_dot_and_dotdot(char *path)
-{
-       size_t expanded_path_len, path_len;
-       const char *curr_char, *path_last_char, *next_slash, *prev_slash;
-
-       path_len = strlen(path);
-       path_last_char = &path[path_len];
-
-       if (path_len == 0) {
-               goto error;
-       }
-
-       expanded_path_len = 0;
-
-       /* We iterate over the provided path to expand the "//", "../" and "./" */
-       for (curr_char = path; curr_char <= path_last_char; curr_char = next_slash + 1) {
-               /* Find the next forward slash. */
-               size_t curr_token_len;
-
-               if (curr_char == path_last_char) {
-                       expanded_path_len++;
-                       break;
-               }
-
-               next_slash = memchr(curr_char, '/', path_last_char - curr_char);
-               if (next_slash == NULL) {
-                       /* Reached the end of the provided path. */
-                       next_slash = path_last_char;
-               }
-
-               /* Compute how long is the previous token. */
-               curr_token_len = next_slash - curr_char;
-               switch(curr_token_len) {
-               case 0:
-                       /*
-                        * The pointer has not move meaning that curr_char is
-                        * pointing to a slash. It that case there is no token
-                        * to copy, so continue the iteration to find the next
-                        * token
-                        */
-                       continue;
-               case 1:
-                       /*
-                        * The pointer moved 1 character. Check if that
-                        * character is a dot ('.'), if it is: omit it, else
-                        * copy the token to the normalized path.
-                        */
-                       if (curr_char[0] == '.') {
-                               continue;
-                       }
-                       break;
-               case 2:
-                       /*
-                        * The pointer moved 2 characters. Check if these
-                        * characters are double dots ('..'). If that is the
-                        * case, we need to remove the last token of the
-                        * normalized path.
-                        */
-                       if (curr_char[0] == '.' && curr_char[1] == '.') {
-                               /*
-                                * Find the previous path component by
-                                * using the memrchr function to find the
-                                * previous forward slash and substract that
-                                * len to the resulting path.
-                                */
-                               prev_slash = lttng_memrchr(path, '/', expanded_path_len);
-                               /*
-                                * If prev_slash is NULL, we reached the
-                                * beginning of the path. We can't go back any
-                                * further.
-                                */
-                               if (prev_slash != NULL) {
-                                       expanded_path_len = prev_slash - path;
-                               }
-                               continue;
-                       }
-                       break;
-               default:
-                       break;
-               }
-
-               /*
-                * Copy the current token which is neither a '.' nor a '..'.
-                */
-               path[expanded_path_len++] = '/';
-               memmove(&path[expanded_path_len], curr_char, curr_token_len);
-               expanded_path_len += curr_token_len;
-       }
-
-       if (expanded_path_len == 0) {
-               path[expanded_path_len++] = '/';
-       }
-
-       path[expanded_path_len] = '\0';
-       return 0;
-error:
-       return -1;
-}
-
-/*
- * Make a full resolution of the given path even if it doesn't exist.
- * This function uses the utils_partial_realpath function to resolve
- * symlinks and relatives paths at the start of the string, and
- * implements functionnalities to resolve the './' and '../' strings
- * in the middle of a path. This function is only necessary because
- * realpath(3) does not accept to resolve unexistent paths.
- * The returned string was allocated in the function, it is thus of
- * the responsibility of the caller to free this memory.
- */
-static
-char *_utils_expand_path(const char *path, bool keep_symlink)
-{
-       int ret;
-       char *absolute_path = NULL;
-       char *last_token;
-       bool is_dot, is_dotdot;
-
-       /* Safety net */
-       if (path == NULL) {
-               goto error;
-       }
-
-       /* Allocate memory for the absolute_path */
-       absolute_path = zmalloc(LTTNG_PATH_MAX);
-       if (absolute_path == NULL) {
-               PERROR("zmalloc expand path");
-               goto error;
-       }
-
-       if (path[0] == '/') {
-               ret = lttng_strncpy(absolute_path, path, LTTNG_PATH_MAX);
-               if (ret) {
-                       ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX);
-                       goto error;
-               }
-       } else {
-               /*
-                * This is a relative path. We need to get the present working
-                * directory and start the path walk from there.
-                */
-               char current_working_dir[LTTNG_PATH_MAX];
-               char *cwd_ret;
-
-               cwd_ret = getcwd(current_working_dir, sizeof(current_working_dir));
-               if (!cwd_ret) {
-                       goto error;
-               }
-               /*
-                * Get the number of character in the CWD and allocate an array
-                * to can hold it and the path provided by the caller.
-                */
-               ret = snprintf(absolute_path, LTTNG_PATH_MAX, "%s/%s",
-                               current_working_dir, path);
-               if (ret >= LTTNG_PATH_MAX) {
-                       ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes",
-                                       current_working_dir, path, LTTNG_PATH_MAX);
-                       goto error;
-               }
-       }
-
-       if (keep_symlink) {
-               /* Resolve partially our path */
-               char *new_absolute_path = utils_partial_realpath(absolute_path);
-               if (!new_absolute_path) {
-                       goto error;
-               }
-
-               free(absolute_path);
-               absolute_path = new_absolute_path;
-       }
-
-       ret = expand_double_slashes_dot_and_dotdot(absolute_path);
-       if (ret) {
-               goto error;
-       }
-
-       /* Identify the last token */
-       last_token = strrchr(absolute_path, '/');
-
-       /* Verify that this token is not a relative path */
-       is_dotdot = (strcmp(last_token, "/..") == 0);
-       is_dot = (strcmp(last_token, "/.") == 0);
-
-       /* If it is, take action */
-       if (is_dot || is_dotdot) {
-               /* For both, remove this token */
-               *last_token = '\0';
-
-               /* If it was a reference to parent directory, go back one more time */
-               if (is_dotdot) {
-                       last_token = strrchr(absolute_path, '/');
-
-                       /* If there was only one level left, we keep the first '/' */
-                       if (last_token == absolute_path) {
-                               last_token++;
-                       }
-
-                       *last_token = '\0';
-               }
-       }
-
-       return absolute_path;
-
-error:
-       free(absolute_path);
-       return NULL;
-}
-char *utils_expand_path(const char *path)
-{
-       return _utils_expand_path(path, true);
-}
-
-char *utils_expand_path_keep_symlink(const char *path)
-{
-       return _utils_expand_path(path, false);
-}
-/*
- * Create a pipe in dst.
- */
-int utils_create_pipe(int *dst)
-{
-       int ret;
-
-       if (dst == NULL) {
-               return -1;
-       }
-
-       ret = pipe(dst);
-       if (ret < 0) {
-               PERROR("create pipe");
-       }
-
-       return ret;
-}
-
-/*
- * Create pipe and set CLOEXEC flag to both fd.
- *
- * Make sure the pipe opened by this function are closed at some point. Use
- * utils_close_pipe().
- */
-int utils_create_pipe_cloexec(int *dst)
-{
-       int ret, i;
-
-       if (dst == NULL) {
-               return -1;
-       }
-
-       ret = utils_create_pipe(dst);
-       if (ret < 0) {
-               goto error;
-       }
-
-       for (i = 0; i < 2; i++) {
-               ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
-               if (ret < 0) {
-                       PERROR("fcntl pipe cloexec");
-                       goto error;
-               }
-       }
-
-error:
-       return ret;
-}
-
-/*
- * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
- *
- * Make sure the pipe opened by this function are closed at some point. Use
- * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
- * support OSes other than Linux 2.6.23+.
- */
-int utils_create_pipe_cloexec_nonblock(int *dst)
-{
-       int ret, i;
-
-       if (dst == NULL) {
-               return -1;
-       }
-
-       ret = utils_create_pipe(dst);
-       if (ret < 0) {
-               goto error;
-       }
-
-       for (i = 0; i < 2; i++) {
-               ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
-               if (ret < 0) {
-                       PERROR("fcntl pipe cloexec");
-                       goto error;
-               }
-               /*
-                * Note: we override any flag that could have been
-                * previously set on the fd.
-                */
-               ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
-               if (ret < 0) {
-                       PERROR("fcntl pipe nonblock");
-                       goto error;
-               }
-       }
-
-error:
-       return ret;
-}
-
-/*
- * Close both read and write side of the pipe.
- */
-void utils_close_pipe(int *src)
-{
-       int i, ret;
-
-       if (src == NULL) {
-               return;
-       }
-
-       for (i = 0; i < 2; i++) {
-               /* Safety check */
-               if (src[i] < 0) {
-                       continue;
-               }
-
-               ret = close(src[i]);
-               if (ret) {
-                       PERROR("close pipe");
-               }
-               src[i] = -1;
-       }
-}
-
-/*
- * Create a new string using two strings range.
- */
-char *utils_strdupdelim(const char *begin, const char *end)
-{
-       char *str;
-
-       str = zmalloc(end - begin + 1);
-       if (str == NULL) {
-               PERROR("zmalloc strdupdelim");
-               goto error;
-       }
-
-       memcpy(str, begin, end - begin);
-       str[end - begin] = '\0';
-
-error:
-       return str;
-}
-
-/*
- * Set CLOEXEC flag to the give file descriptor.
- */
-int utils_set_fd_cloexec(int fd)
-{
-       int ret;
-
-       if (fd < 0) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
-       if (ret < 0) {
-               PERROR("fcntl cloexec");
-               ret = -errno;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Create pid file to the given path and filename.
- */
-int utils_create_pid_file(pid_t pid, const char *filepath)
-{
-       int ret;
-       FILE *fp;
-
-       LTTNG_ASSERT(filepath);
-
-       fp = fopen(filepath, "w");
-       if (fp == NULL) {
-               PERROR("open pid file %s", filepath);
-               ret = -1;
-               goto error;
-       }
-
-       ret = fprintf(fp, "%d\n", (int) pid);
-       if (ret < 0) {
-               PERROR("fprintf pid file");
-               goto error;
-       }
-
-       if (fclose(fp)) {
-               PERROR("fclose");
-       }
-       DBG("Pid %d written in file %s", (int) pid, filepath);
-       ret = 0;
-error:
-       return ret;
-}
-
-/*
- * Create lock file to the given path and filename.
- * Returns the associated file descriptor, -1 on error.
- */
-int utils_create_lock_file(const char *filepath)
-{
-       int ret;
-       int fd;
-       struct flock lock;
-
-       LTTNG_ASSERT(filepath);
-
-       memset(&lock, 0, sizeof(lock));
-       fd = open(filepath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR |
-               S_IRGRP | S_IWGRP);
-       if (fd < 0) {
-               PERROR("open lock file %s", filepath);
-               fd = -1;
-               goto error;
-       }
-
-       /*
-        * Attempt to lock the file. If this fails, there is
-        * already a process using the same lock file running
-        * and we should exit.
-        */
-       lock.l_whence = SEEK_SET;
-       lock.l_type = F_WRLCK;
-
-       ret = fcntl(fd, F_SETLK, &lock);
-       if (ret == -1) {
-               PERROR("fcntl lock file");
-               ERR("Could not get lock file %s, another instance is running.",
-                       filepath);
-               if (close(fd)) {
-                       PERROR("close lock file");
-               }
-               fd = ret;
-               goto error;
-       }
-
-error:
-       return fd;
-}
-
-/*
- * Create directory using the given path and mode.
- *
- * On success, return 0 else a negative error code.
- */
-int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
-{
-       int ret;
-       struct lttng_directory_handle *handle;
-       const struct lttng_credentials creds = {
-               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
-               .gid = LTTNG_OPTIONAL_INIT_VALUE(gid),
-       };
-
-       handle = lttng_directory_handle_create(NULL);
-       if (!handle) {
-               ret = -1;
-               goto end;
-       }
-       ret = lttng_directory_handle_create_subdirectory_as_user(
-                       handle, path, mode,
-                       (uid >= 0 || gid >= 0) ? &creds : NULL);
-end:
-       lttng_directory_handle_put(handle);
-       return ret;
-}
-
-/*
- * Recursively create directory using the given path and mode, under the
- * provided uid and gid.
- *
- * On success, return 0 else a negative error code.
- */
-int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid)
-{
-       int ret;
-       struct lttng_directory_handle *handle;
-       const struct lttng_credentials creds = {
-               .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
-               .gid = LTTNG_OPTIONAL_INIT_VALUE(gid),
-       };
-
-       handle = lttng_directory_handle_create(NULL);
-       if (!handle) {
-               ret = -1;
-               goto end;
-       }
-       ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
-                       handle, path, mode,
-                       (uid >= 0 || gid >= 0) ? &creds : NULL);
-end:
-       lttng_directory_handle_put(handle);
-       return ret;
-}
-
-/*
- * out_stream_path is the output parameter.
- *
- * Return 0 on success or else a negative value.
- */
-int utils_stream_file_path(const char *path_name, const char *file_name,
-               uint64_t size, uint64_t count, const char *suffix,
-               char *out_stream_path, size_t stream_path_len)
-{
-       int ret;
-       char count_str[MAX_INT_DEC_LEN(count) + 1] = {};
-       const char *path_separator;
-
-       if (path_name && (path_name[0] == '\0' ||
-                       path_name[strlen(path_name) - 1] == '/')) {
-               path_separator = "";
-       } else {
-               path_separator = "/";
-       }
-
-       path_name = path_name ? : "";
-       suffix = suffix ? : "";
-       if (size > 0) {
-               ret = snprintf(count_str, sizeof(count_str), "_%" PRIu64,
-                               count);
-               LTTNG_ASSERT(ret > 0 && ret < sizeof(count_str));
-       }
-
-       ret = snprintf(out_stream_path, stream_path_len, "%s%s%s%s%s",
-                       path_name, path_separator, file_name, count_str,
-                       suffix);
-       if (ret < 0 || ret >= stream_path_len) {
-               ERR("Truncation occurred while formatting stream path");
-               ret = -1;
-       } else {
-               ret = 0;
-       }
-       return ret;
-}
-
-/**
- * Parse a string that represents a size in human readable format. It
- * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'.
- *
- * The suffix multiply the integer by:
- * 'k': 1024
- * 'M': 1024^2
- * 'G': 1024^3
- *
- * @param str  The string to parse.
- * @param size Pointer to a uint64_t that will be filled with the
- *             resulting size.
- *
- * @return 0 on success, -1 on failure.
- */
-int utils_parse_size_suffix(const char * const str, uint64_t * const size)
-{
-       int ret;
-       uint64_t base_size;
-       long shift = 0;
-       const char *str_end;
-       char *num_end;
-
-       if (!str) {
-               DBG("utils_parse_size_suffix: received a NULL string.");
-               ret = -1;
-               goto end;
-       }
-
-       /* strtoull will accept a negative number, but we don't want to. */
-       if (strchr(str, '-') != NULL) {
-               DBG("utils_parse_size_suffix: invalid size string, should not contain '-'.");
-               ret = -1;
-               goto end;
-       }
-
-       /* str_end will point to the \0 */
-       str_end = str + strlen(str);
-       errno = 0;
-       base_size = strtoull(str, &num_end, 0);
-       if (errno != 0) {
-               PERROR("utils_parse_size_suffix strtoull");
-               ret = -1;
-               goto end;
-       }
-
-       if (num_end == str) {
-               /* strtoull parsed nothing, not good. */
-               DBG("utils_parse_size_suffix: strtoull had nothing good to parse.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Check if a prefix is present. */
-       switch (*num_end) {
-       case 'G':
-               shift = GIBI_LOG2;
-               num_end++;
-               break;
-       case 'M': /*  */
-               shift = MEBI_LOG2;
-               num_end++;
-               break;
-       case 'K':
-       case 'k':
-               shift = KIBI_LOG2;
-               num_end++;
-               break;
-       case '\0':
-               break;
-       default:
-               DBG("utils_parse_size_suffix: invalid suffix.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Check for garbage after the valid input. */
-       if (num_end != str_end) {
-               DBG("utils_parse_size_suffix: Garbage after size string.");
-               ret = -1;
-               goto end;
-       }
-
-       *size = base_size << shift;
-
-       /* Check for overflow */
-       if ((*size >> shift) != base_size) {
-               DBG("utils_parse_size_suffix: oops, overflow detected.");
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-/**
- * Parse a string that represents a time in human readable format. It
- * supports decimal integers suffixed by:
- *     "us" for microsecond,
- *     "ms" for millisecond,
- *     "s"  for second,
- *     "m"  for minute,
- *     "h"  for hour
- *
- * The suffix multiply the integer by:
- *     "us" : 1
- *     "ms" : 1000
- *     "s"  : 1000000
- *     "m"  : 60000000
- *     "h"  : 3600000000
- *
- * Note that unit-less numbers are assumed to be microseconds.
- *
- * @param str          The string to parse, assumed to be NULL-terminated.
- * @param time_us      Pointer to a uint64_t that will be filled with the
- *                     resulting time in microseconds.
- *
- * @return 0 on success, -1 on failure.
- */
-int utils_parse_time_suffix(char const * const str, uint64_t * const time_us)
-{
-       int ret;
-       uint64_t base_time;
-       uint64_t multiplier = 1;
-       const char *str_end;
-       char *num_end;
-
-       if (!str) {
-               DBG("utils_parse_time_suffix: received a NULL string.");
-               ret = -1;
-               goto end;
-       }
-
-       /* strtoull will accept a negative number, but we don't want to. */
-       if (strchr(str, '-') != NULL) {
-               DBG("utils_parse_time_suffix: invalid time string, should not contain '-'.");
-               ret = -1;
-               goto end;
-       }
-
-       /* str_end will point to the \0 */
-       str_end = str + strlen(str);
-       errno = 0;
-       base_time = strtoull(str, &num_end, 10);
-       if (errno != 0) {
-               PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str);
-               ret = -1;
-               goto end;
-       }
-
-       if (num_end == str) {
-               /* strtoull parsed nothing, not good. */
-               DBG("utils_parse_time_suffix: strtoull had nothing good to parse.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Check if a prefix is present. */
-       switch (*num_end) {
-       case 'u':
-               /*
-                * Microsecond (us)
-                *
-                * Skip the "us" if the string matches the "us" suffix,
-                * otherwise let the check for the end of the string handle
-                * the error reporting.
-                */
-               if (*(num_end + 1) == 's') {
-                       num_end += 2;
-               }
-               break;
-       case 'm':
-               if (*(num_end + 1) == 's') {
-                       /* Millisecond (ms) */
-                       multiplier = USEC_PER_MSEC;
-                       /* Skip the 's' */
-                       num_end++;
-               } else {
-                       /* Minute (m) */
-                       multiplier = USEC_PER_MINUTE;
-               }
-               num_end++;
-               break;
-       case 's':
-               /* Second */
-               multiplier = USEC_PER_SEC;
-               num_end++;
-               break;
-       case 'h':
-               /* Hour */
-               multiplier = USEC_PER_HOURS;
-               num_end++;
-               break;
-       case '\0':
-               break;
-       default:
-               DBG("utils_parse_time_suffix: invalid suffix.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Check for garbage after the valid input. */
-       if (num_end != str_end) {
-               DBG("utils_parse_time_suffix: Garbage after time string.");
-               ret = -1;
-               goto end;
-       }
-
-       *time_us = base_time * multiplier;
-
-       /* Check for overflow */
-       if ((*time_us / multiplier) != base_time) {
-               DBG("utils_parse_time_suffix: oops, overflow detected.");
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-end:
-       return ret;
-}
-
-/*
- * fls: returns the position of the most significant bit.
- * Returns 0 if no bit is set, else returns the position of the most
- * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
- */
-#if defined(__i386) || defined(__x86_64)
-static inline unsigned int fls_u32(uint32_t x)
-{
-       int r;
-
-       asm("bsrl %1,%0\n\t"
-               "jnz 1f\n\t"
-               "movl $-1,%0\n\t"
-               "1:\n\t"
-               : "=r" (r) : "rm" (x));
-       return r + 1;
-}
-#define HAS_FLS_U32
-#endif
-
-#if defined(__x86_64) && defined(__LP64__)
-static inline
-unsigned int fls_u64(uint64_t x)
-{
-       long r;
-
-       asm("bsrq %1,%0\n\t"
-           "jnz 1f\n\t"
-           "movq $-1,%0\n\t"
-           "1:\n\t"
-           : "=r" (r) : "rm" (x));
-       return r + 1;
-}
-#define HAS_FLS_U64
-#endif
-
-#ifndef HAS_FLS_U64
-static __attribute__((unused))
-unsigned int fls_u64(uint64_t x)
-{
-       unsigned int r = 64;
-
-       if (!x)
-               return 0;
-
-       if (!(x & 0xFFFFFFFF00000000ULL)) {
-               x <<= 32;
-               r -= 32;
-       }
-       if (!(x & 0xFFFF000000000000ULL)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF00000000000000ULL)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF000000000000000ULL)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC000000000000000ULL)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x8000000000000000ULL)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-#endif
-
-#ifndef HAS_FLS_U32
-static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
-{
-       unsigned int r = 32;
-
-       if (!x) {
-               return 0;
-       }
-       if (!(x & 0xFFFF0000U)) {
-               x <<= 16;
-               r -= 16;
-       }
-       if (!(x & 0xFF000000U)) {
-               x <<= 8;
-               r -= 8;
-       }
-       if (!(x & 0xF0000000U)) {
-               x <<= 4;
-               r -= 4;
-       }
-       if (!(x & 0xC0000000U)) {
-               x <<= 2;
-               r -= 2;
-       }
-       if (!(x & 0x80000000U)) {
-               x <<= 1;
-               r -= 1;
-       }
-       return r;
-}
-#endif
-
-/*
- * Return the minimum order for which x <= (1UL << order).
- * Return -1 if x is 0.
- */
-int utils_get_count_order_u32(uint32_t x)
-{
-       if (!x) {
-               return -1;
-       }
-
-       return fls_u32(x - 1);
-}
-
-/*
- * Return the minimum order for which x <= (1UL << order).
- * Return -1 if x is 0.
- */
-int utils_get_count_order_u64(uint64_t x)
-{
-       if (!x) {
-               return -1;
-       }
-
-       return fls_u64(x - 1);
-}
-
-/**
- * Obtain the value of LTTNG_HOME environment variable, if exists.
- * Otherwise returns the value of HOME.
- */
-const char *utils_get_home_dir(void)
-{
-       char *val = NULL;
-       struct passwd *pwd;
-
-       val = lttng_secure_getenv(DEFAULT_LTTNG_HOME_ENV_VAR);
-       if (val != NULL) {
-               goto end;
-       }
-       val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
-       if (val != NULL) {
-               goto end;
-       }
-
-       /* Fallback on the password file entry. */
-       pwd = getpwuid(getuid());
-       if (!pwd) {
-               goto end;
-       }
-       val = pwd->pw_dir;
-
-       DBG3("Home directory is '%s'", val);
-
-end:
-       return val;
-}
-
-/**
- * Get user's home directory. Dynamically allocated, must be freed
- * by the caller.
- */
-char *utils_get_user_home_dir(uid_t uid)
-{
-       struct passwd pwd;
-       struct passwd *result;
-       char *home_dir = NULL;
-       char *buf = NULL;
-       long buflen;
-       int ret;
-
-       buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
-       if (buflen == -1) {
-               goto end;
-       }
-retry:
-       buf = zmalloc(buflen);
-       if (!buf) {
-               goto end;
-       }
-
-       ret = getpwuid_r(uid, &pwd, buf, buflen, &result);
-       if (ret || !result) {
-               if (ret == ERANGE) {
-                       free(buf);
-                       buflen *= 2;
-                       goto retry;
-               }
-               goto end;
-       }
-
-       home_dir = strdup(pwd.pw_dir);
-end:
-       free(buf);
-       return home_dir;
-}
-
-/*
- * With the given format, fill dst with the time of len maximum siz.
- *
- * Return amount of bytes set in the buffer or else 0 on error.
- */
-size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
-{
-       size_t ret;
-       time_t rawtime;
-       struct tm *timeinfo;
-
-       LTTNG_ASSERT(format);
-       LTTNG_ASSERT(dst);
-
-       /* Get date and time for session path */
-       time(&rawtime);
-       timeinfo = localtime(&rawtime);
-       ret = strftime(dst, len, format, timeinfo);
-       if (ret == 0) {
-               ERR("Unable to strftime with format %s at dst %p of len %zu", format,
-                               dst, len);
-       }
-
-       return ret;
-}
-
-/*
- * Return 0 on success and set *gid to the group_ID matching the passed name.
- * Else -1 if it cannot be found or an error occurred.
- */
-int utils_get_group_id(const char *name, bool warn, gid_t *gid)
-{
-       static volatile int warn_once;
-       int ret;
-       long sys_len;
-       size_t len;
-       struct group grp;
-       struct group *result;
-       struct lttng_dynamic_buffer buffer;
-
-       /* Get the system limit, if it exists. */
-       sys_len = sysconf(_SC_GETGR_R_SIZE_MAX);
-       if (sys_len == -1) {
-               len = 1024;
-       } else {
-               len = (size_t) sys_len;
-       }
-
-       lttng_dynamic_buffer_init(&buffer);
-       ret = lttng_dynamic_buffer_set_size(&buffer, len);
-       if (ret) {
-               ERR("Failed to allocate group info buffer");
-               ret = -1;
-               goto error;
-       }
-
-       while ((ret = getgrnam_r(name, &grp, buffer.data, buffer.size, &result)) == ERANGE) {
-               const size_t new_len = 2 * buffer.size;
-
-               /* Buffer is not big enough, increase its size. */
-               if (new_len < buffer.size) {
-                       ERR("Group info buffer size overflow");
-                       ret = -1;
-                       goto error;
-               }
-
-               ret = lttng_dynamic_buffer_set_size(&buffer, new_len);
-               if (ret) {
-                       ERR("Failed to grow group info buffer to %zu bytes",
-                                       new_len);
-                       ret = -1;
-                       goto error;
-               }
-       }
-       if (ret) {
-               if (ret == ESRCH) {
-                       DBG("Could not find group file entry for group name '%s'",
-                                       name);
-               } else {
-                       PERROR("Failed to get group file entry for group name '%s'",
-                                       name);
-               }
-
-               ret = -1;
-               goto error;
-       }
-
-       /* Group not found. */
-       if (!result) {
-               ret = -1;
-               goto error;
-       }
-
-       *gid = result->gr_gid;
-       ret = 0;
-
-error:
-       if (ret && warn && !warn_once) {
-               WARN("No tracing group detected");
-               warn_once = 1;
-       }
-       lttng_dynamic_buffer_reset(&buffer);
-       return ret;
-}
-
-/*
- * Return a newly allocated option string. This string is to be used as the
- * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number
- * of elements in the long_options array. Returns NULL if the string's
- * allocation fails.
- */
-char *utils_generate_optstring(const struct option *long_options,
-               size_t opt_count)
-{
-       int i;
-       size_t string_len = opt_count, str_pos = 0;
-       char *optstring;
-
-       /*
-        * Compute the necessary string length. One letter per option, two when an
-        * argument is necessary, and a trailing NULL.
-        */
-       for (i = 0; i < opt_count; i++) {
-               string_len += long_options[i].has_arg ? 1 : 0;
-       }
-
-       optstring = zmalloc(string_len);
-       if (!optstring) {
-               goto end;
-       }
-
-       for (i = 0; i < opt_count; i++) {
-               if (!long_options[i].name) {
-                       /* Got to the trailing NULL element */
-                       break;
-               }
-
-               if (long_options[i].val != '\0') {
-                       optstring[str_pos++] = (char) long_options[i].val;
-                       if (long_options[i].has_arg) {
-                               optstring[str_pos++] = ':';
-                       }
-               }
-       }
-
-end:
-       return optstring;
-}
-
-/*
- * Try to remove a hierarchy of empty directories, recursively. Don't unlink
- * any file. Try to rmdir any empty directory within the hierarchy.
- */
-int utils_recursive_rmdir(const char *path)
-{
-       int ret;
-       struct lttng_directory_handle *handle;
-
-       handle = lttng_directory_handle_create(NULL);
-       if (!handle) {
-               ret = -1;
-               goto end;
-       }
-       ret = lttng_directory_handle_remove_subdirectory(handle, path);
-end:
-       lttng_directory_handle_put(handle);
-       return ret;
-}
-
-int utils_truncate_stream_file(int fd, off_t length)
-{
-       int ret;
-       off_t lseek_ret;
-
-       ret = ftruncate(fd, length);
-       if (ret < 0) {
-               PERROR("ftruncate");
-               goto end;
-       }
-       lseek_ret = lseek(fd, length, SEEK_SET);
-       if (lseek_ret < 0) {
-               PERROR("lseek");
-               ret = -1;
-               goto end;
-       }
-end:
-       return ret;
-}
-
-static const char *get_man_bin_path(void)
-{
-       char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV);
-
-       if (env_man_path) {
-               return env_man_path;
-       }
-
-       return DEFAULT_MAN_BIN_PATH;
-}
-
-int utils_show_help(int section, const char *page_name,
-               const char *help_msg)
-{
-       char section_string[8];
-       const char *man_bin_path = get_man_bin_path();
-       int ret = 0;
-
-       if (help_msg) {
-               printf("%s", help_msg);
-               goto end;
-       }
-
-       /* Section integer -> section string */
-       ret = sprintf(section_string, "%d", section);
-       LTTNG_ASSERT(ret > 0 && ret < 8);
-
-       /*
-        * Execute man pager.
-        *
-        * We provide -M to man here because LTTng-tools can
-        * be installed outside /usr, in which case its man pages are
-        * not located in the default /usr/share/man directory.
-        */
-       ret = execlp(man_bin_path, "man", "-M", MANPATH,
-               section_string, page_name, NULL);
-
-end:
-       return ret;
-}
-
-static
-int read_proc_meminfo_field(const char *field, size_t *value)
-{
-       int ret;
-       FILE *proc_meminfo;
-       char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {};
-
-       proc_meminfo = fopen(PROC_MEMINFO_PATH, "r");
-       if (!proc_meminfo) {
-               PERROR("Failed to fopen() " PROC_MEMINFO_PATH);
-               ret = -1;
-               goto fopen_error;
-        }
-
-       /*
-        * Read the contents of /proc/meminfo line by line to find the right
-        * field.
-        */
-       while (!feof(proc_meminfo)) {
-               unsigned long value_kb;
-
-               ret = fscanf(proc_meminfo,
-                               "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n",
-                               name, &value_kb);
-               if (ret == EOF) {
-                       /*
-                        * fscanf() returning EOF can indicate EOF or an error.
-                        */
-                       if (ferror(proc_meminfo)) {
-                               PERROR("Failed to parse " PROC_MEMINFO_PATH);
-                       }
-                       break;
-               }
-
-               if (ret == 2 && strcmp(name, field) == 0) {
-                       /*
-                        * This number is displayed in kilo-bytes. Return the
-                        * number of bytes.
-                        */
-                       *value = ((size_t) value_kb) * 1024;
-                       ret = 0;
-                       goto found;
-               }
-       }
-       /* Reached the end of the file without finding the right field. */
-       ret = -1;
-
-found:
-       fclose(proc_meminfo);
-fopen_error:
-       return ret;
-}
-
-/*
- * Returns an estimate of the number of bytes of memory available based on the
- * the information in `/proc/meminfo`. The number returned by this function is
- * a best guess.
- */
-int utils_get_memory_available(size_t *value)
-{
-       return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value);
-}
-
-/*
- * Returns the total size of the memory on the system in bytes based on the
- * the information in `/proc/meminfo`.
- */
-int utils_get_memory_total(size_t *value)
-{
-       return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value);
-}
-
-int utils_change_working_directory(const char *path)
-{
-       int ret;
-
-       LTTNG_ASSERT(path);
-
-       DBG("Changing working directory to \"%s\"", path);
-       ret = chdir(path);
-       if (ret) {
-               PERROR("Failed to change working directory to \"%s\"", path);
-               goto end;
-       }
-
-       /* Check for write access */
-       if (access(path, W_OK)) {
-               if (errno == EACCES) {
-                       /*
-                        * Do not treat this as an error since the permission
-                        * might change in the lifetime of the process
-                        */
-                       DBG("Working directory \"%s\" is not writable", path);
-               } else {
-                       PERROR("Failed to check if working directory \"%s\" is writable",
-                                       path);
-               }
-       }
-
-end:
-       return ret;
-}
-
-enum lttng_error_code utils_user_id_from_name(const char *user_name, uid_t *uid)
-{
-       struct passwd p, *pres;
-       int ret;
-       enum lttng_error_code ret_val = LTTNG_OK;
-       char *buf = NULL;
-       ssize_t buflen;
-
-       buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
-       if (buflen < 0) {
-               buflen = FALLBACK_USER_BUFLEN;
-       }
-
-       buf = zmalloc(buflen);
-       if (!buf) {
-               ret_val = LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       for (;;) {
-               ret = getpwnam_r(user_name, &p, buf, buflen, &pres);
-               switch (ret) {
-               case EINTR:
-                       continue;
-               case ERANGE:
-                       buflen *= 2;
-                       free(buf);
-                       buf = zmalloc(buflen);
-                       if (!buf) {
-                               ret_val = LTTNG_ERR_NOMEM;
-                               goto end;
-                       }
-                       continue;
-               default:
-                       goto end_loop;
-               }
-       }
-end_loop:
-
-       switch (ret) {
-       case 0:
-               if (pres == NULL) {
-                       ret_val = LTTNG_ERR_USER_NOT_FOUND;
-               } else {
-                       *uid = p.pw_uid;
-                       DBG("Lookup of tracker UID/VUID: name '%s' maps to uid %" PRId64,
-                                       user_name, (int64_t) *uid);
-                       ret_val = LTTNG_OK;
-               }
-               break;
-       case ENOENT:
-       case ESRCH:
-       case EBADF:
-       case EPERM:
-               ret_val = LTTNG_ERR_USER_NOT_FOUND;
-               break;
-       default:
-               ret_val = LTTNG_ERR_NOMEM;
-       }
-end:
-       free(buf);
-       return ret_val;
-}
-
-enum lttng_error_code utils_group_id_from_name(
-               const char *group_name, gid_t *gid)
-{
-       struct group g, *gres;
-       int ret;
-       enum lttng_error_code ret_val = LTTNG_OK;
-       char *buf = NULL;
-       ssize_t buflen;
-
-       buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
-       if (buflen < 0) {
-               buflen = FALLBACK_GROUP_BUFLEN;
-       }
-
-       buf = zmalloc(buflen);
-       if (!buf) {
-               ret_val = LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       for (;;) {
-               ret = getgrnam_r(group_name, &g, buf, buflen, &gres);
-               switch (ret) {
-               case EINTR:
-                       continue;
-               case ERANGE:
-                       buflen *= 2;
-                       free(buf);
-                       buf = zmalloc(buflen);
-                       if (!buf) {
-                               ret_val = LTTNG_ERR_NOMEM;
-                               goto end;
-                       }
-                       continue;
-               default:
-                       goto end_loop;
-               }
-       }
-end_loop:
-
-       switch (ret) {
-       case 0:
-               if (gres == NULL) {
-                       ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
-               } else {
-                       *gid = g.gr_gid;
-                       DBG("Lookup of tracker GID/GUID: name '%s' maps to gid %" PRId64,
-                                       group_name, (int64_t) *gid);
-                       ret_val = LTTNG_OK;
-               }
-               break;
-       case ENOENT:
-       case ESRCH:
-       case EBADF:
-       case EPERM:
-               ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
-               break;
-       default:
-               ret_val = LTTNG_ERR_NOMEM;
-       }
-end:
-       free(buf);
-       return ret_val;
-}
-
-int utils_parse_unsigned_long_long(const char *str,
-               unsigned long long *value)
-{
-       int ret;
-       char *endptr;
-
-       LTTNG_ASSERT(str);
-       LTTNG_ASSERT(value);
-
-       errno = 0;
-       *value = strtoull(str, &endptr, 10);
-
-       /* Conversion failed. Out of range? */
-       if (errno != 0) {
-               /* Don't print an error; allow the caller to log a better error. */
-               DBG("Failed to parse string as unsigned long long number: string = '%s', errno = %d",
-                               str, errno);
-               ret = -1;
-               goto end;
-       }
-
-       /* Not the end of the string or empty string. */
-       if (*endptr || endptr == str) {
-               DBG("Failed to parse string as unsigned long long number: string = '%s'",
-                               str);
-               ret = -1;
-               goto end;
-       }
-
-       ret = 0;
-
-end:
-       return ret;
-}
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
new file mode 100644 (file)
index 0000000..8aa4ff9
--- /dev/null
@@ -0,0 +1,1675 @@
+/*
+ * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2013 Raphaël Beamonte <raphael.beamonte@gmail.com>
+ * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#include "common/macros.h"
+#define _LGPL_SOURCE
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include <common/common.h>
+#include <common/readwrite.h>
+#include <common/runas.h>
+#include <common/compat/getenv.h>
+#include <common/compat/string.h>
+#include <common/compat/dirent.h>
+#include <common/compat/directory-handle.h>
+#include <common/dynamic-buffer.h>
+#include <common/string-utils/format.h>
+#include <lttng/constant.h>
+
+#include "utils.h"
+#include "defaults.h"
+#include "time.h"
+
+#define PROC_MEMINFO_PATH               "/proc/meminfo"
+#define PROC_MEMINFO_MEMAVAILABLE_LINE  "MemAvailable:"
+#define PROC_MEMINFO_MEMTOTAL_LINE      "MemTotal:"
+
+/* The length of the longest field of `/proc/meminfo`. */
+#define PROC_MEMINFO_FIELD_MAX_NAME_LEN        20
+
+#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20)
+#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19"
+#else
+#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1)
+#endif
+
+#define FALLBACK_USER_BUFLEN 16384
+#define FALLBACK_GROUP_BUFLEN 16384
+
+/*
+ * Return a partial realpath(3) of the path even if the full path does not
+ * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist
+ * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with
+ * /test2/test3 then returned. In normal time, realpath(3) fails if the end
+ * point directory does not exist.
+ *
+ * Return a newly-allocated string.
+ */
+static
+char *utils_partial_realpath(const char *path)
+{
+       char *cut_path = NULL, *try_path = NULL, *try_path_prev = NULL;
+       const char *next, *prev, *end;
+       char *resolved_path = NULL;
+
+       /* Safety net */
+       if (path == NULL) {
+               goto error;
+       }
+
+       /*
+        * Identify the end of the path, we don't want to treat the
+        * last char if it is a '/', we will just keep it on the side
+        * to be added at the end, and return a value coherent with
+        * the path given as argument
+        */
+       end = path + strlen(path);
+       if (*(end-1) == '/') {
+               end--;
+       }
+
+       /* Initiate the values of the pointers before looping */
+       next = path;
+       prev = next;
+       /* Only to ensure try_path is not NULL to enter the while */
+       try_path = (char *)next;
+
+       /* Resolve the canonical path of the first part of the path */
+       while (try_path != NULL && next != end) {
+               char *try_path_buf = NULL;
+
+               /*
+                * If there is not any '/' left, we want to try with
+                * the full path
+                */
+               next = strpbrk(next + 1, "/");
+               if (next == NULL) {
+                       next = end;
+               }
+
+               /* Cut the part we will be trying to resolve */
+               cut_path = lttng_strndup(path, next - path);
+               if (cut_path == NULL) {
+                       PERROR("lttng_strndup");
+                       goto error;
+               }
+
+               try_path_buf = (char *) zmalloc(LTTNG_PATH_MAX);
+               if (!try_path_buf) {
+                       PERROR("zmalloc");
+                       goto error;
+               }
+
+               /* Try to resolve this part */
+               try_path = realpath((char *) cut_path, try_path_buf);
+               if (try_path == NULL) {
+                       free(try_path_buf);
+                       /*
+                        * There was an error, we just want to be assured it
+                        * is linked to an unexistent directory, if it's another
+                        * reason, we spawn an error
+                        */
+                       switch (errno) {
+                       case ENOENT:
+                               /* Ignore the error */
+                               break;
+                       default:
+                               PERROR("realpath (partial_realpath)");
+                               goto error;
+                               break;
+                       }
+               } else {
+                       /* Save the place we are before trying the next step */
+                       try_path_buf = NULL;
+                       free(try_path_prev);
+                       try_path_prev = try_path;
+                       prev = next;
+               }
+
+               /* Free the allocated memory */
+               free(cut_path);
+               cut_path = NULL;
+       }
+
+       /* Allocate memory for the resolved path. */
+       resolved_path = (char *) zmalloc(LTTNG_PATH_MAX);
+       if (resolved_path == NULL) {
+               PERROR("zmalloc resolved path");
+               goto error;
+       }
+
+       /*
+        * If we were able to solve at least partially the path, we can concatenate
+        * what worked and what didn't work
+        */
+       if (try_path_prev != NULL) {
+               /* If we risk to concatenate two '/', we remove one of them */
+               if (try_path_prev[strlen(try_path_prev) - 1] == '/' && prev[0] == '/') {
+                       try_path_prev[strlen(try_path_prev) - 1] = '\0';
+               }
+
+               /*
+                * Duplicate the memory used by prev in case resolved_path and
+                * path are pointers for the same memory space
+                */
+               cut_path = strdup(prev);
+               if (cut_path == NULL) {
+                       PERROR("strdup");
+                       goto error;
+               }
+
+               /* Concatenate the strings */
+               snprintf(resolved_path, LTTNG_PATH_MAX, "%s%s",
+                               try_path_prev, cut_path);
+
+               /* Free the allocated memory */
+               free(cut_path);
+               free(try_path_prev);
+               cut_path = NULL;
+               try_path_prev = NULL;
+       /*
+        * Else, we just copy the path in our resolved_path to
+        * return it as is
+        */
+       } else {
+               strncpy(resolved_path, path, LTTNG_PATH_MAX);
+       }
+
+       /* Then we return the 'partially' resolved path */
+       return resolved_path;
+
+error:
+       free(resolved_path);
+       free(cut_path);
+       free(try_path);
+       if (try_path_prev != try_path) {
+               free(try_path_prev);
+       }
+       return NULL;
+}
+
+static
+int expand_double_slashes_dot_and_dotdot(char *path)
+{
+       size_t expanded_path_len, path_len;
+       const char *curr_char, *path_last_char, *next_slash, *prev_slash;
+
+       path_len = strlen(path);
+       path_last_char = &path[path_len];
+
+       if (path_len == 0) {
+               goto error;
+       }
+
+       expanded_path_len = 0;
+
+       /* We iterate over the provided path to expand the "//", "../" and "./" */
+       for (curr_char = path; curr_char <= path_last_char; curr_char = next_slash + 1) {
+               /* Find the next forward slash. */
+               size_t curr_token_len;
+
+               if (curr_char == path_last_char) {
+                       expanded_path_len++;
+                       break;
+               }
+
+               next_slash = (const char *) memchr(curr_char, '/', path_last_char - curr_char);
+               if (next_slash == NULL) {
+                       /* Reached the end of the provided path. */
+                       next_slash = path_last_char;
+               }
+
+               /* Compute how long is the previous token. */
+               curr_token_len = next_slash - curr_char;
+               switch(curr_token_len) {
+               case 0:
+                       /*
+                        * The pointer has not move meaning that curr_char is
+                        * pointing to a slash. It that case there is no token
+                        * to copy, so continue the iteration to find the next
+                        * token
+                        */
+                       continue;
+               case 1:
+                       /*
+                        * The pointer moved 1 character. Check if that
+                        * character is a dot ('.'), if it is: omit it, else
+                        * copy the token to the normalized path.
+                        */
+                       if (curr_char[0] == '.') {
+                               continue;
+                       }
+                       break;
+               case 2:
+                       /*
+                        * The pointer moved 2 characters. Check if these
+                        * characters are double dots ('..'). If that is the
+                        * case, we need to remove the last token of the
+                        * normalized path.
+                        */
+                       if (curr_char[0] == '.' && curr_char[1] == '.') {
+                               /*
+                                * Find the previous path component by
+                                * using the memrchr function to find the
+                                * previous forward slash and substract that
+                                * len to the resulting path.
+                                */
+                               prev_slash = (const char *) lttng_memrchr(path, '/', expanded_path_len);
+                               /*
+                                * If prev_slash is NULL, we reached the
+                                * beginning of the path. We can't go back any
+                                * further.
+                                */
+                               if (prev_slash != NULL) {
+                                       expanded_path_len = prev_slash - path;
+                               }
+                               continue;
+                       }
+                       break;
+               default:
+                       break;
+               }
+
+               /*
+                * Copy the current token which is neither a '.' nor a '..'.
+                */
+               path[expanded_path_len++] = '/';
+               memmove(&path[expanded_path_len], curr_char, curr_token_len);
+               expanded_path_len += curr_token_len;
+       }
+
+       if (expanded_path_len == 0) {
+               path[expanded_path_len++] = '/';
+       }
+
+       path[expanded_path_len] = '\0';
+       return 0;
+error:
+       return -1;
+}
+
+/*
+ * Make a full resolution of the given path even if it doesn't exist.
+ * This function uses the utils_partial_realpath function to resolve
+ * symlinks and relatives paths at the start of the string, and
+ * implements functionnalities to resolve the './' and '../' strings
+ * in the middle of a path. This function is only necessary because
+ * realpath(3) does not accept to resolve unexistent paths.
+ * The returned string was allocated in the function, it is thus of
+ * the responsibility of the caller to free this memory.
+ */
+static
+char *_utils_expand_path(const char *path, bool keep_symlink)
+{
+       int ret;
+       char *absolute_path = NULL;
+       char *last_token;
+       bool is_dot, is_dotdot;
+
+       /* Safety net */
+       if (path == NULL) {
+               goto error;
+       }
+
+       /* Allocate memory for the absolute_path */
+       absolute_path = (char *) zmalloc(LTTNG_PATH_MAX);
+       if (absolute_path == NULL) {
+               PERROR("zmalloc expand path");
+               goto error;
+       }
+
+       if (path[0] == '/') {
+               ret = lttng_strncpy(absolute_path, path, LTTNG_PATH_MAX);
+               if (ret) {
+                       ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX);
+                       goto error;
+               }
+       } else {
+               /*
+                * This is a relative path. We need to get the present working
+                * directory and start the path walk from there.
+                */
+               char current_working_dir[LTTNG_PATH_MAX];
+               char *cwd_ret;
+
+               cwd_ret = getcwd(current_working_dir, sizeof(current_working_dir));
+               if (!cwd_ret) {
+                       goto error;
+               }
+               /*
+                * Get the number of character in the CWD and allocate an array
+                * to can hold it and the path provided by the caller.
+                */
+               ret = snprintf(absolute_path, LTTNG_PATH_MAX, "%s/%s",
+                               current_working_dir, path);
+               if (ret >= LTTNG_PATH_MAX) {
+                       ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes",
+                                       current_working_dir, path, LTTNG_PATH_MAX);
+                       goto error;
+               }
+       }
+
+       if (keep_symlink) {
+               /* Resolve partially our path */
+               char *new_absolute_path = utils_partial_realpath(absolute_path);
+               if (!new_absolute_path) {
+                       goto error;
+               }
+
+               free(absolute_path);
+               absolute_path = new_absolute_path;
+       }
+
+       ret = expand_double_slashes_dot_and_dotdot(absolute_path);
+       if (ret) {
+               goto error;
+       }
+
+       /* Identify the last token */
+       last_token = strrchr(absolute_path, '/');
+
+       /* Verify that this token is not a relative path */
+       is_dotdot = (strcmp(last_token, "/..") == 0);
+       is_dot = (strcmp(last_token, "/.") == 0);
+
+       /* If it is, take action */
+       if (is_dot || is_dotdot) {
+               /* For both, remove this token */
+               *last_token = '\0';
+
+               /* If it was a reference to parent directory, go back one more time */
+               if (is_dotdot) {
+                       last_token = strrchr(absolute_path, '/');
+
+                       /* If there was only one level left, we keep the first '/' */
+                       if (last_token == absolute_path) {
+                               last_token++;
+                       }
+
+                       *last_token = '\0';
+               }
+       }
+
+       return absolute_path;
+
+error:
+       free(absolute_path);
+       return NULL;
+}
+char *utils_expand_path(const char *path)
+{
+       return _utils_expand_path(path, true);
+}
+
+char *utils_expand_path_keep_symlink(const char *path)
+{
+       return _utils_expand_path(path, false);
+}
+/*
+ * Create a pipe in dst.
+ */
+int utils_create_pipe(int *dst)
+{
+       int ret;
+
+       if (dst == NULL) {
+               return -1;
+       }
+
+       ret = pipe(dst);
+       if (ret < 0) {
+               PERROR("create pipe");
+       }
+
+       return ret;
+}
+
+/*
+ * Create pipe and set CLOEXEC flag to both fd.
+ *
+ * Make sure the pipe opened by this function are closed at some point. Use
+ * utils_close_pipe().
+ */
+int utils_create_pipe_cloexec(int *dst)
+{
+       int ret, i;
+
+       if (dst == NULL) {
+               return -1;
+       }
+
+       ret = utils_create_pipe(dst);
+       if (ret < 0) {
+               goto error;
+       }
+
+       for (i = 0; i < 2; i++) {
+               ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
+               if (ret < 0) {
+                       PERROR("fcntl pipe cloexec");
+                       goto error;
+               }
+       }
+
+error:
+       return ret;
+}
+
+/*
+ * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
+ *
+ * Make sure the pipe opened by this function are closed at some point. Use
+ * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
+ * support OSes other than Linux 2.6.23+.
+ */
+int utils_create_pipe_cloexec_nonblock(int *dst)
+{
+       int ret, i;
+
+       if (dst == NULL) {
+               return -1;
+       }
+
+       ret = utils_create_pipe(dst);
+       if (ret < 0) {
+               goto error;
+       }
+
+       for (i = 0; i < 2; i++) {
+               ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
+               if (ret < 0) {
+                       PERROR("fcntl pipe cloexec");
+                       goto error;
+               }
+               /*
+                * Note: we override any flag that could have been
+                * previously set on the fd.
+                */
+               ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
+               if (ret < 0) {
+                       PERROR("fcntl pipe nonblock");
+                       goto error;
+               }
+       }
+
+error:
+       return ret;
+}
+
+/*
+ * Close both read and write side of the pipe.
+ */
+void utils_close_pipe(int *src)
+{
+       int i, ret;
+
+       if (src == NULL) {
+               return;
+       }
+
+       for (i = 0; i < 2; i++) {
+               /* Safety check */
+               if (src[i] < 0) {
+                       continue;
+               }
+
+               ret = close(src[i]);
+               if (ret) {
+                       PERROR("close pipe");
+               }
+               src[i] = -1;
+       }
+}
+
+/*
+ * Create a new string using two strings range.
+ */
+char *utils_strdupdelim(const char *begin, const char *end)
+{
+       char *str;
+
+       str = (char *) zmalloc(end - begin + 1);
+       if (str == NULL) {
+               PERROR("zmalloc strdupdelim");
+               goto error;
+       }
+
+       memcpy(str, begin, end - begin);
+       str[end - begin] = '\0';
+
+error:
+       return str;
+}
+
+/*
+ * Set CLOEXEC flag to the give file descriptor.
+ */
+int utils_set_fd_cloexec(int fd)
+{
+       int ret;
+
+       if (fd < 0) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
+       if (ret < 0) {
+               PERROR("fcntl cloexec");
+               ret = -errno;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Create pid file to the given path and filename.
+ */
+int utils_create_pid_file(pid_t pid, const char *filepath)
+{
+       int ret;
+       FILE *fp;
+
+       LTTNG_ASSERT(filepath);
+
+       fp = fopen(filepath, "w");
+       if (fp == NULL) {
+               PERROR("open pid file %s", filepath);
+               ret = -1;
+               goto error;
+       }
+
+       ret = fprintf(fp, "%d\n", (int) pid);
+       if (ret < 0) {
+               PERROR("fprintf pid file");
+               goto error;
+       }
+
+       if (fclose(fp)) {
+               PERROR("fclose");
+       }
+       DBG("Pid %d written in file %s", (int) pid, filepath);
+       ret = 0;
+error:
+       return ret;
+}
+
+/*
+ * Create lock file to the given path and filename.
+ * Returns the associated file descriptor, -1 on error.
+ */
+int utils_create_lock_file(const char *filepath)
+{
+       int ret;
+       int fd;
+       struct flock lock;
+
+       LTTNG_ASSERT(filepath);
+
+       memset(&lock, 0, sizeof(lock));
+       fd = open(filepath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR |
+               S_IRGRP | S_IWGRP);
+       if (fd < 0) {
+               PERROR("open lock file %s", filepath);
+               fd = -1;
+               goto error;
+       }
+
+       /*
+        * Attempt to lock the file. If this fails, there is
+        * already a process using the same lock file running
+        * and we should exit.
+        */
+       lock.l_whence = SEEK_SET;
+       lock.l_type = F_WRLCK;
+
+       ret = fcntl(fd, F_SETLK, &lock);
+       if (ret == -1) {
+               PERROR("fcntl lock file");
+               ERR("Could not get lock file %s, another instance is running.",
+                       filepath);
+               if (close(fd)) {
+                       PERROR("close lock file");
+               }
+               fd = ret;
+               goto error;
+       }
+
+error:
+       return fd;
+}
+
+/*
+ * Create directory using the given path and mode.
+ *
+ * On success, return 0 else a negative error code.
+ */
+int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
+{
+       int ret;
+       struct lttng_directory_handle *handle;
+       const struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid),
+       };
+
+       handle = lttng_directory_handle_create(NULL);
+       if (!handle) {
+               ret = -1;
+               goto end;
+       }
+       ret = lttng_directory_handle_create_subdirectory_as_user(
+                       handle, path, mode,
+                       (uid >= 0 || gid >= 0) ? &creds : NULL);
+end:
+       lttng_directory_handle_put(handle);
+       return ret;
+}
+
+/*
+ * Recursively create directory using the given path and mode, under the
+ * provided uid and gid.
+ *
+ * On success, return 0 else a negative error code.
+ */
+int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid)
+{
+       int ret;
+       struct lttng_directory_handle *handle;
+       const struct lttng_credentials creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid),
+       };
+
+       handle = lttng_directory_handle_create(NULL);
+       if (!handle) {
+               ret = -1;
+               goto end;
+       }
+       ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
+                       handle, path, mode,
+                       (uid >= 0 || gid >= 0) ? &creds : NULL);
+end:
+       lttng_directory_handle_put(handle);
+       return ret;
+}
+
+/*
+ * out_stream_path is the output parameter.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int utils_stream_file_path(const char *path_name, const char *file_name,
+               uint64_t size, uint64_t count, const char *suffix,
+               char *out_stream_path, size_t stream_path_len)
+{
+       int ret;
+       char count_str[MAX_INT_DEC_LEN(count) + 1] = {};
+       const char *path_separator;
+
+       if (path_name && (path_name[0] == '\0' ||
+                       path_name[strlen(path_name) - 1] == '/')) {
+               path_separator = "";
+       } else {
+               path_separator = "/";
+       }
+
+       path_name = path_name ? : "";
+       suffix = suffix ? : "";
+       if (size > 0) {
+               ret = snprintf(count_str, sizeof(count_str), "_%" PRIu64,
+                               count);
+               LTTNG_ASSERT(ret > 0 && ret < sizeof(count_str));
+       }
+
+       ret = snprintf(out_stream_path, stream_path_len, "%s%s%s%s%s",
+                       path_name, path_separator, file_name, count_str,
+                       suffix);
+       if (ret < 0 || ret >= stream_path_len) {
+               ERR("Truncation occurred while formatting stream path");
+               ret = -1;
+       } else {
+               ret = 0;
+       }
+       return ret;
+}
+
+/**
+ * Parse a string that represents a size in human readable format. It
+ * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'.
+ *
+ * The suffix multiply the integer by:
+ * 'k': 1024
+ * 'M': 1024^2
+ * 'G': 1024^3
+ *
+ * @param str  The string to parse.
+ * @param size Pointer to a uint64_t that will be filled with the
+ *             resulting size.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int utils_parse_size_suffix(const char * const str, uint64_t * const size)
+{
+       int ret;
+       uint64_t base_size;
+       long shift = 0;
+       const char *str_end;
+       char *num_end;
+
+       if (!str) {
+               DBG("utils_parse_size_suffix: received a NULL string.");
+               ret = -1;
+               goto end;
+       }
+
+       /* strtoull will accept a negative number, but we don't want to. */
+       if (strchr(str, '-') != NULL) {
+               DBG("utils_parse_size_suffix: invalid size string, should not contain '-'.");
+               ret = -1;
+               goto end;
+       }
+
+       /* str_end will point to the \0 */
+       str_end = str + strlen(str);
+       errno = 0;
+       base_size = strtoull(str, &num_end, 0);
+       if (errno != 0) {
+               PERROR("utils_parse_size_suffix strtoull");
+               ret = -1;
+               goto end;
+       }
+
+       if (num_end == str) {
+               /* strtoull parsed nothing, not good. */
+               DBG("utils_parse_size_suffix: strtoull had nothing good to parse.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check if a prefix is present. */
+       switch (*num_end) {
+       case 'G':
+               shift = GIBI_LOG2;
+               num_end++;
+               break;
+       case 'M': /*  */
+               shift = MEBI_LOG2;
+               num_end++;
+               break;
+       case 'K':
+       case 'k':
+               shift = KIBI_LOG2;
+               num_end++;
+               break;
+       case '\0':
+               break;
+       default:
+               DBG("utils_parse_size_suffix: invalid suffix.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check for garbage after the valid input. */
+       if (num_end != str_end) {
+               DBG("utils_parse_size_suffix: Garbage after size string.");
+               ret = -1;
+               goto end;
+       }
+
+       *size = base_size << shift;
+
+       /* Check for overflow */
+       if ((*size >> shift) != base_size) {
+               DBG("utils_parse_size_suffix: oops, overflow detected.");
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+/**
+ * Parse a string that represents a time in human readable format. It
+ * supports decimal integers suffixed by:
+ *     "us" for microsecond,
+ *     "ms" for millisecond,
+ *     "s"  for second,
+ *     "m"  for minute,
+ *     "h"  for hour
+ *
+ * The suffix multiply the integer by:
+ *     "us" : 1
+ *     "ms" : 1000
+ *     "s"  : 1000000
+ *     "m"  : 60000000
+ *     "h"  : 3600000000
+ *
+ * Note that unit-less numbers are assumed to be microseconds.
+ *
+ * @param str          The string to parse, assumed to be NULL-terminated.
+ * @param time_us      Pointer to a uint64_t that will be filled with the
+ *                     resulting time in microseconds.
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int utils_parse_time_suffix(char const * const str, uint64_t * const time_us)
+{
+       int ret;
+       uint64_t base_time;
+       uint64_t multiplier = 1;
+       const char *str_end;
+       char *num_end;
+
+       if (!str) {
+               DBG("utils_parse_time_suffix: received a NULL string.");
+               ret = -1;
+               goto end;
+       }
+
+       /* strtoull will accept a negative number, but we don't want to. */
+       if (strchr(str, '-') != NULL) {
+               DBG("utils_parse_time_suffix: invalid time string, should not contain '-'.");
+               ret = -1;
+               goto end;
+       }
+
+       /* str_end will point to the \0 */
+       str_end = str + strlen(str);
+       errno = 0;
+       base_time = strtoull(str, &num_end, 10);
+       if (errno != 0) {
+               PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str);
+               ret = -1;
+               goto end;
+       }
+
+       if (num_end == str) {
+               /* strtoull parsed nothing, not good. */
+               DBG("utils_parse_time_suffix: strtoull had nothing good to parse.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check if a prefix is present. */
+       switch (*num_end) {
+       case 'u':
+               /*
+                * Microsecond (us)
+                *
+                * Skip the "us" if the string matches the "us" suffix,
+                * otherwise let the check for the end of the string handle
+                * the error reporting.
+                */
+               if (*(num_end + 1) == 's') {
+                       num_end += 2;
+               }
+               break;
+       case 'm':
+               if (*(num_end + 1) == 's') {
+                       /* Millisecond (ms) */
+                       multiplier = USEC_PER_MSEC;
+                       /* Skip the 's' */
+                       num_end++;
+               } else {
+                       /* Minute (m) */
+                       multiplier = USEC_PER_MINUTE;
+               }
+               num_end++;
+               break;
+       case 's':
+               /* Second */
+               multiplier = USEC_PER_SEC;
+               num_end++;
+               break;
+       case 'h':
+               /* Hour */
+               multiplier = USEC_PER_HOURS;
+               num_end++;
+               break;
+       case '\0':
+               break;
+       default:
+               DBG("utils_parse_time_suffix: invalid suffix.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Check for garbage after the valid input. */
+       if (num_end != str_end) {
+               DBG("utils_parse_time_suffix: Garbage after time string.");
+               ret = -1;
+               goto end;
+       }
+
+       *time_us = base_time * multiplier;
+
+       /* Check for overflow */
+       if ((*time_us / multiplier) != base_time) {
+               DBG("utils_parse_time_suffix: oops, overflow detected.");
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+end:
+       return ret;
+}
+
+/*
+ * fls: returns the position of the most significant bit.
+ * Returns 0 if no bit is set, else returns the position of the most
+ * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
+ */
+#if defined(__i386) || defined(__x86_64)
+static inline unsigned int fls_u32(uint32_t x)
+{
+       int r;
+
+       asm("bsrl %1,%0\n\t"
+               "jnz 1f\n\t"
+               "movl $-1,%0\n\t"
+               "1:\n\t"
+               : "=r" (r) : "rm" (x));
+       return r + 1;
+}
+#define HAS_FLS_U32
+#endif
+
+#if defined(__x86_64) && defined(__LP64__)
+static inline
+unsigned int fls_u64(uint64_t x)
+{
+       long r;
+
+       asm("bsrq %1,%0\n\t"
+           "jnz 1f\n\t"
+           "movq $-1,%0\n\t"
+           "1:\n\t"
+           : "=r" (r) : "rm" (x));
+       return r + 1;
+}
+#define HAS_FLS_U64
+#endif
+
+#ifndef HAS_FLS_U64
+static __attribute__((unused))
+unsigned int fls_u64(uint64_t x)
+{
+       unsigned int r = 64;
+
+       if (!x)
+               return 0;
+
+       if (!(x & 0xFFFFFFFF00000000ULL)) {
+               x <<= 32;
+               r -= 32;
+       }
+       if (!(x & 0xFFFF000000000000ULL)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF00000000000000ULL)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF000000000000000ULL)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC000000000000000ULL)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x8000000000000000ULL)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+#endif
+
+#ifndef HAS_FLS_U32
+static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
+{
+       unsigned int r = 32;
+
+       if (!x) {
+               return 0;
+       }
+       if (!(x & 0xFFFF0000U)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xFF000000U)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xF0000000U)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xC0000000U)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x80000000U)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+#endif
+
+/*
+ * Return the minimum order for which x <= (1UL << order).
+ * Return -1 if x is 0.
+ */
+int utils_get_count_order_u32(uint32_t x)
+{
+       if (!x) {
+               return -1;
+       }
+
+       return fls_u32(x - 1);
+}
+
+/*
+ * Return the minimum order for which x <= (1UL << order).
+ * Return -1 if x is 0.
+ */
+int utils_get_count_order_u64(uint64_t x)
+{
+       if (!x) {
+               return -1;
+       }
+
+       return fls_u64(x - 1);
+}
+
+/**
+ * Obtain the value of LTTNG_HOME environment variable, if exists.
+ * Otherwise returns the value of HOME.
+ */
+const char *utils_get_home_dir(void)
+{
+       char *val = NULL;
+       struct passwd *pwd;
+
+       val = lttng_secure_getenv(DEFAULT_LTTNG_HOME_ENV_VAR);
+       if (val != NULL) {
+               goto end;
+       }
+       val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
+       if (val != NULL) {
+               goto end;
+       }
+
+       /* Fallback on the password file entry. */
+       pwd = getpwuid(getuid());
+       if (!pwd) {
+               goto end;
+       }
+       val = pwd->pw_dir;
+
+       DBG3("Home directory is '%s'", val);
+
+end:
+       return val;
+}
+
+/**
+ * Get user's home directory. Dynamically allocated, must be freed
+ * by the caller.
+ */
+char *utils_get_user_home_dir(uid_t uid)
+{
+       struct passwd pwd;
+       struct passwd *result;
+       char *home_dir = NULL;
+       char *buf = NULL;
+       long buflen;
+       int ret;
+
+       buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+       if (buflen == -1) {
+               goto end;
+       }
+retry:
+       buf = (char *) zmalloc(buflen);
+       if (!buf) {
+               goto end;
+       }
+
+       ret = getpwuid_r(uid, &pwd, buf, buflen, &result);
+       if (ret || !result) {
+               if (ret == ERANGE) {
+                       free(buf);
+                       buflen *= 2;
+                       goto retry;
+               }
+               goto end;
+       }
+
+       home_dir = strdup(pwd.pw_dir);
+end:
+       free(buf);
+       return home_dir;
+}
+
+/*
+ * With the given format, fill dst with the time of len maximum siz.
+ *
+ * Return amount of bytes set in the buffer or else 0 on error.
+ */
+size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
+{
+       size_t ret;
+       time_t rawtime;
+       struct tm *timeinfo;
+
+       LTTNG_ASSERT(format);
+       LTTNG_ASSERT(dst);
+
+       /* Get date and time for session path */
+       time(&rawtime);
+       timeinfo = localtime(&rawtime);
+       ret = strftime(dst, len, format, timeinfo);
+       if (ret == 0) {
+               ERR("Unable to strftime with format %s at dst %p of len %zu", format,
+                               dst, len);
+       }
+
+       return ret;
+}
+
+/*
+ * Return 0 on success and set *gid to the group_ID matching the passed name.
+ * Else -1 if it cannot be found or an error occurred.
+ */
+int utils_get_group_id(const char *name, bool warn, gid_t *gid)
+{
+       static volatile int warn_once;
+       int ret;
+       long sys_len;
+       size_t len;
+       struct group grp;
+       struct group *result;
+       struct lttng_dynamic_buffer buffer;
+
+       /* Get the system limit, if it exists. */
+       sys_len = sysconf(_SC_GETGR_R_SIZE_MAX);
+       if (sys_len == -1) {
+               len = 1024;
+       } else {
+               len = (size_t) sys_len;
+       }
+
+       lttng_dynamic_buffer_init(&buffer);
+       ret = lttng_dynamic_buffer_set_size(&buffer, len);
+       if (ret) {
+               ERR("Failed to allocate group info buffer");
+               ret = -1;
+               goto error;
+       }
+
+       while ((ret = getgrnam_r(name, &grp, buffer.data, buffer.size, &result)) == ERANGE) {
+               const size_t new_len = 2 * buffer.size;
+
+               /* Buffer is not big enough, increase its size. */
+               if (new_len < buffer.size) {
+                       ERR("Group info buffer size overflow");
+                       ret = -1;
+                       goto error;
+               }
+
+               ret = lttng_dynamic_buffer_set_size(&buffer, new_len);
+               if (ret) {
+                       ERR("Failed to grow group info buffer to %zu bytes",
+                                       new_len);
+                       ret = -1;
+                       goto error;
+               }
+       }
+       if (ret) {
+               if (ret == ESRCH) {
+                       DBG("Could not find group file entry for group name '%s'",
+                                       name);
+               } else {
+                       PERROR("Failed to get group file entry for group name '%s'",
+                                       name);
+               }
+
+               ret = -1;
+               goto error;
+       }
+
+       /* Group not found. */
+       if (!result) {
+               ret = -1;
+               goto error;
+       }
+
+       *gid = result->gr_gid;
+       ret = 0;
+
+error:
+       if (ret && warn && !warn_once) {
+               WARN("No tracing group detected");
+               warn_once = 1;
+       }
+       lttng_dynamic_buffer_reset(&buffer);
+       return ret;
+}
+
+/*
+ * Return a newly allocated option string. This string is to be used as the
+ * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number
+ * of elements in the long_options array. Returns NULL if the string's
+ * allocation fails.
+ */
+char *utils_generate_optstring(const struct option *long_options,
+               size_t opt_count)
+{
+       int i;
+       size_t string_len = opt_count, str_pos = 0;
+       char *optstring;
+
+       /*
+        * Compute the necessary string length. One letter per option, two when an
+        * argument is necessary, and a trailing NULL.
+        */
+       for (i = 0; i < opt_count; i++) {
+               string_len += long_options[i].has_arg ? 1 : 0;
+       }
+
+       optstring = (char *) zmalloc(string_len);
+       if (!optstring) {
+               goto end;
+       }
+
+       for (i = 0; i < opt_count; i++) {
+               if (!long_options[i].name) {
+                       /* Got to the trailing NULL element */
+                       break;
+               }
+
+               if (long_options[i].val != '\0') {
+                       optstring[str_pos++] = (char) long_options[i].val;
+                       if (long_options[i].has_arg) {
+                               optstring[str_pos++] = ':';
+                       }
+               }
+       }
+
+end:
+       return optstring;
+}
+
+/*
+ * Try to remove a hierarchy of empty directories, recursively. Don't unlink
+ * any file. Try to rmdir any empty directory within the hierarchy.
+ */
+int utils_recursive_rmdir(const char *path)
+{
+       int ret;
+       struct lttng_directory_handle *handle;
+
+       handle = lttng_directory_handle_create(NULL);
+       if (!handle) {
+               ret = -1;
+               goto end;
+       }
+       ret = lttng_directory_handle_remove_subdirectory(handle, path);
+end:
+       lttng_directory_handle_put(handle);
+       return ret;
+}
+
+int utils_truncate_stream_file(int fd, off_t length)
+{
+       int ret;
+       off_t lseek_ret;
+
+       ret = ftruncate(fd, length);
+       if (ret < 0) {
+               PERROR("ftruncate");
+               goto end;
+       }
+       lseek_ret = lseek(fd, length, SEEK_SET);
+       if (lseek_ret < 0) {
+               PERROR("lseek");
+               ret = -1;
+               goto end;
+       }
+end:
+       return ret;
+}
+
+static const char *get_man_bin_path(void)
+{
+       char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV);
+
+       if (env_man_path) {
+               return env_man_path;
+       }
+
+       return DEFAULT_MAN_BIN_PATH;
+}
+
+int utils_show_help(int section, const char *page_name,
+               const char *help_msg)
+{
+       char section_string[8];
+       const char *man_bin_path = get_man_bin_path();
+       int ret = 0;
+
+       if (help_msg) {
+               printf("%s", help_msg);
+               goto end;
+       }
+
+       /* Section integer -> section string */
+       ret = sprintf(section_string, "%d", section);
+       LTTNG_ASSERT(ret > 0 && ret < 8);
+
+       /*
+        * Execute man pager.
+        *
+        * We provide -M to man here because LTTng-tools can
+        * be installed outside /usr, in which case its man pages are
+        * not located in the default /usr/share/man directory.
+        */
+       ret = execlp(man_bin_path, "man", "-M", MANPATH,
+               section_string, page_name, NULL);
+
+end:
+       return ret;
+}
+
+static
+int read_proc_meminfo_field(const char *field, size_t *value)
+{
+       int ret;
+       FILE *proc_meminfo;
+       char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {};
+
+       proc_meminfo = fopen(PROC_MEMINFO_PATH, "r");
+       if (!proc_meminfo) {
+               PERROR("Failed to fopen() " PROC_MEMINFO_PATH);
+               ret = -1;
+               goto fopen_error;
+        }
+
+       /*
+        * Read the contents of /proc/meminfo line by line to find the right
+        * field.
+        */
+       while (!feof(proc_meminfo)) {
+               unsigned long value_kb;
+
+               ret = fscanf(proc_meminfo,
+                               "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n",
+                               name, &value_kb);
+               if (ret == EOF) {
+                       /*
+                        * fscanf() returning EOF can indicate EOF or an error.
+                        */
+                       if (ferror(proc_meminfo)) {
+                               PERROR("Failed to parse " PROC_MEMINFO_PATH);
+                       }
+                       break;
+               }
+
+               if (ret == 2 && strcmp(name, field) == 0) {
+                       /*
+                        * This number is displayed in kilo-bytes. Return the
+                        * number of bytes.
+                        */
+                       *value = ((size_t) value_kb) * 1024;
+                       ret = 0;
+                       goto found;
+               }
+       }
+       /* Reached the end of the file without finding the right field. */
+       ret = -1;
+
+found:
+       fclose(proc_meminfo);
+fopen_error:
+       return ret;
+}
+
+/*
+ * Returns an estimate of the number of bytes of memory available based on the
+ * the information in `/proc/meminfo`. The number returned by this function is
+ * a best guess.
+ */
+int utils_get_memory_available(size_t *value)
+{
+       return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value);
+}
+
+/*
+ * Returns the total size of the memory on the system in bytes based on the
+ * the information in `/proc/meminfo`.
+ */
+int utils_get_memory_total(size_t *value)
+{
+       return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value);
+}
+
+int utils_change_working_directory(const char *path)
+{
+       int ret;
+
+       LTTNG_ASSERT(path);
+
+       DBG("Changing working directory to \"%s\"", path);
+       ret = chdir(path);
+       if (ret) {
+               PERROR("Failed to change working directory to \"%s\"", path);
+               goto end;
+       }
+
+       /* Check for write access */
+       if (access(path, W_OK)) {
+               if (errno == EACCES) {
+                       /*
+                        * Do not treat this as an error since the permission
+                        * might change in the lifetime of the process
+                        */
+                       DBG("Working directory \"%s\" is not writable", path);
+               } else {
+                       PERROR("Failed to check if working directory \"%s\" is writable",
+                                       path);
+               }
+       }
+
+end:
+       return ret;
+}
+
+enum lttng_error_code utils_user_id_from_name(const char *user_name, uid_t *uid)
+{
+       struct passwd p, *pres;
+       int ret;
+       enum lttng_error_code ret_val = LTTNG_OK;
+       char *buf = NULL;
+       ssize_t buflen;
+
+       buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+       if (buflen < 0) {
+               buflen = FALLBACK_USER_BUFLEN;
+       }
+
+       buf = (char *) zmalloc(buflen);
+       if (!buf) {
+               ret_val = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       for (;;) {
+               ret = getpwnam_r(user_name, &p, buf, buflen, &pres);
+               switch (ret) {
+               case EINTR:
+                       continue;
+               case ERANGE:
+                       buflen *= 2;
+                       free(buf);
+                       buf = (char *) zmalloc(buflen);
+                       if (!buf) {
+                               ret_val = LTTNG_ERR_NOMEM;
+                               goto end;
+                       }
+                       continue;
+               default:
+                       goto end_loop;
+               }
+       }
+end_loop:
+
+       switch (ret) {
+       case 0:
+               if (pres == NULL) {
+                       ret_val = LTTNG_ERR_USER_NOT_FOUND;
+               } else {
+                       *uid = p.pw_uid;
+                       DBG("Lookup of tracker UID/VUID: name '%s' maps to uid %" PRId64,
+                                       user_name, (int64_t) *uid);
+                       ret_val = LTTNG_OK;
+               }
+               break;
+       case ENOENT:
+       case ESRCH:
+       case EBADF:
+       case EPERM:
+               ret_val = LTTNG_ERR_USER_NOT_FOUND;
+               break;
+       default:
+               ret_val = LTTNG_ERR_NOMEM;
+       }
+end:
+       free(buf);
+       return ret_val;
+}
+
+enum lttng_error_code utils_group_id_from_name(
+               const char *group_name, gid_t *gid)
+{
+       struct group g, *gres;
+       int ret;
+       enum lttng_error_code ret_val = LTTNG_OK;
+       char *buf = NULL;
+       ssize_t buflen;
+
+       buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+       if (buflen < 0) {
+               buflen = FALLBACK_GROUP_BUFLEN;
+       }
+
+       buf = (char *) zmalloc(buflen);
+       if (!buf) {
+               ret_val = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       for (;;) {
+               ret = getgrnam_r(group_name, &g, buf, buflen, &gres);
+               switch (ret) {
+               case EINTR:
+                       continue;
+               case ERANGE:
+                       buflen *= 2;
+                       free(buf);
+                       buf = (char *) zmalloc(buflen);
+                       if (!buf) {
+                               ret_val = LTTNG_ERR_NOMEM;
+                               goto end;
+                       }
+                       continue;
+               default:
+                       goto end_loop;
+               }
+       }
+end_loop:
+
+       switch (ret) {
+       case 0:
+               if (gres == NULL) {
+                       ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
+               } else {
+                       *gid = g.gr_gid;
+                       DBG("Lookup of tracker GID/GUID: name '%s' maps to gid %" PRId64,
+                                       group_name, (int64_t) *gid);
+                       ret_val = LTTNG_OK;
+               }
+               break;
+       case ENOENT:
+       case ESRCH:
+       case EBADF:
+       case EPERM:
+               ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
+               break;
+       default:
+               ret_val = LTTNG_ERR_NOMEM;
+       }
+end:
+       free(buf);
+       return ret_val;
+}
+
+int utils_parse_unsigned_long_long(const char *str,
+               unsigned long long *value)
+{
+       int ret;
+       char *endptr;
+
+       LTTNG_ASSERT(str);
+       LTTNG_ASSERT(value);
+
+       errno = 0;
+       *value = strtoull(str, &endptr, 10);
+
+       /* Conversion failed. Out of range? */
+       if (errno != 0) {
+               /* Don't print an error; allow the caller to log a better error. */
+               DBG("Failed to parse string as unsigned long long number: string = '%s', errno = %d",
+                               str, errno);
+               ret = -1;
+               goto end;
+       }
+
+       /* Not the end of the string or empty string. */
+       if (*endptr || endptr == str) {
+               DBG("Failed to parse string as unsigned long long number: string = '%s'",
+                               str);
+               ret = -1;
+               goto end;
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
diff --git a/src/common/uuid.c b/src/common/uuid.c
deleted file mode 100644 (file)
index 26fb61e..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include <common/compat/string.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "uuid.h"
-
-static const lttng_uuid nil_uuid;
-static bool lttng_uuid_is_init;
-
-void lttng_uuid_to_str(const lttng_uuid uuid, char *uuid_str)
-{
-       sprintf(uuid_str, LTTNG_UUID_FMT, LTTNG_UUID_FMT_VALUES(uuid));
-}
-
-int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out)
-{
-       int ret = 0;
-       lttng_uuid uuid_scan;
-
-       if ((str_in == NULL) || (uuid_out == NULL)) {
-               ret = -1;
-               goto end;
-       }
-
-       if (lttng_strnlen(str_in, LTTNG_UUID_STR_LEN) != LTTNG_UUID_STR_LEN - 1) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Scan to a temporary location in case of a partial match. */
-       if (sscanf(str_in, LTTNG_UUID_FMT, LTTNG_UUID_SCAN_VALUES(uuid_scan)) !=
-                       LTTNG_UUID_LEN) {
-               ret = -1;
-       }
-
-       lttng_uuid_copy(uuid_out, uuid_scan);
-end:
-       return ret;
-}
-
-bool lttng_uuid_is_equal(const lttng_uuid a, const lttng_uuid b)
-{
-       return memcmp(a, b, LTTNG_UUID_LEN) == 0;
-}
-
-bool lttng_uuid_is_nil(const lttng_uuid uuid)
-{
-       return memcmp(nil_uuid, uuid, sizeof(lttng_uuid)) == 0;
-}
-
-void lttng_uuid_copy(lttng_uuid dst, const lttng_uuid src)
-{
-       memcpy(dst, src, LTTNG_UUID_LEN);
-}
-
-/*
- * Generate a random UUID according to RFC4122, section 4.4.
- */
-int lttng_uuid_generate(lttng_uuid uuid_out)
-{
-       int i, ret = 0;
-
-       if (uuid_out == NULL) {
-               ret = -1;
-               goto end;
-       }
-
-       if (!lttng_uuid_is_init) {
-               /*
-                * We don't need cryptographic quality randomness to
-                * generate UUIDs, seed rand with the epoch.
-                */
-               const time_t epoch = time(NULL);
-
-               if (epoch == (time_t) -1) {
-                       ret = -1;
-                       goto end;
-               }
-               srand(epoch);
-
-               lttng_uuid_is_init = true;
-       }
-
-       /*
-        * Generate 16 bytes of random bits.
-        */
-       for (i = 0; i < LTTNG_UUID_LEN; i++) {
-               uuid_out[i] = (uint8_t) rand();
-       }
-
-       /*
-        * Set the two most significant bits (bits 6 and 7) of the
-        * clock_seq_hi_and_reserved to zero and one, respectively.
-        */
-       uuid_out[8] &= ~(1 << 6);
-       uuid_out[8] |= (1 << 7);
-
-       /*
-        * Set the four most significant bits (bits 12 through 15) of the
-        * time_hi_and_version field to the 4-bit version number from
-        * Section 4.1.3.
-        */
-       uuid_out[6] &= 0x0f;
-       uuid_out[6] |= (LTTNG_UUID_VER << 4);
-
-end:
-       return ret;
-}
diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
new file mode 100644 (file)
index 0000000..77b9a76
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <common/compat/string.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "uuid.h"
+
+static const lttng_uuid nil_uuid = { 0 };
+static bool lttng_uuid_is_init;
+
+void lttng_uuid_to_str(const lttng_uuid uuid, char *uuid_str)
+{
+       sprintf(uuid_str, LTTNG_UUID_FMT, LTTNG_UUID_FMT_VALUES(uuid));
+}
+
+int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out)
+{
+       int ret = 0;
+       lttng_uuid uuid_scan;
+
+       if ((str_in == NULL) || (uuid_out == NULL)) {
+               ret = -1;
+               goto end;
+       }
+
+       if (lttng_strnlen(str_in, LTTNG_UUID_STR_LEN) != LTTNG_UUID_STR_LEN - 1) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Scan to a temporary location in case of a partial match. */
+       if (sscanf(str_in, LTTNG_UUID_FMT, LTTNG_UUID_SCAN_VALUES(uuid_scan)) !=
+                       LTTNG_UUID_LEN) {
+               ret = -1;
+       }
+
+       lttng_uuid_copy(uuid_out, uuid_scan);
+end:
+       return ret;
+}
+
+bool lttng_uuid_is_equal(const lttng_uuid a, const lttng_uuid b)
+{
+       return memcmp(a, b, LTTNG_UUID_LEN) == 0;
+}
+
+bool lttng_uuid_is_nil(const lttng_uuid uuid)
+{
+       return memcmp(nil_uuid, uuid, sizeof(lttng_uuid)) == 0;
+}
+
+void lttng_uuid_copy(lttng_uuid dst, const lttng_uuid src)
+{
+       memcpy(dst, src, LTTNG_UUID_LEN);
+}
+
+/*
+ * Generate a random UUID according to RFC4122, section 4.4.
+ */
+int lttng_uuid_generate(lttng_uuid uuid_out)
+{
+       int i, ret = 0;
+
+       if (uuid_out == NULL) {
+               ret = -1;
+               goto end;
+       }
+
+       if (!lttng_uuid_is_init) {
+               /*
+                * We don't need cryptographic quality randomness to
+                * generate UUIDs, seed rand with the epoch.
+                */
+               const time_t epoch = time(NULL);
+
+               if (epoch == (time_t) -1) {
+                       ret = -1;
+                       goto end;
+               }
+               srand(epoch);
+
+               lttng_uuid_is_init = true;
+       }
+
+       /*
+        * Generate 16 bytes of random bits.
+        */
+       for (i = 0; i < LTTNG_UUID_LEN; i++) {
+               uuid_out[i] = (uint8_t) rand();
+       }
+
+       /*
+        * Set the two most significant bits (bits 6 and 7) of the
+        * clock_seq_hi_and_reserved to zero and one, respectively.
+        */
+       uuid_out[8] &= ~(1 << 6);
+       uuid_out[8] |= (1 << 7);
+
+       /*
+        * Set the four most significant bits (bits 12 through 15) of the
+        * time_hi_and_version field to the 4-bit version number from
+        * Section 4.1.3.
+        */
+       uuid_out[6] &= 0x0f;
+       uuid_out[6] |= (LTTNG_UUID_VER << 4);
+
+end:
+       return ret;
+}
diff --git a/src/common/waiter.c b/src/common/waiter.c
deleted file mode 100644 (file)
index aca88eb..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-only
- *
- */
-
-#include "waiter.h"
-#include <urcu/uatomic.h>
-#include <urcu/futex.h>
-#include "error.h"
-#include <poll.h>
-
-/*
- * Number of busy-loop attempts before waiting on futex.
- */
-#define WAIT_ATTEMPTS 1000
-
-enum waiter_state {
-       /* WAITER_WAITING is compared directly (futex compares it). */
-       WAITER_WAITING =        0,
-       /* non-zero are used as masks. */
-       WAITER_WOKEN_UP =       (1 << 0),
-       WAITER_RUNNING =        (1 << 1),
-       WAITER_TEARDOWN =       (1 << 2),
-};
-
-void lttng_waiter_init(struct lttng_waiter *waiter)
-{
-       cds_wfs_node_init(&waiter->wait_queue_node);
-       uatomic_set(&waiter->state, WAITER_WAITING);
-       cmm_smp_mb();
-}
-
-/*
- * User must init "waiter" before passing its memory to waker thread.
- */
-void lttng_waiter_wait(struct lttng_waiter *waiter)
-{
-       unsigned int i;
-
-       DBG("Beginning of waiter wait period");
-       /* Load and test condition before read state */
-       cmm_smp_rmb();
-       for (i = 0; i < WAIT_ATTEMPTS; i++) {
-               if (uatomic_read(&waiter->state) != WAITER_WAITING) {
-                       goto skip_futex_wait;
-               }
-               caa_cpu_relax();
-       }
-       while (futex_noasync(&waiter->state, FUTEX_WAIT, WAITER_WAITING,
-                       NULL, NULL, 0)) {
-               switch (errno) {
-               case EWOULDBLOCK:
-                       /* Value already changed. */
-                       goto skip_futex_wait;
-               case EINTR:
-                       /* Retry if interrupted by signal. */
-                       break;  /* Get out of switch. */
-               default:
-                       /* Unexpected error. */
-                       PERROR("futex_noasync");
-                       abort();
-               }
-       }
-skip_futex_wait:
-
-       /* Tell waker thread than we are running. */
-       uatomic_or(&waiter->state, WAITER_RUNNING);
-
-       /*
-        * Wait until waker thread lets us know it's ok to tear down
-        * memory allocated for struct lttng_waiter.
-        */
-       for (i = 0; i < WAIT_ATTEMPTS; i++) {
-               if (uatomic_read(&waiter->state) & WAITER_TEARDOWN) {
-                       break;
-               }
-               caa_cpu_relax();
-       }
-       while (!(uatomic_read(&waiter->state) & WAITER_TEARDOWN)) {
-               poll(NULL, 0, 10);
-       }
-       LTTNG_ASSERT(uatomic_read(&waiter->state) & WAITER_TEARDOWN);
-       DBG("End of waiter wait period");
-}
-
-/*
- * Note: lttng_waiter_wake needs waiter to stay allocated throughout its
- * execution. In this scheme, the waiter owns the node memory, and we only allow
- * it to free this memory when it sees the WAITER_TEARDOWN flag.
- */
-void lttng_waiter_wake_up(struct lttng_waiter *waiter)
-{
-       cmm_smp_mb();
-       LTTNG_ASSERT(uatomic_read(&waiter->state) == WAITER_WAITING);
-       uatomic_set(&waiter->state, WAITER_WOKEN_UP);
-       if (!(uatomic_read(&waiter->state) & WAITER_RUNNING)) {
-               if (futex_noasync(&waiter->state, FUTEX_WAKE, 1,
-                               NULL, NULL, 0) < 0) {
-                       PERROR("futex_noasync");
-                       abort();
-               }
-       }
-       /* Allow teardown of struct urcu_wait memory. */
-       uatomic_or(&waiter->state, WAITER_TEARDOWN);
-}
diff --git a/src/common/waiter.cpp b/src/common/waiter.cpp
new file mode 100644 (file)
index 0000000..aca88eb
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include "waiter.h"
+#include <urcu/uatomic.h>
+#include <urcu/futex.h>
+#include "error.h"
+#include <poll.h>
+
+/*
+ * Number of busy-loop attempts before waiting on futex.
+ */
+#define WAIT_ATTEMPTS 1000
+
+enum waiter_state {
+       /* WAITER_WAITING is compared directly (futex compares it). */
+       WAITER_WAITING =        0,
+       /* non-zero are used as masks. */
+       WAITER_WOKEN_UP =       (1 << 0),
+       WAITER_RUNNING =        (1 << 1),
+       WAITER_TEARDOWN =       (1 << 2),
+};
+
+void lttng_waiter_init(struct lttng_waiter *waiter)
+{
+       cds_wfs_node_init(&waiter->wait_queue_node);
+       uatomic_set(&waiter->state, WAITER_WAITING);
+       cmm_smp_mb();
+}
+
+/*
+ * User must init "waiter" before passing its memory to waker thread.
+ */
+void lttng_waiter_wait(struct lttng_waiter *waiter)
+{
+       unsigned int i;
+
+       DBG("Beginning of waiter wait period");
+       /* Load and test condition before read state */
+       cmm_smp_rmb();
+       for (i = 0; i < WAIT_ATTEMPTS; i++) {
+               if (uatomic_read(&waiter->state) != WAITER_WAITING) {
+                       goto skip_futex_wait;
+               }
+               caa_cpu_relax();
+       }
+       while (futex_noasync(&waiter->state, FUTEX_WAIT, WAITER_WAITING,
+                       NULL, NULL, 0)) {
+               switch (errno) {
+               case EWOULDBLOCK:
+                       /* Value already changed. */
+                       goto skip_futex_wait;
+               case EINTR:
+                       /* Retry if interrupted by signal. */
+                       break;  /* Get out of switch. */
+               default:
+                       /* Unexpected error. */
+                       PERROR("futex_noasync");
+                       abort();
+               }
+       }
+skip_futex_wait:
+
+       /* Tell waker thread than we are running. */
+       uatomic_or(&waiter->state, WAITER_RUNNING);
+
+       /*
+        * Wait until waker thread lets us know it's ok to tear down
+        * memory allocated for struct lttng_waiter.
+        */
+       for (i = 0; i < WAIT_ATTEMPTS; i++) {
+               if (uatomic_read(&waiter->state) & WAITER_TEARDOWN) {
+                       break;
+               }
+               caa_cpu_relax();
+       }
+       while (!(uatomic_read(&waiter->state) & WAITER_TEARDOWN)) {
+               poll(NULL, 0, 10);
+       }
+       LTTNG_ASSERT(uatomic_read(&waiter->state) & WAITER_TEARDOWN);
+       DBG("End of waiter wait period");
+}
+
+/*
+ * Note: lttng_waiter_wake needs waiter to stay allocated throughout its
+ * execution. In this scheme, the waiter owns the node memory, and we only allow
+ * it to free this memory when it sees the WAITER_TEARDOWN flag.
+ */
+void lttng_waiter_wake_up(struct lttng_waiter *waiter)
+{
+       cmm_smp_mb();
+       LTTNG_ASSERT(uatomic_read(&waiter->state) == WAITER_WAITING);
+       uatomic_set(&waiter->state, WAITER_WOKEN_UP);
+       if (!(uatomic_read(&waiter->state) & WAITER_RUNNING)) {
+               if (futex_noasync(&waiter->state, FUTEX_WAKE, 1,
+                               NULL, NULL, 0) < 0) {
+                       PERROR("futex_noasync");
+                       abort();
+               }
+       }
+       /* Allow teardown of struct urcu_wait memory. */
+       uatomic_or(&waiter->state, WAITER_TEARDOWN);
+}
index eaab5314aad582ed9bfc526a6333b4abbeb16df5..2f1a9174e9a867cb7f9cbcf712e0b5144120c062 100644 (file)
@@ -62,7 +62,7 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
 rotation_SOURCES = rotation.c
 rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm
 
-default_pipe_size_getter_SOURCES = default_pipe_size_getter.c
+default_pipe_size_getter_SOURCES = default_pipe_size_getter.cpp
 default_pipe_size_getter_LDADD = $(top_builddir)/src/common/libcommon.la
 
 noinst_SCRIPTS = \
diff --git a/tests/regression/tools/notification/default_pipe_size_getter.c b/tests/regression/tools/notification/default_pipe_size_getter.c
deleted file mode 100644 (file)
index cc24cec..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * default_pipe_size_getter.c
- *
- * Tests suite for LTTng notification API (get default size of pipes)
- *
- * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: MIT
- *
- */
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <common/pipe.h>
-#include <common/error.h>
-
-int lttng_opt_verbose;
-int lttng_opt_mi;
-int lttng_opt_quiet;
-
-int main(int argc, const char **argv)
-{
-       int ret;
-       /*
-        * The event notifier pipes are not "special"; they are created using
-        * the lttng_pipe utility. Hence, this should be representative of a
-        * pipe created by the session daemon for event notifier messages to
-        * go through.
-        */
-       struct lttng_pipe *pipe = lttng_pipe_open(0);
-
-       if (!pipe) {
-               /* lttng_pipe_open already logs on error. */
-               ret = EXIT_FAILURE;
-               goto end;
-       }
-
-       ret = fcntl(lttng_pipe_get_writefd(pipe), F_GETPIPE_SZ);
-       if (ret < 0) {
-               PERROR("Failed to get the size of the pipe");
-               ret = EXIT_FAILURE;
-               goto end;
-       }
-
-       printf("%d\n", ret);
-       ret = EXIT_SUCCESS;
-end:
-       lttng_pipe_destroy(pipe);
-       return ret;
-}
diff --git a/tests/regression/tools/notification/default_pipe_size_getter.cpp b/tests/regression/tools/notification/default_pipe_size_getter.cpp
new file mode 100644 (file)
index 0000000..cc24cec
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * default_pipe_size_getter.c
+ *
+ * Tests suite for LTTng notification API (get default size of pipes)
+ *
+ * Copyright (C) 2021 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <common/pipe.h>
+#include <common/error.h>
+
+int lttng_opt_verbose;
+int lttng_opt_mi;
+int lttng_opt_quiet;
+
+int main(int argc, const char **argv)
+{
+       int ret;
+       /*
+        * The event notifier pipes are not "special"; they are created using
+        * the lttng_pipe utility. Hence, this should be representative of a
+        * pipe created by the session daemon for event notifier messages to
+        * go through.
+        */
+       struct lttng_pipe *pipe = lttng_pipe_open(0);
+
+       if (!pipe) {
+               /* lttng_pipe_open already logs on error. */
+               ret = EXIT_FAILURE;
+               goto end;
+       }
+
+       ret = fcntl(lttng_pipe_get_writefd(pipe), F_GETPIPE_SZ);
+       if (ret < 0) {
+               PERROR("Failed to get the size of the pipe");
+               ret = EXIT_FAILURE;
+               goto end;
+       }
+
+       printf("%d\n", ret);
+       ret = EXIT_SUCCESS;
+end:
+       lttng_pipe_destroy(pipe);
+       return ret;
+}
This page took 0.831551 seconds and 4 git commands to generate.