relayd: Add backward compatibility for --group-output-by-session
authorJonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Thu, 19 Sep 2019 20:56:53 +0000 (16:56 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 8 Nov 2019 22:55:06 +0000 (17:55 -0500)
The current implementation works for producer (consumerd/lttng-sessiond)
using protocol version >= 2.11.

For producer using protocol version >= 2.4 and < 2.11, we can use the
session name passed on the RELAYD_CREATE_SESSION command to infer
information (creation datetime, base path) from the stream path passed on
the RELAYD_ADD_STREAM command. That information is then used to craft a
stream path, in a best effort manner, that comply with the
--group-output-by-session requirements:

  <session_name>/<hostname>-<datetime>/trace

Prior to protocol version 2.4, the session name is unknown on lttng-relayd
side. In those cases, we do not perform any transformation and leave the
provided stream path as is. A warning (lttng-relayd) is emitted on such
occasions.

Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
src/bin/lttng-relayd/Makefile.am
src/bin/lttng-relayd/backward-compatibility-group-by.c [new file with mode: 0644]
src/bin/lttng-relayd/backward-compatibility-group-by.h [new file with mode: 0644]
src/bin/lttng-relayd/main.c

index c95359e742a2cca493e25d9bdfefeca4b56e0e8b..8b903680a7e9bf48d148a736d4f4e726d1860ba2 100644 (file)
@@ -23,7 +23,8 @@ lttng_relayd_SOURCES = main.c lttng-relayd.h utils.h utils.c cmd.h \
                        viewer-session.c viewer-session.h \
                        tracefile-array.c tracefile-array.h \
                        tcp_keep_alive.c tcp_keep_alive.h \
                        viewer-session.c viewer-session.h \
                        tracefile-array.c tracefile-array.h \
                        tcp_keep_alive.c tcp_keep_alive.h \
-                       sessiond-trace-chunks.c sessiond-trace-chunks.h
+                       sessiond-trace-chunks.c sessiond-trace-chunks.h \
+                       backward-compatibility-group-by.c backward-compatibility-group-by.h
 
 # link on liblttngctl for check if relayd is already alive.
 lttng_relayd_LDADD = -lurcu-common -lurcu \
 
 # link on liblttngctl for check if relayd is already alive.
 lttng_relayd_LDADD = -lurcu-common -lurcu \
diff --git a/src/bin/lttng-relayd/backward-compatibility-group-by.c b/src/bin/lttng-relayd/backward-compatibility-group-by.c
new file mode 100644 (file)
index 0000000..0127f63
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2019 - Jonathan Rajotte <jonathan.rajotte-julien@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, 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.
+ */
+
+#include <assert.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <common/common.h>
+#include <common/defaults.h>
+#include <common/utils.h>
+
+#include "backward-compatibility-group-by.h"
+
+#define DATETIME_STRING_SIZE 16
+#define DATETIME_REGEX \
+       ".*-[1-2][0-9][0-9][0-9][0-1][0-1][0-3][0-9]-[0-2][0-9][0-5][0-9][0-5][0-9]$"
+
+/*
+ * Provide support for --group-output-by-session for producer >= 2.4 and < 2.11.
+ * Take the stream path, extract all available information, craft a new path to
+ * the best of our ability enforcing the group by session.
+ *
+ * Return the allocated string containing the new stream path or else NULL.
+ */
+char *backward_compat_group_by_session(
+               const char *path, const char *local_session_name)
+{
+       int ret;
+       size_t len;
+       char *leftover_ptr;
+       char *local_copy = NULL;
+       char *datetime = NULL;
+       char *partial_base_path = NULL;
+       char *filepath_per_session = NULL;
+       const char *second_token_ptr;
+       const char *leftover_second_token_ptr;
+       const char *hostname_ptr;
+       regex_t regex;
+
+       assert(path);
+       assert(local_session_name);
+       assert(local_session_name[0] != '\0');
+
+       DBG("Parsing path \"%s\" of session \"%s\" to create a new path that is grouped by session",
+                       path, local_session_name);
+
+       /* Get a local copy for strtok */
+       local_copy = strdup(path);
+       if (!local_copy) {
+               PERROR("Failed to parse session path: couldn't copy input path");
+               goto error;
+       }
+
+       /*
+        * The use of strtok with '/' as delimiter is valid since we refuse '/'
+        * in session name and '/' is not a valid hostname character based on
+        * RFC-952 [1], RFC-921 [2] and refined in RFC-1123 [2].
+        * [1] https://tools.ietf.org/html/rfc952
+        * [2] https://tools.ietf.org/html/rfc921
+        * [3] https://tools.ietf.org/html/rfc1123#page-13
+        */
+
+       /*
+        * Get the hostname and possible session_name.
+        * Note that we can get the hostname and session name from the
+        * relay_session object we already have. Still, it is easier to
+        * tokenized the passed path to obtain the start of the path leftover.
+        */
+       hostname_ptr = strtok_r(local_copy, "/", &leftover_ptr);
+       if (!hostname_ptr) {
+               ERR("Failed to parse session path \"%s\": couldn't identify hostname",
+                               path);
+               goto error;
+       }
+
+       second_token_ptr = strtok_r(NULL, "/", &leftover_ptr);
+       if (!second_token_ptr) {
+               ERR("Failed to parse session path \"%s\": couldn't identify session name",
+                               path);
+               goto error;
+       }
+
+       /*
+        * Check if the second token is a base path set at url level. This is
+        * legal in streaming, live and snapshot [1]. Otherwise it is the
+        * session name with possibly a datetime attached [2]. Note that when
+        * "adding" snapshot output (lttng snapshot add-output), no session name
+        * is present in the path by default. The handling for "base path" take
+        * care of this case as well.
+        * [1] e.g --set-url net://localhost/my_marvellous_path
+        * [2] Can be:
+        *            <session_name>
+        *                When using --snapshot on session create.
+        *            <session_name>-<date>-<time>
+        *            <auto>-<date>-<time>
+        */
+       if (strncmp(second_token_ptr, local_session_name,
+                           strlen(local_session_name)) != 0) {
+               /*
+                * Token does not start with session name.
+                * This mean this is an extra path scenario.
+                * Duplicate the current token since it is part of an
+                * base_path.
+                * Set secDuplicate the current token since it is part of an
+                * base_path. The rest is the leftover.
+                * Set second_token_ptr to the local_session_name for further
+                * processing.
+                */
+               partial_base_path = strdup(second_token_ptr);
+               if (!partial_base_path) {
+                       PERROR("Failed to parse session path: couldn't copy partial base path");
+                       goto error;
+               }
+
+               second_token_ptr = local_session_name;
+       }
+
+       /*
+        * Based on the previous test, we can move inside the token ptr to
+        * remove the "local_session_name" and inspect the rest of the token.
+        * We are looking into extracting the creation datetime from either the
+        * session_name or the token. We need to to all this gymnastic because
+        * an extra path could decide to append a datetime to its first
+        * subdirectory.
+        * Possible scenario:
+        *     <session_name>
+        *     <session_name>-<date>-<time>
+        *     <auto>-<date>-<time>
+        *     <session_name>_base_path_foo_bar
+        *     <session_name>-<false date>-<false-time> (via a base path)
+        *
+        * We have no way to discern from the basic scenario of:
+        *     <session_name>-<date>-<time>
+        * and one done using a base path with the exact format we normally
+        * expect.
+        *
+        * e.g:
+        *     lttng create my_session -U
+        *         net://localhost/my_session-19910319-120000/
+        */
+       ret = regcomp(&regex, DATETIME_REGEX, 0);
+       if (ret) {
+               ERR("Failed to parse session path: regex compilation failed with code %d", ret);
+               goto error;
+       }
+
+       leftover_second_token_ptr =
+                       second_token_ptr + strlen(local_session_name);
+       len = strlen(leftover_second_token_ptr);
+       if (len == 0) {
+               /*
+                * We are either dealing with an auto session name or only the
+                * session_name. If this is a auto session name, we need to
+                * fetch the creation datetime.
+                */
+               ret = regexec(&regex, local_session_name, 0, NULL, 0);
+               if (ret == 0) {
+                       const ssize_t local_session_name_offset =
+                                       strlen(local_session_name) - DATETIME_STRING_SIZE + 1;
+
+                       assert(local_session_name_offset >= 0);
+                       datetime = strdup(local_session_name +
+                                       local_session_name_offset);
+                       if (!datetime) {
+                               PERROR("Failed to parse session path: couldn't copy datetime on regex match");
+                               goto error_regex;
+                       }
+               }
+       } else if (len == DATETIME_STRING_SIZE &&
+                       !regexec(&regex, leftover_second_token_ptr, 0, NULL,
+                                       0)) {
+               /*
+                * The leftover from the second token is of format
+                * "-<datetime>", use it as the creation time.
+                * Ignore leading "-".
+                */
+               datetime = strdup(&leftover_second_token_ptr[1]);
+               if (!datetime) {
+                       PERROR("Failed to parse session path: couldn't copy datetime on regex match");
+                       goto error_regex;
+               }
+       } else {
+               /*
+                * Base path scenario.
+                * We cannot try to extract the datetime from the session name
+                * since nothing prevent a user to name a session in the
+                * "name-<datetime>" format. Using the datetime from such a
+                * session would be invalid.
+                * */
+               assert(partial_base_path == NULL);
+               assert(datetime == NULL);
+
+               partial_base_path = strdup(second_token_ptr);
+               if (!partial_base_path) {
+                       PERROR("Failed to parse session path: couldn't copy partial base path");
+                       goto error_regex;
+               }
+       }
+
+       ret = asprintf(&filepath_per_session, "%s/%s%s%s/%s%s%s",
+                       local_session_name, hostname_ptr, datetime ? "-" : "",
+                       datetime ? datetime : "",
+                       partial_base_path ? partial_base_path : "",
+                       partial_base_path ? "/" : "", leftover_ptr);
+       if (ret < 0) {
+               filepath_per_session = NULL;
+               goto error;
+       }
+error_regex:
+       regfree(&regex);
+error:
+       free(local_copy);
+       free(partial_base_path);
+       free(datetime);
+       return filepath_per_session;
+}
diff --git a/src/bin/lttng-relayd/backward-compatibility-group-by.h b/src/bin/lttng-relayd/backward-compatibility-group-by.h
new file mode 100644 (file)
index 0000000..5d9db80
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef BACKWARD_COMPATIBILITY_GROUP_BY_H
+#define BACKWARD_COMPATIBILITY_GROUP_BY_H
+
+/*
+ * Copyright (C) 2019 - Jonathan Rajotte <jonathan.rajotte-julien@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,
+ * 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.
+ */
+
+char *backward_compat_group_by_session(
+               const char *path, const char *local_session_name);
+
+#endif /* BACKWARD_COMPATIBILITY_GROUP_BY_H */
index e7c5b6c52d28dfcd1fa80fdf925c8d5317f45b27..325be9cf124df7d495fd3a27863195bebb9c55c4 100644 (file)
 #include <common/buffer-view.h>
 #include <common/string-utils/format.h>
 
 #include <common/buffer-view.h>
 #include <common/string-utils/format.h>
 
-#include "version.h"
+#include "backward-compatibility-group-by.h"
 #include "cmd.h"
 #include "cmd.h"
+#include "connection.h"
 #include "ctf-trace.h"
 #include "ctf-trace.h"
+#include "health-relayd.h"
 #include "index.h"
 #include "index.h"
-#include "utils.h"
-#include "lttng-relayd.h"
 #include "live.h"
 #include "live.h"
-#include "health-relayd.h"
-#include "testpoint.h"
-#include "viewer-stream.h"
+#include "lttng-relayd.h"
 #include "session.h"
 #include "session.h"
+#include "sessiond-trace-chunks.h"
 #include "stream.h"
 #include "stream.h"
-#include "connection.h"
-#include "tracefile-array.h"
 #include "tcp_keep_alive.h"
 #include "tcp_keep_alive.h"
-#include "sessiond-trace-chunks.h"
+#include "testpoint.h"
+#include "tracefile-array.h"
+#include "utils.h"
+#include "version.h"
+#include "viewer-stream.h"
 
 static const char *help_msg =
 #ifdef LTTNG_EMBED_HELP
 
 static const char *help_msg =
 #ifdef LTTNG_EMBED_HELP
@@ -1370,12 +1371,57 @@ static int relay_add_stream(const struct lttcomm_relayd_hdr *recv_hdr,
                goto send_reply;
        }
 
                goto send_reply;
        }
 
