X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=e2ca01eb54fe6541323f971095c1d329f28cf747;hp=a8156cf12ed87f3f33a90f3e3fef8cf818f72420;hb=53a80697a772bc2e260e3dff006f910be6709f04;hpb=faf09bcfc465cdf7a5892339ee36b2dcef85b227 diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index a8156cf12..e2ca01eb5 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -2,22 +2,21 @@ * Copyright (C) 2011 - David Goulet * Mathieu Desnoyers * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE -#include #include #include #include @@ -34,61 +33,45 @@ #include #include #include -#include +#include #include #include #include #include +#include #include #include -#include +#include +#include #include "lttng-sessiond.h" #include "channel.h" +#include "consumer.h" #include "context.h" #include "event.h" -#include "futex.h" #include "kernel.h" +#include "kernel-consumer.h" #include "modprobe.h" #include "shm.h" #include "ust-ctl.h" +#include "ust-consumer.h" #include "utils.h" +#include "fd-limit.h" +#include "filter.h" #define CONSUMERD_FILE "lttng-consumerd" -struct consumer_data { - enum lttng_consumer_type type; - - pthread_t thread; /* Worker thread interacting with the consumer */ - sem_t sem; - - /* Mutex to control consumerd pid assignation */ - pthread_mutex_t pid_mutex; - pid_t pid; - - int err_sock; - int cmd_sock; - - /* consumer error and command Unix socket path */ - char err_unix_sock_path[PATH_MAX]; - char cmd_unix_sock_path[PATH_MAX]; -}; - /* Const values */ const char default_home_dir[] = DEFAULT_HOME_DIR; const char default_tracing_group[] = DEFAULT_TRACING_GROUP; const char default_ust_sock_dir[] = DEFAULT_UST_SOCK_DIR; const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE; -/* Variables */ -int opt_verbose; /* Not static for lttngerr.h */ -int opt_verbose_consumer; /* Not static for lttngerr.h */ -int opt_quiet; /* Not static for lttngerr.h */ - const char *progname; const char *opt_tracing_group; static int opt_sig_parent; +static int opt_verbose_consumer; static int opt_daemon; static int opt_no_kernel; static int is_root; /* Set to 1 if the daemon is running as root */ @@ -100,16 +83,22 @@ static struct consumer_data kconsumer_data = { .type = LTTNG_CONSUMER_KERNEL, .err_unix_sock_path = DEFAULT_KCONSUMERD_ERR_SOCK_PATH, .cmd_unix_sock_path = DEFAULT_KCONSUMERD_CMD_SOCK_PATH, + .err_sock = -1, + .cmd_sock = -1, }; static struct consumer_data ustconsumer64_data = { .type = LTTNG_CONSUMER64_UST, .err_unix_sock_path = DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, .cmd_unix_sock_path = DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, + .err_sock = -1, + .cmd_sock = -1, }; static struct consumer_data ustconsumer32_data = { .type = LTTNG_CONSUMER32_UST, .err_unix_sock_path = DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, .cmd_unix_sock_path = DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, + .err_sock = -1, + .cmd_sock = -1, }; static int dispatch_thread_exit; @@ -122,22 +111,22 @@ static char client_unix_sock_path[PATH_MAX]; static char wait_shm_path[PATH_MAX]; /* Sockets and FDs */ -static int client_sock; -static int apps_sock; -static int kernel_tracer_fd; -static int kernel_poll_pipe[2]; +static int client_sock = -1; +static int apps_sock = -1; +static int kernel_tracer_fd = -1; +static int kernel_poll_pipe[2] = { -1, -1 }; /* * Quit pipe for all threads. This permits a single cancellation point * for all threads when receiving an event on the pipe. */ -static int thread_quit_pipe[2]; +static int thread_quit_pipe[2] = { -1, -1 }; /* * This pipe is used to inform the thread managing application communication * that a command is queued and ready to be processed. */ -static int apps_cmd_pipe[2]; +static int apps_cmd_pipe[2] = { -1, -1 }; /* Pthread, Mutexes and Semaphores */ static pthread_t apps_thread; @@ -146,7 +135,6 @@ static pthread_t client_thread; static pthread_t kernel_thread; static pthread_t dispatch_thread; - /* * UST registration command queue. This queue is tied with a futex and uses a N * wakers / 1 waiter implemented and detailed in futex.c/.h @@ -176,6 +164,50 @@ static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; +/* + * Consumer daemon state which is changed when spawning it, killing it or in + * case of a fatal error. + */ +enum consumerd_state { + CONSUMER_STARTED = 1, + CONSUMER_STOPPED = 2, + CONSUMER_ERROR = 3, +}; + +/* + * This consumer daemon state is used to validate if a client command will be + * able to reach the consumer. If not, the client is informed. For instance, + * doing a "lttng start" when the consumer state is set to ERROR will return an + * error to the client. + * + * The following example shows a possible race condition of this scheme: + * + * consumer thread error happens + * client cmd arrives + * client cmd checks state -> still OK + * consumer thread exit, sets error + * client cmd try to talk to consumer + * ... + * + * However, since the consumer is a different daemon, we have no way of making + * sure the command will reach it safely even with this state flag. This is why + * we consider that up to the state validation during command processing, the + * command is safe. After that, we can not guarantee the correctness of the + * client request vis-a-vis the consumer. + */ +static enum consumerd_state ust_consumerd_state; +static enum consumerd_state kernel_consumerd_state; + +/* + * Used to keep a unique index for each relayd socket created where this value + * is associated with streams on the consumer so it can match the right relayd + * to send to. + * + * This value should be incremented atomically for safety purposes and future + * possible concurrent access. + */ +static unsigned int relayd_net_seq_idx; + static void setup_consumerd_path(void) { @@ -295,14 +327,22 @@ static gid_t allowed_group(void) */ static int init_thread_quit_pipe(void) { - int ret; + int ret, i; - ret = pipe2(thread_quit_pipe, O_CLOEXEC); + ret = pipe(thread_quit_pipe); if (ret < 0) { - perror("thread quit pipe"); + PERROR("thread quit pipe"); goto error; } + for (i = 0; i < 2; i++) { + ret = fcntl(thread_quit_pipe[i], F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl"); + goto error; + } + } + error: return ret; } @@ -324,7 +364,8 @@ static void teardown_kernel_session(struct ltt_session *session) * If a custom kernel consumer was registered, close the socket before * tearing down the complete kernel session structure */ - if (session->kernel_session->consumer_fd != kconsumer_data.cmd_sock) { + if (kconsumer_data.cmd_sock >= 0 && + session->kernel_session->consumer_fd != kconsumer_data.cmd_sock) { lttcomm_close_unix_sock(session->kernel_session->consumer_fd); } @@ -419,13 +460,18 @@ static void cleanup(void) if (is_root && !opt_no_kernel) { DBG2("Closing kernel fd"); - close(kernel_tracer_fd); + if (kernel_tracer_fd >= 0) { + ret = close(kernel_tracer_fd); + if (ret) { + PERROR("close"); + } + } DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); } - - close(thread_quit_pipe[0]); - close(thread_quit_pipe[1]); + utils_close_pipe(kernel_poll_pipe); + utils_close_pipe(thread_quit_pipe); + utils_close_pipe(apps_cmd_pipe); /* */ DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm" @@ -467,139 +513,6 @@ static void clean_command_ctx(struct command_ctx **cmd_ctx) } } -/* - * Send all stream fds of kernel channel to the consumer. - */ -static int send_kconsumer_channel_streams(struct consumer_data *consumer_data, - int sock, struct ltt_kernel_channel *channel, - uid_t uid, gid_t gid) -{ - int ret; - struct ltt_kernel_stream *stream; - struct lttcomm_consumer_msg lkm; - - DBG("Sending streams of channel %s to kernel consumer", - channel->channel->name); - - /* Send channel */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - lkm.u.channel.channel_key = channel->fd; - lkm.u.channel.max_sb_size = channel->channel->attr.subbuf_size; - lkm.u.channel.mmap_len = 0; /* for kernel */ - DBG("Sending channel %d to consumer", lkm.u.channel.channel_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - - /* Send streams */ - cds_list_for_each_entry(stream, &channel->stream_list.head, list) { - if (!stream->fd) { - continue; - } - lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lkm.u.stream.channel_key = channel->fd; - lkm.u.stream.stream_key = stream->fd; - lkm.u.stream.state = stream->state; - lkm.u.stream.output = channel->channel->attr.output; - lkm.u.stream.mmap_len = 0; /* for kernel */ - lkm.u.stream.uid = uid; - lkm.u.stream.gid = gid; - strncpy(lkm.u.stream.path_name, stream->pathname, PATH_MAX - 1); - lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending stream %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - ret = lttcomm_send_fds_unix_sock(sock, &stream->fd, 1); - if (ret < 0) { - perror("send consumer stream ancillary data"); - goto error; - } - } - - DBG("consumer channel streams sent"); - - return 0; - -error: - return ret; -} - -/* - * Send all stream fds of the kernel session to the consumer. - */ -static int send_kconsumer_session_streams(struct consumer_data *consumer_data, - struct ltt_kernel_session *session) -{ - int ret; - struct ltt_kernel_channel *chan; - struct lttcomm_consumer_msg lkm; - int sock = session->consumer_fd; - - DBG("Sending metadata stream fd"); - - /* Extra protection. It's NOT supposed to be set to 0 at this point */ - if (session->consumer_fd == 0) { - session->consumer_fd = consumer_data->cmd_sock; - } - - if (session->metadata_stream_fd != 0) { - /* Send metadata channel fd */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - lkm.u.channel.channel_key = session->metadata->fd; - lkm.u.channel.max_sb_size = session->metadata->conf->attr.subbuf_size; - lkm.u.channel.mmap_len = 0; /* for kernel */ - DBG("Sending metadata channel %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - - /* Send metadata stream fd */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lkm.u.stream.channel_key = session->metadata->fd; - lkm.u.stream.stream_key = session->metadata_stream_fd; - lkm.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; - lkm.u.stream.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; - lkm.u.stream.mmap_len = 0; /* for kernel */ - lkm.u.stream.uid = session->uid; - lkm.u.stream.gid = session->gid; - strncpy(lkm.u.stream.path_name, session->metadata->pathname, PATH_MAX - 1); - lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending metadata stream %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - ret = lttcomm_send_fds_unix_sock(sock, &session->metadata_stream_fd, 1); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - } - - cds_list_for_each_entry(chan, &session->channel_list.head, list) { - ret = send_kconsumer_channel_streams(consumer_data, sock, chan, - session->uid, session->gid); - if (ret < 0) { - goto error; - } - } - - DBG("consumer fds (metadata and channel streams) sent"); - - return 0; - -error: - return ret; -} - /* * Notify UST applications using the shm mmap futex. */ @@ -640,7 +553,7 @@ static int setup_lttng_msg(struct command_ctx *cmd_ctx, size_t size) cmd_ctx->llm = zmalloc(sizeof(struct lttcomm_lttng_msg) + buf_size); if (cmd_ctx->llm == NULL) { - perror("zmalloc"); + PERROR("zmalloc"); ret = -ENOMEM; goto error; } @@ -721,8 +634,8 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) continue; } - /* This is not suppose to be 0 but this is an extra security check */ - if (session->kernel_session->consumer_fd == 0) { + /* This is not suppose to be -1 but this is an extra security check */ + if (session->kernel_session->consumer_fd < 0) { session->kernel_session->consumer_fd = consumer_data->cmd_sock; } @@ -740,10 +653,11 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) * that tracing is started so it is safe to send our updated * stream fds. */ - if (session->kernel_session->consumer_fds_sent == 1) { - ret = send_kconsumer_channel_streams(consumer_data, + if (session->kernel_session->consumer_fds_sent == 1 && + session->kernel_session->consumer != NULL) { + ret = kernel_consumer_send_channel_stream( session->kernel_session->consumer_fd, channel, - session->uid, session->gid); + session->kernel_session); if (ret < 0) { goto error; } @@ -800,7 +714,7 @@ static void *thread_manage_kernel(void *data) ret = create_thread_poll_set(&events, 2); if (ret < 0) { - goto error; + goto error_poll_create; } ret = lttng_poll_add(&events, kernel_poll_pipe[0], LPOLLIN); @@ -831,8 +745,15 @@ static void *thread_manage_kernel(void *data) lttng_poll_reset(&events); /* Poll infinite value of time */ + restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } goto error; } else if (ret == 0) { /* Should not happen since timeout is infinite */ @@ -878,12 +799,9 @@ static void *thread_manage_kernel(void *data) } error: - DBG("Kernel thread dying"); - close(kernel_poll_pipe[0]); - close(kernel_poll_pipe[1]); - lttng_poll_clean(&events); - +error_poll_create: + DBG("Kernel thread dying"); return NULL; } @@ -892,7 +810,7 @@ error: */ static void *thread_manage_consumer(void *data) { - int sock = 0, i, ret, pollfd; + int sock = -1, i, ret, pollfd; uint32_t revents, nb_fd; enum lttcomm_return_code code; struct lttng_poll_event events; @@ -902,7 +820,7 @@ static void *thread_manage_consumer(void *data) ret = lttcomm_listen_unix_sock(consumer_data->err_sock); if (ret < 0) { - goto error; + goto error_listen; } /* @@ -911,7 +829,7 @@ static void *thread_manage_consumer(void *data) */ ret = create_thread_poll_set(&events, 2); if (ret < 0) { - goto error; + goto error_poll; } ret = lttng_poll_add(&events, consumer_data->err_sock, LPOLLIN | LPOLLRDHUP); @@ -922,8 +840,15 @@ static void *thread_manage_consumer(void *data) nb_fd = LTTNG_POLL_GETNB(&events); /* Inifinite blocking call, waiting for transmission */ +restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } goto error; } @@ -993,8 +918,15 @@ static void *thread_manage_consumer(void *data) nb_fd = LTTNG_POLL_GETNB(&events); /* Inifinite blocking call, waiting for transmission */ +restart_poll: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart_poll; + } goto error; } @@ -1029,16 +961,44 @@ static void *thread_manage_consumer(void *data) ERR("consumer return code : %s", lttcomm_get_readable_code(-code)); error: - DBG("consumer thread dying"); - close(consumer_data->err_sock); - close(consumer_data->cmd_sock); - close(sock); + /* Immediately set the consumerd state to stopped */ + if (consumer_data->type == LTTNG_CONSUMER_KERNEL) { + uatomic_set(&kernel_consumerd_state, CONSUMER_ERROR); + } else if (consumer_data->type == LTTNG_CONSUMER64_UST || + consumer_data->type == LTTNG_CONSUMER32_UST) { + uatomic_set(&ust_consumerd_state, CONSUMER_ERROR); + } else { + /* Code flow error... */ + assert(0); + } + + if (consumer_data->err_sock >= 0) { + ret = close(consumer_data->err_sock); + if (ret) { + PERROR("close"); + } + } + if (consumer_data->cmd_sock >= 0) { + ret = close(consumer_data->cmd_sock); + if (ret) { + PERROR("close"); + } + } + if (sock >= 0) { + ret = close(sock); + if (ret) { + PERROR("close"); + } + } unlink(consumer_data->err_unix_sock_path); unlink(consumer_data->cmd_unix_sock_path); consumer_data->pid = 0; lttng_poll_clean(&events); +error_poll: +error_listen: + DBG("consumer thread cleanup completed"); return NULL; } @@ -1060,7 +1020,7 @@ static void *thread_manage_apps(void *data) ret = create_thread_poll_set(&events, 2); if (ret < 0) { - goto error; + goto error_poll_create; } ret = lttng_poll_add(&events, apps_cmd_pipe[0], LPOLLIN | LPOLLRDHUP); @@ -1077,8 +1037,15 @@ static void *thread_manage_apps(void *data) DBG("Apps thread polling on %d fds", nb_fd); /* Inifinite blocking call, waiting for transmission */ + restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } goto error; } @@ -1102,7 +1069,7 @@ static void *thread_manage_apps(void *data) /* Empty pipe */ ret = read(apps_cmd_pipe[0], &ust_cmd, sizeof(ust_cmd)); if (ret < 0 || ret < sizeof(ust_cmd)) { - perror("read apps cmd pipe"); + PERROR("read apps cmd pipe"); goto error; } @@ -1138,8 +1105,10 @@ static void *thread_manage_apps(void *data) /* * We just need here to monitor the close of the UST * socket and poll set monitor those by default. + * Listen on POLLIN (even if we never expect any + * data) to ensure that hangup wakes us. */ - ret = lttng_poll_add(&events, ust_cmd.sock, 0); + ret = lttng_poll_add(&events, ust_cmd.sock, LPOLLIN); if (ret < 0) { goto error; } @@ -1171,12 +1140,9 @@ static void *thread_manage_apps(void *data) } error: - DBG("Application communication apps dying"); - close(apps_cmd_pipe[0]); - close(apps_cmd_pipe[1]); - lttng_poll_clean(&events); - +error_poll_create: + DBG("Application communication apps thread cleanup complete"); rcu_thread_offline(); rcu_unregister_thread(); return NULL; @@ -1223,7 +1189,7 @@ static void *thread_dispatch_ust_registration(void *data) ret = write(apps_cmd_pipe[1], ust_cmd, sizeof(struct ust_command)); if (ret < 0) { - perror("write apps cmd pipe"); + PERROR("write apps cmd pipe"); if (errno == EBADF) { /* * We can't inform the application thread to process @@ -1251,7 +1217,7 @@ error: */ static void *thread_registration_apps(void *data) { - int sock = 0, i, ret, pollfd; + int sock = -1, i, ret, pollfd; uint32_t revents, nb_fd; struct lttng_poll_event events; /* @@ -1264,7 +1230,7 @@ static void *thread_registration_apps(void *data) ret = lttcomm_listen_unix_sock(apps_sock); if (ret < 0) { - goto error; + goto error_listen; } /* @@ -1273,13 +1239,13 @@ static void *thread_registration_apps(void *data) */ ret = create_thread_poll_set(&events, 2); if (ret < 0) { - goto error; + goto error_create_poll; } /* Add the application registration socket */ ret = lttng_poll_add(&events, apps_sock, LPOLLIN | LPOLLRDHUP); if (ret < 0) { - goto error; + goto error_poll_add; } /* Notify all applications to register */ @@ -1296,8 +1262,15 @@ static void *thread_registration_apps(void *data) nb_fd = LTTNG_POLL_GETNB(&events); /* Inifinite blocking call, waiting for transmission */ + restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } goto error; } @@ -1326,7 +1299,7 @@ static void *thread_registration_apps(void *data) /* Create UST registration command for enqueuing */ ust_cmd = zmalloc(sizeof(struct ust_command)); if (ust_cmd == NULL) { - perror("ust command zmalloc"); + PERROR("ust command zmalloc"); goto error; } @@ -1334,20 +1307,37 @@ static void *thread_registration_apps(void *data) * Using message-based transmissions to ensure we don't * have to deal with partially received messages. */ + ret = lttng_fd_get(LTTNG_FD_APPS, 1); + if (ret < 0) { + ERR("Exhausted file descriptors allowed for applications."); + free(ust_cmd); + ret = close(sock); + if (ret) { + PERROR("close"); + } + sock = -1; + continue; + } ret = lttcomm_recv_unix_sock(sock, &ust_cmd->reg_msg, sizeof(struct ust_register_msg)); if (ret < 0 || ret < sizeof(struct ust_register_msg)) { if (ret < 0) { - perror("lttcomm_recv_unix_sock register apps"); + PERROR("lttcomm_recv_unix_sock register apps"); } else { ERR("Wrong size received on apps register"); } free(ust_cmd); - close(sock); + ret = close(sock); + if (ret) { + PERROR("close"); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + sock = -1; continue; } ust_cmd->sock = sock; + sock = -1; DBG("UST registration received with pid:%d ppid:%d uid:%d" " gid:%d sock:%d name:%s (version %d.%d)", @@ -1373,16 +1363,29 @@ static void *thread_registration_apps(void *data) } error: - DBG("UST Registration thread dying"); - /* Notify that the registration thread is gone */ notify_ust_apps(0); - close(apps_sock); - close(sock); + if (apps_sock >= 0) { + ret = close(apps_sock); + if (ret) { + PERROR("close"); + } + } + if (sock >= 0) { + ret = close(sock); + if (ret) { + PERROR("close"); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + } unlink(apps_unix_sock_path); +error_poll_add: lttng_poll_clean(&events); +error_listen: +error_create_poll: + DBG("UST Registration thread cleanup complete"); return NULL; } @@ -1622,17 +1625,17 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) break; } default: - perror("unknown consumer type"); + PERROR("unknown consumer type"); exit(EXIT_FAILURE); } if (errno != 0) { - perror("kernel start consumer exec"); + PERROR("kernel start consumer exec"); } exit(EXIT_FAILURE); } else if (pid > 0) { ret = pid; } else { - perror("start consumer fork"); + PERROR("start consumer fork"); ret = -errno; } error: @@ -1723,20 +1726,30 @@ static int init_kernel_tracer(void) error_version: modprobe_remove_lttng_control(); - close(kernel_tracer_fd); - kernel_tracer_fd = 0; + ret = close(kernel_tracer_fd); + if (ret) { + PERROR("close"); + } + kernel_tracer_fd = -1; return LTTCOMM_KERN_VERSION; error_modules: - close(kernel_tracer_fd); + ret = close(kernel_tracer_fd); + if (ret) { + PERROR("close"); + } error_open: modprobe_remove_lttng_control(); error: WARN("No kernel tracer available"); - kernel_tracer_fd = 0; - return LTTCOMM_KERN_NA; + kernel_tracer_fd = -1; + if (!is_root) { + return LTTCOMM_NEED_ROOT_SESSIOND; + } else { + return LTTCOMM_KERN_NA; + } } /* @@ -1746,23 +1759,21 @@ static int init_kernel_tracing(struct ltt_kernel_session *session) { int ret = 0; - if (session->consumer_fds_sent == 0) { + if (session->consumer_fds_sent == 0 && session->consumer != NULL) { /* * Assign default kernel consumer socket if no consumer assigned to the - * kernel session. At this point, it's NOT suppose to be 0 but this is + * kernel session. At this point, it's NOT supposed to be -1 but this is * an extra security check. */ - if (session->consumer_fd == 0) { + if (session->consumer_fd < 0) { session->consumer_fd = kconsumer_data.cmd_sock; } - ret = send_kconsumer_session_streams(&kconsumer_data, session); + ret = kernel_consumer_send_session(session->consumer_fd, session); if (ret < 0) { ret = LTTCOMM_KERN_CONSUMER_FAIL; goto error; } - - session->consumer_fds_sent = 1; } error: @@ -1770,130 +1781,419 @@ error: } /* - * Create an UST session and add it to the session ust list. + * Create a socket to the relayd using the URI. + * + * On success, the relayd_sock pointer is set to the created socket. + * Else, it is untouched and an lttcomm error code is returned. */ -static int create_ust_session(struct ltt_session *session, - struct lttng_domain *domain) +static int create_connect_relayd(struct consumer_output *output, + const char *session_name, struct lttng_uri *uri, + struct lttcomm_sock **relayd_sock) { - struct ltt_ust_session *lus = NULL; int ret; + struct lttcomm_sock *sock; - switch (domain->type) { - case LTTNG_DOMAIN_UST: - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; + /* Create socket object from URI */ + sock = lttcomm_alloc_sock_from_uri(uri); + if (sock == NULL) { + ret = LTTCOMM_FATAL; goto error; } - DBG("Creating UST session"); - - lus = trace_ust_create_session(session->path, session->id, domain); - if (lus == NULL) { - ret = LTTCOMM_UST_SESS_FAIL; + ret = lttcomm_create_sock(sock); + if (ret < 0) { + ret = LTTCOMM_FATAL; goto error; } - ret = run_as_mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, - session->uid, session->gid); + /* Connect to relayd so we can proceed with a session creation. */ + ret = relayd_connect(sock); if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - ret = LTTCOMM_UST_SESS_FAIL; - goto error; - } + ERR("Unable to reach lttng-relayd"); + ret = LTTCOMM_RELAYD_SESSION_FAIL; + goto free_sock; } - /* The domain type dictate different actions on session creation */ - switch (domain->type) { - case LTTNG_DOMAIN_UST: - /* No ustctl for the global UST domain */ - break; - default: - ERR("Unknown UST domain on create session %d", domain->type); - goto error; + /* Create socket for control stream. */ + if (uri->stype == LTTNG_STREAM_CONTROL) { + DBG3("Creating relayd stream socket from URI"); + + /* Check relayd version */ + ret = relayd_version_check(sock, LTTNG_UST_COMM_MAJOR, 0); + if (ret < 0) { + ret = LTTCOMM_RELAYD_VERSION_FAIL; + goto close_sock; + } + } else if (uri->stype == LTTNG_STREAM_DATA) { + DBG3("Creating relayd data socket from URI"); + } else { + /* Command is not valid */ + ERR("Relayd invalid stream type: %d", uri->stype); + ret = LTTCOMM_INVALID; + goto close_sock; } - lus->uid = session->uid; - lus->gid = session->gid; - session->ust_session = lus; + + *relayd_sock = sock; return LTTCOMM_OK; +close_sock: + if (sock) { + (void) relayd_close(sock); + } +free_sock: + if (sock) { + lttcomm_destroy_sock(sock); + } error: - free(lus); return ret; } /* - * Create a kernel tracer session then create the default channel. + * Connect to the relayd using URI and send the socket to the right consumer. */ -static int create_kernel_session(struct ltt_session *session) +static int send_socket_relayd_consumer(int domain, struct ltt_session *session, + struct lttng_uri *relayd_uri, struct consumer_output *consumer, + int consumer_fd) { int ret; + struct lttcomm_sock *sock = NULL; - DBG("Creating kernel session"); + /* Set the network sequence index if not set. */ + if (consumer->net_seq_index == -1) { + /* + * Increment net_seq_idx because we are about to transfer the + * new relayd socket to the consumer. + */ + uatomic_inc(&relayd_net_seq_idx); + /* Assign unique key so the consumer can match streams */ + consumer->net_seq_index = uatomic_read(&relayd_net_seq_idx); + } - ret = kernel_create_session(session, kernel_tracer_fd); - if (ret < 0) { - ret = LTTCOMM_KERN_SESS_FAIL; - goto error; + /* Connect to relayd and make version check if uri is the control. */ + ret = create_connect_relayd(consumer, session->name, relayd_uri, &sock); + if (ret != LTTCOMM_OK) { + goto close_sock; } - /* Set kernel consumer socket fd */ - if (kconsumer_data.cmd_sock) { - session->kernel_session->consumer_fd = kconsumer_data.cmd_sock; + /* If the control socket is connected, network session is ready */ + if (relayd_uri->stype == LTTNG_STREAM_CONTROL) { + session->net_handle = 1; } - ret = run_as_mkdir_recursive(session->kernel_session->trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - goto error; + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Send relayd socket to consumer. */ + ret = kernel_consumer_send_relayd_socket(consumer_fd, sock, + consumer, relayd_uri->stype); + if (ret < 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto close_sock; + } + break; + case LTTNG_DOMAIN_UST: + /* Send relayd socket to consumer. */ + ret = ust_consumer_send_relayd_socket(consumer_fd, sock, + consumer, relayd_uri->stype); + if (ret < 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto close_sock; } + break; + } + + ret = LTTCOMM_OK; + + /* + * Close socket which was dup on the consumer side. The session daemon does + * NOT keep track of the relayd socket(s) once transfer to the consumer. + */ + +close_sock: + if (sock) { + (void) relayd_close(sock); + lttcomm_destroy_sock(sock); } - session->kernel_session->uid = session->uid; - session->kernel_session->gid = session->gid; -error: return ret; } /* - * Check if the UID or GID match the session. Root user has access to all - * sessions. + * Send both relayd sockets to a specific consumer and domain. This is a + * helper function to facilitate sending the information to the consumer for a + * session. */ -static int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) +static int send_sockets_relayd_consumer(int domain, + struct ltt_session *session, struct consumer_output *consumer, int fd) { - if (uid != session->uid && gid != session->gid && uid != 0) { - return 0; - } else { - return 1; - } -} + int ret; -static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) -{ - unsigned int i = 0; - struct ltt_session *session; + /* Sending control relayd socket. */ + ret = send_socket_relayd_consumer(domain, session, + &consumer->dst.net.control, consumer, fd); + if (ret != LTTCOMM_OK) { + goto error; + } - DBG("Counting number of available session for UID %d GID %d", - uid, gid); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { - continue; - } - i++; + /* Sending data relayd socket. */ + ret = send_socket_relayd_consumer(domain, session, + &consumer->dst.net.data, consumer, fd); + if (ret != LTTCOMM_OK) { + goto error; } - return i; + +error: + return ret; } /* - * Using the session list, filled a lttng_session array to send back to the - * client for session listing. + * Setup relayd connections for a tracing session. First creates the socket to + * the relayd and send them to the right domain consumer. Consumer type MUST be + * network. + */ +static int setup_relayd(struct ltt_session *session) +{ + int ret = LTTCOMM_OK; + struct ltt_ust_session *usess; + struct ltt_kernel_session *ksess; + + assert(session); + + usess = session->ust_session; + ksess = session->kernel_session; + + DBG2("Setting relayd for session %s", session->name); + + if (usess && usess->consumer->sock == -1 && + usess->consumer->type == CONSUMER_DST_NET && + usess->consumer->enabled) { + /* Setup relayd for 64 bits consumer */ + if (ust_consumerd64_fd >= 0) { + send_sockets_relayd_consumer(LTTNG_DOMAIN_UST, session, + usess->consumer, ust_consumerd64_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + /* Setup relayd for 32 bits consumer */ + if (ust_consumerd32_fd >= 0) { + send_sockets_relayd_consumer(LTTNG_DOMAIN_UST, session, + usess->consumer, ust_consumerd32_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + } + } else if (ksess && ksess->consumer->sock == -1 && + ksess->consumer->type == CONSUMER_DST_NET && + ksess->consumer->enabled) { + send_sockets_relayd_consumer(LTTNG_DOMAIN_KERNEL, session, + ksess->consumer, ksess->consumer_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + } + +error: + return ret; +} + +/* + * Copy consumer output from the tracing session to the domain session. The + * function also applies the right modification on a per domain basis for the + * trace files destination directory. + */ +static int copy_session_consumer(int domain, struct ltt_session *session) +{ + int ret; + const char *dir_name; + struct consumer_output *consumer; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + DBG3("Copying tracing session consumer output in kernel session"); + session->kernel_session->consumer = + consumer_copy_output(session->consumer); + /* Ease our life a bit for the next part */ + consumer = session->kernel_session->consumer; + dir_name = DEFAULT_KERNEL_TRACE_DIR; + break; + case LTTNG_DOMAIN_UST: + DBG3("Copying tracing session consumer output in UST session"); + session->ust_session->consumer = + consumer_copy_output(session->consumer); + /* Ease our life a bit for the next part */ + consumer = session->ust_session->consumer; + dir_name = DEFAULT_UST_TRACE_DIR; + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + /* Append correct directory to subdir */ + strncat(consumer->subdir, dir_name, sizeof(consumer->subdir)); + DBG3("Copy session consumer subdir %s", consumer->subdir); + + /* Add default trace directory name */ + if (consumer->type == CONSUMER_DST_LOCAL) { + strncat(consumer->dst.trace_path, dir_name, + sizeof(consumer->dst.trace_path)); + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Create an UST session and add it to the session ust list. + */ +static int create_ust_session(struct ltt_session *session, + struct lttng_domain *domain) +{ + int ret; + struct ltt_ust_session *lus = NULL; + + assert(session); + assert(session->consumer); + + switch (domain->type) { + case LTTNG_DOMAIN_UST: + break; + default: + ERR("Unknown UST domain on create session %d", domain->type); + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + DBG("Creating UST session"); + + lus = trace_ust_create_session(session->path, session->id, domain); + if (lus == NULL) { + ret = LTTCOMM_UST_SESS_FAIL; + goto error; + } + + if (session->consumer->type == CONSUMER_DST_LOCAL) { + ret = run_as_mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, + session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_UST_SESS_FAIL; + goto error; + } + } + } + + lus->uid = session->uid; + lus->gid = session->gid; + session->ust_session = lus; + + /* Copy session output to the newly created UST session */ + ret = copy_session_consumer(domain->type, session); + if (ret != LTTCOMM_OK) { + goto error; + } + + return LTTCOMM_OK; + +error: + free(lus); + session->ust_session = NULL; + return ret; +} + +/* + * Create a kernel tracer session then create the default channel. + */ +static int create_kernel_session(struct ltt_session *session) +{ + int ret; + + DBG("Creating kernel session"); + + ret = kernel_create_session(session, kernel_tracer_fd); + if (ret < 0) { + ret = LTTCOMM_KERN_SESS_FAIL; + goto error; + } + + /* Set kernel consumer socket fd */ + if (kconsumer_data.cmd_sock >= 0) { + session->kernel_session->consumer_fd = kconsumer_data.cmd_sock; + } + + /* Copy session output to the newly created Kernel session */ + ret = copy_session_consumer(LTTNG_DOMAIN_KERNEL, session); + if (ret != LTTCOMM_OK) { + goto error; + } + + /* Create directory(ies) on local filesystem. */ + if (session->consumer->type == CONSUMER_DST_LOCAL) { + ret = run_as_mkdir_recursive( + session->kernel_session->consumer->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + goto error; + } + } + } + + session->kernel_session->uid = session->uid; + session->kernel_session->gid = session->gid; + + return LTTCOMM_OK; + +error: + trace_kernel_destroy_session(session->kernel_session); + session->kernel_session = NULL; + return ret; +} + +/* + * Check if the UID or GID match the session. Root user has access to all + * sessions. + */ +static int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) +{ + if (uid != session->uid && gid != session->gid && uid != 0) { + return 0; + } else { + return 1; + } +} + +/* + * Count number of session permitted by uid/gid. + */ +static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) +{ + unsigned int i = 0; + struct ltt_session *session; + + DBG("Counting number of available session for UID %d GID %d", + uid, gid); + cds_list_for_each_entry(session, &session_list_ptr->head, list) { + /* + * Only list the sessions the user can control. + */ + if (!session_access_ok(session, uid, gid)) { + continue; + } + i++; + } + return i; +} + +/* + * Using the session list, filled a lttng_session array to send back to the + * client for session listing. * * The session list lock MUST be acquired before calling this function. Use * session_lock_list() and session_unlock_list(). @@ -2432,6 +2732,46 @@ error: return ret; } +/* + * Command LTTNG_SET_FILTER processed by the client thread. + */ +static int cmd_set_filter(struct ltt_session *session, int domain, + char *channel_name, char *event_name, + struct lttng_filter_bytecode *bytecode) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + ret = LTTCOMM_FATAL; + break; + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess = session->ust_session; + + ret = filter_ust_set(usess, domain, bytecode, event_name, channel_name); + if (ret != LTTCOMM_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 = LTTCOMM_UND; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; + +} + /* * Command LTTNG_ENABLE_EVENT processed by the client thread. */ @@ -2715,6 +3055,36 @@ error: return -ret; } +/* + * Command LTTNG_LIST_TRACEPOINT_FIELDS processed by the client thread. + */ +static ssize_t cmd_list_tracepoint_fields(int domain, + struct lttng_event_field **fields) +{ + int ret; + ssize_t nb_fields = 0; + + switch (domain) { + case LTTNG_DOMAIN_UST: + nb_fields = ust_app_list_event_fields(fields); + if (nb_fields < 0) { + ret = LTTCOMM_UST_LIST_FAIL; + goto error; + } + break; + case LTTNG_DOMAIN_KERNEL: + default: /* fall-through */ + ret = LTTCOMM_UND; + goto error; + } + + return nb_fields; + +error: + /* Return negative value to differentiate return code */ + return -ret; +} + /* * Command LTTNG_START_TRACE processed by the client thread. */ @@ -2723,25 +3093,32 @@ static int cmd_start_trace(struct ltt_session *session) int ret; struct ltt_kernel_session *ksession; struct ltt_ust_session *usess; + struct ltt_kernel_channel *kchan; - /* Short cut */ + /* Ease our life a bit ;) */ ksession = session->kernel_session; usess = session->ust_session; if (session->enabled) { - ret = LTTCOMM_UST_START_FAIL; + /* Already started. */ + ret = LTTCOMM_TRACE_ALREADY_STARTED; goto error; } session->enabled = 1; + ret = setup_relayd(session); + if (ret != LTTCOMM_OK) { + ERR("Error setting up relayd for session %s", session->name); + goto error; + } + /* Kernel tracing */ if (ksession != NULL) { - struct ltt_kernel_channel *kchan; - /* Open kernel metadata */ if (ksession->metadata == NULL) { - ret = kernel_open_metadata(ksession, ksession->trace_path); + ret = kernel_open_metadata(ksession, + ksession->consumer->dst.trace_path); if (ret < 0) { ret = LTTCOMM_KERN_META_FAIL; goto error; @@ -2749,7 +3126,7 @@ static int cmd_start_trace(struct ltt_session *session) } /* Open kernel metadata stream */ - if (ksession->metadata_stream_fd == 0) { + if (ksession->metadata_stream_fd < 0) { ret = kernel_open_metadata_stream(ksession); if (ret < 0) { ERR("Kernel create metadata stream failed"); @@ -2821,7 +3198,7 @@ static int cmd_stop_trace(struct ltt_session *session) usess = session->ust_session; if (!session->enabled) { - ret = LTTCOMM_UST_STOP_FAIL; + ret = LTTCOMM_TRACE_ALREADY_STOPPED; goto error; } @@ -2831,12 +3208,15 @@ static int cmd_stop_trace(struct ltt_session *session) if (ksession != NULL) { DBG("Stop kernel tracing"); - /* Flush all buffers before stopping */ - ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); - if (ret < 0) { - ERR("Kernel metadata flush failed"); + /* Flush metadata if exist */ + if (ksession->metadata_stream_fd >= 0) { + ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); + if (ret < 0) { + ERR("Kernel metadata flush failed"); + } } + /* Flush all buffers before stopping */ cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { ret = kernel_flush_buffer(kchan); if (ret < 0) { @@ -2869,20 +3249,110 @@ error: return ret; } +/* + * Command LTTNG_CREATE_SESSION_URI processed by the client thread. + */ +static int cmd_create_session_uri(char *name, struct lttng_uri *ctrl_uri, + struct lttng_uri *data_uri, unsigned int enable_consumer, + lttng_sock_cred *creds) +{ + int ret; + char *path = NULL; + struct ltt_session *session; + struct consumer_output *consumer; + + /* Verify if the session already exist */ + session = session_find_by_name(name); + if (session != NULL) { + ret = LTTCOMM_EXIST_SESS; + goto error; + } + + /* TODO: validate URIs */ + + /* Create default consumer output */ + consumer = consumer_create_output(CONSUMER_DST_LOCAL); + if (consumer == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + strncpy(consumer->subdir, ctrl_uri->subdir, sizeof(consumer->subdir)); + DBG2("Consumer subdir set to %s", consumer->subdir); + + switch (ctrl_uri->dtype) { + case LTTNG_DST_IPV4: + case LTTNG_DST_IPV6: + /* Set control URI into consumer output object */ + ret = consumer_set_network_uri(consumer, ctrl_uri); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* Set data URI into consumer output object */ + ret = consumer_set_network_uri(consumer, data_uri); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* Empty path since the session is network */ + path = ""; + break; + case LTTNG_DST_PATH: + /* Very volatile pointer. Only used for the create session. */ + path = ctrl_uri->dst.path; + strncpy(consumer->dst.trace_path, path, + sizeof(consumer->dst.trace_path)); + break; + } + + /* Set if the consumer is enabled or not */ + consumer->enabled = enable_consumer; + + ret = session_create(name, path, LTTNG_SOCK_GET_UID_CRED(creds), + LTTNG_SOCK_GET_GID_CRED(creds)); + if (ret != LTTCOMM_OK) { + goto consumer_error; + } + + /* Get the newly created session pointer back */ + session = session_find_by_name(name); + assert(session); + + /* Assign consumer to session */ + session->consumer = consumer; + + return LTTCOMM_OK; + +consumer_error: + consumer_destroy_output(consumer); +error: + return ret; +} + /* * Command LTTNG_CREATE_SESSION processed by the client thread. */ -static int cmd_create_session(char *name, char *path, struct ucred *creds) +static int cmd_create_session(char *name, char *path, lttng_sock_cred *creds) { int ret; + struct lttng_uri uri; - ret = session_create(name, path, creds->uid, creds->gid); + /* Zeroed temporary URI */ + memset(&uri, 0, sizeof(uri)); + + uri.dtype = LTTNG_DST_PATH; + uri.utype = LTTNG_URI_DST; + strncpy(uri.dst.path, path, sizeof(uri.dst.path)); + + /* TODO: Strip date-time from path and put it in uri's subdir */ + + ret = cmd_create_session_uri(name, &uri, NULL, 1, creds); if (ret != LTTCOMM_OK) { goto error; } - ret = LTTCOMM_OK; - error: return ret; } @@ -2905,7 +3375,7 @@ static int cmd_destroy_session(struct ltt_session *session, char *name) */ ret = notify_thread_pipe(kernel_poll_pipe[1]); if (ret < 0) { - perror("write kernel poll pipe"); + PERROR("write kernel poll pipe"); } ret = session_destroy(session); @@ -3034,82 +3504,460 @@ error: } /* - * Command LTTNG_LIST_CHANNELS processed by the client thread. + * Command LTTNG_LIST_CHANNELS processed by the client thread. + */ +static ssize_t cmd_list_channels(int domain, struct ltt_session *session, + struct lttng_channel **channels) +{ + int ret; + ssize_t nb_chan = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + nb_chan = session->kernel_session->channel_count; + } + DBG3("Number of kernel channels %zd", nb_chan); + break; + case LTTNG_DOMAIN_UST: + if (session->ust_session != NULL) { + nb_chan = lttng_ht_get_count( + session->ust_session->domain_global.channels); + } + DBG3("Number of UST global channels %zd", nb_chan); + break; + default: + *channels = NULL; + ret = -LTTCOMM_UND; + goto error; + } + + if (nb_chan > 0) { + *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); + if (*channels == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + list_lttng_channels(domain, session, *channels); + } else { + *channels = NULL; + } + + return nb_chan; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_EVENTS processed by the client thread. + */ +static ssize_t cmd_list_events(int domain, struct ltt_session *session, + char *channel_name, struct lttng_event **events) +{ + int ret = 0; + ssize_t nb_event = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + nb_event = list_lttng_kernel_events(channel_name, + session->kernel_session, events); + } + break; + case LTTNG_DOMAIN_UST: + { + if (session->ust_session != NULL) { + nb_event = list_lttng_ust_global_events(channel_name, + &session->ust_session->domain_global, events); + } + break; + } + default: + ret = -LTTCOMM_UND; + goto error; + } + + ret = nb_event; + +error: + return ret; +} + +/* + * Command LTTNG_SET_CONSUMER_URI processed by the client thread. + */ +static int cmd_set_consumer_uri(int domain, struct ltt_session *session, + struct lttng_uri *uri) +{ + int ret; + struct ltt_kernel_session *ksess = session->kernel_session; + struct ltt_ust_session *usess = session->ust_session; + struct consumer_output *consumer; + + /* Can't enable consumer after session started. */ + if (session->enabled) { + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + /* Create consumer output if none exists */ + consumer = ksess->tmp_consumer; + if (consumer == NULL) { + consumer = consumer_copy_output(ksess->consumer); + if (consumer == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + /* Reassign new pointer */ + ksess->tmp_consumer = consumer; + } + + switch (uri->dtype) { + case LTTNG_DST_IPV4: + case LTTNG_DST_IPV6: + DBG2("Setting network URI for kernel session %s", session->name); + + /* Set URI into consumer output object */ + ret = consumer_set_network_uri(consumer, uri); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* On a new subdir, reappend the default trace dir. */ + if (strlen(uri->subdir) != 0) { + strncat(consumer->subdir, DEFAULT_KERNEL_TRACE_DIR, + sizeof(consumer->subdir)); + } + + ret = send_socket_relayd_consumer(domain, session, uri, consumer, + ksess->consumer_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + case LTTNG_DST_PATH: + 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)); + /* Append default kernel trace dir */ + strncat(consumer->dst.trace_path, DEFAULT_KERNEL_TRACE_DIR, + sizeof(consumer->dst.trace_path)); + break; + } + + /* All good! */ + break; + case LTTNG_DOMAIN_UST: + /* Code flow error if we don't have a kernel session here. */ + assert(usess); + + /* Create consumer output if none exists */ + consumer = usess->tmp_consumer; + if (consumer == NULL) { + consumer = consumer_copy_output(usess->consumer); + if (consumer == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + /* Reassign new pointer */ + usess->tmp_consumer = consumer; + } + + switch (uri->dtype) { + case LTTNG_DST_IPV4: + case LTTNG_DST_IPV6: + { + DBG2("Setting network URI for UST session %s", session->name); + + /* Set URI into consumer object */ + ret = consumer_set_network_uri(consumer, uri); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* On a new subdir, reappend the default trace dir. */ + if (strlen(uri->subdir) != 0) { + strncat(consumer->subdir, DEFAULT_UST_TRACE_DIR, + sizeof(consumer->subdir)); + } + + if (ust_consumerd64_fd >= 0) { + ret = send_socket_relayd_consumer(domain, session, uri, + consumer, ust_consumerd64_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + if (ust_consumerd32_fd >= 0) { + ret = send_socket_relayd_consumer(domain, session, uri, + consumer, ust_consumerd32_fd); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + break; + } + case LTTNG_DST_PATH: + 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)); + /* Append default UST trace dir */ + strncat(consumer->dst.trace_path, DEFAULT_UST_TRACE_DIR, + sizeof(consumer->dst.trace_path)); + break; + } + break; + } + + /* All good! */ + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_DISABLE_CONSUMER processed by the client thread. + */ +static int cmd_disable_consumer(int domain, struct ltt_session *session) +{ + int ret; + struct ltt_kernel_session *ksess = session->kernel_session; + struct ltt_ust_session *usess = session->ust_session; + struct consumer_output *consumer; + + if (session->enabled) { + /* Can't disable consumer on an already started session */ + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + DBG("Disabling kernel consumer"); + consumer = ksess->consumer; + + break; + case LTTNG_DOMAIN_UST: + /* Code flow error if we don't have a UST session here. */ + assert(usess); + + DBG("Disabling UST consumer"); + consumer = usess->consumer; + + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + assert(consumer); + consumer->enabled = 0; + + /* Success at this point */ + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_CONSUMER processed by the client thread. */ -static ssize_t cmd_list_channels(int domain, struct ltt_session *session, - struct lttng_channel **channels) +static int cmd_enable_consumer(int domain, struct ltt_session *session) { int ret; - ssize_t nb_chan = 0; + struct ltt_kernel_session *ksess = session->kernel_session; + struct ltt_ust_session *usess = session->ust_session; + struct consumer_output *tmp_out; + + /* Can't enable consumer after session started. */ + if (session->enabled) { + ret = LTTCOMM_TRACE_ALREADY_STARTED; + goto error; + } switch (domain) { case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_chan = session->kernel_session->channel_count; - } - DBG3("Number of kernel channels %zd", nb_chan); - break; - case LTTNG_DOMAIN_UST: - if (session->ust_session != NULL) { - nb_chan = lttng_ht_get_count( - session->ust_session->domain_global.channels); + /* Code flow error if we don't have a kernel session here. */ + assert(ksess); + + /* + * Check if we have already sent fds to the consumer. In that case, + * the enable-consumer command can't be used because a start trace + * had previously occured. + */ + if (ksess->consumer_fds_sent) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto error; } - DBG3("Number of UST global channels %zd", nb_chan); - break; - default: - *channels = NULL; - ret = -LTTCOMM_UND; - goto error; - } - if (nb_chan > 0) { - *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); - if (*channels == NULL) { - ret = -LTTCOMM_FATAL; + tmp_out = ksess->tmp_consumer; + if (tmp_out == NULL) { + /* No temp. consumer output exists. Using the current one. */ + DBG3("No temporary consumer. Using default"); + ret = LTTCOMM_OK; goto error; } - list_lttng_channels(domain, session, *channels); - } else { - *channels = NULL; - } + switch (tmp_out->type) { + case CONSUMER_DST_LOCAL: + DBG2("Consumer output is local. Creating directory(ies)"); - return nb_chan; + /* Create directory(ies) */ + ret = run_as_mkdir_recursive(tmp_out->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_FATAL; + goto error; + } + } + break; + case CONSUMER_DST_NET: + DBG2("Consumer output is network. Validating URIs"); + /* Validate if we have both control and data path set. */ + if (!tmp_out->dst.net.control_isset) { + ret = LTTCOMM_URI_CTRL_MISS; + goto error; + } -error: - return ret; -} + if (!tmp_out->dst.net.data_isset) { + ret = LTTCOMM_URI_DATA_MISS; + goto error; + } -/* - * Command LTTNG_LIST_EVENTS processed by the client thread. - */ -static ssize_t cmd_list_events(int domain, struct ltt_session *session, - char *channel_name, struct lttng_event **events) -{ - int ret = 0; - ssize_t nb_event = 0; + /* Check established network session state */ + if (session->net_handle == 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + ERR("Session network handle is not set on enable-consumer"); + goto error; + } - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_event = list_lttng_kernel_events(channel_name, - session->kernel_session, events); + /* Append default kernel trace dir to subdir */ + strncat(ksess->consumer->subdir, DEFAULT_KERNEL_TRACE_DIR, + sizeof(ksess->consumer->subdir)); + + break; } + + /* + * @session-lock + * This is race free for now since the session lock is acquired before + * ending up in this function. No other threads can access this kernel + * session without this lock hence freeing the consumer output object + * is valid. + */ + consumer_destroy_output(ksess->consumer); + ksess->consumer = tmp_out; + ksess->tmp_consumer = NULL; + break; case LTTNG_DOMAIN_UST: - { - if (session->ust_session != NULL) { - nb_event = list_lttng_ust_global_events(channel_name, - &session->ust_session->domain_global, events); + /* Code flow error if we don't have a UST session here. */ + assert(usess); + + /* + * Check if we have already sent fds to the consumer. In that case, + * the enable-consumer command can't be used because a start trace + * had previously occured. + */ + if (usess->start_trace) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + goto error; + } + + tmp_out = usess->tmp_consumer; + if (tmp_out == NULL) { + /* No temp. consumer output exists. Using the current one. */ + DBG3("No temporary consumer. Using default"); + ret = LTTCOMM_OK; + goto error; + } + + switch (tmp_out->type) { + case CONSUMER_DST_LOCAL: + DBG2("Consumer output is local. Creating directory(ies)"); + + /* Create directory(ies) */ + ret = run_as_mkdir_recursive(tmp_out->dst.trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_FATAL; + goto error; + } + } + break; + case CONSUMER_DST_NET: + DBG2("Consumer output is network. Validating URIs"); + /* Validate if we have both control and data path set. */ + if (!tmp_out->dst.net.control_isset) { + ret = LTTCOMM_URI_CTRL_MISS; + goto error; + } + + if (!tmp_out->dst.net.data_isset) { + ret = LTTCOMM_URI_DATA_MISS; + goto error; + } + + /* Check established network session state */ + if (session->net_handle == 0) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + DBG2("Session network handle is not set on enable-consumer"); + goto error; + } + + if (tmp_out->net_seq_index == -1) { + ret = LTTCOMM_ENABLE_CONSUMER_FAIL; + DBG2("Network index is not set on the consumer"); + goto error; + } + + /* Append default kernel trace dir to subdir */ + strncat(usess->consumer->subdir, DEFAULT_UST_TRACE_DIR, + sizeof(usess->consumer->subdir)); + + break; } + + /* + * @session-lock + * This is race free for now since the session lock is acquired before + * ending up in this function. No other threads can access this kernel + * session without this lock hence freeing the consumer output object + * is valid. + */ + consumer_destroy_output(usess->consumer); + usess->consumer = tmp_out; + usess->tmp_consumer = NULL; + break; } - default: - ret = -LTTCOMM_UND; - goto error; - } - ret = nb_event; + /* Success at this point */ + ret = LTTCOMM_OK; error: return ret; @@ -3121,8 +3969,11 @@ error: * is set and ready for transmission before returning. * * Return any error encountered or 0 for success. + * + * "sock" is only used for special-case var. len data. */ -static int process_client_msg(struct command_ctx *cmd_ctx) +static int process_client_msg(struct command_ctx *cmd_ctx, int sock, + int *sock_error) { int ret = LTTCOMM_OK; int need_tracing_session = 1; @@ -3130,8 +3981,11 @@ static int process_client_msg(struct command_ctx *cmd_ctx) DBG("Processing client command %d", cmd_ctx->lsm->cmd_type); + *sock_error = 0; + switch (cmd_ctx->lsm->cmd_type) { case LTTNG_CREATE_SESSION: + case LTTNG_CREATE_SESSION_URI: case LTTNG_DESTROY_SESSION: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_DOMAINS: @@ -3145,7 +3999,11 @@ static int process_client_msg(struct command_ctx *cmd_ctx) if (opt_no_kernel && need_domain && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) { - ret = LTTCOMM_KERN_NA; + if (!is_root) { + ret = LTTCOMM_NEED_ROOT_SESSIOND; + } else { + ret = LTTCOMM_KERN_NA; + } goto error; } @@ -3157,6 +4015,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) switch(cmd_ctx->lsm->cmd_type) { case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: + case LTTNG_LIST_TRACEPOINT_FIELDS: case LTTNG_LIST_DOMAINS: case LTTNG_LIST_CHANNELS: case LTTNG_LIST_EVENTS: @@ -3173,16 +4032,22 @@ static int process_client_msg(struct command_ctx *cmd_ctx) /* Commands that DO NOT need a session. */ switch (cmd_ctx->lsm->cmd_type) { case LTTNG_CREATE_SESSION: + case LTTNG_CREATE_SESSION_URI: case LTTNG_CALIBRATE: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: + case LTTNG_LIST_TRACEPOINT_FIELDS: need_tracing_session = 0; break; default: DBG("Getting session %s by name", cmd_ctx->lsm->session.name); + /* + * We keep the session list lock across _all_ commands + * for now, because the per-session lock does not + * handle teardown properly. + */ session_lock_list(); cmd_ctx->session = session_find_by_name(cmd_ctx->lsm->session.name); - session_unlock_list(); if (cmd_ctx->session == NULL) { if (cmd_ctx->lsm->session.name != NULL) { ret = LTTCOMM_SESS_NOT_FOUND; @@ -3207,12 +4072,12 @@ static int process_client_msg(struct command_ctx *cmd_ctx) switch (cmd_ctx->lsm->domain.type) { case LTTNG_DOMAIN_KERNEL: if (!is_root) { - ret = LTTCOMM_KERN_NA; + ret = LTTCOMM_NEED_ROOT_SESSIOND; goto error; } /* Kernel tracer check */ - if (kernel_tracer_fd == 0) { + if (kernel_tracer_fd == -1) { /* Basically, load kernel tracer modules */ ret = init_kernel_tracer(); if (ret != 0) { @@ -3220,6 +4085,12 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } } + /* Consumer is in an ERROR state. Report back to client */ + if (uatomic_read(&kernel_consumerd_state) == CONSUMER_ERROR) { + ret = LTTCOMM_NO_KERNCONSUMERD; + goto error; + } + /* Need a session for kernel command */ if (need_tracing_session) { if (cmd_ctx->session->kernel_session == NULL) { @@ -3240,13 +4111,25 @@ static int process_client_msg(struct command_ctx *cmd_ctx) ret = LTTCOMM_KERN_CONSUMER_FAIL; goto error; } + uatomic_set(&kernel_consumerd_state, CONSUMER_STARTED); + + /* Set consumer fd of the session */ + cmd_ctx->session->kernel_session->consumer_fd = + kconsumer_data.cmd_sock; } else { pthread_mutex_unlock(&kconsumer_data.pid_mutex); } } + break; case LTTNG_DOMAIN_UST: { + /* Consumer is in an ERROR state. Report back to client */ + if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { + ret = LTTCOMM_NO_USTCONSUMERD; + goto error; + } + if (need_tracing_session) { if (cmd_ctx->session->ust_session == NULL) { ret = create_ust_session(cmd_ctx->session, @@ -3255,6 +4138,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) goto error; } } + /* Start the UST consumer daemons */ /* 64-bit */ pthread_mutex_lock(&ustconsumer64_data.pid_mutex); @@ -3270,6 +4154,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } ust_consumerd64_fd = ustconsumer64_data.cmd_sock; + uatomic_set(&ust_consumerd_state, CONSUMER_STARTED); } else { pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); } @@ -3284,7 +4169,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx) ust_consumerd32_fd = -EINVAL; goto error; } + ust_consumerd32_fd = ustconsumer32_data.cmd_sock; + uatomic_set(&ust_consumerd_state, CONSUMER_STARTED); } else { pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); } @@ -3296,13 +4183,33 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } skip_domain: + /* Validate consumer daemon state when start/stop trace command */ + if (cmd_ctx->lsm->cmd_type == LTTNG_START_TRACE || + cmd_ctx->lsm->cmd_type == LTTNG_STOP_TRACE) { + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_UST: + if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) { + ret = LTTCOMM_NO_USTCONSUMERD; + goto error; + } + break; + case LTTNG_DOMAIN_KERNEL: + if (uatomic_read(&kernel_consumerd_state) != CONSUMER_STARTED) { + ret = LTTCOMM_NO_KERNCONSUMERD; + goto error; + } + break; + } + } + /* * Check that the UID or GID match that of the tracing session. * The root user can interact with all sessions. */ if (need_tracing_session) { if (!session_access_ok(cmd_ctx->session, - cmd_ctx->creds.uid, cmd_ctx->creds.gid)) { + LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), + LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds))) { ret = LTTCOMM_EPERM; goto error; } @@ -3339,12 +4246,22 @@ skip_domain: cmd_ctx->lsm->u.disable.channel_name); break; } + case LTTNG_DISABLE_CONSUMER: + { + ret = cmd_disable_consumer(cmd_ctx->lsm->domain.type, cmd_ctx->session); + break; + } case LTTNG_ENABLE_CHANNEL: { ret = cmd_enable_channel(cmd_ctx->session, cmd_ctx->lsm->domain.type, &cmd_ctx->lsm->u.channel.chan); break; } + case LTTNG_ENABLE_CONSUMER: + { + ret = cmd_enable_consumer(cmd_ctx->lsm->domain.type, cmd_ctx->session); + break; + } case LTTNG_ENABLE_EVENT: { ret = cmd_enable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, @@ -3391,6 +4308,42 @@ skip_domain: ret = LTTCOMM_OK; break; } + case LTTNG_LIST_TRACEPOINT_FIELDS: + { + struct lttng_event_field *fields; + ssize_t nb_fields; + + nb_fields = cmd_list_tracepoint_fields(cmd_ctx->lsm->domain.type, &fields); + if (nb_fields < 0) { + ret = -nb_fields; + goto error; + } + + /* + * Setup lttng message with payload size set to the event list size in + * bytes and then copy list into the llm payload. + */ + ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_event_field) * nb_fields); + if (ret < 0) { + free(fields); + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, fields, + sizeof(struct lttng_event_field) * nb_fields); + + free(fields); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_SET_CONSUMER_URI: + { + ret = cmd_set_consumer_uri(cmd_ctx->lsm->domain.type, cmd_ctx->session, + &cmd_ctx->lsm->u.uri); + break; + } case LTTNG_START_TRACE: { ret = cmd_start_trace(cmd_ctx->session); @@ -3407,10 +4360,23 @@ skip_domain: cmd_ctx->lsm->session.path, &cmd_ctx->creds); break; } + case LTTNG_CREATE_SESSION_URI: + { + ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, + &cmd_ctx->lsm->u.create_uri.ctrl_uri, + &cmd_ctx->lsm->u.create_uri.data_uri, + cmd_ctx->lsm->u.create_uri.enable_consumer, &cmd_ctx->creds); + break; + } case LTTNG_DESTROY_SESSION: { ret = cmd_destroy_session(cmd_ctx->session, cmd_ctx->lsm->session.name); + /* + * Set session to NULL so we do not unlock it after + * free. + */ + cmd_ctx->session = NULL; break; } case LTTNG_LIST_DOMAINS: @@ -3440,7 +4406,7 @@ skip_domain: } case LTTNG_LIST_CHANNELS: { - size_t nb_chan; + int nb_chan; struct lttng_channel *channels; nb_chan = cmd_list_channels(cmd_ctx->lsm->domain.type, @@ -3495,7 +4461,9 @@ skip_domain: unsigned int nr_sessions; session_lock_list(); - nr_sessions = lttng_sessions_count(cmd_ctx->creds.uid, cmd_ctx->creds.gid); + nr_sessions = lttng_sessions_count( + LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), + LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)); ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * nr_sessions); if (ret < 0) { @@ -3505,7 +4473,8 @@ skip_domain: /* Filled the session array */ list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload), - cmd_ctx->creds.uid, cmd_ctx->creds.gid); + LTTNG_SOCK_GET_UID_CRED(&cmd_ctx->creds), + LTTNG_SOCK_GET_GID_CRED(&cmd_ctx->creds)); session_unlock_list(); @@ -3524,6 +4493,43 @@ skip_domain: cmd_ctx->lsm->u.reg.path); break; } + case LTTNG_SET_FILTER: + { + struct lttng_filter_bytecode *bytecode; + + if (cmd_ctx->lsm->u.filter.bytecode_len > 65336) { + ret = LTTCOMM_FILTER_INVAL; + goto error; + } + bytecode = zmalloc(cmd_ctx->lsm->u.filter.bytecode_len); + if (!bytecode) { + ret = LTTCOMM_FILTER_NOMEM; + goto error; + } + /* Receive var. len. data */ + DBG("Receiving var len data from client ..."); + ret = lttcomm_recv_unix_sock(sock, bytecode, + cmd_ctx->lsm->u.filter.bytecode_len); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + ret = LTTCOMM_FILTER_INVAL; + goto error; + } + + if (bytecode->len + sizeof(*bytecode) + != cmd_ctx->lsm->u.filter.bytecode_len) { + free(bytecode); + ret = LTTCOMM_FILTER_INVAL; + goto error; + } + + ret = cmd_set_filter(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.filter.channel_name, + cmd_ctx->lsm->u.filter.event_name, + bytecode); + break; + } default: ret = LTTCOMM_UND; break; @@ -3542,6 +4548,9 @@ setup_error: if (cmd_ctx->session) { session_unlock(cmd_ctx->session); } + if (need_tracing_session) { + session_unlock_list(); + } init_setup_error: return ret; } @@ -3552,7 +4561,8 @@ init_setup_error: */ static void *thread_manage_clients(void *data) { - int sock = 0, ret, i, pollfd; + int sock = -1, ret, i, pollfd; + int sock_error; uint32_t revents, nb_fd; struct command_ctx *cmd_ctx = NULL; struct lttng_poll_event events; @@ -3594,8 +4604,15 @@ static void *thread_manage_clients(void *data) nb_fd = LTTNG_POLL_GETNB(&events); /* Inifinite blocking call, waiting for transmission */ + restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } goto error; } @@ -3635,14 +4652,14 @@ static void *thread_manage_clients(void *data) /* Allocate context command to process the client request */ cmd_ctx = zmalloc(sizeof(struct command_ctx)); if (cmd_ctx == NULL) { - perror("zmalloc cmd_ctx"); + PERROR("zmalloc cmd_ctx"); goto error; } /* Allocate data buffer for reception */ cmd_ctx->lsm = zmalloc(sizeof(struct lttcomm_session_msg)); if (cmd_ctx->lsm == NULL) { - perror("zmalloc cmd_ctx->lsm"); + PERROR("zmalloc cmd_ctx->lsm"); goto error; } @@ -3659,8 +4676,12 @@ static void *thread_manage_clients(void *data) sizeof(struct lttcomm_session_msg), &cmd_ctx->creds); if (ret <= 0) { DBG("Nothing recv() from client... continuing"); - close(sock); - free(cmd_ctx); + ret = close(sock); + if (ret) { + PERROR("close"); + } + sock = -1; + clean_command_ctx(&cmd_ctx); continue; } @@ -3674,13 +4695,22 @@ static void *thread_manage_clients(void *data) * informations for the client. The command context struct contains * everything this function may needs. */ - ret = process_client_msg(cmd_ctx); + ret = process_client_msg(cmd_ctx, sock, &sock_error); rcu_thread_offline(); if (ret < 0) { + if (sock_error) { + ret = close(sock); + if (ret) { + PERROR("close"); + } + sock = -1; + } /* * TODO: Inform client somehow of the fatal error. At * this point, ret < 0 means that a zmalloc failed - * (ENOMEM). Error detected but still accept command. + * (ENOMEM). Error detected but still accept + * command, unless a socket error has been + * detected. */ clean_command_ctx(&cmd_ctx); continue; @@ -3695,7 +4725,11 @@ static void *thread_manage_clients(void *data) } /* End of transmission */ - close(sock); + ret = close(sock); + if (ret) { + PERROR("close"); + } + sock = -1; clean_command_ctx(&cmd_ctx); } @@ -3703,8 +4737,18 @@ static void *thread_manage_clients(void *data) error: DBG("Client thread dying"); unlink(client_unix_sock_path); - close(client_sock); - close(sock); + if (client_sock >= 0) { + ret = close(client_sock); + if (ret) { + PERROR("close"); + } + } + if (sock >= 0) { + ret = close(sock); + if (ret) { + PERROR("close"); + } + } lttng_poll_clean(&events); clean_command_ctx(&cmd_ctx); @@ -3833,11 +4877,11 @@ static int parse_args(int argc, char **argv) opt_no_kernel = 1; break; case 'q': - opt_quiet = 1; + lttng_opt_quiet = 1; break; case 'v': /* Verbose level can increase using multiple -v */ - opt_verbose += 1; + lttng_opt_verbose += 1; break; case 'Z': opt_verbose_consumer += 1; @@ -3888,7 +4932,7 @@ static int init_daemon_socket(void) ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (ret < 0) { ERR("Set file permissions failed: %s", client_unix_sock_path); - perror("chmod"); + PERROR("chmod"); goto end; } @@ -3905,7 +4949,7 @@ static int init_daemon_socket(void) S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (ret < 0) { ERR("Set file permissions failed: %s", apps_unix_sock_path); - perror("chmod"); + PERROR("chmod"); goto end; } @@ -3939,53 +4983,55 @@ static int set_permissions(char *rundir) int ret; gid_t gid; - gid = allowed_group(); - if (gid < 0) { + ret = allowed_group(); + if (ret < 0) { WARN("No tracing group detected"); ret = 0; goto end; } + gid = ret; + /* Set lttng run dir */ ret = chown(rundir, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", rundir); - perror("chown"); + PERROR("chown"); } /* Ensure tracing group can search the run dir */ - ret = chmod(rundir, S_IRWXU | S_IXGRP); + ret = chmod(rundir, S_IRWXU | S_IXGRP | S_IXOTH); if (ret < 0) { ERR("Unable to set permissions on %s", rundir); - perror("chmod"); + PERROR("chmod"); } /* lttng client socket path */ ret = chown(client_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", client_unix_sock_path); - perror("chown"); + PERROR("chown"); } /* kconsumer error socket path */ ret = chown(kconsumer_data.err_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", kconsumer_data.err_unix_sock_path); - perror("chown"); + PERROR("chown"); } /* 64-bit ustconsumer error socket path */ ret = chown(ustconsumer64_data.err_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", ustconsumer64_data.err_unix_sock_path); - perror("chown"); + PERROR("chown"); } /* 32-bit ustconsumer compat32 error socket path */ ret = chown(ustconsumer32_data.err_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", ustconsumer32_data.err_unix_sock_path); - perror("chown"); + PERROR("chown"); } DBG("All permissions are set"); @@ -3994,22 +5040,6 @@ end: return ret; } -/* - * Create the pipe used to wake up the kernel thread. - */ -static int create_kernel_poll_pipe(void) -{ - return pipe2(kernel_poll_pipe, O_CLOEXEC); -} - -/* - * Create the application command pipe to wake thread_manage_apps. - */ -static int create_apps_cmd_pipe(void) -{ - return pipe2(apps_cmd_pipe, O_CLOEXEC); -} - /* * Create the lttng run directory needed for all global sockets and pipe. */ @@ -4064,10 +5094,11 @@ static int set_consumer_sockets(struct consumer_data *consumer_data, ret = mkdir(path, S_IRWXU); if (ret < 0) { if (errno != EEXIST) { + PERROR("mkdir"); ERR("Failed to create %s", path); goto error; } - ret = 0; + ret = -1; } /* Create the kconsumerd error unix socket */ @@ -4128,7 +5159,7 @@ static int set_signal_handler(void) sigset_t sigset; if ((ret = sigemptyset(&sigset)) < 0) { - perror("sigemptyset"); + PERROR("sigemptyset"); return ret; } @@ -4136,17 +5167,17 @@ static int set_signal_handler(void) sa.sa_mask = sigset; sa.sa_flags = 0; if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } @@ -4170,7 +5201,7 @@ static void set_ulimit(void) ret = setrlimit(RLIMIT_NOFILE, &lim); if (ret < 0) { - perror("failed to set open files limit"); + PERROR("failed to set open files limit"); } } @@ -4187,11 +5218,6 @@ int main(int argc, char **argv) rcu_register_thread(); - /* Create thread quit pipe */ - if ((ret = init_thread_quit_pipe()) < 0) { - goto error; - } - setup_consumerd_path(); /* Parse arguments */ @@ -4202,11 +5228,31 @@ int main(int argc, char **argv) /* Daemonize */ if (opt_daemon) { + int i; + + /* + * fork + * child: setsid, close FD 0, 1, 2, chdir / + * parent: exit (if fork is successful) + */ ret = daemon(0, 0); if (ret < 0) { - perror("daemon"); + PERROR("daemon"); goto error; } + /* + * We are in the child. Make sure all other file + * descriptors are closed, in case we are called with + * more opened file descriptors than the standard ones. + */ + for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { + (void) close(i); + } + } + + /* Create thread quit pipe */ + if ((ret = init_thread_quit_pipe()) < 0) { + goto error; } /* Check if daemon is UID = 0 */ @@ -4289,6 +5335,10 @@ int main(int argc, char **argv) } } + /* Set consumer initial state */ + kernel_consumerd_state = CONSUMER_STOPPED; + ust_consumerd_state = CONSUMER_STOPPED; + DBG("Client socket path %s", client_unix_sock_path); DBG("Application socket path %s", apps_unix_sock_path); DBG("LTTng run directory path: %s", rundir); @@ -4327,6 +5377,12 @@ int main(int argc, char **argv) goto error; } + /* + * Init UST app hash table. Alloc hash table before this point since + * cleanup() can get called after that point. + */ + ust_app_ht_alloc(); + /* After this point, we can safely call cleanup() with "goto exit" */ /* @@ -4349,6 +5405,8 @@ int main(int argc, char **argv) /* Set ulimit for open files */ set_ulimit(); } + /* init lttng_fd tracking must be done after set_ulimit. */ + lttng_fd_init(); ret = set_consumer_sockets(&ustconsumer64_data, rundir); if (ret < 0) { @@ -4380,21 +5438,18 @@ int main(int argc, char **argv) } /* Setup the kernel pipe for waking up the kernel thread */ - if ((ret = create_kernel_poll_pipe()) < 0) { + if ((ret = utils_create_pipe_cloexec(kernel_poll_pipe)) < 0) { goto exit; } /* Setup the thread apps communication pipe. */ - if ((ret = create_apps_cmd_pipe()) < 0) { + if ((ret = utils_create_pipe_cloexec(apps_cmd_pipe)) < 0) { goto exit; } /* Init UST command queue. */ cds_wfq_init(&ust_cmd_queue.queue); - /* Init UST app hash table */ - ust_app_ht_alloc(); - /* * Get session list pointer. This pointer MUST NOT be free(). This list is * statically declared in session.c @@ -4404,11 +5459,17 @@ int main(int argc, char **argv) /* Set up max poll set size */ lttng_poll_set_max_size(); + /* + * Set network sequence index to 1 for streams to match a relayd socket on + * the consumer side. + */ + uatomic_set(&relayd_net_seq_idx, 1); + /* Create thread to manage the client socket */ ret = pthread_create(&client_thread, NULL, thread_manage_clients, (void *) NULL); if (ret != 0) { - perror("pthread_create clients"); + PERROR("pthread_create clients"); goto exit_client; } @@ -4416,7 +5477,7 @@ int main(int argc, char **argv) ret = pthread_create(&dispatch_thread, NULL, thread_dispatch_ust_registration, (void *) NULL); if (ret != 0) { - perror("pthread_create dispatch"); + PERROR("pthread_create dispatch"); goto exit_dispatch; } @@ -4424,7 +5485,7 @@ int main(int argc, char **argv) ret = pthread_create(®_apps_thread, NULL, thread_registration_apps, (void *) NULL); if (ret != 0) { - perror("pthread_create registration"); + PERROR("pthread_create registration"); goto exit_reg_apps; } @@ -4432,7 +5493,7 @@ int main(int argc, char **argv) ret = pthread_create(&apps_thread, NULL, thread_manage_apps, (void *) NULL); if (ret != 0) { - perror("pthread_create apps"); + PERROR("pthread_create apps"); goto exit_apps; } @@ -4440,47 +5501,47 @@ int main(int argc, char **argv) ret = pthread_create(&kernel_thread, NULL, thread_manage_kernel, (void *) NULL); if (ret != 0) { - perror("pthread_create kernel"); + PERROR("pthread_create kernel"); goto exit_kernel; } ret = pthread_join(kernel_thread, &status); if (ret != 0) { - perror("pthread_join"); + PERROR("pthread_join"); goto error; /* join error, exit without cleanup */ } exit_kernel: ret = pthread_join(apps_thread, &status); if (ret != 0) { - perror("pthread_join"); + PERROR("pthread_join"); goto error; /* join error, exit without cleanup */ } exit_apps: ret = pthread_join(reg_apps_thread, &status); if (ret != 0) { - perror("pthread_join"); + PERROR("pthread_join"); goto error; /* join error, exit without cleanup */ } exit_reg_apps: ret = pthread_join(dispatch_thread, &status); if (ret != 0) { - perror("pthread_join"); + PERROR("pthread_join"); goto error; /* join error, exit without cleanup */ } exit_dispatch: ret = pthread_join(client_thread, &status); if (ret != 0) { - perror("pthread_join"); + PERROR("pthread_join"); goto error; /* join error, exit without cleanup */ } ret = join_consumer_thread(&kconsumer_data); if (ret != 0) { - perror("join_consumer"); + PERROR("join_consumer"); goto error; /* join error, exit without cleanup */ }