X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fnotification-thread-events.c;h=4904a54575e512c1ed4297a98eed17e7abc95dea;hp=f437d018b3fdd493e12465e1b121f705784eece8;hb=51eab943fe37b0fdd541099c4d02ea7b57cb15bb;hpb=9e0bb80e5e06934fe204d77227af31aea38aa323 diff --git a/src/bin/lttng-sessiond/notification-thread-events.c b/src/bin/lttng-sessiond/notification-thread-events.c index f437d018b..4904a5457 100644 --- a/src/bin/lttng-sessiond/notification-thread-events.c +++ b/src/bin/lttng-sessiond/notification-thread-events.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,13 @@ #define CLIENT_POLL_MASK_IN (LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP) #define CLIENT_POLL_MASK_IN_OUT (CLIENT_POLL_MASK_IN | LPOLLOUT) +enum lttng_object_type { + LTTNG_OBJECT_TYPE_UNKNOWN, + LTTNG_OBJECT_TYPE_NONE, + LTTNG_OBJECT_TYPE_CHANNEL, + LTTNG_OBJECT_TYPE_SESSION, +}; + struct lttng_trigger_list_element { /* No ownership of the trigger object is assumed. */ const struct lttng_trigger *trigger; @@ -58,10 +66,53 @@ struct lttng_trigger_list_element { struct lttng_channel_trigger_list { struct channel_key channel_key; + /* List of struct lttng_trigger_list_element. */ struct cds_list_head list; + /* Node in the channel_triggers_ht */ struct cds_lfht_node channel_triggers_ht_node; }; +/* + * List of triggers applying to a given session. + * + * See: + * - lttng_session_trigger_list_create() + * - lttng_session_trigger_list_build() + * - lttng_session_trigger_list_destroy() + * - lttng_session_trigger_list_add() + */ +struct lttng_session_trigger_list { + /* + * Not owned by this; points to the session_info structure's + * session name. + */ + const char *session_name; + /* List of struct lttng_trigger_list_element. */ + struct cds_list_head list; + /* Node in the session_triggers_ht */ + struct cds_lfht_node session_triggers_ht_node; + /* + * Weak reference to the notification system's session triggers + * hashtable. + * + * The session trigger list structure structure is owned by + * the session's session_info. + * + * The session_info is kept alive the the channel_infos holding a + * reference to it (reference counting). When those channels are + * destroyed (at runtime or on teardown), the reference they hold + * to the session_info are released. On destruction of session_info, + * session_info_destroy() will remove the list of triggers applying + * to this session from the notification system's state. + * + * This implies that the session_triggers_ht must be destroyed + * after the channels. + */ + struct cds_lfht *session_triggers_ht; + /* Used for delayed RCU reclaim. */ + struct rcu_head rcu_node; +}; + struct lttng_trigger_ht_element { struct lttng_trigger *trigger; struct cds_lfht_node node; @@ -174,6 +225,7 @@ int send_evaluation_to_clients(const struct lttng_trigger *trigger, uid_t channel_uid, gid_t channel_gid); +/* session_info API */ static void session_info_destroy(void *_data); static @@ -182,7 +234,8 @@ static void session_info_put(struct session_info *session_info); static struct session_info *session_info_create(const char *name, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, + struct lttng_session_trigger_list *trigger_list); static void session_info_add_channel(struct session_info *session_info, struct channel_info *channel_info); @@ -190,6 +243,23 @@ static void session_info_remove_channel(struct session_info *session_info, struct channel_info *channel_info); +/* lttng_session_trigger_list API */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_create( + const char *session_name, + struct cds_lfht *session_triggers_ht); +static +struct lttng_session_trigger_list *lttng_session_trigger_list_build( + const struct notification_thread_state *state, + const char *session_name); +static +void lttng_session_trigger_list_destroy( + struct lttng_session_trigger_list *list); +static +int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, + const struct lttng_trigger *trigger); + + static int match_client(struct cds_lfht_node *node, const void *key) { @@ -216,6 +286,18 @@ int match_channel_trigger_list(struct cds_lfht_node *node, const void *key) (channel_key->domain == trigger_list->channel_key.domain)); } +static +int match_session_trigger_list(struct cds_lfht_node *node, const void *key) +{ + const char *session_name = (const char *) key; + struct lttng_session_trigger_list *trigger_list; + + trigger_list = caa_container_of(node, struct lttng_session_trigger_list, + session_triggers_ht_node); + + return !!(strcmp(trigger_list->session_name, session_name) == 0); +} + static int match_channel_state_sample(struct cds_lfht_node *node, const void *key) { @@ -351,6 +433,21 @@ unsigned long lttng_condition_session_consumed_size_hash( return hash; } +static +unsigned long lttng_condition_session_rotation_hash( + const struct lttng_condition *_condition) +{ + unsigned long hash, condition_type; + struct lttng_condition_session_rotation *condition; + + condition = container_of(_condition, + struct lttng_condition_session_rotation, parent); + condition_type = (unsigned long) condition->parent.type; + hash = hash_key_ulong((void *) condition_type, lttng_ht_seed); + assert(condition->session_name); + hash ^= hash_key_str(condition->session_name, lttng_ht_seed); + return hash; +} /* * The lttng_condition hashing code is kept in this file (rather than @@ -366,6 +463,9 @@ unsigned long lttng_condition_hash(const struct lttng_condition *condition) return lttng_condition_buffer_usage_hash(condition); case LTTNG_CONDITION_TYPE_SESSION_CONSUMED_SIZE: return lttng_condition_session_consumed_size_hash(condition); + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + return lttng_condition_session_rotation_hash(condition); default: ERR("[notification-thread] Unexpected condition type caught"); abort(); @@ -414,6 +514,7 @@ void session_info_destroy(void *_data) ERR("[notification-thread] Failed to destroy channel information hash table"); } } + lttng_session_trigger_list_destroy(session_info->trigger_list); free(session_info->name); free(session_info); } @@ -437,7 +538,8 @@ void session_info_put(struct session_info *session_info) } static -struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid) +struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid, + struct lttng_session_trigger_list *trigger_list) { struct session_info *session_info; @@ -462,6 +564,7 @@ struct session_info *session_info_create(const char *name, uid_t uid, gid_t gid) } session_info->uid = uid; session_info->gid = gid; + session_info->trigger_list = trigger_list; end: return session_info; error: @@ -885,8 +988,8 @@ end: static bool buffer_usage_condition_applies_to_channel( - struct lttng_condition *condition, - struct channel_info *channel_info) + const struct lttng_condition *condition, + const struct channel_info *channel_info) { enum lttng_condition_status status; enum lttng_domain_type condition_domain; @@ -922,8 +1025,8 @@ fail: static bool session_consumed_size_condition_applies_to_channel( - struct lttng_condition *condition, - struct channel_info *channel_info) + const struct lttng_condition *condition, + const struct channel_info *channel_info) { enum lttng_condition_status status; const char *condition_session_name = NULL; @@ -941,14 +1044,42 @@ 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 -bool trigger_applies_to_channel(struct lttng_trigger *trigger, - struct channel_info *channel_info) +enum lttng_object_type get_trigger_binding_object( + const struct lttng_trigger *trigger) { - struct lttng_condition *condition; + 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) +{ + const struct lttng_condition *condition; bool trigger_applies; - condition = lttng_trigger_get_condition(trigger); + condition = lttng_trigger_get_const_condition(trigger); if (!condition) { goto fail; } @@ -1001,6 +1132,166 @@ int match_session(struct cds_lfht_node *node, const void *key) return !strcmp(session_info->name, name); } +/* + * Allocate an empty lttng_session_trigger_list for the session named + * 'session_name'. + * + * No ownership of 'session_name' is assumed by the session trigger list. + * It is the caller's responsability to ensure the session name is alive + * for as long as this list is. + */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_create( + const char *session_name, + struct cds_lfht *session_triggers_ht) +{ + struct lttng_session_trigger_list *list; + + list = zmalloc(sizeof(*list)); + if (!list) { + goto end; + } + list->session_name = session_name; + CDS_INIT_LIST_HEAD(&list->list); + cds_lfht_node_init(&list->session_triggers_ht_node); + list->session_triggers_ht = session_triggers_ht; + + rcu_read_lock(); + /* Publish the list through the session_triggers_ht. */ + cds_lfht_add(session_triggers_ht, + hash_key_str(session_name, lttng_ht_seed), + &list->session_triggers_ht_node); + rcu_read_unlock(); +end: + return list; +} + +static +void free_session_trigger_list_rcu(struct rcu_head *node) +{ + free(caa_container_of(node, struct lttng_session_trigger_list, + rcu_node)); +} + +static +void lttng_session_trigger_list_destroy(struct lttng_session_trigger_list *list) +{ + struct lttng_trigger_list_element *trigger_list_element, *tmp; + + /* Empty the list element by element, and then free the list itself. */ + cds_list_for_each_entry_safe(trigger_list_element, tmp, + &list->list, node) { + cds_list_del(&trigger_list_element->node); + free(trigger_list_element); + } + rcu_read_lock(); + /* Unpublish the list from the session_triggers_ht. */ + cds_lfht_del(list->session_triggers_ht, + &list->session_triggers_ht_node); + rcu_read_unlock(); + call_rcu(&list->rcu_node, free_session_trigger_list_rcu); +} + +static +int lttng_session_trigger_list_add(struct lttng_session_trigger_list *list, + const struct lttng_trigger *trigger) +{ + int ret = 0; + struct lttng_trigger_list_element *new_element = + zmalloc(sizeof(*new_element)); + + if (!new_element) { + ret = -1; + goto end; + } + CDS_INIT_LIST_HEAD(&new_element->node); + new_element->trigger = trigger; + cds_list_add(&new_element->node, &list->list); +end: + return ret; +} + +static +bool trigger_applies_to_session(const struct lttng_trigger *trigger, + const char *session_name) +{ + bool applies = false; + const struct lttng_condition *condition; + + condition = lttng_trigger_get_const_condition(trigger); + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + { + enum lttng_condition_status condition_status; + const char *condition_session_name; + + condition_status = lttng_condition_session_rotation_get_session_name( + condition, &condition_session_name); + if (condition_status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to retrieve session rotation condition's session name"); + goto end; + } + + assert(condition_session_name); + applies = !strcmp(condition_session_name, session_name); + break; + } + default: + goto end; + } +end: + return applies; +} + +/* + * Allocate and initialize an lttng_session_trigger_list which contains + * all triggers that apply to the session named 'session_name'. + * + * No ownership of 'session_name' is assumed by the session trigger list. + * It is the caller's responsability to ensure the session name is alive + * for as long as this list is. + */ +static +struct lttng_session_trigger_list *lttng_session_trigger_list_build( + const struct notification_thread_state *state, + const char *session_name) +{ + int trigger_count = 0; + struct lttng_session_trigger_list *session_trigger_list = NULL; + struct lttng_trigger_ht_element *trigger_ht_element = NULL; + struct cds_lfht_iter iter; + + session_trigger_list = lttng_session_trigger_list_create(session_name, + state->session_triggers_ht); + + /* Add all triggers applying to the session named 'session_name'. */ + cds_lfht_for_each_entry(state->triggers_ht, &iter, trigger_ht_element, + node) { + int ret; + + if (!trigger_applies_to_session(trigger_ht_element->trigger, + session_name)) { + continue; + } + + ret = lttng_session_trigger_list_add(session_trigger_list, + trigger_ht_element->trigger); + if (ret) { + goto error; + } + + trigger_count++; + } + + DBG("[notification-thread] Found %i triggers that apply to newly created session", + trigger_count); + return session_trigger_list; +error: + lttng_session_trigger_list_destroy(session_trigger_list); + return NULL; +} + static struct session_info *find_or_create_session_info( struct notification_thread_state *state, @@ -1009,6 +1300,7 @@ struct session_info *find_or_create_session_info( struct session_info *session = NULL; struct cds_lfht_node *node; struct cds_lfht_iter iter; + struct lttng_session_trigger_list *trigger_list; rcu_read_lock(); cds_lfht_lookup(state->sessions_ht, @@ -1024,21 +1316,30 @@ struct session_info *find_or_create_session_info( sessions_ht_node); assert(session->uid == uid); assert(session->gid == gid); - goto end; + goto error; + } + + trigger_list = lttng_session_trigger_list_build(state, name); + if (!trigger_list) { + goto error; } - session = session_info_create(name, uid, gid); + session = session_info_create(name, uid, gid, trigger_list); if (!session) { ERR("[notification-thread] Failed to allocation session info for session \"%s\" (uid = %i, gid = %i)", name, uid, gid); - goto end; + goto error; } + trigger_list = NULL; cds_lfht_add(state->sessions_ht, hash_key_str(name, lttng_ht_seed), &session->sessions_ht_node); -end: rcu_read_unlock(); return session; +error: + rcu_read_unlock(); + session_info_put(session); + return NULL; } static @@ -1253,13 +1554,118 @@ end: return ret; } +/* Must be called with RCU read lock held. */ +static +int bind_trigger_to_matching_session(const struct lttng_trigger *trigger, + struct notification_thread_state *state) +{ + int ret = 0; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + const struct lttng_condition *condition; + const char *session_name; + struct lttng_session_trigger_list *trigger_list; + + condition = lttng_trigger_get_const_condition(trigger); + switch (lttng_condition_get_type(condition)) { + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_ONGOING: + case LTTNG_CONDITION_TYPE_SESSION_ROTATION_COMPLETED: + { + enum lttng_condition_status status; + + status = lttng_condition_session_rotation_get_session_name( + condition, &session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("[notification-thread] Failed to bind trigger to session: unable to get 'session_rotation' condition's session name"); + ret = -1; + goto end; + } + break; + } + default: + ret = -1; + goto end; + } + + cds_lfht_lookup(state->session_triggers_ht, + hash_key_str(session_name, lttng_ht_seed), + match_session_trigger_list, + session_name, + &iter); + node = cds_lfht_iter_get_node(&iter); + if (!node) { + /* + * Not an error, the list of triggers applying to that session + * will be initialized when the session is created. + */ + DBG("[notification-thread] Unable to bind trigger applying to session \"%s\" as it is not yet known to the notification system", + session_name); + goto end; + } + + trigger_list = caa_container_of(node, + struct lttng_session_trigger_list, + session_triggers_ht_node); + + DBG("[notification-thread] Newly registered trigger bound to session \"%s\"", + session_name); + ret = lttng_session_trigger_list_add(trigger_list, trigger); +end: + return ret; +} + +/* Must be called with RCU read lock held. */ +static +int bind_trigger_to_matching_channels(const struct lttng_trigger *trigger, + struct notification_thread_state *state) +{ + int ret = 0; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct channel_info *channel; + + cds_lfht_for_each_entry(state->channels_ht, &iter, channel, + channels_ht_node) { + struct lttng_trigger_list_element *trigger_list_element; + struct lttng_channel_trigger_list *trigger_list; + + if (!trigger_applies_to_channel(trigger, channel)) { + continue; + } + + cds_lfht_lookup(state->channel_triggers_ht, + hash_channel_key(&channel->key), + match_channel_trigger_list, + &channel->key, + &iter); + node = cds_lfht_iter_get_node(&iter); + assert(node); + trigger_list = caa_container_of(node, + struct lttng_channel_trigger_list, + channel_triggers_ht_node); + + trigger_list_element = zmalloc(sizeof(*trigger_list_element)); + if (!trigger_list_element) { + ret = -1; + goto end; + } + CDS_INIT_LIST_HEAD(&trigger_list_element->node); + trigger_list_element->trigger = trigger; + cds_list_add(&trigger_list_element->node, &trigger_list->list); + DBG("[notification-thread] Newly registered trigger bound to channel \"%s\"", + channel->name); + } +end: + return ret; +} + /* * FIXME A client's credentials are not checked when registering a trigger, nor * are they stored alongside with the trigger. * * The effects of this are benign since: * - The client will succeed in registering the trigger, as it is valid, - * - The trigger will, internally, be bound to the channel, + * - The trigger will, internally, be bound to the channel/session, * - The notifications will not be sent since the client's credentials * are checked against the channel at that moment. * @@ -1283,7 +1689,6 @@ int handle_notification_thread_command_register_trigger( struct notification_client_list_element *client_list_element, *tmp; struct cds_lfht_node *node; struct cds_lfht_iter iter; - struct channel_info *channel; bool free_trigger = true; rcu_read_lock(); @@ -1365,38 +1770,29 @@ int handle_notification_thread_command_register_trigger( lttng_condition_hash(condition), &client_list->notification_trigger_ht_node); - /* - * Add the trigger to list of triggers bound to the channels currently - * known. - */ - cds_lfht_for_each_entry(state->channels_ht, &iter, channel, - channels_ht_node) { - struct lttng_trigger_list_element *trigger_list_element; - struct lttng_channel_trigger_list *trigger_list; - - if (!trigger_applies_to_channel(trigger, channel)) { - continue; + switch (get_trigger_binding_object(trigger)) { + 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; } - - cds_lfht_lookup(state->channel_triggers_ht, - hash_channel_key(&channel->key), - match_channel_trigger_list, - &channel->key, - &iter); - node = cds_lfht_iter_get_node(&iter); - assert(node); - trigger_list = caa_container_of(node, - struct lttng_channel_trigger_list, - channel_triggers_ht_node); - - trigger_list_element = zmalloc(sizeof(*trigger_list_element)); - if (!trigger_list_element) { - ret = -1; + case LTTNG_OBJECT_TYPE_CHANNEL: + /* + * Add the trigger to list of triggers bound to the channels + * currently known. + */ + ret = bind_trigger_to_matching_channels(trigger, state); + if (ret) { goto error_free_client_list; } - CDS_INIT_LIST_HEAD(&trigger_list_element->node); - trigger_list_element->trigger = trigger; - cds_list_add(&trigger_list_element->node, &trigger_list->list); + break; + case LTTNG_OBJECT_TYPE_NONE: + break; + default: + ERR("[notification-thread] Unknown object type on which to bind a newly registered trigger was encountered"); + ret = -1; + goto error_free_client_list; } /*