X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fkernel.c;h=646da3ba83affd3463a30dd7e2efb7a43b05535c;hb=refs%2Fheads%2Fsow-2019-0002-rev2;hp=93afc9d6fb25e795aab694bc77b9ace813c5aacb;hpb=5da88b0f58d7f838068037ea449ddfb25d3e85ad;p=lttng-tools.git diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index 93afc9d6f..646da3ba8 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -1,18 +1,8 @@ /* - * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 David Goulet * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2 only, - * as published by the Free Software Foundation. + * SPDX-License-Identifier: GPL-2.0-only * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _LGPL_SOURCE @@ -25,11 +15,20 @@ #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + #include "lttng-sessiond.h" #include "lttng-syscall.h" #include "consumer.h" @@ -39,6 +38,7 @@ #include "utils.h" #include "rotate.h" #include "modprobe.h" +#include "notification-thread-commands.h" /* * Key used to reference a channel between the sessiond and the consumer. This @@ -49,9 +49,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_trigger_group_fd = -1; +static int kernel_tracer_trigger_group_notification_fd = -1; +static struct ltt_kernel_token_event_rule_list kernel_tracer_token_list; -#include -#include /* * Add context on a kernel channel. * @@ -223,6 +224,44 @@ error: return -1; } +/* + * Create a kernel channel, register it to the kernel tracer and add it to the + * kernel session. + */ +static +int kernel_create_trigger_group(int *trigger_group_fd) +{ + int ret; + int local_fd = -1; + + assert(trigger_group_fd); + + /* Kernel tracer channel creation */ + ret = kernctl_create_trigger_group(kernel_tracer_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_fd = 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. @@ -234,7 +273,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; @@ -271,8 +310,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); @@ -296,7 +334,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; @@ -342,7 +380,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); @@ -363,29 +401,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; @@ -398,7 +423,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; @@ -407,8 +433,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("Adding callsite to ELF userspace probe failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; goto end; } @@ -425,8 +450,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; @@ -435,8 +460,8 @@ 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("Adding callsite to SDT userspace probe " + "failed."); ret = LTTNG_ERR_KERN_ENABLE_FAIL; free(offsets); goto end; @@ -453,6 +478,71 @@ 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) +{ + 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; + } + + ret = userspace_probe_add_callsite(location, session->uid, session->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe event \"%s\" " + "failed.", ev->name); + } + +end: + return ret; +} + +/* + * Extract the offsets of the instrumentation point for the different lookup + * methods. + */ +static int userspace_probe_event_rule_add_callsites( + const struct lttng_event_rule *rule, + const struct lttng_credentials *creds, + int fd) +{ + const struct lttng_userspace_probe_location *location = NULL; + enum lttng_event_rule_status status; + int ret; + + assert(rule); + assert(creds); + assert(lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_UPROBE); + + status = lttng_event_rule_uprobe_get_location(rule, &location); + if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) { + ret = -1; + goto end; + } + + ret = userspace_probe_add_callsite(location, creds->uid, creds->gid, + fd); + if (ret) { + WARN("Adding callsite to userspace probe object %d" + "failed.", 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. @@ -522,7 +612,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; } @@ -679,120 +770,311 @@ error: return ret; } - -int kernel_track_pid(struct ltt_kernel_session *session, int pid) +/* + * Disable a kernel trigger. + */ +int kernel_disable_token_event_rule(struct ltt_kernel_token_event_rule *event) { int ret; - DBG("Kernel track PID %d for session id %" PRIu64 ".", - pid, session->id); - ret = kernctl_track_pid(session->fd, pid); - if (!ret) { - return LTTNG_OK; + assert(event); + + ret = kernctl_disable(event->fd); + if (ret < 0) { + switch (-ret) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("disable kernel event"); + break; + } + goto error; } - switch (-ret) { - case EINVAL: - return LTTNG_ERR_INVALID; - case ENOMEM: - return LTTNG_ERR_NOMEM; - case EEXIST: - return LTTNG_ERR_PID_TRACKED; + + event->enabled = 0; + DBG("Kernel trigger token %" PRIu64" disabled (fd: %d)", event->token, event->fd); + + return 0; + +error: + return ret; +} +static struct lttng_tracker_list *get_id_tracker_list( + struct ltt_kernel_session *session, + enum lttng_tracker_type tracker_type) +{ + 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; default: - return LTTNG_ERR_UNK; + return NULL; } } -int kernel_untrack_pid(struct ltt_kernel_session *session, int pid) +int kernel_track_id(enum lttng_tracker_type tracker_type, + struct ltt_kernel_session *session, + const struct lttng_tracker_id *id) { - int ret; + 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; + } + + /* Add to list. */ + ret = lttng_tracker_list_add(tracker_list, id); + if (ret != LTTNG_OK) { + goto end; + } - DBG("Kernel untrack PID %d for session id %" PRIu64 ".", - pid, session->id); - ret = kernctl_untrack_pid(session->fd, pid); - if (!ret) { - return LTTNG_OK; + 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; + } + 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; + } + break; + default: + ret = -EINVAL; + break; } + + /* Error handling. */ switch (-ret) { case EINVAL: - return LTTNG_ERR_INVALID; + ret = LTTNG_ERR_INVALID; + break; case ENOMEM: - return LTTNG_ERR_NOMEM; - case ENOENT: - return LTTNG_ERR_PID_NOT_TRACKED; + ret = LTTNG_ERR_NOMEM; + break; + case EEXIST: + ret = LTTNG_ERR_ID_TRACKED; + break; default: - return LTTNG_ERR_UNK; + ret = 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; } -ssize_t kernel_list_tracker_pids(struct ltt_kernel_session *session, - int **_pids) +int kernel_untrack_id(enum lttng_tracker_type tracker_type, + struct ltt_kernel_session *session, + const struct lttng_tracker_id *id) { - int fd, ret; - int pid; - ssize_t nbmem, count = 0; - FILE *fp; - int *pids; + int ret, value; + struct lttng_tracker_list *tracker_list; + struct lttng_tracker_ids *saved_ids; - fd = kernctl_list_tracker_pids(session->fd); - if (fd < 0) { - PERROR("kernel tracker pids list"); - goto error; + ret = lttng_tracker_id_lookup_string(tracker_type, id, &value); + if (ret != LTTNG_OK) { + return ret; } - fp = fdopen(fd, "r"); - if (fp == NULL) { - PERROR("kernel tracker pids list fdopen"); - goto error_fp; + tracker_list = get_id_tracker_list(session, tracker_type); + if (!tracker_list) { + return LTTNG_ERR_INVALID; } - - nbmem = KERNEL_TRACKER_PIDS_INIT_LIST_SIZE; - pids = zmalloc(sizeof(*pids) * nbmem); - if (pids == NULL) { - PERROR("alloc list pids"); - count = -ENOMEM; + /* 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; } - while (fscanf(fp, "process { pid = %u; };\n", &pid) == 1) { - if (count >= nbmem) { - int *new_pids; - size_t new_nbmem; - - new_nbmem = nbmem << 1; - DBG("Reallocating pids list from %zu to %zu entries", - nbmem, new_nbmem); - new_pids = realloc(pids, new_nbmem * sizeof(*new_pids)); - if (new_pids == NULL) { - PERROR("realloc list events"); - free(pids); - count = -ENOMEM; - goto end; - } - /* Zero the new memory */ - memset(new_pids + nbmem, 0, - (new_nbmem - nbmem) * sizeof(*new_pids)); - nbmem = new_nbmem; - pids = new_pids; + 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; } - pids[count++] = pid; + 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; + } + 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; + } + 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; + } + break; + default: + ret = -EINVAL; + break; } - *_pids = pids; - DBG("Kernel list tracker pids done (%zd pids)", count); + /* Error handling. */ + switch (-ret) { + case EINVAL: + ret = LTTNG_ERR_INVALID; + break; + case ENOMEM: + ret = LTTNG_ERR_NOMEM; + break; + case EEXIST: + ret = LTTNG_ERR_ID_TRACKED; + break; + default: + ret = LTTNG_ERR_UNK; + break; + } + + if (lttng_tracker_id_set_list(tracker_list, saved_ids) != LTTNG_OK) { + ERR("Error on tracker remove error handling.\n"); + } end: - ret = fclose(fp); /* closes both fp and fd */ - if (ret) { - PERROR("fclose"); + lttng_tracker_ids_destroy(saved_ids); + return ret; +} + +/* + * Called with session lock held. + */ +int kernel_list_tracker_ids(enum lttng_tracker_type tracker_type, + struct ltt_kernel_session *session, + struct lttng_tracker_ids **_ids) +{ + int ret = 0; + struct lttng_tracker_list *tracker_list; + + tracker_list = get_id_tracker_list(session, tracker_type); + if (!tracker_list) { + ret = -LTTNG_ERR_INVALID; + goto end; } - return count; -error_fp: - ret = close(fd); - if (ret) { - PERROR("close"); + ret = lttng_tracker_id_get_list(tracker_list, _ids); + if (ret != LTTNG_OK) { + ret = -LTTNG_ERR_INVALID; + goto end; } -error: - return -1; + +end: + return ret; } /* @@ -1445,9 +1727,10 @@ int kernel_supports_ring_buffer_packet_sequence_number(void) } /* - * Packet sequence number was introduced in 2.8 + * Packet sequence number was introduced in LTTng 2.8, + * lttng-modules ABI 2.1. */ - if (abi.major >= 2 && abi.minor >= 8) { + if (abi.major >= 2 && abi.minor >= 1) { /* Supported */ ret = 1; } else { @@ -1580,20 +1863,37 @@ 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)."); } + ret = kernel_create_trigger_group(&kernel_tracer_trigger_group_fd); + if (ret < 0) { + /* TODO: error handling if it is not supported etc. */ + WARN("Failed trigger group creation"); + kernel_tracer_trigger_group_fd = -1; + /* This is not fatal */ + } else { + ret = kernel_create_trigger_group_notification_fd(&kernel_tracer_trigger_group_notification_fd); + if (ret < 0) { + goto error_modules; + } + } + + CDS_INIT_LIST_HEAD(&kernel_tracer_token_list.head); + DBG("Kernel tracer fd %d", kernel_tracer_fd); + DBG("Kernel tracer trigger group fd %d", kernel_tracer_trigger_group_fd); + DBG("Kernel tracer trigger group notificationi fd %d", kernel_tracer_trigger_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: @@ -1629,6 +1929,31 @@ void cleanup_kernel_tracer(void) { int ret; + struct ltt_kernel_token_event_rule *rule, *rtmp; + cds_list_for_each_entry_safe(rule, rtmp, &kernel_tracer_token_list.head, list) { + kernel_disable_token_event_rule(rule); + trace_kernel_destroy_token_event_rule(rule); + } + + DBG2("Closing kernel trigger group notification fd"); + if (kernel_tracer_trigger_group_notification_fd >= 0) { + ret = close(kernel_tracer_trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_notification_fd = -1; + } + + /* TODO: do we iterate over the list to remove all token? */ + DBG2("Closing kernel trigger group fd"); + if (kernel_tracer_trigger_group_fd >= 0) { + ret = close(kernel_tracer_trigger_group_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_trigger_group_fd = -1; + } + DBG2("Closing kernel fd"); if (kernel_tracer_fd >= 0) { ret = close(kernel_tracer_fd); @@ -1637,6 +1962,8 @@ void cleanup_kernel_tracer(void) } kernel_tracer_fd = -1; } + + DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); free(syscall_table); @@ -1647,3 +1974,354 @@ bool kernel_tracer_is_initialized(void) { return kernel_tracer_fd >= 0; } + +/* + * Clear a kernel session. + * + * Return LTTNG_OK on success or else an LTTng error code. + */ +enum lttng_error_code kernel_clear_session(struct ltt_session *session) +{ + int ret; + enum lttng_error_code status = LTTNG_OK; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + struct ltt_kernel_session *ksess = session->kernel_session; + + assert(ksess); + assert(ksess->consumer); + + DBG("Clear kernel session %s (session %" PRIu64 ")", + session->name, session->id); + + rcu_read_lock(); + + if (ksess->active) { + ERR("Expecting inactive session %s (%" PRIu64 ")", session->name, session->id); + status = LTTNG_ERR_FATAL; + goto end; + } + + /* + * Note that this loop will end after one iteration given that there is + * only one kernel consumer. + */ + cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, + socket, node.node) { + struct ltt_kernel_channel *chan; + + /* For each channel, ask the consumer to clear it. */ + cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { + DBG("Clear kernel channel %" PRIu64 ", session %s", + chan->key, session->name); + ret = consumer_clear_channel(socket, chan->key); + if (ret < 0) { + goto error; + } + } + + if (!ksess->metadata) { + /* + * Nothing to do for the metadata. + * This is a snapshot session. + * The metadata is genererated on the fly. + */ + continue; + } + + /* + * Clear the metadata channel. + * Metadata channel is not cleared per se but we still need to + * perform a rotation operation on it behind the scene. + */ + ret = consumer_clear_channel(socket, ksess->metadata->key); + if (ret < 0) { + goto error; + } + } + + goto end; +error: + switch (-ret) { + case LTTCOMM_CONSUMERD_RELAYD_CLEAR_DISALLOWED: + status = LTTNG_ERR_CLEAR_RELAY_DISALLOWED; + break; + default: + status = LTTNG_ERR_CLEAR_FAIL_CONSUMER; + break; + } +end: + rcu_read_unlock(); + return status; +} + +enum lttng_error_code kernel_create_trigger_group_notification_fd( + int *trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + int local_fd = -1; + + assert(trigger_group_notification_fd); + + ret = kernctl_create_trigger_group_notification_fd(kernel_tracer_trigger_group_fd); + if (ret < 0) { + PERROR("ioctl kernel create trigger group"); + ret = -1; + goto error; + } + + /* Store locally */ + local_fd = ret; + + /* Prevent fd duplication after execlp() */ + ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl session fd"); + } + + DBG("Kernel trigger group notification created (fd: %d)", + local_fd); + ret = 0; + +error: + *trigger_group_notification_fd = local_fd; + return ret; +} + +enum lttng_error_code kernel_destroy_trigger_group_notification_fd( + int trigger_group_notification_fd) +{ + enum lttng_error_code ret = LTTNG_OK; + DBG("Closing trigger group notification fd %d", trigger_group_notification_fd); + if (trigger_group_notification_fd >= 0) { + ret = close(trigger_group_notification_fd); + if (ret) { + PERROR("close"); + } + } + return ret; +} + +static int kernel_create_token_event_rule(struct lttng_event_rule *rule, + const struct lttng_credentials *creds, uint64_t token) +{ + int err, fd; + enum lttng_error_code ret; + struct ltt_kernel_token_event_rule *event; + struct lttng_kernel_trigger trigger; + + assert(rule); + + ret = trace_kernel_create_token_event_rule(rule, token, &event); + if (ret != LTTNG_OK) { + goto error; + } + + trace_kernel_init_trigger_from_event_rule(event->event_rule, &trigger); + trigger.id = event->token; + + fd = kernctl_create_trigger(kernel_tracer_trigger_group_fd, &trigger); + if (fd < 0) { + switch (-fd) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + case ENOSYS: + WARN("Trigger type not implemented"); + ret = LTTNG_ERR_KERN_EVENT_ENOSYS; + break; + case ENOENT: + WARN("Event %s not found!", trigger.name); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + default: + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + PERROR("create trigger ioctl"); + } + goto free_event; + } + + event->fd = fd; + /* Prevent fd duplication after execlp() */ + err = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (err < 0) { + PERROR("fcntl session fd"); + } + + if (event->filter) { + err = kernctl_filter(event->fd, event->filter); + if (err < 0) { + switch (-err) { + case ENOMEM: + ret = LTTNG_ERR_FILTER_NOMEM; + break; + default: + ret = LTTNG_ERR_FILTER_INVAL; + break; + } + goto filter_error; + } + } + + if (lttng_event_rule_get_type(event->event_rule) == + LTTNG_EVENT_RULE_TYPE_UPROBE) { + ret = userspace_probe_event_rule_add_callsites( + rule, creds, event->fd); + if (ret) { + goto add_callsite_error; + } + } + + err = kernctl_enable(event->fd); + if (err < 0) { + switch (-err) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel trigger"); + ret = LTTNG_ERR_KERN_ENABLE_FAIL; + break; + } + goto enable_error; + } + + /* Add event to event list */ + cds_list_add(&event->list, &kernel_tracer_token_list.head); + + DBG("Trigger %s created (fd: %d)", trigger.name, event->fd); + + return 0; + +add_callsite_error: +enable_error: +filter_error: + { + int closeret; + + closeret = close(event->fd); + if (closeret) { + PERROR("close event fd"); + } + } +free_event: + free(event); +error: + return ret; +} + +enum lttng_error_code kernel_update_tokens(void) +{ + enum lttng_error_code ret = LTTNG_OK; + enum lttng_trigger_status t_status; + struct ltt_kernel_token_event_rule *token_event_rule_element; + struct lttng_triggers *triggers; + unsigned int count; + + /* TODO error handling */ + + /* Get list of token trigger from the notification thread here */ + rcu_read_lock(); + pthread_mutex_lock(¬ification_trigger_tokens_ht_lock); + ret = notification_thread_command_get_tokens(notification_thread_handle, &triggers); + if (ret != 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; + goto end; + } + + for (unsigned int i = 0; i < count; i++) { + struct lttng_condition *condition; + struct lttng_event_rule *event_rule; + struct lttng_trigger *trigger; + struct ltt_kernel_token_event_rule *k_token; + const struct lttng_credentials *creds; + uint64_t token; + + trigger = lttng_triggers_get_pointer_of_index(triggers, i); + assert(trigger); + + /* TODO: error checking and type checking */ + token = lttng_trigger_get_key(trigger); + condition = lttng_trigger_get_condition(trigger); + (void) lttng_condition_event_rule_get_rule_no_const(condition, &event_rule); + + if (lttng_event_rule_get_domain_type(event_rule) != LTTNG_DOMAIN_KERNEL) { + /* Skip ust related trigger */ + continue; + } + + creds = lttng_trigger_get_credentials(trigger); + /* Iterate over all known token trigger */ + k_token = trace_kernel_find_trigger_by_token(&kernel_tracer_token_list, token); + if (!k_token) { + ret = kernel_create_token_event_rule(event_rule, creds, token); + if (ret < 0) { + goto end; + } + } + } + + /* Remove all unknown trigger from the app + * TODO find a way better way then this, do it on the unregister command + * and be specific on the token to remove instead of going over all + * trigger known to the app. This is sub optimal. + */ + cds_list_for_each_entry (token_event_rule_element, &kernel_tracer_token_list.head, + list) { + uint64_t token; + bool found = false; + + token = token_event_rule_element->token; + + /* + * Check if the app event trigger still exists on the + * notification side. + * TODO: might want to change the backing data struct of the + * lttng_triggers object to allow quick lookup? + * For kernel mostly all of this can be removed once we delete + * on a per trigger basis. + */ + + for (unsigned int i = 0; i < count; i++) { + struct lttng_trigger *trigger; + uint64_t inner_token; + + trigger = lttng_triggers_get_pointer_of_index( + triggers, i); + assert(trigger); + + inner_token = lttng_trigger_get_key(trigger); + + if (inner_token == token) { + found = true; + break; + } + } + + if (found) { + /* Still valid */ + continue; + } + + kernel_disable_token_event_rule(token_event_rule_element); + trace_kernel_destroy_token_event_rule(token_event_rule_element); + } +end: + rcu_read_unlock(); + pthread_mutex_unlock(¬ification_trigger_tokens_ht_lock); + return ret; + +} + +int kernel_get_notification_fd(void) +{ + return kernel_tracer_trigger_group_notification_fd; +}