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 | ||
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 \ | |
eb60c7af | 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]$" |
2a635488 JR |
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 | |
75f3490a | 63 | * RFC-952 [1], RFC-921 [2] and refined in RFC-1123 [3]. |
2a635488 JR |
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(®ex, 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(®ex, 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(®ex, 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(®ex); | |
217 | error: | |
218 | free(local_copy); | |
219 | free(partial_base_path); | |
220 | free(datetime); | |
221 | return filepath_per_session; | |
222 | } |