+       /*
+        * Backward compatibility for --group-output-by-session.
+        * Prior to lttng 2.11, the complete path is passed by the stream.
+        * Starting at 2.11, lttng-relayd uses chunk. When dealing with producer
+        * >=2.11 the chunk is responsible for the output path. When dealing
+        * with producer < 2.11 the chunk output_path is the root output path
+        * and the stream carries the complete path (path_name).
+        * To support --group-output-by-session with older producer (<2.11), we
+        * need to craft the path based on the stream path.
+        */
+       if (opt_group_output_by == RELAYD_GROUP_OUTPUT_BY_SESSION) {
+               if (conn->minor < 4) {
+                       /*
+                        * From 2.1 to 2.3, the session_name is not passed on
+                        * the RELAYD_CREATE_SESSION command. The session name
+                        * is necessary to detect the presence of a base_path
+                        * inside the stream path. Without it we cannot perform
+                        * a valid group-output-by-session transformation.
+                        */
+                       WARN("Unable to perform a --group-by-session transformation for session %" PRIu64
+                            " for stream with path \"%s\" as it is produced by a peer using a protocol older than v2.4",
+                                       session->id, path_name);
+               } else if (conn->minor >= 4 && conn->minor < 11) {
+                       char *group_by_session_path_name;
+
+                       assert(session->session_name[0] != '\0');
+
+                       group_by_session_path_name =
+                                       backward_compat_group_by_session(
+                                                       path_name,
+                                                       session->session_name);
+                       if (!group_by_session_path_name) {
+                               ERR("Failed to apply group by session to stream of session %" PRIu64,
+                                               session->id);
+                               goto send_reply;
+                       }
+
+                       DBG("Transformed session path from \"%s\" to \"%s\" to honor per-session name grouping",
+                                       path_name, group_by_session_path_name);
+
+                       free(path_name);
+                       path_name = group_by_session_path_name;
+               }
+       }
+
        trace = ctf_trace_get_by_path_or_create(session, path_name);
        if (!trace) {
                goto send_reply;
        }
        trace = ctf_trace_get_by_path_or_create(session, path_name);
        if (!trace) {
                goto send_reply;
        }
-       /* This stream here has one reference on the trace. */
 
 
+       /* This stream here has one reference on the trace. */
        pthread_mutex_lock(&last_relay_stream_id_lock);
        stream_handle = ++last_relay_stream_id;
        pthread_mutex_unlock(&last_relay_stream_id_lock);
        pthread_mutex_lock(&last_relay_stream_id_lock);
        stream_handle = ++last_relay_stream_id;
        pthread_mutex_unlock(&last_relay_stream_id_lock);
This page took 0.029314 seconds and 4 git commands to generate.