+ switch (event->type) {
+ case LTTNG_EVENT_ALL:
+ {
+ char *filter_expression_a = NULL;
+ struct lttng_filter_bytecode *filter_a = NULL;
+
+ /*
+ * We need to duplicate filter_expression and filter,
+ * because ownership is passed to first enable
+ * event.
+ */
+ if (filter_expression) {
+ filter_expression_a = strdup(filter_expression);
+ if (!filter_expression_a) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ }
+ if (filter) {
+ filter_a = zmalloc(sizeof(*filter_a) + filter->len);
+ if (!filter_a) {
+ free(filter_expression_a);
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ memcpy(filter_a, filter, sizeof(*filter_a) + filter->len);
+ }
+ event->type = LTTNG_EVENT_TRACEPOINT; /* Hack */
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ if (channel_created) {
+ /* Let's not leak a useless channel. */
+ kernel_destroy_channel(kchan);
+ }
+ free(filter_expression_a);
+ free(filter_a);
+ goto error;
+ }
+ event->type = LTTNG_EVENT_SYSCALL; /* Hack */
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression_a, filter_a);
+ /* We have passed ownership */
+ filter_expression_a = NULL;
+ filter_a = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_EVENT_PROBE:
+ case LTTNG_EVENT_FUNCTION:
+ case LTTNG_EVENT_FUNCTION_ENTRY:
+ case LTTNG_EVENT_TRACEPOINT:
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ if (channel_created) {
+ /* Let's not leak a useless channel. */
+ kernel_destroy_channel(kchan);
+ }
+ goto error;
+ }
+ break;
+ case LTTNG_EVENT_SYSCALL:
+ ret = event_kernel_enable_event(kchan, event,
+ filter_expression, filter);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ default:
+ ret = LTTNG_ERR_UNK;
+ goto error;
+ }
+
+ kernel_wait_quiescent(kernel_tracer_fd);
+ break;
+ }
+ case LTTNG_DOMAIN_UST:
+ {
+ struct ltt_ust_channel *uchan;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ /*
+ * If a non-default channel has been created in the
+ * session, explicitely require that -c chan_name needs
+ * to be provided.
+ */
+ if (usess->has_non_default_channel && channel_name[0] == '\0') {
+ ret = LTTNG_ERR_NEED_CHANNEL_NAME;
+ goto error;
+ }
+
+ /* Get channel from global UST domain */
+ uchan = trace_ust_find_channel_by_name(usess->domain_global.channels,
+ channel_name);
+ if (uchan == NULL) {
+ /* Create default channel */
+ attr = channel_new_default_attr(LTTNG_DOMAIN_UST,
+ usess->buffer_type);
+ if (attr == NULL) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ if (lttng_strncpy(attr->name, channel_name,
+ sizeof(attr->name))) {
+ ret = LTTNG_ERR_INVALID;
+ goto error;
+ }
+
+ ret = cmd_enable_channel(session, domain, attr, wpipe);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* Get the newly created channel reference back */
+ uchan = trace_ust_find_channel_by_name(
+ usess->domain_global.channels, channel_name);
+ assert(uchan);
+ }
+
+ if (uchan->domain != LTTNG_DOMAIN_UST && !internal_event) {
+ /*
+ * Don't allow users to add UST events to channels which
+ * are assigned to a userspace subdomain (JUL, Log4J,
+ * Python, etc.).
+ */
+ ret = LTTNG_ERR_INVALID_CHANNEL_DOMAIN;
+ goto error;
+ }
+
+ if (!internal_event) {
+ /*
+ * Ensure the event name is not reserved for internal
+ * use.
+ */
+ ret = validate_ust_event_name(event->name);
+ if (ret) {
+ WARN("Userspace event name %s failed validation.",
+ event->name ?
+ event->name : "NULL");
+ ret = LTTNG_ERR_INVALID_EVENT_NAME;
+ goto error;
+ }
+ }
+
+ /* At this point, the session and channel exist on the tracer */
+ ret = event_ust_enable_tracepoint(usess, uchan, event,
+ filter_expression, filter, exclusion,
+ internal_event);
+ /* We have passed ownership */
+ filter_expression = NULL;
+ filter = NULL;
+ exclusion = NULL;
+ if (ret == LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto already_enabled;
+ } else if (ret != LTTNG_OK) {
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ {
+ const char *default_event_name, *default_chan_name;
+ struct agent *agt;
+ struct lttng_event uevent;
+ struct lttng_domain tmp_dom;
+ struct ltt_ust_session *usess = session->ust_session;
+
+ assert(usess);
+
+ agt = trace_ust_find_agent(usess, domain->type);
+ if (!agt) {
+ agt = agent_create(domain->type);
+ if (!agt) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ agent_add(agt, usess->agents);
+ }
+
+ /* Create the default tracepoint. */
+ memset(&uevent, 0, sizeof(uevent));
+ uevent.type = LTTNG_EVENT_TRACEPOINT;
+ uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+ default_event_name = event_get_default_agent_ust_name(
+ domain->type);
+ if (!default_event_name) {
+ ret = LTTNG_ERR_FATAL;
+ goto error;
+ }
+ strncpy(uevent.name, default_event_name, sizeof(uevent.name));
+ uevent.name[sizeof(uevent.name) - 1] = '\0';
+
+ /*
+ * The domain type is changed because we are about to enable the
+ * default channel and event for the JUL domain that are hardcoded.
+ * This happens in the UST domain.
+ */
+ memcpy(&tmp_dom, domain, sizeof(tmp_dom));
+ tmp_dom.type = LTTNG_DOMAIN_UST;
+
+ switch (domain->type) {
+ case LTTNG_DOMAIN_LOG4J:
+ default_chan_name = DEFAULT_LOG4J_CHANNEL_NAME;
+ break;
+ case LTTNG_DOMAIN_JUL:
+ default_chan_name = DEFAULT_JUL_CHANNEL_NAME;
+ break;
+ case LTTNG_DOMAIN_PYTHON:
+ default_chan_name = DEFAULT_PYTHON_CHANNEL_NAME;
+ break;
+ default:
+ /* The switch/case we are in makes this impossible */
+ assert(0);
+ }
+
+ {
+ char *filter_expression_copy = NULL;
+ struct lttng_filter_bytecode *filter_copy = NULL;
+
+ if (filter) {
+ const size_t filter_size = sizeof(
+ struct lttng_filter_bytecode)
+ + filter->len;
+
+ filter_copy = zmalloc(filter_size);
+ if (!filter_copy) {
+ ret = LTTNG_ERR_NOMEM;
+ goto error;
+ }
+ memcpy(filter_copy, filter, filter_size);
+
+ filter_expression_copy =
+ strdup(filter_expression);
+ if (!filter_expression) {
+ ret = LTTNG_ERR_NOMEM;
+ }
+
+ if (!filter_expression_copy || !filter_copy) {
+ free(filter_expression_copy);
+ free(filter_copy);
+ goto error;
+ }
+ }
+
+ ret = cmd_enable_event_internal(session, &tmp_dom,
+ (char *) default_chan_name,
+ &uevent, filter_expression_copy,
+ filter_copy, NULL, wpipe);
+ }
+
+ if (ret == LTTNG_ERR_UST_EVENT_ENABLED) {
+ goto already_enabled;
+ } else if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ /* The wild card * means that everything should be enabled. */
+ if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) {
+ ret = event_agent_enable_all(usess, agt, event, filter,
+ filter_expression);
+ } else {
+ ret = event_agent_enable(usess, agt, event, filter,
+ filter_expression);
+ }
+ filter = NULL;
+ filter_expression = NULL;
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+
+ break;
+ }
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ ret = LTTNG_OK;
+
+already_enabled:
+error:
+ free(filter_expression);
+ free(filter);
+ free(exclusion);
+ free(attr);
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Command LTTNG_ENABLE_EVENT processed by the client thread.
+ * We own filter, exclusion, and filter_expression.
+ */
+int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain,
+ char *channel_name, struct lttng_event *event,
+ char *filter_expression,
+ struct lttng_filter_bytecode *filter,
+ struct lttng_event_exclusion *exclusion,
+ int wpipe)
+{
+ return _cmd_enable_event(session, domain, channel_name, event,
+ filter_expression, filter, exclusion, wpipe, false);
+}
+
+/*
+ * Enable an event which is internal to LTTng. An internal should
+ * never be made visible to clients and are immune to checks such as
+ * reserved names.
+ */
+static int cmd_enable_event_internal(struct ltt_session *session,
+ struct lttng_domain *domain,
+ char *channel_name, struct lttng_event *event,
+ char *filter_expression,
+ struct lttng_filter_bytecode *filter,
+ struct lttng_event_exclusion *exclusion,
+ int wpipe)
+{
+ return _cmd_enable_event(session, domain, channel_name, event,
+ filter_expression, filter, exclusion, wpipe, true);
+}
+
+/*
+ * Command LTTNG_LIST_TRACEPOINTS processed by the client thread.
+ */
+ssize_t cmd_list_tracepoints(enum lttng_domain_type domain,
+ struct lttng_event **events)
+{
+ int ret;
+ ssize_t nb_events = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ nb_events = kernel_list_events(kernel_tracer_fd, events);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_KERN_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_UST:
+ nb_events = ust_app_list_events(events);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ nb_events = agent_list_events(events, domain);
+ if (nb_events < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nb_events;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+/*
+ * Command LTTNG_LIST_TRACEPOINT_FIELDS processed by the client thread.
+ */
+ssize_t cmd_list_tracepoint_fields(enum lttng_domain_type domain,
+ struct lttng_event_field **fields)
+{
+ int ret;
+ ssize_t nb_fields = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_UST:
+ nb_fields = ust_app_list_event_fields(fields);
+ if (nb_fields < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ case LTTNG_DOMAIN_KERNEL:
+ default: /* fall-through */
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nb_fields;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+ssize_t cmd_list_syscalls(struct lttng_event **events)
+{
+ return syscall_table_list(events);
+}
+
+/*
+ * Command LTTNG_LIST_TRACKER_PIDS processed by the client thread.
+ *
+ * Called with session lock held.
+ */
+ssize_t cmd_list_tracker_pids(struct ltt_session *session,
+ enum lttng_domain_type domain, int32_t **pids)
+{
+ int ret;
+ ssize_t nr_pids = 0;
+
+ switch (domain) {
+ case LTTNG_DOMAIN_KERNEL:
+ {
+ struct ltt_kernel_session *ksess;
+
+ ksess = session->kernel_session;
+ nr_pids = kernel_list_tracker_pids(ksess, pids);
+ if (nr_pids < 0) {
+ ret = LTTNG_ERR_KERN_LIST_FAIL;
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_UST:
+ {
+ struct ltt_ust_session *usess;
+
+ usess = session->ust_session;
+ nr_pids = trace_ust_list_tracker_pids(usess, pids);
+ if (nr_pids < 0) {
+ ret = LTTNG_ERR_UST_LIST_FAIL;
+ goto error;
+ }
+ break;
+ }
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_PYTHON:
+ default:
+ ret = LTTNG_ERR_UND;
+ goto error;
+ }
+
+ return nr_pids;
+
+error:
+ /* Return negative value to differentiate return code */
+ return -ret;
+}
+
+/*
+ * Command LTTNG_START_TRACE processed by the client thread.
+ *
+ * Called with session mutex held.
+ */
+int cmd_start_trace(struct ltt_session *session)
+{
+ int ret;
+ unsigned long nb_chan = 0;
+ struct ltt_kernel_session *ksession;
+ struct ltt_ust_session *usess;
+
+ assert(session);
+
+ /* Ease our life a bit ;) */
+ ksession = session->kernel_session;
+ usess = session->ust_session;
+
+ /* Is the session already started? */
+ if (session->active) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STARTED;
+ goto error;
+ }
+
+ /*
+ * Starting a session without channel is useless since after that it's not
+ * possible to enable channel thus inform the client.
+ */
+ if (usess && usess->domain_global.channels) {
+ nb_chan += lttng_ht_get_count(usess->domain_global.channels);
+ }
+ if (ksession) {
+ nb_chan += ksession->channel_count;
+ }
+ if (!nb_chan) {
+ ret = LTTNG_ERR_NO_CHANNEL;
+ goto error;
+ }
+
+ /* Kernel tracing */
+ if (ksession != NULL) {
+ ret = start_kernel_session(ksession, kernel_tracer_fd);
+ if (ret != LTTNG_OK) {
+ goto error;
+ }
+ }
+
+ /* Flag session that trace should start automatically */
+ if (usess) {
+ /*
+ * Even though the start trace might fail, flag this session active so
+ * other application coming in are started by default.
+ */
+ usess->active = 1;
+
+ ret = ust_app_start_trace_all(usess);
+ if (ret < 0) {
+ ret = LTTNG_ERR_UST_START_FAIL;
+ goto error;
+ }
+ }
+
+ /* Flag this after a successful start. */
+ session->has_been_started = 1;
+ session->active = 1;
+
+ ret = LTTNG_OK;
+
+error:
+ return ret;
+}
+
+/*
+ * Command LTTNG_STOP_TRACE processed by the client thread.
+ */
+int cmd_stop_trace(struct ltt_session *session)
+{
+ int ret;
+ struct ltt_kernel_channel *kchan;
+ struct ltt_kernel_session *ksession;
+ struct ltt_ust_session *usess;
+
+ assert(session);
+
+ /* Short cut */
+ ksession = session->kernel_session;
+ usess = session->ust_session;
+
+ /* Session is not active. Skip everythong and inform the client. */
+ if (!session->active) {
+ ret = LTTNG_ERR_TRACE_ALREADY_STOPPED;
+ goto error;
+ }
+
+ /* Kernel tracer */
+ if (ksession && ksession->active) {
+ DBG("Stop kernel tracing");
+
+ /* Flush metadata if exist */
+ if (ksession->metadata_stream_fd >= 0) {
+ ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd);
+ if (ret < 0) {
+ ERR("Kernel metadata flush failed");
+ }
+ }
+
+ /* Flush all buffers before stopping */
+ cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) {
+ ret = kernel_flush_buffer(kchan);
+ if (ret < 0) {
+ ERR("Kernel flush buffer error");
+ }
+ }
+
+ ret = kernel_stop_session(ksession);
+ if (ret < 0) {
+ ret = LTTNG_ERR_KERN_STOP_FAIL;
+ goto error;
+ }