X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fcmd.c;h=ff61166d735190754a6315130270b9428c42bf9d;hb=74c0a111f8f316cdcc99946ad99cf74a359a5f6e;hp=0b97ed61a1ec54fc928b1e5a69890ede4002a162;hpb=b223ca945c74c9c4dca7a9bf803c269b937b51e8;p=lttng-tools.git diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 0b97ed61a..ff61166d7 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -18,6 +18,7 @@ #define _GNU_SOURCE #define _LGPL_SOURCE #include +#include #include #include #include @@ -49,20 +50,15 @@ static pthread_mutex_t relayd_net_seq_idx_lock = PTHREAD_MUTEX_INITIALIZER; static uint64_t relayd_net_seq_idx; -/* - * Both functions below are special case for the Kernel domain when - * enabling/disabling all events. - */ -static -int enable_kevent_all(struct ltt_session *session, - struct lttng_domain *domain, char *channel_name, - struct lttng_event *event, +static int validate_event_name(const char *); +static int validate_ust_event_name(const char *); +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, int wpipe); -static -int disable_kevent_all(struct ltt_session *session, int domain, - char *channel_name, - struct lttng_event *event); + struct lttng_filter_bytecode *filter, + struct lttng_event_exclusion *exclusion, + int wpipe); /* * Create a session path used by list_lttng_sessions for the case that the @@ -174,7 +170,10 @@ static void list_lttng_channels(int domain, struct ltt_session *session, rcu_read_lock(); cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, &iter.iter, uchan, node.node) { - strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN); + if (lttng_strncpy(channels[i].name, uchan->name, + LTTNG_SYMBOL_NAME_LEN)) { + break; + } channels[i].attr.overwrite = uchan->attr.overwrite; channels[i].attr.subbuf_size = uchan->attr.subbuf_size; channels[i].attr.num_subbuf = uchan->attr.num_subbuf; @@ -220,7 +219,9 @@ static int list_lttng_agent_events(struct agent *agt, DBG3("Listing agent events"); + rcu_read_lock(); nb_event = lttng_ht_get_count(agt->events); + rcu_read_unlock(); if (nb_event == 0) { ret = nb_event; goto error; @@ -238,7 +239,7 @@ static int list_lttng_agent_events(struct agent *agt, 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; - tmp_events[i].loglevel = event->loglevel; + tmp_events[i].loglevel = event->loglevel_value; tmp_events[i].loglevel_type = event->loglevel_type; i++; } @@ -279,8 +280,7 @@ static int list_lttng_ust_global_events(char *channel_name, uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); - nb_event += lttng_ht_get_count(uchan->events); - + nb_event = lttng_ht_get_count(uchan->events); if (nb_event == 0) { ret = nb_event; goto error; @@ -295,6 +295,11 @@ static int list_lttng_ust_global_events(char *channel_name, } cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { + if (uevent->internal) { + /* This event should remain hidden from clients */ + nb_event--; + continue; + } strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN); tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; tmp[i].enabled = uevent->enabled; @@ -363,7 +368,7 @@ static int list_lttng_kernel_events(char *channel_name, if (nb_event == 0) { *events = NULL; - goto syscall; + goto end; } *events = zmalloc(nb_event * sizeof(struct lttng_event)); @@ -377,6 +382,8 @@ static int list_lttng_kernel_events(char *channel_name, strncpy((*events)[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); (*events)[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; (*events)[i].enabled = event->enabled; + (*events)[i].filter = + (unsigned char) !!event->filter_expression; switch (event->event->instrumentation) { case LTTNG_KERNEL_TRACEPOINT: @@ -410,19 +417,7 @@ static int list_lttng_kernel_events(char *channel_name, i++; } -syscall: - if (syscall_table) { - ssize_t new_size; - - new_size = syscall_list_channel(kchan, events, nb_event); - if (new_size < 0) { - free(events); - ret = -new_size; - goto error; - } - nb_event = new_size; - } - +end: return nb_event; error: @@ -516,12 +511,15 @@ static int add_uri_to_consumer(struct consumer_output *consumer, DBG2("Setting trace directory path from URI to %s", uri->dst.path); memset(consumer->dst.trace_path, 0, sizeof(consumer->dst.trace_path)); - strncpy(consumer->dst.trace_path, uri->dst.path, - sizeof(consumer->dst.trace_path)); + /* Explicit length checks for strcpy and strcat. */ + if (strlen(uri->dst.path) + strlen(default_trace_dir) + >= sizeof(consumer->dst.trace_path)) { + ret = LTTNG_ERR_FATAL; + goto error; + } + strcpy(consumer->dst.trace_path, uri->dst.path); /* Append default trace dir */ - strncat(consumer->dst.trace_path, default_trace_dir, - sizeof(consumer->dst.trace_path) - - strlen(consumer->dst.trace_path) - 1); + strcat(consumer->dst.trace_path, default_trace_dir); /* Flag consumer as local. */ consumer->type = CONSUMER_DST_LOCAL; break; @@ -909,11 +907,106 @@ int cmd_disable_channel(struct ltt_session *session, int domain, } 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; + } + + ret = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Command LTTNG_TRACK_PID processed by the client thread. + * + * Called with session lock held. + */ +int cmd_track_pid(struct ltt_session *session, int domain, int pid) +{ + int ret; + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_session *ksess; + + ksess = session->kernel_session; + + ret = kernel_track_pid(ksess, pid); + if (ret != LTTNG_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess; + + usess = session->ust_session; + + ret = trace_ust_track_pid(usess, pid); + if (ret != LTTNG_OK) { + goto error; + } + break; + } + default: + ret = LTTNG_ERR_UNKNOWN_DOMAIN; + goto error; + } + + ret = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Command LTTNG_UNTRACK_PID processed by the client thread. + * + * Called with session lock held. + */ +int cmd_untrack_pid(struct ltt_session *session, int domain, int pid) +{ + int ret; + + rcu_read_lock(); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_session *ksess; + + ksess = session->kernel_session; + + ret = kernel_untrack_pid(ksess, pid); + if (ret != LTTNG_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess; + + usess = session->ust_session; + + ret = trace_ust_untrack_pid(usess, pid); + if (ret != LTTNG_OK) { + goto error; + } + break; + } default: ret = LTTNG_ERR_UNKNOWN_DOMAIN; goto error; @@ -937,11 +1030,21 @@ int cmd_enable_channel(struct ltt_session *session, int ret; struct ltt_ust_session *usess = session->ust_session; struct lttng_ht *chan_ht; + size_t len; assert(session); assert(attr); assert(domain); + len = strnlen(attr->name, sizeof(attr->name)); + + /* Validate channel name */ + if (attr->name[0] == '.' || + memchr(attr->name, '/', len) != NULL) { + ret = LTTNG_ERR_INVALID_CHANNEL_NAME; + goto end; + } + DBG("Enabling channel %s for session %s", attr->name, session->name); rcu_read_lock(); @@ -999,9 +1102,40 @@ int cmd_enable_channel(struct ltt_session *session, break; } case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: { struct ltt_ust_channel *uchan; + /* + * FIXME + * + * Current agent implementation limitations force us to allow + * only one channel at once in "agent" subdomains. Each + * subdomain has a default channel name which must be strictly + * adhered to. + */ + if (domain->type == LTTNG_DOMAIN_JUL) { + if (strncmp(attr->name, DEFAULT_JUL_CHANNEL_NAME, + LTTNG_SYMBOL_NAME_LEN)) { + ret = LTTNG_ERR_INVALID_CHANNEL_NAME; + goto error; + } + } else if (domain->type == LTTNG_DOMAIN_LOG4J) { + if (strncmp(attr->name, DEFAULT_LOG4J_CHANNEL_NAME, + LTTNG_SYMBOL_NAME_LEN)) { + ret = LTTNG_ERR_INVALID_CHANNEL_NAME; + goto error; + } + } else if (domain->type == LTTNG_DOMAIN_PYTHON) { + if (strncmp(attr->name, DEFAULT_PYTHON_CHANNEL_NAME, + LTTNG_SYMBOL_NAME_LEN)) { + ret = LTTNG_ERR_INVALID_CHANNEL_NAME; + goto error; + } + } + chan_ht = usess->domain_global.channels; uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); @@ -1022,10 +1156,10 @@ int cmd_enable_channel(struct ltt_session *session, error: rcu_read_unlock(); +end: return ret; } - /* * Command LTTNG_DISABLE_EVENT processed by the client thread. */ @@ -1039,14 +1173,16 @@ int cmd_disable_event(struct ltt_session *session, int domain, DBG("Disable event command for event \'%s\'", event->name); event_name = event->name; + if (validate_event_name(event_name)) { + ret = LTTNG_ERR_INVALID_EVENT_NAME; + goto error; + } - if (event->loglevel_type || event->loglevel || event->enabled + /* Error out on unhandled search criteria */ + if (event->loglevel_type || event->loglevel != -1 || event->enabled || event->pid || event->filter || event->exclusion) { - return LTTNG_ERR_UNK; - } - /* Special handling for kernel domain all events. */ - if (domain == LTTNG_DOMAIN_KERNEL && !strcmp(event_name, "*")) { - return disable_kevent_all(session, domain, channel_name, event); + ret = LTTNG_ERR_UNK; + goto error; } rcu_read_lock(); @@ -1066,32 +1202,36 @@ int cmd_disable_event(struct ltt_session *session, int domain, */ if (ksess->has_non_default_channel && channel_name[0] == '\0') { ret = LTTNG_ERR_NEED_CHANNEL_NAME; - goto error; + goto error_unlock; } kchan = trace_kernel_get_channel_by_name(channel_name, ksess); if (kchan == NULL) { ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; - goto error; + goto error_unlock; } switch (event->type) { case LTTNG_EVENT_ALL: case LTTNG_EVENT_TRACEPOINT: - ret = event_kernel_disable_tracepoint(kchan, event_name); - if (ret != LTTNG_OK) { - goto error; - } - break; case LTTNG_EVENT_SYSCALL: - ret = event_kernel_disable_syscall(kchan, event_name); + case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_FUNCTION: + case LTTNG_EVENT_FUNCTION_ENTRY:/* fall-through */ + if (event_name[0] == '\0') { + ret = event_kernel_disable_event(kchan, + NULL, event->type); + } else { + ret = event_kernel_disable_event(kchan, + event_name, event->type); + } if (ret != LTTNG_OK) { - goto error; + goto error_unlock; } break; default: ret = LTTNG_ERR_UNK; - goto error; + goto error_unlock; } kernel_wait_quiescent(kernel_tracer_fd); @@ -1104,33 +1244,47 @@ int cmd_disable_event(struct ltt_session *session, int domain, usess = session->ust_session; + if (validate_ust_event_name(event_name)) { + ret = LTTNG_ERR_INVALID_EVENT_NAME; + goto error_unlock; + } + /* * If a non-default channel has been created in the - * session, explicitely require that -c chan_name needs + * session, explicitly 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; + goto error_unlock; } uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, channel_name); if (uchan == NULL) { ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; - goto error; + goto error_unlock; } switch (event->type) { case LTTNG_EVENT_ALL: - ret = event_ust_disable_tracepoint(usess, uchan, event_name); + /* + * An empty event name means that everything + * should be disabled. + */ + if (event->name[0] == '\0') { + ret = event_ust_disable_all_tracepoints(usess, uchan); + } else { + ret = event_ust_disable_tracepoint(usess, uchan, + event_name); + } if (ret != LTTNG_OK) { - goto error; + goto error_unlock; } break; default: ret = LTTNG_ERR_UNK; - goto error; + goto error_unlock; } DBG3("Disable UST event %s in channel %s completed", event_name, @@ -1151,109 +1305,39 @@ int cmd_disable_event(struct ltt_session *session, int domain, break; default: ret = LTTNG_ERR_UNK; - goto error; + goto error_unlock; } agt = trace_ust_find_agent(usess, domain); if (!agt) { ret = -LTTNG_ERR_UST_EVENT_NOT_FOUND; - goto error; + goto error_unlock; } - /* The wild card * means that everything should be disabled. */ - if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) { + /* + * An empty event name means that everything + * should be disabled. + */ + if (event->name[0] == '\0') { ret = event_agent_disable_all(usess, agt); } else { ret = event_agent_disable(usess, agt, event_name); } if (ret != LTTNG_OK) { - goto error; + goto error_unlock; } break; } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif default: ret = LTTNG_ERR_UND; - goto error; + goto error_unlock; } ret = LTTNG_OK; -error: +error_unlock: rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_DISABLE_EVENT for event "*" processed by the client thread. - */ -static -int disable_kevent_all(struct ltt_session *session, int domain, - char *channel_name, - struct lttng_event *event) -{ - int ret; - - rcu_read_lock(); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_session *ksess; - struct ltt_kernel_channel *kchan; - - 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; - goto error; - } - - switch (event->type) { - case LTTNG_EVENT_ALL: - ret = event_kernel_disable_all(kchan); - if (ret != LTTNG_OK) { - goto error; - } - break; - case LTTNG_EVENT_SYSCALL: - ret = event_kernel_disable_syscall(kchan, ""); - if (ret != LTTNG_OK) { - goto error; - } - break; - default: - ret = LTTNG_ERR_UNK; - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - default: - ret = LTTNG_ERR_UND; - goto error; - } - - ret = LTTNG_OK; - error: - rcu_read_unlock(); return ret; } @@ -1315,11 +1399,6 @@ int cmd_add_context(struct ltt_session *session, int domain, } break; } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif default: ret = LTTNG_ERR_UND; goto error; @@ -1384,16 +1463,50 @@ end: return ret; } +static inline bool name_starts_with(const char *name, const char *prefix) +{ + const size_t max_cmp_len = min(strlen(prefix), LTTNG_SYMBOL_NAME_LEN); + + return !strncmp(name, prefix, max_cmp_len); +} + +/* Perform userspace-specific event name validation */ +static int validate_ust_event_name(const char *name) +{ + int ret = 0; + + if (!name) { + ret = -1; + goto end; + } + + /* + * Check name against all internal UST event component namespaces used + * by the agents. + */ + if (name_starts_with(name, DEFAULT_JUL_EVENT_COMPONENT) || + name_starts_with(name, DEFAULT_LOG4J_EVENT_COMPONENT) || + name_starts_with(name, DEFAULT_PYTHON_EVENT_COMPONENT)) { + ret = -1; + } + +end: + return ret; +} + /* - * Command LTTNG_ENABLE_EVENT processed by the client thread. - * We own filter, exclusion, and filter_expression. + * Internal version of cmd_enable_event() with a supplemental + * "internal_event" flag which is used to enable internal events which should + * be hidden from clients. Such events are used in the agent implementation to + * enable the events through which all "agent" events are funeled. */ -int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, +static 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) + int wpipe, bool internal_event) { int ret, channel_created = 0; struct lttng_channel *attr; @@ -1404,19 +1517,13 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, DBG("Enable event command for event \'%s\'", event->name); - /* Special handling for kernel domain all events. */ - if (domain->type == LTTNG_DOMAIN_KERNEL && !strcmp(event->name, "*")) { - return enable_kevent_all(session, domain, channel_name, event, - filter_expression, filter, wpipe); - } + rcu_read_lock(); ret = validate_event_name(event->name); if (ret) { goto error; } - rcu_read_lock(); - switch (domain->type) { case LTTNG_DOMAIN_KERNEL: { @@ -1442,7 +1549,12 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, ret = LTTNG_ERR_FATAL; goto error; } - strncpy(attr->name, channel_name, sizeof(attr->name)); + if (lttng_strncpy(attr->name, channel_name, + sizeof(attr->name))) { + ret = LTTNG_ERR_INVALID; + free(attr); + goto error; + } ret = cmd_enable_channel(session, domain, attr, wpipe); if (ret != LTTNG_OK) { @@ -1465,11 +1577,66 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, 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_tracepoint(kchan, event); + 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. */ @@ -1479,7 +1646,11 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, } break; case LTTNG_EVENT_SYSCALL: - ret = event_kernel_enable_syscall(kchan, event->name); + 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; } @@ -1520,7 +1691,12 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, ret = LTTNG_ERR_FATAL; goto error; } - strncpy(attr->name, channel_name, sizeof(attr->name)); + if (lttng_strncpy(attr->name, channel_name, + sizeof(attr->name))) { + ret = LTTNG_ERR_INVALID; + free(attr); + goto error; + } ret = cmd_enable_channel(session, domain, attr, wpipe); if (ret != LTTNG_OK) { @@ -1535,14 +1711,42 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, 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); + filter_expression, filter, exclusion, + internal_event); /* We have passed ownership */ filter_expression = NULL; filter = NULL; exclusion = NULL; - if (ret != LTTNG_OK) { + if (ret == LTTNG_ERR_UST_EVENT_ENABLED) { + goto already_enabled; + } else if (ret != LTTNG_OK) { goto error; } break; @@ -1563,7 +1767,7 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, if (!agt) { agt = agent_create(domain->type); if (!agt) { - ret = -LTTNG_ERR_NOMEM; + ret = LTTNG_ERR_NOMEM; goto error; } agent_add(agt, usess->agents); @@ -1573,9 +1777,10 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, 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); + default_event_name = event_get_default_agent_ust_name( + domain->type); if (!default_event_name) { - ret = -LTTNG_ERR_FATAL; + ret = LTTNG_ERR_FATAL; goto error; } strncpy(uevent.name, default_event_name, sizeof(uevent.name)); @@ -1605,161 +1810,60 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, } { + char *filter_expression_copy = NULL; struct lttng_filter_bytecode *filter_copy = NULL; if (filter) { - filter_copy = zmalloc( - sizeof(struct lttng_filter_bytecode) - + filter->len); + 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); - memcpy(filter_copy, filter, - sizeof(struct lttng_filter_bytecode) - + filter->len); - } - - ret = cmd_enable_event(session, &tmp_dom, - (char *) default_chan_name, - &uevent, filter_expression, filter_copy, - NULL, wpipe); - /* We have passed ownership */ - filter_expression = NULL; - } - - 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_agent_enable_all(usess, agt, event, filter); - filter = NULL; - } else { - ret = event_agent_enable(usess, agt, event, filter); - filter = NULL; - } - if (ret != LTTNG_OK) { - goto error; - } - - break; - } -#if 0 - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: -#endif - default: - ret = LTTNG_ERR_UND; - goto error; - } - - ret = LTTNG_OK; - -error: - free(filter_expression); - free(filter); - free(exclusion); - rcu_read_unlock(); - return ret; -} - -/* - * Command LTTNG_ENABLE_EVENT for event "*" processed by the client thread. - */ -static -int enable_kevent_all(struct ltt_session *session, - struct lttng_domain *domain, char *channel_name, - struct lttng_event *event, - char *filter_expression, - struct lttng_filter_bytecode *filter, int wpipe) -{ - int ret; - struct lttng_channel *attr; - - assert(session); - assert(channel_name); - - rcu_read_lock(); - - 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(LTTNG_DOMAIN_KERNEL, - LTTNG_BUFFER_GLOBAL); - if (attr == NULL) { - ret = LTTNG_ERR_FATAL; - goto error; - } - strncpy(attr->name, channel_name, sizeof(attr->name)); + filter_expression_copy = + strdup(filter_expression); + if (!filter_expression) { + ret = LTTNG_ERR_NOMEM; + } - ret = cmd_enable_channel(session, domain, attr, wpipe); - if (ret != LTTNG_OK) { - free(attr); - goto error; + if (!filter_expression_copy || !filter_copy) { + free(filter_expression_copy); + free(filter_copy); + goto error; + } } - free(attr); - /* Get the newly created kernel channel pointer */ - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - assert(kchan); + ret = cmd_enable_event_internal(session, &tmp_dom, + (char *) default_chan_name, + &uevent, filter_expression_copy, + filter_copy, NULL, wpipe); } - - switch (event->type) { - case LTTNG_EVENT_SYSCALL: - ret = event_kernel_enable_syscall(kchan, ""); - if (ret != LTTNG_OK) { - goto error; - } - break; - case LTTNG_EVENT_TRACEPOINT: - /* - * This call enables all LTTNG_KERNEL_TRACEPOINTS and - * events already registered to the channel. - */ - ret = event_kernel_enable_all_tracepoints(kchan, kernel_tracer_fd); - break; - case LTTNG_EVENT_ALL: - /* Enable syscalls and tracepoints */ - ret = event_kernel_enable_all(kchan, kernel_tracer_fd); - break; - default: - ret = LTTNG_ERR_KERN_ENABLE_FAIL; + + if (ret == LTTNG_ERR_UST_EVENT_ENABLED) { + goto already_enabled; + } else if (ret != LTTNG_OK) { goto error; } - /* Manage return value */ + /* 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) { - /* - * On error, cmd_enable_channel call will take care of destroying - * the created channel if it was needed. - */ goto error; } - kernel_wait_quiescent(kernel_tracer_fd); break; } default: @@ -1769,11 +1873,46 @@ int enable_kevent_all(struct ltt_session *session, ret = LTTNG_OK; +already_enabled: error: + free(filter_expression); + free(filter); + free(exclusion); 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. @@ -1854,8 +1993,61 @@ 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, + int 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) { @@ -2004,13 +2196,12 @@ error: /* * Command LTTNG_SET_CONSUMER_URI processed by the client thread. */ -int cmd_set_consumer_uri(int domain, struct ltt_session *session, - size_t nb_uri, struct lttng_uri *uris) +int cmd_set_consumer_uri(struct ltt_session *session, size_t nb_uri, + struct lttng_uri *uris) { int ret, i; struct ltt_kernel_session *ksess = session->kernel_session; struct ltt_ust_session *usess = session->ust_session; - struct consumer_output *consumer = NULL; assert(session); assert(uris); @@ -2022,41 +2213,41 @@ int cmd_set_consumer_uri(int domain, struct ltt_session *session, goto error; } - /* - * This case switch makes sure the domain session has a temporary consumer - * so the URL can be set. - */ - switch (domain) { - case 0: - /* Code flow error. A session MUST always have a consumer object */ - assert(session->consumer); - /* - * The URL will be added to the tracing session consumer instead of a - * specific domain consumer. - */ - consumer = session->consumer; - break; - case LTTNG_DOMAIN_KERNEL: - /* Code flow error if we don't have a kernel session here. */ - assert(ksess); - assert(ksess->consumer); - consumer = ksess->consumer; - break; - case LTTNG_DOMAIN_UST: - /* Code flow error if we don't have a kernel session here. */ - assert(usess); - assert(usess->consumer); - consumer = usess->consumer; - break; - } - + /* Set the "global" consumer URIs */ for (i = 0; i < nb_uri; i++) { - ret = add_uri_to_consumer(consumer, &uris[i], domain, session->name); + ret = add_uri_to_consumer(session->consumer, + &uris[i], 0, session->name); if (ret != LTTNG_OK) { goto error; } } + /* Set UST session URIs */ + if (session->ust_session) { + for (i = 0; i < nb_uri; i++) { + ret = add_uri_to_consumer( + session->ust_session->consumer, + &uris[i], LTTNG_DOMAIN_UST, + session->name); + if (ret != LTTNG_OK) { + goto error; + } + } + } + + /* Set kernel session URIs */ + if (session->kernel_session) { + for (i = 0; i < nb_uri; i++) { + ret = add_uri_to_consumer( + session->kernel_session->consumer, + &uris[i], LTTNG_DOMAIN_KERNEL, + session->name); + if (ret != LTTNG_OK) { + goto error; + } + } + } + /* * Make sure to set the session in output mode after we set URI since a * session can be created without URL (thus flagged in no output mode). @@ -2064,7 +2255,9 @@ int cmd_set_consumer_uri(int domain, struct ltt_session *session, session->output_traces = 1; if (ksess) { ksess->output_traces = 1; - } else if (usess) { + } + + if (usess) { usess->output_traces = 1; } @@ -2126,7 +2319,7 @@ int cmd_create_session_uri(char *name, struct lttng_uri *uris, } if (uris) { - ret = cmd_set_consumer_uri(0, session, nb_uri, uris); + ret = cmd_set_consumer_uri(session, nb_uri, uris); if (ret != LTTNG_OK) { goto consumer_error; } @@ -2164,7 +2357,7 @@ int cmd_create_session_snapshot(char *name, struct lttng_uri *uris, * 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); + ret = cmd_create_session_uri(name, NULL, 0, creds, 0); if (ret != LTTNG_OK) { goto error; } @@ -2215,6 +2408,8 @@ error: /* * Command LTTNG_DESTROY_SESSION processed by the client thread. + * + * Called with session lock held. */ int cmd_destroy_session(struct ltt_session *session, int wpipe) { @@ -2415,12 +2610,14 @@ ssize_t cmd_list_domains(struct ltt_session *session, DBG3("Listing domains found UST global domain"); nb_dom++; + rcu_read_lock(); cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, agt, node.node) { if (agt->being_used) { nb_dom++; } } + rcu_read_unlock(); } if (!nb_dom) { @@ -2435,6 +2632,10 @@ ssize_t cmd_list_domains(struct ltt_session *session, if (session->kernel_session != NULL) { (*domains)[index].type = LTTNG_DOMAIN_KERNEL; + + /* Kernel session buffer type is always GLOBAL */ + (*domains)[index].buf_type = LTTNG_BUFFER_GLOBAL; + index++; } @@ -2443,6 +2644,7 @@ ssize_t cmd_list_domains(struct ltt_session *session, (*domains)[index].buf_type = session->ust_session->buffer_type; index++; + rcu_read_lock(); cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, agt, node.node) { if (agt->being_used) { @@ -2451,6 +2653,7 @@ ssize_t cmd_list_domains(struct ltt_session *session, index++; } } + rcu_read_unlock(); } end: return nb_dom; @@ -2482,8 +2685,10 @@ ssize_t cmd_list_channels(int domain, struct ltt_session *session, break; case LTTNG_DOMAIN_UST: if (session->ust_session != NULL) { + rcu_read_lock(); nb_chan = lttng_ht_get_count( - session->ust_session->domain_global.channels); + session->ust_session->domain_global.channels); + rcu_read_unlock(); } DBG3("Number of UST global channels %zd", nb_chan); if (nb_chan < 0) { @@ -2544,10 +2749,16 @@ ssize_t cmd_list_events(int domain, struct ltt_session *session, struct lttng_ht_iter iter; struct agent *agt; + rcu_read_lock(); cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, agt, node.node) { - nb_event = list_lttng_agent_events(agt, events); + if (agt->domain == domain) { + nb_event = list_lttng_agent_events( + agt, events); + break; + } } + rcu_read_unlock(); } break; default: @@ -2832,10 +3043,18 @@ ssize_t cmd_snapshot_list_outputs(struct ltt_session *session, 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 (lttng_strncpy(list[idx].name, output->name, + sizeof(list[idx].name))) { + ret = -LTTNG_ERR_INVALID; + goto error; + } if (output->consumer->type == CONSUMER_DST_LOCAL) { - strncpy(list[idx].ctrl_url, output->consumer->dst.trace_path, - sizeof(list[idx].ctrl_url)); + if (lttng_strncpy(list[idx].ctrl_url, + output->consumer->dst.trace_path, + sizeof(list[idx].ctrl_url))) { + ret = -LTTNG_ERR_INVALID; + goto error; + } } else { /* Control URI. */ ret = uri_to_str_url(&output->consumer->dst.net.control, @@ -2918,7 +3137,7 @@ error: */ static int record_kernel_snapshot(struct ltt_kernel_session *ksess, struct snapshot_output *output, struct ltt_session *session, - int wait, uint64_t max_stream_size) + int wait, uint64_t nb_packets_per_stream) { int ret; @@ -2926,13 +3145,6 @@ static int record_kernel_snapshot(struct ltt_kernel_session *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 @@ -2949,17 +3161,19 @@ static int record_kernel_snapshot(struct ltt_kernel_session *ksess, goto error_snapshot; } - ret = kernel_snapshot_record(ksess, output, wait, max_stream_size); + ret = kernel_snapshot_record(ksess, output, wait, nb_packets_per_stream); if (ret != LTTNG_OK) { goto error_snapshot; } ret = LTTNG_OK; + goto end; error_snapshot: /* Clean up copied sockets so this output can use some other later on. */ consumer_destroy_output_sockets(output->consumer); error: +end: return ret; } @@ -2970,7 +3184,7 @@ error: */ static int record_ust_snapshot(struct ltt_ust_session *usess, struct snapshot_output *output, struct ltt_session *session, - int wait, uint64_t max_stream_size) + int wait, uint64_t nb_packets_per_stream) { int ret; @@ -2978,14 +3192,6 @@ static int record_ust_snapshot(struct ltt_ust_session *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. @@ -3001,15 +3207,12 @@ static int record_ust_snapshot(struct ltt_ust_session *usess, goto error_snapshot; } - ret = ust_app_snapshot_record(usess, output, wait, max_stream_size); + ret = ust_app_snapshot_record(usess, output, wait, nb_packets_per_stream); 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; @@ -3026,69 +3229,90 @@ error: return ret; } -/* - * Return the biggest subbuffer size of all channels in the given session. - */ -static uint64_t get_session_max_subbuf_size(struct ltt_session *session) +static +uint64_t get_session_size_one_more_packet_per_stream(struct ltt_session *session, + uint64_t cur_nr_packets) { - uint64_t max_size = 0; - - assert(session); + uint64_t tot_size = 0; if (session->kernel_session) { struct ltt_kernel_channel *chan; struct ltt_kernel_session *ksess = session->kernel_session; - /* - * For each channel, add to the max size the size of each subbuffer - * multiplied by their sized. - */ cds_list_for_each_entry(chan, &ksess->channel_list.head, list) { - if (chan->channel->attr.subbuf_size > max_size) { - max_size = chan->channel->attr.subbuf_size; + if (cur_nr_packets >= chan->channel->attr.num_subbuf) { + /* + * Don't take channel into account if we + * already grab all its packets. + */ + continue; } + tot_size += chan->channel->attr.subbuf_size + * chan->stream_count; } } if (session->ust_session) { - struct lttng_ht_iter iter; - struct ltt_ust_channel *uchan; struct ltt_ust_session *usess = session->ust_session; - rcu_read_lock(); - cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter, - uchan, node.node) { - if (uchan->attr.subbuf_size > max_size) { - max_size = uchan->attr.subbuf_size; - } - } - rcu_read_unlock(); + tot_size += ust_app_get_size_one_more_packet_per_stream(usess, + cur_nr_packets); } - return max_size; + return tot_size; } /* - * Returns the total number of streams for a session or a negative value - * on error. + * Calculate the number of packets we can grab from each stream that + * fits within the overall snapshot max size. + * + * Returns -1 on error, 0 means infinite number of packets, else > 0 is + * the number of packets per stream. + * + * TODO: this approach is not perfect: we consider the worse case + * (packet filling the sub-buffers) as an upper bound, but we could do + * better if we do this calculation while we actually grab the packet + * content: we would know how much padding we don't actually store into + * the file. + * + * This algorithm is currently bounded by the number of packets per + * stream. + * + * Since we call this algorithm before actually grabbing the data, it's + * an approximation: for instance, applications could appear/disappear + * in between this call and actually grabbing data. */ -static unsigned int get_session_nb_streams(struct ltt_session *session) +static +int64_t get_session_nb_packets_per_stream(struct ltt_session *session, uint64_t max_size) { - unsigned int total_streams = 0; - - if (session->kernel_session) { - struct ltt_kernel_session *ksess = session->kernel_session; + int64_t size_left; + uint64_t cur_nb_packets = 0; - total_streams += ksess->stream_count_global; + if (!max_size) { + return 0; /* Infinite */ } - if (session->ust_session) { - struct ltt_ust_session *usess = session->ust_session; + size_left = max_size; + for (;;) { + uint64_t one_more_packet_tot_size; - total_streams += ust_app_get_nb_stream(usess); + one_more_packet_tot_size = get_session_size_one_more_packet_per_stream(session, + cur_nb_packets); + if (!one_more_packet_tot_size) { + /* We are already grabbing all packets. */ + break; + } + size_left -= one_more_packet_tot_size; + if (size_left < 0) { + break; + } + cur_nb_packets++; } - - return total_streams; + if (!cur_nb_packets) { + /* Not enough room to grab one packet of each stream, error. */ + return -1; + } + return cur_nb_packets; } /* @@ -3105,14 +3329,22 @@ int cmd_snapshot_record(struct ltt_session *session, int ret = LTTNG_OK; unsigned int use_tmp_output = 0; struct snapshot_output tmp_output; - unsigned int nb_streams, snapshot_success = 0; - uint64_t session_max_size = 0, max_stream_size = 0; + unsigned int snapshot_success = 0; + char datetime[16]; assert(session); assert(output); DBG("Cmd snapshot record for session %s", session->name); + /* Get the datetime for the snapshot output directory. */ + ret = utils_get_current_time_str("%Y%m%d-%H%M%S", datetime, + sizeof(datetime)); + if (!ret) { + ret = LTTNG_ERR_INVALID; + goto error; + } + /* * Permission denied to create an output if the session is not * set in no output mode. @@ -3143,151 +3375,104 @@ int cmd_snapshot_record(struct ltt_session *session, } /* Use the global session count for the temporary snapshot. */ tmp_output.nb_snapshot = session->snapshot.nb_snapshot; - use_tmp_output = 1; - } - /* - * Get the session maximum size for a snapshot meaning it will compute the - * size of all streams from all domain. - */ - max_stream_size = get_session_max_subbuf_size(session); - - nb_streams = get_session_nb_streams(session); - if (nb_streams) { - /* - * The maximum size of the snapshot is the number of streams multiplied - * by the biggest subbuf size of all channels in a session which is the - * maximum stream size available for each stream. The session max size - * is now checked against the snapshot max size value given by the user - * and if lower, an error is returned. - */ - session_max_size = max_stream_size * nb_streams; + /* Use the global datetime */ + memcpy(tmp_output.datetime, datetime, sizeof(datetime)); + use_tmp_output = 1; } - DBG3("Snapshot max size is %" PRIu64 " for max stream size of %" PRIu64, - session_max_size, max_stream_size); + if (use_tmp_output) { + int64_t nb_packets_per_stream; - /* - * If we use a temporary output, check right away if the max size fits else - * for each output the max size will be checked. - */ - if (use_tmp_output && - (tmp_output.max_size != 0 && - tmp_output.max_size < session_max_size)) { - ret = LTTNG_ERR_MAX_SIZE_INVALID; - goto error; - } + nb_packets_per_stream = get_session_nb_packets_per_stream(session, + tmp_output.max_size); + if (nb_packets_per_stream < 0) { + ret = LTTNG_ERR_MAX_SIZE_INVALID; + goto error; + } - if (session->kernel_session) { - struct ltt_kernel_session *ksess = session->kernel_session; + if (session->kernel_session) { + ret = record_kernel_snapshot(session->kernel_session, + &tmp_output, session, + wait, nb_packets_per_stream); + if (ret != LTTNG_OK) { + goto error; + } + } - if (use_tmp_output) { - ret = record_kernel_snapshot(ksess, &tmp_output, session, - wait, max_stream_size); + if (session->ust_session) { + ret = record_ust_snapshot(session->ust_session, + &tmp_output, session, + wait, nb_packets_per_stream); 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)); + snapshot_success = 1; + } else { + struct snapshot_output *sout; + struct lttng_ht_iter iter; - /* Use temporary max size. */ - if (output->max_size != (uint64_t) -1ULL) { - tmp_output.max_size = output->max_size; - } + rcu_read_lock(); + cds_lfht_for_each_entry(session->snapshot.output_ht->ht, + &iter.iter, sout, node.node) { + int64_t nb_packets_per_stream; - if (tmp_output.max_size != 0 && - tmp_output.max_size < session_max_size) { - rcu_read_unlock(); - ret = LTTNG_ERR_MAX_SIZE_INVALID; - goto error; - } + /* + * 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 name. */ - if (*output->name != '\0') { - strncpy(tmp_output.name, output->name, - sizeof(tmp_output.name)); - } + if (output->max_size != (uint64_t) -1ULL) { + tmp_output.max_size = output->max_size; + } - tmp_output.nb_snapshot = session->snapshot.nb_snapshot; + nb_packets_per_stream = get_session_nb_packets_per_stream(session, + tmp_output.max_size); + if (nb_packets_per_stream < 0) { + ret = LTTNG_ERR_MAX_SIZE_INVALID; + rcu_read_unlock(); + goto error; + } - ret = record_kernel_snapshot(ksess, &tmp_output, - session, wait, max_stream_size); - if (ret != LTTNG_OK) { + /* Use temporary name. */ + if (*output->name != '\0') { + if (lttng_strncpy(tmp_output.name, output->name, + sizeof(tmp_output.name))) { + ret = LTTNG_ERR_INVALID; 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, max_stream_size); - 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; - } + tmp_output.nb_snapshot = session->snapshot.nb_snapshot; + memcpy(tmp_output.datetime, datetime, sizeof(datetime)); - if (tmp_output.max_size != 0 && - tmp_output.max_size < session_max_size) { + if (session->kernel_session) { + ret = record_kernel_snapshot(session->kernel_session, + &tmp_output, session, + wait, nb_packets_per_stream); + if (ret != LTTNG_OK) { rcu_read_unlock(); - ret = LTTNG_ERR_MAX_SIZE_INVALID; goto error; } + } - /* 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, max_stream_size); + if (session->ust_session) { + ret = record_ust_snapshot(session->ust_session, + &tmp_output, session, + wait, nb_packets_per_stream); if (ret != LTTNG_OK) { rcu_read_unlock(); goto error; } - snapshot_success = 1; } - rcu_read_unlock(); + snapshot_success = 1; } + rcu_read_unlock(); } if (snapshot_success) { @@ -3300,6 +3485,29 @@ error: return ret; } +/* + * Command LTTNG_SET_SESSION_SHM_PATH processed by the client thread. + */ +int cmd_set_session_shm_path(struct ltt_session *session, + const char *shm_path) +{ + /* Safety net */ + assert(session); + + /* + * Can only set shm path before session is started. + */ + if (session->has_been_started) { + return LTTNG_ERR_SESSION_STARTED; + } + + strncpy(session->shm_path, shm_path, + sizeof(session->shm_path)); + session->shm_path[sizeof(session->shm_path) - 1] = '\0'; + + return 0; +} + /* * Init command subsystem. */