Implement futex wait scheme
[lttng-ust.git] / libust / lttng-ust-comm.c
index b6c5de51e456e5e3d8f501fd75265819eeebf199..a5f2b0b0069de494f0fb44dd14fdde78537002e6 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#define _LGPL_SOURCE
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/prctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <pthread.h>
@@ -30,6 +36,7 @@
 #include <assert.h>
 #include <signal.h>
 #include <urcu/uatomic.h>
+#include <urcu/futex.h>
 
 #include <lttng-ust-comm.h>
 #include <ust/usterr-signal-safe.h>
@@ -37,6 +44,7 @@
 #include <ust/tracepoint.h>
 #include <ust/tracepoint-internal.h>
 #include <ust/ust.h>
+#include "ltt-tracer-core.h"
 
 /*
  * Has lttng ust comm constructor been called ?
@@ -73,30 +81,42 @@ static int sem_count = { 2 };
  */
 struct sock_info {
        const char *name;
-       char sock_path[PATH_MAX];
-       int socket;
        pthread_t ust_listener; /* listener thread */
        int root_handle;
        int constructor_sem_posted;
        int allowed;
+       int global;
+
+       char sock_path[PATH_MAX];
+       int socket;
+
+       char wait_shm_path[PATH_MAX];
+       char *wait_shm_mmap;
 };
 
 /* Socket from app (connect) to session daemon (listen) for communication */
 struct sock_info global_apps = {
        .name = "global",
-       .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
-       .socket = -1,
+       .global = 1,
+
        .root_handle = -1,
        .allowed = 1,
+
+       .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
+       .socket = -1,
+
+       .wait_shm_path = DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH,
 };
 
 /* TODO: allow global_apps_sock_path override */
 
 struct sock_info local_apps = {
        .name = "local",
-       .socket = -1,
+       .global = 0,
        .root_handle = -1,
        .allowed = 0,   /* Check setuid bit first */
+
+       .socket = -1,
 };
 
 extern void ltt_ring_buffer_client_overwrite_init(void);
@@ -110,11 +130,13 @@ static
 int setup_local_apps(void)
 {
        const char *home_dir;
+       uid_t uid;
 
+       uid = getuid();
        /*
         * Disallow per-user tracing for setuid binaries.
         */
-       if (getuid() != geteuid()) {
+       if (uid != geteuid()) {
                local_apps.allowed = 0;
                return 0;
        } else {
@@ -125,6 +147,8 @@ int setup_local_apps(void)
                return -ENOENT;
        snprintf(local_apps.sock_path, PATH_MAX,
                 DEFAULT_HOME_APPS_UNIX_SOCK, home_dir);
+       snprintf(local_apps.wait_shm_path, PATH_MAX,
+                DEFAULT_HOME_APPS_WAIT_SHM_PATH, uid);
        return 0;
 }
 
@@ -267,7 +291,7 @@ void cleanup_sock_info(struct sock_info *sock_info)
        if (sock_info->socket != -1) {
                ret = close(sock_info->socket);
                if (ret) {
-                       ERR("Error closing local apps socket");
+                       ERR("Error closing apps socket");
                }
                sock_info->socket = -1;
        }
@@ -278,6 +302,221 @@ void cleanup_sock_info(struct sock_info *sock_info)
                }
                sock_info->root_handle = -1;
        }
