Commit | Line | Data |
---|---|---|
2a635488 | 1 | /* |
ab5be9fa | 2 | * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com> |
2a635488 | 3 | * |
ab5be9fa | 4 | * SPDX-License-Identifier: GPL-2.0-only |
2a635488 | 5 | * |
2a635488 JR |
6 | */ |
7 | ||
a961be17 | 8 | #include "common/time.h" |
2a635488 JR |
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 \ | |
eb60c7af | 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]$" |
2a635488 JR |
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 | */ | |
a961be17 JG |
32 | char *backward_compat_group_by_session(const char *path, |
33 | const char *local_session_name, | |
34 | time_t relay_session_creation_time) | |
2a635488 JR |
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 | |
75f3490a | 65 | * RFC-952 [1], RFC-921 [2] and refined in RFC-1123 [3]. |
2a635488 JR |
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(®ex, 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(®ex, local_session_name, 0, NULL, 0); | |
165 | if (ret == 0) { | |
166 | const ssize_t local_session_name_offset = | |
a961be17 | 167 | strlen(local_session_name) - DATETIME_STR_LEN + 1; |
2a635488 JR |
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 | } | |
a961be17 JG |
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 | } | |
2a635488 | 189 | } |
a961be17 | 190 | } else if (len == DATETIME_STR_LEN && |
2a635488 JR |
191 | !regexec(®ex, 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(®ex); | |
232 | error: | |
233 | free(local_copy); | |
234 | free(partial_base_path); | |
235 | free(datetime); | |
236 | return filepath_per_session; | |
237 | } |