2 * Copyright (C) 2017 - Julien Desfossez <jdesfossez@efficios.com>
3 * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
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.
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
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.
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>
36 #include <lttng/notification/channel-internal.h>
37 #include <lttng/rotate-internal.h>
41 #include "rotation-thread.h"
42 #include "lttng-sessiond.h"
43 #include "health-sessiond.h"
46 #include "notification-thread-commands.h"
49 #include <urcu/list.h>
50 #include <urcu/rculfhash.h>
52 /* The session's lock must be held by the caller. */
54 int session_rename_chunk(struct ltt_session
*session
, char *current_path
,
58 struct consumer_socket
*socket
;
59 struct consumer_output
*output
;
60 struct lttng_ht_iter iter
;
64 DBG("Renaming session chunk path of session \"%s\" from %s to %s",
65 session
->name
, current_path
, new_path
);
68 * Either one of the sessions is enough to find the consumer_output
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
;
83 if (!output
|| !output
->socks
) {
84 ERR("No consumer output found for session \"%s\"",
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.
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
);
115 /* The session's lock must be held by the caller. */
117 int rename_first_chunk(struct ltt_session
*session
,
118 struct consumer_output
*consumer
, char *new_path
)
121 char current_full_path
[LTTNG_PATH_MAX
], new_full_path
[LTTNG_PATH_MAX
];
123 if (session
->net_handle
> 0) {
125 * Current domain path:
126 * HOSTNAME/{SESSION-[TIMESTAMP], USER_DIRECTORY}/DOMAIN
128 ret
= snprintf(current_full_path
, sizeof(current_full_path
),
130 consumer
->dst
.net
.base_dir
,
131 consumer
->domain_subdir
);
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\"",
135 ret
= -LTTNG_ERR_UNK
;
140 * Current domain path:
141 * SESSION_OUTPUT_PATH/DOMAIN
143 ret
= snprintf(current_full_path
, sizeof(current_full_path
),
145 consumer
->dst
.session_root_path
,
146 consumer
->domain_subdir
);
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\"",
150 ret
= -LTTNG_ERR_UNK
;
156 * SESSION_BASE_PATH/<START_TS>_<END_TS>-INDEX/DOMAIN
158 ret
= snprintf(new_full_path
, sizeof(new_full_path
), "%s/%s",
159 new_path
, consumer
->domain_subdir
);
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\"",
163 ret
= -LTTNG_ERR_UNK
;
166 /* Move the per-domain inside the first rotation chunk path. */
167 ret
= session_rename_chunk(session
, current_full_path
, new_full_path
);
169 ret
= -LTTNG_ERR_UNK
;
180 * Rename a chunk folder after a rotation is complete.
181 * session_lock_list and session lock must be held.
183 * Returns 0 on success, a negative value on error.
185 int rename_completed_chunk(struct ltt_session
*session
, time_t end_ts
)
190 char new_path
[LTTNG_PATH_MAX
];
191 char start_datetime
[21], end_datetime
[21];
193 DBG("Renaming completed chunk for session %s", session
->name
);
195 /* Format chunk start time. */
196 timeinfo
= localtime(&session
->last_chunk_start_ts
);
198 ERR("Failed to separate local time while renaming completed chunk");
202 strf_ret
= strftime(start_datetime
, sizeof(start_datetime
),
203 "%Y%m%dT%H%M%S%z", timeinfo
);
205 ERR("Failed to format timestamp while renaming completed session chunk");
210 /* Format chunk end time. */
211 timeinfo
= localtime(&end_ts
);
213 ERR("Failed to parse time while renaming completed chunk");
217 strf_ret
= strftime(end_datetime
, sizeof(end_datetime
),
218 "%Y%m%dT%H%M%S%z", timeinfo
);
220 ERR("Failed to format timestamp while renaming completed session chunk");
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\"",
237 if (session
->current_archive_id
== 1) {
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.
243 if (session
->kernel_session
) {
244 ret
= rename_first_chunk(session
,
245 session
->kernel_session
->consumer
,
248 ERR("Failed to rename kernel session trace folder to \"%s\"", new_path
);
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
260 if (session
->ust_session
) {
261 ret
= rename_first_chunk(session
,
262 session
->ust_session
->consumer
,
265 ERR("Failed to rename userspace session trace folder to \"%s\"", new_path
);
272 * After the first rotation, all the trace data is already in
273 * its own chunk folder, we just need to append the suffix.
275 ret
= session_rename_chunk(session
,
276 session
->rotation_chunk
.current_rotate_path
,
279 ERR("Failed to rename session trace folder from \"%s\" to \"%s\"",
280 session
->rotation_chunk
.current_rotate_path
,
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.
292 ret
= lttng_strncpy(session
->rotation_chunk
.current_rotate_path
,
294 sizeof(session
->rotation_chunk
.current_rotate_path
));
296 ERR("Failed the current chunk's path of session \"%s\"",
305 session
->rotation_state
= LTTNG_ROTATION_STATE_ERROR
;
310 int rename_active_chunk(struct ltt_session
*session
)
314 session
->current_archive_id
++;
317 * The currently active tracing path is now the folder we
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
));
324 ERR("Failed to copy active tracing path");
328 ret
= rename_completed_chunk(session
, time(NULL
));
330 ERR("Failed to rename current rotation's path");
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.
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
));
343 ERR("Failed to rename active session chunk tracing path");
347 session
->current_archive_id
--;
351 int subscribe_session_consumed_size_rotation(struct ltt_session
*session
, uint64_t size
,
352 struct notification_thread_handle
*notification_thread_handle
)
355 enum lttng_condition_status condition_status
;
356 enum lttng_notification_channel_status nc_status
;
357 struct lttng_action
*action
;
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");
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
")",
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)",
385 action
= lttng_action_notify_create();
387 ERR("Could not create notify action");
392 session
->rotate_trigger
= lttng_trigger_create(session
->rotate_condition
,
394 if (!session
->rotate_trigger
) {
395 ERR("Could not create size-based rotation trigger");
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");
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
));
422 int unsubscribe_session_consumed_size_rotation(struct ltt_session
*session
,
423 struct notification_thread_handle
*notification_thread_handle
)
426 enum lttng_notification_channel_status status
;
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
);
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
);