X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=4b8d82efe255d398d9e5eba605be42d96cec5421;hp=1a8fbba1eea75f30f4559274a3790f1e1ba599cc;hb=6b453b5e07e90591c955ae14c60c13b7c1ed28a0;hpb=5d2e1e66a968d9e555f9b8b00d0589ebfaf3de32 diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 1a8fbba1e..4b8d82efe 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -17,6 +17,7 @@ #define _GNU_SOURCE #include +#include #include #include @@ -24,10 +25,12 @@ #include #include #include +#include #include "channel.h" #include "consumer.h" #include "event.h" +#include "health-sessiond.h" #include "kernel.h" #include "kernel-consumer.h" #include "lttng-sessiond.h" @@ -109,7 +112,7 @@ static int build_network_session_path(char *dst, size_t size, tmp_urls, kdata_port, tmp_uurl, udata_port); } else { int dport; - if (kuri) { + if (kuri || (!kuri && !uuri)) { dport = kdata_port; } else { /* No kernel URI, use the UST port. */ @@ -179,6 +182,55 @@ static void list_lttng_channels(int domain, struct ltt_session *session, } } +/* + * Create a list of JUL domain events. + * + * Return number of events in list on success or else a negative value. + */ +static int list_lttng_jul_events(struct jul_domain *dom, + struct lttng_event **events) +{ + int i = 0, ret = 0; + unsigned int nb_event = 0; + struct jul_event *event; + struct lttng_event *tmp_events; + struct lttng_ht_iter iter; + + assert(dom); + assert(events); + + DBG3("Listing JUL events"); + + nb_event = lttng_ht_get_count(dom->events); + if (nb_event == 0) { + ret = nb_event; + goto error; + } + + tmp_events = zmalloc(nb_event * sizeof(*tmp_events)); + if (!tmp_events) { + PERROR("zmalloc JUL events session"); + ret = -LTTNG_ERR_FATAL; + goto error; + } + + rcu_read_lock(); + cds_lfht_for_each_entry(dom->events->ht, &iter.iter, event, node.node) { + strncpy(tmp_events[i].name, event->name, sizeof(tmp_events[i].name)); + tmp_events[i].name[sizeof(tmp_events[i].name) - 1] = '\0'; + tmp_events[i].enabled = event->enabled; + i++; + } + rcu_read_unlock(); + + *events = tmp_events; + ret = nb_event; + +error: + assert(nb_event == i); + return ret; +} + /* * Create a list of ust global domain events. */ @@ -253,6 +305,9 @@ static int list_lttng_ust_global_events(char *channel_name, if (uevent->filter) { tmp[i].filter = 1; } + if (uevent->exclusion) { + tmp[i].exclusion = 1; + } i++; } @@ -305,8 +360,12 @@ static int list_lttng_kernel_events(char *channel_name, case LTTNG_KERNEL_TRACEPOINT: (*events)[i].type = LTTNG_EVENT_TRACEPOINT; break; - case LTTNG_KERNEL_KPROBE: case LTTNG_KERNEL_KRETPROBE: + (*events)[i].type = LTTNG_EVENT_FUNCTION; + memcpy(&(*events)[i].attr.probe, &event->event->u.kprobe, + sizeof(struct lttng_kernel_kprobe)); + break; + case LTTNG_KERNEL_KPROBE: (*events)[i].type = LTTNG_EVENT_PROBE; memcpy(&(*events)[i].attr.probe, &event->event->u.kprobe, sizeof(struct lttng_kernel_kprobe)); @@ -375,20 +434,24 @@ static int add_uri_to_consumer(struct consumer_output *consumer, case LTTNG_DST_IPV6: DBG2("Setting network URI to consumer"); - consumer->type = CONSUMER_DST_NET; - - if ((uri->stype == LTTNG_STREAM_CONTROL && + if (consumer->type == CONSUMER_DST_NET) { + if ((uri->stype == LTTNG_STREAM_CONTROL && consumer->dst.net.control_isset) || (uri->stype == LTTNG_STREAM_DATA && consumer->dst.net.data_isset)) { - ret = LTTNG_ERR_URL_EXIST; - goto error; + ret = LTTNG_ERR_URL_EXIST; + goto error; + } + } else { + memset(&consumer->dst.net, 0, sizeof(consumer->dst.net)); } + consumer->type = CONSUMER_DST_NET; + /* Set URI into consumer output object */ ret = consumer_set_network_uri(consumer, uri); if (ret < 0) { - ret = LTTNG_ERR_FATAL; + ret = -ret; goto error; } else if (ret == 1) { /* @@ -429,6 +492,8 @@ static int add_uri_to_consumer(struct consumer_output *consumer, break; } + ret = LTTNG_OK; + error: return ret; } @@ -449,9 +514,6 @@ static int init_kernel_tracing(struct ltt_kernel_session *session) if (session->consumer_fds_sent == 0 && session->consumer != NULL) { cds_lfht_for_each_entry(session->consumer->socks->ht, &iter.iter, socket, node.node) { - /* Code flow error */ - assert(socket->fd >= 0); - pthread_mutex_lock(socket->lock); ret = kernel_consumer_send_session(socket, session); pthread_mutex_unlock(socket->lock); @@ -473,22 +535,15 @@ error: * On success, the relayd_sock pointer is set to the created socket. * Else, it's stays untouched and a lttcomm error code is returned. */ -static int create_connect_relayd(struct consumer_output *output, - const char *session_name, struct lttng_uri *uri, - struct lttcomm_sock **relayd_sock) +static int create_connect_relayd(struct lttng_uri *uri, + struct lttcomm_relayd_sock **relayd_sock) { int ret; - struct lttcomm_sock *sock; + struct lttcomm_relayd_sock *rsock; - /* Create socket object from URI */ - sock = lttcomm_alloc_sock_from_uri(uri); - if (sock == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - - ret = lttcomm_create_sock(sock); - if (ret < 0) { + rsock = lttcomm_alloc_relayd_sock(uri, RELAYD_VERSION_COMM_MAJOR, + RELAYD_VERSION_COMM_MINOR); + if (!rsock) { ret = LTTNG_ERR_FATAL; goto error; } @@ -499,7 +554,7 @@ static int create_connect_relayd(struct consumer_output *output, * state to be in poll execution. */ health_poll_entry(); - ret = relayd_connect(sock); + ret = relayd_connect(rsock); health_poll_exit(); if (ret < 0) { ERR("Unable to reach lttng-relayd"); @@ -512,8 +567,7 @@ static int create_connect_relayd(struct consumer_output *output, DBG3("Creating relayd stream socket from URI"); /* Check relayd version */ - ret = relayd_version_check(sock, RELAYD_VERSION_COMM_MAJOR, - RELAYD_VERSION_COMM_MINOR); + ret = relayd_version_check(rsock); if (ret < 0) { ret = LTTNG_ERR_RELAYD_VERSION_FAIL; goto close_sock; @@ -527,18 +581,15 @@ static int create_connect_relayd(struct consumer_output *output, goto close_sock; } - *relayd_sock = sock; + *relayd_sock = rsock; return LTTNG_OK; close_sock: - if (sock) { - (void) relayd_close(sock); - } + /* The returned value is not useful since we are on an error path. */ + (void) relayd_close(rsock); free_sock: - if (sock) { - lttcomm_destroy_sock(sock); - } + free(rsock); error: return ret; } @@ -546,23 +597,20 @@ error: /* * Connect to the relayd using URI and send the socket to the right consumer. */ -static int send_consumer_relayd_socket(int domain, struct ltt_session *session, +static int send_consumer_relayd_socket(int domain, unsigned int session_id, struct lttng_uri *relayd_uri, struct consumer_output *consumer, - struct consumer_socket *consumer_sock) + struct consumer_socket *consumer_sock, + char *session_name, char *hostname, int session_live_timer) { int ret; - struct lttcomm_sock *sock = NULL; + struct lttcomm_relayd_sock *rsock = NULL; /* Connect to relayd and make version check if uri is the control. */ - ret = create_connect_relayd(consumer, session->name, relayd_uri, &sock); + ret = create_connect_relayd(relayd_uri, &rsock); if (ret != LTTNG_OK) { - goto close_sock; - } - - /* If the control socket is connected, network session is ready */ - if (relayd_uri->stype == LTTNG_STREAM_CONTROL) { - session->net_handle = 1; + goto error; } + assert(rsock); /* Set the network sequence index if not set. */ if (consumer->net_seq_index == (uint64_t) -1ULL) { @@ -577,8 +625,9 @@ static int send_consumer_relayd_socket(int domain, struct ltt_session *session, } /* Send relayd socket to consumer. */ - ret = consumer_send_relayd_socket(consumer_sock, sock, - consumer, relayd_uri->stype, session->id); + ret = consumer_send_relayd_socket(consumer_sock, rsock, consumer, + relayd_uri->stype, session_id, + session_name, hostname, session_live_timer); if (ret < 0) { ret = LTTNG_ERR_ENABLE_CONSUMER_FAIL; goto close_sock; @@ -599,19 +648,18 @@ static int send_consumer_relayd_socket(int domain, struct ltt_session *session, */ close_sock: - if (sock) { - (void) relayd_close(sock); - lttcomm_destroy_sock(sock); - } + (void) relayd_close(rsock); + free(rsock); +error: if (ret != LTTNG_OK) { /* - * On error, nullify the consumer sequence index so streams are not - * associated with it once sent to the consumer. + * The consumer output for this session should not be used anymore + * since the relayd connection failed thus making any tracing or/and + * streaming not usable. */ - uatomic_set(&consumer->net_seq_index, -1); + consumer->enabled = 0; } - return ret; } @@ -620,19 +668,20 @@ close_sock: * helper function to facilitate sending the information to the consumer for a * session. */ -static int send_consumer_relayd_sockets(int domain, - struct ltt_session *session, struct consumer_output *consumer, - struct consumer_socket *sock) +static int send_consumer_relayd_sockets(int domain, unsigned int session_id, + struct consumer_output *consumer, struct consumer_socket *sock, + char *session_name, char *hostname, int session_live_timer) { int ret = LTTNG_OK; - assert(session); assert(consumer); + assert(sock); /* Sending control relayd socket. */ if (!sock->control_sock_sent) { - ret = send_consumer_relayd_socket(domain, session, - &consumer->dst.net.control, consumer, sock); + ret = send_consumer_relayd_socket(domain, session_id, + &consumer->dst.net.control, consumer, sock, + session_name, hostname, session_live_timer); if (ret != LTTNG_OK) { goto error; } @@ -640,8 +689,9 @@ static int send_consumer_relayd_sockets(int domain, /* Sending data relayd socket. */ if (!sock->data_sock_sent) { - ret = send_consumer_relayd_socket(domain, session, - &consumer->dst.net.data, consumer, sock); + ret = send_consumer_relayd_socket(domain, session_id, + &consumer->dst.net.data, consumer, sock, + session_name, hostname, session_live_timer); if (ret != LTTNG_OK) { goto error; } @@ -678,16 +728,17 @@ int cmd_setup_relayd(struct ltt_session *session) /* For each consumer socket, send relayd sockets */ cds_lfht_for_each_entry(usess->consumer->socks->ht, &iter.iter, socket, node.node) { - /* Code flow error */ - assert(socket->fd >= 0); - pthread_mutex_lock(socket->lock); - ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session, - usess->consumer, socket); + ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_UST, session->id, + usess->consumer, socket, + session->name, session->hostname, + session->live_timer); pthread_mutex_unlock(socket->lock); if (ret != LTTNG_OK) { goto error; } + /* Session is now ready for network streaming. */ + session->net_handle = 1; } } @@ -695,16 +746,17 @@ int cmd_setup_relayd(struct ltt_session *session) && ksess->consumer->enabled) { cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, socket, node.node) { - /* Code flow error */ - assert(socket->fd >= 0); - pthread_mutex_lock(socket->lock); - ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session, - ksess->consumer, socket); + ret = send_consumer_relayd_sockets(LTTNG_DOMAIN_KERNEL, session->id, + ksess->consumer, socket, + session->name, session->hostname, + session->live_timer); pthread_mutex_unlock(socket->lock); if (ret != LTTNG_OK) { goto error; } + /* Session is now ready for network streaming. */ + session->net_handle = 1; } } @@ -722,7 +774,7 @@ static int start_kernel_session(struct ltt_kernel_session *ksess, int wpipe) struct ltt_kernel_channel *kchan; /* Open kernel metadata */ - if (ksess->metadata == NULL) { + if (ksess->metadata == NULL && ksess->output_traces) { ret = kernel_open_metadata(ksess); if (ret < 0) { ret = LTTNG_ERR_KERN_META_FAIL; @@ -731,7 +783,7 @@ static int start_kernel_session(struct ltt_kernel_session *ksess, int wpipe) } /* Open kernel metadata stream */ - if (ksess->metadata_stream_fd < 0) { + if (ksess->metadata && ksess->metadata_stream_fd < 0) { ret = kernel_open_metadata_stream(ksess); if (ret < 0) { ERR("Kernel create metadata stream failed"); @@ -755,7 +807,7 @@ static int start_kernel_session(struct ltt_kernel_session *ksess, int wpipe) /* Setup kernel consumer socket and send fds to it */ ret = init_kernel_tracing(ksess); - if (ret < 0) { + if (ret != 0) { ret = LTTNG_ERR_KERN_START_FAIL; goto error; } @@ -816,7 +868,7 @@ int cmd_disable_channel(struct ltt_session *session, int domain, goto error; } - ret = channel_ust_disable(usess, domain, uchan); + ret = channel_ust_disable(usess, uchan); if (ret != LTTNG_OK) { goto error; } @@ -845,7 +897,7 @@ error: * The wpipe arguments is used as a notifier for the kernel thread. */ int cmd_enable_channel(struct ltt_session *session, - int domain, struct lttng_channel *attr, int wpipe) + struct lttng_domain *domain, struct lttng_channel *attr, int wpipe) { int ret; struct ltt_ust_session *usess = session->ust_session; @@ -853,12 +905,32 @@ int cmd_enable_channel(struct ltt_session *session, assert(session); assert(attr); + assert(domain); DBG("Enabling channel %s for session %s", attr->name, session->name); rcu_read_lock(); - switch (domain) { + /* + * Don't try to enable a channel if the session has been started at + * some point in time before. The tracer does not allow it. + */ + if (session->started) { + ret = LTTNG_ERR_TRACE_ALREADY_STARTED; + goto error; + } + + /* + * If the session is a live session, remove the switch timer, the + * live timer does the same thing but sends also synchronisation + * beacons for inactive streams. + */ + if (session->live_timer > 0) { + attr->attr.live_timer_interval = session->live_timer; + attr->attr.switch_timer_interval = 0; + } + + switch (domain->type) { case LTTNG_DOMAIN_KERNEL: { struct ltt_kernel_channel *kchan; @@ -866,7 +938,19 @@ int cmd_enable_channel(struct ltt_session *session, kchan = trace_kernel_get_channel_by_name(attr->name, session->kernel_session); if (kchan == NULL) { + /* + * Don't try to create a channel if the session + * has been started at some point in time + * before. The tracer does not allow it. + */ + if (session->started) { + ret = LTTNG_ERR_TRACE_ALREADY_STARTED; + goto error; + } ret = channel_kernel_create(session->kernel_session, attr, wpipe); + if (attr->name[0] != '\0') { + session->kernel_session->has_non_default_channel = 1; + } } else { ret = channel_kernel_enable(session->kernel_session, kchan); } @@ -876,18 +960,6 @@ int cmd_enable_channel(struct ltt_session *session, } kernel_wait_quiescent(kernel_tracer_fd); - - /* - * If the session was previously started, start as well this newly - * created kernel session so the events/channels enabled *after* the - * start actually work. - */ - if (session->started && !session->kernel_session->started) { - ret = start_kernel_session(session->kernel_session, wpipe); - if (ret != LTTNG_OK) { - goto error; - } - } break; } case LTTNG_DOMAIN_UST: @@ -898,28 +970,24 @@ int cmd_enable_channel(struct ltt_session *session, uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); if (uchan == NULL) { - ret = channel_ust_create(usess, domain, attr); - } else { - ret = channel_ust_enable(usess, domain, uchan); - } - - /* Start the UST session if the session was already started. */ - if (session->started && !usess->start_trace) { - ret = ust_app_start_trace_all(usess); - if (ret < 0) { - ret = LTTNG_ERR_UST_START_FAIL; + /* + * Don't try to create a channel if the session + * has been started at some point in time + * before. The tracer does not allow it. + */ + if (session->started) { + ret = LTTNG_ERR_TRACE_ALREADY_STARTED; goto error; } - ret = LTTNG_OK; - usess->start_trace = 1; + ret = channel_ust_create(usess, attr, domain->buf_type); + if (attr->name[0] != '\0') { + usess->has_non_default_channel = 1; + } + } else { + ret = channel_ust_enable(usess, uchan); } break; } -#if 0 - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: -#endif default: ret = LTTNG_ERR_UNKNOWN_DOMAIN; goto error; @@ -949,6 +1017,16 @@ int cmd_disable_event(struct ltt_session *session, int domain, ksess = session->kernel_session; + /* + * If a non-default channel has been created in the + * session, explicitely require that -c chan_name needs + * to be provided. + */ + if (ksess->has_non_default_channel && channel_name[0] == '\0') { + ret = LTTNG_ERR_NEED_CHANNEL_NAME; + goto error; + } + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); if (kchan == NULL) { ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; @@ -970,6 +1048,16 @@ int cmd_disable_event(struct ltt_session *session, int domain, usess = session->ust_session; + /* + * 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; + } + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, channel_name); if (uchan == NULL) { @@ -977,7 +1065,7 @@ int cmd_disable_event(struct ltt_session *session, int domain, goto error; } - ret = event_ust_disable_tracepoint(usess, domain, uchan, event_name); + ret = event_ust_disable_tracepoint(usess, uchan, event_name); if (ret != LTTNG_OK) { goto error; } @@ -986,6 +1074,19 @@ int cmd_disable_event(struct ltt_session *session, int domain, channel_name); break; } + case LTTNG_DOMAIN_JUL: + { + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + ret = event_jul_disable(usess, event_name); + if (ret != LTTNG_OK) { + goto error; + } + + break; + } #if 0 case LTTNG_DOMAIN_UST_EXEC_NAME: case LTTNG_DOMAIN_UST_PID: @@ -1021,6 +1122,16 @@ int cmd_disable_event_all(struct ltt_session *session, int domain, ksess = session->kernel_session; + /* + * If a non-default channel has been created in the + * session, explicitely require that -c chan_name needs + * to be provided. + */ + if (ksess->has_non_default_channel && channel_name[0] == '\0') { + ret = LTTNG_ERR_NEED_CHANNEL_NAME; + goto error; + } + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); if (kchan == NULL) { ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; @@ -1042,6 +1153,16 @@ int cmd_disable_event_all(struct ltt_session *session, int domain, usess = session->ust_session; + /* + * 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; + } + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, channel_name); if (uchan == NULL) { @@ -1049,13 +1170,26 @@ int cmd_disable_event_all(struct ltt_session *session, int domain, goto error; } - ret = event_ust_disable_all_tracepoints(usess, domain, uchan); + ret = event_ust_disable_all_tracepoints(usess, uchan); if (ret != 0) { goto error; } DBG3("Disable all UST events in channel %s completed", channel_name); + break; + } + case LTTNG_DOMAIN_JUL: + { + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + ret = event_jul_disable_all(usess); + if (ret != LTTNG_OK) { + goto error; + } + break; } #if 0 @@ -1081,7 +1215,7 @@ error: int cmd_add_context(struct ltt_session *session, int domain, char *channel_name, struct lttng_event_context *ctx, int kwpipe) { - int ret; + int ret, chan_kern_created = 0, chan_ust_created = 0; switch (domain) { case LTTNG_DOMAIN_KERNEL: @@ -1093,8 +1227,8 @@ int cmd_add_context(struct ltt_session *session, int domain, if (ret != LTTNG_OK) { goto error; } + chan_kern_created = 1; } - /* Add kernel context to kernel tracer */ ret = context_kernel_add(session->kernel_session, ctx, channel_name); if (ret != LTTNG_OK) { @@ -1104,25 +1238,27 @@ int cmd_add_context(struct ltt_session *session, int domain, case LTTNG_DOMAIN_UST: { struct ltt_ust_session *usess = session->ust_session; + unsigned int chan_count; + assert(usess); - unsigned int chan_count = - lttng_ht_get_count(usess->domain_global.channels); + chan_count = lttng_ht_get_count(usess->domain_global.channels); if (chan_count == 0) { struct lttng_channel *attr; /* Create default channel */ - attr = channel_new_default_attr(domain); + attr = channel_new_default_attr(domain, usess->buffer_type); if (attr == NULL) { ret = LTTNG_ERR_FATAL; goto error; } - ret = channel_ust_create(usess, domain, attr); + ret = channel_ust_create(usess, attr, usess->buffer_type); if (ret != LTTNG_OK) { free(attr); goto error; } free(attr); + chan_ust_created = 1; } ret = context_ust_add(usess, domain, ctx, channel_name); @@ -1141,18 +1277,42 @@ int cmd_add_context(struct ltt_session *session, int domain, goto error; } - ret = LTTNG_OK; + return LTTNG_OK; error: + if (chan_kern_created) { + struct ltt_kernel_channel *kchan = + trace_kernel_get_channel_by_name(DEFAULT_CHANNEL_NAME, + session->kernel_session); + /* Created previously, this should NOT fail. */ + assert(kchan); + kernel_destroy_channel(kchan); + } + + if (chan_ust_created) { + struct ltt_ust_channel *uchan = + trace_ust_find_channel_by_name( + session->ust_session->domain_global.channels, + DEFAULT_CHANNEL_NAME); + /* Created previously, this should NOT fail. */ + assert(uchan); + /* Remove from the channel list of the session. */ + trace_ust_delete_channel(session->ust_session->domain_global.channels, + uchan); + trace_ust_destroy_channel(uchan); + } return ret; } /* * Command LTTNG_ENABLE_EVENT processed by the client thread. */ -int cmd_enable_event(struct ltt_session *session, int domain, +int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, char *channel_name, struct lttng_event *event, - struct lttng_filter_bytecode *filter, int wpipe) + char *filter_expression, + struct lttng_filter_bytecode *filter, + struct lttng_event_exclusion *exclusion, + int wpipe) { int ret, channel_created = 0; struct lttng_channel *attr; @@ -1163,15 +1323,27 @@ int cmd_enable_event(struct ltt_session *session, int domain, rcu_read_lock(); - switch (domain) { + switch (domain->type) { case LTTNG_DOMAIN_KERNEL: { struct ltt_kernel_channel *kchan; + /* + * If a non-default channel has been created in the + * session, explicitely require that -c chan_name needs + * to be provided. + */ + if (session->kernel_session->has_non_default_channel + && channel_name[0] == '\0') { + ret = LTTNG_ERR_NEED_CHANNEL_NAME; + goto error; + } + kchan = trace_kernel_get_channel_by_name(channel_name, session->kernel_session); if (kchan == NULL) { - attr = channel_new_default_attr(domain); + attr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL, + LTTNG_BUFFER_GLOBAL); if (attr == NULL) { ret = LTTNG_ERR_FATAL; goto error; @@ -1216,12 +1388,23 @@ int cmd_enable_event(struct ltt_session *session, int domain, 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(domain); + attr = channel_new_default_attr(LTTNG_DOMAIN_UST, + usess->buffer_type); if (attr == NULL) { ret = LTTNG_ERR_FATAL; goto error; @@ -1242,10 +1425,58 @@ int cmd_enable_event(struct ltt_session *session, int domain, } /* At this point, the session and channel exist on the tracer */ - ret = event_ust_enable_tracepoint(usess, domain, uchan, event, filter); + ret = event_ust_enable_tracepoint(usess, uchan, event, + filter_expression, filter, exclusion); + if (ret != LTTNG_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_JUL: + { + struct lttng_event uevent; + struct lttng_domain tmp_dom; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* Create the default JUL tracepoint. */ + memset(&uevent, 0, sizeof(uevent)); + uevent.type = LTTNG_EVENT_TRACEPOINT; + uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + if (is_root) { + strncpy(uevent.name, DEFAULT_SYS_JUL_EVENT_NAME, + sizeof(uevent.name)); + } else { + strncpy(uevent.name, DEFAULT_USER_JUL_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; + + ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME, + &uevent, NULL, NULL, NULL, wpipe); + if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) { + goto error; + } + + /* The wild card * means that everything should be enabled. */ + if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) { + ret = event_jul_enable_all(usess, event); + } else { + ret = event_jul_enable(usess, event); + } if (ret != LTTNG_OK) { goto error; } + break; } #if 0 @@ -1268,8 +1499,9 @@ error: /* * Command LTTNG_ENABLE_ALL_EVENT processed by the client thread. */ -int cmd_enable_event_all(struct ltt_session *session, int domain, - char *channel_name, int event_type, +int cmd_enable_event_all(struct ltt_session *session, + struct lttng_domain *domain, char *channel_name, int event_type, + char *filter_expression, struct lttng_filter_bytecode *filter, int wpipe) { int ret; @@ -1280,18 +1512,30 @@ int cmd_enable_event_all(struct ltt_session *session, int domain, rcu_read_lock(); - switch (domain) { + switch (domain->type) { case LTTNG_DOMAIN_KERNEL: { struct ltt_kernel_channel *kchan; assert(session->kernel_session); + /* + * If a non-default channel has been created in the + * session, explicitely require that -c chan_name needs + * to be provided. + */ + if (session->kernel_session->has_non_default_channel + && channel_name[0] == '\0') { + ret = LTTNG_ERR_NEED_CHANNEL_NAME; + goto error; + } + kchan = trace_kernel_get_channel_by_name(channel_name, session->kernel_session); if (kchan == NULL) { /* Create default channel */ - attr = channel_new_default_attr(domain); + attr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL, + LTTNG_BUFFER_GLOBAL); if (attr == NULL) { ret = LTTNG_ERR_FATAL; goto error; @@ -1350,12 +1594,23 @@ int cmd_enable_event_all(struct ltt_session *session, int domain, 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(domain); + attr = channel_new_default_attr(LTTNG_DOMAIN_UST, + usess->buffer_type); if (attr == NULL) { ret = LTTNG_ERR_FATAL; goto error; @@ -1380,8 +1635,8 @@ int cmd_enable_event_all(struct ltt_session *session, int domain, switch (event_type) { case LTTNG_EVENT_ALL: case LTTNG_EVENT_TRACEPOINT: - ret = event_ust_enable_all_tracepoints(usess, domain, uchan, - filter); + ret = event_ust_enable_all_tracepoints(usess, uchan, + filter_expression, filter); if (ret != LTTNG_OK) { goto error; } @@ -1396,6 +1651,52 @@ int cmd_enable_event_all(struct ltt_session *session, int domain, goto error; } + break; + } + case LTTNG_DOMAIN_JUL: + { + struct lttng_event uevent, event; + struct lttng_domain tmp_dom; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* Create the default JUL tracepoint. */ + uevent.type = LTTNG_EVENT_TRACEPOINT; + uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + if (is_root) { + strncpy(uevent.name, DEFAULT_SYS_JUL_EVENT_NAME, + sizeof(uevent.name)); + } else { + strncpy(uevent.name, DEFAULT_USER_JUL_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; + + ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME, + &uevent, NULL, NULL, NULL, wpipe); + if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) { + goto error; + } + + event.loglevel = LTTNG_LOGLEVEL_JUL_ALL; + event.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + strncpy(event.name, "*", sizeof(event.name)); + event.name[sizeof(event.name) - 1] = '\0'; + + ret = event_jul_enable_all(usess, &event); + if (ret != LTTNG_OK) { + goto error; + } + break; } #if 0 @@ -1439,6 +1740,13 @@ ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) goto error; } break; + case LTTNG_DOMAIN_JUL: + nb_events = jul_list_events(events); + if (nb_events < 0) { + ret = LTTNG_ERR_UST_LIST_FAIL; + goto error; + } + break; default: ret = LTTNG_ERR_UND; goto error; @@ -1487,6 +1795,7 @@ error: 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; @@ -1502,6 +1811,21 @@ int cmd_start_trace(struct ltt_session *session) 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; + } + session->enabled = 1; /* Kernel tracing */ @@ -1555,7 +1879,7 @@ int cmd_stop_trace(struct ltt_session *session) session->enabled = 0; /* Kernel tracer */ - if (ksession) { + if (ksession && ksession->started) { DBG("Stop kernel tracing"); /* Flush metadata if exist */ @@ -1585,7 +1909,7 @@ int cmd_stop_trace(struct ltt_session *session) ksession->started = 0; } - if (usess) { + if (usess && usess->start_trace) { usess->start_trace = 0; ret = ust_app_stop_trace_all(usess); @@ -1595,8 +1919,6 @@ int cmd_stop_trace(struct ltt_session *session) } } - session->started = 0; - ret = LTTNG_OK; error: @@ -1654,7 +1976,7 @@ int cmd_set_consumer_uri(int domain, struct ltt_session *session, for (i = 0; i < nb_uri; i++) { ret = add_uri_to_consumer(consumer, &uris[i], domain, session->name); - if (ret < 0) { + if (ret != LTTNG_OK) { goto error; } } @@ -1670,19 +1992,13 @@ error: * Command LTTNG_CREATE_SESSION processed by the client thread. */ int cmd_create_session_uri(char *name, struct lttng_uri *uris, - size_t nb_uri, lttng_sock_cred *creds) + size_t nb_uri, lttng_sock_cred *creds, unsigned int live_timer) { int ret; - char *path = NULL; struct ltt_session *session; assert(name); - - /* No URIs is not possible. */ - if (uris == NULL) { - ret = LTTNG_ERR_SESSION_FAIL; - goto session_error; - } + assert(creds); /* * Verify if the session already exist @@ -1698,7 +2014,7 @@ int cmd_create_session_uri(char *name, struct lttng_uri *uris, } /* Create tracing session in the registry */ - ret = session_create(name, path, LTTNG_SOCK_GET_UID_CRED(creds), + ret = session_create(name, LTTNG_SOCK_GET_UID_CRED(creds), LTTNG_SOCK_GET_GID_CRED(creds)); if (ret != LTTNG_OK) { goto session_error; @@ -1714,6 +2030,7 @@ int cmd_create_session_uri(char *name, struct lttng_uri *uris, session = session_find_by_name(name); assert(session); + session->live_timer = live_timer; /* Create default consumer output for the session not yet created. */ session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); if (session->consumer == NULL) { @@ -1721,9 +2038,15 @@ int cmd_create_session_uri(char *name, struct lttng_uri *uris, goto consumer_error; } - ret = cmd_set_consumer_uri(0, session, nb_uri, uris); - if (ret != LTTNG_OK) { - goto consumer_error; + if (uris) { + ret = cmd_set_consumer_uri(0, session, nb_uri, uris); + if (ret != LTTNG_OK) { + goto consumer_error; + } + session->output_traces = 1; + } else { + session->output_traces = 0; + DBG2("Session %s created with no output", session->name); } session->consumer->enabled = 1; @@ -1738,19 +2061,85 @@ find_error: } /* - * Command LTTNG_DESTROY_SESSION processed by the client thread. + * Command LTTNG_CREATE_SESSION_SNAPSHOT processed by the client thread. */ -int cmd_destroy_session(struct ltt_session *session, int wpipe) +int cmd_create_session_snapshot(char *name, struct lttng_uri *uris, + size_t nb_uri, lttng_sock_cred *creds) { int ret; - struct ltt_ust_session *usess; - struct ltt_kernel_session *ksess; + struct ltt_session *session; + struct snapshot_output *new_output = NULL; - /* Safety net */ - assert(session); + assert(name); + assert(creds); - usess = session->ust_session; - ksess = session->kernel_session; + /* + * Create session in no output mode with URIs set to NULL. The uris we've + * received are for a default snapshot output if one. + */ + ret = cmd_create_session_uri(name, NULL, 0, creds, -1); + if (ret != LTTNG_OK) { + goto error; + } + + /* Get the newly created session pointer back. This should NEVER fail. */ + session = session_find_by_name(name); + assert(session); + + /* Flag session for snapshot mode. */ + session->snapshot_mode = 1; + + /* Skip snapshot output creation if no URI is given. */ + if (nb_uri == 0) { + goto end; + } + + new_output = snapshot_output_alloc(); + if (!new_output) { + ret = LTTNG_ERR_NOMEM; + goto error_snapshot_alloc; + } + + ret = snapshot_output_init_with_uri(DEFAULT_SNAPSHOT_MAX_SIZE, NULL, + uris, nb_uri, session->consumer, new_output, &session->snapshot); + if (ret < 0) { + if (ret == -ENOMEM) { + ret = LTTNG_ERR_NOMEM; + } else { + ret = LTTNG_ERR_INVALID; + } + goto error_snapshot; + } + + rcu_read_lock(); + snapshot_add_output(&session->snapshot, new_output); + rcu_read_unlock(); + +end: + return LTTNG_OK; + +error_snapshot: + snapshot_output_destroy(new_output); +error_snapshot_alloc: + session_destroy(session); +error: + return ret; +} + +/* + * Command LTTNG_DESTROY_SESSION processed by the client thread. + */ +int cmd_destroy_session(struct ltt_session *session, int wpipe) +{ + int ret; + struct ltt_ust_session *usess; + struct ltt_kernel_session *ksess; + + /* Safety net */ + assert(session); + + usess = session->ust_session; + ksess = session->kernel_session; /* Clean kernel session teardown */ kernel_destroy_session(ksess); @@ -1796,7 +2185,14 @@ int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) { struct lttng_kernel_calibrate kcalibrate; - kcalibrate.type = calibrate->type; + switch (calibrate->type) { + case LTTNG_CALIBRATE_FUNCTION: + default: + /* Default and only possible calibrate option. */ + kcalibrate.type = LTTNG_KERNEL_CALIBRATE_KRETPROBE; + break; + } + ret = kernel_calibrate(kernel_tracer_fd, &kcalibrate); if (ret < 0) { ret = LTTNG_ERR_KERN_ENABLE_FAIL; @@ -1808,7 +2204,14 @@ int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) { struct lttng_ust_calibrate ucalibrate; - ucalibrate.type = calibrate->type; + switch (calibrate->type) { + case LTTNG_CALIBRATE_FUNCTION: + default: + /* Default and only possible calibrate option. */ + ucalibrate.type = LTTNG_UST_CALIBRATE_TRACEPOINT; + break; + } + ret = ust_app_calibrate_glb(&ucalibrate); if (ret < 0) { ret = LTTNG_ERR_UST_CALIBRATE_FAIL; @@ -1834,7 +2237,7 @@ int cmd_register_consumer(struct ltt_session *session, int domain, const char *sock_path, struct consumer_data *cdata) { int ret, sock; - struct consumer_socket *socket; + struct consumer_socket *socket = NULL; assert(session); assert(cdata); @@ -1858,13 +2261,15 @@ int cmd_register_consumer(struct ltt_session *session, int domain, ret = LTTNG_ERR_CONNECT_FAIL; goto error; } + cdata->cmd_sock = sock; - socket = consumer_allocate_socket(sock); + socket = consumer_allocate_socket(&cdata->cmd_sock); if (socket == NULL) { ret = close(sock); if (ret < 0) { PERROR("close register consumer"); } + cdata->cmd_sock = -1; ret = LTTNG_ERR_FATAL; goto error; } @@ -1894,9 +2299,12 @@ int cmd_register_consumer(struct ltt_session *session, int domain, goto error; } - ret = LTTNG_OK; + return LTTNG_OK; error: + if (socket) { + consumer_destroy_socket(socket); + } return ret; } @@ -1917,6 +2325,10 @@ ssize_t cmd_list_domains(struct ltt_session *session, if (session->ust_session != NULL) { DBG3("Listing domains found UST global domain"); nb_dom++; + + if (session->ust_session->domain_jul.being_used) { + nb_dom++; + } } *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); @@ -1932,7 +2344,14 @@ ssize_t cmd_list_domains(struct ltt_session *session, if (session->ust_session != NULL) { (*domains)[index].type = LTTNG_DOMAIN_UST; + (*domains)[index].buf_type = session->ust_session->buffer_type; index++; + + if (session->ust_session->domain_jul.being_used) { + (*domains)[index].type = LTTNG_DOMAIN_JUL; + (*domains)[index].buf_type = session->ust_session->buffer_type; + index++; + } } return nb_dom; @@ -2023,6 +2442,12 @@ ssize_t cmd_list_events(int domain, struct ltt_session *session, } break; } + case LTTNG_DOMAIN_JUL: + if (session->ust_session) { + nb_event = list_lttng_jul_events( + &session->ust_session->domain_jul, events); + } + break; default: ret = LTTNG_ERR_UND; goto error; @@ -2071,9 +2496,9 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, (ksess && ksess->consumer->type == CONSUMER_DST_NET) || (usess && usess->consumer->type == CONSUMER_DST_NET)) { ret = build_network_session_path(sessions[i].path, - sizeof(session[i].path), session); + sizeof(sessions[i].path), session); } else { - ret = snprintf(sessions[i].path, sizeof(session[i].path), "%s", + ret = snprintf(sessions[i].path, sizeof(sessions[i].path), "%s", session->consumer->dst.trace_path); } if (ret < 0) { @@ -2084,13 +2509,15 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, uid_t uid, strncpy(sessions[i].name, session->name, NAME_MAX); sessions[i].name[NAME_MAX - 1] = '\0'; sessions[i].enabled = session->enabled; + sessions[i].snapshot_mode = session->snapshot_mode; + sessions[i].live_timer_interval = session->live_timer; i++; } } /* * Command LTTNG_DATA_PENDING returning 0 if the data is NOT pending meaning - * ready for trace analysis (or anykind of reader) or else 1 for pending data. + * ready for trace analysis (or any kind of reader) or else 1 for pending data. */ int cmd_data_pending(struct ltt_session *session) { @@ -2129,6 +2556,548 @@ error: return ret; } +/* + * Command LTTNG_SNAPSHOT_ADD_OUTPUT from the lttng ctl library. + * + * Return LTTNG_OK on success or else a LTTNG_ERR code. + */ +int cmd_snapshot_add_output(struct ltt_session *session, + struct lttng_snapshot_output *output, uint32_t *id) +{ + int ret; + struct snapshot_output *new_output; + + assert(session); + assert(output); + + DBG("Cmd snapshot add output for session %s", session->name); + + /* + * Permission denied to create an output if the session is not + * set in no output mode. + */ + if (session->output_traces) { + ret = LTTNG_ERR_EPERM; + goto error; + } + + /* Only one output is allowed until we have the "tee" feature. */ + if (session->snapshot.nb_output == 1) { + ret = LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST; + goto error; + } + + new_output = snapshot_output_alloc(); + if (!new_output) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + ret = snapshot_output_init(output->max_size, output->name, + output->ctrl_url, output->data_url, session->consumer, new_output, + &session->snapshot); + if (ret < 0) { + if (ret == -ENOMEM) { + ret = LTTNG_ERR_NOMEM; + } else { + ret = LTTNG_ERR_INVALID; + } + goto free_error; + } + + rcu_read_lock(); + snapshot_add_output(&session->snapshot, new_output); + if (id) { + *id = new_output->id; + } + rcu_read_unlock(); + + return LTTNG_OK; + +free_error: + snapshot_output_destroy(new_output); +error: + return ret; +} + +/* + * Command LTTNG_SNAPSHOT_DEL_OUTPUT from lib lttng ctl. + * + * Return LTTNG_OK on success or else a LTTNG_ERR code. + */ +int cmd_snapshot_del_output(struct ltt_session *session, + struct lttng_snapshot_output *output) +{ + int ret; + struct snapshot_output *sout = NULL; + + assert(session); + assert(output); + + rcu_read_lock(); + + /* + * Permission denied to create an output if the session is not + * set in no output mode. + */ + if (session->output_traces) { + ret = LTTNG_ERR_EPERM; + goto error; + } + + if (output->id) { + DBG("Cmd snapshot del output id %" PRIu32 " for session %s", output->id, + session->name); + sout = snapshot_find_output_by_id(output->id, &session->snapshot); + } else if (*output->name != '\0') { + DBG("Cmd snapshot del output name %s for session %s", output->name, + session->name); + sout = snapshot_find_output_by_name(output->name, &session->snapshot); + } + if (!sout) { + ret = LTTNG_ERR_INVALID; + goto error; + } + + snapshot_delete_output(&session->snapshot, sout); + snapshot_output_destroy(sout); + ret = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Command LTTNG_SNAPSHOT_LIST_OUTPUT from lib lttng ctl. + * + * If no output is available, outputs is untouched and 0 is returned. + * + * Return the size of the newly allocated outputs or a negative LTTNG_ERR code. + */ +ssize_t cmd_snapshot_list_outputs(struct ltt_session *session, + struct lttng_snapshot_output **outputs) +{ + int ret, idx = 0; + struct lttng_snapshot_output *list; + struct lttng_ht_iter iter; + struct snapshot_output *output; + + assert(session); + assert(outputs); + + DBG("Cmd snapshot list outputs for session %s", session->name); + + /* + * Permission denied to create an output if the session is not + * set in no output mode. + */ + if (session->output_traces) { + ret = LTTNG_ERR_EPERM; + goto error; + } + + if (session->snapshot.nb_output == 0) { + ret = 0; + goto error; + } + + list = zmalloc(session->snapshot.nb_output * sizeof(*list)); + if (!list) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + /* Copy list from session to the new list object. */ + cds_lfht_for_each_entry(session->snapshot.output_ht->ht, &iter.iter, + output, node.node) { + assert(output->consumer); + list[idx].id = output->id; + list[idx].max_size = output->max_size; + strncpy(list[idx].name, output->name, sizeof(list[idx].name)); + if (output->consumer->type == CONSUMER_DST_LOCAL) { + strncpy(list[idx].ctrl_url, output->consumer->dst.trace_path, + sizeof(list[idx].ctrl_url)); + } else { + /* Control URI. */ + ret = uri_to_str_url(&output->consumer->dst.net.control, + list[idx].ctrl_url, sizeof(list[idx].ctrl_url)); + if (ret < 0) { + ret = LTTNG_ERR_NOMEM; + goto free_error; + } + + /* Data URI. */ + ret = uri_to_str_url(&output->consumer->dst.net.data, + list[idx].data_url, sizeof(list[idx].data_url)); + if (ret < 0) { + ret = LTTNG_ERR_NOMEM; + goto free_error; + } + } + idx++; + } + + *outputs = list; + return session->snapshot.nb_output; + +free_error: + free(list); +error: + return -ret; +} + +/* + * Send relayd sockets from snapshot output to consumer. Ignore request if the + * snapshot output is *not* set with a remote destination. + * + * Return 0 on success or a LTTNG_ERR code. + */ +static int set_relayd_for_snapshot(struct consumer_output *consumer, + struct snapshot_output *snap_output, struct ltt_session *session) +{ + int ret = LTTNG_OK; + struct lttng_ht_iter iter; + struct consumer_socket *socket; + + assert(consumer); + assert(snap_output); + assert(session); + + DBG2("Set relayd object from snapshot output"); + + /* Ignore if snapshot consumer output is not network. */ + if (snap_output->consumer->type != CONSUMER_DST_NET) { + goto error; + } + + /* + * For each consumer socket, create and send the relayd object of the + * snapshot output. + */ + rcu_read_lock(); + cds_lfht_for_each_entry(snap_output->consumer->socks->ht, &iter.iter, + socket, node.node) { + ret = send_consumer_relayd_sockets(0, session->id, + snap_output->consumer, socket, + session->name, session->hostname, + session->live_timer); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto error; + } + } + rcu_read_unlock(); + +error: + return ret; +} + +/* + * Record a kernel snapshot. + * + * Return LTTNG_OK on success or a LTTNG_ERR code. + */ +static int record_kernel_snapshot(struct ltt_kernel_session *ksess, + struct snapshot_output *output, struct ltt_session *session, + int wait, int nb_streams) +{ + int ret; + + assert(ksess); + assert(output); + assert(session); + + /* Get the datetime for the snapshot output directory. */ + ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime, + sizeof(output->datetime)); + if (!ret) { + ret = LTTNG_ERR_INVALID; + goto error; + } + + /* + * Copy kernel session sockets so we can communicate with the right + * consumer for the snapshot record command. + */ + ret = consumer_copy_sockets(output->consumer, ksess->consumer); + if (ret < 0) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + ret = set_relayd_for_snapshot(ksess->consumer, output, session); + if (ret != LTTNG_OK) { + goto error_snapshot; + } + + ret = kernel_snapshot_record(ksess, output, wait, nb_streams); + if (ret != LTTNG_OK) { + goto error_snapshot; + } + + ret = LTTNG_OK; + +error_snapshot: + /* Clean up copied sockets so this output can use some other later on. */ + consumer_destroy_output_sockets(output->consumer); +error: + return ret; +} + +/* + * Record a UST snapshot. + * + * Return 0 on success or a LTTNG_ERR error code. + */ +static int record_ust_snapshot(struct ltt_ust_session *usess, + struct snapshot_output *output, struct ltt_session *session, + int wait, int nb_streams) +{ + int ret; + + assert(usess); + assert(output); + assert(session); + + /* Get the datetime for the snapshot output directory. */ + ret = utils_get_current_time_str("%Y%m%d-%H%M%S", output->datetime, + sizeof(output->datetime)); + if (!ret) { + ret = LTTNG_ERR_INVALID; + goto error; + } + + /* + * Copy UST session sockets so we can communicate with the right + * consumer for the snapshot record command. + */ + ret = consumer_copy_sockets(output->consumer, usess->consumer); + if (ret < 0) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + ret = set_relayd_for_snapshot(usess->consumer, output, session); + if (ret != LTTNG_OK) { + goto error_snapshot; + } + + ret = ust_app_snapshot_record(usess, output, wait, nb_streams); + if (ret < 0) { + switch (-ret) { + case EINVAL: + ret = LTTNG_ERR_INVALID; + break; + case ENODATA: + ret = LTTNG_ERR_SNAPSHOT_NODATA; + break; + default: + ret = LTTNG_ERR_SNAPSHOT_FAIL; + break; + } + goto error_snapshot; + } + + ret = LTTNG_OK; + +error_snapshot: + /* Clean up copied sockets so this output can use some other later on. */ + consumer_destroy_output_sockets(output->consumer); +error: + return ret; +} + +/* + * Returns the total number of streams for a session or a negative value + * on error. + */ +static unsigned int get_total_nb_stream(struct ltt_session *session) +{ + unsigned int total_streams = 0; + + if (session->kernel_session) { + struct ltt_kernel_session *ksess = session->kernel_session; + + total_streams += ksess->stream_count_global; + } + + if (session->ust_session) { + struct ltt_ust_session *usess = session->ust_session; + + total_streams += ust_app_get_nb_stream(usess); + } + + return total_streams; +} + +/* + * Command LTTNG_SNAPSHOT_RECORD from lib lttng ctl. + * + * The wait parameter is ignored so this call always wait for the snapshot to + * complete before returning. + * + * Return LTTNG_OK on success or else a LTTNG_ERR code. + */ +int cmd_snapshot_record(struct ltt_session *session, + struct lttng_snapshot_output *output, int wait) +{ + int ret = LTTNG_OK; + unsigned int use_tmp_output = 0; + struct snapshot_output tmp_output; + unsigned int nb_streams, snapshot_success = 0; + + assert(session); + + DBG("Cmd snapshot record for session %s", session->name); + + /* + * Permission denied to create an output if the session is not + * set in no output mode. + */ + if (session->output_traces) { + ret = LTTNG_ERR_EPERM; + goto error; + } + + /* The session needs to be started at least once. */ + if (!session->started) { + ret = LTTNG_ERR_START_SESSION_ONCE; + goto error; + } + + /* Use temporary output for the session. */ + if (output && *output->ctrl_url != '\0') { + ret = snapshot_output_init(output->max_size, output->name, + output->ctrl_url, output->data_url, session->consumer, + &tmp_output, NULL); + if (ret < 0) { + if (ret == -ENOMEM) { + ret = LTTNG_ERR_NOMEM; + } else { + ret = LTTNG_ERR_INVALID; + } + goto error; + } + /* Use the global session count for the temporary snapshot. */ + tmp_output.nb_snapshot = session->snapshot.nb_snapshot; + use_tmp_output = 1; + } + + /* + * Get the total number of stream of that session which is used by the + * maximum size of the snapshot feature. + */ + nb_streams = get_total_nb_stream(session); + + if (session->kernel_session) { + struct ltt_kernel_session *ksess = session->kernel_session; + + if (use_tmp_output) { + ret = record_kernel_snapshot(ksess, &tmp_output, session, + wait, nb_streams); + if (ret != LTTNG_OK) { + goto error; + } + snapshot_success = 1; + } else { + struct snapshot_output *sout; + struct lttng_ht_iter iter; + + rcu_read_lock(); + cds_lfht_for_each_entry(session->snapshot.output_ht->ht, + &iter.iter, sout, node.node) { + /* + * Make a local copy of the output and assign the possible + * temporary value given by the caller. + */ + memset(&tmp_output, 0, sizeof(tmp_output)); + memcpy(&tmp_output, sout, sizeof(tmp_output)); + + /* Use temporary max size. */ + if (output->max_size != (uint64_t) -1ULL) { + tmp_output.max_size = output->max_size; + } + + /* Use temporary name. */ + if (*output->name != '\0') { + strncpy(tmp_output.name, output->name, + sizeof(tmp_output.name)); + } + + tmp_output.nb_snapshot = session->snapshot.nb_snapshot; + + ret = record_kernel_snapshot(ksess, &tmp_output, + session, wait, nb_streams); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto error; + } + snapshot_success = 1; + } + rcu_read_unlock(); + } + } + + if (session->ust_session) { + struct ltt_ust_session *usess = session->ust_session; + + if (use_tmp_output) { + ret = record_ust_snapshot(usess, &tmp_output, session, + wait, nb_streams); + if (ret != LTTNG_OK) { + goto error; + } + snapshot_success = 1; + } else { + struct snapshot_output *sout; + struct lttng_ht_iter iter; + + rcu_read_lock(); + cds_lfht_for_each_entry(session->snapshot.output_ht->ht, + &iter.iter, sout, node.node) { + /* + * Make a local copy of the output and assign the possible + * temporary value given by the caller. + */ + memset(&tmp_output, 0, sizeof(tmp_output)); + memcpy(&tmp_output, sout, sizeof(tmp_output)); + + /* Use temporary max size. */ + if (output->max_size != (uint64_t) -1ULL) { + tmp_output.max_size = output->max_size; + } + + /* Use temporary name. */ + if (*output->name != '\0') { + strncpy(tmp_output.name, output->name, + sizeof(tmp_output.name)); + } + + tmp_output.nb_snapshot = session->snapshot.nb_snapshot; + + ret = record_ust_snapshot(usess, &tmp_output, session, + wait, nb_streams); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto error; + } + snapshot_success = 1; + } + rcu_read_unlock(); + } + } + + if (snapshot_success) { + session->snapshot.nb_snapshot++; + } else { + ret = LTTNG_ERR_SNAPSHOT_FAIL; + } + +error: + return ret; +} + /* * Init command subsystem. */