X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=8d36e7192c8deb32a46ca471c5d58dafd5e19d63;hp=39e824bdef7e4fc10fae81cafc8f2882d4a13e81;hb=39b95a70bbb440eb42a548df21742f6ddebe0e2d;hpb=3e6e0df2f8f9f23d252c2508b6d741916dfcc4b3 diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 39e824bde..8d36e7192 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -29,6 +29,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -483,13 +488,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 +567,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 +664,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; @@ -4268,64 +4293,46 @@ end: return ret; } -int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, - struct notification_thread_handle *notification_thread, - struct lttng_trigger **return_trigger) +static enum lttng_error_code trigger_modifies_event_notifier( + const struct lttng_trigger *trigger, bool *adds_event_notifier) { - 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 = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), - .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), - }; + enum lttng_error_code ret_code = LTTNG_OK; + const struct lttng_condition *condition = NULL; - 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; + condition = lttng_trigger_get_const_condition(trigger); + if (!condition) { + ret_code = LTTNG_ERR_INVALID_TRIGGER; 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; - goto end; - } + *adds_event_notifier = lttng_condition_get_type(condition) == + LTTNG_CONDITION_TYPE_EVENT_RULE_HIT; +end: + return ret_code; +} - /* 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; - goto end; - } - } +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; + bool must_update_event_notifiers; + const char *trigger_name; + uid_t trigger_owner; + enum lttng_trigger_status trigger_status; - /* Deserialize trigger. */ - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &trigger_payload, 0, -1); - - 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; - } - } + 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. @@ -4334,14 +4341,27 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, */ 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"); - ret = LTTNG_ERR_INVALID_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; + } + /* * 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 @@ -4352,81 +4372,126 @@ int cmd_register_trigger(struct command_ctx *cmd_ctx, int sock, * it is safe to use without any locking as its properties are * immutable. */ - ret = notification_thread_command_register_trigger(notification_thread, + ret_code = notification_thread_command_register_trigger(notification_thread, trigger); - if (ret != LTTNG_OK) { - goto end_notification_thread; + 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; } - /* Return an updated trigger to the client. */ - *return_trigger = trigger; - -end_notification_thread: - /* Ownership of trigger was transferred. */ - trigger = NULL; -end: - lttng_trigger_destroy(trigger); - lttng_payload_reset(&trigger_payload); - return ret; -} - -int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, - 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 = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.uid), - .gid = LTTNG_OPTIONAL_INIT_VALUE(cmd_ctx->creds.gid), - }; + trigger_status = lttng_trigger_get_name(trigger, &trigger_name); + trigger_name = trigger_status == LTTNG_TRIGGER_STATUS_OK ? + trigger_name : "(unnamed)"; - 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; + ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); + if (ret_code != LTTNG_OK) { + ERR("Failed to determine if event modifies event notifiers: 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; - goto end; - } + /* + * Synchronize tracers if the trigger adds an event notifier. + */ + if (must_update_event_notifiers) { + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction(trigger); - /* 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; - goto end; + session_lock_list(); + switch (trigger_domain) { + case LTTNG_DOMAIN_KERNEL: + { + ret_code = kernel_register_event_notifier( + trigger, cmd_creds); + if (ret_code != LTTNG_OK) { + const enum lttng_error_code 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); - { - struct lttng_payload_view view = - lttng_payload_view_from_payload( - &trigger_payload, 0, -1); - - 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; + 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; } + } + + session_unlock_list(); } + /* + * 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; +end_unlock_session_list: + session_unlock_list(); + return ret_code; +} + +enum lttng_error_code cmd_unregister_trigger(const struct lttng_credentials *cmd_creds, + const struct lttng_trigger *trigger, + struct notification_thread_handle *notification_thread) +{ + enum lttng_error_code ret_code; + bool must_update_event_notifiers; + const char *trigger_name; + 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 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 @@ -4434,22 +4499,110 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, */ 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"); - ret = LTTNG_ERR_INVALID_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 = notification_thread_command_unregister_trigger(notification_thread, - trigger); + ret_code = trigger_modifies_event_notifier(trigger, &must_update_event_notifiers); + if (ret_code != LTTNG_OK) { + ERR("Failed to determine if event modifies event notifiers: trigger name = '%s', trigger owner uid = %d, error code = %d", + trigger_name, (int) trigger_owner, ret_code); + goto end; + } + + ret_code = notification_thread_command_unregister_trigger(notification_thread, + 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); + } + + /* + * 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 (must_update_event_notifiers) { + const enum lttng_domain_type trigger_domain = + lttng_trigger_get_underlying_domain_type_restriction( + trigger); + + 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: + { + /* 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_disable(trigger, agt); + if (ret_code != LTTNG_OK) { + goto end_unlock_session_list; + } + + break; + } + } + + session_unlock_list(); + } + +end: + return ret_code; +end_unlock_session_list: + session_unlock_list(); + 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_trigger_destroy(trigger); - lttng_payload_reset(&trigger_payload); + lttng_triggers_destroy(triggers); return ret; } - /* * Send relayd sockets from snapshot output to consumer. Ignore request if the * snapshot output is *not* set with a remote destination.