From: Simon Marchi Date: Wed, 22 Sep 2021 12:21:14 +0000 (-0400) Subject: common: compile libcommon as C++ X-Git-Url: https://git.liburcu.org/?p=lttng-tools.git;a=commitdiff_plain;h=a6bc4ca9d659caf016ef932fcd944029737ac57c common: compile libcommon as C++ Change-Id: I5160ef36932d71a4d925018fe0763bbc78b88009 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- diff --git a/src/common/Makefile.am b/src/common/Makefile.am index cb8ac7b10..48e18f311 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -38,81 +38,81 @@ noinst_LTLIBRARIES = libcommon.la EXTRA_DIST = mi-lttng-4.1.xsd libcommon_la_SOURCES = \ - actions/action.c \ - actions/list.c \ - actions/notify.c \ - actions/path.c \ - actions/rotate-session.c \ - actions/snapshot-session.c \ - actions/start-session.c \ - actions/stop-session.c \ - actions/rate-policy.c \ - buffer-view.h buffer-view.c \ + actions/action.cpp \ + actions/list.cpp \ + actions/notify.cpp \ + actions/path.cpp \ + actions/rotate-session.cpp \ + actions/snapshot-session.cpp \ + actions/start-session.cpp \ + actions/stop-session.cpp \ + actions/rate-policy.cpp \ + buffer-view.h buffer-view.cpp \ common.h \ - conditions/buffer-usage.c \ - conditions/condition.c \ - conditions/event-rule-matches.c \ - conditions/session-consumed-size.c \ - conditions/session-rotation.c \ - context.c context.h \ - credentials.c credentials.h \ - daemonize.c daemonize.h \ - defaults.c \ - domain.c \ - dynamic-array.c dynamic-array.h \ - dynamic-buffer.c dynamic-buffer.h \ - endpoint.c \ - error.c error.h \ - error-query.c \ - evaluation.c \ - event.c \ - event-expr/event-expr.c \ - event-field-value.c \ - event-rule/event-rule.c \ - event-rule/kernel-kprobe.c \ - event-rule/kernel-syscall.c \ - event-rule/kernel-uprobe.c \ - event-rule/kernel-tracepoint.c \ - event-rule/user-tracepoint.c \ - event-rule/log4j-logging.c \ - event-rule/jul-logging.c \ - event-rule/python-logging.c \ - filter.c filter.h \ - fd-handle.c fd-handle.h \ - fs-handle.c fs-handle.h fs-handle-internal.h \ - futex.c futex.h \ - kernel-probe.c \ - index-allocator.c index-allocator.h \ - location.c \ - log-level-rule.c \ - mi-lttng.c mi-lttng.h \ - notification.c \ + conditions/buffer-usage.cpp \ + conditions/condition.cpp \ + conditions/event-rule-matches.cpp \ + conditions/session-consumed-size.cpp \ + conditions/session-rotation.cpp \ + context.cpp context.h \ + credentials.cpp credentials.h \ + daemonize.cpp daemonize.h \ + defaults.cpp \ + domain.cpp \ + dynamic-array.cpp dynamic-array.h \ + dynamic-buffer.cpp dynamic-buffer.h \ + endpoint.cpp \ + error.cpp error.h \ + error-query.cpp \ + evaluation.cpp \ + event.cpp \ + event-expr/event-expr.cpp \ + event-field-value.cpp \ + event-rule/event-rule.cpp \ + event-rule/kernel-kprobe.cpp \ + event-rule/kernel-syscall.cpp \ + event-rule/kernel-uprobe.cpp \ + event-rule/kernel-tracepoint.cpp \ + event-rule/user-tracepoint.cpp \ + event-rule/log4j-logging.cpp \ + event-rule/jul-logging.cpp \ + event-rule/python-logging.cpp \ + filter.cpp filter.h \ + fd-handle.cpp fd-handle.h \ + fs-handle.cpp fs-handle.h fs-handle-internal.h \ + futex.cpp futex.h \ + kernel-probe.cpp \ + index-allocator.cpp index-allocator.h \ + location.cpp \ + log-level-rule.cpp \ + mi-lttng.cpp mi-lttng.h \ + notification.cpp \ optional.h \ - payload.c payload.h \ - payload-view.c payload-view.h \ - pipe.c pipe.h \ - readwrite.c readwrite.h \ - runas.c runas.h \ - shm.c shm.h \ - session-descriptor.c \ - snapshot.c snapshot.h \ - spawn-viewer.c spawn-viewer.h \ - time.c \ - trace-chunk.c trace-chunk.h \ + payload.cpp payload.h \ + payload-view.cpp payload-view.h \ + pipe.cpp pipe.h \ + readwrite.cpp readwrite.h \ + runas.cpp runas.h \ + shm.cpp shm.h \ + session-descriptor.cpp \ + snapshot.cpp snapshot.h \ + spawn-viewer.cpp spawn-viewer.h \ + time.cpp \ + trace-chunk.cpp trace-chunk.h \ trace-chunk-registry.h \ - trigger.c \ - unix.c unix.h \ - uri.c uri.h \ - userspace-probe.c \ - utils.c utils.h \ - uuid.c uuid.h \ - thread.c thread.h \ - tracker.c tracker.h \ - waiter.c waiter.h + trigger.cpp \ + unix.cpp unix.h \ + uri.cpp uri.h \ + userspace-probe.cpp \ + utils.cpp utils.h \ + uuid.cpp uuid.h \ + thread.cpp thread.h \ + tracker.cpp tracker.h \ + waiter.cpp waiter.h if HAVE_ELF_H libcommon_la_SOURCES += \ - lttng-elf.c lttng-elf.h + lttng-elf.cpp lttng-elf.h endif libcommon_la_LIBADD = \ diff --git a/src/common/actions/action.c b/src/common/actions/action.c deleted file mode 100644 index 21dee73fa..000000000 --- a/src/common/actions/action.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const char *lttng_action_type_string(enum lttng_action_type action_type) -{ - switch (action_type) { - case LTTNG_ACTION_TYPE_UNKNOWN: - return "UNKNOWN"; - case LTTNG_ACTION_TYPE_LIST: - return "LIST"; - case LTTNG_ACTION_TYPE_NOTIFY: - return "NOTIFY"; - case LTTNG_ACTION_TYPE_ROTATE_SESSION: - return "ROTATE_SESSION"; - case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: - return "SNAPSHOT_SESSION"; - case LTTNG_ACTION_TYPE_START_SESSION: - return "START_SESSION"; - case LTTNG_ACTION_TYPE_STOP_SESSION: - return "STOP_SESSION"; - default: - return "???"; - } -} - -enum lttng_action_type lttng_action_get_type(const struct lttng_action *action) -{ - return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; -} - -void lttng_action_init(struct lttng_action *action, - enum lttng_action_type type, - action_validate_cb validate, - action_serialize_cb serialize, - action_equal_cb equal, - action_destroy_cb destroy, - action_get_rate_policy_cb get_rate_policy, - action_add_error_query_results_cb add_error_query_results, - action_mi_serialize_cb mi) -{ - urcu_ref_init(&action->ref); - action->type = type; - action->validate = validate; - action->serialize = serialize; - action->equal = equal; - action->destroy = destroy; - action->get_rate_policy = get_rate_policy; - action->add_error_query_results = add_error_query_results; - action->mi_serialize = mi; - - action->execution_request_counter = 0; - action->execution_counter = 0; - action->execution_failure_counter = 0; -} - -static -void action_destroy_ref(struct urcu_ref *ref) -{ - struct lttng_action *action = - container_of(ref, struct lttng_action, ref); - - action->destroy(action); -} - -void lttng_action_get(struct lttng_action *action) -{ - urcu_ref_get(&action->ref); -} - -void lttng_action_put(struct lttng_action *action) -{ - if (!action) { - return; - } - - LTTNG_ASSERT(action->destroy); - urcu_ref_put(&action->ref, action_destroy_ref); -} - -void lttng_action_destroy(struct lttng_action *action) -{ - lttng_action_put(action); -} - -bool lttng_action_validate(struct lttng_action *action) -{ - bool valid; - - if (!action) { - valid = false; - goto end; - } - - if (!action->validate) { - /* Sub-class guarantees that it can never be invalid. */ - valid = true; - goto end; - } - - valid = action->validate(action); -end: - return valid; -} - -int lttng_action_serialize(struct lttng_action *action, - struct lttng_payload *payload) -{ - int ret; - struct lttng_action_comm action_comm = { - .action_type = (int8_t) action->type, - }; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &action_comm, - sizeof(action_comm)); - if (ret) { - goto end; - } - - ret = action->serialize(action, payload); - if (ret) { - goto end; - } -end: - return ret; -} - -ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, - struct lttng_action **action) -{ - ssize_t consumed_len, specific_action_consumed_len; - action_create_from_payload_cb create_from_payload_cb; - const struct lttng_action_comm *action_comm; - const struct lttng_payload_view action_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*action_comm)); - - if (!view || !action) { - consumed_len = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&action_comm_view)) { - /* Payload not large enough to contain the header. */ - consumed_len = -1; - goto end; - } - - action_comm = (const struct lttng_action_comm *) action_comm_view.buffer.data; - - DBG("Create action from payload: action-type=%s", - lttng_action_type_string(action_comm->action_type)); - - switch (action_comm->action_type) { - case LTTNG_ACTION_TYPE_NOTIFY: - create_from_payload_cb = lttng_action_notify_create_from_payload; - break; - case LTTNG_ACTION_TYPE_ROTATE_SESSION: - create_from_payload_cb = - lttng_action_rotate_session_create_from_payload; - break; - case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: - create_from_payload_cb = - lttng_action_snapshot_session_create_from_payload; - break; - case LTTNG_ACTION_TYPE_START_SESSION: - create_from_payload_cb = - lttng_action_start_session_create_from_payload; - break; - case LTTNG_ACTION_TYPE_STOP_SESSION: - create_from_payload_cb = - lttng_action_stop_session_create_from_payload; - break; - case LTTNG_ACTION_TYPE_LIST: - create_from_payload_cb = lttng_action_list_create_from_payload; - break; - default: - ERR("Failed to create action from payload, unhandled action type: action-type=%u (%s)", - action_comm->action_type, - lttng_action_type_string( - action_comm->action_type)); - consumed_len = -1; - goto end; - } - - { - /* Create buffer view for the action-type-specific data. */ - struct lttng_payload_view specific_action_view = - lttng_payload_view_from_view(view, - sizeof(struct lttng_action_comm), - -1); - - specific_action_consumed_len = create_from_payload_cb( - &specific_action_view, action); - } - if (specific_action_consumed_len < 0) { - ERR("Failed to create specific action from buffer."); - consumed_len = -1; - goto end; - } - - LTTNG_ASSERT(*action); - - consumed_len = sizeof(struct lttng_action_comm) + - specific_action_consumed_len; - -end: - return consumed_len; -} - -bool lttng_action_is_equal(const struct lttng_action *a, - const struct lttng_action *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a->type != b->type) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - LTTNG_ASSERT(a->equal); - is_equal = a->equal(a, b); -end: - return is_equal; -} - -void lttng_action_increase_execution_request_count(struct lttng_action *action) -{ - action->execution_request_counter++; -} - -void lttng_action_increase_execution_count(struct lttng_action *action) -{ - action->execution_counter++; -} - -void lttng_action_increase_execution_failure_count(struct lttng_action *action) -{ - uatomic_inc(&action->execution_failure_counter); -} - -bool lttng_action_should_execute(const struct lttng_action *action) -{ - const struct lttng_rate_policy *policy = NULL; - bool execute = false; - - if (action->get_rate_policy == NULL) { - execute = true; - goto end; - } - - policy = action->get_rate_policy(action); - if (policy == NULL) { - execute = true; - goto end; - } - - execute = lttng_rate_policy_should_execute( - policy, action->execution_request_counter); -end: - return execute; -} - -enum lttng_action_status lttng_action_add_error_query_results( - const struct lttng_action *action, - struct lttng_error_query_results *results) -{ - return action->add_error_query_results(action, results); -} - -enum lttng_action_status lttng_action_generic_add_error_query_results( - const struct lttng_action *action, - struct lttng_error_query_results *results) -{ - enum lttng_action_status action_status; - struct lttng_error_query_result *error_counter = NULL; - const uint64_t execution_failure_counter = - uatomic_read(&action->execution_failure_counter); - - error_counter = lttng_error_query_result_counter_create( - "total execution failures", - "Aggregated count of errors encountered when executing the action", - execution_failure_counter); - if (!error_counter) { - action_status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - if (lttng_error_query_results_add_result( - results, error_counter)) { - action_status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - /* Ownership transferred to the results. */ - error_counter = NULL; - action_status = LTTNG_ACTION_STATUS_OK; -end: - lttng_error_query_result_destroy(error_counter); - return action_status; -} - -enum lttng_error_code lttng_action_mi_serialize(const struct lttng_trigger *trigger, - const struct lttng_action *action, - struct mi_writer *writer, - const struct mi_lttng_error_query_callbacks - *error_query_callbacks, - struct lttng_dynamic_array *action_path_indexes) -{ - int ret; - enum lttng_error_code ret_code; - struct lttng_action_path *action_path = NULL; - struct lttng_error_query_results *error_query_results = NULL; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(writer); - - /* Open action. */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_action); - if (ret) { - goto mi_error; - } - - if (action->type == LTTNG_ACTION_TYPE_LIST) { - /* - * Recursion is safe since action lists can't be nested for - * the moment. - */ - ret_code = lttng_action_list_mi_serialize(trigger, action, writer, - error_query_callbacks, action_path_indexes); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Nothing else to do. */ - goto close_action_element; - } - - LTTNG_ASSERT(action->mi_serialize); - ret_code = action->mi_serialize(action, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Error query for the action. */ - if (error_query_callbacks && error_query_callbacks->action_cb) { - const uint64_t *action_path_indexes_raw_pointer = NULL; - const size_t action_path_indexes_size = - lttng_dynamic_array_get_count( - action_path_indexes); - - if (action_path_indexes_size != 0) { - action_path_indexes_raw_pointer = - (const uint64_t *) action_path_indexes - ->buffer.data; - } - - action_path = lttng_action_path_create( - action_path_indexes_raw_pointer, - action_path_indexes_size); - LTTNG_ASSERT(action_path); - - ret_code = error_query_callbacks->action_cb( - trigger, action_path, &error_query_results); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Serialize the error query results. */ - ret_code = lttng_error_query_results_mi_serialize( - error_query_results, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - } - -close_action_element: - /* Close action. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - lttng_action_path_destroy(action_path); - lttng_error_query_results_destroy(error_query_results); - return ret_code; -} diff --git a/src/common/actions/action.cpp b/src/common/actions/action.cpp new file mode 100644 index 000000000..b71538b3f --- /dev/null +++ b/src/common/actions/action.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *lttng_action_type_string(enum lttng_action_type action_type) +{ + switch (action_type) { + case LTTNG_ACTION_TYPE_UNKNOWN: + return "UNKNOWN"; + case LTTNG_ACTION_TYPE_LIST: + return "LIST"; + case LTTNG_ACTION_TYPE_NOTIFY: + return "NOTIFY"; + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + return "ROTATE_SESSION"; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + return "SNAPSHOT_SESSION"; + case LTTNG_ACTION_TYPE_START_SESSION: + return "START_SESSION"; + case LTTNG_ACTION_TYPE_STOP_SESSION: + return "STOP_SESSION"; + default: + return "???"; + } +} + +enum lttng_action_type lttng_action_get_type(const struct lttng_action *action) +{ + return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; +} + +void lttng_action_init(struct lttng_action *action, + enum lttng_action_type type, + action_validate_cb validate, + action_serialize_cb serialize, + action_equal_cb equal, + action_destroy_cb destroy, + action_get_rate_policy_cb get_rate_policy, + action_add_error_query_results_cb add_error_query_results, + action_mi_serialize_cb mi) +{ + urcu_ref_init(&action->ref); + action->type = type; + action->validate = validate; + action->serialize = serialize; + action->equal = equal; + action->destroy = destroy; + action->get_rate_policy = get_rate_policy; + action->add_error_query_results = add_error_query_results; + action->mi_serialize = mi; + + action->execution_request_counter = 0; + action->execution_counter = 0; + action->execution_failure_counter = 0; +} + +static +void action_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_action *action = + container_of(ref, struct lttng_action, ref); + + action->destroy(action); +} + +void lttng_action_get(struct lttng_action *action) +{ + urcu_ref_get(&action->ref); +} + +void lttng_action_put(struct lttng_action *action) +{ + if (!action) { + return; + } + + LTTNG_ASSERT(action->destroy); + urcu_ref_put(&action->ref, action_destroy_ref); +} + +void lttng_action_destroy(struct lttng_action *action) +{ + lttng_action_put(action); +} + +bool lttng_action_validate(struct lttng_action *action) +{ + bool valid; + + if (!action) { + valid = false; + goto end; + } + + if (!action->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = action->validate(action); +end: + return valid; +} + +int lttng_action_serialize(struct lttng_action *action, + struct lttng_payload *payload) +{ + int ret; + struct lttng_action_comm action_comm = { + .action_type = (int8_t) action->type, + }; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &action_comm, + sizeof(action_comm)); + if (ret) { + goto end; + } + + ret = action->serialize(action, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, + struct lttng_action **action) +{ + ssize_t consumed_len, specific_action_consumed_len; + action_create_from_payload_cb create_from_payload_cb; + const struct lttng_action_comm *action_comm; + const struct lttng_payload_view action_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*action_comm)); + + if (!view || !action) { + consumed_len = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&action_comm_view)) { + /* Payload not large enough to contain the header. */ + consumed_len = -1; + goto end; + } + + action_comm = (const struct lttng_action_comm *) action_comm_view.buffer.data; + + DBG("Create action from payload: action-type=%s", + lttng_action_type_string((lttng_action_type) action_comm->action_type)); + + switch (action_comm->action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + create_from_payload_cb = lttng_action_notify_create_from_payload; + break; + case LTTNG_ACTION_TYPE_ROTATE_SESSION: + create_from_payload_cb = + lttng_action_rotate_session_create_from_payload; + break; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + create_from_payload_cb = + lttng_action_snapshot_session_create_from_payload; + break; + case LTTNG_ACTION_TYPE_START_SESSION: + create_from_payload_cb = + lttng_action_start_session_create_from_payload; + break; + case LTTNG_ACTION_TYPE_STOP_SESSION: + create_from_payload_cb = + lttng_action_stop_session_create_from_payload; + break; + case LTTNG_ACTION_TYPE_LIST: + create_from_payload_cb = lttng_action_list_create_from_payload; + break; + default: + ERR("Failed to create action from payload, unhandled action type: action-type=%u (%s)", + action_comm->action_type, + lttng_action_type_string( + (lttng_action_type) action_comm->action_type)); + consumed_len = -1; + goto end; + } + + { + /* Create buffer view for the action-type-specific data. */ + struct lttng_payload_view specific_action_view = + lttng_payload_view_from_view(view, + sizeof(struct lttng_action_comm), + -1); + + specific_action_consumed_len = create_from_payload_cb( + &specific_action_view, action); + } + if (specific_action_consumed_len < 0) { + ERR("Failed to create specific action from buffer."); + consumed_len = -1; + goto end; + } + + LTTNG_ASSERT(*action); + + consumed_len = sizeof(struct lttng_action_comm) + + specific_action_consumed_len; + +end: + return consumed_len; +} + +bool lttng_action_is_equal(const struct lttng_action *a, + const struct lttng_action *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + LTTNG_ASSERT(a->equal); + is_equal = a->equal(a, b); +end: + return is_equal; +} + +void lttng_action_increase_execution_request_count(struct lttng_action *action) +{ + action->execution_request_counter++; +} + +void lttng_action_increase_execution_count(struct lttng_action *action) +{ + action->execution_counter++; +} + +void lttng_action_increase_execution_failure_count(struct lttng_action *action) +{ + uatomic_inc(&action->execution_failure_counter); +} + +bool lttng_action_should_execute(const struct lttng_action *action) +{ + const struct lttng_rate_policy *policy = NULL; + bool execute = false; + + if (action->get_rate_policy == NULL) { + execute = true; + goto end; + } + + policy = action->get_rate_policy(action); + if (policy == NULL) { + execute = true; + goto end; + } + + execute = lttng_rate_policy_should_execute( + policy, action->execution_request_counter); +end: + return execute; +} + +enum lttng_action_status lttng_action_add_error_query_results( + const struct lttng_action *action, + struct lttng_error_query_results *results) +{ + return action->add_error_query_results(action, results); +} + +enum lttng_action_status lttng_action_generic_add_error_query_results( + const struct lttng_action *action, + struct lttng_error_query_results *results) +{ + enum lttng_action_status action_status; + struct lttng_error_query_result *error_counter = NULL; + const uint64_t execution_failure_counter = + uatomic_read(&action->execution_failure_counter); + + error_counter = lttng_error_query_result_counter_create( + "total execution failures", + "Aggregated count of errors encountered when executing the action", + execution_failure_counter); + if (!error_counter) { + action_status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + if (lttng_error_query_results_add_result( + results, error_counter)) { + action_status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + /* Ownership transferred to the results. */ + error_counter = NULL; + action_status = LTTNG_ACTION_STATUS_OK; +end: + lttng_error_query_result_destroy(error_counter); + return action_status; +} + +enum lttng_error_code lttng_action_mi_serialize(const struct lttng_trigger *trigger, + const struct lttng_action *action, + struct mi_writer *writer, + const struct mi_lttng_error_query_callbacks + *error_query_callbacks, + struct lttng_dynamic_array *action_path_indexes) +{ + int ret; + enum lttng_error_code ret_code; + struct lttng_action_path *action_path = NULL; + struct lttng_error_query_results *error_query_results = NULL; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(writer); + + /* Open action. */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_action); + if (ret) { + goto mi_error; + } + + if (action->type == LTTNG_ACTION_TYPE_LIST) { + /* + * Recursion is safe since action lists can't be nested for + * the moment. + */ + ret_code = lttng_action_list_mi_serialize(trigger, action, writer, + error_query_callbacks, action_path_indexes); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Nothing else to do. */ + goto close_action_element; + } + + LTTNG_ASSERT(action->mi_serialize); + ret_code = action->mi_serialize(action, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Error query for the action. */ + if (error_query_callbacks && error_query_callbacks->action_cb) { + const uint64_t *action_path_indexes_raw_pointer = NULL; + const size_t action_path_indexes_size = + lttng_dynamic_array_get_count( + action_path_indexes); + + if (action_path_indexes_size != 0) { + action_path_indexes_raw_pointer = + (const uint64_t *) action_path_indexes + ->buffer.data; + } + + action_path = lttng_action_path_create( + action_path_indexes_raw_pointer, + action_path_indexes_size); + LTTNG_ASSERT(action_path); + + ret_code = error_query_callbacks->action_cb( + trigger, action_path, &error_query_results); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Serialize the error query results. */ + ret_code = lttng_error_query_results_mi_serialize( + error_query_results, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + } + +close_action_element: + /* Close action. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + lttng_action_path_destroy(action_path); + lttng_error_query_results_destroy(error_query_results); + return ret_code; +} diff --git a/src/common/actions/list.c b/src/common/actions/list.c deleted file mode 100644 index 86eabda8a..000000000 --- a/src/common/actions/list.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (C) 2019 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_LIST_ACTION(action) \ - (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_LIST) - -struct lttng_action_list { - struct lttng_action parent; - - /* The array owns the action elements. */ - struct lttng_dynamic_pointer_array actions; -}; - -struct lttng_action_list_comm { - uint32_t action_count; - - /* - * Variable data: each element serialized sequentially. - */ - char data[]; -} LTTNG_PACKED; - -static void destroy_lttng_action_list_element(void *ptr) -{ - struct lttng_action *element = (struct lttng_action *) ptr; - - lttng_action_destroy(element); -} - -static struct lttng_action_list *action_list_from_action( - const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_list, parent); -} - -static const struct lttng_action_list *action_list_from_action_const( - const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_list, parent); -} - -static bool lttng_action_list_validate(struct lttng_action *action) -{ - unsigned int i, count; - struct lttng_action_list *action_list; - bool valid; - - LTTNG_ASSERT(IS_LIST_ACTION(action)); - - action_list = action_list_from_action(action); - - count = lttng_dynamic_pointer_array_get_count(&action_list->actions); - - for (i = 0; i < count; i++) { - struct lttng_action *child = - lttng_dynamic_pointer_array_get_pointer( - &action_list->actions, i); - - LTTNG_ASSERT(child); - - if (!lttng_action_validate(child)) { - valid = false; - goto end; - } - } - - valid = true; - -end: - return valid; -} - -static bool lttng_action_list_is_equal( - const struct lttng_action *_a, const struct lttng_action *_b) -{ - bool is_equal = false; - unsigned int i; - unsigned int a_count, b_count; - - if (lttng_action_list_get_count(_a, &a_count) != - LTTNG_ACTION_STATUS_OK) { - goto end; - } - - if (lttng_action_list_get_count(_b, &b_count) != - LTTNG_ACTION_STATUS_OK) { - goto end; - } - - if (a_count != b_count) { - goto end; - } - - for (i = 0; i < a_count; i++) { - const struct lttng_action *child_a = - lttng_action_list_get_at_index(_a, i); - const struct lttng_action *child_b = - lttng_action_list_get_at_index(_b, i); - - LTTNG_ASSERT(child_a); - LTTNG_ASSERT(child_b); - - if (!lttng_action_is_equal(child_a, child_b)) { - goto end; - } - } - - is_equal = true; -end: - return is_equal; -} - -static int lttng_action_list_serialize( - struct lttng_action *action, struct lttng_payload *payload) -{ - struct lttng_action_list *action_list; - struct lttng_action_list_comm comm; - int ret; - unsigned int i, count; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(payload); - LTTNG_ASSERT(IS_LIST_ACTION(action)); - - action_list = action_list_from_action(action); - - DBG("Serializing action list"); - - count = lttng_dynamic_pointer_array_get_count(&action_list->actions); - - comm.action_count = count; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - ret = -1; - goto end; - } - - for (i = 0; i < count; i++) { - struct lttng_action *child = - lttng_dynamic_pointer_array_get_pointer( - &action_list->actions, i); - - LTTNG_ASSERT(child); - - ret = lttng_action_serialize(child, payload); - if (ret) { - goto end; - } - } - - ret = 0; - -end: - return ret; -} - -static void lttng_action_list_destroy(struct lttng_action *action) -{ - struct lttng_action_list *action_list; - - if (!action) { - goto end; - } - - action_list = action_list_from_action(action); - lttng_dynamic_pointer_array_reset(&action_list->actions); - free(action_list); - -end: - return; -} - -ssize_t lttng_action_list_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action **p_action) -{ - ssize_t consumed_len; - const struct lttng_action_list_comm *comm; - struct lttng_action *list; - struct lttng_action *child_action = NULL; - enum lttng_action_status status; - size_t i; - - list = lttng_action_list_create(); - if (!list) { - consumed_len = -1; - goto end; - } - - comm = (typeof(comm)) view->buffer.data; - - consumed_len = sizeof(struct lttng_action_list_comm); - - for (i = 0; i < comm->action_count; i++) { - ssize_t consumed_len_child; - struct lttng_payload_view child_view = - lttng_payload_view_from_view(view, consumed_len, - view->buffer.size - consumed_len); - - if (!lttng_payload_view_is_valid(&child_view)) { - consumed_len = -1; - goto end; - } - - consumed_len_child = lttng_action_create_from_payload( - &child_view, &child_action); - if (consumed_len_child < 0) { - consumed_len = -1; - goto end; - } - - status = lttng_action_list_add_action(list, child_action); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - /* Transfer ownership to the action list. */ - lttng_action_put(child_action); - child_action = NULL; - - consumed_len += consumed_len_child; - } - - *p_action = list; - list = NULL; - -end: - lttng_action_list_destroy(list); - return consumed_len; -} - -static enum lttng_action_status lttng_action_list_add_error_query_results( - const struct lttng_action *action, - struct lttng_error_query_results *results) -{ - unsigned int i, count; - enum lttng_action_status action_status; - const struct lttng_action_list *list = - container_of(action, typeof(*list), parent); - - action_status = lttng_action_list_get_count(action, &count); - if (action_status != LTTNG_ACTION_STATUS_OK) { - goto end; - } - - for (i = 0; i < count; i++) { - struct lttng_action *inner_action = - lttng_action_list_borrow_mutable_at_index(action, i); - - action_status = lttng_action_add_error_query_results( - inner_action, results); - if (action_status != LTTNG_ACTION_STATUS_OK) { - goto end; - } - } -end: - return action_status; -} - -enum lttng_error_code lttng_action_list_mi_serialize( - const struct lttng_trigger *trigger, - const struct lttng_action *action, - struct mi_writer *writer, - const struct mi_lttng_error_query_callbacks - *error_query_callbacks, - struct lttng_dynamic_array *action_path_indexes) -{ - int ret; - struct lttng_action_list *action_list; - unsigned int i, count; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(IS_LIST_ACTION(action)); - LTTNG_ASSERT(writer); - - /* Open action list. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_action_list); - if (ret) { - goto mi_error; - } - - /* Serialize every action of the list. */ - action_list = action_list_from_action(action); - count = lttng_dynamic_pointer_array_get_count(&action_list->actions); - for (i = 0; i < count; i++) { - const struct lttng_action *child = - lttng_action_list_get_at_index(action, i); - const uint64_t index = (uint64_t) i; - - LTTNG_ASSERT(child); - - /* - * Add the index to the action path. - * - * This index is replaced on every iteration to walk the action - * tree in-order and to re-use the dynamic array instead of - * copying it at every level. - */ - ret = lttng_dynamic_array_add_element( - action_path_indexes, &index); - if (ret) { - ret_code = LTTNG_ERR_NOMEM; - goto end; - } - - ret_code = lttng_action_mi_serialize(trigger, child, writer, - error_query_callbacks, action_path_indexes); - if (ret_code != LTTNG_OK) { - goto end; - } - - ret = lttng_dynamic_array_remove_element(action_path_indexes, - lttng_dynamic_array_get_count( - action_path_indexes) - - 1); - if (ret) { - ret_code = LTTNG_ERR_UNK; - goto end; - } - } - - /* Close action_list element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_action *lttng_action_list_create(void) -{ - struct lttng_action_list *action_list; - struct lttng_action *action; - - action_list = zmalloc(sizeof(struct lttng_action_list)); - if (!action_list) { - action = NULL; - goto end; - } - - action = &action_list->parent; - - /* - * The mi for the list is handled at the lttng_action_mi level to ease - * action path management for error query. - */ - lttng_action_init(action, LTTNG_ACTION_TYPE_LIST, - lttng_action_list_validate, lttng_action_list_serialize, - lttng_action_list_is_equal, lttng_action_list_destroy, - NULL, lttng_action_list_add_error_query_results, NULL); - - lttng_dynamic_pointer_array_init(&action_list->actions, - destroy_lttng_action_list_element); - -end: - return action; -} - -enum lttng_action_status lttng_action_list_add_action( - struct lttng_action *list, struct lttng_action *action) -{ - struct lttng_action_list *action_list; - enum lttng_action_status status; - int ret; - - if (!list || !IS_LIST_ACTION(list) || !action) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - /* - * Don't allow adding lists in lists for now, since we're afraid of - * cycles. - */ - if (IS_LIST_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_list = action_list_from_action(list); - - ret = lttng_dynamic_pointer_array_add_pointer(&action_list->actions, - action); - if (ret < 0) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - /* Take ownership of the object. */ - lttng_action_get(action); - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_list_get_count( - const struct lttng_action *list, unsigned int *count) -{ - const struct lttng_action_list *action_list; - enum lttng_action_status status = LTTNG_ACTION_STATUS_OK; - - if (!list || !IS_LIST_ACTION(list)) { - status = LTTNG_ACTION_STATUS_INVALID; - *count = 0; - goto end; - } - - action_list = action_list_from_action_const(list); - *count = lttng_dynamic_pointer_array_get_count(&action_list->actions); -end: - return status; -} - -const struct lttng_action *lttng_action_list_get_at_index( - const struct lttng_action *list, unsigned int index) -{ - return lttng_action_list_borrow_mutable_at_index(list, index); -} - -struct lttng_action *lttng_action_list_borrow_mutable_at_index( - const struct lttng_action *list, unsigned int index) -{ - unsigned int count; - const struct lttng_action_list *action_list; - struct lttng_action *action = NULL; - - if (lttng_action_list_get_count(list, &count) != - LTTNG_ACTION_STATUS_OK) { - goto end; - } - - if (index >= count) { - goto end; - } - - action_list = action_list_from_action_const(list); - action = lttng_dynamic_pointer_array_get_pointer(&action_list->actions, - index); -end: - return action; -} diff --git a/src/common/actions/list.cpp b/src/common/actions/list.cpp new file mode 100644 index 000000000..42d3d653e --- /dev/null +++ b/src/common/actions/list.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_LIST_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_LIST) + +struct lttng_action_list { + struct lttng_action parent; + + /* The array owns the action elements. */ + struct lttng_dynamic_pointer_array actions; +}; + +struct lttng_action_list_comm { + uint32_t action_count; + + /* + * Variable data: each element serialized sequentially. + */ + char data[]; +} LTTNG_PACKED; + +static void destroy_lttng_action_list_element(void *ptr) +{ + struct lttng_action *element = (struct lttng_action *) ptr; + + lttng_action_destroy(element); +} + +static struct lttng_action_list *action_list_from_action( + const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_list, parent); +} + +static const struct lttng_action_list *action_list_from_action_const( + const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_list, parent); +} + +static bool lttng_action_list_validate(struct lttng_action *action) +{ + unsigned int i, count; + struct lttng_action_list *action_list; + bool valid; + + LTTNG_ASSERT(IS_LIST_ACTION(action)); + + action_list = action_list_from_action(action); + + count = lttng_dynamic_pointer_array_get_count(&action_list->actions); + + for (i = 0; i < count; i++) { + struct lttng_action *child = + (lttng_action *) lttng_dynamic_pointer_array_get_pointer( + &action_list->actions, i); + + LTTNG_ASSERT(child); + + if (!lttng_action_validate(child)) { + valid = false; + goto end; + } + } + + valid = true; + +end: + return valid; +} + +static bool lttng_action_list_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + unsigned int i; + unsigned int a_count, b_count; + + if (lttng_action_list_get_count(_a, &a_count) != + LTTNG_ACTION_STATUS_OK) { + goto end; + } + + if (lttng_action_list_get_count(_b, &b_count) != + LTTNG_ACTION_STATUS_OK) { + goto end; + } + + if (a_count != b_count) { + goto end; + } + + for (i = 0; i < a_count; i++) { + const struct lttng_action *child_a = + lttng_action_list_get_at_index(_a, i); + const struct lttng_action *child_b = + lttng_action_list_get_at_index(_b, i); + + LTTNG_ASSERT(child_a); + LTTNG_ASSERT(child_b); + + if (!lttng_action_is_equal(child_a, child_b)) { + goto end; + } + } + + is_equal = true; +end: + return is_equal; +} + +static int lttng_action_list_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_list *action_list; + struct lttng_action_list_comm comm; + int ret; + unsigned int i, count; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(payload); + LTTNG_ASSERT(IS_LIST_ACTION(action)); + + action_list = action_list_from_action(action); + + DBG("Serializing action list"); + + count = lttng_dynamic_pointer_array_get_count(&action_list->actions); + + comm.action_count = count; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + for (i = 0; i < count; i++) { + struct lttng_action *child = + (lttng_action *) lttng_dynamic_pointer_array_get_pointer( + &action_list->actions, i); + + LTTNG_ASSERT(child); + + ret = lttng_action_serialize(child, payload); + if (ret) { + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static void lttng_action_list_destroy(struct lttng_action *action) +{ + struct lttng_action_list *action_list; + + if (!action) { + goto end; + } + + action_list = action_list_from_action(action); + lttng_dynamic_pointer_array_reset(&action_list->actions); + free(action_list); + +end: + return; +} + +ssize_t lttng_action_list_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + const struct lttng_action_list_comm *comm; + struct lttng_action *list; + struct lttng_action *child_action = NULL; + enum lttng_action_status status; + size_t i; + + list = lttng_action_list_create(); + if (!list) { + consumed_len = -1; + goto end; + } + + comm = (typeof(comm)) view->buffer.data; + + consumed_len = sizeof(struct lttng_action_list_comm); + + for (i = 0; i < comm->action_count; i++) { + ssize_t consumed_len_child; + struct lttng_payload_view child_view = + lttng_payload_view_from_view(view, consumed_len, + view->buffer.size - consumed_len); + + if (!lttng_payload_view_is_valid(&child_view)) { + consumed_len = -1; + goto end; + } + + consumed_len_child = lttng_action_create_from_payload( + &child_view, &child_action); + if (consumed_len_child < 0) { + consumed_len = -1; + goto end; + } + + status = lttng_action_list_add_action(list, child_action); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + /* Transfer ownership to the action list. */ + lttng_action_put(child_action); + child_action = NULL; + + consumed_len += consumed_len_child; + } + + *p_action = list; + list = NULL; + +end: + lttng_action_list_destroy(list); + return consumed_len; +} + +static enum lttng_action_status lttng_action_list_add_error_query_results( + const struct lttng_action *action, + struct lttng_error_query_results *results) +{ + unsigned int i, count; + enum lttng_action_status action_status; + const struct lttng_action_list *list = + container_of(action, typeof(*list), parent); + + action_status = lttng_action_list_get_count(action, &count); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto end; + } + + for (i = 0; i < count; i++) { + struct lttng_action *inner_action = + lttng_action_list_borrow_mutable_at_index(action, i); + + action_status = lttng_action_add_error_query_results( + inner_action, results); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto end; + } + } +end: + return action_status; +} + +enum lttng_error_code lttng_action_list_mi_serialize( + const struct lttng_trigger *trigger, + const struct lttng_action *action, + struct mi_writer *writer, + const struct mi_lttng_error_query_callbacks + *error_query_callbacks, + struct lttng_dynamic_array *action_path_indexes) +{ + int ret; + struct lttng_action_list *action_list; + unsigned int i, count; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(IS_LIST_ACTION(action)); + LTTNG_ASSERT(writer); + + /* Open action list. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_action_list); + if (ret) { + goto mi_error; + } + + /* Serialize every action of the list. */ + action_list = action_list_from_action(action); + count = lttng_dynamic_pointer_array_get_count(&action_list->actions); + for (i = 0; i < count; i++) { + const struct lttng_action *child = + lttng_action_list_get_at_index(action, i); + const uint64_t index = (uint64_t) i; + + LTTNG_ASSERT(child); + + /* + * Add the index to the action path. + * + * This index is replaced on every iteration to walk the action + * tree in-order and to re-use the dynamic array instead of + * copying it at every level. + */ + ret = lttng_dynamic_array_add_element( + action_path_indexes, &index); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret_code = lttng_action_mi_serialize(trigger, child, writer, + error_query_callbacks, action_path_indexes); + if (ret_code != LTTNG_OK) { + goto end; + } + + ret = lttng_dynamic_array_remove_element(action_path_indexes, + lttng_dynamic_array_get_count( + action_path_indexes) - + 1); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; + } + } + + /* Close action_list element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_action *lttng_action_list_create(void) +{ + struct lttng_action_list *action_list; + struct lttng_action *action; + + action_list = (lttng_action_list *) zmalloc(sizeof(struct lttng_action_list)); + if (!action_list) { + action = NULL; + goto end; + } + + action = &action_list->parent; + + /* + * The mi for the list is handled at the lttng_action_mi level to ease + * action path management for error query. + */ + lttng_action_init(action, LTTNG_ACTION_TYPE_LIST, + lttng_action_list_validate, lttng_action_list_serialize, + lttng_action_list_is_equal, lttng_action_list_destroy, + NULL, lttng_action_list_add_error_query_results, NULL); + + lttng_dynamic_pointer_array_init(&action_list->actions, + destroy_lttng_action_list_element); + +end: + return action; +} + +enum lttng_action_status lttng_action_list_add_action( + struct lttng_action *list, struct lttng_action *action) +{ + struct lttng_action_list *action_list; + enum lttng_action_status status; + int ret; + + if (!list || !IS_LIST_ACTION(list) || !action) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + /* + * Don't allow adding lists in lists for now, since we're afraid of + * cycles. + */ + if (IS_LIST_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_list = action_list_from_action(list); + + ret = lttng_dynamic_pointer_array_add_pointer(&action_list->actions, + action); + if (ret < 0) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + /* Take ownership of the object. */ + lttng_action_get(action); + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_list_get_count( + const struct lttng_action *list, unsigned int *count) +{ + const struct lttng_action_list *action_list; + enum lttng_action_status status = LTTNG_ACTION_STATUS_OK; + + if (!list || !IS_LIST_ACTION(list)) { + status = LTTNG_ACTION_STATUS_INVALID; + *count = 0; + goto end; + } + + action_list = action_list_from_action_const(list); + *count = lttng_dynamic_pointer_array_get_count(&action_list->actions); +end: + return status; +} + +const struct lttng_action *lttng_action_list_get_at_index( + const struct lttng_action *list, unsigned int index) +{ + return lttng_action_list_borrow_mutable_at_index(list, index); +} + +struct lttng_action *lttng_action_list_borrow_mutable_at_index( + const struct lttng_action *list, unsigned int index) +{ + unsigned int count; + const struct lttng_action_list *action_list; + struct lttng_action *action = NULL; + + if (lttng_action_list_get_count(list, &count) != + LTTNG_ACTION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + action_list = action_list_from_action_const(list); + action = (lttng_action *) lttng_dynamic_pointer_array_get_pointer(&action_list->actions, + index); +end: + return action; +} diff --git a/src/common/actions/notify.c b/src/common/actions/notify.c deleted file mode 100644 index c34f3845f..000000000 --- a/src/common/actions/notify.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/actions/notify.cpp b/src/common/actions/notify.cpp new file mode 100644 index 000000000..e3f837ade --- /dev/null +++ b/src/common/actions/notify.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/actions/path.c b/src/common/actions/path.c deleted file mode 100644 index 09cf08a48..000000000 --- a/src/common/actions/path.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include - -struct lttng_action_path_comm { - uint32_t index_count; - uint64_t indexes[]; -} LTTNG_PACKED; - -struct lttng_action_path *lttng_action_path_create( - const uint64_t *indexes, size_t index_count) -{ - int ret; - size_t i; - struct lttng_action_path *path = NULL; - - if (!indexes && index_count > 0) { - goto error; - } - - path = zmalloc(sizeof(*path)); - if (!path) { - goto error; - } - - lttng_dynamic_array_init(&path->indexes, sizeof(uint64_t), NULL); - - for (i = 0; i < index_count; i++) { - ret = lttng_dynamic_array_add_element( - &path->indexes, &indexes[i]); - if (ret) { - goto error; - } - } - - goto end; -error: - lttng_action_path_destroy(path); - path = NULL; -end: - return path; -} - -enum lttng_action_path_status lttng_action_path_get_index_count( - const struct lttng_action_path *path, size_t *index_count) -{ - enum lttng_action_path_status status; - - if (!path || !index_count) { - status = LTTNG_ACTION_PATH_STATUS_INVALID; - goto end; - } - - *index_count = lttng_dynamic_array_get_count(&path->indexes); - status = LTTNG_ACTION_PATH_STATUS_OK; -end: - return status; -} - -enum lttng_action_path_status lttng_action_path_get_index_at_index( - const struct lttng_action_path *path, - size_t path_index, - uint64_t *out_index) -{ - enum lttng_action_path_status status; - - if (!path || !out_index || - path_index >= lttng_dynamic_array_get_count( - &path->indexes)) { - status = LTTNG_ACTION_PATH_STATUS_INVALID; - goto end; - } - - *out_index = *((typeof(out_index)) lttng_dynamic_array_get_element( - &path->indexes, path_index)); - status = LTTNG_ACTION_PATH_STATUS_OK; -end: - return status; -} - -void lttng_action_path_destroy(struct lttng_action_path *action_path) -{ - if (!action_path) { - goto end; - } - - lttng_dynamic_array_reset(&action_path->indexes); - free(action_path); -end: - return; -} - -int lttng_action_path_copy(const struct lttng_action_path *src, - struct lttng_action_path *dst) -{ - int ret; - size_t i, src_count; - - LTTNG_ASSERT(src); - LTTNG_ASSERT(dst); - - lttng_dynamic_array_init(&dst->indexes, sizeof(uint64_t), NULL); - src_count = lttng_dynamic_array_get_count(&src->indexes); - - for (i = 0; i < src_count; i++) { - const void *index = lttng_dynamic_array_get_element( - &src->indexes, i); - - ret = lttng_dynamic_array_add_element(&dst->indexes, index); - if (ret) { - goto error; - } - } - - ret = 0; - goto end; -error: - lttng_dynamic_array_reset(&dst->indexes); -end: - return ret; -} - -ssize_t lttng_action_path_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action_path **_action_path) -{ - ssize_t consumed_size = 0, ret = -1; - const struct lttng_action_path_comm *header; - struct lttng_action_path *action_path = NULL; - const struct lttng_payload_view header_view = - lttng_payload_view_from_view(view, 0, sizeof(*header)); - - if (!lttng_payload_view_is_valid(&header_view)) { - goto end; - } - - header = (typeof(header)) header_view.buffer.data; - consumed_size += header_view.buffer.size; - - /* - * An action path of size 0 can exist and represents a trigger with a - * single non-list action. Handle it differently since a payload view of - * size 0 is considered invalid. - */ - if (header->index_count != 0) - { - const struct lttng_payload_view indexes_view = - lttng_payload_view_from_view(view, - consumed_size, - header->index_count * - sizeof(uint64_t)); - - if (!lttng_payload_view_is_valid(&indexes_view)) { - goto end; - } - - consumed_size += indexes_view.buffer.size; - action_path = lttng_action_path_create( - (const uint64_t *) indexes_view.buffer.data, - header->index_count); - if (!action_path) { - goto end; - } - } else { - action_path = lttng_action_path_create(NULL, 0); - if (!action_path) { - goto end; - } - } - - ret = consumed_size; - *_action_path = action_path; -end: - return ret; -} - -int lttng_action_path_serialize(const struct lttng_action_path *action_path, - struct lttng_payload *payload) -{ - int ret; - size_t index_count, i; - enum lttng_action_path_status status; - - status = lttng_action_path_get_index_count(action_path, &index_count); - if (status != LTTNG_ACTION_PATH_STATUS_OK) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, - &((struct lttng_action_path_comm) { - .index_count = index_count - }), - sizeof(struct lttng_action_path_comm)); - - for (i = 0; i < index_count; i++) { - uint64_t path_index; - - status = lttng_action_path_get_index_at_index( - action_path, i, &path_index); - if (status != LTTNG_ACTION_PATH_STATUS_OK) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, &path_index, - sizeof(path_index)); - if (ret) { - goto end; - } - } - - ret = 0; -end: - return ret; -} diff --git a/src/common/actions/path.cpp b/src/common/actions/path.cpp new file mode 100644 index 000000000..816f2cd21 --- /dev/null +++ b/src/common/actions/path.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +struct lttng_action_path_comm { + uint32_t index_count; + uint64_t indexes[]; +} LTTNG_PACKED; + +struct lttng_action_path *lttng_action_path_create( + const uint64_t *indexes, size_t index_count) +{ + int ret; + size_t i; + struct lttng_action_path *path = NULL; + + if (!indexes && index_count > 0) { + goto error; + } + + path = (lttng_action_path *) zmalloc(sizeof(*path)); + if (!path) { + goto error; + } + + lttng_dynamic_array_init(&path->indexes, sizeof(uint64_t), NULL); + + for (i = 0; i < index_count; i++) { + ret = lttng_dynamic_array_add_element( + &path->indexes, &indexes[i]); + if (ret) { + goto error; + } + } + + goto end; +error: + lttng_action_path_destroy(path); + path = NULL; +end: + return path; +} + +enum lttng_action_path_status lttng_action_path_get_index_count( + const struct lttng_action_path *path, size_t *index_count) +{ + enum lttng_action_path_status status; + + if (!path || !index_count) { + status = LTTNG_ACTION_PATH_STATUS_INVALID; + goto end; + } + + *index_count = lttng_dynamic_array_get_count(&path->indexes); + status = LTTNG_ACTION_PATH_STATUS_OK; +end: + return status; +} + +enum lttng_action_path_status lttng_action_path_get_index_at_index( + const struct lttng_action_path *path, + size_t path_index, + uint64_t *out_index) +{ + enum lttng_action_path_status status; + + if (!path || !out_index || + path_index >= lttng_dynamic_array_get_count( + &path->indexes)) { + status = LTTNG_ACTION_PATH_STATUS_INVALID; + goto end; + } + + *out_index = *((typeof(out_index)) lttng_dynamic_array_get_element( + &path->indexes, path_index)); + status = LTTNG_ACTION_PATH_STATUS_OK; +end: + return status; +} + +void lttng_action_path_destroy(struct lttng_action_path *action_path) +{ + if (!action_path) { + goto end; + } + + lttng_dynamic_array_reset(&action_path->indexes); + free(action_path); +end: + return; +} + +int lttng_action_path_copy(const struct lttng_action_path *src, + struct lttng_action_path *dst) +{ + int ret; + size_t i, src_count; + + LTTNG_ASSERT(src); + LTTNG_ASSERT(dst); + + lttng_dynamic_array_init(&dst->indexes, sizeof(uint64_t), NULL); + src_count = lttng_dynamic_array_get_count(&src->indexes); + + for (i = 0; i < src_count; i++) { + const void *index = lttng_dynamic_array_get_element( + &src->indexes, i); + + ret = lttng_dynamic_array_add_element(&dst->indexes, index); + if (ret) { + goto error; + } + } + + ret = 0; + goto end; +error: + lttng_dynamic_array_reset(&dst->indexes); +end: + return ret; +} + +ssize_t lttng_action_path_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action_path **_action_path) +{ + ssize_t consumed_size = 0, ret = -1; + const struct lttng_action_path_comm *header; + struct lttng_action_path *action_path = NULL; + const struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + + if (!lttng_payload_view_is_valid(&header_view)) { + goto end; + } + + header = (typeof(header)) header_view.buffer.data; + consumed_size += header_view.buffer.size; + + /* + * An action path of size 0 can exist and represents a trigger with a + * single non-list action. Handle it differently since a payload view of + * size 0 is considered invalid. + */ + if (header->index_count != 0) + { + const struct lttng_payload_view indexes_view = + lttng_payload_view_from_view(view, + consumed_size, + header->index_count * + sizeof(uint64_t)); + + if (!lttng_payload_view_is_valid(&indexes_view)) { + goto end; + } + + consumed_size += indexes_view.buffer.size; + action_path = lttng_action_path_create( + (const uint64_t *) indexes_view.buffer.data, + header->index_count); + if (!action_path) { + goto end; + } + } else { + action_path = lttng_action_path_create(NULL, 0); + if (!action_path) { + goto end; + } + } + + ret = consumed_size; + *_action_path = action_path; +end: + return ret; +} + +int lttng_action_path_serialize(const struct lttng_action_path *action_path, + struct lttng_payload *payload) +{ + int ret; + size_t index_count, i; + enum lttng_action_path_status status; + lttng_action_path_comm comm; + + status = lttng_action_path_get_index_count(action_path, &index_count); + if (status != LTTNG_ACTION_PATH_STATUS_OK) { + ret = -1; + goto end; + } + + comm = { + .index_count = (uint32_t) index_count, + }; + ret = lttng_dynamic_buffer_append(&payload->buffer, + &comm, + sizeof(struct lttng_action_path_comm)); + + for (i = 0; i < index_count; i++) { + uint64_t path_index; + + status = lttng_action_path_get_index_at_index( + action_path, i, &path_index); + if (status != LTTNG_ACTION_PATH_STATUS_OK) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, &path_index, + sizeof(path_index)); + if (ret) { + goto end; + } + } + + ret = 0; +end: + return ret; +} diff --git a/src/common/actions/rate-policy.c b/src/common/actions/rate-policy.c deleted file mode 100644 index c47e17018..000000000 --- a/src/common/actions/rate-policy.c +++ /dev/null @@ -1,812 +0,0 @@ -/* - * Copyright (C) 2021 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_EVERY_N_RATE_POLICY(policy) \ - (lttng_rate_policy_get_type(policy) == LTTNG_RATE_POLICY_TYPE_EVERY_N) - -#define IS_ONCE_AFTER_N_RATE_POLICY(policy) \ - (lttng_rate_policy_get_type(policy) == \ - LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N) - -typedef void (*rate_policy_destroy_cb)(struct lttng_rate_policy *rate_policy); -typedef int (*rate_policy_serialize_cb)(struct lttng_rate_policy *rate_policy, - struct lttng_payload *payload); -typedef bool (*rate_policy_equal_cb)(const struct lttng_rate_policy *a, - const struct lttng_rate_policy *b); -typedef ssize_t (*rate_policy_create_from_payload_cb)( - struct lttng_payload_view *view, - struct lttng_rate_policy **rate_policy); -typedef struct lttng_rate_policy *(*rate_policy_copy_cb)( - const struct lttng_rate_policy *source); -typedef enum lttng_error_code (*rate_policy_mi_serialize_cb)( - const struct lttng_rate_policy *rate_policy, - struct mi_writer *writer); - -struct lttng_rate_policy { - enum lttng_rate_policy_type type; - rate_policy_serialize_cb serialize; - rate_policy_equal_cb equal; - rate_policy_destroy_cb destroy; - rate_policy_copy_cb copy; - rate_policy_mi_serialize_cb mi_serialize; -}; - -struct lttng_rate_policy_every_n { - struct lttng_rate_policy parent; - uint64_t interval; -}; - -struct lttng_rate_policy_once_after_n { - struct lttng_rate_policy parent; - uint64_t threshold; -}; - -struct lttng_rate_policy_comm { - /* enum lttng_rate_policy_type */ - int8_t rate_policy_type; -} LTTNG_PACKED; - -struct lttng_rate_policy_once_after_n_comm { - uint64_t threshold; -} LTTNG_PACKED; - -struct lttng_rate_policy_every_n_comm { - uint64_t interval; -} LTTNG_PACKED; - -/* Forward declaration. */ -static void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy, - enum lttng_rate_policy_type type, - rate_policy_serialize_cb serialize, - rate_policy_equal_cb equal, - rate_policy_destroy_cb destroy, - rate_policy_copy_cb copy, - rate_policy_mi_serialize_cb mi); - -/* Forward declaration. Every n */ -static bool lttng_rate_policy_every_n_should_execute( - const struct lttng_rate_policy *policy, uint64_t counter); - -/* Forward declaration. Once after N */ -static bool lttng_rate_policy_once_after_n_should_execute( - const struct lttng_rate_policy *policy, uint64_t counter); - -const char *lttng_rate_policy_type_string( - enum lttng_rate_policy_type rate_policy_type) -{ - switch (rate_policy_type) { - case LTTNG_RATE_POLICY_TYPE_EVERY_N: - return "EVERY-N"; - case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: - return "ONCE-AFTER-N"; - default: - return "???"; - } -} - -enum lttng_rate_policy_type lttng_rate_policy_get_type( - const struct lttng_rate_policy *policy) -{ - return policy ? policy->type : LTTNG_RATE_POLICY_TYPE_UNKNOWN; -} - -void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy, - enum lttng_rate_policy_type type, - rate_policy_serialize_cb serialize, - rate_policy_equal_cb equal, - rate_policy_destroy_cb destroy, - rate_policy_copy_cb copy, - rate_policy_mi_serialize_cb mi) -{ - rate_policy->type = type; - rate_policy->serialize = serialize; - rate_policy->equal = equal; - rate_policy->destroy = destroy; - rate_policy->copy = copy; - rate_policy->mi_serialize = mi; -} - -void lttng_rate_policy_destroy(struct lttng_rate_policy *rate_policy) -{ - if (!rate_policy) { - return; - } - - rate_policy->destroy(rate_policy); -} - -int lttng_rate_policy_serialize(struct lttng_rate_policy *rate_policy, - struct lttng_payload *payload) -{ - int ret; - struct lttng_rate_policy_comm rate_policy_comm = { - .rate_policy_type = (int8_t) rate_policy->type, - }; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &rate_policy_comm, - sizeof(rate_policy_comm)); - if (ret) { - goto end; - } - - ret = rate_policy->serialize(rate_policy, payload); - if (ret) { - goto end; - } -end: - return ret; -} - -static ssize_t lttng_rate_policy_once_after_n_create_from_payload( - struct lttng_payload_view *view, - struct lttng_rate_policy **rate_policy) -{ - ssize_t consumed_len = -1; - struct lttng_rate_policy *policy = NULL; - const struct lttng_rate_policy_once_after_n_comm *comm; - const struct lttng_payload_view comm_view = - lttng_payload_view_from_view(view, 0, sizeof(*comm)); - - if (!view || !rate_policy) { - consumed_len = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&comm_view)) { - /* Payload not large enough to contain the header. */ - consumed_len = -1; - goto end; - } - - comm = (const struct lttng_rate_policy_once_after_n_comm *) - comm_view.buffer.data; - - policy = lttng_rate_policy_once_after_n_create(comm->threshold); - if (policy == NULL) { - consumed_len = -1; - goto end; - } - - *rate_policy = policy; - consumed_len = sizeof(*comm); - -end: - return consumed_len; -} - -static ssize_t lttng_rate_policy_every_n_create_from_payload( - struct lttng_payload_view *view, - struct lttng_rate_policy **rate_policy) -{ - ssize_t consumed_len = -1; - struct lttng_rate_policy *policy = NULL; - const struct lttng_rate_policy_every_n_comm *comm; - const struct lttng_payload_view comm_view = - lttng_payload_view_from_view(view, 0, sizeof(*comm)); - - if (!view || !rate_policy) { - consumed_len = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&comm_view)) { - /* Payload not large enough to contain the header. */ - consumed_len = -1; - goto end; - } - - comm = (const struct lttng_rate_policy_every_n_comm *) - comm_view.buffer.data; - - policy = lttng_rate_policy_every_n_create(comm->interval); - if (policy == NULL) { - consumed_len = -1; - goto end; - } - - *rate_policy = policy; - consumed_len = sizeof(*comm); - -end: - return consumed_len; -} - -ssize_t lttng_rate_policy_create_from_payload(struct lttng_payload_view *view, - struct lttng_rate_policy **rate_policy) -{ - ssize_t consumed_len, specific_rate_policy_consumed_len; - rate_policy_create_from_payload_cb create_from_payload_cb; - const struct lttng_rate_policy_comm *rate_policy_comm; - const struct lttng_payload_view rate_policy_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*rate_policy_comm)); - - if (!view || !rate_policy) { - consumed_len = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&rate_policy_comm_view)) { - /* Payload not large enough to contain the header. */ - consumed_len = -1; - goto end; - } - - rate_policy_comm = (const struct lttng_rate_policy_comm *) - rate_policy_comm_view.buffer.data; - - DBG("Create rate_policy from payload: rate-policy-type=%s", - lttng_rate_policy_type_string( - rate_policy_comm->rate_policy_type)); - - switch (rate_policy_comm->rate_policy_type) { - case LTTNG_RATE_POLICY_TYPE_EVERY_N: - create_from_payload_cb = - lttng_rate_policy_every_n_create_from_payload; - break; - case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: - create_from_payload_cb = - lttng_rate_policy_once_after_n_create_from_payload; - break; - default: - ERR("Failed to create rate-policy from payload, unhandled rate-policy type: rate-policy-type=%u (%s)", - rate_policy_comm->rate_policy_type, - lttng_rate_policy_type_string( - rate_policy_comm->rate_policy_type)); - consumed_len = -1; - goto end; - } - - { - /* Create buffer view for the rate_policy-type-specific data. - */ - struct lttng_payload_view specific_rate_policy_view = - lttng_payload_view_from_view(view, - sizeof(struct lttng_rate_policy_comm), - -1); - - specific_rate_policy_consumed_len = create_from_payload_cb( - &specific_rate_policy_view, rate_policy); - } - if (specific_rate_policy_consumed_len < 0) { - ERR("Failed to create specific rate_policy from buffer."); - consumed_len = -1; - goto end; - } - - LTTNG_ASSERT(*rate_policy); - - consumed_len = sizeof(struct lttng_rate_policy_comm) + - specific_rate_policy_consumed_len; - -end: - return consumed_len; -} - -bool lttng_rate_policy_is_equal(const struct lttng_rate_policy *a, - const struct lttng_rate_policy *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a->type != b->type) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - LTTNG_ASSERT(a->equal); - is_equal = a->equal(a, b); -end: - return is_equal; -} - -bool lttng_rate_policy_should_execute( - const struct lttng_rate_policy *policy, uint64_t counter) -{ - switch (policy->type) { - case LTTNG_RATE_POLICY_TYPE_EVERY_N: - return lttng_rate_policy_every_n_should_execute( - policy, counter); - case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: - return lttng_rate_policy_once_after_n_should_execute( - policy, counter); - default: - abort(); - break; - } -} - -/* Every N */ -static struct lttng_rate_policy_every_n *rate_policy_every_n_from_rate_policy( - struct lttng_rate_policy *policy) -{ - LTTNG_ASSERT(policy); - - return container_of(policy, struct lttng_rate_policy_every_n, parent); -} - -static const struct lttng_rate_policy_every_n * -rate_policy_every_n_from_rate_policy_const( - const struct lttng_rate_policy *policy) -{ - LTTNG_ASSERT(policy); - - return container_of(policy, struct lttng_rate_policy_every_n, parent); -} - -static int lttng_rate_policy_every_n_serialize( - struct lttng_rate_policy *policy, struct lttng_payload *payload) -{ - int ret; - - struct lttng_rate_policy_every_n *every_n_policy; - struct lttng_rate_policy_every_n_comm comm = {}; - - LTTNG_ASSERT(policy); - LTTNG_ASSERT(payload); - - every_n_policy = rate_policy_every_n_from_rate_policy(policy); - comm.interval = every_n_policy->interval; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - return ret; -} - -static bool lttng_rate_policy_every_n_is_equal( - const struct lttng_rate_policy *_a, - const struct lttng_rate_policy *_b) -{ - bool is_equal = false; - const struct lttng_rate_policy_every_n *a, *b; - - a = rate_policy_every_n_from_rate_policy_const(_a); - b = rate_policy_every_n_from_rate_policy_const(_b); - - if (a->interval != b->interval) { - goto end; - } - - is_equal = true; - -end: - return is_equal; -} - -static void lttng_rate_policy_every_n_destroy(struct lttng_rate_policy *policy) -{ - struct lttng_rate_policy_every_n *every_n_policy; - - if (!policy) { - goto end; - } - - every_n_policy = rate_policy_every_n_from_rate_policy(policy); - - free(every_n_policy); - -end: - return; -} - -static struct lttng_rate_policy *lttng_rate_policy_every_n_copy( - const struct lttng_rate_policy *source) -{ - struct lttng_rate_policy *copy = NULL; - const struct lttng_rate_policy_every_n *every_n_policy; - - if (!source) { - goto end; - } - - every_n_policy = rate_policy_every_n_from_rate_policy_const(source); - copy = lttng_rate_policy_every_n_create(every_n_policy->interval); - -end: - return copy; -} - -static enum lttng_error_code lttng_rate_policy_every_n_mi_serialize( - const struct lttng_rate_policy *rate_policy, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const struct lttng_rate_policy_every_n *every_n_policy = NULL; - - LTTNG_ASSERT(rate_policy); - LTTNG_ASSERT(IS_EVERY_N_RATE_POLICY(rate_policy)); - LTTNG_ASSERT(writer); - - every_n_policy = rate_policy_every_n_from_rate_policy_const( - rate_policy); - - /* Open rate_policy_every_n element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_rate_policy_every_n); - if (ret) { - goto mi_error; - } - - /* Interval. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_rate_policy_every_n_interval, - every_n_policy->interval); - if (ret) { - goto mi_error; - } - - /* Close rate_policy_every_n element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_rate_policy *lttng_rate_policy_every_n_create(uint64_t interval) -{ - struct lttng_rate_policy_every_n *policy = NULL; - struct lttng_rate_policy *_policy = NULL; - - if (interval == 0) { - /* - * An interval of 0 is invalid since it would never be fired. - */ - goto end; - } - - policy = zmalloc(sizeof(struct lttng_rate_policy_every_n)); - if (!policy) { - goto end; - } - - lttng_rate_policy_init(&policy->parent, LTTNG_RATE_POLICY_TYPE_EVERY_N, - lttng_rate_policy_every_n_serialize, - lttng_rate_policy_every_n_is_equal, - lttng_rate_policy_every_n_destroy, - lttng_rate_policy_every_n_copy, - lttng_rate_policy_every_n_mi_serialize); - - policy->interval = interval; - - _policy = &policy->parent; - policy = NULL; - -end: - free(policy); - return _policy; -} - -enum lttng_rate_policy_status lttng_rate_policy_every_n_get_interval( - const struct lttng_rate_policy *policy, uint64_t *interval) -{ - const struct lttng_rate_policy_every_n *every_n_policy; - enum lttng_rate_policy_status status; - - if (!policy || !IS_EVERY_N_RATE_POLICY(policy) || !interval) { - status = LTTNG_RATE_POLICY_STATUS_INVALID; - goto end; - } - - every_n_policy = rate_policy_every_n_from_rate_policy_const(policy); - *interval = every_n_policy->interval; - status = LTTNG_RATE_POLICY_STATUS_OK; -end: - - return status; -} - -static bool lttng_rate_policy_every_n_should_execute( - const struct lttng_rate_policy *policy, uint64_t counter) -{ - const struct lttng_rate_policy_every_n *every_n_policy; - LTTNG_ASSERT(policy); - bool execute = false; - - every_n_policy = rate_policy_every_n_from_rate_policy_const(policy); - - if (every_n_policy->interval == 0) { - abort(); - } - - execute = (counter % every_n_policy->interval) == 0; - - DBG("Policy every N = %" PRIu64 - ": execution %s. Execution count: %" PRIu64, - every_n_policy->interval, - execute ? "accepted" : "denied", counter); - - return execute; -} - -/* Once after N */ - -static struct lttng_rate_policy_once_after_n * -rate_policy_once_after_n_from_rate_policy(struct lttng_rate_policy *policy) -{ - LTTNG_ASSERT(policy); - - return container_of( - policy, struct lttng_rate_policy_once_after_n, parent); -} - -static const struct lttng_rate_policy_once_after_n * -rate_policy_once_after_n_from_rate_policy_const( - const struct lttng_rate_policy *policy) -{ - LTTNG_ASSERT(policy); - - return container_of( - policy, struct lttng_rate_policy_once_after_n, parent); -} -static int lttng_rate_policy_once_after_n_serialize( - struct lttng_rate_policy *policy, struct lttng_payload *payload) -{ - int ret; - - struct lttng_rate_policy_once_after_n *once_after_n_policy; - struct lttng_rate_policy_once_after_n_comm comm = {}; - - LTTNG_ASSERT(policy); - LTTNG_ASSERT(payload); - - once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy); - comm.threshold = once_after_n_policy->threshold; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - return ret; -} - -static bool lttng_rate_policy_once_after_n_is_equal( - const struct lttng_rate_policy *_a, - const struct lttng_rate_policy *_b) -{ - bool is_equal = false; - const struct lttng_rate_policy_once_after_n *a, *b; - - a = rate_policy_once_after_n_from_rate_policy_const(_a); - b = rate_policy_once_after_n_from_rate_policy_const(_b); - - if (a->threshold != b->threshold) { - goto end; - } - - is_equal = true; - -end: - return is_equal; -} - -static void lttng_rate_policy_once_after_n_destroy( - struct lttng_rate_policy *policy) -{ - struct lttng_rate_policy_once_after_n *once_after_n_policy; - - if (!policy) { - goto end; - } - - once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy); - - free(once_after_n_policy); - -end: - return; -} - -static struct lttng_rate_policy *lttng_rate_policy_once_after_n_copy( - const struct lttng_rate_policy *source) -{ - struct lttng_rate_policy *copy = NULL; - const struct lttng_rate_policy_once_after_n *once_after_n_policy; - - if (!source) { - goto end; - } - - once_after_n_policy = - rate_policy_once_after_n_from_rate_policy_const(source); - copy = lttng_rate_policy_once_after_n_create( - once_after_n_policy->threshold); - -end: - return copy; -} - -static enum lttng_error_code lttng_rate_policy_once_after_n_mi_serialize( - const struct lttng_rate_policy *rate_policy, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const struct lttng_rate_policy_once_after_n *once_after_n_policy = NULL; - - LTTNG_ASSERT(rate_policy); - LTTNG_ASSERT(IS_ONCE_AFTER_N_RATE_POLICY(rate_policy)); - LTTNG_ASSERT(writer); - - once_after_n_policy = rate_policy_once_after_n_from_rate_policy_const( - rate_policy); - - /* Open rate_policy_once_after_n. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_rate_policy_once_after_n); - if (ret) { - goto mi_error; - } - - /* Threshold. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_rate_policy_once_after_n_threshold, - once_after_n_policy->threshold); - if (ret) { - goto mi_error; - } - - /* Close rate_policy_once_after_n element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_rate_policy *lttng_rate_policy_once_after_n_create( - uint64_t threshold) -{ - struct lttng_rate_policy_once_after_n *policy = NULL; - struct lttng_rate_policy *_policy = NULL; - - if (threshold == 0) { - /* threshold is expected to be > 0 */ - goto end; - } - - policy = zmalloc(sizeof(struct lttng_rate_policy_once_after_n)); - if (!policy) { - goto end; - } - - lttng_rate_policy_init(&policy->parent, - LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N, - lttng_rate_policy_once_after_n_serialize, - lttng_rate_policy_once_after_n_is_equal, - lttng_rate_policy_once_after_n_destroy, - lttng_rate_policy_once_after_n_copy, - lttng_rate_policy_once_after_n_mi_serialize); - - policy->threshold = threshold; - - _policy = &policy->parent; - policy = NULL; - -end: - free(policy); - return _policy; -} - -enum lttng_rate_policy_status lttng_rate_policy_once_after_n_get_threshold( - const struct lttng_rate_policy *policy, uint64_t *threshold) -{ - const struct lttng_rate_policy_once_after_n *once_after_n_policy; - enum lttng_rate_policy_status status; - - if (!policy || !IS_ONCE_AFTER_N_RATE_POLICY(policy) || !threshold) { - status = LTTNG_RATE_POLICY_STATUS_INVALID; - goto end; - } - - once_after_n_policy = - rate_policy_once_after_n_from_rate_policy_const(policy); - *threshold = once_after_n_policy->threshold; - status = LTTNG_RATE_POLICY_STATUS_OK; -end: - - return status; -} - -struct lttng_rate_policy *lttng_rate_policy_copy( - const struct lttng_rate_policy *source) -{ - LTTNG_ASSERT(source->copy); - return source->copy(source); -} - -static bool lttng_rate_policy_once_after_n_should_execute( - const struct lttng_rate_policy *policy, uint64_t counter) -{ - const struct lttng_rate_policy_once_after_n *once_after_n_policy; - bool execute = false; - LTTNG_ASSERT(policy); - - once_after_n_policy = - rate_policy_once_after_n_from_rate_policy_const(policy); - - execute = counter == once_after_n_policy->threshold; - - DBG("Policy once after N = %" PRIu64 - ": execution %s. Execution count: %" PRIu64, - once_after_n_policy->threshold, - execute ? "accepted" : "denied", counter); - - return counter == once_after_n_policy->threshold; -} - -enum lttng_error_code lttng_rate_policy_mi_serialize( - const struct lttng_rate_policy *rate_policy, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(rate_policy); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(rate_policy->mi_serialize); - - /* Open rate policy element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_rate_policy); - if (ret) { - goto mi_error; - } - - /* Serialize underlying rate policy. */ - ret_code = rate_policy->mi_serialize(rate_policy, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close rate policy element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/actions/rate-policy.cpp b/src/common/actions/rate-policy.cpp new file mode 100644 index 000000000..d0834dd73 --- /dev/null +++ b/src/common/actions/rate-policy.cpp @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2021 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_EVERY_N_RATE_POLICY(policy) \ + (lttng_rate_policy_get_type(policy) == LTTNG_RATE_POLICY_TYPE_EVERY_N) + +#define IS_ONCE_AFTER_N_RATE_POLICY(policy) \ + (lttng_rate_policy_get_type(policy) == \ + LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N) + +typedef void (*rate_policy_destroy_cb)(struct lttng_rate_policy *rate_policy); +typedef int (*rate_policy_serialize_cb)(struct lttng_rate_policy *rate_policy, + struct lttng_payload *payload); +typedef bool (*rate_policy_equal_cb)(const struct lttng_rate_policy *a, + const struct lttng_rate_policy *b); +typedef ssize_t (*rate_policy_create_from_payload_cb)( + struct lttng_payload_view *view, + struct lttng_rate_policy **rate_policy); +typedef struct lttng_rate_policy *(*rate_policy_copy_cb)( + const struct lttng_rate_policy *source); +typedef enum lttng_error_code (*rate_policy_mi_serialize_cb)( + const struct lttng_rate_policy *rate_policy, + struct mi_writer *writer); + +struct lttng_rate_policy { + enum lttng_rate_policy_type type; + rate_policy_serialize_cb serialize; + rate_policy_equal_cb equal; + rate_policy_destroy_cb destroy; + rate_policy_copy_cb copy; + rate_policy_mi_serialize_cb mi_serialize; +}; + +struct lttng_rate_policy_every_n { + struct lttng_rate_policy parent; + uint64_t interval; +}; + +struct lttng_rate_policy_once_after_n { + struct lttng_rate_policy parent; + uint64_t threshold; +}; + +struct lttng_rate_policy_comm { + /* enum lttng_rate_policy_type */ + int8_t rate_policy_type; +} LTTNG_PACKED; + +struct lttng_rate_policy_once_after_n_comm { + uint64_t threshold; +} LTTNG_PACKED; + +struct lttng_rate_policy_every_n_comm { + uint64_t interval; +} LTTNG_PACKED; + +/* Forward declaration. */ +static void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy, + enum lttng_rate_policy_type type, + rate_policy_serialize_cb serialize, + rate_policy_equal_cb equal, + rate_policy_destroy_cb destroy, + rate_policy_copy_cb copy, + rate_policy_mi_serialize_cb mi); + +/* Forward declaration. Every n */ +static bool lttng_rate_policy_every_n_should_execute( + const struct lttng_rate_policy *policy, uint64_t counter); + +/* Forward declaration. Once after N */ +static bool lttng_rate_policy_once_after_n_should_execute( + const struct lttng_rate_policy *policy, uint64_t counter); + +const char *lttng_rate_policy_type_string( + enum lttng_rate_policy_type rate_policy_type) +{ + switch (rate_policy_type) { + case LTTNG_RATE_POLICY_TYPE_EVERY_N: + return "EVERY-N"; + case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: + return "ONCE-AFTER-N"; + default: + return "???"; + } +} + +enum lttng_rate_policy_type lttng_rate_policy_get_type( + const struct lttng_rate_policy *policy) +{ + return policy ? policy->type : LTTNG_RATE_POLICY_TYPE_UNKNOWN; +} + +void lttng_rate_policy_init(struct lttng_rate_policy *rate_policy, + enum lttng_rate_policy_type type, + rate_policy_serialize_cb serialize, + rate_policy_equal_cb equal, + rate_policy_destroy_cb destroy, + rate_policy_copy_cb copy, + rate_policy_mi_serialize_cb mi) +{ + rate_policy->type = type; + rate_policy->serialize = serialize; + rate_policy->equal = equal; + rate_policy->destroy = destroy; + rate_policy->copy = copy; + rate_policy->mi_serialize = mi; +} + +void lttng_rate_policy_destroy(struct lttng_rate_policy *rate_policy) +{ + if (!rate_policy) { + return; + } + + rate_policy->destroy(rate_policy); +} + +int lttng_rate_policy_serialize(struct lttng_rate_policy *rate_policy, + struct lttng_payload *payload) +{ + int ret; + struct lttng_rate_policy_comm rate_policy_comm = { + .rate_policy_type = (int8_t) rate_policy->type, + }; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &rate_policy_comm, + sizeof(rate_policy_comm)); + if (ret) { + goto end; + } + + ret = rate_policy->serialize(rate_policy, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +static ssize_t lttng_rate_policy_once_after_n_create_from_payload( + struct lttng_payload_view *view, + struct lttng_rate_policy **rate_policy) +{ + ssize_t consumed_len = -1; + struct lttng_rate_policy *policy = NULL; + const struct lttng_rate_policy_once_after_n_comm *comm; + const struct lttng_payload_view comm_view = + lttng_payload_view_from_view(view, 0, sizeof(*comm)); + + if (!view || !rate_policy) { + consumed_len = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&comm_view)) { + /* Payload not large enough to contain the header. */ + consumed_len = -1; + goto end; + } + + comm = (const struct lttng_rate_policy_once_after_n_comm *) + comm_view.buffer.data; + + policy = lttng_rate_policy_once_after_n_create(comm->threshold); + if (policy == NULL) { + consumed_len = -1; + goto end; + } + + *rate_policy = policy; + consumed_len = sizeof(*comm); + +end: + return consumed_len; +} + +static ssize_t lttng_rate_policy_every_n_create_from_payload( + struct lttng_payload_view *view, + struct lttng_rate_policy **rate_policy) +{ + ssize_t consumed_len = -1; + struct lttng_rate_policy *policy = NULL; + const struct lttng_rate_policy_every_n_comm *comm; + const struct lttng_payload_view comm_view = + lttng_payload_view_from_view(view, 0, sizeof(*comm)); + + if (!view || !rate_policy) { + consumed_len = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&comm_view)) { + /* Payload not large enough to contain the header. */ + consumed_len = -1; + goto end; + } + + comm = (const struct lttng_rate_policy_every_n_comm *) + comm_view.buffer.data; + + policy = lttng_rate_policy_every_n_create(comm->interval); + if (policy == NULL) { + consumed_len = -1; + goto end; + } + + *rate_policy = policy; + consumed_len = sizeof(*comm); + +end: + return consumed_len; +} + +ssize_t lttng_rate_policy_create_from_payload(struct lttng_payload_view *view, + struct lttng_rate_policy **rate_policy) +{ + ssize_t consumed_len, specific_rate_policy_consumed_len; + rate_policy_create_from_payload_cb create_from_payload_cb; + const struct lttng_rate_policy_comm *rate_policy_comm; + const struct lttng_payload_view rate_policy_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*rate_policy_comm)); + + if (!view || !rate_policy) { + consumed_len = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&rate_policy_comm_view)) { + /* Payload not large enough to contain the header. */ + consumed_len = -1; + goto end; + } + + rate_policy_comm = (const struct lttng_rate_policy_comm *) + rate_policy_comm_view.buffer.data; + + DBG("Create rate_policy from payload: rate-policy-type=%s", + lttng_rate_policy_type_string( + (lttng_rate_policy_type) rate_policy_comm->rate_policy_type)); + + switch (rate_policy_comm->rate_policy_type) { + case LTTNG_RATE_POLICY_TYPE_EVERY_N: + create_from_payload_cb = + lttng_rate_policy_every_n_create_from_payload; + break; + case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: + create_from_payload_cb = + lttng_rate_policy_once_after_n_create_from_payload; + break; + default: + ERR("Failed to create rate-policy from payload, unhandled rate-policy type: rate-policy-type=%u (%s)", + rate_policy_comm->rate_policy_type, + lttng_rate_policy_type_string( + (lttng_rate_policy_type) rate_policy_comm->rate_policy_type)); + consumed_len = -1; + goto end; + } + + { + /* Create buffer view for the rate_policy-type-specific data. + */ + struct lttng_payload_view specific_rate_policy_view = + lttng_payload_view_from_view(view, + sizeof(struct lttng_rate_policy_comm), + -1); + + specific_rate_policy_consumed_len = create_from_payload_cb( + &specific_rate_policy_view, rate_policy); + } + if (specific_rate_policy_consumed_len < 0) { + ERR("Failed to create specific rate_policy from buffer."); + consumed_len = -1; + goto end; + } + + LTTNG_ASSERT(*rate_policy); + + consumed_len = sizeof(struct lttng_rate_policy_comm) + + specific_rate_policy_consumed_len; + +end: + return consumed_len; +} + +bool lttng_rate_policy_is_equal(const struct lttng_rate_policy *a, + const struct lttng_rate_policy *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + LTTNG_ASSERT(a->equal); + is_equal = a->equal(a, b); +end: + return is_equal; +} + +bool lttng_rate_policy_should_execute( + const struct lttng_rate_policy *policy, uint64_t counter) +{ + switch (policy->type) { + case LTTNG_RATE_POLICY_TYPE_EVERY_N: + return lttng_rate_policy_every_n_should_execute( + policy, counter); + case LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N: + return lttng_rate_policy_once_after_n_should_execute( + policy, counter); + default: + abort(); + break; + } +} + +/* Every N */ +static struct lttng_rate_policy_every_n *rate_policy_every_n_from_rate_policy( + struct lttng_rate_policy *policy) +{ + LTTNG_ASSERT(policy); + + return container_of(policy, struct lttng_rate_policy_every_n, parent); +} + +static const struct lttng_rate_policy_every_n * +rate_policy_every_n_from_rate_policy_const( + const struct lttng_rate_policy *policy) +{ + LTTNG_ASSERT(policy); + + return container_of(policy, struct lttng_rate_policy_every_n, parent); +} + +static int lttng_rate_policy_every_n_serialize( + struct lttng_rate_policy *policy, struct lttng_payload *payload) +{ + int ret; + + struct lttng_rate_policy_every_n *every_n_policy; + struct lttng_rate_policy_every_n_comm comm = {}; + + LTTNG_ASSERT(policy); + LTTNG_ASSERT(payload); + + every_n_policy = rate_policy_every_n_from_rate_policy(policy); + comm.interval = every_n_policy->interval; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + return ret; +} + +static bool lttng_rate_policy_every_n_is_equal( + const struct lttng_rate_policy *_a, + const struct lttng_rate_policy *_b) +{ + bool is_equal = false; + const struct lttng_rate_policy_every_n *a, *b; + + a = rate_policy_every_n_from_rate_policy_const(_a); + b = rate_policy_every_n_from_rate_policy_const(_b); + + if (a->interval != b->interval) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static void lttng_rate_policy_every_n_destroy(struct lttng_rate_policy *policy) +{ + struct lttng_rate_policy_every_n *every_n_policy; + + if (!policy) { + goto end; + } + + every_n_policy = rate_policy_every_n_from_rate_policy(policy); + + free(every_n_policy); + +end: + return; +} + +static struct lttng_rate_policy *lttng_rate_policy_every_n_copy( + const struct lttng_rate_policy *source) +{ + struct lttng_rate_policy *copy = NULL; + const struct lttng_rate_policy_every_n *every_n_policy; + + if (!source) { + goto end; + } + + every_n_policy = rate_policy_every_n_from_rate_policy_const(source); + copy = lttng_rate_policy_every_n_create(every_n_policy->interval); + +end: + return copy; +} + +static enum lttng_error_code lttng_rate_policy_every_n_mi_serialize( + const struct lttng_rate_policy *rate_policy, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const struct lttng_rate_policy_every_n *every_n_policy = NULL; + + LTTNG_ASSERT(rate_policy); + LTTNG_ASSERT(IS_EVERY_N_RATE_POLICY(rate_policy)); + LTTNG_ASSERT(writer); + + every_n_policy = rate_policy_every_n_from_rate_policy_const( + rate_policy); + + /* Open rate_policy_every_n element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_rate_policy_every_n); + if (ret) { + goto mi_error; + } + + /* Interval. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_rate_policy_every_n_interval, + every_n_policy->interval); + if (ret) { + goto mi_error; + } + + /* Close rate_policy_every_n element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_rate_policy *lttng_rate_policy_every_n_create(uint64_t interval) +{ + struct lttng_rate_policy_every_n *policy = NULL; + struct lttng_rate_policy *_policy = NULL; + + if (interval == 0) { + /* + * An interval of 0 is invalid since it would never be fired. + */ + goto end; + } + + policy = (lttng_rate_policy_every_n *) zmalloc(sizeof(struct lttng_rate_policy_every_n)); + if (!policy) { + goto end; + } + + lttng_rate_policy_init(&policy->parent, LTTNG_RATE_POLICY_TYPE_EVERY_N, + lttng_rate_policy_every_n_serialize, + lttng_rate_policy_every_n_is_equal, + lttng_rate_policy_every_n_destroy, + lttng_rate_policy_every_n_copy, + lttng_rate_policy_every_n_mi_serialize); + + policy->interval = interval; + + _policy = &policy->parent; + policy = NULL; + +end: + free(policy); + return _policy; +} + +enum lttng_rate_policy_status lttng_rate_policy_every_n_get_interval( + const struct lttng_rate_policy *policy, uint64_t *interval) +{ + const struct lttng_rate_policy_every_n *every_n_policy; + enum lttng_rate_policy_status status; + + if (!policy || !IS_EVERY_N_RATE_POLICY(policy) || !interval) { + status = LTTNG_RATE_POLICY_STATUS_INVALID; + goto end; + } + + every_n_policy = rate_policy_every_n_from_rate_policy_const(policy); + *interval = every_n_policy->interval; + status = LTTNG_RATE_POLICY_STATUS_OK; +end: + + return status; +} + +static bool lttng_rate_policy_every_n_should_execute( + const struct lttng_rate_policy *policy, uint64_t counter) +{ + const struct lttng_rate_policy_every_n *every_n_policy; + LTTNG_ASSERT(policy); + bool execute = false; + + every_n_policy = rate_policy_every_n_from_rate_policy_const(policy); + + if (every_n_policy->interval == 0) { + abort(); + } + + execute = (counter % every_n_policy->interval) == 0; + + DBG("Policy every N = %" PRIu64 + ": execution %s. Execution count: %" PRIu64, + every_n_policy->interval, + execute ? "accepted" : "denied", counter); + + return execute; +} + +/* Once after N */ + +static struct lttng_rate_policy_once_after_n * +rate_policy_once_after_n_from_rate_policy(struct lttng_rate_policy *policy) +{ + LTTNG_ASSERT(policy); + + return container_of( + policy, struct lttng_rate_policy_once_after_n, parent); +} + +static const struct lttng_rate_policy_once_after_n * +rate_policy_once_after_n_from_rate_policy_const( + const struct lttng_rate_policy *policy) +{ + LTTNG_ASSERT(policy); + + return container_of( + policy, struct lttng_rate_policy_once_after_n, parent); +} +static int lttng_rate_policy_once_after_n_serialize( + struct lttng_rate_policy *policy, struct lttng_payload *payload) +{ + int ret; + + struct lttng_rate_policy_once_after_n *once_after_n_policy; + struct lttng_rate_policy_once_after_n_comm comm = {}; + + LTTNG_ASSERT(policy); + LTTNG_ASSERT(payload); + + once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy); + comm.threshold = once_after_n_policy->threshold; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + return ret; +} + +static bool lttng_rate_policy_once_after_n_is_equal( + const struct lttng_rate_policy *_a, + const struct lttng_rate_policy *_b) +{ + bool is_equal = false; + const struct lttng_rate_policy_once_after_n *a, *b; + + a = rate_policy_once_after_n_from_rate_policy_const(_a); + b = rate_policy_once_after_n_from_rate_policy_const(_b); + + if (a->threshold != b->threshold) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static void lttng_rate_policy_once_after_n_destroy( + struct lttng_rate_policy *policy) +{ + struct lttng_rate_policy_once_after_n *once_after_n_policy; + + if (!policy) { + goto end; + } + + once_after_n_policy = rate_policy_once_after_n_from_rate_policy(policy); + + free(once_after_n_policy); + +end: + return; +} + +static struct lttng_rate_policy *lttng_rate_policy_once_after_n_copy( + const struct lttng_rate_policy *source) +{ + struct lttng_rate_policy *copy = NULL; + const struct lttng_rate_policy_once_after_n *once_after_n_policy; + + if (!source) { + goto end; + } + + once_after_n_policy = + rate_policy_once_after_n_from_rate_policy_const(source); + copy = lttng_rate_policy_once_after_n_create( + once_after_n_policy->threshold); + +end: + return copy; +} + +static enum lttng_error_code lttng_rate_policy_once_after_n_mi_serialize( + const struct lttng_rate_policy *rate_policy, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const struct lttng_rate_policy_once_after_n *once_after_n_policy = NULL; + + LTTNG_ASSERT(rate_policy); + LTTNG_ASSERT(IS_ONCE_AFTER_N_RATE_POLICY(rate_policy)); + LTTNG_ASSERT(writer); + + once_after_n_policy = rate_policy_once_after_n_from_rate_policy_const( + rate_policy); + + /* Open rate_policy_once_after_n. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_rate_policy_once_after_n); + if (ret) { + goto mi_error; + } + + /* Threshold. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_rate_policy_once_after_n_threshold, + once_after_n_policy->threshold); + if (ret) { + goto mi_error; + } + + /* Close rate_policy_once_after_n element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_rate_policy *lttng_rate_policy_once_after_n_create( + uint64_t threshold) +{ + struct lttng_rate_policy_once_after_n *policy = NULL; + struct lttng_rate_policy *_policy = NULL; + + if (threshold == 0) { + /* threshold is expected to be > 0 */ + goto end; + } + + policy = (lttng_rate_policy_once_after_n *) zmalloc(sizeof(struct lttng_rate_policy_once_after_n)); + if (!policy) { + goto end; + } + + lttng_rate_policy_init(&policy->parent, + LTTNG_RATE_POLICY_TYPE_ONCE_AFTER_N, + lttng_rate_policy_once_after_n_serialize, + lttng_rate_policy_once_after_n_is_equal, + lttng_rate_policy_once_after_n_destroy, + lttng_rate_policy_once_after_n_copy, + lttng_rate_policy_once_after_n_mi_serialize); + + policy->threshold = threshold; + + _policy = &policy->parent; + policy = NULL; + +end: + free(policy); + return _policy; +} + +enum lttng_rate_policy_status lttng_rate_policy_once_after_n_get_threshold( + const struct lttng_rate_policy *policy, uint64_t *threshold) +{ + const struct lttng_rate_policy_once_after_n *once_after_n_policy; + enum lttng_rate_policy_status status; + + if (!policy || !IS_ONCE_AFTER_N_RATE_POLICY(policy) || !threshold) { + status = LTTNG_RATE_POLICY_STATUS_INVALID; + goto end; + } + + once_after_n_policy = + rate_policy_once_after_n_from_rate_policy_const(policy); + *threshold = once_after_n_policy->threshold; + status = LTTNG_RATE_POLICY_STATUS_OK; +end: + + return status; +} + +struct lttng_rate_policy *lttng_rate_policy_copy( + const struct lttng_rate_policy *source) +{ + LTTNG_ASSERT(source->copy); + return source->copy(source); +} + +static bool lttng_rate_policy_once_after_n_should_execute( + const struct lttng_rate_policy *policy, uint64_t counter) +{ + const struct lttng_rate_policy_once_after_n *once_after_n_policy; + bool execute = false; + LTTNG_ASSERT(policy); + + once_after_n_policy = + rate_policy_once_after_n_from_rate_policy_const(policy); + + execute = counter == once_after_n_policy->threshold; + + DBG("Policy once after N = %" PRIu64 + ": execution %s. Execution count: %" PRIu64, + once_after_n_policy->threshold, + execute ? "accepted" : "denied", counter); + + return counter == once_after_n_policy->threshold; +} + +enum lttng_error_code lttng_rate_policy_mi_serialize( + const struct lttng_rate_policy *rate_policy, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(rate_policy); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(rate_policy->mi_serialize); + + /* Open rate policy element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_rate_policy); + if (ret) { + goto mi_error; + } + + /* Serialize underlying rate policy. */ + ret_code = rate_policy->mi_serialize(rate_policy, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close rate policy element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/actions/rotate-session.c b/src/common/actions/rotate-session.c deleted file mode 100644 index e519d45d8..000000000 --- a/src/common/actions/rotate-session.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) 2019 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_ROTATE_SESSION_ACTION(action) \ - (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION) - -struct lttng_action_rotate_session { - struct lttng_action parent; - - /* Owned by this. */ - char *session_name; - struct lttng_rate_policy *policy; -}; - -struct lttng_action_rotate_session_comm { - /* Includes the trailing \0. */ - uint32_t session_name_len; - - /* - * Variable data: - * - * - session name (null terminated) - * - policy - */ - char data[]; -} LTTNG_PACKED; - -static const struct lttng_rate_policy * -lttng_action_rotate_session_internal_get_rate_policy( - const struct lttng_action *action); - -static struct lttng_action_rotate_session *action_rotate_session_from_action( - struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_rotate_session, parent); -} - -static const struct lttng_action_rotate_session * -action_rotate_session_from_action_const(const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_rotate_session, parent); -} - -static bool lttng_action_rotate_session_validate(struct lttng_action *action) -{ - bool valid; - struct lttng_action_rotate_session *action_rotate_session; - - if (!action) { - valid = false; - goto end; - } - - action_rotate_session = action_rotate_session_from_action(action); - - /* A non-empty session name is mandatory. */ - if (!action_rotate_session->session_name || - strlen(action_rotate_session->session_name) == 0) { - valid = false; - goto end; - } - - valid = true; -end: - return valid; -} - -static bool lttng_action_rotate_session_is_equal( - const struct lttng_action *_a, const struct lttng_action *_b) -{ - bool is_equal = false; - const struct lttng_action_rotate_session *a, *b; - - a = action_rotate_session_from_action_const(_a); - b = action_rotate_session_from_action_const(_b); - - /* Action is not valid if this is not true. */ - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); -end: - return is_equal; -} -static int lttng_action_rotate_session_serialize( - struct lttng_action *action, struct lttng_payload *payload) -{ - struct lttng_action_rotate_session *action_rotate_session; - struct lttng_action_rotate_session_comm comm; - size_t session_name_len; - int ret; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(payload); - - action_rotate_session = action_rotate_session_from_action(action); - - LTTNG_ASSERT(action_rotate_session->session_name); - - DBG("Serializing rotate session action: session-name: %s", - action_rotate_session->session_name); - - session_name_len = strlen(action_rotate_session->session_name) + 1; - comm.session_name_len = session_name_len; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, - action_rotate_session->session_name, session_name_len); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_rate_policy_serialize( - action_rotate_session->policy, payload); - if (ret) { - ret = -1; - goto end; - } -end: - return ret; -} - -static void lttng_action_rotate_session_destroy(struct lttng_action *action) -{ - struct lttng_action_rotate_session *action_rotate_session; - - if (!action) { - goto end; - } - - action_rotate_session = action_rotate_session_from_action(action); - - lttng_rate_policy_destroy(action_rotate_session->policy); - free(action_rotate_session->session_name); - free(action_rotate_session); - -end: - return; -} - -ssize_t lttng_action_rotate_session_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action **p_action) -{ - ssize_t consumed_len, ret; - const struct lttng_action_rotate_session_comm *comm; - const char *session_name; - struct lttng_action *action; - enum lttng_action_status status; - struct lttng_rate_policy *policy = NULL; - - action = lttng_action_rotate_session_create(); - if (!action) { - consumed_len = -1; - goto end; - } - - comm = (typeof(comm)) view->buffer.data; - session_name = (const char *) &comm->data; - - if (!lttng_buffer_view_contains_string( - &view->buffer, session_name, comm->session_name_len)) { - consumed_len = -1; - goto end; - } - consumed_len = sizeof(*comm) + comm->session_name_len; - - /* Rate policy. */ - { - struct lttng_payload_view policy_view = - lttng_payload_view_from_view( - view, consumed_len, -1); - ret = lttng_rate_policy_create_from_payload( - &policy_view, &policy); - if (ret < 0) { - consumed_len = -1; - goto end; - } - consumed_len += ret; - } - - status = lttng_action_rotate_session_set_session_name( - action, session_name); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - LTTNG_ASSERT(policy); - status = lttng_action_rotate_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - *p_action = action; - action = NULL; - -end: - lttng_rate_policy_destroy(policy); - lttng_action_rotate_session_destroy(action); - - return consumed_len; -} - -static enum lttng_error_code lttng_action_rotate_session_mi_serialize( - const struct lttng_action *action, struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_action_status status; - const char *session_name = NULL; - const struct lttng_rate_policy *policy = NULL; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(IS_ROTATE_SESSION_ACTION(action)); - - status = lttng_action_rotate_session_get_session_name( - action, &session_name); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(session_name != NULL); - - status = lttng_action_notify_get_rate_policy(action, &policy); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(policy != NULL); - - /* Open action rotate session element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_action_rotate_session); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Rate policy. */ - ret_code = lttng_rate_policy_mi_serialize(policy, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close action rotate session element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_action *lttng_action_rotate_session_create(void) -{ - struct lttng_action *action = NULL; - struct lttng_rate_policy *policy = NULL; - enum lttng_action_status status; - - /* Create a every N = 1 rate policy. */ - policy = lttng_rate_policy_every_n_create(1); - if (!policy) { - goto end; - } - - action = zmalloc(sizeof(struct lttng_action_rotate_session)); - if (!action) { - goto end; - } - - lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION, - lttng_action_rotate_session_validate, - lttng_action_rotate_session_serialize, - lttng_action_rotate_session_is_equal, - lttng_action_rotate_session_destroy, - lttng_action_rotate_session_internal_get_rate_policy, - lttng_action_generic_add_error_query_results, - lttng_action_rotate_session_mi_serialize); - - status = lttng_action_rotate_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - free(action); - action = NULL; - goto end; - } - -end: - lttng_rate_policy_destroy(policy); - return action; -} - -enum lttng_action_status lttng_action_rotate_session_set_session_name( - struct lttng_action *action, const char *session_name) -{ - struct lttng_action_rotate_session *action_rotate_session; - enum lttng_action_status status; - - if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name || - strlen(session_name) == 0) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_rotate_session = action_rotate_session_from_action(action); - - free(action_rotate_session->session_name); - - action_rotate_session->session_name = strdup(session_name); - if (!action_rotate_session->session_name) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_rotate_session_get_session_name( - const struct lttng_action *action, const char **session_name) -{ - const struct lttng_action_rotate_session *action_rotate_session; - enum lttng_action_status status; - - if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_rotate_session = action_rotate_session_from_action_const(action); - - *session_name = action_rotate_session->session_name; - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_rotate_session_set_rate_policy( - struct lttng_action *action, - const struct lttng_rate_policy *policy) -{ - enum lttng_action_status status; - struct lttng_action_rotate_session *rotate_session_action; - struct lttng_rate_policy *copy = NULL; - - if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - copy = lttng_rate_policy_copy(policy); - if (!copy) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - rotate_session_action = action_rotate_session_from_action(action); - - /* Free the previous rate policy .*/ - lttng_rate_policy_destroy(rotate_session_action->policy); - - /* Assign the policy. */ - rotate_session_action->policy = copy; - status = LTTNG_ACTION_STATUS_OK; - copy = NULL; - -end: - lttng_rate_policy_destroy(copy); - return status; -} - -enum lttng_action_status lttng_action_rotate_session_get_rate_policy( - const struct lttng_action *action, - const struct lttng_rate_policy **policy) -{ - enum lttng_action_status status; - const struct lttng_action_rotate_session *rotate_session_action; - - if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - rotate_session_action = action_rotate_session_from_action_const(action); - - *policy = rotate_session_action->policy; - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -static const struct lttng_rate_policy * -lttng_action_rotate_session_internal_get_rate_policy( - const struct lttng_action *action) -{ - const struct lttng_action_rotate_session *_action; - _action = action_rotate_session_from_action_const(action); - - return _action->policy; -} diff --git a/src/common/actions/rotate-session.cpp b/src/common/actions/rotate-session.cpp new file mode 100644 index 000000000..985036acc --- /dev/null +++ b/src/common/actions/rotate-session.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_ROTATE_SESSION_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_ROTATE_SESSION) + +struct lttng_action_rotate_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + struct lttng_rate_policy *policy; +}; + +struct lttng_action_rotate_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + * - policy + */ + char data[]; +} LTTNG_PACKED; + +static const struct lttng_rate_policy * +lttng_action_rotate_session_internal_get_rate_policy( + const struct lttng_action *action); + +static struct lttng_action_rotate_session *action_rotate_session_from_action( + struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_rotate_session, parent); +} + +static const struct lttng_action_rotate_session * +action_rotate_session_from_action_const(const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_rotate_session, parent); +} + +static bool lttng_action_rotate_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_rotate_session *action_rotate_session; + + if (!action) { + valid = false; + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_rotate_session->session_name || + strlen(action_rotate_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_rotate_session_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_rotate_session *a, *b; + + a = action_rotate_session_from_action_const(_a); + b = action_rotate_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); +end: + return is_equal; +} +static int lttng_action_rotate_session_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_rotate_session *action_rotate_session; + struct lttng_action_rotate_session_comm comm; + size_t session_name_len; + int ret; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(payload); + + action_rotate_session = action_rotate_session_from_action(action); + + LTTNG_ASSERT(action_rotate_session->session_name); + + DBG("Serializing rotate session action: session-name: %s", + action_rotate_session->session_name); + + session_name_len = strlen(action_rotate_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_rotate_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_rate_policy_serialize( + action_rotate_session->policy, payload); + if (ret) { + ret = -1; + goto end; + } +end: + return ret; +} + +static void lttng_action_rotate_session_destroy(struct lttng_action *action) +{ + struct lttng_action_rotate_session *action_rotate_session; + + if (!action) { + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + lttng_rate_policy_destroy(action_rotate_session->policy); + free(action_rotate_session->session_name); + free(action_rotate_session); + +end: + return; +} + +ssize_t lttng_action_rotate_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len, ret; + const struct lttng_action_rotate_session_comm *comm; + const char *session_name; + struct lttng_action *action; + enum lttng_action_status status; + struct lttng_rate_policy *policy = NULL; + + action = lttng_action_rotate_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + comm = (typeof(comm)) view->buffer.data; + session_name = (const char *) &comm->data; + + if (!lttng_buffer_view_contains_string( + &view->buffer, session_name, comm->session_name_len)) { + consumed_len = -1; + goto end; + } + consumed_len = sizeof(*comm) + comm->session_name_len; + + /* Rate policy. */ + { + struct lttng_payload_view policy_view = + lttng_payload_view_from_view( + view, consumed_len, -1); + ret = lttng_rate_policy_create_from_payload( + &policy_view, &policy); + if (ret < 0) { + consumed_len = -1; + goto end; + } + consumed_len += ret; + } + + status = lttng_action_rotate_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + LTTNG_ASSERT(policy); + status = lttng_action_rotate_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + *p_action = action; + action = NULL; + +end: + lttng_rate_policy_destroy(policy); + lttng_action_rotate_session_destroy(action); + + return consumed_len; +} + +static enum lttng_error_code lttng_action_rotate_session_mi_serialize( + const struct lttng_action *action, struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_action_status status; + const char *session_name = NULL; + const struct lttng_rate_policy *policy = NULL; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(IS_ROTATE_SESSION_ACTION(action)); + + status = lttng_action_rotate_session_get_session_name( + action, &session_name); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(session_name != NULL); + + status = lttng_action_notify_get_rate_policy(action, &policy); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(policy != NULL); + + /* Open action rotate session element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_action_rotate_session); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Rate policy. */ + ret_code = lttng_rate_policy_mi_serialize(policy, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close action rotate session element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_action *lttng_action_rotate_session_create(void) +{ + struct lttng_action *action = NULL; + struct lttng_rate_policy *policy = NULL; + enum lttng_action_status status; + + /* Create a every N = 1 rate policy. */ + policy = lttng_rate_policy_every_n_create(1); + if (!policy) { + goto end; + } + + action = (lttng_action *) zmalloc(sizeof(struct lttng_action_rotate_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_ROTATE_SESSION, + lttng_action_rotate_session_validate, + lttng_action_rotate_session_serialize, + lttng_action_rotate_session_is_equal, + lttng_action_rotate_session_destroy, + lttng_action_rotate_session_internal_get_rate_policy, + lttng_action_generic_add_error_query_results, + lttng_action_rotate_session_mi_serialize); + + status = lttng_action_rotate_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + free(action); + action = NULL; + goto end; + } + +end: + lttng_rate_policy_destroy(policy); + return action; +} + +enum lttng_action_status lttng_action_rotate_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_rotate_session *action_rotate_session; + enum lttng_action_status status; + + if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_rotate_session = action_rotate_session_from_action(action); + + free(action_rotate_session->session_name); + + action_rotate_session->session_name = strdup(session_name); + if (!action_rotate_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_rotate_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_rotate_session *action_rotate_session; + enum lttng_action_status status; + + if (!action || !IS_ROTATE_SESSION_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_rotate_session = action_rotate_session_from_action_const(action); + + *session_name = action_rotate_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_rotate_session_set_rate_policy( + struct lttng_action *action, + const struct lttng_rate_policy *policy) +{ + enum lttng_action_status status; + struct lttng_action_rotate_session *rotate_session_action; + struct lttng_rate_policy *copy = NULL; + + if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + copy = lttng_rate_policy_copy(policy); + if (!copy) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + rotate_session_action = action_rotate_session_from_action(action); + + /* Free the previous rate policy .*/ + lttng_rate_policy_destroy(rotate_session_action->policy); + + /* Assign the policy. */ + rotate_session_action->policy = copy; + status = LTTNG_ACTION_STATUS_OK; + copy = NULL; + +end: + lttng_rate_policy_destroy(copy); + return status; +} + +enum lttng_action_status lttng_action_rotate_session_get_rate_policy( + const struct lttng_action *action, + const struct lttng_rate_policy **policy) +{ + enum lttng_action_status status; + const struct lttng_action_rotate_session *rotate_session_action; + + if (!action || !policy || !IS_ROTATE_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + rotate_session_action = action_rotate_session_from_action_const(action); + + *policy = rotate_session_action->policy; + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +static const struct lttng_rate_policy * +lttng_action_rotate_session_internal_get_rate_policy( + const struct lttng_action *action) +{ + const struct lttng_action_rotate_session *_action; + _action = action_rotate_session_from_action_const(action); + + return _action->policy; +} diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c deleted file mode 100644 index 26ddd1ba6..000000000 --- a/src/common/actions/snapshot-session.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Copyright (C) 2019 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_SNAPSHOT_SESSION_ACTION(action) \ - (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION) - -struct lttng_action_snapshot_session { - struct lttng_action parent; - - /* Owned by this. */ - char *session_name; - - /* - * When non-NULL, use this custom output when taking the snapshot, - * rather than the session's registered snapshot output. - * - * Owned by this. - */ - struct lttng_snapshot_output *output; - struct lttng_rate_policy *policy; -}; - -struct lttng_action_snapshot_session_comm { - /* All string lengths include the trailing \0. */ - uint32_t session_name_len; - uint32_t snapshot_output_len; - uint32_t rate_policy_len; - - /* - * Variable data (all strings are null-terminated): - * - * - session name string - * - snapshot output object - * - policy object - */ - char data[]; -} LTTNG_PACKED; - -static const struct lttng_rate_policy * -lttng_action_snapshot_session_internal_get_rate_policy( - const struct lttng_action *action); - -static struct lttng_action_snapshot_session * -action_snapshot_session_from_action(struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of( - action, struct lttng_action_snapshot_session, parent); -} - -static const struct lttng_action_snapshot_session * -action_snapshot_session_from_action_const(const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of( - action, struct lttng_action_snapshot_session, parent); -} - -static bool lttng_action_snapshot_session_validate(struct lttng_action *action) -{ - bool valid = false; - struct lttng_action_snapshot_session *action_snapshot_session; - - if (!action) { - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action(action); - - /* A non-empty session name is mandatory. */ - if (!action_snapshot_session->session_name || - strlen(action_snapshot_session->session_name) == 0) { - goto end; - } - - if (action_snapshot_session->output && - !lttng_snapshot_output_validate(action_snapshot_session->output)) { - goto end; - } - - valid = true; -end: - return valid; -} - -static bool lttng_action_snapshot_session_is_equal( - const struct lttng_action *_a, const struct lttng_action *_b) -{ - bool is_equal = false; - const struct lttng_action_snapshot_session *a, *b; - - a = action_snapshot_session_from_action_const(_a); - b = action_snapshot_session_from_action_const(_b); - - /* Action is not valid if this is not true. */ - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - if (a->output && b->output && - !lttng_snapshot_output_is_equal(a->output, b->output)) { - goto end; - } else if (!!a->output != !!b->output) { - goto end; - } - - is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); -end: - return is_equal; -} - -static size_t serialize_strlen(const char *str) -{ - return str ? strlen(str) + 1 : 0; -} - -static int lttng_action_snapshot_session_serialize( - struct lttng_action *action, struct lttng_payload *payload) -{ - struct lttng_action_snapshot_session *action_snapshot_session; - struct lttng_action_snapshot_session_comm comm = {}; - int ret; - size_t size_before_comm; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(payload); - - size_before_comm = payload->buffer.size; - - action_snapshot_session = action_snapshot_session_from_action(action); - comm.session_name_len = - serialize_strlen(action_snapshot_session->session_name); - - /* Add header. */ - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - goto end; - } - - LTTNG_ASSERT(action_snapshot_session->session_name); - DBG("Serializing snapshot session action: session-name: %s", - action_snapshot_session->session_name); - - /* Add session name. */ - ret = lttng_dynamic_buffer_append(&payload->buffer, - action_snapshot_session->session_name, - comm.session_name_len); - if (ret) { - goto end; - } - - /* Serialize the snapshot output object, if any. */ - if (action_snapshot_session->output) { - const size_t size_before_output = payload->buffer.size; - struct lttng_action_snapshot_session_comm *comm_in_payload; - - ret = lttng_snapshot_output_serialize( - action_snapshot_session->output, - payload); - if (ret) { - goto end; - } - - comm_in_payload = (typeof(comm_in_payload))( - payload->buffer.data + size_before_comm); - /* Adjust action length in header. */ - comm_in_payload->snapshot_output_len = - payload->buffer.size - size_before_output; - } - - /* Serialize the rate policy. */ - { - const size_t size_before_output = payload->buffer.size; - struct lttng_action_snapshot_session_comm *comm_in_payload; - - ret = lttng_rate_policy_serialize( - action_snapshot_session->policy, payload); - if (ret) { - ret = -1; - goto end; - } - - comm_in_payload = (typeof(comm_in_payload))( - payload->buffer.data + size_before_comm); - /* Adjust rate policy length in header. */ - comm_in_payload->rate_policy_len = - payload->buffer.size - size_before_output; - } - -end: - return ret; -} - -static void lttng_action_snapshot_session_destroy(struct lttng_action *action) -{ - struct lttng_action_snapshot_session *action_snapshot_session; - - if (!action) { - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action(action); - - free(action_snapshot_session->session_name); - lttng_snapshot_output_destroy(action_snapshot_session->output); - lttng_rate_policy_destroy(action_snapshot_session->policy); - free(action_snapshot_session); - -end: - return; -} - -ssize_t lttng_action_snapshot_session_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action **p_action) -{ - ssize_t consumed_len; - const char *variable_data; - struct lttng_action *action; - enum lttng_action_status status; - struct lttng_snapshot_output *snapshot_output = NULL; - struct lttng_rate_policy *policy = NULL; - const struct lttng_action_snapshot_session_comm *comm; - const struct lttng_payload_view snapshot_session_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*comm)); - - action = lttng_action_snapshot_session_create(); - if (!action) { - goto error; - } - - if (!lttng_payload_view_is_valid(&snapshot_session_comm_view)) { - /* Payload not large enough to contain the header. */ - goto error; - } - - comm = (typeof(comm)) snapshot_session_comm_view.buffer.data; - variable_data = (const char *) &comm->data; - - consumed_len = sizeof(struct lttng_action_snapshot_session_comm); - - if (!lttng_buffer_view_contains_string( - &view->buffer, variable_data, comm->session_name_len)) { - goto error; - } - - status = lttng_action_snapshot_session_set_session_name( - action, variable_data); - if (status != LTTNG_ACTION_STATUS_OK) { - goto error; - } - - variable_data += comm->session_name_len; - consumed_len += comm->session_name_len; - - /* If there is a snapshot output object, deserialize it. */ - if (comm->snapshot_output_len > 0) { - ssize_t snapshot_output_consumed_len; - enum lttng_action_status action_status; - struct lttng_payload_view snapshot_output_buffer_view = - lttng_payload_view_from_view(view, consumed_len, - comm->snapshot_output_len); - - if (!lttng_payload_view_is_valid(&snapshot_output_buffer_view)) { - ERR("Failed to create buffer view for snapshot output."); - goto error; - } - - snapshot_output_consumed_len = - lttng_snapshot_output_create_from_payload( - &snapshot_output_buffer_view, - &snapshot_output); - if (snapshot_output_consumed_len != comm->snapshot_output_len) { - ERR("Failed to deserialize snapshot output object: " - "consumed-len: %zd, expected-len: %" PRIu32, - snapshot_output_consumed_len, - comm->snapshot_output_len); - goto error; - } - - action_status = lttng_action_snapshot_session_set_output( - action, snapshot_output); - if (action_status != LTTNG_ACTION_STATUS_OK) { - goto error; - } - - /* Ownership has been transferred to the action. */ - snapshot_output = NULL; - } - - variable_data += comm->snapshot_output_len; - consumed_len += comm->snapshot_output_len; - - /* Rate policy. */ - if (comm->rate_policy_len <= 0) { - ERR("Rate policy should be present."); - goto error; - } - { - ssize_t rate_policy_consumed_len; - struct lttng_payload_view policy_view = - lttng_payload_view_from_view(view, consumed_len, - comm->rate_policy_len); - - if (!lttng_payload_view_is_valid(&policy_view)) { - ERR("Failed to create buffer view for rate policy."); - goto error; - } - - rate_policy_consumed_len = - lttng_rate_policy_create_from_payload( - &policy_view, &policy); - if (rate_policy_consumed_len < 0) { - goto error; - } - - if (rate_policy_consumed_len != comm->rate_policy_len) { - ERR("Failed to deserialize rate policy object: " - "consumed-len: %zd, expected-len: %" PRIu32, - rate_policy_consumed_len, - comm->rate_policy_len); - goto error; - } - - status = lttng_action_snapshot_session_set_rate_policy( - action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - goto error; - } - } - - variable_data += comm->rate_policy_len; - consumed_len += comm->rate_policy_len; - - *p_action = action; - action = NULL; - - goto end; - -error: - consumed_len = -1; - -end: - lttng_rate_policy_destroy(policy); - lttng_action_snapshot_session_destroy(action); - lttng_snapshot_output_destroy(snapshot_output); - - return consumed_len; -} - -static enum lttng_error_code lttng_action_snapshot_session_mi_serialize( - const struct lttng_action *action, struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_action_status status; - const char *session_name = NULL; - const struct lttng_snapshot_output *output = NULL; - const struct lttng_rate_policy *policy = NULL; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(IS_SNAPSHOT_SESSION_ACTION(action)); - - status = lttng_action_snapshot_session_get_session_name( - action, &session_name); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(session_name != NULL); - - status = lttng_action_snapshot_session_get_rate_policy(action, &policy); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(policy != NULL); - - /* Open action snapshot session element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_action_snapshot_session); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Output if any. */ - status = lttng_action_snapshot_session_get_output(action, &output); - if (status == LTTNG_ACTION_STATUS_OK) { - LTTNG_ASSERT(output != NULL); - ret_code = lttng_snapshot_output_mi_serialize(output, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - } else if (status != LTTNG_ACTION_STATUS_UNSET) { - /* This should not happen at this point. */ - abort(); - } - - /* Rate policy. */ - ret_code = lttng_rate_policy_mi_serialize(policy, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close action_snapshot_session element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_action *lttng_action_snapshot_session_create(void) -{ - struct lttng_action *action = NULL; - struct lttng_rate_policy *policy = NULL; - enum lttng_action_status status; - - /* Create a every N = 1 rate policy. */ - policy = lttng_rate_policy_every_n_create(1); - if (!policy) { - goto end; - } - - action = zmalloc(sizeof(struct lttng_action_snapshot_session)); - if (!action) { - goto end; - } - - lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, - lttng_action_snapshot_session_validate, - lttng_action_snapshot_session_serialize, - lttng_action_snapshot_session_is_equal, - lttng_action_snapshot_session_destroy, - lttng_action_snapshot_session_internal_get_rate_policy, - lttng_action_generic_add_error_query_results, - lttng_action_snapshot_session_mi_serialize); - - status = lttng_action_snapshot_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - free(action); - action = NULL; - goto end; - } - -end: - lttng_rate_policy_destroy(policy); - return action; -} - -enum lttng_action_status lttng_action_snapshot_session_set_session_name( - struct lttng_action *action, const char *session_name) -{ - struct lttng_action_snapshot_session *action_snapshot_session; - enum lttng_action_status status; - - if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name || - strlen(session_name) == 0) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action(action); - - free(action_snapshot_session->session_name); - - action_snapshot_session->session_name = strdup(session_name); - if (!action_snapshot_session->session_name) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_snapshot_session_get_session_name( - const struct lttng_action *action, const char **session_name) -{ - const struct lttng_action_snapshot_session *action_snapshot_session; - enum lttng_action_status status; - - if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action_const(action); - - if (action_snapshot_session->session_name) { - *session_name = action_snapshot_session->session_name; - status = LTTNG_ACTION_STATUS_OK; - } else { - status = LTTNG_ACTION_STATUS_UNSET; - } - -end: - - return status; -} - -enum lttng_action_status lttng_action_snapshot_session_set_output( - struct lttng_action *action, - struct lttng_snapshot_output *output) -{ - struct lttng_action_snapshot_session *action_snapshot_session; - enum lttng_action_status status; - - if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !output) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action(action); - - lttng_snapshot_output_destroy(action_snapshot_session->output); - action_snapshot_session->output = output; - - status = LTTNG_ACTION_STATUS_OK; - -end: - return status; -} - -enum lttng_action_status lttng_action_snapshot_session_get_output( - const struct lttng_action *action, - const struct lttng_snapshot_output **output) -{ - const struct lttng_action_snapshot_session *action_snapshot_session; - enum lttng_action_status status; - - if (!action || !IS_SNAPSHOT_SESSION_ACTION(action)|| !output) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_snapshot_session = action_snapshot_session_from_action_const(action); - - if (action_snapshot_session->output) { - *output = action_snapshot_session->output; - status = LTTNG_ACTION_STATUS_OK; - } else { - status = LTTNG_ACTION_STATUS_UNSET; - } - -end: - return status; -} - -enum lttng_action_status lttng_action_snapshot_session_set_rate_policy( - struct lttng_action *action, - const struct lttng_rate_policy *policy) -{ - enum lttng_action_status status; - struct lttng_action_snapshot_session *snapshot_session_action; - struct lttng_rate_policy *copy = NULL; - - if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - copy = lttng_rate_policy_copy(policy); - if (!copy) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - snapshot_session_action = action_snapshot_session_from_action(action); - - /* Free the previous rate policy .*/ - lttng_rate_policy_destroy(snapshot_session_action->policy); - - /* Assign the policy. */ - snapshot_session_action->policy = copy; - status = LTTNG_ACTION_STATUS_OK; - copy = NULL; - -end: - lttng_rate_policy_destroy(copy); - return status; -} - -enum lttng_action_status lttng_action_snapshot_session_get_rate_policy( - const struct lttng_action *action, - const struct lttng_rate_policy **policy) -{ - enum lttng_action_status status; - const struct lttng_action_snapshot_session *snapshot_session_action; - - if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - snapshot_session_action = - action_snapshot_session_from_action_const(action); - - *policy = snapshot_session_action->policy; - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -static const struct lttng_rate_policy * -lttng_action_snapshot_session_internal_get_rate_policy( - const struct lttng_action *action) -{ - const struct lttng_action_snapshot_session *_action; - _action = action_snapshot_session_from_action_const(action); - - return _action->policy; -} diff --git a/src/common/actions/snapshot-session.cpp b/src/common/actions/snapshot-session.cpp new file mode 100644 index 000000000..0f3da782f --- /dev/null +++ b/src/common/actions/snapshot-session.cpp @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_SNAPSHOT_SESSION_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION) + +struct lttng_action_snapshot_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + + /* + * When non-NULL, use this custom output when taking the snapshot, + * rather than the session's registered snapshot output. + * + * Owned by this. + */ + struct lttng_snapshot_output *output; + struct lttng_rate_policy *policy; +}; + +struct lttng_action_snapshot_session_comm { + /* All string lengths include the trailing \0. */ + uint32_t session_name_len; + uint32_t snapshot_output_len; + uint32_t rate_policy_len; + + /* + * Variable data (all strings are null-terminated): + * + * - session name string + * - snapshot output object + * - policy object + */ + char data[]; +} LTTNG_PACKED; + +static const struct lttng_rate_policy * +lttng_action_snapshot_session_internal_get_rate_policy( + const struct lttng_action *action); + +static struct lttng_action_snapshot_session * +action_snapshot_session_from_action(struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static const struct lttng_action_snapshot_session * +action_snapshot_session_from_action_const(const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static bool lttng_action_snapshot_session_validate(struct lttng_action *action) +{ + bool valid = false; + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_snapshot_session->session_name || + strlen(action_snapshot_session->session_name) == 0) { + goto end; + } + + if (action_snapshot_session->output && + !lttng_snapshot_output_validate(action_snapshot_session->output)) { + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_snapshot_session_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_snapshot_session *a, *b; + + a = action_snapshot_session_from_action_const(_a); + b = action_snapshot_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + if (a->output && b->output && + !lttng_snapshot_output_is_equal(a->output, b->output)) { + goto end; + } else if (!!a->output != !!b->output) { + goto end; + } + + is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); +end: + return is_equal; +} + +static size_t serialize_strlen(const char *str) +{ + return str ? strlen(str) + 1 : 0; +} + +static int lttng_action_snapshot_session_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + struct lttng_action_snapshot_session_comm comm = {}; + int ret; + size_t size_before_comm; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(payload); + + size_before_comm = payload->buffer.size; + + action_snapshot_session = action_snapshot_session_from_action(action); + comm.session_name_len = + serialize_strlen(action_snapshot_session->session_name); + + /* Add header. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + + LTTNG_ASSERT(action_snapshot_session->session_name); + DBG("Serializing snapshot session action: session-name: %s", + action_snapshot_session->session_name); + + /* Add session name. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_snapshot_session->session_name, + comm.session_name_len); + if (ret) { + goto end; + } + + /* Serialize the snapshot output object, if any. */ + if (action_snapshot_session->output) { + const size_t size_before_output = payload->buffer.size; + struct lttng_action_snapshot_session_comm *comm_in_payload; + + ret = lttng_snapshot_output_serialize( + action_snapshot_session->output, + payload); + if (ret) { + goto end; + } + + comm_in_payload = (typeof(comm_in_payload))( + payload->buffer.data + size_before_comm); + /* Adjust action length in header. */ + comm_in_payload->snapshot_output_len = + payload->buffer.size - size_before_output; + } + + /* Serialize the rate policy. */ + { + const size_t size_before_output = payload->buffer.size; + struct lttng_action_snapshot_session_comm *comm_in_payload; + + ret = lttng_rate_policy_serialize( + action_snapshot_session->policy, payload); + if (ret) { + ret = -1; + goto end; + } + + comm_in_payload = (typeof(comm_in_payload))( + payload->buffer.data + size_before_comm); + /* Adjust rate policy length in header. */ + comm_in_payload->rate_policy_len = + payload->buffer.size - size_before_output; + } + +end: + return ret; +} + +static void lttng_action_snapshot_session_destroy(struct lttng_action *action) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + lttng_snapshot_output_destroy(action_snapshot_session->output); + lttng_rate_policy_destroy(action_snapshot_session->policy); + free(action_snapshot_session); + +end: + return; +} + +ssize_t lttng_action_snapshot_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + const char *variable_data; + struct lttng_action *action; + enum lttng_action_status status; + struct lttng_snapshot_output *snapshot_output = NULL; + struct lttng_rate_policy *policy = NULL; + const struct lttng_action_snapshot_session_comm *comm; + const struct lttng_payload_view snapshot_session_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*comm)); + + action = lttng_action_snapshot_session_create(); + if (!action) { + goto error; + } + + if (!lttng_payload_view_is_valid(&snapshot_session_comm_view)) { + /* Payload not large enough to contain the header. */ + goto error; + } + + comm = (typeof(comm)) snapshot_session_comm_view.buffer.data; + variable_data = (const char *) &comm->data; + + consumed_len = sizeof(struct lttng_action_snapshot_session_comm); + + if (!lttng_buffer_view_contains_string( + &view->buffer, variable_data, comm->session_name_len)) { + goto error; + } + + status = lttng_action_snapshot_session_set_session_name( + action, variable_data); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + variable_data += comm->session_name_len; + consumed_len += comm->session_name_len; + + /* If there is a snapshot output object, deserialize it. */ + if (comm->snapshot_output_len > 0) { + ssize_t snapshot_output_consumed_len; + enum lttng_action_status action_status; + struct lttng_payload_view snapshot_output_buffer_view = + lttng_payload_view_from_view(view, consumed_len, + comm->snapshot_output_len); + + if (!lttng_payload_view_is_valid(&snapshot_output_buffer_view)) { + ERR("Failed to create buffer view for snapshot output."); + goto error; + } + + snapshot_output_consumed_len = + lttng_snapshot_output_create_from_payload( + &snapshot_output_buffer_view, + &snapshot_output); + if (snapshot_output_consumed_len != comm->snapshot_output_len) { + ERR("Failed to deserialize snapshot output object: " + "consumed-len: %zd, expected-len: %" PRIu32, + snapshot_output_consumed_len, + comm->snapshot_output_len); + goto error; + } + + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* Ownership has been transferred to the action. */ + snapshot_output = NULL; + } + + variable_data += comm->snapshot_output_len; + consumed_len += comm->snapshot_output_len; + + /* Rate policy. */ + if (comm->rate_policy_len <= 0) { + ERR("Rate policy should be present."); + goto error; + } + { + ssize_t rate_policy_consumed_len; + struct lttng_payload_view policy_view = + lttng_payload_view_from_view(view, consumed_len, + comm->rate_policy_len); + + if (!lttng_payload_view_is_valid(&policy_view)) { + ERR("Failed to create buffer view for rate policy."); + goto error; + } + + rate_policy_consumed_len = + lttng_rate_policy_create_from_payload( + &policy_view, &policy); + if (rate_policy_consumed_len < 0) { + goto error; + } + + if (rate_policy_consumed_len != comm->rate_policy_len) { + ERR("Failed to deserialize rate policy object: " + "consumed-len: %zd, expected-len: %" PRIu32, + rate_policy_consumed_len, + comm->rate_policy_len); + goto error; + } + + status = lttng_action_snapshot_session_set_rate_policy( + action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + } + + variable_data += comm->rate_policy_len; + consumed_len += comm->rate_policy_len; + + *p_action = action; + action = NULL; + + goto end; + +error: + consumed_len = -1; + +end: + lttng_rate_policy_destroy(policy); + lttng_action_snapshot_session_destroy(action); + lttng_snapshot_output_destroy(snapshot_output); + + return consumed_len; +} + +static enum lttng_error_code lttng_action_snapshot_session_mi_serialize( + const struct lttng_action *action, struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_action_status status; + const char *session_name = NULL; + const struct lttng_snapshot_output *output = NULL; + const struct lttng_rate_policy *policy = NULL; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(IS_SNAPSHOT_SESSION_ACTION(action)); + + status = lttng_action_snapshot_session_get_session_name( + action, &session_name); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(session_name != NULL); + + status = lttng_action_snapshot_session_get_rate_policy(action, &policy); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(policy != NULL); + + /* Open action snapshot session element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_action_snapshot_session); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Output if any. */ + status = lttng_action_snapshot_session_get_output(action, &output); + if (status == LTTNG_ACTION_STATUS_OK) { + LTTNG_ASSERT(output != NULL); + ret_code = lttng_snapshot_output_mi_serialize(output, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + } else if (status != LTTNG_ACTION_STATUS_UNSET) { + /* This should not happen at this point. */ + abort(); + } + + /* Rate policy. */ + ret_code = lttng_rate_policy_mi_serialize(policy, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close action_snapshot_session element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_action *lttng_action_snapshot_session_create(void) +{ + struct lttng_action *action = NULL; + struct lttng_rate_policy *policy = NULL; + enum lttng_action_status status; + + /* Create a every N = 1 rate policy. */ + policy = lttng_rate_policy_every_n_create(1); + if (!policy) { + goto end; + } + + action = (lttng_action *) zmalloc(sizeof(struct lttng_action_snapshot_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, + lttng_action_snapshot_session_validate, + lttng_action_snapshot_session_serialize, + lttng_action_snapshot_session_is_equal, + lttng_action_snapshot_session_destroy, + lttng_action_snapshot_session_internal_get_rate_policy, + lttng_action_generic_add_error_query_results, + lttng_action_snapshot_session_mi_serialize); + + status = lttng_action_snapshot_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + free(action); + action = NULL; + goto end; + } + +end: + lttng_rate_policy_destroy(policy); + return action; +} + +enum lttng_action_status lttng_action_snapshot_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + + action_snapshot_session->session_name = strdup(session_name); + if (!action_snapshot_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->session_name) { + *session_name = action_snapshot_session->session_name; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_set_output( + struct lttng_action *action, + struct lttng_snapshot_output *output) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + lttng_snapshot_output_destroy(action_snapshot_session->output); + action_snapshot_session->output = output; + + status = LTTNG_ACTION_STATUS_OK; + +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_output( + const struct lttng_action *action, + const struct lttng_snapshot_output **output) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action)|| !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->output) { + *output = action_snapshot_session->output; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_set_rate_policy( + struct lttng_action *action, + const struct lttng_rate_policy *policy) +{ + enum lttng_action_status status; + struct lttng_action_snapshot_session *snapshot_session_action; + struct lttng_rate_policy *copy = NULL; + + if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + copy = lttng_rate_policy_copy(policy); + if (!copy) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + snapshot_session_action = action_snapshot_session_from_action(action); + + /* Free the previous rate policy .*/ + lttng_rate_policy_destroy(snapshot_session_action->policy); + + /* Assign the policy. */ + snapshot_session_action->policy = copy; + status = LTTNG_ACTION_STATUS_OK; + copy = NULL; + +end: + lttng_rate_policy_destroy(copy); + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_rate_policy( + const struct lttng_action *action, + const struct lttng_rate_policy **policy) +{ + enum lttng_action_status status; + const struct lttng_action_snapshot_session *snapshot_session_action; + + if (!action || !policy || !IS_SNAPSHOT_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + snapshot_session_action = + action_snapshot_session_from_action_const(action); + + *policy = snapshot_session_action->policy; + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +static const struct lttng_rate_policy * +lttng_action_snapshot_session_internal_get_rate_policy( + const struct lttng_action *action) +{ + const struct lttng_action_snapshot_session *_action; + _action = action_snapshot_session_from_action_const(action); + + return _action->policy; +} diff --git a/src/common/actions/start-session.c b/src/common/actions/start-session.c deleted file mode 100644 index da7aad81f..000000000 --- a/src/common/actions/start-session.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (C) 2019 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_START_SESSION_ACTION(action) \ - (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION) - -struct lttng_action_start_session { - struct lttng_action parent; - - /* Owned by this. */ - char *session_name; - struct lttng_rate_policy *policy; -}; - -struct lttng_action_start_session_comm { - /* Includes the trailing \0. */ - uint32_t session_name_len; - - /* - * Variable data: - * - * - session name (null terminated) - * - policy - */ - char data[]; -} LTTNG_PACKED; - -static const struct lttng_rate_policy * -lttng_action_start_session_internal_get_rate_policy( - const struct lttng_action *action); - -static struct lttng_action_start_session *action_start_session_from_action( - struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_start_session, parent); -} - -static const struct lttng_action_start_session * -action_start_session_from_action_const(const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_start_session, parent); -} - -static bool lttng_action_start_session_validate(struct lttng_action *action) -{ - bool valid; - struct lttng_action_start_session *action_start_session; - - if (!action) { - valid = false; - goto end; - } - - action_start_session = action_start_session_from_action(action); - - /* A non-empty session name is mandatory. */ - if (!action_start_session->session_name || - strlen(action_start_session->session_name) == 0) { - valid = false; - goto end; - } - - valid = true; -end: - return valid; -} - -static bool lttng_action_start_session_is_equal( - const struct lttng_action *_a, const struct lttng_action *_b) -{ - bool is_equal = false; - struct lttng_action_start_session *a, *b; - - a = container_of(_a, struct lttng_action_start_session, parent); - b = container_of(_b, struct lttng_action_start_session, parent); - - /* Action is not valid if this is not true. */ - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); -end: - return is_equal; -} - -static int lttng_action_start_session_serialize( - struct lttng_action *action, struct lttng_payload *payload) -{ - struct lttng_action_start_session *action_start_session; - struct lttng_action_start_session_comm comm; - size_t session_name_len; - int ret; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(payload); - - action_start_session = action_start_session_from_action(action); - - LTTNG_ASSERT(action_start_session->session_name); - - DBG("Serializing start session action: session-name: %s", - action_start_session->session_name); - - session_name_len = strlen(action_start_session->session_name) + 1; - comm.session_name_len = session_name_len; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, sizeof(comm)); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, - action_start_session->session_name, session_name_len); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_rate_policy_serialize( - action_start_session->policy, payload); - if (ret) { - ret = -1; - goto end; - } - - ret = 0; -end: - return ret; -} - -static void lttng_action_start_session_destroy(struct lttng_action *action) -{ - struct lttng_action_start_session *action_start_session; - - if (!action) { - goto end; - } - - action_start_session = action_start_session_from_action(action); - - lttng_rate_policy_destroy(action_start_session->policy); - free(action_start_session->session_name); - free(action_start_session); - -end: - return; -} - -ssize_t lttng_action_start_session_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action **p_action) -{ - ssize_t consumed_len, ret; - const struct lttng_action_start_session_comm *comm; - const char *session_name; - struct lttng_action *action = NULL; - enum lttng_action_status status; - struct lttng_rate_policy *policy = NULL; - - comm = (typeof(comm)) view->buffer.data; - session_name = (const char *) &comm->data; - - /* Session name */ - if (!lttng_buffer_view_contains_string(&view->buffer, session_name, - comm->session_name_len)) { - consumed_len = -1; - goto end; - } - consumed_len = sizeof(*comm) + comm->session_name_len; - - /* Rate policy. */ - { - struct lttng_payload_view policy_view = - lttng_payload_view_from_view( - view, consumed_len, -1); - ret = lttng_rate_policy_create_from_payload( - &policy_view, &policy); - if (ret < 0) { - consumed_len = -1; - goto end; - } - consumed_len += ret; - } - - action = lttng_action_start_session_create(); - if (!action) { - consumed_len = -1; - goto end; - } - - status = lttng_action_start_session_set_session_name( - action, session_name); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - LTTNG_ASSERT(policy); - status = lttng_action_start_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - *p_action = action; - action = NULL; - -end: - lttng_rate_policy_destroy(policy); - lttng_action_start_session_destroy(action); - - return consumed_len; -} - -static enum lttng_error_code lttng_action_start_session_mi_serialize( - const struct lttng_action *action, struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_action_status status; - const char *session_name = NULL; - const struct lttng_rate_policy *policy = NULL; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(IS_START_SESSION_ACTION(action)); - - status = lttng_action_start_session_get_session_name( - action, &session_name); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(session_name != NULL); - - status = lttng_action_start_session_get_rate_policy(action, &policy); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(policy != NULL); - - /* Open action start session element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_action_start_session); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Rate policy. */ - ret_code = lttng_rate_policy_mi_serialize(policy, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close action start session element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_action *lttng_action_start_session_create(void) -{ - struct lttng_action *action = NULL; - struct lttng_rate_policy *policy = NULL; - enum lttng_action_status status; - - /* Create a every N = 1 rate policy. */ - policy = lttng_rate_policy_every_n_create(1); - if (!policy) { - goto end; - } - - action = zmalloc(sizeof(struct lttng_action_start_session)); - if (!action) { - goto end; - } - - lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION, - lttng_action_start_session_validate, - lttng_action_start_session_serialize, - lttng_action_start_session_is_equal, - lttng_action_start_session_destroy, - lttng_action_start_session_internal_get_rate_policy, - lttng_action_generic_add_error_query_results, - lttng_action_start_session_mi_serialize); - - status = lttng_action_start_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - free(action); - action = NULL; - goto end; - } - -end: - lttng_rate_policy_destroy(policy); - return action; -} - -enum lttng_action_status lttng_action_start_session_set_session_name( - struct lttng_action *action, const char *session_name) -{ - struct lttng_action_start_session *action_start_session; - enum lttng_action_status status; - - if (!action || !IS_START_SESSION_ACTION(action) || !session_name || - strlen(session_name) == 0) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_start_session = action_start_session_from_action(action); - - free(action_start_session->session_name); - - action_start_session->session_name = strdup(session_name); - if (!action_start_session->session_name) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_start_session_get_session_name( - const struct lttng_action *action, const char **session_name) -{ - const struct lttng_action_start_session *action_start_session; - enum lttng_action_status status; - - if (!action || !IS_START_SESSION_ACTION(action) || !session_name) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_start_session = action_start_session_from_action_const(action); - - *session_name = action_start_session->session_name; - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_start_session_set_rate_policy( - struct lttng_action *action, - const struct lttng_rate_policy *policy) -{ - enum lttng_action_status status; - struct lttng_action_start_session *start_session_action; - struct lttng_rate_policy *copy = NULL; - - if (!action || !policy || !IS_START_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - copy = lttng_rate_policy_copy(policy); - if (!copy) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - start_session_action = action_start_session_from_action(action); - - /* Release the previous rate policy .*/ - lttng_rate_policy_destroy(start_session_action->policy); - - /* Assign the policy. */ - start_session_action->policy = copy; - status = LTTNG_ACTION_STATUS_OK; - copy = NULL; - -end: - lttng_rate_policy_destroy(copy); - return status; -} - -enum lttng_action_status lttng_action_start_session_get_rate_policy( - const struct lttng_action *action, - const struct lttng_rate_policy **policy) -{ - enum lttng_action_status status; - const struct lttng_action_start_session *start_session_action; - - if (!action || !policy || !IS_START_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - start_session_action = action_start_session_from_action_const(action); - - *policy = start_session_action->policy; - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -static const struct lttng_rate_policy * -lttng_action_start_session_internal_get_rate_policy( - const struct lttng_action *action) -{ - const struct lttng_action_start_session *_action; - _action = action_start_session_from_action_const(action); - - return _action->policy; -} diff --git a/src/common/actions/start-session.cpp b/src/common/actions/start-session.cpp new file mode 100644 index 000000000..1312e5ec5 --- /dev/null +++ b/src/common/actions/start-session.cpp @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_START_SESSION_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_START_SESSION) + +struct lttng_action_start_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + struct lttng_rate_policy *policy; +}; + +struct lttng_action_start_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + * - policy + */ + char data[]; +} LTTNG_PACKED; + +static const struct lttng_rate_policy * +lttng_action_start_session_internal_get_rate_policy( + const struct lttng_action *action); + +static struct lttng_action_start_session *action_start_session_from_action( + struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_start_session, parent); +} + +static const struct lttng_action_start_session * +action_start_session_from_action_const(const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_start_session, parent); +} + +static bool lttng_action_start_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_start_session *action_start_session; + + if (!action) { + valid = false; + goto end; + } + + action_start_session = action_start_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_start_session->session_name || + strlen(action_start_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_start_session_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + struct lttng_action_start_session *a, *b; + + a = container_of(_a, struct lttng_action_start_session, parent); + b = container_of(_b, struct lttng_action_start_session, parent); + + /* Action is not valid if this is not true. */ + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); +end: + return is_equal; +} + +static int lttng_action_start_session_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_start_session *action_start_session; + struct lttng_action_start_session_comm comm; + size_t session_name_len; + int ret; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(payload); + + action_start_session = action_start_session_from_action(action); + + LTTNG_ASSERT(action_start_session->session_name); + + DBG("Serializing start session action: session-name: %s", + action_start_session->session_name); + + session_name_len = strlen(action_start_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_start_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_rate_policy_serialize( + action_start_session->policy, payload); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_start_session_destroy(struct lttng_action *action) +{ + struct lttng_action_start_session *action_start_session; + + if (!action) { + goto end; + } + + action_start_session = action_start_session_from_action(action); + + lttng_rate_policy_destroy(action_start_session->policy); + free(action_start_session->session_name); + free(action_start_session); + +end: + return; +} + +ssize_t lttng_action_start_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len, ret; + const struct lttng_action_start_session_comm *comm; + const char *session_name; + struct lttng_action *action = NULL; + enum lttng_action_status status; + struct lttng_rate_policy *policy = NULL; + + comm = (typeof(comm)) view->buffer.data; + session_name = (const char *) &comm->data; + + /* Session name */ + if (!lttng_buffer_view_contains_string(&view->buffer, session_name, + comm->session_name_len)) { + consumed_len = -1; + goto end; + } + consumed_len = sizeof(*comm) + comm->session_name_len; + + /* Rate policy. */ + { + struct lttng_payload_view policy_view = + lttng_payload_view_from_view( + view, consumed_len, -1); + ret = lttng_rate_policy_create_from_payload( + &policy_view, &policy); + if (ret < 0) { + consumed_len = -1; + goto end; + } + consumed_len += ret; + } + + action = lttng_action_start_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + status = lttng_action_start_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + LTTNG_ASSERT(policy); + status = lttng_action_start_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + *p_action = action; + action = NULL; + +end: + lttng_rate_policy_destroy(policy); + lttng_action_start_session_destroy(action); + + return consumed_len; +} + +static enum lttng_error_code lttng_action_start_session_mi_serialize( + const struct lttng_action *action, struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_action_status status; + const char *session_name = NULL; + const struct lttng_rate_policy *policy = NULL; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(IS_START_SESSION_ACTION(action)); + + status = lttng_action_start_session_get_session_name( + action, &session_name); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(session_name != NULL); + + status = lttng_action_start_session_get_rate_policy(action, &policy); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(policy != NULL); + + /* Open action start session element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_action_start_session); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Rate policy. */ + ret_code = lttng_rate_policy_mi_serialize(policy, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close action start session element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_action *lttng_action_start_session_create(void) +{ + struct lttng_action *action = NULL; + struct lttng_rate_policy *policy = NULL; + enum lttng_action_status status; + + /* Create a every N = 1 rate policy. */ + policy = lttng_rate_policy_every_n_create(1); + if (!policy) { + goto end; + } + + action = (lttng_action *) zmalloc(sizeof(struct lttng_action_start_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_START_SESSION, + lttng_action_start_session_validate, + lttng_action_start_session_serialize, + lttng_action_start_session_is_equal, + lttng_action_start_session_destroy, + lttng_action_start_session_internal_get_rate_policy, + lttng_action_generic_add_error_query_results, + lttng_action_start_session_mi_serialize); + + status = lttng_action_start_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + free(action); + action = NULL; + goto end; + } + +end: + lttng_rate_policy_destroy(policy); + return action; +} + +enum lttng_action_status lttng_action_start_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_start_session *action_start_session; + enum lttng_action_status status; + + if (!action || !IS_START_SESSION_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_start_session = action_start_session_from_action(action); + + free(action_start_session->session_name); + + action_start_session->session_name = strdup(session_name); + if (!action_start_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_start_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_start_session *action_start_session; + enum lttng_action_status status; + + if (!action || !IS_START_SESSION_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_start_session = action_start_session_from_action_const(action); + + *session_name = action_start_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_start_session_set_rate_policy( + struct lttng_action *action, + const struct lttng_rate_policy *policy) +{ + enum lttng_action_status status; + struct lttng_action_start_session *start_session_action; + struct lttng_rate_policy *copy = NULL; + + if (!action || !policy || !IS_START_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + copy = lttng_rate_policy_copy(policy); + if (!copy) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + start_session_action = action_start_session_from_action(action); + + /* Release the previous rate policy .*/ + lttng_rate_policy_destroy(start_session_action->policy); + + /* Assign the policy. */ + start_session_action->policy = copy; + status = LTTNG_ACTION_STATUS_OK; + copy = NULL; + +end: + lttng_rate_policy_destroy(copy); + return status; +} + +enum lttng_action_status lttng_action_start_session_get_rate_policy( + const struct lttng_action *action, + const struct lttng_rate_policy **policy) +{ + enum lttng_action_status status; + const struct lttng_action_start_session *start_session_action; + + if (!action || !policy || !IS_START_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + start_session_action = action_start_session_from_action_const(action); + + *policy = start_session_action->policy; + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +static const struct lttng_rate_policy * +lttng_action_start_session_internal_get_rate_policy( + const struct lttng_action *action) +{ + const struct lttng_action_start_session *_action; + _action = action_start_session_from_action_const(action); + + return _action->policy; +} diff --git a/src/common/actions/stop-session.c b/src/common/actions/stop-session.c deleted file mode 100644 index fa2c77b13..000000000 --- a/src/common/actions/stop-session.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (C) 2019 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_STOP_SESSION_ACTION(action) \ - (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION) - -struct lttng_action_stop_session { - struct lttng_action parent; - - /* Owned by this. */ - char *session_name; - struct lttng_rate_policy *policy; -}; - -struct lttng_action_stop_session_comm { - /* Includes the trailing \0. */ - uint32_t session_name_len; - - /* - * Variable data: - * - * - session name (null terminated) - * - policy - */ - char data[]; -} LTTNG_PACKED; - -static const struct lttng_rate_policy * -lttng_action_stop_session_internal_get_rate_policy( - const struct lttng_action *action); - -static struct lttng_action_stop_session *action_stop_session_from_action( - struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_stop_session, parent); -} - -static const struct lttng_action_stop_session * -action_stop_session_from_action_const(const struct lttng_action *action) -{ - LTTNG_ASSERT(action); - - return container_of(action, struct lttng_action_stop_session, parent); -} - -static bool lttng_action_stop_session_validate(struct lttng_action *action) -{ - bool valid; - struct lttng_action_stop_session *action_stop_session; - - if (!action) { - valid = false; - goto end; - } - - action_stop_session = action_stop_session_from_action(action); - - /* A non-empty session name is mandatory. */ - if (!action_stop_session->session_name || - strlen(action_stop_session->session_name) == 0) { - valid = false; - goto end; - } - - valid = true; -end: - return valid; -} - -static bool lttng_action_stop_session_is_equal( - const struct lttng_action *_a, const struct lttng_action *_b) -{ - bool is_equal = false; - const struct lttng_action_stop_session *a, *b; - - a = action_stop_session_from_action_const(_a); - b = action_stop_session_from_action_const(_b); - - /* Action is not valid if this is not true. */ - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); -end: - return is_equal; -} - -static int lttng_action_stop_session_serialize( - struct lttng_action *action, struct lttng_payload *payload) -{ - struct lttng_action_stop_session *action_stop_session; - struct lttng_action_stop_session_comm comm; - size_t session_name_len; - int ret; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(payload); - - action_stop_session = action_stop_session_from_action(action); - - LTTNG_ASSERT(action_stop_session->session_name); - - DBG("Serializing stop session action: session-name: %s", - action_stop_session->session_name); - - session_name_len = strlen(action_stop_session->session_name) + 1; - comm.session_name_len = session_name_len; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, - action_stop_session->session_name, session_name_len); - if (ret) { - ret = -1; - goto end; - } - - ret = lttng_rate_policy_serialize(action_stop_session->policy, payload); - if (ret) { - ret = -1; - goto end; - } - - ret = 0; -end: - return ret; -} - -static void lttng_action_stop_session_destroy(struct lttng_action *action) -{ - struct lttng_action_stop_session *action_stop_session; - - if (!action) { - goto end; - } - - action_stop_session = action_stop_session_from_action(action); - - lttng_rate_policy_destroy(action_stop_session->policy); - free(action_stop_session->session_name); - free(action_stop_session); - -end: - return; -} - -ssize_t lttng_action_stop_session_create_from_payload( - struct lttng_payload_view *view, - struct lttng_action **p_action) -{ - ssize_t consumed_len, ret; - const struct lttng_action_stop_session_comm *comm; - const char *session_name; - struct lttng_action *action = NULL; - enum lttng_action_status status; - struct lttng_rate_policy *policy = NULL; - - comm = (typeof(comm)) view->buffer.data; - session_name = (const char *) &comm->data; - - /* Session name. */ - if (!lttng_buffer_view_contains_string( - &view->buffer, session_name, comm->session_name_len)) { - consumed_len = -1; - goto end; - } - consumed_len = sizeof(*comm) + comm->session_name_len; - - /* Rate policy. */ - { - struct lttng_payload_view policy_view = - lttng_payload_view_from_view( - view, consumed_len, -1); - ret = lttng_rate_policy_create_from_payload( - &policy_view, &policy); - if (ret < 0) { - consumed_len = -1; - goto end; - } - consumed_len += ret; - } - - action = lttng_action_stop_session_create(); - if (!action) { - consumed_len = -1; - goto end; - } - - status = lttng_action_stop_session_set_session_name( - action, session_name); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - LTTNG_ASSERT(policy); - status = lttng_action_stop_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - consumed_len = -1; - goto end; - } - - *p_action = action; - action = NULL; - -end: - lttng_rate_policy_destroy(policy); - lttng_action_stop_session_destroy(action); - - return consumed_len; -} - -static enum lttng_error_code lttng_action_stop_session_mi_serialize( - const struct lttng_action *action, struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_action_status status; - const char *session_name = NULL; - const struct lttng_rate_policy *policy = NULL; - - LTTNG_ASSERT(action); - LTTNG_ASSERT(IS_STOP_SESSION_ACTION(action)); - - status = lttng_action_stop_session_get_session_name( - action, &session_name); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(session_name != NULL); - - status = lttng_action_stop_session_get_rate_policy(action, &policy); - LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); - LTTNG_ASSERT(policy != NULL); - - /* Open action stop session. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_action_start_session); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Rate policy. */ - ret_code = lttng_rate_policy_mi_serialize(policy, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close action stop session element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_action *lttng_action_stop_session_create(void) -{ - struct lttng_action *action = NULL; - struct lttng_rate_policy *policy = NULL; - enum lttng_action_status status; - - /* Create a every N = 1 rate policy. */ - policy = lttng_rate_policy_every_n_create(1); - if (!policy) { - goto end; - } - - action = zmalloc(sizeof(struct lttng_action_stop_session)); - if (!action) { - goto end; - } - - lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION, - lttng_action_stop_session_validate, - lttng_action_stop_session_serialize, - lttng_action_stop_session_is_equal, - lttng_action_stop_session_destroy, - lttng_action_stop_session_internal_get_rate_policy, - lttng_action_generic_add_error_query_results, - lttng_action_stop_session_mi_serialize); - - status = lttng_action_stop_session_set_rate_policy(action, policy); - if (status != LTTNG_ACTION_STATUS_OK) { - free(action); - action = NULL; - goto end; - } - -end: - lttng_rate_policy_destroy(policy); - return action; -} - -enum lttng_action_status lttng_action_stop_session_set_session_name( - struct lttng_action *action, const char *session_name) -{ - struct lttng_action_stop_session *action_stop_session; - enum lttng_action_status status; - - if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name || - strlen(session_name) == 0) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_stop_session = action_stop_session_from_action(action); - - free(action_stop_session->session_name); - - action_stop_session->session_name = strdup(session_name); - if (!action_stop_session->session_name) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_stop_session_get_session_name( - const struct lttng_action *action, const char **session_name) -{ - const struct lttng_action_stop_session *action_stop_session; - enum lttng_action_status status; - - if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - action_stop_session = action_stop_session_from_action_const(action); - - *session_name = action_stop_session->session_name; - - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -enum lttng_action_status lttng_action_stop_session_set_rate_policy( - struct lttng_action *action, - const struct lttng_rate_policy *policy) -{ - enum lttng_action_status status; - struct lttng_action_stop_session *stop_session_action; - struct lttng_rate_policy *copy = NULL; - - if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - copy = lttng_rate_policy_copy(policy); - if (!copy) { - status = LTTNG_ACTION_STATUS_ERROR; - goto end; - } - stop_session_action = action_stop_session_from_action(action); - - /* Free the previous rate policy .*/ - lttng_rate_policy_destroy(stop_session_action->policy); - - stop_session_action->policy = copy; - status = LTTNG_ACTION_STATUS_OK; - copy = NULL; - -end: - lttng_rate_policy_destroy(copy); - return status; -} - -enum lttng_action_status lttng_action_stop_session_get_rate_policy( - const struct lttng_action *action, - const struct lttng_rate_policy **policy) -{ - enum lttng_action_status status; - const struct lttng_action_stop_session *stop_session_action; - - if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) { - status = LTTNG_ACTION_STATUS_INVALID; - goto end; - } - - stop_session_action = action_stop_session_from_action_const(action); - - *policy = stop_session_action->policy; - status = LTTNG_ACTION_STATUS_OK; -end: - return status; -} - -static const struct lttng_rate_policy * -lttng_action_stop_session_internal_get_rate_policy( - const struct lttng_action *action) -{ - const struct lttng_action_stop_session *_action; - _action = action_stop_session_from_action_const(action); - - return _action->policy; -} diff --git a/src/common/actions/stop-session.cpp b/src/common/actions/stop-session.cpp new file mode 100644 index 000000000..55f1a55ed --- /dev/null +++ b/src/common/actions/stop-session.cpp @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_STOP_SESSION_ACTION(action) \ + (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_STOP_SESSION) + +struct lttng_action_stop_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + struct lttng_rate_policy *policy; +}; + +struct lttng_action_stop_session_comm { + /* Includes the trailing \0. */ + uint32_t session_name_len; + + /* + * Variable data: + * + * - session name (null terminated) + * - policy + */ + char data[]; +} LTTNG_PACKED; + +static const struct lttng_rate_policy * +lttng_action_stop_session_internal_get_rate_policy( + const struct lttng_action *action); + +static struct lttng_action_stop_session *action_stop_session_from_action( + struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_stop_session, parent); +} + +static const struct lttng_action_stop_session * +action_stop_session_from_action_const(const struct lttng_action *action) +{ + LTTNG_ASSERT(action); + + return container_of(action, struct lttng_action_stop_session, parent); +} + +static bool lttng_action_stop_session_validate(struct lttng_action *action) +{ + bool valid; + struct lttng_action_stop_session *action_stop_session; + + if (!action) { + valid = false; + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_stop_session->session_name || + strlen(action_stop_session->session_name) == 0) { + valid = false; + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_stop_session_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_stop_session *a, *b; + + a = action_stop_session_from_action_const(_a); + b = action_stop_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = lttng_rate_policy_is_equal(a->policy, b->policy); +end: + return is_equal; +} + +static int lttng_action_stop_session_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_stop_session *action_stop_session; + struct lttng_action_stop_session_comm comm; + size_t session_name_len; + int ret; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(payload); + + action_stop_session = action_stop_session_from_action(action); + + LTTNG_ASSERT(action_stop_session->session_name); + + DBG("Serializing stop session action: session-name: %s", + action_stop_session->session_name); + + session_name_len = strlen(action_stop_session->session_name) + 1; + comm.session_name_len = session_name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_stop_session->session_name, session_name_len); + if (ret) { + ret = -1; + goto end; + } + + ret = lttng_rate_policy_serialize(action_stop_session->policy, payload); + if (ret) { + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +static void lttng_action_stop_session_destroy(struct lttng_action *action) +{ + struct lttng_action_stop_session *action_stop_session; + + if (!action) { + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + lttng_rate_policy_destroy(action_stop_session->policy); + free(action_stop_session->session_name); + free(action_stop_session); + +end: + return; +} + +ssize_t lttng_action_stop_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len, ret; + const struct lttng_action_stop_session_comm *comm; + const char *session_name; + struct lttng_action *action = NULL; + enum lttng_action_status status; + struct lttng_rate_policy *policy = NULL; + + comm = (typeof(comm)) view->buffer.data; + session_name = (const char *) &comm->data; + + /* Session name. */ + if (!lttng_buffer_view_contains_string( + &view->buffer, session_name, comm->session_name_len)) { + consumed_len = -1; + goto end; + } + consumed_len = sizeof(*comm) + comm->session_name_len; + + /* Rate policy. */ + { + struct lttng_payload_view policy_view = + lttng_payload_view_from_view( + view, consumed_len, -1); + ret = lttng_rate_policy_create_from_payload( + &policy_view, &policy); + if (ret < 0) { + consumed_len = -1; + goto end; + } + consumed_len += ret; + } + + action = lttng_action_stop_session_create(); + if (!action) { + consumed_len = -1; + goto end; + } + + status = lttng_action_stop_session_set_session_name( + action, session_name); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + LTTNG_ASSERT(policy); + status = lttng_action_stop_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + consumed_len = -1; + goto end; + } + + *p_action = action; + action = NULL; + +end: + lttng_rate_policy_destroy(policy); + lttng_action_stop_session_destroy(action); + + return consumed_len; +} + +static enum lttng_error_code lttng_action_stop_session_mi_serialize( + const struct lttng_action *action, struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_action_status status; + const char *session_name = NULL; + const struct lttng_rate_policy *policy = NULL; + + LTTNG_ASSERT(action); + LTTNG_ASSERT(IS_STOP_SESSION_ACTION(action)); + + status = lttng_action_stop_session_get_session_name( + action, &session_name); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(session_name != NULL); + + status = lttng_action_stop_session_get_rate_policy(action, &policy); + LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK); + LTTNG_ASSERT(policy != NULL); + + /* Open action stop session. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_action_start_session); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Rate policy. */ + ret_code = lttng_rate_policy_mi_serialize(policy, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close action stop session element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_action *lttng_action_stop_session_create(void) +{ + struct lttng_action *action = NULL; + struct lttng_rate_policy *policy = NULL; + enum lttng_action_status status; + + /* Create a every N = 1 rate policy. */ + policy = lttng_rate_policy_every_n_create(1); + if (!policy) { + goto end; + } + + action = (lttng_action *) zmalloc(sizeof(struct lttng_action_stop_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_STOP_SESSION, + lttng_action_stop_session_validate, + lttng_action_stop_session_serialize, + lttng_action_stop_session_is_equal, + lttng_action_stop_session_destroy, + lttng_action_stop_session_internal_get_rate_policy, + lttng_action_generic_add_error_query_results, + lttng_action_stop_session_mi_serialize); + + status = lttng_action_stop_session_set_rate_policy(action, policy); + if (status != LTTNG_ACTION_STATUS_OK) { + free(action); + action = NULL; + goto end; + } + +end: + lttng_rate_policy_destroy(policy); + return action; +} + +enum lttng_action_status lttng_action_stop_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_stop_session *action_stop_session; + enum lttng_action_status status; + + if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_stop_session = action_stop_session_from_action(action); + + free(action_stop_session->session_name); + + action_stop_session->session_name = strdup(session_name); + if (!action_stop_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_stop_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_stop_session *action_stop_session; + enum lttng_action_status status; + + if (!action || !IS_STOP_SESSION_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_stop_session = action_stop_session_from_action_const(action); + + *session_name = action_stop_session->session_name; + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_stop_session_set_rate_policy( + struct lttng_action *action, + const struct lttng_rate_policy *policy) +{ + enum lttng_action_status status; + struct lttng_action_stop_session *stop_session_action; + struct lttng_rate_policy *copy = NULL; + + if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + copy = lttng_rate_policy_copy(policy); + if (!copy) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + stop_session_action = action_stop_session_from_action(action); + + /* Free the previous rate policy .*/ + lttng_rate_policy_destroy(stop_session_action->policy); + + stop_session_action->policy = copy; + status = LTTNG_ACTION_STATUS_OK; + copy = NULL; + +end: + lttng_rate_policy_destroy(copy); + return status; +} + +enum lttng_action_status lttng_action_stop_session_get_rate_policy( + const struct lttng_action *action, + const struct lttng_rate_policy **policy) +{ + enum lttng_action_status status; + const struct lttng_action_stop_session *stop_session_action; + + if (!action || !policy || !IS_STOP_SESSION_ACTION(action)) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + stop_session_action = action_stop_session_from_action_const(action); + + *policy = stop_session_action->policy; + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +static const struct lttng_rate_policy * +lttng_action_stop_session_internal_get_rate_policy( + const struct lttng_action *action) +{ + const struct lttng_action_stop_session *_action; + _action = action_stop_session_from_action_const(action); + + return _action->policy; +} diff --git a/src/common/buffer-view.c b/src/common/buffer-view.c deleted file mode 100644 index bfd5ac6d7..000000000 --- a/src/common/buffer-view.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include - -struct lttng_buffer_view lttng_buffer_view_init( - const char *src, size_t offset, ptrdiff_t len) -{ - struct lttng_buffer_view view = { .data = src + offset, .size = len }; - return view; -} - -bool lttng_buffer_view_is_valid(const struct lttng_buffer_view *view) -{ - return view && view->data && view->size > 0; -} - -struct lttng_buffer_view lttng_buffer_view_from_view( - const struct lttng_buffer_view *src, size_t offset, - ptrdiff_t len) -{ - struct lttng_buffer_view view = { .data = NULL, .size = 0 }; - - LTTNG_ASSERT(src); - - if (offset > src->size) { - ERR("Attempt to create buffer view from another view with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd", - src->size, offset, len); - goto end; - } - - if (len != -1 && len > (src->size - offset)) { - ERR("Attempt to create buffer view from another view with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd", - src->size, offset, len); - goto end; - } - - view.data = src->data + offset; - view.size = len == -1 ? (src->size - offset) : len; -end: - return view; -} - -struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer( - const struct lttng_dynamic_buffer *src, size_t offset, - ptrdiff_t len) -{ - struct lttng_buffer_view view = { .data = NULL, .size = 0 }; - - LTTNG_ASSERT(src); - - if (offset > src->size) { - ERR("Attempt to create buffer view from a dynamic buffer with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd", - src->size, offset, len); - goto end; - } - - if (len != -1 && len > (src->size - offset)) { - ERR("Attempt to create buffer view from a dynamic buffer with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd", - src->size, offset, len); - goto end; - } - - view.data = src->data + offset; - view.size = len == -1 ? (src->size - offset) : len; -end: - return view; -} - -bool lttng_buffer_view_contains_string(const struct lttng_buffer_view *buf, - const char *str, - size_t len_with_null_terminator) -{ - const char *past_buf_end; - size_t max_str_len_with_null_terminator; - size_t str_len; - bool ret; - - past_buf_end = buf->data + buf->size; - - /* Is the start of the string in the buffer view? */ - if (str < buf->data || str >= past_buf_end) { - ret = false; - goto end; - } - - /* - * Max length the string could have to fit in the buffer, including - * NULL terminator. - */ - max_str_len_with_null_terminator = past_buf_end - str; - - /* Could the string even fit in the buffer? */ - if (len_with_null_terminator > max_str_len_with_null_terminator) { - ret = false; - goto end; - } - - str_len = lttng_strnlen(str, max_str_len_with_null_terminator); - if (str_len != (len_with_null_terminator - 1)) { - ret = false; - goto end; - } - - ret = true; - -end: - return ret; -} diff --git a/src/common/buffer-view.cpp b/src/common/buffer-view.cpp new file mode 100644 index 000000000..ebe686985 --- /dev/null +++ b/src/common/buffer-view.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include + +struct lttng_buffer_view lttng_buffer_view_init( + const char *src, size_t offset, ptrdiff_t len) +{ + struct lttng_buffer_view view = { .data = src + offset, .size = (size_t) len }; + return view; +} + +bool lttng_buffer_view_is_valid(const struct lttng_buffer_view *view) +{ + return view && view->data && view->size > 0; +} + +struct lttng_buffer_view lttng_buffer_view_from_view( + const struct lttng_buffer_view *src, size_t offset, + ptrdiff_t len) +{ + struct lttng_buffer_view view = { .data = NULL, .size = 0 }; + + LTTNG_ASSERT(src); + + if (offset > src->size) { + ERR("Attempt to create buffer view from another view with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd", + src->size, offset, len); + goto end; + } + + if (len != -1 && len > (src->size - offset)) { + ERR("Attempt to create buffer view from another view with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd", + src->size, offset, len); + goto end; + } + + view.data = src->data + offset; + view.size = len == -1 ? (src->size - offset) : len; +end: + return view; +} + +struct lttng_buffer_view lttng_buffer_view_from_dynamic_buffer( + const struct lttng_dynamic_buffer *src, size_t offset, + ptrdiff_t len) +{ + struct lttng_buffer_view view = { .data = NULL, .size = 0 }; + + LTTNG_ASSERT(src); + + if (offset > src->size) { + ERR("Attempt to create buffer view from a dynamic buffer with invalid offset (offset > source size): source size = %zu, offset in source = %zu, length = %zd", + src->size, offset, len); + goto end; + } + + if (len != -1 && len > (src->size - offset)) { + ERR("Attempt to create buffer view from a dynamic buffer with invalid length (length > space left after offset in source): source size = %zu, offset in source = %zu, length = %zd", + src->size, offset, len); + goto end; + } + + view.data = src->data + offset; + view.size = len == -1 ? (src->size - offset) : len; +end: + return view; +} + +bool lttng_buffer_view_contains_string(const struct lttng_buffer_view *buf, + const char *str, + size_t len_with_null_terminator) +{ + const char *past_buf_end; + size_t max_str_len_with_null_terminator; + size_t str_len; + bool ret; + + past_buf_end = buf->data + buf->size; + + /* Is the start of the string in the buffer view? */ + if (str < buf->data || str >= past_buf_end) { + ret = false; + goto end; + } + + /* + * Max length the string could have to fit in the buffer, including + * NULL terminator. + */ + max_str_len_with_null_terminator = past_buf_end - str; + + /* Could the string even fit in the buffer? */ + if (len_with_null_terminator > max_str_len_with_null_terminator) { + ret = false; + goto end; + } + + str_len = lttng_strnlen(str, max_str_len_with_null_terminator); + if (str_len != (len_with_null_terminator - 1)) { + ret = false; + goto end; + } + + ret = true; + +end: + return ret; +} diff --git a/src/common/conditions/buffer-usage.c b/src/common/conditions/buffer-usage.c deleted file mode 100644 index 714ba582c..000000000 --- a/src/common/conditions/buffer-usage.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_USAGE_CONDITION(condition) ( \ - lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \ - lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH \ - ) - -static -bool is_usage_evaluation(const struct lttng_evaluation *evaluation) -{ - enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); - - return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || - type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH; -} - -static -void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition) -{ - struct lttng_condition_buffer_usage *usage; - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - - free(usage->session_name); - free(usage->channel_name); - free(usage); -} - -static -bool lttng_condition_buffer_usage_validate( - const struct lttng_condition *condition) -{ - bool valid = false; - struct lttng_condition_buffer_usage *usage; - - if (!condition) { - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->session_name) { - ERR("Invalid buffer condition: a target session name must be set."); - goto end; - } - if (!usage->channel_name) { - ERR("Invalid buffer condition: a target channel name must be set."); - goto end; - } - if (usage->threshold_ratio.set == usage->threshold_bytes.set) { - ERR("Invalid buffer condition: a threshold must be set or both type cannot be used simultaneously."); - goto end; - } - if (!usage->domain.set) { - ERR("Invalid buffer usage condition: a domain must be set."); - goto end; - } - - valid = true; -end: - return valid; -} - -static -int lttng_condition_buffer_usage_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload) -{ - int ret; - struct lttng_condition_buffer_usage *usage; - size_t session_name_len, channel_name_len; - struct lttng_condition_buffer_usage_comm usage_comm = {}; - - if (!condition || !IS_USAGE_CONDITION(condition)) { - ret = -1; - goto end; - } - - DBG("Serializing buffer usage condition"); - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - - session_name_len = strlen(usage->session_name) + 1; - channel_name_len = strlen(usage->channel_name) + 1; - if (session_name_len > LTTNG_NAME_MAX || - channel_name_len > LTTNG_NAME_MAX) { - ret = -1; - goto end; - } - - usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set; - usage_comm.session_name_len = session_name_len; - usage_comm.channel_name_len = channel_name_len; - usage_comm.domain_type = (int8_t) usage->domain.type; - - if (usage->threshold_bytes.set) { - usage_comm.threshold_bytes = usage->threshold_bytes.value; - } else { - usage_comm.threshold_ratio = usage->threshold_ratio.value; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, &usage_comm, - sizeof(usage_comm)); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name, - session_name_len); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, usage->channel_name, - channel_name_len); - if (ret) { - goto end; - } -end: - return ret; -} - -static -bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a, - const struct lttng_condition *_b) -{ - bool is_equal = false; - struct lttng_condition_buffer_usage *a, *b; - - a = container_of(_a, struct lttng_condition_buffer_usage, parent); - b = container_of(_b, struct lttng_condition_buffer_usage, parent); - - if ((a->threshold_ratio.set && !b->threshold_ratio.set) || - (a->threshold_bytes.set && !b->threshold_bytes.set)) { - goto end; - } - - if (a->threshold_ratio.set && b->threshold_ratio.set) { - double a_value, b_value, diff; - - a_value = a->threshold_ratio.value; - b_value = b->threshold_ratio.value; - diff = fabs(a_value - b_value); - - if (diff > DBL_EPSILON) { - goto end; - } - } else if (a->threshold_bytes.set && b->threshold_bytes.set) { - uint64_t a_value, b_value; - - a_value = a->threshold_bytes.value; - b_value = b->threshold_bytes.value; - if (a_value != b_value) { - goto end; - } - } - - /* Condition is not valid if this is not true. */ - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - LTTNG_ASSERT(a->channel_name); - LTTNG_ASSERT(b->channel_name); - if (strcmp(a->channel_name, b->channel_name)) { - goto end; - } - - LTTNG_ASSERT(a->domain.set); - LTTNG_ASSERT(b->domain.set); - if (a->domain.type != b->domain.type) { - goto end; - } - is_equal = true; -end: - return is_equal; -} - -static enum lttng_error_code lttng_condition_buffer_usage_mi_serialize( - const struct lttng_condition *condition, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_condition_status status; - const char *session_name = NULL, *channel_name = NULL; - enum lttng_domain_type domain_type; - bool is_threshold_bytes = false; - double threshold_ratio; - uint64_t threshold_bytes; - const char *condition_type_str = NULL; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(IS_USAGE_CONDITION(condition)); - - status = lttng_condition_buffer_usage_get_session_name( - condition, &session_name); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - LTTNG_ASSERT(session_name); - - status = lttng_condition_buffer_usage_get_channel_name( - condition, &channel_name); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - LTTNG_ASSERT(session_name); - - status = lttng_condition_buffer_usage_get_domain_type( - condition, &domain_type); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - - status = lttng_condition_buffer_usage_get_threshold( - condition, &threshold_bytes); - if (status == LTTNG_CONDITION_STATUS_OK) { - is_threshold_bytes = true; - } else if (status != LTTNG_CONDITION_STATUS_UNSET) { - /* Unexpected at this stage. */ - ret_code = LTTNG_ERR_INVALID; - goto end; - } - - if (!is_threshold_bytes) { - status = lttng_condition_buffer_usage_get_threshold_ratio( - condition, &threshold_ratio); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - } - - switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - condition_type_str = - mi_lttng_element_condition_buffer_usage_high; - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - condition_type_str = - mi_lttng_element_condition_buffer_usage_low; - break; - default: - abort(); - break; - } - - /* Open the sub type condition element. */ - ret = mi_lttng_writer_open_element(writer, condition_type_str); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Channel name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_condition_channel_name, channel_name); - if (ret) { - goto mi_error; - } - - /* Domain. */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_domain, - mi_lttng_domaintype_string(domain_type)); - if (ret) { - goto mi_error; - } - - if (is_threshold_bytes) { - /* Usage in bytes. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_condition_threshold_bytes, - threshold_bytes); - if (ret) { - goto mi_error; - } - } else { - /* Ratio. */ - ret = mi_lttng_writer_write_element_double(writer, - mi_lttng_element_condition_threshold_ratio, - threshold_ratio); - if (ret) { - goto mi_error; - } - } - - /* Closing sub type condition element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -struct lttng_condition *lttng_condition_buffer_usage_create( - enum lttng_condition_type type) -{ - struct lttng_condition_buffer_usage *condition; - - condition = zmalloc(sizeof(struct lttng_condition_buffer_usage)); - if (!condition) { - return NULL; - } - - lttng_condition_init(&condition->parent, type); - condition->parent.validate = lttng_condition_buffer_usage_validate; - condition->parent.serialize = lttng_condition_buffer_usage_serialize; - condition->parent.equal = lttng_condition_buffer_usage_is_equal; - condition->parent.destroy = lttng_condition_buffer_usage_destroy; - condition->parent.mi_serialize = lttng_condition_buffer_usage_mi_serialize; - return &condition->parent; -} - -struct lttng_condition *lttng_condition_buffer_usage_low_create(void) -{ - return lttng_condition_buffer_usage_create( - LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); -} - -struct lttng_condition *lttng_condition_buffer_usage_high_create(void) -{ - return lttng_condition_buffer_usage_create( - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); -} - -static -ssize_t init_condition_from_payload(struct lttng_condition *condition, - struct lttng_payload_view *src_view) -{ - ssize_t ret, condition_size; - enum lttng_condition_status status; - enum lttng_domain_type domain_type; - const char *session_name, *channel_name; - struct lttng_buffer_view names_view; - const struct lttng_condition_buffer_usage_comm *condition_comm; - const struct lttng_payload_view condition_comm_view = - lttng_payload_view_from_view( - src_view, 0, sizeof(*condition_comm)); - - if (!lttng_payload_view_is_valid(&condition_comm_view)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); - ret = -1; - goto end; - } - - condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; - names_view = lttng_buffer_view_from_view(&src_view->buffer, - sizeof(*condition_comm), -1); - - if (condition_comm->session_name_len > LTTNG_NAME_MAX || - condition_comm->channel_name_len > LTTNG_NAME_MAX) { - ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); - ret = -1; - goto end; - } - - if (names_view.size < - (condition_comm->session_name_len + - condition_comm->channel_name_len)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names"); - ret = -1; - goto end; - } - - if (condition_comm->threshold_set_in_bytes) { - status = lttng_condition_buffer_usage_set_threshold(condition, - condition_comm->threshold_bytes); - } else { - status = lttng_condition_buffer_usage_set_threshold_ratio( - condition, condition_comm->threshold_ratio); - } - - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to initialize buffer usage condition threshold"); - ret = -1; - goto end; - } - - if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE || - condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) { - /* Invalid domain value. */ - ERR("Invalid domain type value (%i) found in condition buffer", - (int) condition_comm->domain_type); - ret = -1; - goto end; - } - - domain_type = (enum lttng_domain_type) condition_comm->domain_type; - status = lttng_condition_buffer_usage_set_domain_type(condition, - domain_type); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to set buffer usage condition domain"); - ret = -1; - goto end; - } - - session_name = names_view.data; - if (*(session_name + condition_comm->session_name_len - 1) != '\0') { - ERR("Malformed session name encountered in condition buffer"); - ret = -1; - goto end; - } - - channel_name = session_name + condition_comm->session_name_len; - if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') { - ERR("Malformed channel name encountered in condition buffer"); - ret = -1; - goto end; - } - - status = lttng_condition_buffer_usage_set_session_name(condition, - session_name); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to set buffer usage session name"); - ret = -1; - goto end; - } - - status = lttng_condition_buffer_usage_set_channel_name(condition, - channel_name); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to set buffer usage channel name"); - ret = -1; - goto end; - } - - if (!lttng_condition_validate(condition)) { - ret = -1; - goto end; - } - - condition_size = sizeof(*condition_comm) + - (ssize_t) condition_comm->session_name_len + - (ssize_t) condition_comm->channel_name_len; - ret = condition_size; -end: - return ret; -} - -ssize_t lttng_condition_buffer_usage_low_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **_condition) -{ - ssize_t ret; - struct lttng_condition *condition = - lttng_condition_buffer_usage_low_create(); - - if (!_condition || !condition) { - ret = -1; - goto error; - } - - ret = init_condition_from_payload(condition, view); - if (ret < 0) { - goto error; - } - - *_condition = condition; - return ret; -error: - lttng_condition_destroy(condition); - return ret; -} - -ssize_t lttng_condition_buffer_usage_high_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **_condition) -{ - ssize_t ret; - struct lttng_condition *condition = - lttng_condition_buffer_usage_high_create(); - - if (!_condition || !condition) { - ret = -1; - goto error; - } - - ret = init_condition_from_payload(condition, view); - if (ret < 0) { - goto error; - } - - *_condition = condition; - return ret; -error: - lttng_condition_destroy(condition); - return ret; -} - -static -struct lttng_evaluation *create_evaluation_from_payload( - enum lttng_condition_type type, - struct lttng_payload_view *view) -{ - const struct lttng_evaluation_buffer_usage_comm *comm = - (typeof(comm)) view->buffer.data; - struct lttng_evaluation *evaluation = NULL; - - if (view->buffer.size < sizeof(*comm)) { - goto end; - } - - evaluation = lttng_evaluation_buffer_usage_create(type, - comm->buffer_use, comm->buffer_capacity); -end: - return evaluation; -} - -ssize_t lttng_evaluation_buffer_usage_low_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret; - struct lttng_evaluation *evaluation = NULL; - - if (!_evaluation) { - ret = -1; - goto error; - } - - evaluation = create_evaluation_from_payload( - LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view); - if (!evaluation) { - ret = -1; - goto error; - } - - *_evaluation = evaluation; - ret = sizeof(struct lttng_evaluation_buffer_usage_comm); - return ret; -error: - lttng_evaluation_destroy(evaluation); - return ret; -} - -ssize_t lttng_evaluation_buffer_usage_high_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret; - struct lttng_evaluation *evaluation = NULL; - - if (!_evaluation) { - ret = -1; - goto error; - } - - evaluation = create_evaluation_from_payload( - LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view); - if (!evaluation) { - ret = -1; - goto error; - } - - *_evaluation = evaluation; - ret = sizeof(struct lttng_evaluation_buffer_usage_comm); - return ret; -error: - lttng_evaluation_destroy(evaluation); - return ret; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_get_threshold_ratio( - const struct lttng_condition *condition, - double *threshold_ratio) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || - !threshold_ratio) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->threshold_ratio.set) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *threshold_ratio = usage->threshold_ratio.value; -end: - return status; -} - -/* threshold_ratio expressed as [0.0, 1.0]. */ -enum lttng_condition_status -lttng_condition_buffer_usage_set_threshold_ratio( - struct lttng_condition *condition, double threshold_ratio) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || - threshold_ratio < 0.0 || - threshold_ratio > 1.0) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - usage->threshold_ratio.set = true; - usage->threshold_bytes.set = false; - usage->threshold_ratio.value = threshold_ratio; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_get_threshold( - const struct lttng_condition *condition, - uint64_t *threshold_bytes) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->threshold_bytes.set) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *threshold_bytes = usage->threshold_bytes.value; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_set_threshold( - struct lttng_condition *condition, uint64_t threshold_bytes) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition)) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - usage->threshold_ratio.set = false; - usage->threshold_bytes.set = true; - usage->threshold_bytes.value = threshold_bytes; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_get_session_name( - const struct lttng_condition *condition, - const char **session_name) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->session_name) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *session_name = usage->session_name; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_set_session_name( - struct lttng_condition *condition, const char *session_name) -{ - char *session_name_copy; - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !session_name || - strlen(session_name) == 0) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - session_name_copy = strdup(session_name); - if (!session_name_copy) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - if (usage->session_name) { - free(usage->session_name); - } - usage->session_name = session_name_copy; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_get_channel_name( - const struct lttng_condition *condition, - const char **channel_name) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->channel_name) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *channel_name = usage->channel_name; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_set_channel_name( - struct lttng_condition *condition, const char *channel_name) -{ - char *channel_name_copy; - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name || - strlen(channel_name) == 0) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - channel_name_copy = strdup(channel_name); - if (!channel_name_copy) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - if (usage->channel_name) { - free(usage->channel_name); - } - usage->channel_name = channel_name_copy; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_get_domain_type( - const struct lttng_condition *condition, - enum lttng_domain_type *type) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || !type) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - if (!usage->domain.set) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *type = usage->domain.type; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_buffer_usage_set_domain_type( - struct lttng_condition *condition, enum lttng_domain_type type) -{ - struct lttng_condition_buffer_usage *usage; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_USAGE_CONDITION(condition) || - type == LTTNG_DOMAIN_NONE) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - usage = container_of(condition, struct lttng_condition_buffer_usage, - parent); - usage->domain.set = true; - usage->domain.type = type; -end: - return status; -} - -static -int lttng_evaluation_buffer_usage_serialize( - const struct lttng_evaluation *evaluation, - struct lttng_payload *payload) -{ - struct lttng_evaluation_buffer_usage *usage; - struct lttng_evaluation_buffer_usage_comm comm; - - usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, - parent); - comm.buffer_use = usage->buffer_use; - comm.buffer_capacity = usage->buffer_capacity; - - return lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); -} - -static -void lttng_evaluation_buffer_usage_destroy( - struct lttng_evaluation *evaluation) -{ - struct lttng_evaluation_buffer_usage *usage; - - usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, - parent); - free(usage); -} - -struct lttng_evaluation *lttng_evaluation_buffer_usage_create( - enum lttng_condition_type type, uint64_t use, uint64_t capacity) -{ - struct lttng_evaluation_buffer_usage *usage; - - usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage)); - if (!usage) { - goto end; - } - - usage->parent.type = type; - usage->buffer_use = use; - usage->buffer_capacity = capacity; - usage->parent.serialize = lttng_evaluation_buffer_usage_serialize; - usage->parent.destroy = lttng_evaluation_buffer_usage_destroy; -end: - return &usage->parent; -} - -/* - * Get the sampled buffer usage which caused the associated condition to - * evaluate to "true". - */ -enum lttng_evaluation_status -lttng_evaluation_buffer_usage_get_usage_ratio( - const struct lttng_evaluation *evaluation, double *usage_ratio) -{ - struct lttng_evaluation_buffer_usage *usage; - enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; - - if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) { - status = LTTNG_EVALUATION_STATUS_INVALID; - goto end; - } - - usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, - parent); - *usage_ratio = (double) usage->buffer_use / - (double) usage->buffer_capacity; -end: - return status; -} - -enum lttng_evaluation_status -lttng_evaluation_buffer_usage_get_usage( - const struct lttng_evaluation *evaluation, - uint64_t *usage_bytes) -{ - struct lttng_evaluation_buffer_usage *usage; - enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; - - if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) { - status = LTTNG_EVALUATION_STATUS_INVALID; - goto end; - } - - usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, - parent); - *usage_bytes = usage->buffer_use; -end: - return status; -} diff --git a/src/common/conditions/buffer-usage.cpp b/src/common/conditions/buffer-usage.cpp new file mode 100644 index 000000000..fa28db65b --- /dev/null +++ b/src/common/conditions/buffer-usage.cpp @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_USAGE_CONDITION(condition) ( \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH \ + ) + +static +bool is_usage_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || + type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH; +} + +static +void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition) +{ + struct lttng_condition_buffer_usage *usage; + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + + free(usage->session_name); + free(usage->channel_name); + free(usage); +} + +static +bool lttng_condition_buffer_usage_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_buffer_usage *usage; + + if (!condition) { + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->session_name) { + ERR("Invalid buffer condition: a target session name must be set."); + goto end; + } + if (!usage->channel_name) { + ERR("Invalid buffer condition: a target channel name must be set."); + goto end; + } + if (usage->threshold_ratio.set == usage->threshold_bytes.set) { + ERR("Invalid buffer condition: a threshold must be set or both type cannot be used simultaneously."); + goto end; + } + if (!usage->domain.set) { + ERR("Invalid buffer usage condition: a domain must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +int lttng_condition_buffer_usage_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + struct lttng_condition_buffer_usage *usage; + size_t session_name_len, channel_name_len; + struct lttng_condition_buffer_usage_comm usage_comm = {}; + + if (!condition || !IS_USAGE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing buffer usage condition"); + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + + session_name_len = strlen(usage->session_name) + 1; + channel_name_len = strlen(usage->channel_name) + 1; + if (session_name_len > LTTNG_NAME_MAX || + channel_name_len > LTTNG_NAME_MAX) { + ret = -1; + goto end; + } + + usage_comm.threshold_set_in_bytes = !!usage->threshold_bytes.set; + usage_comm.session_name_len = session_name_len; + usage_comm.channel_name_len = channel_name_len; + usage_comm.domain_type = (int8_t) usage->domain.type; + + if (usage->threshold_bytes.set) { + usage_comm.threshold_bytes = usage->threshold_bytes.value; + } else { + usage_comm.threshold_ratio = usage->threshold_ratio.value; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, &usage_comm, + sizeof(usage_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, usage->session_name, + session_name_len); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, usage->channel_name, + channel_name_len); + if (ret) { + goto end; + } +end: + return ret; +} + +static +bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_buffer_usage *a, *b; + + a = container_of(_a, struct lttng_condition_buffer_usage, parent); + b = container_of(_b, struct lttng_condition_buffer_usage, parent); + + if ((a->threshold_ratio.set && !b->threshold_ratio.set) || + (a->threshold_bytes.set && !b->threshold_bytes.set)) { + goto end; + } + + if (a->threshold_ratio.set && b->threshold_ratio.set) { + double a_value, b_value, diff; + + a_value = a->threshold_ratio.value; + b_value = b->threshold_ratio.value; + diff = fabs(a_value - b_value); + + if (diff > DBL_EPSILON) { + goto end; + } + } else if (a->threshold_bytes.set && b->threshold_bytes.set) { + uint64_t a_value, b_value; + + a_value = a->threshold_bytes.value; + b_value = b->threshold_bytes.value; + if (a_value != b_value) { + goto end; + } + } + + /* Condition is not valid if this is not true. */ + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + LTTNG_ASSERT(a->channel_name); + LTTNG_ASSERT(b->channel_name); + if (strcmp(a->channel_name, b->channel_name)) { + goto end; + } + + LTTNG_ASSERT(a->domain.set); + LTTNG_ASSERT(b->domain.set); + if (a->domain.type != b->domain.type) { + goto end; + } + is_equal = true; +end: + return is_equal; +} + +static enum lttng_error_code lttng_condition_buffer_usage_mi_serialize( + const struct lttng_condition *condition, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_condition_status status; + const char *session_name = NULL, *channel_name = NULL; + enum lttng_domain_type domain_type; + bool is_threshold_bytes = false; + double threshold_ratio; + uint64_t threshold_bytes; + const char *condition_type_str = NULL; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(IS_USAGE_CONDITION(condition)); + + status = lttng_condition_buffer_usage_get_session_name( + condition, &session_name); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(session_name); + + status = lttng_condition_buffer_usage_get_channel_name( + condition, &channel_name); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(session_name); + + status = lttng_condition_buffer_usage_get_domain_type( + condition, &domain_type); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + + status = lttng_condition_buffer_usage_get_threshold( + condition, &threshold_bytes); + if (status == LTTNG_CONDITION_STATUS_OK) { + is_threshold_bytes = true; + } else if (status != LTTNG_CONDITION_STATUS_UNSET) { + /* Unexpected at this stage. */ + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + if (!is_threshold_bytes) { + status = lttng_condition_buffer_usage_get_threshold_ratio( + condition, &threshold_ratio); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + } + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + condition_type_str = + mi_lttng_element_condition_buffer_usage_high; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + condition_type_str = + mi_lttng_element_condition_buffer_usage_low; + break; + default: + abort(); + break; + } + + /* Open the sub type condition element. */ + ret = mi_lttng_writer_open_element(writer, condition_type_str); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Channel name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_condition_channel_name, channel_name); + if (ret) { + goto mi_error; + } + + /* Domain. */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_domain, + mi_lttng_domaintype_string(domain_type)); + if (ret) { + goto mi_error; + } + + if (is_threshold_bytes) { + /* Usage in bytes. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_condition_threshold_bytes, + threshold_bytes); + if (ret) { + goto mi_error; + } + } else { + /* Ratio. */ + ret = mi_lttng_writer_write_element_double(writer, + mi_lttng_element_condition_threshold_ratio, + threshold_ratio); + if (ret) { + goto mi_error; + } + } + + /* Closing sub type condition element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +struct lttng_condition *lttng_condition_buffer_usage_create( + enum lttng_condition_type type) +{ + struct lttng_condition_buffer_usage *condition; + + condition = (lttng_condition_buffer_usage *) zmalloc(sizeof(struct lttng_condition_buffer_usage)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, type); + condition->parent.validate = lttng_condition_buffer_usage_validate; + condition->parent.serialize = lttng_condition_buffer_usage_serialize; + condition->parent.equal = lttng_condition_buffer_usage_is_equal; + condition->parent.destroy = lttng_condition_buffer_usage_destroy; + condition->parent.mi_serialize = lttng_condition_buffer_usage_mi_serialize; + return &condition->parent; +} + +struct lttng_condition *lttng_condition_buffer_usage_low_create(void) +{ + return lttng_condition_buffer_usage_create( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); +} + +struct lttng_condition *lttng_condition_buffer_usage_high_create(void) +{ + return lttng_condition_buffer_usage_create( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); +} + +static +ssize_t init_condition_from_payload(struct lttng_condition *condition, + struct lttng_payload_view *src_view) +{ + ssize_t ret, condition_size; + enum lttng_condition_status status; + enum lttng_domain_type domain_type; + const char *session_name, *channel_name; + struct lttng_buffer_view names_view; + const struct lttng_condition_buffer_usage_comm *condition_comm; + const struct lttng_payload_view condition_comm_view = + lttng_payload_view_from_view( + src_view, 0, sizeof(*condition_comm)); + + if (!lttng_payload_view_is_valid(&condition_comm_view)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); + ret = -1; + goto end; + } + + condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; + names_view = lttng_buffer_view_from_view(&src_view->buffer, + sizeof(*condition_comm), -1); + + if (condition_comm->session_name_len > LTTNG_NAME_MAX || + condition_comm->channel_name_len > LTTNG_NAME_MAX) { + ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); + ret = -1; + goto end; + } + + if (names_view.size < + (condition_comm->session_name_len + + condition_comm->channel_name_len)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names"); + ret = -1; + goto end; + } + + if (condition_comm->threshold_set_in_bytes) { + status = lttng_condition_buffer_usage_set_threshold(condition, + condition_comm->threshold_bytes); + } else { + status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, condition_comm->threshold_ratio); + } + + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to initialize buffer usage condition threshold"); + ret = -1; + goto end; + } + + if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE || + condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) { + /* Invalid domain value. */ + ERR("Invalid domain type value (%i) found in condition buffer", + (int) condition_comm->domain_type); + ret = -1; + goto end; + } + + domain_type = (enum lttng_domain_type) condition_comm->domain_type; + status = lttng_condition_buffer_usage_set_domain_type(condition, + domain_type); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage condition domain"); + ret = -1; + goto end; + } + + session_name = names_view.data; + if (*(session_name + condition_comm->session_name_len - 1) != '\0') { + ERR("Malformed session name encountered in condition buffer"); + ret = -1; + goto end; + } + + channel_name = session_name + condition_comm->session_name_len; + if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') { + ERR("Malformed channel name encountered in condition buffer"); + ret = -1; + goto end; + } + + status = lttng_condition_buffer_usage_set_session_name(condition, + session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage session name"); + ret = -1; + goto end; + } + + status = lttng_condition_buffer_usage_set_channel_name(condition, + channel_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage channel name"); + ret = -1; + goto end; + } + + if (!lttng_condition_validate(condition)) { + ret = -1; + goto end; + } + + condition_size = sizeof(*condition_comm) + + (ssize_t) condition_comm->session_name_len + + (ssize_t) condition_comm->channel_name_len; + ret = condition_size; +end: + return ret; +} + +ssize_t lttng_condition_buffer_usage_low_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t ret; + struct lttng_condition *condition = + lttng_condition_buffer_usage_low_create(); + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_payload(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +ssize_t lttng_condition_buffer_usage_high_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t ret; + struct lttng_condition *condition = + lttng_condition_buffer_usage_high_create(); + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_payload(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +static +struct lttng_evaluation *create_evaluation_from_payload( + enum lttng_condition_type type, + struct lttng_payload_view *view) +{ + const struct lttng_evaluation_buffer_usage_comm *comm = + (typeof(comm)) view->buffer.data; + struct lttng_evaluation *evaluation = NULL; + + if (view->buffer.size < sizeof(*comm)) { + goto end; + } + + evaluation = lttng_evaluation_buffer_usage_create(type, + comm->buffer_use, comm->buffer_capacity); +end: + return evaluation; +} + +ssize_t lttng_evaluation_buffer_usage_low_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + evaluation = create_evaluation_from_payload( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + ret = sizeof(struct lttng_evaluation_buffer_usage_comm); + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +ssize_t lttng_evaluation_buffer_usage_high_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + evaluation = create_evaluation_from_payload( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + ret = sizeof(struct lttng_evaluation_buffer_usage_comm); + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold_ratio( + const struct lttng_condition *condition, + double *threshold_ratio) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + !threshold_ratio) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->threshold_ratio.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *threshold_ratio = usage->threshold_ratio.value; +end: + return status; +} + +/* threshold_ratio expressed as [0.0, 1.0]. */ +enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold_ratio( + struct lttng_condition *condition, double threshold_ratio) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + threshold_ratio < 0.0 || + threshold_ratio > 1.0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->threshold_ratio.set = true; + usage->threshold_bytes.set = false; + usage->threshold_ratio.value = threshold_ratio; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold( + const struct lttng_condition *condition, + uint64_t *threshold_bytes) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->threshold_bytes.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *threshold_bytes = usage->threshold_bytes.value; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold( + struct lttng_condition *condition, uint64_t threshold_bytes) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->threshold_ratio.set = false; + usage->threshold_bytes.set = true; + usage->threshold_bytes.value = threshold_bytes; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_session_name( + const struct lttng_condition *condition, + const char **session_name) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->session_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *session_name = usage->session_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_session_name( + struct lttng_condition *condition, const char *session_name) +{ + char *session_name_copy; + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + session_name_copy = strdup(session_name); + if (!session_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + if (usage->session_name) { + free(usage->session_name); + } + usage->session_name = session_name_copy; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_channel_name( + const struct lttng_condition *condition, + const char **channel_name) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->channel_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *channel_name = usage->channel_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_channel_name( + struct lttng_condition *condition, const char *channel_name) +{ + char *channel_name_copy; + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name || + strlen(channel_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + channel_name_copy = strdup(channel_name); + if (!channel_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + if (usage->channel_name) { + free(usage->channel_name); + } + usage->channel_name = channel_name_copy; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_domain_type( + const struct lttng_condition *condition, + enum lttng_domain_type *type) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !type) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->domain.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *type = usage->domain.type; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_domain_type( + struct lttng_condition *condition, enum lttng_domain_type type) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + type == LTTNG_DOMAIN_NONE) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->domain.set = true; + usage->domain.type = type; +end: + return status; +} + +static +int lttng_evaluation_buffer_usage_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + struct lttng_evaluation_buffer_usage *usage; + struct lttng_evaluation_buffer_usage_comm comm; + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + comm.buffer_use = usage->buffer_use; + comm.buffer_capacity = usage->buffer_capacity; + + return lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); +} + +static +void lttng_evaluation_buffer_usage_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_buffer_usage *usage; + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + free(usage); +} + +struct lttng_evaluation *lttng_evaluation_buffer_usage_create( + enum lttng_condition_type type, uint64_t use, uint64_t capacity) +{ + struct lttng_evaluation_buffer_usage *usage; + + usage = (lttng_evaluation_buffer_usage *) zmalloc(sizeof(struct lttng_evaluation_buffer_usage)); + if (!usage) { + goto end; + } + + usage->parent.type = type; + usage->buffer_use = use; + usage->buffer_capacity = capacity; + usage->parent.serialize = lttng_evaluation_buffer_usage_serialize; + usage->parent.destroy = lttng_evaluation_buffer_usage_destroy; +end: + return &usage->parent; +} + +/* + * Get the sampled buffer usage which caused the associated condition to + * evaluate to "true". + */ +enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage_ratio( + const struct lttng_evaluation *evaluation, double *usage_ratio) +{ + struct lttng_evaluation_buffer_usage *usage; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + *usage_ratio = (double) usage->buffer_use / + (double) usage->buffer_capacity; +end: + return status; +} + +enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage( + const struct lttng_evaluation *evaluation, + uint64_t *usage_bytes) +{ + struct lttng_evaluation_buffer_usage *usage; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + *usage_bytes = usage->buffer_use; +end: + return status; +} diff --git a/src/common/conditions/condition.c b/src/common/conditions/condition.c deleted file mode 100644 index 18b756dce..000000000 --- a/src/common/conditions/condition.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum lttng_condition_type lttng_condition_get_type( - const struct lttng_condition *condition) -{ - return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN; -} - -void lttng_condition_destroy(struct lttng_condition *condition) -{ - lttng_condition_put(condition); -} - -static void condition_destroy_ref(struct urcu_ref *ref) -{ - struct lttng_condition *condition = - container_of(ref, struct lttng_condition, ref); - - condition->destroy(condition); -} - -void lttng_condition_get(struct lttng_condition *condition) -{ - urcu_ref_get(&condition->ref); -} - -void lttng_condition_put(struct lttng_condition *condition) -{ - if (!condition) { - return; - } - - LTTNG_ASSERT(condition->destroy); - urcu_ref_put(&condition->ref, condition_destroy_ref); -} - - -bool lttng_condition_validate(const struct lttng_condition *condition) -{ - bool valid; - - if (!condition) { - valid = false; - goto end; - } - - if (!condition->validate) { - /* Sub-class guarantees that it can never be invalid. */ - valid = true; - goto end; - } - - valid = condition->validate(condition); -end: - return valid; -} - -int lttng_condition_serialize(const struct lttng_condition *condition, - struct lttng_payload *payload) -{ - int ret; - struct lttng_condition_comm condition_comm = {}; - - if (!condition) { - ret = -1; - goto end; - } - - condition_comm.condition_type = (int8_t) condition->type; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm, - sizeof(condition_comm)); - if (ret) { - goto end; - } - - ret = condition->serialize(condition, payload); - if (ret) { - goto end; - } -end: - return ret; -} - -bool lttng_condition_is_equal(const struct lttng_condition *a, - const struct lttng_condition *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a->type != b->type) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - is_equal = a->equal ? a->equal(a, b) : true; -end: - return is_equal; -} - -ssize_t lttng_condition_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **condition) -{ - ssize_t ret, condition_size = 0; - condition_create_from_payload_cb create_from_payload = NULL; - const struct lttng_condition_comm *condition_comm; - const struct lttng_payload_view condition_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*condition_comm)); - - if (!view || !condition) { - ret = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&condition_comm_view)) { - /* Payload not large enough to contain the header. */ - ret = -1; - goto end; - } - - DBG("Deserializing condition from buffer"); - condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; - condition_size += sizeof(*condition_comm); - - switch ((enum lttng_condition_type) condition_comm->condition_type) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - create_from_payload = lttng_condition_buffer_usage_low_create_from_payload; - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - create_from_payload = lttng_condition_buffer_usage_high_create_from_payload; - break; - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - create_from_payload = lttng_condition_session_consumed_size_create_from_payload; - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload; - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - create_from_payload = lttng_condition_session_rotation_completed_create_from_payload; - break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: - create_from_payload = - lttng_condition_event_rule_matches_create_from_payload; - break; - default: - ERR("Attempted to create condition of unknown type (%i)", - (int) condition_comm->condition_type); - ret = -1; - goto end; - } - - if (create_from_payload) { - struct lttng_payload_view condition_view = - lttng_payload_view_from_view(view, - sizeof(*condition_comm), -1); - - ret = create_from_payload(&condition_view, condition); - if (ret < 0) { - goto end; - } - condition_size += ret; - - } else { - abort(); - } - - ret = condition_size; -end: - return ret; -} - -void lttng_condition_init(struct lttng_condition *condition, - enum lttng_condition_type type) -{ - condition->type = type; - urcu_ref_init(&condition->ref); -} - -const char *lttng_condition_type_str(enum lttng_condition_type type) -{ - switch (type) { - case LTTNG_CONDITION_TYPE_UNKNOWN: - return "unknown"; - - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - return "session consumed size"; - - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - return "buffer usage high"; - - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - return "buffer usage low"; - - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - return "session rotation ongoing"; - - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - return "session rotation completed"; - - case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: - return "event rule matches"; - - default: - return "???"; - } -} - -enum lttng_error_code lttng_condition_mi_serialize( - const struct lttng_trigger *trigger, - const struct lttng_condition *condition, - struct mi_writer *writer, - const struct mi_lttng_error_query_callbacks *error_query_callbacks) -{ - int ret; - enum lttng_error_code ret_code; - struct lttng_error_query_results *error_query_results = NULL; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(condition->mi_serialize); - - /* Open condition element. */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_condition); - if (ret) { - goto mi_error; - } - - /* Serialize underlying condition. */ - ret_code = condition->mi_serialize(condition, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Serialize error query results for the action. */ - if (error_query_callbacks && error_query_callbacks->action_cb) { - ret_code = error_query_callbacks->condition_cb( - trigger, &error_query_results); - if (ret_code != LTTNG_OK) { - goto end; - } - - ret_code = lttng_error_query_results_mi_serialize( - error_query_results, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - } - - /* Close condition element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - lttng_error_query_results_destroy(error_query_results); - return ret_code; -} diff --git a/src/common/conditions/condition.cpp b/src/common/conditions/condition.cpp new file mode 100644 index 000000000..18b756dce --- /dev/null +++ b/src/common/conditions/condition.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum lttng_condition_type lttng_condition_get_type( + const struct lttng_condition *condition) +{ + return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN; +} + +void lttng_condition_destroy(struct lttng_condition *condition) +{ + lttng_condition_put(condition); +} + +static void condition_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_condition *condition = + container_of(ref, struct lttng_condition, ref); + + condition->destroy(condition); +} + +void lttng_condition_get(struct lttng_condition *condition) +{ + urcu_ref_get(&condition->ref); +} + +void lttng_condition_put(struct lttng_condition *condition) +{ + if (!condition) { + return; + } + + LTTNG_ASSERT(condition->destroy); + urcu_ref_put(&condition->ref, condition_destroy_ref); +} + + +bool lttng_condition_validate(const struct lttng_condition *condition) +{ + bool valid; + + if (!condition) { + valid = false; + goto end; + } + + if (!condition->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = condition->validate(condition); +end: + return valid; +} + +int lttng_condition_serialize(const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + struct lttng_condition_comm condition_comm = {}; + + if (!condition) { + ret = -1; + goto end; + } + + condition_comm.condition_type = (int8_t) condition->type; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &condition_comm, + sizeof(condition_comm)); + if (ret) { + goto end; + } + + ret = condition->serialize(condition, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +bool lttng_condition_is_equal(const struct lttng_condition *a, + const struct lttng_condition *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +ssize_t lttng_condition_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition) +{ + ssize_t ret, condition_size = 0; + condition_create_from_payload_cb create_from_payload = NULL; + const struct lttng_condition_comm *condition_comm; + const struct lttng_payload_view condition_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*condition_comm)); + + if (!view || !condition) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&condition_comm_view)) { + /* Payload not large enough to contain the header. */ + ret = -1; + goto end; + } + + DBG("Deserializing condition from buffer"); + condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; + condition_size += sizeof(*condition_comm); + + switch ((enum lttng_condition_type) condition_comm->condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + create_from_payload = lttng_condition_buffer_usage_low_create_from_payload; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + create_from_payload = lttng_condition_buffer_usage_high_create_from_payload; + break; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + create_from_payload = lttng_condition_session_consumed_size_create_from_payload; + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + create_from_payload = lttng_condition_session_rotation_ongoing_create_from_payload; + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + create_from_payload = lttng_condition_session_rotation_completed_create_from_payload; + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: + create_from_payload = + lttng_condition_event_rule_matches_create_from_payload; + break; + default: + ERR("Attempted to create condition of unknown type (%i)", + (int) condition_comm->condition_type); + ret = -1; + goto end; + } + + if (create_from_payload) { + struct lttng_payload_view condition_view = + lttng_payload_view_from_view(view, + sizeof(*condition_comm), -1); + + ret = create_from_payload(&condition_view, condition); + if (ret < 0) { + goto end; + } + condition_size += ret; + + } else { + abort(); + } + + ret = condition_size; +end: + return ret; +} + +void lttng_condition_init(struct lttng_condition *condition, + enum lttng_condition_type type) +{ + condition->type = type; + urcu_ref_init(&condition->ref); +} + +const char *lttng_condition_type_str(enum lttng_condition_type type) +{ + switch (type) { + case LTTNG_CONDITION_TYPE_UNKNOWN: + return "unknown"; + + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return "session consumed size"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + return "buffer usage high"; + + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + return "buffer usage low"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + return "session rotation ongoing"; + + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return "session rotation completed"; + + case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: + return "event rule matches"; + + default: + return "???"; + } +} + +enum lttng_error_code lttng_condition_mi_serialize( + const struct lttng_trigger *trigger, + const struct lttng_condition *condition, + struct mi_writer *writer, + const struct mi_lttng_error_query_callbacks *error_query_callbacks) +{ + int ret; + enum lttng_error_code ret_code; + struct lttng_error_query_results *error_query_results = NULL; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(condition->mi_serialize); + + /* Open condition element. */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_condition); + if (ret) { + goto mi_error; + } + + /* Serialize underlying condition. */ + ret_code = condition->mi_serialize(condition, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Serialize error query results for the action. */ + if (error_query_callbacks && error_query_callbacks->action_cb) { + ret_code = error_query_callbacks->condition_cb( + trigger, &error_query_results); + if (ret_code != LTTNG_OK) { + goto end; + } + + ret_code = lttng_error_query_results_mi_serialize( + error_query_results, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + } + + /* Close condition element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + lttng_error_query_results_destroy(error_query_results); + return ret_code; +} diff --git a/src/common/conditions/event-rule-matches.c b/src/common/conditions/event-rule-matches.c deleted file mode 100644 index d91a2c3e7..000000000 --- a/src/common/conditions/event-rule-matches.c +++ /dev/null @@ -1,1544 +0,0 @@ -/* - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_EVENT_RULE_MATCHES_CONDITION(condition) \ - (lttng_condition_get_type(condition) == \ - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES) - -static bool is_event_rule_matches_evaluation( - const struct lttng_evaluation *evaluation) -{ - enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); - - return type == LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES; -} - -static bool lttng_condition_event_rule_matches_validate( - const struct lttng_condition *condition); -static int lttng_condition_event_rule_matches_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload); -static bool lttng_condition_event_rule_matches_is_equal( - const struct lttng_condition *_a, - const struct lttng_condition *_b); -static void lttng_condition_event_rule_matches_destroy( - struct lttng_condition *condition); - -static bool lttng_condition_event_rule_matches_validate( - const struct lttng_condition *condition) -{ - bool valid = false; - struct lttng_condition_event_rule_matches *event_rule; - - if (!condition) { - goto end; - } - - event_rule = container_of(condition, - struct lttng_condition_event_rule_matches, parent); - if (!event_rule->rule) { - ERR("Invalid on event condition: a rule must be set"); - goto end; - } - - valid = lttng_event_rule_validate(event_rule->rule); -end: - return valid; -} - -static const char *msgpack_object_type_str(msgpack_object_type type) -{ - const char *name; - - switch (type) { - case MSGPACK_OBJECT_NIL: - name = "MSGPACK_OBJECT_NIL"; - break; - case MSGPACK_OBJECT_BOOLEAN: - name = "MSGPACK_OBJECT_BOOLEAN"; - break; - case MSGPACK_OBJECT_POSITIVE_INTEGER: - name = "MSGPACK_OBJECT_POSITIVE_INTEGER"; - break; - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - name = "MSGPACK_OBJECT_NEGATIVE_INTEGER"; - break; - case MSGPACK_OBJECT_FLOAT32: - name = "MSGPACK_OBJECT_FLOAT32"; - break; - case MSGPACK_OBJECT_FLOAT: - /* Same value as MSGPACK_OBJECT_FLOAT64 */ - name = "MSGPACK_OBJECT_FLOAT(64)"; - break; - case MSGPACK_OBJECT_STR: - name = "MSGPACK_OBJECT_STR"; - break; - case MSGPACK_OBJECT_ARRAY: - name = "MSGPACK_OBJECT_ARRAY"; - break; - case MSGPACK_OBJECT_MAP: - name = "MSGPACK_OBJECT_MAP"; - break; - case MSGPACK_OBJECT_BIN: - name = "MSGPACK_OBJECT_BIN"; - break; - case MSGPACK_OBJECT_EXT: - name = "MSGPACK_OBJECT_EXT"; - break; - default: - abort(); - } - - return name; -} - -/* - * Serializes the C string `str` into `buf`. - * - * Encoding is the length of `str` plus one (for the null character), - * and then the string, including its null terminator. - */ -static -int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf) -{ - int ret; - const uint32_t len = strlen(str) + 1; - - /* Serialize the length, including the null terminator. */ - DBG("Serializing C string's length (including null terminator): " - "%" PRIu32, len); - ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len)); - if (ret) { - goto end; - } - - /* Serialize the string. */ - DBG("Serializing C string: '%s'", str); - ret = lttng_dynamic_buffer_append(buf, str, len); - if (ret) { - goto end; - } - -end: - return ret; -} - -/* - * Serializes the event expression `expr` into `buf`. - */ -static -int serialize_event_expr(const struct lttng_event_expr *expr, - struct lttng_payload *payload) -{ - const uint8_t type = expr->type; - int ret; - - /* Serialize the expression's type. */ - DBG("Serializing event expression's type: %d", expr->type); - ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type)); - if (ret) { - goto end; - } - - /* Serialize the expression */ - switch (expr->type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - const struct lttng_event_expr_field *field_expr = - container_of(expr, - const struct lttng_event_expr_field, - parent); - - /* Serialize the field name. */ - DBG("Serializing field event expression's field name: '%s'", - field_expr->name); - ret = serialize_cstr(field_expr->name, &payload->buffer); - if (ret) { - goto end; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - const struct lttng_event_expr_app_specific_context_field *field_expr = - container_of(expr, - const struct lttng_event_expr_app_specific_context_field, - parent); - - /* Serialize the provider name. */ - DBG("Serializing app-specific context field event expression's " - "provider name: '%s'", - field_expr->provider_name); - ret = serialize_cstr(field_expr->provider_name, &payload->buffer); - if (ret) { - goto end; - } - - /* Serialize the type name. */ - DBG("Serializing app-specific context field event expression's " - "type name: '%s'", - field_expr->provider_name); - ret = serialize_cstr(field_expr->type_name, &payload->buffer); - if (ret) { - goto end; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - const struct lttng_event_expr_array_field_element *elem_expr = - container_of(expr, - const struct lttng_event_expr_array_field_element, - parent); - const uint32_t index = elem_expr->index; - - /* Serialize the index. */ - DBG("Serializing array field element event expression's " - "index: %u", elem_expr->index); - ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index)); - if (ret) { - goto end; - } - - /* Serialize the parent array field expression. */ - DBG("Serializing array field element event expression's " - "parent array field event expression"); - ret = serialize_event_expr(elem_expr->array_field_expr, payload); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -static struct lttng_capture_descriptor * -lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( - const struct lttng_condition *condition, unsigned int index) -{ - const struct lttng_condition_event_rule_matches - *event_rule_matches_cond = container_of(condition, - const struct lttng_condition_event_rule_matches, - parent); - struct lttng_capture_descriptor *desc = NULL; - unsigned int count; - enum lttng_condition_status status; - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { - goto end; - } - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &count); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto end; - } - - if (index >= count) { - goto end; - } - - desc = lttng_dynamic_pointer_array_get_pointer( - &event_rule_matches_cond->capture_descriptors, index); -end: - return desc; -} - -static int lttng_condition_event_rule_matches_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload) -{ - int ret; - struct lttng_condition_event_rule_matches *event_rule_matches_condition; - enum lttng_condition_status status; - /* Used for iteration and communication (size matters). */ - uint32_t i, capture_descr_count; - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { - ret = -1; - goto end; - } - - DBG("Serializing on event condition"); - event_rule_matches_condition = container_of(condition, - struct lttng_condition_event_rule_matches, parent); - - DBG("Serializing on event condition's event rule"); - ret = lttng_event_rule_serialize( - event_rule_matches_condition->rule, payload); - if (ret) { - goto end; - } - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &capture_descr_count); - if (status != LTTNG_CONDITION_STATUS_OK) { - ret = -1; - goto end; - }; - - DBG("Serializing on event condition's capture descriptor count: %" PRIu32, - capture_descr_count); - ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count, - sizeof(capture_descr_count)); - if (ret) { - goto end; - } - - for (i = 0; i < capture_descr_count; i++) { - const struct lttng_capture_descriptor *desc = - lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( - condition, i); - - DBG("Serializing on event condition's capture descriptor %" PRIu32, - i); - ret = serialize_event_expr(desc->event_expression, payload); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -bool capture_descriptors_are_equal( - const struct lttng_condition *condition_a, - const struct lttng_condition *condition_b) -{ - bool is_equal = true; - unsigned int capture_descr_count_a; - unsigned int capture_descr_count_b; - size_t i; - enum lttng_condition_status status; - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition_a, &capture_descr_count_a); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto not_equal; - } - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition_b, &capture_descr_count_b); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto not_equal; - } - - if (capture_descr_count_a != capture_descr_count_b) { - goto not_equal; - } - - for (i = 0; i < capture_descr_count_a; i++) { - const struct lttng_event_expr *expr_a = - lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - condition_a, i); - const struct lttng_event_expr *expr_b = - lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - condition_b, i); - - if (!lttng_event_expr_is_equal(expr_a, expr_b)) { - goto not_equal; - } - } - - goto end; - -not_equal: - is_equal = false; - -end: - return is_equal; -} - -static bool lttng_condition_event_rule_matches_is_equal( - const struct lttng_condition *_a, - const struct lttng_condition *_b) -{ - bool is_equal = false; - struct lttng_condition_event_rule_matches *a, *b; - - a = container_of(_a, struct lttng_condition_event_rule_matches, parent); - b = container_of(_b, struct lttng_condition_event_rule_matches, parent); - - /* Both event rules must be set or both must be unset. */ - if ((a->rule && !b->rule) || (!a->rule && b->rule)) { - WARN("Comparing event_rule conditions with uninitialized rule"); - goto end; - } - - is_equal = lttng_event_rule_is_equal(a->rule, b->rule); - if (!is_equal) { - goto end; - } - - is_equal = capture_descriptors_are_equal(_a, _b); - -end: - return is_equal; -} - -static void lttng_condition_event_rule_matches_destroy( - struct lttng_condition *condition) -{ - struct lttng_condition_event_rule_matches *event_rule_matches_condition; - - event_rule_matches_condition = container_of(condition, - struct lttng_condition_event_rule_matches, parent); - - lttng_event_rule_put(event_rule_matches_condition->rule); - lttng_dynamic_pointer_array_reset( - &event_rule_matches_condition->capture_descriptors); - free(event_rule_matches_condition); -} - -static -void destroy_capture_descriptor(void *ptr) -{ - struct lttng_capture_descriptor *desc = - (struct lttng_capture_descriptor *) ptr; - - lttng_event_expr_destroy(desc->event_expression); - free(desc->bytecode); - free(desc); -} - -static enum lttng_error_code lttng_condition_event_rule_matches_mi_serialize( - const struct lttng_condition *condition, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_condition_status status; - const struct lttng_event_rule *rule = NULL; - unsigned int capture_descriptor_count, i; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(IS_EVENT_RULE_MATCHES_CONDITION(condition)); - - status = lttng_condition_event_rule_matches_get_rule(condition, &rule); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - LTTNG_ASSERT(rule != NULL); - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &capture_descriptor_count); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - - /* Open condition event rule matches element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_condition_event_rule_matches); - if (ret) { - goto mi_error; - } - - /* Serialize the event rule. */ - ret_code = lttng_event_rule_mi_serialize(rule, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Open the capture descriptors element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_capture_descriptors); - if (ret) { - goto mi_error; - } - - for (i = 0; i < capture_descriptor_count; i++) { - const struct lttng_event_expr *descriptor = NULL; - - descriptor = lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - condition, i); - LTTNG_ASSERT(descriptor); - - ret_code = lttng_event_expr_mi_serialize(descriptor, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - } - - /* Close capture descriptors element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - /* Close condition_event_rule_matches. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_condition *lttng_condition_event_rule_matches_create( - struct lttng_event_rule *rule) -{ - struct lttng_condition *parent = NULL; - struct lttng_condition_event_rule_matches *condition = NULL; - - if (!rule) { - goto end; - } - - condition = zmalloc(sizeof(struct lttng_condition_event_rule_matches)); - if (!condition) { - return NULL; - } - - lttng_condition_init(&condition->parent, - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); - condition->parent.validate = - lttng_condition_event_rule_matches_validate, - condition->parent.serialize = - lttng_condition_event_rule_matches_serialize, - condition->parent.equal = lttng_condition_event_rule_matches_is_equal, - condition->parent.destroy = lttng_condition_event_rule_matches_destroy, - condition->parent.mi_serialize = lttng_condition_event_rule_matches_mi_serialize, - - lttng_event_rule_get(rule); - condition->rule = rule; - rule = NULL; - - lttng_dynamic_pointer_array_init(&condition->capture_descriptors, - destroy_capture_descriptor); - - parent = &condition->parent; -end: - return parent; -} - -static -uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size, - size_t *offset) -{ - uint64_t ret; - const struct lttng_buffer_view uint_view = - lttng_buffer_view_from_view(view, *offset, size); - - if (!lttng_buffer_view_is_valid(&uint_view)) { - ret = UINT64_C(-1); - goto end; - } - - switch (size) { - case 1: - ret = (uint64_t) *uint_view.data; - break; - case sizeof(uint32_t): - { - uint32_t u32; - - memcpy(&u32, uint_view.data, sizeof(u32)); - ret = (uint64_t) u32; - break; - } - case sizeof(ret): - memcpy(&ret, uint_view.data, sizeof(ret)); - break; - default: - abort(); - } - - *offset += size; - -end: - return ret; -} - -static -const char *str_from_buffer(const struct lttng_buffer_view *view, - size_t *offset) -{ - uint64_t len; - const char *ret; - - len = uint_from_buffer(view, sizeof(uint32_t), offset); - if (len == UINT64_C(-1)) { - goto error; - } - - ret = &view->data[*offset]; - - if (!lttng_buffer_view_contains_string(view, ret, len)) { - goto error; - } - - *offset += len; - goto end; - -error: - ret = NULL; - -end: - return ret; -} - -static -struct lttng_event_expr *event_expr_from_payload( - struct lttng_payload_view *view, size_t *offset) -{ - struct lttng_event_expr *expr = NULL; - const char *str; - uint64_t type; - - type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset); - if (type == UINT64_C(-1)) { - goto error; - } - - switch (type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - str = str_from_buffer(&view->buffer, offset); - if (!str) { - goto error; - } - - expr = lttng_event_expr_event_payload_field_create(str); - break; - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - str = str_from_buffer(&view->buffer, offset); - if (!str) { - goto error; - } - - expr = lttng_event_expr_channel_context_field_create(str); - break; - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - const char *provider_name; - const char *type_name; - - provider_name = str_from_buffer(&view->buffer, offset); - if (!provider_name) { - goto error; - } - - type_name = str_from_buffer(&view->buffer, offset); - if (!type_name) { - goto error; - } - - expr = lttng_event_expr_app_specific_context_field_create( - provider_name, type_name); - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - struct lttng_event_expr *array_field_expr; - const uint64_t index = uint_from_buffer( - &view->buffer, sizeof(uint32_t), offset); - - if (index == UINT64_C(-1)) { - goto error; - } - - /* Array field expression is the encoded after this. */ - array_field_expr = event_expr_from_payload(view, offset); - if (!array_field_expr) { - goto error; - } - - /* Move ownership of `array_field_expr` to new expression. */ - expr = lttng_event_expr_array_field_element_create( - array_field_expr, (unsigned int) index); - if (!expr) { - /* `array_field_expr` not moved: destroy it. */ - lttng_event_expr_destroy(array_field_expr); - } - - break; - } - default: - ERR("Invalid event expression type encoutered while deserializing event expression: type = %" PRIu64, - type); - goto error; - } - - goto end; - -error: - lttng_event_expr_destroy(expr); - expr = NULL; - -end: - return expr; -} - -ssize_t lttng_condition_event_rule_matches_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **_condition) -{ - ssize_t consumed_length; - size_t offset = 0; - ssize_t event_rule_length; - uint32_t i, capture_descr_count; - struct lttng_condition *condition = NULL; - struct lttng_event_rule *event_rule = NULL; - - if (!view || !_condition) { - goto error; - } - - /* Struct lttng_event_rule. */ - { - struct lttng_payload_view event_rule_view = - lttng_payload_view_from_view(view, offset, -1); - - event_rule_length = lttng_event_rule_create_from_payload( - &event_rule_view, &event_rule); - } - - if (event_rule_length < 0 || !event_rule) { - goto error; - } - - offset += event_rule_length; - - /* Create condition (no capture descriptors yet) at this point */ - condition = lttng_condition_event_rule_matches_create(event_rule); - if (!condition) { - goto error; - } - - /* Capture descriptor count. */ - LTTNG_ASSERT(event_rule_length >= 0); - capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset); - if (capture_descr_count == UINT32_C(-1)) { - goto error; - } - - /* Capture descriptors. */ - for (i = 0; i < capture_descr_count; i++) { - enum lttng_condition_status status; - struct lttng_event_expr *expr = event_expr_from_payload( - view, &offset); - - if (!expr) { - goto error; - } - - /* Move ownership of `expr` to `condition`. */ - status = lttng_condition_event_rule_matches_append_capture_descriptor( - condition, expr); - if (status != LTTNG_CONDITION_STATUS_OK) { - /* `expr` not moved: destroy it. */ - lttng_event_expr_destroy(expr); - goto error; - } - } - - consumed_length = (ssize_t) offset; - *_condition = condition; - condition = NULL; - goto end; - -error: - consumed_length = -1; - -end: - lttng_event_rule_put(event_rule); - lttng_condition_put(condition); - return consumed_length; -} - -enum lttng_condition_status -lttng_condition_event_rule_matches_borrow_rule_mutable( - const struct lttng_condition *condition, - struct lttng_event_rule **rule) -{ - struct lttng_condition_event_rule_matches *event_rule; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || - !rule) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - event_rule = container_of(condition, - struct lttng_condition_event_rule_matches, parent); - if (!event_rule->rule) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - - *rule = event_rule->rule; -end: - return status; -} - -enum lttng_condition_status lttng_condition_event_rule_matches_get_rule( - const struct lttng_condition *condition, - const struct lttng_event_rule **rule) -{ - struct lttng_event_rule *mutable_rule = NULL; - const enum lttng_condition_status status = - lttng_condition_event_rule_matches_borrow_rule_mutable( - condition, &mutable_rule); - - *rule = mutable_rule; - return status; -} - -void lttng_condition_event_rule_matches_set_error_counter_index( - struct lttng_condition *condition, uint64_t error_counter_index) -{ - struct lttng_condition_event_rule_matches *event_rule_matches_cond = - container_of(condition, - struct lttng_condition_event_rule_matches, - parent); - - LTTNG_OPTIONAL_SET(&event_rule_matches_cond->error_counter_index, - error_counter_index); -} - -uint64_t lttng_condition_event_rule_matches_get_error_counter_index( - const struct lttng_condition *condition) -{ - const struct lttng_condition_event_rule_matches - *event_rule_matches_cond = container_of(condition, - const struct lttng_condition_event_rule_matches, - parent); - - return LTTNG_OPTIONAL_GET(event_rule_matches_cond->error_counter_index); -} - -enum lttng_condition_status -lttng_condition_event_rule_matches_append_capture_descriptor( - struct lttng_condition *condition, - struct lttng_event_expr *expr) -{ - int ret; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - struct lttng_condition_event_rule_matches *event_rule_matches_cond = - container_of(condition, - struct lttng_condition_event_rule_matches, - parent); - struct lttng_capture_descriptor *descriptor = NULL; - const struct lttng_event_rule *rule = NULL; - - /* Only accept l-values. */ - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || - !expr || !lttng_event_expr_is_lvalue(expr)) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - status = lttng_condition_event_rule_matches_get_rule(condition, &rule); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto end; - } - - switch(lttng_event_rule_get_type(rule)) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - /* Supported. */ - status = LTTNG_CONDITION_STATUS_OK; - break; - case LTTNG_EVENT_RULE_TYPE_UNKNOWN: - status = LTTNG_CONDITION_STATUS_INVALID; - break; - default: - status = LTTNG_CONDITION_STATUS_UNSUPPORTED; - break; - } - - if (status != LTTNG_CONDITION_STATUS_OK) { - goto end; - } - - descriptor = malloc(sizeof(*descriptor)); - if (descriptor == NULL) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - descriptor->event_expression = expr; - descriptor->bytecode = NULL; - - ret = lttng_dynamic_pointer_array_add_pointer( - &event_rule_matches_cond->capture_descriptors, - descriptor); - if (ret) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - /* Ownership is transfered to the internal capture_descriptors array */ - descriptor = NULL; -end: - free(descriptor); - return status; -} - -enum lttng_condition_status -lttng_condition_event_rule_matches_get_capture_descriptor_count( - const struct lttng_condition *condition, unsigned int *count) -{ - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - const struct lttng_condition_event_rule_matches - *event_rule_matches_condition = container_of(condition, - const struct lttng_condition_event_rule_matches, - parent); - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || - !count) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - *count = lttng_dynamic_pointer_array_get_count( - &event_rule_matches_condition->capture_descriptors); - -end: - return status; -} - -const struct lttng_event_expr * -lttng_condition_event_rule_matches_get_capture_descriptor_at_index( - const struct lttng_condition *condition, unsigned int index) -{ - const struct lttng_event_expr *expr = NULL; - const struct lttng_capture_descriptor *desc = NULL; - - desc = lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( - condition, index); - if (desc == NULL) { - goto end; - } - expr = desc->event_expression; - -end: - return expr; -} - -ssize_t lttng_evaluation_event_rule_matches_create_from_payload( - const struct lttng_condition_event_rule_matches *condition, - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret, offset = 0; - struct lttng_evaluation *evaluation = NULL; - uint32_t capture_payload_size; - const char *capture_payload = NULL; - - if (!_evaluation) { - ret = -1; - goto error; - } - - { - const struct lttng_payload_view current_view = - lttng_payload_view_from_view(view, offset, -1); - - if (current_view.buffer.size < sizeof(capture_payload_size)) { - ret = -1; - goto error; - } - - memcpy(&capture_payload_size, current_view.buffer.data, - sizeof(capture_payload_size)); - } - offset += sizeof(capture_payload_size); - - if (capture_payload_size > 0) { - const struct lttng_payload_view current_view = - lttng_payload_view_from_view(view, offset, -1); - - if (current_view.buffer.size < capture_payload_size) { - ret = -1; - goto error; - } - - capture_payload = current_view.buffer.data; - } - - evaluation = lttng_evaluation_event_rule_matches_create( - condition, capture_payload, capture_payload_size, true); - if (!evaluation) { - ret = -1; - goto error; - } - - offset += capture_payload_size; - *_evaluation = evaluation; - evaluation = NULL; - ret = offset; - -error: - lttng_evaluation_destroy(evaluation); - return ret; -} - -static int lttng_evaluation_event_rule_matches_serialize( - const struct lttng_evaluation *evaluation, - struct lttng_payload *payload) -{ - int ret = 0; - struct lttng_evaluation_event_rule_matches *hit; - uint32_t capture_payload_size; - - hit = container_of(evaluation, - struct lttng_evaluation_event_rule_matches, parent); - - capture_payload_size = (uint32_t) hit->capture_payload.size; - ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size, - sizeof(capture_payload_size)); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data, - hit->capture_payload.size); - if (ret) { - goto end; - } - -end: - return ret; -} - -static -bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str) -{ - bool is_equal = true; - - LTTNG_ASSERT(obj->type == MSGPACK_OBJECT_STR); - - if (obj->via.str.size != strlen(str)) { - is_equal = false; - goto end; - } - - if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) { - is_equal = false; - goto end; - } - -end: - return is_equal; -} - -static -const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj, - const char *name) -{ - const msgpack_object *ret = NULL; - size_t i; - - LTTNG_ASSERT(map_obj->type == MSGPACK_OBJECT_MAP); - - for (i = 0; i < map_obj->via.map.size; i++) { - const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i]; - - LTTNG_ASSERT(kv->key.type == MSGPACK_OBJECT_STR); - - if (msgpack_str_is_equal(&kv->key, name)) { - ret = &kv->val; - goto end; - } - } - -end: - return ret; -} - -static void lttng_evaluation_event_rule_matches_destroy( - struct lttng_evaluation *evaluation) -{ - struct lttng_evaluation_event_rule_matches *hit; - - hit = container_of(evaluation, - struct lttng_evaluation_event_rule_matches, parent); - lttng_dynamic_buffer_reset(&hit->capture_payload); - lttng_event_field_value_destroy(hit->captured_values); - free(hit); -} - -static -int event_field_value_from_obj(const msgpack_object *obj, - struct lttng_event_field_value **field_val) -{ - int ret = 0; - - LTTNG_ASSERT(obj); - LTTNG_ASSERT(field_val); - - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - /* Unavailable. */ - *field_val = NULL; - goto end; - case MSGPACK_OBJECT_POSITIVE_INTEGER: - *field_val = lttng_event_field_value_uint_create( - obj->via.u64); - break; - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - *field_val = lttng_event_field_value_int_create( - obj->via.i64); - break; - case MSGPACK_OBJECT_FLOAT32: - case MSGPACK_OBJECT_FLOAT64: - *field_val = lttng_event_field_value_real_create( - obj->via.f64); - break; - case MSGPACK_OBJECT_STR: - *field_val = lttng_event_field_value_string_create_with_size( - obj->via.str.ptr, obj->via.str.size); - break; - case MSGPACK_OBJECT_ARRAY: - { - size_t i; - - *field_val = lttng_event_field_value_array_create(); - if (!*field_val) { - goto error; - } - - for (i = 0; i < obj->via.array.size; i++) { - const msgpack_object *elem_obj = &obj->via.array.ptr[i]; - struct lttng_event_field_value *elem_field_val; - - ret = event_field_value_from_obj(elem_obj, - &elem_field_val); - if (ret) { - goto error; - } - - if (elem_field_val) { - ret = lttng_event_field_value_array_append( - *field_val, elem_field_val); - } else { - ret = lttng_event_field_value_array_append_unavailable( - *field_val); - } - - if (ret) { - lttng_event_field_value_destroy(elem_field_val); - goto error; - } - } - - break; - } - case MSGPACK_OBJECT_MAP: - { - /* - * As of this version, the only valid map object is - * for an enumeration value, for example: - * - * type: enum - * value: 177 - * labels: - * - Labatt 50 - * - Molson Dry - * - Carling Black Label - */ - const msgpack_object *inner_obj; - size_t label_i; - - inner_obj = get_msgpack_map_obj(obj, "type"); - if (!inner_obj) { - ERR("Missing `type` entry in map object"); - goto error; - } - - if (inner_obj->type != MSGPACK_OBJECT_STR) { - ERR("Map object's `type` entry is not a string: type = %s", - msgpack_object_type_str(inner_obj->type)); - goto error; - } - - if (!msgpack_str_is_equal(inner_obj, "enum")) { - ERR("Map object's `type` entry: expecting `enum`"); - goto error; - } - - inner_obj = get_msgpack_map_obj(obj, "value"); - if (!inner_obj) { - ERR("Missing `value` entry in map object"); - goto error; - } - - if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - *field_val = lttng_event_field_value_enum_uint_create( - inner_obj->via.u64); - } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { - *field_val = lttng_event_field_value_enum_int_create( - inner_obj->via.i64); - } else { - ERR("Map object's `value` entry is not an integer: type = %s", - msgpack_object_type_str(inner_obj->type)); - goto error; - } - - if (!*field_val) { - goto error; - } - - inner_obj = get_msgpack_map_obj(obj, "labels"); - if (!inner_obj) { - /* No labels */ - goto end; - } - - if (inner_obj->type != MSGPACK_OBJECT_ARRAY) { - ERR("Map object's `labels` entry is not an array: type = %s", - msgpack_object_type_str(inner_obj->type)); - goto error; - } - - for (label_i = 0; label_i < inner_obj->via.array.size; - label_i++) { - int iret; - const msgpack_object *elem_obj = - &inner_obj->via.array.ptr[label_i]; - - if (elem_obj->type != MSGPACK_OBJECT_STR) { - ERR("Map object's `labels` entry's type is not a string: type = %s", - msgpack_object_type_str(elem_obj->type)); - goto error; - } - - iret = lttng_event_field_value_enum_append_label_with_size( - *field_val, elem_obj->via.str.ptr, - elem_obj->via.str.size); - if (iret) { - goto error; - } - } - - break; - } - default: - ERR("Unexpected object type: type = %s", - msgpack_object_type_str(obj->type)); - goto error; - } - - if (!*field_val) { - goto error; - } - - goto end; - -error: - lttng_event_field_value_destroy(*field_val); - *field_val = NULL; - ret = -1; - -end: - return ret; -} - -static struct lttng_event_field_value *event_field_value_from_capture_payload( - const struct lttng_condition_event_rule_matches *condition, - const char *capture_payload, - size_t capture_payload_size) -{ - struct lttng_event_field_value *ret = NULL; - msgpack_unpacked unpacked; - msgpack_unpack_return unpack_return; - const msgpack_object *root_obj; - const msgpack_object_array *root_array_obj; - size_t i; - size_t count; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(capture_payload); - - /* Initialize value. */ - msgpack_unpacked_init(&unpacked); - - /* Decode. */ - unpack_return = msgpack_unpack_next(&unpacked, capture_payload, - capture_payload_size, NULL); - if (unpack_return != MSGPACK_UNPACK_SUCCESS) { - ERR("msgpack_unpack_next() failed to decode the " - "MessagePack-encoded capture payload: " - "size = %zu, ret = %d", - capture_payload_size, unpack_return); - goto error; - } - - /* Get root array. */ - root_obj = &unpacked.data; - - if (root_obj->type != MSGPACK_OBJECT_ARRAY) { - ERR("Expecting an array as the root object: type = %s", - msgpack_object_type_str(root_obj->type)); - goto error; - } - - root_array_obj = &root_obj->via.array; - - /* Create an empty root array event field value. */ - ret = lttng_event_field_value_array_create(); - if (!ret) { - goto error; - } - - /* - * For each capture descriptor in the condition object: - * - * 1. Get its corresponding captured field value MessagePack - * object. - * - * 2. Create a corresponding event field value. - * - * 3. Append it to `ret` (the root array event field value). - */ - count = lttng_dynamic_pointer_array_get_count( - &condition->capture_descriptors); - LTTNG_ASSERT(count > 0); - - for (i = 0; i < count; i++) { - const struct lttng_capture_descriptor *capture_descriptor = - lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( - &condition->parent, i); - const msgpack_object *elem_obj; - struct lttng_event_field_value *elem_field_val; - int iret; - - LTTNG_ASSERT(capture_descriptor); - - elem_obj = &root_array_obj->ptr[i]; - iret = event_field_value_from_obj(elem_obj, - &elem_field_val); - if (iret) { - goto error; - } - - if (elem_field_val) { - iret = lttng_event_field_value_array_append(ret, - elem_field_val); - } else { - iret = lttng_event_field_value_array_append_unavailable( - ret); - } - - if (iret) { - lttng_event_field_value_destroy(elem_field_val); - goto error; - } - } - - goto end; - -error: - lttng_event_field_value_destroy(ret); - ret = NULL; - -end: - msgpack_unpacked_destroy(&unpacked); - return ret; -} - -struct lttng_evaluation *lttng_evaluation_event_rule_matches_create( - const struct lttng_condition_event_rule_matches *condition, - const char *capture_payload, - size_t capture_payload_size, - bool decode_capture_payload) -{ - struct lttng_evaluation_event_rule_matches *hit; - struct lttng_evaluation *evaluation = NULL; - - hit = zmalloc(sizeof(struct lttng_evaluation_event_rule_matches)); - if (!hit) { - goto error; - } - - lttng_dynamic_buffer_init(&hit->capture_payload); - - if (capture_payload) { - const int ret = lttng_dynamic_buffer_append( - &hit->capture_payload, capture_payload, - capture_payload_size); - if (ret) { - ERR("Failed to initialize capture payload of event rule evaluation"); - goto error; - } - - if (decode_capture_payload) { - hit->captured_values = - event_field_value_from_capture_payload( - condition, - capture_payload, - capture_payload_size); - if (!hit->captured_values) { - ERR("Failed to decode the capture payload: size = %zu", - capture_payload_size); - goto error; - } - } - } - - hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES; - hit->parent.serialize = lttng_evaluation_event_rule_matches_serialize; - hit->parent.destroy = lttng_evaluation_event_rule_matches_destroy; - - evaluation = &hit->parent; - hit = NULL; - -error: - if (hit) { - lttng_evaluation_event_rule_matches_destroy(&hit->parent); - } - - return evaluation; -} - -enum lttng_evaluation_event_rule_matches_status -lttng_evaluation_event_rule_matches_get_captured_values( - const struct lttng_evaluation *evaluation, - const struct lttng_event_field_value **field_val) -{ - struct lttng_evaluation_event_rule_matches *hit; - enum lttng_evaluation_event_rule_matches_status status = - LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK; - - if (!evaluation || !is_event_rule_matches_evaluation(evaluation) || - !field_val) { - status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_INVALID; - goto end; - } - - hit = container_of(evaluation, - struct lttng_evaluation_event_rule_matches, parent); - if (!hit->captured_values) { - status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_NONE; - goto end; - } - - *field_val = hit->captured_values; - -end: - return status; -} - -enum lttng_error_code -lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode( - struct lttng_condition *condition) -{ - enum lttng_error_code ret; - enum lttng_condition_status status; - unsigned int capture_count, i; - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { - ret = LTTNG_ERR_FATAL; - goto end; - } - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &capture_count); - if (status != LTTNG_CONDITION_STATUS_OK) { - ret = LTTNG_ERR_FATAL; - goto end; - } - - for (i = 0; i < capture_count; i++) { - struct lttng_capture_descriptor *local_capture_desc = - lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( - condition, i); - - if (local_capture_desc == NULL) { - ret = LTTNG_ERR_FATAL; - goto end; - } - - /* Generate the bytecode. */ - status = lttng_event_expr_to_bytecode( - local_capture_desc->event_expression, - &local_capture_desc->bytecode); - if (status < 0 || local_capture_desc->bytecode == NULL) { - ret = LTTNG_ERR_INVALID_CAPTURE_EXPRESSION; - goto end; - } - } - - /* Everything went better than expected */ - ret = LTTNG_OK; - -end: - return ret; -} - -const struct lttng_bytecode * -lttng_condition_event_rule_matches_get_capture_bytecode_at_index( - const struct lttng_condition *condition, unsigned int index) -{ - const struct lttng_condition_event_rule_matches - *event_rule_matches_cond = container_of(condition, - const struct lttng_condition_event_rule_matches, - parent); - struct lttng_capture_descriptor *desc = NULL; - struct lttng_bytecode *bytecode = NULL; - unsigned int count; - enum lttng_condition_status status; - - if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { - goto end; - } - - status = lttng_condition_event_rule_matches_get_capture_descriptor_count( - condition, &count); - if (status != LTTNG_CONDITION_STATUS_OK) { - goto end; - } - - if (index >= count) { - goto end; - } - - desc = lttng_dynamic_pointer_array_get_pointer( - &event_rule_matches_cond->capture_descriptors, index); - if (desc == NULL) { - goto end; - } - - bytecode = desc->bytecode; -end: - return bytecode; -} diff --git a/src/common/conditions/event-rule-matches.cpp b/src/common/conditions/event-rule-matches.cpp new file mode 100644 index 000000000..3e7776f46 --- /dev/null +++ b/src/common/conditions/event-rule-matches.cpp @@ -0,0 +1,1545 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_EVENT_RULE_MATCHES_CONDITION(condition) \ + (lttng_condition_get_type(condition) == \ + LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES) + +static bool is_event_rule_matches_evaluation( + const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES; +} + +static bool lttng_condition_event_rule_matches_validate( + const struct lttng_condition *condition); +static int lttng_condition_event_rule_matches_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload); +static bool lttng_condition_event_rule_matches_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b); +static void lttng_condition_event_rule_matches_destroy( + struct lttng_condition *condition); + +static bool lttng_condition_event_rule_matches_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_event_rule_matches *event_rule; + + if (!condition) { + goto end; + } + + event_rule = container_of(condition, + struct lttng_condition_event_rule_matches, parent); + if (!event_rule->rule) { + ERR("Invalid on event condition: a rule must be set"); + goto end; + } + + valid = lttng_event_rule_validate(event_rule->rule); +end: + return valid; +} + +static const char *msgpack_object_type_str(msgpack_object_type type) +{ + const char *name; + + switch (type) { + case MSGPACK_OBJECT_NIL: + name = "MSGPACK_OBJECT_NIL"; + break; + case MSGPACK_OBJECT_BOOLEAN: + name = "MSGPACK_OBJECT_BOOLEAN"; + break; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + name = "MSGPACK_OBJECT_POSITIVE_INTEGER"; + break; + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + name = "MSGPACK_OBJECT_NEGATIVE_INTEGER"; + break; + case MSGPACK_OBJECT_FLOAT32: + name = "MSGPACK_OBJECT_FLOAT32"; + break; + case MSGPACK_OBJECT_FLOAT: + /* Same value as MSGPACK_OBJECT_FLOAT64 */ + name = "MSGPACK_OBJECT_FLOAT(64)"; + break; + case MSGPACK_OBJECT_STR: + name = "MSGPACK_OBJECT_STR"; + break; + case MSGPACK_OBJECT_ARRAY: + name = "MSGPACK_OBJECT_ARRAY"; + break; + case MSGPACK_OBJECT_MAP: + name = "MSGPACK_OBJECT_MAP"; + break; + case MSGPACK_OBJECT_BIN: + name = "MSGPACK_OBJECT_BIN"; + break; + case MSGPACK_OBJECT_EXT: + name = "MSGPACK_OBJECT_EXT"; + break; + default: + abort(); + } + + return name; +} + +/* + * Serializes the C string `str` into `buf`. + * + * Encoding is the length of `str` plus one (for the null character), + * and then the string, including its null terminator. + */ +static +int serialize_cstr(const char *str, struct lttng_dynamic_buffer *buf) +{ + int ret; + const uint32_t len = strlen(str) + 1; + + /* Serialize the length, including the null terminator. */ + DBG("Serializing C string's length (including null terminator): " + "%" PRIu32, len); + ret = lttng_dynamic_buffer_append(buf, &len, sizeof(len)); + if (ret) { + goto end; + } + + /* Serialize the string. */ + DBG("Serializing C string: '%s'", str); + ret = lttng_dynamic_buffer_append(buf, str, len); + if (ret) { + goto end; + } + +end: + return ret; +} + +/* + * Serializes the event expression `expr` into `buf`. + */ +static +int serialize_event_expr(const struct lttng_event_expr *expr, + struct lttng_payload *payload) +{ + const uint8_t type = expr->type; + int ret; + + /* Serialize the expression's type. */ + DBG("Serializing event expression's type: %d", expr->type); + ret = lttng_dynamic_buffer_append(&payload->buffer, &type, sizeof(type)); + if (ret) { + goto end; + } + + /* Serialize the expression */ + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const struct lttng_event_expr_field *field_expr = + container_of(expr, + const struct lttng_event_expr_field, + parent); + + /* Serialize the field name. */ + DBG("Serializing field event expression's field name: '%s'", + field_expr->name); + ret = serialize_cstr(field_expr->name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent); + + /* Serialize the provider name. */ + DBG("Serializing app-specific context field event expression's " + "provider name: '%s'", + field_expr->provider_name); + ret = serialize_cstr(field_expr->provider_name, &payload->buffer); + if (ret) { + goto end; + } + + /* Serialize the type name. */ + DBG("Serializing app-specific context field event expression's " + "type name: '%s'", + field_expr->provider_name); + ret = serialize_cstr(field_expr->type_name, &payload->buffer); + if (ret) { + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + const struct lttng_event_expr_array_field_element, + parent); + const uint32_t index = elem_expr->index; + + /* Serialize the index. */ + DBG("Serializing array field element event expression's " + "index: %u", elem_expr->index); + ret = lttng_dynamic_buffer_append(&payload->buffer, &index, sizeof(index)); + if (ret) { + goto end; + } + + /* Serialize the parent array field expression. */ + DBG("Serializing array field element event expression's " + "parent array field event expression"); + ret = serialize_event_expr(elem_expr->array_field_expr, payload); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +static struct lttng_capture_descriptor * +lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_condition_event_rule_matches + *event_rule_matches_cond = container_of(condition, + const struct lttng_condition_event_rule_matches, + parent); + struct lttng_capture_descriptor *desc = NULL; + unsigned int count; + enum lttng_condition_status status; + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { + goto end; + } + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &count); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + desc = (lttng_capture_descriptor *) lttng_dynamic_pointer_array_get_pointer( + &event_rule_matches_cond->capture_descriptors, index); +end: + return desc; +} + +static int lttng_condition_event_rule_matches_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + struct lttng_condition_event_rule_matches *event_rule_matches_condition; + enum lttng_condition_status status; + /* Used for iteration and communication (size matters). */ + uint32_t i, capture_descr_count; + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing on event condition"); + event_rule_matches_condition = container_of(condition, + struct lttng_condition_event_rule_matches, parent); + + DBG("Serializing on event condition's event rule"); + ret = lttng_event_rule_serialize( + event_rule_matches_condition->rule, payload); + if (ret) { + goto end; + } + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &capture_descr_count); + if (status != LTTNG_CONDITION_STATUS_OK) { + ret = -1; + goto end; + }; + + DBG("Serializing on event condition's capture descriptor count: %" PRIu32, + capture_descr_count); + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_descr_count, + sizeof(capture_descr_count)); + if (ret) { + goto end; + } + + for (i = 0; i < capture_descr_count; i++) { + const struct lttng_capture_descriptor *desc = + lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( + condition, i); + + DBG("Serializing on event condition's capture descriptor %" PRIu32, + i); + ret = serialize_event_expr(desc->event_expression, payload); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +bool capture_descriptors_are_equal( + const struct lttng_condition *condition_a, + const struct lttng_condition *condition_b) +{ + bool is_equal = true; + unsigned int capture_descr_count_a; + unsigned int capture_descr_count_b; + size_t i; + enum lttng_condition_status status; + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition_a, &capture_descr_count_a); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto not_equal; + } + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition_b, &capture_descr_count_b); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto not_equal; + } + + if (capture_descr_count_a != capture_descr_count_b) { + goto not_equal; + } + + for (i = 0; i < capture_descr_count_a; i++) { + const struct lttng_event_expr *expr_a = + lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + condition_a, i); + const struct lttng_event_expr *expr_b = + lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + condition_b, i); + + if (!lttng_event_expr_is_equal(expr_a, expr_b)) { + goto not_equal; + } + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + +static bool lttng_condition_event_rule_matches_is_equal( + const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_event_rule_matches *a, *b; + + a = container_of(_a, struct lttng_condition_event_rule_matches, parent); + b = container_of(_b, struct lttng_condition_event_rule_matches, parent); + + /* Both event rules must be set or both must be unset. */ + if ((a->rule && !b->rule) || (!a->rule && b->rule)) { + WARN("Comparing event_rule conditions with uninitialized rule"); + goto end; + } + + is_equal = lttng_event_rule_is_equal(a->rule, b->rule); + if (!is_equal) { + goto end; + } + + is_equal = capture_descriptors_are_equal(_a, _b); + +end: + return is_equal; +} + +static void lttng_condition_event_rule_matches_destroy( + struct lttng_condition *condition) +{ + struct lttng_condition_event_rule_matches *event_rule_matches_condition; + + event_rule_matches_condition = container_of(condition, + struct lttng_condition_event_rule_matches, parent); + + lttng_event_rule_put(event_rule_matches_condition->rule); + lttng_dynamic_pointer_array_reset( + &event_rule_matches_condition->capture_descriptors); + free(event_rule_matches_condition); +} + +static +void destroy_capture_descriptor(void *ptr) +{ + struct lttng_capture_descriptor *desc = + (struct lttng_capture_descriptor *) ptr; + + lttng_event_expr_destroy(desc->event_expression); + free(desc->bytecode); + free(desc); +} + +static enum lttng_error_code lttng_condition_event_rule_matches_mi_serialize( + const struct lttng_condition *condition, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_condition_status status; + const struct lttng_event_rule *rule = NULL; + unsigned int capture_descriptor_count, i; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(IS_EVENT_RULE_MATCHES_CONDITION(condition)); + + status = lttng_condition_event_rule_matches_get_rule(condition, &rule); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(rule != NULL); + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &capture_descriptor_count); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + + /* Open condition event rule matches element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_condition_event_rule_matches); + if (ret) { + goto mi_error; + } + + /* Serialize the event rule. */ + ret_code = lttng_event_rule_mi_serialize(rule, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Open the capture descriptors element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_capture_descriptors); + if (ret) { + goto mi_error; + } + + for (i = 0; i < capture_descriptor_count; i++) { + const struct lttng_event_expr *descriptor = NULL; + + descriptor = lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + condition, i); + LTTNG_ASSERT(descriptor); + + ret_code = lttng_event_expr_mi_serialize(descriptor, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + } + + /* Close capture descriptors element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + /* Close condition_event_rule_matches. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_condition *lttng_condition_event_rule_matches_create( + struct lttng_event_rule *rule) +{ + struct lttng_condition *parent = NULL; + struct lttng_condition_event_rule_matches *condition = NULL; + + if (!rule) { + goto end; + } + + condition = (lttng_condition_event_rule_matches *) zmalloc(sizeof(struct lttng_condition_event_rule_matches)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, + LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); + condition->parent.validate = + lttng_condition_event_rule_matches_validate, + condition->parent.serialize = + lttng_condition_event_rule_matches_serialize, + condition->parent.equal = lttng_condition_event_rule_matches_is_equal, + condition->parent.destroy = lttng_condition_event_rule_matches_destroy, + condition->parent.mi_serialize = lttng_condition_event_rule_matches_mi_serialize, + + lttng_event_rule_get(rule); + condition->rule = rule; + rule = NULL; + + lttng_dynamic_pointer_array_init(&condition->capture_descriptors, + destroy_capture_descriptor); + + parent = &condition->parent; +end: + return parent; +} + +static +uint64_t uint_from_buffer(const struct lttng_buffer_view *view, size_t size, + size_t *offset) +{ + uint64_t ret; + const struct lttng_buffer_view uint_view = + lttng_buffer_view_from_view(view, *offset, size); + + if (!lttng_buffer_view_is_valid(&uint_view)) { + ret = UINT64_C(-1); + goto end; + } + + switch (size) { + case 1: + ret = (uint64_t) *uint_view.data; + break; + case sizeof(uint32_t): + { + uint32_t u32; + + memcpy(&u32, uint_view.data, sizeof(u32)); + ret = (uint64_t) u32; + break; + } + case sizeof(ret): + memcpy(&ret, uint_view.data, sizeof(ret)); + break; + default: + abort(); + } + + *offset += size; + +end: + return ret; +} + +static +const char *str_from_buffer(const struct lttng_buffer_view *view, + size_t *offset) +{ + uint64_t len; + const char *ret; + + len = uint_from_buffer(view, sizeof(uint32_t), offset); + if (len == UINT64_C(-1)) { + goto error; + } + + ret = &view->data[*offset]; + + if (!lttng_buffer_view_contains_string(view, ret, len)) { + goto error; + } + + *offset += len; + goto end; + +error: + ret = NULL; + +end: + return ret; +} + +static +struct lttng_event_expr *event_expr_from_payload( + struct lttng_payload_view *view, size_t *offset) +{ + struct lttng_event_expr *expr = NULL; + const char *str; + uint64_t type; + + type = uint_from_buffer(&view->buffer, sizeof(uint8_t), offset); + if (type == UINT64_C(-1)) { + goto error; + } + + switch (type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_event_payload_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + str = str_from_buffer(&view->buffer, offset); + if (!str) { + goto error; + } + + expr = lttng_event_expr_channel_context_field_create(str); + break; + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const char *provider_name; + const char *type_name; + + provider_name = str_from_buffer(&view->buffer, offset); + if (!provider_name) { + goto error; + } + + type_name = str_from_buffer(&view->buffer, offset); + if (!type_name) { + goto error; + } + + expr = lttng_event_expr_app_specific_context_field_create( + provider_name, type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr *array_field_expr; + const uint64_t index = uint_from_buffer( + &view->buffer, sizeof(uint32_t), offset); + + if (index == UINT64_C(-1)) { + goto error; + } + + /* Array field expression is the encoded after this. */ + array_field_expr = event_expr_from_payload(view, offset); + if (!array_field_expr) { + goto error; + } + + /* Move ownership of `array_field_expr` to new expression. */ + expr = lttng_event_expr_array_field_element_create( + array_field_expr, (unsigned int) index); + if (!expr) { + /* `array_field_expr` not moved: destroy it. */ + lttng_event_expr_destroy(array_field_expr); + } + + break; + } + default: + ERR("Invalid event expression type encoutered while deserializing event expression: type = %" PRIu64, + type); + goto error; + } + + goto end; + +error: + lttng_event_expr_destroy(expr); + expr = NULL; + +end: + return expr; +} + +ssize_t lttng_condition_event_rule_matches_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t consumed_length; + size_t offset = 0; + ssize_t event_rule_length; + uint32_t i, capture_descr_count; + struct lttng_condition *condition = NULL; + struct lttng_event_rule *event_rule = NULL; + + if (!view || !_condition) { + goto error; + } + + /* Struct lttng_event_rule. */ + { + struct lttng_payload_view event_rule_view = + lttng_payload_view_from_view(view, offset, -1); + + event_rule_length = lttng_event_rule_create_from_payload( + &event_rule_view, &event_rule); + } + + if (event_rule_length < 0 || !event_rule) { + goto error; + } + + offset += event_rule_length; + + /* Create condition (no capture descriptors yet) at this point */ + condition = lttng_condition_event_rule_matches_create(event_rule); + if (!condition) { + goto error; + } + + /* Capture descriptor count. */ + LTTNG_ASSERT(event_rule_length >= 0); + capture_descr_count = uint_from_buffer(&view->buffer, sizeof(uint32_t), &offset); + if (capture_descr_count == UINT32_C(-1)) { + goto error; + } + + /* Capture descriptors. */ + for (i = 0; i < capture_descr_count; i++) { + enum lttng_condition_status status; + struct lttng_event_expr *expr = event_expr_from_payload( + view, &offset); + + if (!expr) { + goto error; + } + + /* Move ownership of `expr` to `condition`. */ + status = lttng_condition_event_rule_matches_append_capture_descriptor( + condition, expr); + if (status != LTTNG_CONDITION_STATUS_OK) { + /* `expr` not moved: destroy it. */ + lttng_event_expr_destroy(expr); + goto error; + } + } + + consumed_length = (ssize_t) offset; + *_condition = condition; + condition = NULL; + goto end; + +error: + consumed_length = -1; + +end: + lttng_event_rule_put(event_rule); + lttng_condition_put(condition); + return consumed_length; +} + +enum lttng_condition_status +lttng_condition_event_rule_matches_borrow_rule_mutable( + const struct lttng_condition *condition, + struct lttng_event_rule **rule) +{ + struct lttng_condition_event_rule_matches *event_rule; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || + !rule) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + event_rule = container_of(condition, + struct lttng_condition_event_rule_matches, parent); + if (!event_rule->rule) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + + *rule = event_rule->rule; +end: + return status; +} + +enum lttng_condition_status lttng_condition_event_rule_matches_get_rule( + const struct lttng_condition *condition, + const struct lttng_event_rule **rule) +{ + struct lttng_event_rule *mutable_rule = NULL; + const enum lttng_condition_status status = + lttng_condition_event_rule_matches_borrow_rule_mutable( + condition, &mutable_rule); + + *rule = mutable_rule; + return status; +} + +void lttng_condition_event_rule_matches_set_error_counter_index( + struct lttng_condition *condition, uint64_t error_counter_index) +{ + struct lttng_condition_event_rule_matches *event_rule_matches_cond = + container_of(condition, + struct lttng_condition_event_rule_matches, + parent); + + LTTNG_OPTIONAL_SET(&event_rule_matches_cond->error_counter_index, + error_counter_index); +} + +uint64_t lttng_condition_event_rule_matches_get_error_counter_index( + const struct lttng_condition *condition) +{ + const struct lttng_condition_event_rule_matches + *event_rule_matches_cond = container_of(condition, + const struct lttng_condition_event_rule_matches, + parent); + + return LTTNG_OPTIONAL_GET(event_rule_matches_cond->error_counter_index); +} + +enum lttng_condition_status +lttng_condition_event_rule_matches_append_capture_descriptor( + struct lttng_condition *condition, + struct lttng_event_expr *expr) +{ + int ret; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + struct lttng_condition_event_rule_matches *event_rule_matches_cond = + container_of(condition, + struct lttng_condition_event_rule_matches, + parent); + struct lttng_capture_descriptor *descriptor = NULL; + const struct lttng_event_rule *rule = NULL; + + /* Only accept l-values. */ + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || + !expr || !lttng_event_expr_is_lvalue(expr)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + status = lttng_condition_event_rule_matches_get_rule(condition, &rule); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + switch(lttng_event_rule_get_type(rule)) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + /* Supported. */ + status = LTTNG_CONDITION_STATUS_OK; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + status = LTTNG_CONDITION_STATUS_INVALID; + break; + default: + status = LTTNG_CONDITION_STATUS_UNSUPPORTED; + break; + } + + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + descriptor = (lttng_capture_descriptor *) malloc(sizeof(*descriptor)); + if (descriptor == NULL) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + descriptor->event_expression = expr; + descriptor->bytecode = NULL; + + ret = lttng_dynamic_pointer_array_add_pointer( + &event_rule_matches_cond->capture_descriptors, + descriptor); + if (ret) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + /* Ownership is transfered to the internal capture_descriptors array */ + descriptor = NULL; +end: + free(descriptor); + return status; +} + +enum lttng_condition_status +lttng_condition_event_rule_matches_get_capture_descriptor_count( + const struct lttng_condition *condition, unsigned int *count) +{ + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + const struct lttng_condition_event_rule_matches + *event_rule_matches_condition = container_of(condition, + const struct lttng_condition_event_rule_matches, + parent); + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition) || + !count) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count( + &event_rule_matches_condition->capture_descriptors); + +end: + return status; +} + +const struct lttng_event_expr * +lttng_condition_event_rule_matches_get_capture_descriptor_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_event_expr *expr = NULL; + const struct lttng_capture_descriptor *desc = NULL; + + desc = lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( + condition, index); + if (desc == NULL) { + goto end; + } + expr = desc->event_expression; + +end: + return expr; +} + +ssize_t lttng_evaluation_event_rule_matches_create_from_payload( + const struct lttng_condition_event_rule_matches *condition, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret, offset = 0; + struct lttng_evaluation *evaluation = NULL; + uint32_t capture_payload_size; + const char *capture_payload = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < sizeof(capture_payload_size)) { + ret = -1; + goto error; + } + + memcpy(&capture_payload_size, current_view.buffer.data, + sizeof(capture_payload_size)); + } + offset += sizeof(capture_payload_size); + + if (capture_payload_size > 0) { + const struct lttng_payload_view current_view = + lttng_payload_view_from_view(view, offset, -1); + + if (current_view.buffer.size < capture_payload_size) { + ret = -1; + goto error; + } + + capture_payload = current_view.buffer.data; + } + + evaluation = lttng_evaluation_event_rule_matches_create( + condition, capture_payload, capture_payload_size, true); + if (!evaluation) { + ret = -1; + goto error; + } + + offset += capture_payload_size; + *_evaluation = evaluation; + evaluation = NULL; + ret = offset; + +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +static int lttng_evaluation_event_rule_matches_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + int ret = 0; + struct lttng_evaluation_event_rule_matches *hit; + uint32_t capture_payload_size; + + hit = container_of(evaluation, + struct lttng_evaluation_event_rule_matches, parent); + + capture_payload_size = (uint32_t) hit->capture_payload.size; + ret = lttng_dynamic_buffer_append(&payload->buffer, &capture_payload_size, + sizeof(capture_payload_size)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, hit->capture_payload.data, + hit->capture_payload.size); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +bool msgpack_str_is_equal(const struct msgpack_object *obj, const char *str) +{ + bool is_equal = true; + + LTTNG_ASSERT(obj->type == MSGPACK_OBJECT_STR); + + if (obj->via.str.size != strlen(str)) { + is_equal = false; + goto end; + } + + if (strncmp(obj->via.str.ptr, str, obj->via.str.size) != 0) { + is_equal = false; + goto end; + } + +end: + return is_equal; +} + +static +const msgpack_object *get_msgpack_map_obj(const struct msgpack_object *map_obj, + const char *name) +{ + const msgpack_object *ret = NULL; + size_t i; + + LTTNG_ASSERT(map_obj->type == MSGPACK_OBJECT_MAP); + + for (i = 0; i < map_obj->via.map.size; i++) { + const struct msgpack_object_kv *kv = &map_obj->via.map.ptr[i]; + + LTTNG_ASSERT(kv->key.type == MSGPACK_OBJECT_STR); + + if (msgpack_str_is_equal(&kv->key, name)) { + ret = &kv->val; + goto end; + } + } + +end: + return ret; +} + +static void lttng_evaluation_event_rule_matches_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_event_rule_matches *hit; + + hit = container_of(evaluation, + struct lttng_evaluation_event_rule_matches, parent); + lttng_dynamic_buffer_reset(&hit->capture_payload); + lttng_event_field_value_destroy(hit->captured_values); + free(hit); +} + +static +int event_field_value_from_obj(const msgpack_object *obj, + struct lttng_event_field_value **field_val) +{ + int ret = 0; + + LTTNG_ASSERT(obj); + LTTNG_ASSERT(field_val); + + switch (obj->type) { + case MSGPACK_OBJECT_NIL: + /* Unavailable. */ + *field_val = NULL; + goto end; + case MSGPACK_OBJECT_POSITIVE_INTEGER: + *field_val = lttng_event_field_value_uint_create( + obj->via.u64); + break; + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + *field_val = lttng_event_field_value_int_create( + obj->via.i64); + break; + case MSGPACK_OBJECT_FLOAT32: + case MSGPACK_OBJECT_FLOAT64: + *field_val = lttng_event_field_value_real_create( + obj->via.f64); + break; + case MSGPACK_OBJECT_STR: + *field_val = lttng_event_field_value_string_create_with_size( + obj->via.str.ptr, obj->via.str.size); + break; + case MSGPACK_OBJECT_ARRAY: + { + size_t i; + + *field_val = lttng_event_field_value_array_create(); + if (!*field_val) { + goto error; + } + + for (i = 0; i < obj->via.array.size; i++) { + const msgpack_object *elem_obj = &obj->via.array.ptr[i]; + struct lttng_event_field_value *elem_field_val; + + ret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (ret) { + goto error; + } + + if (elem_field_val) { + ret = lttng_event_field_value_array_append( + *field_val, elem_field_val); + } else { + ret = lttng_event_field_value_array_append_unavailable( + *field_val); + } + + if (ret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + break; + } + case MSGPACK_OBJECT_MAP: + { + /* + * As of this version, the only valid map object is + * for an enumeration value, for example: + * + * type: enum + * value: 177 + * labels: + * - Labatt 50 + * - Molson Dry + * - Carling Black Label + */ + const msgpack_object *inner_obj; + size_t label_i; + + inner_obj = get_msgpack_map_obj(obj, "type"); + if (!inner_obj) { + ERR("Missing `type` entry in map object"); + goto error; + } + + if (inner_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `type` entry is not a string: type = %s", + msgpack_object_type_str(inner_obj->type)); + goto error; + } + + if (!msgpack_str_is_equal(inner_obj, "enum")) { + ERR("Map object's `type` entry: expecting `enum`"); + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "value"); + if (!inner_obj) { + ERR("Missing `value` entry in map object"); + goto error; + } + + if (inner_obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_uint_create( + inner_obj->via.u64); + } else if (inner_obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER) { + *field_val = lttng_event_field_value_enum_int_create( + inner_obj->via.i64); + } else { + ERR("Map object's `value` entry is not an integer: type = %s", + msgpack_object_type_str(inner_obj->type)); + goto error; + } + + if (!*field_val) { + goto error; + } + + inner_obj = get_msgpack_map_obj(obj, "labels"); + if (!inner_obj) { + /* No labels */ + goto end; + } + + if (inner_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Map object's `labels` entry is not an array: type = %s", + msgpack_object_type_str(inner_obj->type)); + goto error; + } + + for (label_i = 0; label_i < inner_obj->via.array.size; + label_i++) { + int iret; + const msgpack_object *elem_obj = + &inner_obj->via.array.ptr[label_i]; + + if (elem_obj->type != MSGPACK_OBJECT_STR) { + ERR("Map object's `labels` entry's type is not a string: type = %s", + msgpack_object_type_str(elem_obj->type)); + goto error; + } + + iret = lttng_event_field_value_enum_append_label_with_size( + *field_val, elem_obj->via.str.ptr, + elem_obj->via.str.size); + if (iret) { + goto error; + } + } + + break; + } + default: + ERR("Unexpected object type: type = %s", + msgpack_object_type_str(obj->type)); + goto error; + } + + if (!*field_val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(*field_val); + *field_val = NULL; + ret = -1; + +end: + return ret; +} + +static struct lttng_event_field_value *event_field_value_from_capture_payload( + const struct lttng_condition_event_rule_matches *condition, + const char *capture_payload, + size_t capture_payload_size) +{ + struct lttng_event_field_value *ret = NULL; + msgpack_unpacked unpacked; + msgpack_unpack_return unpack_return; + const msgpack_object *root_obj; + const msgpack_object_array *root_array_obj; + size_t i; + size_t count; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(capture_payload); + + /* Initialize value. */ + msgpack_unpacked_init(&unpacked); + + /* Decode. */ + unpack_return = msgpack_unpack_next(&unpacked, capture_payload, + capture_payload_size, NULL); + if (unpack_return != MSGPACK_UNPACK_SUCCESS) { + ERR("msgpack_unpack_next() failed to decode the " + "MessagePack-encoded capture payload: " + "size = %zu, ret = %d", + capture_payload_size, unpack_return); + goto error; + } + + /* Get root array. */ + root_obj = &unpacked.data; + + if (root_obj->type != MSGPACK_OBJECT_ARRAY) { + ERR("Expecting an array as the root object: type = %s", + msgpack_object_type_str(root_obj->type)); + goto error; + } + + root_array_obj = &root_obj->via.array; + + /* Create an empty root array event field value. */ + ret = lttng_event_field_value_array_create(); + if (!ret) { + goto error; + } + + /* + * For each capture descriptor in the condition object: + * + * 1. Get its corresponding captured field value MessagePack + * object. + * + * 2. Create a corresponding event field value. + * + * 3. Append it to `ret` (the root array event field value). + */ + count = lttng_dynamic_pointer_array_get_count( + &condition->capture_descriptors); + LTTNG_ASSERT(count > 0); + + for (i = 0; i < count; i++) { + const struct lttng_capture_descriptor *capture_descriptor = + lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( + &condition->parent, i); + const msgpack_object *elem_obj; + struct lttng_event_field_value *elem_field_val; + int iret; + + LTTNG_ASSERT(capture_descriptor); + + elem_obj = &root_array_obj->ptr[i]; + iret = event_field_value_from_obj(elem_obj, + &elem_field_val); + if (iret) { + goto error; + } + + if (elem_field_val) { + iret = lttng_event_field_value_array_append(ret, + elem_field_val); + } else { + iret = lttng_event_field_value_array_append_unavailable( + ret); + } + + if (iret) { + lttng_event_field_value_destroy(elem_field_val); + goto error; + } + } + + goto end; + +error: + lttng_event_field_value_destroy(ret); + ret = NULL; + +end: + msgpack_unpacked_destroy(&unpacked); + return ret; +} + +struct lttng_evaluation *lttng_evaluation_event_rule_matches_create( + const struct lttng_condition_event_rule_matches *condition, + const char *capture_payload, + size_t capture_payload_size, + bool decode_capture_payload) +{ + struct lttng_evaluation_event_rule_matches *hit; + struct lttng_evaluation *evaluation = NULL; + + hit = (lttng_evaluation_event_rule_matches *) zmalloc(sizeof(struct lttng_evaluation_event_rule_matches)); + if (!hit) { + goto error; + } + + lttng_dynamic_buffer_init(&hit->capture_payload); + + if (capture_payload) { + const int ret = lttng_dynamic_buffer_append( + &hit->capture_payload, capture_payload, + capture_payload_size); + if (ret) { + ERR("Failed to initialize capture payload of event rule evaluation"); + goto error; + } + + if (decode_capture_payload) { + hit->captured_values = + event_field_value_from_capture_payload( + condition, + capture_payload, + capture_payload_size); + if (!hit->captured_values) { + ERR("Failed to decode the capture payload: size = %zu", + capture_payload_size); + goto error; + } + } + } + + hit->parent.type = LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES; + hit->parent.serialize = lttng_evaluation_event_rule_matches_serialize; + hit->parent.destroy = lttng_evaluation_event_rule_matches_destroy; + + evaluation = &hit->parent; + hit = NULL; + +error: + if (hit) { + lttng_evaluation_event_rule_matches_destroy(&hit->parent); + } + + return evaluation; +} + +enum lttng_evaluation_event_rule_matches_status +lttng_evaluation_event_rule_matches_get_captured_values( + const struct lttng_evaluation *evaluation, + const struct lttng_event_field_value **field_val) +{ + struct lttng_evaluation_event_rule_matches *hit; + enum lttng_evaluation_event_rule_matches_status status = + LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_OK; + + if (!evaluation || !is_event_rule_matches_evaluation(evaluation) || + !field_val) { + status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_INVALID; + goto end; + } + + hit = container_of(evaluation, + struct lttng_evaluation_event_rule_matches, parent); + if (!hit->captured_values) { + status = LTTNG_EVALUATION_EVENT_RULE_MATCHES_STATUS_NONE; + goto end; + } + + *field_val = hit->captured_values; + +end: + return status; +} + +enum lttng_error_code +lttng_condition_event_rule_matches_generate_capture_descriptor_bytecode( + struct lttng_condition *condition) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + unsigned int capture_count, i; + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &capture_count); + if (status != LTTNG_CONDITION_STATUS_OK) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + for (i = 0; i < capture_count; i++) { + struct lttng_capture_descriptor *local_capture_desc = + lttng_condition_event_rule_matches_get_internal_capture_descriptor_at_index( + condition, i); + int bytecode_ret; + + if (local_capture_desc == NULL) { + ret = LTTNG_ERR_FATAL; + goto end; + } + + /* Generate the bytecode. */ + bytecode_ret = lttng_event_expr_to_bytecode( + local_capture_desc->event_expression, + &local_capture_desc->bytecode); + if (bytecode_ret < 0 || local_capture_desc->bytecode == NULL) { + ret = LTTNG_ERR_INVALID_CAPTURE_EXPRESSION; + goto end; + } + } + + /* Everything went better than expected */ + ret = LTTNG_OK; + +end: + return ret; +} + +const struct lttng_bytecode * +lttng_condition_event_rule_matches_get_capture_bytecode_at_index( + const struct lttng_condition *condition, unsigned int index) +{ + const struct lttng_condition_event_rule_matches + *event_rule_matches_cond = container_of(condition, + const struct lttng_condition_event_rule_matches, + parent); + struct lttng_capture_descriptor *desc = NULL; + struct lttng_bytecode *bytecode = NULL; + unsigned int count; + enum lttng_condition_status status; + + if (!condition || !IS_EVENT_RULE_MATCHES_CONDITION(condition)) { + goto end; + } + + status = lttng_condition_event_rule_matches_get_capture_descriptor_count( + condition, &count); + if (status != LTTNG_CONDITION_STATUS_OK) { + goto end; + } + + if (index >= count) { + goto end; + } + + desc = (lttng_capture_descriptor *) lttng_dynamic_pointer_array_get_pointer( + &event_rule_matches_cond->capture_descriptors, index); + if (desc == NULL) { + goto end; + } + + bytecode = desc->bytecode; +end: + return bytecode; +} diff --git a/src/common/conditions/session-consumed-size.c b/src/common/conditions/session-consumed-size.c deleted file mode 100644 index 2f95bd82a..000000000 --- a/src/common/conditions/session-consumed-size.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IS_CONSUMED_SIZE_CONDITION(condition) ( \ - lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \ - ) - -#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \ - lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \ - ) - -static -void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition) -{ - struct lttng_condition_session_consumed_size *consumed_size; - - consumed_size = container_of(condition, - struct lttng_condition_session_consumed_size, parent); - - free(consumed_size->session_name); - free(consumed_size); -} - -static -bool lttng_condition_session_consumed_size_validate( - const struct lttng_condition *condition) -{ - bool valid = false; - struct lttng_condition_session_consumed_size *consumed; - - if (!condition) { - goto end; - } - - consumed = container_of(condition, struct lttng_condition_session_consumed_size, - parent); - if (!consumed->session_name) { - ERR("Invalid session consumed size condition: a target session name must be set."); - goto end; - } - if (!consumed->consumed_threshold_bytes.set) { - ERR("Invalid session consumed size condition: a threshold must be set."); - goto end; - } - - valid = true; -end: - return valid; -} - -static -int lttng_condition_session_consumed_size_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload) -{ - int ret; - size_t session_name_len; - struct lttng_condition_session_consumed_size *consumed; - struct lttng_condition_session_consumed_size_comm consumed_comm; - - if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) { - ret = -1; - goto end; - } - - DBG("Serializing session consumed size condition"); - consumed = container_of(condition, - struct lttng_condition_session_consumed_size, - parent); - - session_name_len = strlen(consumed->session_name) + 1; - if (session_name_len > LTTNG_NAME_MAX) { - ret = -1; - goto end; - } - - consumed_comm.consumed_threshold_bytes = - consumed->consumed_threshold_bytes.value; - consumed_comm.session_name_len = (uint32_t) session_name_len; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &consumed_comm, - sizeof(consumed_comm)); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, consumed->session_name, - session_name_len); - if (ret) { - goto end; - } -end: - return ret; -} - -static -bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a, - const struct lttng_condition *_b) -{ - bool is_equal = false; - struct lttng_condition_session_consumed_size *a, *b; - - a = container_of(_a, struct lttng_condition_session_consumed_size, parent); - b = container_of(_b, struct lttng_condition_session_consumed_size, parent); - - if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) { - uint64_t a_value, b_value; - - a_value = a->consumed_threshold_bytes.value; - b_value = b->consumed_threshold_bytes.value; - if (a_value != b_value) { - goto end; - } - } - - LTTNG_ASSERT(a->session_name); - LTTNG_ASSERT(b->session_name); - if (strcmp(a->session_name, b->session_name)) { - goto end; - } - - is_equal = true; -end: - return is_equal; -} - -static -enum lttng_error_code lttng_condition_session_consumed_size_mi_serialize( - const struct lttng_condition *condition, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_condition_status status; - const char *session_name = NULL; - uint64_t threshold_bytes; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(IS_CONSUMED_SIZE_CONDITION(condition)); - - status = lttng_condition_session_consumed_size_get_session_name( - condition, &session_name); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - LTTNG_ASSERT(session_name); - - status = lttng_condition_session_consumed_size_get_threshold( - condition, &threshold_bytes); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - - /* Open condition session consumed size element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_condition_session_consumed_size); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Threshold in bytes. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_condition_threshold_bytes, - threshold_bytes); - if (ret) { - goto mi_error; - } - - /* Close condition session consumed size element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -struct lttng_condition *lttng_condition_session_consumed_size_create(void) -{ - struct lttng_condition_session_consumed_size *condition; - - condition = zmalloc(sizeof(struct lttng_condition_session_consumed_size)); - if (!condition) { - return NULL; - } - - lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE); - condition->parent.validate = lttng_condition_session_consumed_size_validate; - condition->parent.serialize = lttng_condition_session_consumed_size_serialize; - condition->parent.equal = lttng_condition_session_consumed_size_is_equal; - condition->parent.destroy = lttng_condition_session_consumed_size_destroy; - condition->parent.mi_serialize = lttng_condition_session_consumed_size_mi_serialize; - return &condition->parent; -} - -static -ssize_t init_condition_from_payload(struct lttng_condition *condition, - struct lttng_payload_view *src_view) -{ - ssize_t ret, condition_size; - enum lttng_condition_status status; - const char *session_name; - struct lttng_buffer_view session_name_view; - const struct lttng_condition_session_consumed_size_comm *condition_comm; - struct lttng_payload_view condition_comm_view = lttng_payload_view_from_view( - src_view, 0, sizeof(*condition_comm)); - - if (!lttng_payload_view_is_valid(&condition_comm_view)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); - ret = -1; - goto end; - } - - condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; - session_name_view = lttng_buffer_view_from_view(&src_view->buffer, - sizeof(*condition_comm), condition_comm->session_name_len); - - if (condition_comm->session_name_len > LTTNG_NAME_MAX) { - ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); - ret = -1; - goto end; - } - - if (!lttng_buffer_view_is_valid(&session_name_view)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names"); - ret = -1; - goto end; - } - - status = lttng_condition_session_consumed_size_set_threshold(condition, - condition_comm->consumed_threshold_bytes); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to initialize session consumed size condition threshold"); - ret = -1; - goto end; - } - - session_name = session_name_view.data; - if (*(session_name + condition_comm->session_name_len - 1) != '\0') { - ERR("Malformed session name encountered in condition buffer"); - ret = -1; - goto end; - } - - status = lttng_condition_session_consumed_size_set_session_name(condition, - session_name); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to set session consumed size condition's session name"); - ret = -1; - goto end; - } - - if (!lttng_condition_validate(condition)) { - ret = -1; - goto end; - } - - condition_size = sizeof(*condition_comm) + - (ssize_t) condition_comm->session_name_len; - ret = condition_size; -end: - return ret; -} - -ssize_t lttng_condition_session_consumed_size_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **_condition) -{ - ssize_t ret; - struct lttng_condition *condition = - lttng_condition_session_consumed_size_create(); - - if (!_condition || !condition) { - ret = -1; - goto error; - } - - ret = init_condition_from_payload(condition, view); - if (ret < 0) { - goto error; - } - - *_condition = condition; - return ret; -error: - lttng_condition_destroy(condition); - return ret; -} - -static -struct lttng_evaluation *create_evaluation_from_payload( - const struct lttng_payload_view *view) -{ - const struct lttng_evaluation_session_consumed_size_comm *comm = - (typeof(comm)) view->buffer.data; - struct lttng_evaluation *evaluation = NULL; - - if (view->buffer.size < sizeof(*comm)) { - goto end; - } - - evaluation = lttng_evaluation_session_consumed_size_create( - comm->session_consumed); -end: - return evaluation; -} - -ssize_t lttng_evaluation_session_consumed_size_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret; - struct lttng_evaluation *evaluation = NULL; - - if (!_evaluation) { - ret = -1; - goto error; - } - - evaluation = create_evaluation_from_payload(view); - if (!evaluation) { - ret = -1; - goto error; - } - - *_evaluation = evaluation; - ret = sizeof(struct lttng_evaluation_session_consumed_size_comm); - return ret; -error: - lttng_evaluation_destroy(evaluation); - return ret; -} - -enum lttng_condition_status -lttng_condition_session_consumed_size_get_threshold( - const struct lttng_condition *condition, - uint64_t *consumed_threshold_bytes) -{ - struct lttng_condition_session_consumed_size *consumed; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - consumed = container_of(condition, struct lttng_condition_session_consumed_size, - parent); - if (!consumed->consumed_threshold_bytes.set) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_session_consumed_size_set_threshold( - struct lttng_condition *condition, uint64_t consumed_threshold_bytes) -{ - struct lttng_condition_session_consumed_size *consumed; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - consumed = container_of(condition, struct lttng_condition_session_consumed_size, - parent); - consumed->consumed_threshold_bytes.set = true; - consumed->consumed_threshold_bytes.value = consumed_threshold_bytes; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_session_consumed_size_get_session_name( - const struct lttng_condition *condition, - const char **session_name) -{ - struct lttng_condition_session_consumed_size *consumed; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - consumed = container_of(condition, struct lttng_condition_session_consumed_size, - parent); - if (!consumed->session_name) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *session_name = consumed->session_name; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_session_consumed_size_set_session_name( - struct lttng_condition *condition, const char *session_name) -{ - char *session_name_copy; - struct lttng_condition_session_consumed_size *consumed; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || - !session_name || strlen(session_name) == 0) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - consumed = container_of(condition, struct lttng_condition_session_consumed_size, - parent); - session_name_copy = strdup(session_name); - if (!session_name_copy) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - if (consumed->session_name) { - free(consumed->session_name); - } - consumed->session_name = session_name_copy; -end: - return status; -} - -static -int lttng_evaluation_session_consumed_size_serialize( - const struct lttng_evaluation *evaluation, - struct lttng_payload *payload) -{ - struct lttng_evaluation_session_consumed_size *consumed; - struct lttng_evaluation_session_consumed_size_comm comm; - - consumed = container_of(evaluation, - struct lttng_evaluation_session_consumed_size, parent); - comm.session_consumed = consumed->session_consumed; - return lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); -} - -static -void lttng_evaluation_session_consumed_size_destroy( - struct lttng_evaluation *evaluation) -{ - struct lttng_evaluation_session_consumed_size *consumed; - - consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size, - parent); - free(consumed); -} - -struct lttng_evaluation *lttng_evaluation_session_consumed_size_create( - uint64_t consumed) -{ - struct lttng_evaluation_session_consumed_size *consumed_eval; - - consumed_eval = zmalloc(sizeof(struct lttng_evaluation_session_consumed_size)); - if (!consumed_eval) { - goto end; - } - - consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE; - consumed_eval->session_consumed = consumed; - consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize; - consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy; -end: - return &consumed_eval->parent; -} - -enum lttng_evaluation_status -lttng_evaluation_session_consumed_size_get_consumed_size( - const struct lttng_evaluation *evaluation, - uint64_t *session_consumed) -{ - struct lttng_evaluation_session_consumed_size *consumed; - enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; - - if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) || - !session_consumed) { - status = LTTNG_EVALUATION_STATUS_INVALID; - goto end; - } - - consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size, - parent); - *session_consumed = consumed->session_consumed; -end: - return status; -} diff --git a/src/common/conditions/session-consumed-size.cpp b/src/common/conditions/session-consumed-size.cpp new file mode 100644 index 000000000..86a7f332a --- /dev/null +++ b/src/common/conditions/session-consumed-size.cpp @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_CONSUMED_SIZE_CONDITION(condition) ( \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \ + ) + +#define IS_CONSUMED_SIZE_EVALUATION(evaluation) ( \ + lttng_evaluation_get_type(evaluation) == LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE \ + ) + +static +void lttng_condition_session_consumed_size_destroy(struct lttng_condition *condition) +{ + struct lttng_condition_session_consumed_size *consumed_size; + + consumed_size = container_of(condition, + struct lttng_condition_session_consumed_size, parent); + + free(consumed_size->session_name); + free(consumed_size); +} + +static +bool lttng_condition_session_consumed_size_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_session_consumed_size *consumed; + + if (!condition) { + goto end; + } + + consumed = container_of(condition, struct lttng_condition_session_consumed_size, + parent); + if (!consumed->session_name) { + ERR("Invalid session consumed size condition: a target session name must be set."); + goto end; + } + if (!consumed->consumed_threshold_bytes.set) { + ERR("Invalid session consumed size condition: a threshold must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +int lttng_condition_session_consumed_size_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + size_t session_name_len; + struct lttng_condition_session_consumed_size *consumed; + struct lttng_condition_session_consumed_size_comm consumed_comm; + + if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing session consumed size condition"); + consumed = container_of(condition, + struct lttng_condition_session_consumed_size, + parent); + + session_name_len = strlen(consumed->session_name) + 1; + if (session_name_len > LTTNG_NAME_MAX) { + ret = -1; + goto end; + } + + consumed_comm.consumed_threshold_bytes = + consumed->consumed_threshold_bytes.value; + consumed_comm.session_name_len = (uint32_t) session_name_len; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &consumed_comm, + sizeof(consumed_comm)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, consumed->session_name, + session_name_len); + if (ret) { + goto end; + } +end: + return ret; +} + +static +bool lttng_condition_session_consumed_size_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_session_consumed_size *a, *b; + + a = container_of(_a, struct lttng_condition_session_consumed_size, parent); + b = container_of(_b, struct lttng_condition_session_consumed_size, parent); + + if (a->consumed_threshold_bytes.set && b->consumed_threshold_bytes.set) { + uint64_t a_value, b_value; + + a_value = a->consumed_threshold_bytes.value; + b_value = b->consumed_threshold_bytes.value; + if (a_value != b_value) { + goto end; + } + } + + LTTNG_ASSERT(a->session_name); + LTTNG_ASSERT(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static +enum lttng_error_code lttng_condition_session_consumed_size_mi_serialize( + const struct lttng_condition *condition, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_condition_status status; + const char *session_name = NULL; + uint64_t threshold_bytes; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(IS_CONSUMED_SIZE_CONDITION(condition)); + + status = lttng_condition_session_consumed_size_get_session_name( + condition, &session_name); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(session_name); + + status = lttng_condition_session_consumed_size_get_threshold( + condition, &threshold_bytes); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + + /* Open condition session consumed size element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_condition_session_consumed_size); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Threshold in bytes. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_condition_threshold_bytes, + threshold_bytes); + if (ret) { + goto mi_error; + } + + /* Close condition session consumed size element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +struct lttng_condition *lttng_condition_session_consumed_size_create(void) +{ + struct lttng_condition_session_consumed_size *condition; + + condition = (lttng_condition_session_consumed_size *) zmalloc(sizeof(struct lttng_condition_session_consumed_size)); + if (!condition) { + return NULL; + } + + lttng_condition_init(&condition->parent, LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE); + condition->parent.validate = lttng_condition_session_consumed_size_validate; + condition->parent.serialize = lttng_condition_session_consumed_size_serialize; + condition->parent.equal = lttng_condition_session_consumed_size_is_equal; + condition->parent.destroy = lttng_condition_session_consumed_size_destroy; + condition->parent.mi_serialize = lttng_condition_session_consumed_size_mi_serialize; + return &condition->parent; +} + +static +ssize_t init_condition_from_payload(struct lttng_condition *condition, + struct lttng_payload_view *src_view) +{ + ssize_t ret, condition_size; + enum lttng_condition_status status; + const char *session_name; + struct lttng_buffer_view session_name_view; + const struct lttng_condition_session_consumed_size_comm *condition_comm; + struct lttng_payload_view condition_comm_view = lttng_payload_view_from_view( + src_view, 0, sizeof(*condition_comm)); + + if (!lttng_payload_view_is_valid(&condition_comm_view)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); + ret = -1; + goto end; + } + + condition_comm = (typeof(condition_comm)) condition_comm_view.buffer.data; + session_name_view = lttng_buffer_view_from_view(&src_view->buffer, + sizeof(*condition_comm), condition_comm->session_name_len); + + if (condition_comm->session_name_len > LTTNG_NAME_MAX) { + ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); + ret = -1; + goto end; + } + + if (!lttng_buffer_view_is_valid(&session_name_view)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names"); + ret = -1; + goto end; + } + + status = lttng_condition_session_consumed_size_set_threshold(condition, + condition_comm->consumed_threshold_bytes); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to initialize session consumed size condition threshold"); + ret = -1; + goto end; + } + + session_name = session_name_view.data; + if (*(session_name + condition_comm->session_name_len - 1) != '\0') { + ERR("Malformed session name encountered in condition buffer"); + ret = -1; + goto end; + } + + status = lttng_condition_session_consumed_size_set_session_name(condition, + session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set session consumed size condition's session name"); + ret = -1; + goto end; + } + + if (!lttng_condition_validate(condition)) { + ret = -1; + goto end; + } + + condition_size = sizeof(*condition_comm) + + (ssize_t) condition_comm->session_name_len; + ret = condition_size; +end: + return ret; +} + +ssize_t lttng_condition_session_consumed_size_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition) +{ + ssize_t ret; + struct lttng_condition *condition = + lttng_condition_session_consumed_size_create(); + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_payload(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +static +struct lttng_evaluation *create_evaluation_from_payload( + const struct lttng_payload_view *view) +{ + const struct lttng_evaluation_session_consumed_size_comm *comm = + (typeof(comm)) view->buffer.data; + struct lttng_evaluation *evaluation = NULL; + + if (view->buffer.size < sizeof(*comm)) { + goto end; + } + + evaluation = lttng_evaluation_session_consumed_size_create( + comm->session_consumed); +end: + return evaluation; +} + +ssize_t lttng_evaluation_session_consumed_size_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + evaluation = create_evaluation_from_payload(view); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + ret = sizeof(struct lttng_evaluation_session_consumed_size_comm); + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +enum lttng_condition_status +lttng_condition_session_consumed_size_get_threshold( + const struct lttng_condition *condition, + uint64_t *consumed_threshold_bytes) +{ + struct lttng_condition_session_consumed_size *consumed; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !consumed_threshold_bytes) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + consumed = container_of(condition, struct lttng_condition_session_consumed_size, + parent); + if (!consumed->consumed_threshold_bytes.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *consumed_threshold_bytes = consumed->consumed_threshold_bytes.value; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_session_consumed_size_set_threshold( + struct lttng_condition *condition, uint64_t consumed_threshold_bytes) +{ + struct lttng_condition_session_consumed_size *consumed; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + consumed = container_of(condition, struct lttng_condition_session_consumed_size, + parent); + consumed->consumed_threshold_bytes.set = true; + consumed->consumed_threshold_bytes.value = consumed_threshold_bytes; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_session_consumed_size_get_session_name( + const struct lttng_condition *condition, + const char **session_name) +{ + struct lttng_condition_session_consumed_size *consumed; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || !session_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + consumed = container_of(condition, struct lttng_condition_session_consumed_size, + parent); + if (!consumed->session_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *session_name = consumed->session_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_session_consumed_size_set_session_name( + struct lttng_condition *condition, const char *session_name) +{ + char *session_name_copy; + struct lttng_condition_session_consumed_size *consumed; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_CONSUMED_SIZE_CONDITION(condition) || + !session_name || strlen(session_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + consumed = container_of(condition, struct lttng_condition_session_consumed_size, + parent); + session_name_copy = strdup(session_name); + if (!session_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + if (consumed->session_name) { + free(consumed->session_name); + } + consumed->session_name = session_name_copy; +end: + return status; +} + +static +int lttng_evaluation_session_consumed_size_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + struct lttng_evaluation_session_consumed_size *consumed; + struct lttng_evaluation_session_consumed_size_comm comm; + + consumed = container_of(evaluation, + struct lttng_evaluation_session_consumed_size, parent); + comm.session_consumed = consumed->session_consumed; + return lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); +} + +static +void lttng_evaluation_session_consumed_size_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_session_consumed_size *consumed; + + consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size, + parent); + free(consumed); +} + +struct lttng_evaluation *lttng_evaluation_session_consumed_size_create( + uint64_t consumed) +{ + struct lttng_evaluation_session_consumed_size *consumed_eval; + + consumed_eval = (lttng_evaluation_session_consumed_size *) zmalloc(sizeof(struct lttng_evaluation_session_consumed_size)); + if (!consumed_eval) { + goto end; + } + + consumed_eval->parent.type = LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE; + consumed_eval->session_consumed = consumed; + consumed_eval->parent.serialize = lttng_evaluation_session_consumed_size_serialize; + consumed_eval->parent.destroy = lttng_evaluation_session_consumed_size_destroy; +end: + return &consumed_eval->parent; +} + +enum lttng_evaluation_status +lttng_evaluation_session_consumed_size_get_consumed_size( + const struct lttng_evaluation *evaluation, + uint64_t *session_consumed) +{ + struct lttng_evaluation_session_consumed_size *consumed; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !IS_CONSUMED_SIZE_EVALUATION(evaluation) || + !session_consumed) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + consumed = container_of(evaluation, struct lttng_evaluation_session_consumed_size, + parent); + *session_consumed = consumed->session_consumed; +end: + return status; +} diff --git a/src/common/conditions/session-rotation.c b/src/common/conditions/session-rotation.c deleted file mode 100644 index d352718e1..000000000 --- a/src/common/conditions/session-rotation.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -static -bool lttng_condition_session_rotation_validate( - const struct lttng_condition *condition); -static -int lttng_condition_session_rotation_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload); -static -bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a, - const struct lttng_condition *_b); -static -void lttng_condition_session_rotation_destroy( - struct lttng_condition *condition); - -static -enum lttng_error_code lttng_condition_session_rotation_mi_serialize( - const struct lttng_condition *condition, - struct mi_writer *writer); - -static const -struct lttng_condition rotation_condition_template = { - /* .type omitted; shall be set on creation. */ - .validate = lttng_condition_session_rotation_validate, - .serialize = lttng_condition_session_rotation_serialize, - .equal = lttng_condition_session_rotation_is_equal, - .destroy = lttng_condition_session_rotation_destroy, - .mi_serialize = lttng_condition_session_rotation_mi_serialize, -}; - -static -int lttng_evaluation_session_rotation_serialize( - const struct lttng_evaluation *evaluation, - struct lttng_payload *payload); -static -void lttng_evaluation_session_rotation_destroy( - struct lttng_evaluation *evaluation); - -static const -struct lttng_evaluation rotation_evaluation_template = { - /* .type omitted; shall be set on creation. */ - .serialize = lttng_evaluation_session_rotation_serialize, - .destroy = lttng_evaluation_session_rotation_destroy, -}; - -static -bool is_rotation_condition(const struct lttng_condition *condition) -{ - enum lttng_condition_type type = lttng_condition_get_type(condition); - - return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING || - type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED; -} - -static -bool is_rotation_evaluation(const struct lttng_evaluation *evaluation) -{ - enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); - - return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING || - type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED; -} - -static -bool lttng_condition_session_rotation_validate( - const struct lttng_condition *condition) -{ - bool valid = false; - struct lttng_condition_session_rotation *rotation; - - if (!condition) { - goto end; - } - - rotation = container_of(condition, - struct lttng_condition_session_rotation, parent); - if (!rotation->session_name) { - ERR("Invalid session rotation condition: a target session name must be set."); - goto end; - } - - valid = true; -end: - return valid; -} - -static -int lttng_condition_session_rotation_serialize( - const struct lttng_condition *condition, - struct lttng_payload *payload) -{ - int ret; - size_t session_name_len; - struct lttng_condition_session_rotation *rotation; - struct lttng_condition_session_rotation_comm rotation_comm; - - if (!condition || !is_rotation_condition(condition)) { - ret = -1; - goto end; - } - - DBG("Serializing session rotation condition"); - rotation = container_of(condition, struct lttng_condition_session_rotation, - parent); - - session_name_len = strlen(rotation->session_name) + 1; - if (session_name_len > LTTNG_NAME_MAX) { - ret = -1; - goto end; - } - - rotation_comm.session_name_len = session_name_len; - ret = lttng_dynamic_buffer_append(&payload->buffer, &rotation_comm, - sizeof(rotation_comm)); - if (ret) { - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - rotation->session_name, session_name_len); - if (ret) { - goto end; - } -end: - return ret; -} - -static -bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a, - const struct lttng_condition *_b) -{ - bool is_equal = false; - struct lttng_condition_session_rotation *a, *b; - - a = container_of(_a, struct lttng_condition_session_rotation, parent); - b = container_of(_b, struct lttng_condition_session_rotation, parent); - - /* Both session names must be set or both must be unset. */ - if ((a->session_name && !b->session_name) || - (!a->session_name && b->session_name)) { - WARN("Comparing session rotation conditions with uninitialized session names."); - goto end; - } - - if (a->session_name && b->session_name && - strcmp(a->session_name, b->session_name)) { - goto end; - } - - is_equal = true; -end: - return is_equal; -} - -static -void lttng_condition_session_rotation_destroy( - struct lttng_condition *condition) -{ - struct lttng_condition_session_rotation *rotation; - - rotation = container_of(condition, - struct lttng_condition_session_rotation, parent); - - free(rotation->session_name); - free(rotation); -} - -static -struct lttng_condition *lttng_condition_session_rotation_create( - enum lttng_condition_type type) -{ - struct lttng_condition_session_rotation *condition; - - condition = zmalloc(sizeof(struct lttng_condition_session_rotation)); - if (!condition) { - return NULL; - } - - memcpy(&condition->parent, &rotation_condition_template, - sizeof(condition->parent)); - lttng_condition_init(&condition->parent, type); - return &condition->parent; -} - -struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void) -{ - return lttng_condition_session_rotation_create( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING); -} - -struct lttng_condition *lttng_condition_session_rotation_completed_create(void) -{ - return lttng_condition_session_rotation_create( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED); -} - -static -ssize_t init_condition_from_payload(struct lttng_condition *condition, - struct lttng_payload_view *src_view) -{ - ssize_t ret, condition_size; - enum lttng_condition_status status; - const char *session_name; - struct lttng_buffer_view name_view; - const struct lttng_condition_session_rotation_comm *condition_comm; - struct lttng_payload_view condition_comm_view = - lttng_payload_view_from_view( - src_view, 0, sizeof(*condition_comm)); - - if (!lttng_payload_view_is_valid(&condition_comm_view)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); - ret = -1; - goto end; - } - - condition_comm = (typeof(condition_comm)) src_view->buffer.data; - name_view = lttng_buffer_view_from_view(&src_view->buffer, - sizeof(*condition_comm), condition_comm->session_name_len); - - if (!lttng_buffer_view_is_valid(&name_view)) { - ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name"); - ret = -1; - goto end; - } - - if (condition_comm->session_name_len > LTTNG_NAME_MAX) { - ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); - ret = -1; - goto end; - } - - session_name = name_view.data; - if (*(session_name + condition_comm->session_name_len - 1) != '\0') { - ERR("Malformed session name encountered in condition buffer"); - ret = -1; - goto end; - } - - status = lttng_condition_session_rotation_set_session_name(condition, - session_name); - if (status != LTTNG_CONDITION_STATUS_OK) { - ERR("Failed to set buffer consumed session name"); - ret = -1; - goto end; - } - - if (!lttng_condition_validate(condition)) { - ret = -1; - goto end; - } - - condition_size = sizeof(*condition_comm) + - (ssize_t) condition_comm->session_name_len; - ret = condition_size; -end: - return ret; -} - -static -ssize_t lttng_condition_session_rotation_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **_condition, - enum lttng_condition_type type) -{ - ssize_t ret; - struct lttng_condition *condition = NULL; - - switch (type) { - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - condition = lttng_condition_session_rotation_ongoing_create(); - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - condition = lttng_condition_session_rotation_completed_create(); - break; - default: - ret = -1; - goto error; - } - - if (!_condition || !condition) { - ret = -1; - goto error; - } - - ret = init_condition_from_payload(condition, view); - if (ret < 0) { - goto error; - } - - *_condition = condition; - return ret; -error: - lttng_condition_destroy(condition); - return ret; -} - -ssize_t lttng_condition_session_rotation_ongoing_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **condition) -{ - return lttng_condition_session_rotation_create_from_payload(view, - condition, - LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING); -} - -ssize_t lttng_condition_session_rotation_completed_create_from_payload( - struct lttng_payload_view *view, - struct lttng_condition **condition) -{ - return lttng_condition_session_rotation_create_from_payload(view, - condition, - LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED); -} - -static -struct lttng_evaluation *lttng_evaluation_session_rotation_create( - enum lttng_condition_type type, uint64_t id, - struct lttng_trace_archive_location *location) -{ - struct lttng_evaluation_session_rotation *evaluation; - - evaluation = zmalloc(sizeof(struct lttng_evaluation_session_rotation)); - if (!evaluation) { - return NULL; - } - - memcpy(&evaluation->parent, &rotation_evaluation_template, - sizeof(evaluation->parent)); - lttng_evaluation_init(&evaluation->parent, type); - evaluation->id = id; - if (location) { - lttng_trace_archive_location_get(location); - } - evaluation->location = location; - return &evaluation->parent; -} - -static -ssize_t create_evaluation_from_payload( - enum lttng_condition_type type, - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret, size; - struct lttng_evaluation *evaluation = NULL; - struct lttng_trace_archive_location *location = NULL; - const struct lttng_evaluation_session_rotation_comm *comm; - struct lttng_payload_view comm_view = lttng_payload_view_from_view( - view, 0, sizeof(*comm)); - - if (!lttng_payload_view_is_valid(&comm_view)) { - goto error; - } - - comm = (typeof(comm)) comm_view.buffer.data; - size = sizeof(*comm); - if (comm->has_location) { - const struct lttng_buffer_view location_view = - lttng_buffer_view_from_view( - &view->buffer, sizeof(*comm), -1); - - if (!lttng_buffer_view_is_valid(&location_view)) { - goto error; - } - - ret = lttng_trace_archive_location_create_from_buffer( - &location_view, &location); - if (ret < 0) { - goto error; - } - size += ret; - } - - evaluation = lttng_evaluation_session_rotation_create(type, comm->id, - location); - if (!evaluation) { - goto error; - } - - lttng_trace_archive_location_put(location); - ret = size; - *_evaluation = evaluation; - return ret; -error: - lttng_trace_archive_location_put(location); - evaluation = NULL; - return -1; -} - -static -ssize_t lttng_evaluation_session_rotation_create_from_payload( - enum lttng_condition_type type, - struct lttng_payload_view *view, - struct lttng_evaluation **_evaluation) -{ - ssize_t ret; - struct lttng_evaluation *evaluation = NULL; - - if (!_evaluation) { - ret = -1; - goto error; - } - - ret = create_evaluation_from_payload(type, view, &evaluation); - if (ret < 0) { - goto error; - } - - *_evaluation = evaluation; - return ret; -error: - lttng_evaluation_destroy(evaluation); - return ret; -} - -ssize_t lttng_evaluation_session_rotation_ongoing_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **evaluation) -{ - return lttng_evaluation_session_rotation_create_from_payload( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, - view, evaluation); -} - -ssize_t lttng_evaluation_session_rotation_completed_create_from_payload( - struct lttng_payload_view *view, - struct lttng_evaluation **evaluation) -{ - return lttng_evaluation_session_rotation_create_from_payload( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, - view, evaluation); -} - -struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create( - uint64_t id) -{ - return lttng_evaluation_session_rotation_create( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id, - NULL); -} - -struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create( - uint64_t id, struct lttng_trace_archive_location *location) -{ - return lttng_evaluation_session_rotation_create( - LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id, - location); -} - -enum lttng_condition_status -lttng_condition_session_rotation_get_session_name( - const struct lttng_condition *condition, - const char **session_name) -{ - struct lttng_condition_session_rotation *rotation; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !is_rotation_condition(condition) || !session_name) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - rotation = container_of(condition, struct lttng_condition_session_rotation, - parent); - if (!rotation->session_name) { - status = LTTNG_CONDITION_STATUS_UNSET; - goto end; - } - *session_name = rotation->session_name; -end: - return status; -} - -enum lttng_condition_status -lttng_condition_session_rotation_set_session_name( - struct lttng_condition *condition, const char *session_name) -{ - char *session_name_copy; - struct lttng_condition_session_rotation *rotation; - enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; - - if (!condition || !is_rotation_condition(condition) || - !session_name || strlen(session_name) == 0) { - status = LTTNG_CONDITION_STATUS_INVALID; - goto end; - } - - rotation = container_of(condition, - struct lttng_condition_session_rotation, parent); - session_name_copy = strdup(session_name); - if (!session_name_copy) { - status = LTTNG_CONDITION_STATUS_ERROR; - goto end; - } - - free(rotation->session_name); - rotation->session_name = session_name_copy; -end: - return status; -} - -static -int lttng_evaluation_session_rotation_serialize( - const struct lttng_evaluation *evaluation, - struct lttng_payload *payload) -{ - int ret; - struct lttng_evaluation_session_rotation *rotation; - struct lttng_evaluation_session_rotation_comm comm = { 0 }; - - rotation = container_of(evaluation, - struct lttng_evaluation_session_rotation, parent); - comm.id = rotation->id; - comm.has_location = !!rotation->location; - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - goto end; - } - if (!rotation->location) { - goto end; - } - ret = lttng_trace_archive_location_serialize(rotation->location, - &payload->buffer); -end: - return ret; -} - -static -void lttng_evaluation_session_rotation_destroy( - struct lttng_evaluation *evaluation) -{ - struct lttng_evaluation_session_rotation *rotation; - - rotation = container_of(evaluation, - struct lttng_evaluation_session_rotation, parent); - lttng_trace_archive_location_put(rotation->location); - free(rotation); -} - -enum lttng_evaluation_status -lttng_evaluation_session_rotation_get_id( - const struct lttng_evaluation *evaluation, uint64_t *id) -{ - const struct lttng_evaluation_session_rotation *rotation; - enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; - - if (!evaluation || !id || !is_rotation_evaluation(evaluation)) { - status = LTTNG_EVALUATION_STATUS_INVALID; - goto end; - } - - rotation = container_of(evaluation, - struct lttng_evaluation_session_rotation, parent); - *id = rotation->id; -end: - return status; -} - -/* - * The public API assumes that trace archive locations are always provided as - * "constant". This means that the user of liblttng-ctl never has to destroy a - * trace archive location. Hence, users of liblttng-ctl have no visibility of - * the reference counting of archive locations. - */ -enum lttng_evaluation_status -lttng_evaluation_session_rotation_completed_get_location( - const struct lttng_evaluation *evaluation, - const struct lttng_trace_archive_location **location) -{ - const struct lttng_evaluation_session_rotation *rotation; - enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; - - if (!evaluation || !location || - evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) { - status = LTTNG_EVALUATION_STATUS_INVALID; - goto end; - } - - rotation = container_of(evaluation, - struct lttng_evaluation_session_rotation, parent); - *location = rotation->location; -end: - return status; -} - -static -enum lttng_error_code lttng_condition_session_rotation_mi_serialize( - const struct lttng_condition *condition, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_condition_status status; - const char *session_name = NULL; - const char *type_element_str = NULL; - - LTTNG_ASSERT(condition); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(is_rotation_condition(condition)); - - switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - type_element_str = - mi_lttng_element_condition_session_rotation_completed; - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - type_element_str = - mi_lttng_element_condition_session_rotation_ongoing; - break; - default: - abort(); - break; - } - - status = lttng_condition_session_rotation_get_session_name( - condition, &session_name); - LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); - LTTNG_ASSERT(session_name); - - /* Open condition session rotation_* element. */ - ret = mi_lttng_writer_open_element(writer, type_element_str); - if (ret) { - goto mi_error; - } - - /* Session name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_session_name, session_name); - if (ret) { - goto mi_error; - } - - /* Close condition session rotation element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/conditions/session-rotation.cpp b/src/common/conditions/session-rotation.cpp new file mode 100644 index 000000000..7e59895f9 --- /dev/null +++ b/src/common/conditions/session-rotation.cpp @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static +bool lttng_condition_session_rotation_validate( + const struct lttng_condition *condition); +static +int lttng_condition_session_rotation_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload); +static +bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b); +static +void lttng_condition_session_rotation_destroy( + struct lttng_condition *condition); + +static +enum lttng_error_code lttng_condition_session_rotation_mi_serialize( + const struct lttng_condition *condition, + struct mi_writer *writer); + +static const +struct lttng_condition rotation_condition_template = { + {}, + LTTNG_CONDITION_TYPE_UNKNOWN, /* type unset, shall be set on creation. */ + lttng_condition_session_rotation_validate, + lttng_condition_session_rotation_serialize, + lttng_condition_session_rotation_is_equal, + lttng_condition_session_rotation_destroy, + lttng_condition_session_rotation_mi_serialize, +}; + +static +int lttng_evaluation_session_rotation_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload); +static +void lttng_evaluation_session_rotation_destroy( + struct lttng_evaluation *evaluation); + +static const +struct lttng_evaluation rotation_evaluation_template = { + LTTNG_CONDITION_TYPE_UNKNOWN, /* type unset, shall be set on creation. */ + lttng_evaluation_session_rotation_serialize, + lttng_evaluation_session_rotation_destroy, +}; + +static +bool is_rotation_condition(const struct lttng_condition *condition) +{ + enum lttng_condition_type type = lttng_condition_get_type(condition); + + return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING || + type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED; +} + +static +bool is_rotation_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING || + type == LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED; +} + +static +bool lttng_condition_session_rotation_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_session_rotation *rotation; + + if (!condition) { + goto end; + } + + rotation = container_of(condition, + struct lttng_condition_session_rotation, parent); + if (!rotation->session_name) { + ERR("Invalid session rotation condition: a target session name must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +int lttng_condition_session_rotation_serialize( + const struct lttng_condition *condition, + struct lttng_payload *payload) +{ + int ret; + size_t session_name_len; + struct lttng_condition_session_rotation *rotation; + struct lttng_condition_session_rotation_comm rotation_comm; + + if (!condition || !is_rotation_condition(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing session rotation condition"); + rotation = container_of(condition, struct lttng_condition_session_rotation, + parent); + + session_name_len = strlen(rotation->session_name) + 1; + if (session_name_len > LTTNG_NAME_MAX) { + ret = -1; + goto end; + } + + rotation_comm.session_name_len = session_name_len; + ret = lttng_dynamic_buffer_append(&payload->buffer, &rotation_comm, + sizeof(rotation_comm)); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + rotation->session_name, session_name_len); + if (ret) { + goto end; + } +end: + return ret; +} + +static +bool lttng_condition_session_rotation_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_session_rotation *a, *b; + + a = container_of(_a, struct lttng_condition_session_rotation, parent); + b = container_of(_b, struct lttng_condition_session_rotation, parent); + + /* Both session names must be set or both must be unset. */ + if ((a->session_name && !b->session_name) || + (!a->session_name && b->session_name)) { + WARN("Comparing session rotation conditions with uninitialized session names."); + goto end; + } + + if (a->session_name && b->session_name && + strcmp(a->session_name, b->session_name)) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static +void lttng_condition_session_rotation_destroy( + struct lttng_condition *condition) +{ + struct lttng_condition_session_rotation *rotation; + + rotation = container_of(condition, + struct lttng_condition_session_rotation, parent); + + free(rotation->session_name); + free(rotation); +} + +static +struct lttng_condition *lttng_condition_session_rotation_create( + enum lttng_condition_type type) +{ + struct lttng_condition_session_rotation *condition; + + condition = (lttng_condition_session_rotation *) zmalloc(sizeof(struct lttng_condition_session_rotation)); + if (!condition) { + return NULL; + } + + memcpy(&condition->parent, &rotation_condition_template, + sizeof(condition->parent)); + lttng_condition_init(&condition->parent, type); + return &condition->parent; +} + +struct lttng_condition *lttng_condition_session_rotation_ongoing_create(void) +{ + return lttng_condition_session_rotation_create( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING); +} + +struct lttng_condition *lttng_condition_session_rotation_completed_create(void) +{ + return lttng_condition_session_rotation_create( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED); +} + +static +ssize_t init_condition_from_payload(struct lttng_condition *condition, + struct lttng_payload_view *src_view) +{ + ssize_t ret, condition_size; + enum lttng_condition_status status; + const char *session_name; + struct lttng_buffer_view name_view; + const struct lttng_condition_session_rotation_comm *condition_comm; + struct lttng_payload_view condition_comm_view = + lttng_payload_view_from_view( + src_view, 0, sizeof(*condition_comm)); + + if (!lttng_payload_view_is_valid(&condition_comm_view)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); + ret = -1; + goto end; + } + + condition_comm = (typeof(condition_comm)) src_view->buffer.data; + name_view = lttng_buffer_view_from_view(&src_view->buffer, + sizeof(*condition_comm), condition_comm->session_name_len); + + if (!lttng_buffer_view_is_valid(&name_view)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain session name"); + ret = -1; + goto end; + } + + if (condition_comm->session_name_len > LTTNG_NAME_MAX) { + ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); + ret = -1; + goto end; + } + + session_name = name_view.data; + if (*(session_name + condition_comm->session_name_len - 1) != '\0') { + ERR("Malformed session name encountered in condition buffer"); + ret = -1; + goto end; + } + + status = lttng_condition_session_rotation_set_session_name(condition, + session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer consumed session name"); + ret = -1; + goto end; + } + + if (!lttng_condition_validate(condition)) { + ret = -1; + goto end; + } + + condition_size = sizeof(*condition_comm) + + (ssize_t) condition_comm->session_name_len; + ret = condition_size; +end: + return ret; +} + +static +ssize_t lttng_condition_session_rotation_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **_condition, + enum lttng_condition_type type) +{ + ssize_t ret; + struct lttng_condition *condition = NULL; + + switch (type) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + condition = lttng_condition_session_rotation_ongoing_create(); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + condition = lttng_condition_session_rotation_completed_create(); + break; + default: + ret = -1; + goto error; + } + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_payload(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +ssize_t lttng_condition_session_rotation_ongoing_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition) +{ + return lttng_condition_session_rotation_create_from_payload(view, + condition, + LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING); +} + +ssize_t lttng_condition_session_rotation_completed_create_from_payload( + struct lttng_payload_view *view, + struct lttng_condition **condition) +{ + return lttng_condition_session_rotation_create_from_payload(view, + condition, + LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED); +} + +static +struct lttng_evaluation *lttng_evaluation_session_rotation_create( + enum lttng_condition_type type, uint64_t id, + struct lttng_trace_archive_location *location) +{ + struct lttng_evaluation_session_rotation *evaluation; + + evaluation = (lttng_evaluation_session_rotation *) zmalloc(sizeof(struct lttng_evaluation_session_rotation)); + if (!evaluation) { + return NULL; + } + + memcpy(&evaluation->parent, &rotation_evaluation_template, + sizeof(evaluation->parent)); + lttng_evaluation_init(&evaluation->parent, type); + evaluation->id = id; + if (location) { + lttng_trace_archive_location_get(location); + } + evaluation->location = location; + return &evaluation->parent; +} + +static +ssize_t create_evaluation_from_payload( + enum lttng_condition_type type, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret, size; + struct lttng_evaluation *evaluation = NULL; + struct lttng_trace_archive_location *location = NULL; + const struct lttng_evaluation_session_rotation_comm *comm; + struct lttng_payload_view comm_view = lttng_payload_view_from_view( + view, 0, sizeof(*comm)); + + if (!lttng_payload_view_is_valid(&comm_view)) { + goto error; + } + + comm = (typeof(comm)) comm_view.buffer.data; + size = sizeof(*comm); + if (comm->has_location) { + const struct lttng_buffer_view location_view = + lttng_buffer_view_from_view( + &view->buffer, sizeof(*comm), -1); + + if (!lttng_buffer_view_is_valid(&location_view)) { + goto error; + } + + ret = lttng_trace_archive_location_create_from_buffer( + &location_view, &location); + if (ret < 0) { + goto error; + } + size += ret; + } + + evaluation = lttng_evaluation_session_rotation_create(type, comm->id, + location); + if (!evaluation) { + goto error; + } + + lttng_trace_archive_location_put(location); + ret = size; + *_evaluation = evaluation; + return ret; +error: + lttng_trace_archive_location_put(location); + evaluation = NULL; + return -1; +} + +static +ssize_t lttng_evaluation_session_rotation_create_from_payload( + enum lttng_condition_type type, + struct lttng_payload_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + ret = create_evaluation_from_payload(type, view, &evaluation); + if (ret < 0) { + goto error; + } + + *_evaluation = evaluation; + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +ssize_t lttng_evaluation_session_rotation_ongoing_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **evaluation) +{ + return lttng_evaluation_session_rotation_create_from_payload( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, + view, evaluation); +} + +ssize_t lttng_evaluation_session_rotation_completed_create_from_payload( + struct lttng_payload_view *view, + struct lttng_evaluation **evaluation) +{ + return lttng_evaluation_session_rotation_create_from_payload( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, + view, evaluation); +} + +struct lttng_evaluation *lttng_evaluation_session_rotation_ongoing_create( + uint64_t id) +{ + return lttng_evaluation_session_rotation_create( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING, id, + NULL); +} + +struct lttng_evaluation *lttng_evaluation_session_rotation_completed_create( + uint64_t id, struct lttng_trace_archive_location *location) +{ + return lttng_evaluation_session_rotation_create( + LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED, id, + location); +} + +enum lttng_condition_status +lttng_condition_session_rotation_get_session_name( + const struct lttng_condition *condition, + const char **session_name) +{ + struct lttng_condition_session_rotation *rotation; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !is_rotation_condition(condition) || !session_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + rotation = container_of(condition, struct lttng_condition_session_rotation, + parent); + if (!rotation->session_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *session_name = rotation->session_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_session_rotation_set_session_name( + struct lttng_condition *condition, const char *session_name) +{ + char *session_name_copy; + struct lttng_condition_session_rotation *rotation; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !is_rotation_condition(condition) || + !session_name || strlen(session_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + rotation = container_of(condition, + struct lttng_condition_session_rotation, parent); + session_name_copy = strdup(session_name); + if (!session_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + free(rotation->session_name); + rotation->session_name = session_name_copy; +end: + return status; +} + +static +int lttng_evaluation_session_rotation_serialize( + const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + int ret; + struct lttng_evaluation_session_rotation *rotation; + struct lttng_evaluation_session_rotation_comm comm = { 0 }; + + rotation = container_of(evaluation, + struct lttng_evaluation_session_rotation, parent); + comm.id = rotation->id; + comm.has_location = !!rotation->location; + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + if (!rotation->location) { + goto end; + } + ret = lttng_trace_archive_location_serialize(rotation->location, + &payload->buffer); +end: + return ret; +} + +static +void lttng_evaluation_session_rotation_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_session_rotation *rotation; + + rotation = container_of(evaluation, + struct lttng_evaluation_session_rotation, parent); + lttng_trace_archive_location_put(rotation->location); + free(rotation); +} + +enum lttng_evaluation_status +lttng_evaluation_session_rotation_get_id( + const struct lttng_evaluation *evaluation, uint64_t *id) +{ + const struct lttng_evaluation_session_rotation *rotation; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !id || !is_rotation_evaluation(evaluation)) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + rotation = container_of(evaluation, + struct lttng_evaluation_session_rotation, parent); + *id = rotation->id; +end: + return status; +} + +/* + * The public API assumes that trace archive locations are always provided as + * "constant". This means that the user of liblttng-ctl never has to destroy a + * trace archive location. Hence, users of liblttng-ctl have no visibility of + * the reference counting of archive locations. + */ +enum lttng_evaluation_status +lttng_evaluation_session_rotation_completed_get_location( + const struct lttng_evaluation *evaluation, + const struct lttng_trace_archive_location **location) +{ + const struct lttng_evaluation_session_rotation *rotation; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !location || + evaluation->type != LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + rotation = container_of(evaluation, + struct lttng_evaluation_session_rotation, parent); + *location = rotation->location; +end: + return status; +} + +static +enum lttng_error_code lttng_condition_session_rotation_mi_serialize( + const struct lttng_condition *condition, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_condition_status status; + const char *session_name = NULL; + const char *type_element_str = NULL; + + LTTNG_ASSERT(condition); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(is_rotation_condition(condition)); + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + type_element_str = + mi_lttng_element_condition_session_rotation_completed; + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + type_element_str = + mi_lttng_element_condition_session_rotation_ongoing; + break; + default: + abort(); + break; + } + + status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + LTTNG_ASSERT(status == LTTNG_CONDITION_STATUS_OK); + LTTNG_ASSERT(session_name); + + /* Open condition session rotation_* element. */ + ret = mi_lttng_writer_open_element(writer, type_element_str); + if (ret) { + goto mi_error; + } + + /* Session name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_session_name, session_name); + if (ret) { + goto mi_error; + } + + /* Close condition session rotation element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/context.c b/src/common/context.c deleted file mode 100644 index e2e2b60d6..000000000 --- a/src/common/context.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2016 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "context.h" -#include -#include -#include -#include - -int parse_application_context(const char *str, char **out_provider_name, - char **out_ctx_name) -{ - const char app_ctx_prefix[] = "$app."; - char *provider_name = NULL, *ctx_name = NULL; - size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len; - - if (!str || !out_provider_name || !out_ctx_name) { - goto not_found; - } - - len = strlen(str); - if (len <= sizeof(app_ctx_prefix) - 1) { - goto not_found; - } - - /* String starts with $app. */ - if (strncmp(str, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) { - goto not_found; - } - - /* Validate that the ':' separator is present. */ - for (i = sizeof(app_ctx_prefix); i < len; i++) { - const char c = str[i]; - - if (c == ':') { - colon_pos = i; - break; - } - } - - /* - * No colon found or no ctx name ("$app.provider:") or no provider name - * given ("$app.:..."), which is invalid. - */ - if (!colon_pos || colon_pos == len || - colon_pos == sizeof(app_ctx_prefix)) { - goto not_found; - } - - provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2; - provider_name = zmalloc(provider_name_len); - if (!provider_name) { - PERROR("malloc provider_name"); - goto not_found; - } - strncpy(provider_name, str + sizeof(app_ctx_prefix) - 1, - provider_name_len - 1); - - ctx_name_len = len - colon_pos; - ctx_name = zmalloc(ctx_name_len); - if (!ctx_name) { - PERROR("malloc ctx_name"); - goto not_found; - } - strncpy(ctx_name, str + colon_pos + 1, ctx_name_len - 1); - - *out_provider_name = provider_name; - *out_ctx_name = ctx_name; - return 0; -not_found: - free(provider_name); - free(ctx_name); - return -1; -} - diff --git a/src/common/context.cpp b/src/common/context.cpp new file mode 100644 index 000000000..ab61a8133 --- /dev/null +++ b/src/common/context.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "context.h" +#include +#include +#include +#include + +int parse_application_context(const char *str, char **out_provider_name, + char **out_ctx_name) +{ + const char app_ctx_prefix[] = "$app."; + char *provider_name = NULL, *ctx_name = NULL; + size_t i, len, colon_pos = 0, provider_name_len, ctx_name_len; + + if (!str || !out_provider_name || !out_ctx_name) { + goto not_found; + } + + len = strlen(str); + if (len <= sizeof(app_ctx_prefix) - 1) { + goto not_found; + } + + /* String starts with $app. */ + if (strncmp(str, app_ctx_prefix, sizeof(app_ctx_prefix) - 1)) { + goto not_found; + } + + /* Validate that the ':' separator is present. */ + for (i = sizeof(app_ctx_prefix); i < len; i++) { + const char c = str[i]; + + if (c == ':') { + colon_pos = i; + break; + } + } + + /* + * No colon found or no ctx name ("$app.provider:") or no provider name + * given ("$app.:..."), which is invalid. + */ + if (!colon_pos || colon_pos == len || + colon_pos == sizeof(app_ctx_prefix)) { + goto not_found; + } + + provider_name_len = colon_pos - sizeof(app_ctx_prefix) + 2; + provider_name = (char *) zmalloc(provider_name_len); + if (!provider_name) { + PERROR("malloc provider_name"); + goto not_found; + } + strncpy(provider_name, str + sizeof(app_ctx_prefix) - 1, + provider_name_len - 1); + + ctx_name_len = len - colon_pos; + ctx_name = (char *) zmalloc(ctx_name_len); + if (!ctx_name) { + PERROR("malloc ctx_name"); + goto not_found; + } + strncpy(ctx_name, str + colon_pos + 1, ctx_name_len - 1); + + *out_provider_name = provider_name; + *out_ctx_name = ctx_name; + return 0; +not_found: + free(provider_name); + free(ctx_name); + return -1; +} + diff --git a/src/common/credentials.c b/src/common/credentials.c deleted file mode 100644 index fd8b4477b..000000000 --- a/src/common/credentials.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include "credentials.h" - -uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds) -{ - return LTTNG_OPTIONAL_GET(creds->uid); -} - -gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds) -{ - return LTTNG_OPTIONAL_GET(creds->gid); -} - -bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a, - const struct lttng_credentials *b) -{ - LTTNG_ASSERT(a); - LTTNG_ASSERT(b); - - /* XOR on the is_set value */ - if (!!a->uid.is_set != !!b->uid.is_set) { - return false; - } - - if (!a->uid.is_set && !b->uid.is_set) { - return true; - } - - /* Both a and b are set. */ - return a->uid.value == b->uid.value; -} - -bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a, - const struct lttng_credentials *b) -{ - LTTNG_ASSERT(a); - LTTNG_ASSERT(b); - - /* XOR on the is_set value */ - if (!!a->gid.is_set != !!b->gid.is_set) { - return false; - } - - if (!a->gid.is_set && !b->gid.is_set) { - return true; - } - - /* Both a and b are set. */ - return a->gid.value == b->gid.value; -} - -bool lttng_credentials_is_equal(const struct lttng_credentials *a, - const struct lttng_credentials *b) -{ - LTTNG_ASSERT(a); - LTTNG_ASSERT(b); - - return lttng_credentials_is_equal_uid(a, b) && - lttng_credentials_is_equal_gid(a, b); -} diff --git a/src/common/credentials.cpp b/src/common/credentials.cpp new file mode 100644 index 000000000..fd8b4477b --- /dev/null +++ b/src/common/credentials.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include "credentials.h" + +uid_t lttng_credentials_get_uid(const struct lttng_credentials *creds) +{ + return LTTNG_OPTIONAL_GET(creds->uid); +} + +gid_t lttng_credentials_get_gid(const struct lttng_credentials *creds) +{ + return LTTNG_OPTIONAL_GET(creds->gid); +} + +bool lttng_credentials_is_equal_uid(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + LTTNG_ASSERT(a); + LTTNG_ASSERT(b); + + /* XOR on the is_set value */ + if (!!a->uid.is_set != !!b->uid.is_set) { + return false; + } + + if (!a->uid.is_set && !b->uid.is_set) { + return true; + } + + /* Both a and b are set. */ + return a->uid.value == b->uid.value; +} + +bool lttng_credentials_is_equal_gid(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + LTTNG_ASSERT(a); + LTTNG_ASSERT(b); + + /* XOR on the is_set value */ + if (!!a->gid.is_set != !!b->gid.is_set) { + return false; + } + + if (!a->gid.is_set && !b->gid.is_set) { + return true; + } + + /* Both a and b are set. */ + return a->gid.value == b->gid.value; +} + +bool lttng_credentials_is_equal(const struct lttng_credentials *a, + const struct lttng_credentials *b) +{ + LTTNG_ASSERT(a); + LTTNG_ASSERT(b); + + return lttng_credentials_is_equal_uid(a, b) && + lttng_credentials_is_equal_gid(a, b); +} diff --git a/src/common/daemonize.c b/src/common/daemonize.c deleted file mode 100644 index 16af5a3f7..000000000 --- a/src/common/daemonize.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2013 David Goulet - * Copyright (C) 2014 Mathieu Desnoyers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include - -#include -#include - -int lttng_daemonize(pid_t *child_ppid, int *completion_flag, - int close_fds) -{ - pid_t pid; - - /* Get parent pid of this process. */ - *child_ppid = getppid(); - - pid = fork(); - if (pid < 0) { - PERROR("fork"); - goto error; - } else if (pid == 0) { - int fd; - pid_t sid; - int ret; - - /* Child */ - - /* - * Get the newly created parent pid so we can signal - * that process when we are ready to operate. - */ - *child_ppid = getppid(); - - sid = setsid(); - if (sid < 0) { - PERROR("setsid"); - goto error; - } - - /* - * Try to change directory to /. If we can't well at - * least notify. - */ - ret = chdir("/"); - if (ret < 0) { - PERROR("chdir"); - } - - if (close_fds) { - fd = open(_PATH_DEVNULL, O_RDWR, 0); - if (fd < 0) { - PERROR("open %s", _PATH_DEVNULL); - /* - * Let 0, 1 and 2 open since we can't - * bind them to /dev/null. - */ - } else { - (void) dup2(fd, STDIN_FILENO); - (void) dup2(fd, STDOUT_FILENO); - (void) dup2(fd, STDERR_FILENO); - if (fd > 2) { - ret = close(fd); - if (ret < 0) { - PERROR("close"); - } - } - } - } - goto end; - } else { - /* Parent */ - - /* - * Waiting for child to notify this parent that it can - * exit. Note that sleep() is interrupted before the 1 - * second delay as soon as the signal is received, so it - * will not cause visible delay for the user. - */ - while (!CMM_LOAD_SHARED(*completion_flag)) { - int status; - pid_t ret; - - /* - * Check if child exists without blocking. If - * so, we have to stop this parent process and - * return an error. - */ - ret = waitpid(pid, &status, WNOHANG); - if (ret < 0 || (ret != 0 && WIFEXITED(status))) { - /* The child exited somehow or was not valid. */ - goto error; - } - sleep(1); - } - - /* - * From this point on, the parent can exit and the child - * is now an operational session daemon ready to serve - * clients and applications. - */ - exit(EXIT_SUCCESS); - } - -end: - return 0; - -error: - return -1; -} diff --git a/src/common/daemonize.cpp b/src/common/daemonize.cpp new file mode 100644 index 000000000..16af5a3f7 --- /dev/null +++ b/src/common/daemonize.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2013 David Goulet + * Copyright (C) 2014 Mathieu Desnoyers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include + +#include + +#include +#include + +int lttng_daemonize(pid_t *child_ppid, int *completion_flag, + int close_fds) +{ + pid_t pid; + + /* Get parent pid of this process. */ + *child_ppid = getppid(); + + pid = fork(); + if (pid < 0) { + PERROR("fork"); + goto error; + } else if (pid == 0) { + int fd; + pid_t sid; + int ret; + + /* Child */ + + /* + * Get the newly created parent pid so we can signal + * that process when we are ready to operate. + */ + *child_ppid = getppid(); + + sid = setsid(); + if (sid < 0) { + PERROR("setsid"); + goto error; + } + + /* + * Try to change directory to /. If we can't well at + * least notify. + */ + ret = chdir("/"); + if (ret < 0) { + PERROR("chdir"); + } + + if (close_fds) { + fd = open(_PATH_DEVNULL, O_RDWR, 0); + if (fd < 0) { + PERROR("open %s", _PATH_DEVNULL); + /* + * Let 0, 1 and 2 open since we can't + * bind them to /dev/null. + */ + } else { + (void) dup2(fd, STDIN_FILENO); + (void) dup2(fd, STDOUT_FILENO); + (void) dup2(fd, STDERR_FILENO); + if (fd > 2) { + ret = close(fd); + if (ret < 0) { + PERROR("close"); + } + } + } + } + goto end; + } else { + /* Parent */ + + /* + * Waiting for child to notify this parent that it can + * exit. Note that sleep() is interrupted before the 1 + * second delay as soon as the signal is received, so it + * will not cause visible delay for the user. + */ + while (!CMM_LOAD_SHARED(*completion_flag)) { + int status; + pid_t ret; + + /* + * Check if child exists without blocking. If + * so, we have to stop this parent process and + * return an error. + */ + ret = waitpid(pid, &status, WNOHANG); + if (ret < 0 || (ret != 0 && WIFEXITED(status))) { + /* The child exited somehow or was not valid. */ + goto error; + } + sleep(1); + } + + /* + * From this point on, the parent can exit and the child + * is now an operational session daemon ready to serve + * clients and applications. + */ + exit(EXIT_SUCCESS); + } + +end: + return 0; + +error: + return -1; +} diff --git a/src/common/defaults.c b/src/common/defaults.c deleted file mode 100644 index 2550784cf..000000000 --- a/src/common/defaults.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2012 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include "defaults.h" -#include "macros.h" -#include "error.h" - -static int pthread_attr_init_done; -static pthread_attr_t tattr; - -static size_t get_page_size(void) -{ - const long ret = sysconf(_SC_PAGE_SIZE); - - if (ret < 0) { - /* - * Fatal error since there is no safe way to recover from this. - */ - PERROR("Failed to get system page size using sysconf(_SC_PAGE_SIZE)"); - abort(); - } - - return (size_t) ret; -} - -size_t default_get_channel_subbuf_size(void) -{ - return max(_DEFAULT_CHANNEL_SUBBUF_SIZE, get_page_size()); -} - -size_t default_get_metadata_subbuf_size(void) -{ - return max(DEFAULT_METADATA_SUBBUF_SIZE, get_page_size()); -} - -size_t default_get_kernel_channel_subbuf_size(void) -{ - return max(DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE, get_page_size()); -} - -size_t default_get_ust_pid_channel_subbuf_size(void) -{ - return max(DEFAULT_UST_PID_CHANNEL_SUBBUF_SIZE, get_page_size()); -} - -size_t default_get_ust_uid_channel_subbuf_size(void) -{ - return max(DEFAULT_UST_UID_CHANNEL_SUBBUF_SIZE, get_page_size()); -} - -pthread_attr_t *default_pthread_attr(void) -{ - if (pthread_attr_init_done) { - return &tattr; - } - - WARN("Uninitialized pthread attributes, using libc defaults."); - return NULL; -} - -static void __attribute__((constructor)) init_default_pthread_attr(void) -{ - int ret; - struct rlimit rlim; - size_t pthread_ss, system_ss, selected_ss; - - ret = pthread_attr_init(&tattr); - if (ret) { - errno = ret; - PERROR("pthread_attr_init"); - goto error; - } - - /* Get system stack size limits. */ - ret = getrlimit(RLIMIT_STACK, &rlim); - if (ret < 0) { - PERROR("getrlimit"); - goto error_destroy; - } - DBG("Stack size limits: soft %lld, hard %lld bytes", - (long long) rlim.rlim_cur, - (long long) rlim.rlim_max); - - /* - * getrlimit() may return a stack size of "-1", meaning "unlimited". - * In this case, we impose a known-good default minimum value which will - * override the libc's default stack size if it is smaller. - */ - system_ss = rlim.rlim_cur != -1 ? rlim.rlim_cur : - DEFAULT_LTTNG_THREAD_STACK_SIZE; - - /* Get pthread default thread stack size. */ - ret = pthread_attr_getstacksize(&tattr, &pthread_ss); - if (ret < 0) { - PERROR("pthread_attr_getstacksize"); - goto error_destroy; - } - DBG("Default pthread stack size is %zu bytes", pthread_ss); - - selected_ss = max_t(size_t, pthread_ss, system_ss); - if (selected_ss < DEFAULT_LTTNG_THREAD_STACK_SIZE) { - DBG("Default stack size is too small, setting it to %zu bytes", - (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE); - selected_ss = DEFAULT_LTTNG_THREAD_STACK_SIZE; - } - - if (rlim.rlim_max > 0 && selected_ss > rlim.rlim_max) { - WARN("Your system's stack size restrictions (%zu bytes) may be too low for the LTTng daemons to function properly, please set the stack size limit to at least %zu bytes to ensure reliable operation", - (size_t) rlim.rlim_max, (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE); - selected_ss = (size_t) rlim.rlim_max; - } - - ret = pthread_attr_setstacksize(&tattr, selected_ss); - if (ret < 0) { - PERROR("pthread_attr_setstacksize"); - goto error_destroy; - } - pthread_attr_init_done = 1; -error: - return; -error_destroy: - ret = pthread_attr_destroy(&tattr); - if (ret) { - errno = ret; - PERROR("pthread_attr_destroy"); - } -} - -static void __attribute__((destructor)) fini_default_pthread_attr(void) -{ - int ret; - - if (!pthread_attr_init_done) { - return; - } - - ret = pthread_attr_destroy(&tattr); - if (ret) { - errno = ret; - PERROR("pthread_attr_destroy"); - } -} diff --git a/src/common/defaults.cpp b/src/common/defaults.cpp new file mode 100644 index 000000000..5ecb435bb --- /dev/null +++ b/src/common/defaults.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include + +#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(_DEFAULT_CHANNEL_SUBBUF_SIZE, get_page_size()); +} + +size_t default_get_metadata_subbuf_size(void) +{ + return std::max(DEFAULT_METADATA_SUBBUF_SIZE, get_page_size()); +} + +size_t default_get_kernel_channel_subbuf_size(void) +{ + return std::max(DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE, get_page_size()); +} + +size_t default_get_ust_pid_channel_subbuf_size(void) +{ + return std::max(DEFAULT_UST_PID_CHANNEL_SUBBUF_SIZE, get_page_size()); +} + +size_t default_get_ust_uid_channel_subbuf_size(void) +{ + return std::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 = std::max(pthread_ss, system_ss); + if (selected_ss < DEFAULT_LTTNG_THREAD_STACK_SIZE) { + DBG("Default stack size is too small, setting it to %zu bytes", + (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE); + selected_ss = DEFAULT_LTTNG_THREAD_STACK_SIZE; + } + + if (rlim.rlim_max > 0 && selected_ss > rlim.rlim_max) { + WARN("Your system's stack size restrictions (%zu bytes) may be too low for the LTTng daemons to function properly, please set the stack size limit to at least %zu bytes to ensure reliable operation", + (size_t) rlim.rlim_max, (size_t) DEFAULT_LTTNG_THREAD_STACK_SIZE); + selected_ss = (size_t) rlim.rlim_max; + } + + ret = pthread_attr_setstacksize(&tattr, selected_ss); + if (ret < 0) { + PERROR("pthread_attr_setstacksize"); + goto error_destroy; + } + pthread_attr_init_done = 1; +error: + return; +error_destroy: + ret = pthread_attr_destroy(&tattr); + if (ret) { + errno = ret; + PERROR("pthread_attr_destroy"); + } +} + +static void __attribute__((destructor)) fini_default_pthread_attr(void) +{ + int ret; + + if (!pthread_attr_init_done) { + return; + } + + ret = pthread_attr_destroy(&tattr); + if (ret) { + errno = ret; + PERROR("pthread_attr_destroy"); + } +} diff --git a/src/common/domain.c b/src/common/domain.c deleted file mode 100644 index fb088faca..000000000 --- a/src/common/domain.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2020 Simon Marchi - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "lttng/domain-internal.h" -#include "common/macros.h" - -const char *lttng_domain_type_str(enum lttng_domain_type domain_type) -{ - switch (domain_type) { - case LTTNG_DOMAIN_NONE: - return "none"; - case LTTNG_DOMAIN_KERNEL: - return "kernel"; - case LTTNG_DOMAIN_UST: - return "ust"; - case LTTNG_DOMAIN_JUL: - return "jul"; - case LTTNG_DOMAIN_LOG4J: - return "log4j"; - case LTTNG_DOMAIN_PYTHON: - return "python"; - default: - return "???"; - } -} diff --git a/src/common/domain.cpp b/src/common/domain.cpp new file mode 100644 index 000000000..fb088faca --- /dev/null +++ b/src/common/domain.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/domain-internal.h" +#include "common/macros.h" + +const char *lttng_domain_type_str(enum lttng_domain_type domain_type) +{ + switch (domain_type) { + case LTTNG_DOMAIN_NONE: + return "none"; + case LTTNG_DOMAIN_KERNEL: + return "kernel"; + case LTTNG_DOMAIN_UST: + return "ust"; + case LTTNG_DOMAIN_JUL: + return "jul"; + case LTTNG_DOMAIN_LOG4J: + return "log4j"; + case LTTNG_DOMAIN_PYTHON: + return "python"; + default: + return "???"; + } +} diff --git a/src/common/dynamic-array.c b/src/common/dynamic-array.c deleted file mode 100644 index d723ffacb..000000000 --- a/src/common/dynamic-array.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include - -void lttng_dynamic_array_init(struct lttng_dynamic_array *array, - size_t element_size, - lttng_dynamic_array_element_destructor destructor) -{ - lttng_dynamic_buffer_init(&array->buffer); - array->element_size = element_size; - array->size = 0; - array->destructor = destructor; -} - -int lttng_dynamic_array_set_count(struct lttng_dynamic_array *array, - size_t new_element_count) -{ - int ret; - - if (!array) { - ret = -1; - goto end; - } - - if (array->destructor) { - size_t i; - - for (i = new_element_count; i < array->size; i++) { - void *element = lttng_dynamic_array_get_element( - array, i); - - array->destructor(element); - } - } - - array->size = new_element_count; - ret = lttng_dynamic_buffer_set_size(&array->buffer, - new_element_count * array->element_size); -end: - return ret; -} - -int lttng_dynamic_array_add_element(struct lttng_dynamic_array *array, - const void *element) -{ - int ret; - - if (!array || !element) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(&array->buffer, element, - array->element_size); - if (ret) { - goto end; - } - array->size++; -end: - return ret; -} - -int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array, - size_t element_index) -{ - void *element = lttng_dynamic_array_get_element(array, - element_index); - - if (array->destructor) { - array->destructor(element); - } - if (element_index != lttng_dynamic_array_get_count(array) - 1) { - void *next_element = lttng_dynamic_array_get_element(array, - element_index + 1); - - memmove(element, next_element, - (array->size - element_index - 1) * array->element_size); - } - array->size--; - return lttng_dynamic_buffer_set_size(&array->buffer, - array->buffer.size - array->element_size); -} - -void lttng_dynamic_array_reset(struct lttng_dynamic_array *array) -{ - if (array->destructor) { - size_t i; - - for (i = 0; i < lttng_dynamic_array_get_count(array); i++) { - array->destructor(lttng_dynamic_array_get_element(array, - i)); - } - } - - lttng_dynamic_buffer_reset(&array->buffer); - array->size = 0; -} - -void lttng_dynamic_array_clear(struct lttng_dynamic_array *array) -{ - if (array->destructor) { - size_t i; - - for (i = 0; i < lttng_dynamic_array_get_count(array); i++) { - array->destructor(lttng_dynamic_array_get_element(array, - i)); - } - } - - (void) lttng_dynamic_buffer_set_size(&array->buffer, 0); - array->size = 0; -} - -void lttng_dynamic_pointer_array_init( - struct lttng_dynamic_pointer_array *array, - lttng_dynamic_pointer_array_destructor destructor) -{ - lttng_dynamic_array_init(&array->array, sizeof(void *), destructor); -} - -int lttng_dynamic_pointer_array_remove_pointer( - struct lttng_dynamic_pointer_array *array, size_t index) -{ - int ret; - const lttng_dynamic_array_element_destructor destructor = - array->array.destructor; - - /* - * Prevent the destructor from being used by the underlying - * dynamic array. - */ - array->array.destructor = NULL; - if (destructor) { - destructor(lttng_dynamic_pointer_array_get_pointer(array, - index)); - } - ret = lttng_dynamic_array_remove_element(&array->array, index); - array->array.destructor = destructor; - return ret; -} - -/* Release any memory used by the dynamic array. */ -void lttng_dynamic_pointer_array_reset( - struct lttng_dynamic_pointer_array *array) -{ - if (array->array.destructor) { - size_t i, count = lttng_dynamic_pointer_array_get_count(array); - - for (i = 0; i < count; i++) { - void *ptr = lttng_dynamic_pointer_array_get_pointer( - array, i); - array->array.destructor(ptr); - } - /* - * Prevent the destructor from being used by the underlying - * dynamic array. - */ - array->array.destructor = NULL; - } - lttng_dynamic_array_reset(&array->array); -} - -void lttng_dynamic_pointer_array_clear( - struct lttng_dynamic_pointer_array *array) -{ - const lttng_dynamic_array_element_destructor destructor = - array->array.destructor; - - /* - * Prevent the destructor from being used by the underlying - * dynamic array. - */ - array->array.destructor = NULL; - if (destructor) { - size_t i, count = lttng_dynamic_pointer_array_get_count(array); - - for (i = 0; i < count; i++) { - void *ptr = lttng_dynamic_pointer_array_get_pointer( - array, i); - destructor(ptr); - } - } - lttng_dynamic_array_clear(&array->array); - array->array.destructor = destructor; -} diff --git a/src/common/dynamic-array.cpp b/src/common/dynamic-array.cpp new file mode 100644 index 000000000..d723ffacb --- /dev/null +++ b/src/common/dynamic-array.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +void lttng_dynamic_array_init(struct lttng_dynamic_array *array, + size_t element_size, + lttng_dynamic_array_element_destructor destructor) +{ + lttng_dynamic_buffer_init(&array->buffer); + array->element_size = element_size; + array->size = 0; + array->destructor = destructor; +} + +int lttng_dynamic_array_set_count(struct lttng_dynamic_array *array, + size_t new_element_count) +{ + int ret; + + if (!array) { + ret = -1; + goto end; + } + + if (array->destructor) { + size_t i; + + for (i = new_element_count; i < array->size; i++) { + void *element = lttng_dynamic_array_get_element( + array, i); + + array->destructor(element); + } + } + + array->size = new_element_count; + ret = lttng_dynamic_buffer_set_size(&array->buffer, + new_element_count * array->element_size); +end: + return ret; +} + +int lttng_dynamic_array_add_element(struct lttng_dynamic_array *array, + const void *element) +{ + int ret; + + if (!array || !element) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&array->buffer, element, + array->element_size); + if (ret) { + goto end; + } + array->size++; +end: + return ret; +} + +int lttng_dynamic_array_remove_element(struct lttng_dynamic_array *array, + size_t element_index) +{ + void *element = lttng_dynamic_array_get_element(array, + element_index); + + if (array->destructor) { + array->destructor(element); + } + if (element_index != lttng_dynamic_array_get_count(array) - 1) { + void *next_element = lttng_dynamic_array_get_element(array, + element_index + 1); + + memmove(element, next_element, + (array->size - element_index - 1) * array->element_size); + } + array->size--; + return lttng_dynamic_buffer_set_size(&array->buffer, + array->buffer.size - array->element_size); +} + +void lttng_dynamic_array_reset(struct lttng_dynamic_array *array) +{ + if (array->destructor) { + size_t i; + + for (i = 0; i < lttng_dynamic_array_get_count(array); i++) { + array->destructor(lttng_dynamic_array_get_element(array, + i)); + } + } + + lttng_dynamic_buffer_reset(&array->buffer); + array->size = 0; +} + +void lttng_dynamic_array_clear(struct lttng_dynamic_array *array) +{ + if (array->destructor) { + size_t i; + + for (i = 0; i < lttng_dynamic_array_get_count(array); i++) { + array->destructor(lttng_dynamic_array_get_element(array, + i)); + } + } + + (void) lttng_dynamic_buffer_set_size(&array->buffer, 0); + array->size = 0; +} + +void lttng_dynamic_pointer_array_init( + struct lttng_dynamic_pointer_array *array, + lttng_dynamic_pointer_array_destructor destructor) +{ + lttng_dynamic_array_init(&array->array, sizeof(void *), destructor); +} + +int lttng_dynamic_pointer_array_remove_pointer( + struct lttng_dynamic_pointer_array *array, size_t index) +{ + int ret; + const lttng_dynamic_array_element_destructor destructor = + array->array.destructor; + + /* + * Prevent the destructor from being used by the underlying + * dynamic array. + */ + array->array.destructor = NULL; + if (destructor) { + destructor(lttng_dynamic_pointer_array_get_pointer(array, + index)); + } + ret = lttng_dynamic_array_remove_element(&array->array, index); + array->array.destructor = destructor; + return ret; +} + +/* Release any memory used by the dynamic array. */ +void lttng_dynamic_pointer_array_reset( + struct lttng_dynamic_pointer_array *array) +{ + if (array->array.destructor) { + size_t i, count = lttng_dynamic_pointer_array_get_count(array); + + for (i = 0; i < count; i++) { + void *ptr = lttng_dynamic_pointer_array_get_pointer( + array, i); + array->array.destructor(ptr); + } + /* + * Prevent the destructor from being used by the underlying + * dynamic array. + */ + array->array.destructor = NULL; + } + lttng_dynamic_array_reset(&array->array); +} + +void lttng_dynamic_pointer_array_clear( + struct lttng_dynamic_pointer_array *array) +{ + const lttng_dynamic_array_element_destructor destructor = + array->array.destructor; + + /* + * Prevent the destructor from being used by the underlying + * dynamic array. + */ + array->array.destructor = NULL; + if (destructor) { + size_t i, count = lttng_dynamic_pointer_array_get_count(array); + + for (i = 0; i < count; i++) { + void *ptr = lttng_dynamic_pointer_array_get_pointer( + array, i); + destructor(ptr); + } + } + lttng_dynamic_array_clear(&array->array); + array->array.destructor = destructor; +} diff --git a/src/common/dynamic-buffer.c b/src/common/dynamic-buffer.c deleted file mode 100644 index d6ad6740f..000000000 --- a/src/common/dynamic-buffer.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include - -/* - * Round to (upper) power of two, val is returned if it already is a power of - * two. - */ -static -size_t round_to_power_of_2(size_t val) -{ - size_t rounded; - const int order = utils_get_count_order_u64(val); - - LTTNG_ASSERT(order >= 0); - rounded = (1ULL << order); - LTTNG_ASSERT(rounded >= val); - - return rounded; -} - -void lttng_dynamic_buffer_init(struct lttng_dynamic_buffer *buffer) -{ - LTTNG_ASSERT(buffer); - memset(buffer, 0, sizeof(*buffer)); -} - -int lttng_dynamic_buffer_append(struct lttng_dynamic_buffer *buffer, - const void *buf, size_t len) -{ - int ret = 0; - - if (!buffer || (!buf && len)) { - ret = -1; - goto end; - } - - if (len == 0) { - /* Not an error, no-op. */ - goto end; - } - - LTTNG_ASSERT(buffer->_capacity >= buffer->size); - if (buffer->_capacity < (len + buffer->size)) { - ret = lttng_dynamic_buffer_set_capacity(buffer, - buffer->_capacity + - (len - (buffer->_capacity - buffer->size))); - if (ret) { - goto end; - } - } - - memcpy(buffer->data + buffer->size, buf, len); - buffer->size += len; -end: - return ret; -} - -int lttng_dynamic_buffer_append_buffer(struct lttng_dynamic_buffer *dst_buffer, - const struct lttng_dynamic_buffer *src_buffer) -{ - int ret; - - if (!dst_buffer || !src_buffer) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(dst_buffer, src_buffer->data, - src_buffer->size); -end: - return ret; -} - -int lttng_dynamic_buffer_append_view(struct lttng_dynamic_buffer *buffer, - const struct lttng_buffer_view *src) -{ - int ret; - - if (!buffer || !src) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_buffer_append(buffer, src->data, - src->size); -end: - return ret; -} - -int lttng_dynamic_buffer_set_size(struct lttng_dynamic_buffer *buffer, - size_t new_size) -{ - int ret = 0; - - if (!buffer) { - goto end; - } - - if (new_size == buffer->size) { - goto end; - } - - if (new_size > buffer->_capacity) { - ret = lttng_dynamic_buffer_set_capacity(buffer, new_size); - if (ret) { - goto end; - } - - memset(buffer->data + buffer->size, 0, new_size - buffer->size); - } else if (new_size > buffer->size) { - memset(buffer->data + buffer->size, 0, new_size - buffer->size); - } else { - /* - * Shrinking size. There is no need to zero-out the newly - * released memory as it will either be: - * - overwritten by lttng_dynamic_buffer_append, - * - expanded later, which will zero-out the memory - * - * Users of external APIs are encouraged to set the buffer's - * size _before_ making such calls. - */ - } - - buffer->size = new_size; -end: - return ret; -} - -int lttng_dynamic_buffer_set_capacity(struct lttng_dynamic_buffer *buffer, - size_t demanded_capacity) -{ - int ret = 0; - void *new_buf; - size_t new_capacity = demanded_capacity ? - round_to_power_of_2(demanded_capacity) : 0; - - if (!buffer || demanded_capacity < buffer->size) { - /* - * Shrinking a buffer's size by changing its capacity is - * unsupported. - */ - ret = -1; - goto end; - } - - if (new_capacity == buffer->_capacity) { - goto end; - } - - /* Memory is initialized by the size increases. */ - new_buf = realloc(buffer->data, new_capacity); - if (!new_buf) { - ret = -1; - goto end; - } - - buffer->data = new_buf; - buffer->_capacity = new_capacity; -end: - return ret; -} - -/* Release any memory used by the dynamic buffer. */ -void lttng_dynamic_buffer_reset(struct lttng_dynamic_buffer *buffer) -{ - if (!buffer) { - return; - } - - buffer->size = 0; - buffer->_capacity = 0; - free(buffer->data); - buffer->data = NULL; -} - -size_t lttng_dynamic_buffer_get_capacity_left( - struct lttng_dynamic_buffer *buffer) -{ - if (!buffer) { - return 0; - } - - return buffer->_capacity - buffer->size; -} diff --git a/src/common/dynamic-buffer.cpp b/src/common/dynamic-buffer.cpp new file mode 100644 index 000000000..945434ca9 --- /dev/null +++ b/src/common/dynamic-buffer.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include + +/* + * Round to (upper) power of two, val is returned if it already is a power of + * two. + */ +static +size_t round_to_power_of_2(size_t val) +{ + size_t rounded; + const int order = utils_get_count_order_u64(val); + + LTTNG_ASSERT(order >= 0); + rounded = (1ULL << order); + LTTNG_ASSERT(rounded >= val); + + return rounded; +} + +void lttng_dynamic_buffer_init(struct lttng_dynamic_buffer *buffer) +{ + LTTNG_ASSERT(buffer); + memset(buffer, 0, sizeof(*buffer)); +} + +int lttng_dynamic_buffer_append(struct lttng_dynamic_buffer *buffer, + const void *buf, size_t len) +{ + int ret = 0; + + if (!buffer || (!buf && len)) { + ret = -1; + goto end; + } + + if (len == 0) { + /* Not an error, no-op. */ + goto end; + } + + LTTNG_ASSERT(buffer->_capacity >= buffer->size); + if (buffer->_capacity < (len + buffer->size)) { + ret = lttng_dynamic_buffer_set_capacity(buffer, + buffer->_capacity + + (len - (buffer->_capacity - buffer->size))); + if (ret) { + goto end; + } + } + + memcpy(buffer->data + buffer->size, buf, len); + buffer->size += len; +end: + return ret; +} + +int lttng_dynamic_buffer_append_buffer(struct lttng_dynamic_buffer *dst_buffer, + const struct lttng_dynamic_buffer *src_buffer) +{ + int ret; + + if (!dst_buffer || !src_buffer) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(dst_buffer, src_buffer->data, + src_buffer->size); +end: + return ret; +} + +int lttng_dynamic_buffer_append_view(struct lttng_dynamic_buffer *buffer, + const struct lttng_buffer_view *src) +{ + int ret; + + if (!buffer || !src) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(buffer, src->data, + src->size); +end: + return ret; +} + +int lttng_dynamic_buffer_set_size(struct lttng_dynamic_buffer *buffer, + size_t new_size) +{ + int ret = 0; + + if (!buffer) { + goto end; + } + + if (new_size == buffer->size) { + goto end; + } + + if (new_size > buffer->_capacity) { + ret = lttng_dynamic_buffer_set_capacity(buffer, new_size); + if (ret) { + goto end; + } + + memset(buffer->data + buffer->size, 0, new_size - buffer->size); + } else if (new_size > buffer->size) { + memset(buffer->data + buffer->size, 0, new_size - buffer->size); + } else { + /* + * Shrinking size. There is no need to zero-out the newly + * released memory as it will either be: + * - overwritten by lttng_dynamic_buffer_append, + * - expanded later, which will zero-out the memory + * + * Users of external APIs are encouraged to set the buffer's + * size _before_ making such calls. + */ + } + + buffer->size = new_size; +end: + return ret; +} + +int lttng_dynamic_buffer_set_capacity(struct lttng_dynamic_buffer *buffer, + size_t demanded_capacity) +{ + int ret = 0; + void *new_buf; + size_t new_capacity = demanded_capacity ? + round_to_power_of_2(demanded_capacity) : 0; + + if (!buffer || demanded_capacity < buffer->size) { + /* + * Shrinking a buffer's size by changing its capacity is + * unsupported. + */ + ret = -1; + goto end; + } + + if (new_capacity == buffer->_capacity) { + goto end; + } + + /* Memory is initialized by the size increases. */ + new_buf = realloc(buffer->data, new_capacity); + if (!new_buf) { + ret = -1; + goto end; + } + + buffer->data = (char *) new_buf; + buffer->_capacity = new_capacity; +end: + return ret; +} + +/* Release any memory used by the dynamic buffer. */ +void lttng_dynamic_buffer_reset(struct lttng_dynamic_buffer *buffer) +{ + if (!buffer) { + return; + } + + buffer->size = 0; + buffer->_capacity = 0; + free(buffer->data); + buffer->data = NULL; +} + +size_t lttng_dynamic_buffer_get_capacity_left( + struct lttng_dynamic_buffer *buffer) +{ + if (!buffer) { + return 0; + } + + return buffer->_capacity - buffer->size; +} diff --git a/src/common/endpoint.c b/src/common/endpoint.c deleted file mode 100644 index 3a0bbadcb..000000000 --- a/src/common/endpoint.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include - -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; diff --git a/src/common/endpoint.cpp b/src/common/endpoint.cpp new file mode 100644 index 000000000..3a0bbadcb --- /dev/null +++ b/src/common/endpoint.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +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; diff --git a/src/common/error-query.c b/src/common/error-query.c deleted file mode 100644 index d6bca73b7..000000000 --- a/src/common/error-query.c +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * error-query.c - * - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct lttng_error_query { - enum lttng_error_query_target_type target_type; -}; - -struct lttng_error_query_comm { - /* enum lttng_error_query_target_type */ - int8_t target_type; - /* Target-specific payload. */ - char payload[]; -}; - -struct lttng_error_query_trigger { - struct lttng_error_query parent; - /* Mutable only because of the reference count. */ - struct lttng_trigger *trigger; -}; - -struct lttng_error_query_condition { - struct lttng_error_query parent; - /* Mutable only because of the reference count. */ - struct lttng_trigger *trigger; -}; - -struct lttng_error_query_action { - struct lttng_error_query parent; - /* Mutable only because of the reference count. */ - struct lttng_trigger *trigger; - struct lttng_action_path action_path; -}; - -struct lttng_error_query_result { - enum lttng_error_query_result_type type; - char *name; - char *description; -}; - -struct lttng_error_query_result_comm { - /* enum lttng_error_query_result_type */ - uint8_t type; - /* Length of name (including null-terminator). */ - uint32_t name_len; - /* Length of description (including null-terminator). */ - uint32_t description_len; - /* Name, description, and type-specific payload follow. */ - char payload[]; -} LTTNG_PACKED; - -struct lttng_error_query_result_counter_comm { - uint64_t value; -} LTTNG_PACKED; - -struct lttng_error_query_result_counter { - struct lttng_error_query_result parent; - uint64_t value; -}; - -struct lttng_error_query_results_comm { - uint32_t count; - /* `count` instances of `struct lttng_error_query_result` follow. */ - char payload[]; -} LTTNG_PACKED; - -struct lttng_error_query_results { - struct lttng_dynamic_pointer_array results; -}; - -static -enum lttng_error_code lttng_error_query_result_mi_serialize( - const struct lttng_error_query_result *result, - struct mi_writer *writer); - -static -enum lttng_error_code lttng_error_query_result_counter_mi_serialize( - const struct lttng_error_query_result *result, - struct mi_writer *writer); - -struct lttng_error_query *lttng_error_query_trigger_create( - const struct lttng_trigger *trigger) -{ - struct lttng_error_query_trigger *query = NULL; - struct lttng_trigger *trigger_copy = NULL; - - if (!trigger) { - goto end; - } - - trigger_copy = lttng_trigger_copy(trigger); - if (!trigger_copy) { - goto end; - } - - query = zmalloc(sizeof(*query)); - if (!query) { - PERROR("Failed to allocate trigger error query"); - goto error; - } - - query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER; - query->trigger = trigger_copy; - trigger_copy = NULL; - -error: - lttng_trigger_put(trigger_copy); -end: - return query ? &query->parent : NULL; -} - -struct lttng_error_query *lttng_error_query_condition_create( - const struct lttng_trigger *trigger) -{ - struct lttng_error_query_condition *query = NULL; - struct lttng_trigger *trigger_copy = NULL; - - if (!trigger) { - goto end; - } - - trigger_copy = lttng_trigger_copy(trigger); - if (!trigger_copy) { - goto end; - } - - query = zmalloc(sizeof(*query)); - if (!query) { - PERROR("Failed to allocate condition error query"); - goto error; - } - - query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION; - query->trigger = trigger_copy; - trigger_copy = NULL; - -error: - lttng_trigger_put(trigger_copy); -end: - return query ? &query->parent : NULL; -} - -static -struct lttng_action *get_trigger_action_from_path( - struct lttng_trigger *trigger, - const struct lttng_action_path *action_path) -{ - size_t index_count, i; - enum lttng_action_path_status path_status; - struct lttng_action *current_action = NULL; - - path_status = lttng_action_path_get_index_count( - action_path, &index_count); - if (path_status != LTTNG_ACTION_PATH_STATUS_OK) { - goto end; - } - - current_action = lttng_trigger_get_action(trigger); - for (i = 0; i < index_count; i++) { - uint64_t path_index; - - path_status = lttng_action_path_get_index_at_index( - action_path, i, &path_index); - current_action = lttng_action_list_borrow_mutable_at_index( - current_action, path_index); - if (!current_action) { - /* Invalid action path. */ - goto end; - } - } - -end: - return current_action; -} - -static -bool is_valid_action_path(const struct lttng_trigger *trigger, - const struct lttng_action_path *action_path) -{ - /* - * While 'trigger's constness is casted-away, the trigger and resulting - * action are not modified; we merely check for the action's existence. - */ - return !!get_trigger_action_from_path( - (struct lttng_trigger *) trigger, action_path); -} - -struct lttng_error_query *lttng_error_query_action_create( - const struct lttng_trigger *trigger, - const struct lttng_action_path *action_path) -{ - struct lttng_error_query_action *query = NULL; - struct lttng_trigger *trigger_copy = NULL; - int ret_copy; - - if (!trigger || !action_path || - !is_valid_action_path(trigger, action_path)) { - goto end; - } - - trigger_copy = lttng_trigger_copy(trigger); - if (!trigger_copy) { - goto end; - } - - query = zmalloc(sizeof(*query)); - if (!query) { - PERROR("Failed to allocate action error query"); - goto error; - } - - ret_copy = lttng_action_path_copy(action_path, &query->action_path); - if (ret_copy) { - goto error; - } - - query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION; - query->trigger = trigger_copy; - trigger_copy = NULL; - goto end; - -error: - lttng_trigger_put(trigger_copy); - lttng_error_query_destroy(query ? &query->parent : NULL); -end: - return query ? &query->parent : NULL; -} - -void lttng_error_query_destroy(struct lttng_error_query *query) -{ - struct lttng_error_query_trigger *trigger_query; - - if (!query) { - return; - } - - trigger_query = container_of(query, typeof(*trigger_query), parent); - lttng_trigger_put(trigger_query->trigger); - free(trigger_query); -} - -static -int lttng_error_query_result_counter_serialize( - const struct lttng_error_query_result *result, - struct lttng_payload *payload) -{ - const struct lttng_error_query_result_counter *counter_result; - - LTTNG_ASSERT(result->type == LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER); - counter_result = container_of(result, typeof(*counter_result), parent); - - return lttng_dynamic_buffer_append(&payload->buffer, - &(struct lttng_error_query_result_counter_comm) { - .value = counter_result->value - }, - sizeof(struct lttng_error_query_result_counter_comm)); -} - -int lttng_error_query_result_serialize( - const struct lttng_error_query_result *result, - struct lttng_payload *payload) -{ - int ret; - struct lttng_error_query_result_comm header = { - .type = (uint8_t) result->type, - .name_len = (typeof(header.name_len)) strlen(result->name) + 1, - .description_len = (typeof(header.name_len)) strlen(result->description) + 1, - }; - - /* Header. */ - ret = lttng_dynamic_buffer_append( - &payload->buffer, &header, sizeof(header)); - if (ret) { - ERR("Failed to append error query result communication header to payload"); - goto end; - } - - /* Name. */ - ret = lttng_dynamic_buffer_append( - &payload->buffer, result->name, header.name_len); - if (ret) { - ERR("Failed to append error query result name to payload"); - goto end; - } - - /* Description. */ - ret = lttng_dynamic_buffer_append(&payload->buffer, result->description, - header.description_len); - if (ret) { - ERR("Failed to append error query result description to payload"); - goto end; - } - - /* Type-specific payload. */ - switch (result->type) { - case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: - ret = lttng_error_query_result_counter_serialize( - result, payload); - if (ret) { - ERR("Failed to serialize counter error query result"); - goto end; - } - break; - default: - abort(); - } - -end: - return ret; -} - -static -int lttng_error_query_result_init( - struct lttng_error_query_result *result, - enum lttng_error_query_result_type result_type, - const char *name, - const char *description) -{ - int ret; - - LTTNG_ASSERT(name); - LTTNG_ASSERT(description); - - result->type = result_type; - - result->name = strdup(name); - if (!result->name) { - PERROR("Failed to copy error query result name"); - ret = -1; - goto end; - } - - result->description = strdup(description); - if (!result->description) { - PERROR("Failed to copy error query result description"); - ret = -1; - goto end; - } - - ret = 0; -end: - return ret; -} - -void lttng_error_query_result_destroy(struct lttng_error_query_result *counter) -{ - if (!counter) { - return; - } - - switch (counter->type) { - case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: - /* Nothing to tear down. */ - break; - default: - abort(); - } - - free(counter->name); - free(counter->description); - free(counter); -} - -struct lttng_error_query_result * -lttng_error_query_result_counter_create( - const char *name, const char *description, uint64_t value) -{ - int init_ret; - struct lttng_error_query_result_counter *counter; - - counter = zmalloc(sizeof(*counter)); - if (!counter) { - PERROR("Failed to allocate error query counter result"); - goto end; - } - - init_ret = lttng_error_query_result_init(&counter->parent, - LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER, name, - description); - if (init_ret) { - goto error; - } - - counter->value = value; - goto end; -error: - lttng_error_query_result_destroy(&counter->parent); -end: - return counter ? &counter->parent : NULL; -} - -static -void destroy_result(void *ptr) -{ - struct lttng_error_query_result *result = (typeof(result)) ptr; - - lttng_error_query_result_destroy(result); -} - -struct lttng_error_query_results *lttng_error_query_results_create(void) -{ - struct lttng_error_query_results *set = zmalloc(sizeof(*set)); - - if (!set) { - PERROR("Failed to allocate an error query result set"); - goto end; - } - - lttng_dynamic_pointer_array_init(&set->results, destroy_result); -end: - return set; -} - -int lttng_error_query_results_add_result( - struct lttng_error_query_results *results, - struct lttng_error_query_result *result) -{ - return lttng_dynamic_pointer_array_add_pointer( - &results->results, result); -} - -ssize_t lttng_error_query_result_create_from_payload( - struct lttng_payload_view *view, - struct lttng_error_query_result **result) -{ - ssize_t used_size = 0; - struct lttng_error_query_result_comm *header; - struct lttng_payload_view header_view = - lttng_payload_view_from_view(view, 0, sizeof(*header)); - const char *name; - const char *description; - - if (!lttng_payload_view_is_valid(&header_view)) { - used_size = -1; - goto end; - } - - header = (typeof(header)) header_view.buffer.data; - used_size += sizeof(*header); - - { - struct lttng_payload_view name_view = - lttng_payload_view_from_view(view, used_size, - header->name_len); - - if (!lttng_payload_view_is_valid(&name_view) || - !lttng_buffer_view_contains_string( - &name_view.buffer, - name_view.buffer.data, - header->name_len)) { - used_size = -1; - goto end; - } - - name = name_view.buffer.data; - used_size += header->name_len; - } - - { - struct lttng_payload_view description_view = - lttng_payload_view_from_view(view, used_size, - header->description_len); - - if (!lttng_payload_view_is_valid(&description_view) || - !lttng_buffer_view_contains_string( - &description_view.buffer, - description_view.buffer.data, - header->description_len)) { - used_size = -1; - goto end; - } - - description = description_view.buffer.data; - used_size += header->description_len; - } - - switch (header->type) { - case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: - { - struct lttng_error_query_result_counter_comm *counter; - struct lttng_payload_view counter_payload_view = - lttng_payload_view_from_view(view, used_size, - sizeof(*counter)); - - if (!lttng_payload_view_is_valid(&counter_payload_view)) { - used_size = -1; - goto end; - } - - counter = (typeof(counter)) counter_payload_view.buffer.data; - *result = lttng_error_query_result_counter_create( - name, description, counter->value); - if (!*result) { - used_size = -1; - goto end; - } - - used_size += sizeof(*counter); - break; - } - default: - used_size = -1; - goto end; - } - -end: - return used_size; -} - -int lttng_error_query_results_serialize( - const struct lttng_error_query_results *results, - struct lttng_payload *payload) -{ - int ret; - size_t result_index; - const size_t result_count = lttng_dynamic_pointer_array_get_count( - &results->results); - const struct lttng_error_query_results_comm header = { - .count = (typeof(header.count)) result_count, - }; - - /* Header. */ - ret = lttng_dynamic_buffer_append(&payload->buffer, &header, sizeof(header)); - if (ret) { - ERR("Failed to append error query result set header to payload"); - goto end; - } - - /* Results. */ - for (result_index = 0; result_index < result_count; result_index++) { - const struct lttng_error_query_result *result = (typeof(result)) - lttng_dynamic_pointer_array_get_pointer( - &results->results, - result_index); - - ret = lttng_error_query_result_serialize(result, payload); - if (ret) { - ERR("Failed to append error query result to payload"); - goto end; - } - } -end: - return ret; -} - -ssize_t lttng_error_query_results_create_from_payload( - struct lttng_payload_view *view, - struct lttng_error_query_results **_results) -{ - size_t result_index; - ssize_t total_used_size = 0; - struct lttng_error_query_results_comm *header; - struct lttng_payload_view header_view = - lttng_payload_view_from_view(view, 0, sizeof(*header)); - struct lttng_error_query_results *results = NULL; - - if (!lttng_payload_view_is_valid(&header_view)) { - ERR("Failed to map view to error query result set header"); - total_used_size = -1; - goto end; - } - - header = (typeof(header)) header_view.buffer.data; - total_used_size += sizeof(*header); - results = lttng_error_query_results_create(); - if (!results) { - total_used_size = -1; - goto end; - } - - for (result_index = 0; result_index < header->count; result_index++) { - ssize_t used_size; - struct lttng_error_query_result *result; - struct lttng_payload_view result_view = - lttng_payload_view_from_view( - view, total_used_size, -1); - - if (!lttng_payload_view_is_valid(&result_view)) { - total_used_size = -1; - goto end; - } - - used_size = lttng_error_query_result_create_from_payload( - &result_view, &result); - if (used_size < 0) { - total_used_size = -1; - goto end; - } - - total_used_size += used_size; - - if (lttng_dynamic_pointer_array_add_pointer( - &results->results, result)) { - lttng_error_query_result_destroy(result); - total_used_size = -1; - goto end; - } - } - - *_results = results; - results = NULL; -end: - lttng_error_query_results_destroy(results); - return total_used_size; -} - -static -int lttng_error_query_trigger_serialize(const struct lttng_error_query *query, - struct lttng_payload *payload) -{ - int ret; - const struct lttng_error_query_trigger *query_trigger = - container_of(query, typeof(*query_trigger), parent); - - if (!lttng_trigger_validate(query_trigger->trigger)) { - ret = -1; - goto end; - } - - ret = lttng_trigger_serialize(query_trigger->trigger, payload); - if (ret) { - goto end; - } - -end: - return ret; -} - -static -int lttng_error_query_condition_serialize(const struct lttng_error_query *query, - struct lttng_payload *payload) -{ - int ret; - const struct lttng_error_query_condition *query_trigger = - container_of(query, typeof(*query_trigger), parent); - - if (!lttng_trigger_validate(query_trigger->trigger)) { - ret = -1; - goto end; - } - - ret = lttng_trigger_serialize(query_trigger->trigger, payload); - if (ret) { - goto end; - } - -end: - return ret; -} - -static -int lttng_error_query_action_serialize(const struct lttng_error_query *query, - struct lttng_payload *payload) -{ - int ret; - const struct lttng_error_query_action *query_action = - container_of(query, typeof(*query_action), parent); - - if (!lttng_trigger_validate(query_action->trigger)) { - ret = -1; - goto end; - } - - ret = lttng_trigger_serialize(query_action->trigger, payload); - if (ret) { - goto end; - } - - ret = lttng_action_path_serialize(&query_action->action_path, payload); - if (ret) { - goto end; - } - -end: - return ret; -} - -enum lttng_error_query_target_type lttng_error_query_get_target_type( - const struct lttng_error_query *query) -{ - return query->target_type; -} - -const struct lttng_trigger *lttng_error_query_trigger_borrow_target( - const struct lttng_error_query *query) -{ - const struct lttng_error_query_trigger *query_trigger = - container_of(query, typeof(*query_trigger), parent); - - return query_trigger->trigger; -} - -const struct lttng_trigger *lttng_error_query_condition_borrow_target( - const struct lttng_error_query *query) -{ - const struct lttng_error_query_condition *query_trigger = - container_of(query, typeof(*query_trigger), parent); - - return query_trigger->trigger; -} - -const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target( - const struct lttng_error_query *query) -{ - const struct lttng_error_query_action *query_action = - container_of(query, typeof(*query_action), parent); - - return query_action->trigger; -} - -struct lttng_action *lttng_error_query_action_borrow_action_target( - const struct lttng_error_query *query, - struct lttng_trigger *trigger) -{ - const struct lttng_error_query_action *query_action = - container_of(query, typeof(*query_action), parent); - - return get_trigger_action_from_path( - trigger, &query_action->action_path); -} - -int lttng_error_query_serialize(const struct lttng_error_query *query, - struct lttng_payload *payload) -{ - int ret; - const struct lttng_error_query_comm header = { - .target_type = (typeof(header.target_type)) query->target_type, - }; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &header, sizeof(header)); - if (ret) { - ERR("Failed to append error query header to payload"); - goto end; - } - - switch (query->target_type) { - case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: - ret = lttng_error_query_trigger_serialize(query, payload); - if (ret) { - goto end; - } - - break; - case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: - ret = lttng_error_query_condition_serialize(query, payload); - if (ret) { - goto end; - } - - break; - case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: - ret = lttng_error_query_action_serialize(query, payload); - if (ret) { - goto end; - } - - break; - default: - abort(); - } -end: - return ret; -} - -ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view, - struct lttng_error_query **query) -{ - ssize_t used_size = 0; - struct lttng_error_query_comm *header; - struct lttng_trigger *trigger = NULL; - struct lttng_payload_view header_view = - lttng_payload_view_from_view(view, 0, sizeof(*header)); - - if (!lttng_payload_view_is_valid(&header_view)) { - ERR("Failed to map error query header"); - used_size = -1; - goto end; - } - - used_size = sizeof(*header); - - header = (typeof(header)) header_view.buffer.data; - switch ((enum lttng_error_query_target_type) header->target_type) { - case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: - { - ssize_t trigger_used_size; - struct lttng_payload_view trigger_view = - lttng_payload_view_from_view( - view, used_size, -1); - - if (!lttng_payload_view_is_valid(&trigger_view)) { - used_size = -1; - goto end; - } - - trigger_used_size = lttng_trigger_create_from_payload( - &trigger_view, &trigger); - if (trigger_used_size < 0) { - used_size = -1; - goto end; - } - - used_size += trigger_used_size; - - *query = lttng_error_query_trigger_create(trigger); - if (!*query) { - used_size = -1; - goto end; - } - - break; - } - case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: - { - ssize_t trigger_used_size; - struct lttng_payload_view trigger_view = - lttng_payload_view_from_view( - view, used_size, -1); - - if (!lttng_payload_view_is_valid(&trigger_view)) { - used_size = -1; - goto end; - } - - trigger_used_size = lttng_trigger_create_from_payload( - &trigger_view, &trigger); - if (trigger_used_size < 0) { - used_size = -1; - goto end; - } - - used_size += trigger_used_size; - - *query = lttng_error_query_condition_create(trigger); - if (!*query) { - used_size = -1; - goto end; - } - - break; - } - case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: - { - struct lttng_action_path *action_path = NULL; - - { - ssize_t trigger_used_size; - struct lttng_payload_view trigger_view = - lttng_payload_view_from_view( - view, used_size, -1); - - if (!lttng_payload_view_is_valid(&trigger_view)) { - used_size = -1; - goto end; - } - - trigger_used_size = lttng_trigger_create_from_payload( - &trigger_view, &trigger); - if (trigger_used_size < 0) { - used_size = -1; - goto end; - } - - used_size += trigger_used_size; - } - - { - ssize_t action_path_used_size; - struct lttng_payload_view action_path_view = - lttng_payload_view_from_view( - view, used_size, -1); - - if (!lttng_payload_view_is_valid(&action_path_view)) { - used_size = -1; - goto end; - } - - action_path_used_size = lttng_action_path_create_from_payload( - &action_path_view, &action_path); - if (action_path_used_size < 0) { - used_size = -1; - goto end; - } - - used_size += action_path_used_size; - } - - *query = lttng_error_query_action_create( - trigger, action_path); - lttng_action_path_destroy(action_path); - if (!*query) { - used_size = -1; - goto end; - } - - break; - } - default: - used_size = -1; - goto end; - } - -end: - lttng_trigger_put(trigger); - return used_size; -} - -enum lttng_error_query_results_status lttng_error_query_results_get_count( - const struct lttng_error_query_results *results, - unsigned int *count) -{ - enum lttng_error_query_results_status status; - - if (!results || !count) { - status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; - goto end; - } - - *count = lttng_dynamic_pointer_array_get_count(&results->results); - status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; -end: - return status; -} - -enum lttng_error_query_results_status -lttng_error_query_results_get_result( - const struct lttng_error_query_results *results, - const struct lttng_error_query_result **result, - unsigned int index) -{ - unsigned int result_count; - enum lttng_error_query_results_status status; - - if (!results || !result) { - status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; - goto end; - } - - status = lttng_error_query_results_get_count(results, &result_count); - if (status != LTTNG_ERROR_QUERY_RESULTS_STATUS_OK) { - goto end; - } - - if (index >= result_count) { - status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; - goto end; - } - - *result = (typeof(*result)) lttng_dynamic_pointer_array_get_pointer( - &results->results, index); - LTTNG_ASSERT(*result); - status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; -end: - return status; -} - -void lttng_error_query_results_destroy( - struct lttng_error_query_results *results) -{ - if (!results) { - return; - } - - lttng_dynamic_pointer_array_reset(&results->results); - free(results); -} - -enum lttng_error_query_result_type -lttng_error_query_result_get_type(const struct lttng_error_query_result *result) -{ - return result ? result->type : LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN; -} - -enum lttng_error_query_result_status lttng_error_query_result_get_name( - const struct lttng_error_query_result *result, - const char **name) -{ - enum lttng_error_query_result_status status; - - if (!result || !name) { - status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; - goto end; - } - - *name = result->name; - status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; -end: - return status; -} - -enum lttng_error_query_result_status lttng_error_query_result_get_description( - const struct lttng_error_query_result *result, - const char **description) -{ - enum lttng_error_query_result_status status; - - if (!result || !description) { - status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; - goto end; - } - - *description = result->description; - status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; -end: - return status; -} - -enum lttng_error_query_result_status lttng_error_query_result_counter_get_value( - const struct lttng_error_query_result *result, - uint64_t *value) -{ - enum lttng_error_query_result_status status; - const struct lttng_error_query_result_counter *counter_result; - - if (!result || !value || - result->type != LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) { - status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; - goto end; - } - - counter_result = container_of(result, typeof(*counter_result), parent); - - *value = counter_result->value; - status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; -end: - return status; -} - -static -enum lttng_error_code lttng_error_query_result_counter_mi_serialize( - const struct lttng_error_query_result *result, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_error_query_result_status status; - uint64_t value; - - LTTNG_ASSERT(result); - LTTNG_ASSERT(writer); - - status = lttng_error_query_result_counter_get_value(result, &value); - LTTNG_ASSERT(status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - - /* Open error query result counter element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_error_query_result_counter); - if (ret) { - goto mi_error; - } - - /* Value. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_error_query_result_counter_value, - value); - if (ret) { - goto mi_error; - } - - /* Close error query result counter element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -enum lttng_error_code lttng_error_query_result_mi_serialize( - const struct lttng_error_query_result *result, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_error_query_result_status result_status; - enum lttng_error_query_result_type type; - const char *name = NULL; - const char *description = NULL; - - LTTNG_ASSERT(result); - LTTNG_ASSERT(writer); - - type = lttng_error_query_result_get_type(result); - - result_status = lttng_error_query_result_get_name(result, &name); - LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - - result_status = lttng_error_query_result_get_description( - result, &description); - LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); - - /* Open error query result element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_error_query_result); - if (ret) { - goto mi_error; - } - - /* Name. */ - ret = mi_lttng_writer_write_element_string( - writer, mi_lttng_element_error_query_result_name, name); - if (ret) { - goto mi_error; - } - - /* Description. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_error_query_result_description, - description); - if (ret) { - goto mi_error; - } - - /* Serialize the result according to its sub type. */ - switch (type) { - case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: - ret_code = lttng_error_query_result_counter_mi_serialize( - result, writer); - break; - default: - abort(); - } - - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close error query result element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -enum lttng_error_code lttng_error_query_results_mi_serialize( - const struct lttng_error_query_results *results, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - unsigned int i, count; - enum lttng_error_query_results_status results_status; - - LTTNG_ASSERT(results); - LTTNG_ASSERT(writer); - - /* Open error query results element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_error_query_results); - if (ret) { - goto mi_error; - } - - results_status = lttng_error_query_results_get_count(results, &count); - LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); - - for (i = 0; i < count; i++) { - const struct lttng_error_query_result *result; - - results_status = lttng_error_query_results_get_result( - results, &result, i); - LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); - - /* A single error query result. */ - ret_code = lttng_error_query_result_mi_serialize(result, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - } - - /* Close error query results. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/error-query.cpp b/src/common/error-query.cpp new file mode 100644 index 000000000..5d369c9e0 --- /dev/null +++ b/src/common/error-query.cpp @@ -0,0 +1,1220 @@ +/* + * error-query.c + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct lttng_error_query { + enum lttng_error_query_target_type target_type; +}; + +struct lttng_error_query_comm { + /* enum lttng_error_query_target_type */ + int8_t target_type; + /* Target-specific payload. */ + char payload[]; +}; + +struct lttng_error_query_trigger { + struct lttng_error_query parent; + /* Mutable only because of the reference count. */ + struct lttng_trigger *trigger; +}; + +struct lttng_error_query_condition { + struct lttng_error_query parent; + /* Mutable only because of the reference count. */ + struct lttng_trigger *trigger; +}; + +struct lttng_error_query_action { + struct lttng_error_query parent; + /* Mutable only because of the reference count. */ + struct lttng_trigger *trigger; + struct lttng_action_path action_path; +}; + +struct lttng_error_query_result { + enum lttng_error_query_result_type type; + char *name; + char *description; +}; + +struct lttng_error_query_result_comm { + /* enum lttng_error_query_result_type */ + uint8_t type; + /* Length of name (including null-terminator). */ + uint32_t name_len; + /* Length of description (including null-terminator). */ + uint32_t description_len; + /* Name, description, and type-specific payload follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_error_query_result_counter_comm { + uint64_t value; +} LTTNG_PACKED; + +struct lttng_error_query_result_counter { + struct lttng_error_query_result parent; + uint64_t value; +}; + +struct lttng_error_query_results_comm { + uint32_t count; + /* `count` instances of `struct lttng_error_query_result` follow. */ + char payload[]; +} LTTNG_PACKED; + +struct lttng_error_query_results { + struct lttng_dynamic_pointer_array results; +}; + +static +enum lttng_error_code lttng_error_query_result_mi_serialize( + const struct lttng_error_query_result *result, + struct mi_writer *writer); + +static +enum lttng_error_code lttng_error_query_result_counter_mi_serialize( + const struct lttng_error_query_result *result, + struct mi_writer *writer); + +struct lttng_error_query *lttng_error_query_trigger_create( + const struct lttng_trigger *trigger) +{ + struct lttng_error_query_trigger *query = NULL; + struct lttng_trigger *trigger_copy = NULL; + + if (!trigger) { + goto end; + } + + trigger_copy = lttng_trigger_copy(trigger); + if (!trigger_copy) { + goto end; + } + + query = (lttng_error_query_trigger *) zmalloc(sizeof(*query)); + if (!query) { + PERROR("Failed to allocate trigger error query"); + goto error; + } + + query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER; + query->trigger = trigger_copy; + trigger_copy = NULL; + +error: + lttng_trigger_put(trigger_copy); +end: + return query ? &query->parent : NULL; +} + +struct lttng_error_query *lttng_error_query_condition_create( + const struct lttng_trigger *trigger) +{ + struct lttng_error_query_condition *query = NULL; + struct lttng_trigger *trigger_copy = NULL; + + if (!trigger) { + goto end; + } + + trigger_copy = lttng_trigger_copy(trigger); + if (!trigger_copy) { + goto end; + } + + query = (lttng_error_query_condition *) zmalloc(sizeof(*query)); + if (!query) { + PERROR("Failed to allocate condition error query"); + goto error; + } + + query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION; + query->trigger = trigger_copy; + trigger_copy = NULL; + +error: + lttng_trigger_put(trigger_copy); +end: + return query ? &query->parent : NULL; +} + +static +struct lttng_action *get_trigger_action_from_path( + struct lttng_trigger *trigger, + const struct lttng_action_path *action_path) +{ + size_t index_count, i; + enum lttng_action_path_status path_status; + struct lttng_action *current_action = NULL; + + path_status = lttng_action_path_get_index_count( + action_path, &index_count); + if (path_status != LTTNG_ACTION_PATH_STATUS_OK) { + goto end; + } + + current_action = lttng_trigger_get_action(trigger); + for (i = 0; i < index_count; i++) { + uint64_t path_index; + + path_status = lttng_action_path_get_index_at_index( + action_path, i, &path_index); + current_action = lttng_action_list_borrow_mutable_at_index( + current_action, path_index); + if (!current_action) { + /* Invalid action path. */ + goto end; + } + } + +end: + return current_action; +} + +static +bool is_valid_action_path(const struct lttng_trigger *trigger, + const struct lttng_action_path *action_path) +{ + /* + * While 'trigger's constness is casted-away, the trigger and resulting + * action are not modified; we merely check for the action's existence. + */ + return !!get_trigger_action_from_path( + (struct lttng_trigger *) trigger, action_path); +} + +struct lttng_error_query *lttng_error_query_action_create( + const struct lttng_trigger *trigger, + const struct lttng_action_path *action_path) +{ + struct lttng_error_query_action *query = NULL; + struct lttng_trigger *trigger_copy = NULL; + int ret_copy; + + if (!trigger || !action_path || + !is_valid_action_path(trigger, action_path)) { + goto end; + } + + trigger_copy = lttng_trigger_copy(trigger); + if (!trigger_copy) { + goto end; + } + + query = (lttng_error_query_action *) zmalloc(sizeof(*query)); + if (!query) { + PERROR("Failed to allocate action error query"); + goto error; + } + + ret_copy = lttng_action_path_copy(action_path, &query->action_path); + if (ret_copy) { + goto error; + } + + query->parent.target_type = LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION; + query->trigger = trigger_copy; + trigger_copy = NULL; + goto end; + +error: + lttng_trigger_put(trigger_copy); + lttng_error_query_destroy(query ? &query->parent : NULL); +end: + return query ? &query->parent : NULL; +} + +void lttng_error_query_destroy(struct lttng_error_query *query) +{ + struct lttng_error_query_trigger *trigger_query; + + if (!query) { + return; + } + + trigger_query = container_of(query, typeof(*trigger_query), parent); + lttng_trigger_put(trigger_query->trigger); + free(trigger_query); +} + +static +int lttng_error_query_result_counter_serialize( + const struct lttng_error_query_result *result, + struct lttng_payload *payload) +{ + const struct lttng_error_query_result_counter *counter_result; + + LTTNG_ASSERT(result->type == LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER); + counter_result = container_of(result, typeof(*counter_result), parent); + + lttng_error_query_result_counter_comm comm = { + .value = counter_result->value, + }; + + return lttng_dynamic_buffer_append(&payload->buffer, + &comm, + sizeof(struct lttng_error_query_result_counter_comm)); +} + +int lttng_error_query_result_serialize( + const struct lttng_error_query_result *result, + struct lttng_payload *payload) +{ + int ret; + struct lttng_error_query_result_comm header = { + .type = (uint8_t) result->type, + .name_len = (typeof(header.name_len)) strlen(result->name) + 1, + .description_len = (typeof(header.name_len)) strlen(result->description) + 1, + }; + + /* Header. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query result communication header to payload"); + goto end; + } + + /* Name. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, result->name, header.name_len); + if (ret) { + ERR("Failed to append error query result name to payload"); + goto end; + } + + /* Description. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, result->description, + header.description_len); + if (ret) { + ERR("Failed to append error query result description to payload"); + goto end; + } + + /* Type-specific payload. */ + switch (result->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + ret = lttng_error_query_result_counter_serialize( + result, payload); + if (ret) { + ERR("Failed to serialize counter error query result"); + goto end; + } + break; + default: + abort(); + } + +end: + return ret; +} + +static +int lttng_error_query_result_init( + struct lttng_error_query_result *result, + enum lttng_error_query_result_type result_type, + const char *name, + const char *description) +{ + int ret; + + LTTNG_ASSERT(name); + LTTNG_ASSERT(description); + + result->type = result_type; + + result->name = strdup(name); + if (!result->name) { + PERROR("Failed to copy error query result name"); + ret = -1; + goto end; + } + + result->description = strdup(description); + if (!result->description) { + PERROR("Failed to copy error query result description"); + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +void lttng_error_query_result_destroy(struct lttng_error_query_result *counter) +{ + if (!counter) { + return; + } + + switch (counter->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + /* Nothing to tear down. */ + break; + default: + abort(); + } + + free(counter->name); + free(counter->description); + free(counter); +} + +struct lttng_error_query_result * +lttng_error_query_result_counter_create( + const char *name, const char *description, uint64_t value) +{ + int init_ret; + struct lttng_error_query_result_counter *counter; + + counter = (lttng_error_query_result_counter *) zmalloc(sizeof(*counter)); + if (!counter) { + PERROR("Failed to allocate error query counter result"); + goto end; + } + + init_ret = lttng_error_query_result_init(&counter->parent, + LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER, name, + description); + if (init_ret) { + goto error; + } + + counter->value = value; + goto end; +error: + lttng_error_query_result_destroy(&counter->parent); +end: + return counter ? &counter->parent : NULL; +} + +static +void destroy_result(void *ptr) +{ + struct lttng_error_query_result *result = (typeof(result)) ptr; + + lttng_error_query_result_destroy(result); +} + +struct lttng_error_query_results *lttng_error_query_results_create(void) +{ + struct lttng_error_query_results *set = (lttng_error_query_results *) zmalloc(sizeof(*set)); + + if (!set) { + PERROR("Failed to allocate an error query result set"); + goto end; + } + + lttng_dynamic_pointer_array_init(&set->results, destroy_result); +end: + return set; +} + +int lttng_error_query_results_add_result( + struct lttng_error_query_results *results, + struct lttng_error_query_result *result) +{ + return lttng_dynamic_pointer_array_add_pointer( + &results->results, result); +} + +ssize_t lttng_error_query_result_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_result **result) +{ + ssize_t used_size = 0; + struct lttng_error_query_result_comm *header; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + const char *name; + const char *description; + + if (!lttng_payload_view_is_valid(&header_view)) { + used_size = -1; + goto end; + } + + header = (typeof(header)) header_view.buffer.data; + used_size += sizeof(*header); + + { + struct lttng_payload_view name_view = + lttng_payload_view_from_view(view, used_size, + header->name_len); + + if (!lttng_payload_view_is_valid(&name_view) || + !lttng_buffer_view_contains_string( + &name_view.buffer, + name_view.buffer.data, + header->name_len)) { + used_size = -1; + goto end; + } + + name = name_view.buffer.data; + used_size += header->name_len; + } + + { + struct lttng_payload_view description_view = + lttng_payload_view_from_view(view, used_size, + header->description_len); + + if (!lttng_payload_view_is_valid(&description_view) || + !lttng_buffer_view_contains_string( + &description_view.buffer, + description_view.buffer.data, + header->description_len)) { + used_size = -1; + goto end; + } + + description = description_view.buffer.data; + used_size += header->description_len; + } + + switch (header->type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + { + struct lttng_error_query_result_counter_comm *counter; + struct lttng_payload_view counter_payload_view = + lttng_payload_view_from_view(view, used_size, + sizeof(*counter)); + + if (!lttng_payload_view_is_valid(&counter_payload_view)) { + used_size = -1; + goto end; + } + + counter = (typeof(counter)) counter_payload_view.buffer.data; + *result = lttng_error_query_result_counter_create( + name, description, counter->value); + if (!*result) { + used_size = -1; + goto end; + } + + used_size += sizeof(*counter); + break; + } + default: + used_size = -1; + goto end; + } + +end: + return used_size; +} + +int lttng_error_query_results_serialize( + const struct lttng_error_query_results *results, + struct lttng_payload *payload) +{ + int ret; + size_t result_index; + const size_t result_count = lttng_dynamic_pointer_array_get_count( + &results->results); + const struct lttng_error_query_results_comm header = { + .count = (typeof(header.count)) result_count, + }; + + /* Header. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query result set header to payload"); + goto end; + } + + /* Results. */ + for (result_index = 0; result_index < result_count; result_index++) { + const struct lttng_error_query_result *result = (typeof(result)) + lttng_dynamic_pointer_array_get_pointer( + &results->results, + result_index); + + ret = lttng_error_query_result_serialize(result, payload); + if (ret) { + ERR("Failed to append error query result to payload"); + goto end; + } + } +end: + return ret; +} + +ssize_t lttng_error_query_results_create_from_payload( + struct lttng_payload_view *view, + struct lttng_error_query_results **_results) +{ + size_t result_index; + ssize_t total_used_size = 0; + struct lttng_error_query_results_comm *header; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + struct lttng_error_query_results *results = NULL; + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to map view to error query result set header"); + total_used_size = -1; + goto end; + } + + header = (typeof(header)) header_view.buffer.data; + total_used_size += sizeof(*header); + results = lttng_error_query_results_create(); + if (!results) { + total_used_size = -1; + goto end; + } + + for (result_index = 0; result_index < header->count; result_index++) { + ssize_t used_size; + struct lttng_error_query_result *result; + struct lttng_payload_view result_view = + lttng_payload_view_from_view( + view, total_used_size, -1); + + if (!lttng_payload_view_is_valid(&result_view)) { + total_used_size = -1; + goto end; + } + + used_size = lttng_error_query_result_create_from_payload( + &result_view, &result); + if (used_size < 0) { + total_used_size = -1; + goto end; + } + + total_used_size += used_size; + + if (lttng_dynamic_pointer_array_add_pointer( + &results->results, result)) { + lttng_error_query_result_destroy(result); + total_used_size = -1; + goto end; + } + } + + *_results = results; + results = NULL; +end: + lttng_error_query_results_destroy(results); + return total_used_size; +} + +static +int lttng_error_query_trigger_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_trigger *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + if (!lttng_trigger_validate(query_trigger->trigger)) { + ret = -1; + goto end; + } + + ret = lttng_trigger_serialize(query_trigger->trigger, payload); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +int lttng_error_query_condition_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_condition *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + if (!lttng_trigger_validate(query_trigger->trigger)) { + ret = -1; + goto end; + } + + ret = lttng_trigger_serialize(query_trigger->trigger, payload); + if (ret) { + goto end; + } + +end: + return ret; +} + +static +int lttng_error_query_action_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + + if (!lttng_trigger_validate(query_action->trigger)) { + ret = -1; + goto end; + } + + ret = lttng_trigger_serialize(query_action->trigger, payload); + if (ret) { + goto end; + } + + ret = lttng_action_path_serialize(&query_action->action_path, payload); + if (ret) { + goto end; + } + +end: + return ret; +} + +enum lttng_error_query_target_type lttng_error_query_get_target_type( + const struct lttng_error_query *query) +{ + return query->target_type; +} + +const struct lttng_trigger *lttng_error_query_trigger_borrow_target( + const struct lttng_error_query *query) +{ + const struct lttng_error_query_trigger *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + return query_trigger->trigger; +} + +const struct lttng_trigger *lttng_error_query_condition_borrow_target( + const struct lttng_error_query *query) +{ + const struct lttng_error_query_condition *query_trigger = + container_of(query, typeof(*query_trigger), parent); + + return query_trigger->trigger; +} + +const struct lttng_trigger *lttng_error_query_action_borrow_trigger_target( + const struct lttng_error_query *query) +{ + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + + return query_action->trigger; +} + +struct lttng_action *lttng_error_query_action_borrow_action_target( + const struct lttng_error_query *query, + struct lttng_trigger *trigger) +{ + const struct lttng_error_query_action *query_action = + container_of(query, typeof(*query_action), parent); + + return get_trigger_action_from_path( + trigger, &query_action->action_path); +} + +int lttng_error_query_serialize(const struct lttng_error_query *query, + struct lttng_payload *payload) +{ + int ret; + const struct lttng_error_query_comm header = { + .target_type = (typeof(header.target_type)) query->target_type, + }; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &header, sizeof(header)); + if (ret) { + ERR("Failed to append error query header to payload"); + goto end; + } + + switch (query->target_type) { + case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: + ret = lttng_error_query_trigger_serialize(query, payload); + if (ret) { + goto end; + } + + break; + case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: + ret = lttng_error_query_condition_serialize(query, payload); + if (ret) { + goto end; + } + + break; + case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: + ret = lttng_error_query_action_serialize(query, payload); + if (ret) { + goto end; + } + + break; + default: + abort(); + } +end: + return ret; +} + +ssize_t lttng_error_query_create_from_payload(struct lttng_payload_view *view, + struct lttng_error_query **query) +{ + ssize_t used_size = 0; + struct lttng_error_query_comm *header; + struct lttng_trigger *trigger = NULL; + struct lttng_payload_view header_view = + lttng_payload_view_from_view(view, 0, sizeof(*header)); + + if (!lttng_payload_view_is_valid(&header_view)) { + ERR("Failed to map error query header"); + used_size = -1; + goto end; + } + + used_size = sizeof(*header); + + header = (typeof(header)) header_view.buffer.data; + switch ((enum lttng_error_query_target_type) header->target_type) { + case LTTNG_ERROR_QUERY_TARGET_TYPE_TRIGGER: + { + ssize_t trigger_used_size; + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&trigger_view)) { + used_size = -1; + goto end; + } + + trigger_used_size = lttng_trigger_create_from_payload( + &trigger_view, &trigger); + if (trigger_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += trigger_used_size; + + *query = lttng_error_query_trigger_create(trigger); + if (!*query) { + used_size = -1; + goto end; + } + + break; + } + case LTTNG_ERROR_QUERY_TARGET_TYPE_CONDITION: + { + ssize_t trigger_used_size; + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&trigger_view)) { + used_size = -1; + goto end; + } + + trigger_used_size = lttng_trigger_create_from_payload( + &trigger_view, &trigger); + if (trigger_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += trigger_used_size; + + *query = lttng_error_query_condition_create(trigger); + if (!*query) { + used_size = -1; + goto end; + } + + break; + } + case LTTNG_ERROR_QUERY_TARGET_TYPE_ACTION: + { + struct lttng_action_path *action_path = NULL; + + { + ssize_t trigger_used_size; + struct lttng_payload_view trigger_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&trigger_view)) { + used_size = -1; + goto end; + } + + trigger_used_size = lttng_trigger_create_from_payload( + &trigger_view, &trigger); + if (trigger_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += trigger_used_size; + } + + { + ssize_t action_path_used_size; + struct lttng_payload_view action_path_view = + lttng_payload_view_from_view( + view, used_size, -1); + + if (!lttng_payload_view_is_valid(&action_path_view)) { + used_size = -1; + goto end; + } + + action_path_used_size = lttng_action_path_create_from_payload( + &action_path_view, &action_path); + if (action_path_used_size < 0) { + used_size = -1; + goto end; + } + + used_size += action_path_used_size; + } + + *query = lttng_error_query_action_create( + trigger, action_path); + lttng_action_path_destroy(action_path); + if (!*query) { + used_size = -1; + goto end; + } + + break; + } + default: + used_size = -1; + goto end; + } + +end: + lttng_trigger_put(trigger); + return used_size; +} + +enum lttng_error_query_results_status lttng_error_query_results_get_count( + const struct lttng_error_query_results *results, + unsigned int *count) +{ + enum lttng_error_query_results_status status; + + if (!results || !count) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + *count = lttng_dynamic_pointer_array_get_count(&results->results); + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_results_status +lttng_error_query_results_get_result( + const struct lttng_error_query_results *results, + const struct lttng_error_query_result **result, + unsigned int index) +{ + unsigned int result_count; + enum lttng_error_query_results_status status; + + if (!results || !result) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + status = lttng_error_query_results_get_count(results, &result_count); + if (status != LTTNG_ERROR_QUERY_RESULTS_STATUS_OK) { + goto end; + } + + if (index >= result_count) { + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_INVALID_PARAMETER; + goto end; + } + + *result = (typeof(*result)) lttng_dynamic_pointer_array_get_pointer( + &results->results, index); + LTTNG_ASSERT(*result); + status = LTTNG_ERROR_QUERY_RESULTS_STATUS_OK; +end: + return status; +} + +void lttng_error_query_results_destroy( + struct lttng_error_query_results *results) +{ + if (!results) { + return; + } + + lttng_dynamic_pointer_array_reset(&results->results); + free(results); +} + +enum lttng_error_query_result_type +lttng_error_query_result_get_type(const struct lttng_error_query_result *result) +{ + return result ? result->type : LTTNG_ERROR_QUERY_RESULT_TYPE_UNKNOWN; +} + +enum lttng_error_query_result_status lttng_error_query_result_get_name( + const struct lttng_error_query_result *result, + const char **name) +{ + enum lttng_error_query_result_status status; + + if (!result || !name) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + *name = result->name; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_result_status lttng_error_query_result_get_description( + const struct lttng_error_query_result *result, + const char **description) +{ + enum lttng_error_query_result_status status; + + if (!result || !description) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + *description = result->description; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} + +enum lttng_error_query_result_status lttng_error_query_result_counter_get_value( + const struct lttng_error_query_result *result, + uint64_t *value) +{ + enum lttng_error_query_result_status status; + const struct lttng_error_query_result_counter *counter_result; + + if (!result || !value || + result->type != LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER) { + status = LTTNG_ERROR_QUERY_RESULT_STATUS_INVALID_PARAMETER; + goto end; + } + + counter_result = container_of(result, typeof(*counter_result), parent); + + *value = counter_result->value; + status = LTTNG_ERROR_QUERY_RESULT_STATUS_OK; +end: + return status; +} + +static +enum lttng_error_code lttng_error_query_result_counter_mi_serialize( + const struct lttng_error_query_result *result, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_error_query_result_status status; + uint64_t value; + + LTTNG_ASSERT(result); + LTTNG_ASSERT(writer); + + status = lttng_error_query_result_counter_get_value(result, &value); + LTTNG_ASSERT(status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + + /* Open error query result counter element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_error_query_result_counter); + if (ret) { + goto mi_error; + } + + /* Value. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_error_query_result_counter_value, + value); + if (ret) { + goto mi_error; + } + + /* Close error query result counter element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +enum lttng_error_code lttng_error_query_result_mi_serialize( + const struct lttng_error_query_result *result, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_error_query_result_status result_status; + enum lttng_error_query_result_type type; + const char *name = NULL; + const char *description = NULL; + + LTTNG_ASSERT(result); + LTTNG_ASSERT(writer); + + type = lttng_error_query_result_get_type(result); + + result_status = lttng_error_query_result_get_name(result, &name); + LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + + result_status = lttng_error_query_result_get_description( + result, &description); + LTTNG_ASSERT(result_status == LTTNG_ERROR_QUERY_RESULT_STATUS_OK); + + /* Open error query result element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_error_query_result); + if (ret) { + goto mi_error; + } + + /* Name. */ + ret = mi_lttng_writer_write_element_string( + writer, mi_lttng_element_error_query_result_name, name); + if (ret) { + goto mi_error; + } + + /* Description. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_error_query_result_description, + description); + if (ret) { + goto mi_error; + } + + /* Serialize the result according to its sub type. */ + switch (type) { + case LTTNG_ERROR_QUERY_RESULT_TYPE_COUNTER: + ret_code = lttng_error_query_result_counter_mi_serialize( + result, writer); + break; + default: + abort(); + } + + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close error query result element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +enum lttng_error_code lttng_error_query_results_mi_serialize( + const struct lttng_error_query_results *results, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + unsigned int i, count; + enum lttng_error_query_results_status results_status; + + LTTNG_ASSERT(results); + LTTNG_ASSERT(writer); + + /* Open error query results element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_error_query_results); + if (ret) { + goto mi_error; + } + + results_status = lttng_error_query_results_get_count(results, &count); + LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); + + for (i = 0; i < count; i++) { + const struct lttng_error_query_result *result; + + results_status = lttng_error_query_results_get_result( + results, &result, i); + LTTNG_ASSERT(results_status == LTTNG_ERROR_QUERY_RESULTS_STATUS_OK); + + /* A single error query result. */ + ret_code = lttng_error_query_result_mi_serialize(result, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + } + + /* Close error query results. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/error.c b/src/common/error.c deleted file mode 100644 index b888c2643..000000000 --- a/src/common/error.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "error.h" - -#define ERROR_INDEX(code) (code - LTTNG_OK) - -/* - * lttng_opt_abort_on_error: unset: -1, disabled: 0, enabled: 1. - * Controlled by the LTTNG_ABORT_ON_ERROR environment variable. - */ -static int lttng_opt_abort_on_error = -1; - -/* TLS variable that contains the time of one single log entry. */ -DEFINE_URCU_TLS(struct log_time, error_log_time); -DEFINE_URCU_TLS(const char *, logger_thread_name); - -const char *log_add_time(void) -{ - int ret; - struct tm tm, *res; - struct timespec tp; - time_t now; - const int errsv = errno; - - ret = lttng_clock_gettime(CLOCK_REALTIME, &tp); - if (ret < 0) { - goto error; - } - now = (time_t) tp.tv_sec; - - res = localtime_r(&now, &tm); - if (!res) { - goto error; - } - - /* Format time in the TLS variable. */ - ret = snprintf(URCU_TLS(error_log_time).str, sizeof(URCU_TLS(error_log_time).str), - "%02d:%02d:%02d.%09ld", - tm.tm_hour, tm.tm_min, tm.tm_sec, tp.tv_nsec); - if (ret < 0) { - goto error; - } - - errno = errsv; - return URCU_TLS(error_log_time).str; - -error: - /* Return an empty string on error so logging is not affected. */ - errno = errsv; - return ""; -} - -void logger_set_thread_name(const char *name, bool set_pthread_name) -{ - int ret; - - LTTNG_ASSERT(name); - URCU_TLS(logger_thread_name) = name; - - if (set_pthread_name) { - ret = lttng_thread_setname(name); - if (ret && ret != -ENOSYS) { - /* Don't fail as this is not essential. */ - DBG("Failed to set pthread name attribute"); - } - } -} - -/* - * Human readable error message. - */ -static const char *error_string_array[] = { - /* LTTNG_OK code MUST be at the top for the ERROR_INDEX macro to work */ - [ ERROR_INDEX(LTTNG_OK) ] = "Success", - [ ERROR_INDEX(LTTNG_ERR_UNK) ] = "Unknown error", - [ ERROR_INDEX(LTTNG_ERR_UND) ] = "Undefined command", - [ ERROR_INDEX(LTTNG_ERR_UNKNOWN_DOMAIN) ] = "Unknown tracing domain", - [ ERROR_INDEX(LTTNG_ERR_NO_SESSION) ] = "No session found", - [ ERROR_INDEX(LTTNG_ERR_CREATE_DIR_FAIL) ] = "Create directory failed", - [ ERROR_INDEX(LTTNG_ERR_SESSION_FAIL) ] = "Create session failed", - [ ERROR_INDEX(LTTNG_ERR_SESS_NOT_FOUND) ] = "Session name not found", - [ ERROR_INDEX(LTTNG_ERR_FATAL) ] = "Fatal error of the session daemon", - [ ERROR_INDEX(LTTNG_ERR_SELECT_SESS) ] = "A session MUST be selected", - [ ERROR_INDEX(LTTNG_ERR_EXIST_SESS) ] = "Session name already exists", - [ ERROR_INDEX(LTTNG_ERR_NO_EVENT) ] = "Event not found", - [ ERROR_INDEX(LTTNG_ERR_CONNECT_FAIL) ] = "Unable to connect to Unix socket", - [ ERROR_INDEX(LTTNG_ERR_EPERM) ] = "Permission denied", - [ ERROR_INDEX(LTTNG_ERR_KERN_NA) ] = "Kernel tracer not available", - [ ERROR_INDEX(LTTNG_ERR_KERN_VERSION) ] = "Kernel tracer version is not compatible", - [ ERROR_INDEX(LTTNG_ERR_KERN_EVENT_EXIST) ] = "Kernel event already exists", - [ ERROR_INDEX(LTTNG_ERR_KERN_SESS_FAIL) ] = "Kernel create session failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_EXIST) ] = "Kernel channel already exists", - [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_FAIL) ] = "Kernel create channel failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_NOT_FOUND) ] = "Kernel channel not found", - [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_DISABLE_FAIL) ] = "Disable kernel channel failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_CHAN_ENABLE_FAIL) ] = "Enable kernel channel failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_CONTEXT_FAIL) ] = "Add kernel context failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_ENABLE_FAIL) ] = "Enable kernel event failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_DISABLE_FAIL) ] = "Disable kernel event failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_META_FAIL) ] = "Opening metadata failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_START_FAIL) ] = "Starting kernel trace failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_STOP_FAIL) ] = "Stopping kernel trace failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_CONSUMER_FAIL) ] = "Kernel consumer start failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_STREAM_FAIL) ] = "Kernel create stream failed", - [ ERROR_INDEX(LTTNG_ERR_KERN_LIST_FAIL) ] = "Listing kernel events failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CALIBRATE_FAIL) ] = "UST calibration failed", - [ ERROR_INDEX(LTTNG_ERR_UST_SESS_FAIL) ] = "UST create session failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_FAIL) ] = "UST create channel failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_EXIST) ] = "UST channel already exist", - [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_NOT_FOUND) ] = "UST channel not found", - [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_DISABLE_FAIL) ] = "Disable UST channel failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CHAN_ENABLE_FAIL) ] = "Enable UST channel failed", - [ ERROR_INDEX(LTTNG_ERR_UST_ENABLE_FAIL) ] = "Enable UST event failed", - [ ERROR_INDEX(LTTNG_ERR_UST_DISABLE_FAIL) ] = "Disable UST event failed", - [ ERROR_INDEX(LTTNG_ERR_UST_META_FAIL) ] = "Opening metadata failed", - [ ERROR_INDEX(LTTNG_ERR_UST_START_FAIL) ] = "Starting UST trace failed", - [ ERROR_INDEX(LTTNG_ERR_UST_STOP_FAIL) ] = "Stopping UST trace failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CONSUMER64_FAIL) ] = "64-bit UST consumer start failed", - [ ERROR_INDEX(LTTNG_ERR_UST_CONSUMER32_FAIL) ] = "32-bit UST consumer start failed", - [ ERROR_INDEX(LTTNG_ERR_UST_STREAM_FAIL) ] = "UST create stream failed", - [ ERROR_INDEX(LTTNG_ERR_UST_LIST_FAIL) ] = "Listing UST events failed", - [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_EXIST) ] = "UST event already exist", - [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_NOT_FOUND)] = "UST event not found", - [ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_EXIST)] = "UST context already exist", - [ ERROR_INDEX(LTTNG_ERR_UST_CONTEXT_INVAL)] = "UST invalid context", - [ ERROR_INDEX(LTTNG_ERR_NEED_ROOT_SESSIOND) ] = "Tracing the kernel requires a root lttng-sessiond daemon, as well as \"tracing\" group membership or root user ID for the lttng client", - [ ERROR_INDEX(LTTNG_ERR_NO_UST) ] = "LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled", - [ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STARTED) ] = "Tracing has already been started once", - [ ERROR_INDEX(LTTNG_ERR_TRACE_ALREADY_STOPPED) ] = "Tracing has already been stopped", - [ ERROR_INDEX(LTTNG_ERR_KERN_EVENT_ENOSYS) ] = "Kernel event type not supported", - [ ERROR_INDEX(LTTNG_ERR_NEED_CHANNEL_NAME) ] = "Non-default channel exists within session: channel name needs to be specified with '-c name'", - [ ERROR_INDEX(LTTNG_ERR_INVALID) ] = "Invalid parameter", - [ ERROR_INDEX(LTTNG_ERR_NO_USTCONSUMERD) ] = "No UST consumer detected", - [ ERROR_INDEX(LTTNG_ERR_NO_KERNCONSUMERD) ] = "No kernel consumer detected", - [ ERROR_INDEX(LTTNG_ERR_EVENT_EXIST_LOGLEVEL) ] = "Event already enabled with different loglevel", - [ ERROR_INDEX(LTTNG_ERR_URL_DATA_MISS) ] = "Missing data path URL", - [ ERROR_INDEX(LTTNG_ERR_URL_CTRL_MISS) ] = "Missing control path URL", - [ ERROR_INDEX(LTTNG_ERR_ENABLE_CONSUMER_FAIL) ] = "Enabling consumer failed", - [ ERROR_INDEX(LTTNG_ERR_RELAYD_CONNECT_FAIL) ] = "Unable to connect to lttng-relayd", - [ ERROR_INDEX(LTTNG_ERR_RELAYD_VERSION_FAIL) ] = "Relay daemon not compatible", - [ ERROR_INDEX(LTTNG_ERR_FILTER_INVAL) ] = "Invalid filter bytecode", - [ ERROR_INDEX(LTTNG_ERR_FILTER_NOMEM) ] = "Not enough memory for filter bytecode", - [ ERROR_INDEX(LTTNG_ERR_FILTER_EXIST) ] = "Filter already exist", - [ ERROR_INDEX(LTTNG_ERR_NO_CONSUMER) ] = "Consumer not found for recording session", - [ ERROR_INDEX(LTTNG_ERR_NO_SESSIOND) ] = "No session daemon is available", - [ ERROR_INDEX(LTTNG_ERR_SESSION_STARTED) ] = "Session is running", - [ ERROR_INDEX(LTTNG_ERR_NOT_SUPPORTED) ] = "Operation not supported", - [ ERROR_INDEX(LTTNG_ERR_UST_EVENT_ENABLED) ] = "UST event already enabled", - [ ERROR_INDEX(LTTNG_ERR_SET_URL) ] = "Error setting URL", - [ ERROR_INDEX(LTTNG_ERR_URL_EXIST) ] = "URL already exists", - [ ERROR_INDEX(LTTNG_ERR_BUFFER_NOT_SUPPORTED)] = "Buffer type not supported", - [ ERROR_INDEX(LTTNG_ERR_BUFFER_TYPE_MISMATCH)] = "Buffer type mismatch for session", - [ ERROR_INDEX(LTTNG_ERR_NOMEM)] = "Not enough memory", - [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST) ] = "Snapshot output already exists", - [ ERROR_INDEX(LTTNG_ERR_START_SESSION_ONCE) ] = "Session needs to be started once", - [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_FAIL) ] = "Snapshot record failed", - [ ERROR_INDEX(LTTNG_ERR_CHAN_EXIST) ] = "Channel already exists", - [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_NODATA) ] = "No data available in snapshot", - [ ERROR_INDEX(LTTNG_ERR_NO_CHANNEL) ] = "No channel found in the session", - [ ERROR_INDEX(LTTNG_ERR_SESSION_INVALID_CHAR) ] = "Invalid character found in session name", - [ ERROR_INDEX(LTTNG_ERR_SAVE_FILE_EXIST) ] = "Session file already exists", - [ ERROR_INDEX(LTTNG_ERR_SAVE_IO_FAIL) ] = "IO error while writing session configuration", - [ ERROR_INDEX(LTTNG_ERR_LOAD_INVALID_CONFIG) ] = "Invalid session configuration", - [ ERROR_INDEX(LTTNG_ERR_LOAD_IO_FAIL) ] = "IO error while reading a session configuration", - [ ERROR_INDEX(LTTNG_ERR_LOAD_SESSION_NOENT) ] = "Session file not found", - [ ERROR_INDEX(LTTNG_ERR_MAX_SIZE_INVALID) ] = "Snapshot max size is invalid", - [ ERROR_INDEX(LTTNG_ERR_MI_OUTPUT_TYPE) ] = "Invalid MI output format", - [ ERROR_INDEX(LTTNG_ERR_MI_IO_FAIL) ] = "IO error while writing MI output", - [ ERROR_INDEX(LTTNG_ERR_MI_NOT_IMPLEMENTED) ] = "Mi feature not implemented", - [ ERROR_INDEX(LTTNG_ERR_INVALID_EVENT_NAME) ] = "Invalid event name", - [ ERROR_INDEX(LTTNG_ERR_INVALID_CHANNEL_NAME) ] = "Invalid channel name", - [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_EXISTS) ] = "Process attribute is already tracked", - [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_MISSING) ] = "Process attribute was not tracked", - [ ERROR_INDEX(LTTNG_ERR_INVALID_CHANNEL_DOMAIN) ] = "Invalid channel domain", - [ ERROR_INDEX(LTTNG_ERR_OVERFLOW) ] = "Overflow occurred", - [ ERROR_INDEX(LTTNG_ERR_SESSION_NOT_STARTED) ] = "Session not started", - [ ERROR_INDEX(LTTNG_ERR_LIVE_SESSION) ] = "Live sessions are not supported", - [ ERROR_INDEX(LTTNG_ERR_PER_PID_SESSION) ] = "Per-PID recording sessions are not supported", - [ ERROR_INDEX(LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE) ] = "Context unavailable on this kernel", - [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_FAIL) ] = "Failed to regenerate the state dump", - [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_NOMEM) ] = "Failed to regenerate the state dump, not enough memory", - [ ERROR_INDEX(LTTNG_ERR_NOT_SNAPSHOT_SESSION) ] = "Snapshot command can't be applied to a non-snapshot session", - [ ERROR_INDEX(LTTNG_ERR_INVALID_TRIGGER) ] = "Invalid trigger", - [ ERROR_INDEX(LTTNG_ERR_TRIGGER_EXISTS) ] = "Trigger already registered", - [ ERROR_INDEX(LTTNG_ERR_TRIGGER_NOT_FOUND) ] = "Trigger not found", - [ ERROR_INDEX(LTTNG_ERR_COMMAND_CANCELLED) ] = "Command cancelled", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING) ] = "Rotation already pending for this session", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE) ] = "Rotation feature not available for this session's creation mode", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_SCHEDULE_SET) ] = "A session rotation schedule of this type is already set on the session", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET) ] = "No session rotation schedule of this type is set on the session", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP) ] = "Session was already rotated once since it became inactive", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_WRONG_VERSION) ] = "Session rotation is not supported by this kernel tracer version", - [ ERROR_INDEX(LTTNG_ERR_NO_SESSION_OUTPUT) ] = "Session has no output", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY) ] = "Rotation feature not available on the relay", - [ ERROR_INDEX(LTTNG_ERR_AGENT_TRACING_DISABLED) ] = "Session daemon agent tracing is disabled", - [ ERROR_INDEX(LTTNG_ERR_PROBE_LOCATION_INVAL) ] = "Invalid userspace probe location", - [ ERROR_INDEX(LTTNG_ERR_ELF_PARSING) ] = "ELF parsing error", - [ ERROR_INDEX(LTTNG_ERR_SDT_PROBE_SEMAPHORE) ] = "SDT probe guarded by a semaphore", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_FAIL_CONSUMER) ] = "Rotation failure on consumer", - [ ERROR_INDEX(LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER) ] = "Rotation rename failure on consumer", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER) ] = "Rotation pending check (local) failure on consumer", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER) ] = "Rotation pending check (relay) failure on consumer", - [ ERROR_INDEX(LTTNG_ERR_MKDIR_FAIL_CONSUMER) ] = "Directory creation failure on consumer", - [ ERROR_INDEX(LTTNG_ERR_CHAN_NOT_FOUND) ] = "Channel not found", - [ ERROR_INDEX(LTTNG_ERR_SNAPSHOT_UNSUPPORTED) ] = "Session configuration does not allow the use of snapshots", - [ ERROR_INDEX(LTTNG_ERR_SESSION_NOT_EXIST) ] = "Recording session does not exist", - [ ERROR_INDEX(LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER) ] = "Trace chunk creation failed on consumer", - [ ERROR_INDEX(LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER) ] = "Trace chunk close failed on consumer", - [ ERROR_INDEX(LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER) ] = "Failed to query consumer for trace chunk existence", - [ ERROR_INDEX(LTTNG_ERR_INVALID_PROTOCOL) ] = "Protocol error occurred", - [ ERROR_INDEX(LTTNG_ERR_FILE_CREATION_ERROR) ] = "Failed to create file", - [ ERROR_INDEX(LTTNG_ERR_TIMER_STOP_ERROR) ] = "Failed to stop a timer", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL) ] = "Rotation feature not supported by the kernel tracer", - [ ERROR_INDEX(LTTNG_ERR_CLEAR_RELAY_DISALLOWED) ] = "Relayd daemon peer does not allow sessions to be cleared", - [ ERROR_INDEX(LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY) ] = "Clearing a session is not supported by the relay daemon", - [ ERROR_INDEX(LTTNG_ERR_CLEAR_FAIL_CONSUMER) ] = "Consumer failed to clear the session", - [ ERROR_INDEX(LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR) ] = "Session was already cleared since it became inactive", - [ ERROR_INDEX(LTTNG_ERR_USER_NOT_FOUND) ] = "User not found", - [ ERROR_INDEX(LTTNG_ERR_GROUP_NOT_FOUND) ] = "Group not found", - [ ERROR_INDEX(LTTNG_ERR_UNSUPPORTED_DOMAIN) ] = "Unsupported domain used", - [ ERROR_INDEX(LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) ] = "Operation does not apply to the process attribute tracker's tracking policy", - [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) ] = "Failed to create an event notifier group notification file descriptor", - [ ERROR_INDEX(LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) ] = "Invalid capture expression", - [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION) ] = "Failed to create event notifier", - [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING) ] = "Failed to initialize event notifier error accounting", - [ ERROR_INDEX(LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL) ] = "No index available in event notifier error accounting", - [ ERROR_INDEX(LTTNG_ERR_BUFFER_FLUSH_FAILED) ] = "Failed to flush stream buffer", - - /* Last element */ - [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" -}; - -/* - * Return ptr to string representing a human readable error code from the - * lttng_error_code enum. - * - * These code MUST be negative in other to treat that as an error value. - */ -const char *error_get_str(int32_t code) -{ - code = -code; - - if (code < LTTNG_OK || code > LTTNG_ERR_NR) { - code = LTTNG_ERR_NR; - } - - return error_string_array[ERROR_INDEX(code)]; -} - -void lttng_abort_on_error(void) -{ - if (lttng_opt_abort_on_error < 0) { - /* Use lttng_secure_getenv() to query its state. */ - const char *value; - - value = lttng_secure_getenv("LTTNG_ABORT_ON_ERROR"); - if (value && !strcmp(value, "1")) { - lttng_opt_abort_on_error = 1; - } else { - lttng_opt_abort_on_error = 0; - } - } - if (lttng_opt_abort_on_error > 0) { - abort(); - } -} diff --git a/src/common/error.cpp b/src/common/error.cpp new file mode 100644 index 000000000..6ca5557f4 --- /dev/null +++ b/src/common/error.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2012 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "error.h" + +/* + * lttng_opt_abort_on_error: unset: -1, disabled: 0, enabled: 1. + * Controlled by the LTTNG_ABORT_ON_ERROR environment variable. + */ +static int lttng_opt_abort_on_error = -1; + +/* TLS variable that contains the time of one single log entry. */ +DEFINE_URCU_TLS(struct log_time, error_log_time); +DEFINE_URCU_TLS(const char *, logger_thread_name); + +const char *log_add_time(void) +{ + int ret; + struct tm tm, *res; + struct timespec tp; + time_t now; + const int errsv = errno; + + ret = lttng_clock_gettime(CLOCK_REALTIME, &tp); + if (ret < 0) { + goto error; + } + now = (time_t) tp.tv_sec; + + res = localtime_r(&now, &tm); + if (!res) { + goto error; + } + + /* Format time in the TLS variable. */ + ret = snprintf(URCU_TLS(error_log_time).str, sizeof(URCU_TLS(error_log_time).str), + "%02d:%02d:%02d.%09ld", + tm.tm_hour, tm.tm_min, tm.tm_sec, tp.tv_nsec); + if (ret < 0) { + goto error; + } + + errno = errsv; + return URCU_TLS(error_log_time).str; + +error: + /* Return an empty string on error so logging is not affected. */ + errno = errsv; + return ""; +} + +void logger_set_thread_name(const char *name, bool set_pthread_name) +{ + int ret; + + LTTNG_ASSERT(name); + URCU_TLS(logger_thread_name) = name; + + if (set_pthread_name) { + ret = lttng_thread_setname(name); + if (ret && ret != -ENOSYS) { + /* Don't fail as this is not essential. */ + DBG("Failed to set pthread name attribute"); + } + } +} + +/* + * Human readable error message. + */ +static +const char *lttng_error_code_str(lttng_error_code code) +{ + switch (code) { + case LTTNG_OK: + return "Success"; + case LTTNG_ERR_UNK: + return "Unknown error"; + case LTTNG_ERR_UND: + return "Undefined command"; + case LTTNG_ERR_UNKNOWN_DOMAIN: + return "Unknown tracing domain"; + case LTTNG_ERR_NO_SESSION: + return "No session found"; + case LTTNG_ERR_CREATE_DIR_FAIL: + return "Create directory failed"; + case LTTNG_ERR_SESSION_FAIL: + return "Create session failed"; + case LTTNG_ERR_SESS_NOT_FOUND: + return "Session name not found"; + case LTTNG_ERR_FATAL: + return "Fatal error of the session daemon"; + case LTTNG_ERR_SELECT_SESS: + return "A session MUST be selected"; + case LTTNG_ERR_EXIST_SESS: + return "Session name already exists"; + case LTTNG_ERR_NO_EVENT: + return "Event not found"; + case LTTNG_ERR_CONNECT_FAIL: + return "Unable to connect to Unix socket"; + case LTTNG_ERR_EPERM: + return "Permission denied"; + case LTTNG_ERR_KERN_NA: + return "Kernel tracer not available"; + case LTTNG_ERR_KERN_VERSION: + return "Kernel tracer version is not compatible"; + case LTTNG_ERR_KERN_EVENT_EXIST: + return "Kernel event already exists"; + case LTTNG_ERR_KERN_SESS_FAIL: + return "Kernel create session failed"; + case LTTNG_ERR_KERN_CHAN_EXIST: + return "Kernel channel already exists"; + case LTTNG_ERR_KERN_CHAN_FAIL: + return "Kernel create channel failed"; + case LTTNG_ERR_KERN_CHAN_NOT_FOUND: + return "Kernel channel not found"; + case LTTNG_ERR_KERN_CHAN_DISABLE_FAIL: + return "Disable kernel channel failed"; + case LTTNG_ERR_KERN_CHAN_ENABLE_FAIL: + return "Enable kernel channel failed"; + case LTTNG_ERR_KERN_CONTEXT_FAIL: + return "Add kernel context failed"; + case LTTNG_ERR_KERN_ENABLE_FAIL: + return "Enable kernel event failed"; + case LTTNG_ERR_KERN_DISABLE_FAIL: + return "Disable kernel event failed"; + case LTTNG_ERR_KERN_META_FAIL: + return "Opening metadata failed"; + case LTTNG_ERR_KERN_START_FAIL: + return "Starting kernel trace failed"; + case LTTNG_ERR_KERN_STOP_FAIL: + return "Stopping kernel trace failed"; + case LTTNG_ERR_KERN_CONSUMER_FAIL: + return "Kernel consumer start failed"; + case LTTNG_ERR_KERN_STREAM_FAIL: + return "Kernel create stream failed"; + case LTTNG_ERR_KERN_LIST_FAIL: + return "Listing kernel events failed"; + case LTTNG_ERR_UST_CALIBRATE_FAIL: + return "UST calibration failed"; + case LTTNG_ERR_UST_SESS_FAIL: + return "UST create session failed"; + case LTTNG_ERR_UST_CHAN_FAIL: + return "UST create channel failed"; + case LTTNG_ERR_UST_CHAN_EXIST: + return "UST channel already exist"; + case LTTNG_ERR_UST_CHAN_NOT_FOUND: + return "UST channel not found"; + case LTTNG_ERR_UST_CHAN_DISABLE_FAIL: + return "Disable UST channel failed"; + case LTTNG_ERR_UST_CHAN_ENABLE_FAIL: + return "Enable UST channel failed"; + case LTTNG_ERR_UST_ENABLE_FAIL: + return "Enable UST event failed"; + case LTTNG_ERR_UST_DISABLE_FAIL: + return "Disable UST event failed"; + case LTTNG_ERR_UST_META_FAIL: + return "Opening metadata failed"; + case LTTNG_ERR_UST_START_FAIL: + return "Starting UST trace failed"; + case LTTNG_ERR_UST_STOP_FAIL: + return "Stopping UST trace failed"; + case LTTNG_ERR_UST_CONSUMER64_FAIL: + return "64-bit UST consumer start failed"; + case LTTNG_ERR_UST_CONSUMER32_FAIL: + return "32-bit UST consumer start failed"; + case LTTNG_ERR_UST_STREAM_FAIL: + return "UST create stream failed"; + case LTTNG_ERR_UST_LIST_FAIL: + return "Listing UST events failed"; + case LTTNG_ERR_UST_EVENT_EXIST: + return "UST event already exist"; + case LTTNG_ERR_UST_EVENT_NOT_FOUND: + return "UST event not found"; + case LTTNG_ERR_UST_CONTEXT_EXIST: + return "UST context already exist"; + case LTTNG_ERR_UST_CONTEXT_INVAL: + return "UST invalid context"; + case LTTNG_ERR_NEED_ROOT_SESSIOND: + return "Tracing the kernel requires a root lttng-sessiond daemon, as well as \"tracing\" group membership or root user ID for the lttng client"; + case LTTNG_ERR_NO_UST: + return "LTTng-UST tracer is not supported. Please rebuild lttng-tools with lttng-ust support enabled"; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + return "Tracing has already been started once"; + case LTTNG_ERR_TRACE_ALREADY_STOPPED: + return "Tracing has already been stopped"; + case LTTNG_ERR_KERN_EVENT_ENOSYS: + return "Kernel event type not supported"; + case LTTNG_ERR_NEED_CHANNEL_NAME: + return "Non-default channel exists within session: channel name needs to be specified with '-c name'"; + case LTTNG_ERR_INVALID: + return "Invalid parameter"; + case LTTNG_ERR_NO_USTCONSUMERD: + return "No UST consumer detected"; + case LTTNG_ERR_NO_KERNCONSUMERD: + return "No kernel consumer detected"; + case LTTNG_ERR_EVENT_EXIST_LOGLEVEL: + return "Event already enabled with different loglevel"; + case LTTNG_ERR_URL_DATA_MISS: + return "Missing data path URL"; + case LTTNG_ERR_URL_CTRL_MISS: + return "Missing control path URL"; + case LTTNG_ERR_ENABLE_CONSUMER_FAIL: + return "Enabling consumer failed"; + case LTTNG_ERR_RELAYD_CONNECT_FAIL: + return "Unable to connect to lttng-relayd"; + case LTTNG_ERR_RELAYD_VERSION_FAIL: + return "Relay daemon not compatible"; + case LTTNG_ERR_FILTER_INVAL: + return "Invalid filter bytecode"; + case LTTNG_ERR_FILTER_NOMEM: + return "Not enough memory for filter bytecode"; + case LTTNG_ERR_FILTER_EXIST: + return "Filter already exist"; + case LTTNG_ERR_NO_CONSUMER: + return "Consumer not found for recording session"; + case LTTNG_ERR_EXCLUSION_INVAL: + return "Invalid event exclusion data"; + case LTTNG_ERR_EXCLUSION_NOMEM: + return "Lack of memory while processing event exclusions"; + case LTTNG_ERR_NO_SESSIOND: + return "No session daemon is available"; + case LTTNG_ERR_SESSION_STARTED: + return "Session is running"; + case LTTNG_ERR_NOT_SUPPORTED: + return "Operation not supported"; + case LTTNG_ERR_UST_EVENT_ENABLED: + return "UST event already enabled"; + case LTTNG_ERR_SET_URL: + return "Error setting URL"; + case LTTNG_ERR_URL_EXIST: + return "URL already exists"; + case LTTNG_ERR_BUFFER_NOT_SUPPORTED: + return "Buffer type not supported"; + case LTTNG_ERR_BUFFER_TYPE_MISMATCH: + return "Buffer type mismatch for session"; + case LTTNG_ERR_NOMEM: + return "Not enough memory"; + case LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST: + return "Snapshot output already exists"; + case LTTNG_ERR_START_SESSION_ONCE: + return "Session needs to be started once"; + case LTTNG_ERR_SNAPSHOT_FAIL: + return "Snapshot record failed"; + case LTTNG_ERR_NO_STREAM: + return "Index without stream on relay"; + case LTTNG_ERR_CHAN_EXIST: + return "Channel already exists"; + case LTTNG_ERR_SNAPSHOT_NODATA: + return "No data available in snapshot"; + case LTTNG_ERR_NO_CHANNEL: + return "No channel found in the session"; + case LTTNG_ERR_SESSION_INVALID_CHAR: + return "Invalid character found in session name"; + case LTTNG_ERR_SAVE_FILE_EXIST: + return "Session file already exists"; + case LTTNG_ERR_SAVE_IO_FAIL: + return "IO error while writing session configuration"; + case LTTNG_ERR_LOAD_INVALID_CONFIG: + return "Invalid session configuration"; + case LTTNG_ERR_LOAD_IO_FAIL: + return "IO error while reading a session configuration"; + case LTTNG_ERR_LOAD_SESSION_NOENT: + return "Session file not found"; + case LTTNG_ERR_MAX_SIZE_INVALID: + return "Snapshot max size is invalid"; + case LTTNG_ERR_MI_OUTPUT_TYPE: + return "Invalid MI output format"; + case LTTNG_ERR_MI_IO_FAIL: + return "IO error while writing MI output"; + case LTTNG_ERR_MI_NOT_IMPLEMENTED: + return "Mi feature not implemented"; + case LTTNG_ERR_INVALID_EVENT_NAME: + return "Invalid event name"; + case LTTNG_ERR_INVALID_CHANNEL_NAME: + return "Invalid channel name"; + case LTTNG_ERR_PROCESS_ATTR_EXISTS: + return "Process attribute is already tracked"; + case LTTNG_ERR_PROCESS_ATTR_MISSING: + return "Process attribute was not tracked"; + case LTTNG_ERR_INVALID_CHANNEL_DOMAIN: + return "Invalid channel domain"; + case LTTNG_ERR_OVERFLOW: + return "Overflow occurred"; + case LTTNG_ERR_SESSION_NOT_STARTED: + return "Session not started"; + case LTTNG_ERR_LIVE_SESSION: + return "Live sessions are not supported"; + case LTTNG_ERR_PER_PID_SESSION: + return "Per-PID recording sessions are not supported"; + case LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE: + return "Context unavailable on this kernel"; + case LTTNG_ERR_REGEN_STATEDUMP_FAIL: + return "Failed to regenerate the state dump"; + case LTTNG_ERR_REGEN_STATEDUMP_NOMEM: + return "Failed to regenerate the state dump, not enough memory"; + case LTTNG_ERR_NOT_SNAPSHOT_SESSION: + return "Snapshot command can't be applied to a non-snapshot session"; + case LTTNG_ERR_INVALID_TRIGGER: + return "Invalid trigger"; + case LTTNG_ERR_TRIGGER_EXISTS: + return "Trigger already registered"; + case LTTNG_ERR_TRIGGER_NOT_FOUND: + return "Trigger not found"; + case LTTNG_ERR_COMMAND_CANCELLED: + return "Command cancelled"; + case LTTNG_ERR_ROTATION_PENDING: + return "Rotation already pending for this session"; + case LTTNG_ERR_ROTATION_NOT_AVAILABLE: + return "Rotation feature not available for this session's creation mode"; + case LTTNG_ERR_ROTATION_SCHEDULE_SET: + return "A session rotation schedule of this type is already set on the session"; + case LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET: + return "No session rotation schedule of this type is set on the session"; + case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP: + return "Session was already rotated once since it became inactive"; + case LTTNG_ERR_ROTATION_WRONG_VERSION: + return "Session rotation is not supported by this kernel tracer version"; + case LTTNG_ERR_NO_SESSION_OUTPUT: + return "Session has no output"; + case LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY: + return "Rotation feature not available on the relay"; + case LTTNG_ERR_AGENT_TRACING_DISABLED: + return "Session daemon agent tracing is disabled"; + case LTTNG_ERR_PROBE_LOCATION_INVAL: + return "Invalid userspace probe location"; + case LTTNG_ERR_ELF_PARSING: + return "ELF parsing error"; + case LTTNG_ERR_SDT_PROBE_SEMAPHORE: + return "SDT probe guarded by a semaphore"; + case LTTNG_ERR_ROTATION_FAIL_CONSUMER: + return "Rotation failure on consumer"; + case LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER: + return "Rotation rename failure on consumer"; + case LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER: + return "Rotation pending check (local) failure on consumer"; + case LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER: + return "Rotation pending check (relay) failure on consumer"; + case LTTNG_ERR_MKDIR_FAIL_CONSUMER: + return "Directory creation failure on consumer"; + case LTTNG_ERR_CHAN_NOT_FOUND: + return "Channel not found"; + case LTTNG_ERR_SNAPSHOT_UNSUPPORTED: + return "Session configuration does not allow the use of snapshots"; + case LTTNG_ERR_SESSION_NOT_EXIST: + return "Recording session does not exist"; + case LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER: + return "Trace chunk creation failed on consumer"; + case LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER: + return "Trace chunk close failed on consumer"; + case LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER: + return "Failed to query consumer for trace chunk existence"; + case LTTNG_ERR_INVALID_PROTOCOL: + return "Protocol error occurred"; + case LTTNG_ERR_FILE_CREATION_ERROR: + return "Failed to create file"; + case LTTNG_ERR_TIMER_STOP_ERROR: + return "Failed to stop a timer"; + case LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL: + return "Rotation feature not supported by the kernel tracer"; + case LTTNG_ERR_CLEAR_RELAY_DISALLOWED: + return "Relayd daemon peer does not allow sessions to be cleared"; + case LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY: + return "Clearing a session is not supported by the relay daemon"; + case LTTNG_ERR_CLEAR_FAIL_CONSUMER: + return "Consumer failed to clear the session"; + case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR: + return "Session was already cleared since it became inactive"; + case LTTNG_ERR_USER_NOT_FOUND: + return "User not found"; + case LTTNG_ERR_GROUP_NOT_FOUND: + return "Group not found"; + case LTTNG_ERR_UNSUPPORTED_DOMAIN: + return "Unsupported domain used"; + case LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY: + return "Operation does not apply to the process attribute tracker's tracking policy"; + case LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD: + return "Failed to create an event notifier group notification file descriptor"; + case LTTNG_ERR_INVALID_CAPTURE_EXPRESSION: + return "Invalid capture expression"; + case LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION: + return "Failed to create event notifier"; + case LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING: + return "Failed to initialize event notifier error accounting"; + case LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL: + return "No index available in event notifier error accounting"; + case LTTNG_ERR_INVALID_ERROR_QUERY_TARGET: + return "Invalid error query target."; + case LTTNG_ERR_BUFFER_FLUSH_FAILED: + return "Failed to flush stream buffer"; + case LTTNG_ERR_NR: + abort(); + } + + abort(); +}; + +/* + * Return ptr to string representing a human readable error code from the + * lttng_error_code enum. + * + * These code MUST be negative in other to treat that as an error value. + */ +const char *error_get_str(int32_t code) +{ + code = -code; + + if (code < LTTNG_OK || code >= LTTNG_ERR_NR) { + code = LTTNG_ERR_UNK; + } + + return lttng_error_code_str((lttng_error_code) code); +} + +void lttng_abort_on_error(void) +{ + if (lttng_opt_abort_on_error < 0) { + /* Use lttng_secure_getenv() to query its state. */ + const char *value; + + value = lttng_secure_getenv("LTTNG_ABORT_ON_ERROR"); + if (value && !strcmp(value, "1")) { + lttng_opt_abort_on_error = 1; + } else { + lttng_opt_abort_on_error = 0; + } + } + if (lttng_opt_abort_on_error > 0) { + abort(); + } +} diff --git a/src/common/evaluation.c b/src/common/evaluation.c deleted file mode 100644 index 6b3e63494..000000000 --- a/src/common/evaluation.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void lttng_evaluation_init(struct lttng_evaluation *evaluation, - enum lttng_condition_type type) -{ - evaluation->type = type; -} - -int lttng_evaluation_serialize(const struct lttng_evaluation *evaluation, - struct lttng_payload *payload) -{ - int ret; - struct lttng_evaluation_comm evaluation_comm = { - .type = (int8_t) evaluation->type - }; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &evaluation_comm, - sizeof(evaluation_comm)); - if (ret) { - goto end; - } - - if (evaluation->serialize) { - ret = evaluation->serialize(evaluation, payload); - if (ret) { - goto end; - } - } -end: - return ret; -} - -ssize_t lttng_evaluation_create_from_payload( - const struct lttng_condition *condition, - struct lttng_payload_view *src_view, - struct lttng_evaluation **evaluation) -{ - ssize_t ret, evaluation_size = 0; - const struct lttng_evaluation_comm *evaluation_comm; - struct lttng_payload_view evaluation_comm_view = - lttng_payload_view_from_view( - src_view, 0, sizeof(*evaluation_comm)); - struct lttng_payload_view evaluation_view = - lttng_payload_view_from_view(src_view, - sizeof(*evaluation_comm), -1); - - if (!src_view || !evaluation) { - ret = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&evaluation_comm_view)) { - ret = -1; - goto end; - } - - evaluation_comm = (typeof(evaluation_comm)) evaluation_comm_view.buffer.data; - evaluation_size += sizeof(*evaluation_comm); - - switch ((enum lttng_condition_type) evaluation_comm->type) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - ret = lttng_evaluation_buffer_usage_low_create_from_payload( - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - ret = lttng_evaluation_buffer_usage_high_create_from_payload( - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - ret = lttng_evaluation_session_consumed_size_create_from_payload( - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - ret = lttng_evaluation_session_rotation_ongoing_create_from_payload( - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - ret = lttng_evaluation_session_rotation_completed_create_from_payload( - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: - LTTNG_ASSERT(condition); - LTTNG_ASSERT(condition->type == - LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); - ret = lttng_evaluation_event_rule_matches_create_from_payload( - container_of(condition, - const struct lttng_condition_event_rule_matches, - parent), - &evaluation_view, evaluation); - if (ret < 0) { - goto end; - } - evaluation_size += ret; - break; - default: - ERR("Attempted to create evaluation of unknown type (%i)", - (int) evaluation_comm->type); - ret = -1; - goto end; - } - - ret = evaluation_size; -end: - return ret; -} - -enum lttng_condition_type lttng_evaluation_get_type( - const struct lttng_evaluation *evaluation) -{ - return evaluation ? evaluation->type : LTTNG_CONDITION_TYPE_UNKNOWN; -} - -void lttng_evaluation_destroy(struct lttng_evaluation *evaluation) -{ - if (!evaluation) { - return; - } - - LTTNG_ASSERT(evaluation->destroy); - evaluation->destroy(evaluation); -} diff --git a/src/common/evaluation.cpp b/src/common/evaluation.cpp new file mode 100644 index 000000000..6b3e63494 --- /dev/null +++ b/src/common/evaluation.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void lttng_evaluation_init(struct lttng_evaluation *evaluation, + enum lttng_condition_type type) +{ + evaluation->type = type; +} + +int lttng_evaluation_serialize(const struct lttng_evaluation *evaluation, + struct lttng_payload *payload) +{ + int ret; + struct lttng_evaluation_comm evaluation_comm = { + .type = (int8_t) evaluation->type + }; + + ret = lttng_dynamic_buffer_append(&payload->buffer, &evaluation_comm, + sizeof(evaluation_comm)); + if (ret) { + goto end; + } + + if (evaluation->serialize) { + ret = evaluation->serialize(evaluation, payload); + if (ret) { + goto end; + } + } +end: + return ret; +} + +ssize_t lttng_evaluation_create_from_payload( + const struct lttng_condition *condition, + struct lttng_payload_view *src_view, + struct lttng_evaluation **evaluation) +{ + ssize_t ret, evaluation_size = 0; + const struct lttng_evaluation_comm *evaluation_comm; + struct lttng_payload_view evaluation_comm_view = + lttng_payload_view_from_view( + src_view, 0, sizeof(*evaluation_comm)); + struct lttng_payload_view evaluation_view = + lttng_payload_view_from_view(src_view, + sizeof(*evaluation_comm), -1); + + if (!src_view || !evaluation) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&evaluation_comm_view)) { + ret = -1; + goto end; + } + + evaluation_comm = (typeof(evaluation_comm)) evaluation_comm_view.buffer.data; + evaluation_size += sizeof(*evaluation_comm); + + switch ((enum lttng_condition_type) evaluation_comm->type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + ret = lttng_evaluation_buffer_usage_low_create_from_payload( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + ret = lttng_evaluation_buffer_usage_high_create_from_payload( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + ret = lttng_evaluation_session_consumed_size_create_from_payload( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + ret = lttng_evaluation_session_rotation_ongoing_create_from_payload( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + ret = lttng_evaluation_session_rotation_completed_create_from_payload( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES: + LTTNG_ASSERT(condition); + LTTNG_ASSERT(condition->type == + LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES); + ret = lttng_evaluation_event_rule_matches_create_from_payload( + container_of(condition, + const struct lttng_condition_event_rule_matches, + parent), + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + default: + ERR("Attempted to create evaluation of unknown type (%i)", + (int) evaluation_comm->type); + ret = -1; + goto end; + } + + ret = evaluation_size; +end: + return ret; +} + +enum lttng_condition_type lttng_evaluation_get_type( + const struct lttng_evaluation *evaluation) +{ + return evaluation ? evaluation->type : LTTNG_CONDITION_TYPE_UNKNOWN; +} + +void lttng_evaluation_destroy(struct lttng_evaluation *evaluation) +{ + if (!evaluation) { + return; + } + + LTTNG_ASSERT(evaluation->destroy); + evaluation->destroy(evaluation); +} diff --git a/src/common/event-expr/event-expr.c b/src/common/event-expr/event-expr.c deleted file mode 100644 index 7b07312f2..000000000 --- a/src/common/event-expr/event-expr.c +++ /dev/null @@ -1,918 +0,0 @@ -/* - * event-expr.c - * - * Copyright (C) 2020 Philippe Proulx - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include - -enum lttng_event_expr_type lttng_event_expr_get_type( - const struct lttng_event_expr *expr) -{ - enum lttng_event_expr_type type; - - if (!expr) { - type = LTTNG_EVENT_EXPR_TYPE_INVALID; - goto end; - } - - type = expr->type; - -end: - return type; -} - -static -struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type, - size_t size) -{ - struct lttng_event_expr *expr; - - expr = zmalloc(size); - if (!expr) { - goto end; - } - - expr->type = type; - -end: - return expr; -} - -static -struct lttng_event_expr_field *create_field_event_expr( - enum lttng_event_expr_type type, - const char *name) -{ - struct lttng_event_expr_field *expr = - container_of( - create_empty_expr(type, sizeof(*expr)), - struct lttng_event_expr_field, parent); - - if (!expr) { - goto error; - } - - LTTNG_ASSERT(name); - expr->name = strdup(name); - if (!expr->name) { - goto error; - } - - goto end; - -error: - if (expr) { - lttng_event_expr_destroy(&expr->parent); - } - expr = NULL; - -end: - return expr; -} - -struct lttng_event_expr *lttng_event_expr_event_payload_field_create( - const char *field_name) -{ - struct lttng_event_expr *expr = NULL; - - if (!field_name) { - goto end; - } - - expr = &create_field_event_expr( - LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD, - field_name)->parent; - -end: - return expr; -} - -struct lttng_event_expr *lttng_event_expr_channel_context_field_create( - const char *field_name) -{ - struct lttng_event_expr *expr = NULL; - - if (!field_name) { - goto end; - } - - expr = &create_field_event_expr( - LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD, - field_name)->parent; - -end: - return expr; -} - -struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create( - const char *provider_name, const char *type_name) -{ - struct lttng_event_expr_app_specific_context_field *expr = NULL; - struct lttng_event_expr *ret_parent_expr; - - if (!type_name || !provider_name) { - goto error; - } - - expr = container_of(create_empty_expr( - LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD, - sizeof(*expr)), - struct lttng_event_expr_app_specific_context_field, - parent); - if (!expr) { - goto error; - } - - expr->provider_name = strdup(provider_name); - if (!expr->provider_name) { - goto error; - } - - expr->type_name = strdup(type_name); - if (!expr->type_name) { - goto error; - } - - ret_parent_expr = &expr->parent; - goto end; - -error: - if (expr) { - lttng_event_expr_destroy(&expr->parent); - } - ret_parent_expr = NULL; - -end: - return ret_parent_expr; -} - -struct lttng_event_expr *lttng_event_expr_array_field_element_create( - struct lttng_event_expr *array_field_expr, - unsigned int index) -{ - struct lttng_event_expr_array_field_element *expr = NULL; - struct lttng_event_expr *ret_parent_expr; - - /* The parent array field expression must be an l-value */ - if (!array_field_expr || - !lttng_event_expr_is_lvalue(array_field_expr)) { - goto error; - } - - expr = container_of(create_empty_expr( - LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT, - sizeof(*expr)), - struct lttng_event_expr_array_field_element, - parent); - if (!expr) { - goto error; - } - - expr->array_field_expr = array_field_expr; - expr->index = index; - ret_parent_expr = &expr->parent; - goto end; - -error: - ret_parent_expr = NULL; - -end: - return ret_parent_expr; -} - -const char *lttng_event_expr_event_payload_field_get_name( - const struct lttng_event_expr *expr) -{ - const char *ret = NULL; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) { - goto end; - } - - ret = container_of(expr, - const struct lttng_event_expr_field, parent)->name; - -end: - return ret; -} - -const char *lttng_event_expr_channel_context_field_get_name( - const struct lttng_event_expr *expr) -{ - const char *ret = NULL; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) { - goto end; - } - - ret = container_of(expr, - const struct lttng_event_expr_field, parent)->name; - -end: - return ret; -} - -const char *lttng_event_expr_app_specific_context_field_get_provider_name( - const struct lttng_event_expr *expr) -{ - const char *ret = NULL; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { - goto end; - } - - ret = container_of(expr, - const struct lttng_event_expr_app_specific_context_field, - parent)->provider_name; - -end: - return ret; -} - -const char *lttng_event_expr_app_specific_context_field_get_type_name( - const struct lttng_event_expr *expr) -{ - const char *ret = NULL; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { - goto end; - } - - ret = container_of(expr, - const struct lttng_event_expr_app_specific_context_field, - parent)->type_name; - -end: - return ret; -} - -const struct lttng_event_expr * -lttng_event_expr_array_field_element_get_parent_expr( - const struct lttng_event_expr *expr) -{ - const struct lttng_event_expr *ret = NULL; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) { - goto end; - } - - ret = container_of(expr, - const struct lttng_event_expr_array_field_element, - parent)->array_field_expr; - -end: - return ret; -} - -enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index( - const struct lttng_event_expr *expr, unsigned int *index) -{ - enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK; - - if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT || - !index) { - ret = LTTNG_EVENT_EXPR_STATUS_INVALID; - goto end; - } - - *index = container_of(expr, - const struct lttng_event_expr_array_field_element, - parent)->index; - -end: - return ret; -} - -bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, - const struct lttng_event_expr *expr_b) -{ - bool is_equal = true; - - if (!expr_a && !expr_b) { - /* Both `NULL`: equal */ - goto end; - } - - if (!expr_a || !expr_b) { - /* Only one `NULL`: not equal */ - goto not_equal; - } - - if (expr_a->type != expr_b->type) { - /* Different types: not equal */ - goto not_equal; - } - - switch (expr_a->type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - const struct lttng_event_expr_field *field_expr_a = - container_of(expr_a, - const struct lttng_event_expr_field, - parent); - const struct lttng_event_expr_field *field_expr_b = - container_of(expr_b, - const struct lttng_event_expr_field, - parent); - - if (strcmp(field_expr_a->name, field_expr_b->name) != 0) { - goto not_equal; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - const struct lttng_event_expr_app_specific_context_field *field_expr_a = - container_of(expr_a, - const struct lttng_event_expr_app_specific_context_field, - parent); - const struct lttng_event_expr_app_specific_context_field *field_expr_b = - container_of(expr_b, - const struct lttng_event_expr_app_specific_context_field, - parent); - - if (strcmp(field_expr_a->provider_name, - field_expr_b->provider_name) != 0) { - goto not_equal; - } - - if (strcmp(field_expr_a->type_name, - field_expr_b->type_name) != 0) { - goto not_equal; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - const struct lttng_event_expr_array_field_element *elem_expr_a = - container_of(expr_a, - const struct lttng_event_expr_array_field_element, - parent); - const struct lttng_event_expr_array_field_element *elem_expr_b = - container_of(expr_b, - const struct lttng_event_expr_array_field_element, - parent); - - if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr, - elem_expr_b->array_field_expr)) { - goto not_equal; - } - - if (elem_expr_a->index != elem_expr_b->index) { - goto not_equal; - } - - break; - } - default: - break; - } - - goto end; - -not_equal: - is_equal = false; - -end: - return is_equal; -} - -void lttng_event_expr_destroy(struct lttng_event_expr *expr) -{ - if (!expr) { - goto end; - } - - switch (expr->type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - struct lttng_event_expr_field *field_expr = - container_of(expr, - struct lttng_event_expr_field, parent); - - free(field_expr->name); - break; - } - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - struct lttng_event_expr_app_specific_context_field *field_expr = - container_of(expr, - struct lttng_event_expr_app_specific_context_field, - parent); - - free(field_expr->provider_name); - free(field_expr->type_name); - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - struct lttng_event_expr_array_field_element *elem_expr = - container_of(expr, - struct lttng_event_expr_array_field_element, - parent); - - lttng_event_expr_destroy(elem_expr->array_field_expr); - break; - } - default: - break; - } - - free(expr); - -end: - return; -} - -static int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr, - struct lttng_bytecode_alloc **bytecode, - struct lttng_bytecode_alloc **bytecode_reloc) -{ - int status; - enum lttng_event_expr_status event_expr_status; - - switch (lttng_event_expr_get_type(expr)) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - { - const char *name; - - status = bytecode_push_get_payload_root(bytecode); - if (status) { - ERR("Failed to get payload root from bytecode"); - goto end; - } - - name = lttng_event_expr_event_payload_field_get_name(expr); - if (!name) { - ERR("Failed to get payload field name from event expression"); - status = -1; - goto end; - } - - status = bytecode_push_get_symbol( - bytecode, bytecode_reloc, name); - if (status) { - ERR("Failed to push 'get symbol %s' in bytecode", name); - goto end; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - { - const char *name; - - status = bytecode_push_get_context_root(bytecode); - if (status) { - ERR("Failed to get context root from bytecode"); - goto end; - } - - name = lttng_event_expr_channel_context_field_get_name(expr); - if (!name) { - ERR("Failed to get channel context field name from event expression"); - status = -1; - goto end; - } - - status = bytecode_push_get_symbol( - bytecode, bytecode_reloc, name); - if (status) { - ERR("Failed to push 'get symbol %s' in bytecode", name); - goto end; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - { - int ret; - char *name = NULL; - const char *provider_name, *type_name; - - status = bytecode_push_get_app_context_root(bytecode); - if (status) { - ERR("Failed to get application context root from bytecode"); - goto end; - } - - provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( - expr); - if (!provider_name) { - ERR("Failed to get application context provider name from event expression"); - status = -1; - goto end; - } - - type_name = lttng_event_expr_app_specific_context_field_get_type_name( - expr); - if (!type_name) { - ERR("Failed to get application context type name from event expression"); - status = -1; - goto end; - } - - /* - * Reconstitute the app context field name from its two parts. - */ - ret = asprintf(&name, "%s:%s", provider_name, type_name); - if (ret < 0) { - PERROR("Failed to format application specific context: provider_name = '%s', type_name = '%s'", - provider_name, type_name); - status = -1; - goto end; - } - - status = bytecode_push_get_symbol( - bytecode, bytecode_reloc, name); - free(name); - if (status) { - ERR("Failed to push 'get symbol %s:%s' in bytecode", - provider_name, type_name); - goto end; - } - - break; - } - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - { - unsigned int index; - const struct lttng_event_expr *parent; - - parent = lttng_event_expr_array_field_element_get_parent_expr( - expr); - if (!parent) { - ERR("Failed to get parent expression from array event expression"); - status = -1; - goto end; - } - - status = event_expr_to_bytecode_recursive( - parent, bytecode, bytecode_reloc); - if (status) { - goto end; - } - - event_expr_status = - lttng_event_expr_array_field_element_get_index( - expr, &index); - if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) { - ERR("Failed to get array field element index from event expression"); - status = -1; - goto end; - } - - status = bytecode_push_get_index_u64(bytecode, index); - if (status) { - ERR("Failed to push 'get index %u' in bytecode", index); - goto end; - } - - break; - } - default: - abort(); - } - - status = 0; -end: - return status; -} - -int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr, - struct lttng_bytecode **bytecode_out) -{ - int status; - struct return_op ret_insn; - struct lttng_bytecode_alloc *bytecode = NULL; - struct lttng_bytecode_alloc *bytecode_reloc = NULL; - - status = bytecode_init(&bytecode); - if (status) { - ERR("Failed to initialize bytecode"); - goto end; - } - - status = bytecode_init(&bytecode_reloc); - if (status) { - ERR("Failed to initialize relocation bytecode"); - goto end; - } - - status = event_expr_to_bytecode_recursive( - expr, &bytecode, &bytecode_reloc); - if (status) { - /* Errors already logged. */ - goto end; - } - - ret_insn.op = BYTECODE_OP_RETURN; - bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn)); - - /* Append symbol table to bytecode. */ - bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b); - status = bytecode_push(&bytecode, bytecode_reloc->b.data, 1, - bytecode_get_len(&bytecode_reloc->b)); - if (status) { - ERR("Failed to push symbol table to bytecode"); - goto end; - } - - /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`. */ - *bytecode_out = lttng_bytecode_copy(&bytecode->b); - if (!*bytecode_out) { - status = -1; - goto end; - } - -end: - if (bytecode) { - free(bytecode); - } - - if (bytecode_reloc) { - free(bytecode_reloc); - } - - return status; -} - -static -enum lttng_error_code lttng_event_expr_event_payload_field_mi_serialize( - const struct lttng_event_expr *expression, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *name = NULL; - - LTTNG_ASSERT(expression); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD); - - name = lttng_event_expr_event_payload_field_get_name(expression); - LTTNG_ASSERT(name); - - /* Open event expr payload field element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_event_expr_payload_field); - if (ret) { - goto mi_error; - } - - /* Name. */ - ret = mi_lttng_writer_write_element_string( - writer, config_element_name, name); - if (ret) { - goto mi_error; - } - - /* Close event expr payload field element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -enum lttng_error_code lttng_event_expr_channel_context_field_mi_serialize( - const struct lttng_event_expr *expression, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *name = NULL; - - LTTNG_ASSERT(expression); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD); - - name = lttng_event_expr_channel_context_field_get_name(expression); - LTTNG_ASSERT(name); - - /* Open event expr channel context field element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_event_expr_channel_context_field); - if (ret) { - goto mi_error; - } - - /* Name. */ - ret = mi_lttng_writer_write_element_string( - writer, config_element_name, name); - if (ret) { - goto mi_error; - } - - /* Close event expr channel context field element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -enum lttng_error_code lttng_event_expr_app_specific_context_field_mi_serialize( - const struct lttng_event_expr *expression, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *provider_name = NULL; - const char *type_name = NULL; - - LTTNG_ASSERT(expression); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(expression->type == - LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD); - - provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( - expression); - LTTNG_ASSERT(provider_name); - - type_name = lttng_event_expr_app_specific_context_field_get_type_name( - expression); - LTTNG_ASSERT(provider_name); - - /* Open event expr app specific context field element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_event_expr_app_specific_context_field); - if (ret) { - goto mi_error; - } - - /* Provider name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_event_expr_provider_name, - provider_name); - if (ret) { - goto mi_error; - } - - /* Type name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_event_expr_type_name, type_name); - if (ret) { - goto mi_error; - } - - /* Close event expr app specific context field element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -enum lttng_error_code lttng_event_expr_array_field_element_mi_serialize( - const struct lttng_event_expr *expression, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_event_expr_status status; - const struct lttng_event_expr *parent_expr = NULL; - unsigned int index; - - LTTNG_ASSERT(expression); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT); - - status = lttng_event_expr_array_field_element_get_index( - expression, &index); - LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); - - parent_expr = lttng_event_expr_array_field_element_get_parent_expr( - expression); - LTTNG_ASSERT(parent_expr != NULL); - - /* Open event expr array field element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_event_expr_array_field_element); - if (ret) { - goto mi_error; - } - - /* Index. */ - ret = mi_lttng_writer_write_element_unsigned_int( - writer, mi_lttng_element_event_expr_index, index); - if (ret) { - goto mi_error; - } - - /* Parent expression. */ - ret_code = lttng_event_expr_mi_serialize(parent_expr, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close event expr array field element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -enum lttng_error_code lttng_event_expr_mi_serialize( - const struct lttng_event_expr *expression, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(expression); - LTTNG_ASSERT(writer); - - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_expr); - if (ret) { - goto mi_error; - } - - switch (expression->type) { - case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: - ret_code = lttng_event_expr_event_payload_field_mi_serialize( - expression, writer); - break; - case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: - ret_code = lttng_event_expr_channel_context_field_mi_serialize( - expression, writer); - break; - case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: - ret_code = lttng_event_expr_app_specific_context_field_mi_serialize( - expression, writer); - break; - case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: - ret_code = lttng_event_expr_array_field_element_mi_serialize( - expression, writer); - break; - default: - abort(); - } - - if (ret_code != LTTNG_OK) { - goto end; - } - - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; - -end: - return ret_code; -} diff --git a/src/common/event-expr/event-expr.cpp b/src/common/event-expr/event-expr.cpp new file mode 100644 index 000000000..e80fb1d5d --- /dev/null +++ b/src/common/event-expr/event-expr.cpp @@ -0,0 +1,918 @@ +/* + * event-expr.c + * + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include + +#include +#include +#include +#include +#include +#include +#include + +enum lttng_event_expr_type lttng_event_expr_get_type( + const struct lttng_event_expr *expr) +{ + enum lttng_event_expr_type type; + + if (!expr) { + type = LTTNG_EVENT_EXPR_TYPE_INVALID; + goto end; + } + + type = expr->type; + +end: + return type; +} + +static +struct lttng_event_expr *create_empty_expr(enum lttng_event_expr_type type, + size_t size) +{ + struct lttng_event_expr *expr; + + expr = (lttng_event_expr *) zmalloc(size); + if (!expr) { + goto end; + } + + expr->type = type; + +end: + return expr; +} + +static +struct lttng_event_expr_field *create_field_event_expr( + enum lttng_event_expr_type type, + const char *name) +{ + struct lttng_event_expr_field *expr = + container_of( + create_empty_expr(type, sizeof(*expr)), + struct lttng_event_expr_field, parent); + + if (!expr) { + goto error; + } + + LTTNG_ASSERT(name); + expr->name = strdup(name); + if (!expr->name) { + goto error; + } + + goto end; + +error: + if (expr) { + lttng_event_expr_destroy(&expr->parent); + } + expr = NULL; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_event_payload_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_channel_context_field_create( + const char *field_name) +{ + struct lttng_event_expr *expr = NULL; + + if (!field_name) { + goto end; + } + + expr = &create_field_event_expr( + LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD, + field_name)->parent; + +end: + return expr; +} + +struct lttng_event_expr *lttng_event_expr_app_specific_context_field_create( + const char *provider_name, const char *type_name) +{ + struct lttng_event_expr_app_specific_context_field *expr = NULL; + struct lttng_event_expr *ret_parent_expr; + + if (!type_name || !provider_name) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD, + sizeof(*expr)), + struct lttng_event_expr_app_specific_context_field, + parent); + if (!expr) { + goto error; + } + + expr->provider_name = strdup(provider_name); + if (!expr->provider_name) { + goto error; + } + + expr->type_name = strdup(type_name); + if (!expr->type_name) { + goto error; + } + + ret_parent_expr = &expr->parent; + goto end; + +error: + if (expr) { + lttng_event_expr_destroy(&expr->parent); + } + ret_parent_expr = NULL; + +end: + return ret_parent_expr; +} + +struct lttng_event_expr *lttng_event_expr_array_field_element_create( + struct lttng_event_expr *array_field_expr, + unsigned int index) +{ + struct lttng_event_expr_array_field_element *expr = NULL; + struct lttng_event_expr *ret_parent_expr; + + /* The parent array field expression must be an l-value */ + if (!array_field_expr || + !lttng_event_expr_is_lvalue(array_field_expr)) { + goto error; + } + + expr = container_of(create_empty_expr( + LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT, + sizeof(*expr)), + struct lttng_event_expr_array_field_element, + parent); + if (!expr) { + goto error; + } + + expr->array_field_expr = array_field_expr; + expr->index = index; + ret_parent_expr = &expr->parent; + goto end; + +error: + ret_parent_expr = NULL; + +end: + return ret_parent_expr; +} + +const char *lttng_event_expr_event_payload_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_channel_context_field_get_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_field, parent)->name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_provider_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->provider_name; + +end: + return ret; +} + +const char *lttng_event_expr_app_specific_context_field_get_type_name( + const struct lttng_event_expr *expr) +{ + const char *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_app_specific_context_field, + parent)->type_name; + +end: + return ret; +} + +const struct lttng_event_expr * +lttng_event_expr_array_field_element_get_parent_expr( + const struct lttng_event_expr *expr) +{ + const struct lttng_event_expr *ret = NULL; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT) { + goto end; + } + + ret = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->array_field_expr; + +end: + return ret; +} + +enum lttng_event_expr_status lttng_event_expr_array_field_element_get_index( + const struct lttng_event_expr *expr, unsigned int *index) +{ + enum lttng_event_expr_status ret = LTTNG_EVENT_EXPR_STATUS_OK; + + if (!expr || expr->type != LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT || + !index) { + ret = LTTNG_EVENT_EXPR_STATUS_INVALID; + goto end; + } + + *index = container_of(expr, + const struct lttng_event_expr_array_field_element, + parent)->index; + +end: + return ret; +} + +bool lttng_event_expr_is_equal(const struct lttng_event_expr *expr_a, + const struct lttng_event_expr *expr_b) +{ + bool is_equal = true; + + if (!expr_a && !expr_b) { + /* Both `NULL`: equal */ + goto end; + } + + if (!expr_a || !expr_b) { + /* Only one `NULL`: not equal */ + goto not_equal; + } + + if (expr_a->type != expr_b->type) { + /* Different types: not equal */ + goto not_equal; + } + + switch (expr_a->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const struct lttng_event_expr_field *field_expr_a = + container_of(expr_a, + const struct lttng_event_expr_field, + parent); + const struct lttng_event_expr_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_field, + parent); + + if (strcmp(field_expr_a->name, field_expr_b->name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + const struct lttng_event_expr_app_specific_context_field *field_expr_a = + container_of(expr_a, + const struct lttng_event_expr_app_specific_context_field, + parent); + const struct lttng_event_expr_app_specific_context_field *field_expr_b = + container_of(expr_b, + const struct lttng_event_expr_app_specific_context_field, + parent); + + if (strcmp(field_expr_a->provider_name, + field_expr_b->provider_name) != 0) { + goto not_equal; + } + + if (strcmp(field_expr_a->type_name, + field_expr_b->type_name) != 0) { + goto not_equal; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + const struct lttng_event_expr_array_field_element *elem_expr_a = + container_of(expr_a, + const struct lttng_event_expr_array_field_element, + parent); + const struct lttng_event_expr_array_field_element *elem_expr_b = + container_of(expr_b, + const struct lttng_event_expr_array_field_element, + parent); + + if (!lttng_event_expr_is_equal(elem_expr_a->array_field_expr, + elem_expr_b->array_field_expr)) { + goto not_equal; + } + + if (elem_expr_a->index != elem_expr_b->index) { + goto not_equal; + } + + break; + } + default: + break; + } + + goto end; + +not_equal: + is_equal = false; + +end: + return is_equal; +} + +void lttng_event_expr_destroy(struct lttng_event_expr *expr) +{ + if (!expr) { + goto end; + } + + switch (expr->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + struct lttng_event_expr_field *field_expr = + container_of(expr, + struct lttng_event_expr_field, parent); + + free(field_expr->name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + struct lttng_event_expr_app_specific_context_field *field_expr = + container_of(expr, + struct lttng_event_expr_app_specific_context_field, + parent); + + free(field_expr->provider_name); + free(field_expr->type_name); + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + struct lttng_event_expr_array_field_element *elem_expr = + container_of(expr, + struct lttng_event_expr_array_field_element, + parent); + + lttng_event_expr_destroy(elem_expr->array_field_expr); + break; + } + default: + break; + } + + free(expr); + +end: + return; +} + +static int event_expr_to_bytecode_recursive(const struct lttng_event_expr *expr, + struct lttng_bytecode_alloc **bytecode, + struct lttng_bytecode_alloc **bytecode_reloc) +{ + int status; + enum lttng_event_expr_status event_expr_status; + + switch (lttng_event_expr_get_type(expr)) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + { + const char *name; + + status = bytecode_push_get_payload_root(bytecode); + if (status) { + ERR("Failed to get payload root from bytecode"); + goto end; + } + + name = lttng_event_expr_event_payload_field_get_name(expr); + if (!name) { + ERR("Failed to get payload field name from event expression"); + status = -1; + goto end; + } + + status = bytecode_push_get_symbol( + bytecode, bytecode_reloc, name); + if (status) { + ERR("Failed to push 'get symbol %s' in bytecode", name); + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + { + const char *name; + + status = bytecode_push_get_context_root(bytecode); + if (status) { + ERR("Failed to get context root from bytecode"); + goto end; + } + + name = lttng_event_expr_channel_context_field_get_name(expr); + if (!name) { + ERR("Failed to get channel context field name from event expression"); + status = -1; + goto end; + } + + status = bytecode_push_get_symbol( + bytecode, bytecode_reloc, name); + if (status) { + ERR("Failed to push 'get symbol %s' in bytecode", name); + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + { + int ret; + char *name = NULL; + const char *provider_name, *type_name; + + status = bytecode_push_get_app_context_root(bytecode); + if (status) { + ERR("Failed to get application context root from bytecode"); + goto end; + } + + provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( + expr); + if (!provider_name) { + ERR("Failed to get application context provider name from event expression"); + status = -1; + goto end; + } + + type_name = lttng_event_expr_app_specific_context_field_get_type_name( + expr); + if (!type_name) { + ERR("Failed to get application context type name from event expression"); + status = -1; + goto end; + } + + /* + * Reconstitute the app context field name from its two parts. + */ + ret = asprintf(&name, "%s:%s", provider_name, type_name); + if (ret < 0) { + PERROR("Failed to format application specific context: provider_name = '%s', type_name = '%s'", + provider_name, type_name); + status = -1; + goto end; + } + + status = bytecode_push_get_symbol( + bytecode, bytecode_reloc, name); + free(name); + if (status) { + ERR("Failed to push 'get symbol %s:%s' in bytecode", + provider_name, type_name); + goto end; + } + + break; + } + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + { + unsigned int index; + const struct lttng_event_expr *parent; + + parent = lttng_event_expr_array_field_element_get_parent_expr( + expr); + if (!parent) { + ERR("Failed to get parent expression from array event expression"); + status = -1; + goto end; + } + + status = event_expr_to_bytecode_recursive( + parent, bytecode, bytecode_reloc); + if (status) { + goto end; + } + + event_expr_status = + lttng_event_expr_array_field_element_get_index( + expr, &index); + if (event_expr_status != LTTNG_EVENT_EXPR_STATUS_OK) { + ERR("Failed to get array field element index from event expression"); + status = -1; + goto end; + } + + status = bytecode_push_get_index_u64(bytecode, index); + if (status) { + ERR("Failed to push 'get index %u' in bytecode", index); + goto end; + } + + break; + } + default: + abort(); + } + + status = 0; +end: + return status; +} + +int lttng_event_expr_to_bytecode(const struct lttng_event_expr *expr, + struct lttng_bytecode **bytecode_out) +{ + int status; + struct return_op ret_insn; + struct lttng_bytecode_alloc *bytecode = NULL; + struct lttng_bytecode_alloc *bytecode_reloc = NULL; + + status = bytecode_init(&bytecode); + if (status) { + ERR("Failed to initialize bytecode"); + goto end; + } + + status = bytecode_init(&bytecode_reloc); + if (status) { + ERR("Failed to initialize relocation bytecode"); + goto end; + } + + status = event_expr_to_bytecode_recursive( + expr, &bytecode, &bytecode_reloc); + if (status) { + /* Errors already logged. */ + goto end; + } + + ret_insn.op = BYTECODE_OP_RETURN; + bytecode_push(&bytecode, &ret_insn, 1, sizeof(ret_insn)); + + /* Append symbol table to bytecode. */ + bytecode->b.reloc_table_offset = bytecode_get_len(&bytecode->b); + status = bytecode_push(&bytecode, bytecode_reloc->b.data, 1, + bytecode_get_len(&bytecode_reloc->b)); + if (status) { + ERR("Failed to push symbol table to bytecode"); + goto end; + } + + /* Copy the `lttng_bytecode` out of the `lttng_bytecode_alloc`. */ + *bytecode_out = lttng_bytecode_copy(&bytecode->b); + if (!*bytecode_out) { + status = -1; + goto end; + } + +end: + if (bytecode) { + free(bytecode); + } + + if (bytecode_reloc) { + free(bytecode_reloc); + } + + return status; +} + +static +enum lttng_error_code lttng_event_expr_event_payload_field_mi_serialize( + const struct lttng_event_expr *expression, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *name = NULL; + + LTTNG_ASSERT(expression); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD); + + name = lttng_event_expr_event_payload_field_get_name(expression); + LTTNG_ASSERT(name); + + /* Open event expr payload field element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_event_expr_payload_field); + if (ret) { + goto mi_error; + } + + /* Name. */ + ret = mi_lttng_writer_write_element_string( + writer, config_element_name, name); + if (ret) { + goto mi_error; + } + + /* Close event expr payload field element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +enum lttng_error_code lttng_event_expr_channel_context_field_mi_serialize( + const struct lttng_event_expr *expression, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *name = NULL; + + LTTNG_ASSERT(expression); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD); + + name = lttng_event_expr_channel_context_field_get_name(expression); + LTTNG_ASSERT(name); + + /* Open event expr channel context field element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_event_expr_channel_context_field); + if (ret) { + goto mi_error; + } + + /* Name. */ + ret = mi_lttng_writer_write_element_string( + writer, config_element_name, name); + if (ret) { + goto mi_error; + } + + /* Close event expr channel context field element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +enum lttng_error_code lttng_event_expr_app_specific_context_field_mi_serialize( + const struct lttng_event_expr *expression, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *provider_name = NULL; + const char *type_name = NULL; + + LTTNG_ASSERT(expression); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(expression->type == + LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD); + + provider_name = lttng_event_expr_app_specific_context_field_get_provider_name( + expression); + LTTNG_ASSERT(provider_name); + + type_name = lttng_event_expr_app_specific_context_field_get_type_name( + expression); + LTTNG_ASSERT(provider_name); + + /* Open event expr app specific context field element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_event_expr_app_specific_context_field); + if (ret) { + goto mi_error; + } + + /* Provider name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_event_expr_provider_name, + provider_name); + if (ret) { + goto mi_error; + } + + /* Type name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_event_expr_type_name, type_name); + if (ret) { + goto mi_error; + } + + /* Close event expr app specific context field element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +enum lttng_error_code lttng_event_expr_array_field_element_mi_serialize( + const struct lttng_event_expr *expression, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_event_expr_status status; + const struct lttng_event_expr *parent_expr = NULL; + unsigned int index; + + LTTNG_ASSERT(expression); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(expression->type == LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT); + + status = lttng_event_expr_array_field_element_get_index( + expression, &index); + LTTNG_ASSERT(status == LTTNG_EVENT_EXPR_STATUS_OK); + + parent_expr = lttng_event_expr_array_field_element_get_parent_expr( + expression); + LTTNG_ASSERT(parent_expr != NULL); + + /* Open event expr array field element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_event_expr_array_field_element); + if (ret) { + goto mi_error; + } + + /* Index. */ + ret = mi_lttng_writer_write_element_unsigned_int( + writer, mi_lttng_element_event_expr_index, index); + if (ret) { + goto mi_error; + } + + /* Parent expression. */ + ret_code = lttng_event_expr_mi_serialize(parent_expr, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close event expr array field element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +enum lttng_error_code lttng_event_expr_mi_serialize( + const struct lttng_event_expr *expression, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(expression); + LTTNG_ASSERT(writer); + + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_expr); + if (ret) { + goto mi_error; + } + + switch (expression->type) { + case LTTNG_EVENT_EXPR_TYPE_EVENT_PAYLOAD_FIELD: + ret_code = lttng_event_expr_event_payload_field_mi_serialize( + expression, writer); + break; + case LTTNG_EVENT_EXPR_TYPE_CHANNEL_CONTEXT_FIELD: + ret_code = lttng_event_expr_channel_context_field_mi_serialize( + expression, writer); + break; + case LTTNG_EVENT_EXPR_TYPE_APP_SPECIFIC_CONTEXT_FIELD: + ret_code = lttng_event_expr_app_specific_context_field_mi_serialize( + expression, writer); + break; + case LTTNG_EVENT_EXPR_TYPE_ARRAY_FIELD_ELEMENT: + ret_code = lttng_event_expr_array_field_element_mi_serialize( + expression, writer); + break; + default: + abort(); + } + + if (ret_code != LTTNG_OK) { + goto end; + } + + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; + +end: + return ret_code; +} diff --git a/src/common/event-field-value.c b/src/common/event-field-value.c deleted file mode 100644 index 850f9eda1..000000000 --- a/src/common/event-field-value.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * event-field-value.c - * - * Linux Trace Toolkit Control Library - * - * Copyright (C) 2020 Philippe Proulx - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include - -#include -#include -#include - -static -struct lttng_event_field_value *create_empty_field_val( - enum lttng_event_field_value_type type, size_t size) -{ - struct lttng_event_field_value *field_val; - - field_val = zmalloc(size); - if (!field_val) { - goto end; - } - - field_val->type = type; - -end: - return field_val; -} - -struct lttng_event_field_value *lttng_event_field_value_uint_create( - uint64_t val) -{ - struct lttng_event_field_value_uint *field_val; - - field_val = container_of(create_empty_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT, - sizeof(*field_val)), - struct lttng_event_field_value_uint, parent); - if (!field_val) { - goto error; - } - - field_val->val = val; - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return &field_val->parent; -} - -struct lttng_event_field_value *lttng_event_field_value_int_create( - int64_t val) -{ - struct lttng_event_field_value_int *field_val; - - field_val = container_of(create_empty_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT, - sizeof(*field_val)), - struct lttng_event_field_value_int, parent); - if (!field_val) { - goto error; - } - - field_val->val = val; - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return &field_val->parent; -} - -static -struct lttng_event_field_value_enum *create_enum_field_val( - enum lttng_event_field_value_type type, size_t size) -{ - struct lttng_event_field_value_enum *field_val; - - field_val = container_of(create_empty_field_val(type, size), - struct lttng_event_field_value_enum, parent); - if (!field_val) { - goto error; - } - - lttng_dynamic_pointer_array_init(&field_val->labels, free); - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return field_val; -} - -struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( - uint64_t val) -{ - struct lttng_event_field_value_enum_uint *field_val; - - field_val = container_of(create_enum_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM, - sizeof(*field_val)), - struct lttng_event_field_value_enum_uint, parent); - if (!field_val) { - goto error; - } - - field_val->val = val; - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent.parent); - -end: - return &field_val->parent.parent; -} - -struct lttng_event_field_value *lttng_event_field_value_enum_int_create( - int64_t val) -{ - struct lttng_event_field_value_enum_int *field_val; - - field_val = container_of(create_enum_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM, - sizeof(*field_val)), - struct lttng_event_field_value_enum_int, parent); - if (!field_val) { - goto error; - } - - field_val->val = val; - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent.parent); - -end: - return &field_val->parent.parent; -} - -struct lttng_event_field_value *lttng_event_field_value_real_create(double val) -{ - struct lttng_event_field_value_real *field_val = container_of( - create_empty_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_REAL, - sizeof(*field_val)), - struct lttng_event_field_value_real, parent); - - if (!field_val) { - goto error; - } - - field_val->val = val; - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return &field_val->parent; -} - -struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( - const char *val, size_t size) -{ - struct lttng_event_field_value_string *field_val = container_of( - create_empty_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_STRING, - sizeof(*field_val)), - struct lttng_event_field_value_string, parent); - - if (!field_val) { - goto error; - } - - LTTNG_ASSERT(val); - field_val->val = strndup(val, size); - if (!field_val->val) { - goto error; - } - - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return &field_val->parent; -} - -struct lttng_event_field_value *lttng_event_field_value_string_create( - const char *val) -{ - LTTNG_ASSERT(val); - return lttng_event_field_value_string_create_with_size(val, - strlen(val)); -} - -static -void destroy_field_val(void *field_val) -{ - lttng_event_field_value_destroy(field_val); -} - -struct lttng_event_field_value *lttng_event_field_value_array_create(void) -{ - struct lttng_event_field_value_array *field_val = container_of( - create_empty_field_val( - LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY, - sizeof(*field_val)), - struct lttng_event_field_value_array, parent); - - if (!field_val) { - goto error; - } - - lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val); - goto end; - -error: - lttng_event_field_value_destroy(&field_val->parent); - -end: - return &field_val->parent; -} - -void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val) -{ - if (!field_val) { - goto end; - } - - switch (field_val->type) { - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: - { - struct lttng_event_field_value_enum *enum_field_val = - container_of(field_val, - struct lttng_event_field_value_enum, parent); - - lttng_dynamic_pointer_array_reset(&enum_field_val->labels); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: - { - struct lttng_event_field_value_string *str_field_val = - container_of(field_val, - struct lttng_event_field_value_string, parent); - - free(str_field_val->val); - break; - } - case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: - { - struct lttng_event_field_value_array *array_field_expr = - container_of(field_val, - struct lttng_event_field_value_array, - parent); - - lttng_dynamic_pointer_array_reset(&array_field_expr->elems); - break; - } - default: - break; - } - - free(field_val); - -end: - return; -} - -int lttng_event_field_value_enum_append_label_with_size( - struct lttng_event_field_value *field_val, - const char *label, size_t size) -{ - int ret; - char *new_label; - - LTTNG_ASSERT(field_val); - LTTNG_ASSERT(label); - new_label = strndup(label, size); - if (!new_label) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_pointer_array_add_pointer( - &container_of(field_val, - struct lttng_event_field_value_enum, parent)->labels, - new_label); - if (ret == 0) { - new_label = NULL; - } - -end: - free(new_label); - return ret; -} - -int lttng_event_field_value_enum_append_label( - struct lttng_event_field_value *field_val, - const char *label) -{ - LTTNG_ASSERT(label); - return lttng_event_field_value_enum_append_label_with_size(field_val, - label, strlen(label)); -} - -int lttng_event_field_value_array_append( - struct lttng_event_field_value *array_field_val, - struct lttng_event_field_value *field_val) -{ - LTTNG_ASSERT(array_field_val); - LTTNG_ASSERT(field_val); - return lttng_dynamic_pointer_array_add_pointer( - &container_of(array_field_val, - struct lttng_event_field_value_array, parent)->elems, - field_val); -} - -int lttng_event_field_value_array_append_unavailable( - struct lttng_event_field_value *array_field_val) -{ - LTTNG_ASSERT(array_field_val); - return lttng_dynamic_pointer_array_add_pointer( - &container_of(array_field_val, - struct lttng_event_field_value_array, parent)->elems, - NULL); -} - -enum lttng_event_field_value_type lttng_event_field_value_get_type( - const struct lttng_event_field_value *field_val) -{ - enum lttng_event_field_value_type type; - - if (!field_val) { - type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID; - goto end; - } - - type = field_val->type; - -end: - return type; -} - -enum lttng_event_field_value_status -lttng_event_field_value_unsigned_int_get_value( - const struct lttng_event_field_value *field_val, uint64_t *val) -{ - enum lttng_event_field_value_status status; - - if (!field_val || !val) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - switch (field_val->type) { - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: - *val = container_of(field_val, - const struct lttng_event_field_value_uint, - parent)->val; - break; - case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: - { - const struct lttng_event_field_value_enum *field_val_enum = container_of( - field_val, - const struct lttng_event_field_value_enum, - parent); - const struct lttng_event_field_value_enum_uint - *field_val_enum_uint = container_of( - field_val_enum, - const struct lttng_event_field_value_enum_uint, - parent); - *val = field_val_enum_uint->val; - break; - } - default: - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -enum lttng_event_field_value_status -lttng_event_field_value_signed_int_get_value( - const struct lttng_event_field_value *field_val, int64_t *val) -{ - enum lttng_event_field_value_status status; - - if (!field_val || !val) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - switch (field_val->type) { - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: - *val = container_of(field_val, - const struct lttng_event_field_value_int, - parent)->val; - break; - case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: - { - const struct lttng_event_field_value_enum *field_val_enum = container_of( - field_val, - const struct lttng_event_field_value_enum, - parent); - const struct lttng_event_field_value_enum_int - *field_val_enum_uint = container_of( - field_val_enum, - const struct lttng_event_field_value_enum_int, - parent); - *val = field_val_enum_uint->val; - break; - } - default: - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -enum lttng_event_field_value_status -lttng_event_field_value_real_get_value( - const struct lttng_event_field_value *field_val, double *val) -{ - enum lttng_event_field_value_status status; - - if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL || - !val) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - *val = container_of(field_val, - const struct lttng_event_field_value_real, parent)->val; - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -static -bool is_enum_field_val(const struct lttng_event_field_value *field_val) -{ - return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM || - field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM; -} - -enum lttng_event_field_value_status -lttng_event_field_value_enum_get_label_count( - const struct lttng_event_field_value *field_val, - unsigned int *count) -{ - enum lttng_event_field_value_status status; - - if (!field_val || !is_enum_field_val(field_val) || !count) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - *count = (unsigned int) lttng_dynamic_pointer_array_get_count( - &container_of(field_val, - const struct lttng_event_field_value_enum, - parent)->labels); - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -const char *lttng_event_field_value_enum_get_label_at_index( - const struct lttng_event_field_value *field_val, - unsigned int index) -{ - const char *ret; - const struct lttng_event_field_value_enum *enum_field_val; - - if (!field_val || !is_enum_field_val(field_val)) { - ret = NULL; - goto end; - } - - enum_field_val = container_of(field_val, - const struct lttng_event_field_value_enum, parent); - - if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) { - ret = NULL; - goto end; - } - - ret = lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels, - index); - -end: - return ret; -} - -enum lttng_event_field_value_status lttng_event_field_value_string_get_value( - const struct lttng_event_field_value *field_val, - const char **value) -{ - enum lttng_event_field_value_status status; - - if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - *value = container_of(field_val, - const struct lttng_event_field_value_string, parent)->val; - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -enum lttng_event_field_value_status lttng_event_field_value_array_get_length( - const struct lttng_event_field_value *field_val, - unsigned int *length) -{ - enum lttng_event_field_value_status status; - - if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || - !length) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - *length = (unsigned int) lttng_dynamic_pointer_array_get_count( - &container_of(field_val, - const struct lttng_event_field_value_array, - parent)->elems); - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - -end: - return status; -} - -enum lttng_event_field_value_status -lttng_event_field_value_array_get_element_at_index( - const struct lttng_event_field_value *field_val, - unsigned int index, - const struct lttng_event_field_value **elem_field_val) -{ - enum lttng_event_field_value_status status; - const struct lttng_event_field_value_array *array_field_val; - - if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || - !elem_field_val) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - array_field_val = container_of(field_val, - const struct lttng_event_field_value_array, parent); - - if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; - goto end; - } - - *elem_field_val = lttng_dynamic_pointer_array_get_pointer( - &array_field_val->elems, index); - if (*elem_field_val) { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; - } else { - status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE; - } - -end: - return status; -} diff --git a/src/common/event-field-value.cpp b/src/common/event-field-value.cpp new file mode 100644 index 000000000..757271313 --- /dev/null +++ b/src/common/event-field-value.cpp @@ -0,0 +1,592 @@ +/* + * event-field-value.c + * + * Linux Trace Toolkit Control Library + * + * Copyright (C) 2020 Philippe Proulx + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include + +#include +#include +#include + +static +struct lttng_event_field_value *create_empty_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value *field_val; + + field_val = (lttng_event_field_value *) zmalloc(size); + if (!field_val) { + goto end; + } + + field_val->type = type; + +end: + return field_val; +} + +struct lttng_event_field_value *lttng_event_field_value_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_uint *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +struct lttng_event_field_value *lttng_event_field_value_int_create( + int64_t val) +{ + struct lttng_event_field_value_int *field_val; + + field_val = container_of(create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT, + sizeof(*field_val)), + struct lttng_event_field_value_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +static +struct lttng_event_field_value_enum *create_enum_field_val( + enum lttng_event_field_value_type type, size_t size) +{ + struct lttng_event_field_value_enum *field_val; + + field_val = container_of(create_empty_field_val(type, size), + struct lttng_event_field_value_enum, parent); + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->labels, free); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return field_val; +} + +struct lttng_event_field_value *lttng_event_field_value_enum_uint_create( + uint64_t val) +{ + struct lttng_event_field_value_enum_uint *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_uint, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +struct lttng_event_field_value *lttng_event_field_value_enum_int_create( + int64_t val) +{ + struct lttng_event_field_value_enum_int *field_val; + + field_val = container_of(create_enum_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM, + sizeof(*field_val)), + struct lttng_event_field_value_enum_int, parent); + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent.parent); + +end: + return &field_val->parent.parent; +} + +struct lttng_event_field_value *lttng_event_field_value_real_create(double val) +{ + struct lttng_event_field_value_real *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_REAL, + sizeof(*field_val)), + struct lttng_event_field_value_real, parent); + + if (!field_val) { + goto error; + } + + field_val->val = val; + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +struct lttng_event_field_value *lttng_event_field_value_string_create_with_size( + const char *val, size_t size) +{ + struct lttng_event_field_value_string *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_STRING, + sizeof(*field_val)), + struct lttng_event_field_value_string, parent); + + if (!field_val) { + goto error; + } + + LTTNG_ASSERT(val); + field_val->val = strndup(val, size); + if (!field_val->val) { + goto error; + } + + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +struct lttng_event_field_value *lttng_event_field_value_string_create( + const char *val) +{ + LTTNG_ASSERT(val); + return lttng_event_field_value_string_create_with_size(val, + strlen(val)); +} + +static +void destroy_field_val(void *field_val) +{ + lttng_event_field_value_destroy((lttng_event_field_value *) field_val); +} + +struct lttng_event_field_value *lttng_event_field_value_array_create(void) +{ + struct lttng_event_field_value_array *field_val = container_of( + create_empty_field_val( + LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY, + sizeof(*field_val)), + struct lttng_event_field_value_array, parent); + + if (!field_val) { + goto error; + } + + lttng_dynamic_pointer_array_init(&field_val->elems, destroy_field_val); + goto end; + +error: + lttng_event_field_value_destroy(&field_val->parent); + +end: + return &field_val->parent; +} + +void lttng_event_field_value_destroy(struct lttng_event_field_value *field_val) +{ + if (!field_val) { + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + struct lttng_event_field_value_enum *enum_field_val = + container_of(field_val, + struct lttng_event_field_value_enum, parent); + + lttng_dynamic_pointer_array_reset(&enum_field_val->labels); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_STRING: + { + struct lttng_event_field_value_string *str_field_val = + container_of(field_val, + struct lttng_event_field_value_string, parent); + + free(str_field_val->val); + break; + } + case LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY: + { + struct lttng_event_field_value_array *array_field_expr = + container_of(field_val, + struct lttng_event_field_value_array, + parent); + + lttng_dynamic_pointer_array_reset(&array_field_expr->elems); + break; + } + default: + break; + } + + free(field_val); + +end: + return; +} + +int lttng_event_field_value_enum_append_label_with_size( + struct lttng_event_field_value *field_val, + const char *label, size_t size) +{ + int ret; + char *new_label; + + LTTNG_ASSERT(field_val); + LTTNG_ASSERT(label); + new_label = strndup(label, size); + if (!new_label) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &container_of(field_val, + struct lttng_event_field_value_enum, parent)->labels, + new_label); + if (ret == 0) { + new_label = NULL; + } + +end: + free(new_label); + return ret; +} + +int lttng_event_field_value_enum_append_label( + struct lttng_event_field_value *field_val, + const char *label) +{ + LTTNG_ASSERT(label); + return lttng_event_field_value_enum_append_label_with_size(field_val, + label, strlen(label)); +} + +int lttng_event_field_value_array_append( + struct lttng_event_field_value *array_field_val, + struct lttng_event_field_value *field_val) +{ + LTTNG_ASSERT(array_field_val); + LTTNG_ASSERT(field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + field_val); +} + +int lttng_event_field_value_array_append_unavailable( + struct lttng_event_field_value *array_field_val) +{ + LTTNG_ASSERT(array_field_val); + return lttng_dynamic_pointer_array_add_pointer( + &container_of(array_field_val, + struct lttng_event_field_value_array, parent)->elems, + NULL); +} + +enum lttng_event_field_value_type lttng_event_field_value_get_type( + const struct lttng_event_field_value *field_val) +{ + enum lttng_event_field_value_type type; + + if (!field_val) { + type = LTTNG_EVENT_FIELD_VALUE_TYPE_INVALID; + goto end; + } + + type = field_val->type; + +end: + return type; +} + +enum lttng_event_field_value_status +lttng_event_field_value_unsigned_int_get_value( + const struct lttng_event_field_value *field_val, uint64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_uint, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM: + { + const struct lttng_event_field_value_enum *field_val_enum = container_of( + field_val, + const struct lttng_event_field_value_enum, + parent); + const struct lttng_event_field_value_enum_uint + *field_val_enum_uint = container_of( + field_val_enum, + const struct lttng_event_field_value_enum_uint, + parent); + *val = field_val_enum_uint->val; + break; + } + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_signed_int_get_value( + const struct lttng_event_field_value *field_val, int64_t *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + switch (field_val->type) { + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_INT: + *val = container_of(field_val, + const struct lttng_event_field_value_int, + parent)->val; + break; + case LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM: + { + const struct lttng_event_field_value_enum *field_val_enum = container_of( + field_val, + const struct lttng_event_field_value_enum, + parent); + const struct lttng_event_field_value_enum_int + *field_val_enum_uint = container_of( + field_val_enum, + const struct lttng_event_field_value_enum_int, + parent); + *val = field_val_enum_uint->val; + break; + } + default: + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_real_get_value( + const struct lttng_event_field_value *field_val, double *val) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_REAL || + !val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *val = container_of(field_val, + const struct lttng_event_field_value_real, parent)->val; + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +static +bool is_enum_field_val(const struct lttng_event_field_value *field_val) +{ + return field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_UNSIGNED_ENUM || + field_val->type == LTTNG_EVENT_FIELD_VALUE_TYPE_SIGNED_ENUM; +} + +enum lttng_event_field_value_status +lttng_event_field_value_enum_get_label_count( + const struct lttng_event_field_value *field_val, + unsigned int *count) +{ + enum lttng_event_field_value_status status; + + if (!field_val || !is_enum_field_val(field_val) || !count) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *count = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_enum, + parent)->labels); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +const char *lttng_event_field_value_enum_get_label_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index) +{ + const char *ret; + const struct lttng_event_field_value_enum *enum_field_val; + + if (!field_val || !is_enum_field_val(field_val)) { + ret = NULL; + goto end; + } + + enum_field_val = container_of(field_val, + const struct lttng_event_field_value_enum, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&enum_field_val->labels)) { + ret = NULL; + goto end; + } + + ret = (const char *) lttng_dynamic_pointer_array_get_pointer(&enum_field_val->labels, + index); + +end: + return ret; +} + +enum lttng_event_field_value_status lttng_event_field_value_string_get_value( + const struct lttng_event_field_value *field_val, + const char **value) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_STRING) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *value = container_of(field_val, + const struct lttng_event_field_value_string, parent)->val; + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status lttng_event_field_value_array_get_length( + const struct lttng_event_field_value *field_val, + unsigned int *length) +{ + enum lttng_event_field_value_status status; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !length) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *length = (unsigned int) lttng_dynamic_pointer_array_get_count( + &container_of(field_val, + const struct lttng_event_field_value_array, + parent)->elems); + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + +end: + return status; +} + +enum lttng_event_field_value_status +lttng_event_field_value_array_get_element_at_index( + const struct lttng_event_field_value *field_val, + unsigned int index, + const struct lttng_event_field_value **elem_field_val) +{ + enum lttng_event_field_value_status status; + const struct lttng_event_field_value_array *array_field_val; + + if (!field_val || field_val->type != LTTNG_EVENT_FIELD_VALUE_TYPE_ARRAY || + !elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + array_field_val = container_of(field_val, + const struct lttng_event_field_value_array, parent); + + if (index >= lttng_dynamic_pointer_array_get_count(&array_field_val->elems)) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_INVALID; + goto end; + } + + *elem_field_val = (lttng_event_field_value *) lttng_dynamic_pointer_array_get_pointer( + &array_field_val->elems, index); + if (*elem_field_val) { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_OK; + } else { + status = LTTNG_EVENT_FIELD_VALUE_STATUS_UNAVAILABLE; + } + +end: + return status; +} diff --git a/src/common/event-rule/event-rule.c b/src/common/event-rule/event-rule.c deleted file mode 100644 index 7e3f7248e..000000000 --- a/src/common/event-rule/event-rule.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum lttng_event_rule_type lttng_event_rule_get_type( - const struct lttng_event_rule *event_rule) -{ - return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN; -} - -enum lttng_domain_type lttng_event_rule_get_domain_type( - const struct lttng_event_rule *event_rule) -{ - enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; - - switch (lttng_event_rule_get_type(event_rule)) { - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - domain_type = LTTNG_DOMAIN_UST; - break; - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - domain_type = LTTNG_DOMAIN_JUL; - break; - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - domain_type = LTTNG_DOMAIN_LOG4J; - break; - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - domain_type = LTTNG_DOMAIN_PYTHON; - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - domain_type = LTTNG_DOMAIN_KERNEL; - break; - case LTTNG_EVENT_RULE_TYPE_UNKNOWN: - domain_type = LTTNG_DOMAIN_NONE; - break; - } - - return domain_type; -} - -static void lttng_event_rule_release(struct urcu_ref *ref) -{ - struct lttng_event_rule *event_rule = - container_of(ref, typeof(*event_rule), ref); - - LTTNG_ASSERT(event_rule->destroy); - event_rule->destroy(event_rule); -} - -void lttng_event_rule_destroy(struct lttng_event_rule *event_rule) -{ - lttng_event_rule_put(event_rule); -} - -bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule) -{ - bool valid; - - if (!event_rule) { - valid = false; - goto end; - } - - if (!event_rule->validate) { - /* Sub-class guarantees that it can never be invalid. */ - valid = true; - goto end; - } - - valid = event_rule->validate(event_rule); -end: - return valid; -} - -int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, - struct lttng_payload *payload) -{ - int ret; - struct lttng_event_rule_comm event_rule_comm = {}; - - if (!event_rule) { - ret = -1; - goto end; - } - - event_rule_comm.event_rule_type = (int8_t) event_rule->type; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &event_rule_comm, sizeof(event_rule_comm)); - if (ret) { - goto end; - } - - ret = event_rule->serialize(event_rule, payload); - if (ret) { - goto end; - } -end: - return ret; -} - -bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, - const struct lttng_event_rule *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a->type != b->type) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - is_equal = a->equal ? a->equal(a, b) : true; -end: - return is_equal; -} - -ssize_t lttng_event_rule_create_from_payload( - struct lttng_payload_view *view, - struct lttng_event_rule **event_rule) -{ - ssize_t ret, consumed = 0; - event_rule_create_from_payload_cb create_from_payload = NULL; - const struct lttng_event_rule_comm *event_rule_comm; - const struct lttng_payload_view event_rule_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*event_rule_comm)); - - if (!view || !event_rule) { - ret = -1; - goto end; - } - - if (!lttng_payload_view_is_valid(&event_rule_comm_view)) { - ret = -1; - goto end; - } - - DBG("Deserializing event_rule from payload"); - event_rule_comm = (const struct lttng_event_rule_comm *) event_rule_comm_view.buffer.data; - consumed += sizeof(*event_rule_comm); - - switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) { - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - create_from_payload = lttng_event_rule_kernel_kprobe_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - create_from_payload = lttng_event_rule_kernel_uprobe_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - create_from_payload = - lttng_event_rule_kernel_syscall_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - create_from_payload = - lttng_event_rule_kernel_tracepoint_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - create_from_payload = - lttng_event_rule_user_tracepoint_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - create_from_payload = - lttng_event_rule_jul_logging_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - create_from_payload = - lttng_event_rule_log4j_logging_create_from_payload; - break; - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - create_from_payload = - lttng_event_rule_python_logging_create_from_payload; - break; - default: - ERR("Attempted to create event rule of unknown type (%i)", - (int) event_rule_comm->event_rule_type); - ret = -1; - goto end; - } - - LTTNG_ASSERT(create_from_payload); - - { - struct lttng_payload_view child_view = - lttng_payload_view_from_view( - view, consumed, -1); - - ret = create_from_payload(&child_view, event_rule); - if (ret < 0) { - goto end; - } - - consumed += ret; - } - - if (!lttng_event_rule_validate(*event_rule)) { - ret = -1; - goto end; - } - - ret = consumed; -end: - return ret; -} - -void lttng_event_rule_init(struct lttng_event_rule *event_rule, - enum lttng_event_rule_type type) -{ - urcu_ref_init(&event_rule->ref); - event_rule->type = type; -} - -bool lttng_event_rule_get(struct lttng_event_rule *event_rule) -{ - return urcu_ref_get_unless_zero(&event_rule->ref); -} - -void lttng_event_rule_put(struct lttng_event_rule *event_rule) -{ - if (!event_rule) { - return; - } - - LTTNG_ASSERT(event_rule->ref.refcount); - urcu_ref_put(&event_rule->ref, lttng_event_rule_release); -} - -enum lttng_error_code lttng_event_rule_generate_filter_bytecode( - struct lttng_event_rule *rule, - const struct lttng_credentials *creds) -{ - LTTNG_ASSERT(rule->generate_filter_bytecode); - return rule->generate_filter_bytecode(rule, creds); -} - -const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule) -{ - LTTNG_ASSERT(rule->get_filter); - return rule->get_filter(rule); -} - -const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode( - const struct lttng_event_rule *rule) -{ - LTTNG_ASSERT(rule->get_filter_bytecode); - return rule->get_filter_bytecode(rule); -} - -enum lttng_event_rule_generate_exclusions_status -lttng_event_rule_generate_exclusions(const struct lttng_event_rule *rule, - struct lttng_event_exclusion **exclusions) -{ - LTTNG_ASSERT(rule->generate_exclusions); - return rule->generate_exclusions(rule, exclusions); -} - -struct lttng_event *lttng_event_rule_generate_lttng_event( - const struct lttng_event_rule *rule) -{ - LTTNG_ASSERT(rule->generate_lttng_event); - return rule->generate_lttng_event(rule); -} - -bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule) -{ - bool targets_agent_domain = false; - enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule); - - switch (type) { - case LTTNG_DOMAIN_JUL: - case LTTNG_DOMAIN_LOG4J: - case LTTNG_DOMAIN_PYTHON: - targets_agent_domain = true; - break; - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_KERNEL: - targets_agent_domain = false; - break; - default: - abort(); - }; - - return targets_agent_domain; -} - -const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) -{ - switch (type) { - case LTTNG_EVENT_RULE_TYPE_UNKNOWN: - return "unknown"; - case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: - return "kernel syscall"; - case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: - return "kernel kprobe"; - case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: - return "kernel uprobe"; - case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: - return "kernel tracepoint"; - case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: - return "user tracepoint"; - case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: - return "jul logging"; - case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: - return "log4j logging"; - case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: - return "python logging"; - - default: - abort(); - } -} - -unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule) -{ - LTTNG_ASSERT(rule->hash); - return rule->hash(rule); -} - -enum lttng_error_code lttng_event_rule_mi_serialize( - const struct lttng_event_rule *rule, struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(rule); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(rule->mi_serialize); - - /* Open event rule element. */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_rule); - if (ret) { - goto mi_error; - } - - /* Serialize underlying event rule. */ - ret_code = rule->mi_serialize(rule, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close event rule element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/event-rule/event-rule.cpp b/src/common/event-rule/event-rule.cpp new file mode 100644 index 000000000..7e3f7248e --- /dev/null +++ b/src/common/event-rule/event-rule.cpp @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum lttng_event_rule_type lttng_event_rule_get_type( + const struct lttng_event_rule *event_rule) +{ + return event_rule ? event_rule->type : LTTNG_EVENT_RULE_TYPE_UNKNOWN; +} + +enum lttng_domain_type lttng_event_rule_get_domain_type( + const struct lttng_event_rule *event_rule) +{ + enum lttng_domain_type domain_type = LTTNG_DOMAIN_NONE; + + switch (lttng_event_rule_get_type(event_rule)) { + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + domain_type = LTTNG_DOMAIN_UST; + break; + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + domain_type = LTTNG_DOMAIN_JUL; + break; + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + domain_type = LTTNG_DOMAIN_LOG4J; + break; + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + domain_type = LTTNG_DOMAIN_PYTHON; + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + domain_type = LTTNG_DOMAIN_KERNEL; + break; + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + domain_type = LTTNG_DOMAIN_NONE; + break; + } + + return domain_type; +} + +static void lttng_event_rule_release(struct urcu_ref *ref) +{ + struct lttng_event_rule *event_rule = + container_of(ref, typeof(*event_rule), ref); + + LTTNG_ASSERT(event_rule->destroy); + event_rule->destroy(event_rule); +} + +void lttng_event_rule_destroy(struct lttng_event_rule *event_rule) +{ + lttng_event_rule_put(event_rule); +} + +bool lttng_event_rule_validate(const struct lttng_event_rule *event_rule) +{ + bool valid; + + if (!event_rule) { + valid = false; + goto end; + } + + if (!event_rule->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = event_rule->validate(event_rule); +end: + return valid; +} + +int lttng_event_rule_serialize(const struct lttng_event_rule *event_rule, + struct lttng_payload *payload) +{ + int ret; + struct lttng_event_rule_comm event_rule_comm = {}; + + if (!event_rule) { + ret = -1; + goto end; + } + + event_rule_comm.event_rule_type = (int8_t) event_rule->type; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &event_rule_comm, sizeof(event_rule_comm)); + if (ret) { + goto end; + } + + ret = event_rule->serialize(event_rule, payload); + if (ret) { + goto end; + } +end: + return ret; +} + +bool lttng_event_rule_is_equal(const struct lttng_event_rule *a, + const struct lttng_event_rule *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +ssize_t lttng_event_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_rule **event_rule) +{ + ssize_t ret, consumed = 0; + event_rule_create_from_payload_cb create_from_payload = NULL; + const struct lttng_event_rule_comm *event_rule_comm; + const struct lttng_payload_view event_rule_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*event_rule_comm)); + + if (!view || !event_rule) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&event_rule_comm_view)) { + ret = -1; + goto end; + } + + DBG("Deserializing event_rule from payload"); + event_rule_comm = (const struct lttng_event_rule_comm *) event_rule_comm_view.buffer.data; + consumed += sizeof(*event_rule_comm); + + switch ((enum lttng_event_rule_type) event_rule_comm->event_rule_type) { + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + create_from_payload = lttng_event_rule_kernel_kprobe_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + create_from_payload = lttng_event_rule_kernel_uprobe_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + create_from_payload = + lttng_event_rule_kernel_syscall_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + create_from_payload = + lttng_event_rule_kernel_tracepoint_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + create_from_payload = + lttng_event_rule_user_tracepoint_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + create_from_payload = + lttng_event_rule_jul_logging_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + create_from_payload = + lttng_event_rule_log4j_logging_create_from_payload; + break; + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + create_from_payload = + lttng_event_rule_python_logging_create_from_payload; + break; + default: + ERR("Attempted to create event rule of unknown type (%i)", + (int) event_rule_comm->event_rule_type); + ret = -1; + goto end; + } + + LTTNG_ASSERT(create_from_payload); + + { + struct lttng_payload_view child_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = create_from_payload(&child_view, event_rule); + if (ret < 0) { + goto end; + } + + consumed += ret; + } + + if (!lttng_event_rule_validate(*event_rule)) { + ret = -1; + goto end; + } + + ret = consumed; +end: + return ret; +} + +void lttng_event_rule_init(struct lttng_event_rule *event_rule, + enum lttng_event_rule_type type) +{ + urcu_ref_init(&event_rule->ref); + event_rule->type = type; +} + +bool lttng_event_rule_get(struct lttng_event_rule *event_rule) +{ + return urcu_ref_get_unless_zero(&event_rule->ref); +} + +void lttng_event_rule_put(struct lttng_event_rule *event_rule) +{ + if (!event_rule) { + return; + } + + LTTNG_ASSERT(event_rule->ref.refcount); + urcu_ref_put(&event_rule->ref, lttng_event_rule_release); +} + +enum lttng_error_code lttng_event_rule_generate_filter_bytecode( + struct lttng_event_rule *rule, + const struct lttng_credentials *creds) +{ + LTTNG_ASSERT(rule->generate_filter_bytecode); + return rule->generate_filter_bytecode(rule, creds); +} + +const char *lttng_event_rule_get_filter(const struct lttng_event_rule *rule) +{ + LTTNG_ASSERT(rule->get_filter); + return rule->get_filter(rule); +} + +const struct lttng_bytecode *lttng_event_rule_get_filter_bytecode( + const struct lttng_event_rule *rule) +{ + LTTNG_ASSERT(rule->get_filter_bytecode); + return rule->get_filter_bytecode(rule); +} + +enum lttng_event_rule_generate_exclusions_status +lttng_event_rule_generate_exclusions(const struct lttng_event_rule *rule, + struct lttng_event_exclusion **exclusions) +{ + LTTNG_ASSERT(rule->generate_exclusions); + return rule->generate_exclusions(rule, exclusions); +} + +struct lttng_event *lttng_event_rule_generate_lttng_event( + const struct lttng_event_rule *rule) +{ + LTTNG_ASSERT(rule->generate_lttng_event); + return rule->generate_lttng_event(rule); +} + +bool lttng_event_rule_targets_agent_domain(const struct lttng_event_rule *rule) +{ + bool targets_agent_domain = false; + enum lttng_domain_type type = lttng_event_rule_get_domain_type(rule); + + switch (type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + targets_agent_domain = true; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_KERNEL: + targets_agent_domain = false; + break; + default: + abort(); + }; + + return targets_agent_domain; +} + +const char *lttng_event_rule_type_str(enum lttng_event_rule_type type) +{ + switch (type) { + case LTTNG_EVENT_RULE_TYPE_UNKNOWN: + return "unknown"; + case LTTNG_EVENT_RULE_TYPE_KERNEL_SYSCALL: + return "kernel syscall"; + case LTTNG_EVENT_RULE_TYPE_KERNEL_KPROBE: + return "kernel kprobe"; + case LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE: + return "kernel uprobe"; + case LTTNG_EVENT_RULE_TYPE_KERNEL_TRACEPOINT: + return "kernel tracepoint"; + case LTTNG_EVENT_RULE_TYPE_USER_TRACEPOINT: + return "user tracepoint"; + case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING: + return "jul logging"; + case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING: + return "log4j logging"; + case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING: + return "python logging"; + + default: + abort(); + } +} + +unsigned long lttng_event_rule_hash(const struct lttng_event_rule *rule) +{ + LTTNG_ASSERT(rule->hash); + return rule->hash(rule); +} + +enum lttng_error_code lttng_event_rule_mi_serialize( + const struct lttng_event_rule *rule, struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(rule); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(rule->mi_serialize); + + /* Open event rule element. */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_rule); + if (ret) { + goto mi_error; + } + + /* Serialize underlying event rule. */ + ret_code = rule->mi_serialize(rule, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close event rule element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/event-rule/jul-logging.c b/src/common/event-rule/jul-logging.c deleted file mode 100644 index c6c05542d..000000000 --- a/src/common/event-rule/jul-logging.c +++ /dev/null @@ -1,922 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/jul-logging.cpp b/src/common/event-rule/jul-logging.cpp new file mode 100644 index 000000000..77ae3800c --- /dev/null +++ b/src/common/event-rule/jul-logging.cpp @@ -0,0 +1,922 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/kernel-kprobe.c b/src/common/event-rule/kernel-kprobe.c deleted file mode 100644 index 872d03a7e..000000000 --- a/src/common/event-rule/kernel-kprobe.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/kernel-kprobe.cpp b/src/common/event-rule/kernel-kprobe.cpp new file mode 100644 index 000000000..d33d44605 --- /dev/null +++ b/src/common/event-rule/kernel-kprobe.cpp @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/kernel-syscall.c b/src/common/event-rule/kernel-syscall.c deleted file mode 100644 index 21aa1e221..000000000 --- a/src/common/event-rule/kernel-syscall.c +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "???"; - } -} diff --git a/src/common/event-rule/kernel-syscall.cpp b/src/common/event-rule/kernel-syscall.cpp new file mode 100644 index 000000000..3f8e64ed8 --- /dev/null +++ b/src/common/event-rule/kernel-syscall.cpp @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "???"; + } +} diff --git a/src/common/event-rule/kernel-tracepoint.c b/src/common/event-rule/kernel-tracepoint.c deleted file mode 100644 index a1f5f625a..000000000 --- a/src/common/event-rule/kernel-tracepoint.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/kernel-tracepoint.cpp b/src/common/event-rule/kernel-tracepoint.cpp new file mode 100644 index 000000000..cff3048f3 --- /dev/null +++ b/src/common/event-rule/kernel-tracepoint.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/kernel-uprobe.c b/src/common/event-rule/kernel-uprobe.c deleted file mode 100644 index 805f2bc79..000000000 --- a/src/common/event-rule/kernel-uprobe.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/kernel-uprobe.cpp b/src/common/event-rule/kernel-uprobe.cpp new file mode 100644 index 000000000..d53958df6 --- /dev/null +++ b/src/common/event-rule/kernel-uprobe.cpp @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/log4j-logging.c b/src/common/event-rule/log4j-logging.c deleted file mode 100644 index 6fd05521b..000000000 --- a/src/common/event-rule/log4j-logging.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/log4j-logging.cpp b/src/common/event-rule/log4j-logging.cpp new file mode 100644 index 000000000..25f2c4c0e --- /dev/null +++ b/src/common/event-rule/log4j-logging.cpp @@ -0,0 +1,921 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/python-logging.c b/src/common/event-rule/python-logging.c deleted file mode 100644 index 4ec00c7ef..000000000 --- a/src/common/event-rule/python-logging.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/python-logging.cpp b/src/common/event-rule/python-logging.cpp new file mode 100644 index 000000000..1ccd495cd --- /dev/null +++ b/src/common/event-rule/python-logging.cpp @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event-rule/user-tracepoint.c b/src/common/event-rule/user-tracepoint.c deleted file mode 100644 index cb384f851..000000000 --- a/src/common/event-rule/user-tracepoint.c +++ /dev/null @@ -1,1081 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/src/common/event-rule/user-tracepoint.cpp b/src/common/event-rule/user-tracepoint.cpp new file mode 100644 index 000000000..5158eeaa0 --- /dev/null +++ b/src/common/event-rule/user-tracepoint.cpp @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/common/event.c b/src/common/event.c deleted file mode 100644 index f1792f931..000000000 --- a/src/common/event.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include - -struct lttng_event *lttng_event_copy(const struct lttng_event *event) -{ - struct lttng_event *new_event; - struct lttng_event_extended *new_event_extended; - - new_event = zmalloc(sizeof(*event)); - if (!new_event) { - PERROR("Error allocating event structure"); - goto end; - } - - /* Copy the content of the old event. */ - memcpy(new_event, event, sizeof(*event)); - - /* - * We need to create a new extended since the previous pointer is now - * invalid. - */ - new_event_extended = zmalloc(sizeof(*new_event_extended)); - if (!new_event_extended) { - PERROR("Error allocating event extended structure"); - goto error; - } - - new_event->extended.ptr = new_event_extended; -end: - return new_event; -error: - free(new_event); - new_event = NULL; - goto end; -} diff --git a/src/common/event.cpp b/src/common/event.cpp new file mode 100644 index 000000000..6b22fecf5 --- /dev/null +++ b/src/common/event.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +struct lttng_event *lttng_event_copy(const struct lttng_event *event) +{ + struct lttng_event *new_event; + struct lttng_event_extended *new_event_extended; + + new_event = (lttng_event *) zmalloc(sizeof(*event)); + if (!new_event) { + PERROR("Error allocating event structure"); + goto end; + } + + /* Copy the content of the old event. */ + memcpy(new_event, event, sizeof(*event)); + + /* + * We need to create a new extended since the previous pointer is now + * invalid. + */ + new_event_extended = (lttng_event_extended *) zmalloc(sizeof(*new_event_extended)); + if (!new_event_extended) { + PERROR("Error allocating event extended structure"); + goto error; + } + + new_event->extended.ptr = new_event_extended; +end: + return new_event; +error: + free(new_event); + new_event = NULL; + goto end; +} diff --git a/src/common/fd-handle.c b/src/common/fd-handle.c deleted file mode 100644 index b2b7efd23..000000000 --- a/src/common/fd-handle.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include - -#include "fd-handle.h" -#include - -struct fd_handle { - struct urcu_ref ref; - int fd; -}; - -static void fd_handle_release(struct urcu_ref *ref) -{ - int ret; - struct fd_handle *handle = container_of(ref, struct fd_handle, ref); - - LTTNG_ASSERT(handle->fd >= 0); - ret = close(handle->fd); - if (ret == -1) { - PERROR("Failed to close file descriptor of fd_handle upon release: fd = %d", - handle->fd); - } - - free(handle); -} - -struct fd_handle *fd_handle_create(int fd) -{ - struct fd_handle *handle = NULL; - - if (fd < 0) { - ERR("Attempted to create an fd_handle from an invalid file descriptor: fd = %d", - fd); - goto end; - } - - handle = zmalloc(sizeof(*handle)); - if (!handle) { - PERROR("Failed to allocate fd_handle"); - goto end; - } - - urcu_ref_init(&handle->ref); - handle->fd = fd; - -end: - return handle; -} - -void fd_handle_get(struct fd_handle *handle) -{ - if (!handle) { - return; - } - - urcu_ref_get(&handle->ref); -} - -void fd_handle_put(struct fd_handle *handle) -{ - if (!handle) { - return; - } - - urcu_ref_put(&handle->ref, fd_handle_release); -} - -int fd_handle_get_fd(struct fd_handle *handle) -{ - LTTNG_ASSERT(handle); - return handle->fd; -} - -struct fd_handle *fd_handle_copy(const struct fd_handle *handle) -{ - struct fd_handle *new_handle = NULL; - const int new_fd = dup(handle->fd); - - if (new_fd < 0) { - PERROR("Failed to duplicate file descriptor while copying fd_handle: fd = %d", handle->fd); - goto end; - } - - new_handle = fd_handle_create(new_fd); -end: - return new_handle; -} diff --git a/src/common/fd-handle.cpp b/src/common/fd-handle.cpp new file mode 100644 index 000000000..d8b5b785d --- /dev/null +++ b/src/common/fd-handle.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include + +#include "fd-handle.h" +#include + +struct fd_handle { + struct urcu_ref ref; + int fd; +}; + +static void fd_handle_release(struct urcu_ref *ref) +{ + int ret; + struct fd_handle *handle = container_of(ref, struct fd_handle, ref); + + LTTNG_ASSERT(handle->fd >= 0); + ret = close(handle->fd); + if (ret == -1) { + PERROR("Failed to close file descriptor of fd_handle upon release: fd = %d", + handle->fd); + } + + free(handle); +} + +struct fd_handle *fd_handle_create(int fd) +{ + struct fd_handle *handle = NULL; + + if (fd < 0) { + ERR("Attempted to create an fd_handle from an invalid file descriptor: fd = %d", + fd); + goto end; + } + + handle = (fd_handle *) zmalloc(sizeof(*handle)); + if (!handle) { + PERROR("Failed to allocate fd_handle"); + goto end; + } + + urcu_ref_init(&handle->ref); + handle->fd = fd; + +end: + return handle; +} + +void fd_handle_get(struct fd_handle *handle) +{ + if (!handle) { + return; + } + + urcu_ref_get(&handle->ref); +} + +void fd_handle_put(struct fd_handle *handle) +{ + if (!handle) { + return; + } + + urcu_ref_put(&handle->ref, fd_handle_release); +} + +int fd_handle_get_fd(struct fd_handle *handle) +{ + LTTNG_ASSERT(handle); + return handle->fd; +} + +struct fd_handle *fd_handle_copy(const struct fd_handle *handle) +{ + struct fd_handle *new_handle = NULL; + const int new_fd = dup(handle->fd); + + if (new_fd < 0) { + PERROR("Failed to duplicate file descriptor while copying fd_handle: fd = %d", handle->fd); + goto end; + } + + new_handle = fd_handle_create(new_fd); +end: + return new_handle; +} diff --git a/src/common/filter.c b/src/common/filter.c deleted file mode 100644 index 9195808ee..000000000 --- a/src/common/filter.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2016 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "filter.h" -#include - -struct bytecode_symbol_iterator { - /* No ownership of bytecode is taken. */ - char *bytecode; - size_t offset, len; -}; - -struct bytecode_symbol_iterator *bytecode_symbol_iterator_create( - struct lttng_bytecode *bytecode) -{ - struct bytecode_symbol_iterator *it = NULL; - - if (!bytecode) { - goto end; - } - - it = zmalloc(sizeof(*it)); - if (!it) { - goto end; - } - - it->bytecode = bytecode->data; - it->offset = bytecode->reloc_table_offset; - it->len = bytecode->len; -end: - return it; -} - -int bytecode_symbol_iterator_next(struct bytecode_symbol_iterator *it) -{ - int ret; - size_t len; - - if (!it || it->offset >= it->len) { - ret = -1; - goto end; - } - - len = strlen(it->bytecode + it->offset + sizeof(uint16_t)) + 1; - it->offset += len + sizeof(uint16_t); - ret = it->offset >= it->len ? -1 : 0; -end: - return ret; -} - -int bytecode_symbol_iterator_get_type(struct bytecode_symbol_iterator *it) -{ - int ret; - - if (!it) { - ret = -1; - goto end; - } - - ret = *((uint16_t *) (it->bytecode + it->offset)); -end: - return ret; - } - -const char *bytecode_symbol_iterator_get_name( - struct bytecode_symbol_iterator *it) -{ - const char *ret = NULL; - - if (!it) { - goto end; - } - - ret = it->bytecode + it->offset + sizeof(uint16_t); -end: - return ret; -} - -void bytecode_symbol_iterator_destroy(struct bytecode_symbol_iterator *it) -{ - free(it); -} diff --git a/src/common/filter.cpp b/src/common/filter.cpp new file mode 100644 index 000000000..f74b6374b --- /dev/null +++ b/src/common/filter.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "filter.h" +#include + +struct bytecode_symbol_iterator { + /* No ownership of bytecode is taken. */ + char *bytecode; + size_t offset, len; +}; + +struct bytecode_symbol_iterator *bytecode_symbol_iterator_create( + struct lttng_bytecode *bytecode) +{ + struct bytecode_symbol_iterator *it = NULL; + + if (!bytecode) { + goto end; + } + + it = (bytecode_symbol_iterator *) zmalloc(sizeof(*it)); + if (!it) { + goto end; + } + + it->bytecode = bytecode->data; + it->offset = bytecode->reloc_table_offset; + it->len = bytecode->len; +end: + return it; +} + +int bytecode_symbol_iterator_next(struct bytecode_symbol_iterator *it) +{ + int ret; + size_t len; + + if (!it || it->offset >= it->len) { + ret = -1; + goto end; + } + + len = strlen(it->bytecode + it->offset + sizeof(uint16_t)) + 1; + it->offset += len + sizeof(uint16_t); + ret = it->offset >= it->len ? -1 : 0; +end: + return ret; +} + +int bytecode_symbol_iterator_get_type(struct bytecode_symbol_iterator *it) +{ + int ret; + + if (!it) { + ret = -1; + goto end; + } + + ret = *((uint16_t *) (it->bytecode + it->offset)); +end: + return ret; + } + +const char *bytecode_symbol_iterator_get_name( + struct bytecode_symbol_iterator *it) +{ + const char *ret = NULL; + + if (!it) { + goto end; + } + + ret = it->bytecode + it->offset + sizeof(uint16_t); +end: + return ret; +} + +void bytecode_symbol_iterator_destroy(struct bytecode_symbol_iterator *it) +{ + free(it); +} diff --git a/src/common/fs-handle.c b/src/common/fs-handle.c deleted file mode 100644 index bed0010e7..000000000 --- a/src/common/fs-handle.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include - -int fs_handle_get_fd(struct fs_handle *handle) -{ - return handle->get_fd(handle); -} - -void fs_handle_put_fd(struct fs_handle *handle) -{ - return handle->put_fd(handle); -} - -int fs_handle_unlink(struct fs_handle *handle) -{ - return handle->unlink(handle); -} - -int fs_handle_close(struct fs_handle *handle) -{ - return handle->close(handle); -} - -ssize_t fs_handle_read(struct fs_handle *handle, void *buf, size_t count) -{ - ssize_t ret; - const int fd = fs_handle_get_fd(handle); - - if (fd < 0) { - ret = -1; - goto end; - } - - ret = lttng_read(fd, buf, count); - fs_handle_put_fd(handle); -end: - return ret; -} - -ssize_t fs_handle_write(struct fs_handle *handle, const void *buf, size_t count) -{ - ssize_t ret; - const int fd = fs_handle_get_fd(handle); - - if (fd < 0) { - ret = -1; - goto end; - } - - ret = lttng_write(fd, buf, count); - fs_handle_put_fd(handle); -end: - return ret; -} - -int fs_handle_truncate(struct fs_handle *handle, off_t offset) -{ - int ret; - const int fd = fs_handle_get_fd(handle); - - if (fd < 0) { - ret = -1; - goto end; - } - - ret = ftruncate(fd, offset); - fs_handle_put_fd(handle); -end: - return ret; -} - -off_t fs_handle_seek(struct fs_handle *handle, off_t offset, int whence) -{ - off_t ret; - const int fd = fs_handle_get_fd(handle); - - if (fd < 0) { - ret = -1; - goto end; - } - - ret = lseek(fd, offset, whence); - fs_handle_put_fd(handle); -end: - return ret; -} diff --git a/src/common/fs-handle.cpp b/src/common/fs-handle.cpp new file mode 100644 index 000000000..bed0010e7 --- /dev/null +++ b/src/common/fs-handle.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include + +int fs_handle_get_fd(struct fs_handle *handle) +{ + return handle->get_fd(handle); +} + +void fs_handle_put_fd(struct fs_handle *handle) +{ + return handle->put_fd(handle); +} + +int fs_handle_unlink(struct fs_handle *handle) +{ + return handle->unlink(handle); +} + +int fs_handle_close(struct fs_handle *handle) +{ + return handle->close(handle); +} + +ssize_t fs_handle_read(struct fs_handle *handle, void *buf, size_t count) +{ + ssize_t ret; + const int fd = fs_handle_get_fd(handle); + + if (fd < 0) { + ret = -1; + goto end; + } + + ret = lttng_read(fd, buf, count); + fs_handle_put_fd(handle); +end: + return ret; +} + +ssize_t fs_handle_write(struct fs_handle *handle, const void *buf, size_t count) +{ + ssize_t ret; + const int fd = fs_handle_get_fd(handle); + + if (fd < 0) { + ret = -1; + goto end; + } + + ret = lttng_write(fd, buf, count); + fs_handle_put_fd(handle); +end: + return ret; +} + +int fs_handle_truncate(struct fs_handle *handle, off_t offset) +{ + int ret; + const int fd = fs_handle_get_fd(handle); + + if (fd < 0) { + ret = -1; + goto end; + } + + ret = ftruncate(fd, offset); + fs_handle_put_fd(handle); +end: + return ret; +} + +off_t fs_handle_seek(struct fs_handle *handle, off_t offset, int whence) +{ + off_t ret; + const int fd = fs_handle_get_fd(handle); + + if (fd < 0) { + ret = -1; + goto end; + } + + ret = lseek(fd, offset, whence); + fs_handle_put_fd(handle); +end: + return ret; +} diff --git a/src/common/futex.c b/src/common/futex.c deleted file mode 100644 index d0f95abc8..000000000 --- a/src/common/futex.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2011 Mathieu Desnoyers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include - -#include - -#include "futex.h" - -/* - * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the - * "nto1" added to all function signature. - * - * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu - * git tree for a detail example of this scheme being used. futex_async() is - * the urcu wrapper over the futex() sycall. - * - * There is also a formal verification available in the git tree. - * - * branch: formal-model - * commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a - * - * Ref: git://git.lttng.org/userspace-rcu.git - */ - -/* - * Update futex according to active or not. This scheme is used to wake every - * libust waiting on the shared memory map futex hence the INT_MAX used in the - * futex() call. If active, we set the value and wake everyone else we indicate - * that we are gone (cleanup() case). - */ -void futex_wait_update(int32_t *futex, int active) -{ - if (active) { - uatomic_set(futex, 1); - if (futex_async(futex, FUTEX_WAKE, - INT_MAX, NULL, NULL, 0) < 0) { - PERROR("futex_async"); - abort(); - } - } else { - uatomic_set(futex, 0); - } - - DBG("Futex wait update active %d", active); -} - -/* - * Prepare futex. - */ -void futex_nto1_prepare(int32_t *futex) -{ - uatomic_set(futex, -1); - cmm_smp_mb(); - - DBG("Futex n to 1 prepare done"); -} - -/* - * Wait futex. - */ -void futex_nto1_wait(int32_t *futex) -{ - cmm_smp_mb(); - - if (uatomic_read(futex) != -1) - goto end; - while (futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0)) { - switch (errno) { - case EWOULDBLOCK: - /* Value already changed. */ - goto end; - case EINTR: - /* Retry if interrupted by signal. */ - break; /* Get out of switch. */ - default: - /* Unexpected error. */ - PERROR("futex_async"); - abort(); - } - } -end: - DBG("Futex n to 1 wait done"); -} - -/* - * Wake 1 futex. - */ -void futex_nto1_wake(int32_t *futex) -{ - if (caa_unlikely(uatomic_read(futex) != -1)) - goto end; - uatomic_set(futex, 0); - if (futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0) < 0) { - PERROR("futex_async"); - abort(); - } -end: - DBG("Futex n to 1 wake done"); -} diff --git a/src/common/futex.cpp b/src/common/futex.cpp new file mode 100644 index 000000000..d0f95abc8 --- /dev/null +++ b/src/common/futex.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2011 Mathieu Desnoyers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include + +#include "futex.h" + +/* + * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the + * "nto1" added to all function signature. + * + * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu + * git tree for a detail example of this scheme being used. futex_async() is + * the urcu wrapper over the futex() sycall. + * + * There is also a formal verification available in the git tree. + * + * branch: formal-model + * commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a + * + * Ref: git://git.lttng.org/userspace-rcu.git + */ + +/* + * Update futex according to active or not. This scheme is used to wake every + * libust waiting on the shared memory map futex hence the INT_MAX used in the + * futex() call. If active, we set the value and wake everyone else we indicate + * that we are gone (cleanup() case). + */ +void futex_wait_update(int32_t *futex, int active) +{ + if (active) { + uatomic_set(futex, 1); + if (futex_async(futex, FUTEX_WAKE, + INT_MAX, NULL, NULL, 0) < 0) { + PERROR("futex_async"); + abort(); + } + } else { + uatomic_set(futex, 0); + } + + DBG("Futex wait update active %d", active); +} + +/* + * Prepare futex. + */ +void futex_nto1_prepare(int32_t *futex) +{ + uatomic_set(futex, -1); + cmm_smp_mb(); + + DBG("Futex n to 1 prepare done"); +} + +/* + * Wait futex. + */ +void futex_nto1_wait(int32_t *futex) +{ + cmm_smp_mb(); + + if (uatomic_read(futex) != -1) + goto end; + while (futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0)) { + switch (errno) { + case EWOULDBLOCK: + /* Value already changed. */ + goto end; + case EINTR: + /* Retry if interrupted by signal. */ + break; /* Get out of switch. */ + default: + /* Unexpected error. */ + PERROR("futex_async"); + abort(); + } + } +end: + DBG("Futex n to 1 wait done"); +} + +/* + * Wake 1 futex. + */ +void futex_nto1_wake(int32_t *futex) +{ + if (caa_unlikely(uatomic_read(futex) != -1)) + goto end; + uatomic_set(futex, 0); + if (futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0) < 0) { + PERROR("futex_async"); + abort(); + } +end: + DBG("Futex n to 1 wake done"); +} diff --git a/src/common/index-allocator.c b/src/common/index-allocator.c deleted file mode 100644 index 8c73218a9..000000000 --- a/src/common/index-allocator.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2020 Francis Deslauriers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include - -#include -#include - -#include "macros.h" -#include "error.h" - -#include "index-allocator.h" - -struct lttng_index_allocator { - struct cds_list_head unused_list; - uint64_t size; - uint64_t position; - uint64_t nb_allocated_indexes; -}; - -struct lttng_index { - uint64_t index; - struct cds_list_head head; -}; - -struct lttng_index_allocator *lttng_index_allocator_create( - uint64_t index_count) -{ - struct lttng_index_allocator *allocator = NULL; - - allocator = zmalloc(sizeof(*allocator)); - if (!allocator) { - PERROR("Failed to allocate index allocator"); - goto end; - } - - allocator->size = index_count; - allocator->position = 0; - allocator->nb_allocated_indexes = 0; - - CDS_INIT_LIST_HEAD(&allocator->unused_list); - -end: - return allocator; -} - -uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator) -{ - return allocator->nb_allocated_indexes; -} - -enum lttng_index_allocator_status lttng_index_allocator_alloc( - struct lttng_index_allocator *allocator, - uint64_t *allocated_index) -{ - enum lttng_index_allocator_status status = - LTTNG_INDEX_ALLOCATOR_STATUS_OK; - - if (cds_list_empty(&allocator->unused_list)) { - if (allocator->position >= allocator->size) { - /* No indices left. */ - status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY; - goto end; - } - - *allocated_index = allocator->position++; - } else { - struct lttng_index *index; - - index = cds_list_first_entry(&allocator->unused_list, - typeof(*index), head); - cds_list_del(&index->head); - *allocated_index = index->index; - free(index); - } - - allocator->nb_allocated_indexes++; -end: - return status; -} - -enum lttng_index_allocator_status lttng_index_allocator_release( - struct lttng_index_allocator *allocator, uint64_t idx) -{ - struct lttng_index *index = NULL; - enum lttng_index_allocator_status status = - LTTNG_INDEX_ALLOCATOR_STATUS_OK; - - LTTNG_ASSERT(idx < allocator->size); - - index = zmalloc(sizeof(*index)); - if (!index) { - PERROR("Failed to allocate free index queue"); - status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR; - goto end; - } - - index->index = idx; - cds_list_add_tail(&index->head, &allocator->unused_list); - allocator->nb_allocated_indexes--; - -end: - return status; -} - -void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator) -{ - struct lttng_index *index = NULL, *tmp_index = NULL; - - if (!allocator) { - return; - } - - if (lttng_index_allocator_get_index_count(allocator) > 0) { - WARN("Destroying index allocator with %" PRIu64 - " slot indexes still in use", - lttng_index_allocator_get_index_count(allocator)); - } - - cds_list_for_each_entry_safe(index, tmp_index, - &allocator->unused_list, head) { - cds_list_del(&index->head); - free(index); - } - - free(allocator); -} diff --git a/src/common/index-allocator.cpp b/src/common/index-allocator.cpp new file mode 100644 index 000000000..c48d708d0 --- /dev/null +++ b/src/common/index-allocator.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include + +#include +#include + +#include "macros.h" +#include "error.h" + +#include "index-allocator.h" + +struct lttng_index_allocator { + struct cds_list_head unused_list; + uint64_t size; + uint64_t position; + uint64_t nb_allocated_indexes; +}; + +struct lttng_index { + uint64_t index; + struct cds_list_head head; +}; + +struct lttng_index_allocator *lttng_index_allocator_create( + uint64_t index_count) +{ + struct lttng_index_allocator *allocator = NULL; + + allocator = (lttng_index_allocator *) zmalloc(sizeof(*allocator)); + if (!allocator) { + PERROR("Failed to allocate index allocator"); + goto end; + } + + allocator->size = index_count; + allocator->position = 0; + allocator->nb_allocated_indexes = 0; + + CDS_INIT_LIST_HEAD(&allocator->unused_list); + +end: + return allocator; +} + +uint64_t lttng_index_allocator_get_index_count(struct lttng_index_allocator *allocator) +{ + return allocator->nb_allocated_indexes; +} + +enum lttng_index_allocator_status lttng_index_allocator_alloc( + struct lttng_index_allocator *allocator, + uint64_t *allocated_index) +{ + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + if (cds_list_empty(&allocator->unused_list)) { + if (allocator->position >= allocator->size) { + /* No indices left. */ + status = LTTNG_INDEX_ALLOCATOR_STATUS_EMPTY; + goto end; + } + + *allocated_index = allocator->position++; + } else { + struct lttng_index *index; + + index = cds_list_first_entry(&allocator->unused_list, + typeof(*index), head); + cds_list_del(&index->head); + *allocated_index = index->index; + free(index); + } + + allocator->nb_allocated_indexes++; +end: + return status; +} + +enum lttng_index_allocator_status lttng_index_allocator_release( + struct lttng_index_allocator *allocator, uint64_t idx) +{ + struct lttng_index *index = NULL; + enum lttng_index_allocator_status status = + LTTNG_INDEX_ALLOCATOR_STATUS_OK; + + LTTNG_ASSERT(idx < allocator->size); + + index = (lttng_index *) zmalloc(sizeof(*index)); + if (!index) { + PERROR("Failed to allocate free index queue"); + status = LTTNG_INDEX_ALLOCATOR_STATUS_ERROR; + goto end; + } + + index->index = idx; + cds_list_add_tail(&index->head, &allocator->unused_list); + allocator->nb_allocated_indexes--; + +end: + return status; +} + +void lttng_index_allocator_destroy(struct lttng_index_allocator *allocator) +{ + struct lttng_index *index = NULL, *tmp_index = NULL; + + if (!allocator) { + return; + } + + if (lttng_index_allocator_get_index_count(allocator) > 0) { + WARN("Destroying index allocator with %" PRIu64 + " slot indexes still in use", + lttng_index_allocator_get_index_count(allocator)); + } + + cds_list_for_each_entry_safe(index, tmp_index, + &allocator->unused_list, head) { + cds_list_del(&index->head); + free(index); + } + + free(allocator); +} diff --git a/src/common/kernel-probe.c b/src/common/kernel-probe.c deleted file mode 100644 index 33ba00fca..000000000 --- a/src/common/kernel-probe.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "lttng/lttng-error.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -int lttng_kernel_probe_location_address_serialize( - const struct lttng_kernel_probe_location *location, - struct lttng_payload *payload); - -static -int lttng_kernel_probe_location_symbol_serialize( - const struct lttng_kernel_probe_location *location, - struct lttng_payload *payload); - -static -bool lttng_kernel_probe_location_address_is_equal( - const struct lttng_kernel_probe_location *a, - const struct lttng_kernel_probe_location *b); - -static -bool lttng_kernel_probe_location_symbol_is_equal( - const struct lttng_kernel_probe_location *a, - const struct lttng_kernel_probe_location *b); - -static -unsigned long lttng_kernel_probe_location_address_hash( - const struct lttng_kernel_probe_location *location); - -static -unsigned long lttng_kernel_probe_location_symbol_hash( - const struct lttng_kernel_probe_location *location); - -static -enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize( - const struct lttng_kernel_probe_location *location, - struct mi_writer *writer); - -static -enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize( - const struct lttng_kernel_probe_location *location, - struct mi_writer *writer); - -enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type( - const struct lttng_kernel_probe_location *location) -{ - return location ? location->type : - LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN; -} - -static -void lttng_kernel_probe_location_address_destroy( - struct lttng_kernel_probe_location *location) -{ - LTTNG_ASSERT(location); - free(location); -} - -static -void lttng_kernel_probe_location_symbol_destroy( - struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location_symbol *location_symbol = NULL; - - LTTNG_ASSERT(location); - - location_symbol = container_of(location, - struct lttng_kernel_probe_location_symbol, - parent); - - LTTNG_ASSERT(location_symbol); - - free(location_symbol->symbol_name); - free(location); -} - -void lttng_kernel_probe_location_destroy( - struct lttng_kernel_probe_location *location) -{ - if (!location) { - return; - } - - switch (location->type) { - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: - lttng_kernel_probe_location_address_destroy(location); - break; - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: - lttng_kernel_probe_location_symbol_destroy(location); - break; - default: - abort(); - } -} - -struct lttng_kernel_probe_location * -lttng_kernel_probe_location_address_create(uint64_t address) -{ - struct lttng_kernel_probe_location *ret = NULL; - struct lttng_kernel_probe_location_address *location; - - location = zmalloc(sizeof(*location)); - if (!location) { - PERROR("Error allocating userspace probe location."); - goto end; - } - - location->address = address; - - ret = &location->parent; - ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS; - ret->equal = lttng_kernel_probe_location_address_is_equal; - ret->serialize = lttng_kernel_probe_location_address_serialize; - ret->hash = lttng_kernel_probe_location_address_hash; - ret->mi_serialize = lttng_kernel_probe_location_address_mi_serialize; - -end: - return ret; -} - -struct lttng_kernel_probe_location * -lttng_kernel_probe_location_symbol_create(const char *symbol_name, - uint64_t offset) -{ - char *symbol_name_copy = NULL; - struct lttng_kernel_probe_location *ret = NULL; - struct lttng_kernel_probe_location_symbol *location; - - if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) { - goto error; - } - - symbol_name_copy = strdup(symbol_name); - if (!symbol_name_copy) { - PERROR("Failed to copy symbol name '%s'", symbol_name); - goto error; - } - - location = zmalloc(sizeof(*location)); - if (!location) { - PERROR("Failed to allocate kernel symbol probe location"); - goto error; - } - - location->symbol_name = symbol_name_copy; - location->offset = offset; - - ret = &location->parent; - ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET; - ret->equal = lttng_kernel_probe_location_symbol_is_equal; - ret->serialize = lttng_kernel_probe_location_symbol_serialize; - ret->hash = lttng_kernel_probe_location_symbol_hash; - ret->mi_serialize = lttng_kernel_probe_location_symbol_mi_serialize; - goto end; - -error: - free(symbol_name_copy); -end: - return ret; -} - -enum lttng_kernel_probe_location_status -lttng_kernel_probe_location_address_get_address( - const struct lttng_kernel_probe_location *location, - uint64_t *offset) -{ - enum lttng_kernel_probe_location_status ret = - LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; - struct lttng_kernel_probe_location_address *address_location; - - LTTNG_ASSERT(offset); - - if (!location || lttng_kernel_probe_location_get_type(location) != - LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; - goto end; - } - - address_location = container_of(location, - struct lttng_kernel_probe_location_address, parent); - *offset = address_location->address; -end: - return ret; -} - -const char *lttng_kernel_probe_location_symbol_get_name( - const struct lttng_kernel_probe_location *location) -{ - const char *ret = NULL; - struct lttng_kernel_probe_location_symbol *symbol_location; - - if (!location || lttng_kernel_probe_location_get_type(location) != - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - symbol_location = container_of(location, - struct lttng_kernel_probe_location_symbol, parent); - ret = symbol_location->symbol_name; -end: - return ret; -} - -enum lttng_kernel_probe_location_status -lttng_kernel_probe_location_symbol_get_offset( - const struct lttng_kernel_probe_location *location, - uint64_t *offset) -{ - enum lttng_kernel_probe_location_status ret = - LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; - struct lttng_kernel_probe_location_symbol *symbol_location; - - LTTNG_ASSERT(offset); - - if (!location || lttng_kernel_probe_location_get_type(location) != - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; - goto end; - } - - symbol_location = container_of(location, - struct lttng_kernel_probe_location_symbol, parent); - *offset = symbol_location->offset; -end: - return ret; -} - -static -int lttng_kernel_probe_location_symbol_serialize( - const struct lttng_kernel_probe_location *location, - struct lttng_payload *payload) -{ - int ret; - size_t symbol_name_len; - size_t original_payload_size; - struct lttng_kernel_probe_location_symbol *location_symbol; - struct lttng_kernel_probe_location_symbol_comm location_symbol_comm; - - if (!location || !payload) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - ret = -LTTNG_ERR_INVALID; - goto end; - } - - LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) == - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); - - original_payload_size = payload->buffer.size; - location_symbol = container_of(location, - struct lttng_kernel_probe_location_symbol, parent); - - if (!location_symbol->symbol_name) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - symbol_name_len = strlen(location_symbol->symbol_name); - if (symbol_name_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_symbol_comm.symbol_len = symbol_name_len + 1; - location_symbol_comm.offset = location_symbol->offset; - - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_symbol_comm, sizeof(location_symbol_comm)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_symbol->symbol_name, - location_symbol_comm.symbol_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (int) (payload->buffer.size - original_payload_size); -end: - return ret; -} - -static -int lttng_kernel_probe_location_address_serialize( - const struct lttng_kernel_probe_location *location, - struct lttng_payload *payload) -{ - int ret; - size_t original_payload_size; - struct lttng_kernel_probe_location_address *location_address; - struct lttng_kernel_probe_location_address_comm location_address_comm; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) == - LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); - - original_payload_size = payload->buffer.size; - location_address = container_of(location, - struct lttng_kernel_probe_location_address, - parent); - - location_address_comm.address = location_address->address; - - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_address_comm, - sizeof(location_address_comm)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (int) (payload->buffer.size - original_payload_size); -end: - return ret; -} - -int lttng_kernel_probe_location_serialize( - const struct lttng_kernel_probe_location *location, - struct lttng_payload *payload) -{ - int ret; - size_t original_payload_size; - struct lttng_kernel_probe_location_comm location_generic_comm = {}; - - if (!location || !payload) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - ret = -LTTNG_ERR_INVALID; - goto end; - } - - original_payload_size = payload->buffer.size; - location_generic_comm.type = (int8_t) location->type; - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_generic_comm, - sizeof(location_generic_comm)); - if (ret) { - goto end; - } - - ret = location->serialize(location, payload); - if (ret < 0) { - goto end; - } - - ret = (int) (payload->buffer.size - original_payload_size); -end: - return ret; -} - -static -int lttng_kernel_probe_location_symbol_create_from_payload( - struct lttng_payload_view *view, - struct lttng_kernel_probe_location **location) -{ - struct lttng_kernel_probe_location_symbol_comm *location_symbol_comm; - const char *symbol_name_src; - ssize_t ret = 0; - size_t expected_size; - - LTTNG_ASSERT(location); - - if (view->buffer.size < sizeof(*location_symbol_comm)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_symbol_comm = - (typeof(location_symbol_comm)) view->buffer.data; - - expected_size = sizeof(*location_symbol_comm) + - location_symbol_comm->symbol_len; - - if (view->buffer.size < expected_size) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm); - - if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src, - location_symbol_comm->symbol_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - *location = lttng_kernel_probe_location_symbol_create( - symbol_name_src, location_symbol_comm->offset); - if (!(*location)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (ssize_t) expected_size; -end: - return ret; -} - -static -ssize_t lttng_kernel_probe_location_address_create_from_payload( - struct lttng_payload_view *view, - struct lttng_kernel_probe_location **location) -{ - struct lttng_kernel_probe_location_address_comm *location_address_comm; - ssize_t ret = 0; - size_t expected_size; - - LTTNG_ASSERT(location); - - expected_size = sizeof(*location_address_comm); - - if (view->buffer.size < expected_size) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_address_comm = - (typeof(location_address_comm)) view->buffer.data; - - *location = lttng_kernel_probe_location_address_create(location_address_comm->address); - if (!(*location)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (size_t) expected_size; -end: - return ret; -} - -ssize_t lttng_kernel_probe_location_create_from_payload( - struct lttng_payload_view *view, - struct lttng_kernel_probe_location **location) -{ - enum lttng_kernel_probe_location_type type; - ssize_t consumed = 0; - ssize_t ret; - const struct lttng_kernel_probe_location_comm *probe_location_comm; - const struct lttng_payload_view probe_location_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*probe_location_comm)); - - LTTNG_ASSERT(view); - LTTNG_ASSERT(location); - - if (!lttng_payload_view_is_valid(&probe_location_comm_view)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data; - type = (enum lttng_kernel_probe_location_type) probe_location_comm->type; - consumed += sizeof(*probe_location_comm); - - switch (type) { - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: - { - struct lttng_payload_view location_view = - lttng_payload_view_from_view( - view, consumed, -1); - - ret = lttng_kernel_probe_location_symbol_create_from_payload( - &location_view, location); - break; - } - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: - { - struct lttng_payload_view location_view = - lttng_payload_view_from_view(view, consumed, -1); - - ret = lttng_kernel_probe_location_address_create_from_payload( - &location_view, location); - break; - } - default: - ret = -LTTNG_ERR_INVALID; - break; - } - - if (ret < 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret += consumed; - -end: - return ret; -} - -static -unsigned long lttng_kernel_probe_location_address_hash( - const struct lttng_kernel_probe_location *location) -{ - unsigned long hash = hash_key_ulong( - (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS, - lttng_ht_seed); - struct lttng_kernel_probe_location_address *address_location = - container_of(location, typeof(*address_location), - parent); - - hash ^= hash_key_u64(&address_location->address, lttng_ht_seed); - - return hash; -} - -static -bool lttng_kernel_probe_location_address_is_equal( - const struct lttng_kernel_probe_location *_a, - const struct lttng_kernel_probe_location *_b) -{ - bool is_equal = false; - struct lttng_kernel_probe_location_address *a, *b; - - a = container_of(_a, struct lttng_kernel_probe_location_address, - parent); - b = container_of(_b, struct lttng_kernel_probe_location_address, - parent); - - if (a->address != b->address) { - goto end; - } - - is_equal = true; - -end: - return is_equal; -} - -static -unsigned long lttng_kernel_probe_location_symbol_hash( - const struct lttng_kernel_probe_location *location) -{ - unsigned long hash = hash_key_ulong( - (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET, - lttng_ht_seed); - struct lttng_kernel_probe_location_symbol *symbol_location = - container_of(location, typeof(*symbol_location), - parent); - - hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed); - hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed); - - return hash; -} - -static -bool lttng_kernel_probe_location_symbol_is_equal( - const struct lttng_kernel_probe_location *_a, - const struct lttng_kernel_probe_location *_b) -{ - bool is_equal = false; - struct lttng_kernel_probe_location_symbol *a, *b; - - a = container_of(_a, struct lttng_kernel_probe_location_symbol, - parent); - b = container_of(_b, struct lttng_kernel_probe_location_symbol, - parent); - - LTTNG_ASSERT(a->symbol_name); - LTTNG_ASSERT(b->symbol_name); - if (strcmp(a->symbol_name, b->symbol_name)) { - goto end; - } - - if (a->offset != b->offset) { - goto end; - } - - is_equal = true; - -end: - return is_equal; -} - -bool lttng_kernel_probe_location_is_equal( - const struct lttng_kernel_probe_location *a, - const struct lttng_kernel_probe_location *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - if (a->type != b->type) { - goto end; - } - - is_equal = a->equal ? a->equal(a, b) : true; -end: - return is_equal; -} - -static struct lttng_kernel_probe_location * -lttng_kernel_probe_location_symbol_copy( - const struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location *new_location = NULL; - struct lttng_kernel_probe_location_symbol *symbol_location; - enum lttng_kernel_probe_location_status status; - const char *symbol_name = NULL; - uint64_t offset; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); - symbol_location = container_of( - location, typeof(*symbol_location), parent); - - /* Get probe location offset */ - status = lttng_kernel_probe_location_symbol_get_offset(location, &offset); - if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { - ERR("Get kernel probe location offset failed."); - goto error; - } - - symbol_name = lttng_kernel_probe_location_symbol_get_name(location); - if (!symbol_name) { - ERR("Kernel probe symbol name is NULL."); - goto error; - } - - /* Create the probe_location */ - new_location = lttng_kernel_probe_location_symbol_create( - symbol_name, offset); - - goto end; - -error: - new_location = NULL; -end: - return new_location; -} -static struct lttng_kernel_probe_location * -lttng_kernel_probe_location_address_copy( - const struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location *new_location = NULL; - struct lttng_kernel_probe_location_address *address_location; - enum lttng_kernel_probe_location_status status; - uint64_t address; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); - address_location = container_of( - location, typeof(*address_location), parent); - - - /* Get probe location fields */ - status = lttng_kernel_probe_location_address_get_address(location, &address); - if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { - ERR("Get kernel probe address failed."); - goto error; - } - - /* Create the probe_location */ - new_location = lttng_kernel_probe_location_address_create(address); - - goto end; - -error: - new_location = NULL; -end: - return new_location; -} - -struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy( - const struct lttng_kernel_probe_location *location) -{ - struct lttng_kernel_probe_location *new_location = NULL; - enum lttng_kernel_probe_location_type type; - - if (!location) { - goto err; - } - - type = lttng_kernel_probe_location_get_type(location); - switch (type) { - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: - new_location = - lttng_kernel_probe_location_address_copy(location); - if (!new_location) { - goto err; - } - break; - case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: - new_location = - lttng_kernel_probe_location_symbol_copy(location); - if (!new_location) { - goto err; - } - break; - default: - new_location = NULL; - goto err; - } -err: - return new_location; -} - -unsigned long lttng_kernel_probe_location_hash( - const struct lttng_kernel_probe_location *location) -{ - return location->hash(location); -} - -static -enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize( - const struct lttng_kernel_probe_location *location, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_kernel_probe_location_status status; - uint64_t address; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); - - status = lttng_kernel_probe_location_address_get_address( - location, &address); - LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); - - /* Open kernel probe location address element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_kernel_probe_location_address); - if (ret) { - goto mi_error; - } - - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_kernel_probe_location_address_address, - address); - if (ret) { - goto mi_error; - } - - /* Close kernel probe location address element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static -enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize( - const struct lttng_kernel_probe_location *location, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_kernel_probe_location_status status; - const char *name = NULL; - uint64_t offset; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - LTTNG_ASSERT(location->type == - LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); - - name = lttng_kernel_probe_location_symbol_get_name(location); - LTTNG_ASSERT(name); - - status = lttng_kernel_probe_location_symbol_get_offset( - location, &offset); - LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); - - /* Open kernel probe location symbol offset element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_kernel_probe_location_symbol_offset); - if (ret) { - goto mi_error; - } - - /* Name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_kernel_probe_location_symbol_offset_name, - name); - if (ret) { - goto mi_error; - } - - /* Offset. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_kernel_probe_location_symbol_offset_offset, - offset); - if (ret) { - goto mi_error; - } - - /* Close kernel probe location symbol offset element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -enum lttng_error_code lttng_kernel_probe_location_mi_serialize( - const struct lttng_kernel_probe_location *location, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - - /* Open kernel probe location element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_kernel_probe_location); - if (ret) { - goto mi_error; - } - - /* Serialize the location sub type. */ - ret_code = location->mi_serialize(location, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close kernel probe location element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/kernel-probe.cpp b/src/common/kernel-probe.cpp new file mode 100644 index 000000000..a05b451c3 --- /dev/null +++ b/src/common/kernel-probe.cpp @@ -0,0 +1,879 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/lttng-error.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int lttng_kernel_probe_location_address_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload); + +static +int lttng_kernel_probe_location_symbol_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload); + +static +bool lttng_kernel_probe_location_address_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); + +static +bool lttng_kernel_probe_location_symbol_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b); + +static +unsigned long lttng_kernel_probe_location_address_hash( + const struct lttng_kernel_probe_location *location); + +static +unsigned long lttng_kernel_probe_location_symbol_hash( + const struct lttng_kernel_probe_location *location); + +static +enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize( + const struct lttng_kernel_probe_location *location, + struct mi_writer *writer); + +static +enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize( + const struct lttng_kernel_probe_location *location, + struct mi_writer *writer); + +enum lttng_kernel_probe_location_type lttng_kernel_probe_location_get_type( + const struct lttng_kernel_probe_location *location) +{ + return location ? location->type : + LTTNG_KERNEL_PROBE_LOCATION_TYPE_UNKNOWN; +} + +static +void lttng_kernel_probe_location_address_destroy( + struct lttng_kernel_probe_location *location) +{ + LTTNG_ASSERT(location); + free(location); +} + +static +void lttng_kernel_probe_location_symbol_destroy( + struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location_symbol *location_symbol = NULL; + + LTTNG_ASSERT(location); + + location_symbol = container_of(location, + struct lttng_kernel_probe_location_symbol, + parent); + + LTTNG_ASSERT(location_symbol); + + free(location_symbol->symbol_name); + free(location); +} + +void lttng_kernel_probe_location_destroy( + struct lttng_kernel_probe_location *location) +{ + if (!location) { + return; + } + + switch (location->type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + lttng_kernel_probe_location_address_destroy(location); + break; + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + lttng_kernel_probe_location_symbol_destroy(location); + break; + default: + abort(); + } +} + +struct lttng_kernel_probe_location * +lttng_kernel_probe_location_address_create(uint64_t address) +{ + struct lttng_kernel_probe_location *ret = NULL; + struct lttng_kernel_probe_location_address *location; + + location = (lttng_kernel_probe_location_address *) zmalloc(sizeof(*location)); + if (!location) { + PERROR("Error allocating userspace probe location."); + goto end; + } + + location->address = address; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS; + ret->equal = lttng_kernel_probe_location_address_is_equal; + ret->serialize = lttng_kernel_probe_location_address_serialize; + ret->hash = lttng_kernel_probe_location_address_hash; + ret->mi_serialize = lttng_kernel_probe_location_address_mi_serialize; + +end: + return ret; +} + +struct lttng_kernel_probe_location * +lttng_kernel_probe_location_symbol_create(const char *symbol_name, + uint64_t offset) +{ + char *symbol_name_copy = NULL; + struct lttng_kernel_probe_location *ret = NULL; + struct lttng_kernel_probe_location_symbol *location; + + if (!symbol_name || strlen(symbol_name) >= LTTNG_SYMBOL_NAME_LEN) { + goto error; + } + + symbol_name_copy = strdup(symbol_name); + if (!symbol_name_copy) { + PERROR("Failed to copy symbol name '%s'", symbol_name); + goto error; + } + + location = (lttng_kernel_probe_location_symbol *) zmalloc(sizeof(*location)); + if (!location) { + PERROR("Failed to allocate kernel symbol probe location"); + goto error; + } + + location->symbol_name = symbol_name_copy; + location->offset = offset; + + ret = &location->parent; + ret->type = LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET; + ret->equal = lttng_kernel_probe_location_symbol_is_equal; + ret->serialize = lttng_kernel_probe_location_symbol_serialize; + ret->hash = lttng_kernel_probe_location_symbol_hash; + ret->mi_serialize = lttng_kernel_probe_location_symbol_mi_serialize; + goto end; + +error: + free(symbol_name_copy); +end: + return ret; +} + +enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_address_get_address( + const struct lttng_kernel_probe_location *location, + uint64_t *offset) +{ + enum lttng_kernel_probe_location_status ret = + LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; + struct lttng_kernel_probe_location_address *address_location; + + LTTNG_ASSERT(offset); + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; + goto end; + } + + address_location = container_of(location, + struct lttng_kernel_probe_location_address, parent); + *offset = address_location->address; +end: + return ret; +} + +const char *lttng_kernel_probe_location_symbol_get_name( + const struct lttng_kernel_probe_location *location) +{ + const char *ret = NULL; + struct lttng_kernel_probe_location_symbol *symbol_location; + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_probe_location_symbol, parent); + ret = symbol_location->symbol_name; +end: + return ret; +} + +enum lttng_kernel_probe_location_status +lttng_kernel_probe_location_symbol_get_offset( + const struct lttng_kernel_probe_location *location, + uint64_t *offset) +{ + enum lttng_kernel_probe_location_status ret = + LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK; + struct lttng_kernel_probe_location_symbol *symbol_location; + + LTTNG_ASSERT(offset); + + if (!location || lttng_kernel_probe_location_get_type(location) != + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = LTTNG_KERNEL_PROBE_LOCATION_STATUS_INVALID; + goto end; + } + + symbol_location = container_of(location, + struct lttng_kernel_probe_location_symbol, parent); + *offset = symbol_location->offset; +end: + return ret; +} + +static +int lttng_kernel_probe_location_symbol_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + size_t original_payload_size; + struct lttng_kernel_probe_location_symbol *location_symbol; + struct lttng_kernel_probe_location_symbol_comm location_symbol_comm; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) == + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + original_payload_size = payload->buffer.size; + location_symbol = container_of(location, + struct lttng_kernel_probe_location_symbol, parent); + + if (!location_symbol->symbol_name) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_len = strlen(location_symbol->symbol_name); + if (symbol_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm.symbol_len = symbol_name_len + 1; + location_symbol_comm.offset = location_symbol->offset; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_symbol_comm, sizeof(location_symbol_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_symbol->symbol_name, + location_symbol_comm.symbol_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_probe_location_address_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_probe_location_address *location_address; + struct lttng_kernel_probe_location_address_comm location_address_comm; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(lttng_kernel_probe_location_get_type(location) == + LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + original_payload_size = payload->buffer.size; + location_address = container_of(location, + struct lttng_kernel_probe_location_address, + parent); + + location_address_comm.address = location_address->address; + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_address_comm, + sizeof(location_address_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +int lttng_kernel_probe_location_serialize( + const struct lttng_kernel_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t original_payload_size; + struct lttng_kernel_probe_location_comm location_generic_comm = {}; + + if (!location || !payload) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + original_payload_size = payload->buffer.size; + location_generic_comm.type = (int8_t) location->type; + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_generic_comm, + sizeof(location_generic_comm)); + if (ret) { + goto end; + } + + ret = location->serialize(location, payload); + if (ret < 0) { + goto end; + } + + ret = (int) (payload->buffer.size - original_payload_size); +end: + return ret; +} + +static +int lttng_kernel_probe_location_symbol_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + struct lttng_kernel_probe_location_symbol_comm *location_symbol_comm; + const char *symbol_name_src; + ssize_t ret = 0; + size_t expected_size; + + LTTNG_ASSERT(location); + + if (view->buffer.size < sizeof(*location_symbol_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_symbol_comm = + (typeof(location_symbol_comm)) view->buffer.data; + + expected_size = sizeof(*location_symbol_comm) + + location_symbol_comm->symbol_len; + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + symbol_name_src = view->buffer.data + sizeof(*location_symbol_comm); + + if (!lttng_buffer_view_contains_string(&view->buffer, symbol_name_src, + location_symbol_comm->symbol_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + *location = lttng_kernel_probe_location_symbol_create( + symbol_name_src, location_symbol_comm->offset); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (ssize_t) expected_size; +end: + return ret; +} + +static +ssize_t lttng_kernel_probe_location_address_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + struct lttng_kernel_probe_location_address_comm *location_address_comm; + ssize_t ret = 0; + size_t expected_size; + + LTTNG_ASSERT(location); + + expected_size = sizeof(*location_address_comm); + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_address_comm = + (typeof(location_address_comm)) view->buffer.data; + + *location = lttng_kernel_probe_location_address_create(location_address_comm->address); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (size_t) expected_size; +end: + return ret; +} + +ssize_t lttng_kernel_probe_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_kernel_probe_location **location) +{ + enum lttng_kernel_probe_location_type type; + ssize_t consumed = 0; + ssize_t ret; + const struct lttng_kernel_probe_location_comm *probe_location_comm; + const struct lttng_payload_view probe_location_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*probe_location_comm)); + + LTTNG_ASSERT(view); + LTTNG_ASSERT(location); + + if (!lttng_payload_view_is_valid(&probe_location_comm_view)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data; + type = (enum lttng_kernel_probe_location_type) probe_location_comm->type; + consumed += sizeof(*probe_location_comm); + + switch (type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_kernel_probe_location_symbol_create_from_payload( + &location_view, location); + break; + } + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view(view, consumed, -1); + + ret = lttng_kernel_probe_location_address_create_from_payload( + &location_view, location); + break; + } + default: + ret = -LTTNG_ERR_INVALID; + break; + } + + if (ret < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret += consumed; + +end: + return ret; +} + +static +unsigned long lttng_kernel_probe_location_address_hash( + const struct lttng_kernel_probe_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS, + lttng_ht_seed); + struct lttng_kernel_probe_location_address *address_location = + container_of(location, typeof(*address_location), + parent); + + hash ^= hash_key_u64(&address_location->address, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_probe_location_address_is_equal( + const struct lttng_kernel_probe_location *_a, + const struct lttng_kernel_probe_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_probe_location_address *a, *b; + + a = container_of(_a, struct lttng_kernel_probe_location_address, + parent); + b = container_of(_b, struct lttng_kernel_probe_location_address, + parent); + + if (a->address != b->address) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +static +unsigned long lttng_kernel_probe_location_symbol_hash( + const struct lttng_kernel_probe_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET, + lttng_ht_seed); + struct lttng_kernel_probe_location_symbol *symbol_location = + container_of(location, typeof(*symbol_location), + parent); + + hash ^= hash_key_str(symbol_location->symbol_name, lttng_ht_seed); + hash ^= hash_key_u64(&symbol_location->offset, lttng_ht_seed); + + return hash; +} + +static +bool lttng_kernel_probe_location_symbol_is_equal( + const struct lttng_kernel_probe_location *_a, + const struct lttng_kernel_probe_location *_b) +{ + bool is_equal = false; + struct lttng_kernel_probe_location_symbol *a, *b; + + a = container_of(_a, struct lttng_kernel_probe_location_symbol, + parent); + b = container_of(_b, struct lttng_kernel_probe_location_symbol, + parent); + + LTTNG_ASSERT(a->symbol_name); + LTTNG_ASSERT(b->symbol_name); + if (strcmp(a->symbol_name, b->symbol_name)) { + goto end; + } + + if (a->offset != b->offset) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +bool lttng_kernel_probe_location_is_equal( + const struct lttng_kernel_probe_location *a, + const struct lttng_kernel_probe_location *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +static struct lttng_kernel_probe_location * +lttng_kernel_probe_location_symbol_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + struct lttng_kernel_probe_location_symbol *symbol_location; + enum lttng_kernel_probe_location_status status; + const char *symbol_name = NULL; + uint64_t offset; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + symbol_location = container_of( + location, typeof(*symbol_location), parent); + + /* Get probe location offset */ + status = lttng_kernel_probe_location_symbol_get_offset(location, &offset); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Get kernel probe location offset failed."); + goto error; + } + + symbol_name = lttng_kernel_probe_location_symbol_get_name(location); + if (!symbol_name) { + ERR("Kernel probe symbol name is NULL."); + goto error; + } + + /* Create the probe_location */ + new_location = lttng_kernel_probe_location_symbol_create( + symbol_name, offset); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} +static struct lttng_kernel_probe_location * +lttng_kernel_probe_location_address_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + struct lttng_kernel_probe_location_address *address_location; + enum lttng_kernel_probe_location_status status; + uint64_t address; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + address_location = container_of( + location, typeof(*address_location), parent); + + + /* Get probe location fields */ + status = lttng_kernel_probe_location_address_get_address(location, &address); + if (status != LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK) { + ERR("Get kernel probe address failed."); + goto error; + } + + /* Create the probe_location */ + new_location = lttng_kernel_probe_location_address_create(address); + + goto end; + +error: + new_location = NULL; +end: + return new_location; +} + +struct lttng_kernel_probe_location *lttng_kernel_probe_location_copy( + const struct lttng_kernel_probe_location *location) +{ + struct lttng_kernel_probe_location *new_location = NULL; + enum lttng_kernel_probe_location_type type; + + if (!location) { + goto err; + } + + type = lttng_kernel_probe_location_get_type(location); + switch (type) { + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS: + new_location = + lttng_kernel_probe_location_address_copy(location); + if (!new_location) { + goto err; + } + break; + case LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET: + new_location = + lttng_kernel_probe_location_symbol_copy(location); + if (!new_location) { + goto err; + } + break; + default: + new_location = NULL; + goto err; + } +err: + return new_location; +} + +unsigned long lttng_kernel_probe_location_hash( + const struct lttng_kernel_probe_location *location) +{ + return location->hash(location); +} + +static +enum lttng_error_code lttng_kernel_probe_location_address_mi_serialize( + const struct lttng_kernel_probe_location *location, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_kernel_probe_location_status status; + uint64_t address; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(location->type == LTTNG_KERNEL_PROBE_LOCATION_TYPE_ADDRESS); + + status = lttng_kernel_probe_location_address_get_address( + location, &address); + LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); + + /* Open kernel probe location address element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_kernel_probe_location_address); + if (ret) { + goto mi_error; + } + + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_kernel_probe_location_address_address, + address); + if (ret) { + goto mi_error; + } + + /* Close kernel probe location address element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static +enum lttng_error_code lttng_kernel_probe_location_symbol_mi_serialize( + const struct lttng_kernel_probe_location *location, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_kernel_probe_location_status status; + const char *name = NULL; + uint64_t offset; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + LTTNG_ASSERT(location->type == + LTTNG_KERNEL_PROBE_LOCATION_TYPE_SYMBOL_OFFSET); + + name = lttng_kernel_probe_location_symbol_get_name(location); + LTTNG_ASSERT(name); + + status = lttng_kernel_probe_location_symbol_get_offset( + location, &offset); + LTTNG_ASSERT(status == LTTNG_KERNEL_PROBE_LOCATION_STATUS_OK); + + /* Open kernel probe location symbol offset element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_kernel_probe_location_symbol_offset); + if (ret) { + goto mi_error; + } + + /* Name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_kernel_probe_location_symbol_offset_name, + name); + if (ret) { + goto mi_error; + } + + /* Offset. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_kernel_probe_location_symbol_offset_offset, + offset); + if (ret) { + goto mi_error; + } + + /* Close kernel probe location symbol offset element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +enum lttng_error_code lttng_kernel_probe_location_mi_serialize( + const struct lttng_kernel_probe_location *location, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + + /* Open kernel probe location element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_kernel_probe_location); + if (ret) { + goto mi_error; + } + + /* Serialize the location sub type. */ + ret_code = location->mi_serialize(location, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close kernel probe location element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/location.c b/src/common/location.c deleted file mode 100644 index e68051ee5..000000000 --- a/src/common/location.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2018 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include - -static -struct lttng_trace_archive_location *lttng_trace_archive_location_create( - enum lttng_trace_archive_location_type type) -{ - struct lttng_trace_archive_location *location; - - location = zmalloc(sizeof(*location)); - if (!location) { - goto end; - } - - urcu_ref_init(&location->ref); - location->type = type; -end: - return location; -} - -static -void trace_archive_location_destroy_ref(struct urcu_ref *ref) -{ - struct lttng_trace_archive_location *location = - container_of(ref, struct lttng_trace_archive_location, ref); - - switch (location->type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - free(location->types.local.absolute_path); - break; - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - free(location->types.relay.host); - free(location->types.relay.relative_path); - break; - default: - abort(); - } - - free(location); -} - -void lttng_trace_archive_location_get(struct lttng_trace_archive_location *location) -{ - urcu_ref_get(&location->ref); -} - -void lttng_trace_archive_location_put(struct lttng_trace_archive_location *location) -{ - if (!location) { - return; - } - - urcu_ref_put(&location->ref, trace_archive_location_destroy_ref); -} - -struct lttng_trace_archive_location *lttng_trace_archive_location_local_create( - const char *absolute_path) -{ - struct lttng_trace_archive_location *location = NULL; - - if (!absolute_path) { - goto end; - } - - location = lttng_trace_archive_location_create( - LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL); - if (!location) { - goto end; - } - - location->types.local.absolute_path = strdup(absolute_path); - if (!location->types.local.absolute_path) { - goto error; - } - -end: - return location; -error: - lttng_trace_archive_location_put(location); - return NULL; -} - -struct lttng_trace_archive_location *lttng_trace_archive_location_relay_create( - const char *host, - enum lttng_trace_archive_location_relay_protocol_type protocol, - uint16_t control_port, uint16_t data_port, - const char *relative_path) -{ - struct lttng_trace_archive_location *location = NULL; - - if (!host || !relative_path) { - goto end; - } - - location = lttng_trace_archive_location_create( - LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY); - if (!location) { - goto end; - } - - location->types.relay.host = strdup(host); - if (!location->types.relay.host) { - goto error; - } - location->types.relay.relative_path = strdup(relative_path); - if (!location->types.relay.relative_path) { - goto error; - } - - location->types.relay.protocol = protocol; - location->types.relay.ports.control = control_port; - location->types.relay.ports.data = data_port; -end: - return location; -error: - lttng_trace_archive_location_put(location); - return NULL; -} - -ssize_t lttng_trace_archive_location_create_from_buffer( - const struct lttng_buffer_view *view, - struct lttng_trace_archive_location **location) -{ - size_t offset = 0; - const struct lttng_trace_archive_location_comm *location_comm; - struct lttng_buffer_view location_comm_view; - - location_comm_view = lttng_buffer_view_from_view(view, 0, - sizeof(*location_comm)); - if (!lttng_buffer_view_is_valid(&location_comm_view)) { - goto error; - } - - offset += location_comm_view.size; - location_comm = (const struct lttng_trace_archive_location_comm *) location_comm_view.data; - - switch ((enum lttng_trace_archive_location_type) location_comm->type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - { - const struct lttng_buffer_view absolute_path_view = - lttng_buffer_view_from_view(view, offset, - location_comm->types.local.absolute_path_len); - - if (!lttng_buffer_view_is_valid(&absolute_path_view)) { - goto error; - } - - if (absolute_path_view.data[absolute_path_view.size - 1] != '\0') { - goto error; - } - offset += absolute_path_view.size; - - *location = lttng_trace_archive_location_local_create( - absolute_path_view.data); - if (!*location) { - goto error; - } - break; - } - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - { - const struct lttng_buffer_view hostname_view = - lttng_buffer_view_from_view(view, offset, - location_comm->types.relay.hostname_len); - const struct lttng_buffer_view relative_path_view = - lttng_buffer_view_from_view(view, - offset + hostname_view.size, - location_comm->types.relay.relative_path_len); - - if (!lttng_buffer_view_is_valid(&hostname_view) || - !lttng_buffer_view_is_valid( - &relative_path_view)) { - goto error; - } - - if (hostname_view.data[hostname_view.size - 1] != '\0') { - goto error; - } - if (relative_path_view.data[relative_path_view.size - 1] != '\0') { - goto error; - } - offset += hostname_view.size + relative_path_view.size; - - *location = lttng_trace_archive_location_relay_create( - hostname_view.data, - (enum lttng_trace_archive_location_relay_protocol_type) location_comm->types.relay.protocol, - location_comm->types.relay.ports.control, - location_comm->types.relay.ports.data, - relative_path_view.data); - if (!*location) { - goto error; - } - break; - } - default: - goto error; - } - - return offset; -error: - return -1; -} - -ssize_t lttng_trace_archive_location_serialize( - const struct lttng_trace_archive_location *location, - struct lttng_dynamic_buffer *buffer) -{ - int ret; - struct lttng_trace_archive_location_comm location_comm; - - location_comm.type = (int8_t) location->type; - - switch (location->type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - location_comm.types.local.absolute_path_len = - strlen(location->types.local.absolute_path) + 1; - break; - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - location_comm.types.relay.hostname_len = - strlen(location->types.relay.host) + 1; - location_comm.types.relay.protocol = - (int8_t) location->types.relay.protocol; - location_comm.types.relay.ports.control = - location->types.relay.ports.control; - location_comm.types.relay.ports.data = - location->types.relay.ports.data; - location_comm.types.relay.relative_path_len = - strlen(location->types.relay.relative_path) + 1; - break; - default: - abort(); - } - - ret = lttng_dynamic_buffer_append(buffer, &location_comm, - sizeof(location_comm)); - if (ret) { - goto error; - } - - switch (location->type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - ret = lttng_dynamic_buffer_append(buffer, - location->types.local.absolute_path, - location_comm.types.local.absolute_path_len); - if (ret) { - goto error; - } - break; - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - ret = lttng_dynamic_buffer_append(buffer, - location->types.relay.host, - location_comm.types.relay.hostname_len); - if (ret) { - goto error; - } - ret = lttng_dynamic_buffer_append(buffer, - location->types.relay.relative_path, - location_comm.types.relay.relative_path_len); - if (ret) { - goto error; - } - break; - default: - abort(); - } - - return 0; -error: - return -1; -} - -enum lttng_trace_archive_location_type lttng_trace_archive_location_get_type( - const struct lttng_trace_archive_location *location) -{ - enum lttng_trace_archive_location_type type; - - if (!location) { - type = LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_UNKNOWN; - goto end; - } - - type = location->type; -end: - return type; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_local_get_absolute_path( - const struct lttng_trace_archive_location *location, - const char **absolute_path) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !absolute_path || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *absolute_path = location->types.local.absolute_path; -end: - return status; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_relay_get_host( - const struct lttng_trace_archive_location *location, - const char **relay_host) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !relay_host || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *relay_host = location->types.relay.host; -end: - return status; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_relay_get_relative_path( - const struct lttng_trace_archive_location *location, - const char **relative_path) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !relative_path || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *relative_path = location->types.relay.relative_path; -end: - return status; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_relay_get_control_port( - const struct lttng_trace_archive_location *location, - uint16_t *control_port) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !control_port || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *control_port = location->types.relay.ports.control; -end: - return status; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_relay_get_data_port( - const struct lttng_trace_archive_location *location, - uint16_t *data_port) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !data_port || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *data_port = location->types.relay.ports.data; -end: - return status; -} - -enum lttng_trace_archive_location_status -lttng_trace_archive_location_relay_get_protocol_type( - const struct lttng_trace_archive_location *location, - enum lttng_trace_archive_location_relay_protocol_type *protocol) -{ - enum lttng_trace_archive_location_status status = - LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; - - if (!location || !protocol || - location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { - status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; - goto end; - } - - *protocol = location->types.relay.protocol; -end: - return status; -} diff --git a/src/common/location.cpp b/src/common/location.cpp new file mode 100644 index 000000000..80c4b4d55 --- /dev/null +++ b/src/common/location.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2018 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include + +static +struct lttng_trace_archive_location *lttng_trace_archive_location_create( + enum lttng_trace_archive_location_type type) +{ + struct lttng_trace_archive_location *location; + + location = (lttng_trace_archive_location *) zmalloc(sizeof(*location)); + if (!location) { + goto end; + } + + urcu_ref_init(&location->ref); + location->type = type; +end: + return location; +} + +static +void trace_archive_location_destroy_ref(struct urcu_ref *ref) +{ + struct lttng_trace_archive_location *location = + container_of(ref, struct lttng_trace_archive_location, ref); + + switch (location->type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + free(location->types.local.absolute_path); + break; + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + free(location->types.relay.host); + free(location->types.relay.relative_path); + break; + default: + abort(); + } + + free(location); +} + +void lttng_trace_archive_location_get(struct lttng_trace_archive_location *location) +{ + urcu_ref_get(&location->ref); +} + +void lttng_trace_archive_location_put(struct lttng_trace_archive_location *location) +{ + if (!location) { + return; + } + + urcu_ref_put(&location->ref, trace_archive_location_destroy_ref); +} + +struct lttng_trace_archive_location *lttng_trace_archive_location_local_create( + const char *absolute_path) +{ + struct lttng_trace_archive_location *location = NULL; + + if (!absolute_path) { + goto end; + } + + location = lttng_trace_archive_location_create( + LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL); + if (!location) { + goto end; + } + + location->types.local.absolute_path = strdup(absolute_path); + if (!location->types.local.absolute_path) { + goto error; + } + +end: + return location; +error: + lttng_trace_archive_location_put(location); + return NULL; +} + +struct lttng_trace_archive_location *lttng_trace_archive_location_relay_create( + const char *host, + enum lttng_trace_archive_location_relay_protocol_type protocol, + uint16_t control_port, uint16_t data_port, + const char *relative_path) +{ + struct lttng_trace_archive_location *location = NULL; + + if (!host || !relative_path) { + goto end; + } + + location = lttng_trace_archive_location_create( + LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY); + if (!location) { + goto end; + } + + location->types.relay.host = strdup(host); + if (!location->types.relay.host) { + goto error; + } + location->types.relay.relative_path = strdup(relative_path); + if (!location->types.relay.relative_path) { + goto error; + } + + location->types.relay.protocol = protocol; + location->types.relay.ports.control = control_port; + location->types.relay.ports.data = data_port; +end: + return location; +error: + lttng_trace_archive_location_put(location); + return NULL; +} + +ssize_t lttng_trace_archive_location_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_trace_archive_location **location) +{ + size_t offset = 0; + const struct lttng_trace_archive_location_comm *location_comm; + struct lttng_buffer_view location_comm_view; + + location_comm_view = lttng_buffer_view_from_view(view, 0, + sizeof(*location_comm)); + if (!lttng_buffer_view_is_valid(&location_comm_view)) { + goto error; + } + + offset += location_comm_view.size; + location_comm = (const struct lttng_trace_archive_location_comm *) location_comm_view.data; + + switch ((enum lttng_trace_archive_location_type) location_comm->type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + { + const struct lttng_buffer_view absolute_path_view = + lttng_buffer_view_from_view(view, offset, + location_comm->types.local.absolute_path_len); + + if (!lttng_buffer_view_is_valid(&absolute_path_view)) { + goto error; + } + + if (absolute_path_view.data[absolute_path_view.size - 1] != '\0') { + goto error; + } + offset += absolute_path_view.size; + + *location = lttng_trace_archive_location_local_create( + absolute_path_view.data); + if (!*location) { + goto error; + } + break; + } + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + { + const struct lttng_buffer_view hostname_view = + lttng_buffer_view_from_view(view, offset, + location_comm->types.relay.hostname_len); + const struct lttng_buffer_view relative_path_view = + lttng_buffer_view_from_view(view, + offset + hostname_view.size, + location_comm->types.relay.relative_path_len); + + if (!lttng_buffer_view_is_valid(&hostname_view) || + !lttng_buffer_view_is_valid( + &relative_path_view)) { + goto error; + } + + if (hostname_view.data[hostname_view.size - 1] != '\0') { + goto error; + } + if (relative_path_view.data[relative_path_view.size - 1] != '\0') { + goto error; + } + offset += hostname_view.size + relative_path_view.size; + + *location = lttng_trace_archive_location_relay_create( + hostname_view.data, + (enum lttng_trace_archive_location_relay_protocol_type) location_comm->types.relay.protocol, + location_comm->types.relay.ports.control, + location_comm->types.relay.ports.data, + relative_path_view.data); + if (!*location) { + goto error; + } + break; + } + default: + goto error; + } + + return offset; +error: + return -1; +} + +ssize_t lttng_trace_archive_location_serialize( + const struct lttng_trace_archive_location *location, + struct lttng_dynamic_buffer *buffer) +{ + int ret; + struct lttng_trace_archive_location_comm location_comm; + + location_comm.type = (int8_t) location->type; + + switch (location->type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + location_comm.types.local.absolute_path_len = + strlen(location->types.local.absolute_path) + 1; + break; + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + location_comm.types.relay.hostname_len = + strlen(location->types.relay.host) + 1; + location_comm.types.relay.protocol = + (int8_t) location->types.relay.protocol; + location_comm.types.relay.ports.control = + location->types.relay.ports.control; + location_comm.types.relay.ports.data = + location->types.relay.ports.data; + location_comm.types.relay.relative_path_len = + strlen(location->types.relay.relative_path) + 1; + break; + default: + abort(); + } + + ret = lttng_dynamic_buffer_append(buffer, &location_comm, + sizeof(location_comm)); + if (ret) { + goto error; + } + + switch (location->type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + ret = lttng_dynamic_buffer_append(buffer, + location->types.local.absolute_path, + location_comm.types.local.absolute_path_len); + if (ret) { + goto error; + } + break; + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + ret = lttng_dynamic_buffer_append(buffer, + location->types.relay.host, + location_comm.types.relay.hostname_len); + if (ret) { + goto error; + } + ret = lttng_dynamic_buffer_append(buffer, + location->types.relay.relative_path, + location_comm.types.relay.relative_path_len); + if (ret) { + goto error; + } + break; + default: + abort(); + } + + return 0; +error: + return -1; +} + +enum lttng_trace_archive_location_type lttng_trace_archive_location_get_type( + const struct lttng_trace_archive_location *location) +{ + enum lttng_trace_archive_location_type type; + + if (!location) { + type = LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_UNKNOWN; + goto end; + } + + type = location->type; +end: + return type; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_local_get_absolute_path( + const struct lttng_trace_archive_location *location, + const char **absolute_path) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !absolute_path || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *absolute_path = location->types.local.absolute_path; +end: + return status; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_relay_get_host( + const struct lttng_trace_archive_location *location, + const char **relay_host) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !relay_host || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *relay_host = location->types.relay.host; +end: + return status; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_relay_get_relative_path( + const struct lttng_trace_archive_location *location, + const char **relative_path) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !relative_path || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *relative_path = location->types.relay.relative_path; +end: + return status; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_relay_get_control_port( + const struct lttng_trace_archive_location *location, + uint16_t *control_port) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !control_port || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *control_port = location->types.relay.ports.control; +end: + return status; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_relay_get_data_port( + const struct lttng_trace_archive_location *location, + uint16_t *data_port) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !data_port || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *data_port = location->types.relay.ports.data; +end: + return status; +} + +enum lttng_trace_archive_location_status +lttng_trace_archive_location_relay_get_protocol_type( + const struct lttng_trace_archive_location *location, + enum lttng_trace_archive_location_relay_protocol_type *protocol) +{ + enum lttng_trace_archive_location_status status = + LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK; + + if (!location || !protocol || + location->type != LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY) { + status = LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_INVALID; + goto end; + } + + *protocol = location->types.relay.protocol; +end: + return status; +} diff --git a/src/common/log-level-rule.c b/src/common/log-level-rule.c deleted file mode 100644 index d2bbd4176..000000000 --- a/src/common/log-level-rule.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2020 Jonathan Rajotte - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule) -{ - enum lttng_log_level_rule_type type = - lttng_log_level_rule_get_type(rule); - - return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; -} - -static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule) -{ - - enum lttng_log_level_rule_type type = - lttng_log_level_rule_get_type(rule); - - return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; -} - -enum lttng_log_level_rule_type lttng_log_level_rule_get_type( - const struct lttng_log_level_rule *rule) -{ - return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN; -} - -struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( - int level) -{ - struct lttng_log_level_rule *rule = NULL; - - rule = zmalloc(sizeof(struct lttng_log_level_rule)); - if (!rule) { - goto end; - } - - rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; - rule->level = level; - -end: - return rule; -} - -enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( - const struct lttng_log_level_rule *rule, int *level) -{ - enum lttng_log_level_rule_status status = - LTTNG_LOG_LEVEL_RULE_STATUS_OK; - - if (!rule || !level || !is_log_level_rule_exactly_type(rule)) { - status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; - goto end; - } - - *level = rule->level; -end: - return status; -} - -struct lttng_log_level_rule * -lttng_log_level_rule_at_least_as_severe_as_create(int level) -{ - struct lttng_log_level_rule *rule = NULL; - - rule = zmalloc(sizeof(struct lttng_log_level_rule)); - if (!rule) { - goto end; - } - - rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; - rule->level = level; - -end: - return rule; -} - -enum lttng_log_level_rule_status -lttng_log_level_rule_at_least_as_severe_as_get_level( - const struct lttng_log_level_rule *rule, int *level) -{ - enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; - - if (!rule || !level || - !is_log_level_rule_at_least_as_severe_type(rule)) { - status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; - goto end; - } - - *level = rule->level; -end: - return status; -} - -void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule) -{ - free(log_level_rule); -} - -ssize_t lttng_log_level_rule_create_from_payload( - struct lttng_payload_view *view, - struct lttng_log_level_rule **_rule) -{ - ssize_t ret; - size_t offset = 0; - struct lttng_log_level_rule *rule = NULL; - const struct lttng_log_level_rule_comm *comm = - (const struct lttng_log_level_rule_comm *) - view->buffer.data; - - offset += sizeof(*comm); - - if (!_rule) { - ret = -1; - goto end; - } - - if (view->buffer.size < sizeof(*comm)) { - ret = -1; - goto end; - } - - switch (comm->type) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - rule = lttng_log_level_rule_exactly_create((int) comm->level); - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - rule = lttng_log_level_rule_at_least_as_severe_as_create( - (int) comm->level); - break; - default: - abort(); - } - - if (!rule) { - ret = -1; - goto end; - } - - *_rule = rule; - ret = offset; - -end: - return ret; -} - -int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, - struct lttng_payload *payload) -{ - int ret; - struct lttng_log_level_rule_comm comm; - - - if (!rule) { - ret = 0; - goto end; - } - - comm.type = (int8_t) rule->type; - comm.level = (int32_t) rule->level; - - DBG("Serializing log level rule of type %d", rule->type); - ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, - sizeof(comm)); - if (ret) { - goto end; - } - -end: - return ret; -} - -bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, - const struct lttng_log_level_rule *b) -{ - bool is_equal = false; - - if (a == NULL && b == NULL) { - /* Both are null. */ - is_equal = true; - goto end; - } - - if (a == NULL || b == NULL) { - /* One is NULL.*/ - goto end; - } - - if (a == b) { - /* Same object.*/ - is_equal = true; - goto end; - } - - if (a->type != b->type) { - goto end; - } - - if (a->level != b->level) { - goto end; - } - - is_equal = true; - -end: - return is_equal; -} - -struct lttng_log_level_rule *lttng_log_level_rule_copy( - const struct lttng_log_level_rule *source) -{ - struct lttng_log_level_rule *copy = NULL; - - LTTNG_ASSERT(source); - - copy = zmalloc(sizeof(struct lttng_log_level_rule)); - if (!copy) { - goto end; - } - - copy->type = source->type; - copy->level = source->level; -end: - return copy; -} - -void lttng_log_level_rule_to_loglevel( - const struct lttng_log_level_rule *log_level_rule, - enum lttng_loglevel_type *loglevel_type, - int *loglevel_value) -{ - LTTNG_ASSERT(log_level_rule); - - switch (log_level_rule->type) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; - break; - default: - abort(); - } - - *loglevel_value = log_level_rule->level; -} - -unsigned long lttng_log_level_rule_hash( - const struct lttng_log_level_rule *log_level_rule) -{ - unsigned long hash; - enum lttng_log_level_rule_status llr_status; - int log_level_value; - enum lttng_log_level_rule_type type; - - LTTNG_ASSERT(log_level_rule); - - type = lttng_log_level_rule_get_type(log_level_rule); - - switch (type) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - llr_status = lttng_log_level_rule_exactly_get_level( - log_level_rule, &log_level_value); - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( - log_level_rule, &log_level_value); - break; - default: - abort(); - break; - } - - LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); - - hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed); - - hash ^= hash_key_ulong((void *) (unsigned long) log_level_value, - lttng_ht_seed); - - return hash; -} - -enum lttng_error_code lttng_log_level_rule_mi_serialize( - const struct lttng_log_level_rule *rule, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - enum lttng_log_level_rule_status status; - const char *element_str = NULL; - int level; - - LTTNG_ASSERT(rule); - LTTNG_ASSERT(writer); - - switch (lttng_log_level_rule_get_type(rule)) { - case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: - status = lttng_log_level_rule_exactly_get_level(rule, &level); - element_str = mi_lttng_element_log_level_rule_exactly; - break; - case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: - element_str = mi_lttng_element_log_level_rule_at_least_as_severe_as; - status = lttng_log_level_rule_at_least_as_severe_as_get_level( - rule, &level); - break; - default: - abort(); - break; - } - - LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); - - /* Open log level rule element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_log_level_rule); - if (ret) { - goto mi_error; - } - - /* Log level rule type element. */ - ret = mi_lttng_writer_open_element(writer, element_str); - if (ret) { - goto mi_error; - } - - /* Level. */ - ret = mi_lttng_writer_write_element_signed_int( - writer, mi_lttng_element_log_level_rule_level, level); - if (ret) { - goto mi_error; - } - - /* Close log level rule type element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - /* Close log level rule element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/log-level-rule.cpp b/src/common/log-level-rule.cpp new file mode 100644 index 000000000..e891222e0 --- /dev/null +++ b/src/common/log-level-rule.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2020 Jonathan Rajotte + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_log_level_rule_exactly_type(const struct lttng_log_level_rule *rule) +{ + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; +} + +static bool is_log_level_rule_at_least_as_severe_type(const struct lttng_log_level_rule *rule) +{ + + enum lttng_log_level_rule_type type = + lttng_log_level_rule_get_type(rule); + + return type == LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; +} + +enum lttng_log_level_rule_type lttng_log_level_rule_get_type( + const struct lttng_log_level_rule *rule) +{ + return rule ? rule->type : LTTNG_LOG_LEVEL_RULE_TYPE_UNKNOWN; +} + +struct lttng_log_level_rule *lttng_log_level_rule_exactly_create( + int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status lttng_log_level_rule_exactly_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = + LTTNG_LOG_LEVEL_RULE_STATUS_OK; + + if (!rule || !level || !is_log_level_rule_exactly_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +struct lttng_log_level_rule * +lttng_log_level_rule_at_least_as_severe_as_create(int level) +{ + struct lttng_log_level_rule *rule = NULL; + + rule = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule)); + if (!rule) { + goto end; + } + + rule->type = LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS; + rule->level = level; + +end: + return rule; +} + +enum lttng_log_level_rule_status +lttng_log_level_rule_at_least_as_severe_as_get_level( + const struct lttng_log_level_rule *rule, int *level) +{ + enum lttng_log_level_rule_status status = LTTNG_LOG_LEVEL_RULE_STATUS_OK; + + if (!rule || !level || + !is_log_level_rule_at_least_as_severe_type(rule)) { + status = LTTNG_LOG_LEVEL_RULE_STATUS_INVALID; + goto end; + } + + *level = rule->level; +end: + return status; +} + +void lttng_log_level_rule_destroy(struct lttng_log_level_rule *log_level_rule) +{ + free(log_level_rule); +} + +ssize_t lttng_log_level_rule_create_from_payload( + struct lttng_payload_view *view, + struct lttng_log_level_rule **_rule) +{ + ssize_t ret; + size_t offset = 0; + struct lttng_log_level_rule *rule = NULL; + const struct lttng_log_level_rule_comm *comm = + (const struct lttng_log_level_rule_comm *) + view->buffer.data; + + offset += sizeof(*comm); + + if (!_rule) { + ret = -1; + goto end; + } + + if (view->buffer.size < sizeof(*comm)) { + ret = -1; + goto end; + } + + switch (comm->type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + rule = lttng_log_level_rule_exactly_create((int) comm->level); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + rule = lttng_log_level_rule_at_least_as_severe_as_create( + (int) comm->level); + break; + default: + abort(); + } + + if (!rule) { + ret = -1; + goto end; + } + + *_rule = rule; + ret = offset; + +end: + return ret; +} + +int lttng_log_level_rule_serialize(const struct lttng_log_level_rule *rule, + struct lttng_payload *payload) +{ + int ret; + struct lttng_log_level_rule_comm comm; + + + if (!rule) { + ret = 0; + goto end; + } + + comm.type = (int8_t) rule->type; + comm.level = (int32_t) rule->level; + + DBG("Serializing log level rule of type %d", rule->type); + ret = lttng_dynamic_buffer_append(&payload->buffer, &comm, + sizeof(comm)); + if (ret) { + goto end; + } + +end: + return ret; +} + +bool lttng_log_level_rule_is_equal(const struct lttng_log_level_rule *a, + const struct lttng_log_level_rule *b) +{ + bool is_equal = false; + + if (a == NULL && b == NULL) { + /* Both are null. */ + is_equal = true; + goto end; + } + + if (a == NULL || b == NULL) { + /* One is NULL.*/ + goto end; + } + + if (a == b) { + /* Same object.*/ + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + if (a->level != b->level) { + goto end; + } + + is_equal = true; + +end: + return is_equal; +} + +struct lttng_log_level_rule *lttng_log_level_rule_copy( + const struct lttng_log_level_rule *source) +{ + struct lttng_log_level_rule *copy = NULL; + + LTTNG_ASSERT(source); + + copy = (lttng_log_level_rule *) zmalloc(sizeof(struct lttng_log_level_rule)); + if (!copy) { + goto end; + } + + copy->type = source->type; + copy->level = source->level; +end: + return copy; +} + +void lttng_log_level_rule_to_loglevel( + const struct lttng_log_level_rule *log_level_rule, + enum lttng_loglevel_type *loglevel_type, + int *loglevel_value) +{ + LTTNG_ASSERT(log_level_rule); + + switch (log_level_rule->type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + *loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + break; + default: + abort(); + } + + *loglevel_value = log_level_rule->level; +} + +unsigned long lttng_log_level_rule_hash( + const struct lttng_log_level_rule *log_level_rule) +{ + unsigned long hash; + enum lttng_log_level_rule_status llr_status; + int log_level_value; + enum lttng_log_level_rule_type type; + + LTTNG_ASSERT(log_level_rule); + + type = lttng_log_level_rule_get_type(log_level_rule); + + switch (type) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + llr_status = lttng_log_level_rule_exactly_get_level( + log_level_rule, &log_level_value); + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + llr_status = lttng_log_level_rule_at_least_as_severe_as_get_level( + log_level_rule, &log_level_value); + break; + default: + abort(); + break; + } + + LTTNG_ASSERT(llr_status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + hash = hash_key_ulong((void *) (unsigned long) type, lttng_ht_seed); + + hash ^= hash_key_ulong((void *) (unsigned long) log_level_value, + lttng_ht_seed); + + return hash; +} + +enum lttng_error_code lttng_log_level_rule_mi_serialize( + const struct lttng_log_level_rule *rule, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + enum lttng_log_level_rule_status status; + const char *element_str = NULL; + int level; + + LTTNG_ASSERT(rule); + LTTNG_ASSERT(writer); + + switch (lttng_log_level_rule_get_type(rule)) { + case LTTNG_LOG_LEVEL_RULE_TYPE_EXACTLY: + status = lttng_log_level_rule_exactly_get_level(rule, &level); + element_str = mi_lttng_element_log_level_rule_exactly; + break; + case LTTNG_LOG_LEVEL_RULE_TYPE_AT_LEAST_AS_SEVERE_AS: + element_str = mi_lttng_element_log_level_rule_at_least_as_severe_as; + status = lttng_log_level_rule_at_least_as_severe_as_get_level( + rule, &level); + break; + default: + abort(); + break; + } + + LTTNG_ASSERT(status == LTTNG_LOG_LEVEL_RULE_STATUS_OK); + + /* Open log level rule element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_log_level_rule); + if (ret) { + goto mi_error; + } + + /* Log level rule type element. */ + ret = mi_lttng_writer_open_element(writer, element_str); + if (ret) { + goto mi_error; + } + + /* Level. */ + ret = mi_lttng_writer_write_element_signed_int( + writer, mi_lttng_element_log_level_rule_level, level); + if (ret) { + goto mi_error; + } + + /* Close log level rule type element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + /* Close log level rule element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/lttng-elf.c b/src/common/lttng-elf.c deleted file mode 100644 index d6cef2fe8..000000000 --- a/src/common/lttng-elf.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright (C) 2015 Antoine Busque - * Copyright (C) 2017 Francis Deslauriers - * Copyright (C) 2017 Erica Bugden - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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; -} diff --git a/src/common/lttng-elf.cpp b/src/common/lttng-elf.cpp new file mode 100644 index 000000000..992410a88 --- /dev/null +++ b/src/common/lttng-elf.cpp @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2015 Antoine Busque + * Copyright (C) 2017 Francis Deslauriers + * Copyright (C) 2017 Erica Bugden + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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(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(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; +} diff --git a/src/common/lttng-elf.h b/src/common/lttng-elf.h index 524b87846..41dd49605 100644 --- a/src/common/lttng-elf.h +++ b/src/common/lttng-elf.h @@ -10,9 +10,11 @@ #include -LTTNG_EXPORT int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset); +extern "C" LTTNG_EXPORT +int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset); -LTTNG_EXPORT int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name, +extern "C" LTTNG_EXPORT +int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name, const char *probe_name, uint64_t **offsets, uint32_t *nb_probe); #endif /* _LTTNG_ELF_H */ diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c deleted file mode 100644 index 1ff9f936c..000000000 --- a/src/common/mi-lttng.c +++ /dev/null @@ -1,2718 +0,0 @@ -/* - * Copyright (C) 2014 Jonathan Rajotte - * Copyright (C) 2014 Olivier Cotte - * Copyright (C) 2016 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "lttng/tracker.h" -#define _LGPL_SOURCE -#include "mi-lttng.h" -#include -#include -#include -#include -#include - - -#define MI_SCHEMA_MAJOR_VERSION 4 -#define MI_SCHEMA_MINOR_VERSION 1 - -/* Machine interface namespace URI */ -const char * const mi_lttng_xmlns = "xmlns"; -const char * const mi_lttng_xmlns_xsi = "xmlns:xsi"; -const char * const mi_lttng_w3_schema_uri = "http://www.w3.org/2001/XMLSchema-instance"; -const char * const mi_lttng_schema_location = "xsi:schemaLocation"; -const char * const mi_lttng_schema_location_uri = - DEFAULT_LTTNG_MI_NAMESPACE " " - "https://lttng.org/xml/schemas/lttng-mi/" XSTR(MI_SCHEMA_MAJOR_VERSION) - "/lttng-mi-" XSTR(MI_SCHEMA_MAJOR_VERSION) "." - XSTR(MI_SCHEMA_MINOR_VERSION) ".xsd"; -const char * const mi_lttng_schema_version = "schemaVersion"; -const char * const mi_lttng_schema_version_value = XSTR(MI_SCHEMA_MAJOR_VERSION) - "." XSTR(MI_SCHEMA_MINOR_VERSION); - -/* Strings related to command */ -const char * const mi_lttng_element_command = "command"; -const char * const mi_lttng_element_command_action = "snapshot_action"; -const char * const mi_lttng_element_command_add_context = "add-context"; -const char *const mi_lttng_element_command_add_trigger = "add-trigger"; -const char * const mi_lttng_element_command_create = "create"; -const char * const mi_lttng_element_command_destroy = "destroy"; -const char * const mi_lttng_element_command_disable_channel = "disable-channel"; -const char * const mi_lttng_element_command_disable_event = "disable-event"; -const char * const mi_lttng_element_command_enable_channels = "enable-channel"; -const char * const mi_lttng_element_command_enable_event = "enable-event"; -const char * const mi_lttng_element_command_list = "list"; -const char *const mi_lttng_element_command_list_trigger = "list-trigger"; -const char * const mi_lttng_element_command_load = "load"; -const char * const mi_lttng_element_command_metadata = "metadata"; -const char * const mi_lttng_element_command_metadata_action = "metadata_action"; -const char * const mi_lttng_element_command_regenerate = "regenerate"; -const char * const mi_lttng_element_command_regenerate_action = "regenerate_action"; -const char * const mi_lttng_element_command_name = "name"; -const char * const mi_lttng_element_command_output = "output"; -const char *const mi_lttng_element_command_remove_trigger = "remove-trigger"; -const char * const mi_lttng_element_command_save = "save"; -const char * const mi_lttng_element_command_set_session = "set-session"; -const char * const mi_lttng_element_command_snapshot = "snapshot"; -const char * const mi_lttng_element_command_snapshot_add = "add_snapshot"; -const char * const mi_lttng_element_command_snapshot_del = "del_snapshot"; -const char * const mi_lttng_element_command_snapshot_list = "list_snapshot"; -const char * const mi_lttng_element_command_snapshot_record = "record_snapshot"; -const char * const mi_lttng_element_command_start = "start"; -const char * const mi_lttng_element_command_stop = "stop"; -const char * const mi_lttng_element_command_success = "success"; -const char * const mi_lttng_element_command_track = "track"; -const char * const mi_lttng_element_command_untrack = "untrack"; -const char * const mi_lttng_element_command_version = "version"; -const char * const mi_lttng_element_command_rotate = "rotate"; -const char * const mi_lttng_element_command_enable_rotation = "enable-rotation"; -const char * const mi_lttng_element_command_disable_rotation = "disable-rotation"; -const char * const mi_lttng_element_command_clear = "clear"; - -/* Strings related to version command */ -const char * const mi_lttng_element_version = "version"; -const char * const mi_lttng_element_version_commit = "commit"; -const char * const mi_lttng_element_version_description = "description"; -const char * const mi_lttng_element_version_license = "license"; -const char * const mi_lttng_element_version_major = "major"; -const char * const mi_lttng_element_version_minor = "minor"; -const char * const mi_lttng_element_version_patch_level = "patchLevel"; -const char * const mi_lttng_element_version_str = "string"; -const char * const mi_lttng_element_version_web = "url"; - -/* String related to a lttng_event_field */ -const char * const mi_lttng_element_event_field = "event_field"; -const char * const mi_lttng_element_event_fields = "event_fields"; - -/* String related to lttng_event_perf_counter_ctx */ -const char * const mi_lttng_element_perf_counter_context = "perf"; - -/* Strings related to pid */ -const char * const mi_lttng_element_pid_id = "id"; - -/* Strings related to save command */ -const char * const mi_lttng_element_save = "save"; - -/* Strings related to load command */ -const char * const mi_lttng_element_load = "load"; -const char * const mi_lttng_element_load_overrides = "overrides"; -const char * const mi_lttng_element_load_override_url = "url"; - -/* General elements of mi_lttng */ -const char * const mi_lttng_element_empty = ""; -const char * const mi_lttng_element_id = "id"; -const char * const mi_lttng_element_nowrite = "nowrite"; -const char * const mi_lttng_element_success = "success"; -const char * const mi_lttng_element_type_enum = "ENUM"; -const char * const mi_lttng_element_type_float = "FLOAT"; -const char * const mi_lttng_element_type_integer = "INTEGER"; -const char * const mi_lttng_element_type_other = "OTHER"; -const char * const mi_lttng_element_type_string = "STRING"; - -/* String related to loglevel */ -const char * const mi_lttng_loglevel_str_alert = "TRACE_ALERT"; -const char * const mi_lttng_loglevel_str_crit = "TRACE_CRIT"; -const char * const mi_lttng_loglevel_str_debug = "TRACE_DEBUG"; -const char * const mi_lttng_loglevel_str_debug_function = "TRACE_DEBUG_FUNCTION"; -const char * const mi_lttng_loglevel_str_debug_line = "TRACE_DEBUG_LINE"; -const char * const mi_lttng_loglevel_str_debug_module = "TRACE_DEBUG_MODULE"; -const char * const mi_lttng_loglevel_str_debug_process = "TRACE_DEBUG_PROCESS"; -const char * const mi_lttng_loglevel_str_debug_program = "TRACE_DEBUG_PROGRAM"; -const char * const mi_lttng_loglevel_str_debug_system = "TRACE_DEBUG_SYSTEM"; -const char * const mi_lttng_loglevel_str_debug_unit = "TRACE_DEBUG_UNIT"; -const char * const mi_lttng_loglevel_str_emerg = "TRACE_EMERG"; -const char * const mi_lttng_loglevel_str_err = "TRACE_ERR"; -const char * const mi_lttng_loglevel_str_info = "TRACE_INFO"; -const char * const mi_lttng_loglevel_str_notice = "TRACE_NOTICE"; -const char * const mi_lttng_loglevel_str_unknown = "UNKNOWN"; -const char * const mi_lttng_loglevel_str_warning = "TRACE_WARNING"; - -/* String related to loglevel JUL */ -const char * const mi_lttng_loglevel_str_jul_all = "JUL_ALL"; -const char * const mi_lttng_loglevel_str_jul_config = "JUL_CONFIG"; -const char * const mi_lttng_loglevel_str_jul_fine = "JUL_FINE"; -const char * const mi_lttng_loglevel_str_jul_finer = "JUL_FINER"; -const char * const mi_lttng_loglevel_str_jul_finest = "JUL_FINEST"; -const char * const mi_lttng_loglevel_str_jul_info = "JUL_INFO"; -const char * const mi_lttng_loglevel_str_jul_off = "JUL_OFF"; -const char * const mi_lttng_loglevel_str_jul_severe = "JUL_SEVERE"; -const char * const mi_lttng_loglevel_str_jul_warning = "JUL_WARNING"; - -/* String related to loglevel LOG4J */ -const char * const mi_lttng_loglevel_str_log4j_off = "LOG4J_OFF"; -const char * const mi_lttng_loglevel_str_log4j_fatal = "LOG4J_FATAL"; -const char * const mi_lttng_loglevel_str_log4j_error = "LOG4J_ERROR"; -const char * const mi_lttng_loglevel_str_log4j_warn = "LOG4J_WARN"; -const char * const mi_lttng_loglevel_str_log4j_info = "LOG4J_INFO"; -const char * const mi_lttng_loglevel_str_log4j_debug = "LOG4J_DEBUG"; -const char * const mi_lttng_loglevel_str_log4j_trace = "LOG4J_TRACE"; -const char * const mi_lttng_loglevel_str_log4j_all = "LOG4J_ALL"; - -/* String related to loglevel Python */ -const char * const mi_lttng_loglevel_str_python_critical = "PYTHON_CRITICAL"; -const char * const mi_lttng_loglevel_str_python_error = "PYTHON_ERROR"; -const char * const mi_lttng_loglevel_str_python_warning = "PYTHON_WARNING"; -const char * const mi_lttng_loglevel_str_python_info = "PYTHON_INFO"; -const char * const mi_lttng_loglevel_str_python_debug = "PYTHON_DEBUG"; -const char * const mi_lttng_loglevel_str_python_notset = "PYTHON_NOTSET"; - -/* String related to loglevel type */ -const char * const mi_lttng_loglevel_type_all = "ALL"; -const char * const mi_lttng_loglevel_type_range = "RANGE"; -const char * const mi_lttng_loglevel_type_single = "SINGLE"; -const char * const mi_lttng_loglevel_type_unknown = "UNKNOWN"; - -/* String related to a lttng_snapshot_output */ -const char * const mi_lttng_element_snapshot_ctrl_url = "ctrl_url"; -const char * const mi_lttng_element_snapshot_data_url = "data_url"; -const char * const mi_lttng_element_snapshot_max_size = "max_size"; -const char * const mi_lttng_element_snapshot_n_ptr = "n_ptr"; -const char * const mi_lttng_element_snapshot_session_name = "session_name"; -const char * const mi_lttng_element_snapshots = "snapshots"; - -/* String related to track/untrack command */ -const char * const mi_lttng_element_track_untrack_all_wildcard = "*"; - -const char * const mi_lttng_element_session_name = "session_name"; - -/* String related to rotate command */ -const char * const mi_lttng_element_rotation = "rotation"; -const char * const mi_lttng_element_rotate_status = "status"; -const char * const mi_lttng_element_rotation_schedule = "rotation_schedule"; -const char * const mi_lttng_element_rotation_schedules = "rotation_schedules"; -const char * const mi_lttng_element_rotation_schedule_result = "rotation_schedule_result"; -const char * const mi_lttng_element_rotation_schedule_results = "rotation_schedule_results"; -const char * const mi_lttng_element_rotation_schedule_periodic = "periodic"; -const char * const mi_lttng_element_rotation_schedule_periodic_time_us = "time_us"; -const char * const mi_lttng_element_rotation_schedule_size_threshold = "size_threshold"; -const char * const mi_lttng_element_rotation_schedule_size_threshold_bytes = "bytes"; -const char * const mi_lttng_element_rotation_state = "state"; -const char * const mi_lttng_element_rotation_location = "location"; -const char * const mi_lttng_element_rotation_location_local = "local"; -const char * const mi_lttng_element_rotation_location_local_absolute_path = "absolute_path"; -const char * const mi_lttng_element_rotation_location_relay = "relay"; -const char * const mi_lttng_element_rotation_location_relay_host = "host"; -const char * const mi_lttng_element_rotation_location_relay_control_port = "control_port"; -const char * const mi_lttng_element_rotation_location_relay_data_port = "data_port"; -const char * const mi_lttng_element_rotation_location_relay_protocol = "protocol"; -const char * const mi_lttng_element_rotation_location_relay_relative_path = "relative_path"; - -/* String related to enum lttng_rotation_state */ -const char * const mi_lttng_rotation_state_str_ongoing = "ONGOING"; -const char * const mi_lttng_rotation_state_str_completed = "COMPLETED"; -const char * const mi_lttng_rotation_state_str_expired = "EXPIRED"; -const char * const mi_lttng_rotation_state_str_error = "ERROR"; - -/* String related to enum lttng_trace_archive_location_relay_protocol_type */ -const char * const mi_lttng_rotation_location_relay_protocol_str_tcp = "TCP"; - -/* String related to rate_policy elements */ -const char *const mi_lttng_element_rate_policy = "rate_policy"; -const char *const mi_lttng_element_rate_policy_every_n = - "rate_policy_every_n"; -const char *const mi_lttng_element_rate_policy_once_after_n = - "rate_policy_once_after_n"; - -const char *const mi_lttng_element_rate_policy_every_n_interval = - "interval"; -const char - *const mi_lttng_element_rate_policy_once_after_n_threshold = - "threshold"; - -/* String related to action elements */ -const char *const mi_lttng_element_action = "action"; -const char *const mi_lttng_element_action_list = "action_list"; -const char *const mi_lttng_element_action_notify = "action_notify"; -const char *const mi_lttng_element_action_start_session = - "action_start_session"; -const char *const mi_lttng_element_action_stop_session = - "action_stop_session"; -const char *const mi_lttng_element_action_rotate_session = - "action_rotate_session"; -const char *const mi_lttng_element_action_snapshot_session = - "action_snapshot_session"; -const char *const mi_lttng_element_action_snapshot_session_output = - "output"; - -/* String related to condition */ -const char *const mi_lttng_element_condition = "condition"; -const char *const mi_lttng_element_condition_buffer_usage_high = - "condition_buffer_usage_high"; -const char *const mi_lttng_element_condition_buffer_usage_low = - "condition_buffer_usage_low"; -const char *const mi_lttng_element_condition_event_rule_matches = - "condition_event_rule_matches"; -const char *const mi_lttng_element_condition_session_consumed_size = - "condition_session_consumed_size"; -const char *const mi_lttng_element_condition_session_rotation = - "condition_session_rotation"; -const char - *const mi_lttng_element_condition_session_rotation_completed = - "condition_session_rotation_completed"; -const char - *const mi_lttng_element_condition_session_rotation_ongoing = - "condition_session_rotation_ongoing"; - -const char *const mi_lttng_element_condition_channel_name = - "channel_name"; -const char *const mi_lttng_element_condition_threshold_bytes = - "threshold_bytes"; -const char *const mi_lttng_element_condition_threshold_ratio = - "threshold_ratio"; - -/* String related to capture descriptor */ -const char *const mi_lttng_element_capture_descriptor = - "capture_descriptor"; -const char *const mi_lttng_element_capture_descriptors = - "capture_descriptors"; - -/* String related to event expression */ -const char *const mi_lttng_element_event_expr = "event_expr"; -const char *const mi_lttng_element_event_expr_payload_field = - "event_expr_payload_field"; -const char *const mi_lttng_element_event_expr_channel_context_field = - "event_expr_channel_context_field"; -const char - *const mi_lttng_element_event_expr_app_specific_context_field = - "event_expr_app_specific_context_field"; -const char *const mi_lttng_element_event_expr_array_field_element = - "event_expr_array_field_element"; -const char *const mi_lttng_element_event_expr_provider_name = - "provider_name"; -const char *const mi_lttng_element_event_expr_type_name = - "type_name"; -const char *const mi_lttng_element_event_expr_index = "index"; - -/* String related to event rule */ -const char *const mi_lttng_element_event_rule = "event_rule"; - -/* String related to lttng_event_rule_type */ -const char *const mi_lttng_element_event_rule_event_name = - "event_name"; -const char *const mi_lttng_element_event_rule_name_pattern = - "name_pattern"; -const char *const mi_lttng_element_event_rule_filter_expression = - "filter_expression"; - -const char *const mi_lttng_element_event_rule_jul_logging = - "event_rule_jul_logging"; -const char *const mi_lttng_element_event_rule_kernel_kprobe = - "event_rule_kernel_kprobe"; -const char *const mi_lttng_element_event_rule_kernel_syscall = - "event_rule_kernel_syscall"; -const char *const mi_lttng_element_event_rule_kernel_tracepoint = - "event_rule_kernel_tracepoint"; -const char *const mi_lttng_element_event_rule_kernel_uprobe = - "event_rule_kernel_uprobe"; -const char *const mi_lttng_element_event_rule_log4j_logging = - "event_rule_log4j_logging"; -const char *const mi_lttng_element_event_rule_python_logging = - "event_rule_python_logging"; -const char *const mi_lttng_element_event_rule_user_tracepoint = - "event_rule_user_tracepoint"; - -/* String related to lttng_event_rule_kernel_syscall. */ -const char *const - mi_lttng_element_event_rule_kernel_syscall_emission_site = - "emission_site"; - -/* String related to enum lttng_event_rule_kernel_syscall_emission_site. */ -const char *const - mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit = - "entry+exit"; -const char - *const mi_lttng_event_rule_kernel_syscall_emission_site_entry = - "entry"; -const char *const - mi_lttng_event_rule_kernel_syscall_emission_site_exit = "exit"; - -/* String related to lttng_event_rule_user_tracepoint */ -const char *const - mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions = - "name_pattern_exclusions"; -const char *const - mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion = - "name_pattern_exclusion"; - -/* String related to log level rule. */ -const char *const mi_lttng_element_log_level_rule = - "log_level_rule"; -const char *const mi_lttng_element_log_level_rule_exactly = - "log_level_rule_exactly"; -const char - *const mi_lttng_element_log_level_rule_at_least_as_severe_as = - "log_level_rule_at_least_as_severe_as"; -const char *const mi_lttng_element_log_level_rule_level = "level"; - -/* String related to kernel probe location. */ -const char *const mi_lttng_element_kernel_probe_location = - "kernel_probe_location"; -const char - *const mi_lttng_element_kernel_probe_location_symbol_offset = - "kernel_probe_location_symbol_offset"; -const char *const - mi_lttng_element_kernel_probe_location_symbol_offset_name = - "name"; -const char *const - mi_lttng_element_kernel_probe_location_symbol_offset_offset = - "offset"; - -const char *const mi_lttng_element_kernel_probe_location_address = - "kernel_probe_location_address"; -const char - *const mi_lttng_element_kernel_probe_location_address_address = - "address"; - -/* String related to userspace probe location. */ -const char *const mi_lttng_element_userspace_probe_location = - "userspace_probe_location"; -const char - *const mi_lttng_element_userspace_probe_location_binary_path = - "binary_path"; -const char - *const mi_lttng_element_userspace_probe_location_function = - "userspace_probe_location_function"; -const char - *const mi_lttng_element_userspace_probe_location_function_name = - "name"; -const char - *const mi_lttng_element_userspace_probe_location_lookup_method = - "userspace_probe_location_lookup_method"; -const char *const - mi_lttng_element_userspace_probe_location_lookup_method_function_default = - "userspace_probe_location_lookup_method_function_default"; -const char *const - mi_lttng_element_userspace_probe_location_lookup_method_function_elf = - "userspace_probe_location_lookup_method_function_elf"; -const char *const - mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt = - "userspace_probe_location_lookup_method_tracepoint_sdt"; -const char - *const mi_lttng_element_userspace_probe_location_tracepoint = - "userspace_probe_location_tracepoint"; -const char *const - mi_lttng_element_userspace_probe_location_tracepoint_probe_name = - "probe_name"; -const char *const - mi_lttng_element_userspace_probe_location_tracepoint_provider_name = - "provider_name"; - -/* String related to enum - * lttng_userspace_probe_location_function_instrumentation_type */ -const char *const - mi_lttng_element_userspace_probe_location_function_instrumentation_type = - "instrumentation_type"; -const char *const - mi_lttng_userspace_probe_location_function_instrumentation_type_entry = - "ENTRY"; - -/* String related to trigger */ -const char *const mi_lttng_element_triggers = "triggers"; -const char *const mi_lttng_element_trigger = "trigger"; -const char *const mi_lttng_element_trigger_owner_uid = "owner_uid"; - -/* String related to error_query. */ -const char *const mi_lttng_element_error_query_result = - "error_query_result"; -const char *const mi_lttng_element_error_query_result_counter = - "error_query_result_counter"; -const char *const - mi_lttng_element_error_query_result_counter_value = "value"; -const char *const mi_lttng_element_error_query_result_description = - "description"; -const char *const mi_lttng_element_error_query_result_name = - "name"; -const char *const mi_lttng_element_error_query_result_type = - "type"; -const char *const mi_lttng_element_error_query_results = - "error_query_results"; - -/* String related to add-context command */ -const char * const mi_lttng_element_context_symbol = "symbol"; - -/* Deprecated symbols preserved for ABI compatibility. */ -LTTNG_EXPORT const char * const mi_lttng_context_type_perf_counter; -LTTNG_EXPORT const char * const mi_lttng_context_type_perf_cpu_counter; -LTTNG_EXPORT const char * const mi_lttng_context_type_perf_thread_counter; -LTTNG_EXPORT const char * const mi_lttng_element_track_untrack_pid_target; -LTTNG_EXPORT const char * const mi_lttng_element_track_untrack_targets; -LTTNG_EXPORT const char * const mi_lttng_element_calibrate; -LTTNG_EXPORT const char * const mi_lttng_element_calibrate_function; -LTTNG_EXPORT const char * const mi_lttng_element_command_calibrate; - -/* This is a merge of jul loglevel and regular loglevel - * Those should never overlap by definition - * (see struct lttng_event loglevel) - */ -const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain) -{ - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - case LTTNG_DOMAIN_UST: - switch (value) { - case -1: - return mi_lttng_element_empty; - case LTTNG_LOGLEVEL_EMERG: - return mi_lttng_loglevel_str_emerg; - case LTTNG_LOGLEVEL_ALERT: - return mi_lttng_loglevel_str_alert; - case LTTNG_LOGLEVEL_CRIT: - return mi_lttng_loglevel_str_crit; - case LTTNG_LOGLEVEL_ERR: - return mi_lttng_loglevel_str_err; - case LTTNG_LOGLEVEL_WARNING: - return mi_lttng_loglevel_str_warning; - case LTTNG_LOGLEVEL_NOTICE: - return mi_lttng_loglevel_str_notice; - case LTTNG_LOGLEVEL_INFO: - return mi_lttng_loglevel_str_info; - case LTTNG_LOGLEVEL_DEBUG_SYSTEM: - return mi_lttng_loglevel_str_debug_system; - case LTTNG_LOGLEVEL_DEBUG_PROGRAM: - return mi_lttng_loglevel_str_debug_program; - case LTTNG_LOGLEVEL_DEBUG_PROCESS: - return mi_lttng_loglevel_str_debug_process; - case LTTNG_LOGLEVEL_DEBUG_MODULE: - return mi_lttng_loglevel_str_debug_module; - case LTTNG_LOGLEVEL_DEBUG_UNIT: - return mi_lttng_loglevel_str_debug_unit; - case LTTNG_LOGLEVEL_DEBUG_FUNCTION: - return mi_lttng_loglevel_str_debug_function; - case LTTNG_LOGLEVEL_DEBUG_LINE: - return mi_lttng_loglevel_str_debug_line; - case LTTNG_LOGLEVEL_DEBUG: - return mi_lttng_loglevel_str_debug; - default: - return mi_lttng_loglevel_str_unknown; - } - break; - case LTTNG_DOMAIN_LOG4J: - switch (value) { - case -1: - return mi_lttng_element_empty; - case LTTNG_LOGLEVEL_LOG4J_OFF: - return mi_lttng_loglevel_str_log4j_off; - case LTTNG_LOGLEVEL_LOG4J_FATAL: - return mi_lttng_loglevel_str_log4j_fatal; - case LTTNG_LOGLEVEL_LOG4J_ERROR: - return mi_lttng_loglevel_str_log4j_error; - case LTTNG_LOGLEVEL_LOG4J_WARN: - return mi_lttng_loglevel_str_log4j_warn; - case LTTNG_LOGLEVEL_LOG4J_INFO: - return mi_lttng_loglevel_str_log4j_info; - case LTTNG_LOGLEVEL_LOG4J_DEBUG: - return mi_lttng_loglevel_str_log4j_debug; - case LTTNG_LOGLEVEL_LOG4J_TRACE: - return mi_lttng_loglevel_str_log4j_trace; - case LTTNG_LOGLEVEL_LOG4J_ALL: - return mi_lttng_loglevel_str_log4j_all; - default: - return mi_lttng_loglevel_str_unknown; - } - break; - case LTTNG_DOMAIN_JUL: - switch (value) { - case -1: - return mi_lttng_element_empty; - case LTTNG_LOGLEVEL_JUL_OFF: - return mi_lttng_loglevel_str_jul_off; - case LTTNG_LOGLEVEL_JUL_SEVERE: - return mi_lttng_loglevel_str_jul_severe; - case LTTNG_LOGLEVEL_JUL_WARNING: - return mi_lttng_loglevel_str_jul_warning; - case LTTNG_LOGLEVEL_JUL_INFO: - return mi_lttng_loglevel_str_jul_info; - case LTTNG_LOGLEVEL_JUL_CONFIG: - return mi_lttng_loglevel_str_jul_config; - case LTTNG_LOGLEVEL_JUL_FINE: - return mi_lttng_loglevel_str_jul_fine; - case LTTNG_LOGLEVEL_JUL_FINER: - return mi_lttng_loglevel_str_jul_finer; - case LTTNG_LOGLEVEL_JUL_FINEST: - return mi_lttng_loglevel_str_jul_finest; - case LTTNG_LOGLEVEL_JUL_ALL: - return mi_lttng_loglevel_str_jul_all; - default: - return mi_lttng_loglevel_str_unknown; - } - break; - case LTTNG_DOMAIN_PYTHON: - switch (value) { - case LTTNG_LOGLEVEL_PYTHON_CRITICAL: - return mi_lttng_loglevel_str_python_critical; - case LTTNG_LOGLEVEL_PYTHON_ERROR: - return mi_lttng_loglevel_str_python_error; - case LTTNG_LOGLEVEL_PYTHON_WARNING: - return mi_lttng_loglevel_str_python_warning; - case LTTNG_LOGLEVEL_PYTHON_INFO: - return mi_lttng_loglevel_str_python_info; - case LTTNG_LOGLEVEL_PYTHON_DEBUG: - return mi_lttng_loglevel_str_python_debug; - case LTTNG_LOGLEVEL_PYTHON_NOTSET: - return mi_lttng_loglevel_str_python_notset; - default: - return mi_lttng_loglevel_str_unknown; - } - break; - default: - return mi_lttng_loglevel_str_unknown; - } -} - -const char *mi_lttng_logleveltype_string(enum lttng_loglevel_type value) -{ - switch (value) { - case LTTNG_EVENT_LOGLEVEL_ALL: - return mi_lttng_loglevel_type_all; - case LTTNG_EVENT_LOGLEVEL_RANGE: - return mi_lttng_loglevel_type_range; - case LTTNG_EVENT_LOGLEVEL_SINGLE: - return mi_lttng_loglevel_type_single; - default: - return mi_lttng_loglevel_type_unknown; - } -} - -static -const char *mi_lttng_eventtype_string(enum lttng_event_type value) -{ - switch (value) { - case LTTNG_EVENT_ALL: - return config_event_type_all; - case LTTNG_EVENT_TRACEPOINT: - return config_event_type_tracepoint; - case LTTNG_EVENT_PROBE: - return config_event_type_probe; - case LTTNG_EVENT_USERSPACE_PROBE: - return config_event_type_userspace_probe; - case LTTNG_EVENT_FUNCTION: - return config_event_type_function; - case LTTNG_EVENT_FUNCTION_ENTRY: - return config_event_type_function_entry; - case LTTNG_EVENT_SYSCALL: - return config_event_type_syscall; - case LTTNG_EVENT_NOOP: - return config_event_type_noop; - default: - return mi_lttng_element_empty; - } -} - -static -const char *mi_lttng_event_contexttype_string(enum lttng_event_context_type val) -{ - switch (val) { - case LTTNG_EVENT_CONTEXT_PID: - return config_event_context_pid; - case LTTNG_EVENT_CONTEXT_PROCNAME: - return config_event_context_procname; - case LTTNG_EVENT_CONTEXT_PRIO: - return config_event_context_prio; - case LTTNG_EVENT_CONTEXT_NICE: - return config_event_context_nice; - case LTTNG_EVENT_CONTEXT_VPID: - return config_event_context_vpid; - case LTTNG_EVENT_CONTEXT_TID: - return config_event_context_tid; - case LTTNG_EVENT_CONTEXT_VTID: - return config_event_context_vtid; - case LTTNG_EVENT_CONTEXT_PPID: - return config_event_context_ppid; - case LTTNG_EVENT_CONTEXT_VPPID: - return config_event_context_vppid; - case LTTNG_EVENT_CONTEXT_PTHREAD_ID: - return config_event_context_pthread_id; - case LTTNG_EVENT_CONTEXT_HOSTNAME: - return config_event_context_hostname; - case LTTNG_EVENT_CONTEXT_IP: - return config_event_context_ip; - case LTTNG_EVENT_CONTEXT_INTERRUPTIBLE: - return config_event_context_interruptible; - case LTTNG_EVENT_CONTEXT_PREEMPTIBLE: - return config_event_context_preemptible; - case LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE: - return config_event_context_need_reschedule; - case LTTNG_EVENT_CONTEXT_MIGRATABLE: - return config_event_context_migratable; - case LTTNG_EVENT_CONTEXT_CALLSTACK_USER: - return config_event_context_callstack_user; - case LTTNG_EVENT_CONTEXT_CALLSTACK_KERNEL: - return config_event_context_callstack_kernel; - case LTTNG_EVENT_CONTEXT_CGROUP_NS: - return config_event_context_cgroup_ns; - case LTTNG_EVENT_CONTEXT_IPC_NS: - return config_event_context_ipc_ns; - case LTTNG_EVENT_CONTEXT_MNT_NS: - return config_event_context_mnt_ns; - case LTTNG_EVENT_CONTEXT_NET_NS: - return config_event_context_net_ns; - case LTTNG_EVENT_CONTEXT_PID_NS: - return config_event_context_pid_ns; - case LTTNG_EVENT_CONTEXT_TIME_NS: - return config_event_context_time_ns; - case LTTNG_EVENT_CONTEXT_USER_NS: - return config_event_context_user_ns; - case LTTNG_EVENT_CONTEXT_UTS_NS: - return config_event_context_uts_ns; - case LTTNG_EVENT_CONTEXT_UID: - return config_event_context_uid; - case LTTNG_EVENT_CONTEXT_EUID: - return config_event_context_euid; - case LTTNG_EVENT_CONTEXT_SUID: - return config_event_context_suid; - case LTTNG_EVENT_CONTEXT_GID: - return config_event_context_gid; - case LTTNG_EVENT_CONTEXT_EGID: - return config_event_context_egid; - case LTTNG_EVENT_CONTEXT_SGID: - return config_event_context_sgid; - case LTTNG_EVENT_CONTEXT_VUID: - return config_event_context_vuid; - case LTTNG_EVENT_CONTEXT_VEUID: - return config_event_context_veuid; - case LTTNG_EVENT_CONTEXT_VSUID: - return config_event_context_vsuid; - case LTTNG_EVENT_CONTEXT_VGID: - return config_event_context_vgid; - case LTTNG_EVENT_CONTEXT_VEGID: - return config_event_context_vegid; - case LTTNG_EVENT_CONTEXT_VSGID: - return config_event_context_vsgid; - default: - return NULL; - } -} - -const char *mi_lttng_eventfieldtype_string(enum lttng_event_field_type val) -{ - switch (val) { - case(LTTNG_EVENT_FIELD_INTEGER): - return mi_lttng_element_type_integer; - case(LTTNG_EVENT_FIELD_ENUM): - return mi_lttng_element_type_enum; - case(LTTNG_EVENT_FIELD_FLOAT): - return mi_lttng_element_type_float; - case(LTTNG_EVENT_FIELD_STRING): - return mi_lttng_element_type_string; - default: - return mi_lttng_element_type_other; - } -} - -const char *mi_lttng_domaintype_string(enum lttng_domain_type value) -{ - switch (value) { - case LTTNG_DOMAIN_KERNEL: - return config_domain_type_kernel; - case LTTNG_DOMAIN_UST: - return config_domain_type_ust; - case LTTNG_DOMAIN_JUL: - return config_domain_type_jul; - case LTTNG_DOMAIN_LOG4J: - return config_domain_type_log4j; - case LTTNG_DOMAIN_PYTHON: - return config_domain_type_python; - default: - /* Should not have an unknown domain */ - abort(); - return NULL; - } -} - -const char *mi_lttng_buffertype_string(enum lttng_buffer_type value) -{ - switch (value) { - case LTTNG_BUFFER_PER_PID: - return config_buffer_type_per_pid; - case LTTNG_BUFFER_PER_UID: - return config_buffer_type_per_uid; - case LTTNG_BUFFER_GLOBAL: - return config_buffer_type_global; - default: - /* Should not have an unknow buffer type */ - abort(); - return NULL; - } -} - -const char *mi_lttng_rotation_state_string(enum lttng_rotation_state value) -{ - switch (value) { - case LTTNG_ROTATION_STATE_ONGOING: - return mi_lttng_rotation_state_str_ongoing; - case LTTNG_ROTATION_STATE_COMPLETED: - return mi_lttng_rotation_state_str_completed; - case LTTNG_ROTATION_STATE_EXPIRED: - return mi_lttng_rotation_state_str_expired; - case LTTNG_ROTATION_STATE_ERROR: - return mi_lttng_rotation_state_str_error; - default: - /* Should not have an unknow rotation state. */ - abort(); - return NULL; - } -} - -const char *mi_lttng_trace_archive_location_relay_protocol_type_string( - enum lttng_trace_archive_location_relay_protocol_type value) -{ - switch (value) { - case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP: - return mi_lttng_rotation_location_relay_protocol_str_tcp; - default: - /* Should not have an unknown relay protocol. */ - abort(); - return NULL; - } -} - -struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type) -{ - struct mi_writer *mi_writer; - - mi_writer = zmalloc(sizeof(struct mi_writer)); - if (!mi_writer) { - PERROR("zmalloc mi_writer_create"); - goto end; - } - if (mi_output_type == LTTNG_MI_XML) { - mi_writer->writer = config_writer_create(fd_output, 0); - if (!mi_writer->writer) { - goto err_destroy; - } - mi_writer->type = LTTNG_MI_XML; - } else { - goto err_destroy; - } - -end: - return mi_writer; - -err_destroy: - free(mi_writer); - return NULL; -} - -int mi_lttng_writer_destroy(struct mi_writer *writer) -{ - int ret; - - if (!writer) { - ret = -EINVAL; - goto end; - } - - ret = config_writer_destroy(writer->writer); - if (ret < 0) { - goto end; - } - - free(writer); -end: - return ret; -} - -int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command) -{ - int ret; - - /* - * A command is always the MI's root node, it must declare the current - * namespace and schema URIs and the schema's version. - */ - ret = config_writer_open_element(writer->writer, - mi_lttng_element_command); - if (ret) { - goto end; - } - - ret = config_writer_write_attribute(writer->writer, - mi_lttng_xmlns, DEFAULT_LTTNG_MI_NAMESPACE); - if (ret) { - goto end; - } - - ret = config_writer_write_attribute(writer->writer, - mi_lttng_xmlns_xsi, mi_lttng_w3_schema_uri); - if (ret) { - goto end; - } - - ret = config_writer_write_attribute(writer->writer, - mi_lttng_schema_location, - mi_lttng_schema_location_uri); - if (ret) { - goto end; - } - - ret = config_writer_write_attribute(writer->writer, - mi_lttng_schema_version, - mi_lttng_schema_version_value); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_command_name, command); -end: - return ret; -} - -int mi_lttng_writer_command_close(struct mi_writer *writer) -{ - return mi_lttng_writer_close_element(writer); -} - -int mi_lttng_writer_open_element(struct mi_writer *writer, - const char *element_name) -{ - return config_writer_open_element(writer->writer, element_name); -} - -int mi_lttng_writer_close_element(struct mi_writer *writer) -{ - return config_writer_close_element(writer->writer); -} - -int mi_lttng_close_multi_element(struct mi_writer *writer, - unsigned int nb_element) -{ - int ret, i; - - if (nb_element < 1) { - ret = 0; - goto end; - } - for (i = 0; i < nb_element; i++) { - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } -end: - return ret; -} - -int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer, - const char *element_name, uint64_t value) -{ - return config_writer_write_element_unsigned_int(writer->writer, - element_name, value); -} - -int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer, - const char *element_name, int64_t value) -{ - return config_writer_write_element_signed_int(writer->writer, - element_name, value); -} - -int mi_lttng_writer_write_element_bool(struct mi_writer *writer, - const char *element_name, int value) -{ - return config_writer_write_element_bool(writer->writer, - element_name, value); -} - -int mi_lttng_writer_write_element_string(struct mi_writer *writer, - const char *element_name, const char *value) -{ - return config_writer_write_element_string(writer->writer, - element_name, value); -} - -int mi_lttng_writer_write_element_double(struct mi_writer *writer, - const char *element_name, - double value) -{ - return config_writer_write_element_double( - writer->writer, element_name, value); -} - -int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version, - const char *lttng_description, const char *lttng_license) -{ - int ret; - - /* Open version */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_version); - if (ret) { - goto end; - } - - /* Version string (contain info like rc etc.) */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_version_str, version->version); - if (ret) { - goto end; - } - - /* Major version number */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_version_major, version->version_major); - if (ret) { - goto end; - } - - /* Minor version number */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_version_minor, version->version_minor); - if (ret) { - goto end; - } - - /* Commit version number */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_version_commit, version->version_commit); - if (ret) { - goto end; - } - - /* Patch number */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_version_patch_level, version->version_patchlevel); - if (ret) { - goto end; - } - - /* Name of the version */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, version->version_name); - if (ret) { - goto end; - } - - /* Description mostly related to beer... */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_version_description, lttng_description); - if (ret) { - goto end; - } - - /* url */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_version_web, version->package_url); - if (ret) { - goto end; - } - - /* License: free as in free beer...no...*speech* */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_version_license, lttng_license); - if (ret) { - goto end; - } - - /* Close version element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_sessions_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, config_element_sessions); -} - -int mi_lttng_session(struct mi_writer *writer, - struct lttng_session *session, int is_open) -{ - int ret; - - LTTNG_ASSERT(session); - - /* Open sessions element */ - ret = mi_lttng_writer_open_element(writer, - config_element_session); - if (ret) { - goto end; - } - - /* Name of the session */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, session->name); - if (ret) { - goto end; - } - - /* Path */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_path, session->path); - if (ret) { - goto end; - } - - /* Enabled ? */ - ret = mi_lttng_writer_write_element_bool(writer, - config_element_enabled, session->enabled); - if (ret) { - goto end; - } - - /* Snapshot mode */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_snapshot_mode, session->snapshot_mode); - if (ret) { - goto end; - } - - /* Live timer interval in usec */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_live_timer_interval, - session->live_timer_interval); - if (ret) { - goto end; - } - - if (!is_open) { - /* Closing session element */ - ret = mi_lttng_writer_close_element(writer); - } -end: - return ret; - -} - -int mi_lttng_domains_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, config_element_domains); -} - -int mi_lttng_domain(struct mi_writer *writer, - struct lttng_domain *domain, int is_open) -{ - int ret = 0; - const char *str_domain; - const char *str_buffer; - - LTTNG_ASSERT(domain); - - /* Open domain element */ - ret = mi_lttng_writer_open_element(writer, config_element_domain); - if (ret) { - goto end; - } - - /* Domain Type */ - str_domain = mi_lttng_domaintype_string(domain->type); - ret = mi_lttng_writer_write_element_string(writer, config_element_type, - str_domain); - if (ret) { - goto end; - } - - /* Buffer Type */ - str_buffer= mi_lttng_buffertype_string(domain->buf_type); - ret = mi_lttng_writer_write_element_string(writer, - config_element_buffer_type, str_buffer); - if (ret) { - goto end; - } - - /* TODO: union attr - * This union is not currently used and was added for - * future ust domain support. - * Date: 25-06-2014 - * */ - - if (!is_open) { - /* Closing domain element */ - ret = mi_lttng_writer_close_element(writer); - } - -end: - return ret; - -} - -int mi_lttng_channels_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, config_element_channels); -} - -int mi_lttng_channel(struct mi_writer *writer, - struct lttng_channel *channel, int is_open) -{ - int ret = 0; - - LTTNG_ASSERT(channel); - - /* Opening channel element */ - ret = mi_lttng_writer_open_element(writer, config_element_channel); - if (ret) { - goto end; - } - - /* Name */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - channel->name); - if (ret) { - goto end; - } - - /* Enabled ? */ - ret = mi_lttng_writer_write_element_bool(writer, - config_element_enabled, channel->enabled); - if (ret) { - goto end; - } - - /* Attribute */ - ret = mi_lttng_channel_attr(writer, &channel->attr); - if (ret) { - goto end; - } - - if (!is_open) { - /* Closing channel element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } -end: - return ret; -} - -int mi_lttng_channel_attr(struct mi_writer *writer, - struct lttng_channel_attr *attr) -{ - int ret = 0; - struct lttng_channel *chan = caa_container_of(attr, - struct lttng_channel, attr); - uint64_t discarded_events, lost_packets, monitor_timer_interval; - int64_t blocking_timeout; - - LTTNG_ASSERT(attr); - - ret = lttng_channel_get_discarded_event_count(chan, &discarded_events); - if (ret) { - goto end; - } - - ret = lttng_channel_get_lost_packet_count(chan, &lost_packets); - if (ret) { - goto end; - } - - ret = lttng_channel_get_monitor_timer_interval(chan, - &monitor_timer_interval); - if (ret) { - goto end; - } - - ret = lttng_channel_get_blocking_timeout(chan, - &blocking_timeout); - if (ret) { - goto end; - } - - /* Opening Attributes */ - ret = mi_lttng_writer_open_element(writer, config_element_attributes); - if (ret) { - goto end; - } - - /* Overwrite */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_overwrite_mode, - attr->overwrite ? config_overwrite_mode_overwrite : - config_overwrite_mode_discard); - if (ret) { - goto end; - } - - /* Sub buffer size in byte */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_subbuf_size, attr->subbuf_size); - if (ret) { - goto end; - } - - /* Number of subbuffer (power of two) */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_num_subbuf, - attr->num_subbuf); - if (ret) { - goto end; - } - - /* Switch timer interval in usec */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_switch_timer_interval, - attr->switch_timer_interval); - if (ret) { - goto end; - } - - /* Read timer interval in usec */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_read_timer_interval, - attr->read_timer_interval); - if (ret) { - goto end; - } - - /* Monitor timer interval in usec */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_monitor_timer_interval, - monitor_timer_interval); - if (ret) { - goto end; - } - - /* Retry timeout in usec */ - ret = mi_lttng_writer_write_element_signed_int(writer, - config_element_blocking_timeout, - blocking_timeout); - if (ret) { - goto end; - } - - /* Event output */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_output_type, - attr->output == LTTNG_EVENT_SPLICE ? - config_output_type_splice : config_output_type_mmap); - if (ret) { - goto end; - } - - /* Tracefile size in bytes */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_tracefile_size, attr->tracefile_size); - if (ret) { - goto end; - } - - /* Count of tracefiles */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_tracefile_count, - attr->tracefile_count); - if (ret) { - goto end; - } - - /* Live timer interval in usec*/ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_live_timer_interval, - attr->live_timer_interval); - if (ret) { - goto end; - } - - /* Discarded events */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_discarded_events, - discarded_events); - if (ret) { - goto end; - } - - /* Lost packets */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_lost_packets, - lost_packets); - if (ret) { - goto end; - } - - /* Closing attributes */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } -end: - return ret; - -} - -int mi_lttng_event_common_attributes(struct mi_writer *writer, - struct lttng_event *event) -{ - int ret; - const char *filter_expression; - - /* Open event element */ - ret = mi_lttng_writer_open_element(writer, config_element_event); - if (ret) { - goto end; - } - - /* Event name */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, event->name); - if (ret) { - goto end; - } - - /* Event type */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_type, mi_lttng_eventtype_string(event->type)); - if (ret) { - goto end; - } - - /* Is event enabled */ - ret = mi_lttng_writer_write_element_bool(writer, - config_element_enabled, event->enabled); - if (ret) { - goto end; - } - - /* Event filter expression */ - ret = lttng_event_get_filter_expression(event, &filter_expression); - if (ret) { - goto end; - } - - if (filter_expression) { - ret = mi_lttng_writer_write_element_string(writer, - config_element_filter_expression, - filter_expression); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static int write_event_exclusions(struct mi_writer *writer, - struct lttng_event *event) -{ - int i; - int ret; - int exclusion_count; - - /* Open event exclusions */ - ret = mi_lttng_writer_open_element(writer, config_element_exclusions); - if (ret) { - goto end; - } - - exclusion_count = lttng_event_get_exclusion_name_count(event); - if (exclusion_count < 0) { - ret = exclusion_count; - goto end; - } - - for (i = 0; i < exclusion_count; i++) { - const char *name; - - ret = lttng_event_get_exclusion_name(event, i, &name); - if (ret) { - /* Close exclusions */ - mi_lttng_writer_close_element(writer); - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - config_element_exclusion, name); - if (ret) { - /* Close exclusions */ - mi_lttng_writer_close_element(writer); - goto end; - } - } - - /* Close exclusions */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_event_tracepoint_loglevel(struct mi_writer *writer, - struct lttng_event *event, enum lttng_domain_type domain) -{ - int ret; - - /* Event loglevel */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_loglevel, - mi_lttng_loglevel_string(event->loglevel, domain)); - if (ret) { - goto end; - } - - /* Log level type */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_loglevel_type, - mi_lttng_logleveltype_string(event->loglevel_type)); - if (ret) { - goto end; - } - - /* Event exclusions */ - ret = write_event_exclusions(writer, event); - -end: - return ret; -} - -int mi_lttng_event_tracepoint_no_loglevel(struct mi_writer *writer, - struct lttng_event *event) -{ - /* event exclusion filter */ - return write_event_exclusions(writer, event); -} - -int mi_lttng_event_function_probe(struct mi_writer *writer, - struct lttng_event *event) -{ - int ret; - - ret = mi_lttng_writer_open_element(writer, config_element_attributes); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes); - if (ret) { - goto end; - } - - if (event->attr.probe.addr != 0) { - /* event probe address */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_address, event->attr.probe.addr); - if (ret) { - goto end; - } - } else { - /* event probe offset */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_offset, event->attr.probe.offset); - if (ret) { - goto end; - } - - /* event probe symbol_name */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_symbol_name, event->attr.probe.symbol_name); - if (ret) { - goto end; - } - } - - /* Close probe_attributes and attributes */ - ret = mi_lttng_close_multi_element(writer, 2); -end: - return ret; -} - -static -int mi_lttng_event_userspace_probe(struct mi_writer *writer, - struct lttng_event *event) -{ - int ret; - const struct lttng_userspace_probe_location *location; - const struct lttng_userspace_probe_location_lookup_method *lookup_method; - enum lttng_userspace_probe_location_lookup_method_type lookup_type; - - location = lttng_event_get_userspace_probe_location(event); - if (!location) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - lookup_method = lttng_userspace_probe_location_get_lookup_method(location); - if (!lookup_method) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method); - - ret = mi_lttng_writer_open_element(writer, config_element_attributes); - if (ret) { - goto end; - } - - switch (lttng_userspace_probe_location_get_type(location)) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - { - const char *function_name; - const char *binary_path; - - ret = mi_lttng_writer_open_element(writer, - config_element_userspace_probe_function_attributes); - if (ret) { - goto end; - } - - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_lookup, - config_element_userspace_probe_lookup_function_elf); - if (ret) { - goto end; - } - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_lookup, - config_element_userspace_probe_lookup_function_default); - if (ret) { - goto end; - } - break; - default: - goto end; - } - - binary_path = lttng_userspace_probe_location_function_get_binary_path(location); - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_location_binary_path, binary_path); - if (ret) { - goto end; - } - - function_name = lttng_userspace_probe_location_function_get_function_name(location); - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_function_location_function_name, - function_name); - if (ret) { - goto end; - } - - break; - } - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - { - const char *probe_name, *provider_name; - const char *binary_path; - - ret = mi_lttng_writer_open_element(writer, - config_element_userspace_probe_function_attributes); - if (ret) { - goto end; - } - - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_lookup, - config_element_userspace_probe_lookup_tracepoint_sdt); - if (ret) { - goto end; - } - break; - default: - goto end; - } - - binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location); - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_location_binary_path, - binary_path); - if (ret) { - goto end; - } - - provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_tracepoint_location_provider_name, - provider_name); - if (ret) { - goto end; - } - - probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); - ret = mi_lttng_writer_write_element_string(writer, - config_element_userspace_probe_tracepoint_location_probe_name, probe_name); - if (ret) { - goto end; - } - break; - } - default: - ERR("Invalid probe type encountered"); - } - /* Close probe_attributes and attributes */ - ret = mi_lttng_close_multi_element(writer, 2); -end: - return ret; -} - -int mi_lttng_event_function_entry(struct mi_writer *writer, - struct lttng_event *event) -{ - int ret; - - ret = mi_lttng_writer_open_element(writer, config_element_attributes); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes); - if (ret) { - goto end; - } - - /* event probe symbol_name */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_symbol_name, event->attr.ftrace.symbol_name); - if (ret) { - goto end; - } - - /* Close function_attributes and attributes */ - ret = mi_lttng_close_multi_element(writer, 2); -end: - return ret; -} - -int mi_lttng_events_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, config_element_events); -} - -int mi_lttng_event(struct mi_writer *writer, - struct lttng_event *event, int is_open, enum lttng_domain_type domain) -{ - int ret; - - ret = mi_lttng_event_common_attributes(writer, event); - if (ret) { - goto end; - } - - switch (event->type) { - case LTTNG_EVENT_TRACEPOINT: - { - if (event->loglevel != -1) { - ret = mi_lttng_event_tracepoint_loglevel(writer, event, domain); - } else { - ret = mi_lttng_event_tracepoint_no_loglevel(writer, event); - } - break; - } - case LTTNG_EVENT_FUNCTION: - /* Fallthrough */ - case LTTNG_EVENT_PROBE: - ret = mi_lttng_event_function_probe(writer, event); - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - ret = mi_lttng_event_function_entry(writer, event); - break; - case LTTNG_EVENT_USERSPACE_PROBE: - ret = mi_lttng_event_userspace_probe(writer, event); - break; - case LTTNG_EVENT_ALL: - /* Fallthrough */ - default: - break; - } - - if (ret) { - goto end; - } - - if (!is_open) { - ret = mi_lttng_writer_close_element(writer); - } - -end: - return ret; -} - -int mi_lttng_trackers_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element( - writer, config_element_process_attr_trackers); -} - -static int get_tracker_elements(enum lttng_process_attr process_attr, - const char **element_process_attr_tracker, - const char **element_process_attr_value) -{ - int ret = 0; - - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_pid; - *element_process_attr_value = - config_element_process_attr_pid_value; - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_vpid; - *element_process_attr_value = - config_element_process_attr_vpid_value; - break; - case LTTNG_PROCESS_ATTR_USER_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_uid; - *element_process_attr_value = - config_element_process_attr_uid_value; - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_vuid; - *element_process_attr_value = - config_element_process_attr_vuid_value; - break; - case LTTNG_PROCESS_ATTR_GROUP_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_gid; - *element_process_attr_value = - config_element_process_attr_gid_value; - break; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - *element_process_attr_tracker = - config_element_process_attr_tracker_vgid; - *element_process_attr_value = - config_element_process_attr_vgid_value; - break; - default: - ret = LTTNG_ERR_SAVE_IO_FAIL; - } - return ret; -} - -int mi_lttng_process_attribute_tracker_open( - struct mi_writer *writer, enum lttng_process_attr process_attr) -{ - int ret; - const char *element_tracker, *element_value; - - ret = get_tracker_elements( - process_attr, &element_tracker, &element_value); - if (ret) { - return ret; - } - - /* Open process attribute tracker element */ - ret = mi_lttng_writer_open_element(writer, element_tracker); - if (ret) { - goto end; - } - - /* Open values element */ - ret = mi_lttng_process_attr_values_open(writer); -end: - return ret; -} - -int mi_lttng_pids_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, config_element_pids); -} - -/* - * TODO: move the listing of pid for user agent to process semantic on - * mi api bump. The use of process element break the mi api. - */ -int mi_lttng_pid(struct mi_writer *writer, - pid_t pid, - const char *name, - int is_open) -{ - int ret; - - /* Open pid process */ - ret = mi_lttng_writer_open_element(writer, config_element_pid); - if (ret) { - goto end; - } - - /* Writing pid number */ - ret = mi_lttng_writer_write_element_signed_int(writer, - mi_lttng_element_pid_id, (int)pid); - if (ret) { - goto end; - } - - /* Writing name of the process */ - if (name) { - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - name); - if (ret) { - goto end; - } - } - - if (!is_open) { - /* Closing Pid */ - ret = mi_lttng_writer_close_element(writer); - } - -end: - return ret; -} - -int mi_lttng_process_attr_values_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element( - writer, config_element_process_attr_values); -} - -int mi_lttng_all_process_attribute_value(struct mi_writer *writer, - enum lttng_process_attr process_attr, - bool is_open) -{ - int ret; - const char *element_id_tracker, *element_target_id; - - ret = get_tracker_elements( - process_attr, &element_id_tracker, &element_target_id); - if (ret) { - return ret; - } - - ret = mi_lttng_writer_open_element(writer, element_target_id); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_type); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_bool(writer, config_element_all, 1); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - - if (!is_open) { - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } -end: - return ret; -} - -int mi_lttng_integral_process_attribute_value(struct mi_writer *writer, - enum lttng_process_attr process_attr, - int64_t value, - bool is_open) -{ - int ret; - const char *element_id_tracker, *element_target_id; - - ret = get_tracker_elements( - process_attr, &element_id_tracker, &element_target_id); - if (ret) { - return ret; - } - - ret = mi_lttng_writer_open_element(writer, element_target_id); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_type); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_signed_int( - writer, config_element_process_attr_id, value); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - - if (!is_open) { - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -int mi_lttng_string_process_attribute_value(struct mi_writer *writer, - enum lttng_process_attr process_attr, - const char *value, - bool is_open) - -{ - int ret; - const char *element_id_tracker, *element_target_id; - - ret = get_tracker_elements( - process_attr, &element_id_tracker, &element_target_id); - if (ret) { - return ret; - } - - ret = mi_lttng_writer_open_element(writer, element_target_id); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, config_element_type); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string( - writer, config_element_name, value); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - - if (!is_open) { - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -int mi_lttng_event_fields_open(struct mi_writer *writer) -{ - return mi_lttng_writer_open_element(writer, mi_lttng_element_event_fields); -} - -int mi_lttng_event_field(struct mi_writer *writer, - struct lttng_event_field *field) -{ - int ret; - - if (!field->field_name[0]) { - ret = 0; - goto end; - } - - /* Open field */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_field); - if (ret) { - goto end; - } - - if (!field->field_name[0]) { - goto close; - } - - /* Name */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - field->field_name); - if (ret) { - goto end; - } - - /* Type */ - ret = mi_lttng_writer_write_element_string(writer, config_element_type, - mi_lttng_eventfieldtype_string(field->type)); - if (ret) { - goto end; - } - - /* nowrite */ - ret = mi_lttng_writer_write_element_signed_int(writer, - mi_lttng_element_nowrite, field->nowrite); - if (ret) { - goto end; - } - -close: - /* Close field element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_perf_counter_context(struct mi_writer *writer, - struct lttng_event_perf_counter_ctx *perf_context) -{ - int ret; - - /* Open perf_counter_context */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_perf_counter_context); - if (ret) { - goto end; - } - - /* Type */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_type, perf_context->type); - if (ret) { - goto end; - } - - /* Config */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - config_element_config, perf_context->config); - if (ret) { - goto end; - } - - /* Name of the perf counter */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, perf_context->name); - if (ret) { - goto end; - } - - /* Close perf_counter_context */ - ret = mi_lttng_writer_close_element(writer); -end: - return ret; -} - -static -int mi_lttng_app_context(struct mi_writer *writer, - const char *provider_name, const char *ctx_name) -{ - int ret; - - /* Open app */ - ret = mi_lttng_writer_open_element(writer, - config_element_context_app); - if (ret) { - goto end; - } - - /* provider_name */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_context_app_provider_name, - provider_name); - if (ret) { - goto end; - } - - /* ctx_name */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_context_app_ctx_name, ctx_name); - if (ret) { - goto end; - } - - /* Close app */ - ret = mi_lttng_writer_close_element(writer); -end: - return ret; -} - -int mi_lttng_context(struct mi_writer *writer, - struct lttng_event_context *context, int is_open) -{ - int ret; - - /* Open context */ - ret = mi_lttng_writer_open_element(writer , config_element_context); - if (ret) { - goto end; - } - - /* Special case for PERF_*_COUNTER - * print the lttng_event_perf_counter_ctx*/ - switch (context->ctx) { - case LTTNG_EVENT_CONTEXT_PERF_COUNTER: - case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER: - case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER: - { - struct lttng_event_perf_counter_ctx *perf_context = - &context->u.perf_counter; - ret = mi_lttng_perf_counter_context(writer, perf_context); - if (ret) { - goto end; - } - break; - } - case LTTNG_EVENT_CONTEXT_APP_CONTEXT: - { - ret = mi_lttng_app_context(writer, - context->u.app_ctx.provider_name, - context->u.app_ctx.ctx_name); - if (ret) { - goto end; - } - break; - } - default: - { - const char *type_string = mi_lttng_event_contexttype_string( - context->ctx); - if (!type_string) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - /* Print context type */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_type, type_string); - break; - } - } - - /* Close context */ - if (!is_open) { - ret = mi_lttng_writer_close_element(writer); - } - -end: - return ret; -} - -int mi_lttng_snapshot_output_session_name(struct mi_writer *writer, - const char *session_name) -{ - int ret; - - /* Open session element */ - ret = mi_lttng_writer_open_element(writer, config_element_session); - if (ret) { - goto end; - } - - /* Snapshot output list for current session name */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - session_name); - if (ret) { - goto end; - } - - /* Open element snapshots (sequence one snapshot) */ - ret = mi_lttng_writer_open_element(writer, mi_lttng_element_snapshots); - if (ret) { - goto end; - } - -end: - return ret; -} - -int mi_lttng_snapshot_list_output(struct mi_writer *writer, - const struct lttng_snapshot_output *output) -{ - int ret; - - /* Open element snapshot output */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_snapshot); - if (ret) { - goto end; - } - - /* ID of the snapshot output */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_id, output->id); - if (ret) { - goto end; - } - - /* Name of the output */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - output->name); - if (ret) { - goto end; - } - - /* Destination of the output (ctrl_url)*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); - if (ret) { - goto end; - } - - /* Destination of the output (data_url) */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_data_url, output->data_url); - if (ret) { - goto end; - } - - /* total size of all stream combined */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_snapshot_max_size, output->max_size); - if (ret) { - goto end; - } - - /* Close snapshot output element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_snapshot_del_output(struct mi_writer *writer, int id, - const char *name, const char *current_session_name) -{ - int ret; - - /* Open element del_snapshot */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_snapshot); - if (ret) { - goto end; - } - - - if (id != UINT32_MAX) { - /* "Snapshot output "id" successfully deleted - * for "current_session_name" - * ID of the snapshot output - */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_id, id); - if (ret) { - goto end; - } - } else { - /* "Snapshot output "name" successfully deleted - * for session "current_session_name" - * Name of the output - */ - ret = mi_lttng_writer_write_element_string(writer, config_element_name, - name); - if (ret) { - goto end; - } - } - - /* Snapshot was deleted for session "current_session_name"*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_session_name, - current_session_name); - if (ret) { - goto end; - } - - /* Close snapshot element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_snapshot_add_output(struct mi_writer *writer, - const char *current_session_name, const char *n_ptr, - struct lttng_snapshot_output *output) -{ - int ret; - - /* Open element snapshot */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_snapshot); - if (ret) { - goto end; - } - - /* Snapshot output id */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_id, output->id); - if (ret) { - goto end; - } - - /* Snapshot output names */ - ret = mi_lttng_writer_write_element_string(writer, - config_element_name, n_ptr); - if (ret) { - goto end; - } - - /* Destination of the output (ctrl_url)*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); - if (ret) { - goto end; - } - - /* Snapshot added for session "current_session_name"*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_session_name, current_session_name); - if (ret) { - goto end; - } - - /* total size of all stream combined */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_snapshot_max_size, output->max_size); - if (ret) { - goto end; - } - - /* Close snapshot element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_snapshot_record(struct mi_writer *writer, - const char *current_session_name, const char *url, - const char *cmdline_ctrl_url, const char *cmdline_data_url) -{ - int ret; - - /* Open element snapshot */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_command_snapshot); - if (ret) { - goto end; - } - - /* - * If a valid an URL was given, serialize it, - * else take the command line data and ctrl urls*/ - if (url) { - /* Destination of the output (ctrl_url)*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_ctrl_url, url); - if (ret) { - goto end; - } - } else if (cmdline_ctrl_url) { - /* Destination of the output (ctrl_url)*/ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_ctrl_url, cmdline_ctrl_url); - if (ret) { - goto end; - } - - /* Destination of the output (data_url) */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_data_url, cmdline_data_url); - if (ret) { - goto end; - } - } - - /* Close record_snapshot element */ - ret = mi_lttng_writer_close_element(writer); - -end: - return ret; -} - -int mi_lttng_rotation_schedule(struct mi_writer *writer, - const struct lttng_rotation_schedule *schedule) -{ - int ret = 0; - enum lttng_rotation_status status; - uint64_t value; - const char *element_name; - const char *value_name; - bool empty_schedule = false; - - switch (lttng_rotation_schedule_get_type(schedule)) { - case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: - status = lttng_rotation_schedule_periodic_get_period(schedule, - &value); - element_name = mi_lttng_element_rotation_schedule_periodic; - value_name = mi_lttng_element_rotation_schedule_periodic_time_us; - break; - case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: - status = lttng_rotation_schedule_size_threshold_get_threshold( - schedule, &value); - element_name = mi_lttng_element_rotation_schedule_size_threshold; - value_name = mi_lttng_element_rotation_schedule_size_threshold_bytes; - break; - default: - ret = -1; - goto end; - } - - if (status != LTTNG_ROTATION_STATUS_OK) { - if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) { - empty_schedule = true; - } else { - ret = -1; - goto end; - } - } - - ret = mi_lttng_writer_open_element(writer, element_name); - if (ret) { - goto end; - } - - if (!empty_schedule) { - ret = mi_lttng_writer_write_element_unsigned_int(writer, - value_name, value); - if (ret) { - goto end; - } - } - - /* Close schedule descriptor element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } -end: - return ret; -} - -int mi_lttng_rotation_schedule_result(struct mi_writer *writer, - const struct lttng_rotation_schedule *schedule, - bool success) -{ - int ret = 0; - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_schedule_result); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_schedule); - if (ret) { - goto end; - } - - ret = mi_lttng_rotation_schedule(writer, schedule); - if (ret) { - goto end; - } - - /* Close rotation_schedule element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_bool(writer, - mi_lttng_element_command_success, success); - if (ret) { - goto end; - } - - /* Close rotation_schedule_result element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } -end: - return ret; -} - -static -int mi_lttng_location(struct mi_writer *writer, - const struct lttng_trace_archive_location *location) -{ - int ret = 0; - enum lttng_trace_archive_location_type location_type; - enum lttng_trace_archive_location_status status; - - location_type = lttng_trace_archive_location_get_type(location); - - switch (location_type) { - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: - { - const char *absolute_path; - - status = lttng_trace_archive_location_local_get_absolute_path( - location, &absolute_path); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_location_local); - if (ret) { - goto end; - } - - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_rotation_location_local_absolute_path, - absolute_path); - if (ret) { - goto end; - } - - /* Close local element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - break; - } - case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: - { - uint16_t control_port, data_port; - const char *host, *relative_path; - enum lttng_trace_archive_location_relay_protocol_type protocol; - - /* Fetch all relay location parameters. */ - status = lttng_trace_archive_location_relay_get_protocol_type( - location, &protocol); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_host( - location, &host); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_control_port( - location, &control_port); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_data_port( - location, &data_port); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - status = lttng_trace_archive_location_relay_get_relative_path( - location, &relative_path); - if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { - ret = -1; - goto end; - } - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_location_relay); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_rotation_location_relay_host, - host); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_rotation_location_relay_control_port, - control_port); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_rotation_location_relay_data_port, - data_port); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_rotation_location_relay_protocol, - mi_lttng_trace_archive_location_relay_protocol_type_string(protocol)); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_rotation_location_relay_relative_path, - relative_path); - if (ret) { - goto end; - } - - /* Close relay element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - break; - } - default: - abort(); - } -end: - return ret; -} - -int mi_lttng_rotate(struct mi_writer *writer, - const char *session_name, - enum lttng_rotation_state rotation_state, - const struct lttng_trace_archive_location *location) -{ - int ret; - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_session_name, - session_name); - if (ret) { - goto end; - } - - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_rotation_state, - mi_lttng_rotation_state_string(rotation_state)); - if (ret) { - goto end; - } - - if (!location) { - /* Not a serialization error. */ - goto close_rotation; - } - - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_rotation_location); - if (ret) { - goto end; - } - - ret = mi_lttng_location(writer, location); - if (ret) { - goto close_location; - } - -close_location: - /* Close location element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } - -close_rotation: - /* Close rotation element */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto end; - } -end: - return ret; -} diff --git a/src/common/mi-lttng.cpp b/src/common/mi-lttng.cpp new file mode 100644 index 000000000..d41a3cadc --- /dev/null +++ b/src/common/mi-lttng.cpp @@ -0,0 +1,2718 @@ +/* + * Copyright (C) 2014 Jonathan Rajotte + * Copyright (C) 2014 Olivier Cotte + * Copyright (C) 2016 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "lttng/tracker.h" +#define _LGPL_SOURCE +#include "mi-lttng.h" +#include +#include +#include +#include +#include + + +#define MI_SCHEMA_MAJOR_VERSION 4 +#define MI_SCHEMA_MINOR_VERSION 1 + +/* Machine interface namespace URI */ +const char * const mi_lttng_xmlns = "xmlns"; +const char * const mi_lttng_xmlns_xsi = "xmlns:xsi"; +const char * const mi_lttng_w3_schema_uri = "http://www.w3.org/2001/XMLSchema-instance"; +const char * const mi_lttng_schema_location = "xsi:schemaLocation"; +const char * const mi_lttng_schema_location_uri = + DEFAULT_LTTNG_MI_NAMESPACE " " + "https://lttng.org/xml/schemas/lttng-mi/" XSTR(MI_SCHEMA_MAJOR_VERSION) + "/lttng-mi-" XSTR(MI_SCHEMA_MAJOR_VERSION) "." + XSTR(MI_SCHEMA_MINOR_VERSION) ".xsd"; +const char * const mi_lttng_schema_version = "schemaVersion"; +const char * const mi_lttng_schema_version_value = XSTR(MI_SCHEMA_MAJOR_VERSION) + "." XSTR(MI_SCHEMA_MINOR_VERSION); + +/* Strings related to command */ +const char * const mi_lttng_element_command = "command"; +const char * const mi_lttng_element_command_action = "snapshot_action"; +const char * const mi_lttng_element_command_add_context = "add-context"; +const char *const mi_lttng_element_command_add_trigger = "add-trigger"; +const char * const mi_lttng_element_command_create = "create"; +const char * const mi_lttng_element_command_destroy = "destroy"; +const char * const mi_lttng_element_command_disable_channel = "disable-channel"; +const char * const mi_lttng_element_command_disable_event = "disable-event"; +const char * const mi_lttng_element_command_enable_channels = "enable-channel"; +const char * const mi_lttng_element_command_enable_event = "enable-event"; +const char * const mi_lttng_element_command_list = "list"; +const char *const mi_lttng_element_command_list_trigger = "list-trigger"; +const char * const mi_lttng_element_command_load = "load"; +const char * const mi_lttng_element_command_metadata = "metadata"; +const char * const mi_lttng_element_command_metadata_action = "metadata_action"; +const char * const mi_lttng_element_command_regenerate = "regenerate"; +const char * const mi_lttng_element_command_regenerate_action = "regenerate_action"; +const char * const mi_lttng_element_command_name = "name"; +const char * const mi_lttng_element_command_output = "output"; +const char *const mi_lttng_element_command_remove_trigger = "remove-trigger"; +const char * const mi_lttng_element_command_save = "save"; +const char * const mi_lttng_element_command_set_session = "set-session"; +const char * const mi_lttng_element_command_snapshot = "snapshot"; +const char * const mi_lttng_element_command_snapshot_add = "add_snapshot"; +const char * const mi_lttng_element_command_snapshot_del = "del_snapshot"; +const char * const mi_lttng_element_command_snapshot_list = "list_snapshot"; +const char * const mi_lttng_element_command_snapshot_record = "record_snapshot"; +const char * const mi_lttng_element_command_start = "start"; +const char * const mi_lttng_element_command_stop = "stop"; +const char * const mi_lttng_element_command_success = "success"; +const char * const mi_lttng_element_command_track = "track"; +const char * const mi_lttng_element_command_untrack = "untrack"; +const char * const mi_lttng_element_command_version = "version"; +const char * const mi_lttng_element_command_rotate = "rotate"; +const char * const mi_lttng_element_command_enable_rotation = "enable-rotation"; +const char * const mi_lttng_element_command_disable_rotation = "disable-rotation"; +const char * const mi_lttng_element_command_clear = "clear"; + +/* Strings related to version command */ +const char * const mi_lttng_element_version = "version"; +const char * const mi_lttng_element_version_commit = "commit"; +const char * const mi_lttng_element_version_description = "description"; +const char * const mi_lttng_element_version_license = "license"; +const char * const mi_lttng_element_version_major = "major"; +const char * const mi_lttng_element_version_minor = "minor"; +const char * const mi_lttng_element_version_patch_level = "patchLevel"; +const char * const mi_lttng_element_version_str = "string"; +const char * const mi_lttng_element_version_web = "url"; + +/* String related to a lttng_event_field */ +const char * const mi_lttng_element_event_field = "event_field"; +const char * const mi_lttng_element_event_fields = "event_fields"; + +/* String related to lttng_event_perf_counter_ctx */ +const char * const mi_lttng_element_perf_counter_context = "perf"; + +/* Strings related to pid */ +const char * const mi_lttng_element_pid_id = "id"; + +/* Strings related to save command */ +const char * const mi_lttng_element_save = "save"; + +/* Strings related to load command */ +const char * const mi_lttng_element_load = "load"; +const char * const mi_lttng_element_load_overrides = "overrides"; +const char * const mi_lttng_element_load_override_url = "url"; + +/* General elements of mi_lttng */ +const char * const mi_lttng_element_empty = ""; +const char * const mi_lttng_element_id = "id"; +const char * const mi_lttng_element_nowrite = "nowrite"; +const char * const mi_lttng_element_success = "success"; +const char * const mi_lttng_element_type_enum = "ENUM"; +const char * const mi_lttng_element_type_float = "FLOAT"; +const char * const mi_lttng_element_type_integer = "INTEGER"; +const char * const mi_lttng_element_type_other = "OTHER"; +const char * const mi_lttng_element_type_string = "STRING"; + +/* String related to loglevel */ +const char * const mi_lttng_loglevel_str_alert = "TRACE_ALERT"; +const char * const mi_lttng_loglevel_str_crit = "TRACE_CRIT"; +const char * const mi_lttng_loglevel_str_debug = "TRACE_DEBUG"; +const char * const mi_lttng_loglevel_str_debug_function = "TRACE_DEBUG_FUNCTION"; +const char * const mi_lttng_loglevel_str_debug_line = "TRACE_DEBUG_LINE"; +const char * const mi_lttng_loglevel_str_debug_module = "TRACE_DEBUG_MODULE"; +const char * const mi_lttng_loglevel_str_debug_process = "TRACE_DEBUG_PROCESS"; +const char * const mi_lttng_loglevel_str_debug_program = "TRACE_DEBUG_PROGRAM"; +const char * const mi_lttng_loglevel_str_debug_system = "TRACE_DEBUG_SYSTEM"; +const char * const mi_lttng_loglevel_str_debug_unit = "TRACE_DEBUG_UNIT"; +const char * const mi_lttng_loglevel_str_emerg = "TRACE_EMERG"; +const char * const mi_lttng_loglevel_str_err = "TRACE_ERR"; +const char * const mi_lttng_loglevel_str_info = "TRACE_INFO"; +const char * const mi_lttng_loglevel_str_notice = "TRACE_NOTICE"; +const char * const mi_lttng_loglevel_str_unknown = "UNKNOWN"; +const char * const mi_lttng_loglevel_str_warning = "TRACE_WARNING"; + +/* String related to loglevel JUL */ +const char * const mi_lttng_loglevel_str_jul_all = "JUL_ALL"; +const char * const mi_lttng_loglevel_str_jul_config = "JUL_CONFIG"; +const char * const mi_lttng_loglevel_str_jul_fine = "JUL_FINE"; +const char * const mi_lttng_loglevel_str_jul_finer = "JUL_FINER"; +const char * const mi_lttng_loglevel_str_jul_finest = "JUL_FINEST"; +const char * const mi_lttng_loglevel_str_jul_info = "JUL_INFO"; +const char * const mi_lttng_loglevel_str_jul_off = "JUL_OFF"; +const char * const mi_lttng_loglevel_str_jul_severe = "JUL_SEVERE"; +const char * const mi_lttng_loglevel_str_jul_warning = "JUL_WARNING"; + +/* String related to loglevel LOG4J */ +const char * const mi_lttng_loglevel_str_log4j_off = "LOG4J_OFF"; +const char * const mi_lttng_loglevel_str_log4j_fatal = "LOG4J_FATAL"; +const char * const mi_lttng_loglevel_str_log4j_error = "LOG4J_ERROR"; +const char * const mi_lttng_loglevel_str_log4j_warn = "LOG4J_WARN"; +const char * const mi_lttng_loglevel_str_log4j_info = "LOG4J_INFO"; +const char * const mi_lttng_loglevel_str_log4j_debug = "LOG4J_DEBUG"; +const char * const mi_lttng_loglevel_str_log4j_trace = "LOG4J_TRACE"; +const char * const mi_lttng_loglevel_str_log4j_all = "LOG4J_ALL"; + +/* String related to loglevel Python */ +const char * const mi_lttng_loglevel_str_python_critical = "PYTHON_CRITICAL"; +const char * const mi_lttng_loglevel_str_python_error = "PYTHON_ERROR"; +const char * const mi_lttng_loglevel_str_python_warning = "PYTHON_WARNING"; +const char * const mi_lttng_loglevel_str_python_info = "PYTHON_INFO"; +const char * const mi_lttng_loglevel_str_python_debug = "PYTHON_DEBUG"; +const char * const mi_lttng_loglevel_str_python_notset = "PYTHON_NOTSET"; + +/* String related to loglevel type */ +const char * const mi_lttng_loglevel_type_all = "ALL"; +const char * const mi_lttng_loglevel_type_range = "RANGE"; +const char * const mi_lttng_loglevel_type_single = "SINGLE"; +const char * const mi_lttng_loglevel_type_unknown = "UNKNOWN"; + +/* String related to a lttng_snapshot_output */ +const char * const mi_lttng_element_snapshot_ctrl_url = "ctrl_url"; +const char * const mi_lttng_element_snapshot_data_url = "data_url"; +const char * const mi_lttng_element_snapshot_max_size = "max_size"; +const char * const mi_lttng_element_snapshot_n_ptr = "n_ptr"; +const char * const mi_lttng_element_snapshot_session_name = "session_name"; +const char * const mi_lttng_element_snapshots = "snapshots"; + +/* String related to track/untrack command */ +const char * const mi_lttng_element_track_untrack_all_wildcard = "*"; + +const char * const mi_lttng_element_session_name = "session_name"; + +/* String related to rotate command */ +const char * const mi_lttng_element_rotation = "rotation"; +const char * const mi_lttng_element_rotate_status = "status"; +const char * const mi_lttng_element_rotation_schedule = "rotation_schedule"; +const char * const mi_lttng_element_rotation_schedules = "rotation_schedules"; +const char * const mi_lttng_element_rotation_schedule_result = "rotation_schedule_result"; +const char * const mi_lttng_element_rotation_schedule_results = "rotation_schedule_results"; +const char * const mi_lttng_element_rotation_schedule_periodic = "periodic"; +const char * const mi_lttng_element_rotation_schedule_periodic_time_us = "time_us"; +const char * const mi_lttng_element_rotation_schedule_size_threshold = "size_threshold"; +const char * const mi_lttng_element_rotation_schedule_size_threshold_bytes = "bytes"; +const char * const mi_lttng_element_rotation_state = "state"; +const char * const mi_lttng_element_rotation_location = "location"; +const char * const mi_lttng_element_rotation_location_local = "local"; +const char * const mi_lttng_element_rotation_location_local_absolute_path = "absolute_path"; +const char * const mi_lttng_element_rotation_location_relay = "relay"; +const char * const mi_lttng_element_rotation_location_relay_host = "host"; +const char * const mi_lttng_element_rotation_location_relay_control_port = "control_port"; +const char * const mi_lttng_element_rotation_location_relay_data_port = "data_port"; +const char * const mi_lttng_element_rotation_location_relay_protocol = "protocol"; +const char * const mi_lttng_element_rotation_location_relay_relative_path = "relative_path"; + +/* String related to enum lttng_rotation_state */ +const char * const mi_lttng_rotation_state_str_ongoing = "ONGOING"; +const char * const mi_lttng_rotation_state_str_completed = "COMPLETED"; +const char * const mi_lttng_rotation_state_str_expired = "EXPIRED"; +const char * const mi_lttng_rotation_state_str_error = "ERROR"; + +/* String related to enum lttng_trace_archive_location_relay_protocol_type */ +const char * const mi_lttng_rotation_location_relay_protocol_str_tcp = "TCP"; + +/* String related to rate_policy elements */ +const char *const mi_lttng_element_rate_policy = "rate_policy"; +const char *const mi_lttng_element_rate_policy_every_n = + "rate_policy_every_n"; +const char *const mi_lttng_element_rate_policy_once_after_n = + "rate_policy_once_after_n"; + +const char *const mi_lttng_element_rate_policy_every_n_interval = + "interval"; +const char + *const mi_lttng_element_rate_policy_once_after_n_threshold = + "threshold"; + +/* String related to action elements */ +const char *const mi_lttng_element_action = "action"; +const char *const mi_lttng_element_action_list = "action_list"; +const char *const mi_lttng_element_action_notify = "action_notify"; +const char *const mi_lttng_element_action_start_session = + "action_start_session"; +const char *const mi_lttng_element_action_stop_session = + "action_stop_session"; +const char *const mi_lttng_element_action_rotate_session = + "action_rotate_session"; +const char *const mi_lttng_element_action_snapshot_session = + "action_snapshot_session"; +const char *const mi_lttng_element_action_snapshot_session_output = + "output"; + +/* String related to condition */ +const char *const mi_lttng_element_condition = "condition"; +const char *const mi_lttng_element_condition_buffer_usage_high = + "condition_buffer_usage_high"; +const char *const mi_lttng_element_condition_buffer_usage_low = + "condition_buffer_usage_low"; +const char *const mi_lttng_element_condition_event_rule_matches = + "condition_event_rule_matches"; +const char *const mi_lttng_element_condition_session_consumed_size = + "condition_session_consumed_size"; +const char *const mi_lttng_element_condition_session_rotation = + "condition_session_rotation"; +const char + *const mi_lttng_element_condition_session_rotation_completed = + "condition_session_rotation_completed"; +const char + *const mi_lttng_element_condition_session_rotation_ongoing = + "condition_session_rotation_ongoing"; + +const char *const mi_lttng_element_condition_channel_name = + "channel_name"; +const char *const mi_lttng_element_condition_threshold_bytes = + "threshold_bytes"; +const char *const mi_lttng_element_condition_threshold_ratio = + "threshold_ratio"; + +/* String related to capture descriptor */ +const char *const mi_lttng_element_capture_descriptor = + "capture_descriptor"; +const char *const mi_lttng_element_capture_descriptors = + "capture_descriptors"; + +/* String related to event expression */ +const char *const mi_lttng_element_event_expr = "event_expr"; +const char *const mi_lttng_element_event_expr_payload_field = + "event_expr_payload_field"; +const char *const mi_lttng_element_event_expr_channel_context_field = + "event_expr_channel_context_field"; +const char + *const mi_lttng_element_event_expr_app_specific_context_field = + "event_expr_app_specific_context_field"; +const char *const mi_lttng_element_event_expr_array_field_element = + "event_expr_array_field_element"; +const char *const mi_lttng_element_event_expr_provider_name = + "provider_name"; +const char *const mi_lttng_element_event_expr_type_name = + "type_name"; +const char *const mi_lttng_element_event_expr_index = "index"; + +/* String related to event rule */ +const char *const mi_lttng_element_event_rule = "event_rule"; + +/* String related to lttng_event_rule_type */ +const char *const mi_lttng_element_event_rule_event_name = + "event_name"; +const char *const mi_lttng_element_event_rule_name_pattern = + "name_pattern"; +const char *const mi_lttng_element_event_rule_filter_expression = + "filter_expression"; + +const char *const mi_lttng_element_event_rule_jul_logging = + "event_rule_jul_logging"; +const char *const mi_lttng_element_event_rule_kernel_kprobe = + "event_rule_kernel_kprobe"; +const char *const mi_lttng_element_event_rule_kernel_syscall = + "event_rule_kernel_syscall"; +const char *const mi_lttng_element_event_rule_kernel_tracepoint = + "event_rule_kernel_tracepoint"; +const char *const mi_lttng_element_event_rule_kernel_uprobe = + "event_rule_kernel_uprobe"; +const char *const mi_lttng_element_event_rule_log4j_logging = + "event_rule_log4j_logging"; +const char *const mi_lttng_element_event_rule_python_logging = + "event_rule_python_logging"; +const char *const mi_lttng_element_event_rule_user_tracepoint = + "event_rule_user_tracepoint"; + +/* String related to lttng_event_rule_kernel_syscall. */ +const char *const + mi_lttng_element_event_rule_kernel_syscall_emission_site = + "emission_site"; + +/* String related to enum lttng_event_rule_kernel_syscall_emission_site. */ +const char *const + mi_lttng_event_rule_kernel_syscall_emission_site_entry_exit = + "entry+exit"; +const char + *const mi_lttng_event_rule_kernel_syscall_emission_site_entry = + "entry"; +const char *const + mi_lttng_event_rule_kernel_syscall_emission_site_exit = "exit"; + +/* String related to lttng_event_rule_user_tracepoint */ +const char *const + mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusions = + "name_pattern_exclusions"; +const char *const + mi_lttng_element_event_rule_user_tracepoint_name_pattern_exclusion = + "name_pattern_exclusion"; + +/* String related to log level rule. */ +const char *const mi_lttng_element_log_level_rule = + "log_level_rule"; +const char *const mi_lttng_element_log_level_rule_exactly = + "log_level_rule_exactly"; +const char + *const mi_lttng_element_log_level_rule_at_least_as_severe_as = + "log_level_rule_at_least_as_severe_as"; +const char *const mi_lttng_element_log_level_rule_level = "level"; + +/* String related to kernel probe location. */ +const char *const mi_lttng_element_kernel_probe_location = + "kernel_probe_location"; +const char + *const mi_lttng_element_kernel_probe_location_symbol_offset = + "kernel_probe_location_symbol_offset"; +const char *const + mi_lttng_element_kernel_probe_location_symbol_offset_name = + "name"; +const char *const + mi_lttng_element_kernel_probe_location_symbol_offset_offset = + "offset"; + +const char *const mi_lttng_element_kernel_probe_location_address = + "kernel_probe_location_address"; +const char + *const mi_lttng_element_kernel_probe_location_address_address = + "address"; + +/* String related to userspace probe location. */ +const char *const mi_lttng_element_userspace_probe_location = + "userspace_probe_location"; +const char + *const mi_lttng_element_userspace_probe_location_binary_path = + "binary_path"; +const char + *const mi_lttng_element_userspace_probe_location_function = + "userspace_probe_location_function"; +const char + *const mi_lttng_element_userspace_probe_location_function_name = + "name"; +const char + *const mi_lttng_element_userspace_probe_location_lookup_method = + "userspace_probe_location_lookup_method"; +const char *const + mi_lttng_element_userspace_probe_location_lookup_method_function_default = + "userspace_probe_location_lookup_method_function_default"; +const char *const + mi_lttng_element_userspace_probe_location_lookup_method_function_elf = + "userspace_probe_location_lookup_method_function_elf"; +const char *const + mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt = + "userspace_probe_location_lookup_method_tracepoint_sdt"; +const char + *const mi_lttng_element_userspace_probe_location_tracepoint = + "userspace_probe_location_tracepoint"; +const char *const + mi_lttng_element_userspace_probe_location_tracepoint_probe_name = + "probe_name"; +const char *const + mi_lttng_element_userspace_probe_location_tracepoint_provider_name = + "provider_name"; + +/* String related to enum + * lttng_userspace_probe_location_function_instrumentation_type */ +const char *const + mi_lttng_element_userspace_probe_location_function_instrumentation_type = + "instrumentation_type"; +const char *const + mi_lttng_userspace_probe_location_function_instrumentation_type_entry = + "ENTRY"; + +/* String related to trigger */ +const char *const mi_lttng_element_triggers = "triggers"; +const char *const mi_lttng_element_trigger = "trigger"; +const char *const mi_lttng_element_trigger_owner_uid = "owner_uid"; + +/* String related to error_query. */ +const char *const mi_lttng_element_error_query_result = + "error_query_result"; +const char *const mi_lttng_element_error_query_result_counter = + "error_query_result_counter"; +const char *const + mi_lttng_element_error_query_result_counter_value = "value"; +const char *const mi_lttng_element_error_query_result_description = + "description"; +const char *const mi_lttng_element_error_query_result_name = + "name"; +const char *const mi_lttng_element_error_query_result_type = + "type"; +const char *const mi_lttng_element_error_query_results = + "error_query_results"; + +/* String related to add-context command */ +const char * const mi_lttng_element_context_symbol = "symbol"; + +/* Deprecated symbols preserved for ABI compatibility. */ +LTTNG_EXPORT const char *mi_lttng_context_type_perf_counter; +LTTNG_EXPORT const char *mi_lttng_context_type_perf_cpu_counter; +LTTNG_EXPORT const char *mi_lttng_context_type_perf_thread_counter; +LTTNG_EXPORT const char *mi_lttng_element_track_untrack_pid_target; +LTTNG_EXPORT const char *mi_lttng_element_track_untrack_targets; +LTTNG_EXPORT const char *mi_lttng_element_calibrate; +LTTNG_EXPORT const char *mi_lttng_element_calibrate_function; +LTTNG_EXPORT const char *mi_lttng_element_command_calibrate; + +/* This is a merge of jul loglevel and regular loglevel + * Those should never overlap by definition + * (see struct lttng_event loglevel) + */ +const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain) +{ + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + switch (value) { + case -1: + return mi_lttng_element_empty; + case LTTNG_LOGLEVEL_EMERG: + return mi_lttng_loglevel_str_emerg; + case LTTNG_LOGLEVEL_ALERT: + return mi_lttng_loglevel_str_alert; + case LTTNG_LOGLEVEL_CRIT: + return mi_lttng_loglevel_str_crit; + case LTTNG_LOGLEVEL_ERR: + return mi_lttng_loglevel_str_err; + case LTTNG_LOGLEVEL_WARNING: + return mi_lttng_loglevel_str_warning; + case LTTNG_LOGLEVEL_NOTICE: + return mi_lttng_loglevel_str_notice; + case LTTNG_LOGLEVEL_INFO: + return mi_lttng_loglevel_str_info; + case LTTNG_LOGLEVEL_DEBUG_SYSTEM: + return mi_lttng_loglevel_str_debug_system; + case LTTNG_LOGLEVEL_DEBUG_PROGRAM: + return mi_lttng_loglevel_str_debug_program; + case LTTNG_LOGLEVEL_DEBUG_PROCESS: + return mi_lttng_loglevel_str_debug_process; + case LTTNG_LOGLEVEL_DEBUG_MODULE: + return mi_lttng_loglevel_str_debug_module; + case LTTNG_LOGLEVEL_DEBUG_UNIT: + return mi_lttng_loglevel_str_debug_unit; + case LTTNG_LOGLEVEL_DEBUG_FUNCTION: + return mi_lttng_loglevel_str_debug_function; + case LTTNG_LOGLEVEL_DEBUG_LINE: + return mi_lttng_loglevel_str_debug_line; + case LTTNG_LOGLEVEL_DEBUG: + return mi_lttng_loglevel_str_debug; + default: + return mi_lttng_loglevel_str_unknown; + } + break; + case LTTNG_DOMAIN_LOG4J: + switch (value) { + case -1: + return mi_lttng_element_empty; + case LTTNG_LOGLEVEL_LOG4J_OFF: + return mi_lttng_loglevel_str_log4j_off; + case LTTNG_LOGLEVEL_LOG4J_FATAL: + return mi_lttng_loglevel_str_log4j_fatal; + case LTTNG_LOGLEVEL_LOG4J_ERROR: + return mi_lttng_loglevel_str_log4j_error; + case LTTNG_LOGLEVEL_LOG4J_WARN: + return mi_lttng_loglevel_str_log4j_warn; + case LTTNG_LOGLEVEL_LOG4J_INFO: + return mi_lttng_loglevel_str_log4j_info; + case LTTNG_LOGLEVEL_LOG4J_DEBUG: + return mi_lttng_loglevel_str_log4j_debug; + case LTTNG_LOGLEVEL_LOG4J_TRACE: + return mi_lttng_loglevel_str_log4j_trace; + case LTTNG_LOGLEVEL_LOG4J_ALL: + return mi_lttng_loglevel_str_log4j_all; + default: + return mi_lttng_loglevel_str_unknown; + } + break; + case LTTNG_DOMAIN_JUL: + switch (value) { + case -1: + return mi_lttng_element_empty; + case LTTNG_LOGLEVEL_JUL_OFF: + return mi_lttng_loglevel_str_jul_off; + case LTTNG_LOGLEVEL_JUL_SEVERE: + return mi_lttng_loglevel_str_jul_severe; + case LTTNG_LOGLEVEL_JUL_WARNING: + return mi_lttng_loglevel_str_jul_warning; + case LTTNG_LOGLEVEL_JUL_INFO: + return mi_lttng_loglevel_str_jul_info; + case LTTNG_LOGLEVEL_JUL_CONFIG: + return mi_lttng_loglevel_str_jul_config; + case LTTNG_LOGLEVEL_JUL_FINE: + return mi_lttng_loglevel_str_jul_fine; + case LTTNG_LOGLEVEL_JUL_FINER: + return mi_lttng_loglevel_str_jul_finer; + case LTTNG_LOGLEVEL_JUL_FINEST: + return mi_lttng_loglevel_str_jul_finest; + case LTTNG_LOGLEVEL_JUL_ALL: + return mi_lttng_loglevel_str_jul_all; + default: + return mi_lttng_loglevel_str_unknown; + } + break; + case LTTNG_DOMAIN_PYTHON: + switch (value) { + case LTTNG_LOGLEVEL_PYTHON_CRITICAL: + return mi_lttng_loglevel_str_python_critical; + case LTTNG_LOGLEVEL_PYTHON_ERROR: + return mi_lttng_loglevel_str_python_error; + case LTTNG_LOGLEVEL_PYTHON_WARNING: + return mi_lttng_loglevel_str_python_warning; + case LTTNG_LOGLEVEL_PYTHON_INFO: + return mi_lttng_loglevel_str_python_info; + case LTTNG_LOGLEVEL_PYTHON_DEBUG: + return mi_lttng_loglevel_str_python_debug; + case LTTNG_LOGLEVEL_PYTHON_NOTSET: + return mi_lttng_loglevel_str_python_notset; + default: + return mi_lttng_loglevel_str_unknown; + } + break; + default: + return mi_lttng_loglevel_str_unknown; + } +} + +const char *mi_lttng_logleveltype_string(enum lttng_loglevel_type value) +{ + switch (value) { + case LTTNG_EVENT_LOGLEVEL_ALL: + return mi_lttng_loglevel_type_all; + case LTTNG_EVENT_LOGLEVEL_RANGE: + return mi_lttng_loglevel_type_range; + case LTTNG_EVENT_LOGLEVEL_SINGLE: + return mi_lttng_loglevel_type_single; + default: + return mi_lttng_loglevel_type_unknown; + } +} + +static +const char *mi_lttng_eventtype_string(enum lttng_event_type value) +{ + switch (value) { + case LTTNG_EVENT_ALL: + return config_event_type_all; + case LTTNG_EVENT_TRACEPOINT: + return config_event_type_tracepoint; + case LTTNG_EVENT_PROBE: + return config_event_type_probe; + case LTTNG_EVENT_USERSPACE_PROBE: + return config_event_type_userspace_probe; + case LTTNG_EVENT_FUNCTION: + return config_event_type_function; + case LTTNG_EVENT_FUNCTION_ENTRY: + return config_event_type_function_entry; + case LTTNG_EVENT_SYSCALL: + return config_event_type_syscall; + case LTTNG_EVENT_NOOP: + return config_event_type_noop; + default: + return mi_lttng_element_empty; + } +} + +static +const char *mi_lttng_event_contexttype_string(enum lttng_event_context_type val) +{ + switch (val) { + case LTTNG_EVENT_CONTEXT_PID: + return config_event_context_pid; + case LTTNG_EVENT_CONTEXT_PROCNAME: + return config_event_context_procname; + case LTTNG_EVENT_CONTEXT_PRIO: + return config_event_context_prio; + case LTTNG_EVENT_CONTEXT_NICE: + return config_event_context_nice; + case LTTNG_EVENT_CONTEXT_VPID: + return config_event_context_vpid; + case LTTNG_EVENT_CONTEXT_TID: + return config_event_context_tid; + case LTTNG_EVENT_CONTEXT_VTID: + return config_event_context_vtid; + case LTTNG_EVENT_CONTEXT_PPID: + return config_event_context_ppid; + case LTTNG_EVENT_CONTEXT_VPPID: + return config_event_context_vppid; + case LTTNG_EVENT_CONTEXT_PTHREAD_ID: + return config_event_context_pthread_id; + case LTTNG_EVENT_CONTEXT_HOSTNAME: + return config_event_context_hostname; + case LTTNG_EVENT_CONTEXT_IP: + return config_event_context_ip; + case LTTNG_EVENT_CONTEXT_INTERRUPTIBLE: + return config_event_context_interruptible; + case LTTNG_EVENT_CONTEXT_PREEMPTIBLE: + return config_event_context_preemptible; + case LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE: + return config_event_context_need_reschedule; + case LTTNG_EVENT_CONTEXT_MIGRATABLE: + return config_event_context_migratable; + case LTTNG_EVENT_CONTEXT_CALLSTACK_USER: + return config_event_context_callstack_user; + case LTTNG_EVENT_CONTEXT_CALLSTACK_KERNEL: + return config_event_context_callstack_kernel; + case LTTNG_EVENT_CONTEXT_CGROUP_NS: + return config_event_context_cgroup_ns; + case LTTNG_EVENT_CONTEXT_IPC_NS: + return config_event_context_ipc_ns; + case LTTNG_EVENT_CONTEXT_MNT_NS: + return config_event_context_mnt_ns; + case LTTNG_EVENT_CONTEXT_NET_NS: + return config_event_context_net_ns; + case LTTNG_EVENT_CONTEXT_PID_NS: + return config_event_context_pid_ns; + case LTTNG_EVENT_CONTEXT_TIME_NS: + return config_event_context_time_ns; + case LTTNG_EVENT_CONTEXT_USER_NS: + return config_event_context_user_ns; + case LTTNG_EVENT_CONTEXT_UTS_NS: + return config_event_context_uts_ns; + case LTTNG_EVENT_CONTEXT_UID: + return config_event_context_uid; + case LTTNG_EVENT_CONTEXT_EUID: + return config_event_context_euid; + case LTTNG_EVENT_CONTEXT_SUID: + return config_event_context_suid; + case LTTNG_EVENT_CONTEXT_GID: + return config_event_context_gid; + case LTTNG_EVENT_CONTEXT_EGID: + return config_event_context_egid; + case LTTNG_EVENT_CONTEXT_SGID: + return config_event_context_sgid; + case LTTNG_EVENT_CONTEXT_VUID: + return config_event_context_vuid; + case LTTNG_EVENT_CONTEXT_VEUID: + return config_event_context_veuid; + case LTTNG_EVENT_CONTEXT_VSUID: + return config_event_context_vsuid; + case LTTNG_EVENT_CONTEXT_VGID: + return config_event_context_vgid; + case LTTNG_EVENT_CONTEXT_VEGID: + return config_event_context_vegid; + case LTTNG_EVENT_CONTEXT_VSGID: + return config_event_context_vsgid; + default: + return NULL; + } +} + +const char *mi_lttng_eventfieldtype_string(enum lttng_event_field_type val) +{ + switch (val) { + case(LTTNG_EVENT_FIELD_INTEGER): + return mi_lttng_element_type_integer; + case(LTTNG_EVENT_FIELD_ENUM): + return mi_lttng_element_type_enum; + case(LTTNG_EVENT_FIELD_FLOAT): + return mi_lttng_element_type_float; + case(LTTNG_EVENT_FIELD_STRING): + return mi_lttng_element_type_string; + default: + return mi_lttng_element_type_other; + } +} + +const char *mi_lttng_domaintype_string(enum lttng_domain_type value) +{ + switch (value) { + case LTTNG_DOMAIN_KERNEL: + return config_domain_type_kernel; + case LTTNG_DOMAIN_UST: + return config_domain_type_ust; + case LTTNG_DOMAIN_JUL: + return config_domain_type_jul; + case LTTNG_DOMAIN_LOG4J: + return config_domain_type_log4j; + case LTTNG_DOMAIN_PYTHON: + return config_domain_type_python; + default: + /* Should not have an unknown domain */ + abort(); + return NULL; + } +} + +const char *mi_lttng_buffertype_string(enum lttng_buffer_type value) +{ + switch (value) { + case LTTNG_BUFFER_PER_PID: + return config_buffer_type_per_pid; + case LTTNG_BUFFER_PER_UID: + return config_buffer_type_per_uid; + case LTTNG_BUFFER_GLOBAL: + return config_buffer_type_global; + default: + /* Should not have an unknow buffer type */ + abort(); + return NULL; + } +} + +const char *mi_lttng_rotation_state_string(enum lttng_rotation_state value) +{ + switch (value) { + case LTTNG_ROTATION_STATE_ONGOING: + return mi_lttng_rotation_state_str_ongoing; + case LTTNG_ROTATION_STATE_COMPLETED: + return mi_lttng_rotation_state_str_completed; + case LTTNG_ROTATION_STATE_EXPIRED: + return mi_lttng_rotation_state_str_expired; + case LTTNG_ROTATION_STATE_ERROR: + return mi_lttng_rotation_state_str_error; + default: + /* Should not have an unknow rotation state. */ + abort(); + return NULL; + } +} + +const char *mi_lttng_trace_archive_location_relay_protocol_type_string( + enum lttng_trace_archive_location_relay_protocol_type value) +{ + switch (value) { + case LTTNG_TRACE_ARCHIVE_LOCATION_RELAY_PROTOCOL_TYPE_TCP: + return mi_lttng_rotation_location_relay_protocol_str_tcp; + default: + /* Should not have an unknown relay protocol. */ + abort(); + return NULL; + } +} + +struct mi_writer *mi_lttng_writer_create(int fd_output, int mi_output_type) +{ + struct mi_writer *mi_writer; + + mi_writer = (struct mi_writer *) zmalloc(sizeof(struct mi_writer)); + if (!mi_writer) { + PERROR("zmalloc mi_writer_create"); + goto end; + } + if (mi_output_type == LTTNG_MI_XML) { + mi_writer->writer = config_writer_create(fd_output, 0); + if (!mi_writer->writer) { + goto err_destroy; + } + mi_writer->type = LTTNG_MI_XML; + } else { + goto err_destroy; + } + +end: + return mi_writer; + +err_destroy: + free(mi_writer); + return NULL; +} + +int mi_lttng_writer_destroy(struct mi_writer *writer) +{ + int ret; + + if (!writer) { + ret = -EINVAL; + goto end; + } + + ret = config_writer_destroy(writer->writer); + if (ret < 0) { + goto end; + } + + free(writer); +end: + return ret; +} + +int mi_lttng_writer_command_open(struct mi_writer *writer, const char *command) +{ + int ret; + + /* + * A command is always the MI's root node, it must declare the current + * namespace and schema URIs and the schema's version. + */ + ret = config_writer_open_element(writer->writer, + mi_lttng_element_command); + if (ret) { + goto end; + } + + ret = config_writer_write_attribute(writer->writer, + mi_lttng_xmlns, DEFAULT_LTTNG_MI_NAMESPACE); + if (ret) { + goto end; + } + + ret = config_writer_write_attribute(writer->writer, + mi_lttng_xmlns_xsi, mi_lttng_w3_schema_uri); + if (ret) { + goto end; + } + + ret = config_writer_write_attribute(writer->writer, + mi_lttng_schema_location, + mi_lttng_schema_location_uri); + if (ret) { + goto end; + } + + ret = config_writer_write_attribute(writer->writer, + mi_lttng_schema_version, + mi_lttng_schema_version_value); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_command_name, command); +end: + return ret; +} + +int mi_lttng_writer_command_close(struct mi_writer *writer) +{ + return mi_lttng_writer_close_element(writer); +} + +int mi_lttng_writer_open_element(struct mi_writer *writer, + const char *element_name) +{ + return config_writer_open_element(writer->writer, element_name); +} + +int mi_lttng_writer_close_element(struct mi_writer *writer) +{ + return config_writer_close_element(writer->writer); +} + +int mi_lttng_close_multi_element(struct mi_writer *writer, + unsigned int nb_element) +{ + int ret, i; + + if (nb_element < 1) { + ret = 0; + goto end; + } + for (i = 0; i < nb_element; i++) { + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } +end: + return ret; +} + +int mi_lttng_writer_write_element_unsigned_int(struct mi_writer *writer, + const char *element_name, uint64_t value) +{ + return config_writer_write_element_unsigned_int(writer->writer, + element_name, value); +} + +int mi_lttng_writer_write_element_signed_int(struct mi_writer *writer, + const char *element_name, int64_t value) +{ + return config_writer_write_element_signed_int(writer->writer, + element_name, value); +} + +int mi_lttng_writer_write_element_bool(struct mi_writer *writer, + const char *element_name, int value) +{ + return config_writer_write_element_bool(writer->writer, + element_name, value); +} + +int mi_lttng_writer_write_element_string(struct mi_writer *writer, + const char *element_name, const char *value) +{ + return config_writer_write_element_string(writer->writer, + element_name, value); +} + +int mi_lttng_writer_write_element_double(struct mi_writer *writer, + const char *element_name, + double value) +{ + return config_writer_write_element_double( + writer->writer, element_name, value); +} + +int mi_lttng_version(struct mi_writer *writer, struct mi_lttng_version_data *version, + const char *lttng_description, const char *lttng_license) +{ + int ret; + + /* Open version */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_version); + if (ret) { + goto end; + } + + /* Version string (contain info like rc etc.) */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_version_str, version->version); + if (ret) { + goto end; + } + + /* Major version number */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_version_major, version->version_major); + if (ret) { + goto end; + } + + /* Minor version number */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_version_minor, version->version_minor); + if (ret) { + goto end; + } + + /* Commit version number */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_version_commit, version->version_commit); + if (ret) { + goto end; + } + + /* Patch number */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_version_patch_level, version->version_patchlevel); + if (ret) { + goto end; + } + + /* Name of the version */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, version->version_name); + if (ret) { + goto end; + } + + /* Description mostly related to beer... */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_version_description, lttng_description); + if (ret) { + goto end; + } + + /* url */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_version_web, version->package_url); + if (ret) { + goto end; + } + + /* License: free as in free beer...no...*speech* */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_version_license, lttng_license); + if (ret) { + goto end; + } + + /* Close version element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_sessions_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, config_element_sessions); +} + +int mi_lttng_session(struct mi_writer *writer, + struct lttng_session *session, int is_open) +{ + int ret; + + LTTNG_ASSERT(session); + + /* Open sessions element */ + ret = mi_lttng_writer_open_element(writer, + config_element_session); + if (ret) { + goto end; + } + + /* Name of the session */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, session->name); + if (ret) { + goto end; + } + + /* Path */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_path, session->path); + if (ret) { + goto end; + } + + /* Enabled ? */ + ret = mi_lttng_writer_write_element_bool(writer, + config_element_enabled, session->enabled); + if (ret) { + goto end; + } + + /* Snapshot mode */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_snapshot_mode, session->snapshot_mode); + if (ret) { + goto end; + } + + /* Live timer interval in usec */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_live_timer_interval, + session->live_timer_interval); + if (ret) { + goto end; + } + + if (!is_open) { + /* Closing session element */ + ret = mi_lttng_writer_close_element(writer); + } +end: + return ret; + +} + +int mi_lttng_domains_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, config_element_domains); +} + +int mi_lttng_domain(struct mi_writer *writer, + struct lttng_domain *domain, int is_open) +{ + int ret = 0; + const char *str_domain; + const char *str_buffer; + + LTTNG_ASSERT(domain); + + /* Open domain element */ + ret = mi_lttng_writer_open_element(writer, config_element_domain); + if (ret) { + goto end; + } + + /* Domain Type */ + str_domain = mi_lttng_domaintype_string(domain->type); + ret = mi_lttng_writer_write_element_string(writer, config_element_type, + str_domain); + if (ret) { + goto end; + } + + /* Buffer Type */ + str_buffer= mi_lttng_buffertype_string(domain->buf_type); + ret = mi_lttng_writer_write_element_string(writer, + config_element_buffer_type, str_buffer); + if (ret) { + goto end; + } + + /* TODO: union attr + * This union is not currently used and was added for + * future ust domain support. + * Date: 25-06-2014 + * */ + + if (!is_open) { + /* Closing domain element */ + ret = mi_lttng_writer_close_element(writer); + } + +end: + return ret; + +} + +int mi_lttng_channels_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, config_element_channels); +} + +int mi_lttng_channel(struct mi_writer *writer, + struct lttng_channel *channel, int is_open) +{ + int ret = 0; + + LTTNG_ASSERT(channel); + + /* Opening channel element */ + ret = mi_lttng_writer_open_element(writer, config_element_channel); + if (ret) { + goto end; + } + + /* Name */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + channel->name); + if (ret) { + goto end; + } + + /* Enabled ? */ + ret = mi_lttng_writer_write_element_bool(writer, + config_element_enabled, channel->enabled); + if (ret) { + goto end; + } + + /* Attribute */ + ret = mi_lttng_channel_attr(writer, &channel->attr); + if (ret) { + goto end; + } + + if (!is_open) { + /* Closing channel element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } +end: + return ret; +} + +int mi_lttng_channel_attr(struct mi_writer *writer, + struct lttng_channel_attr *attr) +{ + int ret = 0; + struct lttng_channel *chan = caa_container_of(attr, + struct lttng_channel, attr); + uint64_t discarded_events, lost_packets, monitor_timer_interval; + int64_t blocking_timeout; + + LTTNG_ASSERT(attr); + + ret = lttng_channel_get_discarded_event_count(chan, &discarded_events); + if (ret) { + goto end; + } + + ret = lttng_channel_get_lost_packet_count(chan, &lost_packets); + if (ret) { + goto end; + } + + ret = lttng_channel_get_monitor_timer_interval(chan, + &monitor_timer_interval); + if (ret) { + goto end; + } + + ret = lttng_channel_get_blocking_timeout(chan, + &blocking_timeout); + if (ret) { + goto end; + } + + /* Opening Attributes */ + ret = mi_lttng_writer_open_element(writer, config_element_attributes); + if (ret) { + goto end; + } + + /* Overwrite */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_overwrite_mode, + attr->overwrite ? config_overwrite_mode_overwrite : + config_overwrite_mode_discard); + if (ret) { + goto end; + } + + /* Sub buffer size in byte */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_subbuf_size, attr->subbuf_size); + if (ret) { + goto end; + } + + /* Number of subbuffer (power of two) */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_num_subbuf, + attr->num_subbuf); + if (ret) { + goto end; + } + + /* Switch timer interval in usec */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_switch_timer_interval, + attr->switch_timer_interval); + if (ret) { + goto end; + } + + /* Read timer interval in usec */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_read_timer_interval, + attr->read_timer_interval); + if (ret) { + goto end; + } + + /* Monitor timer interval in usec */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_monitor_timer_interval, + monitor_timer_interval); + if (ret) { + goto end; + } + + /* Retry timeout in usec */ + ret = mi_lttng_writer_write_element_signed_int(writer, + config_element_blocking_timeout, + blocking_timeout); + if (ret) { + goto end; + } + + /* Event output */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_output_type, + attr->output == LTTNG_EVENT_SPLICE ? + config_output_type_splice : config_output_type_mmap); + if (ret) { + goto end; + } + + /* Tracefile size in bytes */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_tracefile_size, attr->tracefile_size); + if (ret) { + goto end; + } + + /* Count of tracefiles */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_tracefile_count, + attr->tracefile_count); + if (ret) { + goto end; + } + + /* Live timer interval in usec*/ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_live_timer_interval, + attr->live_timer_interval); + if (ret) { + goto end; + } + + /* Discarded events */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_discarded_events, + discarded_events); + if (ret) { + goto end; + } + + /* Lost packets */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_lost_packets, + lost_packets); + if (ret) { + goto end; + } + + /* Closing attributes */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } +end: + return ret; + +} + +int mi_lttng_event_common_attributes(struct mi_writer *writer, + struct lttng_event *event) +{ + int ret; + const char *filter_expression; + + /* Open event element */ + ret = mi_lttng_writer_open_element(writer, config_element_event); + if (ret) { + goto end; + } + + /* Event name */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, event->name); + if (ret) { + goto end; + } + + /* Event type */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_type, mi_lttng_eventtype_string(event->type)); + if (ret) { + goto end; + } + + /* Is event enabled */ + ret = mi_lttng_writer_write_element_bool(writer, + config_element_enabled, event->enabled); + if (ret) { + goto end; + } + + /* Event filter expression */ + ret = lttng_event_get_filter_expression(event, &filter_expression); + if (ret) { + goto end; + } + + if (filter_expression) { + ret = mi_lttng_writer_write_element_string(writer, + config_element_filter_expression, + filter_expression); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static int write_event_exclusions(struct mi_writer *writer, + struct lttng_event *event) +{ + int i; + int ret; + int exclusion_count; + + /* Open event exclusions */ + ret = mi_lttng_writer_open_element(writer, config_element_exclusions); + if (ret) { + goto end; + } + + exclusion_count = lttng_event_get_exclusion_name_count(event); + if (exclusion_count < 0) { + ret = exclusion_count; + goto end; + } + + for (i = 0; i < exclusion_count; i++) { + const char *name; + + ret = lttng_event_get_exclusion_name(event, i, &name); + if (ret) { + /* Close exclusions */ + mi_lttng_writer_close_element(writer); + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + config_element_exclusion, name); + if (ret) { + /* Close exclusions */ + mi_lttng_writer_close_element(writer); + goto end; + } + } + + /* Close exclusions */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_event_tracepoint_loglevel(struct mi_writer *writer, + struct lttng_event *event, enum lttng_domain_type domain) +{ + int ret; + + /* Event loglevel */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_loglevel, + mi_lttng_loglevel_string(event->loglevel, domain)); + if (ret) { + goto end; + } + + /* Log level type */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_loglevel_type, + mi_lttng_logleveltype_string(event->loglevel_type)); + if (ret) { + goto end; + } + + /* Event exclusions */ + ret = write_event_exclusions(writer, event); + +end: + return ret; +} + +int mi_lttng_event_tracepoint_no_loglevel(struct mi_writer *writer, + struct lttng_event *event) +{ + /* event exclusion filter */ + return write_event_exclusions(writer, event); +} + +int mi_lttng_event_function_probe(struct mi_writer *writer, + struct lttng_event *event) +{ + int ret; + + ret = mi_lttng_writer_open_element(writer, config_element_attributes); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes); + if (ret) { + goto end; + } + + if (event->attr.probe.addr != 0) { + /* event probe address */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_address, event->attr.probe.addr); + if (ret) { + goto end; + } + } else { + /* event probe offset */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_offset, event->attr.probe.offset); + if (ret) { + goto end; + } + + /* event probe symbol_name */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_symbol_name, event->attr.probe.symbol_name); + if (ret) { + goto end; + } + } + + /* Close probe_attributes and attributes */ + ret = mi_lttng_close_multi_element(writer, 2); +end: + return ret; +} + +static +int mi_lttng_event_userspace_probe(struct mi_writer *writer, + struct lttng_event *event) +{ + int ret; + const struct lttng_userspace_probe_location *location; + const struct lttng_userspace_probe_location_lookup_method *lookup_method; + enum lttng_userspace_probe_location_lookup_method_type lookup_type; + + location = lttng_event_get_userspace_probe_location(event); + if (!location) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); + if (!lookup_method) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lookup_type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method); + + ret = mi_lttng_writer_open_element(writer, config_element_attributes); + if (ret) { + goto end; + } + + switch (lttng_userspace_probe_location_get_type(location)) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + const char *function_name; + const char *binary_path; + + ret = mi_lttng_writer_open_element(writer, + config_element_userspace_probe_function_attributes); + if (ret) { + goto end; + } + + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_lookup, + config_element_userspace_probe_lookup_function_elf); + if (ret) { + goto end; + } + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_lookup, + config_element_userspace_probe_lookup_function_default); + if (ret) { + goto end; + } + break; + default: + goto end; + } + + binary_path = lttng_userspace_probe_location_function_get_binary_path(location); + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_location_binary_path, binary_path); + if (ret) { + goto end; + } + + function_name = lttng_userspace_probe_location_function_get_function_name(location); + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_function_location_function_name, + function_name); + if (ret) { + goto end; + } + + break; + } + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + { + const char *probe_name, *provider_name; + const char *binary_path; + + ret = mi_lttng_writer_open_element(writer, + config_element_userspace_probe_function_attributes); + if (ret) { + goto end; + } + + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_lookup, + config_element_userspace_probe_lookup_tracepoint_sdt); + if (ret) { + goto end; + } + break; + default: + goto end; + } + + binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location); + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_location_binary_path, + binary_path); + if (ret) { + goto end; + } + + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_tracepoint_location_provider_name, + provider_name); + if (ret) { + goto end; + } + + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); + ret = mi_lttng_writer_write_element_string(writer, + config_element_userspace_probe_tracepoint_location_probe_name, probe_name); + if (ret) { + goto end; + } + break; + } + default: + ERR("Invalid probe type encountered"); + } + /* Close probe_attributes and attributes */ + ret = mi_lttng_close_multi_element(writer, 2); +end: + return ret; +} + +int mi_lttng_event_function_entry(struct mi_writer *writer, + struct lttng_event *event) +{ + int ret; + + ret = mi_lttng_writer_open_element(writer, config_element_attributes); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_probe_attributes); + if (ret) { + goto end; + } + + /* event probe symbol_name */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_symbol_name, event->attr.ftrace.symbol_name); + if (ret) { + goto end; + } + + /* Close function_attributes and attributes */ + ret = mi_lttng_close_multi_element(writer, 2); +end: + return ret; +} + +int mi_lttng_events_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, config_element_events); +} + +int mi_lttng_event(struct mi_writer *writer, + struct lttng_event *event, int is_open, enum lttng_domain_type domain) +{ + int ret; + + ret = mi_lttng_event_common_attributes(writer, event); + if (ret) { + goto end; + } + + switch (event->type) { + case LTTNG_EVENT_TRACEPOINT: + { + if (event->loglevel != -1) { + ret = mi_lttng_event_tracepoint_loglevel(writer, event, domain); + } else { + ret = mi_lttng_event_tracepoint_no_loglevel(writer, event); + } + break; + } + case LTTNG_EVENT_FUNCTION: + /* Fallthrough */ + case LTTNG_EVENT_PROBE: + ret = mi_lttng_event_function_probe(writer, event); + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + ret = mi_lttng_event_function_entry(writer, event); + break; + case LTTNG_EVENT_USERSPACE_PROBE: + ret = mi_lttng_event_userspace_probe(writer, event); + break; + case LTTNG_EVENT_ALL: + /* Fallthrough */ + default: + break; + } + + if (ret) { + goto end; + } + + if (!is_open) { + ret = mi_lttng_writer_close_element(writer); + } + +end: + return ret; +} + +int mi_lttng_trackers_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element( + writer, config_element_process_attr_trackers); +} + +static int get_tracker_elements(enum lttng_process_attr process_attr, + const char **element_process_attr_tracker, + const char **element_process_attr_value) +{ + int ret = 0; + + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_pid; + *element_process_attr_value = + config_element_process_attr_pid_value; + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_vpid; + *element_process_attr_value = + config_element_process_attr_vpid_value; + break; + case LTTNG_PROCESS_ATTR_USER_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_uid; + *element_process_attr_value = + config_element_process_attr_uid_value; + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_vuid; + *element_process_attr_value = + config_element_process_attr_vuid_value; + break; + case LTTNG_PROCESS_ATTR_GROUP_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_gid; + *element_process_attr_value = + config_element_process_attr_gid_value; + break; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + *element_process_attr_tracker = + config_element_process_attr_tracker_vgid; + *element_process_attr_value = + config_element_process_attr_vgid_value; + break; + default: + ret = LTTNG_ERR_SAVE_IO_FAIL; + } + return ret; +} + +int mi_lttng_process_attribute_tracker_open( + struct mi_writer *writer, enum lttng_process_attr process_attr) +{ + int ret; + const char *element_tracker, *element_value; + + ret = get_tracker_elements( + process_attr, &element_tracker, &element_value); + if (ret) { + return ret; + } + + /* Open process attribute tracker element */ + ret = mi_lttng_writer_open_element(writer, element_tracker); + if (ret) { + goto end; + } + + /* Open values element */ + ret = mi_lttng_process_attr_values_open(writer); +end: + return ret; +} + +int mi_lttng_pids_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, config_element_pids); +} + +/* + * TODO: move the listing of pid for user agent to process semantic on + * mi api bump. The use of process element break the mi api. + */ +int mi_lttng_pid(struct mi_writer *writer, + pid_t pid, + const char *name, + int is_open) +{ + int ret; + + /* Open pid process */ + ret = mi_lttng_writer_open_element(writer, config_element_pid); + if (ret) { + goto end; + } + + /* Writing pid number */ + ret = mi_lttng_writer_write_element_signed_int(writer, + mi_lttng_element_pid_id, (int)pid); + if (ret) { + goto end; + } + + /* Writing name of the process */ + if (name) { + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + name); + if (ret) { + goto end; + } + } + + if (!is_open) { + /* Closing Pid */ + ret = mi_lttng_writer_close_element(writer); + } + +end: + return ret; +} + +int mi_lttng_process_attr_values_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element( + writer, config_element_process_attr_values); +} + +int mi_lttng_all_process_attribute_value(struct mi_writer *writer, + enum lttng_process_attr process_attr, + bool is_open) +{ + int ret; + const char *element_id_tracker, *element_target_id; + + ret = get_tracker_elements( + process_attr, &element_id_tracker, &element_target_id); + if (ret) { + return ret; + } + + ret = mi_lttng_writer_open_element(writer, element_target_id); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_type); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, config_element_all, 1); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + + if (!is_open) { + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } +end: + return ret; +} + +int mi_lttng_integral_process_attribute_value(struct mi_writer *writer, + enum lttng_process_attr process_attr, + int64_t value, + bool is_open) +{ + int ret; + const char *element_id_tracker, *element_target_id; + + ret = get_tracker_elements( + process_attr, &element_id_tracker, &element_target_id); + if (ret) { + return ret; + } + + ret = mi_lttng_writer_open_element(writer, element_target_id); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_type); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_signed_int( + writer, config_element_process_attr_id, value); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + + if (!is_open) { + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +int mi_lttng_string_process_attribute_value(struct mi_writer *writer, + enum lttng_process_attr process_attr, + const char *value, + bool is_open) + +{ + int ret; + const char *element_id_tracker, *element_target_id; + + ret = get_tracker_elements( + process_attr, &element_id_tracker, &element_target_id); + if (ret) { + return ret; + } + + ret = mi_lttng_writer_open_element(writer, element_target_id); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, config_element_type); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string( + writer, config_element_name, value); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + + if (!is_open) { + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +int mi_lttng_event_fields_open(struct mi_writer *writer) +{ + return mi_lttng_writer_open_element(writer, mi_lttng_element_event_fields); +} + +int mi_lttng_event_field(struct mi_writer *writer, + struct lttng_event_field *field) +{ + int ret; + + if (!field->field_name[0]) { + ret = 0; + goto end; + } + + /* Open field */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_event_field); + if (ret) { + goto end; + } + + if (!field->field_name[0]) { + goto close; + } + + /* Name */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + field->field_name); + if (ret) { + goto end; + } + + /* Type */ + ret = mi_lttng_writer_write_element_string(writer, config_element_type, + mi_lttng_eventfieldtype_string(field->type)); + if (ret) { + goto end; + } + + /* nowrite */ + ret = mi_lttng_writer_write_element_signed_int(writer, + mi_lttng_element_nowrite, field->nowrite); + if (ret) { + goto end; + } + +close: + /* Close field element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_perf_counter_context(struct mi_writer *writer, + struct lttng_event_perf_counter_ctx *perf_context) +{ + int ret; + + /* Open perf_counter_context */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_perf_counter_context); + if (ret) { + goto end; + } + + /* Type */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_type, perf_context->type); + if (ret) { + goto end; + } + + /* Config */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + config_element_config, perf_context->config); + if (ret) { + goto end; + } + + /* Name of the perf counter */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, perf_context->name); + if (ret) { + goto end; + } + + /* Close perf_counter_context */ + ret = mi_lttng_writer_close_element(writer); +end: + return ret; +} + +static +int mi_lttng_app_context(struct mi_writer *writer, + const char *provider_name, const char *ctx_name) +{ + int ret; + + /* Open app */ + ret = mi_lttng_writer_open_element(writer, + config_element_context_app); + if (ret) { + goto end; + } + + /* provider_name */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_context_app_provider_name, + provider_name); + if (ret) { + goto end; + } + + /* ctx_name */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_context_app_ctx_name, ctx_name); + if (ret) { + goto end; + } + + /* Close app */ + ret = mi_lttng_writer_close_element(writer); +end: + return ret; +} + +int mi_lttng_context(struct mi_writer *writer, + struct lttng_event_context *context, int is_open) +{ + int ret; + + /* Open context */ + ret = mi_lttng_writer_open_element(writer , config_element_context); + if (ret) { + goto end; + } + + /* Special case for PERF_*_COUNTER + * print the lttng_event_perf_counter_ctx*/ + switch (context->ctx) { + case LTTNG_EVENT_CONTEXT_PERF_COUNTER: + case LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER: + case LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER: + { + struct lttng_event_perf_counter_ctx *perf_context = + &context->u.perf_counter; + ret = mi_lttng_perf_counter_context(writer, perf_context); + if (ret) { + goto end; + } + break; + } + case LTTNG_EVENT_CONTEXT_APP_CONTEXT: + { + ret = mi_lttng_app_context(writer, + context->u.app_ctx.provider_name, + context->u.app_ctx.ctx_name); + if (ret) { + goto end; + } + break; + } + default: + { + const char *type_string = mi_lttng_event_contexttype_string( + context->ctx); + if (!type_string) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + /* Print context type */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_type, type_string); + break; + } + } + + /* Close context */ + if (!is_open) { + ret = mi_lttng_writer_close_element(writer); + } + +end: + return ret; +} + +int mi_lttng_snapshot_output_session_name(struct mi_writer *writer, + const char *session_name) +{ + int ret; + + /* Open session element */ + ret = mi_lttng_writer_open_element(writer, config_element_session); + if (ret) { + goto end; + } + + /* Snapshot output list for current session name */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + session_name); + if (ret) { + goto end; + } + + /* Open element snapshots (sequence one snapshot) */ + ret = mi_lttng_writer_open_element(writer, mi_lttng_element_snapshots); + if (ret) { + goto end; + } + +end: + return ret; +} + +int mi_lttng_snapshot_list_output(struct mi_writer *writer, + const struct lttng_snapshot_output *output) +{ + int ret; + + /* Open element snapshot output */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_snapshot); + if (ret) { + goto end; + } + + /* ID of the snapshot output */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_id, output->id); + if (ret) { + goto end; + } + + /* Name of the output */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + output->name); + if (ret) { + goto end; + } + + /* Destination of the output (ctrl_url)*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); + if (ret) { + goto end; + } + + /* Destination of the output (data_url) */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_data_url, output->data_url); + if (ret) { + goto end; + } + + /* total size of all stream combined */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_snapshot_max_size, output->max_size); + if (ret) { + goto end; + } + + /* Close snapshot output element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_snapshot_del_output(struct mi_writer *writer, int id, + const char *name, const char *current_session_name) +{ + int ret; + + /* Open element del_snapshot */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_snapshot); + if (ret) { + goto end; + } + + + if (id != UINT32_MAX) { + /* "Snapshot output "id" successfully deleted + * for "current_session_name" + * ID of the snapshot output + */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_id, id); + if (ret) { + goto end; + } + } else { + /* "Snapshot output "name" successfully deleted + * for session "current_session_name" + * Name of the output + */ + ret = mi_lttng_writer_write_element_string(writer, config_element_name, + name); + if (ret) { + goto end; + } + } + + /* Snapshot was deleted for session "current_session_name"*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_session_name, + current_session_name); + if (ret) { + goto end; + } + + /* Close snapshot element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_snapshot_add_output(struct mi_writer *writer, + const char *current_session_name, const char *n_ptr, + struct lttng_snapshot_output *output) +{ + int ret; + + /* Open element snapshot */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_snapshot); + if (ret) { + goto end; + } + + /* Snapshot output id */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_id, output->id); + if (ret) { + goto end; + } + + /* Snapshot output names */ + ret = mi_lttng_writer_write_element_string(writer, + config_element_name, n_ptr); + if (ret) { + goto end; + } + + /* Destination of the output (ctrl_url)*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); + if (ret) { + goto end; + } + + /* Snapshot added for session "current_session_name"*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_session_name, current_session_name); + if (ret) { + goto end; + } + + /* total size of all stream combined */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_snapshot_max_size, output->max_size); + if (ret) { + goto end; + } + + /* Close snapshot element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_snapshot_record(struct mi_writer *writer, + const char *current_session_name, const char *url, + const char *cmdline_ctrl_url, const char *cmdline_data_url) +{ + int ret; + + /* Open element snapshot */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_snapshot); + if (ret) { + goto end; + } + + /* + * If a valid an URL was given, serialize it, + * else take the command line data and ctrl urls*/ + if (url) { + /* Destination of the output (ctrl_url)*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_ctrl_url, url); + if (ret) { + goto end; + } + } else if (cmdline_ctrl_url) { + /* Destination of the output (ctrl_url)*/ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_ctrl_url, cmdline_ctrl_url); + if (ret) { + goto end; + } + + /* Destination of the output (data_url) */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_data_url, cmdline_data_url); + if (ret) { + goto end; + } + } + + /* Close record_snapshot element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +int mi_lttng_rotation_schedule(struct mi_writer *writer, + const struct lttng_rotation_schedule *schedule) +{ + int ret = 0; + enum lttng_rotation_status status; + uint64_t value; + const char *element_name; + const char *value_name; + bool empty_schedule = false; + + switch (lttng_rotation_schedule_get_type(schedule)) { + case LTTNG_ROTATION_SCHEDULE_TYPE_PERIODIC: + status = lttng_rotation_schedule_periodic_get_period(schedule, + &value); + element_name = mi_lttng_element_rotation_schedule_periodic; + value_name = mi_lttng_element_rotation_schedule_periodic_time_us; + break; + case LTTNG_ROTATION_SCHEDULE_TYPE_SIZE_THRESHOLD: + status = lttng_rotation_schedule_size_threshold_get_threshold( + schedule, &value); + element_name = mi_lttng_element_rotation_schedule_size_threshold; + value_name = mi_lttng_element_rotation_schedule_size_threshold_bytes; + break; + default: + ret = -1; + goto end; + } + + if (status != LTTNG_ROTATION_STATUS_OK) { + if (status == LTTNG_ROTATION_STATUS_UNAVAILABLE) { + empty_schedule = true; + } else { + ret = -1; + goto end; + } + } + + ret = mi_lttng_writer_open_element(writer, element_name); + if (ret) { + goto end; + } + + if (!empty_schedule) { + ret = mi_lttng_writer_write_element_unsigned_int(writer, + value_name, value); + if (ret) { + goto end; + } + } + + /* Close schedule descriptor element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } +end: + return ret; +} + +int mi_lttng_rotation_schedule_result(struct mi_writer *writer, + const struct lttng_rotation_schedule *schedule, + bool success) +{ + int ret = 0; + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_schedule_result); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_schedule); + if (ret) { + goto end; + } + + ret = mi_lttng_rotation_schedule(writer, schedule); + if (ret) { + goto end; + } + + /* Close rotation_schedule element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + goto end; + } + + /* Close rotation_schedule_result element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } +end: + return ret; +} + +static +int mi_lttng_location(struct mi_writer *writer, + const struct lttng_trace_archive_location *location) +{ + int ret = 0; + enum lttng_trace_archive_location_type location_type; + enum lttng_trace_archive_location_status status; + + location_type = lttng_trace_archive_location_get_type(location); + + switch (location_type) { + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_LOCAL: + { + const char *absolute_path; + + status = lttng_trace_archive_location_local_get_absolute_path( + location, &absolute_path); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_location_local); + if (ret) { + goto end; + } + + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotation_location_local_absolute_path, + absolute_path); + if (ret) { + goto end; + } + + /* Close local element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + break; + } + case LTTNG_TRACE_ARCHIVE_LOCATION_TYPE_RELAY: + { + uint16_t control_port, data_port; + const char *host, *relative_path; + enum lttng_trace_archive_location_relay_protocol_type protocol; + + /* Fetch all relay location parameters. */ + status = lttng_trace_archive_location_relay_get_protocol_type( + location, &protocol); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_host( + location, &host); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_control_port( + location, &control_port); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_data_port( + location, &data_port); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + status = lttng_trace_archive_location_relay_get_relative_path( + location, &relative_path); + if (status != LTTNG_TRACE_ARCHIVE_LOCATION_STATUS_OK) { + ret = -1; + goto end; + } + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_location_relay); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotation_location_relay_host, + host); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_rotation_location_relay_control_port, + control_port); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_rotation_location_relay_data_port, + data_port); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotation_location_relay_protocol, + mi_lttng_trace_archive_location_relay_protocol_type_string(protocol)); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotation_location_relay_relative_path, + relative_path); + if (ret) { + goto end; + } + + /* Close relay element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + break; + } + default: + abort(); + } +end: + return ret; +} + +int mi_lttng_rotate(struct mi_writer *writer, + const char *session_name, + enum lttng_rotation_state rotation_state, + const struct lttng_trace_archive_location *location) +{ + int ret; + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_session_name, + session_name); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotation_state, + mi_lttng_rotation_state_string(rotation_state)); + if (ret) { + goto end; + } + + if (!location) { + /* Not a serialization error. */ + goto close_rotation; + } + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation_location); + if (ret) { + goto end; + } + + ret = mi_lttng_location(writer, location); + if (ret) { + goto close_location; + } + +close_location: + /* Close location element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + +close_rotation: + /* Close rotation element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } +end: + return ret; +} diff --git a/src/common/notification.c b/src/common/notification.c deleted file mode 100644 index d1403574b..000000000 --- a/src/common/notification.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/src/common/notification.cpp b/src/common/notification.cpp new file mode 100644 index 000000000..989c20ade --- /dev/null +++ b/src/common/notification.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/common/payload-view.c b/src/common/payload-view.c deleted file mode 100644 index 2e6581bb4..000000000 --- a/src/common/payload-view.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include "payload-view.h" -#include "payload.h" -#include - -bool lttng_payload_view_is_valid(const struct lttng_payload_view *view) -{ - return view && lttng_buffer_view_is_valid(&view->buffer); -} - -struct lttng_payload_view lttng_payload_view_from_payload( - const struct lttng_payload *payload, size_t offset, - ptrdiff_t len) -{ - return payload ? (struct lttng_payload_view) { - .buffer = lttng_buffer_view_from_dynamic_buffer( - &payload->buffer, offset, len), - ._fd_handles = payload->_fd_handles, - } : (struct lttng_payload_view) {}; -} - -struct lttng_payload_view lttng_payload_view_from_view( - struct lttng_payload_view *view, size_t offset, - ptrdiff_t len) -{ - return view ? (struct lttng_payload_view) { - .buffer = lttng_buffer_view_from_view( - &view->buffer, offset, len), - ._fd_handles = view->_fd_handles, - ._iterator.p_fd_handles_position = view->_iterator.p_fd_handles_position ?: - &view->_iterator.fd_handles_position, - } : (struct lttng_payload_view) {}; -} - -struct lttng_payload_view lttng_payload_view_from_dynamic_buffer( - const struct lttng_dynamic_buffer *buffer, size_t offset, - ptrdiff_t len) -{ - return buffer ? (struct lttng_payload_view) { - .buffer = lttng_buffer_view_from_dynamic_buffer( - buffer, offset, len) - } : (struct lttng_payload_view) {}; -} - -struct lttng_payload_view lttng_payload_view_from_buffer_view( - const struct lttng_buffer_view *view, size_t offset, - ptrdiff_t len) -{ - return view ? (struct lttng_payload_view) { - .buffer = lttng_buffer_view_from_view( - view, offset, len) - } : (struct lttng_payload_view) {}; -} - -struct lttng_payload_view lttng_payload_view_init_from_buffer( - const char *src, size_t offset, ptrdiff_t len) -{ - return (struct lttng_payload_view) { - .buffer = lttng_buffer_view_init( - src, offset, len) - }; -} - -int lttng_payload_view_get_fd_handle_count( - const struct lttng_payload_view *payload_view) -{ - int ret; - size_t position; - - if (!payload_view) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_pointer_array_get_count(&payload_view->_fd_handles); - if (ret < 0) { - goto end; - } - - position = payload_view->_iterator.p_fd_handles_position ? - *payload_view->_iterator.p_fd_handles_position : - payload_view->_iterator.fd_handles_position; - ret = ret - (int) position; -end: - return ret; -} - -struct fd_handle *lttng_payload_view_pop_fd_handle( - struct lttng_payload_view *view) -{ - struct fd_handle *handle = NULL; - size_t fd_handle_count; - size_t *pos; - - if (!view) { - goto end; - } - - fd_handle_count = lttng_payload_view_get_fd_handle_count(view); - if (fd_handle_count == 0) { - goto end; - } - - pos = view->_iterator.p_fd_handles_position ? - view->_iterator.p_fd_handles_position : - &view->_iterator.fd_handles_position; - handle = lttng_dynamic_pointer_array_get_pointer(&view->_fd_handles, - *pos); - (*pos)++; - fd_handle_get(handle); -end: - return handle; -} diff --git a/src/common/payload-view.cpp b/src/common/payload-view.cpp new file mode 100644 index 000000000..b1a44368d --- /dev/null +++ b/src/common/payload-view.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include "payload-view.h" +#include "payload.h" +#include + +bool lttng_payload_view_is_valid(const struct lttng_payload_view *view) +{ + return view && lttng_buffer_view_is_valid(&view->buffer); +} + +struct lttng_payload_view lttng_payload_view_from_payload( + const struct lttng_payload *payload, size_t offset, + ptrdiff_t len) +{ + return payload ? (struct lttng_payload_view) { + .buffer = lttng_buffer_view_from_dynamic_buffer( + &payload->buffer, offset, len), + ._fd_handles = payload->_fd_handles, + } : (struct lttng_payload_view) {}; +} + +struct lttng_payload_view lttng_payload_view_from_view( + struct lttng_payload_view *view, size_t offset, + ptrdiff_t len) +{ + return view ? (struct lttng_payload_view) { + .buffer = lttng_buffer_view_from_view( + &view->buffer, offset, len), + ._fd_handles = view->_fd_handles, + ._iterator = { + .p_fd_handles_position = view->_iterator.p_fd_handles_position ?: + &view->_iterator.fd_handles_position, + } + } : (struct lttng_payload_view) {}; +} + +struct lttng_payload_view lttng_payload_view_from_dynamic_buffer( + const struct lttng_dynamic_buffer *buffer, size_t offset, + ptrdiff_t len) +{ + return buffer ? (struct lttng_payload_view) { + .buffer = lttng_buffer_view_from_dynamic_buffer( + buffer, offset, len) + } : (struct lttng_payload_view) {}; +} + +struct lttng_payload_view lttng_payload_view_from_buffer_view( + const struct lttng_buffer_view *view, size_t offset, + ptrdiff_t len) +{ + return view ? (struct lttng_payload_view) { + .buffer = lttng_buffer_view_from_view( + view, offset, len) + } : (struct lttng_payload_view) {}; +} + +struct lttng_payload_view lttng_payload_view_init_from_buffer( + const char *src, size_t offset, ptrdiff_t len) +{ + return (struct lttng_payload_view) { + .buffer = lttng_buffer_view_init( + src, offset, len) + }; +} + +int lttng_payload_view_get_fd_handle_count( + const struct lttng_payload_view *payload_view) +{ + int ret; + size_t position; + + if (!payload_view) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_get_count(&payload_view->_fd_handles); + if (ret < 0) { + goto end; + } + + position = payload_view->_iterator.p_fd_handles_position ? + *payload_view->_iterator.p_fd_handles_position : + payload_view->_iterator.fd_handles_position; + ret = ret - (int) position; +end: + return ret; +} + +struct fd_handle *lttng_payload_view_pop_fd_handle( + struct lttng_payload_view *view) +{ + struct fd_handle *handle = NULL; + size_t fd_handle_count; + size_t *pos; + + if (!view) { + goto end; + } + + fd_handle_count = lttng_payload_view_get_fd_handle_count(view); + if (fd_handle_count == 0) { + goto end; + } + + pos = view->_iterator.p_fd_handles_position ? + view->_iterator.p_fd_handles_position : + &view->_iterator.fd_handles_position; + handle = (fd_handle *) lttng_dynamic_pointer_array_get_pointer(&view->_fd_handles, + *pos); + (*pos)++; + fd_handle_get(handle); +end: + return handle; +} diff --git a/src/common/payload.c b/src/common/payload.c deleted file mode 100644 index f26585bb9..000000000 --- a/src/common/payload.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "payload.h" -#include -#include -#include - -static -void release_fd_handle_ref(void *ptr) -{ - struct fd_handle *fd_handle = ptr; - - fd_handle_put(fd_handle); -} - -void lttng_payload_init(struct lttng_payload *payload) -{ - LTTNG_ASSERT(payload); - lttng_dynamic_buffer_init(&payload->buffer); - lttng_dynamic_pointer_array_init(&payload->_fd_handles, - release_fd_handle_ref); -} - -int lttng_payload_copy(const struct lttng_payload *src_payload, - struct lttng_payload *dst_payload) -{ - int ret; - size_t i; - - ret = lttng_dynamic_buffer_append_buffer( - &dst_payload->buffer, &src_payload->buffer); - if (ret) { - goto end; - } - - for (i = 0; i < lttng_dynamic_pointer_array_get_count( - &src_payload->_fd_handles); - i++) { - struct fd_handle *new_fd_handle; - const struct fd_handle *src_fd_handle = - lttng_dynamic_pointer_array_get_pointer( - &src_payload->_fd_handles, i); - - new_fd_handle = fd_handle_copy(src_fd_handle); - if (!new_fd_handle) { - PERROR("Failed to copy fd_handle while copying a payload"); - ret = -1; - goto end; - } - - ret = lttng_payload_push_fd_handle(dst_payload, new_fd_handle); - fd_handle_put(new_fd_handle); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -void lttng_payload_reset(struct lttng_payload *payload) -{ - if (!payload) { - return; - } - - lttng_dynamic_buffer_reset(&payload->buffer); - lttng_dynamic_pointer_array_reset(&payload->_fd_handles); -} - -void lttng_payload_clear(struct lttng_payload *payload) -{ - (void) lttng_dynamic_buffer_set_size(&payload->buffer, 0); - lttng_dynamic_pointer_array_clear(&payload->_fd_handles); -} - -int lttng_payload_push_fd_handle(struct lttng_payload *payload, - struct fd_handle *fd_handle) -{ - int ret; - - if (!payload) { - ret = -1; - goto end; - } - - ret = lttng_dynamic_pointer_array_add_pointer( - &payload->_fd_handles, fd_handle); - if (ret) { - goto end; - } - - fd_handle_get(fd_handle); -end: - return ret; -} diff --git a/src/common/payload.cpp b/src/common/payload.cpp new file mode 100644 index 000000000..0097e65bc --- /dev/null +++ b/src/common/payload.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "payload.h" +#include +#include +#include + +static +void release_fd_handle_ref(void *ptr) +{ + struct fd_handle *fd_handle = (struct fd_handle *) ptr; + + fd_handle_put(fd_handle); +} + +void lttng_payload_init(struct lttng_payload *payload) +{ + LTTNG_ASSERT(payload); + lttng_dynamic_buffer_init(&payload->buffer); + lttng_dynamic_pointer_array_init(&payload->_fd_handles, + release_fd_handle_ref); +} + +int lttng_payload_copy(const struct lttng_payload *src_payload, + struct lttng_payload *dst_payload) +{ + int ret; + size_t i; + + ret = lttng_dynamic_buffer_append_buffer( + &dst_payload->buffer, &src_payload->buffer); + if (ret) { + goto end; + } + + for (i = 0; i < lttng_dynamic_pointer_array_get_count( + &src_payload->_fd_handles); + i++) { + struct fd_handle *new_fd_handle; + const struct fd_handle *src_fd_handle = + (fd_handle *) lttng_dynamic_pointer_array_get_pointer( + &src_payload->_fd_handles, i); + + new_fd_handle = fd_handle_copy(src_fd_handle); + if (!new_fd_handle) { + PERROR("Failed to copy fd_handle while copying a payload"); + ret = -1; + goto end; + } + + ret = lttng_payload_push_fd_handle(dst_payload, new_fd_handle); + fd_handle_put(new_fd_handle); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +void lttng_payload_reset(struct lttng_payload *payload) +{ + if (!payload) { + return; + } + + lttng_dynamic_buffer_reset(&payload->buffer); + lttng_dynamic_pointer_array_reset(&payload->_fd_handles); +} + +void lttng_payload_clear(struct lttng_payload *payload) +{ + (void) lttng_dynamic_buffer_set_size(&payload->buffer, 0); + lttng_dynamic_pointer_array_clear(&payload->_fd_handles); +} + +int lttng_payload_push_fd_handle(struct lttng_payload *payload, + struct fd_handle *fd_handle) +{ + int ret; + + if (!payload) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &payload->_fd_handles, fd_handle); + if (ret) { + goto end; + } + + fd_handle_get(fd_handle); +end: + return ret; +} diff --git a/src/common/pipe.c b/src/common/pipe.c deleted file mode 100644 index 45be43fa3..000000000 --- a/src/common/pipe.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (C) 2013 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include - -#include - -#include "pipe.h" - -/* - * Lock read side of a pipe. - */ -static void lock_read_side(struct lttng_pipe *pipe) -{ - pthread_mutex_lock(&pipe->read_mutex); -} - -/* - * Unlock read side of a pipe. - */ -static void unlock_read_side(struct lttng_pipe *pipe) -{ - pthread_mutex_unlock(&pipe->read_mutex); -} - -/* - * Lock write side of a pipe. - */ -static void lock_write_side(struct lttng_pipe *pipe) -{ - pthread_mutex_lock(&pipe->write_mutex); -} - -/* - * Unlock write side of a pipe. - */ -static void unlock_write_side(struct lttng_pipe *pipe) -{ - pthread_mutex_unlock(&pipe->write_mutex); -} - -/* - * Internal function. Close read side of pipe WITHOUT locking the mutex. - * - * Return 0 on success else a negative errno from close(2). - */ -static int _pipe_read_close(struct lttng_pipe *pipe) -{ - int ret, ret_val = 0; - - LTTNG_ASSERT(pipe); - - if (!lttng_pipe_is_read_open(pipe)) { - goto end; - } - - do { - ret = close(pipe->fd[0]); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - PERROR("close lttng read pipe"); - ret_val = -errno; - } - pipe->r_state = LTTNG_PIPE_STATE_CLOSED; - -end: - return ret_val; -} - -/* - * Internal function. Close write side of pipe WITHOUT locking the mutex. - * - * Return 0 on success else a negative errno from close(2). - */ -static int _pipe_write_close(struct lttng_pipe *pipe) -{ - int ret, ret_val = 0; - - LTTNG_ASSERT(pipe); - - if (!lttng_pipe_is_write_open(pipe)) { - goto end; - } - - do { - ret = close(pipe->fd[1]); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - PERROR("close lttng write pipe"); - ret_val = -errno; - } - pipe->w_state = LTTNG_PIPE_STATE_CLOSED; - -end: - return ret_val; -} - -static struct lttng_pipe *_pipe_create(void) -{ - int ret; - struct lttng_pipe *p; - - p = zmalloc(sizeof(*p)); - if (!p) { - PERROR("zmalloc pipe create"); - goto end; - } - p->fd[0] = p->fd[1] = -1; - - ret = pthread_mutex_init(&p->read_mutex, NULL); - if (ret) { - PERROR("pthread_mutex_init read lock pipe create"); - goto error_destroy; - } - ret = pthread_mutex_init(&p->write_mutex, NULL); - if (ret) { - PERROR("pthread_mutex_init write lock pipe create"); - goto error_destroy_rmutex; - } -end: - return p; -error_destroy_rmutex: - (void) pthread_mutex_destroy(&p->read_mutex); -error_destroy: - free(p); - return NULL; -} - -static int _pipe_set_flags(struct lttng_pipe *pipe, int flags) -{ - int i, ret = 0; - - if (!flags) { - goto end; - } - - for (i = 0; i < 2; i++) { - if (flags & O_NONBLOCK) { - ret = fcntl(pipe->fd[i], F_SETFL, O_NONBLOCK); - if (ret < 0) { - PERROR("fcntl lttng pipe %d", flags); - goto end; - } - } - if (flags & FD_CLOEXEC) { - ret = fcntl(pipe->fd[i], F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl lttng pipe %d", flags); - goto end; - } - } - /* - * We only check for O_NONBLOCK or FD_CLOEXEC, if another flag is - * needed, we can add it, but for now just make sure we don't make - * mistakes with the parameters we pass. - */ - if (!(flags & O_NONBLOCK) && !(flags & FD_CLOEXEC)) { - fprintf(stderr, "Unsupported flag\n"); - ret = -1; - goto end; - } - } -end: - return ret; -} - -/* - * Open a new lttng pipe and set flags using fcntl(). - * - * Return a newly allocated lttng pipe on success or else NULL. - */ -struct lttng_pipe *lttng_pipe_open(int flags) -{ - int ret; - struct lttng_pipe *p; - - p = _pipe_create(); - if (!p) { - goto error; - } - - ret = pipe(p->fd); - if (ret < 0) { - PERROR("lttng pipe"); - goto error; - } - p->r_state = LTTNG_PIPE_STATE_OPENED; - p->w_state = LTTNG_PIPE_STATE_OPENED; - - ret = _pipe_set_flags(p, flags); - if (ret) { - goto error; - } - - p->flags = flags; - - return p; -error: - lttng_pipe_destroy(p); - return NULL; -} - -/* - * Open a new lttng pipe at path and set flags using fcntl(). - * - * Return a newly allocated lttng pipe on success or else NULL. - */ -struct lttng_pipe *lttng_pipe_named_open(const char *path, mode_t mode, - int flags) -{ - int ret, fd_r, fd_w; - struct lttng_pipe *pipe; - - pipe = _pipe_create(); - if (!pipe) { - goto error; - } - - ret = mkfifo(path, mode); - if (ret) { - PERROR("mkfifo"); - goto error; - } - - fd_r = open(path, O_RDONLY | O_NONBLOCK); - if (fd_r < 0) { - PERROR("open fifo"); - goto error; - } - pipe->fd[0] = fd_r; - pipe->r_state = LTTNG_PIPE_STATE_OPENED; - - fd_w = open(path, O_WRONLY | O_NONBLOCK); - if (fd_w < 0) { - PERROR("open fifo"); - goto error; - } - pipe->fd[1] = fd_w; - pipe->w_state = LTTNG_PIPE_STATE_OPENED; - - ret = _pipe_set_flags(pipe, flags); - if (ret) { - goto error; - } - pipe->flags = flags; - - return pipe; -error: - lttng_pipe_destroy(pipe); - return NULL; -} - -/* - * Close read side of a lttng pipe. - * - * Return 0 on success else a negative value. - */ -int lttng_pipe_read_close(struct lttng_pipe *pipe) -{ - int ret; - - LTTNG_ASSERT(pipe); - - /* Handle read side first. */ - lock_read_side(pipe); - ret = _pipe_read_close(pipe); - unlock_read_side(pipe); - - return ret; -} - -/* - * Close write side of a lttng pipe. - * - * Return 0 on success else a negative value. - */ -int lttng_pipe_write_close(struct lttng_pipe *pipe) -{ - int ret; - - LTTNG_ASSERT(pipe); - - lock_write_side(pipe); - ret = _pipe_write_close(pipe); - unlock_write_side(pipe); - - return ret; -} - -/* - * Close both read and write side of a lttng pipe. - * - * Return 0 on success else a negative value. - */ -int lttng_pipe_close(struct lttng_pipe *pipe) -{ - int ret, ret_val = 0; - - LTTNG_ASSERT(pipe); - - ret = lttng_pipe_read_close(pipe); - if (ret < 0) { - ret_val = ret; - } - - ret = lttng_pipe_write_close(pipe); - if (ret < 0) { - ret_val = ret; - } - - return ret_val; -} - -/* - * Close and destroy a lttng pipe object. Finally, pipe is freed. - */ -void lttng_pipe_destroy(struct lttng_pipe *pipe) -{ - int ret; - - if (!pipe) { - return; - } - - /* - * Destroy should *never* be called with a locked mutex. These must always - * succeed so we unlock them after the close pipe below. - */ - ret = pthread_mutex_trylock(&pipe->read_mutex); - LTTNG_ASSERT(!ret); - ret = pthread_mutex_trylock(&pipe->write_mutex); - LTTNG_ASSERT(!ret); - - /* Close pipes WITHOUT trying to lock the pipes. */ - (void) _pipe_read_close(pipe); - (void) _pipe_write_close(pipe); - - unlock_read_side(pipe); - unlock_write_side(pipe); - - (void) pthread_mutex_destroy(&pipe->read_mutex); - (void) pthread_mutex_destroy(&pipe->write_mutex); - - free(pipe); -} - -/* - * Read on a lttng pipe and put the data in buf of at least size count. - * - * Return "count" on success. Return < count on error. errno can be used - * to check the actual error. - */ -ssize_t lttng_pipe_read(struct lttng_pipe *pipe, void *buf, size_t count) -{ - ssize_t ret; - - LTTNG_ASSERT(pipe); - LTTNG_ASSERT(buf); - - lock_read_side(pipe); - if (!lttng_pipe_is_read_open(pipe)) { - ret = -1; - errno = EBADF; - goto error; - } - ret = lttng_read(pipe->fd[0], buf, count); -error: - unlock_read_side(pipe); - return ret; -} - -/* - * Write on a lttng pipe using the data in buf and size of count. - * - * Return "count" on success. Return < count on error. errno can be used - * to check the actual error. - */ -ssize_t lttng_pipe_write(struct lttng_pipe *pipe, const void *buf, - size_t count) -{ - ssize_t ret; - - LTTNG_ASSERT(pipe); - LTTNG_ASSERT(buf); - - lock_write_side(pipe); - if (!lttng_pipe_is_write_open(pipe)) { - ret = -1; - errno = EBADF; - goto error; - } - ret = lttng_write(pipe->fd[1], buf, count); -error: - unlock_write_side(pipe); - return ret; -} - -/* - * Return and release the read end of the pipe. - * - * This call transfers the ownership of the read fd of the underlying pipe - * to the caller if it is still open. - * - * Returns the fd of the read end of the pipe, or -1 if it was already closed or - * released. - */ -int lttng_pipe_release_readfd(struct lttng_pipe *pipe) -{ - int ret; - - if (!pipe) { - ret = -1; - goto end; - } - - lock_read_side(pipe); - if (!lttng_pipe_is_read_open(pipe)) { - ret = -1; - goto end_unlock; - } - ret = pipe->fd[0]; - pipe->fd[0] = -1; - pipe->r_state = LTTNG_PIPE_STATE_CLOSED; -end_unlock: - unlock_read_side(pipe); -end: - return ret; -} - -/* - * Return and release the write end of the pipe. - * - * This call transfers the ownership of the write fd of the underlying pipe - * to the caller if it is still open. - * - * Returns the fd of the write end of the pipe, or -1 if it was alwritey closed - * or released. - */ -int lttng_pipe_release_writefd(struct lttng_pipe *pipe) -{ - int ret; - - if (!pipe) { - ret = -1; - goto end; - } - - lock_write_side(pipe); - if (!lttng_pipe_is_write_open(pipe)) { - ret = -1; - goto end_unlock; - } - ret = pipe->fd[1]; - pipe->fd[1] = -1; - pipe->w_state = LTTNG_PIPE_STATE_CLOSED; -end_unlock: - unlock_write_side(pipe); -end: - return ret; -} diff --git a/src/common/pipe.cpp b/src/common/pipe.cpp new file mode 100644 index 000000000..2de91f5a0 --- /dev/null +++ b/src/common/pipe.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2013 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include + +#include + +#include "pipe.h" + +/* + * Lock read side of a pipe. + */ +static void lock_read_side(struct lttng_pipe *pipe) +{ + pthread_mutex_lock(&pipe->read_mutex); +} + +/* + * Unlock read side of a pipe. + */ +static void unlock_read_side(struct lttng_pipe *pipe) +{ + pthread_mutex_unlock(&pipe->read_mutex); +} + +/* + * Lock write side of a pipe. + */ +static void lock_write_side(struct lttng_pipe *pipe) +{ + pthread_mutex_lock(&pipe->write_mutex); +} + +/* + * Unlock write side of a pipe. + */ +static void unlock_write_side(struct lttng_pipe *pipe) +{ + pthread_mutex_unlock(&pipe->write_mutex); +} + +/* + * Internal function. Close read side of pipe WITHOUT locking the mutex. + * + * Return 0 on success else a negative errno from close(2). + */ +static int _pipe_read_close(struct lttng_pipe *pipe) +{ + int ret, ret_val = 0; + + LTTNG_ASSERT(pipe); + + if (!lttng_pipe_is_read_open(pipe)) { + goto end; + } + + do { + ret = close(pipe->fd[0]); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + PERROR("close lttng read pipe"); + ret_val = -errno; + } + pipe->r_state = LTTNG_PIPE_STATE_CLOSED; + +end: + return ret_val; +} + +/* + * Internal function. Close write side of pipe WITHOUT locking the mutex. + * + * Return 0 on success else a negative errno from close(2). + */ +static int _pipe_write_close(struct lttng_pipe *pipe) +{ + int ret, ret_val = 0; + + LTTNG_ASSERT(pipe); + + if (!lttng_pipe_is_write_open(pipe)) { + goto end; + } + + do { + ret = close(pipe->fd[1]); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + PERROR("close lttng write pipe"); + ret_val = -errno; + } + pipe->w_state = LTTNG_PIPE_STATE_CLOSED; + +end: + return ret_val; +} + +static struct lttng_pipe *_pipe_create(void) +{ + int ret; + struct lttng_pipe *p; + + p = (lttng_pipe *) zmalloc(sizeof(*p)); + if (!p) { + PERROR("zmalloc pipe create"); + goto end; + } + p->fd[0] = p->fd[1] = -1; + + ret = pthread_mutex_init(&p->read_mutex, NULL); + if (ret) { + PERROR("pthread_mutex_init read lock pipe create"); + goto error_destroy; + } + ret = pthread_mutex_init(&p->write_mutex, NULL); + if (ret) { + PERROR("pthread_mutex_init write lock pipe create"); + goto error_destroy_rmutex; + } +end: + return p; +error_destroy_rmutex: + (void) pthread_mutex_destroy(&p->read_mutex); +error_destroy: + free(p); + return NULL; +} + +static int _pipe_set_flags(struct lttng_pipe *pipe, int flags) +{ + int i, ret = 0; + + if (!flags) { + goto end; + } + + for (i = 0; i < 2; i++) { + if (flags & O_NONBLOCK) { + ret = fcntl(pipe->fd[i], F_SETFL, O_NONBLOCK); + if (ret < 0) { + PERROR("fcntl lttng pipe %d", flags); + goto end; + } + } + if (flags & FD_CLOEXEC) { + ret = fcntl(pipe->fd[i], F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl lttng pipe %d", flags); + goto end; + } + } + /* + * We only check for O_NONBLOCK or FD_CLOEXEC, if another flag is + * needed, we can add it, but for now just make sure we don't make + * mistakes with the parameters we pass. + */ + if (!(flags & O_NONBLOCK) && !(flags & FD_CLOEXEC)) { + fprintf(stderr, "Unsupported flag\n"); + ret = -1; + goto end; + } + } +end: + return ret; +} + +/* + * Open a new lttng pipe and set flags using fcntl(). + * + * Return a newly allocated lttng pipe on success or else NULL. + */ +struct lttng_pipe *lttng_pipe_open(int flags) +{ + int ret; + struct lttng_pipe *p; + + p = _pipe_create(); + if (!p) { + goto error; + } + + ret = pipe(p->fd); + if (ret < 0) { + PERROR("lttng pipe"); + goto error; + } + p->r_state = LTTNG_PIPE_STATE_OPENED; + p->w_state = LTTNG_PIPE_STATE_OPENED; + + ret = _pipe_set_flags(p, flags); + if (ret) { + goto error; + } + + p->flags = flags; + + return p; +error: + lttng_pipe_destroy(p); + return NULL; +} + +/* + * Open a new lttng pipe at path and set flags using fcntl(). + * + * Return a newly allocated lttng pipe on success or else NULL. + */ +struct lttng_pipe *lttng_pipe_named_open(const char *path, mode_t mode, + int flags) +{ + int ret, fd_r, fd_w; + struct lttng_pipe *pipe; + + pipe = _pipe_create(); + if (!pipe) { + goto error; + } + + ret = mkfifo(path, mode); + if (ret) { + PERROR("mkfifo"); + goto error; + } + + fd_r = open(path, O_RDONLY | O_NONBLOCK); + if (fd_r < 0) { + PERROR("open fifo"); + goto error; + } + pipe->fd[0] = fd_r; + pipe->r_state = LTTNG_PIPE_STATE_OPENED; + + fd_w = open(path, O_WRONLY | O_NONBLOCK); + if (fd_w < 0) { + PERROR("open fifo"); + goto error; + } + pipe->fd[1] = fd_w; + pipe->w_state = LTTNG_PIPE_STATE_OPENED; + + ret = _pipe_set_flags(pipe, flags); + if (ret) { + goto error; + } + pipe->flags = flags; + + return pipe; +error: + lttng_pipe_destroy(pipe); + return NULL; +} + +/* + * Close read side of a lttng pipe. + * + * Return 0 on success else a negative value. + */ +int lttng_pipe_read_close(struct lttng_pipe *pipe) +{ + int ret; + + LTTNG_ASSERT(pipe); + + /* Handle read side first. */ + lock_read_side(pipe); + ret = _pipe_read_close(pipe); + unlock_read_side(pipe); + + return ret; +} + +/* + * Close write side of a lttng pipe. + * + * Return 0 on success else a negative value. + */ +int lttng_pipe_write_close(struct lttng_pipe *pipe) +{ + int ret; + + LTTNG_ASSERT(pipe); + + lock_write_side(pipe); + ret = _pipe_write_close(pipe); + unlock_write_side(pipe); + + return ret; +} + +/* + * Close both read and write side of a lttng pipe. + * + * Return 0 on success else a negative value. + */ +int lttng_pipe_close(struct lttng_pipe *pipe) +{ + int ret, ret_val = 0; + + LTTNG_ASSERT(pipe); + + ret = lttng_pipe_read_close(pipe); + if (ret < 0) { + ret_val = ret; + } + + ret = lttng_pipe_write_close(pipe); + if (ret < 0) { + ret_val = ret; + } + + return ret_val; +} + +/* + * Close and destroy a lttng pipe object. Finally, pipe is freed. + */ +void lttng_pipe_destroy(struct lttng_pipe *pipe) +{ + int ret; + + if (!pipe) { + return; + } + + /* + * Destroy should *never* be called with a locked mutex. These must always + * succeed so we unlock them after the close pipe below. + */ + ret = pthread_mutex_trylock(&pipe->read_mutex); + LTTNG_ASSERT(!ret); + ret = pthread_mutex_trylock(&pipe->write_mutex); + LTTNG_ASSERT(!ret); + + /* Close pipes WITHOUT trying to lock the pipes. */ + (void) _pipe_read_close(pipe); + (void) _pipe_write_close(pipe); + + unlock_read_side(pipe); + unlock_write_side(pipe); + + (void) pthread_mutex_destroy(&pipe->read_mutex); + (void) pthread_mutex_destroy(&pipe->write_mutex); + + free(pipe); +} + +/* + * Read on a lttng pipe and put the data in buf of at least size count. + * + * Return "count" on success. Return < count on error. errno can be used + * to check the actual error. + */ +ssize_t lttng_pipe_read(struct lttng_pipe *pipe, void *buf, size_t count) +{ + ssize_t ret; + + LTTNG_ASSERT(pipe); + LTTNG_ASSERT(buf); + + lock_read_side(pipe); + if (!lttng_pipe_is_read_open(pipe)) { + ret = -1; + errno = EBADF; + goto error; + } + ret = lttng_read(pipe->fd[0], buf, count); +error: + unlock_read_side(pipe); + return ret; +} + +/* + * Write on a lttng pipe using the data in buf and size of count. + * + * Return "count" on success. Return < count on error. errno can be used + * to check the actual error. + */ +ssize_t lttng_pipe_write(struct lttng_pipe *pipe, const void *buf, + size_t count) +{ + ssize_t ret; + + LTTNG_ASSERT(pipe); + LTTNG_ASSERT(buf); + + lock_write_side(pipe); + if (!lttng_pipe_is_write_open(pipe)) { + ret = -1; + errno = EBADF; + goto error; + } + ret = lttng_write(pipe->fd[1], buf, count); +error: + unlock_write_side(pipe); + return ret; +} + +/* + * Return and release the read end of the pipe. + * + * This call transfers the ownership of the read fd of the underlying pipe + * to the caller if it is still open. + * + * Returns the fd of the read end of the pipe, or -1 if it was already closed or + * released. + */ +int lttng_pipe_release_readfd(struct lttng_pipe *pipe) +{ + int ret; + + if (!pipe) { + ret = -1; + goto end; + } + + lock_read_side(pipe); + if (!lttng_pipe_is_read_open(pipe)) { + ret = -1; + goto end_unlock; + } + ret = pipe->fd[0]; + pipe->fd[0] = -1; + pipe->r_state = LTTNG_PIPE_STATE_CLOSED; +end_unlock: + unlock_read_side(pipe); +end: + return ret; +} + +/* + * Return and release the write end of the pipe. + * + * This call transfers the ownership of the write fd of the underlying pipe + * to the caller if it is still open. + * + * Returns the fd of the write end of the pipe, or -1 if it was alwritey closed + * or released. + */ +int lttng_pipe_release_writefd(struct lttng_pipe *pipe) +{ + int ret; + + if (!pipe) { + ret = -1; + goto end; + } + + lock_write_side(pipe); + if (!lttng_pipe_is_write_open(pipe)) { + ret = -1; + goto end_unlock; + } + ret = pipe->fd[1]; + pipe->fd[1] = -1; + pipe->w_state = LTTNG_PIPE_STATE_CLOSED; +end_unlock: + unlock_write_side(pipe); +end: + return ret; +} diff --git a/src/common/readwrite.c b/src/common/readwrite.c deleted file mode 100644 index 14ec171f9..000000000 --- a/src/common/readwrite.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2013 Mathieu Desnoyers - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#define _LGPL_SOURCE -#include -#include - -#include - -#include "readwrite.h" - -/* - * lttng_read and lttng_write take care of EINTR and partial read/write. - * Upon success, they return the "count" received as parameter. - * They can return a negative value if an error occurs. - * If a value lower than the requested "count" is returned, it means an - * error occurred. - * The error can be checked by querying errno. - */ -ssize_t lttng_read(int fd, void *buf, size_t count) -{ - size_t i = 0; - ssize_t ret; - - LTTNG_ASSERT(buf); - - /* - * Deny a read count that can be bigger then the returned value max size. - * This makes the function to never return an overflow value. - */ - if (count > SSIZE_MAX) { - return -EINVAL; - } - - do { - ret = read(fd, buf + i, count - i); - if (ret < 0) { - if (errno == EINTR) { - continue; /* retry operation */ - } else { - goto error; - } - } - i += ret; - LTTNG_ASSERT(i <= count); - } while (count - i > 0 && ret > 0); - return i; - -error: - if (i == 0) { - return -1; - } else { - return i; - } -} - -ssize_t lttng_write(int fd, const void *buf, size_t count) -{ - size_t i = 0; - ssize_t ret; - - LTTNG_ASSERT(buf); - - /* - * Deny a write count that can be bigger then the returned value max size. - * This makes the function to never return an overflow value. - */ - if (count > SSIZE_MAX) { - return -EINVAL; - } - - do { - ret = write(fd, buf + i, count - i); - if (ret < 0) { - if (errno == EINTR) { - continue; /* retry operation */ - } else { - goto error; - } - } - i += ret; - LTTNG_ASSERT(i <= count); - } while (count - i > 0 && ret > 0); - return i; - -error: - if (i == 0) { - return -1; - } else { - return i; - } -} diff --git a/src/common/readwrite.cpp b/src/common/readwrite.cpp new file mode 100644 index 000000000..857781c6a --- /dev/null +++ b/src/common/readwrite.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 Mathieu Desnoyers + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#define _LGPL_SOURCE +#include +#include + +#include + +#include "readwrite.h" + +/* + * lttng_read and lttng_write take care of EINTR and partial read/write. + * Upon success, they return the "count" received as parameter. + * They can return a negative value if an error occurs. + * If a value lower than the requested "count" is returned, it means an + * error occurred. + * The error can be checked by querying errno. + */ +ssize_t lttng_read(int fd, void *buf, size_t count) +{ + size_t i = 0; + ssize_t ret; + + LTTNG_ASSERT(buf); + + /* + * Deny a read count that can be bigger then the returned value max size. + * This makes the function to never return an overflow value. + */ + if (count > SSIZE_MAX) { + return -EINVAL; + } + + do { + ret = read(fd, (char *) buf + i, count - i); + if (ret < 0) { + if (errno == EINTR) { + continue; /* retry operation */ + } else { + goto error; + } + } + i += ret; + LTTNG_ASSERT(i <= count); + } while (count - i > 0 && ret > 0); + return i; + +error: + if (i == 0) { + return -1; + } else { + return i; + } +} + +ssize_t lttng_write(int fd, const void *buf, size_t count) +{ + size_t i = 0; + ssize_t ret; + + LTTNG_ASSERT(buf); + + /* + * Deny a write count that can be bigger then the returned value max size. + * This makes the function to never return an overflow value. + */ + if (count > SSIZE_MAX) { + return -EINVAL; + } + + do { + ret = write(fd, (char *) buf + i, count - i); + if (ret < 0) { + if (errno == EINTR) { + continue; /* retry operation */ + } else { + goto error; + } + } + i += ret; + LTTNG_ASSERT(i <= count); + } while (count - i > 0 && ret > 0); + return i; + +error: + if (i == 0) { + return -1; + } else { + return i; + } +} diff --git a/src/common/runas.c b/src/common/runas.c deleted file mode 100644 index 930c973ff..000000000 --- a/src/common/runas.c +++ /dev/null @@ -1,2033 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2011 Mathieu Desnoyers - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "runas.h" - -#define GETPW_BUFFER_FALLBACK_SIZE 4096 - -struct run_as_data; -struct run_as_ret; -typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value); - -enum run_as_cmd { - RUN_AS_MKDIR, - RUN_AS_MKDIRAT, - RUN_AS_MKDIR_RECURSIVE, - RUN_AS_MKDIRAT_RECURSIVE, - RUN_AS_OPEN, - RUN_AS_OPENAT, - RUN_AS_UNLINK, - RUN_AS_UNLINKAT, - RUN_AS_RMDIR, - RUN_AS_RMDIRAT, - RUN_AS_RMDIR_RECURSIVE, - RUN_AS_RMDIRAT_RECURSIVE, - RUN_AS_RENAME, - RUN_AS_RENAMEAT, - RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, - RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, - RUN_AS_GENERATE_FILTER_BYTECODE, -}; - -struct run_as_mkdir_data { - int dirfd; - char path[LTTNG_PATH_MAX]; - mode_t mode; -} LTTNG_PACKED; - -struct run_as_open_data { - int dirfd; - char path[LTTNG_PATH_MAX]; - int flags; - mode_t mode; -} LTTNG_PACKED; - -struct run_as_unlink_data { - int dirfd; - char path[LTTNG_PATH_MAX]; -} LTTNG_PACKED; - -struct run_as_rmdir_data { - int dirfd; - char path[LTTNG_PATH_MAX]; - int flags; /* enum lttng_directory_handle_rmdir_recursive_flags. */ -} LTTNG_PACKED; - -struct run_as_extract_elf_symbol_offset_data { - int fd; - char function[LTTNG_SYMBOL_NAME_LEN]; -} LTTNG_PACKED; - -struct run_as_extract_sdt_probe_offsets_data { - int fd; - char probe_name[LTTNG_SYMBOL_NAME_LEN]; - char provider_name[LTTNG_SYMBOL_NAME_LEN]; -} LTTNG_PACKED; - -struct run_as_generate_filter_bytecode_data { - char filter_expression[LTTNG_FILTER_MAX_LEN]; -} LTTNG_PACKED; - -struct run_as_rename_data { - /* - * [0] = old_dirfd - * [1] = new_dirfd - */ - int dirfds[2]; - char old_path[LTTNG_PATH_MAX]; - char new_path[LTTNG_PATH_MAX]; -} LTTNG_PACKED; - -struct run_as_open_ret { - int fd; -} LTTNG_PACKED; - -struct run_as_extract_elf_symbol_offset_ret { - uint64_t offset; -} LTTNG_PACKED; - -struct run_as_extract_sdt_probe_offsets_ret { - uint32_t num_offset; - uint64_t offsets[LTTNG_KERNEL_ABI_MAX_UPROBE_NUM]; -} LTTNG_PACKED; - -struct run_as_generate_filter_bytecode_ret { - /* A lttng_bytecode_filter struct with 'dynamic' payload. */ - char bytecode[LTTNG_FILTER_MAX_LEN]; -} LTTNG_PACKED; - -struct run_as_data { - enum run_as_cmd cmd; - union { - struct run_as_mkdir_data mkdir; - struct run_as_open_data open; - struct run_as_unlink_data unlink; - struct run_as_rmdir_data rmdir; - struct run_as_rename_data rename; - struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset; - struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets; - struct run_as_generate_filter_bytecode_data generate_filter_bytecode; - } u; - uid_t uid; - gid_t gid; -} LTTNG_PACKED; - -/* - * The run_as_ret structure holds the returned value and status of the command. - * - * The `u` union field holds the return value of the command; in most cases it - * represents the success or the failure of the command. In more complex - * commands, it holds a computed value. - * - * The _errno field is the errno recorded after the execution of the command. - * - * The _error fields is used the signify that return status of the command. For - * simple commands returning `int` the _error field will be the same as the - * ret_int field. In complex commands, it signify the success or failure of the - * command. - * - */ -struct run_as_ret { - union { - int ret; - struct run_as_open_ret open; - struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset; - struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets; - struct run_as_generate_filter_bytecode_ret generate_filter_bytecode; - } u; - int _errno; - bool _error; -} LTTNG_PACKED; - -#define COMMAND_IN_FDS(data_ptr) ({ \ - int *fds = NULL; \ - if (command_properties[data_ptr->cmd].in_fds_offset != -1) { \ - fds = (int *) ((char *) data_ptr + command_properties[data_ptr->cmd].in_fds_offset); \ - } \ - fds; \ -}) - -#define COMMAND_OUT_FDS(cmd, ret_ptr) ({ \ - int *fds = NULL; \ - if (command_properties[cmd].out_fds_offset != -1) { \ - fds = (int *) ((char *) ret_ptr + command_properties[cmd].out_fds_offset); \ - } \ - fds; \ -}) - -#define COMMAND_IN_FD_COUNT(data_ptr) ({ \ - command_properties[data_ptr->cmd].in_fd_count; \ -}) - -#define COMMAND_OUT_FD_COUNT(cmd) ({ \ - command_properties[cmd].out_fd_count; \ -}) - -#define COMMAND_USE_CWD_FD(data_ptr) command_properties[data_ptr->cmd].use_cwd_fd - -struct run_as_command_properties { - /* Set to -1 when not applicable. */ - ptrdiff_t in_fds_offset, out_fds_offset; - unsigned int in_fd_count, out_fd_count; - bool use_cwd_fd; -}; - -static const struct run_as_command_properties command_properties[] = { - [RUN_AS_MKDIR] = { - .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_MKDIRAT] = { - .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_MKDIR_RECURSIVE] = { - .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_MKDIRAT_RECURSIVE] = { - .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_OPEN] = { - .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), - .in_fd_count = 1, - .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), - .out_fd_count = 1, - .use_cwd_fd = true, - }, - [RUN_AS_OPENAT] = { - .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), - .in_fd_count = 1, - .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), - .out_fd_count = 1, - .use_cwd_fd = false, - }, - [RUN_AS_UNLINK] = { - .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_UNLINKAT] = { - .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_RMDIR_RECURSIVE] = { - .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_RMDIRAT_RECURSIVE] = { - .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_RMDIR] = { - .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_RMDIRAT] = { - .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_RENAME] = { - .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), - .in_fd_count = 2, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = true, - }, - [RUN_AS_RENAMEAT] = { - .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), - .in_fd_count = 2, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET] = { - .in_fds_offset = offsetof(struct run_as_data, - u.extract_elf_symbol_offset.fd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_EXTRACT_SDT_PROBE_OFFSETS] = { - .in_fds_offset = offsetof(struct run_as_data, - u.extract_sdt_probe_offsets.fd), - .in_fd_count = 1, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, - [RUN_AS_GENERATE_FILTER_BYTECODE] = { - .in_fds_offset = -1, - .in_fd_count = 0, - .out_fds_offset = -1, - .out_fd_count = 0, - .use_cwd_fd = false, - }, -}; - -struct run_as_worker { - pid_t pid; /* Worker PID. */ - int sockpair[2]; - char *procname; -}; - -/* Single global worker per process (for now). */ -static struct run_as_worker *global_worker; -/* Lock protecting the worker. */ -static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER; - -#ifdef VALGRIND -static -int use_clone(void) -{ - return 0; -} -#else -static -int use_clone(void) -{ - return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE"); -} -#endif - -/* - * Create recursively directory using the FULL path. - */ -static -int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value) -{ - const char *path; - mode_t mode; - struct lttng_directory_handle *handle; - - path = data->u.mkdir.path; - mode = data->u.mkdir.mode; - - handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd); - if (!handle) { - ret_value->_errno = errno; - ret_value->_error = true; - ret_value->u.ret = -1; - goto end; - } - /* Ownership of dirfd is transferred to the handle. */ - data->u.mkdir.dirfd = -1; - /* Safe to call as we have transitioned to the requested uid/gid. */ - ret_value->u.ret = lttng_directory_handle_create_subdirectory_recursive( - handle, path, mode); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value) -{ - const char *path; - mode_t mode; - struct lttng_directory_handle *handle; - - path = data->u.mkdir.path; - mode = data->u.mkdir.mode; - - handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd); - if (!handle) { - ret_value->u.ret = -1; - ret_value->_errno = errno; - ret_value->_error = true; - goto end; - } - /* Ownership of dirfd is transferred to the handle. */ - data->u.mkdir.dirfd = -1; - /* Safe to call as we have transitioned to the requested uid/gid. */ - ret_value->u.ret = lttng_directory_handle_create_subdirectory( - handle, path, mode); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _open(struct run_as_data *data, struct run_as_ret *ret_value) -{ - int fd; - struct lttng_directory_handle *handle; - - handle = lttng_directory_handle_create_from_dirfd(data->u.open.dirfd); - if (!handle) { - ret_value->_errno = errno; - ret_value->_error = true; - ret_value->u.ret = -1; - goto end; - } - /* Ownership of dirfd is transferred to the handle. */ - data->u.open.dirfd = -1; - - fd = lttng_directory_handle_open_file(handle, - data->u.open.path, data->u.open.flags, - data->u.open.mode); - if (fd < 0) { - ret_value->u.ret = -1; - ret_value->u.open.fd = -1; - } else { - ret_value->u.ret = 0; - ret_value->u.open.fd = fd; - } - - ret_value->_errno = errno; - ret_value->_error = fd < 0; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _unlink(struct run_as_data *data, struct run_as_ret *ret_value) -{ - struct lttng_directory_handle *handle; - - handle = lttng_directory_handle_create_from_dirfd(data->u.unlink.dirfd); - if (!handle) { - ret_value->u.ret = -1; - ret_value->_errno = errno; - ret_value->_error = true; - goto end; - } - - /* Ownership of dirfd is transferred to the handle. */ - data->u.unlink.dirfd = -1; - - ret_value->u.ret = lttng_directory_handle_unlink_file(handle, - data->u.unlink.path); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _rmdir(struct run_as_data *data, struct run_as_ret *ret_value) -{ - struct lttng_directory_handle *handle; - - handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd); - if (!handle) { - ret_value->u.ret = -1; - ret_value->_errno = errno; - ret_value->_error = true; - goto end; - } - - /* Ownership of dirfd is transferred to the handle. */ - data->u.rmdir.dirfd = -1; - - ret_value->u.ret = lttng_directory_handle_remove_subdirectory( - handle, data->u.rmdir.path); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value) -{ - struct lttng_directory_handle *handle; - - handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd); - if (!handle) { - ret_value->u.ret = -1; - ret_value->_errno = errno; - ret_value->_error = true; - goto end; - } - - /* Ownership of dirfd is transferred to the handle. */ - data->u.rmdir.dirfd = -1; - - ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive( - handle, data->u.rmdir.path, data->u.rmdir.flags); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - lttng_directory_handle_put(handle); -end: - return ret_value->u.ret; -} - -static -int _rename(struct run_as_data *data, struct run_as_ret *ret_value) -{ - const char *old_path, *new_path; - struct lttng_directory_handle *old_handle = NULL, *new_handle = NULL; - - old_path = data->u.rename.old_path; - new_path = data->u.rename.new_path; - - old_handle = lttng_directory_handle_create_from_dirfd( - data->u.rename.dirfds[0]); - if (!old_handle) { - ret_value->u.ret = -1; - goto end; - } - new_handle = lttng_directory_handle_create_from_dirfd( - data->u.rename.dirfds[1]); - if (!new_handle) { - ret_value->u.ret = -1; - goto end; - } - - /* Ownership of dirfds are transferred to the handles. */ - data->u.rename.dirfds[0] = data->u.rename.dirfds[1] = -1; - - /* Safe to call as we have transitioned to the requested uid/gid. */ - ret_value->u.ret = lttng_directory_handle_rename( - old_handle, old_path, new_handle, new_path); -end: - lttng_directory_handle_put(old_handle); - lttng_directory_handle_put(new_handle); - ret_value->_errno = errno; - ret_value->_error = (ret_value->u.ret) ? true : false; - return ret_value->u.ret; -} - -#ifdef HAVE_ELF_H -static -int _extract_elf_symbol_offset(struct run_as_data *data, - struct run_as_ret *ret_value) -{ - int ret = 0; - uint64_t offset; - - ret_value->_error = false; - ret = lttng_elf_get_symbol_offset(data->u.extract_elf_symbol_offset.fd, - data->u.extract_elf_symbol_offset.function, - &offset); - if (ret) { - DBG("Failed to extract ELF function offset"); - ret_value->_error = true; - } - ret_value->u.extract_elf_symbol_offset.offset = offset; - - return ret; -} - -static -int _extract_sdt_probe_offsets(struct run_as_data *data, - struct run_as_ret *ret_value) -{ - int ret = 0; - uint64_t *offsets = NULL; - uint32_t num_offset; - - ret_value->_error = false; - - /* On success, this call allocates the offsets paramater. */ - ret = lttng_elf_get_sdt_probe_offsets( - data->u.extract_sdt_probe_offsets.fd, - data->u.extract_sdt_probe_offsets.provider_name, - data->u.extract_sdt_probe_offsets.probe_name, - &offsets, &num_offset); - - if (ret) { - DBG("Failed to extract SDT probe offsets"); - ret_value->_error = true; - goto end; - } - - if (num_offset <= 0 || num_offset > LTTNG_KERNEL_ABI_MAX_UPROBE_NUM) { - DBG("Wrong number of probes."); - ret = -1; - ret_value->_error = true; - goto free_offset; - } - - /* Copy the content of the offsets array to the ret struct. */ - memcpy(ret_value->u.extract_sdt_probe_offsets.offsets, - offsets, num_offset * sizeof(uint64_t)); - - ret_value->u.extract_sdt_probe_offsets.num_offset = num_offset; - -free_offset: - free(offsets); -end: - return ret; -} -#else -static -int _extract_elf_symbol_offset(struct run_as_data *data, - struct run_as_ret *ret_value) -{ - ERR("Unimplemented runas command RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET"); - return -1; -} - -static -int _extract_sdt_probe_offsets(struct run_as_data *data, - struct run_as_ret *ret_value) -{ - ERR("Unimplemented runas command RUN_AS_EXTRACT_SDT_PROBE_OFFSETS"); - return -1; -} -#endif - -static -int _generate_filter_bytecode(struct run_as_data *data, - struct run_as_ret *ret_value) { - int ret = 0; - const char *filter_expression = NULL; - struct filter_parser_ctx *ctx = NULL; - - ret_value->_error = false; - - filter_expression = data->u.generate_filter_bytecode.filter_expression; - - if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) { - ret_value->_error = true; - ret = -1; - goto end; - } - - ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); - if (ret < 0) { - ret_value->_error = true; - ret = -1; - goto end; - } - - DBG("Size of bytecode generated: %u bytes.", - bytecode_get_len(&ctx->bytecode->b)); - - /* Copy the lttng_bytecode_filter object to the return structure. */ - memcpy(ret_value->u.generate_filter_bytecode.bytecode, - &ctx->bytecode->b, - sizeof(ctx->bytecode->b) + - bytecode_get_len(&ctx->bytecode->b)); - -end: - if (ctx) { - filter_bytecode_free(ctx); - filter_ir_free(ctx); - filter_parser_ctx_free(ctx); - } - - return ret; -} -static -run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) -{ - switch (cmd) { - case RUN_AS_MKDIR: - case RUN_AS_MKDIRAT: - return _mkdirat; - case RUN_AS_MKDIR_RECURSIVE: - case RUN_AS_MKDIRAT_RECURSIVE: - return _mkdirat_recursive; - case RUN_AS_OPEN: - case RUN_AS_OPENAT: - return _open; - case RUN_AS_UNLINK: - case RUN_AS_UNLINKAT: - return _unlink; - case RUN_AS_RMDIR: - case RUN_AS_RMDIRAT: - return _rmdir; - case RUN_AS_RMDIR_RECURSIVE: - case RUN_AS_RMDIRAT_RECURSIVE: - return _rmdir_recursive; - case RUN_AS_RENAME: - case RUN_AS_RENAMEAT: - return _rename; - case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: - return _extract_elf_symbol_offset; - case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: - return _extract_sdt_probe_offsets; - case RUN_AS_GENERATE_FILTER_BYTECODE: - return _generate_filter_bytecode; - default: - ERR("Unknown command %d", (int) cmd); - return NULL; - } -} - -static -int do_send_fds(int sock, const int *fds, unsigned int fd_count) -{ - ssize_t len; - unsigned int i; - - for (i = 0; i < fd_count; i++) { - if (fds[i] < 0) { - DBG("Attempt to send invalid file descriptor (fd = %i)", - fds[i]); - /* Return 0 as this is not a fatal error. */ - return 0; - } - } - - len = lttcomm_send_fds_unix_sock(sock, fds, fd_count); - return len < 0 ? -1 : 0; -} - -static -int do_recv_fds(int sock, int *fds, unsigned int fd_count) -{ - int ret = 0; - unsigned int i; - ssize_t len; - - len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count); - if (len == 0) { - ret = -1; - goto end; - } else if (len < 0) { - PERROR("Failed to receive file descriptors from socket"); - ret = -1; - goto end; - } - - for (i = 0; i < fd_count; i++) { - if (fds[i] < 0) { - ERR("Invalid file descriptor received from worker (fd = %i)", fds[i]); - /* Return 0 as this is not a fatal error. */ - } - } -end: - return ret; -} - -static -int send_fds_to_worker(const struct run_as_worker *worker, - const struct run_as_data *data) -{ - int ret = 0; - unsigned int i; - - if (COMMAND_USE_CWD_FD(data) || COMMAND_IN_FD_COUNT(data) == 0) { - goto end; - } - - for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { - if (COMMAND_IN_FDS(data)[i] < 0) { - ERR("Refusing to send invalid fd to worker (fd = %i)", - COMMAND_IN_FDS(data)[i]); - ret = -1; - goto end; - } - } - - ret = do_send_fds(worker->sockpair[0], COMMAND_IN_FDS(data), - COMMAND_IN_FD_COUNT(data)); - if (ret < 0) { - PERROR("Failed to send file descriptor to run-as worker"); - ret = -1; - goto end; - } -end: - return ret; -} - -static -int send_fds_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, - struct run_as_ret *run_as_ret) -{ - int ret = 0; - unsigned int i; - - if (COMMAND_OUT_FD_COUNT(cmd) == 0) { - goto end; - } - - ret = do_send_fds(worker->sockpair[1], COMMAND_OUT_FDS(cmd, run_as_ret), - COMMAND_OUT_FD_COUNT(cmd)); - if (ret < 0) { - PERROR("Failed to send file descriptor to master process"); - goto end; - } - - for (i = 0; i < COMMAND_OUT_FD_COUNT(cmd); i++) { - int fd = COMMAND_OUT_FDS(cmd, run_as_ret)[i]; - if (fd >= 0) { - int ret_close = close(fd); - - if (ret_close < 0) { - PERROR("Failed to close result file descriptor (fd = %i)", - fd); - } - } - } -end: - return ret; -} - -static -int recv_fds_from_worker(const struct run_as_worker *worker, enum run_as_cmd cmd, - struct run_as_ret *run_as_ret) -{ - int ret = 0; - - if (COMMAND_OUT_FD_COUNT(cmd) == 0) { - goto end; - } - - ret = do_recv_fds(worker->sockpair[0], COMMAND_OUT_FDS(cmd, run_as_ret), - COMMAND_OUT_FD_COUNT(cmd)); - if (ret < 0) { - PERROR("Failed to receive file descriptor from run-as worker"); - ret = -1; - } -end: - return ret; -} - -static -int recv_fds_from_master(struct run_as_worker *worker, struct run_as_data *data) -{ - int ret = 0; - - if (COMMAND_USE_CWD_FD(data)) { - unsigned int i; - - for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { - COMMAND_IN_FDS(data)[i] = AT_FDCWD; - } - goto end; - } - - if (COMMAND_IN_FD_COUNT(data) == 0) { - goto end; - } - - ret = do_recv_fds(worker->sockpair[1], COMMAND_IN_FDS(data), - COMMAND_IN_FD_COUNT(data)); - if (ret < 0) { - PERROR("Failed to receive file descriptors from master process"); - ret = -1; - } -end: - return ret; -} - -static -int cleanup_received_fds(struct run_as_data *data) -{ - int ret = 0, i; - - for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { - if (COMMAND_IN_FDS(data)[i] == -1) { - continue; - } - ret = close(COMMAND_IN_FDS(data)[i]); - if (ret) { - PERROR("Failed to close file descriptor received fd in run-as worker"); - goto end; - } - } -end: - return ret; -} - -static int get_user_infos_from_uid( - uid_t uid, char **username, gid_t *primary_gid) -{ - int ret; - char *buf = NULL; - long raw_get_pw_buf_size; - size_t get_pw_buf_size; - struct passwd pwd; - struct passwd *result = NULL; - - /* Fetch the max size for the temporary buffer. */ - errno = 0; - raw_get_pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); - if (raw_get_pw_buf_size < 0) { - if (errno != 0) { - PERROR("Failed to query _SC_GETPW_R_SIZE_MAX"); - goto error; - } - - /* Limit is indeterminate. */ - WARN("Failed to query _SC_GETPW_R_SIZE_MAX as it is " - "indeterminate; falling back to default buffer size"); - raw_get_pw_buf_size = GETPW_BUFFER_FALLBACK_SIZE; - } - - get_pw_buf_size = (size_t) raw_get_pw_buf_size; - - buf = zmalloc(get_pw_buf_size); - if (buf == NULL) { - PERROR("Failed to allocate buffer to get password file entries"); - goto error; - } - - ret = getpwuid_r(uid, &pwd, buf, get_pw_buf_size, &result); - if (ret < 0) { - PERROR("Failed to get user information for user: uid = %d", - (int) uid); - goto error; - } - - if (result == NULL) { - ERR("Failed to find user information in password entries: uid = %d", - (int) uid); - ret = -1; - goto error; - } - - *username = strdup(result->pw_name); - if (*username == NULL) { - PERROR("Failed to copy user name"); - goto error; - } - - *primary_gid = result->pw_gid; - -end: - free(buf); - return ret; -error: - *username = NULL; - *primary_gid = -1; - ret = -1; - goto end; -} - -static int demote_creds( - uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid) -{ - int ret = 0; - gid_t primary_gid; - char *username = NULL; - - /* Change the group id. */ - if (prev_gid != new_gid) { - ret = setegid(new_gid); - if (ret < 0) { - PERROR("Failed to set effective group id: new_gid = %d", - (int) new_gid); - goto end; - } - } - - /* Change the user id. */ - if (prev_uid != new_uid) { - ret = get_user_infos_from_uid(new_uid, &username, &primary_gid); - if (ret < 0) { - goto end; - } - - /* - * Initialize the supplementary group access list. - * - * This is needed to handle cases where the supplementary groups - * of the user the process is demoting-to would give it access - * to a given file/folder, but not it's primary group. - * - * e.g - * username: User1 - * Primary Group: User1 - * Secondary group: Disk, Network - * - * mkdir inside the following directory must work since User1 - * is part of the Network group. - * - * drwxrwx--- 2 root Network 4096 Jul 23 17:17 /tmp/my_folder/ - * - * - * The order of the following initgroups and seteuid calls is - * important here; - * Only a root process or one with CAP_SETGID capability can - * call the the initgroups() function. We must initialize the - * supplementary groups before we change the effective - * UID to a less-privileged user. - */ - ret = initgroups(username, primary_gid); - if (ret < 0) { - PERROR("Failed to init the supplementary group access list: " - "username = `%s`, primary gid = %d", username, - (int) primary_gid); - goto end; - } - - ret = seteuid(new_uid); - if (ret < 0) { - PERROR("Failed to set effective user id: new_uid = %d", - (int) new_uid); - goto end; - } - } -end: - free(username); - return ret; -} - -static int promote_creds( - uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid) -{ - int ret = 0; - gid_t primary_gid; - char *username = NULL; - - /* Change the group id. */ - if (prev_gid != new_gid) { - ret = setegid(new_gid); - if (ret < 0) { - PERROR("Failed to set effective group id: new_gid = %d", - (int) new_gid); - goto end; - } - } - - /* Change the user id. */ - if (prev_uid != new_uid) { - ret = get_user_infos_from_uid(new_uid, &username, &primary_gid); - if (ret < 0) { - goto end; - } - - /* - * seteuid call must be done before the initgroups call because - * we need to be privileged (CAP_SETGID) to call initgroups(). - */ - ret = seteuid(new_uid); - if (ret < 0) { - PERROR("Failed to set effective user id: new_uid = %d", - (int) new_uid); - goto end; - } - - /* - * Initialize the supplementary group access list. - * - * There is a possibility the groups we set in the following - * initgroups() call are not exactly the same as the ones we - * had when we originally demoted. This can happen if the - * /etc/group file is modified after the runas process is - * forked. This is very unlikely. - */ - ret = initgroups(username, primary_gid); - if (ret < 0) { - PERROR("Failed to init the supplementary group access " - "list: username = `%s`, primary gid = %d", - username, (int) primary_gid) - goto end; - } - } -end: - free(username); - return ret; -} - -/* - * Return < 0 on error, 0 if OK, 1 on hangup. - */ -static -int handle_one_cmd(struct run_as_worker *worker) -{ - int ret = 0, promote_ret; - struct run_as_data data = {}; - ssize_t readlen, writelen; - struct run_as_ret sendret = {}; - run_as_fct cmd; - const uid_t prev_ruid = getuid(); - const gid_t prev_rgid = getgid(); - - /* - * Stage 1: Receive run_as_data struct from the master. - * The structure contains the command type and all the parameters needed for - * its execution - */ - readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data, - sizeof(data)); - if (readlen == 0) { - /* hang up */ - ret = 1; - goto end; - } - if (readlen < sizeof(data)) { - PERROR("lttcomm_recv_unix_sock error"); - ret = -1; - goto end; - } - - cmd = run_as_enum_to_fct(data.cmd); - if (!cmd) { - ret = -1; - goto end; - } - - /* - * Stage 2: Receive file descriptor from master. - * Some commands need a file descriptor as input so if it's needed we - * receive the fd using the Unix socket. - */ - ret = recv_fds_from_master(worker, &data); - if (ret < 0) { - PERROR("recv_fd_from_master error"); - ret = -1; - goto end; - } - - ret = demote_creds(prev_ruid, prev_rgid, data.uid, data.gid); - if (ret < 0) { - goto write_return; - } - - /* - * Also set umask to 0 for mkdir executable bit. - */ - umask(0); - - /* - * Stage 3: Execute the command - */ - ret = (*cmd)(&data, &sendret); - if (ret < 0) { - DBG("Execution of command returned an error"); - } - -write_return: - ret = cleanup_received_fds(&data); - if (ret < 0) { - ERR("Error cleaning up FD"); - goto promote_back; - } - - /* - * Stage 4: Send run_as_ret structure to the master. - * This structure contain the return value of the command and the errno. - */ - writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, - sizeof(sendret)); - if (writelen < sizeof(sendret)) { - PERROR("lttcomm_send_unix_sock error"); - ret = -1; - goto promote_back; - } - - /* - * Stage 5: Send resulting file descriptors to the master. - */ - ret = send_fds_to_master(worker, data.cmd, &sendret); - if (ret < 0) { - DBG("Sending FD to master returned an error"); - } - - ret = 0; - -promote_back: - /* Return to previous uid/gid. */ - promote_ret = promote_creds(data.uid, data.gid, prev_ruid, prev_rgid); - if (promote_ret < 0) { - ERR("Failed to promote back to the initial credentials"); - } - -end: - return ret; -} - -static -int run_as_worker(struct run_as_worker *worker) -{ - int ret; - ssize_t writelen; - struct run_as_ret sendret; - size_t proc_orig_len; - - /* - * Initialize worker. Set a different process cmdline. - */ - proc_orig_len = strlen(worker->procname); - memset(worker->procname, 0, proc_orig_len); - strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len); - - ret = lttng_thread_setname(DEFAULT_RUN_AS_WORKER_NAME); - if (ret && ret != -ENOSYS) { - /* Don't fail as this is not essential. */ - DBG("Failed to set pthread name attribute"); - } - - memset(&sendret, 0, sizeof(sendret)); - - writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, - sizeof(sendret)); - if (writelen < sizeof(sendret)) { - PERROR("lttcomm_send_unix_sock error"); - ret = EXIT_FAILURE; - goto end; - } - - for (;;) { - ret = handle_one_cmd(worker); - if (ret < 0) { - ret = EXIT_FAILURE; - goto end; - } else if (ret > 0) { - break; - } else { - continue; /* Next command. */ - } - } - ret = EXIT_SUCCESS; -end: - return ret; -} - -static -int run_as_cmd(struct run_as_worker *worker, - enum run_as_cmd cmd, - struct run_as_data *data, - struct run_as_ret *ret_value, - uid_t uid, gid_t gid) -{ - int ret = 0; - ssize_t readlen, writelen; - - /* - * If we are non-root, we can only deal with our own uid. - */ - if (geteuid() != 0) { - if (uid != geteuid()) { - ret = -1; - ret_value->_errno = EPERM; - ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", - (int) uid, (int) geteuid()); - goto end; - } - } - - data->cmd = cmd; - data->uid = uid; - data->gid = gid; - - /* - * Stage 1: Send the run_as_data struct to the worker process - */ - writelen = lttcomm_send_unix_sock(worker->sockpair[0], data, - sizeof(*data)); - if (writelen < sizeof(*data)) { - PERROR("Error writing message to run_as"); - ret = -1; - ret_value->_errno = EIO; - goto end; - } - - /* - * Stage 2: Send file descriptor to the worker process if needed - */ - ret = send_fds_to_worker(worker, data); - if (ret) { - PERROR("do_send_fd error"); - ret = -1; - ret_value->_errno = EIO; - goto end; - } - - /* - * Stage 3: Wait for the execution of the command - */ - - /* - * Stage 4: Receive the run_as_ret struct containing the return value and - * errno - */ - readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value, - sizeof(*ret_value)); - if (!readlen) { - ERR("Run-as worker has hung-up during run_as_cmd"); - ret = -1; - ret_value->_errno = EIO; - goto end; - } else if (readlen < sizeof(*ret_value)) { - PERROR("Error reading response from run_as"); - ret = -1; - ret_value->_errno = errno; - goto end; - } - - if (ret_value->_error) { - /* Skip stage 5 on error as there will be no fd to receive. */ - goto end; - } - - /* - * Stage 5: Receive file descriptor if needed - */ - ret = recv_fds_from_worker(worker, cmd, ret_value); - if (ret < 0) { - ERR("Error receiving fd"); - ret = -1; - ret_value->_errno = EIO; - } - -end: - return ret; -} - -/* - * This is for debugging ONLY and should not be considered secure. - */ -static -int run_as_noworker(enum run_as_cmd cmd, - struct run_as_data *data, struct run_as_ret *ret_value, - uid_t uid, gid_t gid) -{ - int ret, saved_errno; - mode_t old_mask; - run_as_fct fct; - - fct = run_as_enum_to_fct(cmd); - if (!fct) { - errno = -ENOSYS; - ret = -1; - goto end; - } - old_mask = umask(0); - ret = fct(data, ret_value); - saved_errno = ret_value->_errno; - umask(old_mask); - errno = saved_errno; -end: - return ret; -} - -static -int reset_sighandler(void) -{ - int sig; - - DBG("Resetting run_as worker signal handlers to default"); - for (sig = 1; sig <= 31; sig++) { - (void) signal(sig, SIG_DFL); - } - return 0; -} - -static -void worker_sighandler(int sig) -{ - const char *signame; - - /* - * The worker will inherit its parent's signals since they are part of - * the same process group. However, in the case of SIGINT and SIGTERM, - * we want to give the worker a chance to teardown gracefully when its - * parent closes the command socket. - */ - switch (sig) { - case SIGINT: - signame = "SIGINT"; - break; - case SIGTERM: - signame = "SIGTERM"; - break; - default: - signame = NULL; - } - - if (signame) { - DBG("run_as worker received signal %s", signame); - } else { - DBG("run_as_worker received signal %d", sig); - } -} - -static -int set_worker_sighandlers(void) -{ - int ret = 0; - sigset_t sigset; - struct sigaction sa; - - if ((ret = sigemptyset(&sigset)) < 0) { - PERROR("sigemptyset"); - goto end; - } - - sa.sa_handler = worker_sighandler; - sa.sa_mask = sigset; - sa.sa_flags = 0; - if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { - PERROR("sigaction SIGINT"); - goto end; - } - - if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - PERROR("sigaction SIGTERM"); - goto end; - } - - DBG("run_as signal handler set for SIGTERM and SIGINT"); -end: - return ret; -} - -static -int run_as_create_worker_no_lock(const char *procname, - post_fork_cleanup_cb clean_up_func, - void *clean_up_user_data) -{ - pid_t pid; - int i, ret = 0; - ssize_t readlen; - struct run_as_ret recvret; - struct run_as_worker *worker; - - LTTNG_ASSERT(!global_worker); - if (!use_clone()) { - /* - * Don't initialize a worker, all run_as tasks will be performed - * in the current process. - */ - ret = 0; - goto end; - } - worker = zmalloc(sizeof(*worker)); - if (!worker) { - ret = -ENOMEM; - goto end; - } - worker->procname = strdup(procname); - if (!worker->procname) { - ret = -ENOMEM; - goto error_procname_alloc; - } - /* Create unix socket. */ - if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) { - ret = -1; - goto error_sock; - } - - /* Fork worker. */ - pid = fork(); - if (pid < 0) { - PERROR("fork"); - ret = -1; - goto error_fork; - } else if (pid == 0) { - /* Child */ - - reset_sighandler(); - - set_worker_sighandlers(); - - logger_set_thread_name("Run-as worker", true); - - if (clean_up_func) { - if (clean_up_func(clean_up_user_data) < 0) { - ERR("Run-as post-fork clean-up failed, exiting."); - exit(EXIT_FAILURE); - } - } - - /* Just close, no shutdown. */ - if (close(worker->sockpair[0])) { - PERROR("close"); - exit(EXIT_FAILURE); - } - - /* - * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1] - * Sockpair[1] is used as a control channel with the master - */ - for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { - if (i != worker->sockpair[1]) { - (void) close(i); - } - } - - worker->sockpair[0] = -1; - ret = run_as_worker(worker); - if (lttcomm_close_unix_sock(worker->sockpair[1])) { - PERROR("close"); - ret = -1; - } - worker->sockpair[1] = -1; - free(worker->procname); - free(worker); - LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret); - exit(ret ? EXIT_FAILURE : EXIT_SUCCESS); - } else { - /* Parent */ - - /* Just close, no shutdown. */ - if (close(worker->sockpair[1])) { - PERROR("close"); - ret = -1; - goto error_fork; - } - worker->sockpair[1] = -1; - worker->pid = pid; - /* Wait for worker to become ready. */ - readlen = lttcomm_recv_unix_sock(worker->sockpair[0], - &recvret, sizeof(recvret)); - if (readlen < sizeof(recvret)) { - ERR("readlen: %zd", readlen); - PERROR("Error reading response from run_as at creation"); - ret = -1; - goto error_fork; - } - global_worker = worker; - } -end: - return ret; - - /* Error handling. */ -error_fork: - for (i = 0; i < 2; i++) { - if (worker->sockpair[i] < 0) { - continue; - } - if (lttcomm_close_unix_sock(worker->sockpair[i])) { - PERROR("close"); - } - worker->sockpair[i] = -1; - } -error_sock: - free(worker->procname); -error_procname_alloc: - free(worker); - return ret; -} - -static -void run_as_destroy_worker_no_lock(void) -{ - struct run_as_worker *worker = global_worker; - - DBG("Destroying run_as worker"); - if (!worker) { - return; - } - /* Close unix socket */ - DBG("Closing run_as worker socket"); - if (lttcomm_close_unix_sock(worker->sockpair[0])) { - PERROR("close"); - } - worker->sockpair[0] = -1; - /* Wait for worker. */ - for (;;) { - int status; - pid_t wait_ret; - - wait_ret = waitpid(worker->pid, &status, 0); - if (wait_ret < 0) { - if (errno == EINTR) { - continue; - } - PERROR("waitpid"); - break; - } - - if (WIFEXITED(status)) { - LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR, - DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d", - WEXITSTATUS(status)); - break; - } else if (WIFSIGNALED(status)) { - ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d", - WTERMSIG(status)); - break; - } - } - free(worker->procname); - free(worker); - global_worker = NULL; -} - -static -int run_as_restart_worker(struct run_as_worker *worker) -{ - int ret = 0; - char *procname = NULL; - - procname = worker->procname; - - /* Close socket to run_as worker process and clean up the zombie process */ - run_as_destroy_worker_no_lock(); - - /* Create a new run_as worker process*/ - ret = run_as_create_worker_no_lock(procname, NULL, NULL); - if (ret < 0 ) { - ERR("Restarting the worker process failed"); - ret = -1; - goto err; - } -err: - return ret; -} - -static -int run_as(enum run_as_cmd cmd, struct run_as_data *data, - struct run_as_ret *ret_value, uid_t uid, gid_t gid) -{ - int ret, saved_errno; - - pthread_mutex_lock(&worker_lock); - if (use_clone()) { - DBG("Using run_as worker"); - - LTTNG_ASSERT(global_worker); - - ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid); - saved_errno = ret_value->_errno; - - /* - * If the worker thread crashed the errno is set to EIO. we log - * the error and start a new worker process. - */ - if (ret == -1 && saved_errno == EIO) { - DBG("Socket closed unexpectedly... " - "Restarting the worker process"); - ret = run_as_restart_worker(global_worker); - if (ret == -1) { - ERR("Failed to restart worker process."); - goto err; - } - } - } else { - DBG("Using run_as without worker"); - ret = run_as_noworker(cmd, data, ret_value, uid, gid); - } -err: - pthread_mutex_unlock(&worker_lock); - return ret; -} - -int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid); -} - -int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode, - uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, (int) mode, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.mkdir.path, path, - sizeof(data.u.mkdir.path)); - if (ret) { - ERR("Failed to copy path argument of mkdirat recursive command"); - goto error; - } - data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; - data.u.mkdir.mode = mode; - data.u.mkdir.dirfd = dirfd; - run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE, - &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid); -} - -int run_as_mkdirat(int dirfd, const char *path, mode_t mode, - uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, (int) mode, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.mkdir.path, path, - sizeof(data.u.mkdir.path)); - if (ret) { - ERR("Failed to copy path argument of mkdirat command"); - goto error; - } - data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; - data.u.mkdir.mode = mode; - data.u.mkdir.dirfd = dirfd; - run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT, - &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, - gid_t gid) -{ - return run_as_openat(AT_FDCWD, path, flags, mode, uid, gid); -} - -int run_as_openat(int dirfd, const char *path, int flags, mode_t mode, - uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("openat() fd = %d%s, path = %s, flags = %X, mode = %d, uid %d, gid %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, flags, (int) mode, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.open.path, path, sizeof(data.u.open.path)); - if (ret) { - ERR("Failed to copy path argument of open command"); - goto error; - } - data.u.open.flags = flags; - data.u.open.mode = mode; - data.u.open.dirfd = dirfd; - run_as(dirfd == AT_FDCWD ? RUN_AS_OPEN : RUN_AS_OPENAT, - &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret < 0 ? run_as_ret.u.ret : - run_as_ret.u.open.fd; -error: - return ret; -} - -int run_as_unlink(const char *path, uid_t uid, gid_t gid) -{ - return run_as_unlinkat(AT_FDCWD, path, uid, gid); -} - -int run_as_unlinkat(int dirfd, const char *path, uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("unlinkat() fd = %d%s, path = %s, uid = %d, gid = %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.unlink.path, path, - sizeof(data.u.unlink.path)); - if (ret) { - goto error; - } - data.u.unlink.dirfd = dirfd; - run_as(dirfd == AT_FDCWD ? RUN_AS_UNLINK : RUN_AS_UNLINKAT, &data, - &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_rmdir(const char *path, uid_t uid, gid_t gid) -{ - return run_as_rmdirat(AT_FDCWD, path, uid, gid); -} - -int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("rmdirat() fd = %d%s, path = %s, uid = %d, gid = %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.rmdir.path, path, - sizeof(data.u.rmdir.path)); - if (ret) { - goto error; - } - data.u.rmdir.dirfd = dirfd; - run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR : RUN_AS_RMDIRAT, &data, - &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags) -{ - return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags); -} - -int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("rmdirat() recursive fd = %d%s, path = %s, uid = %d, gid = %d", - dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - path, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.rmdir.path, path, - sizeof(data.u.rmdir.path)); - if (ret) { - goto error; - } - data.u.rmdir.dirfd = dirfd; - data.u.rmdir.flags = flags; - run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE, - &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_rename(const char *old, const char *new, uid_t uid, gid_t gid) -{ - return run_as_renameat(AT_FDCWD, old, AT_FDCWD, new, uid, gid); -} - -int run_as_renameat(int old_dirfd, const char *old_name, - int new_dirfd, const char *new_name, uid_t uid, gid_t gid) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("renameat() old_dirfd = %d%s, old_name = %s, new_dirfd = %d%s, new_name = %s, uid = %d, gid = %d", - old_dirfd, old_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - old_name, - new_dirfd, new_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", - new_name, (int) uid, (int) gid); - ret = lttng_strncpy(data.u.rename.old_path, old_name, - sizeof(data.u.rename.old_path)); - if (ret) { - goto error; - } - ret = lttng_strncpy(data.u.rename.new_path, new_name, - sizeof(data.u.rename.new_path)); - if (ret) { - goto error; - } - - data.u.rename.dirfds[0] = old_dirfd; - data.u.rename.dirfds[1] = new_dirfd; - run_as(old_dirfd == AT_FDCWD && new_dirfd == AT_FDCWD ? - RUN_AS_RENAME : RUN_AS_RENAMEAT, - &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - ret = run_as_ret.u.ret; -error: - return ret; -} - -int run_as_extract_elf_symbol_offset(int fd, const char* function, - uid_t uid, gid_t gid, uint64_t *offset) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("extract_elf_symbol_offset() on fd=%d and function=%s " - "with for uid %d and gid %d", fd, function, - (int) uid, (int) gid); - - data.u.extract_elf_symbol_offset.fd = fd; - - strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1); - data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - ret = lttng_strncpy(data.u.extract_elf_symbol_offset.function, - function, - sizeof(data.u.extract_elf_symbol_offset.function)); - if (ret) { - goto error; - } - - run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - if (run_as_ret._error) { - ret = -1; - goto error; - } - - *offset = run_as_ret.u.extract_elf_symbol_offset.offset; -error: - return ret; -} - -int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name, - const char* probe_name, uid_t uid, gid_t gid, - uint64_t **offsets, uint32_t *num_offset) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - - DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and " - "provider_name=%s with for uid %d and gid %d", fd, - probe_name, provider_name, (int) uid, (int) gid); - - data.u.extract_sdt_probe_offsets.fd = fd; - - ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, - sizeof(data.u.extract_sdt_probe_offsets.probe_name)); - if (ret) { - goto error; - } - ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.provider_name, - provider_name, - sizeof(data.u.extract_sdt_probe_offsets.provider_name)); - if (ret) { - goto error; - } - - run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - if (run_as_ret._error) { - ret = -1; - goto error; - } - - *num_offset = run_as_ret.u.extract_sdt_probe_offsets.num_offset; - *offsets = zmalloc(*num_offset * sizeof(uint64_t)); - if (!*offsets) { - ret = -ENOMEM; - goto error; - } - - memcpy(*offsets, run_as_ret.u.extract_sdt_probe_offsets.offsets, - *num_offset * sizeof(uint64_t)); -error: - return ret; -} - -int run_as_generate_filter_bytecode(const char *filter_expression, - const struct lttng_credentials *creds, - struct lttng_bytecode **bytecode) -{ - int ret; - struct run_as_data data = {}; - struct run_as_ret run_as_ret = {}; - const struct lttng_bytecode *view_bytecode = NULL; - struct lttng_bytecode *local_bytecode = NULL; - const uid_t uid = lttng_credentials_get_uid(creds); - const gid_t gid = lttng_credentials_get_gid(creds); - - DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d", - filter_expression, (int) uid, (int) gid); - - ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression, - sizeof(data.u.generate_filter_bytecode.filter_expression)); - if (ret) { - goto error; - } - - run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid); - errno = run_as_ret._errno; - if (run_as_ret._error) { - ret = -1; - goto error; - } - - view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode; - - local_bytecode = zmalloc(sizeof(*local_bytecode) + view_bytecode->len); - if (!local_bytecode) { - ret = -ENOMEM; - goto error; - } - - memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode, - sizeof(*local_bytecode) + view_bytecode->len); - *bytecode = local_bytecode; -error: - return ret; -} - -int run_as_create_worker(const char *procname, - post_fork_cleanup_cb clean_up_func, - void *clean_up_user_data) -{ - int ret; - - pthread_mutex_lock(&worker_lock); - ret = run_as_create_worker_no_lock(procname, clean_up_func, - clean_up_user_data); - pthread_mutex_unlock(&worker_lock); - return ret; -} - -void run_as_destroy_worker(void) -{ - pthread_mutex_lock(&worker_lock); - run_as_destroy_worker_no_lock(); - pthread_mutex_unlock(&worker_lock); -} diff --git a/src/common/runas.cpp b/src/common/runas.cpp new file mode 100644 index 000000000..3c9eea8c3 --- /dev/null +++ b/src/common/runas.cpp @@ -0,0 +1,2033 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2011 Mathieu Desnoyers + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "runas.h" + +#define GETPW_BUFFER_FALLBACK_SIZE 4096 + +struct run_as_data; +struct run_as_ret; +typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value); + +enum run_as_cmd { + RUN_AS_MKDIR, + RUN_AS_MKDIRAT, + RUN_AS_MKDIR_RECURSIVE, + RUN_AS_MKDIRAT_RECURSIVE, + RUN_AS_OPEN, + RUN_AS_OPENAT, + RUN_AS_UNLINK, + RUN_AS_UNLINKAT, + RUN_AS_RMDIR, + RUN_AS_RMDIRAT, + RUN_AS_RMDIR_RECURSIVE, + RUN_AS_RMDIRAT_RECURSIVE, + RUN_AS_RENAME, + RUN_AS_RENAMEAT, + RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, + RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, + RUN_AS_GENERATE_FILTER_BYTECODE, +}; + +struct run_as_mkdir_data { + int dirfd; + char path[LTTNG_PATH_MAX]; + mode_t mode; +} LTTNG_PACKED; + +struct run_as_open_data { + int dirfd; + char path[LTTNG_PATH_MAX]; + int flags; + mode_t mode; +} LTTNG_PACKED; + +struct run_as_unlink_data { + int dirfd; + char path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; + +struct run_as_rmdir_data { + int dirfd; + char path[LTTNG_PATH_MAX]; + int flags; /* enum lttng_directory_handle_rmdir_recursive_flags. */ +} LTTNG_PACKED; + +struct run_as_extract_elf_symbol_offset_data { + int fd; + char function[LTTNG_SYMBOL_NAME_LEN]; +} LTTNG_PACKED; + +struct run_as_extract_sdt_probe_offsets_data { + int fd; + char probe_name[LTTNG_SYMBOL_NAME_LEN]; + char provider_name[LTTNG_SYMBOL_NAME_LEN]; +} LTTNG_PACKED; + +struct run_as_generate_filter_bytecode_data { + char filter_expression[LTTNG_FILTER_MAX_LEN]; +} LTTNG_PACKED; + +struct run_as_rename_data { + /* + * [0] = old_dirfd + * [1] = new_dirfd + */ + int dirfds[2]; + char old_path[LTTNG_PATH_MAX]; + char new_path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; + +struct run_as_open_ret { + int fd; +} LTTNG_PACKED; + +struct run_as_extract_elf_symbol_offset_ret { + uint64_t offset; +} LTTNG_PACKED; + +struct run_as_extract_sdt_probe_offsets_ret { + uint32_t num_offset; + uint64_t offsets[LTTNG_KERNEL_ABI_MAX_UPROBE_NUM]; +} LTTNG_PACKED; + +struct run_as_generate_filter_bytecode_ret { + /* A lttng_bytecode_filter struct with 'dynamic' payload. */ + char bytecode[LTTNG_FILTER_MAX_LEN]; +} LTTNG_PACKED; + +struct run_as_data { + enum run_as_cmd cmd; + union { + struct run_as_mkdir_data mkdir; + struct run_as_open_data open; + struct run_as_unlink_data unlink; + struct run_as_rmdir_data rmdir; + struct run_as_rename_data rename; + struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset; + struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets; + struct run_as_generate_filter_bytecode_data generate_filter_bytecode; + } u; + uid_t uid; + gid_t gid; +} LTTNG_PACKED; + +/* + * The run_as_ret structure holds the returned value and status of the command. + * + * The `u` union field holds the return value of the command; in most cases it + * represents the success or the failure of the command. In more complex + * commands, it holds a computed value. + * + * The _errno field is the errno recorded after the execution of the command. + * + * The _error fields is used the signify that return status of the command. For + * simple commands returning `int` the _error field will be the same as the + * ret_int field. In complex commands, it signify the success or failure of the + * command. + * + */ +struct run_as_ret { + union { + int ret; + struct run_as_open_ret open; + struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset; + struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets; + struct run_as_generate_filter_bytecode_ret generate_filter_bytecode; + } u; + int _errno; + bool _error; +} LTTNG_PACKED; + +#define COMMAND_IN_FDS(data_ptr) ({ \ + int *fds = NULL; \ + if (command_properties[data_ptr->cmd].in_fds_offset != -1) { \ + fds = (int *) ((char *) data_ptr + command_properties[data_ptr->cmd].in_fds_offset); \ + } \ + fds; \ +}) + +#define COMMAND_OUT_FDS(cmd, ret_ptr) ({ \ + int *fds = NULL; \ + if (command_properties[cmd].out_fds_offset != -1) { \ + fds = (int *) ((char *) ret_ptr + command_properties[cmd].out_fds_offset); \ + } \ + fds; \ +}) + +#define COMMAND_IN_FD_COUNT(data_ptr) ({ \ + command_properties[data_ptr->cmd].in_fd_count; \ +}) + +#define COMMAND_OUT_FD_COUNT(cmd) ({ \ + command_properties[cmd].out_fd_count; \ +}) + +#define COMMAND_USE_CWD_FD(data_ptr) command_properties[data_ptr->cmd].use_cwd_fd + +struct run_as_command_properties { + /* Set to -1 when not applicable. */ + ptrdiff_t in_fds_offset, out_fds_offset; + unsigned int in_fd_count, out_fd_count; + bool use_cwd_fd; +}; + +static const struct run_as_command_properties command_properties[] = { + { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.mkdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), + .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), + .in_fd_count = 1, + .out_fd_count = 1, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.open.dirfd), + .out_fds_offset = offsetof(struct run_as_ret, u.open.fd), + .in_fd_count = 1, + .out_fd_count = 1, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.unlink.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rmdir.dirfd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), + .out_fds_offset = -1, + .in_fd_count = 2, + .out_fd_count = 0, + .use_cwd_fd = true, + }, + { + .in_fds_offset = offsetof(struct run_as_data, u.rename.dirfds), + .out_fds_offset = -1, + .in_fd_count = 2, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, + u.extract_elf_symbol_offset.fd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = offsetof(struct run_as_data, + u.extract_sdt_probe_offsets.fd), + .out_fds_offset = -1, + .in_fd_count = 1, + .out_fd_count = 0, + .use_cwd_fd = false, + }, + { + .in_fds_offset = -1, + .out_fds_offset = -1, + .in_fd_count = 0, + .out_fd_count = 0, + .use_cwd_fd = false, + }, +}; + +struct run_as_worker_data { + pid_t pid; /* Worker PID. */ + int sockpair[2]; + char *procname; +}; + +/* Single global worker per process (for now). */ +static run_as_worker_data *global_worker; +/* Lock protecting the worker. */ +static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER; + +#ifdef VALGRIND +static +int use_clone(void) +{ + return 0; +} +#else +static +int use_clone(void) +{ + return !lttng_secure_getenv("LTTNG_DEBUG_NOCLONE"); +} +#endif + +/* + * Create recursively directory using the FULL path. + */ +static +int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value) +{ + const char *path; + mode_t mode; + struct lttng_directory_handle *handle; + + path = data->u.mkdir.path; + mode = data->u.mkdir.mode; + + handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd); + if (!handle) { + ret_value->_errno = errno; + ret_value->_error = true; + ret_value->u.ret = -1; + goto end; + } + /* Ownership of dirfd is transferred to the handle. */ + data->u.mkdir.dirfd = -1; + /* Safe to call as we have transitioned to the requested uid/gid. */ + ret_value->u.ret = lttng_directory_handle_create_subdirectory_recursive( + handle, path, mode); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value) +{ + const char *path; + mode_t mode; + struct lttng_directory_handle *handle; + + path = data->u.mkdir.path; + mode = data->u.mkdir.mode; + + handle = lttng_directory_handle_create_from_dirfd(data->u.mkdir.dirfd); + if (!handle) { + ret_value->u.ret = -1; + ret_value->_errno = errno; + ret_value->_error = true; + goto end; + } + /* Ownership of dirfd is transferred to the handle. */ + data->u.mkdir.dirfd = -1; + /* Safe to call as we have transitioned to the requested uid/gid. */ + ret_value->u.ret = lttng_directory_handle_create_subdirectory( + handle, path, mode); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _open(struct run_as_data *data, struct run_as_ret *ret_value) +{ + int fd; + struct lttng_directory_handle *handle; + + handle = lttng_directory_handle_create_from_dirfd(data->u.open.dirfd); + if (!handle) { + ret_value->_errno = errno; + ret_value->_error = true; + ret_value->u.ret = -1; + goto end; + } + /* Ownership of dirfd is transferred to the handle. */ + data->u.open.dirfd = -1; + + fd = lttng_directory_handle_open_file(handle, + data->u.open.path, data->u.open.flags, + data->u.open.mode); + if (fd < 0) { + ret_value->u.ret = -1; + ret_value->u.open.fd = -1; + } else { + ret_value->u.ret = 0; + ret_value->u.open.fd = fd; + } + + ret_value->_errno = errno; + ret_value->_error = fd < 0; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _unlink(struct run_as_data *data, struct run_as_ret *ret_value) +{ + struct lttng_directory_handle *handle; + + handle = lttng_directory_handle_create_from_dirfd(data->u.unlink.dirfd); + if (!handle) { + ret_value->u.ret = -1; + ret_value->_errno = errno; + ret_value->_error = true; + goto end; + } + + /* Ownership of dirfd is transferred to the handle. */ + data->u.unlink.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_unlink_file(handle, + data->u.unlink.path); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _rmdir(struct run_as_data *data, struct run_as_ret *ret_value) +{ + struct lttng_directory_handle *handle; + + handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd); + if (!handle) { + ret_value->u.ret = -1; + ret_value->_errno = errno; + ret_value->_error = true; + goto end; + } + + /* Ownership of dirfd is transferred to the handle. */ + data->u.rmdir.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_remove_subdirectory( + handle, data->u.rmdir.path); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value) +{ + struct lttng_directory_handle *handle; + + handle = lttng_directory_handle_create_from_dirfd(data->u.rmdir.dirfd); + if (!handle) { + ret_value->u.ret = -1; + ret_value->_errno = errno; + ret_value->_error = true; + goto end; + } + + /* Ownership of dirfd is transferred to the handle. */ + data->u.rmdir.dirfd = -1; + + ret_value->u.ret = lttng_directory_handle_remove_subdirectory_recursive( + handle, data->u.rmdir.path, data->u.rmdir.flags); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + lttng_directory_handle_put(handle); +end: + return ret_value->u.ret; +} + +static +int _rename(struct run_as_data *data, struct run_as_ret *ret_value) +{ + const char *old_path, *new_path; + struct lttng_directory_handle *old_handle = NULL, *new_handle = NULL; + + old_path = data->u.rename.old_path; + new_path = data->u.rename.new_path; + + old_handle = lttng_directory_handle_create_from_dirfd( + data->u.rename.dirfds[0]); + if (!old_handle) { + ret_value->u.ret = -1; + goto end; + } + new_handle = lttng_directory_handle_create_from_dirfd( + data->u.rename.dirfds[1]); + if (!new_handle) { + ret_value->u.ret = -1; + goto end; + } + + /* Ownership of dirfds are transferred to the handles. */ + data->u.rename.dirfds[0] = data->u.rename.dirfds[1] = -1; + + /* Safe to call as we have transitioned to the requested uid/gid. */ + ret_value->u.ret = lttng_directory_handle_rename( + old_handle, old_path, new_handle, new_path); +end: + lttng_directory_handle_put(old_handle); + lttng_directory_handle_put(new_handle); + ret_value->_errno = errno; + ret_value->_error = (ret_value->u.ret) ? true : false; + return ret_value->u.ret; +} + +#ifdef HAVE_ELF_H +static +int _extract_elf_symbol_offset(struct run_as_data *data, + struct run_as_ret *ret_value) +{ + int ret = 0; + uint64_t offset; + + ret_value->_error = false; + ret = lttng_elf_get_symbol_offset(data->u.extract_elf_symbol_offset.fd, + data->u.extract_elf_symbol_offset.function, + &offset); + if (ret) { + DBG("Failed to extract ELF function offset"); + ret_value->_error = true; + } + ret_value->u.extract_elf_symbol_offset.offset = offset; + + return ret; +} + +static +int _extract_sdt_probe_offsets(struct run_as_data *data, + struct run_as_ret *ret_value) +{ + int ret = 0; + uint64_t *offsets = NULL; + uint32_t num_offset; + + ret_value->_error = false; + + /* On success, this call allocates the offsets paramater. */ + ret = lttng_elf_get_sdt_probe_offsets( + data->u.extract_sdt_probe_offsets.fd, + data->u.extract_sdt_probe_offsets.provider_name, + data->u.extract_sdt_probe_offsets.probe_name, + &offsets, &num_offset); + + if (ret) { + DBG("Failed to extract SDT probe offsets"); + ret_value->_error = true; + goto end; + } + + if (num_offset <= 0 || num_offset > LTTNG_KERNEL_ABI_MAX_UPROBE_NUM) { + DBG("Wrong number of probes."); + ret = -1; + ret_value->_error = true; + goto free_offset; + } + + /* Copy the content of the offsets array to the ret struct. */ + memcpy(ret_value->u.extract_sdt_probe_offsets.offsets, + offsets, num_offset * sizeof(uint64_t)); + + ret_value->u.extract_sdt_probe_offsets.num_offset = num_offset; + +free_offset: + free(offsets); +end: + return ret; +} +#else +static +int _extract_elf_symbol_offset(struct run_as_data *data, + struct run_as_ret *ret_value) +{ + ERR("Unimplemented runas command RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET"); + return -1; +} + +static +int _extract_sdt_probe_offsets(struct run_as_data *data, + struct run_as_ret *ret_value) +{ + ERR("Unimplemented runas command RUN_AS_EXTRACT_SDT_PROBE_OFFSETS"); + return -1; +} +#endif + +static +int _generate_filter_bytecode(struct run_as_data *data, + struct run_as_ret *ret_value) { + int ret = 0; + const char *filter_expression = NULL; + struct filter_parser_ctx *ctx = NULL; + + ret_value->_error = false; + + filter_expression = data->u.generate_filter_bytecode.filter_expression; + + if (lttng_strnlen(filter_expression, LTTNG_FILTER_MAX_LEN - 1) == LTTNG_FILTER_MAX_LEN - 1) { + ret_value->_error = true; + ret = -1; + goto end; + } + + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); + if (ret < 0) { + ret_value->_error = true; + ret = -1; + goto end; + } + + DBG("Size of bytecode generated: %u bytes.", + bytecode_get_len(&ctx->bytecode->b)); + + /* Copy the lttng_bytecode_filter object to the return structure. */ + memcpy(ret_value->u.generate_filter_bytecode.bytecode, + &ctx->bytecode->b, + sizeof(ctx->bytecode->b) + + bytecode_get_len(&ctx->bytecode->b)); + +end: + if (ctx) { + filter_bytecode_free(ctx); + filter_ir_free(ctx); + filter_parser_ctx_free(ctx); + } + + return ret; +} +static +run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd) +{ + switch (cmd) { + case RUN_AS_MKDIR: + case RUN_AS_MKDIRAT: + return _mkdirat; + case RUN_AS_MKDIR_RECURSIVE: + case RUN_AS_MKDIRAT_RECURSIVE: + return _mkdirat_recursive; + case RUN_AS_OPEN: + case RUN_AS_OPENAT: + return _open; + case RUN_AS_UNLINK: + case RUN_AS_UNLINKAT: + return _unlink; + case RUN_AS_RMDIR: + case RUN_AS_RMDIRAT: + return _rmdir; + case RUN_AS_RMDIR_RECURSIVE: + case RUN_AS_RMDIRAT_RECURSIVE: + return _rmdir_recursive; + case RUN_AS_RENAME: + case RUN_AS_RENAMEAT: + return _rename; + case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET: + return _extract_elf_symbol_offset; + case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS: + return _extract_sdt_probe_offsets; + case RUN_AS_GENERATE_FILTER_BYTECODE: + return _generate_filter_bytecode; + default: + ERR("Unknown command %d", (int) cmd); + return NULL; + } +} + +static +int do_send_fds(int sock, const int *fds, unsigned int fd_count) +{ + ssize_t len; + unsigned int i; + + for (i = 0; i < fd_count; i++) { + if (fds[i] < 0) { + DBG("Attempt to send invalid file descriptor (fd = %i)", + fds[i]); + /* Return 0 as this is not a fatal error. */ + return 0; + } + } + + len = lttcomm_send_fds_unix_sock(sock, fds, fd_count); + return len < 0 ? -1 : 0; +} + +static +int do_recv_fds(int sock, int *fds, unsigned int fd_count) +{ + int ret = 0; + unsigned int i; + ssize_t len; + + len = lttcomm_recv_fds_unix_sock(sock, fds, fd_count); + if (len == 0) { + ret = -1; + goto end; + } else if (len < 0) { + PERROR("Failed to receive file descriptors from socket"); + ret = -1; + goto end; + } + + for (i = 0; i < fd_count; i++) { + if (fds[i] < 0) { + ERR("Invalid file descriptor received from worker (fd = %i)", fds[i]); + /* Return 0 as this is not a fatal error. */ + } + } +end: + return ret; +} + +static +int send_fds_to_worker(const run_as_worker_data *worker, + const struct run_as_data *data) +{ + int ret = 0; + unsigned int i; + + if (COMMAND_USE_CWD_FD(data) || COMMAND_IN_FD_COUNT(data) == 0) { + goto end; + } + + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + if (COMMAND_IN_FDS(data)[i] < 0) { + ERR("Refusing to send invalid fd to worker (fd = %i)", + COMMAND_IN_FDS(data)[i]); + ret = -1; + goto end; + } + } + + ret = do_send_fds(worker->sockpair[0], COMMAND_IN_FDS(data), + COMMAND_IN_FD_COUNT(data)); + if (ret < 0) { + PERROR("Failed to send file descriptor to run-as worker"); + ret = -1; + goto end; + } +end: + return ret; +} + +static +int send_fds_to_master(run_as_worker_data *worker, enum run_as_cmd cmd, + struct run_as_ret *run_as_ret) +{ + int ret = 0; + unsigned int i; + + if (COMMAND_OUT_FD_COUNT(cmd) == 0) { + goto end; + } + + ret = do_send_fds(worker->sockpair[1], COMMAND_OUT_FDS(cmd, run_as_ret), + COMMAND_OUT_FD_COUNT(cmd)); + if (ret < 0) { + PERROR("Failed to send file descriptor to master process"); + goto end; + } + + for (i = 0; i < COMMAND_OUT_FD_COUNT(cmd); i++) { + int fd = COMMAND_OUT_FDS(cmd, run_as_ret)[i]; + if (fd >= 0) { + int ret_close = close(fd); + + if (ret_close < 0) { + PERROR("Failed to close result file descriptor (fd = %i)", + fd); + } + } + } +end: + return ret; +} + +static +int recv_fds_from_worker(const run_as_worker_data *worker, enum run_as_cmd cmd, + struct run_as_ret *run_as_ret) +{ + int ret = 0; + + if (COMMAND_OUT_FD_COUNT(cmd) == 0) { + goto end; + } + + ret = do_recv_fds(worker->sockpair[0], COMMAND_OUT_FDS(cmd, run_as_ret), + COMMAND_OUT_FD_COUNT(cmd)); + if (ret < 0) { + PERROR("Failed to receive file descriptor from run-as worker"); + ret = -1; + } +end: + return ret; +} + +static +int recv_fds_from_master(run_as_worker_data *worker, struct run_as_data *data) +{ + int ret = 0; + + if (COMMAND_USE_CWD_FD(data)) { + unsigned int i; + + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + COMMAND_IN_FDS(data)[i] = AT_FDCWD; + } + goto end; + } + + if (COMMAND_IN_FD_COUNT(data) == 0) { + goto end; + } + + ret = do_recv_fds(worker->sockpair[1], COMMAND_IN_FDS(data), + COMMAND_IN_FD_COUNT(data)); + if (ret < 0) { + PERROR("Failed to receive file descriptors from master process"); + ret = -1; + } +end: + return ret; +} + +static +int cleanup_received_fds(struct run_as_data *data) +{ + int ret = 0, i; + + for (i = 0; i < COMMAND_IN_FD_COUNT(data); i++) { + if (COMMAND_IN_FDS(data)[i] == -1) { + continue; + } + ret = close(COMMAND_IN_FDS(data)[i]); + if (ret) { + PERROR("Failed to close file descriptor received fd in run-as worker"); + goto end; + } + } +end: + return ret; +} + +static int get_user_infos_from_uid( + uid_t uid, char **username, gid_t *primary_gid) +{ + int ret; + char *buf = NULL; + long raw_get_pw_buf_size; + size_t get_pw_buf_size; + struct passwd pwd; + struct passwd *result = NULL; + + /* Fetch the max size for the temporary buffer. */ + errno = 0; + raw_get_pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (raw_get_pw_buf_size < 0) { + if (errno != 0) { + PERROR("Failed to query _SC_GETPW_R_SIZE_MAX"); + goto error; + } + + /* Limit is indeterminate. */ + WARN("Failed to query _SC_GETPW_R_SIZE_MAX as it is " + "indeterminate; falling back to default buffer size"); + raw_get_pw_buf_size = GETPW_BUFFER_FALLBACK_SIZE; + } + + get_pw_buf_size = (size_t) raw_get_pw_buf_size; + + buf = (char *) zmalloc(get_pw_buf_size); + if (buf == NULL) { + PERROR("Failed to allocate buffer to get password file entries"); + goto error; + } + + ret = getpwuid_r(uid, &pwd, buf, get_pw_buf_size, &result); + if (ret < 0) { + PERROR("Failed to get user information for user: uid = %d", + (int) uid); + goto error; + } + + if (result == NULL) { + ERR("Failed to find user information in password entries: uid = %d", + (int) uid); + ret = -1; + goto error; + } + + *username = strdup(result->pw_name); + if (*username == NULL) { + PERROR("Failed to copy user name"); + goto error; + } + + *primary_gid = result->pw_gid; + +end: + free(buf); + return ret; +error: + *username = NULL; + *primary_gid = -1; + ret = -1; + goto end; +} + +static int demote_creds( + uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid) +{ + int ret = 0; + gid_t primary_gid; + char *username = NULL; + + /* Change the group id. */ + if (prev_gid != new_gid) { + ret = setegid(new_gid); + if (ret < 0) { + PERROR("Failed to set effective group id: new_gid = %d", + (int) new_gid); + goto end; + } + } + + /* Change the user id. */ + if (prev_uid != new_uid) { + ret = get_user_infos_from_uid(new_uid, &username, &primary_gid); + if (ret < 0) { + goto end; + } + + /* + * Initialize the supplementary group access list. + * + * This is needed to handle cases where the supplementary groups + * of the user the process is demoting-to would give it access + * to a given file/folder, but not it's primary group. + * + * e.g + * username: User1 + * Primary Group: User1 + * Secondary group: Disk, Network + * + * mkdir inside the following directory must work since User1 + * is part of the Network group. + * + * drwxrwx--- 2 root Network 4096 Jul 23 17:17 /tmp/my_folder/ + * + * + * The order of the following initgroups and seteuid calls is + * important here; + * Only a root process or one with CAP_SETGID capability can + * call the the initgroups() function. We must initialize the + * supplementary groups before we change the effective + * UID to a less-privileged user. + */ + ret = initgroups(username, primary_gid); + if (ret < 0) { + PERROR("Failed to init the supplementary group access list: " + "username = `%s`, primary gid = %d", username, + (int) primary_gid); + goto end; + } + + ret = seteuid(new_uid); + if (ret < 0) { + PERROR("Failed to set effective user id: new_uid = %d", + (int) new_uid); + goto end; + } + } +end: + free(username); + return ret; +} + +static int promote_creds( + uid_t prev_uid, gid_t prev_gid, uid_t new_uid, gid_t new_gid) +{ + int ret = 0; + gid_t primary_gid; + char *username = NULL; + + /* Change the group id. */ + if (prev_gid != new_gid) { + ret = setegid(new_gid); + if (ret < 0) { + PERROR("Failed to set effective group id: new_gid = %d", + (int) new_gid); + goto end; + } + } + + /* Change the user id. */ + if (prev_uid != new_uid) { + ret = get_user_infos_from_uid(new_uid, &username, &primary_gid); + if (ret < 0) { + goto end; + } + + /* + * seteuid call must be done before the initgroups call because + * we need to be privileged (CAP_SETGID) to call initgroups(). + */ + ret = seteuid(new_uid); + if (ret < 0) { + PERROR("Failed to set effective user id: new_uid = %d", + (int) new_uid); + goto end; + } + + /* + * Initialize the supplementary group access list. + * + * There is a possibility the groups we set in the following + * initgroups() call are not exactly the same as the ones we + * had when we originally demoted. This can happen if the + * /etc/group file is modified after the runas process is + * forked. This is very unlikely. + */ + ret = initgroups(username, primary_gid); + if (ret < 0) { + PERROR("Failed to init the supplementary group access " + "list: username = `%s`, primary gid = %d", + username, (int) primary_gid) + goto end; + } + } +end: + free(username); + return ret; +} + +/* + * Return < 0 on error, 0 if OK, 1 on hangup. + */ +static +int handle_one_cmd(run_as_worker_data *worker) +{ + int ret = 0, promote_ret; + struct run_as_data data = {}; + ssize_t readlen, writelen; + struct run_as_ret sendret = {}; + run_as_fct cmd; + const uid_t prev_ruid = getuid(); + const gid_t prev_rgid = getgid(); + + /* + * Stage 1: Receive run_as_data struct from the master. + * The structure contains the command type and all the parameters needed for + * its execution + */ + readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data, + sizeof(data)); + if (readlen == 0) { + /* hang up */ + ret = 1; + goto end; + } + if (readlen < sizeof(data)) { + PERROR("lttcomm_recv_unix_sock error"); + ret = -1; + goto end; + } + + cmd = run_as_enum_to_fct(data.cmd); + if (!cmd) { + ret = -1; + goto end; + } + + /* + * Stage 2: Receive file descriptor from master. + * Some commands need a file descriptor as input so if it's needed we + * receive the fd using the Unix socket. + */ + ret = recv_fds_from_master(worker, &data); + if (ret < 0) { + PERROR("recv_fd_from_master error"); + ret = -1; + goto end; + } + + ret = demote_creds(prev_ruid, prev_rgid, data.uid, data.gid); + if (ret < 0) { + goto write_return; + } + + /* + * Also set umask to 0 for mkdir executable bit. + */ + umask(0); + + /* + * Stage 3: Execute the command + */ + ret = (*cmd)(&data, &sendret); + if (ret < 0) { + DBG("Execution of command returned an error"); + } + +write_return: + ret = cleanup_received_fds(&data); + if (ret < 0) { + ERR("Error cleaning up FD"); + goto promote_back; + } + + /* + * Stage 4: Send run_as_ret structure to the master. + * This structure contain the return value of the command and the errno. + */ + writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, + sizeof(sendret)); + if (writelen < sizeof(sendret)) { + PERROR("lttcomm_send_unix_sock error"); + ret = -1; + goto promote_back; + } + + /* + * Stage 5: Send resulting file descriptors to the master. + */ + ret = send_fds_to_master(worker, data.cmd, &sendret); + if (ret < 0) { + DBG("Sending FD to master returned an error"); + } + + ret = 0; + +promote_back: + /* Return to previous uid/gid. */ + promote_ret = promote_creds(data.uid, data.gid, prev_ruid, prev_rgid); + if (promote_ret < 0) { + ERR("Failed to promote back to the initial credentials"); + } + +end: + return ret; +} + +static +int run_as_worker(run_as_worker_data *worker) +{ + int ret; + ssize_t writelen; + struct run_as_ret sendret; + size_t proc_orig_len; + + /* + * Initialize worker. Set a different process cmdline. + */ + proc_orig_len = strlen(worker->procname); + memset(worker->procname, 0, proc_orig_len); + strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len); + + ret = lttng_thread_setname(DEFAULT_RUN_AS_WORKER_NAME); + if (ret && ret != -ENOSYS) { + /* Don't fail as this is not essential. */ + DBG("Failed to set pthread name attribute"); + } + + memset(&sendret, 0, sizeof(sendret)); + + writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, + sizeof(sendret)); + if (writelen < sizeof(sendret)) { + PERROR("lttcomm_send_unix_sock error"); + ret = EXIT_FAILURE; + goto end; + } + + for (;;) { + ret = handle_one_cmd(worker); + if (ret < 0) { + ret = EXIT_FAILURE; + goto end; + } else if (ret > 0) { + break; + } else { + continue; /* Next command. */ + } + } + ret = EXIT_SUCCESS; +end: + return ret; +} + +static +int run_as_cmd(run_as_worker_data *worker, + enum run_as_cmd cmd, + struct run_as_data *data, + struct run_as_ret *ret_value, + uid_t uid, gid_t gid) +{ + int ret = 0; + ssize_t readlen, writelen; + + /* + * If we are non-root, we can only deal with our own uid. + */ + if (geteuid() != 0) { + if (uid != geteuid()) { + ret = -1; + ret_value->_errno = EPERM; + ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", + (int) uid, (int) geteuid()); + goto end; + } + } + + data->cmd = cmd; + data->uid = uid; + data->gid = gid; + + /* + * Stage 1: Send the run_as_data struct to the worker process + */ + writelen = lttcomm_send_unix_sock(worker->sockpair[0], data, + sizeof(*data)); + if (writelen < sizeof(*data)) { + PERROR("Error writing message to run_as"); + ret = -1; + ret_value->_errno = EIO; + goto end; + } + + /* + * Stage 2: Send file descriptor to the worker process if needed + */ + ret = send_fds_to_worker(worker, data); + if (ret) { + PERROR("do_send_fd error"); + ret = -1; + ret_value->_errno = EIO; + goto end; + } + + /* + * Stage 3: Wait for the execution of the command + */ + + /* + * Stage 4: Receive the run_as_ret struct containing the return value and + * errno + */ + readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value, + sizeof(*ret_value)); + if (!readlen) { + ERR("Run-as worker has hung-up during run_as_cmd"); + ret = -1; + ret_value->_errno = EIO; + goto end; + } else if (readlen < sizeof(*ret_value)) { + PERROR("Error reading response from run_as"); + ret = -1; + ret_value->_errno = errno; + goto end; + } + + if (ret_value->_error) { + /* Skip stage 5 on error as there will be no fd to receive. */ + goto end; + } + + /* + * Stage 5: Receive file descriptor if needed + */ + ret = recv_fds_from_worker(worker, cmd, ret_value); + if (ret < 0) { + ERR("Error receiving fd"); + ret = -1; + ret_value->_errno = EIO; + } + +end: + return ret; +} + +/* + * This is for debugging ONLY and should not be considered secure. + */ +static +int run_as_noworker(enum run_as_cmd cmd, + struct run_as_data *data, struct run_as_ret *ret_value, + uid_t uid, gid_t gid) +{ + int ret, saved_errno; + mode_t old_mask; + run_as_fct fct; + + fct = run_as_enum_to_fct(cmd); + if (!fct) { + errno = -ENOSYS; + ret = -1; + goto end; + } + old_mask = umask(0); + ret = fct(data, ret_value); + saved_errno = ret_value->_errno; + umask(old_mask); + errno = saved_errno; +end: + return ret; +} + +static +int reset_sighandler(void) +{ + int sig; + + DBG("Resetting run_as worker signal handlers to default"); + for (sig = 1; sig <= 31; sig++) { + (void) signal(sig, SIG_DFL); + } + return 0; +} + +static +void worker_sighandler(int sig) +{ + const char *signame; + + /* + * The worker will inherit its parent's signals since they are part of + * the same process group. However, in the case of SIGINT and SIGTERM, + * we want to give the worker a chance to teardown gracefully when its + * parent closes the command socket. + */ + switch (sig) { + case SIGINT: + signame = "SIGINT"; + break; + case SIGTERM: + signame = "SIGTERM"; + break; + default: + signame = NULL; + } + + if (signame) { + DBG("run_as worker received signal %s", signame); + } else { + DBG("run_as_worker received signal %d", sig); + } +} + +static +int set_worker_sighandlers(void) +{ + int ret = 0; + sigset_t sigset; + struct sigaction sa; + + if ((ret = sigemptyset(&sigset)) < 0) { + PERROR("sigemptyset"); + goto end; + } + + sa.sa_handler = worker_sighandler; + sa.sa_mask = sigset; + sa.sa_flags = 0; + if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { + PERROR("sigaction SIGINT"); + goto end; + } + + if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { + PERROR("sigaction SIGTERM"); + goto end; + } + + DBG("run_as signal handler set for SIGTERM and SIGINT"); +end: + return ret; +} + +static +int run_as_create_worker_no_lock(const char *procname, + post_fork_cleanup_cb clean_up_func, + void *clean_up_user_data) +{ + pid_t pid; + int i, ret = 0; + ssize_t readlen; + struct run_as_ret recvret; + run_as_worker_data *worker; + + LTTNG_ASSERT(!global_worker); + if (!use_clone()) { + /* + * Don't initialize a worker, all run_as tasks will be performed + * in the current process. + */ + ret = 0; + goto end; + } + worker = (run_as_worker_data *) zmalloc(sizeof(*worker)); + if (!worker) { + ret = -ENOMEM; + goto end; + } + worker->procname = strdup(procname); + if (!worker->procname) { + ret = -ENOMEM; + goto error_procname_alloc; + } + /* Create unix socket. */ + if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) { + ret = -1; + goto error_sock; + } + + /* Fork worker. */ + pid = fork(); + if (pid < 0) { + PERROR("fork"); + ret = -1; + goto error_fork; + } else if (pid == 0) { + /* Child */ + + reset_sighandler(); + + set_worker_sighandlers(); + + logger_set_thread_name("Run-as worker", true); + + if (clean_up_func) { + if (clean_up_func(clean_up_user_data) < 0) { + ERR("Run-as post-fork clean-up failed, exiting."); + exit(EXIT_FAILURE); + } + } + + /* Just close, no shutdown. */ + if (close(worker->sockpair[0])) { + PERROR("close"); + exit(EXIT_FAILURE); + } + + /* + * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1] + * Sockpair[1] is used as a control channel with the master + */ + for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { + if (i != worker->sockpair[1]) { + (void) close(i); + } + } + + worker->sockpair[0] = -1; + ret = run_as_worker(worker); + if (lttcomm_close_unix_sock(worker->sockpair[1])) { + PERROR("close"); + ret = -1; + } + worker->sockpair[1] = -1; + free(worker->procname); + free(worker); + LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret); + exit(ret ? EXIT_FAILURE : EXIT_SUCCESS); + } else { + /* Parent */ + + /* Just close, no shutdown. */ + if (close(worker->sockpair[1])) { + PERROR("close"); + ret = -1; + goto error_fork; + } + worker->sockpair[1] = -1; + worker->pid = pid; + /* Wait for worker to become ready. */ + readlen = lttcomm_recv_unix_sock(worker->sockpair[0], + &recvret, sizeof(recvret)); + if (readlen < sizeof(recvret)) { + ERR("readlen: %zd", readlen); + PERROR("Error reading response from run_as at creation"); + ret = -1; + goto error_fork; + } + global_worker = worker; + } +end: + return ret; + + /* Error handling. */ +error_fork: + for (i = 0; i < 2; i++) { + if (worker->sockpair[i] < 0) { + continue; + } + if (lttcomm_close_unix_sock(worker->sockpair[i])) { + PERROR("close"); + } + worker->sockpair[i] = -1; + } +error_sock: + free(worker->procname); +error_procname_alloc: + free(worker); + return ret; +} + +static +void run_as_destroy_worker_no_lock(void) +{ + run_as_worker_data *worker = global_worker; + + DBG("Destroying run_as worker"); + if (!worker) { + return; + } + /* Close unix socket */ + DBG("Closing run_as worker socket"); + if (lttcomm_close_unix_sock(worker->sockpair[0])) { + PERROR("close"); + } + worker->sockpair[0] = -1; + /* Wait for worker. */ + for (;;) { + int status; + pid_t wait_ret; + + wait_ret = waitpid(worker->pid, &status, 0); + if (wait_ret < 0) { + if (errno == EINTR) { + continue; + } + PERROR("waitpid"); + break; + } + + if (WIFEXITED(status)) { + LOG(WEXITSTATUS(status) == 0 ? PRINT_DBG : PRINT_ERR, + DEFAULT_RUN_AS_WORKER_NAME " terminated with status code %d", + WEXITSTATUS(status)); + break; + } else if (WIFSIGNALED(status)) { + ERR(DEFAULT_RUN_AS_WORKER_NAME " was killed by signal %d", + WTERMSIG(status)); + break; + } + } + free(worker->procname); + free(worker); + global_worker = NULL; +} + +static +int run_as_restart_worker(run_as_worker_data *worker) +{ + int ret = 0; + char *procname = NULL; + + procname = worker->procname; + + /* Close socket to run_as worker process and clean up the zombie process */ + run_as_destroy_worker_no_lock(); + + /* Create a new run_as worker process*/ + ret = run_as_create_worker_no_lock(procname, NULL, NULL); + if (ret < 0 ) { + ERR("Restarting the worker process failed"); + ret = -1; + goto err; + } +err: + return ret; +} + +static +int run_as(enum run_as_cmd cmd, struct run_as_data *data, + struct run_as_ret *ret_value, uid_t uid, gid_t gid) +{ + int ret, saved_errno; + + pthread_mutex_lock(&worker_lock); + if (use_clone()) { + DBG("Using run_as worker"); + + LTTNG_ASSERT(global_worker); + + ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid); + saved_errno = ret_value->_errno; + + /* + * If the worker thread crashed the errno is set to EIO. we log + * the error and start a new worker process. + */ + if (ret == -1 && saved_errno == EIO) { + DBG("Socket closed unexpectedly... " + "Restarting the worker process"); + ret = run_as_restart_worker(global_worker); + if (ret == -1) { + ERR("Failed to restart worker process."); + goto err; + } + } + } else { + DBG("Using run_as without worker"); + ret = run_as_noworker(cmd, data, ret_value, uid, gid); + } +err: + pthread_mutex_unlock(&worker_lock); + return ret; +} + +int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid); +} + +int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode, + uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) mode, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.mkdir.path, path, + sizeof(data.u.mkdir.path)); + if (ret) { + ERR("Failed to copy path argument of mkdirat recursive command"); + goto error; + } + data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; + data.u.mkdir.mode = mode; + data.u.mkdir.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid); +} + +int run_as_mkdirat(int dirfd, const char *path, mode_t mode, + uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) mode, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.mkdir.path, path, + sizeof(data.u.mkdir.path)); + if (ret) { + ERR("Failed to copy path argument of mkdirat command"); + goto error; + } + data.u.mkdir.path[sizeof(data.u.mkdir.path) - 1] = '\0'; + data.u.mkdir.mode = mode; + data.u.mkdir.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, + gid_t gid) +{ + return run_as_openat(AT_FDCWD, path, flags, mode, uid, gid); +} + +int run_as_openat(int dirfd, const char *path, int flags, mode_t mode, + uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("openat() fd = %d%s, path = %s, flags = %X, mode = %d, uid %d, gid %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, flags, (int) mode, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.open.path, path, sizeof(data.u.open.path)); + if (ret) { + ERR("Failed to copy path argument of open command"); + goto error; + } + data.u.open.flags = flags; + data.u.open.mode = mode; + data.u.open.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_OPEN : RUN_AS_OPENAT, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret < 0 ? run_as_ret.u.ret : + run_as_ret.u.open.fd; +error: + return ret; +} + +int run_as_unlink(const char *path, uid_t uid, gid_t gid) +{ + return run_as_unlinkat(AT_FDCWD, path, uid, gid); +} + +int run_as_unlinkat(int dirfd, const char *path, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("unlinkat() fd = %d%s, path = %s, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.unlink.path, path, + sizeof(data.u.unlink.path)); + if (ret) { + goto error; + } + data.u.unlink.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_UNLINK : RUN_AS_UNLINKAT, &data, + &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_rmdir(const char *path, uid_t uid, gid_t gid) +{ + return run_as_rmdirat(AT_FDCWD, path, uid, gid); +} + +int run_as_rmdirat(int dirfd, const char *path, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("rmdirat() fd = %d%s, path = %s, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.rmdir.path, path, + sizeof(data.u.rmdir.path)); + if (ret) { + goto error; + } + data.u.rmdir.dirfd = dirfd; + run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR : RUN_AS_RMDIRAT, &data, + &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid, int flags) +{ + return run_as_rmdirat_recursive(AT_FDCWD, path, uid, gid, flags); +} + +int run_as_rmdirat_recursive(int dirfd, const char *path, uid_t uid, gid_t gid, int flags) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("rmdirat() recursive fd = %d%s, path = %s, uid = %d, gid = %d", + dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + path, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.rmdir.path, path, + sizeof(data.u.rmdir.path)); + if (ret) { + goto error; + } + data.u.rmdir.dirfd = dirfd; + data.u.rmdir.flags = flags; + run_as(dirfd == AT_FDCWD ? RUN_AS_RMDIR_RECURSIVE : RUN_AS_RMDIRAT_RECURSIVE, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_rename(const char *old_name, const char *new_name, uid_t uid, gid_t gid) +{ + return run_as_renameat(AT_FDCWD, old_name, AT_FDCWD, new_name, uid, gid); +} + +int run_as_renameat(int old_dirfd, const char *old_name, + int new_dirfd, const char *new_name, uid_t uid, gid_t gid) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("renameat() old_dirfd = %d%s, old_name = %s, new_dirfd = %d%s, new_name = %s, uid = %d, gid = %d", + old_dirfd, old_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + old_name, + new_dirfd, new_dirfd == AT_FDCWD ? " (AT_FDCWD)" : "", + new_name, (int) uid, (int) gid); + ret = lttng_strncpy(data.u.rename.old_path, old_name, + sizeof(data.u.rename.old_path)); + if (ret) { + goto error; + } + ret = lttng_strncpy(data.u.rename.new_path, new_name, + sizeof(data.u.rename.new_path)); + if (ret) { + goto error; + } + + data.u.rename.dirfds[0] = old_dirfd; + data.u.rename.dirfds[1] = new_dirfd; + run_as(old_dirfd == AT_FDCWD && new_dirfd == AT_FDCWD ? + RUN_AS_RENAME : RUN_AS_RENAMEAT, + &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + ret = run_as_ret.u.ret; +error: + return ret; +} + +int run_as_extract_elf_symbol_offset(int fd, const char* function, + uid_t uid, gid_t gid, uint64_t *offset) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("extract_elf_symbol_offset() on fd=%d and function=%s " + "with for uid %d and gid %d", fd, function, + (int) uid, (int) gid); + + data.u.extract_elf_symbol_offset.fd = fd; + + strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1); + data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ret = lttng_strncpy(data.u.extract_elf_symbol_offset.function, + function, + sizeof(data.u.extract_elf_symbol_offset.function)); + if (ret) { + goto error; + } + + run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; + } + + *offset = run_as_ret.u.extract_elf_symbol_offset.offset; +error: + return ret; +} + +int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name, + const char* probe_name, uid_t uid, gid_t gid, + uint64_t **offsets, uint32_t *num_offset) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + + DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and " + "provider_name=%s with for uid %d and gid %d", fd, + probe_name, provider_name, (int) uid, (int) gid); + + data.u.extract_sdt_probe_offsets.fd = fd; + + ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, + sizeof(data.u.extract_sdt_probe_offsets.probe_name)); + if (ret) { + goto error; + } + ret = lttng_strncpy(data.u.extract_sdt_probe_offsets.provider_name, + provider_name, + sizeof(data.u.extract_sdt_probe_offsets.provider_name)); + if (ret) { + goto error; + } + + run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; + } + + *num_offset = run_as_ret.u.extract_sdt_probe_offsets.num_offset; + *offsets = (uint64_t *) zmalloc(*num_offset * sizeof(uint64_t)); + if (!*offsets) { + ret = -ENOMEM; + goto error; + } + + memcpy(*offsets, run_as_ret.u.extract_sdt_probe_offsets.offsets, + *num_offset * sizeof(uint64_t)); +error: + return ret; +} + +int run_as_generate_filter_bytecode(const char *filter_expression, + const struct lttng_credentials *creds, + struct lttng_bytecode **bytecode) +{ + int ret; + struct run_as_data data = {}; + struct run_as_ret run_as_ret = {}; + const struct lttng_bytecode *view_bytecode = NULL; + struct lttng_bytecode *local_bytecode = NULL; + const uid_t uid = lttng_credentials_get_uid(creds); + const gid_t gid = lttng_credentials_get_gid(creds); + + DBG3("generate_filter_bytecode() from expression=\"%s\" for uid %d and gid %d", + filter_expression, (int) uid, (int) gid); + + ret = lttng_strncpy(data.u.generate_filter_bytecode.filter_expression, filter_expression, + sizeof(data.u.generate_filter_bytecode.filter_expression)); + if (ret) { + goto error; + } + + run_as(RUN_AS_GENERATE_FILTER_BYTECODE, &data, &run_as_ret, uid, gid); + errno = run_as_ret._errno; + if (run_as_ret._error) { + ret = -1; + goto error; + } + + view_bytecode = (const struct lttng_bytecode *) run_as_ret.u.generate_filter_bytecode.bytecode; + + local_bytecode = (lttng_bytecode *) zmalloc(sizeof(*local_bytecode) + view_bytecode->len); + if (!local_bytecode) { + ret = -ENOMEM; + goto error; + } + + memcpy(local_bytecode, run_as_ret.u.generate_filter_bytecode.bytecode, + sizeof(*local_bytecode) + view_bytecode->len); + *bytecode = local_bytecode; +error: + return ret; +} + +int run_as_create_worker(const char *procname, + post_fork_cleanup_cb clean_up_func, + void *clean_up_user_data) +{ + int ret; + + pthread_mutex_lock(&worker_lock); + ret = run_as_create_worker_no_lock(procname, clean_up_func, + clean_up_user_data); + pthread_mutex_unlock(&worker_lock); + return ret; +} + +void run_as_destroy_worker(void) +{ + pthread_mutex_lock(&worker_lock); + run_as_destroy_worker_no_lock(); + pthread_mutex_unlock(&worker_lock); +} diff --git a/src/common/session-descriptor.c b/src/common/session-descriptor.c deleted file mode 100644 index 93c7dfa47..000000000 --- a/src/common/session-descriptor.c +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - */ - -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/src/common/session-descriptor.cpp b/src/common/session-descriptor.cpp new file mode 100644 index 000000000..ec510b0c6 --- /dev/null +++ b/src/common/session-descriptor.cpp @@ -0,0 +1,1157 @@ +/* + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/common/shm.c b/src/common/shm.c deleted file mode 100644 index c006258c7..000000000 --- a/src/common/shm.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2011 Mathieu Desnoyers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shm.h" - -/* - * Using fork to set umask in the child process (not multi-thread safe). We - * deal with the shm_open vs ftruncate race (happening when the sessiond owns - * the shm and does not let everybody modify it, to ensure safety against - * shm_unlink) by simply letting the mmap fail and retrying after a few - * seconds. For global shm, everybody has rw access to it until the sessiond - * starts. - */ -static int get_wait_shm(char *shm_path, size_t mmap_size, int global) -{ - int wait_shm_fd, ret; - mode_t mode; - - LTTNG_ASSERT(shm_path); - - /* Default permissions */ - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; - - /* - * Change owner of the shm path. - */ - if (global) { - /* - * If global session daemon, any application can - * register. Make it initially writeable so applications - * registering concurrently can do ftruncate() by - * themselves. - */ - mode |= S_IROTH | S_IWOTH; - } - - /* - * We're alone in a child process, so we can modify the process-wide - * umask. - */ - umask(~mode); - - /* - * Try creating shm (or get rw access). We don't do an exclusive open, - * because we allow other processes to create+ftruncate it concurrently. - * - * A sysctl, fs.protected_regular may prevent the session daemon from - * opening a previously created shm when the O_CREAT flag is provided. - * Systemd enables this ABI-breaking change by default since v241. - * - * First, attempt to use the create-or-open semantic that is - * desired here. If this fails with EACCES, work around this broken - * behaviour and attempt to open the shm without the O_CREAT flag. - * - * The two attempts are made in this order since applications are - * expected to race with the session daemon to create this shm. - * Attempting an shm_open() without the O_CREAT flag first could fail - * because the file doesn't exist. It could then be created by an - * application, which would cause a second try with the O_CREAT flag to - * fail with EACCES. - * - * Note that this introduces a new failure mode where a user could - * launch an application (creating the shm) and unlink the shm while - * the session daemon is launching, causing the second attempt - * to fail. This is not recovered-from as unlinking the shm will - * prevent userspace tracing from succeeding anyhow: the sessiond would - * use a now-unlinked shm, while the next application would create - * a new named shm. - */ - wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); - if (wait_shm_fd < 0) { - if (errno == EACCES) { - /* Work around sysctl fs.protected_regular. */ - DBG("shm_open of %s returned EACCES, this may be caused " - "by the fs.protected_regular sysctl. " - "Attempting to open the shm without " - "creating it.", shm_path); - wait_shm_fd = shm_open(shm_path, O_RDWR, mode); - } - if (wait_shm_fd < 0) { - PERROR("Failed to open \"wait\" shared memory object: path = '%s'", shm_path); - goto error; - } - } - - ret = ftruncate(wait_shm_fd, mmap_size); - if (ret < 0) { - PERROR("Failed to truncate \"wait\" shared memory object: fd = %d, size = %zu", - wait_shm_fd, mmap_size); - exit(EXIT_FAILURE); - } - - if (global) { - ret = fchown(wait_shm_fd, 0, 0); - if (ret < 0) { - PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = 0, group = 0", - wait_shm_fd); - exit(EXIT_FAILURE); - } - /* - * If global session daemon, any application can - * register so the shm needs to be set in read-only mode - * for others. - */ - mode &= ~S_IWOTH; - ret = fchmod(wait_shm_fd, mode); - if (ret < 0) { - PERROR("Failed to set the mode of the \"wait\" shared memory object: fd = %d, mode = %d", - wait_shm_fd, mode); - exit(EXIT_FAILURE); - } - } else { - ret = fchown(wait_shm_fd, getuid(), getgid()); - if (ret < 0) { - PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = %d, group = %d", - wait_shm_fd, getuid(), getgid()); - exit(EXIT_FAILURE); - } - } - - DBG("Wait shared memory file descriptor created successfully: path = '%s', mmap_size = %zu, global = %s, fd = %d", - shm_path, mmap_size, global ? "true" : "false", - wait_shm_fd); - - return wait_shm_fd; - -error: - DBG("Failed to open shared memory file descriptor: path = '%s', mmap_size = %zu, global = %s", - shm_path, mmap_size, global ? "true" : "false"); - - return -1; -} - -/* - * Return the wait shm mmap for UST application notification. The global - * variable is used to indicate if the the session daemon is global - * (root:tracing) or running with an unprivileged user. - * - * This returned value is used by futex_wait_update() in futex.c to WAKE all - * waiters which are UST application waiting for a session daemon. - */ -char *shm_ust_get_mmap(char *shm_path, int global) -{ - size_t mmap_size; - int wait_shm_fd, ret; - char *wait_shm_mmap; - long sys_page_size; - - LTTNG_ASSERT(shm_path); - - sys_page_size = sysconf(_SC_PAGE_SIZE); - if (sys_page_size < 0) { - PERROR("Failed to get PAGE_SIZE of system"); - goto error; - } - mmap_size = sys_page_size; - - wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); - if (wait_shm_fd < 0) { - goto error; - } - - wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, - MAP_SHARED, wait_shm_fd, 0); - - /* close shm fd immediately after taking the mmap reference */ - ret = close(wait_shm_fd); - if (ret) { - PERROR("Failed to close \"wait\" shared memory object file descriptor: fd = %d", - wait_shm_fd); - } - - if (wait_shm_mmap == MAP_FAILED) { - DBG("Failed to mmap the \"wait\" shareed memory object (can be caused by race with ust): path = '%s', global = %s", - shm_path, global ? "true" : "false"); - goto error; - } - - return wait_shm_mmap; - -error: - return NULL; -} - -/* - * shm_create_anonymous is never called concurrently within a process. - */ -int shm_create_anonymous(const char *owner_name) -{ - char tmp_name[NAME_MAX]; - int shmfd, ret; - - ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid()); - if (ret < 0) { - PERROR("Failed to format shm path: owner_name = '%s', pid = %d", - owner_name, getpid()); - return -1; - } - - /* - * Allocate shm, and immediately unlink its shm oject, keeping only the - * file descriptor as a reference to the object. - */ - shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); - if (shmfd < 0) { - PERROR("Failed to open shared memory object: path = '%s'", tmp_name); - goto error_shm_open; - } - - ret = shm_unlink(tmp_name); - if (ret < 0 && errno != ENOENT) { - PERROR("Failed to unlink shared memory object: path = '%s'", - tmp_name); - goto error_shm_release; - } - - return shmfd; - -error_shm_release: - ret = close(shmfd); - if (ret) { - PERROR("Failed to close shared memory object file descriptor: fd = %d, path = '%s'", - shmfd, tmp_name); - } -error_shm_open: - return -1; -} diff --git a/src/common/shm.cpp b/src/common/shm.cpp new file mode 100644 index 000000000..99535c468 --- /dev/null +++ b/src/common/shm.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2011 Mathieu Desnoyers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shm.h" + +/* + * Using fork to set umask in the child process (not multi-thread safe). We + * deal with the shm_open vs ftruncate race (happening when the sessiond owns + * the shm and does not let everybody modify it, to ensure safety against + * shm_unlink) by simply letting the mmap fail and retrying after a few + * seconds. For global shm, everybody has rw access to it until the sessiond + * starts. + */ +static int get_wait_shm(char *shm_path, size_t mmap_size, int global) +{ + int wait_shm_fd, ret; + mode_t mode; + + LTTNG_ASSERT(shm_path); + + /* Default permissions */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + /* + * Change owner of the shm path. + */ + if (global) { + /* + * If global session daemon, any application can + * register. Make it initially writeable so applications + * registering concurrently can do ftruncate() by + * themselves. + */ + mode |= S_IROTH | S_IWOTH; + } + + /* + * We're alone in a child process, so we can modify the process-wide + * umask. + */ + umask(~mode); + + /* + * Try creating shm (or get rw access). We don't do an exclusive open, + * because we allow other processes to create+ftruncate it concurrently. + * + * A sysctl, fs.protected_regular may prevent the session daemon from + * opening a previously created shm when the O_CREAT flag is provided. + * Systemd enables this ABI-breaking change by default since v241. + * + * First, attempt to use the create-or-open semantic that is + * desired here. If this fails with EACCES, work around this broken + * behaviour and attempt to open the shm without the O_CREAT flag. + * + * The two attempts are made in this order since applications are + * expected to race with the session daemon to create this shm. + * Attempting an shm_open() without the O_CREAT flag first could fail + * because the file doesn't exist. It could then be created by an + * application, which would cause a second try with the O_CREAT flag to + * fail with EACCES. + * + * Note that this introduces a new failure mode where a user could + * launch an application (creating the shm) and unlink the shm while + * the session daemon is launching, causing the second attempt + * to fail. This is not recovered-from as unlinking the shm will + * prevent userspace tracing from succeeding anyhow: the sessiond would + * use a now-unlinked shm, while the next application would create + * a new named shm. + */ + wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); + if (wait_shm_fd < 0) { + if (errno == EACCES) { + /* Work around sysctl fs.protected_regular. */ + DBG("shm_open of %s returned EACCES, this may be caused " + "by the fs.protected_regular sysctl. " + "Attempting to open the shm without " + "creating it.", shm_path); + wait_shm_fd = shm_open(shm_path, O_RDWR, mode); + } + if (wait_shm_fd < 0) { + PERROR("Failed to open \"wait\" shared memory object: path = '%s'", shm_path); + goto error; + } + } + + ret = ftruncate(wait_shm_fd, mmap_size); + if (ret < 0) { + PERROR("Failed to truncate \"wait\" shared memory object: fd = %d, size = %zu", + wait_shm_fd, mmap_size); + exit(EXIT_FAILURE); + } + + if (global) { + ret = fchown(wait_shm_fd, 0, 0); + if (ret < 0) { + PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = 0, group = 0", + wait_shm_fd); + exit(EXIT_FAILURE); + } + /* + * If global session daemon, any application can + * register so the shm needs to be set in read-only mode + * for others. + */ + mode &= ~S_IWOTH; + ret = fchmod(wait_shm_fd, mode); + if (ret < 0) { + PERROR("Failed to set the mode of the \"wait\" shared memory object: fd = %d, mode = %d", + wait_shm_fd, mode); + exit(EXIT_FAILURE); + } + } else { + ret = fchown(wait_shm_fd, getuid(), getgid()); + if (ret < 0) { + PERROR("Failed to set ownership of \"wait\" shared memory object: fd = %d, owner = %d, group = %d", + wait_shm_fd, getuid(), getgid()); + exit(EXIT_FAILURE); + } + } + + DBG("Wait shared memory file descriptor created successfully: path = '%s', mmap_size = %zu, global = %s, fd = %d", + shm_path, mmap_size, global ? "true" : "false", + wait_shm_fd); + + return wait_shm_fd; + +error: + DBG("Failed to open shared memory file descriptor: path = '%s', mmap_size = %zu, global = %s", + shm_path, mmap_size, global ? "true" : "false"); + + return -1; +} + +/* + * Return the wait shm mmap for UST application notification. The global + * variable is used to indicate if the the session daemon is global + * (root:tracing) or running with an unprivileged user. + * + * This returned value is used by futex_wait_update() in futex.c to WAKE all + * waiters which are UST application waiting for a session daemon. + */ +char *shm_ust_get_mmap(char *shm_path, int global) +{ + size_t mmap_size; + int wait_shm_fd, ret; + char *wait_shm_mmap; + long sys_page_size; + + LTTNG_ASSERT(shm_path); + + sys_page_size = sysconf(_SC_PAGE_SIZE); + if (sys_page_size < 0) { + PERROR("Failed to get PAGE_SIZE of system"); + goto error; + } + mmap_size = sys_page_size; + + wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); + if (wait_shm_fd < 0) { + goto error; + } + + wait_shm_mmap = (char *) mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, + MAP_SHARED, wait_shm_fd, 0); + + /* close shm fd immediately after taking the mmap reference */ + ret = close(wait_shm_fd); + if (ret) { + PERROR("Failed to close \"wait\" shared memory object file descriptor: fd = %d", + wait_shm_fd); + } + + if (wait_shm_mmap == MAP_FAILED) { + DBG("Failed to mmap the \"wait\" shareed memory object (can be caused by race with ust): path = '%s', global = %s", + shm_path, global ? "true" : "false"); + goto error; + } + + return wait_shm_mmap; + +error: + return NULL; +} + +/* + * shm_create_anonymous is never called concurrently within a process. + */ +int shm_create_anonymous(const char *owner_name) +{ + char tmp_name[NAME_MAX]; + int shmfd, ret; + + ret = snprintf(tmp_name, NAME_MAX, "/shm-%s-%d", owner_name, getpid()); + if (ret < 0) { + PERROR("Failed to format shm path: owner_name = '%s', pid = %d", + owner_name, getpid()); + return -1; + } + + /* + * Allocate shm, and immediately unlink its shm oject, keeping only the + * file descriptor as a reference to the object. + */ + shmfd = shm_open(tmp_name, O_CREAT | O_EXCL | O_RDWR, 0700); + if (shmfd < 0) { + PERROR("Failed to open shared memory object: path = '%s'", tmp_name); + goto error_shm_open; + } + + ret = shm_unlink(tmp_name); + if (ret < 0 && errno != ENOENT) { + PERROR("Failed to unlink shared memory object: path = '%s'", + tmp_name); + goto error_shm_release; + } + + return shmfd; + +error_shm_release: + ret = close(shmfd); + if (ret) { + PERROR("Failed to close shared memory object file descriptor: fd = %d, path = '%s'", + shmfd, tmp_name); + } +error_shm_open: + return -1; +} diff --git a/src/common/snapshot.c b/src/common/snapshot.c deleted file mode 100644 index 960240b2c..000000000 --- a/src/common/snapshot.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2020 Simon Marchi - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output) -{ - bool valid = false; - size_t len; - - /* - * It is mandatory to have a ctrl_url. If there is only one output - * URL (in the net://, net6:// or file:// form), it will be in this - * field. - */ - len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url)); - if (len == 0 || len >= sizeof(output->ctrl_url)) { - goto end; - } - - len = lttng_strnlen(output->data_url, sizeof(output->data_url)); - if (len >= sizeof(output->data_url)) { - goto end; - } - - len = lttng_strnlen(output->name, sizeof(output->name)); - if (len >= sizeof(output->name)) { - goto end; - } - - valid = true; - -end: - return valid; -} - -bool lttng_snapshot_output_is_equal( - const struct lttng_snapshot_output *a, - const struct lttng_snapshot_output *b) -{ - bool equal = false; - - LTTNG_ASSERT(a); - LTTNG_ASSERT(b); - - if (a->max_size != b->max_size) { - goto end; - } - - if (strcmp(a->name, b->name) != 0) { - goto end; - } - - if (strcmp(a->ctrl_url, b->ctrl_url) != 0) { - goto end; - } - - if (strcmp(a->data_url, b->data_url) != 0) { - goto end; - } - - equal = true; - -end: - return equal; -} - -/* - * This is essentially the same as `struct lttng_snapshot_output`, but packed. - */ -struct lttng_snapshot_output_comm { - uint32_t id; - uint64_t max_size; - char name[LTTNG_NAME_MAX]; - char ctrl_url[PATH_MAX]; - char data_url[PATH_MAX]; -} LTTNG_PACKED; - -int lttng_snapshot_output_serialize( - const struct lttng_snapshot_output *output, - struct lttng_payload *payload) -{ - struct lttng_snapshot_output_comm comm; - int ret; - - comm.id = output->id; - comm.max_size = output->max_size; - - ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name)); - if (ret) { - goto end; - } - - ret = lttng_strncpy( - comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url)); - if (ret) { - goto end; - } - - ret = lttng_strncpy( - comm.data_url, output->data_url, sizeof(comm.data_url)); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &comm, sizeof(comm)); - if (ret) { - goto end; - } - -end: - return ret; -} - -ssize_t lttng_snapshot_output_create_from_payload( - struct lttng_payload_view *view, - struct lttng_snapshot_output **output_p) -{ - const struct lttng_snapshot_output_comm *comm; - struct lttng_snapshot_output *output = NULL; - int ret; - - if (view->buffer.size != sizeof(*comm)) { - ret = -1; - goto end; - } - - output = lttng_snapshot_output_create(); - if (!output) { - ret = -1; - goto end; - } - - comm = (typeof(comm)) view->buffer.data; - - output->id = comm->id; - output->max_size = comm->max_size; - - ret = lttng_strncpy(output->name, comm->name, sizeof(output->name)); - if (ret) { - goto end; - } - - ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, - sizeof(output->ctrl_url)); - if (ret) { - goto end; - } - - ret = lttng_strncpy(output->data_url, comm->data_url, - sizeof(output->data_url)); - if (ret) { - goto end; - } - - *output_p = output; - output = NULL; - ret = sizeof(*comm); - -end: - lttng_snapshot_output_destroy(output); - return ret; -} - -enum lttng_error_code lttng_snapshot_output_mi_serialize( - const struct lttng_snapshot_output *output, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - - LTTNG_ASSERT(output); - LTTNG_ASSERT(writer); - - /* Open output element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_action_snapshot_session_output); - if (ret) { - goto mi_error; - } - - /* Name. */ - if (strnlen(output->name, LTTNG_NAME_MAX) != 0) { - ret = mi_lttng_writer_write_element_string( - writer, config_element_name, output->name); - if (ret) { - goto mi_error; - } - } - - /* Control url (always present). */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); - if (ret) { - goto mi_error; - } - - /* Data url (optional). */ - if (strnlen(output->data_url, PATH_MAX) != 0) { - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_snapshot_data_url, - output->data_url); - if (ret) { - goto mi_error; - } - } - - /* - * Maximum size in bytes of the snapshot meaning the total size of all - * streams combined. A value of 0 means unlimited. The default value is - * UINT64_MAX which also means unlimited in practice. - * - * The value is not serialized when it is set to either of those values - * to normalize them to '0'. - */ - if (output->max_size > 0 && output->max_size != UINT64_MAX) { - /* Total size of all stream combined. */ - ret = mi_lttng_writer_write_element_unsigned_int(writer, - mi_lttng_element_snapshot_max_size, - output->max_size); - if (ret) { - goto mi_error; - } - } - - /* Close output element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/snapshot.cpp b/src/common/snapshot.cpp new file mode 100644 index 000000000..960240b2c --- /dev/null +++ b/src/common/snapshot.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2020 Simon Marchi + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +bool lttng_snapshot_output_validate(const struct lttng_snapshot_output *output) +{ + bool valid = false; + size_t len; + + /* + * It is mandatory to have a ctrl_url. If there is only one output + * URL (in the net://, net6:// or file:// form), it will be in this + * field. + */ + len = lttng_strnlen(output->ctrl_url, sizeof(output->ctrl_url)); + if (len == 0 || len >= sizeof(output->ctrl_url)) { + goto end; + } + + len = lttng_strnlen(output->data_url, sizeof(output->data_url)); + if (len >= sizeof(output->data_url)) { + goto end; + } + + len = lttng_strnlen(output->name, sizeof(output->name)); + if (len >= sizeof(output->name)) { + goto end; + } + + valid = true; + +end: + return valid; +} + +bool lttng_snapshot_output_is_equal( + const struct lttng_snapshot_output *a, + const struct lttng_snapshot_output *b) +{ + bool equal = false; + + LTTNG_ASSERT(a); + LTTNG_ASSERT(b); + + if (a->max_size != b->max_size) { + goto end; + } + + if (strcmp(a->name, b->name) != 0) { + goto end; + } + + if (strcmp(a->ctrl_url, b->ctrl_url) != 0) { + goto end; + } + + if (strcmp(a->data_url, b->data_url) != 0) { + goto end; + } + + equal = true; + +end: + return equal; +} + +/* + * This is essentially the same as `struct lttng_snapshot_output`, but packed. + */ +struct lttng_snapshot_output_comm { + uint32_t id; + uint64_t max_size; + char name[LTTNG_NAME_MAX]; + char ctrl_url[PATH_MAX]; + char data_url[PATH_MAX]; +} LTTNG_PACKED; + +int lttng_snapshot_output_serialize( + const struct lttng_snapshot_output *output, + struct lttng_payload *payload) +{ + struct lttng_snapshot_output_comm comm; + int ret; + + comm.id = output->id; + comm.max_size = output->max_size; + + ret = lttng_strncpy(comm.name, output->name, sizeof(comm.name)); + if (ret) { + goto end; + } + + ret = lttng_strncpy( + comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url)); + if (ret) { + goto end; + } + + ret = lttng_strncpy( + comm.data_url, output->data_url, sizeof(comm.data_url)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + +end: + return ret; +} + +ssize_t lttng_snapshot_output_create_from_payload( + struct lttng_payload_view *view, + struct lttng_snapshot_output **output_p) +{ + const struct lttng_snapshot_output_comm *comm; + struct lttng_snapshot_output *output = NULL; + int ret; + + if (view->buffer.size != sizeof(*comm)) { + ret = -1; + goto end; + } + + output = lttng_snapshot_output_create(); + if (!output) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) view->buffer.data; + + output->id = comm->id; + output->max_size = comm->max_size; + + ret = lttng_strncpy(output->name, comm->name, sizeof(output->name)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, + sizeof(output->ctrl_url)); + if (ret) { + goto end; + } + + ret = lttng_strncpy(output->data_url, comm->data_url, + sizeof(output->data_url)); + if (ret) { + goto end; + } + + *output_p = output; + output = NULL; + ret = sizeof(*comm); + +end: + lttng_snapshot_output_destroy(output); + return ret; +} + +enum lttng_error_code lttng_snapshot_output_mi_serialize( + const struct lttng_snapshot_output *output, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + + LTTNG_ASSERT(output); + LTTNG_ASSERT(writer); + + /* Open output element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_action_snapshot_session_output); + if (ret) { + goto mi_error; + } + + /* Name. */ + if (strnlen(output->name, LTTNG_NAME_MAX) != 0) { + ret = mi_lttng_writer_write_element_string( + writer, config_element_name, output->name); + if (ret) { + goto mi_error; + } + } + + /* Control url (always present). */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_ctrl_url, output->ctrl_url); + if (ret) { + goto mi_error; + } + + /* Data url (optional). */ + if (strnlen(output->data_url, PATH_MAX) != 0) { + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_snapshot_data_url, + output->data_url); + if (ret) { + goto mi_error; + } + } + + /* + * Maximum size in bytes of the snapshot meaning the total size of all + * streams combined. A value of 0 means unlimited. The default value is + * UINT64_MAX which also means unlimited in practice. + * + * The value is not serialized when it is set to either of those values + * to normalize them to '0'. + */ + if (output->max_size > 0 && output->max_size != UINT64_MAX) { + /* Total size of all stream combined. */ + ret = mi_lttng_writer_write_element_unsigned_int(writer, + mi_lttng_element_snapshot_max_size, + output->max_size); + if (ret) { + goto mi_error; + } + } + + /* Close output element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/spawn-viewer.c b/src/common/spawn-viewer.c deleted file mode 100644 index 52be70526..000000000 --- a/src/common/spawn-viewer.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2014 Mathieu Desnoyers - * Copyright (C) 2020 Francis Deslauriers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include - -#include - -#include -#include "error.h" -#include "macros.h" -#include "spawn-viewer.h" - - -static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN; -static const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN; - -/* - * This is needed for each viewer since we are using execvp(). - */ -static const char *babeltrace_opts[] = { "babeltrace" }; -static const char *babeltrace2_opts[] = { "babeltrace2" }; - -/* - * Type is also use as the index in the viewers array. So please, make sure - * your enum value is in the right order in the array below. - */ -enum viewer_type { - VIEWER_BABELTRACE = 0, - VIEWER_BABELTRACE2 = 1, - VIEWER_USER_DEFINED = 2, -}; - -static const struct viewer { - const char *exec_name; - enum viewer_type type; -} viewers[] = { - { "babeltrace", VIEWER_BABELTRACE }, - { "babeltrace2", VIEWER_BABELTRACE2 }, - { NULL, VIEWER_USER_DEFINED }, -}; - -static const struct viewer *parse_viewer_option(const char *opt_viewer) -{ - if (opt_viewer == NULL) { - /* Default is babeltrace2 */ - return &(viewers[VIEWER_BABELTRACE2]); - } - - return &(viewers[VIEWER_USER_DEFINED]); -} - -/* - * Alloc an array of string pointer from a simple string having all options - * seperated by spaces. Also adds the trace path to the arguments. - * - * The returning pointer is ready to be passed to execvp(). - */ -static char **alloc_argv_from_user_opts(char *opts, const char *trace_path) -{ - int i = 0, ignore_space = 0; - unsigned int num_opts = 1; - char **argv, *token = opts, *saveptr = NULL; - - /* Count number of arguments. */ - do { - if (*token == ' ') { - /* Use to ignore consecutive spaces */ - if (!ignore_space) { - num_opts++; - } - ignore_space = 1; - } else { - ignore_space = 0; - } - token++; - } while (*token != '\0'); - - /* Add two here for the NULL terminating element and trace path */ - argv = zmalloc(sizeof(char *) * (num_opts + 2)); - if (argv == NULL) { - goto error; - } - - token = strtok_r(opts, " ", &saveptr); - while (token != NULL) { - argv[i] = strdup(token); - if (argv[i] == NULL) { - goto error; - } - token = strtok_r(NULL, " ", &saveptr); - i++; - } - - argv[num_opts] = (char *) trace_path; - argv[num_opts + 1] = NULL; - - return argv; - -error: - if (argv) { - for (i = 0; i < num_opts + 2; i++) { - free(argv[i]); - } - free(argv); - } - - return NULL; -} - -/* - * Alloc an array of string pointer from an array of strings. It also adds - * the trace path to the argv. - * - * The returning pointer is ready to be passed to execvp(). - */ -static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len, - const char *trace_path, bool opt_live_mode) -{ - char **argv; - size_t size, mem_len; - - /* Add one for the NULL terminating element. */ - mem_len = opts_len + 1; - if (opt_live_mode) { - /* Add 3 option for the live mode being "-i lttng-live URL". */ - mem_len += 3; - } else { - /* Add option for the trace path. */ - mem_len += 1; - } - - size = sizeof(char *) * mem_len; - - /* Add two here for the trace_path and the NULL terminating element. */ - argv = zmalloc(size); - if (argv == NULL) { - goto error; - } - - memcpy(argv, opts, sizeof(char *) * opts_len); - - if (opt_live_mode) { - argv[opts_len] = (char *) "-i"; - argv[opts_len + 1] = (char *) "lttng-live"; - argv[opts_len + 2] = (char *) trace_path; - argv[opts_len + 3] = NULL; - } else { - argv[opts_len] = (char *) trace_path; - argv[opts_len + 1] = NULL; - } - -error: - return argv; -} - - -/* - * Spawn viewer with the trace directory path. - */ -int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode) -{ - int ret = 0; - struct stat status; - const char *viewer_bin = NULL; - const struct viewer *viewer; - char **argv = NULL; - - /* Check for --viewer option. */ - viewer = parse_viewer_option(opt_viewer); - if (viewer == NULL) { - ret = -1; - goto error; - } - -retry_viewer: - switch (viewer->type) { - case VIEWER_BABELTRACE2: - if (stat(babeltrace2_bin, &status) == 0) { - viewer_bin = babeltrace2_bin; - } else { - viewer_bin = viewer->exec_name; - } - argv = alloc_argv_from_local_opts(babeltrace2_opts, - ARRAY_SIZE(babeltrace2_opts), trace_path, - opt_live_mode); - break; - case VIEWER_BABELTRACE: - if (stat(babeltrace_bin, &status) == 0) { - viewer_bin = babeltrace_bin; - } else { - viewer_bin = viewer->exec_name; - } - argv = alloc_argv_from_local_opts(babeltrace_opts, - ARRAY_SIZE(babeltrace_opts), trace_path, - opt_live_mode); - break; - case VIEWER_USER_DEFINED: - argv = alloc_argv_from_user_opts(opt_viewer, trace_path); - if (argv) { - viewer_bin = argv[0]; - } - break; - default: - abort(); - } - - if (argv == NULL || !viewer_bin) { - ret = -1; - goto error; - } - - DBG("Using %s viewer", viewer_bin); - - ret = execvp(viewer_bin, argv); - if (ret) { - if (errno == ENOENT && viewer->exec_name) { - if (viewer->type == VIEWER_BABELTRACE2) { - /* Fallback to legacy babeltrace. */ - DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"", - viewers[VIEWER_BABELTRACE2].exec_name, - viewers[VIEWER_BABELTRACE].exec_name); - viewer = &viewers[VIEWER_BABELTRACE]; - free(argv); - argv = NULL; - goto retry_viewer; - } else { - ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system", - viewers[VIEWER_BABELTRACE2].exec_name, - viewers[VIEWER_BABELTRACE].exec_name); - } - } else { - PERROR("Failed to launch \"%s\" viewer", viewer_bin); - } - ret = -1; - goto error; - } - - /* - * This function should never return if successfull because `execvp(3)` - * onle returns if an error has occurred. - */ - LTTNG_ASSERT(ret != 0); -error: - free(argv); - return ret; -} diff --git a/src/common/spawn-viewer.cpp b/src/common/spawn-viewer.cpp new file mode 100644 index 000000000..bcbc229a2 --- /dev/null +++ b/src/common/spawn-viewer.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2014 Mathieu Desnoyers + * Copyright (C) 2020 Francis Deslauriers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include + +#include + +#include +#include "error.h" +#include "macros.h" +#include "spawn-viewer.h" + + +static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN; +static const char *babeltrace2_bin = CONFIG_BABELTRACE2_BIN; + +/* + * This is needed for each viewer since we are using execvp(). + */ +static const char *babeltrace_opts[] = { "babeltrace" }; +static const char *babeltrace2_opts[] = { "babeltrace2" }; + +/* + * Type is also use as the index in the viewers array. So please, make sure + * your enum value is in the right order in the array below. + */ +enum viewer_type { + VIEWER_BABELTRACE = 0, + VIEWER_BABELTRACE2 = 1, + VIEWER_USER_DEFINED = 2, +}; + +static const struct viewer { + const char *exec_name; + enum viewer_type type; +} viewers[] = { + { "babeltrace", VIEWER_BABELTRACE }, + { "babeltrace2", VIEWER_BABELTRACE2 }, + { NULL, VIEWER_USER_DEFINED }, +}; + +static const struct viewer *parse_viewer_option(const char *opt_viewer) +{ + if (opt_viewer == NULL) { + /* Default is babeltrace2 */ + return &(viewers[VIEWER_BABELTRACE2]); + } + + return &(viewers[VIEWER_USER_DEFINED]); +} + +/* + * Alloc an array of string pointer from a simple string having all options + * seperated by spaces. Also adds the trace path to the arguments. + * + * The returning pointer is ready to be passed to execvp(). + */ +static char **alloc_argv_from_user_opts(char *opts, const char *trace_path) +{ + int i = 0, ignore_space = 0; + unsigned int num_opts = 1; + char **argv, *token = opts, *saveptr = NULL; + + /* Count number of arguments. */ + do { + if (*token == ' ') { + /* Use to ignore consecutive spaces */ + if (!ignore_space) { + num_opts++; + } + ignore_space = 1; + } else { + ignore_space = 0; + } + token++; + } while (*token != '\0'); + + /* Add two here for the NULL terminating element and trace path */ + argv = (char **) zmalloc(sizeof(char *) * (num_opts + 2)); + if (argv == NULL) { + goto error; + } + + token = strtok_r(opts, " ", &saveptr); + while (token != NULL) { + argv[i] = strdup(token); + if (argv[i] == NULL) { + goto error; + } + token = strtok_r(NULL, " ", &saveptr); + i++; + } + + argv[num_opts] = (char *) trace_path; + argv[num_opts + 1] = NULL; + + return argv; + +error: + if (argv) { + for (i = 0; i < num_opts + 2; i++) { + free(argv[i]); + } + free(argv); + } + + return NULL; +} + +/* + * Alloc an array of string pointer from an array of strings. It also adds + * the trace path to the argv. + * + * The returning pointer is ready to be passed to execvp(). + */ +static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len, + const char *trace_path, bool opt_live_mode) +{ + char **argv; + size_t size, mem_len; + + /* Add one for the NULL terminating element. */ + mem_len = opts_len + 1; + if (opt_live_mode) { + /* Add 3 option for the live mode being "-i lttng-live URL". */ + mem_len += 3; + } else { + /* Add option for the trace path. */ + mem_len += 1; + } + + size = sizeof(char *) * mem_len; + + /* Add two here for the trace_path and the NULL terminating element. */ + argv = (char **) zmalloc(size); + if (argv == NULL) { + goto error; + } + + memcpy(argv, opts, sizeof(char *) * opts_len); + + if (opt_live_mode) { + argv[opts_len] = (char *) "-i"; + argv[opts_len + 1] = (char *) "lttng-live"; + argv[opts_len + 2] = (char *) trace_path; + argv[opts_len + 3] = NULL; + } else { + argv[opts_len] = (char *) trace_path; + argv[opts_len + 1] = NULL; + } + +error: + return argv; +} + + +/* + * Spawn viewer with the trace directory path. + */ +int spawn_viewer(const char *trace_path, char *opt_viewer, bool opt_live_mode) +{ + int ret = 0; + struct stat status; + const char *viewer_bin = NULL; + const struct viewer *viewer; + char **argv = NULL; + + /* Check for --viewer option. */ + viewer = parse_viewer_option(opt_viewer); + if (viewer == NULL) { + ret = -1; + goto error; + } + +retry_viewer: + switch (viewer->type) { + case VIEWER_BABELTRACE2: + if (stat(babeltrace2_bin, &status) == 0) { + viewer_bin = babeltrace2_bin; + } else { + viewer_bin = viewer->exec_name; + } + argv = alloc_argv_from_local_opts(babeltrace2_opts, + ARRAY_SIZE(babeltrace2_opts), trace_path, + opt_live_mode); + break; + case VIEWER_BABELTRACE: + if (stat(babeltrace_bin, &status) == 0) { + viewer_bin = babeltrace_bin; + } else { + viewer_bin = viewer->exec_name; + } + argv = alloc_argv_from_local_opts(babeltrace_opts, + ARRAY_SIZE(babeltrace_opts), trace_path, + opt_live_mode); + break; + case VIEWER_USER_DEFINED: + argv = alloc_argv_from_user_opts(opt_viewer, trace_path); + if (argv) { + viewer_bin = argv[0]; + } + break; + default: + abort(); + } + + if (argv == NULL || !viewer_bin) { + ret = -1; + goto error; + } + + DBG("Using %s viewer", viewer_bin); + + ret = execvp(viewer_bin, argv); + if (ret) { + if (errno == ENOENT && viewer->exec_name) { + if (viewer->type == VIEWER_BABELTRACE2) { + /* Fallback to legacy babeltrace. */ + DBG("Default viewer \"%s\" not installed on the system, falling back to \"%s\"", + viewers[VIEWER_BABELTRACE2].exec_name, + viewers[VIEWER_BABELTRACE].exec_name); + viewer = &viewers[VIEWER_BABELTRACE]; + free(argv); + argv = NULL; + goto retry_viewer; + } else { + ERR("Default viewer \"%s\" (and fallback \"%s\") not found on the system", + viewers[VIEWER_BABELTRACE2].exec_name, + viewers[VIEWER_BABELTRACE].exec_name); + } + } else { + PERROR("Failed to launch \"%s\" viewer", viewer_bin); + } + ret = -1; + goto error; + } + + /* + * This function should never return if successfull because `execvp(3)` + * onle returns if an error has occurred. + */ + LTTNG_ASSERT(ret != 0); +error: + free(argv); + return ret; +} diff --git a/src/common/thread.c b/src/common/thread.c deleted file mode 100644 index 34a620679..000000000 --- a/src/common/thread.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2020 Michael Jeanson - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include - -#include -#include "thread.h" - - -int lttng_thread_setname(const char *name) -{ - int ret; - char pthread_name[LTTNG_PTHREAD_NAMELEN]; - - /* - * Truncations are expected since pthread limits thread names to - * a generous 16 characters. - */ - strncpy(pthread_name, name, sizeof(pthread_name)); - pthread_name[sizeof(pthread_name) - 1] = '\0'; - - ret = lttng_pthread_setname_np(pthread_name); - - return ret; -} - diff --git a/src/common/thread.cpp b/src/common/thread.cpp new file mode 100644 index 000000000..34a620679 --- /dev/null +++ b/src/common/thread.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 Michael Jeanson + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include + +#include +#include "thread.h" + + +int lttng_thread_setname(const char *name) +{ + int ret; + char pthread_name[LTTNG_PTHREAD_NAMELEN]; + + /* + * Truncations are expected since pthread limits thread names to + * a generous 16 characters. + */ + strncpy(pthread_name, name, sizeof(pthread_name)); + pthread_name[sizeof(pthread_name) - 1] = '\0'; + + ret = lttng_pthread_setname_np(pthread_name); + + return ret; +} + diff --git a/src/common/time.c b/src/common/time.c deleted file mode 100644 index ccb587474..000000000 --- a/src/common/time.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2013 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool utf8_output_supported; - -bool locale_supports_utf8(void) -{ - return utf8_output_supported; -} - -int timespec_to_ms(struct timespec ts, unsigned long *ms) -{ - unsigned long res, remain_ms; - - if (ts.tv_sec > ULONG_MAX / MSEC_PER_SEC) { - errno = EOVERFLOW; - return -1; /* multiplication overflow */ - } - res = ts.tv_sec * MSEC_PER_SEC; - remain_ms = ULONG_MAX - res; - if (ts.tv_nsec / NSEC_PER_MSEC > remain_ms) { - errno = EOVERFLOW; - return -1; /* addition overflow */ - } - res += ts.tv_nsec / NSEC_PER_MSEC; - *ms = res; - return 0; -} - -struct timespec timespec_abs_diff(struct timespec t1, struct timespec t2) -{ - uint64_t ts1 = (uint64_t) t1.tv_sec * (uint64_t) NSEC_PER_SEC + - (uint64_t) t1.tv_nsec; - uint64_t ts2 = (uint64_t) t2.tv_sec * (uint64_t) NSEC_PER_SEC + - (uint64_t) t2.tv_nsec; - uint64_t diff = max(ts1, ts2) - min(ts1, ts2); - struct timespec res; - - res.tv_sec = diff / (uint64_t) NSEC_PER_SEC; - res.tv_nsec = diff % (uint64_t) NSEC_PER_SEC; - return res; -} - -static -void __attribute__((constructor)) init_locale_utf8_support(void) -{ - const char *program_locale = setlocale(LC_ALL, NULL); - const char *lang = getenv("LANG"); - - if (program_locale && strstr(program_locale, "utf8")) { - utf8_output_supported = true; - } else if (lang && strstr(lang, "utf8")) { - utf8_output_supported = true; - } -} - -int time_to_iso8601_str(time_t time, char *str, size_t len) -{ - int ret = 0; - struct tm *tm_result; - struct tm tm_storage; - size_t strf_ret; - - if (len < ISO8601_STR_LEN) { - ERR("Buffer too short to format ISO 8601 timestamp: %zu bytes provided when at least %zu are needed", - len, ISO8601_STR_LEN); - ret = -1; - goto end; - } - - tm_result = localtime_r(&time, &tm_storage); - if (!tm_result) { - ret = -1; - PERROR("Failed to break down timestamp to tm structure"); - goto end; - } - - strf_ret = strftime(str, len, "%Y%m%dT%H%M%S%z", tm_result); - if (strf_ret == 0) { - ret = -1; - ERR("Failed to format timestamp as local time"); - goto end; - } -end: - return ret; -} - -int time_to_datetime_str(time_t time, char *str, size_t len) -{ - int ret = 0; - struct tm *tm_result; - struct tm tm_storage; - size_t strf_ret; - - if (len < DATETIME_STR_LEN) { - ERR("Buffer too short to format to datetime: %zu bytes provided when at least %zu are needed", - len, DATETIME_STR_LEN); - ret = -1; - goto end; - } - - tm_result = localtime_r(&time, &tm_storage); - if (!tm_result) { - ret = -1; - PERROR("Failed to break down timestamp to tm structure"); - goto end; - } - - strf_ret = strftime(str, len, "%Y%m%d-%H%M%S", tm_result); - if (strf_ret == 0) { - ret = -1; - ERR("Failed to format timestamp as local time"); - goto end; - } -end: - return ret; -} diff --git a/src/common/time.cpp b/src/common/time.cpp new file mode 100644 index 000000000..a08358ffc --- /dev/null +++ b/src/common/time.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool utf8_output_supported; + +bool locale_supports_utf8(void) +{ + return utf8_output_supported; +} + +int timespec_to_ms(struct timespec ts, unsigned long *ms) +{ + unsigned long res, remain_ms; + + if (ts.tv_sec > ULONG_MAX / MSEC_PER_SEC) { + errno = EOVERFLOW; + return -1; /* multiplication overflow */ + } + res = ts.tv_sec * MSEC_PER_SEC; + remain_ms = ULONG_MAX - res; + if (ts.tv_nsec / NSEC_PER_MSEC > remain_ms) { + errno = EOVERFLOW; + return -1; /* addition overflow */ + } + res += ts.tv_nsec / NSEC_PER_MSEC; + *ms = res; + return 0; +} + +struct timespec timespec_abs_diff(struct timespec t1, struct timespec t2) +{ + uint64_t ts1 = (uint64_t) t1.tv_sec * (uint64_t) NSEC_PER_SEC + + (uint64_t) t1.tv_nsec; + uint64_t ts2 = (uint64_t) t2.tv_sec * (uint64_t) NSEC_PER_SEC + + (uint64_t) t2.tv_nsec; + uint64_t diff = std::max(ts1, ts2) - std::min(ts1, ts2); + struct timespec res; + + res.tv_sec = diff / (uint64_t) NSEC_PER_SEC; + res.tv_nsec = diff % (uint64_t) NSEC_PER_SEC; + return res; +} + +static +void __attribute__((constructor)) init_locale_utf8_support(void) +{ + const char *program_locale = setlocale(LC_ALL, NULL); + const char *lang = getenv("LANG"); + + if (program_locale && strstr(program_locale, "utf8")) { + utf8_output_supported = true; + } else if (lang && strstr(lang, "utf8")) { + utf8_output_supported = true; + } +} + +int time_to_iso8601_str(time_t time, char *str, size_t len) +{ + int ret = 0; + struct tm *tm_result; + struct tm tm_storage; + size_t strf_ret; + + if (len < ISO8601_STR_LEN) { + ERR("Buffer too short to format ISO 8601 timestamp: %zu bytes provided when at least %zu are needed", + len, ISO8601_STR_LEN); + ret = -1; + goto end; + } + + tm_result = localtime_r(&time, &tm_storage); + if (!tm_result) { + ret = -1; + PERROR("Failed to break down timestamp to tm structure"); + goto end; + } + + strf_ret = strftime(str, len, "%Y%m%dT%H%M%S%z", tm_result); + if (strf_ret == 0) { + ret = -1; + ERR("Failed to format timestamp as local time"); + goto end; + } +end: + return ret; +} + +int time_to_datetime_str(time_t time, char *str, size_t len) +{ + int ret = 0; + struct tm *tm_result; + struct tm tm_storage; + size_t strf_ret; + + if (len < DATETIME_STR_LEN) { + ERR("Buffer too short to format to datetime: %zu bytes provided when at least %zu are needed", + len, DATETIME_STR_LEN); + ret = -1; + goto end; + } + + tm_result = localtime_r(&time, &tm_storage); + if (!tm_result) { + ret = -1; + PERROR("Failed to break down timestamp to tm structure"); + goto end; + } + + strf_ret = strftime(str, len, "%Y%m%d-%H%M%S", tm_result); + if (strf_ret == 0) { + ret = -1; + ERR("Failed to format timestamp as local time"); + goto end; + } +end: + return ret; +} diff --git a/src/common/trace-chunk.c b/src/common/trace-chunk.c deleted file mode 100644 index d04d51637..000000000 --- a/src/common/trace-chunk.c +++ /dev/null @@ -1,2191 +0,0 @@ -/* - * Copyright (C) 2019 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * Two ISO 8601-compatible timestamps, separated by a hypen, followed an - * index, i.e. --. - */ -#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t)) -#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) - -enum trace_chunk_mode { - TRACE_CHUNK_MODE_USER, - TRACE_CHUNK_MODE_OWNER, -}; - -/* - * Callback to invoke on release of a trace chunk. Note that there is no - * need to 'lock' the trace chunk during the execution of these callbacks - * since only one thread may access a chunk during its destruction (the last - * to release its reference to the chunk). - */ -typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk); - -/* Move a completed trace chunk to the 'completed' trace archive folder. */ -static -int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk); -/* Empty callback. */ -static -int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk); -/* Unlink old chunk files. */ -static -int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk); -static -enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( - struct lttng_trace_chunk *chunk, const char *path); - -struct chunk_credentials { - bool use_current_user; - struct lttng_credentials user; -}; - -/* - * NOTE: Make sure to update: - * - lttng_trace_chunk_copy(), - * - lttng_trace_chunk_registry_element_create_from_chunk() - * if you modify this structure. - */ -struct lttng_trace_chunk { - pthread_mutex_t lock; - struct urcu_ref ref; - LTTNG_OPTIONAL(enum trace_chunk_mode) mode; - /* - * First-level directories created within the trace chunk. - * Elements are of type 'char *'. - * - * Only used by _owner_ mode chunks. - */ - struct lttng_dynamic_pointer_array top_level_directories; - /* - * All files contained within the trace chunk. - * Array of paths (char *). - */ - struct lttng_dynamic_pointer_array files; - /* Is contained within an lttng_trace_chunk_registry_element? */ - bool in_registry_element; - bool name_overridden; - char *name; - char *path; - /* An unset id means the chunk is anonymous. */ - LTTNG_OPTIONAL(uint64_t) id; - - /* - * The creation and close timestamps are NOT monotonic. - * They must not be used in context were monotonicity is required. - */ - LTTNG_OPTIONAL(time_t) timestamp_creation; - LTTNG_OPTIONAL(time_t) timestamp_close; - - LTTNG_OPTIONAL(struct chunk_credentials) credentials; - struct lttng_directory_handle *session_output_directory; - struct lttng_directory_handle *chunk_directory; - LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command; - /* - * fd_tracker instance through which file descriptors should be - * created/closed. - * - * An fd_tracker always outlives any trace chunk; there is no - * need to perform any reference counting of that object. - */ - struct fd_tracker *fd_tracker; -}; - -/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */ -struct lttng_trace_chunk_registry_element { - struct lttng_trace_chunk chunk; - uint64_t session_id; - /* Weak and only set when added. */ - struct lttng_trace_chunk_registry *registry; - struct cds_lfht_node trace_chunk_registry_ht_node; - /* call_rcu delayed reclaim. */ - struct rcu_head rcu_node; -}; - -struct lttng_trace_chunk_registry { - struct cds_lfht *ht; -}; - -struct fs_handle_untracked { - struct fs_handle parent; - int fd; - struct { - struct lttng_directory_handle *directory_handle; - char *path; - } location; -}; - -static -int fs_handle_untracked_get_fd(struct fs_handle *handle); -static -void fs_handle_untracked_put_fd(struct fs_handle *handle); -static -int fs_handle_untracked_unlink(struct fs_handle *handle); -static -int fs_handle_untracked_close(struct fs_handle *handle); - -static const -char *close_command_names[] = { - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] = - "move to completed chunk folder", - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] = - "no operation", - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] = - "delete", -}; - -static const -chunk_command close_command_post_release_funcs[] = { - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED] = - lttng_trace_chunk_move_to_completed_post_release, - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION] = - lttng_trace_chunk_no_operation, - [LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE] = - lttng_trace_chunk_delete_post_release, -}; - -static -struct fs_handle *fs_handle_untracked_create( - struct lttng_directory_handle *directory_handle, - const char *path, - int fd) -{ - struct fs_handle_untracked *handle = NULL; - bool reference_acquired; - char *path_copy = strdup(path); - - LTTNG_ASSERT(fd >= 0); - if (!path_copy) { - PERROR("Failed to copy file path while creating untracked filesystem handle"); - goto end; - } - - handle = zmalloc(sizeof(typeof(*handle))); - if (!handle) { - PERROR("Failed to allocate untracked filesystem handle"); - goto end; - } - - handle->parent = (typeof(handle->parent)) { - .get_fd = fs_handle_untracked_get_fd, - .put_fd = fs_handle_untracked_put_fd, - .unlink = fs_handle_untracked_unlink, - .close = fs_handle_untracked_close, - }; - - handle->fd = fd; - reference_acquired = lttng_directory_handle_get(directory_handle); - LTTNG_ASSERT(reference_acquired); - handle->location.directory_handle = directory_handle; - /* Ownership is transferred. */ - handle->location.path = path_copy; - path_copy = NULL; -end: - free(path_copy); - return handle ? &handle->parent : NULL; -} - -static -int fs_handle_untracked_get_fd(struct fs_handle *_handle) -{ - struct fs_handle_untracked *handle = container_of( - _handle, struct fs_handle_untracked, parent); - - return handle->fd; -} - -static -void fs_handle_untracked_put_fd(struct fs_handle *_handle) -{ - /* no-op. */ -} - -static -int fs_handle_untracked_unlink(struct fs_handle *_handle) -{ - struct fs_handle_untracked *handle = container_of( - _handle, struct fs_handle_untracked, parent); - - return lttng_directory_handle_unlink_file( - handle->location.directory_handle, - handle->location.path); -} - -static -void fs_handle_untracked_destroy(struct fs_handle_untracked *handle) -{ - lttng_directory_handle_put(handle->location.directory_handle); - free(handle->location.path); - free(handle); -} - -static -int fs_handle_untracked_close(struct fs_handle *_handle) -{ - struct fs_handle_untracked *handle = container_of( - _handle, struct fs_handle_untracked, parent); - int ret = close(handle->fd); - - fs_handle_untracked_destroy(handle); - return ret; -} - -static -bool lttng_trace_chunk_registry_element_equals( - const struct lttng_trace_chunk_registry_element *a, - const struct lttng_trace_chunk_registry_element *b) -{ - if (a->session_id != b->session_id) { - goto not_equal; - } - if (a->chunk.id.is_set != b->chunk.id.is_set) { - goto not_equal; - } - if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) { - goto not_equal; - } - return true; -not_equal: - return false; -} - -static -int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node, - const void *key) -{ - const struct lttng_trace_chunk_registry_element *element_a, *element_b; - - element_a = (const struct lttng_trace_chunk_registry_element *) key; - element_b = caa_container_of(node, typeof(*element_b), - trace_chunk_registry_ht_node); - return lttng_trace_chunk_registry_element_equals(element_a, element_b); -} - -static -unsigned long lttng_trace_chunk_registry_element_hash( - const struct lttng_trace_chunk_registry_element *element) -{ - unsigned long hash = hash_key_u64(&element->session_id, - lttng_ht_seed); - - if (element->chunk.id.is_set) { - hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed); - } - - return hash; -} - -static -char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, - const time_t *close_timestamp) -{ - int ret = 0; - char *new_name= NULL; - char start_datetime[ISO8601_STR_LEN] = {}; - /* Add 1 for a '-' prefix. */ - char end_datetime_suffix[ISO8601_STR_LEN + 1] = {}; - - ret = time_to_iso8601_str( - creation_timestamp, - start_datetime, sizeof(start_datetime)); - if (ret) { - ERR("Failed to format trace chunk start date time"); - goto error; - } - if (close_timestamp) { - *end_datetime_suffix = '-'; - ret = time_to_iso8601_str( - *close_timestamp, - end_datetime_suffix + 1, - sizeof(end_datetime_suffix) - 1); - if (ret) { - ERR("Failed to format trace chunk end date time"); - goto error; - } - } - new_name = zmalloc(GENERATED_CHUNK_NAME_LEN); - if (!new_name) { - ERR("Failed to allocate buffer for automatically-generated trace chunk name"); - goto error; - } - ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64, - start_datetime, end_datetime_suffix, chunk_id); - if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) { - ERR("Failed to format trace chunk name"); - goto error; - } - - return new_name; -error: - free(new_name); - return NULL; -} - -static -void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk) -{ - urcu_ref_init(&chunk->ref); - pthread_mutex_init(&chunk->lock, NULL); - lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free); - lttng_dynamic_pointer_array_init(&chunk->files, free); -} - -static -void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk) -{ - if (chunk->session_output_directory) { - lttng_directory_handle_put( - chunk->session_output_directory); - chunk->session_output_directory = NULL; - } - if (chunk->chunk_directory) { - lttng_directory_handle_put(chunk->chunk_directory); - chunk->chunk_directory = NULL; - } - free(chunk->name); - chunk->name = NULL; - free(chunk->path); - chunk->path = NULL; - lttng_dynamic_pointer_array_reset(&chunk->top_level_directories); - lttng_dynamic_pointer_array_reset(&chunk->files); - pthread_mutex_destroy(&chunk->lock); -} - -static -struct lttng_trace_chunk *lttng_trace_chunk_allocate(void) -{ - struct lttng_trace_chunk *chunk = NULL; - - chunk = zmalloc(sizeof(*chunk)); - if (!chunk) { - ERR("Failed to allocate trace chunk"); - goto end; - } - lttng_trace_chunk_init(chunk); -end: - return chunk; -} - -struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void) -{ - DBG("Creating anonymous trace chunk"); - return lttng_trace_chunk_allocate(); -} - -struct lttng_trace_chunk *lttng_trace_chunk_create( - uint64_t chunk_id, time_t chunk_creation_time, const char *path) -{ - struct lttng_trace_chunk *chunk; - char chunk_creation_datetime_buf[16] = {}; - const char *chunk_creation_datetime_str = "(formatting error)"; - struct tm timeinfo_buf, *timeinfo; - - timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf); - if (timeinfo) { - size_t strftime_ret; - - /* Don't fail because of this; it is only used for logging. */ - strftime_ret = strftime(chunk_creation_datetime_buf, - sizeof(chunk_creation_datetime_buf), - "%Y%m%d-%H%M%S", timeinfo); - if (strftime_ret) { - chunk_creation_datetime_str = - chunk_creation_datetime_buf; - } - } - - DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s", - chunk_id, chunk_creation_datetime_str); - chunk = lttng_trace_chunk_allocate(); - if (!chunk) { - goto end; - } - - LTTNG_OPTIONAL_SET(&chunk->id, chunk_id); - LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time); - if (chunk_id != 0) { - chunk->name = generate_chunk_name(chunk_id, - chunk_creation_time, NULL); - if (!chunk->name) { - ERR("Failed to allocate trace chunk name storage"); - goto error; - } - } - if (path) { - chunk->path = strdup(path); - if (!chunk->path) { - goto error; - } - } else { - if (chunk->name) { - chunk->path = strdup(chunk->name); - if (!chunk->path) { - goto error; - } - } - } - - DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)"); -end: - return chunk; -error: - lttng_trace_chunk_put(chunk); - return NULL; -} - -void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk, - struct fd_tracker *fd_tracker) -{ - LTTNG_ASSERT(!chunk->session_output_directory); - LTTNG_ASSERT(!chunk->chunk_directory); - LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0); - chunk->fd_tracker = fd_tracker; -} - -struct lttng_trace_chunk *lttng_trace_chunk_copy( - struct lttng_trace_chunk *source_chunk) -{ - struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate(); - - if (!new_chunk) { - goto end; - } - - pthread_mutex_lock(&source_chunk->lock); - /* - * A new chunk is always a user; it shall create no new trace - * subdirectories. - */ - new_chunk->mode = (typeof(new_chunk->mode)) { - .is_set = true, - .value = TRACE_CHUNK_MODE_USER, - }; - /* - * top_level_directories is not copied as it is never used - * by _user_ mode chunks. - */ - /* The new chunk is not part of a registry (yet, at least). */ - new_chunk->in_registry_element = false; - new_chunk->name_overridden = source_chunk->name_overridden; - if (source_chunk->name) { - new_chunk->name = strdup(source_chunk->name); - if (!new_chunk->name) { - ERR("Failed to copy source trace chunk name in %s()", - __FUNCTION__); - goto error_unlock; - } - } - if (source_chunk->path) { - new_chunk->path = strdup(source_chunk->path); - if (!new_chunk->path) { - ERR("Failed to copy source trace chunk path in %s()", - __FUNCTION__); - } - } - new_chunk->id = source_chunk->id; - new_chunk->timestamp_creation = source_chunk->timestamp_creation; - new_chunk->timestamp_close = source_chunk->timestamp_close; - new_chunk->credentials = source_chunk->credentials; - if (source_chunk->session_output_directory) { - const bool reference_acquired = lttng_directory_handle_get( - source_chunk->session_output_directory); - - LTTNG_ASSERT(reference_acquired); - new_chunk->session_output_directory = - source_chunk->session_output_directory; - } - if (source_chunk->chunk_directory) { - const bool reference_acquired = lttng_directory_handle_get( - source_chunk->chunk_directory); - - LTTNG_ASSERT(reference_acquired); - new_chunk->chunk_directory = source_chunk->chunk_directory; - } - new_chunk->close_command = source_chunk->close_command; - new_chunk->fd_tracker = source_chunk->fd_tracker; - pthread_mutex_unlock(&source_chunk->lock); -end: - return new_chunk; -error_unlock: - pthread_mutex_unlock(&source_chunk->lock); - lttng_trace_chunk_put(new_chunk); - return NULL; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_id( - struct lttng_trace_chunk *chunk, uint64_t *id) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->id.is_set) { - *id = chunk->id.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp( - struct lttng_trace_chunk *chunk, time_t *creation_ts) - -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->timestamp_creation.is_set) { - *creation_ts = chunk->timestamp_creation.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp( - struct lttng_trace_chunk *chunk, time_t *close_ts) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->timestamp_close.is_set) { - *close_ts = chunk->timestamp_close.value; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp( - struct lttng_trace_chunk *chunk, time_t close_ts) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (!chunk->timestamp_creation.is_set) { - ERR("Failed to set trace chunk close timestamp: creation timestamp is unset"); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - - /* - * Note: we do not enforce that the closing timestamp be greater or - * equal to the begin timestamp. These timestamps are used for - * generating the chunk name and should only be used in context where - * the monotonicity of time is not important. The source of those - * timestamps is NOT monotonic and represent the system calendar time, - * also know as the wall time. - */ - if (chunk->timestamp_creation.value > close_ts) { - WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld", - chunk->timestamp_creation.value, close_ts); - } - - LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts); - if (!chunk->name_overridden) { - free(chunk->name); - chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id), - LTTNG_OPTIONAL_GET(chunk->timestamp_creation), - &close_ts); - if (!chunk->name) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - } - } -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_name( - struct lttng_trace_chunk *chunk, const char **name, - bool *name_overridden) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (name_overridden) { - *name_overridden = chunk->name_overridden; - } - if (!chunk->name) { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - goto end; - } - *name = chunk->name; -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk) -{ - bool name_overridden; - - pthread_mutex_lock(&chunk->lock); - name_overridden = chunk->name_overridden; - pthread_mutex_unlock(&chunk->lock); - return name_overridden; -} - -static -bool is_valid_chunk_name(const char *name) -{ - size_t len; - - if (!name) { - return false; - } - - len = lttng_strnlen(name, LTTNG_NAME_MAX); - if (len == 0 || len == LTTNG_NAME_MAX) { - return false; - } - - if (strchr(name, '/') || strchr(name, '.')) { - return false; - } - - return true; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_override_name( - struct lttng_trace_chunk *chunk, const char *name) - -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - char *new_name, *new_path; - - DBG("Override trace chunk name from %s to %s", chunk->name, name); - if (!is_valid_chunk_name(name)) { - ERR("Attempted to set an invalid name on a trace chunk: name = %s", - name ? : "NULL"); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - - pthread_mutex_lock(&chunk->lock); - if (!chunk->id.is_set) { - ERR("Attempted to set an override name on an anonymous trace chunk: name = %s", - name); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end_unlock; - } - - new_name = strdup(name); - if (!new_name) { - ERR("Failed to allocate new trace chunk name"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end_unlock; - } - free(chunk->name); - chunk->name = new_name; - - new_path = strdup(name); - if (!new_path) { - ERR("Failed to allocate new trace chunk path"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end_unlock; - } - free(chunk->path); - chunk->path = new_path; - - chunk->name_overridden = true; -end_unlock: - pthread_mutex_unlock(&chunk->lock); -end: - return status; -} - -static -enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( - struct lttng_trace_chunk *chunk, const char *path) - -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - struct lttng_directory_handle *rename_directory = NULL; - char *new_path, *old_path; - int ret; - - if (chunk->name_overridden) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - - old_path = chunk->path; - DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path); - - if ((!old_path && !path) || - (old_path && path && !strcmp(old_path, path))) { - goto end; - } - /* - * Use chunk name as path if NULL path is specified. - */ - if (!path) { - path = chunk->name; - } - - /* Renaming from "" to "" is not accepted. */ - if (path[0] == '\0' && old_path[0] == '\0') { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - - /* - * If a rename is performed on a chunk for which the chunk_directory - * is not set (yet), or the session_output_directory is not set - * (interacting with a relay daemon), there is no rename to perform. - */ - if (!chunk->chunk_directory || - !chunk->session_output_directory) { - goto skip_move; - } - - if (old_path && old_path[0] != '\0' && path[0] != '\0') { - /* Rename chunk directory. */ - ret = lttng_directory_handle_rename_as_user( - chunk->session_output_directory, - old_path, - chunk->session_output_directory, - path, - LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? - NULL : - &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"", - old_path, path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - rename_directory = chunk->fd_tracker ? - fd_tracker_create_directory_handle_from_handle( - chunk->fd_tracker, - chunk->session_output_directory, - path) : - lttng_directory_handle_create_from_handle( - path, - chunk->session_output_directory); - if (!rename_directory) { - ERR("Failed to get handle to trace chunk rename directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - - /* Release old handle. */ - lttng_directory_handle_put(chunk->chunk_directory); - /* - * Transfer new handle reference to chunk as the current chunk - * handle. - */ - chunk->chunk_directory = rename_directory; - rename_directory = NULL; - } else if (old_path && old_path[0] == '\0') { - size_t i, count = lttng_dynamic_pointer_array_get_count( - &chunk->top_level_directories); - - ret = lttng_directory_handle_create_subdirectory_as_user( - chunk->session_output_directory, - path, - DIR_CREATION_MODE, - LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? - NULL : - &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to create trace chunk rename directory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - - rename_directory = lttng_directory_handle_create_from_handle( - path, chunk->session_output_directory); - if (!rename_directory) { - ERR("Failed to get handle to trace chunk rename directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - - /* Move toplevel directories. */ - for (i = 0; i < count; i++) { - const char *top_level_name = - lttng_dynamic_pointer_array_get_pointer( - &chunk->top_level_directories, i); - - ret = lttng_directory_handle_rename_as_user( - chunk->chunk_directory, - top_level_name, - rename_directory, - top_level_name, - LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? - NULL : - &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to move \"%s\" to trace chunk rename directory", - top_level_name); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - } - /* Release old handle. */ - lttng_directory_handle_put(chunk->chunk_directory); - /* - * Transfer new handle reference to chunk as the current chunk - * handle. - */ - chunk->chunk_directory = rename_directory; - rename_directory = NULL; - } else if (old_path) { - size_t i, count = lttng_dynamic_pointer_array_get_count( - &chunk->top_level_directories); - const bool reference_acquired = lttng_directory_handle_get( - chunk->session_output_directory); - - LTTNG_ASSERT(reference_acquired); - rename_directory = chunk->session_output_directory; - - /* Move toplevel directories. */ - for (i = 0; i < count; i++) { - const char *top_level_name = - lttng_dynamic_pointer_array_get_pointer( - &chunk->top_level_directories, i); - - ret = lttng_directory_handle_rename_as_user( - chunk->chunk_directory, - top_level_name, - rename_directory, - top_level_name, - LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? - NULL : - &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to move \"%s\" to trace chunk rename directory", - top_level_name); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - } - /* Release old handle. */ - lttng_directory_handle_put(chunk->chunk_directory); - /* - * Transfer new handle reference to chunk as the current chunk - * handle. - */ - chunk->chunk_directory = rename_directory; - rename_directory = NULL; - - /* Remove old directory. */ - status = lttng_directory_handle_remove_subdirectory( - chunk->session_output_directory, - old_path); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ERR("Error removing subdirectory '%s' file when deleting chunk", - old_path); - goto end; - } - } else { - /* Unexpected !old_path && !path. */ - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - -skip_move: - new_path = strdup(path); - if (!new_path) { - ERR("Failed to allocate new trace chunk path"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - free(chunk->path); - chunk->path = new_path; -end: - lttng_directory_handle_put(rename_directory); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_rename_path( - struct lttng_trace_chunk *chunk, const char *path) - -{ - enum lttng_trace_chunk_status status; - - pthread_mutex_lock(&chunk->lock); - status = lttng_trace_chunk_rename_path_no_lock(chunk, path); - pthread_mutex_unlock(&chunk->lock); - - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( - struct lttng_trace_chunk *chunk, - struct lttng_credentials *credentials) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - if (chunk->credentials.value.use_current_user) { - LTTNG_OPTIONAL_SET(&credentials->uid, geteuid()); - LTTNG_OPTIONAL_SET(&credentials->gid, getegid()); - } else { - *credentials = chunk->credentials.value.user; - } - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials( - struct lttng_trace_chunk *chunk, - const struct lttng_credentials *user_credentials) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - const struct chunk_credentials credentials = { - .user = *user_credentials, - .use_current_user = false, - }; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user( - struct lttng_trace_chunk *chunk) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - const struct chunk_credentials credentials = { - .use_current_user = true, - }; - - pthread_mutex_lock(&chunk->lock); - if (chunk->credentials.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - - -enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( - struct lttng_trace_chunk *chunk, - struct lttng_directory_handle *session_output_directory) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - struct lttng_directory_handle *chunk_directory_handle = NULL; - bool reference_acquired; - - pthread_mutex_lock(&chunk->lock); - if (chunk->mode.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * directory is created. - */ - ERR("Credentials of trace chunk are unset: refusing to set session output directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (chunk->path && chunk->path[0] != '\0') { - ret = lttng_directory_handle_create_subdirectory_as_user( - session_output_directory, - chunk->path, - DIR_CREATION_MODE, - !chunk->credentials.value.use_current_user ? - &chunk->credentials.value.user : NULL); - if (ret) { - PERROR("Failed to create chunk output directory \"%s\"", - chunk->path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - chunk_directory_handle = - chunk->fd_tracker ? - fd_tracker_create_directory_handle_from_handle( - chunk->fd_tracker, - session_output_directory, - chunk->path) : - lttng_directory_handle_create_from_handle( - chunk->path, - session_output_directory); - if (!chunk_directory_handle) { - /* The function already logs on all error paths. */ - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - } else { - /* - * A nameless chunk does not need its own output directory. - * The session's output directory will be used. - */ - reference_acquired = lttng_directory_handle_get( - session_output_directory); - - LTTNG_ASSERT(reference_acquired); - chunk_directory_handle = session_output_directory; - } - chunk->chunk_directory = chunk_directory_handle; - chunk_directory_handle = NULL; - reference_acquired = lttng_directory_handle_get( - session_output_directory); - LTTNG_ASSERT(reference_acquired); - chunk->session_output_directory = session_output_directory; - LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user( - struct lttng_trace_chunk *chunk, - struct lttng_directory_handle *chunk_directory) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - bool reference_acquired; - - pthread_mutex_lock(&chunk->lock); - if (chunk->mode.is_set) { - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - if (!chunk->credentials.is_set) { - ERR("Credentials of trace chunk are unset: refusing to set chunk output directory"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - reference_acquired = lttng_directory_handle_get(chunk_directory); - LTTNG_ASSERT(reference_acquired); - chunk->chunk_directory = chunk_directory; - LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status -lttng_trace_chunk_get_session_output_directory_handle( - struct lttng_trace_chunk *chunk, - struct lttng_directory_handle **handle) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (!chunk->session_output_directory) { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - *handle = NULL; - goto end; - } else { - const bool reference_acquired = lttng_directory_handle_get( - chunk->session_output_directory); - - LTTNG_ASSERT(reference_acquired); - *handle = chunk->session_output_directory; - } -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle( - struct lttng_trace_chunk *chunk, - const struct lttng_directory_handle **handle) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (!chunk->chunk_directory) { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - goto end; - } - - *handle = chunk->chunk_directory; -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -/* Add a top-level directory to the trace chunk if it was previously unknown. */ -static -int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, - const char *new_path) -{ - int ret = 0; - bool found = false; - size_t i, count = lttng_dynamic_pointer_array_get_count( - &chunk->top_level_directories); - const char *new_path_separator_pos = strchr(new_path, '/'); - const ptrdiff_t new_path_top_level_len = new_path_separator_pos ? - new_path_separator_pos - new_path : strlen(new_path); - - for (i = 0; i < count; i++) { - const char *path = lttng_dynamic_pointer_array_get_pointer( - &chunk->top_level_directories, i); - const ptrdiff_t path_top_level_len = strlen(path); - - if (path_top_level_len != new_path_top_level_len) { - continue; - } - if (!strncmp(path, new_path, path_top_level_len)) { - found = true; - break; - } - } - - if (!found) { - char *copy = lttng_strndup(new_path, new_path_top_level_len); - - DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"", - new_path, chunk->name ? : "(unnamed)"); - if (!copy) { - PERROR("Failed to copy path"); - ret = -1; - goto end; - } - ret = lttng_dynamic_pointer_array_add_pointer( - &chunk->top_level_directories, copy); - if (ret) { - ERR("Allocation failure while adding top-level directory entry to a trace chunk"); - free(copy); - goto end; - } - } -end: - return ret; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory( - struct lttng_trace_chunk *chunk, - const char *path) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Creating trace chunk subdirectory \"%s\"", path); - pthread_mutex_lock(&chunk->lock); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * directory is created. - */ - ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (!chunk->mode.is_set || - chunk->mode.value != TRACE_CHUNK_MODE_OWNER) { - ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk", - path); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; - goto end; - } - if (!chunk->chunk_directory) { - ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (*path == '/') { - ERR("Refusing to create absolute trace chunk directory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - ret = lttng_directory_handle_create_subdirectory_recursive_as_user( - chunk->chunk_directory, path, - DIR_CREATION_MODE, - chunk->credentials.value.use_current_user ? - NULL : &chunk->credentials.value.user); - if (ret) { - PERROR("Failed to create trace chunk subdirectory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - ret = add_top_level_directory_unique(chunk, path); - if (ret) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -/* - * TODO: Implement O(1) lookup. - */ -static -bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk, - const char *path, size_t *index) -{ - size_t i, count; - - count = lttng_dynamic_pointer_array_get_count(&chunk->files); - for (i = 0; i < count; i++) { - const char *iter_path = - lttng_dynamic_pointer_array_get_pointer( - &chunk->files, i); - if (!strcmp(iter_path, path)) { - if (index) { - *index = i; - } - return true; - } - } - return false; -} - -static -enum lttng_trace_chunk_status lttng_trace_chunk_add_file( - struct lttng_trace_chunk *chunk, - const char *path) -{ - char *copy; - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - if (lttng_trace_chunk_find_file(chunk, path, NULL)) { - return LTTNG_TRACE_CHUNK_STATUS_OK; - } - DBG("Adding new file \"%s\" to trace chunk \"%s\"", - path, chunk->name ? : "(unnamed)"); - copy = strdup(path); - if (!copy) { - PERROR("Failed to copy path"); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - ret = lttng_dynamic_pointer_array_add_pointer( - &chunk->files, copy); - if (ret) { - ERR("Allocation failure while adding file to a trace chunk"); - free(copy); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } -end: - return status; -} - -static -void lttng_trace_chunk_remove_file( - struct lttng_trace_chunk *chunk, - const char *path) -{ - size_t index; - bool found; - int ret; - - found = lttng_trace_chunk_find_file(chunk, path, &index); - if (!found) { - return; - } - ret = lttng_dynamic_pointer_array_remove_pointer( - &chunk->files, index); - LTTNG_ASSERT(!ret); -} - -static -enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked( - struct lttng_trace_chunk *chunk, - const char *file_path, - int flags, - mode_t mode, - struct fs_handle **out_handle, - bool expect_no_file) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Opening trace chunk file \"%s\"", file_path); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * file is created. - */ - ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"", - file_path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (!chunk->chunk_directory) { - ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory", - file_path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - status = lttng_trace_chunk_add_file(chunk, file_path); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - goto end; - } - if (chunk->fd_tracker) { - LTTNG_ASSERT(chunk->credentials.value.use_current_user); - *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker, - chunk->chunk_directory, file_path, flags, &mode); - ret = *out_handle ? 0 : -1; - } else { - ret = lttng_directory_handle_open_file_as_user( - chunk->chunk_directory, file_path, flags, mode, - chunk->credentials.value.use_current_user ? - NULL : - &chunk->credentials.value.user); - if (ret >= 0) { - *out_handle = fs_handle_untracked_create( - chunk->chunk_directory, file_path, ret); - if (!*out_handle) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - } - } - if (ret < 0) { - if (errno == ENOENT && expect_no_file) { - status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE; - } else { - PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d", - file_path, flags, (int) mode); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - } - lttng_trace_chunk_remove_file(chunk, file_path); - goto end; - } -end: - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle( - struct lttng_trace_chunk *chunk, - const char *file_path, - int flags, - mode_t mode, - struct fs_handle **out_handle, - bool expect_no_file) -{ - enum lttng_trace_chunk_status status; - - pthread_mutex_lock(&chunk->lock); - status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, - flags, mode, out_handle, expect_no_file); - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_open_file( - struct lttng_trace_chunk *chunk, - const char *file_path, - int flags, - mode_t mode, - int *out_fd, - bool expect_no_file) -{ - enum lttng_trace_chunk_status status; - struct fs_handle *fs_handle; - - pthread_mutex_lock(&chunk->lock); - /* - * Using this method is never valid when an fd_tracker is being - * used since the resulting file descriptor would not be tracked. - */ - LTTNG_ASSERT(!chunk->fd_tracker); - status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, - flags, mode, &fs_handle, expect_no_file); - pthread_mutex_unlock(&chunk->lock); - - if (status == LTTNG_TRACE_CHUNK_STATUS_OK) { - *out_fd = fs_handle_get_fd(fs_handle); - /* - * Does not close the fd; we just "unbox" it from the fs_handle. - */ - fs_handle_untracked_destroy(container_of( - fs_handle, struct fs_handle_untracked, parent)); - } - - return status; -} - -int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, - const char *file_path) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Unlinking trace chunk file \"%s\"", file_path); - pthread_mutex_lock(&chunk->lock); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * file is unlinked. - */ - ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"", - file_path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (!chunk->chunk_directory) { - ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory", - file_path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - ret = lttng_directory_handle_unlink_file_as_user( - chunk->chunk_directory, file_path, - chunk->credentials.value.use_current_user ? - NULL : &chunk->credentials.value.user); - if (ret < 0) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - lttng_trace_chunk_remove_file(chunk, file_path); -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -static -int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk, - const char *path) -{ - int ret; - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - DBG("Recursively removing trace chunk directory \"%s\"", path); - pthread_mutex_lock(&chunk->lock); - if (!chunk->credentials.is_set) { - /* - * Fatal error, credentials must be set before a - * directory is removed. - */ - ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - if (!chunk->chunk_directory) { - ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory", - path); - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } - ret = lttng_directory_handle_remove_subdirectory_recursive_as_user( - chunk->chunk_directory, path, - chunk->credentials.value.use_current_user ? - NULL : &chunk->credentials.value.user, - LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); - if (ret < 0) { - status = LTTNG_TRACE_CHUNK_STATUS_ERROR; - goto end; - } -end: - pthread_mutex_unlock(&chunk->lock); - return status; -} - -static -int lttng_trace_chunk_move_to_completed_post_release( - struct lttng_trace_chunk *trace_chunk) -{ - int ret = 0; - char *archived_chunk_name = NULL; - const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id); - const time_t creation_timestamp = - LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation); - const time_t close_timestamp = - LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close); - struct lttng_directory_handle *archived_chunks_directory = NULL; - enum lttng_trace_chunk_status status; - - if (!trace_chunk->mode.is_set || - trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER || - !trace_chunk->session_output_directory) { - /* - * This command doesn't need to run if the output is remote - * or if the trace chunk is not owned by this process. - */ - goto end; - } - - LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER); - LTTNG_ASSERT(!trace_chunk->name_overridden); - LTTNG_ASSERT(trace_chunk->path); - - archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp, - &close_timestamp); - if (!archived_chunk_name) { - ERR("Failed to generate archived trace chunk name while renaming trace chunk"); - ret = -1; - goto end; - } - - ret = lttng_directory_handle_create_subdirectory_as_user( - trace_chunk->session_output_directory, - DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, - DIR_CREATION_MODE, - !trace_chunk->credentials.value.use_current_user ? - &trace_chunk->credentials.value.user : - NULL); - if (ret) { - PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY - "\" directory for archived trace chunks"); - goto end; - } - - archived_chunks_directory = trace_chunk->fd_tracker ? - fd_tracker_create_directory_handle_from_handle( - trace_chunk->fd_tracker, - trace_chunk->session_output_directory, - DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) : - lttng_directory_handle_create_from_handle( - DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, - trace_chunk->session_output_directory); - if (!archived_chunks_directory) { - PERROR("Failed to get handle to archived trace chunks directory"); - ret = -1; - goto end; - } - - /* - * Make sure chunk is renamed to old directory if not already done by - * the creation of the next chunk. This happens if a rotation is - * performed while tracing is stopped. - */ - if (!trace_chunk->path || strcmp(trace_chunk->path, - DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) { - status = lttng_trace_chunk_rename_path_no_lock(trace_chunk, - DEFAULT_CHUNK_TMP_OLD_DIRECTORY); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY); - ret = -1; - goto end; - } - } - - ret = lttng_directory_handle_rename_as_user( - trace_chunk->session_output_directory, - trace_chunk->path, - archived_chunks_directory, - archived_chunk_name, - LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? - NULL : - &trace_chunk->credentials.value.user); - if (ret) { - PERROR("Failed to rename folder \"%s\" to \"%s\"", - trace_chunk->path, - archived_chunk_name); - } - -end: - lttng_directory_handle_put(archived_chunks_directory); - free(archived_chunk_name); - return ret; -} - -static -int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk) -{ - return 0; -} - -static -int lttng_trace_chunk_delete_post_release_user( - struct lttng_trace_chunk *trace_chunk) -{ - int ret = 0; - - DBG("Trace chunk \"delete\" close command post-release (User)"); - - /* Unlink all files. */ - while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) { - enum lttng_trace_chunk_status status; - const char *path; - - /* Remove first. */ - path = lttng_dynamic_pointer_array_get_pointer( - &trace_chunk->files, 0); - DBG("Unlink file: %s", path); - status = lttng_trace_chunk_unlink_file(trace_chunk, path); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ERR("Error unlinking file '%s' when deleting chunk", path); - ret = -1; - goto end; - } - } -end: - return ret; -} - -static -int lttng_trace_chunk_delete_post_release_owner( - struct lttng_trace_chunk *trace_chunk) -{ - enum lttng_trace_chunk_status status; - size_t i, count; - int ret = 0; - - ret = lttng_trace_chunk_delete_post_release_user(trace_chunk); - if (ret) { - goto end; - } - - DBG("Trace chunk \"delete\" close command post-release (Owner)"); - - LTTNG_ASSERT(trace_chunk->session_output_directory); - LTTNG_ASSERT(trace_chunk->chunk_directory); - - /* Remove empty directories. */ - count = lttng_dynamic_pointer_array_get_count( - &trace_chunk->top_level_directories); - - for (i = 0; i < count; i++) { - const char *top_level_name = - lttng_dynamic_pointer_array_get_pointer( - &trace_chunk->top_level_directories, i); - - status = lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ERR("Error recursively removing subdirectory '%s' file when deleting chunk", - top_level_name); - ret = -1; - break; - } - } - if (!ret) { - lttng_directory_handle_put(trace_chunk->chunk_directory); - trace_chunk->chunk_directory = NULL; - - if (trace_chunk->path && trace_chunk->path[0] != '\0') { - status = lttng_directory_handle_remove_subdirectory( - trace_chunk->session_output_directory, - trace_chunk->path); - if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ERR("Error removing subdirectory '%s' file when deleting chunk", - trace_chunk->path); - ret = -1; - } - } - } - free(trace_chunk->path); - trace_chunk->path = NULL; -end: - return ret; -} - -/* - * For local files, session and consumer daemons all run the delete hook. The - * consumer daemons have the list of files to unlink, and technically the - * session daemon is the owner of the chunk. Unlink all files owned by each - * consumer daemon. - */ -static -int lttng_trace_chunk_delete_post_release( - struct lttng_trace_chunk *trace_chunk) -{ - if (!trace_chunk->chunk_directory) { - return 0; - } - - if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) { - return lttng_trace_chunk_delete_post_release_owner(trace_chunk); - } else { - return lttng_trace_chunk_delete_post_release_user(trace_chunk); - } -} - -enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command( - struct lttng_trace_chunk *chunk, - enum lttng_trace_chunk_command_type *command_type) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - pthread_mutex_lock(&chunk->lock); - if (chunk->close_command.is_set) { - *command_type = chunk->close_command.value; - status = LTTNG_TRACE_CHUNK_STATUS_OK; - } else { - status = LTTNG_TRACE_CHUNK_STATUS_NONE; - } - pthread_mutex_unlock(&chunk->lock); - return status; -} - -enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( - struct lttng_trace_chunk *chunk, - enum lttng_trace_chunk_command_type close_command) -{ - enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; - - if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED || - close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) { - status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; - goto end; - } - - pthread_mutex_lock(&chunk->lock); - if (chunk->close_command.is_set) { - DBG("Overriding trace chunk close command from \"%s\" to \"%s\"", - close_command_names[chunk->close_command.value], - close_command_names[close_command]); - } else { - DBG("Setting trace chunk close command to \"%s\"", - close_command_names[close_command]); - } - /* - * Unset close command for no-op for backward compatibility with relayd - * 2.11. - */ - if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) { - LTTNG_OPTIONAL_SET(&chunk->close_command, close_command); - } else { - LTTNG_OPTIONAL_UNSET(&chunk->close_command); - } - pthread_mutex_unlock(&chunk->lock); -end: - return status; -} - -const char *lttng_trace_chunk_command_type_get_name( - enum lttng_trace_chunk_command_type command) -{ - switch (command) { - case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED: - return "move to completed trace chunk folder"; - case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION: - return "no operation"; - case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE: - return "delete"; - default: - abort(); - } -} - -bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a, - const struct lttng_trace_chunk *chunk_b) -{ - bool equal = false; - - if (chunk_a == chunk_b) { - equal = true; - goto end; - } - - if (!!chunk_a ^ !!chunk_b) { - goto end; - } - - if (chunk_a->id.is_set ^ chunk_a->id.is_set) { - /* One id is set and not the other, thus they are not equal. */ - goto end; - } - - if (!chunk_a->id.is_set) { - /* Both ids are unset. */ - equal = true; - } else { - equal = chunk_a->id.value == chunk_b->id.value; - } - -end: - return equal; -} - -bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk) -{ - return urcu_ref_get_unless_zero(&chunk->ref); -} - -static -void free_lttng_trace_chunk_registry_element(struct rcu_head *node) -{ - struct lttng_trace_chunk_registry_element *element = - container_of(node, typeof(*element), rcu_node); - - lttng_trace_chunk_fini(&element->chunk); - free(element); -} - -static -void lttng_trace_chunk_release(struct urcu_ref *ref) -{ - struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk), - ref); - - if (chunk->close_command.is_set) { - if (close_command_post_release_funcs[ - chunk->close_command.value](chunk)) { - ERR("Trace chunk post-release command %s has failed.", - close_command_names[chunk->close_command.value]); - } - } - - if (chunk->in_registry_element) { - struct lttng_trace_chunk_registry_element *element; - - element = container_of(chunk, typeof(*element), chunk); - if (element->registry) { - rcu_read_lock(); - cds_lfht_del(element->registry->ht, - &element->trace_chunk_registry_ht_node); - rcu_read_unlock(); - call_rcu(&element->rcu_node, - free_lttng_trace_chunk_registry_element); - } else { - /* Never published, can be free'd immediately. */ - free_lttng_trace_chunk_registry_element( - &element->rcu_node); - } - } else { - /* Not RCU-protected, free immediately. */ - lttng_trace_chunk_fini(chunk); - free(chunk); - } -} - -void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk) -{ - if (!chunk) { - return; - } - LTTNG_ASSERT(chunk->ref.refcount); - urcu_ref_put(&chunk->ref, lttng_trace_chunk_release); -} - -struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void) -{ - struct lttng_trace_chunk_registry *registry; - - registry = zmalloc(sizeof(*registry)); - if (!registry) { - goto end; - } - - registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, - CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); - if (!registry->ht) { - goto error; - } -end: - return registry; -error: - lttng_trace_chunk_registry_destroy(registry); - return NULL; -} - -void lttng_trace_chunk_registry_destroy( - struct lttng_trace_chunk_registry *registry) -{ - if (!registry) { - return; - } - if (registry->ht) { - int ret = cds_lfht_destroy(registry->ht, NULL); - LTTNG_ASSERT(!ret); - } - free(registry); -} - -static -struct lttng_trace_chunk_registry_element * -lttng_trace_chunk_registry_element_create_from_chunk( - struct lttng_trace_chunk *chunk, uint64_t session_id) -{ - struct lttng_trace_chunk_registry_element *element = - zmalloc(sizeof(*element)); - - if (!element) { - goto end; - } - cds_lfht_node_init(&element->trace_chunk_registry_ht_node); - element->session_id = session_id; - - element->chunk = *chunk; - lttng_trace_chunk_init(&element->chunk); - if (chunk->session_output_directory) { - /* Transferred ownership. */ - element->chunk.session_output_directory = - chunk->session_output_directory; - chunk->session_output_directory = NULL; - } - if (chunk->chunk_directory) { - /* Transferred ownership. */ - element->chunk.chunk_directory = chunk->chunk_directory; - chunk->chunk_directory = NULL; - } - /* - * The original chunk becomes invalid; the name and path attributes are - * transferred to the new chunk instance. - */ - chunk->name = NULL; - chunk->path = NULL; - element->chunk.fd_tracker = chunk->fd_tracker; - element->chunk.in_registry_element = true; -end: - return element; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_publish_chunk( - struct lttng_trace_chunk_registry *registry, - uint64_t session_id, struct lttng_trace_chunk *chunk) -{ - struct lttng_trace_chunk_registry_element *element; - unsigned long element_hash; - - pthread_mutex_lock(&chunk->lock); - element = lttng_trace_chunk_registry_element_create_from_chunk(chunk, - session_id); - pthread_mutex_unlock(&chunk->lock); - if (!element) { - goto end; - } - /* - * chunk is now invalid, the only valid operation is a 'put' from the - * caller. - */ - chunk = NULL; - element_hash = lttng_trace_chunk_registry_element_hash(element); - - rcu_read_lock(); - while (1) { - struct cds_lfht_node *published_node; - struct lttng_trace_chunk *published_chunk; - struct lttng_trace_chunk_registry_element *published_element; - - published_node = cds_lfht_add_unique(registry->ht, - element_hash, - lttng_trace_chunk_registry_element_match, - element, - &element->trace_chunk_registry_ht_node); - if (published_node == &element->trace_chunk_registry_ht_node) { - /* Successfully published the new element. */ - element->registry = registry; - /* Acquire a reference for the caller. */ - if (lttng_trace_chunk_get(&element->chunk)) { - break; - } else { - /* - * Another thread concurrently unpublished the - * trace chunk. This is currently unexpected. - * - * Re-attempt to publish. - */ - ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion"); - continue; - } - } - - /* - * An equivalent trace chunk was published before this trace - * chunk. Attempt to acquire a reference to the one that was - * already published and release the reference to the copy we - * created if successful. - */ - published_element = container_of(published_node, - typeof(*published_element), - trace_chunk_registry_ht_node); - published_chunk = &published_element->chunk; - if (lttng_trace_chunk_get(published_chunk)) { - lttng_trace_chunk_put(&element->chunk); - element = published_element; - break; - } - /* - * A reference to the previously published trace chunk could not - * be acquired. Hence, retry to publish our copy of the trace - * chunk. - */ - } - rcu_read_unlock(); -end: - return element ? &element->chunk : NULL; -} - -/* - * Note that the caller must be registered as an RCU thread. - * However, it does not need to hold the RCU read lock. The RCU read lock is - * acquired to perform the look-up in the registry's hash table and held until - * after a reference to the "found" trace chunk is acquired. - * - * IOW, holding a reference guarantees the existence of the object for the - * caller. - */ -static -struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id, uint64_t *chunk_id) -{ - const struct lttng_trace_chunk_registry_element target_element = { - .chunk.id.is_set = !!chunk_id, - .chunk.id.value = chunk_id ? *chunk_id : 0, - .session_id = session_id, - }; - const unsigned long element_hash = - lttng_trace_chunk_registry_element_hash( - &target_element); - struct cds_lfht_node *published_node; - struct lttng_trace_chunk_registry_element *published_element; - struct lttng_trace_chunk *published_chunk = NULL; - struct cds_lfht_iter iter; - - rcu_read_lock(); - cds_lfht_lookup(registry->ht, - element_hash, - lttng_trace_chunk_registry_element_match, - &target_element, - &iter); - published_node = cds_lfht_iter_get_node(&iter); - if (!published_node) { - goto end; - } - - published_element = container_of(published_node, - typeof(*published_element), - trace_chunk_registry_ht_node); - if (lttng_trace_chunk_get(&published_element->chunk)) { - published_chunk = &published_element->chunk; - } -end: - rcu_read_unlock(); - return published_chunk; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_find_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id, uint64_t chunk_id) -{ - return _lttng_trace_chunk_registry_find_chunk(registry, - session_id, &chunk_id); -} - -int lttng_trace_chunk_registry_chunk_exists( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id, uint64_t chunk_id, bool *chunk_exists) -{ - int ret = 0; - const struct lttng_trace_chunk_registry_element target_element = { - .chunk.id.is_set = true, - .chunk.id.value = chunk_id, - .session_id = session_id, - }; - const unsigned long element_hash = - lttng_trace_chunk_registry_element_hash( - &target_element); - struct cds_lfht_node *published_node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - cds_lfht_lookup(registry->ht, - element_hash, - lttng_trace_chunk_registry_element_match, - &target_element, - &iter); - published_node = cds_lfht_iter_get_node(&iter); - if (!published_node) { - *chunk_exists = false; - goto end; - } - - *chunk_exists = !cds_lfht_is_node_deleted(published_node); -end: - rcu_read_unlock(); - return ret; -} - -struct lttng_trace_chunk * -lttng_trace_chunk_registry_find_anonymous_chunk( - const struct lttng_trace_chunk_registry *registry, - uint64_t session_id) -{ - return _lttng_trace_chunk_registry_find_chunk(registry, - session_id, NULL); -} - -unsigned int lttng_trace_chunk_registry_put_each_chunk( - const struct lttng_trace_chunk_registry *registry) -{ - struct cds_lfht_iter iter; - struct lttng_trace_chunk_registry_element *chunk_element; - unsigned int trace_chunks_left = 0; - - DBG("Releasing trace chunk registry to all trace chunks"); - rcu_read_lock(); - cds_lfht_for_each_entry(registry->ht, - &iter, chunk_element, trace_chunk_registry_ht_node) { - const char *chunk_id_str = "none"; - char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)]; - - pthread_mutex_lock(&chunk_element->chunk.lock); - if (chunk_element->chunk.id.is_set) { - int fmt_ret; - - fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf), - "%" PRIu64, - chunk_element->chunk.id.value); - if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) { - chunk_id_str = "formatting error"; - } else { - chunk_id_str = chunk_id_buf; - } - } - - DBG("Releasing reference to trace chunk: session_id = %" PRIu64 - "chunk_id = %s, name = \"%s\", status = %s", - chunk_element->session_id, - chunk_id_str, - chunk_element->chunk.name ? : "none", - chunk_element->chunk.close_command.is_set ? - "open" : "closed"); - pthread_mutex_unlock(&chunk_element->chunk.lock); - lttng_trace_chunk_put(&chunk_element->chunk); - trace_chunks_left++; - } - rcu_read_unlock(); - DBG("Released reference to %u trace chunks in %s()", trace_chunks_left, - __FUNCTION__); - - return trace_chunks_left; -} diff --git a/src/common/trace-chunk.cpp b/src/common/trace-chunk.cpp new file mode 100644 index 000000000..eed3d558b --- /dev/null +++ b/src/common/trace-chunk.cpp @@ -0,0 +1,2208 @@ +/* + * Copyright (C) 2019 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Two ISO 8601-compatible timestamps, separated by a hypen, followed an + * index, i.e. --. + */ +#define GENERATED_CHUNK_NAME_LEN (2 * sizeof("YYYYmmddTHHMMSS+HHMM") + MAX_INT_DEC_LEN(uint64_t)) +#define DIR_CREATION_MODE (S_IRWXU | S_IRWXG) + +enum trace_chunk_mode { + TRACE_CHUNK_MODE_USER, + TRACE_CHUNK_MODE_OWNER, +}; + +/* + * Callback to invoke on release of a trace chunk. Note that there is no + * need to 'lock' the trace chunk during the execution of these callbacks + * since only one thread may access a chunk during its destruction (the last + * to release its reference to the chunk). + */ +typedef int (*chunk_command)(struct lttng_trace_chunk *trace_chunk); + +/* Move a completed trace chunk to the 'completed' trace archive folder. */ +static +int lttng_trace_chunk_move_to_completed_post_release(struct lttng_trace_chunk *trace_chunk); +/* Empty callback. */ +static +int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk); +/* Unlink old chunk files. */ +static +int lttng_trace_chunk_delete_post_release(struct lttng_trace_chunk *trace_chunk); +static +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( + struct lttng_trace_chunk *chunk, const char *path); + +struct chunk_credentials { + bool use_current_user; + struct lttng_credentials user; +}; + +/* + * NOTE: Make sure to update: + * - lttng_trace_chunk_copy(), + * - lttng_trace_chunk_registry_element_create_from_chunk() + * if you modify this structure. + */ +struct lttng_trace_chunk { + pthread_mutex_t lock; + struct urcu_ref ref; + LTTNG_OPTIONAL(enum trace_chunk_mode) mode; + /* + * First-level directories created within the trace chunk. + * Elements are of type 'char *'. + * + * Only used by _owner_ mode chunks. + */ + struct lttng_dynamic_pointer_array top_level_directories; + /* + * All files contained within the trace chunk. + * Array of paths (char *). + */ + struct lttng_dynamic_pointer_array files; + /* Is contained within an lttng_trace_chunk_registry_element? */ + bool in_registry_element; + bool name_overridden; + char *name; + char *path; + /* An unset id means the chunk is anonymous. */ + LTTNG_OPTIONAL(uint64_t) id; + + /* + * The creation and close timestamps are NOT monotonic. + * They must not be used in context were monotonicity is required. + */ + LTTNG_OPTIONAL(time_t) timestamp_creation; + LTTNG_OPTIONAL(time_t) timestamp_close; + + LTTNG_OPTIONAL(struct chunk_credentials) credentials; + struct lttng_directory_handle *session_output_directory; + struct lttng_directory_handle *chunk_directory; + LTTNG_OPTIONAL(enum lttng_trace_chunk_command_type) close_command; + /* + * fd_tracker instance through which file descriptors should be + * created/closed. + * + * An fd_tracker always outlives any trace chunk; there is no + * need to perform any reference counting of that object. + */ + struct fd_tracker *fd_tracker; +}; + +/* A trace chunk is uniquely identified by its (session id, chunk id) tuple. */ +struct lttng_trace_chunk_registry_element { + struct lttng_trace_chunk chunk; + uint64_t session_id; + /* Weak and only set when added. */ + struct lttng_trace_chunk_registry *registry; + struct cds_lfht_node trace_chunk_registry_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; +}; + +struct lttng_trace_chunk_registry { + struct cds_lfht *ht; +}; + +struct fs_handle_untracked { + struct fs_handle parent; + int fd; + struct { + struct lttng_directory_handle *directory_handle; + char *path; + } location; +}; + +static +int fs_handle_untracked_get_fd(struct fs_handle *handle); +static +void fs_handle_untracked_put_fd(struct fs_handle *handle); +static +int fs_handle_untracked_unlink(struct fs_handle *handle); +static +int fs_handle_untracked_close(struct fs_handle *handle); + +static +const char *lttng_trace_chunk_command_type_str( + lttng_trace_chunk_command_type type) { + switch (type) { + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED: + return "move to completed chunk folder"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION: + return "no operation"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE: + return "delete"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX: + abort(); + } + + abort(); +}; + +static +const chunk_command close_command_get_post_release_func( + lttng_trace_chunk_command_type type) { + switch (type) { + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED: + return lttng_trace_chunk_move_to_completed_post_release; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION: + return lttng_trace_chunk_no_operation; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE: + return lttng_trace_chunk_delete_post_release; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX: + abort(); + } + + abort(); +}; + +static +struct fs_handle *fs_handle_untracked_create( + struct lttng_directory_handle *directory_handle, + const char *path, + int fd) +{ + struct fs_handle_untracked *handle = NULL; + bool reference_acquired; + char *path_copy = strdup(path); + + LTTNG_ASSERT(fd >= 0); + if (!path_copy) { + PERROR("Failed to copy file path while creating untracked filesystem handle"); + goto end; + } + + handle = (fs_handle_untracked *) zmalloc(sizeof(typeof(*handle))); + if (!handle) { + PERROR("Failed to allocate untracked filesystem handle"); + goto end; + } + + handle->parent = (typeof(handle->parent)) { + .get_fd = fs_handle_untracked_get_fd, + .put_fd = fs_handle_untracked_put_fd, + .unlink = fs_handle_untracked_unlink, + .close = fs_handle_untracked_close, + }; + + handle->fd = fd; + reference_acquired = lttng_directory_handle_get(directory_handle); + LTTNG_ASSERT(reference_acquired); + handle->location.directory_handle = directory_handle; + /* Ownership is transferred. */ + handle->location.path = path_copy; + path_copy = NULL; +end: + free(path_copy); + return handle ? &handle->parent : NULL; +} + +static +int fs_handle_untracked_get_fd(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + + return handle->fd; +} + +static +void fs_handle_untracked_put_fd(struct fs_handle *_handle) +{ + /* no-op. */ +} + +static +int fs_handle_untracked_unlink(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + + return lttng_directory_handle_unlink_file( + handle->location.directory_handle, + handle->location.path); +} + +static +void fs_handle_untracked_destroy(struct fs_handle_untracked *handle) +{ + lttng_directory_handle_put(handle->location.directory_handle); + free(handle->location.path); + free(handle); +} + +static +int fs_handle_untracked_close(struct fs_handle *_handle) +{ + struct fs_handle_untracked *handle = container_of( + _handle, struct fs_handle_untracked, parent); + int ret = close(handle->fd); + + fs_handle_untracked_destroy(handle); + return ret; +} + +static +bool lttng_trace_chunk_registry_element_equals( + const struct lttng_trace_chunk_registry_element *a, + const struct lttng_trace_chunk_registry_element *b) +{ + if (a->session_id != b->session_id) { + goto not_equal; + } + if (a->chunk.id.is_set != b->chunk.id.is_set) { + goto not_equal; + } + if (a->chunk.id.is_set && a->chunk.id.value != b->chunk.id.value) { + goto not_equal; + } + return true; +not_equal: + return false; +} + +static +int lttng_trace_chunk_registry_element_match(struct cds_lfht_node *node, + const void *key) +{ + const struct lttng_trace_chunk_registry_element *element_a, *element_b; + + element_a = (const struct lttng_trace_chunk_registry_element *) key; + element_b = caa_container_of(node, typeof(*element_b), + trace_chunk_registry_ht_node); + return lttng_trace_chunk_registry_element_equals(element_a, element_b); +} + +static +unsigned long lttng_trace_chunk_registry_element_hash( + const struct lttng_trace_chunk_registry_element *element) +{ + unsigned long hash = hash_key_u64(&element->session_id, + lttng_ht_seed); + + if (element->chunk.id.is_set) { + hash ^= hash_key_u64(&element->chunk.id.value, lttng_ht_seed); + } + + return hash; +} + +static +char *generate_chunk_name(uint64_t chunk_id, time_t creation_timestamp, + const time_t *close_timestamp) +{ + int ret = 0; + char *new_name= NULL; + char start_datetime[ISO8601_STR_LEN] = {}; + /* Add 1 for a '-' prefix. */ + char end_datetime_suffix[ISO8601_STR_LEN + 1] = {}; + + ret = time_to_iso8601_str( + creation_timestamp, + start_datetime, sizeof(start_datetime)); + if (ret) { + ERR("Failed to format trace chunk start date time"); + goto error; + } + if (close_timestamp) { + *end_datetime_suffix = '-'; + ret = time_to_iso8601_str( + *close_timestamp, + end_datetime_suffix + 1, + sizeof(end_datetime_suffix) - 1); + if (ret) { + ERR("Failed to format trace chunk end date time"); + goto error; + } + } + new_name = (char *) zmalloc(GENERATED_CHUNK_NAME_LEN); + if (!new_name) { + ERR("Failed to allocate buffer for automatically-generated trace chunk name"); + goto error; + } + ret = snprintf(new_name, GENERATED_CHUNK_NAME_LEN, "%s%s-%" PRIu64, + start_datetime, end_datetime_suffix, chunk_id); + if (ret >= GENERATED_CHUNK_NAME_LEN || ret == -1) { + ERR("Failed to format trace chunk name"); + goto error; + } + + return new_name; +error: + free(new_name); + return NULL; +} + +static +void lttng_trace_chunk_init(struct lttng_trace_chunk *chunk) +{ + urcu_ref_init(&chunk->ref); + pthread_mutex_init(&chunk->lock, NULL); + lttng_dynamic_pointer_array_init(&chunk->top_level_directories, free); + lttng_dynamic_pointer_array_init(&chunk->files, free); +} + +static +void lttng_trace_chunk_fini(struct lttng_trace_chunk *chunk) +{ + if (chunk->session_output_directory) { + lttng_directory_handle_put( + chunk->session_output_directory); + chunk->session_output_directory = NULL; + } + if (chunk->chunk_directory) { + lttng_directory_handle_put(chunk->chunk_directory); + chunk->chunk_directory = NULL; + } + free(chunk->name); + chunk->name = NULL; + free(chunk->path); + chunk->path = NULL; + lttng_dynamic_pointer_array_reset(&chunk->top_level_directories); + lttng_dynamic_pointer_array_reset(&chunk->files); + pthread_mutex_destroy(&chunk->lock); +} + +static +struct lttng_trace_chunk *lttng_trace_chunk_allocate(void) +{ + struct lttng_trace_chunk *chunk = NULL; + + chunk = (lttng_trace_chunk *) zmalloc(sizeof(*chunk)); + if (!chunk) { + ERR("Failed to allocate trace chunk"); + goto end; + } + lttng_trace_chunk_init(chunk); +end: + return chunk; +} + +struct lttng_trace_chunk *lttng_trace_chunk_create_anonymous(void) +{ + DBG("Creating anonymous trace chunk"); + return lttng_trace_chunk_allocate(); +} + +struct lttng_trace_chunk *lttng_trace_chunk_create( + uint64_t chunk_id, time_t chunk_creation_time, const char *path) +{ + struct lttng_trace_chunk *chunk; + char chunk_creation_datetime_buf[16] = {}; + const char *chunk_creation_datetime_str = "(formatting error)"; + struct tm timeinfo_buf, *timeinfo; + + timeinfo = localtime_r(&chunk_creation_time, &timeinfo_buf); + if (timeinfo) { + size_t strftime_ret; + + /* Don't fail because of this; it is only used for logging. */ + strftime_ret = strftime(chunk_creation_datetime_buf, + sizeof(chunk_creation_datetime_buf), + "%Y%m%d-%H%M%S", timeinfo); + if (strftime_ret) { + chunk_creation_datetime_str = + chunk_creation_datetime_buf; + } + } + + DBG("Creating trace chunk: chunk_id = %" PRIu64 ", creation time = %s", + chunk_id, chunk_creation_datetime_str); + chunk = lttng_trace_chunk_allocate(); + if (!chunk) { + goto end; + } + + LTTNG_OPTIONAL_SET(&chunk->id, chunk_id); + LTTNG_OPTIONAL_SET(&chunk->timestamp_creation, chunk_creation_time); + if (chunk_id != 0) { + chunk->name = generate_chunk_name(chunk_id, + chunk_creation_time, NULL); + if (!chunk->name) { + ERR("Failed to allocate trace chunk name storage"); + goto error; + } + } + if (path) { + chunk->path = strdup(path); + if (!chunk->path) { + goto error; + } + } else { + if (chunk->name) { + chunk->path = strdup(chunk->name); + if (!chunk->path) { + goto error; + } + } + } + + DBG("Chunk name set to \"%s\"", chunk->name ? : "(none)"); +end: + return chunk; +error: + lttng_trace_chunk_put(chunk); + return NULL; +} + +void lttng_trace_chunk_set_fd_tracker(struct lttng_trace_chunk *chunk, + struct fd_tracker *fd_tracker) +{ + LTTNG_ASSERT(!chunk->session_output_directory); + LTTNG_ASSERT(!chunk->chunk_directory); + LTTNG_ASSERT(lttng_dynamic_pointer_array_get_count(&chunk->files) == 0); + chunk->fd_tracker = fd_tracker; +} + +struct lttng_trace_chunk *lttng_trace_chunk_copy( + struct lttng_trace_chunk *source_chunk) +{ + struct lttng_trace_chunk *new_chunk = lttng_trace_chunk_allocate(); + + if (!new_chunk) { + goto end; + } + + pthread_mutex_lock(&source_chunk->lock); + /* + * A new chunk is always a user; it shall create no new trace + * subdirectories. + */ + new_chunk->mode = (typeof(new_chunk->mode)) { + .is_set = true, + .value = TRACE_CHUNK_MODE_USER, + }; + /* + * top_level_directories is not copied as it is never used + * by _user_ mode chunks. + */ + /* The new chunk is not part of a registry (yet, at least). */ + new_chunk->in_registry_element = false; + new_chunk->name_overridden = source_chunk->name_overridden; + if (source_chunk->name) { + new_chunk->name = strdup(source_chunk->name); + if (!new_chunk->name) { + ERR("Failed to copy source trace chunk name in %s()", + __FUNCTION__); + goto error_unlock; + } + } + if (source_chunk->path) { + new_chunk->path = strdup(source_chunk->path); + if (!new_chunk->path) { + ERR("Failed to copy source trace chunk path in %s()", + __FUNCTION__); + } + } + new_chunk->id = source_chunk->id; + new_chunk->timestamp_creation = source_chunk->timestamp_creation; + new_chunk->timestamp_close = source_chunk->timestamp_close; + new_chunk->credentials = source_chunk->credentials; + if (source_chunk->session_output_directory) { + const bool reference_acquired = lttng_directory_handle_get( + source_chunk->session_output_directory); + + LTTNG_ASSERT(reference_acquired); + new_chunk->session_output_directory = + source_chunk->session_output_directory; + } + if (source_chunk->chunk_directory) { + const bool reference_acquired = lttng_directory_handle_get( + source_chunk->chunk_directory); + + LTTNG_ASSERT(reference_acquired); + new_chunk->chunk_directory = source_chunk->chunk_directory; + } + new_chunk->close_command = source_chunk->close_command; + new_chunk->fd_tracker = source_chunk->fd_tracker; + pthread_mutex_unlock(&source_chunk->lock); +end: + return new_chunk; +error_unlock: + pthread_mutex_unlock(&source_chunk->lock); + lttng_trace_chunk_put(new_chunk); + return NULL; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_id( + struct lttng_trace_chunk *chunk, uint64_t *id) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->id.is_set) { + *id = chunk->id.value; + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_creation_timestamp( + struct lttng_trace_chunk *chunk, time_t *creation_ts) + +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->timestamp_creation.is_set) { + *creation_ts = chunk->timestamp_creation.value; + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_close_timestamp( + struct lttng_trace_chunk *chunk, time_t *close_ts) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->timestamp_close.is_set) { + *close_ts = chunk->timestamp_close.value; + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_set_close_timestamp( + struct lttng_trace_chunk *chunk, time_t close_ts) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (!chunk->timestamp_creation.is_set) { + ERR("Failed to set trace chunk close timestamp: creation timestamp is unset"); + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; + goto end; + } + + /* + * Note: we do not enforce that the closing timestamp be greater or + * equal to the begin timestamp. These timestamps are used for + * generating the chunk name and should only be used in context where + * the monotonicity of time is not important. The source of those + * timestamps is NOT monotonic and represent the system calendar time, + * also know as the wall time. + */ + if (chunk->timestamp_creation.value > close_ts) { + WARN("Set trace chunk close timestamp: close timestamp is before creation timestamp, begin : %ld, close : %ld", + chunk->timestamp_creation.value, close_ts); + } + + LTTNG_OPTIONAL_SET(&chunk->timestamp_close, close_ts); + if (!chunk->name_overridden) { + free(chunk->name); + chunk->name = generate_chunk_name(LTTNG_OPTIONAL_GET(chunk->id), + LTTNG_OPTIONAL_GET(chunk->timestamp_creation), + &close_ts); + if (!chunk->name) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + } + } +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_name( + struct lttng_trace_chunk *chunk, const char **name, + bool *name_overridden) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (name_overridden) { + *name_overridden = chunk->name_overridden; + } + if (!chunk->name) { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + goto end; + } + *name = chunk->name; +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +bool lttng_trace_chunk_get_name_overridden(struct lttng_trace_chunk *chunk) +{ + bool name_overridden; + + pthread_mutex_lock(&chunk->lock); + name_overridden = chunk->name_overridden; + pthread_mutex_unlock(&chunk->lock); + return name_overridden; +} + +static +bool is_valid_chunk_name(const char *name) +{ + size_t len; + + if (!name) { + return false; + } + + len = lttng_strnlen(name, LTTNG_NAME_MAX); + if (len == 0 || len == LTTNG_NAME_MAX) { + return false; + } + + if (strchr(name, '/') || strchr(name, '.')) { + return false; + } + + return true; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_override_name( + struct lttng_trace_chunk *chunk, const char *name) + +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + char *new_name, *new_path; + + DBG("Override trace chunk name from %s to %s", chunk->name, name); + if (!is_valid_chunk_name(name)) { + ERR("Attempted to set an invalid name on a trace chunk: name = %s", + name ? : "NULL"); + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; + goto end; + } + + pthread_mutex_lock(&chunk->lock); + if (!chunk->id.is_set) { + ERR("Attempted to set an override name on an anonymous trace chunk: name = %s", + name); + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; + goto end_unlock; + } + + new_name = strdup(name); + if (!new_name) { + ERR("Failed to allocate new trace chunk name"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end_unlock; + } + free(chunk->name); + chunk->name = new_name; + + new_path = strdup(name); + if (!new_path) { + ERR("Failed to allocate new trace chunk path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end_unlock; + } + free(chunk->path); + chunk->path = new_path; + + chunk->name_overridden = true; +end_unlock: + pthread_mutex_unlock(&chunk->lock); +end: + return status; +} + +static +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path_no_lock( + struct lttng_trace_chunk *chunk, const char *path) + +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + struct lttng_directory_handle *rename_directory = NULL; + char *new_path, *old_path; + int ret; + + if (chunk->name_overridden) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + old_path = chunk->path; + DBG("lttng_trace_chunk_rename_path from %s to %s", old_path, path); + + if ((!old_path && !path) || + (old_path && path && !strcmp(old_path, path))) { + goto end; + } + /* + * Use chunk name as path if NULL path is specified. + */ + if (!path) { + path = chunk->name; + } + + /* Renaming from "" to "" is not accepted. */ + if (path[0] == '\0' && old_path[0] == '\0') { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* + * If a rename is performed on a chunk for which the chunk_directory + * is not set (yet), or the session_output_directory is not set + * (interacting with a relay daemon), there is no rename to perform. + */ + if (!chunk->chunk_directory || + !chunk->session_output_directory) { + goto skip_move; + } + + if (old_path && old_path[0] != '\0' && path[0] != '\0') { + /* Rename chunk directory. */ + ret = lttng_directory_handle_rename_as_user( + chunk->session_output_directory, + old_path, + chunk->session_output_directory, + path, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move trace chunk directory \"%s\" to \"%s\"", + old_path, path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + rename_directory = chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + chunk->fd_tracker, + chunk->session_output_directory, + path) : + lttng_directory_handle_create_from_handle( + path, + chunk->session_output_directory); + if (!rename_directory) { + ERR("Failed to get handle to trace chunk rename directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + } else if (old_path && old_path[0] == '\0') { + size_t i, count = lttng_dynamic_pointer_array_get_count( + &chunk->top_level_directories); + + ret = lttng_directory_handle_create_subdirectory_as_user( + chunk->session_output_directory, + path, + DIR_CREATION_MODE, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to create trace chunk rename directory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + rename_directory = lttng_directory_handle_create_from_handle( + path, chunk->session_output_directory); + if (!rename_directory) { + ERR("Failed to get handle to trace chunk rename directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + + /* Move toplevel directories. */ + for (i = 0; i < count; i++) { + const char *top_level_name = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &chunk->top_level_directories, i); + + ret = lttng_directory_handle_rename_as_user( + chunk->chunk_directory, + top_level_name, + rename_directory, + top_level_name, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move \"%s\" to trace chunk rename directory", + top_level_name); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + } else if (old_path) { + size_t i, count = lttng_dynamic_pointer_array_get_count( + &chunk->top_level_directories); + const bool reference_acquired = lttng_directory_handle_get( + chunk->session_output_directory); + + LTTNG_ASSERT(reference_acquired); + rename_directory = chunk->session_output_directory; + + /* Move toplevel directories. */ + for (i = 0; i < count; i++) { + const char *top_level_name = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &chunk->top_level_directories, i); + + ret = lttng_directory_handle_rename_as_user( + chunk->chunk_directory, + top_level_name, + rename_directory, + top_level_name, + LTTNG_OPTIONAL_GET(chunk->credentials).use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to move \"%s\" to trace chunk rename directory", + top_level_name); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + /* Release old handle. */ + lttng_directory_handle_put(chunk->chunk_directory); + /* + * Transfer new handle reference to chunk as the current chunk + * handle. + */ + chunk->chunk_directory = rename_directory; + rename_directory = NULL; + + /* Remove old directory. */ + status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory( + chunk->session_output_directory, + old_path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error removing subdirectory '%s' file when deleting chunk", + old_path); + goto end; + } + } else { + /* Unexpected !old_path && !path. */ + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; + goto end; + } + +skip_move: + new_path = strdup(path); + if (!new_path) { + ERR("Failed to allocate new trace chunk path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + free(chunk->path); + chunk->path = new_path; +end: + lttng_directory_handle_put(rename_directory); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_rename_path( + struct lttng_trace_chunk *chunk, const char *path) + +{ + enum lttng_trace_chunk_status status; + + pthread_mutex_lock(&chunk->lock); + status = lttng_trace_chunk_rename_path_no_lock(chunk, path); + pthread_mutex_unlock(&chunk->lock); + + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_credentials( + struct lttng_trace_chunk *chunk, + struct lttng_credentials *credentials) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->credentials.is_set) { + if (chunk->credentials.value.use_current_user) { + LTTNG_OPTIONAL_SET(&credentials->uid, geteuid()); + LTTNG_OPTIONAL_SET(&credentials->gid, getegid()); + } else { + *credentials = chunk->credentials.value.user; + } + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials( + struct lttng_trace_chunk *chunk, + const struct lttng_credentials *user_credentials) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + const struct chunk_credentials credentials = { + .use_current_user = false, + .user = *user_credentials, + }; + + pthread_mutex_lock(&chunk->lock); + if (chunk->credentials.is_set) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_set_credentials_current_user( + struct lttng_trace_chunk *chunk) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + const struct chunk_credentials credentials = { + .use_current_user = true, + }; + + pthread_mutex_lock(&chunk->lock); + if (chunk->credentials.is_set) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + LTTNG_OPTIONAL_SET(&chunk->credentials, credentials); +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + + +enum lttng_trace_chunk_status lttng_trace_chunk_set_as_owner( + struct lttng_trace_chunk *chunk, + struct lttng_directory_handle *session_output_directory) +{ + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + struct lttng_directory_handle *chunk_directory_handle = NULL; + bool reference_acquired; + + pthread_mutex_lock(&chunk->lock); + if (chunk->mode.is_set) { + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; + goto end; + } + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * directory is created. + */ + ERR("Credentials of trace chunk are unset: refusing to set session output directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (chunk->path && chunk->path[0] != '\0') { + ret = lttng_directory_handle_create_subdirectory_as_user( + session_output_directory, + chunk->path, + DIR_CREATION_MODE, + !chunk->credentials.value.use_current_user ? + &chunk->credentials.value.user : NULL); + if (ret) { + PERROR("Failed to create chunk output directory \"%s\"", + chunk->path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + chunk_directory_handle = + chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + chunk->fd_tracker, + session_output_directory, + chunk->path) : + lttng_directory_handle_create_from_handle( + chunk->path, + session_output_directory); + if (!chunk_directory_handle) { + /* The function already logs on all error paths. */ + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } else { + /* + * A nameless chunk does not need its own output directory. + * The session's output directory will be used. + */ + reference_acquired = lttng_directory_handle_get( + session_output_directory); + + LTTNG_ASSERT(reference_acquired); + chunk_directory_handle = session_output_directory; + } + chunk->chunk_directory = chunk_directory_handle; + chunk_directory_handle = NULL; + reference_acquired = lttng_directory_handle_get( + session_output_directory); + LTTNG_ASSERT(reference_acquired); + chunk->session_output_directory = session_output_directory; + LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_OWNER); +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_set_as_user( + struct lttng_trace_chunk *chunk, + struct lttng_directory_handle *chunk_directory) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + bool reference_acquired; + + pthread_mutex_lock(&chunk->lock); + if (chunk->mode.is_set) { + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; + goto end; + } + if (!chunk->credentials.is_set) { + ERR("Credentials of trace chunk are unset: refusing to set chunk output directory"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + reference_acquired = lttng_directory_handle_get(chunk_directory); + LTTNG_ASSERT(reference_acquired); + chunk->chunk_directory = chunk_directory; + LTTNG_OPTIONAL_SET(&chunk->mode, TRACE_CHUNK_MODE_USER); +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status +lttng_trace_chunk_get_session_output_directory_handle( + struct lttng_trace_chunk *chunk, + struct lttng_directory_handle **handle) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (!chunk->session_output_directory) { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + *handle = NULL; + goto end; + } else { + const bool reference_acquired = lttng_directory_handle_get( + chunk->session_output_directory); + + LTTNG_ASSERT(reference_acquired); + *handle = chunk->session_output_directory; + } +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_borrow_chunk_directory_handle( + struct lttng_trace_chunk *chunk, + const struct lttng_directory_handle **handle) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (!chunk->chunk_directory) { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + goto end; + } + + *handle = chunk->chunk_directory; +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +/* Add a top-level directory to the trace chunk if it was previously unknown. */ +static +int add_top_level_directory_unique(struct lttng_trace_chunk *chunk, + const char *new_path) +{ + int ret = 0; + bool found = false; + size_t i, count = lttng_dynamic_pointer_array_get_count( + &chunk->top_level_directories); + const char *new_path_separator_pos = strchr(new_path, '/'); + const ptrdiff_t new_path_top_level_len = new_path_separator_pos ? + new_path_separator_pos - new_path : strlen(new_path); + + for (i = 0; i < count; i++) { + const char *path = (const char *) lttng_dynamic_pointer_array_get_pointer( + &chunk->top_level_directories, i); + const ptrdiff_t path_top_level_len = strlen(path); + + if (path_top_level_len != new_path_top_level_len) { + continue; + } + if (!strncmp(path, new_path, path_top_level_len)) { + found = true; + break; + } + } + + if (!found) { + char *copy = lttng_strndup(new_path, new_path_top_level_len); + + DBG("Adding new top-level directory \"%s\" to trace chunk \"%s\"", + new_path, chunk->name ? : "(unnamed)"); + if (!copy) { + PERROR("Failed to copy path"); + ret = -1; + goto end; + } + ret = lttng_dynamic_pointer_array_add_pointer( + &chunk->top_level_directories, copy); + if (ret) { + ERR("Allocation failure while adding top-level directory entry to a trace chunk"); + free(copy); + goto end; + } + } +end: + return ret; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_create_subdirectory( + struct lttng_trace_chunk *chunk, + const char *path) +{ + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + DBG("Creating trace chunk subdirectory \"%s\"", path); + pthread_mutex_lock(&chunk->lock); + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * directory is created. + */ + ERR("Credentials of trace chunk are unset: refusing to create subdirectory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (!chunk->mode.is_set || + chunk->mode.value != TRACE_CHUNK_MODE_OWNER) { + ERR("Attempted to create trace chunk subdirectory \"%s\" through a non-owner chunk", + path); + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_OPERATION; + goto end; + } + if (!chunk->chunk_directory) { + ERR("Attempted to create trace chunk subdirectory \"%s\" before setting the chunk output directory", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (*path == '/') { + ERR("Refusing to create absolute trace chunk directory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; + goto end; + } + ret = lttng_directory_handle_create_subdirectory_recursive_as_user( + chunk->chunk_directory, path, + DIR_CREATION_MODE, + chunk->credentials.value.use_current_user ? + NULL : &chunk->credentials.value.user); + if (ret) { + PERROR("Failed to create trace chunk subdirectory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = add_top_level_directory_unique(chunk, path); + if (ret) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +/* + * TODO: Implement O(1) lookup. + */ +static +bool lttng_trace_chunk_find_file(struct lttng_trace_chunk *chunk, + const char *path, size_t *index) +{ + size_t i, count; + + count = lttng_dynamic_pointer_array_get_count(&chunk->files); + for (i = 0; i < count; i++) { + const char *iter_path = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &chunk->files, i); + if (!strcmp(iter_path, path)) { + if (index) { + *index = i; + } + return true; + } + } + return false; +} + +static +enum lttng_trace_chunk_status lttng_trace_chunk_add_file( + struct lttng_trace_chunk *chunk, + const char *path) +{ + char *copy; + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + if (lttng_trace_chunk_find_file(chunk, path, NULL)) { + return LTTNG_TRACE_CHUNK_STATUS_OK; + } + DBG("Adding new file \"%s\" to trace chunk \"%s\"", + path, chunk->name ? : "(unnamed)"); + copy = strdup(path); + if (!copy) { + PERROR("Failed to copy path"); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = lttng_dynamic_pointer_array_add_pointer( + &chunk->files, copy); + if (ret) { + ERR("Allocation failure while adding file to a trace chunk"); + free(copy); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } +end: + return status; +} + +static +void lttng_trace_chunk_remove_file( + struct lttng_trace_chunk *chunk, + const char *path) +{ + size_t index; + bool found; + int ret; + + found = lttng_trace_chunk_find_file(chunk, path, &index); + if (!found) { + return; + } + ret = lttng_dynamic_pointer_array_remove_pointer( + &chunk->files, index); + LTTNG_ASSERT(!ret); +} + +static +enum lttng_trace_chunk_status _lttng_trace_chunk_open_fs_handle_locked( + struct lttng_trace_chunk *chunk, + const char *file_path, + int flags, + mode_t mode, + struct fs_handle **out_handle, + bool expect_no_file) +{ + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + DBG("Opening trace chunk file \"%s\"", file_path); + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * file is created. + */ + ERR("Credentials of trace chunk are unset: refusing to open file \"%s\"", + file_path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (!chunk->chunk_directory) { + ERR("Attempted to open trace chunk file \"%s\" before setting the chunk output directory", + file_path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + status = lttng_trace_chunk_add_file(chunk, file_path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + goto end; + } + if (chunk->fd_tracker) { + LTTNG_ASSERT(chunk->credentials.value.use_current_user); + *out_handle = fd_tracker_open_fs_handle(chunk->fd_tracker, + chunk->chunk_directory, file_path, flags, &mode); + ret = *out_handle ? 0 : -1; + } else { + ret = lttng_directory_handle_open_file_as_user( + chunk->chunk_directory, file_path, flags, mode, + chunk->credentials.value.use_current_user ? + NULL : + &chunk->credentials.value.user); + if (ret >= 0) { + *out_handle = fs_handle_untracked_create( + chunk->chunk_directory, file_path, ret); + if (!*out_handle) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + } + } + if (ret < 0) { + if (errno == ENOENT && expect_no_file) { + status = LTTNG_TRACE_CHUNK_STATUS_NO_FILE; + } else { + PERROR("Failed to open file relative to trace chunk file_path = \"%s\", flags = %d, mode = %d", + file_path, flags, (int) mode); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + } + lttng_trace_chunk_remove_file(chunk, file_path); + goto end; + } +end: + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_open_fs_handle( + struct lttng_trace_chunk *chunk, + const char *file_path, + int flags, + mode_t mode, + struct fs_handle **out_handle, + bool expect_no_file) +{ + enum lttng_trace_chunk_status status; + + pthread_mutex_lock(&chunk->lock); + status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, + flags, mode, out_handle, expect_no_file); + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_open_file( + struct lttng_trace_chunk *chunk, + const char *file_path, + int flags, + mode_t mode, + int *out_fd, + bool expect_no_file) +{ + enum lttng_trace_chunk_status status; + struct fs_handle *fs_handle; + + pthread_mutex_lock(&chunk->lock); + /* + * Using this method is never valid when an fd_tracker is being + * used since the resulting file descriptor would not be tracked. + */ + LTTNG_ASSERT(!chunk->fd_tracker); + status = _lttng_trace_chunk_open_fs_handle_locked(chunk, file_path, + flags, mode, &fs_handle, expect_no_file); + pthread_mutex_unlock(&chunk->lock); + + if (status == LTTNG_TRACE_CHUNK_STATUS_OK) { + *out_fd = fs_handle_get_fd(fs_handle); + /* + * Does not close the fd; we just "unbox" it from the fs_handle. + */ + fs_handle_untracked_destroy(container_of( + fs_handle, struct fs_handle_untracked, parent)); + } + + return status; +} + +int lttng_trace_chunk_unlink_file(struct lttng_trace_chunk *chunk, + const char *file_path) +{ + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + DBG("Unlinking trace chunk file \"%s\"", file_path); + pthread_mutex_lock(&chunk->lock); + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * file is unlinked. + */ + ERR("Credentials of trace chunk are unset: refusing to unlink file \"%s\"", + file_path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (!chunk->chunk_directory) { + ERR("Attempted to unlink trace chunk file \"%s\" before setting the chunk output directory", + file_path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = lttng_directory_handle_unlink_file_as_user( + chunk->chunk_directory, file_path, + chunk->credentials.value.use_current_user ? + NULL : &chunk->credentials.value.user); + if (ret < 0) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + lttng_trace_chunk_remove_file(chunk, file_path); +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +static +int lttng_trace_chunk_remove_subdirectory_recursive(struct lttng_trace_chunk *chunk, + const char *path) +{ + int ret; + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + DBG("Recursively removing trace chunk directory \"%s\"", path); + pthread_mutex_lock(&chunk->lock); + if (!chunk->credentials.is_set) { + /* + * Fatal error, credentials must be set before a + * directory is removed. + */ + ERR("Credentials of trace chunk are unset: refusing to recursively remove directory \"%s\"", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + if (!chunk->chunk_directory) { + ERR("Attempted to recursively remove trace chunk directory \"%s\" before setting the chunk output directory", + path); + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } + ret = lttng_directory_handle_remove_subdirectory_recursive_as_user( + chunk->chunk_directory, path, + chunk->credentials.value.use_current_user ? + NULL : &chunk->credentials.value.user, + LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG); + if (ret < 0) { + status = LTTNG_TRACE_CHUNK_STATUS_ERROR; + goto end; + } +end: + pthread_mutex_unlock(&chunk->lock); + return status; +} + +static +int lttng_trace_chunk_move_to_completed_post_release( + struct lttng_trace_chunk *trace_chunk) +{ + int ret = 0; + char *archived_chunk_name = NULL; + const uint64_t chunk_id = LTTNG_OPTIONAL_GET(trace_chunk->id); + const time_t creation_timestamp = + LTTNG_OPTIONAL_GET(trace_chunk->timestamp_creation); + const time_t close_timestamp = + LTTNG_OPTIONAL_GET(trace_chunk->timestamp_close); + struct lttng_directory_handle *archived_chunks_directory = NULL; + enum lttng_trace_chunk_status status; + + if (!trace_chunk->mode.is_set || + trace_chunk->mode.value != TRACE_CHUNK_MODE_OWNER || + !trace_chunk->session_output_directory) { + /* + * This command doesn't need to run if the output is remote + * or if the trace chunk is not owned by this process. + */ + goto end; + } + + LTTNG_ASSERT(trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER); + LTTNG_ASSERT(!trace_chunk->name_overridden); + LTTNG_ASSERT(trace_chunk->path); + + archived_chunk_name = generate_chunk_name(chunk_id, creation_timestamp, + &close_timestamp); + if (!archived_chunk_name) { + ERR("Failed to generate archived trace chunk name while renaming trace chunk"); + ret = -1; + goto end; + } + + ret = lttng_directory_handle_create_subdirectory_as_user( + trace_chunk->session_output_directory, + DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, + DIR_CREATION_MODE, + !trace_chunk->credentials.value.use_current_user ? + &trace_chunk->credentials.value.user : + NULL); + if (ret) { + PERROR("Failed to create \"" DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY + "\" directory for archived trace chunks"); + goto end; + } + + archived_chunks_directory = trace_chunk->fd_tracker ? + fd_tracker_create_directory_handle_from_handle( + trace_chunk->fd_tracker, + trace_chunk->session_output_directory, + DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY) : + lttng_directory_handle_create_from_handle( + DEFAULT_ARCHIVED_TRACE_CHUNKS_DIRECTORY, + trace_chunk->session_output_directory); + if (!archived_chunks_directory) { + PERROR("Failed to get handle to archived trace chunks directory"); + ret = -1; + goto end; + } + + /* + * Make sure chunk is renamed to old directory if not already done by + * the creation of the next chunk. This happens if a rotation is + * performed while tracing is stopped. + */ + if (!trace_chunk->path || strcmp(trace_chunk->path, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY)) { + status = lttng_trace_chunk_rename_path_no_lock(trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Failed to rename chunk to %s", DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + ret = -1; + goto end; + } + } + + ret = lttng_directory_handle_rename_as_user( + trace_chunk->session_output_directory, + trace_chunk->path, + archived_chunks_directory, + archived_chunk_name, + LTTNG_OPTIONAL_GET(trace_chunk->credentials).use_current_user ? + NULL : + &trace_chunk->credentials.value.user); + if (ret) { + PERROR("Failed to rename folder \"%s\" to \"%s\"", + trace_chunk->path, + archived_chunk_name); + } + +end: + lttng_directory_handle_put(archived_chunks_directory); + free(archived_chunk_name); + return ret; +} + +static +int lttng_trace_chunk_no_operation(struct lttng_trace_chunk *trace_chunk) +{ + return 0; +} + +static +int lttng_trace_chunk_delete_post_release_user( + struct lttng_trace_chunk *trace_chunk) +{ + int ret = 0; + + DBG("Trace chunk \"delete\" close command post-release (User)"); + + /* Unlink all files. */ + while (lttng_dynamic_pointer_array_get_count(&trace_chunk->files) != 0) { + enum lttng_trace_chunk_status status; + const char *path; + + /* Remove first. */ + path = (const char *) lttng_dynamic_pointer_array_get_pointer( + &trace_chunk->files, 0); + DBG("Unlink file: %s", path); + status = (lttng_trace_chunk_status) lttng_trace_chunk_unlink_file(trace_chunk, path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error unlinking file '%s' when deleting chunk", path); + ret = -1; + goto end; + } + } +end: + return ret; +} + +static +int lttng_trace_chunk_delete_post_release_owner( + struct lttng_trace_chunk *trace_chunk) +{ + enum lttng_trace_chunk_status status; + size_t i, count; + int ret = 0; + + ret = lttng_trace_chunk_delete_post_release_user(trace_chunk); + if (ret) { + goto end; + } + + DBG("Trace chunk \"delete\" close command post-release (Owner)"); + + LTTNG_ASSERT(trace_chunk->session_output_directory); + LTTNG_ASSERT(trace_chunk->chunk_directory); + + /* Remove empty directories. */ + count = lttng_dynamic_pointer_array_get_count( + &trace_chunk->top_level_directories); + + for (i = 0; i < count; i++) { + const char *top_level_name = + (const char *) lttng_dynamic_pointer_array_get_pointer( + &trace_chunk->top_level_directories, i); + + status = (lttng_trace_chunk_status) lttng_trace_chunk_remove_subdirectory_recursive(trace_chunk, top_level_name); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error recursively removing subdirectory '%s' file when deleting chunk", + top_level_name); + ret = -1; + break; + } + } + if (!ret) { + lttng_directory_handle_put(trace_chunk->chunk_directory); + trace_chunk->chunk_directory = NULL; + + if (trace_chunk->path && trace_chunk->path[0] != '\0') { + status = (lttng_trace_chunk_status) lttng_directory_handle_remove_subdirectory( + trace_chunk->session_output_directory, + trace_chunk->path); + if (status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Error removing subdirectory '%s' file when deleting chunk", + trace_chunk->path); + ret = -1; + } + } + } + free(trace_chunk->path); + trace_chunk->path = NULL; +end: + return ret; +} + +/* + * For local files, session and consumer daemons all run the delete hook. The + * consumer daemons have the list of files to unlink, and technically the + * session daemon is the owner of the chunk. Unlink all files owned by each + * consumer daemon. + */ +static +int lttng_trace_chunk_delete_post_release( + struct lttng_trace_chunk *trace_chunk) +{ + if (!trace_chunk->chunk_directory) { + return 0; + } + + if (trace_chunk->mode.value == TRACE_CHUNK_MODE_OWNER) { + return lttng_trace_chunk_delete_post_release_owner(trace_chunk); + } else { + return lttng_trace_chunk_delete_post_release_user(trace_chunk); + } +} + +enum lttng_trace_chunk_status lttng_trace_chunk_get_close_command( + struct lttng_trace_chunk *chunk, + enum lttng_trace_chunk_command_type *command_type) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + pthread_mutex_lock(&chunk->lock); + if (chunk->close_command.is_set) { + *command_type = chunk->close_command.value; + status = LTTNG_TRACE_CHUNK_STATUS_OK; + } else { + status = LTTNG_TRACE_CHUNK_STATUS_NONE; + } + pthread_mutex_unlock(&chunk->lock); + return status; +} + +enum lttng_trace_chunk_status lttng_trace_chunk_set_close_command( + struct lttng_trace_chunk *chunk, + enum lttng_trace_chunk_command_type close_command) +{ + enum lttng_trace_chunk_status status = LTTNG_TRACE_CHUNK_STATUS_OK; + + if (close_command < LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED || + close_command >= LTTNG_TRACE_CHUNK_COMMAND_TYPE_MAX) { + status = LTTNG_TRACE_CHUNK_STATUS_INVALID_ARGUMENT; + goto end; + } + + pthread_mutex_lock(&chunk->lock); + if (chunk->close_command.is_set) { + DBG("Overriding trace chunk close command from \"%s\" to \"%s\"", + lttng_trace_chunk_command_type_str(chunk->close_command.value), + lttng_trace_chunk_command_type_str(close_command)); + } else { + DBG("Setting trace chunk close command to \"%s\"", + lttng_trace_chunk_command_type_str(close_command)); + } + /* + * Unset close command for no-op for backward compatibility with relayd + * 2.11. + */ + if (close_command != LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) { + LTTNG_OPTIONAL_SET(&chunk->close_command, close_command); + } else { + LTTNG_OPTIONAL_UNSET(&chunk->close_command); + } + pthread_mutex_unlock(&chunk->lock); +end: + return status; +} + +const char *lttng_trace_chunk_command_type_get_name( + enum lttng_trace_chunk_command_type command) +{ + switch (command) { + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED: + return "move to completed trace chunk folder"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION: + return "no operation"; + case LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE: + return "delete"; + default: + abort(); + } +} + +bool lttng_trace_chunk_ids_equal(const struct lttng_trace_chunk *chunk_a, + const struct lttng_trace_chunk *chunk_b) +{ + bool equal = false; + + if (chunk_a == chunk_b) { + equal = true; + goto end; + } + + if (!!chunk_a ^ !!chunk_b) { + goto end; + } + + if (chunk_a->id.is_set ^ chunk_a->id.is_set) { + /* One id is set and not the other, thus they are not equal. */ + goto end; + } + + if (!chunk_a->id.is_set) { + /* Both ids are unset. */ + equal = true; + } else { + equal = chunk_a->id.value == chunk_b->id.value; + } + +end: + return equal; +} + +bool lttng_trace_chunk_get(struct lttng_trace_chunk *chunk) +{ + return urcu_ref_get_unless_zero(&chunk->ref); +} + +static +void free_lttng_trace_chunk_registry_element(struct rcu_head *node) +{ + struct lttng_trace_chunk_registry_element *element = + container_of(node, typeof(*element), rcu_node); + + lttng_trace_chunk_fini(&element->chunk); + free(element); +} + +static +void lttng_trace_chunk_release(struct urcu_ref *ref) +{ + struct lttng_trace_chunk *chunk = container_of(ref, typeof(*chunk), + ref); + + if (chunk->close_command.is_set) { + chunk_command func = close_command_get_post_release_func(chunk->close_command.value); + + if (func(chunk)) { + ERR("Trace chunk post-release command %s has failed.", + lttng_trace_chunk_command_type_str(chunk->close_command.value)); + } + } + + if (chunk->in_registry_element) { + struct lttng_trace_chunk_registry_element *element; + + element = container_of(chunk, typeof(*element), chunk); + if (element->registry) { + rcu_read_lock(); + cds_lfht_del(element->registry->ht, + &element->trace_chunk_registry_ht_node); + rcu_read_unlock(); + call_rcu(&element->rcu_node, + free_lttng_trace_chunk_registry_element); + } else { + /* Never published, can be free'd immediately. */ + free_lttng_trace_chunk_registry_element( + &element->rcu_node); + } + } else { + /* Not RCU-protected, free immediately. */ + lttng_trace_chunk_fini(chunk); + free(chunk); + } +} + +void lttng_trace_chunk_put(struct lttng_trace_chunk *chunk) +{ + if (!chunk) { + return; + } + LTTNG_ASSERT(chunk->ref.refcount); + urcu_ref_put(&chunk->ref, lttng_trace_chunk_release); +} + +struct lttng_trace_chunk_registry *lttng_trace_chunk_registry_create(void) +{ + struct lttng_trace_chunk_registry *registry; + + registry = (lttng_trace_chunk_registry *) zmalloc(sizeof(*registry)); + if (!registry) { + goto end; + } + + registry->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL); + if (!registry->ht) { + goto error; + } +end: + return registry; +error: + lttng_trace_chunk_registry_destroy(registry); + return NULL; +} + +void lttng_trace_chunk_registry_destroy( + struct lttng_trace_chunk_registry *registry) +{ + if (!registry) { + return; + } + if (registry->ht) { + int ret = cds_lfht_destroy(registry->ht, NULL); + LTTNG_ASSERT(!ret); + } + free(registry); +} + +static +struct lttng_trace_chunk_registry_element * +lttng_trace_chunk_registry_element_create_from_chunk( + struct lttng_trace_chunk *chunk, uint64_t session_id) +{ + struct lttng_trace_chunk_registry_element *element = + (lttng_trace_chunk_registry_element *) zmalloc(sizeof(*element)); + + if (!element) { + goto end; + } + cds_lfht_node_init(&element->trace_chunk_registry_ht_node); + element->session_id = session_id; + + element->chunk = *chunk; + lttng_trace_chunk_init(&element->chunk); + if (chunk->session_output_directory) { + /* Transferred ownership. */ + element->chunk.session_output_directory = + chunk->session_output_directory; + chunk->session_output_directory = NULL; + } + if (chunk->chunk_directory) { + /* Transferred ownership. */ + element->chunk.chunk_directory = chunk->chunk_directory; + chunk->chunk_directory = NULL; + } + /* + * The original chunk becomes invalid; the name and path attributes are + * transferred to the new chunk instance. + */ + chunk->name = NULL; + chunk->path = NULL; + element->chunk.fd_tracker = chunk->fd_tracker; + element->chunk.in_registry_element = true; +end: + return element; +} + +struct lttng_trace_chunk * +lttng_trace_chunk_registry_publish_chunk( + struct lttng_trace_chunk_registry *registry, + uint64_t session_id, struct lttng_trace_chunk *chunk) +{ + struct lttng_trace_chunk_registry_element *element; + unsigned long element_hash; + + pthread_mutex_lock(&chunk->lock); + element = lttng_trace_chunk_registry_element_create_from_chunk(chunk, + session_id); + pthread_mutex_unlock(&chunk->lock); + if (!element) { + goto end; + } + /* + * chunk is now invalid, the only valid operation is a 'put' from the + * caller. + */ + chunk = NULL; + element_hash = lttng_trace_chunk_registry_element_hash(element); + + rcu_read_lock(); + while (1) { + struct cds_lfht_node *published_node; + struct lttng_trace_chunk *published_chunk; + struct lttng_trace_chunk_registry_element *published_element; + + published_node = cds_lfht_add_unique(registry->ht, + element_hash, + lttng_trace_chunk_registry_element_match, + element, + &element->trace_chunk_registry_ht_node); + if (published_node == &element->trace_chunk_registry_ht_node) { + /* Successfully published the new element. */ + element->registry = registry; + /* Acquire a reference for the caller. */ + if (lttng_trace_chunk_get(&element->chunk)) { + break; + } else { + /* + * Another thread concurrently unpublished the + * trace chunk. This is currently unexpected. + * + * Re-attempt to publish. + */ + ERR("Attempt to publish a trace chunk to the chunk registry raced with a trace chunk deletion"); + continue; + } + } + + /* + * An equivalent trace chunk was published before this trace + * chunk. Attempt to acquire a reference to the one that was + * already published and release the reference to the copy we + * created if successful. + */ + published_element = container_of(published_node, + typeof(*published_element), + trace_chunk_registry_ht_node); + published_chunk = &published_element->chunk; + if (lttng_trace_chunk_get(published_chunk)) { + lttng_trace_chunk_put(&element->chunk); + element = published_element; + break; + } + /* + * A reference to the previously published trace chunk could not + * be acquired. Hence, retry to publish our copy of the trace + * chunk. + */ + } + rcu_read_unlock(); +end: + return element ? &element->chunk : NULL; +} + +/* + * Note that the caller must be registered as an RCU thread. + * However, it does not need to hold the RCU read lock. The RCU read lock is + * acquired to perform the look-up in the registry's hash table and held until + * after a reference to the "found" trace chunk is acquired. + * + * IOW, holding a reference guarantees the existence of the object for the + * caller. + */ +static +struct lttng_trace_chunk *_lttng_trace_chunk_registry_find_chunk( + const struct lttng_trace_chunk_registry *registry, + uint64_t session_id, uint64_t *chunk_id) +{ + lttng_trace_chunk_registry_element target_element {}; + + target_element.chunk.id.is_set = !!chunk_id; + target_element.chunk.id.value = chunk_id ? *chunk_id : 0; + target_element.session_id = session_id; + + const unsigned long element_hash = + lttng_trace_chunk_registry_element_hash( + &target_element); + struct cds_lfht_node *published_node; + struct lttng_trace_chunk_registry_element *published_element; + struct lttng_trace_chunk *published_chunk = NULL; + struct cds_lfht_iter iter; + + rcu_read_lock(); + cds_lfht_lookup(registry->ht, + element_hash, + lttng_trace_chunk_registry_element_match, + &target_element, + &iter); + published_node = cds_lfht_iter_get_node(&iter); + if (!published_node) { + goto end; + } + + published_element = container_of(published_node, + typeof(*published_element), + trace_chunk_registry_ht_node); + if (lttng_trace_chunk_get(&published_element->chunk)) { + published_chunk = &published_element->chunk; + } +end: + rcu_read_unlock(); + return published_chunk; +} + +struct lttng_trace_chunk * +lttng_trace_chunk_registry_find_chunk( + const struct lttng_trace_chunk_registry *registry, + uint64_t session_id, uint64_t chunk_id) +{ + return _lttng_trace_chunk_registry_find_chunk(registry, + session_id, &chunk_id); +} + +int lttng_trace_chunk_registry_chunk_exists( + const struct lttng_trace_chunk_registry *registry, + uint64_t session_id, uint64_t chunk_id, bool *chunk_exists) +{ + int ret = 0; + lttng_trace_chunk_registry_element target_element; + + target_element.chunk.id.is_set = true; + target_element.chunk.id.value = chunk_id; + target_element.session_id = session_id; + + const unsigned long element_hash = + lttng_trace_chunk_registry_element_hash( + &target_element); + struct cds_lfht_node *published_node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + cds_lfht_lookup(registry->ht, + element_hash, + lttng_trace_chunk_registry_element_match, + &target_element, + &iter); + published_node = cds_lfht_iter_get_node(&iter); + if (!published_node) { + *chunk_exists = false; + goto end; + } + + *chunk_exists = !cds_lfht_is_node_deleted(published_node); +end: + rcu_read_unlock(); + return ret; +} + +struct lttng_trace_chunk * +lttng_trace_chunk_registry_find_anonymous_chunk( + const struct lttng_trace_chunk_registry *registry, + uint64_t session_id) +{ + return _lttng_trace_chunk_registry_find_chunk(registry, + session_id, NULL); +} + +unsigned int lttng_trace_chunk_registry_put_each_chunk( + const struct lttng_trace_chunk_registry *registry) +{ + struct cds_lfht_iter iter; + struct lttng_trace_chunk_registry_element *chunk_element; + unsigned int trace_chunks_left = 0; + + DBG("Releasing trace chunk registry to all trace chunks"); + rcu_read_lock(); + cds_lfht_for_each_entry(registry->ht, + &iter, chunk_element, trace_chunk_registry_ht_node) { + const char *chunk_id_str = "none"; + char chunk_id_buf[MAX_INT_DEC_LEN(uint64_t)]; + + pthread_mutex_lock(&chunk_element->chunk.lock); + if (chunk_element->chunk.id.is_set) { + int fmt_ret; + + fmt_ret = snprintf(chunk_id_buf, sizeof(chunk_id_buf), + "%" PRIu64, + chunk_element->chunk.id.value); + if (fmt_ret < 0 || fmt_ret >= sizeof(chunk_id_buf)) { + chunk_id_str = "formatting error"; + } else { + chunk_id_str = chunk_id_buf; + } + } + + DBG("Releasing reference to trace chunk: session_id = %" PRIu64 + "chunk_id = %s, name = \"%s\", status = %s", + chunk_element->session_id, + chunk_id_str, + chunk_element->chunk.name ? : "none", + chunk_element->chunk.close_command.is_set ? + "open" : "closed"); + pthread_mutex_unlock(&chunk_element->chunk.lock); + lttng_trace_chunk_put(&chunk_element->chunk); + trace_chunks_left++; + } + rcu_read_unlock(); + DBG("Released reference to %u trace chunks in %s()", trace_chunks_left, + __FUNCTION__); + + return trace_chunks_left; +} diff --git a/src/common/tracker.c b/src/common/tracker.c deleted file mode 100644 index f3a9f258b..000000000 --- a/src/common/tracker.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (C) 2019 Jonathan Rajotte - * Copyright (C) 2020 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -struct process_attr_tracker_values_comm_header { - uint32_t count; -}; - -struct process_attr_tracker_value_comm { - /* enum lttng_process_attr_value_type */ - int32_t type; - union { - struct process_attr_integral_value_comm integral; - /* Includes the '\0' terminator. */ - uint32_t name_len; - } value; -}; - -#define GET_INTEGRAL_COMM_VALUE(value_ptr, as_type) \ - ((as_type)(is_signed(as_type) ? (value_ptr)->u._signed : \ - (value_ptr)->u._unsigned)) - -#define SET_INTEGRAL_COMM_VALUE(comm_value, value) \ - if (is_signed(typeof(value))) { \ - (comm_value)->u._signed = \ - (typeof((comm_value)->u._signed)) value; \ - } else { \ - (comm_value)->u._unsigned = \ - (typeof((comm_value)->u._unsigned)) value; \ - } - -static inline bool is_virtual_process_attr(enum lttng_process_attr process_attr) -{ - return process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID || - process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID || - process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID; -} - -static inline bool is_value_type_name( - enum lttng_process_attr_value_type value_type) -{ - return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME || - value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME; -} - -enum lttng_error_code process_attr_value_from_comm( - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - enum lttng_process_attr_value_type value_type, - const struct process_attr_integral_value_comm *integral_value, - const struct lttng_buffer_view *value_view, - struct process_attr_value **_value) -{ - char *name = NULL; - enum lttng_error_code ret = LTTNG_OK; - struct process_attr_value *value = zmalloc(sizeof(*value)); - - if (!value) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - - if (value_view && value_view->size > 0) { - if (value_view->data[value_view->size - 1] != '\0') { - ret = LTTNG_ERR_INVALID; - goto error; - } - name = strdup(value_view->data); - if (!name) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - } - - if (domain != LTTNG_DOMAIN_UST && domain != LTTNG_DOMAIN_KERNEL) { - ERR("Only the user space and kernel space domains may be specified to configure process attribute trackers"); - ret = LTTNG_ERR_UNSUPPORTED_DOMAIN; - goto error; - } - - if (!is_virtual_process_attr(process_attr) && - domain != LTTNG_DOMAIN_KERNEL) { - ERR("Non-virtual process attributes can only be used in the kernel domain"); - ret = LTTNG_ERR_UNSUPPORTED_DOMAIN; - goto error; - } - - /* Only expect a payload for name value types. */ - if (is_value_type_name(value_type) && - (!value_view || value_view->size == 0)) { - ret = LTTNG_ERR_INVALID_PROTOCOL; - goto error; - } else if (!is_value_type_name(value_type) && value_view && - value_view->size != 0) { - ret = LTTNG_ERR_INVALID_PROTOCOL; - goto error; - } - - value->type = value_type; - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - if (value_type != LTTNG_PROCESS_ATTR_VALUE_TYPE_PID) { - ERR("Invalid value type used for process ID process attribute"); - ret = LTTNG_ERR_INVALID; - goto error; - } - value->value.pid = - GET_INTEGRAL_COMM_VALUE(integral_value, pid_t); - break; - case LTTNG_PROCESS_ATTR_USER_ID: - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - switch (value_type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: - value->value.uid = GET_INTEGRAL_COMM_VALUE( - integral_value, uid_t); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: - if (!name) { - ret = LTTNG_ERR_INVALID; - goto error; - } - - value->value.user_name = name; - name = NULL; - break; - default: - ERR("Invalid value type used for user ID process attribute"); - ret = LTTNG_ERR_INVALID; - goto error; - } - break; - case LTTNG_PROCESS_ATTR_GROUP_ID: - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - switch (value_type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: - value->value.gid = GET_INTEGRAL_COMM_VALUE( - integral_value, gid_t); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: - if (!name) { - ret = LTTNG_ERR_INVALID; - goto error; - } - - value->value.group_name = name; - name = NULL; - break; - default: - ERR("Invalid value type used for group ID process attribute"); - ret = LTTNG_ERR_INVALID; - goto error; - } - break; - default: - ret = LTTNG_ERR_INVALID_PROTOCOL; - goto error; - } - - *_value = value; - value = NULL; - free(name); - return LTTNG_OK; -error: - free(name); - process_attr_value_destroy(value); - return ret; -} - -const char *lttng_process_attr_to_string(enum lttng_process_attr process_attr) -{ - switch (process_attr) { - case LTTNG_PROCESS_ATTR_PROCESS_ID: - return "process ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: - return "virtual process ID"; - case LTTNG_PROCESS_ATTR_USER_ID: - return "user ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: - return "virtual user ID"; - case LTTNG_PROCESS_ATTR_GROUP_ID: - return "group ID"; - case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: - return "virtual group ID"; - default: - return "unknown process attribute"; - } -} - -static void process_attr_tracker_value_destructor(void *ptr) -{ - struct process_attr_value *value = (typeof(value)) ptr; - - process_attr_value_destroy(value); -} - -struct lttng_process_attr_values *lttng_process_attr_values_create(void) -{ - struct lttng_process_attr_values *values = zmalloc(sizeof(*values)); - - if (!values) { - goto end; - } - - lttng_dynamic_pointer_array_init( - &values->array, process_attr_tracker_value_destructor); -end: - return values; -} - -unsigned int _lttng_process_attr_values_get_count( - const struct lttng_process_attr_values *values) -{ - return (unsigned int) lttng_dynamic_pointer_array_get_count( - &values->array); -} - -const struct process_attr_value *lttng_process_attr_tracker_values_get_at_index( - const struct lttng_process_attr_values *values, - unsigned int index) -{ - return lttng_dynamic_pointer_array_get_pointer(&values->array, index); -} - -static -int process_attr_tracker_value_serialize(const struct process_attr_value *value, - struct lttng_dynamic_buffer *buffer) -{ - int ret; - struct process_attr_tracker_value_comm value_comm = { - .type = (int32_t) value->type, - }; - const char *name = NULL; - - switch (value->type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: - SET_INTEGRAL_COMM_VALUE( - &value_comm.value.integral, value->value.pid); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: - SET_INTEGRAL_COMM_VALUE( - &value_comm.value.integral, value->value.uid); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: - SET_INTEGRAL_COMM_VALUE( - &value_comm.value.integral, value->value.gid); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: - name = value->value.user_name; - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: - name = value->value.group_name; - break; - default: - abort(); - } - - if (name) { - value_comm.value.name_len = strlen(name) + 1; - } - - ret = lttng_dynamic_buffer_append( - buffer, &value_comm, sizeof(value_comm)); - if (ret) { - goto end; - } - - if (name) { - ret = lttng_dynamic_buffer_append( - buffer, name, value_comm.value.name_len); - } -end: - return ret; -} - -int lttng_process_attr_values_serialize( - const struct lttng_process_attr_values *values, - struct lttng_dynamic_buffer *buffer) -{ - int ret; - unsigned int count, i; - struct process_attr_tracker_values_comm_header header = {}; - - count = _lttng_process_attr_values_get_count(values); - header.count = (uint32_t) count; - - ret = lttng_dynamic_buffer_append(buffer, &header, sizeof(header)); - if (ret) { - goto end; - } - - for (i = 0; i < count; i++) { - const struct process_attr_value *value = - lttng_process_attr_tracker_values_get_at_index( - values, i); - - ret = process_attr_tracker_value_serialize(value, buffer); - if (ret) { - goto end; - } - } -end: - return ret; -} - -ssize_t lttng_process_attr_values_create_from_buffer( - enum lttng_domain_type domain, - enum lttng_process_attr process_attr, - const struct lttng_buffer_view *buffer_view, - struct lttng_process_attr_values **_values) -{ - ssize_t offset; - unsigned int i; - struct lttng_process_attr_values *values; - struct lttng_buffer_view header_view; - const struct process_attr_tracker_values_comm_header *header; - - values = lttng_process_attr_values_create(); - if (!values) { - goto error; - } - - header_view = lttng_buffer_view_from_view( - buffer_view, 0, sizeof(*header)); - if (!lttng_buffer_view_is_valid(&header_view)) { - goto error; - } - - offset = header_view.size; - header = (typeof(header)) header_view.data; - - /* - * Check that the number of values is not absurdly large with respect to - * the received buffer's size. - */ - if (buffer_view->size < - header->count * sizeof(struct process_attr_tracker_value_comm)) { - goto error; - } - for (i = 0; i < (unsigned int) header->count; i++) { - int ret; - enum lttng_error_code ret_code; - const struct process_attr_tracker_value_comm *value_comm; - struct process_attr_value *value; - enum lttng_process_attr_value_type type; - struct lttng_buffer_view value_view; - struct lttng_buffer_view value_name_view = {}; - - value_view = lttng_buffer_view_from_view( - buffer_view, offset, sizeof(*value_comm)); - if (!lttng_buffer_view_is_valid(&value_view)) { - goto error; - } - - offset += value_view.size; - value_comm = (typeof(value_comm)) value_view.data; - type = (typeof(type)) value_comm->type; - - if (is_value_type_name(type)) { - value_name_view = lttng_buffer_view_from_view( - buffer_view, offset, - value_comm->value.name_len); - if (!lttng_buffer_view_is_valid(&value_name_view)) { - goto error; - } - - offset += value_name_view.size; - } - - ret_code = process_attr_value_from_comm(domain, process_attr, - type, &value_comm->value.integral, - &value_name_view, &value); - if (ret_code != LTTNG_OK) { - goto error; - } - - ret = lttng_dynamic_pointer_array_add_pointer( - &values->array, value); - if (ret) { - process_attr_value_destroy(value); - goto error; - } - } - - *_values = values; - return offset; -error: - lttng_process_attr_values_destroy(values); - return -1; -} - -void lttng_process_attr_values_destroy(struct lttng_process_attr_values *values) -{ - if (!values) { - return; - } - lttng_dynamic_pointer_array_reset(&values->array); - free(values); -} - -struct process_attr_value *process_attr_value_copy( - const struct process_attr_value *value) -{ - struct process_attr_value *new_value = NULL; - - if (!value) { - goto end; - } - - new_value = zmalloc(sizeof(*new_value)); - if (!new_value) { - goto end; - } - if (is_value_type_name(value->type)) { - const char *src = - value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? - value->value.user_name : - value->value.group_name; - char **dst = value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? - &new_value->value.user_name : - &new_value->value.group_name; - - new_value->type = value->type; - *dst = strdup(src); - if (!*dst) { - goto error; - } - } else { - *new_value = *value; - } -end: - return new_value; -error: - free(new_value); - return NULL; -} - -unsigned long process_attr_value_hash(const struct process_attr_value *a) -{ - unsigned long hash = hash_key_ulong((void *) a->type, lttng_ht_seed); - - switch (a->type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: - hash ^= hash_key_ulong((void *) (unsigned long) a->value.pid, - lttng_ht_seed); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: - hash ^= hash_key_ulong((void *) (unsigned long) a->value.uid, - lttng_ht_seed); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: - hash ^= hash_key_ulong((void *) (unsigned long) a->value.gid, - lttng_ht_seed); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: - hash ^= hash_key_str(a->value.user_name, lttng_ht_seed); - break; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: - hash ^= hash_key_str(a->value.group_name, lttng_ht_seed); - break; - default: - abort(); - } - - return hash; -} - -bool process_attr_tracker_value_equal(const struct process_attr_value *a, - const struct process_attr_value *b) -{ - if (a->type != b->type) { - return false; - } - switch (a->type) { - case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: - return a->value.pid == b->value.pid; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: - return a->value.uid == b->value.uid; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: - return a->value.gid == b->value.gid; - case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: - return !strcmp(a->value.user_name, b->value.user_name); - case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: - return !strcmp(a->value.group_name, b->value.group_name); - default: - abort(); - } -} - -void process_attr_value_destroy(struct process_attr_value *value) -{ - if (!value) { - return; - } - if (is_value_type_name(value->type)) { - free(value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? - value->value.user_name : - value->value.group_name); - } - free(value); -} diff --git a/src/common/tracker.cpp b/src/common/tracker.cpp new file mode 100644 index 000000000..84b6c6b8f --- /dev/null +++ b/src/common/tracker.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2019 Jonathan Rajotte + * Copyright (C) 2020 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +struct process_attr_tracker_values_comm_header { + uint32_t count; +}; + +struct process_attr_tracker_value_comm { + /* enum lttng_process_attr_value_type */ + int32_t type; + union { + struct process_attr_integral_value_comm integral; + /* Includes the '\0' terminator. */ + uint32_t name_len; + } value; +}; + +#define GET_INTEGRAL_COMM_VALUE(value_ptr, as_type) \ + ((as_type)(is_signed(as_type) ? (value_ptr)->u._signed : \ + (value_ptr)->u._unsigned)) + +#define SET_INTEGRAL_COMM_VALUE(comm_value, value) \ + if (is_signed(typeof(value))) { \ + (comm_value)->u._signed = \ + (typeof((comm_value)->u._signed)) value; \ + } else { \ + (comm_value)->u._unsigned = \ + (typeof((comm_value)->u._unsigned)) value; \ + } + +static inline bool is_virtual_process_attr(enum lttng_process_attr process_attr) +{ + return process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID || + process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID || + process_attr == LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID; +} + +static inline bool is_value_type_name( + enum lttng_process_attr_value_type value_type) +{ + return value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME || + value_type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME; +} + +enum lttng_error_code process_attr_value_from_comm( + enum lttng_domain_type domain, + enum lttng_process_attr process_attr, + enum lttng_process_attr_value_type value_type, + const struct process_attr_integral_value_comm *integral_value, + const struct lttng_buffer_view *value_view, + struct process_attr_value **_value) +{ + char *name = NULL; + enum lttng_error_code ret = LTTNG_OK; + struct process_attr_value *value = (process_attr_value *) zmalloc(sizeof(*value)); + + if (!value) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + if (value_view && value_view->size > 0) { + if (value_view->data[value_view->size - 1] != '\0') { + ret = LTTNG_ERR_INVALID; + goto error; + } + name = strdup(value_view->data); + if (!name) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + } + + if (domain != LTTNG_DOMAIN_UST && domain != LTTNG_DOMAIN_KERNEL) { + ERR("Only the user space and kernel space domains may be specified to configure process attribute trackers"); + ret = LTTNG_ERR_UNSUPPORTED_DOMAIN; + goto error; + } + + if (!is_virtual_process_attr(process_attr) && + domain != LTTNG_DOMAIN_KERNEL) { + ERR("Non-virtual process attributes can only be used in the kernel domain"); + ret = LTTNG_ERR_UNSUPPORTED_DOMAIN; + goto error; + } + + /* Only expect a payload for name value types. */ + if (is_value_type_name(value_type) && + (!value_view || value_view->size == 0)) { + ret = LTTNG_ERR_INVALID_PROTOCOL; + goto error; + } else if (!is_value_type_name(value_type) && value_view && + value_view->size != 0) { + ret = LTTNG_ERR_INVALID_PROTOCOL; + goto error; + } + + value->type = value_type; + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + if (value_type != LTTNG_PROCESS_ATTR_VALUE_TYPE_PID) { + ERR("Invalid value type used for process ID process attribute"); + ret = LTTNG_ERR_INVALID; + goto error; + } + value->value.pid = + GET_INTEGRAL_COMM_VALUE(integral_value, pid_t); + break; + case LTTNG_PROCESS_ATTR_USER_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + switch (value_type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: + value->value.uid = GET_INTEGRAL_COMM_VALUE( + integral_value, uid_t); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: + if (!name) { + ret = LTTNG_ERR_INVALID; + goto error; + } + + value->value.user_name = name; + name = NULL; + break; + default: + ERR("Invalid value type used for user ID process attribute"); + ret = LTTNG_ERR_INVALID; + goto error; + } + break; + case LTTNG_PROCESS_ATTR_GROUP_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + switch (value_type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: + value->value.gid = GET_INTEGRAL_COMM_VALUE( + integral_value, gid_t); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: + if (!name) { + ret = LTTNG_ERR_INVALID; + goto error; + } + + value->value.group_name = name; + name = NULL; + break; + default: + ERR("Invalid value type used for group ID process attribute"); + ret = LTTNG_ERR_INVALID; + goto error; + } + break; + default: + ret = LTTNG_ERR_INVALID_PROTOCOL; + goto error; + } + + *_value = value; + value = NULL; + free(name); + return LTTNG_OK; +error: + free(name); + process_attr_value_destroy(value); + return ret; +} + +const char *lttng_process_attr_to_string(enum lttng_process_attr process_attr) +{ + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + return "process ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + return "virtual process ID"; + case LTTNG_PROCESS_ATTR_USER_ID: + return "user ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + return "virtual user ID"; + case LTTNG_PROCESS_ATTR_GROUP_ID: + return "group ID"; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + return "virtual group ID"; + default: + return "unknown process attribute"; + } +} + +static void process_attr_tracker_value_destructor(void *ptr) +{ + struct process_attr_value *value = (typeof(value)) ptr; + + process_attr_value_destroy(value); +} + +struct lttng_process_attr_values *lttng_process_attr_values_create(void) +{ + struct lttng_process_attr_values *values = (lttng_process_attr_values *) zmalloc(sizeof(*values)); + + if (!values) { + goto end; + } + + lttng_dynamic_pointer_array_init( + &values->array, process_attr_tracker_value_destructor); +end: + return values; +} + +unsigned int _lttng_process_attr_values_get_count( + const struct lttng_process_attr_values *values) +{ + return (unsigned int) lttng_dynamic_pointer_array_get_count( + &values->array); +} + +const struct process_attr_value *lttng_process_attr_tracker_values_get_at_index( + const struct lttng_process_attr_values *values, + unsigned int index) +{ + return (process_attr_value *) lttng_dynamic_pointer_array_get_pointer(&values->array, index); +} + +static +int process_attr_tracker_value_serialize(const struct process_attr_value *value, + struct lttng_dynamic_buffer *buffer) +{ + int ret; + struct process_attr_tracker_value_comm value_comm = { + .type = (int32_t) value->type, + }; + const char *name = NULL; + + switch (value->type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: + SET_INTEGRAL_COMM_VALUE( + &value_comm.value.integral, value->value.pid); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: + SET_INTEGRAL_COMM_VALUE( + &value_comm.value.integral, value->value.uid); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: + SET_INTEGRAL_COMM_VALUE( + &value_comm.value.integral, value->value.gid); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: + name = value->value.user_name; + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: + name = value->value.group_name; + break; + default: + abort(); + } + + if (name) { + value_comm.value.name_len = strlen(name) + 1; + } + + ret = lttng_dynamic_buffer_append( + buffer, &value_comm, sizeof(value_comm)); + if (ret) { + goto end; + } + + if (name) { + ret = lttng_dynamic_buffer_append( + buffer, name, value_comm.value.name_len); + } +end: + return ret; +} + +int lttng_process_attr_values_serialize( + const struct lttng_process_attr_values *values, + struct lttng_dynamic_buffer *buffer) +{ + int ret; + unsigned int count, i; + struct process_attr_tracker_values_comm_header header = {}; + + count = _lttng_process_attr_values_get_count(values); + header.count = (uint32_t) count; + + ret = lttng_dynamic_buffer_append(buffer, &header, sizeof(header)); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + const struct process_attr_value *value = + lttng_process_attr_tracker_values_get_at_index( + values, i); + + ret = process_attr_tracker_value_serialize(value, buffer); + if (ret) { + goto end; + } + } +end: + return ret; +} + +ssize_t lttng_process_attr_values_create_from_buffer( + enum lttng_domain_type domain, + enum lttng_process_attr process_attr, + const struct lttng_buffer_view *buffer_view, + struct lttng_process_attr_values **_values) +{ + ssize_t offset; + unsigned int i; + struct lttng_process_attr_values *values; + struct lttng_buffer_view header_view; + const struct process_attr_tracker_values_comm_header *header; + + values = lttng_process_attr_values_create(); + if (!values) { + goto error; + } + + header_view = lttng_buffer_view_from_view( + buffer_view, 0, sizeof(*header)); + if (!lttng_buffer_view_is_valid(&header_view)) { + goto error; + } + + offset = header_view.size; + header = (typeof(header)) header_view.data; + + /* + * Check that the number of values is not absurdly large with respect to + * the received buffer's size. + */ + if (buffer_view->size < + header->count * sizeof(struct process_attr_tracker_value_comm)) { + goto error; + } + for (i = 0; i < (unsigned int) header->count; i++) { + int ret; + enum lttng_error_code ret_code; + const struct process_attr_tracker_value_comm *value_comm; + struct process_attr_value *value; + enum lttng_process_attr_value_type type; + struct lttng_buffer_view value_view; + struct lttng_buffer_view value_name_view = {}; + + value_view = lttng_buffer_view_from_view( + buffer_view, offset, sizeof(*value_comm)); + if (!lttng_buffer_view_is_valid(&value_view)) { + goto error; + } + + offset += value_view.size; + value_comm = (typeof(value_comm)) value_view.data; + type = (typeof(type)) value_comm->type; + + if (is_value_type_name(type)) { + value_name_view = lttng_buffer_view_from_view( + buffer_view, offset, + value_comm->value.name_len); + if (!lttng_buffer_view_is_valid(&value_name_view)) { + goto error; + } + + offset += value_name_view.size; + } + + ret_code = process_attr_value_from_comm(domain, process_attr, + type, &value_comm->value.integral, + &value_name_view, &value); + if (ret_code != LTTNG_OK) { + goto error; + } + + ret = lttng_dynamic_pointer_array_add_pointer( + &values->array, value); + if (ret) { + process_attr_value_destroy(value); + goto error; + } + } + + *_values = values; + return offset; +error: + lttng_process_attr_values_destroy(values); + return -1; +} + +void lttng_process_attr_values_destroy(struct lttng_process_attr_values *values) +{ + if (!values) { + return; + } + lttng_dynamic_pointer_array_reset(&values->array); + free(values); +} + +struct process_attr_value *process_attr_value_copy( + const struct process_attr_value *value) +{ + struct process_attr_value *new_value = NULL; + + if (!value) { + goto end; + } + + new_value = (process_attr_value *) zmalloc(sizeof(*new_value)); + if (!new_value) { + goto end; + } + if (is_value_type_name(value->type)) { + const char *src = + value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? + value->value.user_name : + value->value.group_name; + char **dst = value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? + &new_value->value.user_name : + &new_value->value.group_name; + + new_value->type = value->type; + *dst = strdup(src); + if (!*dst) { + goto error; + } + } else { + *new_value = *value; + } +end: + return new_value; +error: + free(new_value); + return NULL; +} + +unsigned long process_attr_value_hash(const struct process_attr_value *a) +{ + unsigned long hash = hash_key_ulong((void *) a->type, lttng_ht_seed); + + switch (a->type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: + hash ^= hash_key_ulong((void *) (unsigned long) a->value.pid, + lttng_ht_seed); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: + hash ^= hash_key_ulong((void *) (unsigned long) a->value.uid, + lttng_ht_seed); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: + hash ^= hash_key_ulong((void *) (unsigned long) a->value.gid, + lttng_ht_seed); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: + hash ^= hash_key_str(a->value.user_name, lttng_ht_seed); + break; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: + hash ^= hash_key_str(a->value.group_name, lttng_ht_seed); + break; + default: + abort(); + } + + return hash; +} + +bool process_attr_tracker_value_equal(const struct process_attr_value *a, + const struct process_attr_value *b) +{ + if (a->type != b->type) { + return false; + } + switch (a->type) { + case LTTNG_PROCESS_ATTR_VALUE_TYPE_PID: + return a->value.pid == b->value.pid; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_UID: + return a->value.uid == b->value.uid; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GID: + return a->value.gid == b->value.gid; + case LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME: + return !strcmp(a->value.user_name, b->value.user_name); + case LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME: + return !strcmp(a->value.group_name, b->value.group_name); + default: + abort(); + } +} + +void process_attr_value_destroy(struct process_attr_value *value) +{ + if (!value) { + return; + } + if (is_value_type_name(value->type)) { + free(value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME ? + value->value.user_name : + value->value.group_name); + } + free(value); +} diff --git a/src/common/trigger.c b/src/common/trigger.c deleted file mode 100644 index 76082a085..000000000 --- a/src/common/trigger.c +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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; -} diff --git a/src/common/trigger.cpp b/src/common/trigger.cpp new file mode 100644 index 000000000..3782f49ca --- /dev/null +++ b/src/common/trigger.cpp @@ -0,0 +1,1240 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/src/common/unix.c b/src/common/unix.c deleted file mode 100644 index 9918db2e6..000000000 --- a/src/common/unix.c +++ /dev/null @@ -1,1147 +0,0 @@ -/* - * Copyright (C) 2011 David Goulet - * Copyright (C) 2011 Mathieu Desnoyers - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "unix.h" - -/* - * Connect to unix socket using the path name. - */ -int lttcomm_connect_unix_sock(const char *pathname) -{ - struct sockaddr_un s_un; - int fd, ret, closeret; - - if (strlen(pathname) >= sizeof(s_un.sun_path)) { - ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).", - pathname, strlen(pathname) + 1, - sizeof(s_un.sun_path)); - ret = -ENAMETOOLONG; - goto error; - } - - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - PERROR("socket"); - ret = fd; - goto error; - } - - memset(&s_un, 0, sizeof(s_un)); - s_un.sun_family = AF_UNIX; - strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path)); - s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0'; - - ret = connect(fd, (struct sockaddr *) &s_un, sizeof(s_un)); - if (ret < 0) { - /* - * Don't print message on connect error, because connect is used in - * normal execution to detect if sessiond is alive. - */ - goto error_connect; - } - - return fd; - -error_connect: - closeret = close(fd); - if (closeret) { - PERROR("close"); - } -error: - return ret; -} - -/* - * Do an accept(2) on the sock and return the new file descriptor. The socket - * MUST be bind(2) before. - */ -int lttcomm_accept_unix_sock(int sock) -{ - int new_fd; - struct sockaddr_un s_un; - socklen_t len = sizeof(s_un); - - /* Blocking call */ - new_fd = accept(sock, (struct sockaddr *) &s_un, &len); - if (new_fd < 0) { - PERROR("accept"); - } - - return new_fd; -} - -int lttcomm_create_anon_unix_socketpair(int *fds) -{ - if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) < 0) { - PERROR("socketpair"); - return -1; - } - return 0; -} - -/* - * Creates a AF_UNIX local socket using pathname bind the socket upon creation - * and return the fd. - */ -int lttcomm_create_unix_sock(const char *pathname) -{ - struct sockaddr_un s_un; - int fd = -1; - int ret = -1; - - if (strlen(pathname) >= sizeof(s_un.sun_path)) { - ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).", - pathname, strlen(pathname) + 1, - sizeof(s_un.sun_path)); - ret = -ENAMETOOLONG; - goto error; - } - - /* Create server socket */ - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - PERROR("socket"); - goto error; - } - - memset(&s_un, 0, sizeof(s_un)); - s_un.sun_family = AF_UNIX; - strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path)); - s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0'; - - /* Unlink the old file if present */ - (void) unlink(pathname); - ret = bind(fd, (struct sockaddr *) &s_un, sizeof(s_un)); - if (ret < 0) { - PERROR("bind"); - goto error; - } - - return fd; - -error: - if (fd >= 0) { - if (close(fd) < 0) { - PERROR("close create unix sock"); - } - } - return ret; -} - -/* - * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN. - */ -int lttcomm_listen_unix_sock(int sock) -{ - int ret; - - ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN); - if (ret < 0) { - PERROR("listen"); - } - - return ret; -} - -/* - * Receive data of size len in put that data into the buf param. Using recvmsg - * API. - * - * Return the size of received data. - */ -ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret = -1; - size_t len_last; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - do { - len_last = iov[0].iov_len; - ret = lttng_recvmsg_nosigpipe(sock, &msg); - if (ret > 0) { - iov[0].iov_base += ret; - iov[0].iov_len -= ret; - LTTNG_ASSERT(ret <= len_last); - } - } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR)); - if (ret < 0) { - PERROR("recvmsg"); - } else if (ret > 0) { - ret = len; - } - /* Else ret = 0 meaning an orderly shutdown. */ - - return ret; -} - -/* - * Receive data of size len in put that data into the buf param. Using recvmsg - * API. Only use with sockets set in non-blocking mode. - * - * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a - * poll set. The poll loop will handle the EPIPE original cause. - * - * Return the size of received data. - */ -ssize_t lttcomm_recv_unix_sock_non_block(int sock, void *buf, size_t len) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - -retry: - ret = lttng_recvmsg_nosigpipe(sock, &msg); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } else { - /* - * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. - */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EPIPE) { - /* - * Nothing was recv. - */ - ret = 0; - goto end; - } - - /* Unexpected error */ - PERROR("recvmsg"); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -/* - * Send buf data of size len. Using sendmsg API. - * - * Return the size of sent data. - */ -ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t len) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = (void *) buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - while (iov[0].iov_len) { - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - if (errno == EINTR) { - continue; - } else { - /* - * Only warn about EPIPE when quiet mode is - * deactivated. - * We consider EPIPE as expected. - */ - if (errno != EPIPE || !lttng_opt_quiet) { - PERROR("sendmsg"); - } - goto end; - } - } - iov[0].iov_len -= ret; - iov[0].iov_base += ret; - } - ret = len; -end: - return ret; -} - -/* - * Send buf data of size len. Using sendmsg API. - * Only use with non-blocking sockets. The difference with the blocking version - * of the function is that this one does not retry to send on partial sends, - * except if the interruption was caused by a signal (EINTR). - * - * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a - * poll set. The poll loop will handle the EPIPE original cause. - * - * Return the size of sent data. - */ -ssize_t lttcomm_send_unix_sock_non_block(int sock, const void *buf, size_t len) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - - memset(&msg, 0, sizeof(msg)); - - iov[0].iov_base = (void *) buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - -retry: - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } else { - /* - * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. - */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EPIPE) { - /* - * This can happen in non blocking mode. - * Nothing was sent. - */ - ret = 0; - goto end; - } - - /* Unexpected error */ - PERROR("sendmsg"); - ret = -1; - goto end; - } - } -end: - return ret; -} - -/* - * Shutdown cleanly a unix socket. - */ -int lttcomm_close_unix_sock(int sock) -{ - int ret, closeret; - - /* Shutdown receptions and transmissions */ - ret = shutdown(sock, SHUT_RDWR); - if (ret < 0) { - PERROR("shutdown"); - } - - closeret = close(sock); - if (closeret) { - PERROR("close"); - } - - return ret; -} - -/* - * Send a message accompanied by fd(s) over a unix socket. - * - * Returns the size of data sent, or negative error value. - */ -ssize_t lttcomm_send_fds_unix_sock(int sock, const int *fds, size_t nb_fd) -{ - struct msghdr msg; - struct cmsghdr *cmptr; - struct iovec iov[1]; - ssize_t ret = -1; - unsigned int sizeof_fds = nb_fd * sizeof(int); - char tmp[CMSG_SPACE(sizeof_fds)]; - char dummy = 0; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(fds); - LTTNG_ASSERT(nb_fd > 0); - - memset(&msg, 0, sizeof(msg)); - memset(tmp, 0, sizeof(tmp)); - - if (nb_fd > LTTCOMM_MAX_SEND_FDS) - return -EINVAL; - - msg.msg_control = (caddr_t)tmp; - msg.msg_controllen = CMSG_LEN(sizeof_fds); - - cmptr = CMSG_FIRSTHDR(&msg); - if (!cmptr) { - return -1; - } - - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - cmptr->cmsg_len = CMSG_LEN(sizeof_fds); - memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); - /* Sum of the length of all control messages in the buffer: */ - msg.msg_controllen = cmptr->cmsg_len; - - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - do { - ret = sendmsg(sock, &msg, 0); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - /* - * Only warn about EPIPE when quiet mode is deactivated. - * We consider EPIPE as expected. - */ - if (errno != EPIPE || !lttng_opt_quiet) { - PERROR("sendmsg"); - } - } - return ret; -} - -/* - * Send the fd(s) of a payload view over a unix socket. - * - * Returns the size of data sent, or negative error value. - */ -static -ssize_t _lttcomm_send_payload_view_fds_unix_sock(int sock, - struct lttng_payload_view *view, - bool blocking) -{ - int i; - ssize_t ret; - struct lttng_dynamic_array raw_fds; - const int fd_count = lttng_payload_view_get_fd_handle_count(view); - - lttng_dynamic_array_init(&raw_fds, sizeof(int), NULL); - - if (fd_count < 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - /* - * Prepare a contiguous array of file descriptors to send them. - * - * Note that the reference to each fd is released during the iteration; - * we're just getting the numerical value of the fds to conform to the - * syscall's interface. We rely on the fact that "view" must remain - * valid for the duration of the call and that the underlying payload - * owns a reference to the fd_handles. - */ - for (i = 0; i < fd_count; i++) { - struct fd_handle *handle = - lttng_payload_view_pop_fd_handle(view); - const int raw_fd = fd_handle_get_fd(handle); - const int add_ret = lttng_dynamic_array_add_element( - &raw_fds, &raw_fd); - - fd_handle_put(handle); - if (add_ret) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - } - - if (blocking) { - ret = lttcomm_send_fds_unix_sock(sock, - (const int *) raw_fds.buffer.data, fd_count); - } else { - ret = lttcomm_send_fds_unix_sock_non_block(sock, - (const int *) raw_fds.buffer.data, fd_count); - } - -end: - lttng_dynamic_array_reset(&raw_fds); - return ret; -} - -ssize_t lttcomm_send_payload_view_fds_unix_sock(int sock, - struct lttng_payload_view *view) -{ - return _lttcomm_send_payload_view_fds_unix_sock(sock, view, true); -} - -ssize_t lttcomm_send_payload_view_fds_unix_sock_non_block(int sock, - struct lttng_payload_view *view) -{ - return _lttcomm_send_payload_view_fds_unix_sock(sock, view, false); -} - -/* - * Send a message accompanied by fd(s) over a unix socket. - * Only use for non blocking socket. - * - * Returns the size of data sent, or negative error value. - */ -ssize_t lttcomm_send_fds_unix_sock_non_block(int sock, const int *fds, size_t nb_fd) -{ - struct msghdr msg; - struct cmsghdr *cmptr; - struct iovec iov[1]; - ssize_t ret = -1; - unsigned int sizeof_fds = nb_fd * sizeof(int); - char tmp[CMSG_SPACE(sizeof_fds)]; - char dummy = 0; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(fds); - LTTNG_ASSERT(nb_fd > 0); - - memset(&msg, 0, sizeof(msg)); - memset(tmp, 0, sizeof(tmp)); - - if (nb_fd > LTTCOMM_MAX_SEND_FDS) - return -EINVAL; - - msg.msg_control = (caddr_t)tmp; - msg.msg_controllen = CMSG_LEN(sizeof_fds); - - cmptr = CMSG_FIRSTHDR(&msg); - if (!cmptr) { - return -1; - } - - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - cmptr->cmsg_len = CMSG_LEN(sizeof_fds); - memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); - /* Sum of the length of all control messages in the buffer: */ - msg.msg_controllen = cmptr->cmsg_len; - - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - -retry: - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } else { - /* - * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. - */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - /* - * This can happen in non blocking mode. - * Nothing was sent. - */ - ret = 0; - goto end; - } - - if (errno == EPIPE) { - /* Expected error, pass error to caller */ - DBG3("EPIPE on sendmsg"); - ret = -1; - goto end; - } - - /* Unexpected error */ - PERROR("sendmsg"); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -/* - * Recv a message accompanied by fd(s) from a unix socket. - * - * Returns the size of received data, or negative error value. - * - * Expect at most "nb_fd" file descriptors. Returns the number of fd - * actually received in nb_fd. - */ -ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd) -{ - struct iovec iov[1]; - ssize_t ret = 0; - struct cmsghdr *cmsg; - size_t sizeof_fds = nb_fd * sizeof(int); - -#ifdef __linux__ -/* Account for the struct ucred cmsg in the buffer size */ -#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred)) -#else -#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) -#endif /* __linux__ */ - - char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE]; - struct msghdr msg; - char dummy; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(fds); - LTTNG_ASSERT(nb_fd > 0); - - memset(&msg, 0, sizeof(msg)); - - /* Prepare to receive the structures */ - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - cmsg = (struct cmsghdr *) recv_buf; - cmsg->cmsg_len = CMSG_LEN(sizeof_fds); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - - msg.msg_control = cmsg; - msg.msg_controllen = CMSG_LEN(sizeof(recv_buf)); - msg.msg_flags = 0; - -retry: - ret = lttng_recvmsg_nosigpipe(sock, &msg); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } else { - /* We consider EPIPE and EAGAIN as expected. */ - if (!lttng_opt_quiet && - (errno != EPIPE && errno != EAGAIN)) { - PERROR("recvmsg"); - } - goto end; - } - } - - if (ret != 1) { - fprintf(stderr, "Error: Received %zd bytes, expected %d\n", - ret, 1); - goto end; - } - - if (msg.msg_flags & MSG_CTRUNC) { - fprintf(stderr, "Error: Control message truncated.\n"); - ret = -1; - goto end; - } - - /* - * If the socket was configured with SO_PASSCRED, the kernel will add a - * control message (cmsg) to the ancillary data of the unix socket. We - * need to expect a cmsg of the SCM_CREDENTIALS as the first control - * message. - */ - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) { - fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n"); - ret = -1; - goto end; - } - if (cmsg->cmsg_type == SCM_RIGHTS) { - /* - * We found the controle message for file descriptors, - * now copy the fds to the fds ptr and return success. - */ - if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { - fprintf(stderr, "Error: Received %zu bytes of" - "ancillary data for FDs, expected %zu\n", - (size_t) cmsg->cmsg_len, - (size_t) CMSG_LEN(sizeof_fds)); - ret = -1; - goto end; - } - memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); - ret = sizeof_fds; - goto end; - } -#ifdef __linux__ - if (cmsg->cmsg_type == SCM_CREDENTIALS) { - /* - * Expect credentials to be sent when expecting fds even - * if no credential were include in the send(). The - * kernel adds them... - */ - ret = -1; - } -#endif /* __linux__ */ - } -end: - return ret; -} - -static -void close_raw_fd(void *ptr) -{ - const int raw_fd = *((const int *) ptr); - - if (raw_fd >= 0) { - const int ret = close(raw_fd); - - if (ret) { - PERROR("Failed to close file descriptor %d", raw_fd); - } - } -} - -static -enum lttng_error_code add_fds_to_payload(struct lttng_dynamic_array *raw_fds, - struct lttng_payload *payload) -{ - int i; - enum lttng_error_code ret_code = LTTNG_OK; - const int fd_count = lttng_dynamic_array_get_count(raw_fds); - - for (i = 0; i < fd_count; i++) { - int ret; - struct fd_handle *handle; - int *raw_fd = (int *) lttng_dynamic_array_get_element( - raw_fds, i); - - LTTNG_ASSERT(*raw_fd != -1); - - handle = fd_handle_create(*raw_fd); - if (!handle) { - ret_code = LTTNG_ERR_NOMEM; - goto end; - } - - /* FD ownership transferred to the handle. */ - *raw_fd = -1; - - ret = lttng_payload_push_fd_handle(payload, handle); - fd_handle_put(handle); - if (ret) { - ret_code = LTTNG_ERR_NOMEM; - goto end; - } - } - -end: - return ret_code; -} - -static -ssize_t _lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd, - struct lttng_payload *payload, bool blocking) -{ - int i = 0; - enum lttng_error_code add_ret; - ssize_t ret; - int default_value = -1; - struct lttng_dynamic_array raw_fds; - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(payload); - LTTNG_ASSERT(nb_fd > 0); - - lttng_dynamic_array_init(&raw_fds, sizeof(int), close_raw_fd); - - for (i = 0; i < nb_fd; i++) { - if (lttng_dynamic_array_add_element(&raw_fds, &default_value)) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - } - - if (blocking) { - ret = lttcomm_recv_fds_unix_sock( - sock, (int *) raw_fds.buffer.data, nb_fd); - } else { - ret = lttcomm_recv_fds_unix_sock_non_block( - sock, (int *) raw_fds.buffer.data, nb_fd); - } - - if (ret <= 0) { - goto end; - } - - add_ret = add_fds_to_payload(&raw_fds, payload); - if (add_ret != LTTNG_OK) { - ret = - (int) add_ret; - goto end; - } - -end: - lttng_dynamic_array_reset(&raw_fds); - return ret; -} - -ssize_t lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd, - struct lttng_payload *payload) -{ - return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, true); -} - -ssize_t lttcomm_recv_payload_fds_unix_sock_non_block(int sock, size_t nb_fd, - struct lttng_payload *payload) -{ - return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, false); -} - -/* - * Recv a message accompanied by fd(s) from a non-blocking unix socket. - * Only use with non-blocking sockets. - * - * Returns the size of received data, or negative error value. - * - * Expect at most "nb_fd" file descriptors. - * - * Note that based on our comprehension, partial reception of fds is not - * possible since the FDs are actually in the control message. It is all or - * nothing, still the sender side can send the wrong number of fds. - */ -ssize_t lttcomm_recv_fds_unix_sock_non_block(int sock, int *fds, size_t nb_fd) -{ - struct iovec iov[1]; - ssize_t ret = 0; - struct cmsghdr *cmsg; - size_t sizeof_fds = nb_fd * sizeof(int); - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(fds); - LTTNG_ASSERT(nb_fd > 0); - -#ifdef __linux__ -/* Account for the struct ucred cmsg in the buffer size */ -#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred)) -#else -#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) -#endif /* __linux__ */ - - char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE]; - struct msghdr msg; - char dummy; - - memset(&msg, 0, sizeof(msg)); - - /* Prepare to receive the structures */ - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - cmsg = (struct cmsghdr *) recv_buf; - cmsg->cmsg_len = CMSG_LEN(sizeof_fds); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - - msg.msg_control = cmsg; - msg.msg_controllen = CMSG_LEN(sizeof(recv_buf)); - msg.msg_flags = 0; - -retry: - ret = lttng_recvmsg_nosigpipe(sock, &msg); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } else { - /* - * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. - */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - /* - * This can happen in non blocking mode. - * Nothing was recv. - */ - ret = 0; - goto end; - } - - if (errno == EPIPE) { - /* Expected error, pass error to caller */ - DBG3("EPIPE on recvmsg"); - ret = -1; - goto end; - } - - /* Unexpected error */ - PERROR("recvmsg"); - ret = -1; - goto end; - } - } - - if (ret != 1) { - fprintf(stderr, "Error: Received %zd bytes, expected %d\n", - ret, 1); - goto end; - } - - if (msg.msg_flags & MSG_CTRUNC) { - fprintf(stderr, "Error: Control message truncated.\n"); - ret = -1; - goto end; - } - - /* - * If the socket was configured with SO_PASSCRED, the kernel will add a - * control message (cmsg) to the ancillary data of the unix socket. We - * need to expect a cmsg of the SCM_CREDENTIALS as the first control - * message. - */ - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) { - fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n"); - ret = -1; - goto end; - } - if (cmsg->cmsg_type == SCM_RIGHTS) { - /* - * We found the controle message for file descriptors, - * now copy the fds to the fds ptr and return success. - */ - if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { - fprintf(stderr, "Error: Received %zu bytes of" - "ancillary data for FDs, expected %zu\n", - (size_t) cmsg->cmsg_len, - (size_t) CMSG_LEN(sizeof_fds)); - ret = -1; - goto end; - } - memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); - ret = sizeof_fds; - goto end; - } -#ifdef __linux__ - if (cmsg->cmsg_type == SCM_CREDENTIALS) { - /* - * Expect credentials to be sent when expecting fds even - * if no credential were include in the send(). The - * kernel adds them... - */ - ret = -1; - } -#endif /* __linux__ */ - } -end: - return ret; -} - -/* - * Send a message with credentials over a unix socket. - * - * Returns the size of data sent, or negative error value. - */ -ssize_t lttcomm_send_creds_unix_sock(int sock, const void *buf, size_t len) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret = -1; -#if defined(__linux__) || defined(__CYGWIN__) - struct cmsghdr *cmptr; - size_t sizeof_cred = sizeof(lttng_sock_cred); - char anc_buf[CMSG_SPACE(sizeof_cred)]; - lttng_sock_cred *creds; - - memset(anc_buf, 0, CMSG_SPACE(sizeof_cred) * sizeof(char)); -#endif /* __linux__, __CYGWIN__ */ - - memset(&msg, 0, sizeof(msg)); - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - - iov[0].iov_base = (void *) buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - -#if defined(__linux__) || defined(__CYGWIN__) - msg.msg_control = (caddr_t) anc_buf; - msg.msg_controllen = CMSG_LEN(sizeof_cred); - - cmptr = CMSG_FIRSTHDR(&msg); - if (!cmptr) { - return -1; - } - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = LTTNG_SOCK_CREDS; - cmptr->cmsg_len = CMSG_LEN(sizeof_cred); - - creds = (lttng_sock_cred*) CMSG_DATA(cmptr); - - LTTNG_SOCK_SET_UID_CRED(creds, geteuid()); - LTTNG_SOCK_SET_GID_CRED(creds, getegid()); - LTTNG_SOCK_SET_PID_CRED(creds, getpid()); -#endif /* __linux__, __CYGWIN__ */ - - do { - ret = sendmsg(sock, &msg, 0); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - /* - * Only warn about EPIPE when quiet mode is deactivated. - * We consider EPIPE as expected. - */ - if (errno != EPIPE || !lttng_opt_quiet) { - PERROR("sendmsg"); - } - } - return ret; -} - -/* - * Recv a message accompanied with credentials from a unix socket. - * - * Returns the size of received data, or negative error value. - */ -ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, - lttng_sock_cred *creds) -{ - struct msghdr msg; - struct iovec iov[1]; - ssize_t ret; - size_t len_last; -#if defined(__linux__) || defined(__CYGWIN__) - struct cmsghdr *cmptr; - size_t sizeof_cred = sizeof(lttng_sock_cred); - char anc_buf[CMSG_SPACE(sizeof_cred)]; -#endif /* __linux__, __CYGWIN__ */ - - LTTNG_ASSERT(sock); - LTTNG_ASSERT(buf); - LTTNG_ASSERT(len > 0); - LTTNG_ASSERT(creds); - - memset(&msg, 0, sizeof(msg)); - - /* Prepare to receive the structures */ - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - -#if defined(__linux__) || defined(__CYGWIN__) - msg.msg_control = anc_buf; - msg.msg_controllen = sizeof(anc_buf); -#endif /* __linux__, __CYGWIN__ */ - - do { - len_last = iov[0].iov_len; - ret = recvmsg(sock, &msg, 0); - if (ret > 0) { - iov[0].iov_base += ret; - iov[0].iov_len -= ret; - LTTNG_ASSERT(ret <= len_last); - } - } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR)); - if (ret < 0) { - PERROR("recvmsg fds"); - goto end; - } else if (ret > 0) { - ret = len; - } - /* Else ret = 0 meaning an orderly shutdown. */ - -#if defined(__linux__) || defined(__CYGWIN__) - if (msg.msg_flags & MSG_CTRUNC) { - fprintf(stderr, "Error: Control message truncated.\n"); - ret = -1; - goto end; - } - - cmptr = CMSG_FIRSTHDR(&msg); - if (cmptr == NULL) { - fprintf(stderr, "Error: Invalid control message header\n"); - ret = -1; - goto end; - } - - if (cmptr->cmsg_level != SOL_SOCKET || - cmptr->cmsg_type != LTTNG_SOCK_CREDS) { - fprintf(stderr, "Didn't received any credentials\n"); - ret = -1; - goto end; - } - - if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) { - fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", - (size_t) cmptr->cmsg_len, (size_t) CMSG_LEN(sizeof_cred)); - ret = -1; - goto end; - } - - memcpy(creds, CMSG_DATA(cmptr), sizeof_cred); -#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__)) - if (lttng_get_unix_socket_peer_creds(sock, creds)) { - fprintf(stderr, "ARG\n"); - ret = -1; - goto end; - } -#else -#error "Please implement credential support for your OS." -#endif /* __linux__, __CYGWIN__ */ - -end: - return ret; -} - -/* - * Set socket option to use credentials passing. - */ -#if defined(__linux__) || defined(__CYGWIN__) -int lttcomm_setsockopt_creds_unix_sock(int sock) -{ - int ret, on = 1; - - /* Set socket for credentials retrieval */ - ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (ret < 0) { - PERROR("setsockopt creds unix sock"); - } - return ret; -} -#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__)) -int lttcomm_setsockopt_creds_unix_sock(int sock) -{ - return 0; -} -#else -#error "Please implement credential support for your OS." -#endif /* __linux__ */ diff --git a/src/common/unix.cpp b/src/common/unix.cpp new file mode 100644 index 000000000..504970ea8 --- /dev/null +++ b/src/common/unix.cpp @@ -0,0 +1,1147 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2011 Mathieu Desnoyers + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "unix.h" + +/* + * Connect to unix socket using the path name. + */ +int lttcomm_connect_unix_sock(const char *pathname) +{ + struct sockaddr_un s_un; + int fd, ret, closeret; + + if (strlen(pathname) >= sizeof(s_un.sun_path)) { + ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).", + pathname, strlen(pathname) + 1, + sizeof(s_un.sun_path)); + ret = -ENAMETOOLONG; + goto error; + } + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + PERROR("socket"); + ret = fd; + goto error; + } + + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path)); + s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0'; + + ret = connect(fd, (struct sockaddr *) &s_un, sizeof(s_un)); + if (ret < 0) { + /* + * Don't print message on connect error, because connect is used in + * normal execution to detect if sessiond is alive. + */ + goto error_connect; + } + + return fd; + +error_connect: + closeret = close(fd); + if (closeret) { + PERROR("close"); + } +error: + return ret; +} + +/* + * Do an accept(2) on the sock and return the new file descriptor. The socket + * MUST be bind(2) before. + */ +int lttcomm_accept_unix_sock(int sock) +{ + int new_fd; + struct sockaddr_un s_un; + socklen_t len = sizeof(s_un); + + /* Blocking call */ + new_fd = accept(sock, (struct sockaddr *) &s_un, &len); + if (new_fd < 0) { + PERROR("accept"); + } + + return new_fd; +} + +int lttcomm_create_anon_unix_socketpair(int *fds) +{ + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) < 0) { + PERROR("socketpair"); + return -1; + } + return 0; +} + +/* + * Creates a AF_UNIX local socket using pathname bind the socket upon creation + * and return the fd. + */ +int lttcomm_create_unix_sock(const char *pathname) +{ + struct sockaddr_un s_un; + int fd = -1; + int ret = -1; + + if (strlen(pathname) >= sizeof(s_un.sun_path)) { + ERR("unix socket address (\"%s\") is longer than the platform's limit (%zu > %zu).", + pathname, strlen(pathname) + 1, + sizeof(s_un.sun_path)); + ret = -ENAMETOOLONG; + goto error; + } + + /* Create server socket */ + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + PERROR("socket"); + goto error; + } + + memset(&s_un, 0, sizeof(s_un)); + s_un.sun_family = AF_UNIX; + strncpy(s_un.sun_path, pathname, sizeof(s_un.sun_path)); + s_un.sun_path[sizeof(s_un.sun_path) - 1] = '\0'; + + /* Unlink the old file if present */ + (void) unlink(pathname); + ret = bind(fd, (struct sockaddr *) &s_un, sizeof(s_un)); + if (ret < 0) { + PERROR("bind"); + goto error; + } + + return fd; + +error: + if (fd >= 0) { + if (close(fd) < 0) { + PERROR("close create unix sock"); + } + } + return ret; +} + +/* + * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN. + */ +int lttcomm_listen_unix_sock(int sock) +{ + int ret; + + ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN); + if (ret < 0) { + PERROR("listen"); + } + + return ret; +} + +/* + * Receive data of size len in put that data into the buf param. Using recvmsg + * API. + * + * Return the size of received data. + */ +ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret = -1; + size_t len_last; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + do { + len_last = iov[0].iov_len; + ret = lttng_recvmsg_nosigpipe(sock, &msg); + if (ret > 0) { + iov[0].iov_base = (char *) iov[0].iov_base + ret; + iov[0].iov_len -= ret; + LTTNG_ASSERT(ret <= len_last); + } + } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR)); + if (ret < 0) { + PERROR("recvmsg"); + } else if (ret > 0) { + ret = len; + } + /* Else ret = 0 meaning an orderly shutdown. */ + + return ret; +} + +/* + * Receive data of size len in put that data into the buf param. Using recvmsg + * API. Only use with sockets set in non-blocking mode. + * + * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a + * poll set. The poll loop will handle the EPIPE original cause. + * + * Return the size of received data. + */ +ssize_t lttcomm_recv_unix_sock_non_block(int sock, void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +retry: + ret = lttng_recvmsg_nosigpipe(sock, &msg); + if (ret < 0) { + if (errno == EINTR) { + goto retry; + } else { + /* + * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. + */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EPIPE) { + /* + * Nothing was recv. + */ + ret = 0; + goto end; + } + + /* Unexpected error */ + PERROR("recvmsg"); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +/* + * Send buf data of size len. Using sendmsg API. + * + * Return the size of sent data. + */ +ssize_t lttcomm_send_unix_sock(int sock, const void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *) buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + while (iov[0].iov_len) { + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else { + /* + * Only warn about EPIPE when quiet mode is + * deactivated. + * We consider EPIPE as expected. + */ + if (errno != EPIPE || !lttng_opt_quiet) { + PERROR("sendmsg"); + } + goto end; + } + } + iov[0].iov_len -= ret; + iov[0].iov_base = (char *) iov[0].iov_base + ret; + } + ret = len; +end: + return ret; +} + +/* + * Send buf data of size len. Using sendmsg API. + * Only use with non-blocking sockets. The difference with the blocking version + * of the function is that this one does not retry to send on partial sends, + * except if the interruption was caused by a signal (EINTR). + * + * NOTE: EPIPE errors are NOT reported. This call expects the socket to be in a + * poll set. The poll loop will handle the EPIPE original cause. + * + * Return the size of sent data. + */ +ssize_t lttcomm_send_unix_sock_non_block(int sock, const void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_base = (void *) buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +retry: + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + if (errno == EINTR) { + goto retry; + } else { + /* + * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. + */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EPIPE) { + /* + * This can happen in non blocking mode. + * Nothing was sent. + */ + ret = 0; + goto end; + } + + /* Unexpected error */ + PERROR("sendmsg"); + ret = -1; + goto end; + } + } +end: + return ret; +} + +/* + * Shutdown cleanly a unix socket. + */ +int lttcomm_close_unix_sock(int sock) +{ + int ret, closeret; + + /* Shutdown receptions and transmissions */ + ret = shutdown(sock, SHUT_RDWR); + if (ret < 0) { + PERROR("shutdown"); + } + + closeret = close(sock); + if (closeret) { + PERROR("close"); + } + + return ret; +} + +/* + * Send a message accompanied by fd(s) over a unix socket. + * + * Returns the size of data sent, or negative error value. + */ +ssize_t lttcomm_send_fds_unix_sock(int sock, const int *fds, size_t nb_fd) +{ + struct msghdr msg; + struct cmsghdr *cmptr; + struct iovec iov[1]; + ssize_t ret = -1; + unsigned int sizeof_fds = nb_fd * sizeof(int); + char tmp[CMSG_SPACE(sizeof_fds)]; + char dummy = 0; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(fds); + LTTNG_ASSERT(nb_fd > 0); + + memset(&msg, 0, sizeof(msg)); + memset(tmp, 0, sizeof(tmp)); + + if (nb_fd > LTTCOMM_MAX_SEND_FDS) + return -EINVAL; + + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof_fds); + + cmptr = CMSG_FIRSTHDR(&msg); + if (!cmptr) { + return -1; + } + + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + cmptr->cmsg_len = CMSG_LEN(sizeof_fds); + memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); + /* Sum of the length of all control messages in the buffer: */ + msg.msg_controllen = cmptr->cmsg_len; + + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + do { + ret = sendmsg(sock, &msg, 0); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + /* + * Only warn about EPIPE when quiet mode is deactivated. + * We consider EPIPE as expected. + */ + if (errno != EPIPE || !lttng_opt_quiet) { + PERROR("sendmsg"); + } + } + return ret; +} + +/* + * Send the fd(s) of a payload view over a unix socket. + * + * Returns the size of data sent, or negative error value. + */ +static +ssize_t _lttcomm_send_payload_view_fds_unix_sock(int sock, + struct lttng_payload_view *view, + bool blocking) +{ + int i; + ssize_t ret; + struct lttng_dynamic_array raw_fds; + const int fd_count = lttng_payload_view_get_fd_handle_count(view); + + lttng_dynamic_array_init(&raw_fds, sizeof(int), NULL); + + if (fd_count < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + /* + * Prepare a contiguous array of file descriptors to send them. + * + * Note that the reference to each fd is released during the iteration; + * we're just getting the numerical value of the fds to conform to the + * syscall's interface. We rely on the fact that "view" must remain + * valid for the duration of the call and that the underlying payload + * owns a reference to the fd_handles. + */ + for (i = 0; i < fd_count; i++) { + struct fd_handle *handle = + lttng_payload_view_pop_fd_handle(view); + const int raw_fd = fd_handle_get_fd(handle); + const int add_ret = lttng_dynamic_array_add_element( + &raw_fds, &raw_fd); + + fd_handle_put(handle); + if (add_ret) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } + + if (blocking) { + ret = lttcomm_send_fds_unix_sock(sock, + (const int *) raw_fds.buffer.data, fd_count); + } else { + ret = lttcomm_send_fds_unix_sock_non_block(sock, + (const int *) raw_fds.buffer.data, fd_count); + } + +end: + lttng_dynamic_array_reset(&raw_fds); + return ret; +} + +ssize_t lttcomm_send_payload_view_fds_unix_sock(int sock, + struct lttng_payload_view *view) +{ + return _lttcomm_send_payload_view_fds_unix_sock(sock, view, true); +} + +ssize_t lttcomm_send_payload_view_fds_unix_sock_non_block(int sock, + struct lttng_payload_view *view) +{ + return _lttcomm_send_payload_view_fds_unix_sock(sock, view, false); +} + +/* + * Send a message accompanied by fd(s) over a unix socket. + * Only use for non blocking socket. + * + * Returns the size of data sent, or negative error value. + */ +ssize_t lttcomm_send_fds_unix_sock_non_block(int sock, const int *fds, size_t nb_fd) +{ + struct msghdr msg; + struct cmsghdr *cmptr; + struct iovec iov[1]; + ssize_t ret = -1; + unsigned int sizeof_fds = nb_fd * sizeof(int); + char tmp[CMSG_SPACE(sizeof_fds)]; + char dummy = 0; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(fds); + LTTNG_ASSERT(nb_fd > 0); + + memset(&msg, 0, sizeof(msg)); + memset(tmp, 0, sizeof(tmp)); + + if (nb_fd > LTTCOMM_MAX_SEND_FDS) + return -EINVAL; + + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof_fds); + + cmptr = CMSG_FIRSTHDR(&msg); + if (!cmptr) { + return -1; + } + + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + cmptr->cmsg_len = CMSG_LEN(sizeof_fds); + memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); + /* Sum of the length of all control messages in the buffer: */ + msg.msg_controllen = cmptr->cmsg_len; + + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +retry: + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + if (errno == EINTR) { + goto retry; + } else { + /* + * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. + */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * This can happen in non blocking mode. + * Nothing was sent. + */ + ret = 0; + goto end; + } + + if (errno == EPIPE) { + /* Expected error, pass error to caller */ + DBG3("EPIPE on sendmsg"); + ret = -1; + goto end; + } + + /* Unexpected error */ + PERROR("sendmsg"); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +/* + * Recv a message accompanied by fd(s) from a unix socket. + * + * Returns the size of received data, or negative error value. + * + * Expect at most "nb_fd" file descriptors. Returns the number of fd + * actually received in nb_fd. + */ +ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd) +{ + struct iovec iov[1]; + ssize_t ret = 0; + struct cmsghdr *cmsg; + size_t sizeof_fds = nb_fd * sizeof(int); + +#ifdef __linux__ +/* Account for the struct ucred cmsg in the buffer size */ +#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred)) +#else +#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) +#endif /* __linux__ */ + + char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE]; + struct msghdr msg; + char dummy; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(fds); + LTTNG_ASSERT(nb_fd > 0); + + memset(&msg, 0, sizeof(msg)); + + /* Prepare to receive the structures */ + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + cmsg = (struct cmsghdr *) recv_buf; + cmsg->cmsg_len = CMSG_LEN(sizeof_fds); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + msg.msg_control = cmsg; + msg.msg_controllen = CMSG_LEN(sizeof(recv_buf)); + msg.msg_flags = 0; + +retry: + ret = lttng_recvmsg_nosigpipe(sock, &msg); + if (ret < 0) { + if (errno == EINTR) { + goto retry; + } else { + /* We consider EPIPE and EAGAIN as expected. */ + if (!lttng_opt_quiet && + (errno != EPIPE && errno != EAGAIN)) { + PERROR("recvmsg"); + } + goto end; + } + } + + if (ret != 1) { + fprintf(stderr, "Error: Received %zd bytes, expected %d\n", + ret, 1); + goto end; + } + + if (msg.msg_flags & MSG_CTRUNC) { + fprintf(stderr, "Error: Control message truncated.\n"); + ret = -1; + goto end; + } + + /* + * If the socket was configured with SO_PASSCRED, the kernel will add a + * control message (cmsg) to the ancillary data of the unix socket. We + * need to expect a cmsg of the SCM_CREDENTIALS as the first control + * message. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) { + fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n"); + ret = -1; + goto end; + } + if (cmsg->cmsg_type == SCM_RIGHTS) { + /* + * We found the controle message for file descriptors, + * now copy the fds to the fds ptr and return success. + */ + if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { + fprintf(stderr, "Error: Received %zu bytes of" + "ancillary data for FDs, expected %zu\n", + (size_t) cmsg->cmsg_len, + (size_t) CMSG_LEN(sizeof_fds)); + ret = -1; + goto end; + } + memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); + ret = sizeof_fds; + goto end; + } +#ifdef __linux__ + if (cmsg->cmsg_type == SCM_CREDENTIALS) { + /* + * Expect credentials to be sent when expecting fds even + * if no credential were include in the send(). The + * kernel adds them... + */ + ret = -1; + } +#endif /* __linux__ */ + } +end: + return ret; +} + +static +void close_raw_fd(void *ptr) +{ + const int raw_fd = *((const int *) ptr); + + if (raw_fd >= 0) { + const int ret = close(raw_fd); + + if (ret) { + PERROR("Failed to close file descriptor %d", raw_fd); + } + } +} + +static +enum lttng_error_code add_fds_to_payload(struct lttng_dynamic_array *raw_fds, + struct lttng_payload *payload) +{ + int i; + enum lttng_error_code ret_code = LTTNG_OK; + const int fd_count = lttng_dynamic_array_get_count(raw_fds); + + for (i = 0; i < fd_count; i++) { + int ret; + struct fd_handle *handle; + int *raw_fd = (int *) lttng_dynamic_array_get_element( + raw_fds, i); + + LTTNG_ASSERT(*raw_fd != -1); + + handle = fd_handle_create(*raw_fd); + if (!handle) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + /* FD ownership transferred to the handle. */ + *raw_fd = -1; + + ret = lttng_payload_push_fd_handle(payload, handle); + fd_handle_put(handle); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + +end: + return ret_code; +} + +static +ssize_t _lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd, + struct lttng_payload *payload, bool blocking) +{ + int i = 0; + enum lttng_error_code add_ret; + ssize_t ret; + int default_value = -1; + struct lttng_dynamic_array raw_fds; + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(payload); + LTTNG_ASSERT(nb_fd > 0); + + lttng_dynamic_array_init(&raw_fds, sizeof(int), close_raw_fd); + + for (i = 0; i < nb_fd; i++) { + if (lttng_dynamic_array_add_element(&raw_fds, &default_value)) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + } + + if (blocking) { + ret = lttcomm_recv_fds_unix_sock( + sock, (int *) raw_fds.buffer.data, nb_fd); + } else { + ret = lttcomm_recv_fds_unix_sock_non_block( + sock, (int *) raw_fds.buffer.data, nb_fd); + } + + if (ret <= 0) { + goto end; + } + + add_ret = add_fds_to_payload(&raw_fds, payload); + if (add_ret != LTTNG_OK) { + ret = - (int) add_ret; + goto end; + } + +end: + lttng_dynamic_array_reset(&raw_fds); + return ret; +} + +ssize_t lttcomm_recv_payload_fds_unix_sock(int sock, size_t nb_fd, + struct lttng_payload *payload) +{ + return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, true); +} + +ssize_t lttcomm_recv_payload_fds_unix_sock_non_block(int sock, size_t nb_fd, + struct lttng_payload *payload) +{ + return _lttcomm_recv_payload_fds_unix_sock(sock, nb_fd, payload, false); +} + +/* + * Recv a message accompanied by fd(s) from a non-blocking unix socket. + * Only use with non-blocking sockets. + * + * Returns the size of received data, or negative error value. + * + * Expect at most "nb_fd" file descriptors. + * + * Note that based on our comprehension, partial reception of fds is not + * possible since the FDs are actually in the control message. It is all or + * nothing, still the sender side can send the wrong number of fds. + */ +ssize_t lttcomm_recv_fds_unix_sock_non_block(int sock, int *fds, size_t nb_fd) +{ + struct iovec iov[1]; + ssize_t ret = 0; + struct cmsghdr *cmsg; + size_t sizeof_fds = nb_fd * sizeof(int); + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(fds); + LTTNG_ASSERT(nb_fd > 0); + +#ifdef __linux__ +/* Account for the struct ucred cmsg in the buffer size */ +#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) + CMSG_SPACE(sizeof(struct ucred)) +#else +#define LTTNG_SOCK_RECV_FDS_BUF_SIZE CMSG_SPACE(sizeof_fds) +#endif /* __linux__ */ + + char recv_buf[LTTNG_SOCK_RECV_FDS_BUF_SIZE]; + struct msghdr msg; + char dummy; + + memset(&msg, 0, sizeof(msg)); + + /* Prepare to receive the structures */ + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + cmsg = (struct cmsghdr *) recv_buf; + cmsg->cmsg_len = CMSG_LEN(sizeof_fds); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + msg.msg_control = cmsg; + msg.msg_controllen = CMSG_LEN(sizeof(recv_buf)); + msg.msg_flags = 0; + +retry: + ret = lttng_recvmsg_nosigpipe(sock, &msg); + if (ret < 0) { + if (errno == EINTR) { + goto retry; + } else { + /* + * We consider EPIPE and EAGAIN/EWOULDBLOCK as expected. + */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * This can happen in non blocking mode. + * Nothing was recv. + */ + ret = 0; + goto end; + } + + if (errno == EPIPE) { + /* Expected error, pass error to caller */ + DBG3("EPIPE on recvmsg"); + ret = -1; + goto end; + } + + /* Unexpected error */ + PERROR("recvmsg"); + ret = -1; + goto end; + } + } + + if (ret != 1) { + fprintf(stderr, "Error: Received %zd bytes, expected %d\n", + ret, 1); + goto end; + } + + if (msg.msg_flags & MSG_CTRUNC) { + fprintf(stderr, "Error: Control message truncated.\n"); + ret = -1; + goto end; + } + + /* + * If the socket was configured with SO_PASSCRED, the kernel will add a + * control message (cmsg) to the ancillary data of the unix socket. We + * need to expect a cmsg of the SCM_CREDENTIALS as the first control + * message. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) { + fprintf(stderr, "Error: The socket needs to be of type SOL_SOCKET\n"); + ret = -1; + goto end; + } + if (cmsg->cmsg_type == SCM_RIGHTS) { + /* + * We found the controle message for file descriptors, + * now copy the fds to the fds ptr and return success. + */ + if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { + fprintf(stderr, "Error: Received %zu bytes of" + "ancillary data for FDs, expected %zu\n", + (size_t) cmsg->cmsg_len, + (size_t) CMSG_LEN(sizeof_fds)); + ret = -1; + goto end; + } + memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); + ret = sizeof_fds; + goto end; + } +#ifdef __linux__ + if (cmsg->cmsg_type == SCM_CREDENTIALS) { + /* + * Expect credentials to be sent when expecting fds even + * if no credential were include in the send(). The + * kernel adds them... + */ + ret = -1; + } +#endif /* __linux__ */ + } +end: + return ret; +} + +/* + * Send a message with credentials over a unix socket. + * + * Returns the size of data sent, or negative error value. + */ +ssize_t lttcomm_send_creds_unix_sock(int sock, const void *buf, size_t len) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret = -1; +#if defined(__linux__) || defined(__CYGWIN__) + struct cmsghdr *cmptr; + size_t sizeof_cred = sizeof(lttng_sock_cred); + char anc_buf[CMSG_SPACE(sizeof_cred)]; + lttng_sock_cred *creds; + + memset(anc_buf, 0, CMSG_SPACE(sizeof_cred) * sizeof(char)); +#endif /* __linux__, __CYGWIN__ */ + + memset(&msg, 0, sizeof(msg)); + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + + iov[0].iov_base = (void *) buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +#if defined(__linux__) || defined(__CYGWIN__) + msg.msg_control = (caddr_t) anc_buf; + msg.msg_controllen = CMSG_LEN(sizeof_cred); + + cmptr = CMSG_FIRSTHDR(&msg); + if (!cmptr) { + return -1; + } + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = LTTNG_SOCK_CREDS; + cmptr->cmsg_len = CMSG_LEN(sizeof_cred); + + creds = (lttng_sock_cred*) CMSG_DATA(cmptr); + + LTTNG_SOCK_SET_UID_CRED(creds, geteuid()); + LTTNG_SOCK_SET_GID_CRED(creds, getegid()); + LTTNG_SOCK_SET_PID_CRED(creds, getpid()); +#endif /* __linux__, __CYGWIN__ */ + + do { + ret = sendmsg(sock, &msg, 0); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + /* + * Only warn about EPIPE when quiet mode is deactivated. + * We consider EPIPE as expected. + */ + if (errno != EPIPE || !lttng_opt_quiet) { + PERROR("sendmsg"); + } + } + return ret; +} + +/* + * Recv a message accompanied with credentials from a unix socket. + * + * Returns the size of received data, or negative error value. + */ +ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, + lttng_sock_cred *creds) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t ret; + size_t len_last; +#if defined(__linux__) || defined(__CYGWIN__) + struct cmsghdr *cmptr; + size_t sizeof_cred = sizeof(lttng_sock_cred); + char anc_buf[CMSG_SPACE(sizeof_cred)]; +#endif /* __linux__, __CYGWIN__ */ + + LTTNG_ASSERT(sock); + LTTNG_ASSERT(buf); + LTTNG_ASSERT(len > 0); + LTTNG_ASSERT(creds); + + memset(&msg, 0, sizeof(msg)); + + /* Prepare to receive the structures */ + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +#if defined(__linux__) || defined(__CYGWIN__) + msg.msg_control = anc_buf; + msg.msg_controllen = sizeof(anc_buf); +#endif /* __linux__, __CYGWIN__ */ + + do { + len_last = iov[0].iov_len; + ret = recvmsg(sock, &msg, 0); + if (ret > 0) { + iov[0].iov_base = (char *) iov[0].iov_base + ret; + iov[0].iov_len -= ret; + LTTNG_ASSERT(ret <= len_last); + } + } while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR)); + if (ret < 0) { + PERROR("recvmsg fds"); + goto end; + } else if (ret > 0) { + ret = len; + } + /* Else ret = 0 meaning an orderly shutdown. */ + +#if defined(__linux__) || defined(__CYGWIN__) + if (msg.msg_flags & MSG_CTRUNC) { + fprintf(stderr, "Error: Control message truncated.\n"); + ret = -1; + goto end; + } + + cmptr = CMSG_FIRSTHDR(&msg); + if (cmptr == NULL) { + fprintf(stderr, "Error: Invalid control message header\n"); + ret = -1; + goto end; + } + + if (cmptr->cmsg_level != SOL_SOCKET || + cmptr->cmsg_type != LTTNG_SOCK_CREDS) { + fprintf(stderr, "Didn't received any credentials\n"); + ret = -1; + goto end; + } + + if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) { + fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", + (size_t) cmptr->cmsg_len, (size_t) CMSG_LEN(sizeof_cred)); + ret = -1; + goto end; + } + + memcpy(creds, CMSG_DATA(cmptr), sizeof_cred); +#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__)) + if (lttng_get_unix_socket_peer_creds(sock, creds)) { + fprintf(stderr, "ARG\n"); + ret = -1; + goto end; + } +#else +#error "Please implement credential support for your OS." +#endif /* __linux__, __CYGWIN__ */ + +end: + return ret; +} + +/* + * Set socket option to use credentials passing. + */ +#if defined(__linux__) || defined(__CYGWIN__) +int lttcomm_setsockopt_creds_unix_sock(int sock) +{ + int ret, on = 1; + + /* Set socket for credentials retrieval */ + ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + if (ret < 0) { + PERROR("setsockopt creds unix sock"); + } + return ret; +} +#elif (defined(__FreeBSD__) || defined(__sun__) || defined(__APPLE__)) +int lttcomm_setsockopt_creds_unix_sock(int sock) +{ + return 0; +} +#else +#error "Please implement credential support for your OS." +#endif /* __linux__ */ diff --git a/src/common/uri.c b/src/common/uri.c deleted file mode 100644 index fef21b38d..000000000 --- a/src/common/uri.c +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#include - -#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://[]... - * ^ - */ - if (*purl == '[') { - /* Address begins after '[' */ - addr_b = purl + 1; - addr_e = strchr(addr_b, ']'); - if (addr_e == NULL || addr_b == addr_e) { - ERR("Broken IPv6 address %s", addr_b); - goto free_error; - } - - /* Moving parsed URL pointer after the final bracket ']' */ - purl = addr_e + 1; - - /* - * The closing bracket must be followed by a seperator or NULL char. - */ - if (strchr(seps, *purl) == NULL) { - ERR("Unknown symbol after IPv6 address: %s", purl); - goto free_error; - } - } else { - purl = strpbrk_or_eos(purl, seps); - addr_e = purl; - } - - /* Check if we at least have a char for the addr or hostname. */ - if (addr_b == addr_e) { - ERR("No address or hostname detected."); - goto free_error; - } - - addr_f = utils_strdupdelim(addr_b, addr_e); - if (addr_f == NULL) { - goto free_error; - } - - /* - * Detect PORT after address. The net/net6 protocol allows up to two port - * so we can define the control and data port. - */ - while (*purl == ':') { - const char *port_b, *port_e; - char *port_f; - - /* Update pass counter */ - i++; - - /* - * Maximum of two ports is possible if P_NET/NET6. Bigger than that, - * two much stuff. - */ - if ((i == 2 && (proto->code != P_NET && proto->code != P_NET6)) - || i > 2) { - break; - } - - /* - * Move parsed URL to port value. - * proto://addr_host:PORT1:PORT2/foo/bar - * ^ - */ - ++purl; - port_b = purl; - purl = strpbrk_or_eos(purl, seps); - port_e = purl; - - if (port_b != port_e) { - int port; - - port_f = utils_strdupdelim(port_b, port_e); - if (port_f == NULL) { - goto free_error; - } - - port = atoi(port_f); - if (port > 0xffff || port <= 0x0) { - ERR("Invalid port number %d", port); - free(port_f); - goto free_error; - } - free(port_f); - - if (i == 1) { - ctrl_port = port; - } else { - data_port = port; - } - } - }; - - /* Check for a valid subdir or trailing garbage */ - if (*purl == '/') { - /* - * Move to subdir value. - * proto://addr_host:PORT1:PORT2/foo/bar - * ^ - */ - ++purl; - subdir_b = purl; - } else if (*purl != '\0') { - ERR("Trailing characters not recognized: %s", purl); - goto free_error; - } - - /* We have enough valid information to create URI(s) object */ - - /* Copy generic information */ - tmp_uris[0].port = ctrl_port; - - /* Copy subdirectory if one. */ - if (subdir_b) { - strncpy(tmp_uris[0].subdir, subdir_b, sizeof(tmp_uris[0].subdir)); - tmp_uris[0].subdir[sizeof(tmp_uris[0].subdir) - 1] = '\0'; - } - - switch (proto->code) { - case P_NET: - ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4, - sizeof(tmp_uris[0].dst.ipv4)); - if (ret < 0) { - goto free_error; - } - - memcpy(tmp_uris[1].dst.ipv4, tmp_uris[0].dst.ipv4, sizeof(tmp_uris[1].dst.ipv4)); - - tmp_uris[1].dtype = proto->dtype; - tmp_uris[1].proto = proto->type; - tmp_uris[1].port = data_port; - break; - case P_NET6: - ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6, - sizeof(tmp_uris[0].dst.ipv6)); - if (ret < 0) { - goto free_error; - } - - memcpy(tmp_uris[1].dst.ipv6, tmp_uris[0].dst.ipv6, sizeof(tmp_uris[1].dst.ipv6)); - - tmp_uris[1].dtype = proto->dtype; - tmp_uris[1].proto = proto->type; - tmp_uris[1].port = data_port; - break; - case P_TCP: - ret = set_ip_address(addr_f, AF_INET, tmp_uris[0].dst.ipv4, - sizeof(tmp_uris[0].dst.ipv4)); - if (ret < 0) { - goto free_error; - } - break; - case P_TCP6: - ret = set_ip_address(addr_f, AF_INET6, tmp_uris[0].dst.ipv6, - sizeof(tmp_uris[0].dst.ipv6)); - if (ret < 0) { - goto free_error; - } - break; - default: - goto free_error; - } - -end: - DBG3("URI dtype: %d, proto: %d, host: %s, subdir: %s, ctrl: %d, data: %d", - proto->dtype, proto->type, (addr_f == NULL) ? "" : addr_f, - (subdir_b == NULL) ? "" : subdir_b, ctrl_port, data_port); - - free(addr_f); - - *uris = tmp_uris; - LTTNG_ASSERT(size == 1 || size == 2); - return size; - -free_error: - free(addr_f); - free(tmp_uris); -error: - return -1; -} - -/* - * Parse a string URL and creates URI(s) returning the size of the populated - * array. - */ -ssize_t uri_parse_str_urls(const char *ctrl_url, const char *data_url, - struct lttng_uri **uris) -{ - unsigned int equal = 1, idx = 0; - /* Add the "file://" size to the URL maximum size */ - char url[PATH_MAX + 7]; - ssize_t ctrl_uri_count = 0, data_uri_count = 0, uri_count; - struct lttng_uri *ctrl_uris = NULL, *data_uris = NULL; - struct lttng_uri *tmp_uris = NULL; - - /* No URL(s) is allowed. This means that the consumer will be disabled. */ - if (ctrl_url == NULL && data_url == NULL) { - return 0; - } - - /* Check if URLs are equal and if so, only use the control URL */ - if ((ctrl_url && *ctrl_url != '\0') && (data_url && *data_url != '\0')) { - equal = !strcmp(ctrl_url, data_url); - } - - /* - * Since we allow the str_url to be a full local filesystem path, we are - * going to create a valid file:// URL if it's the case. - * - * Check if first character is a '/' or else reject the URL. - */ - if (ctrl_url && ctrl_url[0] == '/') { - int ret; - - ret = snprintf(url, sizeof(url), "file://%s", ctrl_url); - if (ret < 0) { - PERROR("snprintf file url"); - goto parse_error; - } else if (ret >= sizeof(url)) { - PERROR("snprintf file url is too long"); - goto parse_error; - - } - ctrl_url = url; - } - - /* Parse the control URL if there is one */ - if (ctrl_url && *ctrl_url != '\0') { - ctrl_uri_count = uri_parse(ctrl_url, &ctrl_uris); - if (ctrl_uri_count < 1) { - ERR("Unable to parse the URL %s", ctrl_url); - goto parse_error; - } - - /* 1 and 2 are the only expected values on success. */ - LTTNG_ASSERT(ctrl_uri_count == 1 || ctrl_uri_count == 2); - - /* At this point, we know there is at least one URI in the array */ - set_default_uri_attr(&ctrl_uris[0], LTTNG_STREAM_CONTROL); - - if (ctrl_uris[0].dtype == LTTNG_DST_PATH && - (data_url && *data_url != '\0')) { - ERR("Cannot have a data URL when destination is file://"); - goto error; - } - - /* URL are not equal but the control URL uses a net:// protocol */ - if (ctrl_uri_count == 2) { - if (!equal) { - ERR("Control URL uses the net:// protocol and the data URL is " - "different. Not allowed."); - goto error; - } else { - set_default_uri_attr(&ctrl_uris[1], LTTNG_STREAM_DATA); - /* - * The data_url and ctrl_url are equal and the ctrl_url - * contains a net:// protocol so we just skip the data part. - */ - data_url = NULL; - } - } - } - - if (data_url && *data_url != '\0') { - int ret; - - /* We have to parse the data URL in this case */ - data_uri_count = uri_parse(data_url, &data_uris); - if (data_uri_count < 1) { - ERR("Unable to parse the URL %s", data_url); - goto error; - } else if (data_uri_count == 2) { - ERR("Data URL can not be set with the net[4|6]:// protocol"); - goto error; - } else { - /* 1 and 2 are the only expected values on success. */ - LTTNG_ASSERT(data_uri_count == 1); - } - - set_default_uri_attr(&data_uris[0], LTTNG_STREAM_DATA); - - if (ctrl_uris) { - ret = compare_destination(&ctrl_uris[0], &data_uris[0]); - if (ret != 0) { - ERR("Control and data destination mismatch"); - goto error; - } - } - } - - /* Compute total size. */ - uri_count = ctrl_uri_count + data_uri_count; - if (uri_count <= 0) { - goto error; - } - - tmp_uris = zmalloc(sizeof(struct lttng_uri) * uri_count); - if (tmp_uris == NULL) { - PERROR("zmalloc uris"); - goto error; - } - - if (ctrl_uris) { - /* It's possible the control URIs array contains more than one URI */ - memcpy(tmp_uris, ctrl_uris, sizeof(struct lttng_uri) * ctrl_uri_count); - ++idx; - free(ctrl_uris); - } - - if (data_uris) { - memcpy(&tmp_uris[idx], data_uris, sizeof(struct lttng_uri)); - free(data_uris); - } - - *uris = tmp_uris; - - return uri_count; - -error: - free(ctrl_uris); - free(data_uris); - free(tmp_uris); -parse_error: - return -1; -} diff --git a/src/common/uri.cpp b/src/common/uri.cpp new file mode 100644 index 000000000..b1c2c63d0 --- /dev/null +++ b/src/common/uri.cpp @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2012 David Goulet + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +#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://[]... + * ^ + */ + 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; +} diff --git a/src/common/uri.h b/src/common/uri.h index a270db9d9..3a02dda5c 100644 --- a/src/common/uri.h +++ b/src/common/uri.h @@ -44,6 +44,7 @@ enum lttng_stream_type { * should be ignored. */ enum lttng_proto_type { + LTTNG_PROTO_TYPE_NONE = 0, LTTNG_TCP = 1, /* * UDP protocol is not supported for now. diff --git a/src/common/userspace-probe.c b/src/common/userspace-probe.c deleted file mode 100644 index 084fec5c4..000000000 --- a/src/common/userspace-probe.c +++ /dev/null @@ -1,2260 +0,0 @@ -/* - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "lttng/lttng-error.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -int lttng_userspace_probe_location_function_set_binary_fd_handle( - struct lttng_userspace_probe_location *location, - struct fd_handle *binary_fd_handle); - -static -int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( - struct lttng_userspace_probe_location *location, - struct fd_handle *binary_fd_handle); - -static -enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize( - const struct lttng_userspace_probe_location_lookup_method - *method, - struct mi_writer *writer); - -static -enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize( - const struct lttng_userspace_probe_location *location, - struct mi_writer *writer); - -static -enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize( - const struct lttng_userspace_probe_location *location, - struct mi_writer *writer); - -enum lttng_userspace_probe_location_lookup_method_type -lttng_userspace_probe_location_lookup_method_get_type( - const struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - return lookup_method ? lookup_method->type : - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_UNKNOWN; -} - -void lttng_userspace_probe_location_lookup_method_destroy( - struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - if (!lookup_method){ - return; - } - - free(lookup_method); -} - -struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_lookup_method_function_elf_create(void) -{ - struct lttng_userspace_probe_location_lookup_method *ret = NULL; - struct lttng_userspace_probe_location_lookup_method_elf *elf_method; - - elf_method = zmalloc(sizeof(*elf_method)); - if (!elf_method) { - PERROR("zmalloc"); - goto end; - } - - ret = &elf_method->parent; - ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; -end: - return ret; -} - -struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(void) -{ - struct lttng_userspace_probe_location_lookup_method *ret = NULL; - struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; - - sdt_method = zmalloc(sizeof(*sdt_method)); - if (!sdt_method) { - PERROR("zmalloc"); - goto end; - } - - ret = &sdt_method->parent; - ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; -end: - return ret; -} - -enum lttng_userspace_probe_location_type lttng_userspace_probe_location_get_type( - const struct lttng_userspace_probe_location *location) -{ - return location ? location->type : - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN; -} - -static -void lttng_userspace_probe_location_function_destroy( - struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location_function *location_function = NULL; - - LTTNG_ASSERT(location); - - location_function = container_of(location, - struct lttng_userspace_probe_location_function, parent); - - LTTNG_ASSERT(location_function); - - free(location_function->function_name); - free(location_function->binary_path); - fd_handle_put(location_function->binary_fd_handle); - free(location); -} - -static -void lttng_userspace_probe_location_tracepoint_destroy( - struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location_tracepoint *location_tracepoint = NULL; - - LTTNG_ASSERT(location); - - location_tracepoint = container_of(location, - struct lttng_userspace_probe_location_tracepoint, - parent); - - LTTNG_ASSERT(location_tracepoint); - - free(location_tracepoint->probe_name); - free(location_tracepoint->provider_name); - free(location_tracepoint->binary_path); - fd_handle_put(location_tracepoint->binary_fd_handle); - free(location); -} - -void lttng_userspace_probe_location_destroy( - struct lttng_userspace_probe_location *location) -{ - if (!location) { - return; - } - - lttng_userspace_probe_location_lookup_method_destroy( - location->lookup_method); - - switch (location->type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - lttng_userspace_probe_location_function_destroy(location); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - lttng_userspace_probe_location_tracepoint_destroy(location); - break; - default: - abort(); - } -} - -/* Compare two file descriptors based on their inode and device numbers. */ -static bool fd_is_equal(int a, int b) -{ - int ret; - bool is_equal = false; - struct stat a_stat, b_stat; - - if (a < 0 && b >= 0) { - goto end; - } - - if (b < 0 && a >= 0) { - goto end; - } - - if (a < 0 && b < 0) { - if (a == -1 && b == -1) { - is_equal = true; - goto end; - } - - /* Invalid state, abort. */ - abort(); - } - - /* Both are valid file descriptors. */ - ret = fstat(a, &a_stat); - if (ret) { - PERROR("Failed to fstat userspace probe location binary fd %d", - a); - goto end; - } - - ret = fstat(b, &b_stat); - if (ret) { - PERROR("Failed to fstat userspace probe location binary fd %d", - b); - goto end; - } - - is_equal = (a_stat.st_ino == b_stat.st_ino) && - (a_stat.st_dev == b_stat.st_dev); - -end: - return is_equal; -} - -static unsigned long lttng_userspace_probe_location_function_hash( - const struct lttng_userspace_probe_location *location) -{ - unsigned long hash = hash_key_ulong( - (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION, - lttng_ht_seed); - struct lttng_userspace_probe_location_function *function_location = - container_of(location, typeof(*function_location), - parent); - - hash ^= hash_key_str(function_location->function_name, lttng_ht_seed); - hash ^= hash_key_str(function_location->binary_path, lttng_ht_seed); - /* - * No need to hash on the fd. Worst comes to worse, - * the equal function will discriminate. - */ - return hash; -} - -static bool lttng_userspace_probe_location_function_is_equal( - const struct lttng_userspace_probe_location *_a, - const struct lttng_userspace_probe_location *_b) -{ - bool is_equal = false; - struct lttng_userspace_probe_location_function *a, *b; - - a = container_of(_a, struct lttng_userspace_probe_location_function, - parent); - b = container_of(_b, struct lttng_userspace_probe_location_function, - parent); - - if (a->instrumentation_type != b->instrumentation_type) { - goto end; - } - - LTTNG_ASSERT(a->function_name); - LTTNG_ASSERT(b->function_name); - if (strcmp(a->function_name, b->function_name)) { - goto end; - } - - LTTNG_ASSERT(a->binary_path); - LTTNG_ASSERT(b->binary_path); - if (strcmp(a->binary_path, b->binary_path)) { - goto end; - } - - is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1, - b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1); -end: - return is_equal; -} - -static struct lttng_userspace_probe_location * -lttng_userspace_probe_location_function_create_no_check(const char *binary_path, - const char *function_name, - struct lttng_userspace_probe_location_lookup_method *lookup_method, - bool open_binary) -{ - int binary_fd = -1; - struct fd_handle *binary_fd_handle = NULL; - char *function_name_copy = NULL, *binary_path_copy = NULL; - struct lttng_userspace_probe_location *ret = NULL; - struct lttng_userspace_probe_location_function *location; - - if (open_binary) { - binary_fd = open(binary_path, O_RDONLY); - if (binary_fd < 0) { - PERROR("Error opening the binary"); - goto error; - } - - binary_fd_handle = fd_handle_create(binary_fd); - if (!binary_fd) { - goto error; - } - - /* Ownership transferred to fd_handle. */ - binary_fd = -1; - } - - function_name_copy = lttng_strndup(function_name, LTTNG_SYMBOL_NAME_LEN); - if (!function_name_copy) { - PERROR("Error duplicating the function name"); - goto error; - } - - binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); - if (!binary_path_copy) { - PERROR("Error duplicating the function name"); - goto error; - } - - location = zmalloc(sizeof(*location)); - if (!location) { - PERROR("Error allocating userspace probe location"); - goto error; - } - - location->function_name = function_name_copy; - location->binary_path = binary_path_copy; - location->binary_fd_handle = binary_fd_handle; - binary_fd_handle = NULL; - location->instrumentation_type = - LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY; - - ret = &location->parent; - ret->lookup_method = lookup_method; - ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION; - ret->equal = lttng_userspace_probe_location_function_is_equal; - ret->hash = lttng_userspace_probe_location_function_hash; - goto end; - -error: - free(function_name_copy); - free(binary_path_copy); - if (binary_fd >= 0) { - if (close(binary_fd)) { - PERROR("Error closing binary fd in error path"); - } - } - fd_handle_put(binary_fd_handle); -end: - return ret; -} - -static unsigned long lttng_userspace_probe_location_tracepoint_hash( - const struct lttng_userspace_probe_location *location) -{ - unsigned long hash = hash_key_ulong( - (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT, - lttng_ht_seed); - struct lttng_userspace_probe_location_tracepoint *tp_location = - container_of(location, typeof(*tp_location), parent); - - hash ^= hash_key_str(tp_location->probe_name, lttng_ht_seed); - hash ^= hash_key_str(tp_location->provider_name, lttng_ht_seed); - hash ^= hash_key_str(tp_location->binary_path, lttng_ht_seed); - /* - * No need to hash on the fd. Worst comes to worse, - * the equal function will discriminate. - */ - return hash; -} - -static bool lttng_userspace_probe_location_tracepoint_is_equal( - const struct lttng_userspace_probe_location *_a, - const struct lttng_userspace_probe_location *_b) -{ - bool is_equal = false; - struct lttng_userspace_probe_location_tracepoint *a, *b; - - a = container_of(_a, struct lttng_userspace_probe_location_tracepoint, - parent); - b = container_of(_b, struct lttng_userspace_probe_location_tracepoint, - parent); - - LTTNG_ASSERT(a->probe_name); - LTTNG_ASSERT(b->probe_name); - if (strcmp(a->probe_name, b->probe_name)) { - goto end; - } - - LTTNG_ASSERT(a->provider_name); - LTTNG_ASSERT(b->provider_name); - if (strcmp(a->provider_name, b->provider_name)) { - goto end; - } - - LTTNG_ASSERT(a->binary_path); - LTTNG_ASSERT(b->binary_path); - if (strcmp(a->binary_path, b->binary_path)) { - goto end; - } - - is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1, - b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1); - -end: - return is_equal; -} - -static struct lttng_userspace_probe_location * -lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path, - const char *provider_name, const char *probe_name, - struct lttng_userspace_probe_location_lookup_method *lookup_method, - bool open_binary) -{ - int binary_fd = -1; - struct fd_handle *binary_fd_handle = NULL; - char *probe_name_copy = NULL; - char *provider_name_copy = NULL; - char *binary_path_copy = NULL; - struct lttng_userspace_probe_location *ret = NULL; - struct lttng_userspace_probe_location_tracepoint *location; - - if (open_binary) { - binary_fd = open(binary_path, O_RDONLY); - if (binary_fd < 0) { - PERROR("open"); - goto error; - } - - binary_fd_handle = fd_handle_create(binary_fd); - if (!binary_fd) { - goto error; - } - - /* Ownership transferred to fd_handle. */ - binary_fd = -1; - } - - probe_name_copy = lttng_strndup(probe_name, LTTNG_SYMBOL_NAME_LEN); - if (!probe_name_copy) { - PERROR("lttng_strndup"); - goto error; - } - - provider_name_copy = lttng_strndup(provider_name, LTTNG_SYMBOL_NAME_LEN); - if (!provider_name_copy) { - PERROR("lttng_strndup"); - goto error; - } - - binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); - if (!binary_path_copy) { - PERROR("lttng_strndup"); - goto error; - } - - location = zmalloc(sizeof(*location)); - if (!location) { - PERROR("zmalloc"); - goto error; - } - - location->probe_name = probe_name_copy; - location->provider_name = provider_name_copy; - location->binary_path = binary_path_copy; - location->binary_fd_handle = binary_fd_handle; - binary_fd_handle = NULL; - - ret = &location->parent; - ret->lookup_method = lookup_method; - ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT; - ret->equal = lttng_userspace_probe_location_tracepoint_is_equal; - ret->hash = lttng_userspace_probe_location_tracepoint_hash; - goto end; - -error: - free(probe_name_copy); - free(provider_name_copy); - free(binary_path_copy); - if (binary_fd >= 0) { - if (close(binary_fd)) { - PERROR("Error closing binary fd in error path"); - } - } - fd_handle_put(binary_fd_handle); -end: - return ret; -} - -struct lttng_userspace_probe_location * -lttng_userspace_probe_location_function_create(const char *binary_path, - const char *function_name, - struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - struct lttng_userspace_probe_location *ret = NULL; - - if (!binary_path || !function_name) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - switch (lttng_userspace_probe_location_lookup_method_get_type( - lookup_method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - break; - default: - /* Invalid probe location lookup method. */ - goto end; - } - - ret = lttng_userspace_probe_location_function_create_no_check( - binary_path, function_name, lookup_method, true); -end: - return ret; -} - -struct lttng_userspace_probe_location * -lttng_userspace_probe_location_tracepoint_create(const char *binary_path, - const char *provider_name, const char *probe_name, - struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - struct lttng_userspace_probe_location *ret = NULL; - - if (!binary_path || !probe_name || !provider_name) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - switch (lttng_userspace_probe_location_lookup_method_get_type( - lookup_method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - break; - default: - /* Invalid probe location lookup method. */ - goto end; - } - - ret = lttng_userspace_probe_location_tracepoint_create_no_check( - binary_path, provider_name, probe_name, lookup_method, true); -end: - return ret; -} - -static struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_lookup_method_function_elf_copy( - const struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - struct lttng_userspace_probe_location_lookup_method *parent = NULL; - struct lttng_userspace_probe_location_lookup_method_elf *elf_method; - - LTTNG_ASSERT(lookup_method); - LTTNG_ASSERT(lookup_method->type == - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF); - - elf_method = zmalloc(sizeof(*elf_method)); - if (!elf_method) { - PERROR("Error allocating ELF userspace probe lookup method"); - goto error; - } - - elf_method->parent.type = lookup_method->type; - parent = &elf_method->parent; - - goto end; -error: - parent = NULL; -end: - return parent; -} - -static struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( - struct lttng_userspace_probe_location_lookup_method *lookup_method) -{ - struct lttng_userspace_probe_location_lookup_method *parent = NULL; - struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; - - LTTNG_ASSERT(lookup_method); - LTTNG_ASSERT(lookup_method->type == - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT); - - sdt_method = zmalloc(sizeof(*sdt_method)); - if (!sdt_method) { - PERROR("zmalloc"); - goto error; - } - - sdt_method->parent.type = lookup_method->type; - parent = &sdt_method->parent; - - goto end; - -error: - parent = NULL; -end: - return parent; -} - -static struct lttng_userspace_probe_location * -lttng_userspace_probe_location_function_copy( - const struct lttng_userspace_probe_location *location) -{ - enum lttng_userspace_probe_location_lookup_method_type lookup_type; - struct lttng_userspace_probe_location *new_location = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; - const char *binary_path = NULL; - const char *function_name = NULL; - struct lttng_userspace_probe_location_function *function_location; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); - function_location = container_of( - location, typeof(*function_location), parent); - - /* Get probe location fields */ - binary_path = lttng_userspace_probe_location_function_get_binary_path(location); - if (!binary_path) { - ERR("Userspace probe binary path is NULL"); - goto error; - } - - function_name = lttng_userspace_probe_location_function_get_function_name(location); - if (!function_name) { - ERR("Userspace probe function name is NULL"); - goto error; - } - - /* - * Duplicate probe location method fields - */ - lookup_type = lttng_userspace_probe_location_lookup_method_get_type( - location->lookup_method); - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - lookup_method = - lttng_userspace_probe_location_lookup_method_function_elf_copy( - location->lookup_method); - if (!lookup_method) { - goto error; - } - break; - default: - /* Invalid probe location lookup method. */ - goto error; - } - - /* Create the probe_location */ - new_location = lttng_userspace_probe_location_function_create_no_check( - binary_path, function_name, lookup_method, false); - if (!new_location) { - goto destroy_lookup_method; - } - - /* Set the duplicated fd to the new probe_location */ - if (lttng_userspace_probe_location_function_set_binary_fd_handle(new_location, - function_location->binary_fd_handle) < 0) { - goto destroy_probe_location; - } - - goto end; - -destroy_probe_location: - lttng_userspace_probe_location_destroy(new_location); -destroy_lookup_method: - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); -error: - new_location = NULL; -end: - return new_location; -} - -static struct lttng_userspace_probe_location * -lttng_userspace_probe_location_tracepoint_copy( - const struct lttng_userspace_probe_location *location) -{ - enum lttng_userspace_probe_location_lookup_method_type lookup_type; - struct lttng_userspace_probe_location *new_location = NULL; - struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; - const char *binary_path = NULL; - const char *probe_name = NULL; - const char *provider_name = NULL; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); - tracepoint_location = container_of( - location, typeof(*tracepoint_location), parent); - - /* Get probe location fields */ - binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location); - if (!binary_path) { - ERR("Userspace probe binary path is NULL"); - goto error; - } - - probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); - if (!probe_name) { - ERR("Userspace probe probe name is NULL"); - goto error; - } - - provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); - if (!provider_name) { - ERR("Userspace probe provider name is NULL"); - goto error; - } - - /* - * Duplicate probe location method fields - */ - lookup_type = lttng_userspace_probe_location_lookup_method_get_type( - location->lookup_method); - switch (lookup_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - lookup_method = - lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( - location->lookup_method); - if (!lookup_method) { - goto error; - } - break; - default: - /* Invalid probe location lookup method. */ - goto error; - } - - /* Create the probe_location */ - new_location = lttng_userspace_probe_location_tracepoint_create_no_check( - binary_path, provider_name, probe_name, lookup_method, false); - if (!new_location) { - goto destroy_lookup_method; - } - - /* Set the duplicated fd to the new probe_location */ - if (lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(new_location, - tracepoint_location->binary_fd_handle) < 0) { - goto destroy_probe_location; - } - - goto end; - -destroy_probe_location: - lttng_userspace_probe_location_destroy(new_location); -destroy_lookup_method: - lttng_userspace_probe_location_lookup_method_destroy(lookup_method); -error: - new_location = NULL; -end: - return new_location; -} - -const char *lttng_userspace_probe_location_function_get_binary_path( - const struct lttng_userspace_probe_location *location) -{ - const char *ret = NULL; - struct lttng_userspace_probe_location_function *function_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, - parent); - ret = function_location->binary_path; -end: - return ret; -} - -const char *lttng_userspace_probe_location_tracepoint_get_binary_path( - const struct lttng_userspace_probe_location *location) -{ - const char *ret = NULL; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - tracepoint_location = container_of(location, - struct lttng_userspace_probe_location_tracepoint, - parent); - ret = tracepoint_location->binary_path; -end: - return ret; -} - -const char *lttng_userspace_probe_location_function_get_function_name( - const struct lttng_userspace_probe_location *location) -{ - const char *ret = NULL; - struct lttng_userspace_probe_location_function *function_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, parent); - ret = function_location->function_name; -end: - return ret; -} - -const char *lttng_userspace_probe_location_tracepoint_get_probe_name( - const struct lttng_userspace_probe_location *location) -{ - const char *ret = NULL; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - tracepoint_location = container_of(location, - struct lttng_userspace_probe_location_tracepoint, parent); - ret = tracepoint_location->probe_name; -end: - return ret; -} - -const char *lttng_userspace_probe_location_tracepoint_get_provider_name( - const struct lttng_userspace_probe_location *location) -{ - const char *ret = NULL; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - tracepoint_location = container_of(location, - struct lttng_userspace_probe_location_tracepoint, parent); - ret = tracepoint_location->provider_name; -end: - return ret; -} - -int lttng_userspace_probe_location_function_get_binary_fd( - const struct lttng_userspace_probe_location *location) -{ - int ret = -1; - struct lttng_userspace_probe_location_function *function_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, parent); - ret = function_location->binary_fd_handle ? - fd_handle_get_fd(function_location->binary_fd_handle) : -1; -end: - return ret; -} - -enum lttng_userspace_probe_location_function_instrumentation_type -lttng_userspace_probe_location_function_get_instrumentation_type( - const struct lttng_userspace_probe_location *location) -{ - enum lttng_userspace_probe_location_function_instrumentation_type type; - struct lttng_userspace_probe_location_function *function_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN; - goto end; - } - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, parent); - type = function_location->instrumentation_type; -end: - return type; -} - -enum lttng_userspace_probe_location_status -lttng_userspace_probe_location_function_set_instrumentation_type( - const struct lttng_userspace_probe_location *location, - enum lttng_userspace_probe_location_function_instrumentation_type instrumentation_type) -{ - enum lttng_userspace_probe_location_status status = - LTTNG_USERSPACE_PROBE_LOCATION_STATUS_OK; - struct lttng_userspace_probe_location_function *function_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION || - instrumentation_type != - LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID; - goto end; - } - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, parent); - function_location->instrumentation_type = instrumentation_type; -end: - return status; -} - -int lttng_userspace_probe_location_tracepoint_get_binary_fd( - const struct lttng_userspace_probe_location *location) -{ - int ret = -1; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - tracepoint_location = container_of(location, - struct lttng_userspace_probe_location_tracepoint, parent); - ret = tracepoint_location->binary_fd_handle ? - fd_handle_get_fd(tracepoint_location->binary_fd_handle) : -1; -end: - return ret; -} - -static struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_function_get_lookup_method( - const struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location_lookup_method *ret = NULL; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - ret = location->lookup_method; -end: - return ret; -} - -static struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_tracepoint_get_lookup_method( - const struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location_lookup_method *ret = NULL; - - if (!location || lttng_userspace_probe_location_get_type(location) != - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - goto end; - } - - ret = location->lookup_method; -end: - return ret; -} - -const struct lttng_userspace_probe_location_lookup_method * -lttng_userspace_probe_location_get_lookup_method( - const struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location_lookup_method *ret = NULL; - - LTTNG_ASSERT(location); - switch (location->type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - ret = lttng_userspace_probe_location_function_get_lookup_method( - location); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - ret = lttng_userspace_probe_location_tracepoint_get_lookup_method( - location); - break; - default: - ERR("Unknowned lookup method."); - break; - } - return ret; -} - -static -int lttng_userspace_probe_location_lookup_method_serialize( - struct lttng_userspace_probe_location_lookup_method *method, - struct lttng_payload *payload) -{ - int ret; - struct lttng_userspace_probe_location_lookup_method_comm - lookup_method_comm; - - lookup_method_comm.type = (int8_t) (method ? method->type : - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT); - if (payload) { - ret = lttng_dynamic_buffer_append(&payload->buffer, &lookup_method_comm, - sizeof(lookup_method_comm)); - if (ret) { - goto end; - } - } - ret = sizeof(lookup_method_comm); -end: - return ret; -} - -static -int lttng_userspace_probe_location_function_serialize( - const struct lttng_userspace_probe_location *location, - struct lttng_payload *payload) -{ - int ret; - size_t function_name_len, binary_path_len; - struct lttng_userspace_probe_location_function *location_function; - struct lttng_userspace_probe_location_function_comm location_function_comm; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) == - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); - - location_function = container_of(location, - struct lttng_userspace_probe_location_function, - parent); - if (!location_function->function_name || !location_function->binary_path) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (payload && !location_function->binary_fd_handle) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - function_name_len = strlen(location_function->function_name); - if (function_name_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - binary_path_len = strlen(location_function->binary_path); - if (binary_path_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_function_comm.function_name_len = function_name_len + 1; - location_function_comm.binary_path_len = binary_path_len + 1; - - if (payload) { - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_function_comm, - sizeof(location_function_comm)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_function->function_name, - location_function_comm.function_name_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_function->binary_path, - location_function_comm.binary_path_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_payload_push_fd_handle( - payload, location_function->binary_fd_handle); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - } - ret = sizeof(location_function_comm) + - location_function_comm.function_name_len + - location_function_comm.binary_path_len; -end: - return ret; -} - -static -int lttng_userspace_probe_location_tracepoint_serialize( - const struct lttng_userspace_probe_location *location, - struct lttng_payload *payload) -{ - int ret; - size_t probe_name_len, provider_name_len, binary_path_len; - struct lttng_userspace_probe_location_tracepoint *location_tracepoint; - struct lttng_userspace_probe_location_tracepoint_comm location_tracepoint_comm; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) == - LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); - - location_tracepoint = container_of(location, - struct lttng_userspace_probe_location_tracepoint, - parent); - if (!location_tracepoint->probe_name || - !location_tracepoint->provider_name || - !location_tracepoint->binary_path) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (payload && !location_tracepoint->binary_fd_handle) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_name_len = strlen(location_tracepoint->probe_name); - if (probe_name_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - provider_name_len = strlen(location_tracepoint->provider_name); - if (provider_name_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - binary_path_len = strlen(location_tracepoint->binary_path); - if (binary_path_len == 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_tracepoint_comm.probe_name_len = probe_name_len + 1; - location_tracepoint_comm.provider_name_len = provider_name_len + 1; - location_tracepoint_comm.binary_path_len = binary_path_len + 1; - - if (payload) { - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_tracepoint_comm, - sizeof(location_tracepoint_comm)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_tracepoint->probe_name, - location_tracepoint_comm.probe_name_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_tracepoint->provider_name, - location_tracepoint_comm.provider_name_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_dynamic_buffer_append(&payload->buffer, - location_tracepoint->binary_path, - location_tracepoint_comm.binary_path_len); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - ret = lttng_payload_push_fd_handle( - payload, location_tracepoint->binary_fd_handle); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - } - - ret = sizeof(location_tracepoint_comm) + - location_tracepoint_comm.probe_name_len + - location_tracepoint_comm.provider_name_len + - location_tracepoint_comm.binary_path_len; -end: - return ret; -} - -int lttng_userspace_probe_location_serialize( - const struct lttng_userspace_probe_location *location, - struct lttng_payload *payload) -{ - int ret, buffer_use = 0; - struct lttng_userspace_probe_location_comm location_generic_comm; - - if (!location) { - ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); - ret = -LTTNG_ERR_INVALID; - goto end; - } - - memset(&location_generic_comm, 0, sizeof(location_generic_comm)); - - location_generic_comm.type = (int8_t) location->type; - if (payload) { - ret = lttng_dynamic_buffer_append(&payload->buffer, - &location_generic_comm, - sizeof(location_generic_comm)); - if (ret) { - goto end; - } - } - buffer_use += sizeof(location_generic_comm); - - switch (lttng_userspace_probe_location_get_type(location)) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - ret = lttng_userspace_probe_location_function_serialize( - location, payload); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - ret = lttng_userspace_probe_location_tracepoint_serialize( - location, payload); - break; - default: - ERR("Unsupported probe location type"); - ret = -LTTNG_ERR_INVALID; - goto end; - } - if (ret < 0) { - goto end; - } - buffer_use += ret; - - ret = lttng_userspace_probe_location_lookup_method_serialize( - location->lookup_method, payload); - if (ret < 0) { - goto end; - } - ret += buffer_use; -end: - return ret; -} - -static -int lttng_userspace_probe_location_function_create_from_payload( - struct lttng_payload_view *view, - struct lttng_userspace_probe_location **location) -{ - struct lttng_userspace_probe_location_function_comm *location_function_comm; - const char *function_name_src, *binary_path_src; - char *function_name = NULL, *binary_path = NULL; - int ret = 0; - size_t expected_size; - struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view); - - LTTNG_ASSERT(location); - - if (view->buffer.size < sizeof(*location_function_comm)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_function_comm = - (typeof(location_function_comm)) view->buffer.data; - - expected_size = sizeof(*location_function_comm) + - location_function_comm->function_name_len + - location_function_comm->binary_path_len; - - if (view->buffer.size < expected_size) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - function_name_src = view->buffer.data + sizeof(*location_function_comm); - binary_path_src = function_name_src + - location_function_comm->function_name_len; - - if (!lttng_buffer_view_contains_string(&view->buffer, function_name_src, - location_function_comm->function_name_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, - location_function_comm->binary_path_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - function_name = lttng_strndup(function_name_src, LTTNG_SYMBOL_NAME_LEN); - if (!function_name) { - PERROR("lttng_strndup"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX); - if (!binary_path) { - PERROR("lttng_strndup"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - *location = lttng_userspace_probe_location_function_create_no_check( - binary_path, function_name, NULL, false); - if (!(*location)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = lttng_userspace_probe_location_function_set_binary_fd_handle( - *location, binary_fd_handle); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (int) expected_size; -end: - fd_handle_put(binary_fd_handle); - free(function_name); - free(binary_path); - return ret; -} - -static -int lttng_userspace_probe_location_tracepoint_create_from_payload( - struct lttng_payload_view *view, - struct lttng_userspace_probe_location **location) -{ - struct lttng_userspace_probe_location_tracepoint_comm *location_tracepoint_comm; - const char *probe_name_src, *provider_name_src, *binary_path_src; - char *probe_name = NULL, *provider_name = NULL, *binary_path = NULL; - int ret = 0; - size_t expected_size; - struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view); - - LTTNG_ASSERT(location); - - if (!binary_fd_handle) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (view->buffer.size < sizeof(*location_tracepoint_comm)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - location_tracepoint_comm = - (typeof(location_tracepoint_comm)) view->buffer.data; - - expected_size = sizeof(*location_tracepoint_comm) + - location_tracepoint_comm->probe_name_len + - location_tracepoint_comm->provider_name_len + - location_tracepoint_comm->binary_path_len; - - if (view->buffer.size < expected_size) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_name_src = view->buffer.data + sizeof(*location_tracepoint_comm); - provider_name_src = probe_name_src + - location_tracepoint_comm->probe_name_len; - binary_path_src = provider_name_src + - location_tracepoint_comm->provider_name_len; - - if (!lttng_buffer_view_contains_string(&view->buffer, probe_name_src, - location_tracepoint_comm->probe_name_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (!lttng_buffer_view_contains_string(&view->buffer, provider_name_src, - location_tracepoint_comm->provider_name_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, - location_tracepoint_comm->binary_path_len)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_name = lttng_strndup(probe_name_src, LTTNG_SYMBOL_NAME_LEN); - if (!probe_name) { - PERROR("Failed to allocate probe name"); - ret = -LTTNG_ERR_INVALID; - goto end; - } - provider_name = lttng_strndup(provider_name_src, LTTNG_SYMBOL_NAME_LEN); - if (!provider_name) { - PERROR("Failed to allocate provider name"); - ret = -LTTNG_ERR_INVALID; - goto end; - } - - binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX); - if (!binary_path) { - PERROR("Failed to allocate binary path"); - ret = -LTTNG_ERR_INVALID; - goto end; - } - - *location = lttng_userspace_probe_location_tracepoint_create_no_check( - binary_path, provider_name, probe_name, NULL, false); - if (!(*location)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( - *location, binary_fd_handle); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = (int) expected_size; -end: - fd_handle_put(binary_fd_handle); - free(probe_name); - free(provider_name); - free(binary_path); - return ret; -} - -static -int lttng_userspace_probe_location_lookup_method_create_from_payload( - struct lttng_payload_view *view, - struct lttng_userspace_probe_location_lookup_method **lookup_method) -{ - int ret; - struct lttng_userspace_probe_location_lookup_method_comm *lookup_comm; - enum lttng_userspace_probe_location_lookup_method_type type; - - LTTNG_ASSERT(view); - LTTNG_ASSERT(lookup_method); - - if (view->buffer.size < sizeof(*lookup_comm)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - lookup_comm = (typeof(lookup_comm)) view->buffer.data; - type = (enum lttng_userspace_probe_location_lookup_method_type) - lookup_comm->type; - switch (type) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: - *lookup_method = NULL; - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - *lookup_method = - lttng_userspace_probe_location_lookup_method_function_elf_create(); - if (!(*lookup_method)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - *lookup_method = - lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); - if (!(*lookup_method)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - break; - default: - ret = -LTTNG_ERR_INVALID; - goto end; - } - - ret = sizeof(*lookup_comm); -end: - return ret; -} - -int lttng_userspace_probe_location_create_from_payload( - struct lttng_payload_view *view, - struct lttng_userspace_probe_location **location) -{ - struct lttng_userspace_probe_location_lookup_method *lookup_method; - enum lttng_userspace_probe_location_type type; - int consumed = 0; - int ret; - struct lttng_userspace_probe_location_comm *probe_location_comm; - struct lttng_payload_view probe_location_comm_view = - lttng_payload_view_from_view( - view, 0, sizeof(*probe_location_comm)); - - LTTNG_ASSERT(view); - LTTNG_ASSERT(location); - - lookup_method = NULL; - - if (!lttng_payload_view_is_valid(&probe_location_comm_view)) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data; - type = (enum lttng_userspace_probe_location_type) probe_location_comm->type; - consumed += sizeof(*probe_location_comm); - - switch (type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - { - struct lttng_payload_view location_view = - lttng_payload_view_from_view( - view, consumed, -1); - - ret = lttng_userspace_probe_location_function_create_from_payload( - &location_view, location); - if (ret < 0) { - goto end; - } - break; - } - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - { - struct lttng_payload_view location_view = - lttng_payload_view_from_view(view, consumed, -1); - - ret = lttng_userspace_probe_location_tracepoint_create_from_payload( - &location_view, location); - if (ret < 0) { - goto end; - } - break; - } - default: - ret = -LTTNG_ERR_INVALID; - goto end; - } - - consumed += ret; - if (view->buffer.size <= consumed) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - { - struct lttng_payload_view lookup_method_view = - lttng_payload_view_from_view( - view, consumed, -1); - - ret = lttng_userspace_probe_location_lookup_method_create_from_payload( - &lookup_method_view, &lookup_method); - } - if (ret < 0) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - LTTNG_ASSERT(lookup_method); - (*location)->lookup_method = lookup_method; - lookup_method = NULL; - ret += consumed; -end: - return ret; -} - -static -int lttng_userspace_probe_location_function_set_binary_fd_handle( - struct lttng_userspace_probe_location *location, - struct fd_handle *binary_fd) -{ - int ret = 0; - struct lttng_userspace_probe_location_function *function_location; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); - - function_location = container_of(location, - struct lttng_userspace_probe_location_function, parent); - fd_handle_put(function_location->binary_fd_handle); - fd_handle_get(binary_fd); - function_location->binary_fd_handle = binary_fd; - return ret; -} - -static -int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( - struct lttng_userspace_probe_location *location, - struct fd_handle *binary_fd) -{ - int ret = 0; - struct lttng_userspace_probe_location_tracepoint *tracepoint_location; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); - - tracepoint_location = container_of(location, - struct lttng_userspace_probe_location_tracepoint, parent); - fd_handle_put(tracepoint_location->binary_fd_handle); - fd_handle_get(binary_fd); - tracepoint_location->binary_fd_handle = binary_fd; - return ret; -} - -static -int lttng_userspace_probe_location_function_flatten( - const struct lttng_userspace_probe_location *location, - struct lttng_dynamic_buffer *buffer) -{ - struct lttng_userspace_probe_location_lookup_method_elf flat_lookup_method; - struct lttng_userspace_probe_location_function *probe_function; - struct lttng_userspace_probe_location_function flat_probe; - size_t function_name_len, binary_path_len; - size_t padding_needed = 0; - char *flat_probe_start; - int storage_needed = 0; - int ret; - - LTTNG_ASSERT(location); - - if (location->lookup_method && location->lookup_method->type != - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - probe_function = container_of(location, - struct lttng_userspace_probe_location_function, - parent); - LTTNG_ASSERT(probe_function->function_name); - LTTNG_ASSERT(probe_function->binary_path); - - storage_needed += - sizeof(struct lttng_userspace_probe_location_function); - function_name_len = strlen(probe_function->function_name) + 1; - binary_path_len = strlen(probe_function->binary_path) + 1; - storage_needed += function_name_len + binary_path_len; - - /* - * The lookup method is aligned to 64-bit within the buffer. - * This is needed even if there is no lookup method since - * the next structure in the buffer probably needs to be - * aligned too (depending on the arch). - */ - padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed; - storage_needed += padding_needed; - - if (location->lookup_method) { - /* NOTE: elf look-up method is assumed here. */ - storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf); - } - - if (!buffer) { - ret = storage_needed; - goto end; - } - - if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { - ret = lttng_dynamic_buffer_set_capacity(buffer, - buffer->size + storage_needed); - if (ret) { - goto end; - } - } - - memset(&flat_probe, 0, sizeof(flat_probe)); - - flat_probe_start = buffer->data + buffer->size; - flat_probe.parent.type = location->type; - /* - * The lookup method, if present, is the last element in the flat - * representation of the probe. - */ - if (location->lookup_method) { - flat_probe.parent.lookup_method = - (struct lttng_userspace_probe_location_lookup_method *) - (flat_probe_start + sizeof(flat_probe) + - function_name_len + binary_path_len + padding_needed); - } else { - flat_probe.parent.lookup_method = NULL; - } - - flat_probe.function_name = flat_probe_start + sizeof(flat_probe); - flat_probe.binary_path = flat_probe.function_name + function_name_len; - flat_probe.binary_fd_handle = NULL; - ret = lttng_dynamic_buffer_append(buffer, &flat_probe, - sizeof(flat_probe)); - if (ret) { - goto end; - } - - ret = lttng_dynamic_buffer_append(buffer, - probe_function->function_name, function_name_len); - if (ret) { - goto end; - } - ret = lttng_dynamic_buffer_append(buffer, - probe_function->binary_path, binary_path_len); - if (ret) { - goto end; - } - - /* Insert padding before the lookup method. */ - ret = lttng_dynamic_buffer_set_size(buffer, - buffer->size + padding_needed); - if (ret) { - goto end; - } - - if (!location->lookup_method) { - /* Not an error, the default method is used. */ - ret = storage_needed; - goto end; - } - - memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); - flat_lookup_method.parent.type = - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; - ret = lttng_dynamic_buffer_append(buffer, - &flat_lookup_method, sizeof(flat_lookup_method)); - if (ret) { - goto end; - } - ret = storage_needed; -end: - return ret; -} - -static -int lttng_userspace_probe_location_tracepoint_flatten( - const struct lttng_userspace_probe_location *location, - struct lttng_dynamic_buffer *buffer) -{ - struct lttng_userspace_probe_location_lookup_method_sdt flat_lookup_method; - struct lttng_userspace_probe_location_tracepoint *probe_tracepoint; - struct lttng_userspace_probe_location_tracepoint flat_probe; - size_t probe_name_len, provider_name_len, binary_path_len; - size_t padding_needed = 0; - int storage_needed = 0; - char *flat_probe_start; - int ret = 0; - - LTTNG_ASSERT(location); - - /* Only SDT tracepoints are supported at the moment */ - if (location->lookup_method && location->lookup_method->type != - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - probe_tracepoint = container_of(location, - struct lttng_userspace_probe_location_tracepoint, - parent); - LTTNG_ASSERT(probe_tracepoint->probe_name); - LTTNG_ASSERT(probe_tracepoint->provider_name); - LTTNG_ASSERT(probe_tracepoint->binary_path); - - /* Compute the storage space needed to flatten the probe location */ - storage_needed += sizeof(struct lttng_userspace_probe_location_tracepoint); - - probe_name_len = strlen(probe_tracepoint->probe_name) + 1; - provider_name_len = strlen(probe_tracepoint->provider_name) + 1; - binary_path_len = strlen(probe_tracepoint->binary_path) + 1; - - storage_needed += probe_name_len + provider_name_len + binary_path_len; - - /* - * The lookup method is aligned to 64-bit within the buffer. - * This is needed even if there is no lookup method since - * the next structure in the buffer probably needs to be - * aligned too (depending on the arch). - */ - padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed; - storage_needed += padding_needed; - - if (location->lookup_method) { - /* NOTE: elf look-up method is assumed here. */ - storage_needed += - sizeof(struct lttng_userspace_probe_location_lookup_method_elf); - } - - /* - * If the caller set buffer to NULL, return the size of the needed buffer. - */ - if (!buffer) { - ret = storage_needed; - goto end; - } - - if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { - ret = lttng_dynamic_buffer_set_capacity(buffer, - buffer->size + storage_needed); - if (ret) { - goto end; - } - } - - memset(&flat_probe, 0, sizeof(flat_probe)); - - flat_probe_start = buffer->data + buffer->size; - flat_probe.parent.type = location->type; - - /* - * The lookup method, if present, is the last element in the flat - * representation of the probe. - */ - if (location->lookup_method) { - flat_probe.parent.lookup_method = - (struct lttng_userspace_probe_location_lookup_method *) - (flat_probe_start + sizeof(flat_probe) + - probe_name_len + provider_name_len + - binary_path_len + padding_needed); - } else { - flat_probe.parent.lookup_method = NULL; - } - - flat_probe.probe_name = flat_probe_start + sizeof(flat_probe); - flat_probe.provider_name = flat_probe.probe_name + probe_name_len; - flat_probe.binary_path = flat_probe.provider_name + provider_name_len; - flat_probe.binary_fd_handle = NULL; - ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe)); - if (ret) { - goto end; - } - - /* Append all the fields to the buffer */ - ret = lttng_dynamic_buffer_append(buffer, - probe_tracepoint->probe_name, probe_name_len); - if (ret) { - goto end; - } - ret = lttng_dynamic_buffer_append(buffer, - probe_tracepoint->provider_name, provider_name_len); - if (ret) { - goto end; - } - ret = lttng_dynamic_buffer_append(buffer, - probe_tracepoint->binary_path, binary_path_len); - if (ret) { - goto end; - } - - /* Insert padding before the lookup method. */ - ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed); - if (ret) { - goto end; - } - - if (!location->lookup_method) { - /* Not an error, the default method is used. */ - ret = storage_needed; - goto end; - } - - memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); - - flat_lookup_method.parent.type = - LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; - ret = lttng_dynamic_buffer_append(buffer, - &flat_lookup_method, sizeof(flat_lookup_method)); - if (ret) { - goto end; - } - ret = storage_needed; -end: - return ret; -} - -int lttng_userspace_probe_location_flatten( - const struct lttng_userspace_probe_location *location, - struct lttng_dynamic_buffer *buffer) -{ - int ret; - if (!location) { - ret = -LTTNG_ERR_INVALID; - goto end; - } - - /* Only types currently supported. */ - switch (location->type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - ret = lttng_userspace_probe_location_function_flatten(location, buffer); - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - ret = lttng_userspace_probe_location_tracepoint_flatten(location, buffer); - break; - default: - ret = -LTTNG_ERR_INVALID; - goto end; - } - -end: - return ret; -} - -struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy( - const struct lttng_userspace_probe_location *location) -{ - struct lttng_userspace_probe_location *new_location = NULL; - enum lttng_userspace_probe_location_type type; - - if (!location) { - goto err; - } - - type = lttng_userspace_probe_location_get_type(location); - switch (type) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - new_location = - lttng_userspace_probe_location_function_copy(location); - if (!new_location) { - goto err; - } - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - new_location = - lttng_userspace_probe_location_tracepoint_copy(location); - if (!new_location) { - goto err; - } - break; - default: - new_location = NULL; - goto err; - } -err: - return new_location; -} - -bool lttng_userspace_probe_location_lookup_method_is_equal( - const struct lttng_userspace_probe_location_lookup_method *a, - const struct lttng_userspace_probe_location_lookup_method *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - if (a->type != b->type) { - goto end; - } - - is_equal = true; -end: - return is_equal; -} - -bool lttng_userspace_probe_location_is_equal( - const struct lttng_userspace_probe_location *a, - const struct lttng_userspace_probe_location *b) -{ - bool is_equal = false; - - if (!a || !b) { - goto end; - } - - if (a == b) { - is_equal = true; - goto end; - } - - if (!lttng_userspace_probe_location_lookup_method_is_equal( - a->lookup_method, b->lookup_method)) { - goto end; - } - - if (a->type != b->type) { - goto end; - } - - is_equal = a->equal ? a->equal(a, b) : true; -end: - return is_equal; -} - -unsigned long lttng_userspace_probe_location_hash( - const struct lttng_userspace_probe_location *location) -{ - return location->hash(location); -} - -enum lttng_error_code lttng_userspace_probe_location_mi_serialize( - const struct lttng_userspace_probe_location *location, - struct mi_writer *writer) -{ - typedef enum lttng_error_code (*mi_fp)( - const struct lttng_userspace_probe_location *, - struct mi_writer *); - - int ret; - enum lttng_error_code ret_code; - mi_fp mi_function = NULL; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - - switch (lttng_userspace_probe_location_get_type(location)) { - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: - mi_function = lttng_userspace_probe_location_function_mi_serialize; - break; - case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: - mi_function = lttng_userspace_probe_location_tracepoint_mi_serialize; - break; - default: - abort(); - break; - } - - /* Open userspace probe location element. */ - ret = mi_lttng_writer_open_element( - writer, mi_lttng_element_userspace_probe_location); - if (ret) { - goto mi_error; - } - - /* Underlying user space probe location. */ - ret_code = mi_function(location, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close userspace probe location element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize( - const struct lttng_userspace_probe_location_lookup_method - *method, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *type_element_str; - - LTTNG_ASSERT(method); - LTTNG_ASSERT(writer); - - switch (lttng_userspace_probe_location_lookup_method_get_type(method)) { - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: - type_element_str = - mi_lttng_element_userspace_probe_location_lookup_method_function_default; - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: - type_element_str = - mi_lttng_element_userspace_probe_location_lookup_method_function_elf; - break; - case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: - type_element_str = - mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt; - break; - default: - abort(); - break; - } - - /* Open userspace probe location lookup method element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_userspace_probe_location_lookup_method); - if (ret) { - goto mi_error; - } - - /* User space probe location lookup method empty element. */ - ret = mi_lttng_writer_open_element(writer, type_element_str); - if (ret) { - goto mi_error; - } - - /* Close userspace probe location lookup method element. */ - ret = mi_lttng_close_multi_element(writer, 2); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize( - const struct lttng_userspace_probe_location *location, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *probe_name = NULL; - const char *provider_name = NULL; - const char *binary_path = NULL; - const struct lttng_userspace_probe_location_lookup_method - *lookup_method = NULL; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - - probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name( - location); - provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name( - location); - binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path( - location); - lookup_method = lttng_userspace_probe_location_tracepoint_get_lookup_method( - location); - - /* Open userspace probe location tracepoint element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_userspace_probe_location_tracepoint); - if (ret) { - goto mi_error; - } - - /* Probe name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_tracepoint_probe_name, - probe_name); - if (ret) { - goto mi_error; - } - - /* Provider name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_tracepoint_provider_name, - provider_name); - if (ret) { - goto mi_error; - } - - /* Binary path. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_binary_path, - binary_path); - if (ret) { - goto mi_error; - } - - /* The lookup method. */ - ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize( - lookup_method, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close userspace probe location tracepoint. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} - -static enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize( - const struct lttng_userspace_probe_location *location, - struct mi_writer *writer) -{ - int ret; - enum lttng_error_code ret_code; - const char *function_name = NULL; - const char *binary_path = NULL; - const char *instrumentation_type_str = NULL; - enum lttng_userspace_probe_location_function_instrumentation_type - instrumentation_type; - const struct lttng_userspace_probe_location_lookup_method - *lookup_method = NULL; - - LTTNG_ASSERT(location); - LTTNG_ASSERT(writer); - - function_name = lttng_userspace_probe_location_function_get_function_name( - location); - binary_path = lttng_userspace_probe_location_function_get_binary_path( - location); - instrumentation_type = - lttng_userspace_probe_location_function_get_instrumentation_type( - location); - lookup_method = lttng_userspace_probe_location_function_get_lookup_method( - location); - - switch (instrumentation_type) { - case LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY: - instrumentation_type_str = - mi_lttng_userspace_probe_location_function_instrumentation_type_entry; - break; - default: - abort(); - break; - } - - /* Open userspace probe location function element. */ - ret = mi_lttng_writer_open_element(writer, - mi_lttng_element_userspace_probe_location_function); - if (ret) { - goto mi_error; - } - - /* Function name. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_function_name, - function_name); - if (ret) { - goto mi_error; - } - - /* Binary path. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_binary_path, - binary_path); - if (ret) { - goto mi_error; - } - - /* Instrumentation type. */ - ret = mi_lttng_writer_write_element_string(writer, - mi_lttng_element_userspace_probe_location_function_instrumentation_type, - instrumentation_type_str); - if (ret) { - goto mi_error; - } - - /* The lookup method. */ - ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize( - lookup_method, writer); - if (ret_code != LTTNG_OK) { - goto end; - } - - /* Close userspace probe location function element. */ - ret = mi_lttng_writer_close_element(writer); - if (ret) { - goto mi_error; - } - - ret_code = LTTNG_OK; - goto end; - -mi_error: - ret_code = LTTNG_ERR_MI_IO_FAIL; -end: - return ret_code; -} diff --git a/src/common/userspace-probe.cpp b/src/common/userspace-probe.cpp new file mode 100644 index 000000000..4ade5d84f --- /dev/null +++ b/src/common/userspace-probe.cpp @@ -0,0 +1,2260 @@ +/* + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "lttng/lttng-error.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static +int lttng_userspace_probe_location_function_set_binary_fd_handle( + struct lttng_userspace_probe_location *location, + struct fd_handle *binary_fd_handle); + +static +int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( + struct lttng_userspace_probe_location *location, + struct fd_handle *binary_fd_handle); + +static +enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize( + const struct lttng_userspace_probe_location_lookup_method + *method, + struct mi_writer *writer); + +static +enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize( + const struct lttng_userspace_probe_location *location, + struct mi_writer *writer); + +static +enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize( + const struct lttng_userspace_probe_location *location, + struct mi_writer *writer); + +enum lttng_userspace_probe_location_lookup_method_type +lttng_userspace_probe_location_lookup_method_get_type( + const struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + return lookup_method ? lookup_method->type : + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_UNKNOWN; +} + +void lttng_userspace_probe_location_lookup_method_destroy( + struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + if (!lookup_method){ + return; + } + + free(lookup_method); +} + +struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_lookup_method_function_elf_create(void) +{ + struct lttng_userspace_probe_location_lookup_method *ret = NULL; + struct lttng_userspace_probe_location_lookup_method_elf *elf_method; + + elf_method = (lttng_userspace_probe_location_lookup_method_elf *) zmalloc(sizeof(*elf_method)); + if (!elf_method) { + PERROR("zmalloc"); + goto end; + } + + ret = &elf_method->parent; + ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; +end: + return ret; +} + +struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(void) +{ + struct lttng_userspace_probe_location_lookup_method *ret = NULL; + struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; + + sdt_method = (lttng_userspace_probe_location_lookup_method_sdt *) zmalloc(sizeof(*sdt_method)); + if (!sdt_method) { + PERROR("zmalloc"); + goto end; + } + + ret = &sdt_method->parent; + ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; +end: + return ret; +} + +enum lttng_userspace_probe_location_type lttng_userspace_probe_location_get_type( + const struct lttng_userspace_probe_location *location) +{ + return location ? location->type : + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN; +} + +static +void lttng_userspace_probe_location_function_destroy( + struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location_function *location_function = NULL; + + LTTNG_ASSERT(location); + + location_function = container_of(location, + struct lttng_userspace_probe_location_function, parent); + + LTTNG_ASSERT(location_function); + + free(location_function->function_name); + free(location_function->binary_path); + fd_handle_put(location_function->binary_fd_handle); + free(location); +} + +static +void lttng_userspace_probe_location_tracepoint_destroy( + struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location_tracepoint *location_tracepoint = NULL; + + LTTNG_ASSERT(location); + + location_tracepoint = container_of(location, + struct lttng_userspace_probe_location_tracepoint, + parent); + + LTTNG_ASSERT(location_tracepoint); + + free(location_tracepoint->probe_name); + free(location_tracepoint->provider_name); + free(location_tracepoint->binary_path); + fd_handle_put(location_tracepoint->binary_fd_handle); + free(location); +} + +void lttng_userspace_probe_location_destroy( + struct lttng_userspace_probe_location *location) +{ + if (!location) { + return; + } + + lttng_userspace_probe_location_lookup_method_destroy( + location->lookup_method); + + switch (location->type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + lttng_userspace_probe_location_function_destroy(location); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + lttng_userspace_probe_location_tracepoint_destroy(location); + break; + default: + abort(); + } +} + +/* Compare two file descriptors based on their inode and device numbers. */ +static bool fd_is_equal(int a, int b) +{ + int ret; + bool is_equal = false; + struct stat a_stat, b_stat; + + if (a < 0 && b >= 0) { + goto end; + } + + if (b < 0 && a >= 0) { + goto end; + } + + if (a < 0 && b < 0) { + if (a == -1 && b == -1) { + is_equal = true; + goto end; + } + + /* Invalid state, abort. */ + abort(); + } + + /* Both are valid file descriptors. */ + ret = fstat(a, &a_stat); + if (ret) { + PERROR("Failed to fstat userspace probe location binary fd %d", + a); + goto end; + } + + ret = fstat(b, &b_stat); + if (ret) { + PERROR("Failed to fstat userspace probe location binary fd %d", + b); + goto end; + } + + is_equal = (a_stat.st_ino == b_stat.st_ino) && + (a_stat.st_dev == b_stat.st_dev); + +end: + return is_equal; +} + +static unsigned long lttng_userspace_probe_location_function_hash( + const struct lttng_userspace_probe_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION, + lttng_ht_seed); + struct lttng_userspace_probe_location_function *function_location = + container_of(location, typeof(*function_location), + parent); + + hash ^= hash_key_str(function_location->function_name, lttng_ht_seed); + hash ^= hash_key_str(function_location->binary_path, lttng_ht_seed); + /* + * No need to hash on the fd. Worst comes to worse, + * the equal function will discriminate. + */ + return hash; +} + +static bool lttng_userspace_probe_location_function_is_equal( + const struct lttng_userspace_probe_location *_a, + const struct lttng_userspace_probe_location *_b) +{ + bool is_equal = false; + struct lttng_userspace_probe_location_function *a, *b; + + a = container_of(_a, struct lttng_userspace_probe_location_function, + parent); + b = container_of(_b, struct lttng_userspace_probe_location_function, + parent); + + if (a->instrumentation_type != b->instrumentation_type) { + goto end; + } + + LTTNG_ASSERT(a->function_name); + LTTNG_ASSERT(b->function_name); + if (strcmp(a->function_name, b->function_name)) { + goto end; + } + + LTTNG_ASSERT(a->binary_path); + LTTNG_ASSERT(b->binary_path); + if (strcmp(a->binary_path, b->binary_path)) { + goto end; + } + + is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1, + b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1); +end: + return is_equal; +} + +static struct lttng_userspace_probe_location * +lttng_userspace_probe_location_function_create_no_check(const char *binary_path, + const char *function_name, + struct lttng_userspace_probe_location_lookup_method *lookup_method, + bool open_binary) +{ + int binary_fd = -1; + struct fd_handle *binary_fd_handle = NULL; + char *function_name_copy = NULL, *binary_path_copy = NULL; + struct lttng_userspace_probe_location *ret = NULL; + struct lttng_userspace_probe_location_function *location; + + if (open_binary) { + binary_fd = open(binary_path, O_RDONLY); + if (binary_fd < 0) { + PERROR("Error opening the binary"); + goto error; + } + + binary_fd_handle = fd_handle_create(binary_fd); + if (!binary_fd) { + goto error; + } + + /* Ownership transferred to fd_handle. */ + binary_fd = -1; + } + + function_name_copy = lttng_strndup(function_name, LTTNG_SYMBOL_NAME_LEN); + if (!function_name_copy) { + PERROR("Error duplicating the function name"); + goto error; + } + + binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); + if (!binary_path_copy) { + PERROR("Error duplicating the function name"); + goto error; + } + + location = (lttng_userspace_probe_location_function *) zmalloc(sizeof(*location)); + if (!location) { + PERROR("Error allocating userspace probe location"); + goto error; + } + + location->function_name = function_name_copy; + location->binary_path = binary_path_copy; + location->binary_fd_handle = binary_fd_handle; + binary_fd_handle = NULL; + location->instrumentation_type = + LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY; + + ret = &location->parent; + ret->lookup_method = lookup_method; + ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION; + ret->equal = lttng_userspace_probe_location_function_is_equal; + ret->hash = lttng_userspace_probe_location_function_hash; + goto end; + +error: + free(function_name_copy); + free(binary_path_copy); + if (binary_fd >= 0) { + if (close(binary_fd)) { + PERROR("Error closing binary fd in error path"); + } + } + fd_handle_put(binary_fd_handle); +end: + return ret; +} + +static unsigned long lttng_userspace_probe_location_tracepoint_hash( + const struct lttng_userspace_probe_location *location) +{ + unsigned long hash = hash_key_ulong( + (void *) LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT, + lttng_ht_seed); + struct lttng_userspace_probe_location_tracepoint *tp_location = + container_of(location, typeof(*tp_location), parent); + + hash ^= hash_key_str(tp_location->probe_name, lttng_ht_seed); + hash ^= hash_key_str(tp_location->provider_name, lttng_ht_seed); + hash ^= hash_key_str(tp_location->binary_path, lttng_ht_seed); + /* + * No need to hash on the fd. Worst comes to worse, + * the equal function will discriminate. + */ + return hash; +} + +static bool lttng_userspace_probe_location_tracepoint_is_equal( + const struct lttng_userspace_probe_location *_a, + const struct lttng_userspace_probe_location *_b) +{ + bool is_equal = false; + struct lttng_userspace_probe_location_tracepoint *a, *b; + + a = container_of(_a, struct lttng_userspace_probe_location_tracepoint, + parent); + b = container_of(_b, struct lttng_userspace_probe_location_tracepoint, + parent); + + LTTNG_ASSERT(a->probe_name); + LTTNG_ASSERT(b->probe_name); + if (strcmp(a->probe_name, b->probe_name)) { + goto end; + } + + LTTNG_ASSERT(a->provider_name); + LTTNG_ASSERT(b->provider_name); + if (strcmp(a->provider_name, b->provider_name)) { + goto end; + } + + LTTNG_ASSERT(a->binary_path); + LTTNG_ASSERT(b->binary_path); + if (strcmp(a->binary_path, b->binary_path)) { + goto end; + } + + is_equal = fd_is_equal(a->binary_fd_handle ? fd_handle_get_fd(a->binary_fd_handle) : -1, + b->binary_fd_handle ? fd_handle_get_fd(b->binary_fd_handle) : -1); + +end: + return is_equal; +} + +static struct lttng_userspace_probe_location * +lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path, + const char *provider_name, const char *probe_name, + struct lttng_userspace_probe_location_lookup_method *lookup_method, + bool open_binary) +{ + int binary_fd = -1; + struct fd_handle *binary_fd_handle = NULL; + char *probe_name_copy = NULL; + char *provider_name_copy = NULL; + char *binary_path_copy = NULL; + struct lttng_userspace_probe_location *ret = NULL; + struct lttng_userspace_probe_location_tracepoint *location; + + if (open_binary) { + binary_fd = open(binary_path, O_RDONLY); + if (binary_fd < 0) { + PERROR("open"); + goto error; + } + + binary_fd_handle = fd_handle_create(binary_fd); + if (!binary_fd) { + goto error; + } + + /* Ownership transferred to fd_handle. */ + binary_fd = -1; + } + + probe_name_copy = lttng_strndup(probe_name, LTTNG_SYMBOL_NAME_LEN); + if (!probe_name_copy) { + PERROR("lttng_strndup"); + goto error; + } + + provider_name_copy = lttng_strndup(provider_name, LTTNG_SYMBOL_NAME_LEN); + if (!provider_name_copy) { + PERROR("lttng_strndup"); + goto error; + } + + binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); + if (!binary_path_copy) { + PERROR("lttng_strndup"); + goto error; + } + + location = (lttng_userspace_probe_location_tracepoint *) zmalloc(sizeof(*location)); + if (!location) { + PERROR("zmalloc"); + goto error; + } + + location->probe_name = probe_name_copy; + location->provider_name = provider_name_copy; + location->binary_path = binary_path_copy; + location->binary_fd_handle = binary_fd_handle; + binary_fd_handle = NULL; + + ret = &location->parent; + ret->lookup_method = lookup_method; + ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT; + ret->equal = lttng_userspace_probe_location_tracepoint_is_equal; + ret->hash = lttng_userspace_probe_location_tracepoint_hash; + goto end; + +error: + free(probe_name_copy); + free(provider_name_copy); + free(binary_path_copy); + if (binary_fd >= 0) { + if (close(binary_fd)) { + PERROR("Error closing binary fd in error path"); + } + } + fd_handle_put(binary_fd_handle); +end: + return ret; +} + +struct lttng_userspace_probe_location * +lttng_userspace_probe_location_function_create(const char *binary_path, + const char *function_name, + struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + struct lttng_userspace_probe_location *ret = NULL; + + if (!binary_path || !function_name) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + switch (lttng_userspace_probe_location_lookup_method_get_type( + lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + break; + default: + /* Invalid probe location lookup method. */ + goto end; + } + + ret = lttng_userspace_probe_location_function_create_no_check( + binary_path, function_name, lookup_method, true); +end: + return ret; +} + +struct lttng_userspace_probe_location * +lttng_userspace_probe_location_tracepoint_create(const char *binary_path, + const char *provider_name, const char *probe_name, + struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + struct lttng_userspace_probe_location *ret = NULL; + + if (!binary_path || !probe_name || !provider_name) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + switch (lttng_userspace_probe_location_lookup_method_get_type( + lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + break; + default: + /* Invalid probe location lookup method. */ + goto end; + } + + ret = lttng_userspace_probe_location_tracepoint_create_no_check( + binary_path, provider_name, probe_name, lookup_method, true); +end: + return ret; +} + +static struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_lookup_method_function_elf_copy( + const struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + struct lttng_userspace_probe_location_lookup_method *parent = NULL; + struct lttng_userspace_probe_location_lookup_method_elf *elf_method; + + LTTNG_ASSERT(lookup_method); + LTTNG_ASSERT(lookup_method->type == + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF); + + elf_method = (lttng_userspace_probe_location_lookup_method_elf *) zmalloc(sizeof(*elf_method)); + if (!elf_method) { + PERROR("Error allocating ELF userspace probe lookup method"); + goto error; + } + + elf_method->parent.type = lookup_method->type; + parent = &elf_method->parent; + + goto end; +error: + parent = NULL; +end: + return parent; +} + +static struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( + struct lttng_userspace_probe_location_lookup_method *lookup_method) +{ + struct lttng_userspace_probe_location_lookup_method *parent = NULL; + struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; + + LTTNG_ASSERT(lookup_method); + LTTNG_ASSERT(lookup_method->type == + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT); + + sdt_method = (lttng_userspace_probe_location_lookup_method_sdt *) zmalloc(sizeof(*sdt_method)); + if (!sdt_method) { + PERROR("zmalloc"); + goto error; + } + + sdt_method->parent.type = lookup_method->type; + parent = &sdt_method->parent; + + goto end; + +error: + parent = NULL; +end: + return parent; +} + +static struct lttng_userspace_probe_location * +lttng_userspace_probe_location_function_copy( + const struct lttng_userspace_probe_location *location) +{ + enum lttng_userspace_probe_location_lookup_method_type lookup_type; + struct lttng_userspace_probe_location *new_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; + const char *binary_path = NULL; + const char *function_name = NULL; + struct lttng_userspace_probe_location_function *function_location; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); + function_location = container_of( + location, typeof(*function_location), parent); + + /* Get probe location fields */ + binary_path = lttng_userspace_probe_location_function_get_binary_path(location); + if (!binary_path) { + ERR("Userspace probe binary path is NULL"); + goto error; + } + + function_name = lttng_userspace_probe_location_function_get_function_name(location); + if (!function_name) { + ERR("Userspace probe function name is NULL"); + goto error; + } + + /* + * Duplicate probe location method fields + */ + lookup_type = lttng_userspace_probe_location_lookup_method_get_type( + location->lookup_method); + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_copy( + location->lookup_method); + if (!lookup_method) { + goto error; + } + break; + default: + /* Invalid probe location lookup method. */ + goto error; + } + + /* Create the probe_location */ + new_location = lttng_userspace_probe_location_function_create_no_check( + binary_path, function_name, lookup_method, false); + if (!new_location) { + goto destroy_lookup_method; + } + + /* Set the duplicated fd to the new probe_location */ + if (lttng_userspace_probe_location_function_set_binary_fd_handle(new_location, + function_location->binary_fd_handle) < 0) { + goto destroy_probe_location; + } + + goto end; + +destroy_probe_location: + lttng_userspace_probe_location_destroy(new_location); +destroy_lookup_method: + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +error: + new_location = NULL; +end: + return new_location; +} + +static struct lttng_userspace_probe_location * +lttng_userspace_probe_location_tracepoint_copy( + const struct lttng_userspace_probe_location *location) +{ + enum lttng_userspace_probe_location_lookup_method_type lookup_type; + struct lttng_userspace_probe_location *new_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; + const char *binary_path = NULL; + const char *probe_name = NULL; + const char *provider_name = NULL; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); + tracepoint_location = container_of( + location, typeof(*tracepoint_location), parent); + + /* Get probe location fields */ + binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location); + if (!binary_path) { + ERR("Userspace probe binary path is NULL"); + goto error; + } + + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); + if (!probe_name) { + ERR("Userspace probe probe name is NULL"); + goto error; + } + + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); + if (!provider_name) { + ERR("Userspace probe provider name is NULL"); + goto error; + } + + /* + * Duplicate probe location method fields + */ + lookup_type = lttng_userspace_probe_location_lookup_method_get_type( + location->lookup_method); + switch (lookup_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( + location->lookup_method); + if (!lookup_method) { + goto error; + } + break; + default: + /* Invalid probe location lookup method. */ + goto error; + } + + /* Create the probe_location */ + new_location = lttng_userspace_probe_location_tracepoint_create_no_check( + binary_path, provider_name, probe_name, lookup_method, false); + if (!new_location) { + goto destroy_lookup_method; + } + + /* Set the duplicated fd to the new probe_location */ + if (lttng_userspace_probe_location_tracepoint_set_binary_fd_handle(new_location, + tracepoint_location->binary_fd_handle) < 0) { + goto destroy_probe_location; + } + + goto end; + +destroy_probe_location: + lttng_userspace_probe_location_destroy(new_location); +destroy_lookup_method: + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +error: + new_location = NULL; +end: + return new_location; +} + +const char *lttng_userspace_probe_location_function_get_binary_path( + const struct lttng_userspace_probe_location *location) +{ + const char *ret = NULL; + struct lttng_userspace_probe_location_function *function_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, + parent); + ret = function_location->binary_path; +end: + return ret; +} + +const char *lttng_userspace_probe_location_tracepoint_get_binary_path( + const struct lttng_userspace_probe_location *location) +{ + const char *ret = NULL; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + tracepoint_location = container_of(location, + struct lttng_userspace_probe_location_tracepoint, + parent); + ret = tracepoint_location->binary_path; +end: + return ret; +} + +const char *lttng_userspace_probe_location_function_get_function_name( + const struct lttng_userspace_probe_location *location) +{ + const char *ret = NULL; + struct lttng_userspace_probe_location_function *function_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, parent); + ret = function_location->function_name; +end: + return ret; +} + +const char *lttng_userspace_probe_location_tracepoint_get_probe_name( + const struct lttng_userspace_probe_location *location) +{ + const char *ret = NULL; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + tracepoint_location = container_of(location, + struct lttng_userspace_probe_location_tracepoint, parent); + ret = tracepoint_location->probe_name; +end: + return ret; +} + +const char *lttng_userspace_probe_location_tracepoint_get_provider_name( + const struct lttng_userspace_probe_location *location) +{ + const char *ret = NULL; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + tracepoint_location = container_of(location, + struct lttng_userspace_probe_location_tracepoint, parent); + ret = tracepoint_location->provider_name; +end: + return ret; +} + +int lttng_userspace_probe_location_function_get_binary_fd( + const struct lttng_userspace_probe_location *location) +{ + int ret = -1; + struct lttng_userspace_probe_location_function *function_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, parent); + ret = function_location->binary_fd_handle ? + fd_handle_get_fd(function_location->binary_fd_handle) : -1; +end: + return ret; +} + +enum lttng_userspace_probe_location_function_instrumentation_type +lttng_userspace_probe_location_function_get_instrumentation_type( + const struct lttng_userspace_probe_location *location) +{ + enum lttng_userspace_probe_location_function_instrumentation_type type; + struct lttng_userspace_probe_location_function *function_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN; + goto end; + } + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, parent); + type = function_location->instrumentation_type; +end: + return type; +} + +enum lttng_userspace_probe_location_status +lttng_userspace_probe_location_function_set_instrumentation_type( + const struct lttng_userspace_probe_location *location, + enum lttng_userspace_probe_location_function_instrumentation_type instrumentation_type) +{ + enum lttng_userspace_probe_location_status status = + LTTNG_USERSPACE_PROBE_LOCATION_STATUS_OK; + struct lttng_userspace_probe_location_function *function_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION || + instrumentation_type != + LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID; + goto end; + } + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, parent); + function_location->instrumentation_type = instrumentation_type; +end: + return status; +} + +int lttng_userspace_probe_location_tracepoint_get_binary_fd( + const struct lttng_userspace_probe_location *location) +{ + int ret = -1; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + tracepoint_location = container_of(location, + struct lttng_userspace_probe_location_tracepoint, parent); + ret = tracepoint_location->binary_fd_handle ? + fd_handle_get_fd(tracepoint_location->binary_fd_handle) : -1; +end: + return ret; +} + +static struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_function_get_lookup_method( + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location_lookup_method *ret = NULL; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + ret = location->lookup_method; +end: + return ret; +} + +static struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_tracepoint_get_lookup_method( + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location_lookup_method *ret = NULL; + + if (!location || lttng_userspace_probe_location_get_type(location) != + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + goto end; + } + + ret = location->lookup_method; +end: + return ret; +} + +const struct lttng_userspace_probe_location_lookup_method * +lttng_userspace_probe_location_get_lookup_method( + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location_lookup_method *ret = NULL; + + LTTNG_ASSERT(location); + switch (location->type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + ret = lttng_userspace_probe_location_function_get_lookup_method( + location); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + ret = lttng_userspace_probe_location_tracepoint_get_lookup_method( + location); + break; + default: + ERR("Unknowned lookup method."); + break; + } + return ret; +} + +static +int lttng_userspace_probe_location_lookup_method_serialize( + struct lttng_userspace_probe_location_lookup_method *method, + struct lttng_payload *payload) +{ + int ret; + struct lttng_userspace_probe_location_lookup_method_comm + lookup_method_comm; + + lookup_method_comm.type = (int8_t) (method ? method->type : + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT); + if (payload) { + ret = lttng_dynamic_buffer_append(&payload->buffer, &lookup_method_comm, + sizeof(lookup_method_comm)); + if (ret) { + goto end; + } + } + ret = sizeof(lookup_method_comm); +end: + return ret; +} + +static +int lttng_userspace_probe_location_function_serialize( + const struct lttng_userspace_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t function_name_len, binary_path_len; + struct lttng_userspace_probe_location_function *location_function; + struct lttng_userspace_probe_location_function_comm location_function_comm; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) == + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); + + location_function = container_of(location, + struct lttng_userspace_probe_location_function, + parent); + if (!location_function->function_name || !location_function->binary_path) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (payload && !location_function->binary_fd_handle) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + function_name_len = strlen(location_function->function_name); + if (function_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + binary_path_len = strlen(location_function->binary_path); + if (binary_path_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_function_comm.function_name_len = function_name_len + 1; + location_function_comm.binary_path_len = binary_path_len + 1; + + if (payload) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_function_comm, + sizeof(location_function_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_function->function_name, + location_function_comm.function_name_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_function->binary_path, + location_function_comm.binary_path_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_payload_push_fd_handle( + payload, location_function->binary_fd_handle); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + } + ret = sizeof(location_function_comm) + + location_function_comm.function_name_len + + location_function_comm.binary_path_len; +end: + return ret; +} + +static +int lttng_userspace_probe_location_tracepoint_serialize( + const struct lttng_userspace_probe_location *location, + struct lttng_payload *payload) +{ + int ret; + size_t probe_name_len, provider_name_len, binary_path_len; + struct lttng_userspace_probe_location_tracepoint *location_tracepoint; + struct lttng_userspace_probe_location_tracepoint_comm location_tracepoint_comm; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(lttng_userspace_probe_location_get_type(location) == + LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); + + location_tracepoint = container_of(location, + struct lttng_userspace_probe_location_tracepoint, + parent); + if (!location_tracepoint->probe_name || + !location_tracepoint->provider_name || + !location_tracepoint->binary_path) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (payload && !location_tracepoint->binary_fd_handle) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_name_len = strlen(location_tracepoint->probe_name); + if (probe_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + provider_name_len = strlen(location_tracepoint->provider_name); + if (provider_name_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + binary_path_len = strlen(location_tracepoint->binary_path); + if (binary_path_len == 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_tracepoint_comm.probe_name_len = probe_name_len + 1; + location_tracepoint_comm.provider_name_len = provider_name_len + 1; + location_tracepoint_comm.binary_path_len = binary_path_len + 1; + + if (payload) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_tracepoint_comm, + sizeof(location_tracepoint_comm)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_tracepoint->probe_name, + location_tracepoint_comm.probe_name_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_tracepoint->provider_name, + location_tracepoint_comm.provider_name_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_dynamic_buffer_append(&payload->buffer, + location_tracepoint->binary_path, + location_tracepoint_comm.binary_path_len); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + ret = lttng_payload_push_fd_handle( + payload, location_tracepoint->binary_fd_handle); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + } + + ret = sizeof(location_tracepoint_comm) + + location_tracepoint_comm.probe_name_len + + location_tracepoint_comm.provider_name_len + + location_tracepoint_comm.binary_path_len; +end: + return ret; +} + +int lttng_userspace_probe_location_serialize( + const struct lttng_userspace_probe_location *location, + struct lttng_payload *payload) +{ + int ret, buffer_use = 0; + struct lttng_userspace_probe_location_comm location_generic_comm; + + if (!location) { + ERR("Invalid argument(s) passed to '%s'", __FUNCTION__); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + memset(&location_generic_comm, 0, sizeof(location_generic_comm)); + + location_generic_comm.type = (int8_t) location->type; + if (payload) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + &location_generic_comm, + sizeof(location_generic_comm)); + if (ret) { + goto end; + } + } + buffer_use += sizeof(location_generic_comm); + + switch (lttng_userspace_probe_location_get_type(location)) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + ret = lttng_userspace_probe_location_function_serialize( + location, payload); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + ret = lttng_userspace_probe_location_tracepoint_serialize( + location, payload); + break; + default: + ERR("Unsupported probe location type"); + ret = -LTTNG_ERR_INVALID; + goto end; + } + if (ret < 0) { + goto end; + } + buffer_use += ret; + + ret = lttng_userspace_probe_location_lookup_method_serialize( + location->lookup_method, payload); + if (ret < 0) { + goto end; + } + ret += buffer_use; +end: + return ret; +} + +static +int lttng_userspace_probe_location_function_create_from_payload( + struct lttng_payload_view *view, + struct lttng_userspace_probe_location **location) +{ + struct lttng_userspace_probe_location_function_comm *location_function_comm; + const char *function_name_src, *binary_path_src; + char *function_name = NULL, *binary_path = NULL; + int ret = 0; + size_t expected_size; + struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view); + + LTTNG_ASSERT(location); + + if (view->buffer.size < sizeof(*location_function_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_function_comm = + (typeof(location_function_comm)) view->buffer.data; + + expected_size = sizeof(*location_function_comm) + + location_function_comm->function_name_len + + location_function_comm->binary_path_len; + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + function_name_src = view->buffer.data + sizeof(*location_function_comm); + binary_path_src = function_name_src + + location_function_comm->function_name_len; + + if (!lttng_buffer_view_contains_string(&view->buffer, function_name_src, + location_function_comm->function_name_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_function_comm->binary_path_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + function_name = lttng_strndup(function_name_src, LTTNG_SYMBOL_NAME_LEN); + if (!function_name) { + PERROR("lttng_strndup"); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX); + if (!binary_path) { + PERROR("lttng_strndup"); + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + *location = lttng_userspace_probe_location_function_create_no_check( + binary_path, function_name, NULL, false); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_userspace_probe_location_function_set_binary_fd_handle( + *location, binary_fd_handle); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) expected_size; +end: + fd_handle_put(binary_fd_handle); + free(function_name); + free(binary_path); + return ret; +} + +static +int lttng_userspace_probe_location_tracepoint_create_from_payload( + struct lttng_payload_view *view, + struct lttng_userspace_probe_location **location) +{ + struct lttng_userspace_probe_location_tracepoint_comm *location_tracepoint_comm; + const char *probe_name_src, *provider_name_src, *binary_path_src; + char *probe_name = NULL, *provider_name = NULL, *binary_path = NULL; + int ret = 0; + size_t expected_size; + struct fd_handle *binary_fd_handle = lttng_payload_view_pop_fd_handle(view); + + LTTNG_ASSERT(location); + + if (!binary_fd_handle) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (view->buffer.size < sizeof(*location_tracepoint_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + location_tracepoint_comm = + (typeof(location_tracepoint_comm)) view->buffer.data; + + expected_size = sizeof(*location_tracepoint_comm) + + location_tracepoint_comm->probe_name_len + + location_tracepoint_comm->provider_name_len + + location_tracepoint_comm->binary_path_len; + + if (view->buffer.size < expected_size) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_name_src = view->buffer.data + sizeof(*location_tracepoint_comm); + provider_name_src = probe_name_src + + location_tracepoint_comm->probe_name_len; + binary_path_src = provider_name_src + + location_tracepoint_comm->provider_name_len; + + if (!lttng_buffer_view_contains_string(&view->buffer, probe_name_src, + location_tracepoint_comm->probe_name_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (!lttng_buffer_view_contains_string(&view->buffer, provider_name_src, + location_tracepoint_comm->provider_name_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (!lttng_buffer_view_contains_string(&view->buffer, binary_path_src, + location_tracepoint_comm->binary_path_len)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_name = lttng_strndup(probe_name_src, LTTNG_SYMBOL_NAME_LEN); + if (!probe_name) { + PERROR("Failed to allocate probe name"); + ret = -LTTNG_ERR_INVALID; + goto end; + } + provider_name = lttng_strndup(provider_name_src, LTTNG_SYMBOL_NAME_LEN); + if (!provider_name) { + PERROR("Failed to allocate provider name"); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX); + if (!binary_path) { + PERROR("Failed to allocate binary path"); + ret = -LTTNG_ERR_INVALID; + goto end; + } + + *location = lttng_userspace_probe_location_tracepoint_create_no_check( + binary_path, provider_name, probe_name, NULL, false); + if (!(*location)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( + *location, binary_fd_handle); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = (int) expected_size; +end: + fd_handle_put(binary_fd_handle); + free(probe_name); + free(provider_name); + free(binary_path); + return ret; +} + +static +int lttng_userspace_probe_location_lookup_method_create_from_payload( + struct lttng_payload_view *view, + struct lttng_userspace_probe_location_lookup_method **lookup_method) +{ + int ret; + struct lttng_userspace_probe_location_lookup_method_comm *lookup_comm; + enum lttng_userspace_probe_location_lookup_method_type type; + + LTTNG_ASSERT(view); + LTTNG_ASSERT(lookup_method); + + if (view->buffer.size < sizeof(*lookup_comm)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lookup_comm = (typeof(lookup_comm)) view->buffer.data; + type = (enum lttng_userspace_probe_location_lookup_method_type) + lookup_comm->type; + switch (type) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: + *lookup_method = NULL; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + *lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!(*lookup_method)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + *lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); + if (!(*lookup_method)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + break; + default: + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = sizeof(*lookup_comm); +end: + return ret; +} + +int lttng_userspace_probe_location_create_from_payload( + struct lttng_payload_view *view, + struct lttng_userspace_probe_location **location) +{ + struct lttng_userspace_probe_location_lookup_method *lookup_method; + enum lttng_userspace_probe_location_type type; + int consumed = 0; + int ret; + struct lttng_userspace_probe_location_comm *probe_location_comm; + struct lttng_payload_view probe_location_comm_view = + lttng_payload_view_from_view( + view, 0, sizeof(*probe_location_comm)); + + LTTNG_ASSERT(view); + LTTNG_ASSERT(location); + + lookup_method = NULL; + + if (!lttng_payload_view_is_valid(&probe_location_comm_view)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_location_comm = (typeof(probe_location_comm)) probe_location_comm_view.buffer.data; + type = (enum lttng_userspace_probe_location_type) probe_location_comm->type; + consumed += sizeof(*probe_location_comm); + + switch (type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_userspace_probe_location_function_create_from_payload( + &location_view, location); + if (ret < 0) { + goto end; + } + break; + } + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + { + struct lttng_payload_view location_view = + lttng_payload_view_from_view(view, consumed, -1); + + ret = lttng_userspace_probe_location_tracepoint_create_from_payload( + &location_view, location); + if (ret < 0) { + goto end; + } + break; + } + default: + ret = -LTTNG_ERR_INVALID; + goto end; + } + + consumed += ret; + if (view->buffer.size <= consumed) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + { + struct lttng_payload_view lookup_method_view = + lttng_payload_view_from_view( + view, consumed, -1); + + ret = lttng_userspace_probe_location_lookup_method_create_from_payload( + &lookup_method_view, &lookup_method); + } + if (ret < 0) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + LTTNG_ASSERT(lookup_method); + (*location)->lookup_method = lookup_method; + lookup_method = NULL; + ret += consumed; +end: + return ret; +} + +static +int lttng_userspace_probe_location_function_set_binary_fd_handle( + struct lttng_userspace_probe_location *location, + struct fd_handle *binary_fd) +{ + int ret = 0; + struct lttng_userspace_probe_location_function *function_location; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); + + function_location = container_of(location, + struct lttng_userspace_probe_location_function, parent); + fd_handle_put(function_location->binary_fd_handle); + fd_handle_get(binary_fd); + function_location->binary_fd_handle = binary_fd; + return ret; +} + +static +int lttng_userspace_probe_location_tracepoint_set_binary_fd_handle( + struct lttng_userspace_probe_location *location, + struct fd_handle *binary_fd) +{ + int ret = 0; + struct lttng_userspace_probe_location_tracepoint *tracepoint_location; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); + + tracepoint_location = container_of(location, + struct lttng_userspace_probe_location_tracepoint, parent); + fd_handle_put(tracepoint_location->binary_fd_handle); + fd_handle_get(binary_fd); + tracepoint_location->binary_fd_handle = binary_fd; + return ret; +} + +static +int lttng_userspace_probe_location_function_flatten( + const struct lttng_userspace_probe_location *location, + struct lttng_dynamic_buffer *buffer) +{ + struct lttng_userspace_probe_location_lookup_method_elf flat_lookup_method; + struct lttng_userspace_probe_location_function *probe_function; + struct lttng_userspace_probe_location_function flat_probe; + size_t function_name_len, binary_path_len; + size_t padding_needed = 0; + char *flat_probe_start; + int storage_needed = 0; + int ret; + + LTTNG_ASSERT(location); + + if (location->lookup_method && location->lookup_method->type != + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + probe_function = container_of(location, + struct lttng_userspace_probe_location_function, + parent); + LTTNG_ASSERT(probe_function->function_name); + LTTNG_ASSERT(probe_function->binary_path); + + storage_needed += + sizeof(struct lttng_userspace_probe_location_function); + function_name_len = strlen(probe_function->function_name) + 1; + binary_path_len = strlen(probe_function->binary_path) + 1; + storage_needed += function_name_len + binary_path_len; + + /* + * The lookup method is aligned to 64-bit within the buffer. + * This is needed even if there is no lookup method since + * the next structure in the buffer probably needs to be + * aligned too (depending on the arch). + */ + padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed; + storage_needed += padding_needed; + + if (location->lookup_method) { + /* NOTE: elf look-up method is assumed here. */ + storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf); + } + + if (!buffer) { + ret = storage_needed; + goto end; + } + + if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { + ret = lttng_dynamic_buffer_set_capacity(buffer, + buffer->size + storage_needed); + if (ret) { + goto end; + } + } + + memset(&flat_probe, 0, sizeof(flat_probe)); + + flat_probe_start = buffer->data + buffer->size; + flat_probe.parent.type = location->type; + /* + * The lookup method, if present, is the last element in the flat + * representation of the probe. + */ + if (location->lookup_method) { + flat_probe.parent.lookup_method = + (struct lttng_userspace_probe_location_lookup_method *) + (flat_probe_start + sizeof(flat_probe) + + function_name_len + binary_path_len + padding_needed); + } else { + flat_probe.parent.lookup_method = NULL; + } + + flat_probe.function_name = flat_probe_start + sizeof(flat_probe); + flat_probe.binary_path = flat_probe.function_name + function_name_len; + flat_probe.binary_fd_handle = NULL; + ret = lttng_dynamic_buffer_append(buffer, &flat_probe, + sizeof(flat_probe)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(buffer, + probe_function->function_name, function_name_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buffer, + probe_function->binary_path, binary_path_len); + if (ret) { + goto end; + } + + /* Insert padding before the lookup method. */ + ret = lttng_dynamic_buffer_set_size(buffer, + buffer->size + padding_needed); + if (ret) { + goto end; + } + + if (!location->lookup_method) { + /* Not an error, the default method is used. */ + ret = storage_needed; + goto end; + } + + memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); + flat_lookup_method.parent.type = + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; + ret = lttng_dynamic_buffer_append(buffer, + &flat_lookup_method, sizeof(flat_lookup_method)); + if (ret) { + goto end; + } + ret = storage_needed; +end: + return ret; +} + +static +int lttng_userspace_probe_location_tracepoint_flatten( + const struct lttng_userspace_probe_location *location, + struct lttng_dynamic_buffer *buffer) +{ + struct lttng_userspace_probe_location_lookup_method_sdt flat_lookup_method; + struct lttng_userspace_probe_location_tracepoint *probe_tracepoint; + struct lttng_userspace_probe_location_tracepoint flat_probe; + size_t probe_name_len, provider_name_len, binary_path_len; + size_t padding_needed = 0; + int storage_needed = 0; + char *flat_probe_start; + int ret = 0; + + LTTNG_ASSERT(location); + + /* Only SDT tracepoints are supported at the moment */ + if (location->lookup_method && location->lookup_method->type != + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + probe_tracepoint = container_of(location, + struct lttng_userspace_probe_location_tracepoint, + parent); + LTTNG_ASSERT(probe_tracepoint->probe_name); + LTTNG_ASSERT(probe_tracepoint->provider_name); + LTTNG_ASSERT(probe_tracepoint->binary_path); + + /* Compute the storage space needed to flatten the probe location */ + storage_needed += sizeof(struct lttng_userspace_probe_location_tracepoint); + + probe_name_len = strlen(probe_tracepoint->probe_name) + 1; + provider_name_len = strlen(probe_tracepoint->provider_name) + 1; + binary_path_len = strlen(probe_tracepoint->binary_path) + 1; + + storage_needed += probe_name_len + provider_name_len + binary_path_len; + + /* + * The lookup method is aligned to 64-bit within the buffer. + * This is needed even if there is no lookup method since + * the next structure in the buffer probably needs to be + * aligned too (depending on the arch). + */ + padding_needed = lttng_align_ceil(storage_needed, sizeof(uint64_t)) - storage_needed; + storage_needed += padding_needed; + + if (location->lookup_method) { + /* NOTE: elf look-up method is assumed here. */ + storage_needed += + sizeof(struct lttng_userspace_probe_location_lookup_method_elf); + } + + /* + * If the caller set buffer to NULL, return the size of the needed buffer. + */ + if (!buffer) { + ret = storage_needed; + goto end; + } + + if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { + ret = lttng_dynamic_buffer_set_capacity(buffer, + buffer->size + storage_needed); + if (ret) { + goto end; + } + } + + memset(&flat_probe, 0, sizeof(flat_probe)); + + flat_probe_start = buffer->data + buffer->size; + flat_probe.parent.type = location->type; + + /* + * The lookup method, if present, is the last element in the flat + * representation of the probe. + */ + if (location->lookup_method) { + flat_probe.parent.lookup_method = + (struct lttng_userspace_probe_location_lookup_method *) + (flat_probe_start + sizeof(flat_probe) + + probe_name_len + provider_name_len + + binary_path_len + padding_needed); + } else { + flat_probe.parent.lookup_method = NULL; + } + + flat_probe.probe_name = flat_probe_start + sizeof(flat_probe); + flat_probe.provider_name = flat_probe.probe_name + probe_name_len; + flat_probe.binary_path = flat_probe.provider_name + provider_name_len; + flat_probe.binary_fd_handle = NULL; + ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe)); + if (ret) { + goto end; + } + + /* Append all the fields to the buffer */ + ret = lttng_dynamic_buffer_append(buffer, + probe_tracepoint->probe_name, probe_name_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buffer, + probe_tracepoint->provider_name, provider_name_len); + if (ret) { + goto end; + } + ret = lttng_dynamic_buffer_append(buffer, + probe_tracepoint->binary_path, binary_path_len); + if (ret) { + goto end; + } + + /* Insert padding before the lookup method. */ + ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed); + if (ret) { + goto end; + } + + if (!location->lookup_method) { + /* Not an error, the default method is used. */ + ret = storage_needed; + goto end; + } + + memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); + + flat_lookup_method.parent.type = + LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; + ret = lttng_dynamic_buffer_append(buffer, + &flat_lookup_method, sizeof(flat_lookup_method)); + if (ret) { + goto end; + } + ret = storage_needed; +end: + return ret; +} + +int lttng_userspace_probe_location_flatten( + const struct lttng_userspace_probe_location *location, + struct lttng_dynamic_buffer *buffer) +{ + int ret; + if (!location) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + /* Only types currently supported. */ + switch (location->type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + ret = lttng_userspace_probe_location_function_flatten(location, buffer); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + ret = lttng_userspace_probe_location_tracepoint_flatten(location, buffer); + break; + default: + ret = -LTTNG_ERR_INVALID; + goto end; + } + +end: + return ret; +} + +struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy( + const struct lttng_userspace_probe_location *location) +{ + struct lttng_userspace_probe_location *new_location = NULL; + enum lttng_userspace_probe_location_type type; + + if (!location) { + goto err; + } + + type = lttng_userspace_probe_location_get_type(location); + switch (type) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + new_location = + lttng_userspace_probe_location_function_copy(location); + if (!new_location) { + goto err; + } + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + new_location = + lttng_userspace_probe_location_tracepoint_copy(location); + if (!new_location) { + goto err; + } + break; + default: + new_location = NULL; + goto err; + } +err: + return new_location; +} + +bool lttng_userspace_probe_location_lookup_method_is_equal( + const struct lttng_userspace_probe_location_lookup_method *a, + const struct lttng_userspace_probe_location_lookup_method *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +bool lttng_userspace_probe_location_is_equal( + const struct lttng_userspace_probe_location *a, + const struct lttng_userspace_probe_location *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a == b) { + is_equal = true; + goto end; + } + + if (!lttng_userspace_probe_location_lookup_method_is_equal( + a->lookup_method, b->lookup_method)) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +unsigned long lttng_userspace_probe_location_hash( + const struct lttng_userspace_probe_location *location) +{ + return location->hash(location); +} + +enum lttng_error_code lttng_userspace_probe_location_mi_serialize( + const struct lttng_userspace_probe_location *location, + struct mi_writer *writer) +{ + typedef enum lttng_error_code (*mi_fp)( + const struct lttng_userspace_probe_location *, + struct mi_writer *); + + int ret; + enum lttng_error_code ret_code; + mi_fp mi_function = NULL; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + + switch (lttng_userspace_probe_location_get_type(location)) { + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: + mi_function = lttng_userspace_probe_location_function_mi_serialize; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: + mi_function = lttng_userspace_probe_location_tracepoint_mi_serialize; + break; + default: + abort(); + break; + } + + /* Open userspace probe location element. */ + ret = mi_lttng_writer_open_element( + writer, mi_lttng_element_userspace_probe_location); + if (ret) { + goto mi_error; + } + + /* Underlying user space probe location. */ + ret_code = mi_function(location, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close userspace probe location element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +enum lttng_error_code lttng_userspace_probe_location_lookup_method_mi_serialize( + const struct lttng_userspace_probe_location_lookup_method + *method, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *type_element_str; + + LTTNG_ASSERT(method); + LTTNG_ASSERT(writer); + + switch (lttng_userspace_probe_location_lookup_method_get_type(method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: + type_element_str = + mi_lttng_element_userspace_probe_location_lookup_method_function_default; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + type_element_str = + mi_lttng_element_userspace_probe_location_lookup_method_function_elf; + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + type_element_str = + mi_lttng_element_userspace_probe_location_lookup_method_tracepoint_sdt; + break; + default: + abort(); + break; + } + + /* Open userspace probe location lookup method element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_userspace_probe_location_lookup_method); + if (ret) { + goto mi_error; + } + + /* User space probe location lookup method empty element. */ + ret = mi_lttng_writer_open_element(writer, type_element_str); + if (ret) { + goto mi_error; + } + + /* Close userspace probe location lookup method element. */ + ret = mi_lttng_close_multi_element(writer, 2); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static enum lttng_error_code lttng_userspace_probe_location_tracepoint_mi_serialize( + const struct lttng_userspace_probe_location *location, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *probe_name = NULL; + const char *provider_name = NULL; + const char *binary_path = NULL; + const struct lttng_userspace_probe_location_lookup_method + *lookup_method = NULL; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + + probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name( + location); + provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name( + location); + binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path( + location); + lookup_method = lttng_userspace_probe_location_tracepoint_get_lookup_method( + location); + + /* Open userspace probe location tracepoint element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_userspace_probe_location_tracepoint); + if (ret) { + goto mi_error; + } + + /* Probe name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_tracepoint_probe_name, + probe_name); + if (ret) { + goto mi_error; + } + + /* Provider name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_tracepoint_provider_name, + provider_name); + if (ret) { + goto mi_error; + } + + /* Binary path. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_binary_path, + binary_path); + if (ret) { + goto mi_error; + } + + /* The lookup method. */ + ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize( + lookup_method, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close userspace probe location tracepoint. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} + +static enum lttng_error_code lttng_userspace_probe_location_function_mi_serialize( + const struct lttng_userspace_probe_location *location, + struct mi_writer *writer) +{ + int ret; + enum lttng_error_code ret_code; + const char *function_name = NULL; + const char *binary_path = NULL; + const char *instrumentation_type_str = NULL; + enum lttng_userspace_probe_location_function_instrumentation_type + instrumentation_type; + const struct lttng_userspace_probe_location_lookup_method + *lookup_method = NULL; + + LTTNG_ASSERT(location); + LTTNG_ASSERT(writer); + + function_name = lttng_userspace_probe_location_function_get_function_name( + location); + binary_path = lttng_userspace_probe_location_function_get_binary_path( + location); + instrumentation_type = + lttng_userspace_probe_location_function_get_instrumentation_type( + location); + lookup_method = lttng_userspace_probe_location_function_get_lookup_method( + location); + + switch (instrumentation_type) { + case LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY: + instrumentation_type_str = + mi_lttng_userspace_probe_location_function_instrumentation_type_entry; + break; + default: + abort(); + break; + } + + /* Open userspace probe location function element. */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_userspace_probe_location_function); + if (ret) { + goto mi_error; + } + + /* Function name. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_function_name, + function_name); + if (ret) { + goto mi_error; + } + + /* Binary path. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_binary_path, + binary_path); + if (ret) { + goto mi_error; + } + + /* Instrumentation type. */ + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_userspace_probe_location_function_instrumentation_type, + instrumentation_type_str); + if (ret) { + goto mi_error; + } + + /* The lookup method. */ + ret_code = lttng_userspace_probe_location_lookup_method_mi_serialize( + lookup_method, writer); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* Close userspace probe location function element. */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto mi_error; + } + + ret_code = LTTNG_OK; + goto end; + +mi_error: + ret_code = LTTNG_ERR_MI_IO_FAIL; +end: + return ret_code; +} diff --git a/src/common/utils.c b/src/common/utils.c deleted file mode 100644 index eebed2adb..000000000 --- a/src/common/utils.c +++ /dev/null @@ -1,1675 +0,0 @@ -/* - * Copyright (C) 2012 David Goulet - * Copyright (C) 2013 Raphaël Beamonte - * Copyright (C) 2013 Jérémie Galarneau - * - * SPDX-License-Identifier: GPL-2.0-only - * - */ - -#include "common/macros.h" -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "defaults.h" -#include "time.h" - -#define PROC_MEMINFO_PATH "/proc/meminfo" -#define PROC_MEMINFO_MEMAVAILABLE_LINE "MemAvailable:" -#define PROC_MEMINFO_MEMTOTAL_LINE "MemTotal:" - -/* The length of the longest field of `/proc/meminfo`. */ -#define PROC_MEMINFO_FIELD_MAX_NAME_LEN 20 - -#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20) -#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19" -#else -#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1) -#endif - -#define FALLBACK_USER_BUFLEN 16384 -#define FALLBACK_GROUP_BUFLEN 16384 - -/* - * Return a partial realpath(3) of the path even if the full path does not - * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist - * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with - * /test2/test3 then returned. In normal time, realpath(3) fails if the end - * point directory does not exist. - * - * Return a newly-allocated string. - */ -static -char *utils_partial_realpath(const char *path) -{ - char *cut_path = NULL, *try_path = NULL, *try_path_prev = NULL; - const char *next, *prev, *end; - char *resolved_path = NULL; - - /* Safety net */ - if (path == NULL) { - goto error; - } - - /* - * Identify the end of the path, we don't want to treat the - * last char if it is a '/', we will just keep it on the side - * to be added at the end, and return a value coherent with - * the path given as argument - */ - end = path + strlen(path); - if (*(end-1) == '/') { - end--; - } - - /* Initiate the values of the pointers before looping */ - next = path; - prev = next; - /* Only to ensure try_path is not NULL to enter the while */ - try_path = (char *)next; - - /* Resolve the canonical path of the first part of the path */ - while (try_path != NULL && next != end) { - char *try_path_buf = NULL; - - /* - * If there is not any '/' left, we want to try with - * the full path - */ - next = strpbrk(next + 1, "/"); - if (next == NULL) { - next = end; - } - - /* Cut the part we will be trying to resolve */ - cut_path = lttng_strndup(path, next - path); - if (cut_path == NULL) { - PERROR("lttng_strndup"); - goto error; - } - - try_path_buf = zmalloc(LTTNG_PATH_MAX); - if (!try_path_buf) { - PERROR("zmalloc"); - goto error; - } - - /* Try to resolve this part */ - try_path = realpath((char *) cut_path, try_path_buf); - if (try_path == NULL) { - free(try_path_buf); - /* - * There was an error, we just want to be assured it - * is linked to an unexistent directory, if it's another - * reason, we spawn an error - */ - switch (errno) { - case ENOENT: - /* Ignore the error */ - break; - default: - PERROR("realpath (partial_realpath)"); - goto error; - break; - } - } else { - /* Save the place we are before trying the next step */ - try_path_buf = NULL; - free(try_path_prev); - try_path_prev = try_path; - prev = next; - } - - /* Free the allocated memory */ - free(cut_path); - cut_path = NULL; - } - - /* Allocate memory for the resolved path. */ - resolved_path = zmalloc(LTTNG_PATH_MAX); - if (resolved_path == NULL) { - PERROR("zmalloc resolved path"); - goto error; - } - - /* - * If we were able to solve at least partially the path, we can concatenate - * what worked and what didn't work - */ - if (try_path_prev != NULL) { - /* If we risk to concatenate two '/', we remove one of them */ - if (try_path_prev[strlen(try_path_prev) - 1] == '/' && prev[0] == '/') { - try_path_prev[strlen(try_path_prev) - 1] = '\0'; - } - - /* - * Duplicate the memory used by prev in case resolved_path and - * path are pointers for the same memory space - */ - cut_path = strdup(prev); - if (cut_path == NULL) { - PERROR("strdup"); - goto error; - } - - /* Concatenate the strings */ - snprintf(resolved_path, LTTNG_PATH_MAX, "%s%s", - try_path_prev, cut_path); - - /* Free the allocated memory */ - free(cut_path); - free(try_path_prev); - cut_path = NULL; - try_path_prev = NULL; - /* - * Else, we just copy the path in our resolved_path to - * return it as is - */ - } else { - strncpy(resolved_path, path, LTTNG_PATH_MAX); - } - - /* Then we return the 'partially' resolved path */ - return resolved_path; - -error: - free(resolved_path); - free(cut_path); - free(try_path); - if (try_path_prev != try_path) { - free(try_path_prev); - } - return NULL; -} - -static -int expand_double_slashes_dot_and_dotdot(char *path) -{ - size_t expanded_path_len, path_len; - const char *curr_char, *path_last_char, *next_slash, *prev_slash; - - path_len = strlen(path); - path_last_char = &path[path_len]; - - if (path_len == 0) { - goto error; - } - - expanded_path_len = 0; - - /* We iterate over the provided path to expand the "//", "../" and "./" */ - for (curr_char = path; curr_char <= path_last_char; curr_char = next_slash + 1) { - /* Find the next forward slash. */ - size_t curr_token_len; - - if (curr_char == path_last_char) { - expanded_path_len++; - break; - } - - next_slash = memchr(curr_char, '/', path_last_char - curr_char); - if (next_slash == NULL) { - /* Reached the end of the provided path. */ - next_slash = path_last_char; - } - - /* Compute how long is the previous token. */ - curr_token_len = next_slash - curr_char; - switch(curr_token_len) { - case 0: - /* - * The pointer has not move meaning that curr_char is - * pointing to a slash. It that case there is no token - * to copy, so continue the iteration to find the next - * token - */ - continue; - case 1: - /* - * The pointer moved 1 character. Check if that - * character is a dot ('.'), if it is: omit it, else - * copy the token to the normalized path. - */ - if (curr_char[0] == '.') { - continue; - } - break; - case 2: - /* - * The pointer moved 2 characters. Check if these - * characters are double dots ('..'). If that is the - * case, we need to remove the last token of the - * normalized path. - */ - if (curr_char[0] == '.' && curr_char[1] == '.') { - /* - * Find the previous path component by - * using the memrchr function to find the - * previous forward slash and substract that - * len to the resulting path. - */ - prev_slash = lttng_memrchr(path, '/', expanded_path_len); - /* - * If prev_slash is NULL, we reached the - * beginning of the path. We can't go back any - * further. - */ - if (prev_slash != NULL) { - expanded_path_len = prev_slash - path; - } - continue; - } - break; - default: - break; - } - - /* - * Copy the current token which is neither a '.' nor a '..'. - */ - path[expanded_path_len++] = '/'; - memmove(&path[expanded_path_len], curr_char, curr_token_len); - expanded_path_len += curr_token_len; - } - - if (expanded_path_len == 0) { - path[expanded_path_len++] = '/'; - } - - path[expanded_path_len] = '\0'; - return 0; -error: - return -1; -} - -/* - * Make a full resolution of the given path even if it doesn't exist. - * This function uses the utils_partial_realpath function to resolve - * symlinks and relatives paths at the start of the string, and - * implements functionnalities to resolve the './' and '../' strings - * in the middle of a path. This function is only necessary because - * realpath(3) does not accept to resolve unexistent paths. - * The returned string was allocated in the function, it is thus of - * the responsibility of the caller to free this memory. - */ -static -char *_utils_expand_path(const char *path, bool keep_symlink) -{ - int ret; - char *absolute_path = NULL; - char *last_token; - bool is_dot, is_dotdot; - - /* Safety net */ - if (path == NULL) { - goto error; - } - - /* Allocate memory for the absolute_path */ - absolute_path = zmalloc(LTTNG_PATH_MAX); - if (absolute_path == NULL) { - PERROR("zmalloc expand path"); - goto error; - } - - if (path[0] == '/') { - ret = lttng_strncpy(absolute_path, path, LTTNG_PATH_MAX); - if (ret) { - ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX); - goto error; - } - } else { - /* - * This is a relative path. We need to get the present working - * directory and start the path walk from there. - */ - char current_working_dir[LTTNG_PATH_MAX]; - char *cwd_ret; - - cwd_ret = getcwd(current_working_dir, sizeof(current_working_dir)); - if (!cwd_ret) { - goto error; - } - /* - * Get the number of character in the CWD and allocate an array - * to can hold it and the path provided by the caller. - */ - ret = snprintf(absolute_path, LTTNG_PATH_MAX, "%s/%s", - current_working_dir, path); - if (ret >= LTTNG_PATH_MAX) { - ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes", - current_working_dir, path, LTTNG_PATH_MAX); - goto error; - } - } - - if (keep_symlink) { - /* Resolve partially our path */ - char *new_absolute_path = utils_partial_realpath(absolute_path); - if (!new_absolute_path) { - goto error; - } - - free(absolute_path); - absolute_path = new_absolute_path; - } - - ret = expand_double_slashes_dot_and_dotdot(absolute_path); - if (ret) { - goto error; - } - - /* Identify the last token */ - last_token = strrchr(absolute_path, '/'); - - /* Verify that this token is not a relative path */ - is_dotdot = (strcmp(last_token, "/..") == 0); - is_dot = (strcmp(last_token, "/.") == 0); - - /* If it is, take action */ - if (is_dot || is_dotdot) { - /* For both, remove this token */ - *last_token = '\0'; - - /* If it was a reference to parent directory, go back one more time */ - if (is_dotdot) { - last_token = strrchr(absolute_path, '/'); - - /* If there was only one level left, we keep the first '/' */ - if (last_token == absolute_path) { - last_token++; - } - - *last_token = '\0'; - } - } - - return absolute_path; - -error: - free(absolute_path); - return NULL; -} -char *utils_expand_path(const char *path) -{ - return _utils_expand_path(path, true); -} - -char *utils_expand_path_keep_symlink(const char *path) -{ - return _utils_expand_path(path, false); -} -/* - * Create a pipe in dst. - */ -int utils_create_pipe(int *dst) -{ - int ret; - - if (dst == NULL) { - return -1; - } - - ret = pipe(dst); - if (ret < 0) { - PERROR("create pipe"); - } - - return ret; -} - -/* - * Create pipe and set CLOEXEC flag to both fd. - * - * Make sure the pipe opened by this function are closed at some point. Use - * utils_close_pipe(). - */ -int utils_create_pipe_cloexec(int *dst) -{ - int ret, i; - - if (dst == NULL) { - return -1; - } - - ret = utils_create_pipe(dst); - if (ret < 0) { - goto error; - } - - for (i = 0; i < 2; i++) { - ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl pipe cloexec"); - goto error; - } - } - -error: - return ret; -} - -/* - * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK. - * - * Make sure the pipe opened by this function are closed at some point. Use - * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to - * support OSes other than Linux 2.6.23+. - */ -int utils_create_pipe_cloexec_nonblock(int *dst) -{ - int ret, i; - - if (dst == NULL) { - return -1; - } - - ret = utils_create_pipe(dst); - if (ret < 0) { - goto error; - } - - for (i = 0; i < 2; i++) { - ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl pipe cloexec"); - goto error; - } - /* - * Note: we override any flag that could have been - * previously set on the fd. - */ - ret = fcntl(dst[i], F_SETFL, O_NONBLOCK); - if (ret < 0) { - PERROR("fcntl pipe nonblock"); - goto error; - } - } - -error: - return ret; -} - -/* - * Close both read and write side of the pipe. - */ -void utils_close_pipe(int *src) -{ - int i, ret; - - if (src == NULL) { - return; - } - - for (i = 0; i < 2; i++) { - /* Safety check */ - if (src[i] < 0) { - continue; - } - - ret = close(src[i]); - if (ret) { - PERROR("close pipe"); - } - src[i] = -1; - } -} - -/* - * Create a new string using two strings range. - */ -char *utils_strdupdelim(const char *begin, const char *end) -{ - char *str; - - str = zmalloc(end - begin + 1); - if (str == NULL) { - PERROR("zmalloc strdupdelim"); - goto error; - } - - memcpy(str, begin, end - begin); - str[end - begin] = '\0'; - -error: - return str; -} - -/* - * Set CLOEXEC flag to the give file descriptor. - */ -int utils_set_fd_cloexec(int fd) -{ - int ret; - - if (fd < 0) { - ret = -EINVAL; - goto end; - } - - ret = fcntl(fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - PERROR("fcntl cloexec"); - ret = -errno; - } - -end: - return ret; -} - -/* - * Create pid file to the given path and filename. - */ -int utils_create_pid_file(pid_t pid, const char *filepath) -{ - int ret; - FILE *fp; - - LTTNG_ASSERT(filepath); - - fp = fopen(filepath, "w"); - if (fp == NULL) { - PERROR("open pid file %s", filepath); - ret = -1; - goto error; - } - - ret = fprintf(fp, "%d\n", (int) pid); - if (ret < 0) { - PERROR("fprintf pid file"); - goto error; - } - - if (fclose(fp)) { - PERROR("fclose"); - } - DBG("Pid %d written in file %s", (int) pid, filepath); - ret = 0; -error: - return ret; -} - -/* - * Create lock file to the given path and filename. - * Returns the associated file descriptor, -1 on error. - */ -int utils_create_lock_file(const char *filepath) -{ - int ret; - int fd; - struct flock lock; - - LTTNG_ASSERT(filepath); - - memset(&lock, 0, sizeof(lock)); - fd = open(filepath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | - S_IRGRP | S_IWGRP); - if (fd < 0) { - PERROR("open lock file %s", filepath); - fd = -1; - goto error; - } - - /* - * Attempt to lock the file. If this fails, there is - * already a process using the same lock file running - * and we should exit. - */ - lock.l_whence = SEEK_SET; - lock.l_type = F_WRLCK; - - ret = fcntl(fd, F_SETLK, &lock); - if (ret == -1) { - PERROR("fcntl lock file"); - ERR("Could not get lock file %s, another instance is running.", - filepath); - if (close(fd)) { - PERROR("close lock file"); - } - fd = ret; - goto error; - } - -error: - return fd; -} - -/* - * Create directory using the given path and mode. - * - * On success, return 0 else a negative error code. - */ -int utils_mkdir(const char *path, mode_t mode, int uid, int gid) -{ - int ret; - struct lttng_directory_handle *handle; - const struct lttng_credentials creds = { - .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), - .gid = LTTNG_OPTIONAL_INIT_VALUE(gid), - }; - - handle = lttng_directory_handle_create(NULL); - if (!handle) { - ret = -1; - goto end; - } - ret = lttng_directory_handle_create_subdirectory_as_user( - handle, path, mode, - (uid >= 0 || gid >= 0) ? &creds : NULL); -end: - lttng_directory_handle_put(handle); - return ret; -} - -/* - * Recursively create directory using the given path and mode, under the - * provided uid and gid. - * - * On success, return 0 else a negative error code. - */ -int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid) -{ - int ret; - struct lttng_directory_handle *handle; - const struct lttng_credentials creds = { - .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), - .gid = LTTNG_OPTIONAL_INIT_VALUE(gid), - }; - - handle = lttng_directory_handle_create(NULL); - if (!handle) { - ret = -1; - goto end; - } - ret = lttng_directory_handle_create_subdirectory_recursive_as_user( - handle, path, mode, - (uid >= 0 || gid >= 0) ? &creds : NULL); -end: - lttng_directory_handle_put(handle); - return ret; -} - -/* - * out_stream_path is the output parameter. - * - * Return 0 on success or else a negative value. - */ -int utils_stream_file_path(const char *path_name, const char *file_name, - uint64_t size, uint64_t count, const char *suffix, - char *out_stream_path, size_t stream_path_len) -{ - int ret; - char count_str[MAX_INT_DEC_LEN(count) + 1] = {}; - const char *path_separator; - - if (path_name && (path_name[0] == '\0' || - path_name[strlen(path_name) - 1] == '/')) { - path_separator = ""; - } else { - path_separator = "/"; - } - - path_name = path_name ? : ""; - suffix = suffix ? : ""; - if (size > 0) { - ret = snprintf(count_str, sizeof(count_str), "_%" PRIu64, - count); - LTTNG_ASSERT(ret > 0 && ret < sizeof(count_str)); - } - - ret = snprintf(out_stream_path, stream_path_len, "%s%s%s%s%s", - path_name, path_separator, file_name, count_str, - suffix); - if (ret < 0 || ret >= stream_path_len) { - ERR("Truncation occurred while formatting stream path"); - ret = -1; - } else { - ret = 0; - } - return ret; -} - -/** - * Parse a string that represents a size in human readable format. It - * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'. - * - * The suffix multiply the integer by: - * 'k': 1024 - * 'M': 1024^2 - * 'G': 1024^3 - * - * @param str The string to parse. - * @param size Pointer to a uint64_t that will be filled with the - * resulting size. - * - * @return 0 on success, -1 on failure. - */ -int utils_parse_size_suffix(const char * const str, uint64_t * const size) -{ - int ret; - uint64_t base_size; - long shift = 0; - const char *str_end; - char *num_end; - - if (!str) { - DBG("utils_parse_size_suffix: received a NULL string."); - ret = -1; - goto end; - } - - /* strtoull will accept a negative number, but we don't want to. */ - if (strchr(str, '-') != NULL) { - DBG("utils_parse_size_suffix: invalid size string, should not contain '-'."); - ret = -1; - goto end; - } - - /* str_end will point to the \0 */ - str_end = str + strlen(str); - errno = 0; - base_size = strtoull(str, &num_end, 0); - if (errno != 0) { - PERROR("utils_parse_size_suffix strtoull"); - ret = -1; - goto end; - } - - if (num_end == str) { - /* strtoull parsed nothing, not good. */ - DBG("utils_parse_size_suffix: strtoull had nothing good to parse."); - ret = -1; - goto end; - } - - /* Check if a prefix is present. */ - switch (*num_end) { - case 'G': - shift = GIBI_LOG2; - num_end++; - break; - case 'M': /* */ - shift = MEBI_LOG2; - num_end++; - break; - case 'K': - case 'k': - shift = KIBI_LOG2; - num_end++; - break; - case '\0': - break; - default: - DBG("utils_parse_size_suffix: invalid suffix."); - ret = -1; - goto end; - } - - /* Check for garbage after the valid input. */ - if (num_end != str_end) { - DBG("utils_parse_size_suffix: Garbage after size string."); - ret = -1; - goto end; - } - - *size = base_size << shift; - - /* Check for overflow */ - if ((*size >> shift) != base_size) { - DBG("utils_parse_size_suffix: oops, overflow detected."); - ret = -1; - goto end; - } - - ret = 0; -end: - return ret; -} - -/** - * Parse a string that represents a time in human readable format. It - * supports decimal integers suffixed by: - * "us" for microsecond, - * "ms" for millisecond, - * "s" for second, - * "m" for minute, - * "h" for hour - * - * The suffix multiply the integer by: - * "us" : 1 - * "ms" : 1000 - * "s" : 1000000 - * "m" : 60000000 - * "h" : 3600000000 - * - * Note that unit-less numbers are assumed to be microseconds. - * - * @param str The string to parse, assumed to be NULL-terminated. - * @param time_us Pointer to a uint64_t that will be filled with the - * resulting time in microseconds. - * - * @return 0 on success, -1 on failure. - */ -int utils_parse_time_suffix(char const * const str, uint64_t * const time_us) -{ - int ret; - uint64_t base_time; - uint64_t multiplier = 1; - const char *str_end; - char *num_end; - - if (!str) { - DBG("utils_parse_time_suffix: received a NULL string."); - ret = -1; - goto end; - } - - /* strtoull will accept a negative number, but we don't want to. */ - if (strchr(str, '-') != NULL) { - DBG("utils_parse_time_suffix: invalid time string, should not contain '-'."); - ret = -1; - goto end; - } - - /* str_end will point to the \0 */ - str_end = str + strlen(str); - errno = 0; - base_time = strtoull(str, &num_end, 10); - if (errno != 0) { - PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str); - ret = -1; - goto end; - } - - if (num_end == str) { - /* strtoull parsed nothing, not good. */ - DBG("utils_parse_time_suffix: strtoull had nothing good to parse."); - ret = -1; - goto end; - } - - /* Check if a prefix is present. */ - switch (*num_end) { - case 'u': - /* - * Microsecond (us) - * - * Skip the "us" if the string matches the "us" suffix, - * otherwise let the check for the end of the string handle - * the error reporting. - */ - if (*(num_end + 1) == 's') { - num_end += 2; - } - break; - case 'm': - if (*(num_end + 1) == 's') { - /* Millisecond (ms) */ - multiplier = USEC_PER_MSEC; - /* Skip the 's' */ - num_end++; - } else { - /* Minute (m) */ - multiplier = USEC_PER_MINUTE; - } - num_end++; - break; - case 's': - /* Second */ - multiplier = USEC_PER_SEC; - num_end++; - break; - case 'h': - /* Hour */ - multiplier = USEC_PER_HOURS; - num_end++; - break; - case '\0': - break; - default: - DBG("utils_parse_time_suffix: invalid suffix."); - ret = -1; - goto end; - } - - /* Check for garbage after the valid input. */ - if (num_end != str_end) { - DBG("utils_parse_time_suffix: Garbage after time string."); - ret = -1; - goto end; - } - - *time_us = base_time * multiplier; - - /* Check for overflow */ - if ((*time_us / multiplier) != base_time) { - DBG("utils_parse_time_suffix: oops, overflow detected."); - ret = -1; - goto end; - } - - ret = 0; -end: - return ret; -} - -/* - * fls: returns the position of the most significant bit. - * Returns 0 if no bit is set, else returns the position of the most - * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). - */ -#if defined(__i386) || defined(__x86_64) -static inline unsigned int fls_u32(uint32_t x) -{ - int r; - - asm("bsrl %1,%0\n\t" - "jnz 1f\n\t" - "movl $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U32 -#endif - -#if defined(__x86_64) && defined(__LP64__) -static inline -unsigned int fls_u64(uint64_t x) -{ - long r; - - asm("bsrq %1,%0\n\t" - "jnz 1f\n\t" - "movq $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U64 -#endif - -#ifndef HAS_FLS_U64 -static __attribute__((unused)) -unsigned int fls_u64(uint64_t x) -{ - unsigned int r = 64; - - if (!x) - return 0; - - if (!(x & 0xFFFFFFFF00000000ULL)) { - x <<= 32; - r -= 32; - } - if (!(x & 0xFFFF000000000000ULL)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF00000000000000ULL)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF000000000000000ULL)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC000000000000000ULL)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x8000000000000000ULL)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -#ifndef HAS_FLS_U32 -static __attribute__((unused)) unsigned int fls_u32(uint32_t x) -{ - unsigned int r = 32; - - if (!x) { - return 0; - } - if (!(x & 0xFFFF0000U)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF000000U)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF0000000U)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC0000000U)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000U)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int utils_get_count_order_u32(uint32_t x) -{ - if (!x) { - return -1; - } - - return fls_u32(x - 1); -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int utils_get_count_order_u64(uint64_t x) -{ - if (!x) { - return -1; - } - - return fls_u64(x - 1); -} - -/** - * Obtain the value of LTTNG_HOME environment variable, if exists. - * Otherwise returns the value of HOME. - */ -const char *utils_get_home_dir(void) -{ - char *val = NULL; - struct passwd *pwd; - - val = lttng_secure_getenv(DEFAULT_LTTNG_HOME_ENV_VAR); - if (val != NULL) { - goto end; - } - val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR); - if (val != NULL) { - goto end; - } - - /* Fallback on the password file entry. */ - pwd = getpwuid(getuid()); - if (!pwd) { - goto end; - } - val = pwd->pw_dir; - - DBG3("Home directory is '%s'", val); - -end: - return val; -} - -/** - * Get user's home directory. Dynamically allocated, must be freed - * by the caller. - */ -char *utils_get_user_home_dir(uid_t uid) -{ - struct passwd pwd; - struct passwd *result; - char *home_dir = NULL; - char *buf = NULL; - long buflen; - int ret; - - buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen == -1) { - goto end; - } -retry: - buf = zmalloc(buflen); - if (!buf) { - goto end; - } - - ret = getpwuid_r(uid, &pwd, buf, buflen, &result); - if (ret || !result) { - if (ret == ERANGE) { - free(buf); - buflen *= 2; - goto retry; - } - goto end; - } - - home_dir = strdup(pwd.pw_dir); -end: - free(buf); - return home_dir; -} - -/* - * With the given format, fill dst with the time of len maximum siz. - * - * Return amount of bytes set in the buffer or else 0 on error. - */ -size_t utils_get_current_time_str(const char *format, char *dst, size_t len) -{ - size_t ret; - time_t rawtime; - struct tm *timeinfo; - - LTTNG_ASSERT(format); - LTTNG_ASSERT(dst); - - /* Get date and time for session path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - ret = strftime(dst, len, format, timeinfo); - if (ret == 0) { - ERR("Unable to strftime with format %s at dst %p of len %zu", format, - dst, len); - } - - return ret; -} - -/* - * Return 0 on success and set *gid to the group_ID matching the passed name. - * Else -1 if it cannot be found or an error occurred. - */ -int utils_get_group_id(const char *name, bool warn, gid_t *gid) -{ - static volatile int warn_once; - int ret; - long sys_len; - size_t len; - struct group grp; - struct group *result; - struct lttng_dynamic_buffer buffer; - - /* Get the system limit, if it exists. */ - sys_len = sysconf(_SC_GETGR_R_SIZE_MAX); - if (sys_len == -1) { - len = 1024; - } else { - len = (size_t) sys_len; - } - - lttng_dynamic_buffer_init(&buffer); - ret = lttng_dynamic_buffer_set_size(&buffer, len); - if (ret) { - ERR("Failed to allocate group info buffer"); - ret = -1; - goto error; - } - - while ((ret = getgrnam_r(name, &grp, buffer.data, buffer.size, &result)) == ERANGE) { - const size_t new_len = 2 * buffer.size; - - /* Buffer is not big enough, increase its size. */ - if (new_len < buffer.size) { - ERR("Group info buffer size overflow"); - ret = -1; - goto error; - } - - ret = lttng_dynamic_buffer_set_size(&buffer, new_len); - if (ret) { - ERR("Failed to grow group info buffer to %zu bytes", - new_len); - ret = -1; - goto error; - } - } - if (ret) { - if (ret == ESRCH) { - DBG("Could not find group file entry for group name '%s'", - name); - } else { - PERROR("Failed to get group file entry for group name '%s'", - name); - } - - ret = -1; - goto error; - } - - /* Group not found. */ - if (!result) { - ret = -1; - goto error; - } - - *gid = result->gr_gid; - ret = 0; - -error: - if (ret && warn && !warn_once) { - WARN("No tracing group detected"); - warn_once = 1; - } - lttng_dynamic_buffer_reset(&buffer); - return ret; -} - -/* - * Return a newly allocated option string. This string is to be used as the - * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number - * of elements in the long_options array. Returns NULL if the string's - * allocation fails. - */ -char *utils_generate_optstring(const struct option *long_options, - size_t opt_count) -{ - int i; - size_t string_len = opt_count, str_pos = 0; - char *optstring; - - /* - * Compute the necessary string length. One letter per option, two when an - * argument is necessary, and a trailing NULL. - */ - for (i = 0; i < opt_count; i++) { - string_len += long_options[i].has_arg ? 1 : 0; - } - - optstring = zmalloc(string_len); - if (!optstring) { - goto end; - } - - for (i = 0; i < opt_count; i++) { - if (!long_options[i].name) { - /* Got to the trailing NULL element */ - break; - } - - if (long_options[i].val != '\0') { - optstring[str_pos++] = (char) long_options[i].val; - if (long_options[i].has_arg) { - optstring[str_pos++] = ':'; - } - } - } - -end: - return optstring; -} - -/* - * Try to remove a hierarchy of empty directories, recursively. Don't unlink - * any file. Try to rmdir any empty directory within the hierarchy. - */ -int utils_recursive_rmdir(const char *path) -{ - int ret; - struct lttng_directory_handle *handle; - - handle = lttng_directory_handle_create(NULL); - if (!handle) { - ret = -1; - goto end; - } - ret = lttng_directory_handle_remove_subdirectory(handle, path); -end: - lttng_directory_handle_put(handle); - return ret; -} - -int utils_truncate_stream_file(int fd, off_t length) -{ - int ret; - off_t lseek_ret; - - ret = ftruncate(fd, length); - if (ret < 0) { - PERROR("ftruncate"); - goto end; - } - lseek_ret = lseek(fd, length, SEEK_SET); - if (lseek_ret < 0) { - PERROR("lseek"); - ret = -1; - goto end; - } -end: - return ret; -} - -static const char *get_man_bin_path(void) -{ - char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV); - - if (env_man_path) { - return env_man_path; - } - - return DEFAULT_MAN_BIN_PATH; -} - -int utils_show_help(int section, const char *page_name, - const char *help_msg) -{ - char section_string[8]; - const char *man_bin_path = get_man_bin_path(); - int ret = 0; - - if (help_msg) { - printf("%s", help_msg); - goto end; - } - - /* Section integer -> section string */ - ret = sprintf(section_string, "%d", section); - LTTNG_ASSERT(ret > 0 && ret < 8); - - /* - * Execute man pager. - * - * We provide -M to man here because LTTng-tools can - * be installed outside /usr, in which case its man pages are - * not located in the default /usr/share/man directory. - */ - ret = execlp(man_bin_path, "man", "-M", MANPATH, - section_string, page_name, NULL); - -end: - return ret; -} - -static -int read_proc_meminfo_field(const char *field, size_t *value) -{ - int ret; - FILE *proc_meminfo; - char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {}; - - proc_meminfo = fopen(PROC_MEMINFO_PATH, "r"); - if (!proc_meminfo) { - PERROR("Failed to fopen() " PROC_MEMINFO_PATH); - ret = -1; - goto fopen_error; - } - - /* - * Read the contents of /proc/meminfo line by line to find the right - * field. - */ - while (!feof(proc_meminfo)) { - unsigned long value_kb; - - ret = fscanf(proc_meminfo, - "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n", - name, &value_kb); - if (ret == EOF) { - /* - * fscanf() returning EOF can indicate EOF or an error. - */ - if (ferror(proc_meminfo)) { - PERROR("Failed to parse " PROC_MEMINFO_PATH); - } - break; - } - - if (ret == 2 && strcmp(name, field) == 0) { - /* - * This number is displayed in kilo-bytes. Return the - * number of bytes. - */ - *value = ((size_t) value_kb) * 1024; - ret = 0; - goto found; - } - } - /* Reached the end of the file without finding the right field. */ - ret = -1; - -found: - fclose(proc_meminfo); -fopen_error: - return ret; -} - -/* - * Returns an estimate of the number of bytes of memory available based on the - * the information in `/proc/meminfo`. The number returned by this function is - * a best guess. - */ -int utils_get_memory_available(size_t *value) -{ - return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value); -} - -/* - * Returns the total size of the memory on the system in bytes based on the - * the information in `/proc/meminfo`. - */ -int utils_get_memory_total(size_t *value) -{ - return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value); -} - -int utils_change_working_directory(const char *path) -{ - int ret; - - LTTNG_ASSERT(path); - - DBG("Changing working directory to \"%s\"", path); - ret = chdir(path); - if (ret) { - PERROR("Failed to change working directory to \"%s\"", path); - goto end; - } - - /* Check for write access */ - if (access(path, W_OK)) { - if (errno == EACCES) { - /* - * Do not treat this as an error since the permission - * might change in the lifetime of the process - */ - DBG("Working directory \"%s\" is not writable", path); - } else { - PERROR("Failed to check if working directory \"%s\" is writable", - path); - } - } - -end: - return ret; -} - -enum lttng_error_code utils_user_id_from_name(const char *user_name, uid_t *uid) -{ - struct passwd p, *pres; - int ret; - enum lttng_error_code ret_val = LTTNG_OK; - char *buf = NULL; - ssize_t buflen; - - buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen < 0) { - buflen = FALLBACK_USER_BUFLEN; - } - - buf = zmalloc(buflen); - if (!buf) { - ret_val = LTTNG_ERR_NOMEM; - goto end; - } - - for (;;) { - ret = getpwnam_r(user_name, &p, buf, buflen, &pres); - switch (ret) { - case EINTR: - continue; - case ERANGE: - buflen *= 2; - free(buf); - buf = zmalloc(buflen); - if (!buf) { - ret_val = LTTNG_ERR_NOMEM; - goto end; - } - continue; - default: - goto end_loop; - } - } -end_loop: - - switch (ret) { - case 0: - if (pres == NULL) { - ret_val = LTTNG_ERR_USER_NOT_FOUND; - } else { - *uid = p.pw_uid; - DBG("Lookup of tracker UID/VUID: name '%s' maps to uid %" PRId64, - user_name, (int64_t) *uid); - ret_val = LTTNG_OK; - } - break; - case ENOENT: - case ESRCH: - case EBADF: - case EPERM: - ret_val = LTTNG_ERR_USER_NOT_FOUND; - break; - default: - ret_val = LTTNG_ERR_NOMEM; - } -end: - free(buf); - return ret_val; -} - -enum lttng_error_code utils_group_id_from_name( - const char *group_name, gid_t *gid) -{ - struct group g, *gres; - int ret; - enum lttng_error_code ret_val = LTTNG_OK; - char *buf = NULL; - ssize_t buflen; - - buflen = sysconf(_SC_GETGR_R_SIZE_MAX); - if (buflen < 0) { - buflen = FALLBACK_GROUP_BUFLEN; - } - - buf = zmalloc(buflen); - if (!buf) { - ret_val = LTTNG_ERR_NOMEM; - goto end; - } - - for (;;) { - ret = getgrnam_r(group_name, &g, buf, buflen, &gres); - switch (ret) { - case EINTR: - continue; - case ERANGE: - buflen *= 2; - free(buf); - buf = zmalloc(buflen); - if (!buf) { - ret_val = LTTNG_ERR_NOMEM; - goto end; - } - continue; - default: - goto end_loop; - } - } -end_loop: - - switch (ret) { - case 0: - if (gres == NULL) { - ret_val = LTTNG_ERR_GROUP_NOT_FOUND; - } else { - *gid = g.gr_gid; - DBG("Lookup of tracker GID/GUID: name '%s' maps to gid %" PRId64, - group_name, (int64_t) *gid); - ret_val = LTTNG_OK; - } - break; - case ENOENT: - case ESRCH: - case EBADF: - case EPERM: - ret_val = LTTNG_ERR_GROUP_NOT_FOUND; - break; - default: - ret_val = LTTNG_ERR_NOMEM; - } -end: - free(buf); - return ret_val; -} - -int utils_parse_unsigned_long_long(const char *str, - unsigned long long *value) -{ - int ret; - char *endptr; - - LTTNG_ASSERT(str); - LTTNG_ASSERT(value); - - errno = 0; - *value = strtoull(str, &endptr, 10); - - /* Conversion failed. Out of range? */ - if (errno != 0) { - /* Don't print an error; allow the caller to log a better error. */ - DBG("Failed to parse string as unsigned long long number: string = '%s', errno = %d", - str, errno); - ret = -1; - goto end; - } - - /* Not the end of the string or empty string. */ - if (*endptr || endptr == str) { - DBG("Failed to parse string as unsigned long long number: string = '%s'", - str); - ret = -1; - goto end; - } - - ret = 0; - -end: - return ret; -} diff --git a/src/common/utils.cpp b/src/common/utils.cpp new file mode 100644 index 000000000..8aa4ff9f9 --- /dev/null +++ b/src/common/utils.cpp @@ -0,0 +1,1675 @@ +/* + * Copyright (C) 2012 David Goulet + * Copyright (C) 2013 Raphaël Beamonte + * Copyright (C) 2013 Jérémie Galarneau + * + * SPDX-License-Identifier: GPL-2.0-only + * + */ + +#include "common/macros.h" +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "defaults.h" +#include "time.h" + +#define PROC_MEMINFO_PATH "/proc/meminfo" +#define PROC_MEMINFO_MEMAVAILABLE_LINE "MemAvailable:" +#define PROC_MEMINFO_MEMTOTAL_LINE "MemTotal:" + +/* The length of the longest field of `/proc/meminfo`. */ +#define PROC_MEMINFO_FIELD_MAX_NAME_LEN 20 + +#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20) +#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19" +#else +#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1) +#endif + +#define FALLBACK_USER_BUFLEN 16384 +#define FALLBACK_GROUP_BUFLEN 16384 + +/* + * Return a partial realpath(3) of the path even if the full path does not + * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist + * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with + * /test2/test3 then returned. In normal time, realpath(3) fails if the end + * point directory does not exist. + * + * Return a newly-allocated string. + */ +static +char *utils_partial_realpath(const char *path) +{ + char *cut_path = NULL, *try_path = NULL, *try_path_prev = NULL; + const char *next, *prev, *end; + char *resolved_path = NULL; + + /* Safety net */ + if (path == NULL) { + goto error; + } + + /* + * Identify the end of the path, we don't want to treat the + * last char if it is a '/', we will just keep it on the side + * to be added at the end, and return a value coherent with + * the path given as argument + */ + end = path + strlen(path); + if (*(end-1) == '/') { + end--; + } + + /* Initiate the values of the pointers before looping */ + next = path; + prev = next; + /* Only to ensure try_path is not NULL to enter the while */ + try_path = (char *)next; + + /* Resolve the canonical path of the first part of the path */ + while (try_path != NULL && next != end) { + char *try_path_buf = NULL; + + /* + * If there is not any '/' left, we want to try with + * the full path + */ + next = strpbrk(next + 1, "/"); + if (next == NULL) { + next = end; + } + + /* Cut the part we will be trying to resolve */ + cut_path = lttng_strndup(path, next - path); + if (cut_path == NULL) { + PERROR("lttng_strndup"); + goto error; + } + + try_path_buf = (char *) zmalloc(LTTNG_PATH_MAX); + if (!try_path_buf) { + PERROR("zmalloc"); + goto error; + } + + /* Try to resolve this part */ + try_path = realpath((char *) cut_path, try_path_buf); + if (try_path == NULL) { + free(try_path_buf); + /* + * There was an error, we just want to be assured it + * is linked to an unexistent directory, if it's another + * reason, we spawn an error + */ + switch (errno) { + case ENOENT: + /* Ignore the error */ + break; + default: + PERROR("realpath (partial_realpath)"); + goto error; + break; + } + } else { + /* Save the place we are before trying the next step */ + try_path_buf = NULL; + free(try_path_prev); + try_path_prev = try_path; + prev = next; + } + + /* Free the allocated memory */ + free(cut_path); + cut_path = NULL; + } + + /* Allocate memory for the resolved path. */ + resolved_path = (char *) zmalloc(LTTNG_PATH_MAX); + if (resolved_path == NULL) { + PERROR("zmalloc resolved path"); + goto error; + } + + /* + * If we were able to solve at least partially the path, we can concatenate + * what worked and what didn't work + */ + if (try_path_prev != NULL) { + /* If we risk to concatenate two '/', we remove one of them */ + if (try_path_prev[strlen(try_path_prev) - 1] == '/' && prev[0] == '/') { + try_path_prev[strlen(try_path_prev) - 1] = '\0'; + } + + /* + * Duplicate the memory used by prev in case resolved_path and + * path are pointers for the same memory space + */ + cut_path = strdup(prev); + if (cut_path == NULL) { + PERROR("strdup"); + goto error; + } + + /* Concatenate the strings */ + snprintf(resolved_path, LTTNG_PATH_MAX, "%s%s", + try_path_prev, cut_path); + + /* Free the allocated memory */ + free(cut_path); + free(try_path_prev); + cut_path = NULL; + try_path_prev = NULL; + /* + * Else, we just copy the path in our resolved_path to + * return it as is + */ + } else { + strncpy(resolved_path, path, LTTNG_PATH_MAX); + } + + /* Then we return the 'partially' resolved path */ + return resolved_path; + +error: + free(resolved_path); + free(cut_path); + free(try_path); + if (try_path_prev != try_path) { + free(try_path_prev); + } + return NULL; +} + +static +int expand_double_slashes_dot_and_dotdot(char *path) +{ + size_t expanded_path_len, path_len; + const char *curr_char, *path_last_char, *next_slash, *prev_slash; + + path_len = strlen(path); + path_last_char = &path[path_len]; + + if (path_len == 0) { + goto error; + } + + expanded_path_len = 0; + + /* We iterate over the provided path to expand the "//", "../" and "./" */ + for (curr_char = path; curr_char <= path_last_char; curr_char = next_slash + 1) { + /* Find the next forward slash. */ + size_t curr_token_len; + + if (curr_char == path_last_char) { + expanded_path_len++; + break; + } + + next_slash = (const char *) memchr(curr_char, '/', path_last_char - curr_char); + if (next_slash == NULL) { + /* Reached the end of the provided path. */ + next_slash = path_last_char; + } + + /* Compute how long is the previous token. */ + curr_token_len = next_slash - curr_char; + switch(curr_token_len) { + case 0: + /* + * The pointer has not move meaning that curr_char is + * pointing to a slash. It that case there is no token + * to copy, so continue the iteration to find the next + * token + */ + continue; + case 1: + /* + * The pointer moved 1 character. Check if that + * character is a dot ('.'), if it is: omit it, else + * copy the token to the normalized path. + */ + if (curr_char[0] == '.') { + continue; + } + break; + case 2: + /* + * The pointer moved 2 characters. Check if these + * characters are double dots ('..'). If that is the + * case, we need to remove the last token of the + * normalized path. + */ + if (curr_char[0] == '.' && curr_char[1] == '.') { + /* + * Find the previous path component by + * using the memrchr function to find the + * previous forward slash and substract that + * len to the resulting path. + */ + prev_slash = (const char *) lttng_memrchr(path, '/', expanded_path_len); + /* + * If prev_slash is NULL, we reached the + * beginning of the path. We can't go back any + * further. + */ + if (prev_slash != NULL) { + expanded_path_len = prev_slash - path; + } + continue; + } + break; + default: + break; + } + + /* + * Copy the current token which is neither a '.' nor a '..'. + */ + path[expanded_path_len++] = '/'; + memmove(&path[expanded_path_len], curr_char, curr_token_len); + expanded_path_len += curr_token_len; + } + + if (expanded_path_len == 0) { + path[expanded_path_len++] = '/'; + } + + path[expanded_path_len] = '\0'; + return 0; +error: + return -1; +} + +/* + * Make a full resolution of the given path even if it doesn't exist. + * This function uses the utils_partial_realpath function to resolve + * symlinks and relatives paths at the start of the string, and + * implements functionnalities to resolve the './' and '../' strings + * in the middle of a path. This function is only necessary because + * realpath(3) does not accept to resolve unexistent paths. + * The returned string was allocated in the function, it is thus of + * the responsibility of the caller to free this memory. + */ +static +char *_utils_expand_path(const char *path, bool keep_symlink) +{ + int ret; + char *absolute_path = NULL; + char *last_token; + bool is_dot, is_dotdot; + + /* Safety net */ + if (path == NULL) { + goto error; + } + + /* Allocate memory for the absolute_path */ + absolute_path = (char *) zmalloc(LTTNG_PATH_MAX); + if (absolute_path == NULL) { + PERROR("zmalloc expand path"); + goto error; + } + + if (path[0] == '/') { + ret = lttng_strncpy(absolute_path, path, LTTNG_PATH_MAX); + if (ret) { + ERR("Path exceeds maximal size of %i bytes", LTTNG_PATH_MAX); + goto error; + } + } else { + /* + * This is a relative path. We need to get the present working + * directory and start the path walk from there. + */ + char current_working_dir[LTTNG_PATH_MAX]; + char *cwd_ret; + + cwd_ret = getcwd(current_working_dir, sizeof(current_working_dir)); + if (!cwd_ret) { + goto error; + } + /* + * Get the number of character in the CWD and allocate an array + * to can hold it and the path provided by the caller. + */ + ret = snprintf(absolute_path, LTTNG_PATH_MAX, "%s/%s", + current_working_dir, path); + if (ret >= LTTNG_PATH_MAX) { + ERR("Concatenating current working directory %s and path %s exceeds maximal size of %i bytes", + current_working_dir, path, LTTNG_PATH_MAX); + goto error; + } + } + + if (keep_symlink) { + /* Resolve partially our path */ + char *new_absolute_path = utils_partial_realpath(absolute_path); + if (!new_absolute_path) { + goto error; + } + + free(absolute_path); + absolute_path = new_absolute_path; + } + + ret = expand_double_slashes_dot_and_dotdot(absolute_path); + if (ret) { + goto error; + } + + /* Identify the last token */ + last_token = strrchr(absolute_path, '/'); + + /* Verify that this token is not a relative path */ + is_dotdot = (strcmp(last_token, "/..") == 0); + is_dot = (strcmp(last_token, "/.") == 0); + + /* If it is, take action */ + if (is_dot || is_dotdot) { + /* For both, remove this token */ + *last_token = '\0'; + + /* If it was a reference to parent directory, go back one more time */ + if (is_dotdot) { + last_token = strrchr(absolute_path, '/'); + + /* If there was only one level left, we keep the first '/' */ + if (last_token == absolute_path) { + last_token++; + } + + *last_token = '\0'; + } + } + + return absolute_path; + +error: + free(absolute_path); + return NULL; +} +char *utils_expand_path(const char *path) +{ + return _utils_expand_path(path, true); +} + +char *utils_expand_path_keep_symlink(const char *path) +{ + return _utils_expand_path(path, false); +} +/* + * Create a pipe in dst. + */ +int utils_create_pipe(int *dst) +{ + int ret; + + if (dst == NULL) { + return -1; + } + + ret = pipe(dst); + if (ret < 0) { + PERROR("create pipe"); + } + + return ret; +} + +/* + * Create pipe and set CLOEXEC flag to both fd. + * + * Make sure the pipe opened by this function are closed at some point. Use + * utils_close_pipe(). + */ +int utils_create_pipe_cloexec(int *dst) +{ + int ret, i; + + if (dst == NULL) { + return -1; + } + + ret = utils_create_pipe(dst); + if (ret < 0) { + goto error; + } + + for (i = 0; i < 2; i++) { + ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl pipe cloexec"); + goto error; + } + } + +error: + return ret; +} + +/* + * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK. + * + * Make sure the pipe opened by this function are closed at some point. Use + * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to + * support OSes other than Linux 2.6.23+. + */ +int utils_create_pipe_cloexec_nonblock(int *dst) +{ + int ret, i; + + if (dst == NULL) { + return -1; + } + + ret = utils_create_pipe(dst); + if (ret < 0) { + goto error; + } + + for (i = 0; i < 2; i++) { + ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl pipe cloexec"); + goto error; + } + /* + * Note: we override any flag that could have been + * previously set on the fd. + */ + ret = fcntl(dst[i], F_SETFL, O_NONBLOCK); + if (ret < 0) { + PERROR("fcntl pipe nonblock"); + goto error; + } + } + +error: + return ret; +} + +/* + * Close both read and write side of the pipe. + */ +void utils_close_pipe(int *src) +{ + int i, ret; + + if (src == NULL) { + return; + } + + for (i = 0; i < 2; i++) { + /* Safety check */ + if (src[i] < 0) { + continue; + } + + ret = close(src[i]); + if (ret) { + PERROR("close pipe"); + } + src[i] = -1; + } +} + +/* + * Create a new string using two strings range. + */ +char *utils_strdupdelim(const char *begin, const char *end) +{ + char *str; + + str = (char *) zmalloc(end - begin + 1); + if (str == NULL) { + PERROR("zmalloc strdupdelim"); + goto error; + } + + memcpy(str, begin, end - begin); + str[end - begin] = '\0'; + +error: + return str; +} + +/* + * Set CLOEXEC flag to the give file descriptor. + */ +int utils_set_fd_cloexec(int fd) +{ + int ret; + + if (fd < 0) { + ret = -EINVAL; + goto end; + } + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl cloexec"); + ret = -errno; + } + +end: + return ret; +} + +/* + * Create pid file to the given path and filename. + */ +int utils_create_pid_file(pid_t pid, const char *filepath) +{ + int ret; + FILE *fp; + + LTTNG_ASSERT(filepath); + + fp = fopen(filepath, "w"); + if (fp == NULL) { + PERROR("open pid file %s", filepath); + ret = -1; + goto error; + } + + ret = fprintf(fp, "%d\n", (int) pid); + if (ret < 0) { + PERROR("fprintf pid file"); + goto error; + } + + if (fclose(fp)) { + PERROR("fclose"); + } + DBG("Pid %d written in file %s", (int) pid, filepath); + ret = 0; +error: + return ret; +} + +/* + * Create lock file to the given path and filename. + * Returns the associated file descriptor, -1 on error. + */ +int utils_create_lock_file(const char *filepath) +{ + int ret; + int fd; + struct flock lock; + + LTTNG_ASSERT(filepath); + + memset(&lock, 0, sizeof(lock)); + fd = open(filepath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP); + if (fd < 0) { + PERROR("open lock file %s", filepath); + fd = -1; + goto error; + } + + /* + * Attempt to lock the file. If this fails, there is + * already a process using the same lock file running + * and we should exit. + */ + lock.l_whence = SEEK_SET; + lock.l_type = F_WRLCK; + + ret = fcntl(fd, F_SETLK, &lock); + if (ret == -1) { + PERROR("fcntl lock file"); + ERR("Could not get lock file %s, another instance is running.", + filepath); + if (close(fd)) { + PERROR("close lock file"); + } + fd = ret; + goto error; + } + +error: + return fd; +} + +/* + * Create directory using the given path and mode. + * + * On success, return 0 else a negative error code. + */ +int utils_mkdir(const char *path, mode_t mode, int uid, int gid) +{ + int ret; + struct lttng_directory_handle *handle; + const struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid), + }; + + handle = lttng_directory_handle_create(NULL); + if (!handle) { + ret = -1; + goto end; + } + ret = lttng_directory_handle_create_subdirectory_as_user( + handle, path, mode, + (uid >= 0 || gid >= 0) ? &creds : NULL); +end: + lttng_directory_handle_put(handle); + return ret; +} + +/* + * Recursively create directory using the given path and mode, under the + * provided uid and gid. + * + * On success, return 0 else a negative error code. + */ +int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid) +{ + int ret; + struct lttng_directory_handle *handle; + const struct lttng_credentials creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid), + }; + + handle = lttng_directory_handle_create(NULL); + if (!handle) { + ret = -1; + goto end; + } + ret = lttng_directory_handle_create_subdirectory_recursive_as_user( + handle, path, mode, + (uid >= 0 || gid >= 0) ? &creds : NULL); +end: + lttng_directory_handle_put(handle); + return ret; +} + +/* + * out_stream_path is the output parameter. + * + * Return 0 on success or else a negative value. + */ +int utils_stream_file_path(const char *path_name, const char *file_name, + uint64_t size, uint64_t count, const char *suffix, + char *out_stream_path, size_t stream_path_len) +{ + int ret; + char count_str[MAX_INT_DEC_LEN(count) + 1] = {}; + const char *path_separator; + + if (path_name && (path_name[0] == '\0' || + path_name[strlen(path_name) - 1] == '/')) { + path_separator = ""; + } else { + path_separator = "/"; + } + + path_name = path_name ? : ""; + suffix = suffix ? : ""; + if (size > 0) { + ret = snprintf(count_str, sizeof(count_str), "_%" PRIu64, + count); + LTTNG_ASSERT(ret > 0 && ret < sizeof(count_str)); + } + + ret = snprintf(out_stream_path, stream_path_len, "%s%s%s%s%s", + path_name, path_separator, file_name, count_str, + suffix); + if (ret < 0 || ret >= stream_path_len) { + ERR("Truncation occurred while formatting stream path"); + ret = -1; + } else { + ret = 0; + } + return ret; +} + +/** + * Parse a string that represents a size in human readable format. It + * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'. + * + * The suffix multiply the integer by: + * 'k': 1024 + * 'M': 1024^2 + * 'G': 1024^3 + * + * @param str The string to parse. + * @param size Pointer to a uint64_t that will be filled with the + * resulting size. + * + * @return 0 on success, -1 on failure. + */ +int utils_parse_size_suffix(const char * const str, uint64_t * const size) +{ + int ret; + uint64_t base_size; + long shift = 0; + const char *str_end; + char *num_end; + + if (!str) { + DBG("utils_parse_size_suffix: received a NULL string."); + ret = -1; + goto end; + } + + /* strtoull will accept a negative number, but we don't want to. */ + if (strchr(str, '-') != NULL) { + DBG("utils_parse_size_suffix: invalid size string, should not contain '-'."); + ret = -1; + goto end; + } + + /* str_end will point to the \0 */ + str_end = str + strlen(str); + errno = 0; + base_size = strtoull(str, &num_end, 0); + if (errno != 0) { + PERROR("utils_parse_size_suffix strtoull"); + ret = -1; + goto end; + } + + if (num_end == str) { + /* strtoull parsed nothing, not good. */ + DBG("utils_parse_size_suffix: strtoull had nothing good to parse."); + ret = -1; + goto end; + } + + /* Check if a prefix is present. */ + switch (*num_end) { + case 'G': + shift = GIBI_LOG2; + num_end++; + break; + case 'M': /* */ + shift = MEBI_LOG2; + num_end++; + break; + case 'K': + case 'k': + shift = KIBI_LOG2; + num_end++; + break; + case '\0': + break; + default: + DBG("utils_parse_size_suffix: invalid suffix."); + ret = -1; + goto end; + } + + /* Check for garbage after the valid input. */ + if (num_end != str_end) { + DBG("utils_parse_size_suffix: Garbage after size string."); + ret = -1; + goto end; + } + + *size = base_size << shift; + + /* Check for overflow */ + if ((*size >> shift) != base_size) { + DBG("utils_parse_size_suffix: oops, overflow detected."); + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +/** + * Parse a string that represents a time in human readable format. It + * supports decimal integers suffixed by: + * "us" for microsecond, + * "ms" for millisecond, + * "s" for second, + * "m" for minute, + * "h" for hour + * + * The suffix multiply the integer by: + * "us" : 1 + * "ms" : 1000 + * "s" : 1000000 + * "m" : 60000000 + * "h" : 3600000000 + * + * Note that unit-less numbers are assumed to be microseconds. + * + * @param str The string to parse, assumed to be NULL-terminated. + * @param time_us Pointer to a uint64_t that will be filled with the + * resulting time in microseconds. + * + * @return 0 on success, -1 on failure. + */ +int utils_parse_time_suffix(char const * const str, uint64_t * const time_us) +{ + int ret; + uint64_t base_time; + uint64_t multiplier = 1; + const char *str_end; + char *num_end; + + if (!str) { + DBG("utils_parse_time_suffix: received a NULL string."); + ret = -1; + goto end; + } + + /* strtoull will accept a negative number, but we don't want to. */ + if (strchr(str, '-') != NULL) { + DBG("utils_parse_time_suffix: invalid time string, should not contain '-'."); + ret = -1; + goto end; + } + + /* str_end will point to the \0 */ + str_end = str + strlen(str); + errno = 0; + base_time = strtoull(str, &num_end, 10); + if (errno != 0) { + PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str); + ret = -1; + goto end; + } + + if (num_end == str) { + /* strtoull parsed nothing, not good. */ + DBG("utils_parse_time_suffix: strtoull had nothing good to parse."); + ret = -1; + goto end; + } + + /* Check if a prefix is present. */ + switch (*num_end) { + case 'u': + /* + * Microsecond (us) + * + * Skip the "us" if the string matches the "us" suffix, + * otherwise let the check for the end of the string handle + * the error reporting. + */ + if (*(num_end + 1) == 's') { + num_end += 2; + } + break; + case 'm': + if (*(num_end + 1) == 's') { + /* Millisecond (ms) */ + multiplier = USEC_PER_MSEC; + /* Skip the 's' */ + num_end++; + } else { + /* Minute (m) */ + multiplier = USEC_PER_MINUTE; + } + num_end++; + break; + case 's': + /* Second */ + multiplier = USEC_PER_SEC; + num_end++; + break; + case 'h': + /* Hour */ + multiplier = USEC_PER_HOURS; + num_end++; + break; + case '\0': + break; + default: + DBG("utils_parse_time_suffix: invalid suffix."); + ret = -1; + goto end; + } + + /* Check for garbage after the valid input. */ + if (num_end != str_end) { + DBG("utils_parse_time_suffix: Garbage after time string."); + ret = -1; + goto end; + } + + *time_us = base_time * multiplier; + + /* Check for overflow */ + if ((*time_us / multiplier) != base_time) { + DBG("utils_parse_time_suffix: oops, overflow detected."); + ret = -1; + goto end; + } + + ret = 0; +end: + return ret; +} + +/* + * fls: returns the position of the most significant bit. + * Returns 0 if no bit is set, else returns the position of the most + * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). + */ +#if defined(__i386) || defined(__x86_64) +static inline unsigned int fls_u32(uint32_t x) +{ + int r; + + asm("bsrl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U32 +#endif + +#if defined(__x86_64) && defined(__LP64__) +static inline +unsigned int fls_u64(uint64_t x) +{ + long r; + + asm("bsrq %1,%0\n\t" + "jnz 1f\n\t" + "movq $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U64 +#endif + +#ifndef HAS_FLS_U64 +static __attribute__((unused)) +unsigned int fls_u64(uint64_t x) +{ + unsigned int r = 64; + + if (!x) + return 0; + + if (!(x & 0xFFFFFFFF00000000ULL)) { + x <<= 32; + r -= 32; + } + if (!(x & 0xFFFF000000000000ULL)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF00000000000000ULL)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF000000000000000ULL)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC000000000000000ULL)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x8000000000000000ULL)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +#ifndef HAS_FLS_U32 +static __attribute__((unused)) unsigned int fls_u32(uint32_t x) +{ + unsigned int r = 32; + + if (!x) { + return 0; + } + if (!(x & 0xFFFF0000U)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF000000U)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF0000000U)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC0000000U)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000U)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int utils_get_count_order_u32(uint32_t x) +{ + if (!x) { + return -1; + } + + return fls_u32(x - 1); +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int utils_get_count_order_u64(uint64_t x) +{ + if (!x) { + return -1; + } + + return fls_u64(x - 1); +} + +/** + * Obtain the value of LTTNG_HOME environment variable, if exists. + * Otherwise returns the value of HOME. + */ +const char *utils_get_home_dir(void) +{ + char *val = NULL; + struct passwd *pwd; + + val = lttng_secure_getenv(DEFAULT_LTTNG_HOME_ENV_VAR); + if (val != NULL) { + goto end; + } + val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR); + if (val != NULL) { + goto end; + } + + /* Fallback on the password file entry. */ + pwd = getpwuid(getuid()); + if (!pwd) { + goto end; + } + val = pwd->pw_dir; + + DBG3("Home directory is '%s'", val); + +end: + return val; +} + +/** + * Get user's home directory. Dynamically allocated, must be freed + * by the caller. + */ +char *utils_get_user_home_dir(uid_t uid) +{ + struct passwd pwd; + struct passwd *result; + char *home_dir = NULL; + char *buf = NULL; + long buflen; + int ret; + + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) { + goto end; + } +retry: + buf = (char *) zmalloc(buflen); + if (!buf) { + goto end; + } + + ret = getpwuid_r(uid, &pwd, buf, buflen, &result); + if (ret || !result) { + if (ret == ERANGE) { + free(buf); + buflen *= 2; + goto retry; + } + goto end; + } + + home_dir = strdup(pwd.pw_dir); +end: + free(buf); + return home_dir; +} + +/* + * With the given format, fill dst with the time of len maximum siz. + * + * Return amount of bytes set in the buffer or else 0 on error. + */ +size_t utils_get_current_time_str(const char *format, char *dst, size_t len) +{ + size_t ret; + time_t rawtime; + struct tm *timeinfo; + + LTTNG_ASSERT(format); + LTTNG_ASSERT(dst); + + /* Get date and time for session path */ + time(&rawtime); + timeinfo = localtime(&rawtime); + ret = strftime(dst, len, format, timeinfo); + if (ret == 0) { + ERR("Unable to strftime with format %s at dst %p of len %zu", format, + dst, len); + } + + return ret; +} + +/* + * Return 0 on success and set *gid to the group_ID matching the passed name. + * Else -1 if it cannot be found or an error occurred. + */ +int utils_get_group_id(const char *name, bool warn, gid_t *gid) +{ + static volatile int warn_once; + int ret; + long sys_len; + size_t len; + struct group grp; + struct group *result; + struct lttng_dynamic_buffer buffer; + + /* Get the system limit, if it exists. */ + sys_len = sysconf(_SC_GETGR_R_SIZE_MAX); + if (sys_len == -1) { + len = 1024; + } else { + len = (size_t) sys_len; + } + + lttng_dynamic_buffer_init(&buffer); + ret = lttng_dynamic_buffer_set_size(&buffer, len); + if (ret) { + ERR("Failed to allocate group info buffer"); + ret = -1; + goto error; + } + + while ((ret = getgrnam_r(name, &grp, buffer.data, buffer.size, &result)) == ERANGE) { + const size_t new_len = 2 * buffer.size; + + /* Buffer is not big enough, increase its size. */ + if (new_len < buffer.size) { + ERR("Group info buffer size overflow"); + ret = -1; + goto error; + } + + ret = lttng_dynamic_buffer_set_size(&buffer, new_len); + if (ret) { + ERR("Failed to grow group info buffer to %zu bytes", + new_len); + ret = -1; + goto error; + } + } + if (ret) { + if (ret == ESRCH) { + DBG("Could not find group file entry for group name '%s'", + name); + } else { + PERROR("Failed to get group file entry for group name '%s'", + name); + } + + ret = -1; + goto error; + } + + /* Group not found. */ + if (!result) { + ret = -1; + goto error; + } + + *gid = result->gr_gid; + ret = 0; + +error: + if (ret && warn && !warn_once) { + WARN("No tracing group detected"); + warn_once = 1; + } + lttng_dynamic_buffer_reset(&buffer); + return ret; +} + +/* + * Return a newly allocated option string. This string is to be used as the + * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number + * of elements in the long_options array. Returns NULL if the string's + * allocation fails. + */ +char *utils_generate_optstring(const struct option *long_options, + size_t opt_count) +{ + int i; + size_t string_len = opt_count, str_pos = 0; + char *optstring; + + /* + * Compute the necessary string length. One letter per option, two when an + * argument is necessary, and a trailing NULL. + */ + for (i = 0; i < opt_count; i++) { + string_len += long_options[i].has_arg ? 1 : 0; + } + + optstring = (char *) zmalloc(string_len); + if (!optstring) { + goto end; + } + + for (i = 0; i < opt_count; i++) { + if (!long_options[i].name) { + /* Got to the trailing NULL element */ + break; + } + + if (long_options[i].val != '\0') { + optstring[str_pos++] = (char) long_options[i].val; + if (long_options[i].has_arg) { + optstring[str_pos++] = ':'; + } + } + } + +end: + return optstring; +} + +/* + * Try to remove a hierarchy of empty directories, recursively. Don't unlink + * any file. Try to rmdir any empty directory within the hierarchy. + */ +int utils_recursive_rmdir(const char *path) +{ + int ret; + struct lttng_directory_handle *handle; + + handle = lttng_directory_handle_create(NULL); + if (!handle) { + ret = -1; + goto end; + } + ret = lttng_directory_handle_remove_subdirectory(handle, path); +end: + lttng_directory_handle_put(handle); + return ret; +} + +int utils_truncate_stream_file(int fd, off_t length) +{ + int ret; + off_t lseek_ret; + + ret = ftruncate(fd, length); + if (ret < 0) { + PERROR("ftruncate"); + goto end; + } + lseek_ret = lseek(fd, length, SEEK_SET); + if (lseek_ret < 0) { + PERROR("lseek"); + ret = -1; + goto end; + } +end: + return ret; +} + +static const char *get_man_bin_path(void) +{ + char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV); + + if (env_man_path) { + return env_man_path; + } + + return DEFAULT_MAN_BIN_PATH; +} + +int utils_show_help(int section, const char *page_name, + const char *help_msg) +{ + char section_string[8]; + const char *man_bin_path = get_man_bin_path(); + int ret = 0; + + if (help_msg) { + printf("%s", help_msg); + goto end; + } + + /* Section integer -> section string */ + ret = sprintf(section_string, "%d", section); + LTTNG_ASSERT(ret > 0 && ret < 8); + + /* + * Execute man pager. + * + * We provide -M to man here because LTTng-tools can + * be installed outside /usr, in which case its man pages are + * not located in the default /usr/share/man directory. + */ + ret = execlp(man_bin_path, "man", "-M", MANPATH, + section_string, page_name, NULL); + +end: + return ret; +} + +static +int read_proc_meminfo_field(const char *field, size_t *value) +{ + int ret; + FILE *proc_meminfo; + char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {}; + + proc_meminfo = fopen(PROC_MEMINFO_PATH, "r"); + if (!proc_meminfo) { + PERROR("Failed to fopen() " PROC_MEMINFO_PATH); + ret = -1; + goto fopen_error; + } + + /* + * Read the contents of /proc/meminfo line by line to find the right + * field. + */ + while (!feof(proc_meminfo)) { + unsigned long value_kb; + + ret = fscanf(proc_meminfo, + "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %lu kB\n", + name, &value_kb); + if (ret == EOF) { + /* + * fscanf() returning EOF can indicate EOF or an error. + */ + if (ferror(proc_meminfo)) { + PERROR("Failed to parse " PROC_MEMINFO_PATH); + } + break; + } + + if (ret == 2 && strcmp(name, field) == 0) { + /* + * This number is displayed in kilo-bytes. Return the + * number of bytes. + */ + *value = ((size_t) value_kb) * 1024; + ret = 0; + goto found; + } + } + /* Reached the end of the file without finding the right field. */ + ret = -1; + +found: + fclose(proc_meminfo); +fopen_error: + return ret; +} + +/* + * Returns an estimate of the number of bytes of memory available based on the + * the information in `/proc/meminfo`. The number returned by this function is + * a best guess. + */ +int utils_get_memory_available(size_t *value) +{ + return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value); +} + +/* + * Returns the total size of the memory on the system in bytes based on the + * the information in `/proc/meminfo`. + */ +int utils_get_memory_total(size_t *value) +{ + return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value); +} + +int utils_change_working_directory(const char *path) +{ + int ret; + + LTTNG_ASSERT(path); + + DBG("Changing working directory to \"%s\"", path); + ret = chdir(path); + if (ret) { + PERROR("Failed to change working directory to \"%s\"", path); + goto end; + } + + /* Check for write access */ + if (access(path, W_OK)) { + if (errno == EACCES) { + /* + * Do not treat this as an error since the permission + * might change in the lifetime of the process + */ + DBG("Working directory \"%s\" is not writable", path); + } else { + PERROR("Failed to check if working directory \"%s\" is writable", + path); + } + } + +end: + return ret; +} + +enum lttng_error_code utils_user_id_from_name(const char *user_name, uid_t *uid) +{ + struct passwd p, *pres; + int ret; + enum lttng_error_code ret_val = LTTNG_OK; + char *buf = NULL; + ssize_t buflen; + + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen < 0) { + buflen = FALLBACK_USER_BUFLEN; + } + + buf = (char *) zmalloc(buflen); + if (!buf) { + ret_val = LTTNG_ERR_NOMEM; + goto end; + } + + for (;;) { + ret = getpwnam_r(user_name, &p, buf, buflen, &pres); + switch (ret) { + case EINTR: + continue; + case ERANGE: + buflen *= 2; + free(buf); + buf = (char *) zmalloc(buflen); + if (!buf) { + ret_val = LTTNG_ERR_NOMEM; + goto end; + } + continue; + default: + goto end_loop; + } + } +end_loop: + + switch (ret) { + case 0: + if (pres == NULL) { + ret_val = LTTNG_ERR_USER_NOT_FOUND; + } else { + *uid = p.pw_uid; + DBG("Lookup of tracker UID/VUID: name '%s' maps to uid %" PRId64, + user_name, (int64_t) *uid); + ret_val = LTTNG_OK; + } + break; + case ENOENT: + case ESRCH: + case EBADF: + case EPERM: + ret_val = LTTNG_ERR_USER_NOT_FOUND; + break; + default: + ret_val = LTTNG_ERR_NOMEM; + } +end: + free(buf); + return ret_val; +} + +enum lttng_error_code utils_group_id_from_name( + const char *group_name, gid_t *gid) +{ + struct group g, *gres; + int ret; + enum lttng_error_code ret_val = LTTNG_OK; + char *buf = NULL; + ssize_t buflen; + + buflen = sysconf(_SC_GETGR_R_SIZE_MAX); + if (buflen < 0) { + buflen = FALLBACK_GROUP_BUFLEN; + } + + buf = (char *) zmalloc(buflen); + if (!buf) { + ret_val = LTTNG_ERR_NOMEM; + goto end; + } + + for (;;) { + ret = getgrnam_r(group_name, &g, buf, buflen, &gres); + switch (ret) { + case EINTR: + continue; + case ERANGE: + buflen *= 2; + free(buf); + buf = (char *) zmalloc(buflen); + if (!buf) { + ret_val = LTTNG_ERR_NOMEM; + goto end; + } + continue; + default: + goto end_loop; + } + } +end_loop: + + switch (ret) { + case 0: + if (gres == NULL) { + ret_val = LTTNG_ERR_GROUP_NOT_FOUND; + } else { + *gid = g.gr_gid; + DBG("Lookup of tracker GID/GUID: name '%s' maps to gid %" PRId64, + group_name, (int64_t) *gid); + ret_val = LTTNG_OK; + } + break; + case ENOENT: + case ESRCH: + case EBADF: + case EPERM: + ret_val = LTTNG_ERR_GROUP_NOT_FOUND; + break; + default: + ret_val = LTTNG_ERR_NOMEM; + } +end: + free(buf); + return ret_val; +} + +int utils_parse_unsigned_long_long(const char *str, + unsigned long long *value) +{ + int ret; + char *endptr; + + LTTNG_ASSERT(str); + LTTNG_ASSERT(value); + + errno = 0; + *value = strtoull(str, &endptr, 10); + + /* Conversion failed. Out of range? */ + if (errno != 0) { + /* Don't print an error; allow the caller to log a better error. */ + DBG("Failed to parse string as unsigned long long number: string = '%s', errno = %d", + str, errno); + ret = -1; + goto end; + } + + /* Not the end of the string or empty string. */ + if (*endptr || endptr == str) { + DBG("Failed to parse string as unsigned long long number: string = '%s'", + str); + ret = -1; + goto end; + } + + ret = 0; + +end: + return ret; +} diff --git a/src/common/uuid.c b/src/common/uuid.c deleted file mode 100644 index 26fb61e99..000000000 --- a/src/common/uuid.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2018 Jérémie Galarneau - * Copyright (C) 2019 Michael Jeanson - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "uuid.h" - -static const lttng_uuid nil_uuid; -static bool lttng_uuid_is_init; - -void lttng_uuid_to_str(const lttng_uuid uuid, char *uuid_str) -{ - sprintf(uuid_str, LTTNG_UUID_FMT, LTTNG_UUID_FMT_VALUES(uuid)); -} - -int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out) -{ - int ret = 0; - lttng_uuid uuid_scan; - - if ((str_in == NULL) || (uuid_out == NULL)) { - ret = -1; - goto end; - } - - if (lttng_strnlen(str_in, LTTNG_UUID_STR_LEN) != LTTNG_UUID_STR_LEN - 1) { - ret = -1; - goto end; - } - - /* Scan to a temporary location in case of a partial match. */ - if (sscanf(str_in, LTTNG_UUID_FMT, LTTNG_UUID_SCAN_VALUES(uuid_scan)) != - LTTNG_UUID_LEN) { - ret = -1; - } - - lttng_uuid_copy(uuid_out, uuid_scan); -end: - return ret; -} - -bool lttng_uuid_is_equal(const lttng_uuid a, const lttng_uuid b) -{ - return memcmp(a, b, LTTNG_UUID_LEN) == 0; -} - -bool lttng_uuid_is_nil(const lttng_uuid uuid) -{ - return memcmp(nil_uuid, uuid, sizeof(lttng_uuid)) == 0; -} - -void lttng_uuid_copy(lttng_uuid dst, const lttng_uuid src) -{ - memcpy(dst, src, LTTNG_UUID_LEN); -} - -/* - * Generate a random UUID according to RFC4122, section 4.4. - */ -int lttng_uuid_generate(lttng_uuid uuid_out) -{ - int i, ret = 0; - - if (uuid_out == NULL) { - ret = -1; - goto end; - } - - if (!lttng_uuid_is_init) { - /* - * We don't need cryptographic quality randomness to - * generate UUIDs, seed rand with the epoch. - */ - const time_t epoch = time(NULL); - - if (epoch == (time_t) -1) { - ret = -1; - goto end; - } - srand(epoch); - - lttng_uuid_is_init = true; - } - - /* - * Generate 16 bytes of random bits. - */ - for (i = 0; i < LTTNG_UUID_LEN; i++) { - uuid_out[i] = (uint8_t) rand(); - } - - /* - * Set the two most significant bits (bits 6 and 7) of the - * clock_seq_hi_and_reserved to zero and one, respectively. - */ - uuid_out[8] &= ~(1 << 6); - uuid_out[8] |= (1 << 7); - - /* - * Set the four most significant bits (bits 12 through 15) of the - * time_hi_and_version field to the 4-bit version number from - * Section 4.1.3. - */ - uuid_out[6] &= 0x0f; - uuid_out[6] |= (LTTNG_UUID_VER << 4); - -end: - return ret; -} diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp new file mode 100644 index 000000000..77b9a767b --- /dev/null +++ b/src/common/uuid.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 Jérémie Galarneau + * Copyright (C) 2019 Michael Jeanson + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "uuid.h" + +static const lttng_uuid nil_uuid = { 0 }; +static bool lttng_uuid_is_init; + +void lttng_uuid_to_str(const lttng_uuid uuid, char *uuid_str) +{ + sprintf(uuid_str, LTTNG_UUID_FMT, LTTNG_UUID_FMT_VALUES(uuid)); +} + +int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out) +{ + int ret = 0; + lttng_uuid uuid_scan; + + if ((str_in == NULL) || (uuid_out == NULL)) { + ret = -1; + goto end; + } + + if (lttng_strnlen(str_in, LTTNG_UUID_STR_LEN) != LTTNG_UUID_STR_LEN - 1) { + ret = -1; + goto end; + } + + /* Scan to a temporary location in case of a partial match. */ + if (sscanf(str_in, LTTNG_UUID_FMT, LTTNG_UUID_SCAN_VALUES(uuid_scan)) != + LTTNG_UUID_LEN) { + ret = -1; + } + + lttng_uuid_copy(uuid_out, uuid_scan); +end: + return ret; +} + +bool lttng_uuid_is_equal(const lttng_uuid a, const lttng_uuid b) +{ + return memcmp(a, b, LTTNG_UUID_LEN) == 0; +} + +bool lttng_uuid_is_nil(const lttng_uuid uuid) +{ + return memcmp(nil_uuid, uuid, sizeof(lttng_uuid)) == 0; +} + +void lttng_uuid_copy(lttng_uuid dst, const lttng_uuid src) +{ + memcpy(dst, src, LTTNG_UUID_LEN); +} + +/* + * Generate a random UUID according to RFC4122, section 4.4. + */ +int lttng_uuid_generate(lttng_uuid uuid_out) +{ + int i, ret = 0; + + if (uuid_out == NULL) { + ret = -1; + goto end; + } + + if (!lttng_uuid_is_init) { + /* + * We don't need cryptographic quality randomness to + * generate UUIDs, seed rand with the epoch. + */ + const time_t epoch = time(NULL); + + if (epoch == (time_t) -1) { + ret = -1; + goto end; + } + srand(epoch); + + lttng_uuid_is_init = true; + } + + /* + * Generate 16 bytes of random bits. + */ + for (i = 0; i < LTTNG_UUID_LEN; i++) { + uuid_out[i] = (uint8_t) rand(); + } + + /* + * Set the two most significant bits (bits 6 and 7) of the + * clock_seq_hi_and_reserved to zero and one, respectively. + */ + uuid_out[8] &= ~(1 << 6); + uuid_out[8] |= (1 << 7); + + /* + * Set the four most significant bits (bits 12 through 15) of the + * time_hi_and_version field to the 4-bit version number from + * Section 4.1.3. + */ + uuid_out[6] &= 0x0f; + uuid_out[6] |= (LTTNG_UUID_VER << 4); + +end: + return ret; +} diff --git a/src/common/waiter.c b/src/common/waiter.c deleted file mode 100644 index aca88eb4d..000000000 --- a/src/common/waiter.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2012 Mathieu Desnoyers - * Copyright (C) 2017 Jérémie Galarneau - * - * SPDX-License-Identifier: LGPL-2.1-only - * - */ - -#include "waiter.h" -#include -#include -#include "error.h" -#include - -/* - * Number of busy-loop attempts before waiting on futex. - */ -#define WAIT_ATTEMPTS 1000 - -enum waiter_state { - /* WAITER_WAITING is compared directly (futex compares it). */ - WAITER_WAITING = 0, - /* non-zero are used as masks. */ - WAITER_WOKEN_UP = (1 << 0), - WAITER_RUNNING = (1 << 1), - WAITER_TEARDOWN = (1 << 2), -}; - -void lttng_waiter_init(struct lttng_waiter *waiter) -{ - cds_wfs_node_init(&waiter->wait_queue_node); - uatomic_set(&waiter->state, WAITER_WAITING); - cmm_smp_mb(); -} - -/* - * User must init "waiter" before passing its memory to waker thread. - */ -void lttng_waiter_wait(struct lttng_waiter *waiter) -{ - unsigned int i; - - DBG("Beginning of waiter wait period"); - /* Load and test condition before read state */ - cmm_smp_rmb(); - for (i = 0; i < WAIT_ATTEMPTS; i++) { - if (uatomic_read(&waiter->state) != WAITER_WAITING) { - goto skip_futex_wait; - } - caa_cpu_relax(); - } - while (futex_noasync(&waiter->state, FUTEX_WAIT, WAITER_WAITING, - NULL, NULL, 0)) { - switch (errno) { - case EWOULDBLOCK: - /* Value already changed. */ - goto skip_futex_wait; - case EINTR: - /* Retry if interrupted by signal. */ - break; /* Get out of switch. */ - default: - /* Unexpected error. */ - PERROR("futex_noasync"); - abort(); - } - } -skip_futex_wait: - - /* Tell waker thread than we are running. */ - uatomic_or(&waiter->state, WAITER_RUNNING); - - /* - * Wait until waker thread lets us know it's ok to tear down - * memory allocated for struct lttng_waiter. - */ - for (i = 0; i < WAIT_ATTEMPTS; i++) { - if (uatomic_read(&waiter->state) & WAITER_TEARDOWN) { - break; - } - caa_cpu_relax(); - } - while (!(uatomic_read(&waiter->state) & WAITER_TEARDOWN)) { - poll(NULL, 0, 10); - } - LTTNG_ASSERT(uatomic_read(&waiter->state) & WAITER_TEARDOWN); - DBG("End of waiter wait period"); -} - -/* - * Note: lttng_waiter_wake needs waiter to stay allocated throughout its - * execution. In this scheme, the waiter owns the node memory, and we only allow - * it to free this memory when it sees the WAITER_TEARDOWN flag. - */ -void lttng_waiter_wake_up(struct lttng_waiter *waiter) -{ - cmm_smp_mb(); - LTTNG_ASSERT(uatomic_read(&waiter->state) == WAITER_WAITING); - uatomic_set(&waiter->state, WAITER_WOKEN_UP); - if (!(uatomic_read(&waiter->state) & WAITER_RUNNING)) { - if (futex_noasync(&waiter->state, FUTEX_WAKE, 1, - NULL, NULL, 0) < 0) { - PERROR("futex_noasync"); - abort(); - } - } - /* Allow teardown of struct urcu_wait memory. */ - uatomic_or(&waiter->state, WAITER_TEARDOWN); -} diff --git a/src/common/waiter.cpp b/src/common/waiter.cpp new file mode 100644 index 000000000..aca88eb4d --- /dev/null +++ b/src/common/waiter.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 Mathieu Desnoyers + * Copyright (C) 2017 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include "waiter.h" +#include +#include +#include "error.h" +#include + +/* + * Number of busy-loop attempts before waiting on futex. + */ +#define WAIT_ATTEMPTS 1000 + +enum waiter_state { + /* WAITER_WAITING is compared directly (futex compares it). */ + WAITER_WAITING = 0, + /* non-zero are used as masks. */ + WAITER_WOKEN_UP = (1 << 0), + WAITER_RUNNING = (1 << 1), + WAITER_TEARDOWN = (1 << 2), +}; + +void lttng_waiter_init(struct lttng_waiter *waiter) +{ + cds_wfs_node_init(&waiter->wait_queue_node); + uatomic_set(&waiter->state, WAITER_WAITING); + cmm_smp_mb(); +} + +/* + * User must init "waiter" before passing its memory to waker thread. + */ +void lttng_waiter_wait(struct lttng_waiter *waiter) +{ + unsigned int i; + + DBG("Beginning of waiter wait period"); + /* Load and test condition before read state */ + cmm_smp_rmb(); + for (i = 0; i < WAIT_ATTEMPTS; i++) { + if (uatomic_read(&waiter->state) != WAITER_WAITING) { + goto skip_futex_wait; + } + caa_cpu_relax(); + } + while (futex_noasync(&waiter->state, FUTEX_WAIT, WAITER_WAITING, + NULL, NULL, 0)) { + switch (errno) { + case EWOULDBLOCK: + /* Value already changed. */ + goto skip_futex_wait; + case EINTR: + /* Retry if interrupted by signal. */ + break; /* Get out of switch. */ + default: + /* Unexpected error. */ + PERROR("futex_noasync"); + abort(); + } + } +skip_futex_wait: + + /* Tell waker thread than we are running. */ + uatomic_or(&waiter->state, WAITER_RUNNING); + + /* + * Wait until waker thread lets us know it's ok to tear down + * memory allocated for struct lttng_waiter. + */ + for (i = 0; i < WAIT_ATTEMPTS; i++) { + if (uatomic_read(&waiter->state) & WAITER_TEARDOWN) { + break; + } + caa_cpu_relax(); + } + while (!(uatomic_read(&waiter->state) & WAITER_TEARDOWN)) { + poll(NULL, 0, 10); + } + LTTNG_ASSERT(uatomic_read(&waiter->state) & WAITER_TEARDOWN); + DBG("End of waiter wait period"); +} + +/* + * Note: lttng_waiter_wake needs waiter to stay allocated throughout its + * execution. In this scheme, the waiter owns the node memory, and we only allow + * it to free this memory when it sees the WAITER_TEARDOWN flag. + */ +void lttng_waiter_wake_up(struct lttng_waiter *waiter) +{ + cmm_smp_mb(); + LTTNG_ASSERT(uatomic_read(&waiter->state) == WAITER_WAITING); + uatomic_set(&waiter->state, WAITER_WOKEN_UP); + if (!(uatomic_read(&waiter->state) & WAITER_RUNNING)) { + if (futex_noasync(&waiter->state, FUTEX_WAKE, 1, + NULL, NULL, 0) < 0) { + PERROR("futex_noasync"); + abort(); + } + } + /* Allow teardown of struct urcu_wait memory. */ + uatomic_or(&waiter->state, WAITER_TEARDOWN); +} diff --git a/tests/regression/tools/notification/Makefile.am b/tests/regression/tools/notification/Makefile.am index eaab5314a..2f1a9174e 100644 --- a/tests/regression/tools/notification/Makefile.am +++ b/tests/regression/tools/notification/Makefile.am @@ -62,7 +62,7 @@ notification_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm rotation_SOURCES = rotation.c rotation_LDADD = $(LIB_LTTNG_CTL) $(LIBTAP) -lm -default_pipe_size_getter_SOURCES = default_pipe_size_getter.c +default_pipe_size_getter_SOURCES = default_pipe_size_getter.cpp default_pipe_size_getter_LDADD = $(top_builddir)/src/common/libcommon.la noinst_SCRIPTS = \ diff --git a/tests/regression/tools/notification/default_pipe_size_getter.c b/tests/regression/tools/notification/default_pipe_size_getter.c deleted file mode 100644 index cc24cec1e..000000000 --- a/tests/regression/tools/notification/default_pipe_size_getter.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * default_pipe_size_getter.c - * - * Tests suite for LTTng notification API (get default size of pipes) - * - * Copyright (C) 2021 Jérémie Galarneau - * - * SPDX-License-Identifier: MIT - * - */ - -#include -#include -#include -#include - -#include -#include - -int lttng_opt_verbose; -int lttng_opt_mi; -int lttng_opt_quiet; - -int main(int argc, const char **argv) -{ - int ret; - /* - * The event notifier pipes are not "special"; they are created using - * the lttng_pipe utility. Hence, this should be representative of a - * pipe created by the session daemon for event notifier messages to - * go through. - */ - struct lttng_pipe *pipe = lttng_pipe_open(0); - - if (!pipe) { - /* lttng_pipe_open already logs on error. */ - ret = EXIT_FAILURE; - goto end; - } - - ret = fcntl(lttng_pipe_get_writefd(pipe), F_GETPIPE_SZ); - if (ret < 0) { - PERROR("Failed to get the size of the pipe"); - ret = EXIT_FAILURE; - goto end; - } - - printf("%d\n", ret); - ret = EXIT_SUCCESS; -end: - lttng_pipe_destroy(pipe); - return ret; -} diff --git a/tests/regression/tools/notification/default_pipe_size_getter.cpp b/tests/regression/tools/notification/default_pipe_size_getter.cpp new file mode 100644 index 000000000..cc24cec1e --- /dev/null +++ b/tests/regression/tools/notification/default_pipe_size_getter.cpp @@ -0,0 +1,53 @@ +/* + * default_pipe_size_getter.c + * + * Tests suite for LTTng notification API (get default size of pipes) + * + * Copyright (C) 2021 Jérémie Galarneau + * + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include +#include + +#include +#include + +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; +}