Fix: zero out data structure before using it
[lttng-tools.git] / src / bin / lttng-sessiond / main.c
index a790844a1a484e3745305f91999ca98da8d390e3..4d9b22de2d4d93e3def0cd43597c61c8b33db87b 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * 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,
@@ -20,6 +21,7 @@
 #include <getopt.h>
 #include <grp.h>
 #include <limits.h>
+#include <paths.h>
 #include <pthread.h>
 #include <signal.h>
 #include <stdio.h>
@@ -44,6 +46,7 @@
 #include <common/futex.h>
 #include <common/relayd/relayd.h>
 #include <common/utils.h>
+#include <common/config/config.h>
 
 #include "lttng-sessiond.h"
 #include "buffer-registry.h"
 
 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_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.
@@ -116,6 +124,38 @@ static struct consumer_data ustconsumer32_data = {
        .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' },
+       { "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' },
+       { "config", 1, 0, 'f' },
+       { 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;
 
@@ -187,6 +227,10 @@ static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN;
 static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN;
 static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR;
 static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR;
+static int consumerd32_bin_override;
+static int consumerd64_bin_override;
+static int consumerd32_libdir_override;
+static int consumerd64_libdir_override;
 
 static const char *module_proc_lttng = "/proc/lttng";
 
@@ -238,6 +282,8 @@ struct health_app *health_sessiond;
 /* JUL TCP port for registration. Used by the JUL thread. */
 unsigned int jul_tcp_port = DEFAULT_JUL_TCP_PORT;
 
+const char * const config_section_name = "sessiond";
+
 static
 void setup_consumerd_path(void)
 {
@@ -499,6 +545,12 @@ 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");
@@ -532,6 +584,30 @@ static void cleanup(void)
 
        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);
+       }
+
        /* <fun> */
        DBG("%c[%d;%dm*** assert failed :-) *** ==> %c[%dm%c[%d;%dm"
                        "Matthew, BEET driven development works!%c[%dm",
@@ -865,9 +941,8 @@ static void *thread_manage_kernel(void *data)
 
                        /* Check for data on kernel pipe */
                        if (pollfd == kernel_poll_pipe[0] && (revents & LPOLLIN)) {
-                               do {
-                                       ret = read(kernel_poll_pipe[0], &tmp, 1);
-                               } while (ret < 0 && errno == EINTR);
+                               (void) lttng_read(kernel_poll_pipe[0],
+                                       &tmp, 1);
                                /*
                                 * Ret value is useless here, if this pipe gets any actions an
                                 * update is required anyway.
@@ -1239,6 +1314,7 @@ error_poll:
 static void *thread_manage_apps(void *data)
 {
        int i, ret, pollfd, err = -1;
+       ssize_t size_ret;
        uint32_t revents, nb_fd;
        struct lttng_poll_event events;
 
@@ -1314,10 +1390,8 @@ static void *thread_manage_apps(void *data)
                                        int sock;
 
                                        /* Empty pipe */
-                                       do {
-                                               ret = read(apps_cmd_pipe[0], &sock, sizeof(sock));
-                                       } while (ret < 0 && errno == EINTR);
-                                       if (ret < 0 || ret < sizeof(sock)) {
+                                       size_ret = lttng_read(apps_cmd_pipe[0], &sock, sizeof(sock));
+                                       if (size_ret < sizeof(sock)) {
                                                PERROR("read apps cmd pipe");
                                                goto error;
                                        }
@@ -1336,10 +1410,6 @@ static void *thread_manage_apps(void *data)
                                        }
 
                                        DBG("Apps with sock %d added to poll set", sock);
-
-                                       health_code_update();
-
-                                       break;
                                }
                        } else {
                                /*
@@ -1355,7 +1425,6 @@ static void *thread_manage_apps(void *data)
 
                                        /* Socket closed on remote end. */
                                        ust_app_unregister(pollfd);
-                                       break;
                                }
                        }
 
@@ -1400,7 +1469,7 @@ error_testpoint:
  */
 static int send_socket_to_thread(int fd, int sock)
 {
-       int ret;
+       ssize_t ret;
 
        /*
         * It's possible that the FD is set as invalid with -1 concurrently just
@@ -1411,10 +1480,8 @@ static int send_socket_to_thread(int fd, int sock)
                goto error;
        }
 
-       do {
-               ret = write(fd, &sock, sizeof(sock));
-       } while (ret < 0 && errno == EINTR);
-       if (ret < 0 || ret != sizeof(sock)) {
+       ret = lttng_write(fd, &sock, sizeof(sock));
+       if (ret < sizeof(sock)) {
                PERROR("write apps pipe %d", fd);
                if (ret < 0) {
                        ret = -errno;
@@ -1425,7 +1492,7 @@ static int send_socket_to_thread(int fd, int sock)
        /* All good. Don't send back the write positive ret value. */
        ret = 0;
 error:
-       return ret;
+       return (int) ret;
 }
 
 /*
@@ -2930,9 +2997,74 @@ skip_domain:
        }
        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:
@@ -3256,46 +3388,6 @@ skip_domain:
                                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);
@@ -3694,11 +3786,17 @@ static void *thread_manage_clients(void *data)
 
        /*
         * 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);
+       }
+
        if (testpoint(thread_manage_clients_before_loop)) {
                goto error;
        }
@@ -3919,157 +4017,257 @@ static void usage(void)
        fprintf(stderr, "  -d, --daemonize                    Start as a daemon.\n");
        fprintf(stderr, "  -g, --group NAME                   Specify the tracing group name. (default: tracing)\n");
        fprintf(stderr, "  -V, --version                      Show version number.\n");
-       fprintf(stderr, "  -S, --sig-parent                   Send SIGCHLD to parent pid to notify readiness.\n");
+       fprintf(stderr, "  -S, --sig-parent                   Send SIGUSR1 to parent pid to notify readiness.\n");
        fprintf(stderr, "  -q, --quiet                        No output at all.\n");
        fprintf(stderr, "  -v, --verbose                      Verbose mode. Activate DBG() macro.\n");
        fprintf(stderr, "  -p, --pidfile FILE                 Write a pid to FILE name overriding the default value.\n");
        fprintf(stderr, "      --verbose-consumer             Verbose mode for consumer. Activate DBG() macro.\n");
        fprintf(stderr, "      --no-kernel                    Disable kernel tracer\n");
        fprintf(stderr, "      --jul-tcp-port                 JUL application registration TCP port\n");
+       fprintf(stderr, "  -f  --config                       Load daemon configuration file\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 */
+               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 'g':
+               tracing_group_name = strdup(arg);
+               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) {
+                       lttng_opt_verbose = config_parse_value(arg);
+               } else {
                        lttng_opt_verbose += 1;
-                       break;
-               case 'Z':
+               }
+               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':
+               consumerd32_bin = strdup(arg);
+               consumerd32_bin_override = 1;
+               break;
+       case 'U':
+               consumerd32_libdir = strdup(arg);
+               consumerd32_libdir_override = 1;
+               break;
+       case 't':
+               consumerd64_bin = strdup(arg);
+               consumerd64_bin_override = 1;
+               break;
+       case 'T':
+               consumerd64_libdir = strdup(arg);
+               consumerd64_libdir_override = 1;
+               break;
+       case 'p':
+               opt_pidfile = strdup(arg);
+               break;
+       case 'J': /* JUL 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 --jul-tcp-port parameter: %s", arg);
+                       return -1;
+               }
+               if (v == 0 || v >= 65535) {
+                       ERR("Port overflow in --jul-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);
+               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;
 }
 
 /*
@@ -4340,6 +4538,9 @@ static void sighandler(int sig)
                DBG("SIGTERM caught");
                stop_threads();
                break;
+       case SIGUSR1:
+               CMM_STORE_SHARED(recv_child_signal, 1);
+               break;
        default:
                break;
        }
@@ -4378,7 +4579,12 @@ static int set_signal_handler(void)
                return ret;
        }
 
-       DBG("Signal handler set for SIGTERM, SIGPIPE and SIGINT");
+       if ((ret = sigaction(SIGUSR1, &sa, NULL)) < 0) {
+               PERROR("sigaction");
+               return ret;
+       }
+
+       DBG("Signal handler set for SIGTERM, SIGUSR1, SIGPIPE and SIGINT");
 
        return ret;
 }
@@ -4462,6 +4668,107 @@ 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.
+ */
+static int daemonize(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);
+               }
+
+               /*
+                * 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);
+       }
+
+end:
+       return 0;
+
+error:
+       return -1;
+}
+
 /*
  * main
  */
