#include <getopt.h>
#include <grp.h>
#include <limits.h>
+#include <paths.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <common/futex.h>
#include <common/relayd/relayd.h>
#include <common/utils.h>
+#include <common/daemonize.h>
#include "lttng-sessiond.h"
#include "buffer-registry.h"
static const 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 pid_t ppid; /* Parent PID for --sig-parent option */
+static pid_t child_ppid; /* Internal parent PID use with daemonize. */
static char *rundir;
+/* 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
* set to 0 by the static definition.
/* JUL TCP port for registration. Used by the JUL thread. */
unsigned int jul_tcp_port = DEFAULT_JUL_TCP_PORT;
+/*
+ * Whether sessiond is ready for commands/health check requests.
+ * NR_LTTNG_SESSIOND_READY must match the number of calls to
+ * lttng_sessiond_notify_ready().
+ */
+#define NR_LTTNG_SESSIOND_READY 2
+int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY;
+
+/* Notify parents that we are ready for cmd and health check */
+static
+void lttng_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)
{
static void *thread_manage_kernel(void *data)
{
int ret, i, pollfd, update_poll_flag = 1, err = -1;
- ssize_t size_ret;
uint32_t revents, nb_fd;
char tmp;
struct lttng_poll_event events;
*/
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;
}
restart:
health_poll_entry();
- if (testpoint(thread_manage_consumer)) {
+ if (testpoint(sessiond_thread_manage_consumer)) {
goto error;
}
health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_MANAGE);
- if (testpoint(thread_manage_apps)) {
+ if (testpoint(sessiond_thread_manage_apps)) {
goto error_testpoint;
}
goto error;
}
- if (testpoint(thread_manage_apps_before_loop)) {
+ if (testpoint(sessiond_thread_manage_apps_before_loop)) {
goto error;
}
}
DBG("Apps with sock %d added to poll set", sock);
-
- health_code_update();
-
- break;
}
} else {
/*
/* Socket closed on remote end. */
ust_app_unregister(pollfd);
- break;
}
}
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);
free(wait_node);
}
+error_testpoint:
DBG("Dispatch thread dying");
if (err) {
health_error();
health_register(health_sessiond, HEALTH_SESSIOND_TYPE_APP_REG);
- if (testpoint(thread_registration_apps)) {
+ if (testpoint(sessiond_thread_registration_apps)) {
goto error_testpoint;
}
exit:
error:
- if (err) {
- health_error();
- ERR("Health error occurred in %s", __func__);
- }
-
/* Notify that the registration thread is gone */
notify_ust_apps(0);
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;
}
case LTTNG_ENABLE_EVENT:
{
+ struct lttng_event_exclusion *exclusion = NULL;
+ struct lttng_filter_bytecode *bytecode = 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;
+ }
+ }
+
+ /* 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(exclusion);
+ goto error;
+ }
+
+ bytecode = zmalloc(bytecode_len);
+ if (!bytecode) {
+ 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(bytecode);
+ free(exclusion);
+ ret = LTTNG_ERR_FILTER_INVAL;
+ goto error;
+ }
+
+ if ((bytecode->len + sizeof(*bytecode)) != bytecode_len) {
+ 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, NULL, kernel_poll_pipe[1]);
+ &cmd_ctx->lsm->u.enable.event, bytecode, exclusion,
+ kernel_poll_pipe[1]);
break;
}
case LTTNG_ENABLE_ALL_EVENT:
cmd_ctx->lsm->u.reg.path, cdata);
break;
}
- case LTTNG_ENABLE_EVENT_WITH_FILTER:
- {
- struct lttng_filter_bytecode *bytecode;
-
- 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;
- }
-
- if (bytecode->len + sizeof(*bytecode)
- != cmd_ctx->lsm->u.enable.bytecode_len) {
- free(bytecode);
- 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, bytecode, kernel_poll_pipe[1]);
- break;
- }
case LTTNG_DATA_PENDING:
{
ret = cmd_data_pending(cmd_ctx->session);
goto error;
}
+ lttng_sessiond_notify_ready();
+
while (1) {
DBG("Health check ready");
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);
goto error;
}
- /*
- * Notify parent pid that we are ready to accept command for client side.
- */
- if (opt_sig_parent) {
- kill(ppid, SIGUSR1);
+ lttng_sessiond_notify_ready();
+
+ /* 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;
}
error_listen:
error_create_poll:
-error_testpoint:
unlink(client_unix_sock_path);
if (client_sock >= 0) {
ret = close(client_sock);
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");
{ "no-kernel", 0, 0, 'N' },
{ "pidfile", 1, 0, 'p' },
{ "jul-tcp-port", 1, 0, 'J' },
+ { "background", 0, 0, 'b' },
{ NULL, 0, 0, 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:J:",
+ c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t:p:J:b",
long_options, &option_index);
if (c == -1) {
break;
case 'd':
opt_daemon = 1;
break;
+ case 'b':
+ opt_background = 1;
+ break;
case 'g':
tracing_group_name = optarg;
break;
DBG("SIGTERM caught");
stop_threads();
break;
+ case SIGUSR1:
+ CMM_STORE_SHARED(recv_child_signal, 1);
+ break;
default:
break;
}
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;
}
rcu_register_thread();
+ if ((ret = set_signal_handler()) < 0) {
+ goto error;
+ }
+
setup_consumerd_path();
page_size = sysconf(_SC_PAGESIZE);
}
/* 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;
}
+
/*
- * 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);
goto exit;
}
- if ((ret = set_signal_handler()) < 0) {
- goto exit;
- }
-
/* Setup the needed unix socket */
if ((ret = init_daemon_socket()) < 0) {
goto exit;
goto exit_health_sessiond_cleanup;
}
- /* Create thread to manage the client socket */
+ /* Create thread to clean up RCU hash tables */
ret = pthread_create(&ht_cleanup_thread, NULL,
thread_ht_cleanup, (void *) NULL);
if (ret != 0) {
goto exit_ht_cleanup;
}
- /* 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) {