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