+       sock_info->constructor_sem_posted = 0;
+       if (sock_info->wait_shm_mmap) {
+               ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE));
+               if (ret) {
+                       ERR("Error unmapping wait shm");
+               }
+               sock_info->wait_shm_mmap = NULL;
+       }
+}
+
+/*
+ * Using fork to set umask in the child process (not multi-thread safe).
+ * We deal with the shm_open vs ftruncate race (happening when the
+ * sessiond owns the shm and does not let everybody modify it, to ensure
+ * safety against shm_unlink) by simply letting the mmap fail and
+ * retrying after a few seconds.
+ * For global shm, everybody has rw access to it until the sessiond
+ * starts.
+ */
+static
+int get_wait_shm(struct sock_info *sock_info, size_t mmap_size)
+{
+       int wait_shm_fd, ret;
+       pid_t pid;
+
+       /*
+        * Try to open read-only.
+        */
+       wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+       if (wait_shm_fd >= 0) {
+               goto end;
+       } else if (wait_shm_fd < 0 && errno != ENOENT) {
+               /*
+                * Real-only open did not work, and it's not because the
+                * entry was not present. It's a failure that prohibits
+                * using shm.
+                */
+               ERR("Error opening shm %s", sock_info->wait_shm_path);
+               goto end;
+       }
+       /*
+        * If the open failed because the file did not exist, try
+        * creating it ourself.
+        */
+       pid = fork();
+       if (pid > 0) {
+               int status;
+
+               /*
+                * Parent: wait for child to return, in which case the
+                * shared memory map will have been created.
+                */
+               pid = wait(&status);
+               if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+                       wait_shm_fd = -1;
+                       goto end;
+               }
+               /*
+                * Try to open read-only again after creation.
+                */
+               wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+               if (wait_shm_fd < 0) {
+                       /*
+                        * Real-only open did not work. It's a failure
+                        * that prohibits using shm.
+                        */
+                       ERR("Error opening shm %s", sock_info->wait_shm_path);
+                       goto end;
+               }
+               goto end;
+       } else if (pid == 0) {
+               int create_mode;
+
+               /* Child */
+               create_mode = S_IRUSR | S_IWUSR | S_IRGRP;
+               if (sock_info->global)
+                       create_mode |= S_IROTH | S_IWGRP | S_IWOTH;
+               /*
+                * We're alone in a child process, so we can modify the
+                * process-wide umask.
+                */
+               umask(~create_mode);
+               /*
+                * Try creating shm (or get rw access).
+                * We don't do an exclusive open, because we allow other
+                * processes to create+ftruncate it concurrently.
+                */
+               wait_shm_fd = shm_open(sock_info->wait_shm_path,
+                               O_RDWR | O_CREAT, create_mode);
+               if (wait_shm_fd >= 0) {
+                       ret = ftruncate(wait_shm_fd, mmap_size);
+                       if (ret) {
+                               PERROR("ftruncate");
+                               exit(EXIT_FAILURE);
+                       }
+                       exit(EXIT_SUCCESS);
+               }
+               /*
+                * For local shm, we need to have rw access to accept
+                * opening it: this means the local sessiond will be
+                * able to wake us up. For global shm, we open it even
+                * if rw access is not granted, because the root.root
+                * sessiond will be able to override all rights and wake
+                * us up.
+                */
+               if (!sock_info->global && errno != EACCES) {
+                       ERR("Error opening shm %s", sock_info->wait_shm_path);
+                       exit(EXIT_FAILURE);
+               }
+               /*
+                * The shm exists, but we cannot open it RW. Report
+                * success.
+                */
+               exit(EXIT_SUCCESS);
+       } else {
+               return -1;
+       }
+end:
+       if (wait_shm_fd >= 0 && !sock_info->global) {
+               struct stat statbuf;
+
+               /*
+                * Ensure that our user is the owner of the shm file for
+                * local shm. If we do not own the file, it means our
+                * sessiond will not have access to wake us up (there is
+                * probably a rogue process trying to fake our
+                * sessiond). Fallback to polling method in this case.
+                */
+               ret = fstat(wait_shm_fd, &statbuf);
+               if (ret) {
+                       PERROR("fstat");
+                       goto error_close;
+               }
+               if (statbuf.st_uid != getuid())
+                       goto error_close;
+       }
+       return wait_shm_fd;
+
+error_close:
+       ret = close(wait_shm_fd);
+       if (ret) {
+               PERROR("Error closing fd");
+       }
+       return -1;
+}
+
+static
+char *get_map_shm(struct sock_info *sock_info)
+{
+       size_t mmap_size = sysconf(_SC_PAGE_SIZE);
+       int wait_shm_fd, ret;
+       char *wait_shm_mmap;
+
+       wait_shm_fd = get_wait_shm(sock_info, mmap_size);
+       if (wait_shm_fd < 0) {
+               goto error;
+       }
+       wait_shm_mmap = mmap(NULL, mmap_size, PROT_READ,
+                 MAP_SHARED, wait_shm_fd, 0);
+       /* close shm fd immediately after taking the mmap reference */
+       ret = close(wait_shm_fd);
+       if (ret) {
+               PERROR("Error closing fd");
+       }
+       if (wait_shm_mmap == MAP_FAILED) {
+               DBG("mmap error (can be caused by race with sessiond). Fallback to poll mode.");
+               goto error;
+       }
+       return wait_shm_mmap;
+
+error:
+       return NULL;
+}
+
+static
+void wait_for_sessiond(struct sock_info *sock_info)
+{
+       int ret;
+
+       ust_lock();
+       if (lttng_ust_comm_should_quit) {
+               goto quit;
+       }
+       if (!sock_info->wait_shm_mmap) {
+               sock_info->wait_shm_mmap = get_map_shm(sock_info);
+               if (!sock_info->wait_shm_mmap)
+                       goto error;
+       }
+       ust_unlock();
+
+       DBG("Waiting for %s apps sessiond", sock_info->name);
+       /* Wait for futex wakeup */
+       if (uatomic_read((int32_t *) sock_info->wait_shm_mmap) == 0) {
+               ret = futex_async((int32_t *) sock_info->wait_shm_mmap,
+                       FUTEX_WAIT, 0, NULL, NULL, 0);
+               /*
+                * FIXME: Currently, futexes on read-only shm seems to
+                * EFAULT.
+                */
+               if (ret < 0) {
+                       PERROR("futex");
+                       sleep(5);
+               }
+       }
+       return;
+
+quit:
+       ust_unlock();
+       return;
+
+error:
+       ust_unlock();
+       /* Error handling: fallback on a 5 seconds sleep. */
+       sleep(5);
+       return;
 }
 
 /*
@@ -310,8 +549,6 @@ restart:
                sock_info->socket = -1;
        }
 
-       /* Check for sessiond availability with pipe TODO */
-
        /* Register */
        ret = lttcomm_connect_unix_sock(sock_info->sock_path);
        if (ret < 0) {
@@ -323,7 +560,9 @@ restart:
                ret = handle_register_done(sock_info);
                assert(!ret);
                ust_unlock();
-               sleep(5);
+
+               /* Wait for sessiond availability with pipe */
+               wait_for_sessiond(sock_info);
                goto restart;
        }
 
@@ -353,7 +592,7 @@ restart:
                ret = handle_register_done(sock_info);
                assert(!ret);
                ust_unlock();
-               sleep(5);
+               wait_for_sessiond(sock_info);
                goto restart;
        }
        ust_unlock();
@@ -618,7 +857,7 @@ void ust_after_fork_child(ust_fork_info_t *fork_info)
        /* Release urcu mutexes */
        rcu_bp_after_fork_child();
        lttng_ust_cleanup(0);
-       lttng_ust_init();
        /* Release mutexes and reenable signals */
        ust_after_fork_common(fork_info);
+       lttng_ust_init();
 }
This page took 0.027127 seconds and 4 git commands to generate.