X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=67ac9fd7bd7c2cfe3313c57ecdf54d624f383e00;hp=15e559d03b5c31faa0e08b39e6f1921dcd1c54c2;hb=refs%2Fheads%2Fsow-2020-0002-rev3;hpb=0e270a1ed773dba5c2f47621494771d4f0bf466d diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 15e559d03..67ac9fd7b 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -6,9 +6,6 @@ * */ -#include "bin/lttng-sessiond/tracker.h" -#include "lttng/lttng-error.h" -#include "lttng/tracker.h" #define _LGPL_SOURCE #include #include @@ -19,6 +16,7 @@ #include #include +#include #include #include #include @@ -32,14 +30,27 @@ #include #include #include +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include +#include "lttng/domain.h" +#include "lttng/map/map.h" +#include #include #include #include #include #include +#include +#include #include #include "channel.h" @@ -53,12 +64,15 @@ #include "lttng-syscall.h" #include "agent.h" #include "buffer-registry.h" +#include "map.h" #include "notification-thread.h" #include "notification-thread-commands.h" #include "rotate.h" #include "rotation-thread.h" #include "timer.h" #include "agent-thread.h" +#include "session.h" +#include "tracker.h" #include "cmd.h" @@ -110,7 +124,7 @@ static int cmd_enable_event_internal(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe); @@ -283,6 +297,340 @@ end: return ret; } +enum tracer_executed_action_state { + TRACER_EXECUTED_ACTION_STATE_REGISTER, + TRACER_EXECUTED_ACTION_STATE_UNREGISTER +}; + +static +enum lttng_error_code sync_incr_value_action_ust( + struct ltt_session *session, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + enum lttng_event_rule_status er_status; + struct ltt_ust_session *usess = session->ust_session; + const char *pattern; + struct ltt_ust_map *map; + char *filter_expression; + struct lttng_bytecode *filter; + struct lttng_event_exclusion *exclusion; + enum lttng_event_rule_generate_exclusions_status + generate_exclusion_status; + + DBG("Syncing UST incr-value action for session '%s', map '%s'", + session->name, map_name); + if (!usess) { + DBG("No UST session"); + ret_code = LTTNG_OK; + goto end; + } + + assert(usess->domain_global.maps); + + map = trace_ust_find_map_by_name(usess->domain_global.maps, map_name); + if (!map) { + DBG("UST map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + er_status = lttng_event_rule_tracepoint_get_pattern(event_rule, &pattern); + if (er_status != LTTNG_EVENT_RULE_STATUS_OK) { + /* At this point, this is a fatal error. */ + abort(); + } + + /* + * FIXME: frdeso, reuse the event notifier functions and approach to + * create the event. + */ + filter_expression = (char *) lttng_event_rule_get_filter(event_rule); + filter = (struct lttng_bytecode *)lttng_event_rule_get_filter_bytecode( + event_rule); + generate_exclusion_status = lttng_event_rule_generate_exclusions( + event_rule, &exclusion); + if (generate_exclusion_status == LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_ERROR) { + ERR("Error generating the exclusion"); + ret_code = LTTNG_ERR_EXCLUSION_INVAL; + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_ust_enable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_ust_disable_tracepoint(usess, + map, tracer_token, (char *) pattern, key, + LTTNG_EVENT_TRACEPOINT, LTTNG_EVENT_LOGLEVEL_ALL, + 0, filter_expression, filter, exclusion, false); + if (ret_code == LTTNG_ERR_UST_EVENT_ENABLED) { + ret_code = LTTNG_OK; + } else if (ret_code != LTTNG_OK) { + ERR("Enabling UST map event"); + goto end; + } + break; + default: + abort(); + } + + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action_kernel( + struct ltt_session *session, + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const char *map_name, + uint64_t tracer_token, + struct lttng_map_key *key, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret_code; + const struct lttng_event_rule *event_rule; + enum lttng_condition_status cond_status; + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + DBG("Syncing kernel incr-value action for session '%s', map '%s'", + session->name, map_name); + + if (!ksess) { + DBG("No kernel session"); + ret_code = LTTNG_OK; + goto end; + } + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (!kmap) { + DBG("Kernel map \"%s\" not found", map_name); + ret_code = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ret_code = LTTNG_ERR_INVALID_MAP; + ERR("Error getting on-event condition event-rule"); + goto end; + } + + switch (state) { + case TRACER_EXECUTED_ACTION_STATE_REGISTER: + ret_code = map_event_kernel_enable_event(kmap, creds, tracer_token, + event_rule, key); + if(ret_code != LTTNG_OK) { + ERR("Error enabling event counter to the kernel tracer"); + goto end; + } + break; + case TRACER_EXECUTED_ACTION_STATE_UNREGISTER: + ret_code = map_event_kernel_disable_event(kmap, tracer_token); + if(ret_code != LTTNG_OK) { + ERR("Error disabling event counter to the kernel tracer"); + goto end; + } + break; + } + +end: + return ret_code; +} + +static +enum lttng_error_code sync_incr_value_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + const char *session_name, *map_name; + enum lttng_action_status action_status; + enum lttng_condition_status cond_status; + const struct lttng_event_rule *event_rule; + struct ltt_session *session = NULL; + struct lttng_map_key *key; + uint64_t action_tracer_token; + + action_status = lttng_action_incr_value_get_map_name(action, + &map_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Map name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_get_session_name(action, + &session_name); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Session name not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + action_status = lttng_action_incr_value_borrow_key_mutable(action, &key); + if (action_status != LTTNG_ACTION_STATUS_OK) { + ERR("Key not set for incr-value action"); + ret = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if(!session) { + DBG("Session not found for incr-value action: session-name=%s", + session_name); + ret = LTTNG_OK; + goto end; + } + + cond_status = lttng_condition_on_event_get_rule(condition, &event_rule); + if (cond_status != LTTNG_CONDITION_STATUS_OK) { + ERR("Error getting on-event condition event-rule"); + ret = LTTNG_ERR_INVALID_MAP; + session_put(session); + goto end; + } + + action_tracer_token = lttng_action_incr_value_get_tracer_token(action); + + switch (lttng_event_rule_get_domain_type(event_rule)) { + case LTTNG_DOMAIN_UST: + ret = sync_incr_value_action_ust(session, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_UST_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + case LTTNG_DOMAIN_KERNEL: + ret = sync_incr_value_action_kernel(session, creds, condition, + map_name, action_tracer_token, key, state); + if (ret == LTTNG_ERR_KERN_EVENT_EXIST) { + DBG("Incr-value action already registered"); + ret = LTTNG_OK; + } + break; + default: + abort(); + } + + goto end; +end: + if (session) { + session_put(session); + } + return ret; +} + +static +enum lttng_error_code sync_one_tracer_executed_action( + const struct lttng_credentials *creds, + const struct lttng_condition *condition, + const struct lttng_action *action, + enum tracer_executed_action_state state) +{ + enum lttng_action_type action_type; + enum lttng_error_code ret; + + action_type = lttng_action_get_type(action); + assert(action_type != LTTNG_ACTION_TYPE_GROUP); + + switch (action_type) { + case LTTNG_ACTION_TYPE_INCREMENT_VALUE: + DBG("Action type \"%s\" is a tracer executed action.", + lttng_action_type_string(action_type)); + + ret = sync_incr_value_action(creds, condition, action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing increment value action to the tracer"); + } + break; + default: + DBG("Action type \"%s\" is not a tracer executed action.", + lttng_action_type_string(action_type)); + ret = LTTNG_OK; + goto end; + } + +end: + return ret; +} + +static +enum lttng_error_code sync_all_tracer_executed_actions( + const struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds, + enum tracer_executed_action_state state) +{ + enum lttng_error_code ret; + unsigned int i, count; + enum lttng_action_status action_status; + enum lttng_action_type action_type; + const struct lttng_action *action; + const struct lttng_condition *condition; + + condition = lttng_trigger_get_const_condition(trigger); + action = lttng_trigger_get_const_action(trigger); + + action_type = lttng_action_get_type(action); + + DBG("Iterating over all actions of trigger \"%s\" to sync any tracer executed actions", + trigger->name); + + if (action_type != LTTNG_ACTION_TYPE_GROUP) { + ret = sync_one_tracer_executed_action(cmd_creds, condition, action, + state); + } else { + action_status = lttng_action_group_get_count(action, &count); + assert(action_status == LTTNG_ACTION_STATUS_OK); + + for (i = 0; i < count; i++) { + const struct lttng_action *inner_action = + lttng_action_group_get_at_index(action, i); + + ret = sync_one_tracer_executed_action(cmd_creds, condition, + inner_action, state); + if (ret != LTTNG_OK) { + ERR("Error syncing tracer executed action"); + goto end; + } + } + } + +end: + return ret; +} + /* * Fill lttng_channel array of all channels. */ @@ -483,13 +831,19 @@ static int list_lttng_agent_events(struct agent *agt, cds_lfht_for_each_entry ( agt->events->ht, &iter.iter, agent_event, node.node) { struct lttng_event event = { - .enabled = agent_event->enabled, + .enabled = AGENT_EVENT_IS_ENABLED(agent_event), .loglevel = agent_event->loglevel_value, .loglevel_type = agent_event->loglevel_type, }; - strncpy(event.name, agent_event->name, sizeof(event.name)); - event.name[sizeof(event.name) - 1] = '\0'; + ret = lttng_strncpy(event.name, agent_event->name, sizeof(event.name)); + if (ret) { + /* Internal error, invalid name. */ + ERR("Invalid event name while listing agent events: '%s' exceeds the maximal allowed length of %zu bytes", + agent_event->name, sizeof(event.name)); + ret = -LTTNG_ERR_UNK; + goto end; + } ret = lttng_dynamic_buffer_append( &payload->buffer, &event, sizeof(event)); @@ -556,8 +910,14 @@ static int list_lttng_ust_global_events(char *channel_name, continue; } - strncpy(event.name, uevent->attr.name, sizeof(event.name)); - event.name[sizeof(event.name) - 1] = '\0'; + ret = lttng_strncpy(event.name, uevent->attr.name, sizeof(event.name)); + if (ret) { + /* Internal error, invalid name. */ + ERR("Invalid event name while listing user space tracer events: '%s' exceeds the maximal allowed length of %zu bytes", + uevent->attr.name, sizeof(event.name)); + ret = -LTTNG_ERR_UNK; + goto end; + } event.enabled = uevent->enabled; @@ -647,8 +1007,16 @@ static int list_lttng_kernel_events(char *channel_name, cds_list_for_each_entry(kevent, &kchan->events_list.head , list) { struct lttng_event event = {}; - strncpy(event.name, kevent->event->name, sizeof(event.name)); - event.name[sizeof(event.name) - 1] = '\0'; + ret = lttng_strncpy(event.name, kevent->event->name, sizeof(event.name)); + if (ret) { + /* Internal error, invalid name. */ + ERR("Invalid event name while listing kernel events: '%s' exceeds the maximal allowed length of %zu bytes", + kevent->event->name, + sizeof(event.name)); + ret = -LTTNG_ERR_UNK; + goto end; + } + event.enabled = kevent->enabled; event.filter = (unsigned char) !!kevent->filter_expression; @@ -1471,31 +1839,262 @@ int cmd_enable_channel(struct ltt_session *session, } } - chan_ht = usess->domain_global.channels; + chan_ht = usess->domain_global.channels; + + uchan = trace_ust_find_channel_by_name(chan_ht, attr.name); + if (uchan == NULL) { + ret = channel_ust_create(usess, &attr, domain->buf_type); + if (attr.name[0] != '\0') { + usess->has_non_default_channel = 1; + } + } else { + ret = channel_ust_enable(usess, uchan); + } + break; + } + default: + ret = LTTNG_ERR_UNKNOWN_DOMAIN; + goto error; + } + + if (ret == LTTNG_OK && attr.attr.output != LTTNG_EVENT_MMAP) { + session->has_non_mmap_channel = true; + } +error: + rcu_read_unlock(); +end: + return ret; +} + +enum lttng_error_code cmd_add_map(struct command_ctx *cmd_ctx, int sock) +{ + int ret; + enum lttng_error_code ret_code; + size_t map_len; + struct lttng_payload map_payload; + ssize_t sock_recv_len; + struct lttng_map *map = NULL; + const struct lttng_credentials cmd_creds = { + .uid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), + }; + + lttng_payload_init(&map_payload); + map_len = (size_t) cmd_ctx->lsm.u.add_map.length; + ret = lttng_dynamic_buffer_set_size( + &map_payload.buffer, map_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, map_payload.buffer.data, map_len); + if (sock_recv_len < 0 || sock_recv_len != map_len) { + ERR("Failed to receive \"register map\" command payload"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + + /* Deserialize map. */ + { + struct lttng_payload_view view = + lttng_payload_view_from_payload( + &map_payload, 0, -1); + + if (lttng_map_create_from_payload(&view, &map) != map_len) { + ERR("Invalid map payload received in \"add map\" command"); + ret_code = LTTNG_ERR_INVALID_MAP; + goto end; + } + } + + switch (lttng_map_get_domain(map)) { + case LTTNG_DOMAIN_KERNEL: + ret_code = map_kernel_add(cmd_ctx->session->kernel_session, map); + if (ret_code != LTTNG_OK) { + ERR("Creating a new kernel map: %s", lttng_strerror(ret_code)); + goto end; + } + + ret_code = LTTNG_OK; + break; + case LTTNG_DOMAIN_UST: + ret = map_ust_add(cmd_ctx->session->ust_session, map); + if (ret) { + ERR("Creating a new UST map: %s", lttng_strerror(-ret)); + ret_code = ret; + goto end; + } + + ret_code = LTTNG_OK; + break; + default: + abort(); + } + + + { + struct lttng_triggers *triggers = NULL; + enum lttng_trigger_status t_status; + unsigned int count, i; + + /* + * FRDESO: beware of moving this code. This is currently not + * racy because this is executed by the client thread and the + * client thread is the thread registering new triggers. If + * this code is relocate special care must be taken. + */ + ret_code = notification_thread_command_list_triggers( + notification_thread_handle, 0, &triggers); + if (ret_code != LTTNG_OK) { + ret = -1; + goto end; + } + + assert(triggers); + + t_status = lttng_triggers_get_count(triggers, &count); + if (t_status != LTTNG_TRIGGER_STATUS_OK) { + ret = -1; + lttng_triggers_destroy(triggers); + goto end; + } + + for (i = 0; i < count; i++) { + const struct lttng_trigger *trigger; + + trigger = lttng_triggers_get_at_index(triggers, i); + assert(trigger); + + ret_code = sync_all_tracer_executed_actions(trigger, + &cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); + assert(ret_code == LTTNG_OK); + } + + lttng_triggers_destroy(triggers); + } + + lttng_map_put(map); + +end: + lttng_payload_reset(&map_payload); + return ret_code; +} + +enum lttng_error_code cmd_enable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + struct ltt_ust_session *usess = session->ust_session; + enum lttng_error_code ret_code; + + DBG("Enabling map %s for session %s", map_name, session->name); + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_enable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_enable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + default: + abort(); + } + + ret_code = LTTNG_OK; +error: + rcu_read_unlock(); + return ret_code; +} + +enum lttng_error_code cmd_disable_map(struct ltt_session *session, + enum lttng_domain_type domain, char *map_name) +{ + enum lttng_error_code ret_code; + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_map *kmap; + struct ltt_kernel_session *ksess = session->kernel_session; + + kmap = trace_kernel_get_map_by_name(map_name, ksess); + if (kmap == NULL) { + ret_code = LTTNG_ERR_KERNEL_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_kernel_disable(ksess, kmap); + if (ret_code != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht *map_ht; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); - uchan = trace_ust_find_channel_by_name(chan_ht, attr.name); - if (uchan == NULL) { - ret = channel_ust_create(usess, &attr, domain->buf_type); - if (attr.name[0] != '\0') { - usess->has_non_default_channel = 1; - } - } else { - ret = channel_ust_enable(usess, uchan); + map_ht = usess->domain_global.maps; + + umap = trace_ust_find_map_by_name(map_ht, map_name); + if (umap == NULL) { + ret_code = LTTNG_ERR_UST_MAP_NOT_FOUND; + goto error; + } + + ret_code = map_ust_disable(usess, umap); + if (ret_code != LTTNG_OK) { + goto error; } break; } default: - ret = LTTNG_ERR_UNKNOWN_DOMAIN; - goto error; + abort(); } - if (ret == LTTNG_OK && attr.attr.output != LTTNG_EVENT_MMAP) { - session->has_non_mmap_channel = true; - } + ret_code = LTTNG_OK; + error: rcu_read_unlock(); -end: - return ret; + return ret_code; } enum lttng_error_code cmd_process_attr_tracker_get_tracking_policy( @@ -2058,7 +2657,7 @@ static int _cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe, bool internal_event) { @@ -2141,7 +2740,7 @@ static int _cmd_enable_event(struct ltt_session *session, case LTTNG_EVENT_ALL: { char *filter_expression_a = NULL; - struct lttng_filter_bytecode *filter_a = NULL; + struct lttng_bytecode *filter_a = NULL; /* * We need to duplicate filter_expression and filter, @@ -2376,11 +2975,11 @@ static int _cmd_enable_event(struct ltt_session *session, { char *filter_expression_copy = NULL; - struct lttng_filter_bytecode *filter_copy = NULL; + struct lttng_bytecode *filter_copy = NULL; if (filter) { const size_t filter_size = sizeof( - struct lttng_filter_bytecode) + struct lttng_bytecode) + filter->len; filter_copy = zmalloc(filter_size); @@ -2456,7 +3055,7 @@ int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe) { @@ -2473,7 +3072,7 @@ static int cmd_enable_event_internal(struct ltt_session *session, const struct lttng_domain *domain, char *channel_name, struct lttng_event *event, char *filter_expression, - struct lttng_filter_bytecode *filter, + struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, int wpipe) { @@ -2569,7 +3168,7 @@ ssize_t cmd_list_syscalls(struct lttng_event **events) int cmd_start_trace(struct ltt_session *session) { enum lttng_error_code ret; - unsigned long nb_chan = 0; + unsigned long nb_chan = 0, nb_map = 0; struct ltt_kernel_session *ksession; struct ltt_ust_session *usess; const bool session_rotated_after_last_stop = @@ -2612,11 +3211,13 @@ int cmd_start_trace(struct ltt_session *session) */ if (usess && usess->domain_global.channels) { nb_chan += lttng_ht_get_count(usess->domain_global.channels); + nb_map += lttng_ht_get_count(usess->domain_global.maps); } if (ksession) { nb_chan += ksession->channel_count; + nb_map += ksession->map_count; } - if (!nb_chan) { + if (!nb_chan && !nb_map) { ret = LTTNG_ERR_NO_CHANNEL; goto error; } @@ -3107,10 +3708,22 @@ enum lttng_error_code cmd_create_session(struct command_ctx *cmd_ctx, int sock, &payload, 0, cmd_ctx->lsm.u.create_session.home_dir_size); + if (cmd_ctx->lsm.u.create_session.home_dir_size > 0 && + !lttng_buffer_view_is_valid(&home_dir_view)) { + ERR("Invalid payload in \"create session\" command: buffer too short to contain home directory"); + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto error; + } + session_descriptor_view = lttng_buffer_view_from_dynamic_buffer( &payload, cmd_ctx->lsm.u.create_session.home_dir_size, cmd_ctx->lsm.u.create_session.session_descriptor_size); + if (!lttng_buffer_view_is_valid(&session_descriptor_view)) { + ERR("Invalid payload in \"create session\" command: buffer too short to contain session descriptor"); + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto error; + } ret = lttng_session_descriptor_create_from_buffer( &session_descriptor_view, &session_descriptor); @@ -3616,6 +4229,64 @@ end: return ret; } +enum lttng_error_code cmd_list_maps(enum lttng_domain_type domain, + struct ltt_session *session, + struct lttng_map_list **return_map_list) +{ + enum lttng_error_code ret_code; + struct lttng_map_list *map_list = NULL; + + map_list = lttng_map_list_create(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + struct ltt_kernel_map *kmap; + cds_list_for_each_entry(kmap, + &session->kernel_session->map_list.head, list) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, kmap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending kernel map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + + } + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_map *umap; + struct lttng_ht_iter iter; + + rcu_read_lock(); + cds_lfht_for_each_entry(session->ust_session->domain_global.maps->ht, + &iter.iter, umap, node.node) { + enum lttng_map_status map_status; + map_status = lttng_map_list_add(map_list, umap->map); + if (map_status != LTTNG_MAP_STATUS_OK) { + ERR("Error appending UST map to list"); + ret_code = LTTNG_ERR_FATAL; + break; + } + } + rcu_read_unlock(); + break; + } + default: + ret_code = LTTNG_ERR_UND; + goto end; + } + + *return_map_list = map_list; + map_list = NULL; + ret_code = LTTNG_OK; +end: + lttng_map_list_destroy(map_list); + return ret_code; +} + /* * Command LTTNG_LIST_EVENTS processed by the client thread. */ @@ -3715,7 +4386,7 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, /* * Only list the sessions the user can control. */ - if (!session_access_ok(session, uid, gid) || + if (!session_access_ok(session, uid) || session->destroyed) { session_put(session); continue; @@ -4166,7 +4837,7 @@ end: * then regenerate the metadata. Live and per-pid sessions are not * supported and return an error. * - * Return 0 on success or else a LTTNG_ERR code. + * Return LTTNG_OK on success or else a LTTNG_ERR code. */ int cmd_regenerate_metadata(struct ltt_session *session) { @@ -4207,7 +4878,7 @@ end: * * Ask the tracer to regenerate a new statedump. * - * Return 0 on success or else a LTTNG_ERR code. + * Return LTTNG_OK on success or else a LTTNG_ERR code. */ int cmd_regenerate_statedump(struct ltt_session *session) { @@ -4256,142 +4927,426 @@ end: return ret; } -int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread) +static +enum lttng_error_code synchronize_tracer_notifier_register( + struct notification_thread_handle *notification_thread, + struct lttng_trigger *trigger, const struct lttng_credentials *cmd_creds) { - int ret; - size_t trigger_len; - ssize_t sock_recv_len; - struct lttng_trigger *trigger = NULL; - struct lttng_payload trigger_payload; - struct lttng_credentials cmd_creds = { - .uid = cmd_ctx->creds.uid, - .gid = cmd_ctx->creds.gid, - }; + enum lttng_error_code ret_code; + struct lttng_condition *condition = lttng_trigger_get_condition(trigger); + const char *trigger_name; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); - lttng_payload_init(&trigger_payload); - trigger_len = (size_t) cmd_ctx->lsm.u.trigger.length; - ret = lttng_dynamic_buffer_set_size( - &trigger_payload.buffer, trigger_len); - if (ret) { - ret = LTTNG_ERR_NOMEM; + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { + ret_code = kernel_register_event_notifier(trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + enum lttng_error_code notif_thread_unregister_ret; + + notif_thread_unregister_ret = + notification_thread_command_unregister_trigger( + notification_thread, trigger); + + if (notif_thread_unregister_ret != LTTNG_OK) { + /* Return the original error code. */ + ERR("Failed to unregister trigger from notification thread during error recovery: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, + (int) trigger_owner, + ret_code); + } + } + break; + } + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: + { + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } + + ret_code = trigger_agent_enable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } + } + + ret_code = LTTNG_OK; +end_unlock_session_list: + session_unlock_list(); + return ret_code; +} + +enum lttng_error_code cmd_register_trigger(const struct lttng_credentials *cmd_creds, + struct lttng_trigger *trigger, + struct notification_thread_handle *notification_thread, + struct lttng_trigger **return_trigger) +{ + enum lttng_error_code ret_code; + const char *trigger_name; + const struct lttng_condition *condition; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; + + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? + trigger_name : "(unnamed)"; + + trigger_status = lttng_trigger_get_owner_uid( + trigger, &trigger_owner); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + DBG("Running register trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", + trigger_name, (int) trigger_owner, + (int) lttng_credentials_get_uid(cmd_creds)); + +/* + * Validate the trigger credentials against the command credentials. + * Only the root user can register a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + cmd_creds)) { + if (lttng_credentials_get_uid(cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", + trigger_name, (int) trigger_owner, + (int) lttng_credentials_get_uid(cmd_creds)); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } + + /* + * The bytecode generation also serves as a validation step for the + * bytecode expressions. + */ + ret_code = lttng_trigger_generate_bytecode(trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + ERR("Failed to generate bytecode of trigger: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, (int) trigger_owner, ret_code); goto end; } - sock_recv_len = lttcomm_recv_unix_sock( - sock, trigger_payload.buffer.data, trigger_len); - if (sock_recv_len < 0 || sock_recv_len != trigger_len) { - ERR("Failed to receive \"register trigger\" command payload"); - /* TODO: should this be a new error enum ? */ - ret = LTTNG_ERR_INVALID_TRIGGER; + /* + * A reference to the trigger is acquired by the notification thread. + * It is safe to return the same trigger to the caller since it the + * other user holds a reference. + * + * The trigger is modified during the execution of the + * "register trigger" command. However, by the time the command returns, + * it is safe to use without any locking as its properties are + * immutable. + */ + ret_code = notification_thread_command_register_trigger(notification_thread, + trigger); + if (ret_code != LTTNG_OK) { + DBG("Failed to register trigger to notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, (int) trigger_owner, ret_code); goto end; } - /* Receive fds, if any. */ - if (cmd_ctx->lsm.fd_count > 0) { - ret = lttcomm_recv_payload_fds_unix_sock( - sock, cmd_ctx->lsm.fd_count, &trigger_payload); - if (ret > 0 && ret != cmd_ctx->lsm.fd_count * sizeof(int)) { - ret = LTTNG_ERR_INVALID_PROTOCOL; + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? + trigger_name : "(unnamed)"; + + /* + * Synchronize tracers if the trigger adds an event notifier. + */ + if (lttng_trigger_needs_tracer_notifier(trigger)) { + ret_code = synchronize_tracer_notifier_register(notification_thread, + trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer notifier"); goto end; - } else if (ret <= 0) { - ret = LTTNG_ERR_FATAL; + } + } + + condition = lttng_trigger_get_const_condition(trigger); + + /* TODO: Extract condition below to lttng_trigger internal function */ + if (lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT) { + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_REGISTER); + session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); goto end; } } - /* Deserialize trigger. */ + /* + * Return an updated trigger to the client. + * + * Since a modified version of the same trigger is returned, acquire a + * reference to the trigger so the caller doesn't have to care if those + * are distinct instances or not. + */ + if (ret_code == LTTNG_OK) { + lttng_trigger_get(trigger); + *return_trigger = trigger; + /* Ownership of trigger was transferred to caller. */ + trigger = NULL; + } +end: + return ret_code; +} + +static +enum lttng_error_code synchronize_tracer_notifier_unregister( + const struct lttng_trigger *trigger) +{ + enum lttng_error_code ret_code; + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + assert(condition); + assert(lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_ON_EVENT); + + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + ret_code = kernel_unregister_event_notifier(trigger); + break; + case LTTNG_DOMAIN_UST: + ust_app_global_update_all_event_notifier_rules(); + break; + case LTTNG_DOMAIN_NONE: + abort(); + default: { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &trigger_payload, 0, -1); + /* Agent domains. */ + struct agent *agt = agent_find_by_event_notifier_domain( + trigger_domain); + if (!agt) { + agt = agent_create(trigger_domain); + if (!agt) { + ret_code = LTTNG_ERR_NOMEM; + goto end_unlock_session_list; + } + agent_add(agt, trigger_agents_ht_by_domain); + } - if (lttng_trigger_create_from_payload(&view, &trigger) != - trigger_len) { - ERR("Invalid trigger payload received in \"register trigger\" command"); - ret = LTTNG_ERR_INVALID_TRIGGER; - goto end; + ret_code = trigger_agent_disable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; } + + break; + } } - /* Set the trigger credential */ - lttng_trigger_set_credentials(trigger, &cmd_creds); + ret_code = LTTNG_OK; - /* Inform the notification thread */ - ret = notification_thread_command_register_trigger(notification_thread, - trigger); - /* Ownership of trigger was transferred. */ - trigger = NULL; -end: - lttng_trigger_destroy(trigger); - lttng_payload_reset(&trigger_payload); - return ret; +end_unlock_session_list: + session_unlock_list(); + return ret_code; } -int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, +enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd_creds, + const struct lttng_trigger *trigger, struct notification_thread_handle *notification_thread) { - int ret; - size_t trigger_len; - ssize_t sock_recv_len; - struct lttng_trigger *trigger = NULL; - struct lttng_payload trigger_payload; - struct lttng_credentials cmd_creds = { - .uid = cmd_ctx->creds.uid, - .gid = cmd_ctx->creds.gid, - }; + enum lttng_error_code ret_code; + const char *trigger_name; + struct lttng_trigger *real_trigger; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; - lttng_payload_init(&trigger_payload); - trigger_len = (size_t) cmd_ctx->lsm.u.trigger.length; - ret = lttng_dynamic_buffer_set_size( - &trigger_payload.buffer, trigger_len); - if (ret) { - ret = LTTNG_ERR_NOMEM; + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? trigger_name : "(unnamed)"; + trigger_status = lttng_trigger_get_owner_uid(trigger, &trigger_owner); + assert(trigger_status == LTTNG_TRIGGER_STATUS_OK); + + DBG("Running unregister trigger command: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", + trigger_name, (int) trigger_owner, + (int) lttng_credentials_get_uid(cmd_creds)); + + /* + * Validate the trigger credentials against the command credentials. + * Only the root user can unregister a trigger with non-matching + * credentials. + */ + if (!lttng_credentials_is_equal_uid( + lttng_trigger_get_credentials(trigger), + cmd_creds)) { + if (lttng_credentials_get_uid(cmd_creds) != 0) { + ERR("Trigger credentials do not match the command credentials: trigger name = '%s', trigger owner uid = %d, command creds uid = %d", + trigger_name, (int) trigger_owner, + (int) lttng_credentials_get_uid(cmd_creds)); + ret_code = LTTNG_ERR_INVALID_TRIGGER; + goto end; + } + } + + ret_code = notification_thread_command_get_trigger( + notification_thread, trigger, &real_trigger); + if (ret_code != LTTNG_OK) { + DBG("Failed to get the real trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, (int) trigger_owner, ret_code); goto end; } - sock_recv_len = lttcomm_recv_unix_sock( - sock, trigger_payload.buffer.data, trigger_len); - if (sock_recv_len < 0 || sock_recv_len != trigger_len) { - ERR("Failed to receive \"unregister trigger\" command payload"); - /* TODO: should this be a new error enum ? */ - ret = LTTNG_ERR_INVALID_TRIGGER; + ret_code = notification_thread_command_unregister_trigger( + notification_thread, real_trigger); + if (ret_code != LTTNG_OK) { + DBG("Failed to unregister trigger from notification thread: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, (int) trigger_owner, ret_code); goto end; } - /* Receive fds, if any. */ - if (cmd_ctx->lsm.fd_count > 0) { - ret = lttcomm_recv_payload_fds_unix_sock( - sock, cmd_ctx->lsm.fd_count, &trigger_payload); - if (ret > 0 && ret != cmd_ctx->lsm.fd_count * sizeof(int)) { - ret = LTTNG_ERR_INVALID_PROTOCOL; - goto end; - } else if (ret <= 0) { - ret = LTTNG_ERR_FATAL; + /* + * Synchronize tracers if the trigger removes an event notifier. + * Do this even if the trigger unregistration failed to at least stop + * the tracers from producing notifications associated with this + * event notifier. + */ + if (lttng_trigger_needs_tracer_notifier(real_trigger)) { + ret_code = synchronize_tracer_notifier_unregister(real_trigger); + if (ret_code != LTTNG_OK) { + ERR("Error unregistering trigger to tracer."); goto end; } + } - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &trigger_payload, 0, -1); + session_lock_list(); + ret_code = sync_all_tracer_executed_actions(real_trigger, + cmd_creds, TRACER_EXECUTED_ACTION_STATE_UNREGISTER); + session_unlock_list(); + if (ret_code != LTTNG_OK) { + ERR("Error registering tracer executed actions"); + goto end; + } - if (lttng_trigger_create_from_payload(&view, &trigger) != - trigger_len) { - ERR("Invalid trigger payload received in \"unregister trigger\" command"); - ret = LTTNG_ERR_INVALID_TRIGGER; - goto end; + lttng_trigger_put(real_trigger); +end: + return ret_code; +} + +int cmd_list_triggers(struct command_ctx *cmd_ctx, + struct notification_thread_handle *notification_thread, + struct lttng_triggers **return_triggers) +{ + int ret = 0; + enum lttng_error_code ret_code; + struct lttng_triggers *triggers = NULL; + + /* Get the set of triggers from the notification thread. */ + ret_code = notification_thread_command_list_triggers( + notification_thread, cmd_ctx->creds.uid, &triggers); + if (ret_code != LTTNG_OK) { + ret = ret_code; + goto end; + } + + *return_triggers = triggers; + triggers = NULL; + ret = LTTNG_OK; +end: + lttng_triggers_destroy(triggers); + return ret; +} + +int cmd_list_map_values(const char *session_name, + const struct lttng_map *map, + const struct lttng_map_query *map_query, + struct lttng_map_content **return_map_content) +{ + enum lttng_error_code ret; + struct ltt_session *session; + enum lttng_domain_type domain; + const char *map_name; + enum lttng_map_status map_status; + + /* Returns a refcounted reference */ + session = session_find_by_name(session_name); + if (!session) { + DBG("Session '%s' not found", session_name); + ret = LTTNG_ERR_SESS_NOT_FOUND; + goto end; + } + + domain = lttng_map_get_domain(map); + + map_status = lttng_map_get_name(map, &map_name); + assert(map_status == LTTNG_MAP_STATUS_OK); + + if (domain == LTTNG_DOMAIN_KERNEL) { + if (session->kernel_session) { + struct ltt_kernel_map *kmap; + + kmap = trace_kernel_get_map_by_name(map_name, + session->kernel_session); + if (kmap) { + ret = kernel_list_map_values(kmap, map_query, + return_map_content); + if (ret != LTTNG_OK) { + ERR("Error listing kernel map '%s' values", map_name); + goto end; + } + } else { + DBG("No kernel map '%s' in session '%s'", map_name, session_name); + } + } + } else if (domain == LTTNG_DOMAIN_UST) { + if (session->ust_session) { + struct ltt_ust_map *umap; + struct ltt_ust_session *usess = session->ust_session; + + umap = trace_ust_find_map_by_name( + usess->domain_global.maps, map_name); + if (umap) { + ret = ust_app_map_list_values(usess, umap, + map_query, return_map_content); + if (ret) { + ret = LTTNG_ERR_MAP_VALUES_LIST_FAIL; + ERR("Error listing UST map '%s' values", map_name); + goto end; + } + } else { + DBG("No UST map '%s' in session '%s'", map_name, session_name); + } } } - lttng_trigger_set_credentials(trigger, &cmd_creds); - ret = notification_thread_command_unregister_trigger(notification_thread, - trigger); + ret = LTTNG_OK; end: - lttng_trigger_destroy(trigger); - lttng_payload_reset(&trigger_payload); + session_put(session); return ret; } @@ -4916,7 +5871,7 @@ int cmd_set_session_shm_path(struct ltt_session *session, sizeof(session->shm_path)); session->shm_path[sizeof(session->shm_path) - 1] = '\0'; - return 0; + return LTTNG_OK; } /* @@ -5269,7 +6224,7 @@ end: * 'activate' to false means deactivate the rotation schedule and validate that * 'new_value' has the same value as the currently active value. * - * Return 0 on success or else a positive LTTNG_ERR code. + * Return LTTNG_OK on success or else a positive LTTNG_ERR code. */ int cmd_rotation_set_schedule(struct ltt_session *session, bool activate, enum lttng_rotation_schedule_type schedule_type,