X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=43f750a820fbf7c087ca9e5df4d275b3cc065cc9;hp=de4736643010143c6af8c7a554f1682ddaebc651;hb=0658924ecc1b36649ec467474bb82db8032a80c0;hpb=8c6c56c2c6577c540786b341e5334f676c12d43a diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index de4736643..43f750a82 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011 - David Goulet * Mathieu Desnoyers + * 2013 - Jérémie Galarneau * * 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, @@ -17,9 +18,11 @@ */ #define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include +#include #include #include #include @@ -39,11 +42,14 @@ #include #include +#include #include #include #include #include #include +#include +#include #include "lttng-sessiond.h" #include "buffer-registry.h" @@ -63,19 +69,30 @@ #include "health-sessiond.h" #include "testpoint.h" #include "ust-thread.h" +#include "agent-thread.h" +#include "save.h" +#include "load-session-thread.h" +#include "syscall.h" +#include "agent.h" #define CONSUMERD_FILE "lttng-consumerd" const char *progname; static const char *tracing_group_name = DEFAULT_TRACING_GROUP; -static const char *opt_pidfile; +static int tracing_group_name_override; +static char *opt_pidfile; static int opt_sig_parent; static int opt_verbose_consumer; -static int opt_daemon; +static int opt_daemon, opt_background; static int opt_no_kernel; -static int is_root; /* Set to 1 if the daemon is running as root */ +static char *opt_load_session_path; static pid_t ppid; /* Parent PID for --sig-parent option */ +static pid_t child_ppid; /* Internal parent PID use with daemonize. */ static char *rundir; +static int lockfile_fd = -1; + +/* Set to 1 when a SIGUSR1 signal is received. */ +static int recv_child_signal; /* * Consumer daemon specific control data. Every value not initialized here is @@ -115,6 +132,42 @@ static struct consumer_data ustconsumer32_data = { .cond_mutex = PTHREAD_MUTEX_INITIALIZER, }; +/* Command line options */ +static const struct option long_options[] = { + { "client-sock", required_argument, 0, 'c' }, + { "apps-sock", required_argument, 0, 'a' }, + { "kconsumerd-cmd-sock", required_argument, 0, '\0' }, + { "kconsumerd-err-sock", required_argument, 0, '\0' }, + { "ustconsumerd32-cmd-sock", required_argument, 0, '\0' }, + { "ustconsumerd32-err-sock", required_argument, 0, '\0' }, + { "ustconsumerd64-cmd-sock", required_argument, 0, '\0' }, + { "ustconsumerd64-err-sock", required_argument, 0, '\0' }, + { "consumerd32-path", required_argument, 0, '\0' }, + { "consumerd32-libdir", required_argument, 0, '\0' }, + { "consumerd64-path", required_argument, 0, '\0' }, + { "consumerd64-libdir", required_argument, 0, '\0' }, + { "daemonize", no_argument, 0, 'd' }, + { "background", no_argument, 0, 'b' }, + { "sig-parent", no_argument, 0, 'S' }, + { "help", no_argument, 0, 'h' }, + { "group", required_argument, 0, 'g' }, + { "version", no_argument, 0, 'V' }, + { "quiet", no_argument, 0, 'q' }, + { "verbose", no_argument, 0, 'v' }, + { "verbose-consumer", no_argument, 0, '\0' }, + { "no-kernel", no_argument, 0, '\0' }, + { "pidfile", required_argument, 0, 'p' }, + { "agent-tcp-port", required_argument, 0, '\0' }, + { "config", required_argument, 0, 'f' }, + { "load", required_argument, 0, 'l' }, + { "kmod-probes", required_argument, 0, '\0' }, + { "extra-kmod-probes", required_argument, 0, '\0' }, + { NULL, 0, 0, 0 } +}; + +/* Command line options to ignore from configuration file */ +static const char *config_ignore_options[] = { "help", "version", "config" }; + /* Shared between threads */ static int dispatch_thread_exit; @@ -138,6 +191,7 @@ static int kernel_poll_pipe[2] = { -1, -1 }; * for all threads when receiving an event on the pipe. */ static int thread_quit_pipe[2] = { -1, -1 }; +static int ht_cleanup_quit_pipe[2] = { -1, -1 }; /* * This pipe is used to inform the thread managing application communication @@ -156,13 +210,17 @@ static pthread_t kernel_thread; static pthread_t dispatch_thread; static pthread_t health_thread; static pthread_t ht_cleanup_thread; +static pthread_t agent_reg_thread; +static pthread_t load_session_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 * - * The thread_manage_apps and thread_dispatch_ust_registration interact with - * this queue and the wait/wake scheme. + * The thread_registration_apps and thread_dispatch_ust_registration uses this + * queue along with the wait/wake scheme. The thread_manage_apps receives down + * the line new application socket and monitors it for any I/O error or clean + * close that triggers an unregistration of the application. */ static struct ust_cmd_queue ust_cmd_queue; @@ -185,6 +243,10 @@ static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN; static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; +static int consumerd32_bin_override; +static int consumerd64_bin_override; +static int consumerd32_libdir_override; +static int consumerd64_libdir_override; static const char *module_proc_lttng = "/proc/lttng"; @@ -233,6 +295,52 @@ long page_size; /* Application health monitoring */ struct health_app *health_sessiond; +/* Agent TCP port for registration. Used by the agent thread. */ +unsigned int agent_tcp_port = DEFAULT_AGENT_TCP_PORT; + +/* Am I root or not. */ +int is_root; /* Set to 1 if the daemon is running as root */ + +const char * const config_section_name = "sessiond"; + +/* Load session thread information to operate. */ +struct load_session_thread_data *load_info; + +/* Global hash tables */ +struct lttng_ht *agent_apps_ht_by_sock = NULL; + +/* + * Whether sessiond is ready for commands/health check requests. + * NR_LTTNG_SESSIOND_READY must match the number of calls to + * sessiond_notify_ready(). + */ +#define NR_LTTNG_SESSIOND_READY 3 +int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY; + +/* Notify parents that we are ready for cmd and health check */ +LTTNG_HIDDEN +void sessiond_notify_ready(void) +{ + if (uatomic_sub_return(<tng_sessiond_ready, 1) == 0) { + /* + * Notify parent pid that we are ready to accept command + * for client side. This ppid is the one from the + * external process that spawned us. + */ + if (opt_sig_parent) { + kill(ppid, SIGUSR1); + } + + /* + * Notify the parent of the fork() process that we are + * ready. + */ + if (opt_daemon || opt_background) { + kill(child_ppid, SIGUSR1); + } + } +} + static void setup_consumerd_path(void) { @@ -264,28 +372,27 @@ void setup_consumerd_path(void) /* * runtime env. var. overrides the build default. */ - bin = getenv("LTTNG_CONSUMERD32_BIN"); + bin = lttng_secure_getenv("LTTNG_CONSUMERD32_BIN"); if (bin) { consumerd32_bin = bin; } - bin = getenv("LTTNG_CONSUMERD64_BIN"); + bin = lttng_secure_getenv("LTTNG_CONSUMERD64_BIN"); if (bin) { consumerd64_bin = bin; } - libdir = getenv("LTTNG_CONSUMERD32_LIBDIR"); + libdir = lttng_secure_getenv("LTTNG_CONSUMERD32_LIBDIR"); if (libdir) { consumerd32_libdir = libdir; } - libdir = getenv("LTTNG_CONSUMERD64_LIBDIR"); + libdir = lttng_secure_getenv("LTTNG_CONSUMERD64_LIBDIR"); if (libdir) { consumerd64_libdir = libdir; } } -/* - * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. - */ -int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) +static +int __sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size, + int *a_pipe) { int ret; @@ -297,7 +404,7 @@ int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) } /* Add quit pipe */ - ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN | LPOLLERR); + ret = lttng_poll_add(events, a_pipe[0], LPOLLIN | LPOLLERR); if (ret < 0) { goto error; } @@ -308,6 +415,33 @@ error: return ret; } +/* + * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. + */ +int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) +{ + return __sessiond_set_thread_pollset(events, size, thread_quit_pipe); +} + +/* + * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. + */ +int sessiond_set_ht_cleanup_thread_pollset(struct lttng_poll_event *events, + size_t size) +{ + return __sessiond_set_thread_pollset(events, size, + ht_cleanup_quit_pipe); +} + +static +int __sessiond_check_thread_quit_pipe(int fd, uint32_t events, int a_pipe) +{ + if (fd == a_pipe && (events & LPOLLIN)) { + return 1; + } + return 0; +} + /* * Check if the thread quit pipe was triggered. * @@ -315,11 +449,19 @@ error: */ int sessiond_check_thread_quit_pipe(int fd, uint32_t events) { - if (fd == thread_quit_pipe[0] && (events & LPOLLIN)) { - return 1; - } + return __sessiond_check_thread_quit_pipe(fd, events, + thread_quit_pipe[0]); +} - return 0; +/* + * Check if the ht_cleanup thread quit pipe was triggered. + * + * Return 1 if it was triggered else 0; + */ +int sessiond_check_ht_cleanup_quit(int fd, uint32_t events) +{ + return __sessiond_check_thread_quit_pipe(fd, events, + ht_cleanup_quit_pipe[0]); } /* @@ -327,18 +469,18 @@ int sessiond_check_thread_quit_pipe(int fd, uint32_t events) * * Return -1 on error or 0 if all pipes are created. */ -static int init_thread_quit_pipe(void) +static int __init_thread_quit_pipe(int *a_pipe) { int ret, i; - ret = pipe(thread_quit_pipe); + ret = pipe(a_pipe); if (ret < 0) { PERROR("thread quit pipe"); goto error; } for (i = 0; i < 2; i++) { - ret = fcntl(thread_quit_pipe[i], F_SETFD, FD_CLOEXEC); + ret = fcntl(a_pipe[i], F_SETFD, FD_CLOEXEC); if (ret < 0) { PERROR("fcntl"); goto error; @@ -349,6 +491,16 @@ error: return ret; } +static int init_thread_quit_pipe(void) +{ + return __init_thread_quit_pipe(thread_quit_pipe); +} + +static int init_ht_cleanup_quit_pipe(void) +{ + return __init_thread_quit_pipe(ht_cleanup_quit_pipe); +} + /* * Stop all threads by closing the thread quit pipe. */ @@ -414,15 +566,36 @@ static void close_consumer_sockets(void) } /* - * Cleanup the daemon + * Generate the full lock file path using the rundir. + * + * Return the snprintf() return value thus a negative value is an error. + */ +static int generate_lock_file_path(char *path, size_t len) +{ + int ret; + + assert(path); + assert(rundir); + + /* Build lockfile path from rundir. */ + ret = snprintf(path, len, "%s/" DEFAULT_LTTNG_SESSIOND_LOCKFILE, rundir); + if (ret < 0) { + PERROR("snprintf lockfile path"); + } + + return ret; +} + +/* + * Cleanup the session daemon's data structures. */ -static void cleanup(void) +static void sessiond_cleanup(void) { int ret; struct ltt_session *sess, *stmp; char path[PATH_MAX]; - DBG("Cleaning up"); + DBG("Cleanup sessiond"); /* * Close the thread quit pipe. It has already done its job, @@ -450,6 +623,11 @@ static void cleanup(void) DBG("Removing %s", path); (void) unlink(path); + snprintf(path, PATH_MAX, "%s/%s", rundir, + DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE); + DBG("Removing %s", path); + (void) unlink(path); + /* kconsumerd */ snprintf(path, PATH_MAX, DEFAULT_KCONSUMERD_ERR_SOCK_PATH, @@ -489,8 +667,6 @@ static void cleanup(void) DBG("Removing directory %s", path); (void) rmdir(path); - free(rundir); - DBG("Cleaning up all sessions"); /* Destroy session list mutex */ @@ -504,6 +680,9 @@ static void cleanup(void) } } + DBG("Cleaning up all agent apps"); + agent_app_ht_clean(); + DBG("Closing all UST sockets"); ust_app_clean_list(); buffer_reg_destroy_registries(); @@ -518,10 +697,78 @@ static void cleanup(void) } DBG("Unloading kernel modules"); modprobe_remove_lttng_all(); + free(syscall_table); } close_consumer_sockets(); + if (load_info) { + load_session_destroy_data(load_info); + free(load_info); + } + + /* + * Cleanup lock file by deleting it and finaly closing it which will + * release the file system lock. + */ + if (lockfile_fd >= 0) { + char lockfile_path[PATH_MAX]; + + ret = generate_lock_file_path(lockfile_path, + sizeof(lockfile_path)); + if (ret > 0) { + ret = remove(lockfile_path); + if (ret < 0) { + PERROR("remove lock file"); + } + ret = close(lockfile_fd); + if (ret < 0) { + PERROR("close lock file"); + } + } + } + + /* + * We do NOT rmdir rundir because there are other processes + * using it, for instance lttng-relayd, which can start in + * parallel with this teardown. + */ + + free(rundir); +} + +/* + * Cleanup the daemon's option data structures. + */ +static void sessiond_cleanup_options(void) +{ + DBG("Cleaning up options"); + + /* + * If the override option is set, the pointer points to a *non* const + * thus freeing it even though the variable type is set to const. + */ + if (tracing_group_name_override) { + free((void *) tracing_group_name); + } + if (consumerd32_bin_override) { + free((void *) consumerd32_bin); + } + if (consumerd64_bin_override) { + free((void *) consumerd64_bin); + } + if (consumerd32_libdir_override) { + free((void *) consumerd32_libdir); + } + if (consumerd64_libdir_override) { + free((void *) consumerd64_libdir); + } + + free(opt_pidfile); + free(opt_load_session_path); + free(kmod_probes_list); + free(kmod_extra_probes_list); + /* */ DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm" "Matthew, BEET driven development works!%c[%dm", @@ -685,42 +932,47 @@ static int update_kernel_stream(struct consumer_data *consumer_data, int fd) } ksess = session->kernel_session; - cds_list_for_each_entry(channel, &ksess->channel_list.head, list) { - if (channel->fd == fd) { - DBG("Channel found, updating kernel streams"); - ret = kernel_open_channel_stream(channel); - if (ret < 0) { - goto error; - } - /* Update the stream global counter */ - ksess->stream_count_global += ret; + cds_list_for_each_entry(channel, + &ksess->channel_list.head, list) { + struct lttng_ht_iter iter; + struct consumer_socket *socket; - /* - * Have we already sent fds to the consumer? If yes, it means - * that tracing is started so it is safe to send our updated - * stream fds. - */ - if (ksess->consumer_fds_sent == 1 && ksess->consumer != NULL) { - struct lttng_ht_iter iter; - struct consumer_socket *socket; - - rcu_read_lock(); - cds_lfht_for_each_entry(ksess->consumer->socks->ht, - &iter.iter, socket, node.node) { - pthread_mutex_lock(socket->lock); - ret = kernel_consumer_send_channel_stream(socket, - channel, ksess, - session->output_traces ? 1 : 0); - pthread_mutex_unlock(socket->lock); - if (ret < 0) { - rcu_read_unlock(); - goto error; - } - } + if (channel->fd != fd) { + continue; + } + DBG("Channel found, updating kernel streams"); + ret = kernel_open_channel_stream(channel); + 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 that tracing is started so it is safe to send + * our updated stream fds. + */ + if (ksess->consumer_fds_sent != 1 + || ksess->consumer == NULL) { + ret = -1; + goto error; + } + + rcu_read_lock(); + cds_lfht_for_each_entry(ksess->consumer->socks->ht, + &iter.iter, socket, node.node) { + pthread_mutex_lock(socket->lock); + ret = kernel_consumer_send_channel_stream(socket, + channel, ksess, + session->output_traces ? 1 : 0); + pthread_mutex_unlock(socket->lock); + if (ret < 0) { rcu_read_unlock(); + goto error; } - goto error; } + rcu_read_unlock(); } session_unlock(session); } @@ -749,10 +1001,30 @@ static void update_ust_app(int app_sock) /* For all tracing session(s) */ cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { + struct ust_app *app; + session_lock(sess); - if (sess->ust_session) { - ust_app_global_update(sess->ust_session, app_sock); + if (!sess->ust_session) { + goto unlock_session; } + + rcu_read_lock(); + assert(app_sock >= 0); + app = ust_app_find_by_sock(app_sock); + if (app == NULL) { + /* + * Application can be unregistered before so + * this is possible hence simply stopping the + * update. + */ + DBG3("UST app update failed to find app sock %d", + app_sock); + goto unlock_rcu; + } + ust_app_global_update(sess->ust_session, app); + unlock_rcu: + rcu_read_unlock(); + unlock_session: session_unlock(sess); } } @@ -780,13 +1052,13 @@ static void *thread_manage_kernel(void *data) */ lttng_poll_init(&events); - if (testpoint(thread_manage_kernel)) { + if (testpoint(sessiond_thread_manage_kernel)) { goto error_testpoint; } health_code_update(); - if (testpoint(thread_manage_kernel_before_loop)) { + if (testpoint(sessiond_thread_manage_kernel_before_loop)) { goto error_testpoint; } @@ -815,12 +1087,14 @@ static void *thread_manage_kernel(void *data) update_poll_flag = 0; } - DBG("Thread kernel polling on %d fds", LTTNG_POLL_GETNB(&events)); + DBG("Thread kernel polling"); /* Poll infinite value of time */ restart: health_poll_entry(); ret = lttng_poll_wait(&events, -1); + DBG("Thread kernel return from poll on %d fds", + LTTNG_POLL_GETNB(&events)); health_poll_exit(); if (ret < 0) { /* @@ -846,6 +1120,11 @@ static void *thread_manage_kernel(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -855,9 +1134,8 @@ static void *thread_manage_kernel(void *data) /* Check for data on kernel pipe */ if (pollfd == kernel_poll_pipe[0] && (revents & LPOLLIN)) { - do { - ret = read(kernel_poll_pipe[0], &tmp, 1); - } while (ret < 0 && errno == EINTR); + (void) lttng_read(kernel_poll_pipe[0], + &tmp, 1); /* * Ret value is useless here, if this pipe gets any actions an * update is required anyway. @@ -930,7 +1208,7 @@ static void signal_consumer_condition(struct consumer_data *data, int state) */ static void *thread_manage_consumer(void *data) { - int sock = -1, i, ret, pollfd, err = -1; + int sock = -1, i, ret, pollfd, err = -1, should_quit = 0; uint32_t revents, nb_fd; enum lttcomm_return_code code; struct lttng_poll_event events; @@ -938,6 +1216,9 @@ static void *thread_manage_consumer(void *data) DBG("[thread] Manage consumer started"); + rcu_register_thread(); + rcu_thread_online(); + health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CONSUMER); health_code_update(); @@ -967,7 +1248,7 @@ static void *thread_manage_consumer(void *data) restart: health_poll_entry(); - if (testpoint(thread_manage_consumer)) { + if (testpoint(sessiond_thread_manage_consumer)) { goto error; } @@ -992,6 +1273,11 @@ restart: health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1031,7 +1317,6 @@ restart: } health_code_update(); - if (code == LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) { /* Connect both socket, command and metadata. */ consumer_data->cmd_sock = @@ -1089,6 +1374,15 @@ restart: /* Infinite blocking call, waiting for transmission */ restart_poll: while (1) { + health_code_update(); + + /* Exit the thread because the thread quit pipe has been triggered. */ + if (should_quit) { + /* Not a health error. */ + err = 0; + goto exit; + } + health_poll_entry(); ret = lttng_poll_wait(&events, -1); health_poll_exit(); @@ -1111,13 +1405,18 @@ restart_poll: health_code_update(); - /* Thread quit pipe has been closed. Killing thread. */ - ret = sessiond_check_thread_quit_pipe(pollfd, revents); - if (ret) { - err = 0; - goto exit; + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; } + /* + * Thread quit pipe has been triggered, flag that we should stop + * but continue the current loop to handle potential data from + * consumer. + */ + should_quit = sessiond_check_thread_quit_pipe(pollfd, revents); + if (pollfd == sock) { /* Event on the consumerd socket */ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { @@ -1145,11 +1444,8 @@ restart_poll: ERR("Handling metadata request"); goto error; } - break; - } else { - ERR("Unknown pollfd"); - goto error; } + /* No need for an else branch all FDs are tested prior. */ } health_code_update(); } @@ -1188,13 +1484,13 @@ error: } consumer_data->cmd_sock = -1; } - if (*consumer_data->metadata_sock.fd_ptr >= 0) { + if (consumer_data->metadata_sock.fd_ptr && + *consumer_data->metadata_sock.fd_ptr >= 0) { ret = close(*consumer_data->metadata_sock.fd_ptr); if (ret) { PERROR("close"); } } - if (sock >= 0) { ret = close(sock); if (ret) { @@ -1208,9 +1504,10 @@ error: pthread_mutex_unlock(&consumer_data->lock); /* Cleanup metadata socket mutex. */ - pthread_mutex_destroy(consumer_data->metadata_sock.lock); - free(consumer_data->metadata_sock.lock); - + if (consumer_data->metadata_sock.lock) { + pthread_mutex_destroy(consumer_data->metadata_sock.lock); + free(consumer_data->metadata_sock.lock); + } lttng_poll_clean(&events); error_poll: if (err) { @@ -1220,6 +1517,9 @@ error_poll: health_unregister(health_sessiond); DBG("consumer thread cleanup completed"); + rcu_thread_offline(); + rcu_unregister_thread(); + return NULL; } @@ -1229,6 +1529,7 @@ error_poll: static void *thread_manage_apps(void *data) { int i, ret, pollfd, err = -1; + ssize_t size_ret; uint32_t revents, nb_fd; struct lttng_poll_event events; @@ -1239,7 +1540,7 @@ static void *thread_manage_apps(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_MANAGE); - if (testpoint(thread_manage_apps)) { + if (testpoint(sessiond_thread_manage_apps)) { goto error_testpoint; } @@ -1255,19 +1556,21 @@ static void *thread_manage_apps(void *data) goto error; } - if (testpoint(thread_manage_apps_before_loop)) { + if (testpoint(sessiond_thread_manage_apps_before_loop)) { goto error; } health_code_update(); while (1) { - DBG("Apps thread polling on %d fds", LTTNG_POLL_GETNB(&events)); + DBG("Apps thread polling"); /* Inifinite blocking call, waiting for transmission */ restart: health_poll_entry(); ret = lttng_poll_wait(&events, -1); + DBG("Apps thread return from poll on %d fds", + LTTNG_POLL_GETNB(&events)); health_poll_exit(); if (ret < 0) { /* @@ -1288,6 +1591,11 @@ static void *thread_manage_apps(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1304,10 +1612,8 @@ static void *thread_manage_apps(void *data) int sock; /* Empty pipe */ - do { - ret = read(apps_cmd_pipe[0], &sock, sizeof(sock)); - } while (ret < 0 && errno == EINTR); - if (ret < 0 || ret < sizeof(sock)) { + size_ret = lttng_read(apps_cmd_pipe[0], &sock, sizeof(sock)); + if (size_ret < sizeof(sock)) { PERROR("read apps cmd pipe"); goto error; } @@ -1325,23 +1631,7 @@ static void *thread_manage_apps(void *data) goto error; } - /* - * 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 * 1000); - (void) lttcomm_setsockopt_snd_timeout(sock, - app_socket_timeout * 1000); - DBG("Apps with sock %d added to poll set", sock); - - health_code_update(); - - break; } } else { /* @@ -1357,7 +1647,6 @@ static void *thread_manage_apps(void *data) /* Socket closed on remote end. */ ust_app_unregister(pollfd); - break; } } @@ -1402,7 +1691,7 @@ error_testpoint: */ static int send_socket_to_thread(int fd, int sock) { - int ret; + ssize_t ret; /* * It's possible that the FD is set as invalid with -1 concurrently just @@ -1413,10 +1702,8 @@ static int send_socket_to_thread(int fd, int sock) goto error; } - do { - ret = write(fd, &sock, sizeof(sock)); - } while (ret < 0 && errno == EINTR); - if (ret < 0 || ret != sizeof(sock)) { + ret = lttng_write(fd, &sock, sizeof(sock)); + if (ret < sizeof(sock)) { PERROR("write apps pipe %d", fd); if (ret < 0) { ret = -errno; @@ -1427,7 +1714,7 @@ static int send_socket_to_thread(int fd, int sock) /* All good. Don't send back the write positive ret value. */ ret = 0; error: - return ret; + return (int) ret; } /* @@ -1487,6 +1774,11 @@ static void sanitize_wait_queue(struct ust_reg_wait_queue *wait_queue) uint32_t revents = LTTNG_POLL_GETEV(&events, i); int pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + cds_list_for_each_entry_safe(wait_node, tmp_wait_node, &wait_queue->head, head) { if (pollfd == wait_node->app->sock && @@ -1522,7 +1814,7 @@ error_create: static void *thread_dispatch_ust_registration(void *data) { int ret, err = -1; - struct cds_wfq_node *node; + struct cds_wfcq_node *node; struct ust_command *ust_cmd = NULL; struct ust_reg_wait_node *wait_node = NULL, *tmp_wait_node; struct ust_reg_wait_queue wait_queue = { @@ -1531,6 +1823,10 @@ static void *thread_dispatch_ust_registration(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH); + if (testpoint(sessiond_thread_app_reg_dispatch)) { + goto error_testpoint; + } + health_code_update(); CDS_INIT_LIST_HEAD(&wait_queue.head); @@ -1556,7 +1852,7 @@ static void *thread_dispatch_ust_registration(void *data) health_code_update(); /* Dequeue command for registration */ - node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); + node = cds_wfcq_dequeue_blocking(&ust_cmd_queue.head, &ust_cmd_queue.tail); if (node == NULL) { DBG("Woken up but nothing in the UST command queue"); /* Continue thread execution */ @@ -1580,7 +1876,7 @@ static void *thread_dispatch_ust_registration(void *data) if (ret < 0) { PERROR("close ust sock dispatch %d", ust_cmd->sock); } - lttng_fd_put(1, LTTNG_FD_APPS); + lttng_fd_put(LTTNG_FD_APPS, 1); free(ust_cmd); goto error; } @@ -1594,7 +1890,7 @@ static void *thread_dispatch_ust_registration(void *data) if (ret < 0) { PERROR("close ust sock dispatch %d", ust_cmd->sock); } - lttng_fd_put(1, LTTNG_FD_APPS); + lttng_fd_put(LTTNG_FD_APPS, 1); free(wait_node); free(ust_cmd); continue; @@ -1642,7 +1938,7 @@ static void *thread_dispatch_ust_registration(void *data) if (ret < 0) { PERROR("close ust sock dispatch %d", ust_cmd->sock); } - lttng_fd_put(1, LTTNG_FD_APPS); + lttng_fd_put(LTTNG_FD_APPS, 1); } free(ust_cmd); } @@ -1735,6 +2031,23 @@ error: free(wait_node); } + /* Empty command queue. */ + for (;;) { + /* Dequeue command for registration */ + node = cds_wfcq_dequeue_blocking(&ust_cmd_queue.head, &ust_cmd_queue.tail); + if (node == NULL) { + break; + } + ust_cmd = caa_container_of(node, struct ust_command, node); + ret = close(ust_cmd->sock); + if (ret < 0) { + PERROR("close ust sock exit dispatch %d", ust_cmd->sock); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + free(ust_cmd); + } + +error_testpoint: DBG("Dispatch thread dying"); if (err) { health_error(); @@ -1762,7 +2075,7 @@ static void *thread_registration_apps(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG); - if (testpoint(thread_registration_apps)) { + if (testpoint(sessiond_thread_registration_apps)) { goto error_testpoint; } @@ -1821,6 +2134,11 @@ static void *thread_registration_apps(void *data) revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -1839,6 +2157,18 @@ static void *thread_registration_apps(void *data) goto error; } + /* + * 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 * 1000); + (void) lttcomm_setsockopt_snd_timeout(sock, + app_socket_timeout * 1000); + /* * Set the CLOEXEC flag. Return code is useless because * either way, the show must go on. @@ -1849,6 +2179,10 @@ static void *thread_registration_apps(void *data) ust_cmd = zmalloc(sizeof(struct ust_command)); if (ust_cmd == NULL) { PERROR("ust command zmalloc"); + ret = close(sock); + if (ret) { + PERROR("close"); + } goto error; } @@ -1897,11 +2231,11 @@ static void *thread_registration_apps(void *data) * Lock free enqueue the registration request. The red pill * has been taken! This apps will be part of the *system*. */ - cds_wfq_enqueue(&ust_cmd_queue.queue, &ust_cmd->node); + cds_wfcq_enqueue(&ust_cmd_queue.head, &ust_cmd_queue.tail, &ust_cmd->node); /* * Wake the registration queue futex. Implicit memory - * barrier with the exchange in cds_wfq_enqueue. + * barrier with the exchange in cds_wfcq_enqueue. */ futex_nto1_wake(&ust_cmd_queue.futex); } @@ -1911,11 +2245,6 @@ static void *thread_registration_apps(void *data) exit: error: - if (err) { - health_error(); - ERR("Health error occurred in %s", __func__); - } - /* Notify that the registration thread is gone */ notify_ust_apps(0); @@ -1940,6 +2269,10 @@ error_listen: error_create_poll: error_testpoint: DBG("UST Registration thread cleanup complete"); + if (err) { + health_error(); + ERR("Health error occurred in %s", __func__); + } health_unregister(health_sessiond); return NULL; @@ -1959,7 +2292,7 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) /* Setup pthread condition */ ret = pthread_condattr_init(&consumer_data->condattr); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_condattr_init consumer data"); goto error; @@ -1971,14 +2304,14 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) * for a more details and how we noticed it. */ ret = pthread_condattr_setclock(&consumer_data->condattr, CLOCK_MONOTONIC); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_condattr_setclock consumer data"); goto error; } ret = pthread_cond_init(&consumer_data->cond, &consumer_data->condattr); - if (ret != 0) { + if (ret) { errno = ret; PERROR("pthread_cond_init consumer data"); goto error; @@ -1986,7 +2319,8 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) ret = pthread_create(&consumer_data->thread, NULL, thread_manage_consumer, consumer_data); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create consumer"); ret = -1; goto error; @@ -2036,19 +2370,23 @@ static int spawn_consumer_thread(struct consumer_data *consumer_data) if (ret != 0) { errno = ret; if (ret == ETIMEDOUT) { + int pth_ret; + /* * Call has timed out so we kill the kconsumerd_thread and return * an error. */ ERR("Condition timed out. The consumer thread was never ready." " Killing it"); - ret = pthread_cancel(consumer_data->thread); - if (ret < 0) { + pth_ret = pthread_cancel(consumer_data->thread); + if (pth_ret < 0) { PERROR("pthread_cancel consumer thread"); } } else { PERROR("pthread_cond_wait failed consumer thread"); } + /* Caller is expecting a negative value on failure. */ + ret = -1; goto error; } @@ -2078,7 +2416,7 @@ static int join_consumer_thread(struct consumer_data *consumer_data) int ret; ret = kill(consumer_data->pid, SIGTERM); if (ret) { - ERR("Error killing consumer daemon"); + PERROR("Error killing consumer daemon"); return ret; } return pthread_join(consumer_data->thread, &status); @@ -2109,9 +2447,12 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) */ if (opt_verbose_consumer) { verbosity = "--verbose"; - } else { + } else if (lttng_opt_quiet) { verbosity = "--quiet"; + } else { + verbosity = ""; } + switch (consumer_data->type) { case LTTNG_CONSUMER_KERNEL: /* @@ -2134,10 +2475,11 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) consumer_to_use = consumerd32_bin; } else { DBG("Could not find any valid consumerd executable"); + ret = -EINVAL; break; } DBG("Using kernel consumer at: %s", consumer_to_use); - execl(consumer_to_use, + ret = execl(consumer_to_use, "lttng-consumerd", verbosity, "-k", "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, "--consumerd-err-sock", consumer_data->err_unix_sock_path, @@ -2152,7 +2494,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) char *tmp; size_t tmplen; - tmp = getenv("LD_LIBRARY_PATH"); + tmp = lttng_secure_getenv("LD_LIBRARY_PATH"); if (!tmp) { tmp = ""; } @@ -2185,9 +2527,6 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) if (consumerd64_libdir[0] != '\0') { free(tmpnew); } - if (ret) { - goto error; - } break; } case LTTNG_CONSUMER32_UST: @@ -2198,7 +2537,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) char *tmp; size_t tmplen; - tmp = getenv("LD_LIBRARY_PATH"); + tmp = lttng_secure_getenv("LD_LIBRARY_PATH"); if (!tmp) { tmp = ""; } @@ -2231,9 +2570,6 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) if (consumerd32_libdir[0] != '\0') { free(tmpnew); } - if (ret) { - goto error; - } break; } default: @@ -2241,8 +2577,9 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) exit(EXIT_FAILURE); } if (errno != 0) { - PERROR("kernel start consumer exec"); + PERROR("Consumer execl()"); } + /* Reaching this point, we got a failure on our execl(). */ exit(EXIT_FAILURE); } else if (pid > 0) { ret = pid; @@ -2402,7 +2739,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session) * domain. */ if (session->kernel_session->consumer) { - consumer_destroy_output(session->kernel_session->consumer); + consumer_output_put(session->kernel_session->consumer); } session->kernel_session->consumer = consumer_copy_output(session->consumer); @@ -2410,10 +2747,13 @@ static int copy_session_consumer(int domain, struct ltt_session *session) consumer = session->kernel_session->consumer; dir_name = DEFAULT_KERNEL_TRACE_DIR; break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: DBG3("Copying tracing session consumer output in UST session"); if (session->ust_session->consumer) { - consumer_destroy_output(session->ust_session->consumer); + consumer_output_put(session->ust_session->consumer); } session->ust_session->consumer = consumer_copy_output(session->consumer); @@ -2453,6 +2793,9 @@ static int create_ust_session(struct ltt_session *session, assert(session->consumer); switch (domain->type) { + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: break; default: @@ -2475,7 +2818,16 @@ static int create_ust_session(struct ltt_session *session, lus->snapshot_mode = session->snapshot_mode; lus->live_timer_interval = session->live_timer; session->ust_session = lus; - + if (session->shm_path[0]) { + strncpy(lus->root_shm_path, session->shm_path, + sizeof(lus->root_shm_path)); + lus->root_shm_path[sizeof(lus->root_shm_path) - 1] = '\0'; + strncpy(lus->shm_path, session->shm_path, + sizeof(lus->shm_path)); + lus->shm_path[sizeof(lus->shm_path) - 1] = '\0'; + strncat(lus->shm_path, "/ust", + sizeof(lus->shm_path) - strlen(lus->shm_path) - 1); + } /* Copy session output to the newly created UST session */ ret = copy_session_consumer(domain->type, session); if (ret != LTTNG_OK) { @@ -2521,7 +2873,7 @@ static int create_kernel_session(struct ltt_session *session) session->kernel_session->consumer->dst.trace_path, S_IRWXU | S_IRWXG, session->uid, session->gid); if (ret < 0) { - if (ret != -EEXIST) { + if (errno != EEXIST) { ERR("Trace directory creation error"); goto error; } @@ -2599,6 +2951,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_SNAPSHOT_DEL_OUTPUT: case LTTNG_SNAPSHOT_LIST_OUTPUT: case LTTNG_SNAPSHOT_RECORD: + case LTTNG_SAVE_SESSION: + case LTTNG_SET_SESSION_SHM_PATH: need_domain = 0; break; default: @@ -2638,6 +2992,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_LIST_DOMAINS: case LTTNG_LIST_CHANNELS: case LTTNG_LIST_EVENTS: + case LTTNG_LIST_SYSCALLS: + case LTTNG_LIST_TRACKER_PIDS: break; default: /* Setup lttng message with no payload */ @@ -2656,7 +3012,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_CALIBRATE: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: + case LTTNG_LIST_SYSCALLS: case LTTNG_LIST_TRACEPOINT_FIELDS: + case LTTNG_SAVE_SESSION: need_tracing_session = 0; break; default: @@ -2678,6 +3036,39 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; } + /* + * Commands that need a valid session but should NOT create one if none + * exists. Instead of creating one and destroying it when the command is + * handled, process that right before so we save some round trip in useless + * code path. + */ + switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_DISABLE_CHANNEL: + case LTTNG_DISABLE_EVENT: + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_KERNEL: + if (!cmd_ctx->session->kernel_session) { + ret = LTTNG_ERR_NO_CHANNEL; + goto error; + } + break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + case LTTNG_DOMAIN_UST: + if (!cmd_ctx->session->ust_session) { + ret = LTTNG_ERR_NO_CHANNEL; + goto error; + } + break; + default: + ret = LTTNG_ERR_UNKNOWN_DOMAIN; + goto error; + } + default: + break; + } + if (!need_domain) { goto skip_domain; } @@ -2744,6 +3135,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, } break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { @@ -2797,6 +3191,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, } /* 32-bit */ + pthread_mutex_lock(&ustconsumer32_data.pid_mutex); if (consumerd32_bin[0] != '\0' && ustconsumer32_data.pid == 0 && cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { @@ -2835,6 +3230,9 @@ skip_domain: 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_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) { ret = LTTNG_ERR_NO_USTCONSUMERD; @@ -2895,17 +3293,37 @@ skip_domain: } case LTTNG_DISABLE_EVENT: { + + /* + * FIXME: handle filter; for now we just receive the filter's + * bytecode along with the filter expression which are sent by + * liblttng-ctl and discard them. + * + * This fixes an issue where the client may block while sending + * the filter payload and encounter an error because the session + * daemon closes the socket without ever handling this data. + */ + size_t count = cmd_ctx->lsm->u.disable.expression_len + + cmd_ctx->lsm->u.disable.bytecode_len; + + if (count) { + char data[LTTNG_FILTER_MAX_LEN]; + + DBG("Discarding disable event command payload of size %zu", count); + while (count) { + ret = lttcomm_recv_unix_sock(sock, data, + count > sizeof(data) ? sizeof(data) : count); + if (ret < 0) { + goto error; + } + + count -= (size_t) ret; + } + } + /* FIXME: passing packed structure to non-packed pointer */ ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, cmd_ctx->lsm->u.disable.channel_name, - cmd_ctx->lsm->u.disable.name); - break; - } - case LTTNG_DISABLE_ALL_EVENT: - { - DBG("Disabling all events"); - - ret = cmd_disable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.disable.channel_name); + &cmd_ctx->lsm->u.disable.event); break; } case LTTNG_ENABLE_CHANNEL: @@ -2914,20 +3332,128 @@ skip_domain: &cmd_ctx->lsm->u.channel.chan, kernel_poll_pipe[1]); break; } - case LTTNG_ENABLE_EVENT: + case LTTNG_TRACK_PID: { - ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, - cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, NULL, kernel_poll_pipe[1]); + ret = cmd_track_pid(cmd_ctx->session, + cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.pid_tracker.pid); break; } - case LTTNG_ENABLE_ALL_EVENT: + case LTTNG_UNTRACK_PID: { - DBG("Enabling all events"); + ret = cmd_untrack_pid(cmd_ctx->session, + cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.pid_tracker.pid); + break; + } + case LTTNG_ENABLE_EVENT: + { + struct lttng_event_exclusion *exclusion = NULL; + struct lttng_filter_bytecode *bytecode = NULL; + char *filter_expression = NULL; + + /* Handle exclusion events and receive it from the client. */ + if (cmd_ctx->lsm->u.enable.exclusion_count > 0) { + size_t count = cmd_ctx->lsm->u.enable.exclusion_count; + + exclusion = zmalloc(sizeof(struct lttng_event_exclusion) + + (count * LTTNG_SYMBOL_NAME_LEN)); + if (!exclusion) { + ret = LTTNG_ERR_EXCLUSION_NOMEM; + goto error; + } + + DBG("Receiving var len exclusion event list from client ..."); + exclusion->count = count; + ret = lttcomm_recv_unix_sock(sock, exclusion->names, + count * LTTNG_SYMBOL_NAME_LEN); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + free(exclusion); + ret = LTTNG_ERR_EXCLUSION_INVAL; + goto error; + } + } + + /* Get filter expression from client. */ + if (cmd_ctx->lsm->u.enable.expression_len > 0) { + size_t expression_len = + cmd_ctx->lsm->u.enable.expression_len; + + if (expression_len > LTTNG_FILTER_MAX_LEN) { + ret = LTTNG_ERR_FILTER_INVAL; + free(exclusion); + goto error; + } - ret = cmd_enable_event_all(cmd_ctx->session, &cmd_ctx->lsm->domain, + filter_expression = zmalloc(expression_len); + if (!filter_expression) { + free(exclusion); + ret = LTTNG_ERR_FILTER_NOMEM; + goto error; + } + + /* Receive var. len. data */ + DBG("Receiving var len filter's expression from client ..."); + ret = lttcomm_recv_unix_sock(sock, filter_expression, + expression_len); + if (ret <= 0) { + DBG("Nothing recv() from client car len data... continuing"); + *sock_error = 1; + free(filter_expression); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + } + + /* Handle filter and get bytecode from client. */ + if (cmd_ctx->lsm->u.enable.bytecode_len > 0) { + size_t bytecode_len = cmd_ctx->lsm->u.enable.bytecode_len; + + if (bytecode_len > LTTNG_FILTER_MAX_LEN) { + ret = LTTNG_ERR_FILTER_INVAL; + free(filter_expression); + free(exclusion); + goto error; + } + + bytecode = zmalloc(bytecode_len); + if (!bytecode) { + free(filter_expression); + free(exclusion); + ret = LTTNG_ERR_FILTER_NOMEM; + goto error; + } + + /* Receive var. len. data */ + DBG("Receiving var len filter's bytecode from client ..."); + ret = lttcomm_recv_unix_sock(sock, bytecode, bytecode_len); + if (ret <= 0) { + DBG("Nothing recv() from client car len data... continuing"); + *sock_error = 1; + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + + if ((bytecode->len + sizeof(*bytecode)) != bytecode_len) { + free(filter_expression); + free(bytecode); + free(exclusion); + ret = LTTNG_ERR_FILTER_INVAL; + goto error; + } + } + + ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, cmd_ctx->lsm->u.enable.channel_name, - cmd_ctx->lsm->u.enable.event.type, NULL, kernel_poll_pipe[1]); + &cmd_ctx->lsm->u.enable.event, + filter_expression, bytecode, exclusion, + kernel_poll_pipe[1]); break; } case LTTNG_LIST_TRACEPOINTS: @@ -2935,7 +3461,9 @@ skip_domain: struct lttng_event *events; ssize_t nb_events; + session_lock_list(); nb_events = cmd_list_tracepoints(cmd_ctx->lsm->domain.type, &events); + session_unlock_list(); if (nb_events < 0) { /* Return value is a negative lttng_error_code. */ ret = -nb_events; @@ -2966,8 +3494,10 @@ skip_domain: struct lttng_event_field *fields; ssize_t nb_fields; + session_lock_list(); nb_fields = cmd_list_tracepoint_fields(cmd_ctx->lsm->domain.type, &fields); + session_unlock_list(); if (nb_fields < 0) { /* Return value is a negative lttng_error_code. */ ret = -nb_fields; @@ -2994,6 +3524,69 @@ skip_domain: ret = LTTNG_OK; break; } + case LTTNG_LIST_SYSCALLS: + { + struct lttng_event *events; + ssize_t nb_events; + + nb_events = cmd_list_syscalls(&events); + if (nb_events < 0) { + /* Return value is a negative lttng_error_code. */ + ret = -nb_events; + 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) * nb_events); + if (ret < 0) { + free(events); + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, events, + sizeof(struct lttng_event) * nb_events); + + free(events); + + ret = LTTNG_OK; + break; + } + case LTTNG_LIST_TRACKER_PIDS: + { + int32_t *pids = NULL; + ssize_t nr_pids; + + nr_pids = cmd_list_tracker_pids(cmd_ctx->session, + cmd_ctx->lsm->domain.type, &pids); + if (nr_pids < 0) { + /* Return value is a negative lttng_error_code. */ + ret = -nr_pids; + 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(int32_t) * nr_pids); + if (ret < 0) { + free(pids); + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, pids, + sizeof(int) * nr_pids); + + free(pids); + + ret = LTTNG_OK; + break; + } case LTTNG_SET_CONSUMER_URI: { size_t nb_uri, len; @@ -3024,31 +3617,12 @@ skip_domain: goto error; } - ret = cmd_set_consumer_uri(cmd_ctx->lsm->domain.type, cmd_ctx->session, - nb_uri, uris); + ret = cmd_set_consumer_uri(cmd_ctx->session, nb_uri, uris); + free(uris); if (ret != LTTNG_OK) { - free(uris); goto error; } - /* - * XXX: 0 means that this URI should be applied on the session. Should - * be a DOMAIN enuam. - */ - if (cmd_ctx->lsm->domain.type == 0) { - /* Add the URI for the UST session if a consumer is present. */ - if (cmd_ctx->session->ust_session && - cmd_ctx->session->ust_session->consumer) { - ret = cmd_set_consumer_uri(LTTNG_DOMAIN_UST, cmd_ctx->session, - nb_uri, uris); - } else if (cmd_ctx->session->kernel_session && - cmd_ctx->session->kernel_session->consumer) { - ret = cmd_set_consumer_uri(LTTNG_DOMAIN_KERNEL, - cmd_ctx->session, nb_uri, uris); - } - } - - free(uris); break; } @@ -3114,7 +3688,7 @@ skip_domain: case LTTNG_LIST_DOMAINS: { ssize_t nb_dom; - struct lttng_domain *domains; + struct lttng_domain *domains = NULL; nb_dom = cmd_list_domains(cmd_ctx->session, &domains); if (nb_dom < 0) { @@ -3141,7 +3715,7 @@ skip_domain: case LTTNG_LIST_CHANNELS: { int nb_chan; - struct lttng_channel *channels; + struct lttng_channel *channels = NULL; nb_chan = cmd_list_channels(cmd_ctx->lsm->domain.type, cmd_ctx->session, &channels); @@ -3242,49 +3816,37 @@ skip_domain: cmd_ctx->lsm->u.reg.path, cdata); break; } - case LTTNG_ENABLE_EVENT_WITH_FILTER: + case LTTNG_DATA_PENDING: { - struct lttng_filter_bytecode *bytecode; + int pending_ret; - if (cmd_ctx->lsm->u.enable.bytecode_len > LTTNG_FILTER_MAX_LEN) { - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - if (cmd_ctx->lsm->u.enable.bytecode_len == 0) { - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - bytecode = zmalloc(cmd_ctx->lsm->u.enable.bytecode_len); - if (!bytecode) { - ret = LTTNG_ERR_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.enable.bytecode_len); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_FILTER_INVAL; - goto error; + /* 1 byte to return whether or not data is pending */ + ret = setup_lttng_msg(cmd_ctx, 1); + if (ret < 0) { + goto setup_error; } - if (bytecode->len + sizeof(*bytecode) - != cmd_ctx->lsm->u.enable.bytecode_len) { - free(bytecode); - ret = LTTNG_ERR_FILTER_INVAL; - goto error; + pending_ret = cmd_data_pending(cmd_ctx->session); + /* + * FIXME + * + * This function may returns 0 or 1 to indicate whether or not + * there is data pending. In case of error, it should return an + * LTTNG_ERR code. However, some code paths may still return + * a nondescript error code, which we handle by returning an + * "unknown" error. + */ + if (pending_ret == 0 || pending_ret == 1) { + ret = LTTNG_OK; + } else if (pending_ret < 0) { + ret = LTTNG_ERR_UNK; + goto setup_error; + } else { + ret = pending_ret; + goto setup_error; } - ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, - cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, bytecode, kernel_poll_pipe[1]); - break; - } - case LTTNG_DATA_PENDING: - { - ret = cmd_data_pending(cmd_ctx->session); + *cmd_ctx->llm->payload = (uint8_t) pending_ret; break; } case LTTNG_SNAPSHOT_ADD_OUTPUT: @@ -3426,6 +3988,18 @@ skip_domain: free(uris); break; } + case LTTNG_SAVE_SESSION: + { + ret = cmd_save_sessions(&cmd_ctx->lsm->u.save_session.attr, + &cmd_ctx->creds); + break; + } + case LTTNG_SET_SESSION_SHM_PATH: + { + ret = cmd_set_session_shm_path(cmd_ctx->session, + cmd_ctx->lsm->u.set_shm_path.shm_path); + break; + } default: ret = LTTNG_ERR_UND; break; @@ -3524,6 +4098,8 @@ static void *thread_manage_health(void *data) goto error; } + sessiond_notify_ready(); + while (1) { DBG("Health check ready"); @@ -3547,6 +4123,11 @@ restart: revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -3588,7 +4169,7 @@ restart: rcu_thread_online(); - reply.ret_code = 0; + memset(&reply, 0, sizeof(reply)); for (i = 0; i < NR_HEALTH_SESSIOND_TYPES; i++) { /* * health_check_state returns 0 if health is @@ -3652,10 +4233,6 @@ static void *thread_manage_clients(void *data) health_register(health_sessiond, HEALTH_SESSIOND_TYPE_CMD); - if (testpoint(thread_manage_clients)) { - goto error_testpoint; - } - health_code_update(); ret = lttcomm_listen_unix_sock(client_sock); @@ -3678,14 +4255,19 @@ static void *thread_manage_clients(void *data) goto error; } - /* - * Notify parent pid that we are ready to accept command for client side. - */ - if (opt_sig_parent) { - kill(ppid, SIGUSR1); + sessiond_notify_ready(); + ret = sem_post(&load_info->message_thread_ready); + if (ret) { + PERROR("sem_post message_thread_ready"); + goto error; + } + + /* This testpoint is after we signal readiness to the parent. */ + if (testpoint(sessiond_thread_manage_clients)) { + goto error; } - if (testpoint(thread_manage_clients_before_loop)) { + if (testpoint(sessiond_thread_manage_clients_before_loop)) { goto error; } @@ -3718,6 +4300,11 @@ static void *thread_manage_clients(void *data) health_code_update(); + if (!revents) { + /* No activity for this FD (poll implementation). */ + continue; + } + /* Thread quit pipe has been closed. Killing thread. */ ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { @@ -3826,9 +4413,10 @@ static void *thread_manage_clients(void *data) health_code_update(); - DBG("Sending response (size: %d, retcode: %s)", + DBG("Sending response (size: %d, retcode: %s (%d))", cmd_ctx->lttng_msg_size, - lttng_strerror(-cmd_ctx->llm->ret_code)); + lttng_strerror(-cmd_ctx->llm->ret_code), + cmd_ctx->llm->ret_code); ret = send_unix_sock(sock, cmd_ctx->llm, cmd_ctx->lttng_msg_size); if (ret < 0) { ERR("Failed to send data back to client"); @@ -3860,7 +4448,6 @@ error: error_listen: error_create_poll: -error_testpoint: unlink(client_unix_sock_path); if (client_sock >= 0) { ret = close(client_sock); @@ -3879,6 +4466,28 @@ error_testpoint: DBG("Client thread dying"); rcu_unregister_thread(); + + /* + * Since we are creating the consumer threads, we own them, so we need + * to join them before our thread exits. + */ + ret = join_consumer_thread(&kconsumer_data); + if (ret) { + errno = ret; + PERROR("join_consumer"); + } + + ret = join_consumer_thread(&ustconsumer32_data); + if (ret) { + errno = ret; + PERROR("join_consumer ust32"); + } + + ret = join_consumer_thread(&ustconsumer64_data); + if (ret) { + errno = ret; + PERROR("join_consumer ust64"); + } return NULL; } @@ -3903,139 +4512,454 @@ static void usage(void) fprintf(stderr, " --consumerd64-path PATH Specify path for the 64-bit UST consumer daemon binary\n"); fprintf(stderr, " --consumerd64-libdir PATH Specify path for the 64-bit UST consumer daemon libraries\n"); fprintf(stderr, " -d, --daemonize Start as a daemon.\n"); + fprintf(stderr, " -b, --background Start as a daemon, keeping console open.\n"); fprintf(stderr, " -g, --group NAME Specify the tracing group name. (default: tracing)\n"); fprintf(stderr, " -V, --version Show version number.\n"); - fprintf(stderr, " -S, --sig-parent Send SIGCHLD to parent pid to notify readiness.\n"); + fprintf(stderr, " -S, --sig-parent Send SIGUSR1 to parent pid to notify readiness.\n"); fprintf(stderr, " -q, --quiet No output at all.\n"); fprintf(stderr, " -v, --verbose Verbose mode. Activate DBG() macro.\n"); fprintf(stderr, " -p, --pidfile FILE Write a pid to FILE name overriding the default value.\n"); fprintf(stderr, " --verbose-consumer Verbose mode for consumer. Activate DBG() macro.\n"); fprintf(stderr, " --no-kernel Disable kernel tracer\n"); + fprintf(stderr, " --agent-tcp-port Agent registration TCP port\n"); + fprintf(stderr, " -f --config PATH Load daemon configuration file\n"); + fprintf(stderr, " -l --load PATH Load session configuration\n"); + fprintf(stderr, " --kmod-probes Specify kernel module probes to load\n"); + fprintf(stderr, " --extra-kmod-probes Specify extra kernel module probes to load\n"); +} + +static int string_match(const char *str1, const char *str2) +{ + return (str1 && str2) && !strcmp(str1, str2); } /* - * daemon argument parsing + * Take an option from the getopt output and set it in the right variable to be + * used later. + * + * Return 0 on success else a negative value. */ -static int parse_args(int argc, char **argv) +static int set_option(int opt, const char *arg, const char *optname) { - int c; - - static struct option long_options[] = { - { "client-sock", 1, 0, 'c' }, - { "apps-sock", 1, 0, 'a' }, - { "kconsumerd-cmd-sock", 1, 0, 'C' }, - { "kconsumerd-err-sock", 1, 0, 'E' }, - { "ustconsumerd32-cmd-sock", 1, 0, 'G' }, - { "ustconsumerd32-err-sock", 1, 0, 'H' }, - { "ustconsumerd64-cmd-sock", 1, 0, 'D' }, - { "ustconsumerd64-err-sock", 1, 0, 'F' }, - { "consumerd32-path", 1, 0, 'u' }, - { "consumerd32-libdir", 1, 0, 'U' }, - { "consumerd64-path", 1, 0, 't' }, - { "consumerd64-libdir", 1, 0, 'T' }, - { "daemonize", 0, 0, 'd' }, - { "sig-parent", 0, 0, 'S' }, - { "help", 0, 0, 'h' }, - { "group", 1, 0, 'g' }, - { "version", 0, 0, 'V' }, - { "quiet", 0, 0, 'q' }, - { "verbose", 0, 0, 'v' }, - { "verbose-consumer", 0, 0, 'Z' }, - { "no-kernel", 0, 0, 'N' }, - { "pidfile", 1, 0, 'p' }, - { NULL, 0, 0, 0 } - }; + int ret = 0; - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t:p:", - long_options, &option_index); - if (c == -1) { - break; - } + if (arg && arg[0] == '\0') { + /* + * This only happens if the value is read from daemon config + * file. This means the option requires an argument and the + * configuration file contains a line such as: + * my_option = + */ + ret = -EINVAL; + goto end; + } - switch (c) { - case 0: - fprintf(stderr, "option %s", long_options[option_index].name); - if (optarg) { - fprintf(stderr, " with arg %s\n", optarg); + if (string_match(optname, "client-sock") || opt == 'c') { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-c, --client-sock"); + } else { + snprintf(client_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "apps-sock") || opt == 'a') { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-a, --apps-sock"); + } else { + snprintf(apps_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "daemonize") || opt == 'd') { + opt_daemon = 1; + } else if (string_match(optname, "background") || opt == 'b') { + opt_background = 1; + } else if (string_match(optname, "group") || opt == 'g') { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-g, --group"); + } else { + /* + * If the override option is set, the pointer points to a + * *non* const thus freeing it even though the variable type is + * set to const. + */ + if (tracing_group_name_override) { + free((void *) tracing_group_name); } - break; - case 'c': - snprintf(client_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'a': - snprintf(apps_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'd': - opt_daemon = 1; - break; - case 'g': - tracing_group_name = optarg; - break; - case 'h': - usage(); - exit(EXIT_FAILURE); - case 'V': - fprintf(stdout, "%s\n", VERSION); - exit(EXIT_SUCCESS); - case 'S': - opt_sig_parent = 1; - break; - case 'E': - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'C': - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'F': - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'D': - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'H': - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'G': - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'N': - opt_no_kernel = 1; - break; - case 'q': - lttng_opt_quiet = 1; - break; - case 'v': - /* Verbose level can increase using multiple -v */ - lttng_opt_verbose += 1; - break; - case 'Z': + tracing_group_name = strdup(arg); + if (!tracing_group_name) { + PERROR("strdup"); + ret = -ENOMEM; + } + tracing_group_name_override = 1; + } + } else if (string_match(optname, "help") || opt == 'h') { + usage(); + exit(EXIT_SUCCESS); + } else if (string_match(optname, "version") || opt == 'V') { + fprintf(stdout, "%s\n", VERSION); + exit(EXIT_SUCCESS); + } else if (string_match(optname, "sig-parent") || opt == 'S') { + opt_sig_parent = 1; + } else if (string_match(optname, "kconsumerd-err-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--kconsumerd-err-sock"); + } else { + snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "kconsumerd-cmd-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--kconsumerd-cmd-sock"); + } else { + snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "ustconsumerd64-err-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--ustconsumerd64-err-sock"); + } else { + snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "ustconsumerd64-cmd-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--ustconsumerd64-cmd-sock"); + } else { + snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "ustconsumerd32-err-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--ustconsumerd32-err-sock"); + } else { + snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "ustconsumerd32-cmd-sock")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--ustconsumerd32-cmd-sock"); + } else { + snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", arg); + } + } else if (string_match(optname, "no-kernel")) { + opt_no_kernel = 1; + } else if (string_match(optname, "quiet") || opt == 'q') { + lttng_opt_quiet = 1; + } else if (string_match(optname, "verbose") || opt == 'v') { + /* Verbose level can increase using multiple -v */ + if (arg) { + /* Value obtained from config file */ + lttng_opt_verbose = config_parse_value(arg); + } else { + /* -v used on command line */ + lttng_opt_verbose++; + } + /* Clamp value to [0, 3] */ + lttng_opt_verbose = lttng_opt_verbose < 0 ? 0 : + (lttng_opt_verbose <= 3 ? lttng_opt_verbose : 3); + } else if (string_match(optname, "verbose-consumer")) { + if (arg) { + opt_verbose_consumer = config_parse_value(arg); + } else { opt_verbose_consumer += 1; + } + } else if (string_match(optname, "consumerd32-path")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--consumerd32-path"); + } else { + if (consumerd32_bin_override) { + free((void *) consumerd32_bin); + } + consumerd32_bin = strdup(arg); + if (!consumerd32_bin) { + PERROR("strdup"); + ret = -ENOMEM; + } + consumerd32_bin_override = 1; + } + } else if (string_match(optname, "consumerd32-libdir")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--consumerd32-libdir"); + } else { + if (consumerd32_libdir_override) { + free((void *) consumerd32_libdir); + } + consumerd32_libdir = strdup(arg); + if (!consumerd32_libdir) { + PERROR("strdup"); + ret = -ENOMEM; + } + consumerd32_libdir_override = 1; + } + } else if (string_match(optname, "consumerd64-path")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--consumerd64-path"); + } else { + if (consumerd64_bin_override) { + free((void *) consumerd64_bin); + } + consumerd64_bin = strdup(arg); + if (!consumerd64_bin) { + PERROR("strdup"); + ret = -ENOMEM; + } + consumerd64_bin_override = 1; + } + } else if (string_match(optname, "consumerd64-libdir")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--consumerd64-libdir"); + } else { + if (consumerd64_libdir_override) { + free((void *) consumerd64_libdir); + } + consumerd64_libdir = strdup(arg); + if (!consumerd64_libdir) { + PERROR("strdup"); + ret = -ENOMEM; + } + consumerd64_libdir_override = 1; + } + } else if (string_match(optname, "pidfile") || opt == 'p') { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-p, --pidfile"); + } else { + free(opt_pidfile); + opt_pidfile = strdup(arg); + if (!opt_pidfile) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } else if (string_match(optname, "agent-tcp-port")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--agent-tcp-port"); + } else { + unsigned long v; + + if (!arg) { + ret = -EINVAL; + goto end; + } + errno = 0; + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --agent-tcp-port parameter: %s", arg); + return -1; + } + if (v == 0 || v >= 65535) { + ERR("Port overflow in --agent-tcp-port parameter: %s", arg); + return -1; + } + agent_tcp_port = (uint32_t) v; + DBG3("Agent TCP port set to non default: %u", agent_tcp_port); + } + } else if (string_match(optname, "load") || opt == 'l') { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-l, --load"); + } else { + free(opt_load_session_path); + opt_load_session_path = strdup(arg); + if (!opt_load_session_path) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } else if (string_match(optname, "kmod-probes")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--kmod-probes"); + } else { + free(kmod_probes_list); + kmod_probes_list = strdup(arg); + if (!kmod_probes_list) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } else if (string_match(optname, "extra-kmod-probes")) { + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "--extra-kmod-probes"); + } else { + free(kmod_extra_probes_list); + kmod_extra_probes_list = strdup(arg); + if (!kmod_extra_probes_list) { + PERROR("strdup"); + ret = -ENOMEM; + } + } + } else if (string_match(optname, "config") || opt == 'f') { + /* This is handled in set_options() thus silent skip. */ + goto end; + } else { + /* Unknown option or other error. + * Error is printed by getopt, just return */ + ret = -1; + } + +end: + if (ret == -EINVAL) { + const char *opt_name = "unknown"; + int i; + + for (i = 0; i < sizeof(long_options) / sizeof(struct option); + i++) { + if (opt == long_options[i].val) { + opt_name = long_options[i].name; + break; + } + } + + WARN("Invalid argument provided for option \"%s\", using default value.", + opt_name); + } + + return ret; +} + +/* + * config_entry_handler_cb used to handle options read from a config file. + * See config_entry_handler_cb comment in common/config/config.h for the + * return value conventions. + */ +static int config_entry_handler(const struct config_entry *entry, void *unused) +{ + int ret = 0, i; + + if (!entry || !entry->name || !entry->value) { + ret = -EINVAL; + goto end; + } + + /* Check if the option is to be ignored */ + for (i = 0; i < sizeof(config_ignore_options) / sizeof(char *); i++) { + if (!strcmp(entry->name, config_ignore_options[i])) { + goto end; + } + } + + for (i = 0; i < (sizeof(long_options) / sizeof(struct option)) - 1; + i++) { + + /* Ignore if not fully matched. */ + if (strcmp(entry->name, long_options[i].name)) { + continue; + } + + /* + * If the option takes no argument on the command line, we have to + * check if the value is "true". We support non-zero numeric values, + * true, on and yes. + */ + if (!long_options[i].has_arg) { + ret = config_parse_value(entry->value); + if (ret <= 0) { + if (ret) { + WARN("Invalid configuration value \"%s\" for option %s", + entry->value, entry->name); + } + /* False, skip boolean config option. */ + goto end; + } + } + + ret = set_option(long_options[i].val, entry->value, entry->name); + goto end; + } + + WARN("Unrecognized option \"%s\" in daemon configuration file.", entry->name); + +end: + return ret; +} + +/* + * daemon configuration loading and argument parsing + */ +static int set_options(int argc, char **argv) +{ + int ret = 0, c = 0, option_index = 0; + int orig_optopt = optopt, orig_optind = optind; + char *optstring; + const char *config_path = NULL; + + optstring = utils_generate_optstring(long_options, + sizeof(long_options) / sizeof(struct option)); + if (!optstring) { + ret = -ENOMEM; + goto end; + } + + /* Check for the --config option */ + while ((c = getopt_long(argc, argv, optstring, long_options, + &option_index)) != -1) { + if (c == '?') { + ret = -EINVAL; + goto end; + } else if (c != 'f') { + /* if not equal to --config option. */ + continue; + } + + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-f, --config"); + } else { + config_path = utils_expand_path(optarg); + if (!config_path) { + ERR("Failed to resolve path: %s", optarg); + } + } + } + + ret = config_get_section_entries(config_path, config_section_name, + config_entry_handler, NULL); + if (ret) { + if (ret > 0) { + ERR("Invalid configuration option at line %i", ret); + ret = -1; + } + goto end; + } + + /* Reset getopt's global state */ + optopt = orig_optopt; + optind = orig_optind; + while (1) { + option_index = -1; + /* + * getopt_long() will not set option_index if it encounters a + * short option. + */ + c = getopt_long(argc, argv, optstring, long_options, + &option_index); + if (c == -1) { break; - case 'u': - consumerd32_bin= optarg; - break; - case 'U': - consumerd32_libdir = optarg; - break; - case 't': - consumerd64_bin = optarg; - break; - case 'T': - consumerd64_libdir = optarg; - break; - case 'p': - opt_pidfile = optarg; + } + + /* + * Pass NULL as the long option name if popt left the index + * unset. + */ + ret = set_option(c, optarg, + option_index < 0 ? NULL : + long_options[option_index].name); + if (ret < 0) { break; - default: - /* Unknown option or other error. - * Error is printed by getopt, just return */ - return -1; } } - return 0; +end: + free(optstring); + return ret; } /* @@ -4306,6 +5230,9 @@ static void sighandler(int sig) DBG("SIGTERM caught"); stop_threads(); break; + case SIGUSR1: + CMM_STORE_SHARED(recv_child_signal, 1); + break; default: break; } @@ -4344,7 +5271,12 @@ static int set_signal_handler(void) return ret; } - DBG("Signal handler set for SIGTERM, SIGPIPE and SIGINT"); + if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) { + PERROR("sigaction"); + return ret; + } + + DBG("Signal handler set for SIGTERM, SIGUSR1, SIGPIPE and SIGINT"); return ret; } @@ -4371,7 +5303,7 @@ static void set_ulimit(void) /* * Write pidfile using the rundir and opt_pidfile. */ -static void write_pidfile(void) +static int write_pidfile(void) { int ret; char pidfile_path[PATH_MAX]; @@ -4391,13 +5323,55 @@ static void write_pidfile(void) } /* - * Create pid file in rundir. Return value is of no importance. The - * execution will continue even though we are not able to write the file. + * Create pid file in rundir. + */ + ret = utils_create_pid_file(getpid(), pidfile_path); +error: + return ret; +} + +/* + * Create lockfile using the rundir and return its fd. + */ +static int create_lockfile(void) +{ + int ret; + char lockfile_path[PATH_MAX]; + + ret = generate_lock_file_path(lockfile_path, sizeof(lockfile_path)); + if (ret < 0) { + goto error; + } + + ret = utils_create_lock_file(lockfile_path); +error: + return ret; +} + +/* + * Write agent TCP port using the rundir. + */ +static int write_agent_port(void) +{ + int ret; + char path[PATH_MAX]; + + assert(rundir); + + ret = snprintf(path, sizeof(path), "%s/" + DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE, rundir); + if (ret < 0) { + PERROR("snprintf agent port path"); + goto error; + } + + /* + * Create TCP agent port file in rundir. */ - (void) utils_create_pid_file(getpid(), pidfile_path); + ret = utils_create_pid_file(agent_tcp_port, path); error: - return; + return ret; } /* @@ -4405,7 +5379,7 @@ error: */ int main(int argc, char **argv) { - int ret = 0; + int ret = 0, retval = 0; void *status; const char *home_path, *env_app_timeout; @@ -4413,6 +5387,11 @@ int main(int argc, char **argv) rcu_register_thread(); + if (set_signal_handler()) { + retval = -1; + goto exit_set_signal_handler; + } + setup_consumerd_path(); page_size = sysconf(_SC_PAGESIZE); @@ -4422,39 +5401,88 @@ int main(int argc, char **argv) WARN("Fallback page size to %ld", page_size); } - /* Parse arguments */ + /* + * Parse arguments and load the daemon configuration file. + * + * We have an exit_options exit path to free memory reserved by + * set_options. This is needed because the rest of sessiond_cleanup() + * depends on ht_cleanup_thread, which depends on lttng_daemonize, which + * depends on set_options. + */ progname = argv[0]; - if ((ret = parse_args(argc, argv)) < 0) { - goto error; + if (set_options(argc, argv)) { + retval = -1; + goto exit_options; } /* Daemonize */ - if (opt_daemon) { + if (opt_daemon || opt_background) { int i; - /* - * fork - * child: setsid, close FD 0, 1, 2, chdir / - * parent: exit (if fork is successful) - */ - ret = daemon(0, 0); + ret = lttng_daemonize(&child_ppid, &recv_child_signal, + !opt_background); if (ret < 0) { - PERROR("daemon"); - goto error; + retval = -1; + goto exit_options; } + /* - * 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. + * 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); } } + /* + * Starting from here, we can create threads. This needs to be after + * lttng_daemonize due to RCU. + */ + + /* + * Initialize the health check subsystem. This call should set the + * appropriate time values. + */ + health_sessiond = health_app_create(NR_HEALTH_SESSIOND_TYPES); + if (!health_sessiond) { + PERROR("health_app_create error"); + retval = -1; + goto exit_health_sessiond_cleanup; + } + + if (init_ht_cleanup_quit_pipe()) { + retval = -1; + goto exit_ht_cleanup_quit_pipe; + } + + /* Setup the thread ht_cleanup communication pipe. */ + if (utils_create_pipe_cloexec(ht_cleanup_pipe)) { + retval = -1; + goto exit_ht_cleanup_pipe; + } + + /* Set up max poll set size */ + if (lttng_poll_set_max_size()) { + retval = -1; + goto exit_set_max_size; + } + + /* Create thread to clean up RCU hash tables */ + ret = pthread_create(&ht_cleanup_thread, NULL, + thread_ht_cleanup, (void *) NULL); + if (ret) { + errno = ret; + PERROR("pthread_create ht_cleanup"); + retval = -1; + goto exit_ht_cleanup; + } + /* Create thread quit pipe */ - if ((ret = init_thread_quit_pipe()) < 0) { - goto error; + if (init_thread_quit_pipe()) { + retval = -1; + goto exit_init_data; } /* Check if daemon is UID = 0 */ @@ -4462,39 +5490,68 @@ int main(int argc, char **argv) if (is_root) { rundir = strdup(DEFAULT_LTTNG_RUNDIR); + if (!rundir) { + retval = -1; + goto exit_init_data; + } /* Create global run dir with root access */ - ret = create_lttng_rundir(rundir); - if (ret < 0) { - goto error; + if (create_lttng_rundir(rundir)) { + retval = -1; + goto exit_init_data; } if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, + ret = snprintf(apps_unix_sock_path, PATH_MAX, DEFAULT_GLOBAL_APPS_UNIX_SOCK); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, + ret = snprintf(client_unix_sock_path, PATH_MAX, DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } /* Set global SHM for ust */ if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, + ret = snprintf(wait_shm_path, PATH_MAX, DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } if (strlen(health_unix_sock_path) == 0) { - snprintf(health_unix_sock_path, sizeof(health_unix_sock_path), + ret = snprintf(health_unix_sock_path, + sizeof(health_unix_sock_path), DEFAULT_GLOBAL_HEALTH_UNIX_SOCK); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } /* Setup kernel consumerd path */ - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, + ret = snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, DEFAULT_KCONSUMERD_ERR_SOCK_PATH, rundir); - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, + if (ret < 0) { + retval = -1; + goto exit_init_data; + } + ret = snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, DEFAULT_KCONSUMERD_CMD_SOCK_PATH, rundir); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } DBG2("Kernel consumer err path: %s", kconsumer_data.err_unix_sock_path); @@ -4505,8 +5562,8 @@ int main(int argc, char **argv) if (home_path == NULL) { /* TODO: Add --socket PATH option */ ERR("Can't get HOME directory for sockets creation."); - ret = -EPERM; - goto error; + retval = -1; + goto exit_init_data; } /* @@ -4515,39 +5572,66 @@ int main(int argc, char **argv) */ ret = asprintf(&rundir, DEFAULT_LTTNG_HOME_RUNDIR, home_path); if (ret < 0) { - ret = -ENOMEM; - goto error; + retval = -1; + goto exit_init_data; } - ret = create_lttng_rundir(rundir); - if (ret < 0) { - goto error; + if (create_lttng_rundir(rundir)) { + retval = -1; + goto exit_init_data; } if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_HOME_APPS_UNIX_SOCK, home_path); + ret = snprintf(apps_unix_sock_path, PATH_MAX, + DEFAULT_HOME_APPS_UNIX_SOCK, + home_path); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } /* Set the cli tool unix socket path */ if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path); + ret = snprintf(client_unix_sock_path, PATH_MAX, + DEFAULT_HOME_CLIENT_UNIX_SOCK, + home_path); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } /* Set global SHM for ust */ if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, - DEFAULT_HOME_APPS_WAIT_SHM_PATH, getuid()); + ret = snprintf(wait_shm_path, PATH_MAX, + DEFAULT_HOME_APPS_WAIT_SHM_PATH, + getuid()); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } /* Set health check Unix path */ if (strlen(health_unix_sock_path) == 0) { - snprintf(health_unix_sock_path, sizeof(health_unix_sock_path), - DEFAULT_HOME_HEALTH_UNIX_SOCK, home_path); + ret = snprintf(health_unix_sock_path, + sizeof(health_unix_sock_path), + DEFAULT_HOME_HEALTH_UNIX_SOCK, + home_path); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } } } + lockfile_fd = create_lockfile(); + if (lockfile_fd < 0) { + retval = -1; + goto exit_init_data; + } + /* Set consumer initial state */ kernel_consumerd_state = CONSUMER_STOPPED; ust_consumerd_state = CONSUMER_STOPPED; @@ -4558,10 +5642,20 @@ int main(int argc, char **argv) DBG("LTTng run directory path: %s", rundir); /* 32 bits consumerd path setup */ - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, + ret = snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, + if (ret < 0) { + PERROR("snprintf 32-bit consumer error socket path"); + retval = -1; + goto exit_init_data; + } + ret = snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, rundir); + if (ret < 0) { + PERROR("snprintf 32-bit consumer command socket path"); + retval = -1; + goto exit_init_data; + } DBG2("UST consumer 32 bits err path: %s", ustconsumer32_data.err_unix_sock_path); @@ -4569,10 +5663,20 @@ int main(int argc, char **argv) ustconsumer32_data.cmd_unix_sock_path); /* 64 bits consumerd path setup */ - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, + ret = snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, + if (ret < 0) { + PERROR("snprintf 64-bit consumer error socket path"); + retval = -1; + goto exit_init_data; + } + ret = snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, rundir); + if (ret < 0) { + PERROR("snprintf 64-bit consumer command socket path"); + retval = -1; + goto exit_init_data; + } DBG2("UST consumer 64 bits err path: %s", ustconsumer64_data.err_unix_sock_path); @@ -4582,22 +5686,35 @@ int main(int argc, char **argv) /* * See if daemon already exist. */ - if ((ret = check_existing_daemon()) < 0) { + if (check_existing_daemon()) { ERR("Already running daemon.\n"); /* * We do not goto exit because we must not cleanup() * because a daemon is already running. */ - goto error; + retval = -1; + goto exit_init_data; } /* * Init UST app hash table. Alloc hash table before this point since * cleanup() can get called after that point. */ - ust_app_ht_alloc(); + if (ust_app_ht_alloc()) { + ERR("Failed to allocate UST app hash table"); + retval = -1; + goto exit_init_data; + } - /* After this point, we can safely call cleanup() with "goto exit" */ + /* + * Initialize agent app hash table. We allocate the hash table here + * since cleanup() can get called after this point. + */ + if (agent_app_ht_alloc()) { + ERR("Failed to allocate Agent app hash table"); + retval = -1; + goto exit_init_data; + } /* * These actions must be executed as root. We do that *after* setting up @@ -4606,14 +5723,22 @@ int main(int argc, char **argv) * kernel tracer. */ if (is_root) { - ret = set_consumer_sockets(&kconsumer_data, rundir); - if (ret < 0) { - goto exit; + if (set_consumer_sockets(&kconsumer_data, rundir)) { + retval = -1; + goto exit_init_data; } /* Setup kernel tracer */ if (!opt_no_kernel) { init_kernel_tracer(); + if (kernel_tracer_fd >= 0) { + ret = syscall_init_table(); + if (ret < 0) { + ERR("Unable to populate syscall table. " + "Syscall tracing won't work " + "for this session daemon."); + } + } } /* Set ulimit for open files */ @@ -4622,28 +5747,26 @@ int main(int argc, char **argv) /* init lttng_fd tracking must be done after set_ulimit. */ lttng_fd_init(); - ret = set_consumer_sockets(&ustconsumer64_data, rundir); - if (ret < 0) { - goto exit; + if (set_consumer_sockets(&ustconsumer64_data, rundir)) { + retval = -1; + goto exit_init_data; } - ret = set_consumer_sockets(&ustconsumer32_data, rundir); - if (ret < 0) { - goto exit; - } - - if ((ret = set_signal_handler()) < 0) { - goto exit; + if (set_consumer_sockets(&ustconsumer32_data, rundir)) { + retval = -1; + goto exit_init_data; } /* Setup the needed unix socket */ - if ((ret = init_daemon_socket()) < 0) { - goto exit; + if (init_daemon_socket()) { + retval = -1; + goto exit_init_data; } /* Set credentials to socket */ - if (is_root && ((ret = set_permissions(rundir)) < 0)) { - goto exit; + if (is_root && set_permissions(rundir)) { + retval = -1; + goto exit_init_data; } /* Get parent pid if -S, --sig-parent is specified. */ @@ -4653,24 +5776,22 @@ int main(int argc, char **argv) /* Setup the kernel pipe for waking up the kernel thread */ if (is_root && !opt_no_kernel) { - if ((ret = utils_create_pipe_cloexec(kernel_poll_pipe)) < 0) { - goto exit; + if (utils_create_pipe_cloexec(kernel_poll_pipe)) { + retval = -1; + goto exit_init_data; } } - /* 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; + if (utils_create_pipe_cloexec(apps_cmd_pipe)) { + retval = -1; + goto exit_init_data; } /* Setup the thread apps notify communication pipe. */ - if (utils_create_pipe_cloexec(apps_cmd_notify_pipe) < 0) { - goto exit; + if (utils_create_pipe_cloexec(apps_cmd_notify_pipe)) { + retval = -1; + goto exit_init_data; } /* Initialize global buffer per UID and PID registry. */ @@ -4678,17 +5799,14 @@ int main(int argc, char **argv) buffer_reg_init_pid_registry(); /* Init UST command queue. */ - cds_wfq_init(&ust_cmd_queue.queue); + cds_wfcq_init(&ust_cmd_queue.head, &ust_cmd_queue.tail); /* - * Get session list pointer. This pointer MUST NOT be free(). This list is - * statically declared in session.c + * Get session list pointer. This pointer MUST NOT be free'd. This list + * is statically declared in session.c */ session_list_ptr = session_get_list(); - /* Set up max poll set size */ - lttng_poll_set_max_size(); - cmd_init(); /* Check for the application socket timeout env variable. */ @@ -4699,177 +5817,253 @@ int main(int argc, char **argv) app_socket_timeout = DEFAULT_APP_SOCKET_RW_TIMEOUT; } - write_pidfile(); + ret = write_pidfile(); + if (ret) { + ERR("Error in write_pidfile"); + retval = -1; + goto exit_init_data; + } + ret = write_agent_port(); + if (ret) { + ERR("Error in write_agent_port"); + retval = -1; + goto exit_init_data; + } /* Initialize communication library */ lttcomm_init(); - /* This is to get the TCP timeout value. */ + /* Initialize TCP timeout values */ lttcomm_inet_init(); - /* - * Initialize the health check subsystem. This call should set the - * appropriate time values. - */ - health_sessiond = health_app_create(NR_HEALTH_SESSIOND_TYPES); - 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; + if (load_session_init_data(&load_info) < 0) { + retval = -1; + goto exit_init_data; } + load_info->path = opt_load_session_path; - /* Create thread to manage the client socket */ + /* Create health-check thread */ ret = pthread_create(&health_thread, NULL, thread_manage_health, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create health"); + retval = -1; goto exit_health; } /* Create thread to manage the client socket */ ret = pthread_create(&client_thread, NULL, thread_manage_clients, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create clients"); + retval = -1; goto exit_client; } /* Create thread to dispatch registration */ ret = pthread_create(&dispatch_thread, NULL, thread_dispatch_ust_registration, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create dispatch"); + retval = -1; goto exit_dispatch; } /* Create thread to manage application registration. */ ret = pthread_create(®_apps_thread, NULL, thread_registration_apps, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create registration"); + retval = -1; goto exit_reg_apps; } /* Create thread to manage application socket */ ret = pthread_create(&apps_thread, NULL, thread_manage_apps, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create apps"); + retval = -1; goto exit_apps; } /* Create thread to manage application notify socket */ ret = pthread_create(&apps_notify_thread, NULL, ust_thread_manage_notify, (void *) NULL); - if (ret != 0) { - PERROR("pthread_create apps"); + if (ret) { + errno = ret; + PERROR("pthread_create notify"); + retval = -1; goto exit_apps_notify; } + /* Create agent registration thread. */ + ret = pthread_create(&agent_reg_thread, NULL, + agent_thread_manage_registration, (void *) NULL); + if (ret) { + errno = ret; + PERROR("pthread_create agent"); + retval = -1; + goto exit_agent_reg; + } + /* Don't start this thread if kernel tracing is not requested nor root */ if (is_root && !opt_no_kernel) { /* Create kernel thread to manage kernel event */ ret = pthread_create(&kernel_thread, NULL, thread_manage_kernel, (void *) NULL); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_create kernel"); + retval = -1; goto exit_kernel; } + } + + /* Create session loading thread. */ + ret = pthread_create(&load_session_thread, NULL, thread_load_session, + load_info); + if (ret) { + errno = ret; + PERROR("pthread_create load_session_thread"); + retval = -1; + goto exit_load_session; + } + /* + * This is where we start awaiting program completion (e.g. through + * signal that asks threads to teardown). + */ + + ret = pthread_join(load_session_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join load_session_thread"); + retval = -1; + } +exit_load_session: + + if (is_root && !opt_no_kernel) { ret = pthread_join(kernel_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } } - exit_kernel: + + ret = pthread_join(agent_reg_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join agent"); + retval = -1; + } +exit_agent_reg: + ret = pthread_join(apps_notify_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join apps notify"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - exit_apps_notify: + ret = pthread_join(apps_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join apps"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - - exit_apps: + ret = pthread_join(reg_apps_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - exit_reg_apps: + + /* + * Join dispatch thread after joining reg_apps_thread to ensure + * we don't leak applications in the queue. + */ ret = pthread_join(dispatch_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ + retval = -1; } - exit_dispatch: + ret = pthread_join(client_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - - ret = join_consumer_thread(&kconsumer_data); - if (ret != 0) { - PERROR("join_consumer"); - goto error; /* join error, exit without cleanup */ + retval = -1; } +exit_client: - ret = join_consumer_thread(&ustconsumer32_data); - if (ret != 0) { - PERROR("join_consumer ust32"); - goto error; /* join error, exit without cleanup */ + ret = pthread_join(health_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join health thread"); + retval = -1; } +exit_health: - ret = join_consumer_thread(&ustconsumer64_data); - if (ret != 0) { - PERROR("join_consumer ust64"); - goto error; /* join error, exit without cleanup */ - } +exit_init_data: + /* + * sessiond_cleanup() is called when no other thread is running, except + * the ht_cleanup thread, which is needed to destroy the hash tables. + */ + rcu_thread_online(); + sessiond_cleanup(); + rcu_thread_offline(); + rcu_unregister_thread(); -exit_client: - ret = pthread_join(health_thread, &status); - if (ret != 0) { - PERROR("pthread_join health thread"); - goto error; /* join error, exit without cleanup */ + ret = notify_thread_pipe(ht_cleanup_quit_pipe[1]); + if (ret < 0) { + ERR("write error on ht_cleanup quit pipe"); + retval = -1; } -exit_health: ret = pthread_join(ht_cleanup_thread, &status); - if (ret != 0) { + if (ret) { + errno = ret; PERROR("pthread_join ht cleanup thread"); - goto error; /* join error, exit without cleanup */ + retval = -1; } exit_ht_cleanup: - health_app_destroy(health_sessiond); -exit_health_sessiond_cleanup: -exit: +exit_set_max_size: + + utils_close_pipe(ht_cleanup_pipe); +exit_ht_cleanup_pipe: + /* - * cleanup() is called when no other thread is running. + * Close the ht_cleanup quit pipe. */ - rcu_thread_online(); - cleanup(); - rcu_thread_offline(); - rcu_unregister_thread(); - if (!ret) { + utils_close_pipe(ht_cleanup_quit_pipe); +exit_ht_cleanup_quit_pipe: + + health_app_destroy(health_sessiond); +exit_health_sessiond_cleanup: + +exit_options: + sessiond_cleanup_options(); + +exit_set_signal_handler: + /* Ensure all prior call_rcu are done. */ + rcu_barrier(); + + if (!retval) { exit(EXIT_SUCCESS); + } else { + exit(EXIT_FAILURE); } -error: - exit(EXIT_FAILURE); }