X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fnotification-thread-events.c;h=6f0ee7d7942cf762b38c6c7edc37ab97caa8972b;hp=80ae8d00d1cf489f0605fe2e3ffb278e7d5c0dda;hb=f37d0f861f20a72a0b77fb43fa27744521dd7995;hpb=ed3272044d751bde5d2fb5fd9fe3d5859240eb00 diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index 80ae8d00d..6f0ee7d79 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -1,18 +1,8 @@ /* - * Copyright (C) 2017 - Jérémie Galarneau + * Copyright (C) 2017 Jérémie Galarneau * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License, version 2 only, as - * published by the Free Software Foundation. + * SPDX-License-Identifier: GPL-2.0-only * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _LGPL_SOURCE @@ -70,6 +60,8 @@ struct lttng_channel_trigger_list { struct cds_list_head list; /* Node in the channel_triggers_ht */ struct cds_lfht_node channel_triggers_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; /* @@ -116,6 +108,8 @@ struct lttng_session_trigger_list { struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct lttng_condition_list_element { @@ -129,9 +123,11 @@ struct notification_client_list_element { }; struct notification_client_list { - struct lttng_trigger *trigger; + const struct lttng_trigger *trigger; struct cds_list_head list; struct cds_lfht_node notification_trigger_ht_node; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct notification_client { @@ -198,6 +194,8 @@ struct notification_client { struct lttng_dynamic_buffer buffer; } outbound; } communication; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; struct channel_state_sample { @@ -206,6 +204,8 @@ struct channel_state_sample { uint64_t highest_usage; uint64_t lowest_usage; uint64_t channel_total_consumed; + /* call_rcu delayed reclaim. */ + struct rcu_head rcu_node; }; static unsigned long hash_channel_key(struct channel_key *key); @@ -345,17 +345,27 @@ int match_client_list_condition(struct cds_lfht_node *node, const void *key) { struct lttng_condition *condition_key = (struct lttng_condition *) key; struct notification_client_list *client_list; - struct lttng_condition *condition; + const struct lttng_condition *condition; assert(condition_key); client_list = caa_container_of(node, struct notification_client_list, notification_trigger_ht_node); - condition = lttng_trigger_get_condition(client_list->trigger); + condition = lttng_trigger_get_const_condition(client_list->trigger); return !!lttng_condition_is_equal(condition_key, condition); } +static +int match_session(struct cds_lfht_node *node, const void *key) +{ + const char *name = key; + struct session_info *session_info = caa_container_of( + node, struct session_info, sessions_ht_node); + + return !strcmp(session_info->name, name); +} + static unsigned long lttng_condition_buffer_usage_hash( const struct lttng_condition *_condition) @@ -465,6 +475,37 @@ unsigned long hash_channel_key(struct channel_key *key) return key_hash ^ domain_hash; } +/* + * Get the type of object to which a given condition applies. Bindings let + * the notification system evaluate a trigger's condition when a given + * object's state is updated. + * + * For instance, a condition bound to a channel will be evaluated everytime + * the channel's state is changed by a channel monitoring sample. + */ +static +enum lttng_object_type get_condition_binding_object( + const struct lttng_condition *condition) +{ + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + return LTTNG_OBJECT_TYPE_CHANNEL; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return LTTNG_OBJECT_TYPE_SESSION; + default: + return LTTNG_OBJECT_TYPE_UNKNOWN; + } +} + +static +void free_channel_info_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct channel_info, rcu_node)); +} + static void channel_info_destroy(struct channel_info *channel_info) { @@ -480,7 +521,13 @@ void channel_info_destroy(struct channel_info *channel_info) if (channel_info->name) { free(channel_info->name); } - free(channel_info); + call_rcu(&channel_info->rcu_node, free_channel_info_rcu); +} + +static +void free_session_info_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct session_info, rcu_node)); } /* Don't call directly, use the ref-counting mechanism. */ @@ -504,7 +551,7 @@ void session_info_destroy(void *_data) &session_info->sessions_ht_node); rcu_read_unlock(); free(session_info->name); - free(session_info); + call_rcu(&session_info->rcu_node, free_session_info_rcu); } static @@ -635,17 +682,18 @@ struct notification_client_list *get_client_list_from_condition( &iter); node = cds_lfht_iter_get_node(&iter); - return node ? caa_container_of(node, + return node ? caa_container_of(node, struct notification_client_list, notification_trigger_ht_node) : NULL; } /* This function must be called with the RCU read lock held. */ static -int evaluate_condition_for_client(struct lttng_trigger *trigger, - struct lttng_condition *condition, - struct notification_client *client, - struct notification_thread_state *state) +int evaluate_channel_condition_for_client( + const struct lttng_condition *condition, + struct notification_thread_state *state, + struct lttng_evaluation **evaluation, + uid_t *session_uid, gid_t *session_gid) { int ret; struct cds_lfht_iter iter; @@ -654,23 +702,15 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, struct channel_key *channel_key = NULL; struct channel_state_sample *last_sample = NULL; struct lttng_channel_trigger_list *channel_trigger_list = NULL; - struct lttng_evaluation *evaluation = NULL; - struct notification_client_list client_list = { 0 }; - struct notification_client_list_element client_list_element = { 0 }; - - assert(trigger); - assert(condition); - assert(client); - assert(state); - /* Find the channel associated with the trigger. */ + /* Find the channel associated with the condition. */ cds_lfht_for_each_entry(state->channel_triggers_ht, &iter, - channel_trigger_list , channel_triggers_ht_node) { + channel_trigger_list, channel_triggers_ht_node) { struct lttng_trigger_list_element *element; cds_list_for_each_entry(element, &channel_trigger_list->list, node) { const struct lttng_condition *current_condition = - lttng_trigger_get_const_condition( + lttng_trigger_get_const_condition( element->trigger); assert(current_condition); @@ -691,7 +731,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, if (!channel_key){ /* No channel found; normal exit. */ - DBG("[notification-thread] No channel associated with newly subscribed-to condition"); + DBG("[notification-thread] No known channel associated with newly subscribed-to condition"); ret = 0; goto end; } @@ -725,7 +765,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, goto end; } - ret = evaluate_buffer_condition(condition, &evaluation, state, + ret = evaluate_buffer_condition(condition, evaluation, state, NULL, last_sample, 0, channel_info->session_info->consumed_data_size, channel_info); @@ -734,6 +774,154 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, goto end; } + *session_uid = channel_info->session_info->uid; + *session_gid = channel_info->session_info->gid; +end: + return ret; +} + +static +const char *get_condition_session_name(const struct lttng_condition *condition) +{ + const char *session_name = NULL; + enum lttng_condition_status status; + + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + status = lttng_condition_buffer_usage_get_session_name( + condition, &session_name); + break; + case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: + status = lttng_condition_session_consumed_size_get_session_name( + condition, &session_name); + break; + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + break; + default: + abort(); + } + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to retrieve session rotation condition's session name"); + goto end; + } +end: + return session_name; +} + +/* This function must be called with the RCU read lock held. */ +static +int evaluate_session_condition_for_client( + const struct lttng_condition *condition, + struct notification_thread_state *state, + struct lttng_evaluation **evaluation, + uid_t *session_uid, gid_t *session_gid) +{ + int ret; + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + const char *session_name; + struct session_info *session_info = NULL; + + session_name = get_condition_session_name(condition); + + /* Find the session associated with the trigger. */ + cds_lfht_lookup(state->sessions_ht, + hash_key_str(session_name, lttng_ht_seed), + match_session, + session_name, + &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + DBG("[notification-thread] No known session matching name \"%s\"", + session_name); + ret = 0; + goto end; + } + + session_info = caa_container_of(node, struct session_info, + sessions_ht_node); + session_info_get(session_info); + + /* + * Evaluation is performed in-line here since only one type of + * session-bound condition is handled for the moment. + */ + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + if (!session_info->rotation.ongoing) { + ret = 0; + goto end_session_put; + } + + *evaluation = lttng_evaluation_session_rotation_ongoing_create( + session_info->rotation.id); + if (!*evaluation) { + /* Fatal error. */ + ERR("[notification-thread] Failed to create session rotation ongoing evaluation for session \"%s\"", + session_info->name); + ret = -1; + goto end_session_put; + } + ret = 0; + break; + default: + ret = 0; + goto end_session_put; + } + + *session_uid = session_info->uid; + *session_gid = session_info->gid; + +end_session_put: + session_info_put(session_info); +end: + return ret; +} + +/* This function must be called with the RCU read lock held. */ +static +int evaluate_condition_for_client(const struct lttng_trigger *trigger, + const struct lttng_condition *condition, + struct notification_client *client, + struct notification_thread_state *state) +{ + int ret; + struct lttng_evaluation *evaluation = NULL; + struct notification_client_list client_list = { 0 }; + struct notification_client_list_element client_list_element = { 0 }; + uid_t object_uid = 0; + gid_t object_gid = 0; + + assert(trigger); + assert(condition); + assert(client); + assert(state); + + switch (get_condition_binding_object(condition)) { + case LTTNG_OBJECT_TYPE_SESSION: + ret = evaluate_session_condition_for_client(condition, state, + &evaluation, &object_uid, &object_gid); + break; + case LTTNG_OBJECT_TYPE_CHANNEL: + ret = evaluate_channel_condition_for_client(condition, state, + &evaluation, &object_uid, &object_gid); + break; + case LTTNG_OBJECT_TYPE_NONE: + ret = 0; + goto end; + case LTTNG_OBJECT_TYPE_UNKNOWN: + default: + ret = -1; + goto end; + } + if (ret) { + /* Fatal error. */ + goto end; + } if (!evaluation) { /* Evaluation yielded nothing. Normal exit. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to false, nothing to report to client"); @@ -756,8 +944,7 @@ int evaluate_condition_for_client(struct lttng_trigger *trigger, /* Send evaluation result to the newly-subscribed client. */ DBG("[notification-thread] Newly subscribed-to condition evaluated to true, notifying client"); ret = send_evaluation_to_clients(trigger, evaluation, &client_list, - state, channel_info->session_info->uid, - channel_info->session_info->gid); + state, object_uid, object_gid); end: return ret; @@ -926,6 +1113,12 @@ end: return 0; } +static +void free_notification_client_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_client, rcu_node)); +} + static void notification_client_destroy(struct notification_client *client, struct notification_thread_state *state) @@ -948,7 +1141,7 @@ void notification_client_destroy(struct notification_client *client, } lttng_dynamic_buffer_reset(&client->communication.inbound.buffer); lttng_dynamic_buffer_reset(&client->communication.outbound.buffer); - free(client); + call_rcu(&client->rcu_node, free_notification_client_rcu); } /* @@ -1037,34 +1230,6 @@ fail: return false; } -/* - * Get the type of object to which a given trigger applies. Binding lets - * the notification system evaluate a trigger's condition when a given - * object's state is updated. - * - * For instance, a condition bound to a channel will be evaluated everytime - * the channel's state is changed by a channel monitoring sample. - */ -static -enum lttng_object_type get_trigger_binding_object( - const struct lttng_trigger *trigger) -{ - const struct lttng_condition *condition; - - condition = lttng_trigger_get_const_condition(trigger); - switch (lttng_condition_get_type(condition)) { - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: - case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: - case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: - return LTTNG_OBJECT_TYPE_CHANNEL; - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: - case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: - return LTTNG_OBJECT_TYPE_SESSION; - default: - return LTTNG_OBJECT_TYPE_UNKNOWN; - } -} - static bool trigger_applies_to_channel(const struct lttng_trigger *trigger, const struct channel_info *channel_info) @@ -1115,16 +1280,6 @@ bool trigger_applies_to_client(struct lttng_trigger *trigger, return applies; } -static -int match_session(struct cds_lfht_node *node, const void *key) -{ - const char *name = key; - struct session_info *session_info = caa_container_of( - node, struct session_info, sessions_ht_node); - - return !strcmp(session_info->name, name); -} - /* Must be called with RCU read lock held. */ static struct lttng_session_trigger_list *get_session_trigger_list( @@ -1151,7 +1306,7 @@ struct lttng_session_trigger_list *get_session_trigger_list( goto end; } - list = caa_container_of(node, + list = caa_container_of(node, struct lttng_session_trigger_list, session_triggers_ht_node); end: @@ -1356,6 +1511,7 @@ struct session_info *find_or_create_session_info( if (!session) { ERR("[notification-thread] Failed to allocation session info for session \"%s\" (uid = %i, gid = %i)", name, uid, gid); + lttng_session_trigger_list_destroy(trigger_list); goto error; } trigger_list = NULL; @@ -1400,7 +1556,7 @@ int handle_notification_thread_command_add_channel( session_info = find_or_create_session_info(state, session_name, session_uid, session_gid); if (!session_info) { - /* Allocation error or an internal error occured. */ + /* Allocation error or an internal error occurred. */ goto error; } @@ -1410,6 +1566,7 @@ int handle_notification_thread_command_add_channel( goto error; } + rcu_read_lock(); /* Build a list of all triggers applying to the new channel. */ cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, node) { @@ -1422,6 +1579,7 @@ int handle_notification_thread_command_add_channel( new_element = zmalloc(sizeof(*new_element)); if (!new_element) { + rcu_read_unlock(); goto error; } CDS_INIT_LIST_HEAD(&new_element->node); @@ -1429,6 +1587,7 @@ int handle_notification_thread_command_add_channel( cds_list_add(&new_element->node, &trigger_list); trigger_count++; } + rcu_read_unlock(); DBG("[notification-thread] Found %i triggers that apply to newly added channel", trigger_count); @@ -1463,6 +1622,20 @@ error: return 1; } +static +void free_channel_trigger_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_channel_trigger_list, + rcu_node)); +} + +static +void free_channel_state_sample_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct channel_state_sample, + rcu_node)); +} + static int handle_notification_thread_command_remove_channel( struct notification_thread_state *state, @@ -1505,7 +1678,7 @@ int handle_notification_thread_command_remove_channel( free(trigger_list_element); } cds_lfht_del(state->channel_triggers_ht, node); - free(trigger_list); + call_rcu(&trigger_list->rcu_node, free_channel_trigger_list_rcu); /* Free sampled channel state. */ cds_lfht_lookup(state->channel_state_ht, @@ -1524,7 +1697,7 @@ int handle_notification_thread_command_remove_channel( channel_state_ht_node); cds_lfht_del(state->channel_state_ht, node); - free(sample); + call_rcu(&sample->rcu_node, free_channel_state_sample_rcu); } /* Remove the channel from the channels_ht and free it. */ @@ -1565,7 +1738,7 @@ int handle_notification_thread_command_session_rotation( session_info = find_or_create_session_info(state, session_name, session_uid, session_gid); if (!session_info) { - /* Allocation error or an internal error occured. */ + /* Allocation error or an internal error occurred. */ ret = -1; cmd_result = LTTNG_ERR_NOMEM; goto end; @@ -1680,8 +1853,7 @@ int condition_is_supported(struct lttng_condition *condition) * buffers. Therefore, we reject triggers that require that * mechanism to be available to be evaluated. */ - ret = kernel_supports_ring_buffer_snapshot_sample_positions( - kernel_tracer_fd); + ret = kernel_supports_ring_buffer_snapshot_sample_positions(); break; } default: @@ -1751,6 +1923,7 @@ int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger, channels_ht_node) { struct lttng_trigger_list_element *trigger_list_element; struct lttng_channel_trigger_list *trigger_list; + struct cds_lfht_iter lookup_iter; if (!trigger_applies_to_channel(trigger, channel)) { continue; @@ -1760,8 +1933,8 @@ int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger, hash_channel_key(&channel->key), match_channel_trigger_list, &channel->key, - &iter); - node = cds_lfht_iter_get_node(&iter); + &lookup_iter); + node = cds_lfht_iter_get_node(&lookup_iter); assert(node); trigger_list = caa_container_of(node, struct lttng_channel_trigger_list, @@ -1893,13 +2066,14 @@ int handle_notification_thread_command_register_trigger( lttng_condition_hash(condition), &client_list->notification_trigger_ht_node); - switch (get_trigger_binding_object(trigger)) { + switch (get_condition_binding_object(condition)) { case LTTNG_OBJECT_TYPE_SESSION: /* Add the trigger to the list if it matches a known session. */ ret = bind_trigger_to_matching_session(trigger, state); if (ret) { goto error_free_client_list; } + break; case LTTNG_OBJECT_TYPE_CHANNEL: /* * Add the trigger to list of triggers bound to the channels @@ -1972,16 +2146,26 @@ error_free_ht_element: free(trigger_ht_element); error: if (free_trigger) { - struct lttng_action *action = lttng_trigger_get_action(trigger); - - lttng_condition_destroy(condition); - lttng_action_destroy(action); lttng_trigger_destroy(trigger); } rcu_read_unlock(); return ret; } +static +void free_notification_client_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct notification_client_list, + rcu_node)); +} + +static +void free_lttng_trigger_ht_element_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_trigger_ht_element, + rcu_node)); +} + static int handle_notification_thread_command_unregister_trigger( struct notification_thread_state *state, @@ -1996,7 +2180,6 @@ int handle_notification_thread_command_unregister_trigger( struct lttng_trigger_ht_element *trigger_ht_element = NULL; struct lttng_condition *condition = lttng_trigger_get_condition( trigger); - struct lttng_action *action; enum lttng_error_code cmd_reply; rcu_read_lock(); @@ -2051,19 +2234,16 @@ int handle_notification_thread_command_unregister_trigger( } cds_lfht_del(state->notification_trigger_clients_ht, &client_list->notification_trigger_ht_node); - free(client_list); + call_rcu(&client_list->rcu_node, free_notification_client_list_rcu); /* Remove trigger from triggers_ht. */ trigger_ht_element = caa_container_of(triggers_ht_node, struct lttng_trigger_ht_element, node); cds_lfht_del(state->triggers_ht, triggers_ht_node); - condition = lttng_trigger_get_condition(trigger_ht_element->trigger); - lttng_condition_destroy(condition); - action = lttng_trigger_get_action(trigger_ht_element->trigger); - lttng_action_destroy(action); + /* Release the ownership of the trigger. */ lttng_trigger_destroy(trigger_ht_element->trigger); - free(trigger_ht_element); + call_rcu(&trigger_ht_element->rcu_node, free_lttng_trigger_ht_element_rcu); end: rcu_read_unlock(); if (_cmd_reply) { @@ -2082,9 +2262,9 @@ int handle_notification_thread_command( struct notification_thread_command *cmd; /* Read the event pipe to put it back into a quiescent state. */ - ret = read(lttng_pipe_get_readfd(handle->cmd_queue.event_pipe), &counter, + ret = lttng_read(lttng_pipe_get_readfd(handle->cmd_queue.event_pipe), &counter, sizeof(counter)); - if (ret == -1) { + if (ret != sizeof(counter)) { goto error; } @@ -2238,7 +2418,7 @@ int handle_notification_thread_client_connect( ret = client_reset_inbound_state(client); if (ret) { ERR("[notification-thread] Failed to reset client communication's inbound state"); - ret = 0; + ret = 0; goto error; } @@ -2310,7 +2490,7 @@ int handle_notification_thread_client_disconnect( if (ret) { ERR("[notification-thread] Failed to remove client socket from poll set"); } - cds_lfht_del(state->client_socket_ht, + cds_lfht_del(state->client_socket_ht, &client->client_socket_ht_node); notification_client_destroy(client, state); end: @@ -2348,6 +2528,7 @@ int handle_notification_thread_trigger_unregister_all( struct cds_lfht_iter iter; struct lttng_trigger_ht_element *trigger_ht_element; + rcu_read_lock(); cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, node) { int ret = handle_notification_thread_command_unregister_trigger( @@ -2356,6 +2537,7 @@ int handle_notification_thread_trigger_unregister_all( error_occurred = true; } } + rcu_read_unlock(); return error_occurred ? -1 : 0; } @@ -2374,8 +2556,7 @@ int client_flush_outgoing_queue(struct notification_client *client, ret = lttcomm_send_unix_sock_non_block(client->socket, client->communication.outbound.buffer.data, to_send_count); - if ((ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) || - (ret > 0 && ret < to_send_count)) { + if ((ret >= 0 && ret < to_send_count)) { DBG("[notification-thread] Client (socket fd = %i) outgoing queue could not be completely flushed", client->socket); to_send_count -= max(ret, 0); @@ -2611,14 +2792,14 @@ int client_dispatch_message(struct notification_client *client, struct lttng_condition *condition; enum lttng_notification_channel_status status = LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; - const struct lttng_buffer_view condition_view = - lttng_buffer_view_from_dynamic_buffer( + struct lttng_payload_view condition_view = + lttng_payload_view_from_dynamic_buffer( &client->communication.inbound.buffer, 0, -1); size_t expected_condition_size = client->communication.inbound.buffer.size; - ret = lttng_condition_create_from_buffer(&condition_view, + ret = lttng_condition_create_from_payload(&condition_view, &condition); if (ret != expected_condition_size) { ERR("[notification-thread] Malformed condition received from client"); @@ -2759,7 +2940,7 @@ bool evaluate_buffer_usage_condition(const struct lttng_condition *condition, * forego this double-multiplication or it could be performed * as fixed-point math. * - * Note that caching should accomodate the case where the + * Note that caching should accommodates the case where the * condition applies to multiple channels (i.e. don't assume * that all channels matching my_chann* have the same size...) */ @@ -2899,8 +3080,7 @@ end: } static -int client_enqueue_dropped_notification(struct notification_client *client, - struct notification_thread_state *state) +int client_enqueue_dropped_notification(struct notification_client *client) { int ret; struct lttng_notification_channel_message msg = { @@ -2914,15 +3094,48 @@ int client_enqueue_dropped_notification(struct notification_client *client, return ret; } +/* + * Permission checks relative to notification channel clients are performed + * here. Notice how object, client, and trigger credentials are involved in + * this check. + * + * The `object` credentials are the credentials associated with the "subject" + * of a condition. For instance, a `rotation completed` condition applies + * to a session. When that condition is met, it will produce an evaluation + * against a session. Hence, in this case, the `object` credentials are the + * credentials of the "subject" session. + * + * The `trigger` credentials are the credentials of the user that registered the + * trigger. + * + * The `client` credentials are the credentials of the user that created a given + * notification channel. + * + * In terms of visibility, it is expected that non-privilieged users can only + * register triggers against "their" objects (their own sessions and + * applications they are allowed to interact with). They can then open a + * notification channel and subscribe to notifications associated with those + * triggers. + * + * As for privilieged users, they can register triggers against the objects of + * other users. They can then subscribe to the notifications associated to their + * triggers. Privilieged users _can't_ subscribe to the notifications of + * triggers owned by other users; they must create their own triggers. + * + * This is more a concern of usability than security. It would be difficult for + * a root user reliably subscribe to a specific set of conditions without + * interference from external users (those could, for instance, unregister + * their triggers). + */ static int send_evaluation_to_clients(const struct lttng_trigger *trigger, const struct lttng_evaluation *evaluation, struct notification_client_list* client_list, struct notification_thread_state *state, - uid_t channel_uid, gid_t channel_gid) + uid_t object_uid, gid_t object_gid) { int ret = 0; - struct lttng_dynamic_buffer msg_buffer; + struct lttng_payload msg_payload; struct notification_client_list_element *client_list_element, *tmp; const struct lttng_notification notification = { .condition = (struct lttng_condition *) lttng_trigger_get_const_condition(trigger), @@ -2931,16 +3144,17 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, struct lttng_notification_channel_message msg_header = { .type = (int8_t) LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION, }; + const struct lttng_credentials *trigger_creds = lttng_trigger_get_credentials(trigger); - lttng_dynamic_buffer_init(&msg_buffer); + lttng_payload_init(&msg_payload); - ret = lttng_dynamic_buffer_append(&msg_buffer, &msg_header, + ret = lttng_dynamic_buffer_append(&msg_payload.buffer, &msg_header, sizeof(msg_header)); if (ret) { goto end; } - ret = lttng_notification_serialize(¬ification, &msg_buffer); + ret = lttng_notification_serialize(¬ification, &msg_payload); if (ret) { ERR("[notification-thread] Failed to serialize notification"); ret = -1; @@ -2948,23 +3162,28 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, } /* Update payload size. */ - ((struct lttng_notification_channel_message * ) msg_buffer.data)->size = - (uint32_t) (msg_buffer.size - sizeof(msg_header)); + ((struct lttng_notification_channel_message * ) msg_payload.buffer.data)->size = + (uint32_t) (msg_payload.buffer.size - sizeof(msg_header)); cds_list_for_each_entry_safe(client_list_element, tmp, &client_list->list, node) { struct notification_client *client = client_list_element->client; - if (client->uid != channel_uid && client->gid != channel_gid && + if (client->uid != object_uid && client->gid != object_gid && client->uid != 0) { /* Client is not allowed to monitor this channel. */ - DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this channel"); + DBG("[notification-thread] Skipping client at it does not have the object permission to receive notification for this trigger"); + continue; + } + + if (client->uid != trigger_creds->uid && client->gid != trigger_creds->gid) { + DBG("[notification-thread] Skipping client at it does not have the permission to receive notification for this trigger"); continue; } DBG("[notification-thread] Sending notification to client (fd = %i, %zu bytes)", - client->socket, msg_buffer.size); + client->socket, msg_payload.buffer.size); if (client->communication.outbound.buffer.size) { /* * Outgoing data is already buffered for this client; @@ -2978,7 +3197,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, if (!client->communication.outbound.dropped_notification) { client->communication.outbound.dropped_notification = true; ret = client_enqueue_dropped_notification( - client, state); + client); if (ret) { goto end; } @@ -2988,7 +3207,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, ret = lttng_dynamic_buffer_append_buffer( &client->communication.outbound.buffer, - &msg_buffer); + &msg_payload.buffer); if (ret) { goto end; } @@ -3000,7 +3219,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, } ret = 0; end: - lttng_dynamic_buffer_reset(&msg_buffer); + lttng_payload_reset(&msg_payload); return ret; } @@ -3139,7 +3358,7 @@ int handle_notification_thread_channel_sample( trigger_list = caa_container_of(node, struct lttng_channel_trigger_list, channel_triggers_ht_node); cds_list_for_each_entry(trigger_list_element, &trigger_list->list, - node) { + node) { const struct lttng_condition *condition; const struct lttng_action *action; const struct lttng_trigger *trigger;