X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=550aa1b83f68d65f01e936d1e81fb5308b0b2b4e;hp=9527371380e02c45011ad60a0f806872e194a861;hb=55d097957f5bb8138959ad2202a40d85d49f029e;hpb=12744796179ed3bfa9c3b6e22bfdeabf2e6081bf diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 952737138..550aa1b83 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -60,7 +60,7 @@ #include "ust-consumer.h" #include "utils.h" #include "fd-limit.h" -#include "health.h" +#include "health-sessiond.h" #include "testpoint.h" #include "ust-thread.h" @@ -90,7 +90,6 @@ static struct consumer_data kconsumer_data = { .cmd_unix_sock_path = DEFAULT_KCONSUMERD_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, - .metadata_sock.fd = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -102,7 +101,6 @@ static struct consumer_data ustconsumer64_data = { .cmd_unix_sock_path = DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, - .metadata_sock.fd = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -114,7 +112,6 @@ static struct consumer_data ustconsumer32_data = { .cmd_unix_sock_path = DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, .err_sock = -1, .cmd_sock = -1, - .metadata_sock.fd = -1, .pid_mutex = PTHREAD_MUTEX_INITIALIZER, .lock = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER, @@ -161,6 +158,7 @@ static pthread_t client_thread; static pthread_t kernel_thread; static pthread_t dispatch_thread; static pthread_t health_thread; +static pthread_t ht_cleanup_thread; /* * UST registration command queue. This queue is tied with a futex and uses a N @@ -235,6 +233,9 @@ static int app_socket_timeout; /* Set in main() with the current page size. */ long page_size; +/* Application health monitoring */ +struct health_app *health_sessiond; + static void setup_consumerd_path(void) { @@ -389,6 +390,51 @@ static void stop_threads(void) futex_nto1_wake(&ust_cmd_queue.futex); } +/* + * Close every consumer sockets. + */ +static void close_consumer_sockets(void) +{ + int ret; + + if (kconsumer_data.err_sock >= 0) { + ret = close(kconsumer_data.err_sock); + if (ret < 0) { + PERROR("kernel consumer err_sock close"); + } + } + if (ustconsumer32_data.err_sock >= 0) { + ret = close(ustconsumer32_data.err_sock); + if (ret < 0) { + PERROR("UST consumerd32 err_sock close"); + } + } + if (ustconsumer64_data.err_sock >= 0) { + ret = close(ustconsumer64_data.err_sock); + if (ret < 0) { + PERROR("UST consumerd64 err_sock close"); + } + } + if (kconsumer_data.cmd_sock >= 0) { + ret = close(kconsumer_data.cmd_sock); + if (ret < 0) { + PERROR("kernel consumer cmd_sock close"); + } + } + if (ustconsumer32_data.cmd_sock >= 0) { + ret = close(ustconsumer32_data.cmd_sock); + if (ret < 0) { + PERROR("UST consumerd32 cmd_sock close"); + } + } + if (ustconsumer64_data.cmd_sock >= 0) { + ret = close(ustconsumer64_data.cmd_sock); + if (ret < 0) { + PERROR("UST consumerd64 cmd_sock close"); + } + } +} + /* * Cleanup the daemon */ @@ -400,7 +446,10 @@ static void cleanup(void) DBG("Cleaning up"); - /* First thing first, stop all threads */ + /* + * Close the thread quit pipe. It has already done its job, + * since we are now called. + */ utils_close_pipe(thread_quit_pipe); /* @@ -457,6 +506,8 @@ static void cleanup(void) modprobe_remove_lttng_all(); } + close_consumer_sockets(); + /* */ DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm" "Matthew, BEET driven development works!%c[%dm", @@ -627,6 +678,8 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) if (ret < 0) { goto error; } + /* Update the stream global counter */ + ksess->stream_count_global += ret; /* * Have we already sent fds to the consumer? If yes, it means @@ -640,12 +693,10 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) rcu_read_lock(); cds_lfht_for_each_entry(ksess->consumer->socks->ht, &iter.iter, socket, node.node) { - /* Code flow error */ - assert(socket->fd >= 0); - pthread_mutex_lock(socket->lock); ret = kernel_consumer_send_channel_stream(socket, - channel, ksess); + channel, ksess, + session->output_traces ? 1 : 0); pthread_mutex_unlock(socket->lock); if (ret < 0) { rcu_read_unlock(); @@ -676,6 +727,12 @@ static void update_ust_app(int app_sock) { struct ltt_session *sess, *stmp; + /* Consumer is in an ERROR state. Stop any application update. */ + if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { + /* Stop the update process since the consumer is dead. */ + return; + } + /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { session_lock(sess); @@ -701,13 +758,13 @@ static void *thread_manage_kernel(void *data) DBG("[thread] Thread manage kernel started"); - health_register(HEALTH_TYPE_KERNEL); + health_register(health_sessiond, HEALTH_TYPE_KERNEL); /* * This first step of the while is to clean this structure which could free - * non NULL pointers so zero it before the loop. + * non NULL pointers so initialize it before the loop. */ - memset(&events, 0, sizeof(events)); + lttng_poll_init(&events); if (testpoint(thread_manage_kernel)) { goto error_testpoint; @@ -826,7 +883,7 @@ error_testpoint: WARN("Kernel thread died unexpectedly. " "Kernel tracing can continue but CPU hotplug is disabled."); } - health_unregister(); + health_unregister(health_sessiond); DBG("Kernel thread dying"); return NULL; } @@ -867,7 +924,7 @@ static void *thread_manage_consumer(void *data) DBG("[thread] Manage consumer started"); - health_register(HEALTH_TYPE_CONSUMER); + health_register(health_sessiond, HEALTH_TYPE_CONSUMER); health_code_update(); @@ -965,15 +1022,16 @@ restart: /* Connect both socket, command and metadata. */ consumer_data->cmd_sock = lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); - consumer_data->metadata_sock.fd = + consumer_data->metadata_fd = lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); - if (consumer_data->cmd_sock < 0 || - consumer_data->metadata_sock.fd < 0) { + if (consumer_data->cmd_sock < 0 + || consumer_data->metadata_fd < 0) { PERROR("consumer connect cmd socket"); /* On error, signal condition and quit. */ signal_consumer_condition(consumer_data, -1); goto error; } + consumer_data->metadata_sock.fd_ptr = &consumer_data->metadata_fd; /* Create metadata socket lock. */ consumer_data->metadata_sock.lock = zmalloc(sizeof(pthread_mutex_t)); if (consumer_data->metadata_sock.lock == NULL) { @@ -986,7 +1044,7 @@ restart: signal_consumer_condition(consumer_data, 1); DBG("Consumer command socket ready (fd: %d", consumer_data->cmd_sock); DBG("Consumer metadata socket ready (fd: %d)", - consumer_data->metadata_sock.fd); + consumer_data->metadata_fd); } else { ERR("consumer error when waiting for SOCK_READY : %s", lttcomm_get_readable_code(-code)); @@ -1006,7 +1064,7 @@ restart: } /* Add metadata socket that is successfully connected. */ - ret = lttng_poll_add(&events, consumer_data->metadata_sock.fd, + ret = lttng_poll_add(&events, consumer_data->metadata_fd, LPOLLIN | LPOLLRDHUP); if (ret < 0) { goto error; @@ -1065,7 +1123,7 @@ restart_poll: lttcomm_get_readable_code(-code)); goto exit; - } else if (pollfd == consumer_data->metadata_sock.fd) { + } else if (pollfd == consumer_data->metadata_fd) { /* UST metadata requests */ ret = ust_consumer_metadata_request( &consumer_data->metadata_sock); @@ -1084,6 +1142,13 @@ restart_poll: exit: error: + /* + * We lock here because we are about to close the sockets and some other + * thread might be using them so get exclusive access which will abort all + * other consumer command by other threads. + */ + pthread_mutex_lock(&consumer_data->lock); + /* Immediately set the consumerd state to stopped */ if (consumer_data->type == LTTNG_CONSUMER_KERNEL) { uatomic_set(&kernel_consumerd_state, CONSUMER_ERROR); @@ -1100,22 +1165,21 @@ error: if (ret) { PERROR("close"); } + consumer_data->err_sock = -1; } if (consumer_data->cmd_sock >= 0) { ret = close(consumer_data->cmd_sock); if (ret) { PERROR("close"); } + consumer_data->cmd_sock = -1; } - if (consumer_data->metadata_sock.fd >= 0) { - ret = close(consumer_data->metadata_sock.fd); + if (*consumer_data->metadata_sock.fd_ptr >= 0) { + ret = close(*consumer_data->metadata_sock.fd_ptr); if (ret) { PERROR("close"); } } - /* Cleanup metadata socket mutex. */ - pthread_mutex_destroy(consumer_data->metadata_sock.lock); - free(consumer_data->metadata_sock.lock); if (sock >= 0) { ret = close(sock); @@ -1127,6 +1191,11 @@ error: unlink(consumer_data->err_unix_sock_path); unlink(consumer_data->cmd_unix_sock_path); consumer_data->pid = 0; + pthread_mutex_unlock(&consumer_data->lock); + + /* Cleanup metadata socket mutex. */ + pthread_mutex_destroy(consumer_data->metadata_sock.lock); + free(consumer_data->metadata_sock.lock); lttng_poll_clean(&events); error_poll: @@ -1134,7 +1203,7 @@ error_poll: health_error(); ERR("Health error occurred in %s", __func__); } - health_unregister(); + health_unregister(health_sessiond); DBG("consumer thread cleanup completed"); return NULL; @@ -1154,7 +1223,7 @@ static void *thread_manage_apps(void *data) rcu_register_thread(); rcu_thread_online(); - health_register(HEALTH_TYPE_APP_MANAGE); + health_register(health_sessiond, HEALTH_TYPE_APP_MANAGE); if (testpoint(thread_manage_apps)) { goto error_testpoint; @@ -1242,11 +1311,17 @@ static void *thread_manage_apps(void *data) goto error; } - /* Set socket timeout for both receiving and ending */ + /* + * Set socket timeout for both receiving and ending. + * app_socket_timeout is in seconds, whereas + * lttcomm_setsockopt_rcv_timeout and + * lttcomm_setsockopt_snd_timeout expect msec as + * parameter. + */ (void) lttcomm_setsockopt_rcv_timeout(sock, - app_socket_timeout); + app_socket_timeout * 1000); (void) lttcomm_setsockopt_snd_timeout(sock, - app_socket_timeout); + app_socket_timeout * 1000); DBG("Apps with sock %d added to poll set", sock); @@ -1294,7 +1369,7 @@ error_testpoint: health_error(); ERR("Health error occurred in %s", __func__); } - health_unregister(); + health_unregister(health_sessiond); DBG("Application communication apps thread cleanup complete"); rcu_thread_offline(); rcu_unregister_thread(); @@ -1305,6 +1380,9 @@ error_testpoint: * Send a socket to a thread This is called from the dispatch UST registration * thread once all sockets are set for the application. * + * The sock value can be invalid, we don't really care, the thread will handle + * it and make the necessary cleanup if so. + * * On success, return 0 else a negative value being the errno message of the * write(). */ @@ -1312,9 +1390,14 @@ static int send_socket_to_thread(int fd, int sock) { int ret; - /* Sockets MUST be set or else this should not have been called. */ - assert(fd >= 0); - assert(sock >= 0); + /* + * It's possible that the FD is set as invalid with -1 concurrently just + * before calling this function being a shutdown state of the thread. + */ + if (fd < 0) { + ret = -EBADF; + goto error; + } do { ret = write(fd, &sock, sizeof(sock)); @@ -1333,25 +1416,116 @@ error: return ret; } +/* + * Sanitize the wait queue of the dispatch registration thread meaning removing + * invalid nodes from it. This is to avoid memory leaks for the case the UST + * notify socket is never received. + */ +static void sanitize_wait_queue(struct ust_reg_wait_queue *wait_queue) +{ + int ret, nb_fd = 0, i; + unsigned int fd_added = 0; + struct lttng_poll_event events; + struct ust_reg_wait_node *wait_node = NULL, *tmp_wait_node; + + assert(wait_queue); + + lttng_poll_init(&events); + + /* Just skip everything for an empty queue. */ + if (!wait_queue->count) { + goto end; + } + + ret = lttng_poll_create(&events, wait_queue->count, LTTNG_CLOEXEC); + if (ret < 0) { + goto error_create; + } + + cds_list_for_each_entry_safe(wait_node, tmp_wait_node, + &wait_queue->head, head) { + assert(wait_node->app); + ret = lttng_poll_add(&events, wait_node->app->sock, + LPOLLHUP | LPOLLERR); + if (ret < 0) { + goto error; + } + + fd_added = 1; + } + + if (!fd_added) { + goto end; + } + + /* + * Poll but don't block so we can quickly identify the faulty events and + * clean them afterwards from the wait queue. + */ + ret = lttng_poll_wait(&events, 0); + if (ret < 0) { + goto error; + } + nb_fd = ret; + + for (i = 0; i < nb_fd; i++) { + /* Get faulty FD. */ + uint32_t revents = LTTNG_POLL_GETEV(&events, i); + int pollfd = LTTNG_POLL_GETFD(&events, i); + + cds_list_for_each_entry_safe(wait_node, tmp_wait_node, + &wait_queue->head, head) { + if (pollfd == wait_node->app->sock && + (revents & (LPOLLHUP | LPOLLERR))) { + cds_list_del(&wait_node->head); + wait_queue->count--; + ust_app_destroy(wait_node->app); + free(wait_node); + break; + } + } + } + + if (nb_fd > 0) { + DBG("Wait queue sanitized, %d node were cleaned up", nb_fd); + } + +end: + lttng_poll_clean(&events); + return; + +error: + lttng_poll_clean(&events); +error_create: + ERR("Unable to sanitize wait queue"); + return; +} + /* * Dispatch request from the registration threads to the application * communication thread. */ static void *thread_dispatch_ust_registration(void *data) { - int ret; + int ret, err = -1; struct cds_wfq_node *node; struct ust_command *ust_cmd = NULL; - struct { - struct ust_app *app; - struct cds_list_head head; - } *wait_node = NULL, *tmp_wait_node; + struct ust_reg_wait_node *wait_node = NULL, *tmp_wait_node; + struct ust_reg_wait_queue wait_queue = { + .count = 0, + }; + + health_register(health_sessiond, HEALTH_TYPE_APP_REG_DISPATCH); + + health_code_update(); - CDS_LIST_HEAD(wait_queue); + CDS_INIT_LIST_HEAD(&wait_queue.head); DBG("[thread] Dispatch UST command started"); while (!CMM_LOAD_SHARED(dispatch_thread_exit)) { + health_code_update(); + /* Atomically prepare the queue futex */ futex_nto1_prepare(&ust_cmd_queue.futex); @@ -1359,6 +1533,14 @@ static void *thread_dispatch_ust_registration(void *data) struct ust_app *app = NULL; ust_cmd = NULL; + /* + * Make sure we don't have node(s) that have hung up before receiving + * the notify socket. This is to clean the list in order to avoid + * memory leaks from notify socket that are never seen. + */ + sanitize_wait_queue(&wait_queue); + + health_code_update(); /* Dequeue command for registration */ node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); if (node == NULL) { @@ -1380,6 +1562,11 @@ static void *thread_dispatch_ust_registration(void *data) wait_node = zmalloc(sizeof(*wait_node)); if (!wait_node) { PERROR("zmalloc wait_node dispatch"); + ret = close(ust_cmd->sock); + if (ret < 0) { + PERROR("close ust sock dispatch %d", ust_cmd->sock); + } + lttng_fd_put(1, LTTNG_FD_APPS); free(ust_cmd); goto error; } @@ -1402,7 +1589,8 @@ static void *thread_dispatch_ust_registration(void *data) * Add application to the wait queue so we can set the notify * socket before putting this object in the global ht. */ - cds_list_add(&wait_node->head, &wait_queue); + cds_list_add(&wait_node->head, &wait_queue.head); + wait_queue.count++; free(ust_cmd); /* @@ -1417,16 +1605,31 @@ static void *thread_dispatch_ust_registration(void *data) * notify socket if found. */ cds_list_for_each_entry_safe(wait_node, tmp_wait_node, - &wait_queue, head) { + &wait_queue.head, head) { + health_code_update(); if (wait_node->app->pid == ust_cmd->reg_msg.pid) { wait_node->app->notify_sock = ust_cmd->sock; cds_list_del(&wait_node->head); + wait_queue.count--; app = wait_node->app; free(wait_node); DBG3("UST app notify socket %d is set", ust_cmd->sock); break; } } + + /* + * With no application at this stage the received socket is + * basically useless so close it before we free the cmd data + * structure for good. + */ + if (!app) { + ret = close(ust_cmd->sock); + if (ret < 0) { + PERROR("close ust sock dispatch %d", ust_cmd->sock); + } + lttng_fd_put(1, LTTNG_FD_APPS); + } free(ust_cmd); } @@ -1457,7 +1660,12 @@ static void *thread_dispatch_ust_registration(void *data) if (ret < 0) { rcu_read_unlock(); session_unlock_list(); - /* No notify thread, stop the UST tracing. */ + /* + * No notify thread, stop the UST tracing. However, this is + * not an internal error of the this thread thus setting + * the health error code to a normal exit. + */ + err = 0; goto error; } @@ -1482,35 +1690,43 @@ static void *thread_dispatch_ust_registration(void *data) if (ret < 0) { rcu_read_unlock(); session_unlock_list(); - /* No apps. thread, stop the UST tracing. */ + /* + * No apps. thread, stop the UST tracing. However, this is + * not an internal error of the this thread thus setting + * the health error code to a normal exit. + */ + err = 0; goto error; } rcu_read_unlock(); session_unlock_list(); - } else { - /* Application manager threads are not available. */ - ret = close(ust_cmd->sock); - if (ret < 0) { - PERROR("close ust_cmd sock"); - } - lttng_fd_put(1, LTTNG_FD_APPS); } } while (node != NULL); + health_poll_entry(); /* Futex wait on queue. Blocking call on futex() */ futex_nto1_wait(&ust_cmd_queue.futex); + health_poll_exit(); } + /* Normal exit, no error */ + err = 0; error: /* Clean up wait queue. */ cds_list_for_each_entry_safe(wait_node, tmp_wait_node, - &wait_queue, head) { + &wait_queue.head, head) { cds_list_del(&wait_node->head); + wait_queue.count--; free(wait_node); } DBG("Dispatch thread dying"); + if (err) { + health_error(); + ERR("Health error occurred in %s", __func__); + } + health_unregister(health_sessiond); return NULL; } @@ -1530,7 +1746,7 @@ static void *thread_registration_apps(void *data) DBG("[thread] Manage application registration started"); - health_register(HEALTH_TYPE_APP_REG); + health_register(health_sessiond, HEALTH_TYPE_APP_REG); if (testpoint(thread_registration_apps)) { goto error_testpoint; @@ -1710,7 +1926,7 @@ error_listen: error_create_poll: error_testpoint: DBG("UST Registration thread cleanup complete"); - health_unregister(); + health_unregister(health_sessiond); return NULL; } @@ -2087,7 +2303,7 @@ static int check_consumer_health(void) { int ret; - ret = health_check_state(HEALTH_TYPE_CONSUMER); + ret = health_check_state(health_sessiond, HEALTH_TYPE_CONSUMER); DBG3("Health consumer check %d", ret); @@ -2253,6 +2469,9 @@ static int create_ust_session(struct ltt_session *session, lus->uid = session->uid; lus->gid = session->gid; + lus->output_traces = session->output_traces; + lus->snapshot_mode = session->snapshot_mode; + lus->live_timer_interval = session->live_timer; session->ust_session = lus; /* Copy session output to the newly created UST session */ @@ -2309,6 +2528,8 @@ static int create_kernel_session(struct ltt_session *session) session->kernel_session->uid = session->uid; session->kernel_session->gid = session->gid; + session->kernel_session->output_traces = session->output_traces; + session->kernel_session->snapshot_mode = session->snapshot_mode; return LTTNG_OK; @@ -2364,12 +2585,18 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, switch (cmd_ctx->lsm->cmd_type) { case LTTNG_CREATE_SESSION: + case LTTNG_CREATE_SESSION_SNAPSHOT: + case LTTNG_CREATE_SESSION_LIVE: case LTTNG_DESTROY_SESSION: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_DOMAINS: case LTTNG_START_TRACE: case LTTNG_STOP_TRACE: case LTTNG_DATA_PENDING: + case LTTNG_SNAPSHOT_ADD_OUTPUT: + case LTTNG_SNAPSHOT_DEL_OUTPUT: + case LTTNG_SNAPSHOT_LIST_OUTPUT: + case LTTNG_SNAPSHOT_RECORD: need_domain = 0; break; default: @@ -2422,6 +2649,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, /* Commands that DO NOT need a session. */ switch (cmd_ctx->lsm->cmd_type) { case LTTNG_CREATE_SESSION: + case LTTNG_CREATE_SESSION_SNAPSHOT: + case LTTNG_CREATE_SESSION_LIVE: case LTTNG_CALIBRATE: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: @@ -2438,12 +2667,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, session_lock_list(); cmd_ctx->session = session_find_by_name(cmd_ctx->lsm->session.name); if (cmd_ctx->session == NULL) { - if (cmd_ctx->lsm->session.name != NULL) { - ret = LTTNG_ERR_SESS_NOT_FOUND; - } else { - /* If no session name specified */ - ret = LTTNG_ERR_SELECT_SESS; - } + ret = LTTNG_ERR_SESS_NOT_FOUND; goto error; } else { /* Acquire lock for the session */ @@ -2520,6 +2744,10 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; case LTTNG_DOMAIN_UST: { + if (!ust_app_supported()) { + ret = LTTNG_ERR_NO_UST; + goto error; + } /* Consumer is in an ERROR state. Report back to client */ if (uatomic_read(&ust_consumerd_state) == CONSUMER_ERROR) { ret = LTTNG_ERR_NO_USTCONSUMERD; @@ -2867,7 +3095,7 @@ skip_domain: } ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, nb_uri, - &cmd_ctx->creds); + &cmd_ctx->creds, 0); free(uris); @@ -2895,6 +3123,7 @@ skip_domain: ret = setup_lttng_msg(cmd_ctx, nb_dom * sizeof(struct lttng_domain)); if (ret < 0) { + free(domains); goto setup_error; } @@ -2922,6 +3151,7 @@ skip_domain: ret = setup_lttng_msg(cmd_ctx, nb_chan * sizeof(struct lttng_channel)); if (ret < 0) { + free(channels); goto setup_error; } @@ -2949,6 +3179,7 @@ skip_domain: ret = setup_lttng_msg(cmd_ctx, nb_event * sizeof(struct lttng_event)); if (ret < 0) { + free(events); goto setup_error; } @@ -3054,6 +3285,145 @@ skip_domain: ret = cmd_data_pending(cmd_ctx->session); break; } + case LTTNG_SNAPSHOT_ADD_OUTPUT: + { + struct lttcomm_lttng_output_id reply; + + ret = cmd_snapshot_add_output(cmd_ctx->session, + &cmd_ctx->lsm->u.snapshot_output.output, &reply.id); + if (ret != LTTNG_OK) { + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, sizeof(reply)); + if (ret < 0) { + goto setup_error; + } + + /* Copy output list into message payload */ + memcpy(cmd_ctx->llm->payload, &reply, sizeof(reply)); + ret = LTTNG_OK; + break; + } + case LTTNG_SNAPSHOT_DEL_OUTPUT: + { + ret = cmd_snapshot_del_output(cmd_ctx->session, + &cmd_ctx->lsm->u.snapshot_output.output); + break; + } + case LTTNG_SNAPSHOT_LIST_OUTPUT: + { + ssize_t nb_output; + struct lttng_snapshot_output *outputs = NULL; + + nb_output = cmd_snapshot_list_outputs(cmd_ctx->session, &outputs); + if (nb_output < 0) { + ret = -nb_output; + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, + nb_output * sizeof(struct lttng_snapshot_output)); + if (ret < 0) { + free(outputs); + goto setup_error; + } + + if (outputs) { + /* Copy output list into message payload */ + memcpy(cmd_ctx->llm->payload, outputs, + nb_output * sizeof(struct lttng_snapshot_output)); + free(outputs); + } + + ret = LTTNG_OK; + break; + } + case LTTNG_SNAPSHOT_RECORD: + { + ret = cmd_snapshot_record(cmd_ctx->session, + &cmd_ctx->lsm->u.snapshot_record.output, + cmd_ctx->lsm->u.snapshot_record.wait); + break; + } + case LTTNG_CREATE_SESSION_SNAPSHOT: + { + size_t nb_uri, len; + struct lttng_uri *uris = NULL; + + nb_uri = cmd_ctx->lsm->u.uri.size; + len = nb_uri * sizeof(struct lttng_uri); + + if (nb_uri > 0) { + uris = zmalloc(len); + if (uris == NULL) { + ret = LTTNG_ERR_FATAL; + goto error; + } + + /* Receive variable len data */ + DBG("Waiting for %zu URIs from client ...", nb_uri); + ret = lttcomm_recv_unix_sock(sock, uris, len); + if (ret <= 0) { + DBG("No URIs received from client... continuing"); + *sock_error = 1; + ret = LTTNG_ERR_SESSION_FAIL; + free(uris); + goto error; + } + + if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) { + DBG("Creating session with ONE network URI is a bad call"); + ret = LTTNG_ERR_SESSION_FAIL; + free(uris); + goto error; + } + } + + ret = cmd_create_session_snapshot(cmd_ctx->lsm->session.name, uris, + nb_uri, &cmd_ctx->creds); + free(uris); + break; + } + case LTTNG_CREATE_SESSION_LIVE: + { + size_t nb_uri, len; + struct lttng_uri *uris = NULL; + + nb_uri = cmd_ctx->lsm->u.uri.size; + len = nb_uri * sizeof(struct lttng_uri); + + if (nb_uri > 0) { + uris = zmalloc(len); + if (uris == NULL) { + ret = LTTNG_ERR_FATAL; + goto error; + } + + /* Receive variable len data */ + DBG("Waiting for %zu URIs from client ...", nb_uri); + ret = lttcomm_recv_unix_sock(sock, uris, len); + if (ret <= 0) { + DBG("No URIs received from client... continuing"); + *sock_error = 1; + ret = LTTNG_ERR_SESSION_FAIL; + free(uris); + goto error; + } + + if (nb_uri == 1 && uris[0].dtype != LTTNG_DST_PATH) { + DBG("Creating session with ONE network URI is a bad call"); + ret = LTTNG_ERR_SESSION_FAIL; + free(uris); + goto error; + } + } + + ret = cmd_create_session_uri(cmd_ctx->lsm->session.name, uris, + nb_uri, &cmd_ctx->creds, cmd_ctx->lsm->u.session_live.timer_interval); + free(uris); + break; + } default: ret = LTTNG_ERR_UND; break; @@ -3094,6 +3464,9 @@ static void *thread_manage_health(void *data) rcu_register_thread(); + /* We might hit an error path before this is created. */ + lttng_poll_init(&events); + /* Create unix socket */ sock = lttcomm_create_unix_sock(health_unix_sock_path); if (sock < 0) { @@ -3194,27 +3567,39 @@ restart: switch (msg.component) { case LTTNG_HEALTH_CMD: - reply.ret_code = health_check_state(HEALTH_TYPE_CMD); + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_CMD); break; case LTTNG_HEALTH_APP_MANAGE: - reply.ret_code = health_check_state(HEALTH_TYPE_APP_MANAGE); + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_APP_MANAGE); break; case LTTNG_HEALTH_APP_REG: - reply.ret_code = health_check_state(HEALTH_TYPE_APP_REG); + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_APP_REG); break; case LTTNG_HEALTH_KERNEL: - reply.ret_code = health_check_state(HEALTH_TYPE_KERNEL); + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_KERNEL); break; case LTTNG_HEALTH_CONSUMER: reply.ret_code = check_consumer_health(); break; + case LTTNG_HEALTH_HT_CLEANUP: + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_HT_CLEANUP); + break; + case LTTNG_HEALTH_APP_MANAGE_NOTIFY: + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_APP_MANAGE_NOTIFY); + break; + case LTTNG_HEALTH_APP_REG_DISPATCH: + reply.ret_code = health_check_state(health_sessiond, HEALTH_TYPE_APP_REG_DISPATCH); + break; case LTTNG_HEALTH_ALL: reply.ret_code = - health_check_state(HEALTH_TYPE_APP_MANAGE) && - health_check_state(HEALTH_TYPE_APP_REG) && - health_check_state(HEALTH_TYPE_CMD) && - health_check_state(HEALTH_TYPE_KERNEL) && - check_consumer_health(); + health_check_state(health_sessiond, HEALTH_TYPE_APP_MANAGE) && + health_check_state(health_sessiond, HEALTH_TYPE_APP_REG) && + health_check_state(health_sessiond, HEALTH_TYPE_CMD) && + health_check_state(health_sessiond, HEALTH_TYPE_KERNEL) && + check_consumer_health() && + health_check_state(health_sessiond, HEALTH_TYPE_HT_CLEANUP) && + health_check_state(health_sessiond, HEALTH_TYPE_APP_MANAGE_NOTIFY) && + health_check_state(health_sessiond, HEALTH_TYPE_APP_REG_DISPATCH); break; default: reply.ret_code = LTTNG_ERR_UND; @@ -3259,12 +3644,6 @@ error: PERROR("close"); } } - if (new_sock >= 0) { - ret = close(new_sock); - if (ret) { - PERROR("close"); - } - } lttng_poll_clean(&events); @@ -3288,7 +3667,7 @@ static void *thread_manage_clients(void *data) rcu_register_thread(); - health_register(HEALTH_TYPE_CMD); + health_register(health_sessiond, HEALTH_TYPE_CMD); if (testpoint(thread_manage_clients)) { goto error_testpoint; @@ -3446,13 +3825,11 @@ static void *thread_manage_clients(void *data) 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; + 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 @@ -3514,7 +3891,7 @@ error_testpoint: ERR("Health error occurred in %s", __func__); } - health_unregister(); + health_unregister(health_sessiond); DBG("Client thread dying"); @@ -3775,12 +4152,12 @@ static int set_permissions(char *rundir) ret = allowed_group(); if (ret < 0) { WARN("No tracing group detected"); - ret = 0; - goto end; + /* Setting gid to 0 if no tracing group is found */ + gid = 0; + } else { + gid = ret; } - gid = ret; - /* Set lttng run dir */ ret = chown(rundir, 0, gid); if (ret < 0) { @@ -3788,7 +4165,7 @@ static int set_permissions(char *rundir) PERROR("chown"); } - /* Ensure tracing group can search the run dir */ + /* Ensure all applications and tracing group can search the run dir */ ret = chmod(rundir, S_IRWXU | S_IXGRP | S_IXOTH); if (ret < 0) { ERR("Unable to set permissions on %s", rundir); @@ -3825,7 +4202,6 @@ static int set_permissions(char *rundir) DBG("All permissions are set"); -end: return ret; } @@ -3899,6 +4275,16 @@ static int set_consumer_sockets(struct consumer_data *consumer_data, goto error; } + /* + * Set the CLOEXEC flag. Return code is useless because either way, the + * show must go on. + */ + ret = utils_set_fd_cloexec(consumer_data->err_sock); + if (ret < 0) { + PERROR("utils_set_fd_cloexec"); + /* continue anyway */ + } + /* File permission MUST be 660 */ ret = chmod(consumer_data->err_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); @@ -4127,7 +4513,7 @@ int main(int argc, char **argv) DBG2("Kernel consumer cmd path: %s", kconsumer_data.cmd_unix_sock_path); } else { - home_path = get_home_dir(); + home_path = utils_get_home_dir(); if (home_path == NULL) { /* TODO: Add --socket PATH option */ ERR("Can't get HOME directory for sockets creation."); @@ -4284,6 +4670,11 @@ int main(int argc, char **argv) } } + /* Setup the thread ht_cleanup communication pipe. */ + if (utils_create_pipe_cloexec(ht_cleanup_pipe) < 0) { + goto exit; + } + /* Setup the thread apps communication pipe. */ if ((ret = utils_create_pipe_cloexec(apps_cmd_pipe)) < 0) { goto exit; @@ -4322,6 +4713,29 @@ int main(int argc, char **argv) write_pidfile(); + /* Initialize communication library */ + lttcomm_init(); + /* This is to get the TCP timeout value. */ + lttcomm_inet_init(); + + /* + * Initialize the health check subsystem. This call should set the + * appropriate time values. + */ + health_sessiond = health_app_create(HEALTH_NUM_TYPE); + if (!health_sessiond) { + PERROR("health_app_create error"); + goto exit_health_sessiond_cleanup; + } + + /* Create thread to manage the client socket */ + ret = pthread_create(&ht_cleanup_thread, NULL, + thread_ht_cleanup, (void *) NULL); + if (ret != 0) { + PERROR("pthread_create ht_cleanup"); + goto exit_ht_cleanup; + } + /* Create thread to manage the client socket */ ret = pthread_create(&health_thread, NULL, thread_manage_health, (void *) NULL); @@ -4367,7 +4781,7 @@ int main(int argc, char **argv) ust_thread_manage_notify, (void *) NULL); if (ret != 0) { PERROR("pthread_create apps"); - goto exit_apps; + goto exit_apps_notify; } /* Don't start this thread if kernel tracing is not requested nor root */ @@ -4388,12 +4802,20 @@ int main(int argc, char **argv) } exit_kernel: + ret = pthread_join(apps_notify_thread, &status); + if (ret != 0) { + PERROR("pthread_join apps notify"); + goto error; /* join error, exit without cleanup */ + } + +exit_apps_notify: ret = pthread_join(apps_thread, &status); if (ret != 0) { - PERROR("pthread_join"); + PERROR("pthread_join apps"); goto error; /* join error, exit without cleanup */ } + exit_apps: ret = pthread_join(reg_apps_thread, &status); if (ret != 0) { @@ -4441,6 +4863,14 @@ exit_client: } exit_health: + ret = pthread_join(ht_cleanup_thread, &status); + if (ret != 0) { + PERROR("pthread_join ht cleanup thread"); + goto error; /* join error, exit without cleanup */ + } +exit_ht_cleanup: + health_app_destroy(health_sessiond); +exit_health_sessiond_cleanup: exit: /* * cleanup() is called when no other thread is running.