From 92da2da7ae54f0e7b143e859806510f30ed690fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Genevi=C3=A8ve=20Bastien?= <gbastien+lttng@versatic.net>
Date: Mon, 26 Feb 2018 09:51:35 -0500
Subject: [PATCH] Add thread to monitor udev
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
---
 configure.ac                                   |  19 +++
 src/bin/lttng-sessiond/Makefile.am             |   7 +-
 src/bin/lttng-sessiond/health-sessiond.h       |   1 +
 src/bin/lttng-sessiond/main.c                  |  14 ++
 src/bin/lttng-sessiond/udev-monitor-provider.h |  67 ++++++++
 src/bin/lttng-sessiond/udev-monitor.c          | 202 +++++++++++++++++++++++++
 src/bin/lttng-sessiond/udev-monitor.h          |  34 +++++
 7 files changed, 341 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/lttng-sessiond/udev-monitor-provider.h
 create mode 100644 src/bin/lttng-sessiond/udev-monitor.c
 create mode 100644 src/bin/lttng-sessiond/udev-monitor.h

diff --git a/configure.ac b/configure.ac
index 972a73e8..bfeeeb66 100644
--- a/configure.ac
+++ b/configure.ac
@@ -561,6 +561,25 @@ AC_CHECK_LIB([dl], [dlopen],
 )
 AC_SUBST(DL_LIBS)
 
