Commit | Line | Data |
---|---|---|
db66e574 JD |
1 | /* |
2 | * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com> | |
92816cc3 | 3 | * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com> |
db66e574 JD |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License, version 2 only, as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program; if not, write to the Free Software Foundation, Inc., 51 | |
16 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
17 | */ | |
18 | ||
19 | #define _LGPL_SOURCE | |
20 | #include <lttng/trigger/trigger.h> | |
21 | #include <common/error.h> | |
22 | #include <common/config/session-config.h> | |
23 | #include <common/defaults.h> | |
24 | #include <common/utils.h> | |
25 | #include <common/futex.h> | |
26 | #include <common/align.h> | |
27 | #include <common/time.h> | |
28 | #include <common/hashtable/utils.h> | |
29 | #include <common/kernel-ctl/kernel-ctl.h> | |
30 | #include <sys/eventfd.h> | |
31 | #include <sys/stat.h> | |
32 | #include <time.h> | |
33 | #include <signal.h> | |
34 | #include <inttypes.h> | |
35 | ||
90936dcf | 36 | #include <lttng/notification/channel-internal.h> |
d88744a4 JD |
37 | #include <lttng/rotate-internal.h> |
38 | ||
db66e574 JD |
39 | #include "session.h" |
40 | #include "rotate.h" | |
41 | #include "rotation-thread.h" | |
42 | #include "lttng-sessiond.h" | |
43 | #include "health-sessiond.h" | |
44 | #include "cmd.h" | |
45 | #include "utils.h" | |
90936dcf | 46 | #include "notification-thread-commands.h" |
db66e574 JD |
47 | |
48 | #include <urcu.h> | |
49 | #include <urcu/list.h> | |
50 | #include <urcu/rculfhash.h> | |
51 | ||
db66e574 JD |
52 | /* The session's lock must be held by the caller. */ |
53 | static | |
54 | int session_rename_chunk(struct ltt_session *session, char *current_path, | |
55 | char *new_path) | |
56 | { | |
57 | int ret; | |
58 | struct consumer_socket *socket; | |
59 | struct consumer_output *output; | |
60 | struct lttng_ht_iter iter; | |
61 | uid_t uid; | |
62 | gid_t gid; | |
63 | ||
64 | DBG("Renaming session chunk path of session \"%s\" from %s to %s", | |
65 | session->name, current_path, new_path); | |
66 | ||
67 | /* | |
68 | * Either one of the sessions is enough to find the consumer_output | |
69 | * and uid/gid. | |
70 | */ | |
71 | if (session->kernel_session) { | |
72 | output = session->kernel_session->consumer; | |
73 | uid = session->kernel_session->uid; | |
74 | gid = session->kernel_session->gid; | |
75 | } else if (session->ust_session) { | |
76 | output = session->ust_session->consumer; | |
77 | uid = session->ust_session->uid; | |
78 | gid = session->ust_session->gid; | |
79 | } else { | |
80 | assert(0); | |
81 | } | |
82 | ||
83 | if (!output || !output->socks) { | |
84 | ERR("No consumer output found for session \"%s\"", | |
85 | session->name); | |
86 | ret = -1; | |
87 | goto end; | |
88 | } | |
89 | ||
90 | rcu_read_lock(); | |
91 | /* | |
92 | * We have to iterate to find a socket, but we only need to send the | |
93 | * rename command to one consumer, so we break after the first one. | |
94 | */ | |
95 | cds_lfht_for_each_entry(output->socks->ht, &iter.iter, socket, node.node) { | |
96 | pthread_mutex_lock(socket->lock); | |
97 | ret = consumer_rotate_rename(socket, session->id, output, | |
98 | current_path, new_path, uid, gid); | |
99 | pthread_mutex_unlock(socket->lock); | |
100 | if (ret) { | |
101 | ret = -1; | |
102 | goto end_unlock; | |
103 | } | |
104 | break; | |
105 | } | |
106 | ||
107 | ret = 0; | |
108 | ||
109 | end_unlock: | |
110 | rcu_read_unlock(); | |
111 | end: | |
112 | return ret; | |
113 | } | |
114 | ||
115 | /* The session's lock must be held by the caller. */ | |
116 | static | |
117 | int rename_first_chunk(struct ltt_session *session, | |
118 | struct consumer_output *consumer, char *new_path) | |
119 | { | |
120 | int ret; | |
121 | char current_full_path[LTTNG_PATH_MAX], new_full_path[LTTNG_PATH_MAX]; | |
122 | ||
db66e574 | 123 | if (session->net_handle > 0) { |
b178f53e JG |
124 | /* |
125 | * Current domain path: | |
126 | * HOSTNAME/{SESSION-[TIMESTAMP], USER_DIRECTORY}/DOMAIN | |
127 | */ | |
128 | ret = snprintf(current_full_path, sizeof(current_full_path), | |
129 | "%s%s", | |
130 | consumer->dst.net.base_dir, | |
131 | consumer->domain_subdir); | |
db66e574 JD |
132 | if (ret < 0 || ret >= sizeof(current_full_path)) { |
133 | ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"", | |
134 | session->name); | |
b178f53e | 135 | ret = -LTTNG_ERR_UNK; |
db66e574 JD |
136 | goto error; |
137 | } | |
138 | } else { | |
b178f53e JG |
139 | /* |
140 | * Current domain path: | |
141 | * SESSION_OUTPUT_PATH/DOMAIN | |
142 | */ | |
143 | ret = snprintf(current_full_path, sizeof(current_full_path), | |
144 | "%s/%s", | |
145 | consumer->dst.session_root_path, | |
146 | consumer->domain_subdir); | |
db66e574 JD |
147 | if (ret < 0 || ret >= sizeof(current_full_path)) { |
148 | ERR("Failed to initialize current full path while renaming first rotation chunk of session \"%s\"", | |
149 | session->name); | |
b178f53e | 150 | ret = -LTTNG_ERR_UNK; |
db66e574 JD |
151 | goto error; |
152 | } | |
153 | } | |
b178f53e JG |
154 | /* |
155 | * New domain path: | |
156 | * SESSION_BASE_PATH/<START_TS>_<END_TS>-INDEX/DOMAIN | |
157 | */ | |
db66e574 | 158 | ret = snprintf(new_full_path, sizeof(new_full_path), "%s/%s", |
b178f53e | 159 | new_path, consumer->domain_subdir); |
db66e574 JD |
160 | if (ret < 0 || ret >= sizeof(new_full_path)) { |
161 | ERR("Failed to initialize new full path while renaming first rotation chunk of session \"%s\"", | |
162 | session->name); | |
b178f53e | 163 | ret = -LTTNG_ERR_UNK; |
db66e574 JD |
164 | goto error; |
165 | } | |
b178f53e | 166 | /* Move the per-domain inside the first rotation chunk path. */ |
db66e574 JD |
167 | ret = session_rename_chunk(session, current_full_path, new_full_path); |
168 | if (ret < 0) { | |
169 | ret = -LTTNG_ERR_UNK; | |
170 | goto error; | |
171 | } | |
172 | ||
173 | ret = 0; | |
174 | ||
175 | error: | |
176 | return ret; | |
177 | } | |
178 | ||
179 | /* | |
180 | * Rename a chunk folder after a rotation is complete. | |
181 | * session_lock_list and session lock must be held. | |
182 | * | |
183 | * Returns 0 on success, a negative value on error. | |
184 | */ | |
b178f53e | 185 | int rename_completed_chunk(struct ltt_session *session, time_t end_ts) |
db66e574 | 186 | { |
db66e574 JD |
187 | int ret; |
188 | size_t strf_ret; | |
b178f53e JG |
189 | struct tm *timeinfo; |
190 | char new_path[LTTNG_PATH_MAX]; | |
191 | char start_datetime[21], end_datetime[21]; | |
db66e574 JD |
192 | |
193 | DBG("Renaming completed chunk for session %s", session->name); | |
b178f53e JG |
194 | |
195 | /* Format chunk start time. */ | |
196 | timeinfo = localtime(&session->last_chunk_start_ts); | |
db66e574 | 197 | if (!timeinfo) { |
b178f53e | 198 | ERR("Failed to separate local time while renaming completed chunk"); |
db66e574 JD |
199 | ret = -1; |
200 | goto end; | |
201 | } | |
b178f53e JG |
202 | strf_ret = strftime(start_datetime, sizeof(start_datetime), |
203 | "%Y%m%dT%H%M%S%z", timeinfo); | |
db66e574 JD |
204 | if (strf_ret == 0) { |
205 | ERR("Failed to format timestamp while renaming completed session chunk"); | |
206 | ret = -1; | |
207 | goto end; | |
208 | } | |
209 | ||
b178f53e JG |
210 | /* Format chunk end time. */ |
211 | timeinfo = localtime(&end_ts); | |
212 | if (!timeinfo) { | |
213 | ERR("Failed to parse time while renaming completed chunk"); | |
214 | ret = -1; | |
215 | goto end; | |
216 | } | |
217 | strf_ret = strftime(end_datetime, sizeof(end_datetime), | |
218 | "%Y%m%dT%H%M%S%z", timeinfo); | |
219 | if (strf_ret == 0) { | |
220 | ERR("Failed to format timestamp while renaming completed session chunk"); | |
221 | ret = -1; | |
222 | goto end; | |
223 | } | |
db66e574 | 224 | |
b178f53e JG |
225 | /* Format completed chunk's path. */ |
226 | ret = snprintf(new_path, sizeof(new_path), "%s/archives/%s-%s-%" PRIu64, | |
227 | session_get_base_path(session), | |
228 | start_datetime, end_datetime, | |
229 | session->current_archive_id); | |
230 | if (ret < 0 || ret >= sizeof(new_path)) { | |
231 | ERR("Failed to format new chunk path while renaming chunk of session \"%s\"", | |
232 | session->name); | |
233 | ret = -1; | |
234 | goto error; | |
235 | } | |
db66e574 | 236 | |
b178f53e | 237 | if (session->current_archive_id == 1) { |
db66e574 JD |
238 | /* |
239 | * On the first rotation, the current_rotate_path is the | |
240 | * session_root_path, so we need to create the chunk folder | |
241 | * and move the domain-specific folders inside it. | |
242 | */ | |
db66e574 JD |
243 | if (session->kernel_session) { |
244 | ret = rename_first_chunk(session, | |
245 | session->kernel_session->consumer, | |
246 | new_path); | |
247 | if (ret) { | |
b178f53e | 248 | ERR("Failed to rename kernel session trace folder to \"%s\"", new_path); |
db66e574 JD |
249 | /* |
250 | * This is not a fatal error for the rotation | |
251 | * thread, we just need to inform the client | |
252 | * that a problem occurred with the rotation. | |
253 | * Returning 0, same for the other errors | |
254 | * below. | |
255 | */ | |
256 | ret = 0; | |
257 | goto error; | |
258 | } | |
259 | } | |
260 | if (session->ust_session) { | |
261 | ret = rename_first_chunk(session, | |
262 | session->ust_session->consumer, | |
263 | new_path); | |
264 | if (ret) { | |
b178f53e | 265 | ERR("Failed to rename userspace session trace folder to \"%s\"", new_path); |
db66e574 JD |
266 | ret = 0; |
267 | goto error; | |
268 | } | |
269 | } | |
270 | } else { | |
271 | /* | |
272 | * After the first rotation, all the trace data is already in | |
273 | * its own chunk folder, we just need to append the suffix. | |
274 | */ | |
db66e574 JD |
275 | ret = session_rename_chunk(session, |
276 | session->rotation_chunk.current_rotate_path, | |
277 | new_path); | |
278 | if (ret) { | |
b178f53e | 279 | ERR("Failed to rename session trace folder from \"%s\" to \"%s\"", |
db66e574 JD |
280 | session->rotation_chunk.current_rotate_path, |
281 | new_path); | |
282 | ret = 0; | |
283 | goto error; | |
284 | } | |
285 | } | |
286 | ||
287 | /* | |
288 | * Store the path where the readable chunk is. This path is valid | |
289 | * and can be queried by the client with rotate_pending until the next | |
290 | * rotation is started. | |
291 | */ | |
292 | ret = lttng_strncpy(session->rotation_chunk.current_rotate_path, | |
293 | new_path, | |
294 | sizeof(session->rotation_chunk.current_rotate_path)); | |
295 | if (ret) { | |
296 | ERR("Failed the current chunk's path of session \"%s\"", | |
297 | session->name); | |
298 | ret = -1; | |
299 | goto error; | |
300 | } | |
301 | ||
302 | goto end; | |
303 | ||
304 | error: | |
d68c9a04 | 305 | session->rotation_state = LTTNG_ROTATION_STATE_ERROR; |
db66e574 JD |
306 | end: |
307 | return ret; | |
308 | } | |
d88744a4 | 309 | |
9402c5b9 JG |
310 | int rename_active_chunk(struct ltt_session *session) |
311 | { | |
312 | int ret; | |
313 | ||
314 | session->current_archive_id++; | |
315 | ||
316 | /* | |
317 | * The currently active tracing path is now the folder we | |
318 | * want to rename. | |
319 | */ | |
320 | ret = lttng_strncpy(session->rotation_chunk.current_rotate_path, | |
321 | session->rotation_chunk.active_tracing_path, | |
322 | sizeof(session->rotation_chunk.current_rotate_path)); | |
323 | if (ret) { | |
324 | ERR("Failed to copy active tracing path"); | |
325 | goto end; | |
326 | } | |
327 | ||
328 | ret = rename_completed_chunk(session, time(NULL)); | |
329 | if (ret < 0) { | |
330 | ERR("Failed to rename current rotation's path"); | |
331 | goto end; | |
332 | } | |
333 | ||
334 | /* | |
335 | * We just renamed, the folder, we didn't do an actual rotation, so | |
336 | * the active tracing path is now the renamed folder and we have to | |
337 | * restore the rotate count. | |
338 | */ | |
339 | ret = lttng_strncpy(session->rotation_chunk.active_tracing_path, | |
340 | session->rotation_chunk.current_rotate_path, | |
341 | sizeof(session->rotation_chunk.active_tracing_path)); | |
342 | if (ret) { | |
343 | ERR("Failed to rename active session chunk tracing path"); | |
344 | goto end; | |
345 | } | |
346 | end: | |
347 | session->current_archive_id--; | |
348 | return ret; | |
349 | } | |
350 | ||
90936dcf JD |
351 | int subscribe_session_consumed_size_rotation(struct ltt_session *session, uint64_t size, |
352 | struct notification_thread_handle *notification_thread_handle) | |
353 | { | |
354 | int ret; | |
355 | enum lttng_condition_status condition_status; | |
356 | enum lttng_notification_channel_status nc_status; | |
357 | struct lttng_action *action; | |
358 | ||
359 | session->rotate_condition = lttng_condition_session_consumed_size_create(); | |
360 | if (!session->rotate_condition) { | |
361 | ERR("Failed to create session consumed size condition object"); | |
362 | ret = -1; | |
363 | goto end; | |
364 | } | |
365 | ||
366 | condition_status = lttng_condition_session_consumed_size_set_threshold( | |
367 | session->rotate_condition, size); | |
368 | if (condition_status != LTTNG_CONDITION_STATUS_OK) { | |
369 | ERR("Could not set session consumed size condition threshold (size = %" PRIu64 ")", | |
370 | size); | |
371 | ret = -1; | |
372 | goto end; | |
373 | } | |
374 | ||
375 | condition_status = | |
376 | lttng_condition_session_consumed_size_set_session_name( | |
377 | session->rotate_condition, session->name); | |
378 | if (condition_status != LTTNG_CONDITION_STATUS_OK) { | |
379 | ERR("Could not set session consumed size condition session name (name = %s)", | |
380 | session->name); | |
381 | ret = -1; | |
382 | goto end; | |
383 | } | |
384 | ||
385 | action = lttng_action_notify_create(); | |
386 | if (!action) { | |
387 | ERR("Could not create notify action"); | |
388 | ret = -1; | |
389 | goto end; | |
390 | } | |
391 | ||
392 | session->rotate_trigger = lttng_trigger_create(session->rotate_condition, | |
393 | action); | |
394 | if (!session->rotate_trigger) { | |
395 | ERR("Could not create size-based rotation trigger"); | |
396 | ret = -1; | |
397 | goto end; | |
398 | } | |
399 | ||
400 | nc_status = lttng_notification_channel_subscribe( | |
401 | rotate_notification_channel, session->rotate_condition); | |
402 | if (nc_status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { | |
403 | ERR("Could not subscribe to session consumed size notification"); | |
404 | ret = -1; | |
405 | goto end; | |
406 | } | |
407 | ||
408 | ret = notification_thread_command_register_trigger( | |
409 | notification_thread_handle, session->rotate_trigger); | |
410 | if (ret < 0 && ret != -LTTNG_ERR_TRIGGER_EXISTS) { | |
411 | ERR("Register trigger, %s", lttng_strerror(ret)); | |
412 | ret = -1; | |
413 | goto end; | |
414 | } | |
415 | ||
416 | ret = 0; | |
417 | ||
418 | end: | |
419 | return ret; | |
420 | } | |
421 | ||
422 | int unsubscribe_session_consumed_size_rotation(struct ltt_session *session, | |
423 | struct notification_thread_handle *notification_thread_handle) | |
424 | { | |
425 | int ret = 0; | |
426 | enum lttng_notification_channel_status status; | |
427 | ||
428 | status = lttng_notification_channel_unsubscribe( | |
429 | rotate_notification_channel, | |
430 | session->rotate_condition); | |
431 | if (status != LTTNG_NOTIFICATION_CHANNEL_STATUS_OK) { | |
432 | ERR("Session unsubscribe error: %d", (int) status); | |
433 | ret = -1; | |
434 | goto end; | |
435 | } | |
436 | ||
437 | ret = notification_thread_command_unregister_trigger( | |
438 | notification_thread_handle, session->rotate_trigger); | |
439 | if (ret != LTTNG_OK) { | |
440 | ERR("Session unregister trigger error: %d", ret); | |
441 | goto end; | |
442 | } | |
443 | ||
444 | ret = 0; | |
445 | end: | |
446 | return ret; | |
447 | } |