kernel-ctl.c kernel-ctl.h \
context.c context.h \
futex.c futex.h \
+ shm.c shm.h \
session.c session.h \
ltt-sessiond.h main.c
# link on liblttngctl for check if sessiond is already alive.
-ltt_sessiond_LDADD = \
+ltt_sessiond_LDADD = -lrt \
$(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \
$(top_builddir)/libkernelctl/libkernelctl.la \
$(top_builddir)/libustctl/libustctl.la \
*/
#define _GNU_SOURCE
+#include <limits.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <urcu.h>
* Ref: git://git.lttng.org/userspace-rcu.git
*/
+/*
+ * Update futex according to active or not. This scheme is used to wake every
+ * libust waiting on the shared memory map futex hence the INT_MAX used in the
+ * futex() call. If active, we set the value and wake everyone else we indicate
+ * that we are gone (cleanup() case).
+ */
+void futex_wait_update(int32_t *futex, int active)
+{
+ if (active) {
+ uatomic_set(futex, 1);
+ futex_async(futex, FUTEX_WAKE,
+ INT_MAX, NULL, NULL, 0);
+ } else {
+ uatomic_set(futex, 0);
+ }
+
+ DBG("Futex wait update active %d", active);
+}
+
/*
* Prepare futex.
*/
#ifndef _LTT_FUTEX_H
#define _LTT_FUTEX_H
+void futex_wait_update(int32_t *futex, int active);
void futex_nto1_prepare(int32_t *futex);
void futex_nto1_wait(int32_t *futex);
void futex_nto1_wake(int32_t *futex);
#define DEFAULT_GLOBAL_APPS_PIPE DEFAULT_UST_SOCK_DIR "/global"
#define DEFAULT_TRACE_OUTPUT DEFAULT_HOME_DIR "/lttng"
+#define DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait"
+#define DEFAULT_HOME_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait-%u"
+
struct module_param {
const char *name;
int required;
/*
* Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
+#include <urcu/futex.h>
#include <unistd.h>
#include <ltt-kconsumerd.h>
#include "futex.h"
#include "kernel-ctl.h"
#include "ltt-sessiond.h"
+#include "shm.h"
#include "traceable-app.h"
#include "ust-ctl.h"
#include "utils.h"
static char client_unix_sock_path[PATH_MAX]; /* Global client Unix socket path */
static char kconsumerd_err_unix_sock_path[PATH_MAX]; /* kconsumerd error Unix socket path */
static char kconsumerd_cmd_unix_sock_path[PATH_MAX]; /* kconsumerd command Unix socket path */
+static char wait_shm_path[PATH_MAX]; /* global wait shm path for UST */
/* Sockets and FDs */
static int client_sock;
static pthread_mutex_t kconsumerd_pid_mutex; /* Mutex to control kconsumerd pid assignation */
-static int modprobe_remove_kernel_modules(void);
-
/*
* 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
*/
static struct ltt_session_list *session_list_ptr;
+/*
+ * Remove modules in reverse load order.
+ */
+static int modprobe_remove_kernel_modules(void)
+{
+ int ret = 0, i;
+ char modprobe[256];
+
+ for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) {
+ ret = snprintf(modprobe, sizeof(modprobe),
+ "/sbin/modprobe --remove --quiet %s",
+ kernel_modules_list[i].name);
+ if (ret < 0) {
+ perror("snprintf modprobe --remove");
+ goto error;
+ }
+ modprobe[sizeof(modprobe) - 1] = '\0';
+ ret = system(modprobe);
+ if (ret == -1) {
+ ERR("Unable to launch modprobe --remove for module %s",
+ kernel_modules_list[i].name);
+ } else if (kernel_modules_list[i].required
+ && WEXITSTATUS(ret) != 0) {
+ ERR("Unable to remove module %s",
+ kernel_modules_list[i].name);
+ } else {
+ DBG("Modprobe removal successful %s",
+ kernel_modules_list[i].name);
+ }
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Return group ID of the tracing group or -1 if not found.
+ */
static gid_t allowed_group(void)
{
struct group *grp;
return ret;
}
-#ifdef DISABLED
/*
- * Notify apps by writing 42 to a named pipe using name. Every applications
- * waiting for a ltt-sessiond will be notified and re-register automatically to
- * the session daemon.
- *
- * Return open or write error value.
+ * Notify UST applications using the shm mmap futex.
*/
-static int notify_apps(const char *name)
+static int notify_ust_apps(int active)
{
- int fd;
- int ret = -1;
+ char *wait_shm_mmap;
- DBG("Notify the global application pipe");
+ DBG("Notifying applications of session daemon state: %d", active);
- /* Try opening the global pipe */
- fd = open(name, O_WRONLY);
- if (fd < 0) {
+ /* See shm.c for this call implying mmap, shm and futex calls */
+ wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root);
+ if (wait_shm_mmap == NULL) {
goto error;
}
- /* Notify by writing on the pipe */
- ret = write(fd, "42", 2);
- if (ret < 0) {
- perror("write");
- }
+ /* Wake waiting process */
+ futex_wait_update((int32_t *) wait_shm_mmap, active);
+
+ /* Apps notified successfully */
+ return 0;
error:
- return ret;
+ return -1;
}
-#endif /* DISABLED */
/*
* Setup the outgoing data buffer for the response (llm) by allocating the
pollfd[1].events = POLLIN;
/* Notify all applications to register */
- //notify_apps(default_global_apps_pipe);
+ ret = notify_ust_apps(1);
+ if (ret < 0) {
+ ERR("Failed to notify applications or create the wait shared memory.\n"
+ "Execution continues but there might be problem for already running\n"
+ "applications that wishes to register.");
+ }
while (1) {
DBG("Accepting application registration");
}
error:
- DBG("Register apps thread dying");
- if (apps_sock) {
- close(apps_sock);
- }
- if (sock) {
- close(sock);
- }
+ DBG("UST Registration thread dying");
+
+ /* Notify that the registration thread is gone */
+ notify_ust_apps(0);
+
+ close(apps_sock);
+ close(sock);
unlink(apps_unix_sock_path);
+
return NULL;
}
return ret;
}
-/*
- * modprobe_remove_kernel_modules
- * Remove modules in reverse load order.
- */
-static int modprobe_remove_kernel_modules(void)
-{
- int ret = 0, i;
- char modprobe[256];
-
- for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) {
- ret = snprintf(modprobe, sizeof(modprobe),
- "/sbin/modprobe --remove --quiet %s",
- kernel_modules_list[i].name);
- if (ret < 0) {
- perror("snprintf modprobe --remove");
- goto error;
- }
- modprobe[sizeof(modprobe) - 1] = '\0';
- ret = system(modprobe);
- if (ret == -1) {
- ERR("Unable to launch modprobe --remove for module %s",
- kernel_modules_list[i].name);
- } else if (kernel_modules_list[i].required
- && WEXITSTATUS(ret) != 0) {
- ERR("Unable to remove module %s",
- kernel_modules_list[i].name);
- } else {
- DBG("Modprobe removal successful %s",
- kernel_modules_list[i].name);
- }
- }
-
-error:
- return ret;
-}
-
/*
* mount_debugfs
*/
}
}
break;
+ case LTTNG_DOMAIN_UST_PID:
+ break;
default:
break;
}
kernel_wait_quiescent(kernel_tracer_fd);
break;
case LTTNG_DOMAIN_UST_PID:
+
break;
default:
ret = LTTCOMM_NOT_IMPLEMENTED;
snprintf(client_unix_sock_path, PATH_MAX,
DEFAULT_GLOBAL_CLIENT_UNIX_SOCK);
}
+
+ /* Set global SHM for ust */
+ if (strlen(wait_shm_path) == 0) {
+ snprintf(wait_shm_path, PATH_MAX,
+ DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH);
+ }
} else {
home_path = get_home_dir();
if (home_path == NULL) {
snprintf(client_unix_sock_path, PATH_MAX,
DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path);
}
+
+ /* Set global SHM for ust */
+ if (strlen(wait_shm_path) == 0) {
+ snprintf(wait_shm_path, PATH_MAX,
+ DEFAULT_HOME_APPS_WAIT_SHM_PATH, geteuid());
+ }
}
DBG("Client socket path %s", client_unix_sock_path);
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include <lttngerr.h>
+
+#include "shm.h"
+
+/*
+ * 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(char *shm_path, size_t mmap_size, int global)
+{
+ int wait_shm_fd, ret;
+ pid_t pid;
+ mode_t mode;
+
+ /* Default permissions */
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+ /* Change owner of the shm path */
+ if (global) {
+ ret = chown(shm_path, 0, 0);
+ if (ret < 0) {
+ if (errno != ENOENT) {
+ perror("chown wait shm");
+ goto error;
+ }
+ }
+
+ /*
+ * If global session daemon, any application can register so the shm
+ * needs to be set in read-only mode for others.
+ */
+ mode |= S_IROTH;
+ } else {
+ ret = chown(shm_path, getuid(), getgid());
+ if (ret < 0) {
+ if (errno != ENOENT) {
+ perror("chown wait shm");
+ goto error;
+ }
+ }
+ }
+
+ /*
+ * Set permissions to the shm even if we did not create the shm.
+ */
+ ret = chmod(shm_path, mode);
+ if (ret < 0) {
+ if (errno != ENOENT) {
+ perror("chmod wait shm");
+ goto error;
+ }
+ }
+
+ /*
+ * 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) {
+ goto error;
+ }
+
+ /*
+ * Try to open read-only again after creation.
+ */
+ wait_shm_fd = shm_open(shm_path, O_RDWR, 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", shm_path);
+ goto error;
+ }
+ goto end;
+ } else if (pid == 0) {
+ /*
+ * We're alone in a child process, so we can modify the process-wide
+ * umask.
+ */
+ umask(~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(shm_path, O_RDWR | O_CREAT, mode);
+ if (wait_shm_fd >= 0) {
+ ret = ftruncate(wait_shm_fd, mmap_size);
+ if (ret < 0) {
+ perror("ftruncate wait shm");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = fchmod(wait_shm_fd, mode);
+ if (ret < 0) {
+ perror("fchmod");
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ ERR("Error opening shm %s", shm_path);
+ exit(EXIT_FAILURE);
+ } else {
+ return -1;
+ }
+
+end:
+ DBG("Got the wait shm fd %d", wait_shm_fd);
+
+ return wait_shm_fd;
+
+error:
+ DBG("Failing to get the wait shm fd");
+
+ return -1;
+}
+
+/*
+ * Return the wait shm mmap for UST application notification. The global
+ * variable is used to indicate if the the session daemon is global
+ * (root:tracing) or running with an unprivileged user.
+ *
+ * This returned value is used by futex_wait_update() in futex.c to WAKE all
+ * waiters which are UST application waiting for a session daemon.
+ */
+char *shm_ust_get_mmap(char *shm_path, int global)
+{
+ size_t mmap_size = sysconf(_SC_PAGE_SIZE);
+ int wait_shm_fd, ret;
+ char *wait_shm_mmap;
+
+ wait_shm_fd = get_wait_shm(shm_path, mmap_size, global);
+ if (wait_shm_fd < 0) {
+ goto error;
+ }
+
+ wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | 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 ust).");
+ goto error;
+ }
+
+ return wait_shm_mmap;
+
+error:
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _LTT_SHM_H
+#define _LTT_SHM_H
+
+char *shm_ust_get_mmap(char *shm_path, int global);
+
+#endif /* _LTT_SHM_H */
}
/*
- * enable_channel
- *
- * Adding channel using the lttng API.
+ * Adding channel using the lttng API.
*/
static int enable_channel(char *session_name)
{
if (opt_kernel) {
dom.type = LTTNG_DOMAIN_KERNEL;
+ } else if (opt_pid != 0) {
+ dom.type = LTTNG_DOMAIN_UST_PID;
+ dom.attr.pid = opt_pid;
+ DBG("PID %d set to lttng handle", opt_pid);
+ } else {
+ ret = CMD_NOT_IMPLEMENTED;
+ goto error;
}
handle = lttng_create_handle(session_name, &dom);
if (handle == NULL) {
ret = -1;
goto error;
+ } else {
+ ERR("Please specify a tracer (--kernel or --userspace)");
+ goto error;
}
- /* Strip event list */
+ /* Strip channel list (format: chan1,chan2,...) */
channel_name = strtok(opt_channels, ",");
while (channel_name != NULL) {
- /* Kernel tracer action */
- if (opt_kernel) {
- DBG("Enabling kernel channel %s", channel_name);
+ /* Copy channel name and normalize it */
+ strncpy(chan.name, channel_name, NAME_MAX);
+ chan.name[NAME_MAX - 1] = '\0';
- /* Copy channel name and normalize it */
- strncpy(chan.name, channel_name, NAME_MAX);
- chan.name[NAME_MAX - 1] = '\0';
+ DBG("Enabling channel %s", channel_name);
- ret = lttng_enable_channel(handle, &chan);
- if (ret < 0) {
- goto error;
- } else {
- MSG("Kernel channel enabled %s", channel_name);
- }
- } else if (opt_userspace) { /* User-space tracer action */
- /*
- * TODO: Waiting on lttng UST 2.0
- */
- if (opt_pid_all) {
- } else if (opt_pid != 0) {
- }
- ret = CMD_NOT_IMPLEMENTED;
+ ret = lttng_enable_channel(handle, &chan);
+ if (ret < 0) {
goto error;
} else {
- ERR("Please specify a tracer (--kernel or --userspace)");
- goto error;
+ MSG("Channel enabled %s for session %s",
+ channel_name, session_name);
}
/* Next event */
}
/*
- * init_channel_config
- *
- * Default value for channel configuration.
+ * Default value for channel configuration.
*/
static void init_channel_config(void)
{
}
/*
- * Add channel to trace session
+ * Add channel to trace session
*/
int cmd_enable_channels(int argc, const char **argv)
{