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 = \
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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(¬ify->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 = ¬ify->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;
-}
--- /dev/null
+/*
+ * 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(¬ify->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 = ¬ify->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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
-
--- /dev/null
+/*
+ * 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;
+}
+
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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");
- }
-}
--- /dev/null
+/*
+ * 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");
+ }
+}
+++ /dev/null
-/*
- * 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 "???";
- }
-}
--- /dev/null
+/*
+ * 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 "???";
+ }
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 =
- <tng_session_daemon_notification_endpoint_instance;
-
-struct lttng_endpoint *lttng_session_daemon_command_endpoint =
- <tng_session_daemon_command_endpoint_instance;
--- /dev/null
+/*
+ * 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 =
+ <tng_session_daemon_notification_endpoint_instance;
+
+struct lttng_endpoint *lttng_session_daemon_command_endpoint =
+ <tng_session_daemon_command_endpoint_instance;
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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();
- }
-}
--- /dev/null
+/*
+ * 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();
+ }
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_payload_view)) {
- ret = -1;
- goto end;
- }
-
- name = current_payload_view.buffer.data;
- if (!lttng_buffer_view_contains_string(
- ¤t_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(¤t_payload_view)) {
- ret = -1;
- goto end;
- }
-
- ret = lttng_kernel_probe_location_create_from_payload(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_payload_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ name = current_payload_view.buffer.data;
+ if (!lttng_buffer_view_contains_string(
+ ¤t_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(¤t_payload_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = lttng_kernel_probe_location_create_from_payload(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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 "???";
- }
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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 "???";
+ }
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- name = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_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(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ name = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_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(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- pattern = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- filter_expression = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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(
- ¤t_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(¤t_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(¤t_buffer_view)) {
- ret = -1;
- goto end;
- }
-
- exclusion = current_buffer_view.data;
- if (!lttng_buffer_view_contains_string(¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ pattern = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ filter_expression = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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(
+ ¤t_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(¤t_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(¤t_buffer_view)) {
+ ret = -1;
+ goto end;
+ }
+
+ exclusion = current_buffer_view.data;
+ if (!lttng_buffer_view_contains_string(¤t_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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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");
-}
--- /dev/null
+/*
+ * 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");
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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, §ion_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;
-}
--- /dev/null
+/*
+ * 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, §ion_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;
+}
#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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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, ¬ification_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(¬ification_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;
-}
--- /dev/null
+/*
+ * 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, ¬ification_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(¬ification_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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
- }
-}
--- /dev/null
+/*
+ * 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;
+ }
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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(¤t_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(¤t_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(¤t_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(¤t_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;
-}
--- /dev/null
+/*
+ * 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(¤t_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(¤t_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(¤t_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(¤t_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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
-
--- /dev/null
+/*
+ * 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;
+}
+
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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(©_buffer);
-
- ret = lttng_condition_serialize(trigger->condition, ©_buffer);
- if (ret < 0) {
- goto end;
- }
-
- {
- struct lttng_payload_view view =
- lttng_payload_view_from_payload(
- ©_buffer, 0, -1);
-
- ret = lttng_condition_create_from_payload(
- &view, &condition_copy);
- if (ret < 0) {
- goto end;
- }
- }
-
- lttng_payload_clear(©_buffer);
-
- ret = lttng_action_serialize(trigger->action, ©_buffer);
- if (ret < 0) {
- goto end;
- }
-
- {
- struct lttng_payload_view view =
- lttng_payload_view_from_payload(
- ©_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(©->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(©_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;
-}
--- /dev/null
+/*
+ * 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(©_buffer);
+
+ ret = lttng_condition_serialize(trigger->condition, ©_buffer);
+ if (ret < 0) {
+ goto end;
+ }
+
+ {
+ struct lttng_payload_view view =
+ lttng_payload_view_from_payload(
+ ©_buffer, 0, -1);
+
+ ret = lttng_condition_create_from_payload(
+ &view, &condition_copy);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+
+ lttng_payload_clear(©_buffer);
+
+ ret = lttng_action_serialize(trigger->action, ©_buffer);
+ if (ret < 0) {
+ goto end;
+ }
+
+ {
+ struct lttng_payload_view view =
+ lttng_payload_view_from_payload(
+ ©_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(©->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(©_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;
+}
+++ /dev/null
-/*
- * 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__ */
--- /dev/null
+/*
+ * 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__ */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
* should be ignored.
*/
enum lttng_proto_type {
+ LTTNG_PROTO_TYPE_NONE = 0,
LTTNG_TCP = 1,
/*
* UDP protocol is not supported for now.
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
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 = \
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}