X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=03a358a0d3f1da9478ef220c3e93309298b469f7;hp=4ee4bea64ff630abae9617624f86ce21cc87950d;hb=e8b1187d0ff0323106069738b2fff5c9e5a7d9bb;hpb=ab5be9fa2eb5ba9600a82cd18fd3cfcbac69169a diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 4ee4bea64..03a358a0d 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -15,13 +15,28 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include #include "lttng-sessiond.h" #include "lttng-syscall.h" +#include "condition-internal.h" #include "consumer.h" #include "kernel.h" #include "kernel-consumer.h" @@ -29,6 +44,8 @@ #include "utils.h" #include "rotate.h" #include "modprobe.h" +#include "tracker.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -39,9 +56,10 @@ static uint64_t next_kernel_channel_key; static const char *module_proc_lttng = "/proc/lttng"; static int kernel_tracer_fd = -1; +static int kernel_tracer_event_notifier_group_fd = -1; +static int kernel_tracer_event_notifier_group_notification_fd = -1; +static struct cds_lfht *kernel_token_to_event_notifier_rule_ht; -#include -#include /* * Add context on a kernel channel. * @@ -213,6 +231,51 @@ error: return -1; } +/* + * Create a kernel event notifier group, register it to the kernel tracer and + * add it to the kernel session. + */ +static int kernel_create_event_notifier_group(int *event_notifier_group_fd) +{ + int ret; + int local_fd = -1; + + assert(event_notifier_group_fd); + + /* Kernel event notifier group creation. */ + ret = kernctl_create_event_notifier_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group"); + ret = -1; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group file descriptor: fd = %d", + local_fd); + goto error; + } + + DBG("Created kernel event notifier group: fd = %d", local_fd); + *event_notifier_group_fd = local_fd; + local_fd = -1; + ret = 0; +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + local_fd); + } + } + + return ret; +} + /* * Compute the offset of the instrumentation byte in the binary based on the * function probe location using the ELF lookup method. @@ -224,7 +287,7 @@ error: static int extract_userspace_probe_offset_function_elf( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t *offset) + uid_t uid, gid_t gid, uint64_t *offset) { int fd; int ret = 0; @@ -261,8 +324,7 @@ int extract_userspace_probe_offset_function_elf( goto end; } - ret = run_as_extract_elf_symbol_offset(fd, symbol, session->uid, - session->gid, offset); + ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset); if (ret < 0) { DBG("userspace probe offset calculation failed for " "function %s", symbol); @@ -286,7 +348,7 @@ end: static int extract_userspace_probe_offset_tracepoint_sdt( const struct lttng_userspace_probe_location *probe_location, - struct ltt_kernel_session *session, uint64_t **offsets, + uid_t uid, gid_t gid, uint64_t **offsets, uint32_t *offsets_count) { enum lttng_userspace_probe_location_lookup_method_type lookup_method_type; @@ -332,7 +394,7 @@ int extract_userspace_probe_offset_tracepoint_sdt( } ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name, - session->uid, session->gid, offsets, offsets_count); + uid, gid, offsets, offsets_count); if (ret < 0) { DBG("userspace probe offset calculation failed for sdt " "probe %s:%s", provider_name, probe_name); @@ -353,29 +415,16 @@ end: return ret; } -/* - * Extract the offsets of the instrumentation point for the different lookup - * methods. - */ static -int userspace_probe_add_callsites(struct lttng_event *ev, - struct ltt_kernel_session *session, int fd) +int userspace_probe_add_callsite( + const struct lttng_userspace_probe_location *location, + uid_t uid, gid_t gid, int fd) { const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; enum lttng_userspace_probe_location_lookup_method_type type; - const struct lttng_userspace_probe_location *location = NULL; int ret; - assert(ev); - assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); - - location = lttng_event_get_userspace_probe_location(ev); - if (!location) { - ret = -1; - goto end; - } - lookup_method = - lttng_userspace_probe_location_get_lookup_method(location); + lookup_method = lttng_userspace_probe_location_get_lookup_method(location); if (!lookup_method) { ret = -1; goto end; @@ -388,7 +437,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, struct lttng_kernel_event_callsite callsite; uint64_t offset; - ret = extract_userspace_probe_offset_function_elf(location, session, &offset); + ret = extract_userspace_probe_offset_function_elf(location, + uid, gid, &offset); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -397,8 +447,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offset; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Failed to add callsite to ELF userspace probe."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; goto end; } @@ -415,8 +464,8 @@ int userspace_probe_add_callsites(struct lttng_event *ev, * This call allocates the offsets buffer. This buffer must be freed * by the caller */ - ret = extract_userspace_probe_offset_tracepoint_sdt(location, session, - &offsets, &offsets_count); + ret = extract_userspace_probe_offset_tracepoint_sdt(location, + uid, gid, &offsets, &offsets_count); if (ret) { ret = LTTNG_ERR_PROBE_LOCATION_INVAL; goto end; @@ -425,8 +474,7 @@ int userspace_probe_add_callsites(struct lttng_event *ev, callsite.u.uprobe.offset = offsets[i]; ret = kernctl_add_callsite(fd, &callsite); if (ret) { - WARN("Adding callsite to userspace probe " - "event %s failed.", ev->name); + WARN("Failed to add callsite to SDT userspace probe"); ret = LTTNG_ERR_KERN_ENABLE_FAIL; free(offsets); goto end; @@ -443,6 +491,75 @@ end: return ret; } +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static +int userspace_probe_event_add_callsites(struct lttng_event *ev, + struct ltt_kernel_session *session, int fd) +{ + int ret; + const struct lttng_userspace_probe_location *location = NULL; + + assert(ev); + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + location = lttng_event_get_userspace_probe_location(ev); + if (!location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, session->uid, session->gid, + fd); + if (ret) { + WARN("Failed to add callsite to userspace probe event '%s'", + ev->name); + } + +end: + return ret; +} + +/* + * Extract the offsets of the instrumentation point for the different look-up + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + int ret; + enum lttng_event_rule_status status; + enum lttng_event_rule_type event_rule_type; + const struct lttng_userspace_probe_location *location = NULL; + + assert(rule); + assert(creds); + + event_rule_type = lttng_event_rule_get_type(rule); + assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE); + + status = lttng_event_rule_userspace_probe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, + lttng_credentials_get_uid(creds), + lttng_credentials_get_gid(creds), fd); + if (ret) { + WARN("Failed to add callsite to user space probe object: fd = %d", + fd); + } + +end: + return ret; +} + /* * Create a kernel event, enable it to the kernel tracer and add it to the * channel event list of the kernel session. @@ -451,7 +568,7 @@ end: int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, char *filter_expression, - struct lttng_filter_bytecode *filter) + struct lttng_bytecode *filter) { int err, fd; enum lttng_error_code ret; @@ -512,7 +629,8 @@ int kernel_create_event(struct lttng_event *ev, } if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { - ret = userspace_probe_add_callsites(ev, channel->session, event->fd); + ret = userspace_probe_event_add_callsites(ev, channel->session, + event->fd); if (ret) { goto add_callsite_error; } @@ -669,281 +787,406 @@ error: return ret; } -static struct lttng_tracker_list *get_id_tracker_list( +/* + * Disable a kernel event notifier. + */ +static +int kernel_disable_event_notifier_rule(struct ltt_kernel_event_notifier_rule *event) +{ + int ret; + + assert(event); + + rcu_read_lock(); + cds_lfht_del(kernel_token_to_event_notifier_rule_ht, &event->ht_node); + rcu_read_unlock(); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + switch (-ret) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("Failed to disable kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + break; + } + goto error; + } + + event->enabled = 0; + DBG("Disabled kernel event notifier: fd = %d, token = %" PRIu64, + event->fd, event->token); + +error: + return ret; +} + +static +struct process_attr_tracker *_kernel_get_process_attr_tracker( struct ltt_kernel_session *session, - enum lttng_tracker_type tracker_type) + enum lttng_process_attr process_attr) { - switch (tracker_type) { - case LTTNG_TRACKER_PID: - return session->tracker_list_pid; - case LTTNG_TRACKER_VPID: - return session->tracker_list_vpid; - case LTTNG_TRACKER_UID: - return session->tracker_list_uid; - case LTTNG_TRACKER_VUID: - return session->tracker_list_vuid; - case LTTNG_TRACKER_GID: - return session->tracker_list_gid; - case LTTNG_TRACKER_VGID: - return session->tracker_list_vgid; + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + return session->tracker_pid; + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + return session->tracker_vpid; + case LTTNG_PROCESS_ATTR_USER_ID: + return session->tracker_uid; + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + return session->tracker_vuid; + case LTTNG_PROCESS_ATTR_GROUP_ID: + return session->tracker_gid; + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + return session->tracker_vgid; default: return NULL; } } -int kernel_track_id(enum lttng_tracker_type tracker_type, +const struct process_attr_tracker *kernel_get_process_attr_tracker( struct ltt_kernel_session *session, - const struct lttng_tracker_id *id) + enum lttng_process_attr process_attr) { - int ret, value; - struct lttng_tracker_list *tracker_list; - struct lttng_tracker_ids *saved_ids; + return (const struct process_attr_tracker *) + _kernel_get_process_attr_tracker(session, process_attr); +} - ret = lttng_tracker_id_lookup_string(tracker_type, id, &value); - if (ret != LTTNG_OK) { - return ret; - } +enum lttng_error_code kernel_process_attr_tracker_set_tracking_policy( + struct ltt_kernel_session *session, + enum lttng_process_attr process_attr, + enum lttng_tracking_policy policy) +{ + int ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct process_attr_tracker *tracker = + _kernel_get_process_attr_tracker(session, process_attr); + enum lttng_tracking_policy previous_policy; - tracker_list = get_id_tracker_list(session, tracker_type); - if (!tracker_list) { - return LTTNG_ERR_INVALID; + if (!tracker) { + ret_code = LTTNG_ERR_INVALID; + goto end; } - /* Save list for restore on error. */ - ret = lttng_tracker_id_get_list(tracker_list, &saved_ids); - if (ret != LTTNG_OK) { - return LTTNG_ERR_INVALID; + previous_policy = process_attr_tracker_get_tracking_policy(tracker); + ret = process_attr_tracker_set_tracking_policy(tracker, policy); + if (ret) { + ret_code = LTTNG_ERR_UNK; + goto end; } - /* Add to list. */ - ret = lttng_tracker_list_add(tracker_list, id); - if (ret != LTTNG_OK) { + if (previous_policy == policy) { goto end; } - switch (tracker_type) { - case LTTNG_TRACKER_PID: - DBG("Kernel track PID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_pid(session->fd, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_VPID: - DBG("Kernel track VPID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_id(session->fd, LTTNG_TRACKER_VPID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_UID: - DBG("Kernel track UID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_id(session->fd, LTTNG_TRACKER_UID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_GID: - DBG("Kernel track GID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_id(session->fd, LTTNG_TRACKER_GID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_VUID: - DBG("Kernel track VUID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_id(session->fd, LTTNG_TRACKER_VUID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; + switch (policy) { + case LTTNG_TRACKING_POLICY_INCLUDE_ALL: + if (process_attr == LTTNG_PROCESS_ATTR_PROCESS_ID) { + /* + * Maintain a special case for the process ID process + * attribute tracker as it was the only supported + * attribute prior to 2.12. + */ + ret = kernctl_track_pid(session->fd, -1); + } else { + ret = kernctl_track_id(session->fd, process_attr, -1); } break; - case LTTNG_TRACKER_VGID: - DBG("Kernel track VGID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_track_id(session->fd, LTTNG_TRACKER_VGID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; + case LTTNG_TRACKING_POLICY_EXCLUDE_ALL: + case LTTNG_TRACKING_POLICY_INCLUDE_SET: + /* fall-through. */ + if (process_attr == LTTNG_PROCESS_ATTR_PROCESS_ID) { + /* + * Maintain a special case for the process ID process + * attribute tracker as it was the only supported + * attribute prior to 2.12. + */ + ret = kernctl_untrack_pid(session->fd, -1); + } else { + ret = kernctl_untrack_id(session->fd, process_attr, -1); } break; default: - ret = -EINVAL; - break; + abort(); } - - /* Error handling. */ + /* kern-ctl error handling */ switch (-ret) { + case 0: + ret_code = LTTNG_OK; + break; case EINVAL: - ret = LTTNG_ERR_INVALID; + ret_code = LTTNG_ERR_INVALID; break; case ENOMEM: - ret = LTTNG_ERR_NOMEM; + ret_code = LTTNG_ERR_NOMEM; break; case EEXIST: - ret = LTTNG_ERR_ID_TRACKED; + ret_code = LTTNG_ERR_PROCESS_ATTR_EXISTS; break; default: - ret = LTTNG_ERR_UNK; + ret_code = LTTNG_ERR_UNK; break; } - - if (lttng_tracker_id_set_list(tracker_list, saved_ids) != LTTNG_OK) { - ERR("Error on tracker add error handling.\n"); - } end: - lttng_tracker_ids_destroy(saved_ids); - return ret; + return ret_code; } -int kernel_untrack_id(enum lttng_tracker_type tracker_type, +enum lttng_error_code kernel_process_attr_tracker_inclusion_set_add_value( struct ltt_kernel_session *session, - const struct lttng_tracker_id *id) + enum lttng_process_attr process_attr, + const struct process_attr_value *value) { - int ret, value; - struct lttng_tracker_list *tracker_list; - struct lttng_tracker_ids *saved_ids; - - ret = lttng_tracker_id_lookup_string(tracker_type, id, &value); - if (ret != LTTNG_OK) { - return ret; - } - - tracker_list = get_id_tracker_list(session, tracker_type); - if (!tracker_list) { - return LTTNG_ERR_INVALID; - } - /* Save list for restore on error. */ - ret = lttng_tracker_id_get_list(tracker_list, &saved_ids); - if (ret != LTTNG_OK) { - return LTTNG_ERR_INVALID; - } - /* Remove from list. */ - ret = lttng_tracker_list_remove(tracker_list, id); - if (ret != LTTNG_OK) { - goto end; - } + int ret, integral_value; + enum lttng_error_code ret_code; + struct process_attr_tracker *tracker; + enum process_attr_tracker_status status; - switch (tracker_type) { - case LTTNG_TRACKER_PID: - DBG("Kernel untrack PID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_pid(session->fd, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_VPID: - DBG("Kernel untrack VPID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_id( - session->fd, LTTNG_TRACKER_VPID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_UID: - DBG("Kernel untrack UID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_id(session->fd, LTTNG_TRACKER_UID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } - break; - case LTTNG_TRACKER_GID: - DBG("Kernel untrack GID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_id(session->fd, LTTNG_TRACKER_GID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; - } + /* + * Convert process attribute tracker value to the integral + * representation required by the kern-ctl API. + */ + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + integral_value = (int) value->value.pid; break; - case LTTNG_TRACKER_VUID: - DBG("Kernel untrack VUID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_id( - session->fd, LTTNG_TRACKER_VUID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; + case LTTNG_PROCESS_ATTR_USER_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + if (value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME) { + uid_t uid; + + ret_code = utils_user_id_from_name( + value->value.user_name, &uid); + if (ret_code != LTTNG_OK) { + goto end; + } + integral_value = (int) uid; + } else { + integral_value = (int) value->value.uid; } break; - case LTTNG_TRACKER_VGID: - DBG("Kernel untrack VGID %d for session id %" PRIu64 ".", value, - session->id); - ret = kernctl_untrack_id( - session->fd, LTTNG_TRACKER_VGID, value); - if (!ret) { - ret = LTTNG_OK; - goto end; + case LTTNG_PROCESS_ATTR_GROUP_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + if (value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME) { + gid_t gid; + + ret_code = utils_group_id_from_name( + value->value.group_name, &gid); + if (ret_code != LTTNG_OK) { + goto end; + } + integral_value = (int) gid; + } else { + integral_value = (int) value->value.gid; } break; default: - ret = -EINVAL; - break; + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + tracker = _kernel_get_process_attr_tracker(session, process_attr); + if (!tracker) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + status = process_attr_tracker_inclusion_set_add_value(tracker, value); + if (status != PROCESS_ATTR_TRACKER_STATUS_OK) { + switch (status) { + case PROCESS_ATTR_TRACKER_STATUS_EXISTS: + ret_code = LTTNG_ERR_PROCESS_ATTR_EXISTS; + break; + case PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY: + ret_code = LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY; + break; + case PROCESS_ATTR_TRACKER_STATUS_ERROR: + default: + ret_code = LTTNG_ERR_UNK; + break; + } + goto end; } - /* Error handling. */ + DBG("Kernel track %s %d for session id %" PRIu64, + lttng_process_attr_to_string(process_attr), + integral_value, session->id); + if (process_attr == LTTNG_PROCESS_ATTR_PROCESS_ID) { + /* + * Maintain a special case for the process ID process attribute + * tracker as it was the only supported attribute prior to 2.12. + */ + ret = kernctl_track_pid(session->fd, integral_value); + } else { + ret = kernctl_track_id( + session->fd, process_attr, integral_value); + } + if (ret == 0) { + ret_code = LTTNG_OK; + goto end; + } + + kernel_wait_quiescent(); + + /* kern-ctl error handling */ switch (-ret) { + case 0: + ret_code = LTTNG_OK; + break; case EINVAL: - ret = LTTNG_ERR_INVALID; + ret_code = LTTNG_ERR_INVALID; break; case ENOMEM: - ret = LTTNG_ERR_NOMEM; + ret_code = LTTNG_ERR_NOMEM; break; case EEXIST: - ret = LTTNG_ERR_ID_TRACKED; + ret_code = LTTNG_ERR_PROCESS_ATTR_EXISTS; break; default: - ret = LTTNG_ERR_UNK; + ret_code = LTTNG_ERR_UNK; break; } - if (lttng_tracker_id_set_list(tracker_list, saved_ids) != LTTNG_OK) { - ERR("Error on tracker remove error handling.\n"); + /* Attempt to remove the value from the tracker. */ + status = process_attr_tracker_inclusion_set_remove_value( + tracker, value); + if (status != PROCESS_ATTR_TRACKER_STATUS_OK) { + ERR("Failed to roll-back the tracking of kernel %s process attribute %d while handling a kern-ctl error", + lttng_process_attr_to_string(process_attr), + integral_value); } end: - lttng_tracker_ids_destroy(saved_ids); - return ret; + return ret_code; } -/* - * Called with session lock held. - */ -int kernel_list_tracker_ids(enum lttng_tracker_type tracker_type, +enum lttng_error_code kernel_process_attr_tracker_inclusion_set_remove_value( struct ltt_kernel_session *session, - struct lttng_tracker_ids **_ids) + enum lttng_process_attr process_attr, + const struct process_attr_value *value) { - int ret = 0; - struct lttng_tracker_list *tracker_list; + int ret, integral_value; + enum lttng_error_code ret_code; + struct process_attr_tracker *tracker; + enum process_attr_tracker_status status; - tracker_list = get_id_tracker_list(session, tracker_type); - if (!tracker_list) { - ret = -LTTNG_ERR_INVALID; + /* + * Convert process attribute tracker value to the integral + * representation required by the kern-ctl API. + */ + switch (process_attr) { + case LTTNG_PROCESS_ATTR_PROCESS_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_PROCESS_ID: + integral_value = (int) value->value.pid; + break; + case LTTNG_PROCESS_ATTR_USER_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_USER_ID: + if (value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_USER_NAME) { + uid_t uid; + + ret_code = utils_user_id_from_name( + value->value.user_name, &uid); + if (ret_code != LTTNG_OK) { + goto end; + } + integral_value = (int) uid; + } else { + integral_value = (int) value->value.uid; + } + break; + case LTTNG_PROCESS_ATTR_GROUP_ID: + case LTTNG_PROCESS_ATTR_VIRTUAL_GROUP_ID: + if (value->type == LTTNG_PROCESS_ATTR_VALUE_TYPE_GROUP_NAME) { + gid_t gid; + + ret_code = utils_group_id_from_name( + value->value.group_name, &gid); + if (ret_code != LTTNG_OK) { + goto end; + } + integral_value = (int) gid; + } else { + integral_value = (int) value->value.gid; + } + break; + default: + ret_code = LTTNG_ERR_INVALID; goto end; } - ret = lttng_tracker_id_get_list(tracker_list, _ids); - if (ret != LTTNG_OK) { - ret = -LTTNG_ERR_INVALID; + tracker = _kernel_get_process_attr_tracker(session, process_attr); + if (!tracker) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + status = process_attr_tracker_inclusion_set_remove_value( + tracker, value); + if (status != PROCESS_ATTR_TRACKER_STATUS_OK) { + switch (status) { + case PROCESS_ATTR_TRACKER_STATUS_MISSING: + ret_code = LTTNG_ERR_PROCESS_ATTR_MISSING; + break; + case PROCESS_ATTR_TRACKER_STATUS_INVALID_TRACKING_POLICY: + ret_code = LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY; + break; + case PROCESS_ATTR_TRACKER_STATUS_ERROR: + default: + ret_code = LTTNG_ERR_UNK; + break; + } goto end; } + DBG("Kernel track %s %d for session id %" PRIu64, + lttng_process_attr_to_string(process_attr), + integral_value, session->id); + if (process_attr == LTTNG_PROCESS_ATTR_PROCESS_ID) { + /* + * Maintain a special case for the process ID process attribute + * tracker as it was the only supported attribute prior to 2.12. + */ + ret = kernctl_untrack_pid(session->fd, integral_value); + } else { + ret = kernctl_untrack_id( + session->fd, process_attr, integral_value); + } + if (ret == 0) { + ret_code = LTTNG_OK; + goto end; + } + kernel_wait_quiescent(); + + /* kern-ctl error handling */ + switch (-ret) { + case 0: + ret_code = LTTNG_OK; + break; + case EINVAL: + ret_code = LTTNG_ERR_INVALID; + break; + case ENOMEM: + ret_code = LTTNG_ERR_NOMEM; + break; + case ENOENT: + ret_code = LTTNG_ERR_PROCESS_ATTR_MISSING; + break; + default: + ret_code = LTTNG_ERR_UNK; + break; + } + + /* Attempt to add the value to the tracker. */ + status = process_attr_tracker_inclusion_set_add_value( + tracker, value); + if (status != PROCESS_ATTR_TRACKER_STATUS_OK) { + ERR("Failed to roll-back the tracking of kernel %s process attribute %d while handling a kern-ctl error", + lttng_process_attr_to_string(process_attr), + integral_value); + } end: - return ret; + return ret_code; } /* @@ -1546,16 +1789,10 @@ int kernel_syscall_mask(int chan_fd, char **syscall_mask, uint32_t *nr_bits) return kernctl_syscall_mask(chan_fd, syscall_mask, nr_bits); } -/* - * Check for the support of the RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS via abi - * version number. - * - * Return 1 on success, 0 when feature is not supported, negative value in case - * of errors. - */ -int kernel_supports_ring_buffer_snapshot_sample_positions(void) +static +int kernel_tracer_abi_greater_or_equal(unsigned int major, unsigned int minor) { - int ret = 0; // Not supported by default + int ret; struct lttng_kernel_tracer_abi_version abi; ret = kernctl_tracer_abi_version(kernel_tracer_fd, &abi); @@ -1564,18 +1801,24 @@ int kernel_supports_ring_buffer_snapshot_sample_positions(void) goto error; } + ret = abi.major > major || (abi.major == major && abi.minor >= minor); +error: + return ret; +} + +/* + * Check for the support of the RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS via abi + * version number. + * + * Return 1 on success, 0 when feature is not supported, negative value in case + * of errors. + */ +int kernel_supports_ring_buffer_snapshot_sample_positions(void) +{ /* * RING_BUFFER_SNAPSHOT_SAMPLE_POSITIONS was introduced in 2.3 */ - if (abi.major >= 2 && abi.minor >= 3) { - /* Supported */ - ret = 1; - } else { - /* Not supported */ - ret = 0; - } -error: - return ret; + return kernel_tracer_abi_greater_or_equal(2, 3); } /* @@ -1586,28 +1829,25 @@ error: */ int kernel_supports_ring_buffer_packet_sequence_number(void) { - int ret = 0; // Not supported by default - struct lttng_kernel_tracer_abi_version abi; - - ret = kernctl_tracer_abi_version(kernel_tracer_fd, &abi); - if (ret < 0) { - ERR("Failed to retrieve lttng-modules ABI version"); - goto error; - } - /* * Packet sequence number was introduced in LTTng 2.8, * lttng-modules ABI 2.1. */ - if (abi.major >= 2 && abi.minor >= 1) { - /* Supported */ - ret = 1; - } else { - /* Not supported */ - ret = 0; - } -error: - return ret; + return kernel_tracer_abi_greater_or_equal(2, 1); +} + +/* + * Check for the support of event notifiers via abi version number. + * + * Return 1 on success, 0 when feature is not supported, negative value in case + * of errors. + */ +int kernel_supports_event_notifiers(void) +{ + /* + * Event notifiers were introduced in LTTng 2.13, lttng-modules ABI 2.6. + */ + return kernel_tracer_abi_greater_or_equal(2, 6); } /* @@ -1639,7 +1879,7 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) socket, node.node) { struct ltt_kernel_channel *chan; - /* For each channel, ask the consumer to rotate it. */ + /* For each channel, ask the consumer to rotate it. */ cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { DBG("Rotate kernel channel %" PRIu64 ", session %s", chan->key, session->name); @@ -1647,7 +1887,7 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) ksess->uid, ksess->gid, ksess->consumer, /* is_metadata_channel */ false); if (ret < 0) { - status = LTTNG_ERR_KERN_CONSUMER_FAIL; + status = LTTNG_ERR_ROTATION_FAIL_CONSUMER; goto error; } } @@ -1659,7 +1899,7 @@ enum lttng_error_code kernel_rotate_session(struct ltt_session *session) ksess->uid, ksess->gid, ksess->consumer, /* is_metadata_channel */ true); if (ret < 0) { - status = LTTNG_ERR_KERN_CONSUMER_FAIL; + status = LTTNG_ERR_ROTATION_FAIL_CONSUMER; goto error; } } @@ -1732,35 +1972,81 @@ int init_kernel_tracer(void) if (ret < 0) { goto error_modules; } - if (ret < 1) { WARN("Kernel tracer does not support buffer monitoring. " "The monitoring timer of channels in the kernel domain " "will be set to 0 (disabled)."); } - DBG("Kernel tracer fd %d", kernel_tracer_fd); + ret = kernel_supports_event_notifiers(); + if (ret < 0) { + ERR("Failed to check for kernel tracer event notifier support"); + goto error_modules; + } + ret = kernel_create_event_notifier_group(&kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + /* This is not fatal. */ + WARN("Failed to create kernel event notifier group"); + kernel_tracer_event_notifier_group_fd = -1; + } else { + const enum lttng_error_code error_code_ret = + kernel_create_event_notifier_group_notification_fd( + &kernel_tracer_event_notifier_group_notification_fd); + + if (error_code_ret != LTTNG_OK) { + goto error_modules; + } + + kernel_token_to_event_notifier_rule_ht = cds_lfht_new( + DEFAULT_HT_SIZE, 1, 0, + CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, + NULL); + if (!kernel_token_to_event_notifier_rule_ht) { + goto error_token_ht; + } + } + + DBG("Kernel tracer initialized: kernel tracer fd = %d, event notifier group fd = %d, event notifier group notification fd = %d", + kernel_tracer_fd, kernel_tracer_event_notifier_group_fd, + kernel_tracer_event_notifier_group_notification_fd); ret = syscall_init_table(kernel_tracer_fd); if (ret < 0) { ERR("Unable to populate syscall table. Syscall tracing won't " "work for this session daemon."); } + return 0; error_version: modprobe_remove_lttng_control(); ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } kernel_tracer_fd = -1; return LTTNG_ERR_KERN_VERSION; + +error_token_ht: + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + error_modules: + ret = close(kernel_tracer_event_notifier_group_fd); + if (ret) { + PERROR("Failed to close kernel tracer event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + ret = close(kernel_tracer_fd); if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } error_open: @@ -1779,16 +2065,54 @@ error: LTTNG_HIDDEN void cleanup_kernel_tracer(void) { - int ret; + DBG2("Closing kernel event notifier group notification file descriptor"); + if (kernel_tracer_event_notifier_group_notification_fd >= 0) { + int ret = notification_thread_command_remove_tracer_event_source( + notification_thread_handle, + kernel_tracer_event_notifier_group_notification_fd); + if (ret != LTTNG_OK) { + ERR("Failed to remove kernel event notifier notification from notification thread"); + } + + ret = close(kernel_tracer_event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + kernel_tracer_event_notifier_group_notification_fd); + } + + kernel_tracer_event_notifier_group_notification_fd = -1; + } + + if (kernel_token_to_event_notifier_rule_ht) { + const int ret = cds_lfht_destroy( + kernel_token_to_event_notifier_rule_ht, NULL); + assert(ret == 0); + } + + DBG2("Closing kernel event notifier group file descriptor"); + if (kernel_tracer_event_notifier_group_fd >= 0) { + const int ret = close(kernel_tracer_event_notifier_group_fd); + + if (ret) { + PERROR("Failed to close kernel event notifier group file descriptor: fd = %d", + kernel_tracer_event_notifier_group_fd); + } + + kernel_tracer_event_notifier_group_fd = -1; + } DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { - ret = close(kernel_tracer_fd); + const int ret = close(kernel_tracer_fd); + if (ret) { - PERROR("close"); + PERROR("Failed to close kernel tracer file descriptor: fd = %d", + kernel_tracer_fd); } + kernel_tracer_fd = -1; } + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1879,3 +2203,337 @@ end: rcu_read_unlock(); return status; } + +enum lttng_error_code kernel_create_event_notifier_group_notification_fd( + int *event_notifier_group_notification_fd) +{ + int local_fd = -1, ret; + enum lttng_error_code error_code_ret; + + assert(event_notifier_group_notification_fd); + + ret = kernctl_create_event_notifier_group_notification_fd( + kernel_tracer_event_notifier_group_fd); + if (ret < 0) { + PERROR("Failed to create kernel event notifier group notification file descriptor"); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + local_fd = ret; + + /* Prevent fd duplication after execlp(). */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD; + goto error; + } + + DBG("Created kernel notifier group notification file descriptor: fd = %d", + local_fd); + error_code_ret = LTTNG_OK; + *event_notifier_group_notification_fd = local_fd; + local_fd = -1; + +error: + if (local_fd >= 0) { + ret = close(local_fd); + if (ret) { + PERROR("Failed to close kernel event notifier group notification file descriptor: fd = %d", + local_fd); + } + } + + return error_code_ret; +} + +enum lttng_error_code kernel_destroy_event_notifier_group_notification_fd( + int event_notifier_group_notification_fd) +{ + enum lttng_error_code ret_code = LTTNG_OK; + + DBG("Closing event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + if (event_notifier_group_notification_fd >= 0) { + const int ret = close(event_notifier_group_notification_fd); + if (ret) { + PERROR("Failed to close event notifier group notification file descriptor: fd = %d", + event_notifier_group_notification_fd); + } + } + + return ret_code; +} + +static +unsigned long hash_trigger(const struct lttng_trigger *trigger) +{ + const struct lttng_condition *condition = + lttng_trigger_get_const_condition(trigger); + + return lttng_condition_hash(condition); +} + +static +int match_trigger(struct cds_lfht_node *node, const void *key) +{ + const struct ltt_kernel_event_notifier_rule *event_notifier_rule; + const struct lttng_trigger *trigger = key; + + event_notifier_rule = caa_container_of(node, + const struct ltt_kernel_event_notifier_rule, ht_node); + + return lttng_trigger_is_equal(trigger, event_notifier_rule->trigger); +} + +static enum lttng_error_code kernel_create_event_notifier_rule( + struct lttng_trigger *trigger, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd, ret = 0; + enum lttng_error_code error_code_ret; + enum lttng_condition_status condition_status; + enum lttng_condition_type condition_type; + enum lttng_event_rule_type event_rule_type; + struct ltt_kernel_event_notifier_rule *event_notifier_rule; + struct lttng_kernel_event_notifier kernel_event_notifier = {}; + unsigned int capture_bytecode_count = 0, i; + const struct lttng_condition *condition = NULL; + const struct lttng_event_rule *event_rule = NULL; + enum lttng_condition_status cond_status; + + assert(trigger); + + condition = lttng_trigger_get_const_condition(trigger); + assert(condition); + + condition_type = lttng_condition_get_type(condition); + assert(condition_type == LTTNG_CONDITION_TYPE_ON_EVENT); + + /* Does not acquire a reference. */ + condition_status = lttng_condition_on_event_get_rule( + condition, &event_rule); + assert(condition_status == LTTNG_CONDITION_STATUS_OK); + assert(event_rule); + + event_rule_type = lttng_event_rule_get_type(event_rule); + assert(event_rule_type != LTTNG_EVENT_RULE_TYPE_UNKNOWN); + + error_code_ret = trace_kernel_create_event_notifier_rule(trigger, token, + &event_notifier_rule); + if (error_code_ret != LTTNG_OK) { + goto error; + } + + error_code_ret = trace_kernel_init_event_notifier_from_event_rule( + event_rule, &kernel_event_notifier); + if (error_code_ret != LTTNG_OK) { + goto free_event; + } + + kernel_event_notifier.event.token = event_notifier_rule->token; + + fd = kernctl_create_event_notifier( + kernel_tracer_event_notifier_group_fd, + &kernel_event_notifier); + if (fd < 0) { + switch (-fd) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Failed to create kernel event notifier: not notifier type not implemented"); + error_code_ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Failed to create kernel event notifier: not found: name = '%s'", + kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + PERROR("Failed to create kernel event notifier: error code = %d, name = '%s'", + fd, kernel_event_notifier.event.name); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + } + goto free_event; + } + + event_notifier_rule->fd = fd; + /* Prevent fd duplication after execlp(). */ + err = fcntl(event_notifier_rule->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("Failed to set FD_CLOEXEC on kernel event notifier file descriptor: fd = %d", + fd); + error_code_ret = LTTNG_ERR_FATAL; + goto set_cloexec_error; + } + + if (event_notifier_rule->filter) { + err = kernctl_filter(event_notifier_rule->fd, event_notifier_rule->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + error_code_ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + error_code_ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event_rule) == + LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE) { + ret = userspace_probe_event_rule_add_callsites( + event_rule, creds, event_notifier_rule->fd); + if (ret) { + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto add_callsite_error; + } + } + + /* Set the capture bytecode if any. */ + cond_status = lttng_condition_on_event_get_capture_descriptor_count( + condition, &capture_bytecode_count); + assert(cond_status == LTTNG_CONDITION_STATUS_OK); + + for (i = 0; i < capture_bytecode_count; i++) { + const struct lttng_bytecode *capture_bytecode = + lttng_condition_on_event_get_capture_bytecode_at_index( + condition, i); + + if (capture_bytecode == NULL) { + ERR("Unexpected NULL capture bytecode on condition"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto capture_error; + } + + ret = kernctl_capture(event_notifier_rule->fd, capture_bytecode); + if (ret < 0) { + ERR("Failed to set capture bytecode on event notifier rule fd: fd = %d", + event_notifier_rule->fd); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + goto capture_error; + } + } + + err = kernctl_enable(event_notifier_rule->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + error_code_ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event notifier"); + error_code_ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add trigger to kernel token mapping in the hash table. */ + rcu_read_lock(); + cds_lfht_add(kernel_token_to_event_notifier_rule_ht, hash_trigger(trigger), + &event_notifier_rule->ht_node); + rcu_read_unlock(); + + DBG("Created kernel event notifier: name = '%s', fd = %d", + kernel_event_notifier.event.name, + event_notifier_rule->fd); + + return LTTNG_OK; + +capture_error: +add_callsite_error: +enable_error: +set_cloexec_error: +filter_error: + { + const int close_ret = close(event_notifier_rule->fd); + + if (close_ret) { + PERROR("Failed to close kernel event notifier file descriptor: fd = %d", + event_notifier_rule->fd); + } + } +free_event: + free(event_notifier_rule); +error: + return error_code_ret; +} + +enum lttng_error_code kernel_register_event_notifier( + struct lttng_trigger *trigger, + const struct lttng_credentials *cmd_creds) +{ + enum lttng_error_code ret; + enum lttng_condition_status status; + enum lttng_domain_type domain_type; + const struct lttng_event_rule *event_rule; + const struct lttng_condition *const condition = + lttng_trigger_get_const_condition(trigger); + const uint64_t token = lttng_trigger_get_tracer_token(trigger); + + assert(condition); + + /* Does not acquire a reference to the event rule. */ + status = lttng_condition_on_event_get_rule( + condition, &event_rule); + assert(status == LTTNG_CONDITION_STATUS_OK); + + domain_type = lttng_event_rule_get_domain_type(event_rule); + assert(domain_type == LTTNG_DOMAIN_KERNEL); + + ret = kernel_create_event_notifier_rule(trigger, cmd_creds, token); + if (ret != LTTNG_OK) { + ERR("Failed to create kernel event notifier rule"); + } + + return ret; +} + +enum lttng_error_code kernel_unregister_event_notifier( + const struct lttng_trigger *trigger) +{ + struct ltt_kernel_event_notifier_rule *token_event_rule_element; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + enum lttng_error_code error_code_ret; + int ret; + + rcu_read_lock(); + + cds_lfht_lookup(kernel_token_to_event_notifier_rule_ht, + hash_trigger(trigger), match_trigger, trigger, &iter); + + node = cds_lfht_iter_get_node(&iter); + if (!node) { + error_code_ret = LTTNG_ERR_TRIGGER_NOT_FOUND; + goto error; + } + + token_event_rule_element = caa_container_of(node, + struct ltt_kernel_event_notifier_rule, ht_node); + + ret = kernel_disable_event_notifier_rule(token_event_rule_element); + if (ret) { + error_code_ret = LTTNG_ERR_FATAL; + goto error; + } + + trace_kernel_destroy_event_notifier_rule(token_event_rule_element); + error_code_ret = LTTNG_OK; + +error: + rcu_read_unlock(); + + return error_code_ret; +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_event_notifier_group_notification_fd; +}