X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-consumerd%2Flttng-consumerd.c;h=9fb4747536ad7fb3551217f1f3d6aaa0198caae6;hp=b501be8730ca8b4d22a997fbb10001d394aaf906;hb=e9404c27e7cc9d841785e6c4292c1add19fbc1cc;hpb=990570edd474b304d4c935d82be6201d872025e4 diff --git a/src/bin/lttng-consumerd/lttng-consumerd.c b/src/bin/lttng-consumerd/lttng-consumerd.c index b501be873..9fb474753 100644 --- a/src/bin/lttng-consumerd/lttng-consumerd.c +++ b/src/bin/lttng-consumerd/lttng-consumerd.c @@ -2,22 +2,21 @@ * Copyright (C) 2011 - Julien Desfossez * Mathieu Desnoyers * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE +#define _LGPL_SOURCE #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,29 +37,34 @@ #include #include #include -#include #include +#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include "lttng-consumerd.h" +#include "health-consumerd.h" -/* TODO : support UST (all direct kernctl accesses). */ +/* threads (channel handling, poll, metadata, sessiond) */ -/* the two threads (receive fd and poll) */ -static pthread_t threads[2]; +static pthread_t channel_thread, data_thread, metadata_thread, + sessiond_thread, metadata_timer_thread, health_thread; -/* to count the number of time the user pressed ctrl+c */ +/* to count the number of times the user pressed ctrl+c */ static int sigintcount = 0; /* Argument variables */ -int opt_quiet; -int opt_verbose; +int lttng_opt_quiet; /* not static in error.h */ +int lttng_opt_verbose; /* not static in error.h */ +int lttng_opt_mi; /* not static in error.h */ + static int opt_daemon; static const char *progname; static char command_sock_path[PATH_MAX]; /* Global command socket path */ @@ -69,6 +74,21 @@ static enum lttng_consumer_type opt_type = LTTNG_CONSUMER_KERNEL; /* the liblttngconsumerd context */ static struct lttng_consumer_local_data *ctx; +/* Consumerd health monitoring */ +struct health_app *health_consumerd; + +const char *tracing_group_name = DEFAULT_TRACING_GROUP; + +int lttng_consumer_ready = NR_LTTNG_CONSUMER_READY; + +enum lttng_consumer_type lttng_consumer_get_type(void) +{ + if (!ctx) { + return LTTNG_CONSUMER_UNKNOWN; + } + return ctx->type; +} + /* * Signal handler for the daemon */ @@ -79,7 +99,9 @@ static void sighandler(int sig) return; } - lttng_consumer_should_exit(ctx); + if (ctx) { + lttng_consumer_should_exit(ctx); + } } /* @@ -93,25 +115,27 @@ static int set_signal_handler(void) sigset_t sigset; if ((ret = sigemptyset(&sigset)) < 0) { - perror("sigemptyset"); + PERROR("sigemptyset"); return ret; } - sa.sa_handler = sighandler; sa.sa_mask = sigset; sa.sa_flags = 0; + + sa.sa_handler = sighandler; if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } + sa.sa_handler = SIG_IGN; if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { - perror("sigaction"); + PERROR("sigaction"); return ret; } @@ -119,28 +143,30 @@ static int set_signal_handler(void) } /* - * usage function on stderr + * Usage function on stream file. */ -static void usage(void) +static void usage(FILE *fp) { - fprintf(stderr, "Usage: %s OPTIONS\n\nOptions:\n", progname); - fprintf(stderr, " -h, --help " + fprintf(fp, "Usage: %s OPTIONS\n\nOptions:\n", progname); + fprintf(fp, " -h, --help " "Display this usage.\n"); - fprintf(stderr, " -c, --consumerd-cmd-sock PATH " + fprintf(fp, " -c, --consumerd-cmd-sock PATH " "Specify path for the command socket\n"); - fprintf(stderr, " -e, --consumerd-err-sock PATH " + fprintf(fp, " -e, --consumerd-err-sock PATH " "Specify path for the error socket\n"); - fprintf(stderr, " -d, --daemonize " + fprintf(fp, " -d, --daemonize " "Start as a daemon.\n"); - fprintf(stderr, " -q, --quiet " + fprintf(fp, " -q, --quiet " "No output at all.\n"); - fprintf(stderr, " -v, --verbose " + fprintf(fp, " -v, --verbose " "Verbose mode. Activate DBG() macro.\n"); - fprintf(stderr, " -V, --version " + fprintf(fp, " -V, --version " "Show version number.\n"); - fprintf(stderr, " -k, --kernel " + fprintf(fp, " -g, --group NAME " + "Specify the tracing group name. (default: tracing)\n"); + fprintf(fp, " -k, --kernel " "Consumer kernel buffers (default).\n"); - fprintf(stderr, " -u, --ust " + fprintf(fp, " -u, --ust " "Consumer UST buffers.%s\n", #ifdef HAVE_LIBLTTNG_UST_CTL "" @@ -153,14 +179,15 @@ static void usage(void) /* * daemon argument parsing */ -static void parse_args(int argc, char **argv) +static int parse_args(int argc, char **argv) { - int c; + int c, ret = 0; static struct option long_options[] = { { "consumerd-cmd-sock", 1, 0, 'c' }, { "consumerd-err-sock", 1, 0, 'e' }, { "daemonize", 0, 0, 'd' }, + { "group", 1, 0, 'g' }, { "help", 0, 0, 'h' }, { "quiet", 0, 0, 'q' }, { "verbose", 0, 0, 'v' }, @@ -174,35 +201,57 @@ static void parse_args(int argc, char **argv) while (1) { int option_index = 0; - c = getopt_long(argc, argv, "dhqvVku" "c:e:", long_options, &option_index); + c = getopt_long(argc, argv, "dhqvVku" "c:e:g:", + long_options, &option_index); if (c == -1) { break; } switch (c) { case 0: - fprintf(stderr, "option %s", long_options[option_index].name); + fprintf(stderr, "option %s", + long_options[option_index].name); if (optarg) { fprintf(stderr, " with arg %s\n", optarg); + ret = -1; + goto end; } break; case 'c': - snprintf(command_sock_path, PATH_MAX, "%s", optarg); + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-c, --consumerd-cmd-sock"); + } else { + snprintf(command_sock_path, PATH_MAX, "%s", optarg); + } break; case 'e': - snprintf(error_sock_path, PATH_MAX, "%s", optarg); + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-e, --consumerd-err-sock"); + } else { + snprintf(error_sock_path, PATH_MAX, "%s", optarg); + } break; case 'd': opt_daemon = 1; break; + case 'g': + if (lttng_is_setuid_setgid()) { + WARN("Getting '%s' argument from setuid/setgid binary refused for security reasons.", + "-g, --group"); + } else { + tracing_group_name = optarg; + } + break; case 'h': - usage(); - exit(EXIT_FAILURE); + usage(stdout); + exit(EXIT_SUCCESS); case 'q': - opt_quiet = 1; + lttng_opt_quiet = 1; break; case 'v': - opt_verbose = 1; + lttng_opt_verbose = 1; break; case 'V': fprintf(stdout, "%s\n", VERSION); @@ -222,10 +271,32 @@ static void parse_args(int argc, char **argv) break; #endif default: - usage(); - exit(EXIT_FAILURE); + usage(stderr); + ret = -1; + goto end; } } +end: + return ret; +} + +/* + * Set open files limit to unlimited. This daemon can open a large number of + * file descriptors in order to consumer multiple kernel traces. + */ +static void set_ulimit(void) +{ + int ret; + struct rlimit lim; + + /* The kernel does not allowed an infinite limit for open files */ + lim.rlim_cur = 65535; + lim.rlim_max = 65535; + + ret = setrlimit(RLIMIT_NOFILE, &lim); + if (ret < 0) { + PERROR("failed to set open files limit"); + } } /* @@ -233,121 +304,360 @@ static void parse_args(int argc, char **argv) */ int main(int argc, char **argv) { - int i; - int ret = 0; + int ret = 0, retval = 0; void *status; + struct lttng_consumer_local_data *tmp_ctx; + + rcu_register_thread(); + + if (set_signal_handler()) { + retval = -1; + goto exit_set_signal_handler; + } /* Parse arguments */ progname = argv[0]; - parse_args(argc, argv); + if (parse_args(argc, argv)) { + retval = -1; + goto exit_options; + } /* Daemonize */ if (opt_daemon) { + int i; + + /* + * fork + * child: setsid, close FD 0, 1, 2, chdir / + * parent: exit (if fork is successful) + */ ret = daemon(0, 0); if (ret < 0) { - perror("daemon"); - goto error; + PERROR("daemon"); + 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. + */ + for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { + (void) close(i); } } - if (strlen(command_sock_path) == 0) { + /* + * Starting from here, we can create threads. This needs to be after + * lttng_daemonize due to RCU. + */ + + health_consumerd = health_app_create(NR_HEALTH_CONSUMERD_TYPES); + if (!health_consumerd) { + retval = -1; + goto exit_health_consumerd_cleanup; + } + + if (*command_sock_path == '\0') { switch (opt_type) { case LTTNG_CONSUMER_KERNEL: - snprintf(command_sock_path, PATH_MAX, KCONSUMERD_CMD_SOCK_PATH, + ret = snprintf(command_sock_path, PATH_MAX, + DEFAULT_KCONSUMERD_CMD_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; case LTTNG_CONSUMER64_UST: - snprintf(command_sock_path, PATH_MAX, - USTCONSUMERD64_CMD_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + ret = snprintf(command_sock_path, PATH_MAX, + DEFAULT_USTCONSUMERD64_CMD_SOCK_PATH, + DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; case LTTNG_CONSUMER32_UST: - snprintf(command_sock_path, PATH_MAX, - USTCONSUMERD32_CMD_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + ret = snprintf(command_sock_path, PATH_MAX, + DEFAULT_USTCONSUMERD32_CMD_SOCK_PATH, + DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; default: - WARN("Unknown consumerd type"); - goto error; + ERR("Unknown consumerd type"); + retval = -1; + goto exit_init_data; } } /* Init */ - lttng_consumer_init(); + if (lttng_consumer_init()) { + retval = -1; + goto exit_init_data; + } + + /* Initialize communication library */ + lttcomm_init(); + /* Initialize TCP timeout values */ + lttcomm_inet_init(); + + if (!getuid()) { + /* Set limit for open files */ + set_ulimit(); + } + + if (run_as_create_worker(argv[0]) < 0) { + goto exit_init_data; + } /* create the consumer instance with and assign the callbacks */ ctx = lttng_consumer_create(opt_type, lttng_consumer_read_subbuffer, NULL, lttng_consumer_on_recv_stream, NULL); - if (ctx == NULL) { - goto error; + if (!ctx) { + retval = -1; + goto exit_init_data; } lttng_consumer_set_command_sock_path(ctx, command_sock_path); - if (strlen(error_sock_path) == 0) { + if (*error_sock_path == '\0') { switch (opt_type) { case LTTNG_CONSUMER_KERNEL: - snprintf(error_sock_path, PATH_MAX, KCONSUMERD_ERR_SOCK_PATH, + ret = snprintf(error_sock_path, PATH_MAX, + DEFAULT_KCONSUMERD_ERR_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; case LTTNG_CONSUMER64_UST: - snprintf(error_sock_path, PATH_MAX, - USTCONSUMERD64_ERR_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + ret = snprintf(error_sock_path, PATH_MAX, + DEFAULT_USTCONSUMERD64_ERR_SOCK_PATH, + DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; case LTTNG_CONSUMER32_UST: - snprintf(error_sock_path, PATH_MAX, - USTCONSUMERD32_ERR_SOCK_PATH, DEFAULT_LTTNG_RUNDIR); + ret = snprintf(error_sock_path, PATH_MAX, + DEFAULT_USTCONSUMERD32_ERR_SOCK_PATH, + DEFAULT_LTTNG_RUNDIR); + if (ret < 0) { + retval = -1; + goto exit_init_data; + } break; default: - WARN("Unknown consumerd type"); - goto error; + ERR("Unknown consumerd type"); + retval = -1; + goto exit_init_data; } } - if (set_signal_handler() < 0) { - goto error; - } - /* Connect to the socket created by lttng-sessiond to report errors */ DBG("Connecting to error socket %s", error_sock_path); ret = lttcomm_connect_unix_sock(error_sock_path); - /* not a fatal error, but all communication with lttng-sessiond will fail */ + /* + * Not a fatal error, but all communication with lttng-sessiond will + * fail. + */ if (ret < 0) { - WARN("Cannot connect to error socket, is lttng-sessiond started ?"); + WARN("Cannot connect to error socket (is lttng-sessiond started?)"); } lttng_consumer_set_error_sock(ctx, ret); - /* Create the thread to manage the receive of fd */ - ret = pthread_create(&threads[0], NULL, lttng_consumer_thread_receive_fds, + /* + * Block RT signals used for UST periodical metadata flush and the live + * timer in main, and create a dedicated thread to handle these signals. + */ + if (consumer_signal_init()) { + retval = -1; + goto exit_init_data; + } + + ctx->type = opt_type; + + if (utils_create_pipe(health_quit_pipe)) { + retval = -1; + goto exit_health_pipe; + } + + /* Create thread to manage the client socket */ + ret = pthread_create(&health_thread, default_pthread_attr(), + thread_manage_health, (void *) NULL); + if (ret) { + errno = ret; + PERROR("pthread_create health"); + retval = -1; + goto exit_health_thread; + } + + /* + * Wait for health thread to be initialized before letting the + * sessiond thread reply to the sessiond that we are ready. + */ + while (uatomic_read(<tng_consumer_ready)) { + usleep(100000); + } + cmm_smp_mb(); /* Read ready before following operations */ + + /* Create thread to manage channels */ + ret = pthread_create(&channel_thread, default_pthread_attr(), + consumer_thread_channel_poll, + (void *) ctx); + if (ret) { + errno = ret; + PERROR("pthread_create"); + retval = -1; + goto exit_channel_thread; + } + + /* Create thread to manage the polling/writing of trace metadata */ + ret = pthread_create(&metadata_thread, default_pthread_attr(), + consumer_thread_metadata_poll, (void *) ctx); - if (ret != 0) { - perror("pthread_create"); - goto error; + if (ret) { + errno = ret; + PERROR("pthread_create"); + retval = -1; + goto exit_metadata_thread; + } + + /* Create thread to manage the polling/writing of trace data */ + ret = pthread_create(&data_thread, default_pthread_attr(), + consumer_thread_data_poll, (void *) ctx); + if (ret) { + errno = ret; + PERROR("pthread_create"); + retval = -1; + goto exit_data_thread; } - /* Create thread to manage the polling/writing of traces */ - ret = pthread_create(&threads[1], NULL, lttng_consumer_thread_poll_fds, + /* Create the thread to manage the reception of fds */ + ret = pthread_create(&sessiond_thread, default_pthread_attr(), + consumer_thread_sessiond_poll, (void *) ctx); - if (ret != 0) { - perror("pthread_create"); - goto error; + if (ret) { + errno = ret; + PERROR("pthread_create"); + retval = -1; + goto exit_sessiond_thread; + } + + /* + * Create the thread to manage the UST metadata periodic timer and + * live timer. + */ + ret = pthread_create(&metadata_timer_thread, default_pthread_attr(), + consumer_timer_thread, (void *) ctx); + if (ret) { + errno = ret; + PERROR("pthread_create"); + retval = -1; + goto exit_metadata_timer_thread; + } + + ret = pthread_detach(metadata_timer_thread); + if (ret) { + errno = ret; + PERROR("pthread_detach"); + retval = -1; + goto exit_metadata_timer_detach; + } + + /* + * This is where we start awaiting program completion (e.g. through + * signal that asks threads to teardown. + */ + +exit_metadata_timer_detach: +exit_metadata_timer_thread: + ret = pthread_join(sessiond_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join sessiond_thread"); + retval = -1; } - for (i = 0; i < 2; i++) { - ret = pthread_join(threads[i], &status); - if (ret != 0) { - perror("pthread_join"); - goto error; + ret = consumer_timer_thread_get_channel_monitor_pipe(); + if (ret >= 0) { + ret = close(ret); + if (ret) { + PERROR("close channel monitor pipe"); } } - ret = EXIT_SUCCESS; - lttng_consumer_send_error(ctx, CONSUMERD_EXIT_SUCCESS); - goto end; +exit_sessiond_thread: -error: - ret = EXIT_FAILURE; - lttng_consumer_send_error(ctx, CONSUMERD_EXIT_FAILURE); + ret = pthread_join(data_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join data_thread"); + retval = -1; + } +exit_data_thread: -end: - lttng_consumer_destroy(ctx); + ret = pthread_join(metadata_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join metadata_thread"); + retval = -1; + } +exit_metadata_thread: + + ret = pthread_join(channel_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join channel_thread"); + retval = -1; + } +exit_channel_thread: + + ret = pthread_join(health_thread, &status); + if (ret) { + errno = ret; + PERROR("pthread_join health_thread"); + retval = -1; + } +exit_health_thread: + + utils_close_pipe(health_quit_pipe); +exit_health_pipe: + +exit_init_data: + tmp_ctx = ctx; + ctx = NULL; + cmm_barrier(); /* Clear ctx for signal handler. */ + /* + * Wait for all pending call_rcu work to complete before tearing + * down data structures. call_rcu worker may be trying to + * perform lookups in those structures. + */ + rcu_barrier(); + lttng_consumer_destroy(tmp_ctx); lttng_consumer_cleanup(); - return ret; + if (health_consumerd) { + health_app_destroy(health_consumerd); + } + /* Ensure all prior call_rcu are done. */ + rcu_barrier(); + + run_as_destroy_worker(); + +exit_health_consumerd_cleanup: +exit_options: +exit_set_signal_handler: + + rcu_unregister_thread(); + + if (!retval) { + exit(EXIT_SUCCESS); + } else { + exit(EXIT_FAILURE); + } }