+# Check libudev
+PKG_CHECK_MODULES([UDEV], [libudev],
+  [
+    dnl PKG_CHECK_MODULES defines UDEV_LIBS
+  ],
+  [
+    AC_MSG_WARN([pkg-config was unable to find a valid .pc for libudev. Set PKG_CONFIG_PATH to specify the pkgconfig configuration file location])
+    AC_MSG_WARN([Finding libudev without pkg-config.])
+    AC_CHECK_LIB([popt],
+      [udevGetContext],
+      [UDEV_LIBS="-ludev"],
+      [
+        AC_MSG_FAILURE([Cannot find libudev. Either set PKG_CONFIG_PATH to the configuration file location or use LDFLAGS=-Ldir to specify the library location])
+      ]
+    )
+  ]
+)
+AC_SUBST(UDEV_LIBS)
+
 # Check for fmemopen
 AC_CHECK_LIB([c], [fmemopen],
 [
diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am
index 413fe75a..f3d92b45 100644
--- a/src/bin/lttng-sessiond/Makefile.am
+++ b/src/bin/lttng-sessiond/Makefile.am
@@ -11,7 +11,7 @@ lttng_sessiond_SOURCES = utils.c utils.h \
                        trace-kernel.c trace-kernel.h \
                        kernel.c kernel.h \
                        ust-ctl.h ust-app.h trace-ust.h ust-thread.h \
-                       ust-registry.h \
+                       ust-registry.h udev-monitor.h \
                        context.c context.h \
                        channel.c channel.h \
                        event.c event.h \
@@ -40,7 +40,8 @@ lttng_sessiond_SOURCES = utils.c utils.h \
 if HAVE_LIBLTTNG_UST_CTL
 lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
 			ust-consumer.c ust-consumer.h ust-thread.c \
-			ust-metadata.c ust-clock.h agent-thread.c agent-thread.h
+			ust-metadata.c ust-clock.h agent-thread.c agent-thread.h \
+			udev-monitor-provider.h udev-monitor.c
 endif
 
 # Add main.c at the end for compile order
@@ -62,5 +63,5 @@ lttng_sessiond_LDADD = -lurcu-common -lurcu $(KMOD_LIBS) \
 
 
 if HAVE_LIBLTTNG_UST_CTL
-lttng_sessiond_LDADD += -llttng-ust-ctl
+lttng_sessiond_LDADD += -llttng-ust-ctl -ludev -llttng-ust
 endif
diff --git a/src/bin/lttng-sessiond/health-sessiond.h b/src/bin/lttng-sessiond/health-sessiond.h
index 5d94cc63..851f8cf3 100644
--- a/src/bin/lttng-sessiond/health-sessiond.h
+++ b/src/bin/lttng-sessiond/health-sessiond.h
@@ -30,6 +30,7 @@ enum health_type_sessiond {
 	HEALTH_SESSIOND_TYPE_APP_MANAGE_NOTIFY	= 6,
 	HEALTH_SESSIOND_TYPE_APP_REG_DISPATCH	= 7,
 	HEALTH_SESSIOND_TYPE_NOTIFICATION	= 8,
+	HEALTH_SESSIOND_TYPE_UDEV_MONITOR	= 9,
 
 	NR_HEALTH_SESSIOND_TYPES,
 };
diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c
index 726c4e38..3bf08ab8 100644
--- a/src/bin/lttng-sessiond/main.c
+++ b/src/bin/lttng-sessiond/main.c
@@ -68,6 +68,7 @@
 #include "health-sessiond.h"
 #include "testpoint.h"
 #include "ust-thread.h"
+#include "udev-monitor.h"
 #include "agent-thread.h"
 #include "save.h"
 #include "load-session-thread.h"
@@ -202,6 +203,7 @@ static pthread_t ht_cleanup_thread;
 static pthread_t agent_reg_thread;
 static pthread_t load_session_thread;
 static pthread_t notification_thread;
+static pthread_t monitor_udev_thread;
 
 /*
  * UST registration command queue. This queue is tied with a futex and uses a N
@@ -5914,6 +5916,18 @@ int main(int argc, char **argv)
 		}
 	}
 
+	/* Create the udev monitoring thread */
+	ret = pthread_create(&monitor_udev_thread, default_pthread_attr(),
+				ust_thread_monitor_udev, (void *) NULL);
+	if (ret) {
+		errno = ret;
+		PERROR("pthread_create udev_monitor_thread");
+		retval = -1;
+		stop_threads();
+		goto exit_load_session;
+	}
+
+
 	/* Create session loading thread. */
 	ret = pthread_create(&load_session_thread, default_pthread_attr(),
 			thread_load_session, load_info);
diff --git a/src/bin/lttng-sessiond/udev-monitor-provider.h b/src/bin/lttng-sessiond/udev-monitor-provider.h
new file mode 100644
index 00000000..d47d3f13
--- /dev/null
+++ b/src/bin/lttng-sessiond/udev-monitor-provider.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 Geneviève Bastien <gbastien@versatic.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+ 
+/*
+ * udev_monitor lttng-ust tracepoint provider.
+ */
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER udev_monitor
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "./udev-monitor-provider.h"
+
+#if !defined(_UDEV_MONITOR_PROVIDER_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _UDEV_MONITOR_PROVIDER_H
+
+#include <lttng/tracepoint.h> 
+
+/*
+ * KVM creation tracepoint. Adds the pid of the newly created machine
+ * and its uuid to match it with an 'env'.'uuid' metadata in the guest
+ * trace
+ */
+TRACEPOINT_EVENT(
+	udev_monitor,
+    kvm_created,
+    TP_ARGS(unsigned int, pid, const char *, uuid),
+    TP_FIELDS(
+    	ctf_integer(unsigned int, pid, pid)
+		ctf_string(uuid, uuid)
+    )
+)
+
+/*
+ * KVM destruction tracepoint. Adds the pid of the destroyed machine
+ */
+TRACEPOINT_EVENT(
+	udev_monitor,
+    kvm_destroyed,
+	TP_ARGS(unsigned int, pid),
+	TP_FIELDS(
+		ctf_integer(unsigned int, pid, pid)
+    )
+)
+
+#endif /* _UDEV_MONITOR_PROVIDER_H */
+
+#include <lttng/tracepoint-event.h>
diff --git a/src/bin/lttng-sessiond/udev-monitor.c b/src/bin/lttng-sessiond/udev-monitor.c
new file mode 100644
index 00000000..bfc54af8
--- /dev/null
+++ b/src/bin/lttng-sessiond/udev-monitor.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 - Geneviève Bastien <gbastien@versatic.net>
+ *
+ * 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, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _LGPL_SOURCE
+#include <assert.h>
+
+#include <common/common.h>
+#include <common/utils.h>
+#include <libudev.h>
+
+#include "fd-limit.h"
+#include "lttng-sessiond.h"
+#include "udev-monitor.h"
+#include "health-sessiond.h"
+#include "testpoint.h"
+
+/*
+ * We need to define TRACEPOINT_DEFINE in one C file in the program
+ * before including provider headers.
+ */
+#define TRACEPOINT_DEFINE
+//#define TRACEPOINT_CREATE_PROBES
+#include "udev-monitor-provider.h"
+
+#define KVM_NODE "/dev/kvm"
+#define KVM_EVENT_CREATE "create"
+#define KVM_EVENT_DESTROY "destroy"
+#define KVM_UUID_PARAM "-uuid"
+#define QEMU_KVM_UUID_LEN	37
+
+int doTraceKvmCreate(int pid) {
+	FILE *file;
+	char *line = NULL, uuid[QEMU_KVM_UUID_LEN];
+	size_t len = 0;
+	char cmdline_file[64];
+	uuid[0] = '\0';
+
+	if (pid > 0) {
+		sprintf(cmdline_file, "/proc/%d/cmdline", pid);
+		file = fopen(cmdline_file, "r");
+		if (file) {
+		    while (getdelim(&line, &len, '\0', file) != -1) {
+		    	if (!strcmp(KVM_UUID_PARAM, line)) {
+		    		// Read the next value in uuid
+		    		if (getdelim(&line, &len, '\0', file) != -1)
+		    			strcpy(uuid, line);
+		    	}
+		    }
+		    free(line);
+		    fclose(file);
+		}
+	}
+	DBG("[udev-monitor-thread] kvm created %d: %s", pid, uuid);
+//	tracepoint(udev_monitor, kvm_created, pid, uuid);
+
+	return 0;
+}
+
+int doTraceKvmEvent(struct udev_device * dev) {
+	struct udev_list_entry *list, *current;
+	const char *event, *pidstr;
+	int pid = -1;
+
+//	if (!tracepoint_enabled(udev_monitor, kvm_created)) {
+//		// No need to do anything
+//	    return 0;
+//	}
+
+	list = udev_device_get_properties_list_entry(dev);
+	current = udev_list_entry_get_by_name(list, "EVENT");
+	if (!current) {
+		return 0;
+	}
+	event = udev_list_entry_get_value(current);
+	current = udev_list_entry_get_by_name(list, "PID");
+	if (!current) {
+		return 0;
+	}
+	pidstr = udev_list_entry_get_value(current);
+	pid = strtoul(pidstr, NULL, 0);
+	if (!strcmp(KVM_EVENT_CREATE, event)) {
+		doTraceKvmCreate(pid);
+	} else if (!strcmp(KVM_EVENT_DESTROY, event)) {
+		DBG("[udev-monitor-thread] kvm destroyed %d", pid);
+//		tracepoint(udev_monitor, kvm_destroyed, pid);
+	}
+
+	return 0;
+}
+
+/*
+ * This thread manage application notify communication.
+ */
+void *ust_thread_monitor_udev(void *data)
+{
+	int ret, err = -1;
+	struct udev *udev;
+	struct udev_device *dev;
+	struct udev_monitor *mon;
+	int fd;
+	const char *node;
+
+	DBG("[udev-monitor-thread] Monitoring udev events ");
+
+	rcu_register_thread();
+	rcu_thread_online();
+
+	health_register(health_sessiond,
+			HEALTH_SESSIOND_TYPE_UDEV_MONITOR);
+
+//	if (testpoint(sessiond_thread_app_manage_notify)) {
+//		goto error_testpoint;
+//	}
+
+	health_code_update();
+
+	/* Create the udev object */
+	udev = udev_new();
+	if (!udev) {
+		printf("[udev-monitor-thread] Can't create udev\n");
+		goto error;
+	}
+
+	/* Set up a monitor to monitor the misc devices */
+	mon = udev_monitor_new_from_netlink(udev, "udev");
+	udev_monitor_filter_add_match_subsystem_devtype(mon, "misc", NULL);
+	udev_monitor_enable_receiving(mon);
+	/* Get the file descriptor (fd) for the monitor.
+	   This fd will get passed to select() */
+	fd = udev_monitor_get_fd(mon);
+
+	health_code_update();
+
+	/* This section will run continuously, calling usleep() at
+	   the end of each pass. This is to demonstrate how to use
+	   a udev_monitor in a non-blocking way. */
+	while (1) {
+		/* Set up the call to select(). In this case, select() will
+		   only operate on a single file descriptor, the one
+		   associated with our udev_monitor. Note that the timeval
+		   object is set to 0, which will cause select() to not
+		   block. */
+		fd_set fds;
+		struct timeval tv;
+
+		// TODO See if thread needs to be killed
+
+		FD_ZERO(&fds);
+		FD_SET(fd, &fds);
+		tv.tv_sec = 0;
+		tv.tv_usec = 0;
+
+		ret = select(fd+1, &fds, NULL, NULL, &tv);
+
+		/* Check if our file descriptor has received data. */
+		if (ret > 0 && FD_ISSET(fd, &fds)) {
+			DBG3("[udev-monitor-thread] Received events from udev");
+			/* Make the call to receive the device.
+			   select() ensured that this will not block. */
+			dev = udev_monitor_receive_device(mon);
+			if (dev) {
+				node = udev_device_get_devnode(dev);
+				if (!strcmp(KVM_NODE, node)) {
+					DBG3("[udev-monitor-thread] Received events from udev for kvm node");
+//					doTraceKvmEvent(dev);
+				}
+
+				udev_device_unref(dev);
+			}
+			health_code_update();
+		}
+		usleep(250*1000);
+	}
+
+error:
+
+//	utils_close_pipe(apps_cmd_notify_pipe);
+//	apps_cmd_notify_pipe[0] = apps_cmd_notify_pipe[1] = -1;
+	DBG("[udev-monitor-thread] Udev monitoring thread cleanup complete");
+	if (err) {
+		health_error();
+		ERR("[udev-monitor-thread] Health error occurred in %s", __func__);
+	}
+	health_unregister(health_sessiond);
+	rcu_thread_offline();
+	rcu_unregister_thread();
+	return NULL;
+}
diff --git a/src/bin/lttng-sessiond/udev-monitor.h b/src/bin/lttng-sessiond/udev-monitor.h
new file mode 100644
index 00000000..032e33fb
--- /dev/null
+++ b/src/bin/lttng-sessiond/udev-monitor.h
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (C) 2018 - Geneviève Bastien <gbastien@versatic.net>
+ *
+ * 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, as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UDEV_MONITOR_H
+#define UDEV_MONITOR_H
+
+#ifdef HAVE_LIBLTTNG_UST_CTL
+
+void *ust_thread_monitor_udev(void *data);
+
+#else /* HAVE_LIBLTTNG_UST_CTL */
+
+void *ust_thread_monitor_udev(void *data)
+{
+	return NULL;
+}
+
+#endif /* HAVE_LIBLTTNG_UST_CTL */
+
+#endif /* UDEV_MONITOR_H */
-- 
2.16.2