@@ -4475,6 +4782,10 @@ int main(int argc, char **argv)
 
        rcu_register_thread();
 
+       if ((ret = set_signal_handler()) < 0) {
+               goto error;
+       }
+
        setup_consumerd_path();
 
        page_size = sysconf(_SC_PAGESIZE);
@@ -4484,9 +4795,9 @@ int main(int argc, char **argv)
                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;
        }
 
@@ -4494,20 +4805,15 @@ int main(int argc, char **argv)
        if (opt_daemon) {
                int i;
 
-               /*
-                * fork
-                * child: setsid, close FD 0, 1, 2, chdir /
-                * parent: exit (if fork is successful)
-                */
-               ret = daemon(0, 0);
+               ret = daemonize();
                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);
@@ -4700,10 +5006,6 @@ int main(int argc, char **argv)
                goto exit;
        }
 
-       if ((ret = set_signal_handler()) < 0) {
-               goto exit;
-       }
-
        /* Setup the needed unix socket */
        if ((ret = init_daemon_socket()) < 0) {
                goto exit;
@@ -4785,7 +5087,7 @@ int main(int argc, char **argv)
                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) {
@@ -4793,7 +5095,7 @@ int main(int argc, char **argv)
                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) {
This page took 0.035636 seconds and 5 git commands to generate.