Adding log4j agent support
[lttng-tools.git] / src / bin / lttng-sessiond / main.c
index 5205984461a759074c945575c05ebf497554a0b0..c7fc178cdf09820809f14c218ac236f989439317 100644 (file)
@@ -46,6 +46,7 @@
 #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"
@@ -66,7 +67,9 @@
 #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"
 
 #define CONSUMERD_FILE "lttng-consumerd"
 
@@ -76,12 +79,13 @@ 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;
@@ -139,6 +143,7 @@ static const struct option long_options[] = {
        { "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' },
@@ -148,8 +153,10 @@ static const struct option long_options[] = {
        { "verbose-consumer", 0, 0, 'Z' },
        { "no-kernel", 0, 0, 'N' },
        { "pidfile", 1, 0, 'p' },
-       { "jul-tcp-port", 1, 0, 'J' },
+       { "agent-tcp-port", 1, 0, 'J' },
        { "config", 1, 0, 'f' },
+       { "load", 1, 0, 'l' },
+       { "kmod-probes", 1, 0, 'P' },
        { NULL, 0, 0, 0 }
 };
 
@@ -197,14 +204,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 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;
 
@@ -279,11 +289,49 @@ long page_size;
 /* 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(&lttng_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)
 {
@@ -464,6 +512,27 @@ static void close_consumer_sockets(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
  */
@@ -502,7 +571,7 @@ static void cleanup(void)
        (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);
 
@@ -545,14 +614,6 @@ static void cleanup(void)
        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 */
@@ -608,6 +669,43 @@ static void cleanup(void)
                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",
@@ -1116,7 +1214,6 @@ restart:
        }
 
        health_code_update();
-
        if (code == LTTCOMM_CONSUMERD_COMMAND_SOCK_READY) {
                /* Connect both socket, command and metadata. */
                consumer_data->cmd_sock =
@@ -1273,13 +1370,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) {
@@ -1293,9 +1390,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) {
@@ -1587,7 +1685,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 = {
@@ -1596,6 +1694,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);
@@ -1621,7 +1723,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 */
@@ -1800,6 +1902,7 @@ error:
                free(wait_node);
        }
 
+error_testpoint:
        DBG("Dispatch thread dying");
        if (err) {
                health_error();
@@ -1974,11 +2077,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);
                                }
@@ -1988,11 +2091,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);
 
@@ -2017,6 +2115,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;
@@ -2113,19 +2215,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;
        }
 
