relayd: clean-up: reference is repeated in comment
[lttng-tools.git] / src / bin / lttng-relayd / backward-compatibility-group-by.c
1 /*
2 * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include <assert.h>
9 #include <regex.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include <common/common.h>
15 #include <common/defaults.h>
16 #include <common/utils.h>
17
18 #include "backward-compatibility-group-by.h"
19
20 #define DATETIME_STRING_SIZE 16
21 #define DATETIME_REGEX \
22 ".*-[1-2][0-9][0-9][0-9][0-1][0-9][0-3][0-9]-[0-2][0-9][0-5][0-9][0-5][0-9]$"
23
24 /*
25 * Provide support for --group-output-by-session for producer >= 2.4 and < 2.11.
26 * Take the stream path, extract all available information, craft a new path to
27 * the best of our ability enforcing the group by session.
28 *
29 * Return the allocated string containing the new stream path or else NULL.
30 */
31 char *backward_compat_group_by_session(
32 const char *path, const char *local_session_name)
33 {
34 int ret;
35 size_t len;
36 char *leftover_ptr;
37 char *local_copy = NULL;
38 char *datetime = NULL;
39 char *partial_base_path = NULL;
40 char *filepath_per_session = NULL;
41 const char *second_token_ptr;
42 const char *leftover_second_token_ptr;
43 const char *hostname_ptr;
44 regex_t regex;
45
46 assert(path);
47 assert(local_session_name);
48 assert(local_session_name[0] != '\0');
49
50 DBG("Parsing path \"%s\" of session \"%s\" to create a new path that is grouped by session",
51 path, local_session_name);
52
53 /* Get a local copy for strtok */
54 local_copy = strdup(path);
55 if (!local_copy) {
56 PERROR("Failed to parse session path: couldn't copy input path");
57 goto error;
58 }
59
60 /*
61 * The use of strtok with '/' as delimiter is valid since we refuse '/'
62 * in session name and '/' is not a valid hostname character based on
63 * RFC-952 [1], RFC-921 [2] and refined in RFC-1123 [3].
64 * [1] https://tools.ietf.org/html/rfc952
65 * [2] https://tools.ietf.org/html/rfc921
66 * [3] https://tools.ietf.org/html/rfc1123#page-13
67 */
68
69 /*
70 * Get the hostname and possible session_name.
71 * Note that we can get the hostname and session name from the
72 * relay_session object we already have. Still, it is easier to
73 * tokenized the passed path to obtain the start of the path leftover.
74 */
75 hostname_ptr = strtok_r(local_copy, "/", &leftover_ptr);
76 if (!hostname_ptr) {
77 ERR("Failed to parse session path \"%s\": couldn't identify hostname",
78 path);
79 goto error;
80 }
81
82 second_token_ptr = strtok_r(NULL, "/", &leftover_ptr);
83 if (!second_token_ptr) {
84 ERR("Failed to parse session path \"%s\": couldn't identify session name",
85 path);
86 goto error;
87 }
88
89 /*
90 * Check if the second token is a base path set at url level. This is
91 * legal in streaming, live and snapshot [1]. Otherwise it is the
92 * session name with possibly a datetime attached [2]. Note that when
93 * "adding" snapshot output (lttng snapshot add-output), no session name
94 * is present in the path by default. The handling for "base path" take
95 * care of this case as well.
96 * [1] e.g --set-url net://localhost/my_marvellous_path
97 * [2] Can be:
98 * <session_name>
99 * When using --snapshot on session create.
100 * <session_name>-<date>-<time>
101 * <auto>-<date>-<time>
102 */
103 if (strncmp(second_token_ptr, local_session_name,
104 strlen(local_session_name)) != 0) {
105 /*
106 * Token does not start with session name.
107 * This mean this is an extra path scenario.
108 * Duplicate the current token since it is part of an
109 * base_path.
110 * Set secDuplicate the current token since it is part of an
111 * base_path. The rest is the leftover.
112 * Set second_token_ptr to the local_session_name for further
113 * processing.
114 */
115 partial_base_path = strdup(second_token_ptr);
116 if (!partial_base_path) {
117 PERROR("Failed to parse session path: couldn't copy partial base path");
118 goto error;
119 }
120
121 second_token_ptr = local_session_name;
122 }
123
124 /*
125 * Based on the previous test, we can move inside the token ptr to
126 * remove the "local_session_name" and inspect the rest of the token.
127 * We are looking into extracting the creation datetime from either the
128 * session_name or the token. We need to to all this gymnastic because
129 * an extra path could decide to append a datetime to its first
130 * subdirectory.
131 * Possible scenario:
132 * <session_name>
133 * <session_name>-<date>-<time>
134 * <auto>-<date>-<time>
135 * <session_name>_base_path_foo_bar
136 * <session_name>-<false date>-<false-time> (via a base path)
137 *
138 * We have no way to discern from the basic scenario of:
139 * <session_name>-<date>-<time>
140 * and one done using a base path with the exact format we normally
141 * expect.
142 *
143 * e.g:
144 * lttng create my_session -U
145 * net://localhost/my_session-19910319-120000/
146 */
147 ret = regcomp(&regex, DATETIME_REGEX, 0);
148 if (ret) {
149 ERR("Failed to parse session path: regex compilation failed with code %d", ret);
150 goto error;
151 }
152
153 leftover_second_token_ptr =
154 second_token_ptr + strlen(local_session_name);
155 len = strlen(leftover_second_token_ptr);
156 if (len == 0) {
157 /*
158 * We are either dealing with an auto session name or only the
159 * session_name. If this is a auto session name, we need to
160 * fetch the creation datetime.
161 */
162 ret = regexec(&regex, local_session_name, 0, NULL, 0);
163 if (ret == 0) {
164 const ssize_t local_session_name_offset =
165 strlen(local_session_name) - DATETIME_STRING_SIZE + 1;
166
167 assert(local_session_name_offset >= 0);
168 datetime = strdup(local_session_name +
169 local_session_name_offset);
170 if (!datetime) {
171 PERROR("Failed to parse session path: couldn't copy datetime on regex match");
172 goto error_regex;
173 }
174 }
175 } else if (len == DATETIME_STRING_SIZE &&
176 !regexec(&regex, leftover_second_token_ptr, 0, NULL,
177 0)) {
178 /*
179 * The leftover from the second token is of format
180 * "-<datetime>", use it as the creation time.
181 * Ignore leading "-".
182 */
183 datetime = strdup(&leftover_second_token_ptr[1]);
184 if (!datetime) {
185 PERROR("Failed to parse session path: couldn't copy datetime on regex match");
186 goto error_regex;
187 }
188 } else {
189 /*
190 * Base path scenario.
191 * We cannot try to extract the datetime from the session name
192 * since nothing prevent a user to name a session in the
193 * "name-<datetime>" format. Using the datetime from such a
194 * session would be invalid.
195 * */
196 assert(partial_base_path == NULL);
197 assert(datetime == NULL);
198
199 partial_base_path = strdup(second_token_ptr);
200 if (!partial_base_path) {
201 PERROR("Failed to parse session path: couldn't copy partial base path");
202 goto error_regex;
203 }
204 }
205
206 ret = asprintf(&filepath_per_session, "%s/%s%s%s/%s%s%s",
207 local_session_name, hostname_ptr, datetime ? "-" : "",
208 datetime ? datetime : "",
209 partial_base_path ? partial_base_path : "",
210 partial_base_path ? "/" : "", leftover_ptr);
211 if (ret < 0) {
212 filepath_per_session = NULL;
213 goto error;
214 }
215 error_regex:
216 regfree(&regex);
217 error:
218 free(local_copy);
219 free(partial_base_path);
220 free(datetime);
221 return filepath_per_session;
222 }
This page took 0.04902 seconds and 4 git commands to generate.