/*
* Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
* Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * 2013 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
*
* 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,
*/
#define _GNU_SOURCE
+#define _LGPL_SOURCE
#include <getopt.h>
#include <grp.h>
#include <limits.h>
#include <common/futex.h>
#include <common/relayd/relayd.h>
#include <common/utils.h>
+#include <common/daemonize.h>
+#include <common/config/config.h>
#include "lttng-sessiond.h"
#include "buffer-registry.h"
#include "health-sessiond.h"
#include "testpoint.h"
#include "ust-thread.h"
-#include "jul-thread.h"
+#include "agent-thread.h"
+#include "save.h"
+#include "load-session-thread.h"
+#include "syscall.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;
.cond_mutex = PTHREAD_MUTEX_INITIALIZER,
};
+/* Command line options */
+static const 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' },
+ { "background", 0, 0, 'b' },
+ { "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' },
+ { "agent-tcp-port", 1, 0, 'J' },
+ { "config", 1, 0, 'f' },
+ { "load", 1, 0, 'l' },
+ { "kmod-probes", 1, 0, 'P' },
+ { "extra-kmod-probes", 1, 0, 'e' },
+ { 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;
static pthread_t dispatch_thread;
static pthread_t health_thread;
static pthread_t ht_cleanup_thread;
-static pthread_t jul_reg_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;
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";
/* Application health monitoring */
struct health_app *health_sessiond;
-/* JUL TCP port for registration. Used by the JUL thread. */
-unsigned int jul_tcp_port = DEFAULT_JUL_TCP_PORT;
+/* 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;
+
+/*
+ * 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)
}
}
+/*
+ * 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 daemon
*/
(void) unlink(path);
snprintf(path, PATH_MAX, "%s/%s", rundir,
- DEFAULT_LTTNG_SESSIOND_JULPORT_FILE);
+ DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE);
DBG("Removing %s", path);
(void) unlink(path);
DBG("Removing directory %s", path);
(void) rmdir(path);
- /*
- * 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);
-
DBG("Cleaning up all sessions");
/* Destroy session list mutex */
}
DBG("Unloading kernel modules");
modprobe_remove_lttng_all();
+ free(syscall_table);
}
close_consumer_sockets();
+ /*
+ * 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);
+ }
+
+ if (opt_pidfile) {
+ free(opt_pidfile);
+ }
+
+ if (opt_load_session_path) {
+ free(opt_load_session_path);
+ }
+
+ 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);
+
/* <fun> */
DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm"
"Matthew, BEET driven development works!%c[%dm",
*/
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;
}
*/
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;
restart:
health_poll_entry();
- if (testpoint(thread_manage_consumer)) {
+ if (testpoint(sessiond_thread_manage_consumer)) {
goto error;
}
}
health_code_update();
-
if (code == LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) {
/* Connect both socket, command and metadata. */
consumer_data->cmd_sock =
/* 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();
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;
- }
+ /*
+ * 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 */
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();
}
}
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) {
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) {
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;
}
}
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 = {
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);
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 */
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;
}
* 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);
}
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;
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;
}
*/
if (opt_verbose_consumer) {
verbosity = "--verbose";
- } else {
+ } else if (lttng_opt_quiet) {
verbosity = "--quiet";
+ } else {
+ verbosity = "";
}
+
switch (consumer_data->type) {
case LTTNG_CONSUMER_KERNEL:
/*
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,
if (consumerd64_libdir[0] != '\0') {
free(tmpnew);
}
- if (ret) {
- goto error;
- }
break;
}
case LTTNG_CONSUMER32_UST:
if (consumerd32_libdir[0] != '\0') {
free(tmpnew);
}
- if (ret) {
- goto error;
- }
break;
}
default:
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;
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) {
switch (domain->type) {
case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_PYTHON:
case LTTNG_DOMAIN_UST:
break;
default:
case LTTNG_SNAPSHOT_DEL_OUTPUT:
case LTTNG_SNAPSHOT_LIST_OUTPUT:
case LTTNG_SNAPSHOT_RECORD:
+ case LTTNG_SAVE_SESSION:
need_domain = 0;
break;
default:
case LTTNG_LIST_DOMAINS:
case LTTNG_LIST_CHANNELS:
case LTTNG_LIST_EVENTS:
+ case LTTNG_LIST_SYSCALLS:
break;
default:
/* Setup lttng message with no payload */
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:
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;
}
break;
case LTTNG_DOMAIN_JUL:
+ case LTTNG_DOMAIN_LOG4J:
+ case LTTNG_DOMAIN_PYTHON:
case LTTNG_DOMAIN_UST:
{
if (!ust_app_supported()) {
}
/* 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) {
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;
}
case LTTNG_DISABLE_EVENT:
{
+ /* FIXME: passing packed structure to non-packed pointer */
+ /* TODO: handle filter */
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:
break;
}
case LTTNG_ENABLE_EVENT:
- case LTTNG_ENABLE_EVENT_WITH_EXCLUSION:
{
struct lttng_event_exclusion *exclusion = NULL;
struct lttng_filter_bytecode *bytecode = NULL;
+ char *filter_expression = NULL;
- if (cmd_ctx->lsm->cmd_type == LTTNG_ENABLE_EVENT ||
- (cmd_ctx->lsm->u.enable.exclusion_count == 0 && cmd_ctx->lsm->u.enable.bytecode_len == 0)) {
- 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, NULL, kernel_poll_pipe[1]);
- } else {
- if (cmd_ctx->lsm->u.enable.exclusion_count != 0) {
- exclusion = zmalloc(sizeof(struct lttng_event_exclusion) +
- cmd_ctx->lsm->u.enable.exclusion_count * LTTNG_SYMBOL_NAME_LEN);
- if (!exclusion) {
- ret = LTTNG_ERR_EXCLUSION_NOMEM;
- goto error;
- }
- DBG("Receiving var len data from client ...");
- exclusion->count = cmd_ctx->lsm->u.enable.exclusion_count;
- ret = lttcomm_recv_unix_sock(sock, exclusion->names,
- cmd_ctx->lsm->u.enable.exclusion_count * LTTNG_SYMBOL_NAME_LEN);
- if (ret <= 0) {
- DBG("Nothing recv() from client var len data... continuing");
- *sock_error = 1;
- ret = LTTNG_ERR_EXCLUSION_INVAL;
- goto error;
- }
+ /* 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;
}
- if (cmd_ctx->lsm->u.enable.bytecode_len != 0) {
- bytecode = zmalloc(cmd_ctx->lsm->u.enable.bytecode_len);
- if (!bytecode) {
- if (!exclusion)
- free(exclusion);
- 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 car len data... continuing");
- *sock_error = 1;
- if (!exclusion)
- free(exclusion);
- ret = LTTNG_ERR_FILTER_INVAL;
- goto error;
- }
- if (bytecode->len + sizeof(*bytecode)
- != cmd_ctx->lsm->u.enable.bytecode_len) {
- free(bytecode);
- if (!exclusion)
- free(exclusion);
- ret = LTTNG_ERR_FILTER_INVAL;
- 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;
- 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,
- exclusion,
- kernel_poll_pipe[1]);
+ if (expression_len > LTTNG_FILTER_MAX_LEN) {
+ ret = LTTNG_ERR_FILTER_INVAL;
+ free(exclusion);
+ goto error;
+ }
+
+ 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;
+ }
}
- break;
- }
- case LTTNG_ENABLE_ALL_EVENT:
- {
- DBG("Enabling all events");
- ret = cmd_enable_event_all(cmd_ctx->session, &cmd_ctx->lsm->domain,
+ /* 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.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:
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;
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;
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_SET_CONSUMER_URI:
{
size_t nb_uri, len;
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) {
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);
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, NULL, kernel_poll_pipe[1]);
- break;
- }
case LTTNG_DATA_PENDING:
{
ret = cmd_data_pending(cmd_ctx->session);
free(uris);
break;
}
+ case LTTNG_SAVE_SESSION:
+ {
+ ret = cmd_save_sessions(&cmd_ctx->lsm->u.save_session.attr,
+ &cmd_ctx->creds);
+ break;
+ }
default:
ret = LTTNG_ERR_UND;
break;
goto error;
}
+ sessiond_notify_ready();
+
while (1) {
DBG("Health check ready");
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
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.
- * This ppid is the one from the external process that spawned us.
- */
- 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;
}
- /* Notify the parent of the fork() process that we are ready. */
- if (opt_daemon) {
- kill(child_ppid, SIGUSR1);
+ /* 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 SIGUSR1 to parent pid to notify readiness.\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, " --jul-tcp-port JUL application registration TCP port\n");
+ fprintf(stderr, " --agent-tcp-port Agent registration TCP port\n");
+ fprintf(stderr, " -f --config 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");
}
/*
- * 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' },
- { "jul-tcp-port", 1, 0, 'J' },
- { 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:J:",
- long_options, &option_index);
- if (c == -1) {
- break;
+ switch (opt) {
+ case 0:
+ fprintf(stderr, "option %s", optname);
+ if (arg) {
+ fprintf(stderr, " with arg %s\n", arg);
}
-
- switch (c) {
- case 0:
- fprintf(stderr, "option %s", long_options[option_index].name);
- if (optarg) {
- fprintf(stderr, " with arg %s\n", optarg);
- }
- 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':
+ break;
+ case 'c':
+ snprintf(client_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'a':
+ snprintf(apps_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'd':
+ opt_daemon = 1;
+ break;
+ case 'b':
+ opt_background = 1;
+ break;
+ case 'g':
+ /*
+ * 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);
+ }
+ tracing_group_name = strdup(arg);
+ if (!tracing_group_name) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ tracing_group_name_override = 1;
+ 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", arg);
+ break;
+ case 'C':
+ snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'F':
+ snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'D':
+ snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'H':
+ snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'G':
+ snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", arg);
+ break;
+ case 'N':
+ opt_no_kernel = 1;
+ break;
+ case 'q':
+ lttng_opt_quiet = 1;
+ break;
+ case '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);
+ break;
+ case 'Z':
+ if (arg) {
+ opt_verbose_consumer = config_parse_value(arg);
+ } else {
opt_verbose_consumer += 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;
- break;
- case 'J': /* JUL TCP port. */
- {
- unsigned long v;
+ }
+ break;
+ case 'u':
+ if (consumerd32_bin_override) {
+ free((void *) consumerd32_bin);
+ }
+ consumerd32_bin = strdup(arg);
+ if (!consumerd32_bin) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ consumerd32_bin_override = 1;
+ break;
+ case 'U':
+ if (consumerd32_libdir_override) {
+ free((void *) consumerd32_libdir);
+ }
+ consumerd32_libdir = strdup(arg);
+ if (!consumerd32_libdir) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ consumerd32_libdir_override = 1;
+ break;
+ case 't':
+ if (consumerd64_bin_override) {
+ free((void *) consumerd64_bin);
+ }
+ consumerd64_bin = strdup(arg);
+ if (!consumerd64_bin) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ consumerd64_bin_override = 1;
+ break;
+ case 'T':
+ if (consumerd64_libdir_override) {
+ free((void *) consumerd64_libdir);
+ }
+ consumerd64_libdir = strdup(arg);
+ if (!consumerd64_libdir) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ consumerd64_libdir_override = 1;
+ break;
+ case 'p':
+ free(opt_pidfile);
+ opt_pidfile = strdup(arg);
+ if (!opt_pidfile) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ break;
+ case 'J': /* Agent TCP port. */
+ {
+ unsigned long v;
- errno = 0;
- v = strtoul(optarg, NULL, 0);
- if (errno != 0 || !isdigit(optarg[0])) {
- ERR("Wrong value in --jul-tcp-port parameter: %s", optarg);
- return -1;
- }
- if (v == 0 || v >= 65535) {
- ERR("Port overflow in --jul-tcp-port parameter: %s", optarg);
- return -1;
+ 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);
+ break;
+ }
+ case 'l':
+ free(opt_load_session_path);
+ opt_load_session_path = strdup(arg);
+ if (!opt_load_session_path) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ break;
+ case 'P': /* probe modules list */
+ free(kmod_probes_list);
+ kmod_probes_list = strdup(arg);
+ if (!kmod_probes_list) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ break;
+ case 'e':
+ free(kmod_extra_probes_list);
+ kmod_extra_probes_list = strdup(arg);
+ if (!kmod_extra_probes_list) {
+ perror("strdup");
+ ret = -ENOMEM;
+ }
+ break;
+ case 'f':
+ /* This is handled in set_options() thus silent break. */
+ break;
+ default:
+ /* Unknown option or other error.
+ * Error is printed by getopt, just return */
+ ret = -1;
+ }
+
+ 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;
}
- jul_tcp_port = (uint32_t) v;
- DBG3("JUL TCP port set to non default: %u", jul_tcp_port);
+ }
+
+ 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;
+ }
+
+ 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) {
+ c = getopt_long(argc, argv, optstring, long_options, &option_index);
+ if (c == -1) {
break;
}
- default:
- /* Unknown option or other error.
- * Error is printed by getopt, just return */
- return -1;
+
+ ret = set_option(c, optarg, long_options[option_index].name);
+ if (ret < 0) {
+ break;
}
}
- return 0;
+end:
+ free(optstring);
+ return ret;
}
/*
}
/*
- * Write JUL TCP port using the rundir.
+ * Create lockfile using the rundir and return its fd.
*/
-static void write_julport(void)
+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 void write_agent_port(void)
{
int ret;
char path[PATH_MAX];
assert(rundir);
ret = snprintf(path, sizeof(path), "%s/"
- DEFAULT_LTTNG_SESSIOND_JULPORT_FILE, rundir);
+ DEFAULT_LTTNG_SESSIOND_AGENTPORT_FILE, rundir);
if (ret < 0) {
- PERROR("snprintf julport path");
+ PERROR("snprintf agent port path");
goto error;
}
/*
- * Create TCP JUL port file in rundir. Return value is of no importance.
+ * Create TCP agent port file in rundir. Return value is of no importance.
* The execution will continue even though we are not able to write the
* file.
*/
- (void) utils_create_pid_file(jul_tcp_port, path);
+ (void) utils_create_pid_file(agent_tcp_port, path);
error:
return;
}
/*
- * Daemonize this process by forking and making the parent wait for the child
- * to signal it indicating readiness. Once received, the parent successfully
- * quits.
- *
- * The child process undergoes the same action that daemon(3) does meaning
- * setsid, chdir, and dup /dev/null into 0, 1 and 2.
- *
- * Return 0 on success else -1 on error.
+ * Start the load session thread and dettach from it so the main thread can
+ * continue. This does not return a value since whatever the outcome, the main
+ * thread will continue.
*/
-static int daemonize(void)
+static void start_load_session_thread(void)
{
int ret;
- pid_t pid;
-
- /* Get parent pid of this process. */
- child_ppid = getppid();
-
- pid = fork();
- if (pid < 0) {
- PERROR("fork");
- goto error;
- } else if (pid == 0) {
- int fd;
- pid_t sid;
-
- /* Child */
-
- /*
- * Get the newly created parent pid so we can signal that process when
- * we are ready to operate.
- */
- child_ppid = getppid();
-
- sid = setsid();
- if (sid < 0) {
- PERROR("setsid");
- goto error;
- }
-
- /* Try to change directory to /. If we can't well at least notify. */
- ret = chdir("/");
- if (ret < 0) {
- PERROR("chdir");
- }
-
- fd = open(_PATH_DEVNULL, O_RDWR, 0);
- if (fd < 0) {
- PERROR("open %s", _PATH_DEVNULL);
- /* Let 0, 1 and 2 open since we can't bind them to /dev/null. */
- } else {
- (void) dup2(fd, STDIN_FILENO);
- (void) dup2(fd, STDOUT_FILENO);
- (void) dup2(fd, STDERR_FILENO);
- if (fd > 2) {
- ret = close(fd);
- if (ret < 0) {
- PERROR("close");
- }
- }
- }
- goto end;
- } else {
- /* Parent */
- /*
- * Waiting for child to notify this parent that it can exit. Note that
- * sleep() is interrupted before the 1 second delay as soon as the
- * signal is received, so it will not cause visible delay for the
- * user.
- */
- while (!CMM_LOAD_SHARED(recv_child_signal)) {
- sleep(1);
- }
+ /* Create session loading thread. */
+ ret = pthread_create(&load_session_thread, NULL, thread_load_session,
+ load_info);
+ if (ret != 0) {
+ PERROR("pthread_create load_session_thread");
+ goto error_create;
+ }
- /*
- * From this point on, the parent can exit and the child is now an
- * operationnal session daemon ready to serve clients and applications.
- */
- exit(EXIT_SUCCESS);
+ ret = pthread_detach(load_session_thread);
+ if (ret != 0) {
+ PERROR("pthread_detach load_session_thread");
}
-end:
- return 0;
+ /* Everything went well so don't cleanup anything. */
-error:
- return -1;
+error_create:
+ /* The cleanup() function will destroy the load_info data. */
+ return;
}
/*
WARN("Fallback page size to %ld", page_size);
}
- /* Parse arguments */
+ /* Parse arguments and load the daemon configuration file */
progname = argv[0];
- if ((ret = parse_args(argc, argv)) < 0) {
+ if ((ret = set_options(argc, argv)) < 0) {
goto error;
}
/* Daemonize */
- if (opt_daemon) {
+ if (opt_daemon || opt_background) {
int i;
- ret = daemonize();
+ ret = lttng_daemonize(&child_ppid, &recv_child_signal,
+ !opt_background);
if (ret < 0) {
goto error;
}
}
}
+ lockfile_fd = create_lockfile();
+ if (lockfile_fd < 0) {
+ goto error;
+ }
+
/* Set consumer initial state */
kernel_consumerd_state = CONSUMER_STOPPED;
ust_consumerd_state = CONSUMER_STOPPED;
*/
ust_app_ht_alloc();
- /* Initialize JUL domain subsystem. */
- if ((ret = jul_init()) < 0) {
+ /* Initialize agent domain subsystem. */
+ if ((ret = agent_setup()) < 0) {
/* ENOMEM at this point. */
goto error;
}
/* 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 */
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
}
write_pidfile();
- write_julport();
+ write_agent_port();
/* Initialize communication library */
lttcomm_init();
/* This is to get the TCP timeout value. */
lttcomm_inet_init();
+ if (load_session_init_data(&load_info) < 0) {
+ goto exit;
+ }
+ load_info->path = opt_load_session_path;
+
/*
* Initialize the health check subsystem. This call should set the
* appropriate time values.
ret = pthread_create(&apps_notify_thread, NULL,
ust_thread_manage_notify, (void *) NULL);
if (ret != 0) {
- PERROR("pthread_create apps");
+ PERROR("pthread_create notify");
goto exit_apps_notify;
}
- /* Create JUL registration thread. */
- ret = pthread_create(&jul_reg_thread, NULL,
- jul_thread_manage_registration, (void *) NULL);
+ /* Create agent registration thread. */
+ ret = pthread_create(&agent_reg_thread, NULL,
+ agent_thread_manage_registration, (void *) NULL);
if (ret != 0) {
- PERROR("pthread_create apps");
- goto exit_jul_reg;
+ PERROR("pthread_create agent");
+ goto exit_agent_reg;
}
/* Don't start this thread if kernel tracing is not requested nor root */
PERROR("pthread_create kernel");
goto exit_kernel;
}
+ }
+
+ /* Load possible session(s). */
+ start_load_session_thread();
+ if (is_root && !opt_no_kernel) {
ret = pthread_join(kernel_thread, &status);
if (ret != 0) {
PERROR("pthread_join");
}
exit_kernel:
- ret = pthread_join(jul_reg_thread, &status);
+ ret = pthread_join(agent_reg_thread, &status);
if (ret != 0) {
- PERROR("pthread_join JUL");
+ PERROR("pthread_join agent");
goto error; /* join error, exit without cleanup */
}
-exit_jul_reg:
+exit_agent_reg:
ret = pthread_join(apps_notify_thread, &status);
if (ret != 0) {
PERROR("pthread_join apps notify");