@@ -2186,9 +2292,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:
                        /*
@@ -2211,10 +2320,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,
@@ -2262,9 +2372,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:
@@ -2308,9 +2415,6 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data)
                        if (consumerd32_libdir[0] != '\0') {
                                free(tmpnew);
                        }
-                       if (ret) {
-                               goto error;
-                       }
                        break;
                }
                default:
@@ -2318,8 +2422,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;
@@ -2488,6 +2593,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session)
                dir_name = DEFAULT_KERNEL_TRACE_DIR;
                break;
        case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
        case LTTNG_DOMAIN_UST:
                DBG3("Copying tracing session consumer output in UST session");
                if (session->ust_session->consumer) {
@@ -2532,6 +2638,7 @@ static int create_ust_session(struct ltt_session *session,
 
        switch (domain->type) {
        case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
        case LTTNG_DOMAIN_UST:
                break;
        default:
@@ -2678,6 +2785,7 @@ 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:
                need_domain = 0;
                break;
        default:
@@ -2736,6 +2844,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
        case LTTNG_LIST_SESSIONS:
        case LTTNG_LIST_TRACEPOINTS:
        case LTTNG_LIST_TRACEPOINT_FIELDS:
+       case LTTNG_SAVE_SESSION:
                need_tracing_session = 0;
                break;
        default:
@@ -2757,6 +2866,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:
+       case LTTNG_DISABLE_ALL_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_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;
        }
@@ -2824,6 +2966,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock,
 
                break;
        case LTTNG_DOMAIN_JUL:
+       case LTTNG_DOMAIN_LOG4J:
        case LTTNG_DOMAIN_UST:
        {
                if (!ust_app_supported()) {
@@ -2877,6 +3020,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) {
@@ -2916,6 +3060,7 @@ skip_domain:
                        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_UST:
                        if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) {
                                ret = LTTNG_ERR_NO_USTCONSUMERD;
@@ -2999,6 +3144,7 @@ skip_domain:
        {
                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) {
@@ -3024,6 +3170,38 @@ skip_domain:
                        }
                }
 
+               /* 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;
+                       }
+
+                       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;
@@ -3063,7 +3241,8 @@ skip_domain:
 
                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,
+                               &cmd_ctx->lsm->u.enable.event,
+                               filter_expression, bytecode, exclusion,
                                kernel_poll_pipe[1]);
                break;
        }
@@ -3073,7 +3252,8 @@ skip_domain:
 
                ret = cmd_enable_event_all(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.type, NULL, NULL,
+                               kernel_poll_pipe[1]);
                break;
        }
        case LTTNG_LIST_TRACEPOINTS:
@@ -3081,7 +3261,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;
@@ -3112,8 +3294,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;
@@ -3532,6 +3716,12 @@ skip_domain:
                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;
@@ -3630,6 +3820,8 @@ static void *thread_manage_health(void *data)
                goto error;
        }
 
+       sessiond_notify_ready();
+
        while (1) {
                DBG("Health check ready");
 
@@ -3694,7 +3886,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
@@ -3780,17 +3972,11 @@ static void *thread_manage_clients(void *data)
                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);
-       }
-
-       /* Notify the parent of the fork() process that we are ready. */
-       if (opt_daemon) {
-               kill(child_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. */
@@ -4015,6 +4201,7 @@ 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 SIGUSR1 to parent pid to notify readiness.\n");
@@ -4023,8 +4210,10 @@ static void usage(void)
        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");
 }
 
 /*
@@ -4053,8 +4242,24 @@ static int set_option(int opt, const char *arg, const char *optname)
        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();
@@ -4092,10 +4297,15 @@ static int set_option(int opt, const char *arg, const char *optname)
        case 'v':
                /* Verbose level can increase using multiple -v */
                if (arg) {
+                       /* Value obtained from config file */
                        lttng_opt_verbose = config_parse_value(arg);
                } else {
-                       lttng_opt_verbose += 1;
+                       /* -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) {
@@ -4105,42 +4315,94 @@ static int set_option(int opt, const char *arg, const char *optname)
                }
                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': /* JUL TCP port. */
+       case 'J': /* Agent TCP port. */
        {
                unsigned long v;
 
                errno = 0;
                v = strtoul(arg, NULL, 0);
                if (errno != 0 || !isdigit(arg[0])) {
-                       ERR("Wrong value in --jul-tcp-port parameter: %s", arg);
+                       ERR("Wrong value in --agent-tcp-port parameter: %s", arg);
                        return -1;
                }
                if (v == 0 || v >= 65535) {
-                       ERR("Port overflow in --jul-tcp-port parameter: %s", arg);
+                       ERR("Port overflow in --agent-tcp-port parameter: %s", arg);
                        return -1;
                }
-               jul_tcp_port = (uint32_t) v;
-               DBG3("JUL TCP port set to non default: %u", jul_tcp_port);
+               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 'f':
+               /* This is handled in set_options() thus silent break. */
+               break;
        default:
                /* Unknown option or other error.
                 * Error is printed by getopt, just return */
@@ -4641,9 +4903,27 @@ error:
 }
 
 /*
- * Write JUL TCP port using the rundir.
+ * 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 void write_julport(void)
+static void write_agent_port(void)
 {
        int ret;
        char path[PATH_MAX];
@@ -4651,122 +4931,50 @@ static void write_julport(void)
        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)) {
-                       int status;
-                       pid_t ret;
 
-                       /*
-                        * Check if child exists without blocking. If so, we have to stop
-                        * this parent process and return an error.
-                        */
-                       ret = waitpid(pid, &status, WNOHANG);
-                       if (ret < 0 || (ret != 0 && WIFEXITED(status))) {
-                               /* The child exited somehow or was not valid. */
-                               goto error;
-                       }
-                       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;
 }
 
 /*
@@ -4802,10 +5010,11 @@ int main(int argc, char **argv)
        }
 
        /* 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;
                }
@@ -4916,6 +5125,11 @@ int main(int argc, char **argv)
                }
        }
 
+       lockfile_fd = create_lockfile();
+       if (lockfile_fd < 0) {
+               goto error;
+       }
+
        /* Set consumer initial state */
        kernel_consumerd_state = CONSUMER_STOPPED;
        ust_consumerd_state = CONSUMER_STOPPED;
@@ -4965,8 +5179,8 @@ int main(int argc, char **argv)
         */
        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;
        }
@@ -5048,7 +5262,7 @@ 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
@@ -5070,13 +5284,18 @@ int main(int argc, char **argv)
        }
 
        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.
@@ -5139,16 +5358,16 @@ int main(int argc, char **argv)
        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 */
@@ -5160,7 +5379,12 @@ int main(int argc, char **argv)
                        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");
@@ -5169,13 +5393,13 @@ int main(int argc, char **argv)
        }
 
 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");
This page took 0.037457 seconds and 4 git commands to generate.