From: Julien Desfossez Date: Mon, 18 Dec 2017 21:51:41 +0000 (-0500) Subject: lttng rotate command X-Git-Tag: v2.11.0-rc1~342 X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=d68c9a04537b683991a7355b812b0af954008cf1 lttng rotate command The command line and API interface to the lttng rotate command. Signed-off-by: Julien Desfossez Signed-off-by: Jérémie Galarneau --- diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index c07bd57b2..57f9de05b 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -155,6 +155,7 @@ enum lttng_error_code { LTTNG_ERR_ROTATION_SIZE_IS_SET = 132, /* Rotate size already setup for this session. */ LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP = 133, /* Already rotated once after a stop. */ LTTNG_ERR_ROTATION_WRONG_VERSION = 134, /* Rotate not supported by this kernel tracer version */ + LTTNG_ERR_NO_SESSION_OUTPUT = 135, /* Session has no output configured. */ /* MUST be last element */ LTTNG_ERR_NR, /* Last element */ diff --git a/include/lttng/rotate-internal.h b/include/lttng/rotate-internal.h index 908422e19..265148376 100644 --- a/include/lttng/rotate-internal.h +++ b/include/lttng/rotate-internal.h @@ -20,20 +20,67 @@ #include #include +#include #include #include #include +/* + * Object used as input parameter to the rotate session API for immediate + * rotations. + * This is opaque to the public library. + */ +struct lttng_rotation_immediate_attr { + /* Session name to rotate. */ + char session_name[LTTNG_NAME_MAX]; + /* For the rotate pending request. */ + uint64_t rotate_id; +}; + +/* + * Object returned by the rotate session API. + * This is opaque to the public library. + */ +struct lttng_rotation_handle { + char session_name[LTTNG_NAME_MAX]; + /* + * ID of the rotate command. + * This matches the session->rotate_count, so the handle is valid until + * the next rotate command. After that, the rotation_get_state command + * returns the "expired" state. + */ + uint64_t rotation_id; + /* + * Where the rotated (readable) trace has been stored when the + * rotation is completed. + */ + struct { + bool is_set; + char path[LTTNG_PATH_MAX]; + } archive_location; +}; + /* * Internal objects between lttng-ctl and the session daemon, the values - * are then copied to the user's lttng_rotate_session_handle object. + * are then copied to the user's lttng_rotation_handle object. */ + /* For the LTTNG_ROTATE_SESSION command. */ struct lttng_rotate_session_return { - uint64_t rotate_id; - /* Represents values defined in enum lttng_rotation_status. */ + uint64_t rotation_id; +} LTTNG_PACKED; + +/* For the LTTNG_ROTATION_GET_INFO command. */ +struct lttng_rotation_get_info_return { + /* Represents values defined in enum lttng_rotation_state. */ int32_t status; + char path[LTTNG_PATH_MAX]; +} LTTNG_PACKED; + +/* For the LTTNG_SESSION_GET_CURRENT_OUTPUT command. */ +struct lttng_session_get_current_output_return { + char path[LTTNG_PATH_MAX]; } LTTNG_PACKED; #endif /* LTTNG_ROTATE_INTERNAL_ABI_H */ diff --git a/include/lttng/rotation.h b/include/lttng/rotation.h index 47ca25715..05216fc37 100644 --- a/include/lttng/rotation.h +++ b/include/lttng/rotation.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 - Julien Desfossez + * Copyright (C) 2018 - Jérémie Galarneau * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License, version 2.1 only, @@ -25,32 +26,130 @@ extern "C" { #endif /* - * Return codes for lttng_rotate_session_get_output_path. + * Return codes for lttng_rotation_handle_get_state() */ -enum lttng_rotation_status { - /* - * After starting a rotation. - */ - LTTNG_ROTATION_STATUS_STARTED = 0, +enum lttng_rotation_state { /* - * When the rotation is complete. + * Rotation is ongoing, but has not been completed yet. */ - LTTNG_ROTATION_STATUS_COMPLETED = 1, + LTTNG_ROTATION_STATE_ONGOING = 0, /* - * If the handle does not match the last rotate command, we cannot - * retrieve the path for the chunk. + * Rotation has been completed and the resulting chunk + * can now safely be read. */ - LTTNG_ROTATION_STATUS_EXPIRED = 2, + LTTNG_ROTATION_STATE_COMPLETED = 1, /* - * On error. + * The rotation has expired. + * + * The information associated with a given rotation is eventually + * purged by the session daemon. In such a case, the attributes of + * the rotation, such as its path, may no longer be available. + * + * Note that this state does not guarantee the the rotation was + * completed successfully. */ - LTTNG_ROTATION_STATUS_ERROR = 3, + LTTNG_ROTATION_STATE_EXPIRED = 2, /* - * If no rotation occured during this session. + * The rotation could not be completed due to an error. */ - LTTNG_ROTATION_STATUS_NO_ROTATION = 4, + LTTNG_ROTATION_STATE_ERROR = 3, +}; + +enum lttng_rotation_status { + LTTNG_ROTATION_STATUS_OK = 0, + /* Information not available. */ + LTTNG_ROTATION_STATUS_UNAVAILABLE = 1, + /* Generic error. */ + LTTNG_ROTATION_STATUS_ERROR = -1, + /* Invalid parameters provided. */ + LTTNG_ROTATION_STATUS_INVALID = -2, }; +/* + * Input parameter to the lttng_rotate_session command. + * + * An immediate rotation is performed as soon as possible by the tracers. + * + * The lttng_rotation_immediate_attr object is opaque to the user. Use the + * helper functions below to access it. + */ +struct lttng_rotation_immediate_attr; + +/* + * Handle used to represent a specific rotation. + * + * This object is opaque to the user. Use the helper functions below to access + * it. + */ +struct lttng_rotation_handle; + +/* + * Return a newly allocated immediate session rotation descriptor object or NULL + * on error. + */ +extern struct lttng_rotation_immediate_attr * +lttng_rotation_immediate_attr_create(void); + +/* + * Destroy a given immediate session rotation descriptor object. + */ +extern void lttng_rotation_immediate_attr_destroy( + struct lttng_rotation_immediate_attr *attr); + +/* + * Set the name of the session to rotate immediately. + * + * The session_name parameter is copied to the immediate session rotation + * attributes. + */ +extern enum lttng_rotation_status lttng_rotation_immediate_attr_set_session_name( + struct lttng_rotation_immediate_attr *attr, + const char *session_name); + +/* + * Get the current state of the rotation referenced by the handle. + * + * This will issue a request to the session daemon on every call. Hence, + * the result of this call may change over time. + */ +extern enum lttng_rotation_status lttng_rotation_handle_get_state( + struct lttng_rotation_handle *rotation_handle, + enum lttng_rotation_state *rotation_state); + +/* + * Get the location of the rotation's resulting archive. + * + * The rotation must be completed in order for this call to succeed. + * The path returned is owned by the rotation handle. + * + * Note that path will not be set in case of error, or if the session + * rotation has expired. + * + * FIXME: Return an lttng_location object instead of a path. + */ +extern enum lttng_rotation_status lttng_rotation_handle_get_completed_archive_location( + struct lttng_rotation_handle *rotation_handle, + const char **path); + +/* + * Destroy an lttng_rotate_session handle. + */ +extern void lttng_rotation_handle_destroy( + struct lttng_rotation_handle *rotation_handle); + +/* + * Rotate the output folder of the session + * + * On success, handle is allocated and can be used to monitor the progress + * of the rotation with lttng_rotation_get_state(). The handle must be freed + * by the caller with lttng_rotation_handle_destroy(). + * + * Return 0 if the rotate action was successfully launched or a negative + * LTTng error code on error. + */ +extern int lttng_rotate_session(struct lttng_rotation_immediate_attr *attr, + struct lttng_rotation_handle **rotation_handle); + #ifdef __cplusplus } #endif diff --git a/include/lttng/session.h b/include/lttng/session.h index 599892d13..7211eeec1 100644 --- a/include/lttng/session.h +++ b/include/lttng/session.h @@ -162,6 +162,23 @@ extern int lttng_untrack_pid(struct lttng_handle *handle, int pid); extern int lttng_list_tracker_pids(struct lttng_handle *handle, int *enabled, int32_t **pids, size_t *nr_pids); +/* + * Ask the session daemon where the data for this session is currently being + * written to. If rotations occured during a session, this call is useful to + * know the location of the last chunk. + * + * Return 0 and allocate chunk_path if rotations occured for this session, the + * caller needs to free chunk_path. + * Return 1 if no rotation occured during the session, chunk_path is left + * unallocated. + * + * Return a negative LTTng error code on error (readable with lttng_strerror). + * + * FIXME: Return an lttng_location object rather than a path. + */ +extern int lttng_session_get_current_archive_location(const char *session_name, + char **chunk_path); + #ifdef __cplusplus } #endif diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index a2af4c38a..5377ad8ee 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -4367,13 +4367,13 @@ int cmd_rotate_session(struct ltt_session *session, if (!session->has_been_started) { ret = -LTTNG_ERR_START_SESSION_ONCE; - goto error; + goto end; } if (session->live_timer || session->snapshot_mode || !session->output_traces) { ret = -LTTNG_ERR_ROTATION_NOT_AVAILABLE; - goto error; + goto end; } /* @@ -4383,13 +4383,13 @@ int cmd_rotate_session(struct ltt_session *session, (session->consumer->relay_major_version == 2 && session->consumer->relay_minor_version < 11)) { ret = -LTTNG_ERR_ROTATION_NOT_AVAILABLE; - goto error; + goto end; } if (session->rotate_pending || session->rotate_pending_relay) { ret = -LTTNG_ERR_ROTATION_PENDING; DBG("Rotate already in progress"); - goto error; + goto end; } /* @@ -4400,7 +4400,7 @@ int cmd_rotate_session(struct ltt_session *session, DBG("Session \"%s\" was already rotated after stop, refusing rotation", session->name); ret = -LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP; - goto error; + goto end; } /* Special case for the first rotation. */ @@ -4422,7 +4422,7 @@ int cmd_rotate_session(struct ltt_session *session, if (ret) { ERR("Failed to copy session base path to current rotation chunk path"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } } else { /* @@ -4435,14 +4435,14 @@ int cmd_rotate_session(struct ltt_session *session, if (ret) { ERR("Failed to copy the active tracing path to the current rotate path"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } } DBG("Current rotate path %s", session->rotation_chunk.current_rotate_path); session->rotate_count++; session->rotate_pending = true; - session->rotation_status = LTTNG_ROTATION_STATUS_STARTED; + session->rotation_state = LTTNG_ROTATION_STATE_ONGOING; /* * Create the path name for the next chunk. @@ -4450,7 +4450,7 @@ int cmd_rotate_session(struct ltt_session *session, now = time(NULL); if (now == (time_t) -1) { ret = -LTTNG_ERR_ROTATION_NOT_AVAILABLE; - goto error; + goto end; } session->last_chunk_start_ts = session->current_chunk_start_ts; session->current_chunk_start_ts = now; @@ -4459,14 +4459,14 @@ int cmd_rotate_session(struct ltt_session *session, if (!timeinfo) { PERROR("Failed to sample local time in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } strf_ret = strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); if (!strf_ret) { ERR("Failed to format local time timestamp in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } if (session->kernel_session) { /* @@ -4481,7 +4481,7 @@ int cmd_rotate_session(struct ltt_session *session, if (ret < 0 || ret == sizeof(session->rotation_chunk.active_tracing_path)) { ERR("Failed to format active kernel tracing path in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } /* * The sub-directory for the consumer @@ -4494,7 +4494,7 @@ int cmd_rotate_session(struct ltt_session *session, if (ret < 0 || ret == sizeof(session->kernel_session->consumer->chunk_path)) { ERR("Failed to format the kernel consumer's sub-directory in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } /* * Create the new chunk folder, before the rotation begins so we don't @@ -4505,12 +4505,14 @@ int cmd_rotate_session(struct ltt_session *session, session->kernel_session->gid); if (ret) { ERR("Failed to create kernel session tracing path at %s", - session->kernel_session->chunk_path); - goto error; + session->kernel_session->consumer->chunk_path); + ret = -LTTNG_ERR_CREATE_DIR_FAIL; + goto end; } ret = kernel_rotate_session(session); if (ret != LTTNG_OK) { - goto error; + ret = -ret; + goto end; } } if (session->ust_session) { @@ -4521,7 +4523,7 @@ int cmd_rotate_session(struct ltt_session *session, if (ret < 0) { ERR("Failed to format active UST tracing path in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } ret = snprintf(session->ust_session->consumer->chunk_path, PATH_MAX, "/%s-%" PRIu64, datetime, @@ -4529,7 +4531,7 @@ int cmd_rotate_session(struct ltt_session *session, if (ret < 0) { ERR("Failed to format the UST consumer's sub-directory in rotate session command"); ret = -LTTNG_ERR_UNK; - goto error; + goto end; } /* * Create the new chunk folder, before the rotation begins so we don't @@ -4540,7 +4542,8 @@ int cmd_rotate_session(struct ltt_session *session, session->ust_session->gid); ret = ust_app_rotate_session(session, &ust_active); if (ret != LTTNG_OK) { - goto error; + ret = -LTTNG_ERR_CREATE_DIR_FAIL; + goto end; } /* * Handle the case where we did not start a rotation on any channel. @@ -4555,7 +4558,7 @@ int cmd_rotate_session(struct ltt_session *session, goto end; } session->rotate_pending = false; - session->rotation_status = LTTNG_ROTATION_STATUS_COMPLETED; + session->rotation_state = LTTNG_ROTATION_STATE_COMPLETED; } } @@ -4564,86 +4567,67 @@ int cmd_rotate_session(struct ltt_session *session, } if (rotate_return) { - (*rotate_return)->rotate_id = session->rotate_count; - (*rotate_return)->status = LTTNG_ROTATION_STATUS_STARTED; + rotate_return->rotation_id = session->rotate_count; } - DBG("Cmd rotate session %s, rotate_id %" PRIu64 " completed", session->name, session->rotate_count); ret = LTTNG_OK; - goto end; - -error: - if (rotate_return) { - (*rotate_return)->status = LTTNG_ROTATION_STATUS_ERROR; - } end: return ret; } /* - * Command LTTNG_ROTATE_PENDING from the lttng-ctl library. + * Command LTTNG_ROTATION_GET_INFO from the lttng-ctl library. * * Check if the session has finished its rotation. * * Return 0 on success or else a LTTNG_ERR code. */ -int cmd_rotate_pending(struct ltt_session *session, - struct lttng_rotate_pending_return **pending_return, - uint64_t rotate_id) +int cmd_rotate_get_info(struct ltt_session *session, + struct lttng_rotation_get_info_return *info_return, + uint64_t rotation_id) { int ret; assert(session); - DBG("Cmd rotate pending session %s, rotate_id %" PRIu64, session->name, + DBG("Cmd rotate_get_info session %s, rotation id %" PRIu64, session->name, session->rotate_count); - *pending_return = zmalloc(sizeof(struct lttng_rotate_pending_return)); - if (!*pending_return) { - ret = -ENOMEM; - goto end; - } - - if (session->rotate_count != rotate_id) { - (*pending_return)->status = LTTNG_ROTATION_STATUS_EXPIRED; + if (session->rotate_count != rotation_id) { + info_return->status = (int32_t) LTTNG_ROTATION_STATE_EXPIRED; ret = LTTNG_OK; goto end; } - if (session->rotation_status == LTTNG_ROTATION_STATUS_ERROR) { - DBG("An error occurred during rotation"); - (*pending_return)->status = LTTNG_ROTATION_STATUS_ERROR; - /* Rotate with a relay */ - } else if (session->rotate_pending_relay) { - DBG("Session %s, rotate_id %" PRIu64 " still pending", - session->name, session->rotate_count); - (*pending_return)->status = LTTNG_ROTATION_STATUS_STARTED; - } else if (session->rotate_pending) { - DBG("Session %s, rotate_id %" PRIu64 " still pending", - session->name, session->rotate_count); - (*pending_return)->status = LTTNG_ROTATION_STATUS_STARTED; - } else { - DBG("Session %s, rotate_id %" PRIu64 " finished", - session->name, session->rotate_count); - (*pending_return)->status = LTTNG_ROTATION_STATUS_COMPLETED; - ret = lttng_strncpy((*pending_return)->output_path, + switch (session->rotation_state) { + case LTTNG_ROTATION_STATE_ONGOING: + DBG("Reporting that rotation id %" PRIu64 " of session %s is still pending", + rotation_id, session->name); + break; + case LTTNG_ROTATION_STATE_COMPLETED: + ret = lttng_strncpy(info_return->path, session->rotation_chunk.current_rotate_path, - sizeof((*pending_return)->output_path)); + sizeof(info_return->path)); if (ret) { - ERR("Failed to copy active tracing path to rotate pending command reply"); - (*pending_return)->status = LTTNG_ROTATION_STATUS_ERROR; - ret = -1; + ERR("Failed to copy active tracing path to rotate_get_info reply"); + info_return->status = LTTNG_ROTATION_STATUS_ERROR; + ret = -LTTNG_ERR_UNK; goto end; } + break; + case LTTNG_ROTATION_STATE_ERROR: + DBG("Reporting that an error occurred during rotation %" PRIu64 " of session %s", + rotation_id, session->name); + break; + default: + abort(); } + info_return->status = (int32_t) session->rotation_state; ret = LTTNG_OK; - - goto end; - end: return ret; } @@ -4656,33 +4640,46 @@ end: * * Return LTTNG_OK on success or else a LTTNG_ERR code. */ -int cmd_rotate_get_current_path(struct ltt_session *session, - struct lttng_rotate_get_current_path **get_return) +int cmd_session_get_current_output(struct ltt_session *session, + struct lttng_session_get_current_output_return *output_return) { int ret; + const char *path; - *get_return = zmalloc(sizeof(struct lttng_rotate_get_current_path)); - if (!*get_return) { - ret = -ENOMEM; - goto end; + if (!session->snapshot_mode) { + if (session->rotate_count == 0) { + if (session->kernel_session) { + path = session_get_base_path(session); + } else if (session->ust_session) { + path = session_get_base_path(session); + } else { + abort(); + } + assert(path); + } else { + path = session->rotation_chunk.active_tracing_path; + } + } else { + /* + * A snapshot session does not have a "current" trace archive + * location. + */ + path = ""; } - if (session->rotate_count == 0) { - (*get_return)->status = LTTNG_ROTATION_STATUS_NO_ROTATION; - } else { - (*get_return)->status = session->rotation_status; - ret = lttng_strncpy((*get_return)->output_path, - session->rotation_chunk.current_rotate_path, - sizeof((*get_return)->output_path)); - if (ret) { - ERR("Failed to copy trace output path to rotate get current path command reply"); - ret = -1; - goto end; - } + DBG("Cmd get current output for session %s, returning %s", + session->name, path); + + ret = lttng_strncpy(output_return->path, + path, + sizeof(output_return->path)); + if (ret) { + ERR("Failed to copy trace output path to session get current output command reply"); + ret = -LTTNG_ERR_UNK; + goto end; } ret = LTTNG_OK; - end: return ret; } diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 685c2f3b0..49e9992cc 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -120,5 +120,10 @@ int cmd_unregister_trigger(struct command_ctx *cmd_ctx, int sock, int cmd_rotate_session(struct ltt_session *session, struct lttng_rotate_session_return *rotate_return); +int cmd_rotate_get_info(struct ltt_session *session, + struct lttng_rotation_get_info_return *info_return, + uint64_t rotate_id); +int cmd_session_get_current_output(struct ltt_session *session, + struct lttng_session_get_current_output_return *output_return); #endif /* CMD_H */ diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index f89bb1df2..5906cdca5 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -1752,7 +1752,7 @@ int consumer_rotate_pending_relay(struct consumer_socket *socket, assert(socket); - DBG("Consumer rotate pending on relay for session %" PRIu64 ", chunk id % " PRIu64, + DBG("Consumer rotate pending on relay for session %" PRIu64 ", chunk id %" PRIu64, session_id, chunk_id); assert(output->type == CONSUMER_DST_NET); diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 1d9ca91c5..226433ead 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -2964,8 +2964,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_REGISTER_TRIGGER: case LTTNG_UNREGISTER_TRIGGER: case LTTNG_ROTATE_SESSION: - case LTTNG_ROTATE_PENDING: - case LTTNG_ROTATE_GET_CURRENT_PATH: + case LTTNG_ROTATION_GET_INFO: + case LTTNG_SESSION_GET_CURRENT_OUTPUT: need_domain = 0; break; default: @@ -3009,7 +3009,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_LIST_TRACKER_PIDS: case LTTNG_DATA_PENDING: case LTTNG_ROTATE_SESSION: - case LTTNG_ROTATE_PENDING: + case LTTNG_ROTATION_GET_INFO: break; default: /* Setup lttng message with no payload */ @@ -4104,6 +4104,7 @@ error_add_context: DBG("Client rotate session \"%s\"", cmd_ctx->session->name); + memset(&rotate_return, 0, sizeof(rotate_return)); if (cmd_ctx->session->kernel_session && !check_rotate_compatible()) { DBG("Kernel tracer version is not compatible with the rotation feature"); ret = LTTNG_ERR_ROTATION_WRONG_VERSION; @@ -4126,20 +4127,20 @@ error_add_context: ret = LTTNG_OK; break; } - case LTTNG_ROTATE_PENDING: + case LTTNG_ROTATION_GET_INFO: { - struct lttng_rotate_pending_return *pending_return = NULL; + struct lttng_rotation_get_info_return get_info_return; - ret = cmd_rotate_pending(cmd_ctx->session, &pending_return, - cmd_ctx->lsm->u.rotate_pending.rotate_id); + memset(&get_info_return, 0, sizeof(get_info_return)); + ret = cmd_rotate_get_info(cmd_ctx->session, &get_info_return, + cmd_ctx->lsm->u.get_rotation_info.rotation_id); if (ret < 0) { ret = -ret; goto error; } - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, pending_return, - sizeof(struct lttng_rotate_session_handle)); - free(pending_return); + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &get_info_return, + sizeof(get_info_return)); if (ret < 0) { ret = -ret; goto error; @@ -4148,19 +4149,20 @@ error_add_context: ret = LTTNG_OK; break; } - case LTTNG_ROTATE_GET_CURRENT_PATH: + case LTTNG_SESSION_GET_CURRENT_OUTPUT: { - struct lttng_rotate_get_current_path *get_return = NULL; + struct lttng_session_get_current_output_return output_return; - ret = cmd_rotate_get_current_path(cmd_ctx->session, &get_return); + memset(&output_return, 0, sizeof(output_return)); + ret = cmd_session_get_current_output(cmd_ctx->session, + &output_return); if (ret < 0) { ret = -ret; goto error; } - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, get_return, - sizeof(struct lttng_rotate_get_current_path)); - free(get_return); + ret = setup_lttng_msg_no_cmd_header(cmd_ctx, &output_return, + sizeof(output_return)); if (ret < 0) { ret = -ret; goto error; diff --git a/src/bin/lttng-sessiond/rotate.c b/src/bin/lttng-sessiond/rotate.c index 3ca80ce6a..237723d3d 100644 --- a/src/bin/lttng-sessiond/rotate.c +++ b/src/bin/lttng-sessiond/rotate.c @@ -348,7 +348,7 @@ int rename_complete_chunk(struct ltt_session *session, time_t ts) goto end; error: - session->rotation_status = LTTNG_ROTATION_STATUS_ERROR; + session->rotation_state = LTTNG_ROTATION_STATE_ERROR; end: return ret; } diff --git a/src/bin/lttng-sessiond/rotation-thread.c b/src/bin/lttng-sessiond/rotation-thread.c index 21f20b403..9e599e489 100644 --- a/src/bin/lttng-sessiond/rotation-thread.c +++ b/src/bin/lttng-sessiond/rotation-thread.c @@ -356,7 +356,7 @@ int handle_channel_rotation_pipe(int fd, uint32_t revents, time_t now = time(NULL); if (now == (time_t) -1) { - session->rotation_status = LTTNG_ROTATION_STATUS_ERROR; + session->rotation_state = LTTNG_ROTATION_STATE_ERROR; ret = LTTNG_ERR_UNK; goto end_unlock_session; } @@ -367,7 +367,7 @@ int handle_channel_rotation_pipe(int fd, uint32_t revents, goto end_unlock_session; } session->rotate_pending = false; - session->rotation_status = LTTNG_ROTATION_STATUS_COMPLETED; + session->rotation_state = LTTNG_ROTATION_STATE_COMPLETED; session->last_chunk_start_ts = session->current_chunk_start_ts; if (session->rotate_pending_relay) { ret = sessiond_timer_rotate_pending_start( diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 2e22885e8..3c9bc1824 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -132,8 +132,8 @@ struct ltt_session { * True until the relay has finished the rotation of all the streams. */ bool rotate_pending_relay; - /* Current status of a rotation. */ - enum lttng_rotation_status rotation_status; + /* Current state of a rotation. */ + enum lttng_rotation_state rotation_state; /* * Number of channels waiting for a rotation. * When this number reaches 0, we can handle the rename of the chunk diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index 1a6977eb5..b56ba2be8 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -23,6 +23,7 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/metadata.c \ commands/regenerate.c \ commands/help.c \ + commands/rotate.c \ utils.c utils.h lttng.c lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index bda6ef937..32d54d224 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -83,6 +83,7 @@ DECL_COMMAND(track); DECL_COMMAND(untrack); DECL_COMMAND(metadata); DECL_COMMAND(regenerate); +DECL_COMMAND(rotate); extern int cmd_help(int argc, const char **argv, const struct cmd_struct commands[]); diff --git a/src/bin/lttng/commands/rotate.c b/src/bin/lttng/commands/rotate.c new file mode 100644 index 000000000..f8cba7992 --- /dev/null +++ b/src/bin/lttng/commands/rotate.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2017 - Julien Desfossez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../command.h" +#include + +static char *opt_session_name; +static int opt_no_wait; +static struct mi_writer *writer; + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"no-wait", 'n', POPT_ARG_VAL, &opt_no_wait, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static int mi_output_rotate(const char *status, const char *path, + const char *session_name) +{ + int ret; + + if (!lttng_opt_mi) { + ret = 0; + goto end; + } + + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotation); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_session_name, session_name); + if (ret) { + goto end; + } + + ret = mi_lttng_writer_write_element_string(writer, + mi_lttng_element_rotate_status, status); + if (ret) { + goto end; + } + if (path) { + ret = mi_lttng_writer_write_element_string(writer, + config_element_path, path); + if (ret) { + goto end; + } + } + /* Close rotation element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + +end: + return ret; +} + +static int rotate_tracing(char *session_name) +{ + int ret; + struct lttng_rotation_immediate_attr *attr = NULL; + struct lttng_rotation_handle *handle = NULL; + enum lttng_rotation_status rotation_status; + enum lttng_rotation_state rotation_state = LTTNG_ROTATION_STATE_ONGOING; + + DBG("Rotating the output files of session %s", session_name); + + attr = lttng_rotation_immediate_attr_create(); + if (!attr) { + goto error; + } + + ret = lttng_rotation_immediate_attr_set_session_name(attr, session_name); + if (ret < 0) { + ERR("Session name exceeds the maximal allowed length"); + goto error; + } + + ret = lttng_rotate_session(attr, &handle); + if (ret < 0) { + switch (-ret) { + case LTTNG_ERR_SESSION_NOT_STARTED: + WARN("Tracing session %s not started yet", session_name); + break; + default: + ERR("%s", lttng_strerror(ret)); + break; + } + goto error; + } + + if (!opt_no_wait) { + _MSG("Waiting for rotation to complete"); + ret = fflush(stdout); + if (ret) { + PERROR("fflush"); + goto error; + } + + do { + rotation_status = lttng_rotation_handle_get_state(handle, + &rotation_state); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to query the state of the rotation"); + goto error; + } + + /* + * Data sleep time before retrying (in usec). Don't + * sleep if the call returned value indicates + * availability. + */ + if (rotation_state == LTTNG_ROTATION_STATE_ONGOING) { + ret = usleep(DEFAULT_DATA_AVAILABILITY_WAIT_TIME); + if (ret) { + PERROR("usleep"); + goto error; + } + _MSG("."); + + ret = fflush(stdout); + if (ret) { + PERROR("fflush"); + goto error; + } + } + } while (rotation_state == LTTNG_ROTATION_STATE_ONGOING); + MSG(""); + } + + switch (rotation_state) { + case LTTNG_ROTATION_STATE_COMPLETED: + { + const char *path; + + rotation_status = lttng_rotation_handle_get_completed_archive_location( + handle, &path); + if (rotation_status != LTTNG_ROTATION_STATUS_OK) { + ERR("Failed to retrieve the rotation's completed chunk archive location"); + goto error; + } + MSG("Trace chunk archive for session %s is now readable at %s", + session_name, path); + ret = mi_output_rotate("completed", path, session_name); + if (ret) { + goto error; + } + ret = CMD_SUCCESS; + goto end; + } + case LTTNG_ROTATION_STATE_EXPIRED: + MSG("Session %s rotated, but handle expired", session_name); + ret = mi_output_rotate("expired", NULL, session_name); + if (ret) { + goto error; + } + ret = CMD_SUCCESS; + goto end; + default: + ERR("Unexpected rotation state received, aborting..."); + goto error; + } + +error: + ret = CMD_ERROR; +end: + lttng_rotation_handle_destroy(handle); + lttng_rotation_immediate_attr_destroy(attr); + return ret; +} + +/* + * cmd_rotate + * + * The 'rotate ' first level command + */ +int cmd_rotate(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; + int popt_ret; + static poptContext pc; + char *session_name = NULL; + bool free_session_name = false; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + popt_ret = poptReadDefaultConfig(pc, 0); + if (popt_ret) { + ret = CMD_ERROR; + ERR("poptReadDefaultConfig"); + goto end; + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + SHOW_HELP(); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + if (!opt_session_name) { + session_name = get_session_name(); + if (!session_name) { + goto end; + } + free_session_name = true; + } else { + session_name = opt_session_name; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open rotate command */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_rotate); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + goto end; + } + + /* Open rotations element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_rotations); + if (ret) { + goto end; + } + + } + + command_ret = rotate_tracing(session_name); + if (command_ret) { + success = 0; + } + + /* Mi closing */ + if (lttng_opt_mi) { + /* Close rotations element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + goto end; + } + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + +end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : -LTTNG_ERR_MI_IO_FAIL; + } + + /* Overwrite ret if an error occurred with start_tracing */ + ret = command_ret ? command_ret : ret; + poptFreeContext(pc); + if (free_session_name) { + free(session_name); + } + return ret; +} diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index bf2128ca3..c0f5a05ca 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -85,6 +85,7 @@ static struct cmd_struct commands[] = { { "load", cmd_load}, { "metadata", cmd_metadata}, { "regenerate", cmd_regenerate}, + { "rotate", cmd_rotate}, { "save", cmd_save}, { "set-session", cmd_set_session}, { "snapshot", cmd_snapshot}, diff --git a/src/common/error.c b/src/common/error.c index c859eeca5..1e161c9bd 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -196,6 +196,7 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_ROTATION_SIZE_IS_SET) ] = "Automatic rotation schedule with a size threshold condition already set for this session", [ ERROR_INDEX(LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP) ] = "Session was already rotated once since it became inactive", [ ERROR_INDEX(LTTNG_ERR_ROTATION_WRONG_VERSION) ] = "Rotation feature is not supported by this kernel tracer version", + [ ERROR_INDEX(LTTNG_ERR_NO_SESSION_OUTPUT) ] = "Session has no output", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/mi-lttng-3.0.xsd b/src/common/mi-lttng-3.0.xsd index ae00ee80e..f800758aa 100644 --- a/src/common/mi-lttng-3.0.xsd +++ b/src/common/mi-lttng-3.0.xsd @@ -580,6 +580,7 @@ THE SOFTWARE. + @@ -606,6 +607,7 @@ THE SOFTWARE. + diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c index f0244d9fd..21cf78693 100644 --- a/src/common/mi-lttng.c +++ b/src/common/mi-lttng.c @@ -74,6 +74,7 @@ const char * const mi_lttng_element_command_success = "success"; const char * const mi_lttng_element_command_track = "track"; const char * const mi_lttng_element_command_untrack = "untrack"; const char * const mi_lttng_element_command_version = "version"; +const char * const mi_lttng_element_command_rotate = "rotate"; /* Strings related to version command */ const char * const mi_lttng_element_version = "version"; @@ -179,6 +180,13 @@ const char * const mi_lttng_element_snapshots = "snapshots"; /* String related to track/untrack command */ const char * const mi_lttng_element_track_untrack_all_wildcard = "*"; +LTTNG_HIDDEN const char * const mi_lttng_element_session_name = "session_name"; + +/* String related to rotate command */ +LTTNG_HIDDEN const char * const mi_lttng_element_rotation = "rotation"; +LTTNG_HIDDEN const char * const mi_lttng_element_rotations = "rotations"; +LTTNG_HIDDEN const char * const mi_lttng_element_rotate_status = "status"; + /* Deprecated symbols preserved for ABI compatibility. */ const char * const mi_lttng_context_type_perf_counter; const char * const mi_lttng_context_type_perf_cpu_counter; diff --git a/src/common/mi-lttng.h b/src/common/mi-lttng.h index e7cf8af92..b74d30086 100644 --- a/src/common/mi-lttng.h +++ b/src/common/mi-lttng.h @@ -80,6 +80,7 @@ extern const char * const mi_lttng_element_command_success; extern const char * const mi_lttng_element_command_track; extern const char * const mi_lttng_element_command_untrack; extern const char * const mi_lttng_element_command_version; +extern const char * const mi_lttng_element_command_rotate; /* Strings related to version command */ extern const char * const mi_lttng_element_version; @@ -185,6 +186,13 @@ extern const char * const mi_lttng_element_snapshots; /* String related to track/untrack command */ const char * const mi_lttng_element_track_untrack_all_wildcard; +LTTNG_HIDDEN const char * const mi_lttng_element_session_name; + +/* String related to rotate command */ +LTTNG_HIDDEN const char * const mi_lttng_element_rotation; +LTTNG_HIDDEN const char * const mi_lttng_element_rotations; +LTTNG_HIDDEN const char * const mi_lttng_element_rotate_status; + /* Utility string function */ const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain); const char *mi_lttng_logleveltype_string(enum lttng_loglevel_type value); diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 4eed95dec..5b7cc089e 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -102,7 +102,8 @@ enum lttcomm_sessiond_command { LTTNG_REGISTER_TRIGGER = 43, LTTNG_UNREGISTER_TRIGGER = 44, LTTNG_ROTATE_SESSION = 45, - LTTNG_ROTATE_PENDING = 46, + LTTNG_ROTATION_GET_INFO = 46, + LTTNG_SESSION_GET_CURRENT_OUTPUT = 48, }; enum lttcomm_relayd_command { @@ -333,8 +334,8 @@ struct lttcomm_session_msg { uint32_t length; } LTTNG_PACKED trigger; struct { - uint64_t rotate_id; - } LTTNG_PACKED rotate_pending; + uint64_t rotation_id; + } LTTNG_PACKED get_rotation_info; } u; } LTTNG_PACKED; diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index 03c073c9d..bc340d20f 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -6,7 +6,7 @@ lib_LTLIBRARIES = liblttng-ctl.la liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \ lttng-ctl-health.c save.c load.c deprecated-symbols.c \ - channel.c + channel.c rotate.c liblttng_ctl_la_LDFLAGS = \ $(LT_NO_UNDEFINED) diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 3db9b89b4..0468b6746 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -2756,6 +2756,50 @@ end: return ret; } +int lttng_session_get_current_archive_location(const char *session_name, + char **chunk_path) +{ + struct lttcomm_session_msg lsm; + struct lttng_session_get_current_output_return *output_return = NULL; + int ret; + size_t path_len; + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_SESSION_GET_CURRENT_OUTPUT; + ret = lttng_strncpy(lsm.session.name, session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + ret = lttng_ctl_ask_sessiond(&lsm, (void **) &output_return); + if (ret < 0) { + ret = -1; + goto end; + } + + path_len = lttng_strnlen(output_return->path, + sizeof(output_return->path)); + if (path_len == 0 || path_len == sizeof(output_return->path)) { + ret = -LTTNG_ERR_NO_SESSION_OUTPUT; + goto end; + } + + *chunk_path = zmalloc(path_len + 1); + if (!*chunk_path) { + ret = -1; + goto end; + } + memcpy(*chunk_path, output_return->path, path_len); + + ret = 0; + +end: + free(output_return); + return ret; +} + /* * lib constructor. */ diff --git a/src/lib/lttng-ctl/rotate.c b/src/lib/lttng-ctl/rotate.c new file mode 100644 index 000000000..413215932 --- /dev/null +++ b/src/lib/lttng-ctl/rotate.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2017 - Julien Desfossez + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _LGPL_SOURCE +#include +#include + +#include +#include +#include +#include +#include + +#include "lttng-ctl-helper.h" + +struct lttng_rotation_immediate_attr *lttng_rotation_immediate_attr_create(void) +{ + return zmalloc(sizeof(struct lttng_rotation_immediate_attr)); +} + +void lttng_rotation_immediate_attr_destroy( + struct lttng_rotation_immediate_attr *attr) +{ + free(attr); +} + +enum lttng_rotation_status lttng_rotation_immediate_attr_set_session_name( + struct lttng_rotation_immediate_attr *attr, + const char *session_name) +{ + enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK; + int ret; + + if (!attr || !session_name) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto error; + } + + ret = lttng_strncpy(attr->session_name, session_name, + sizeof(attr->session_name)); + if (ret) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto error; + } + +error: + return status; +} + +static +enum lttng_rotation_status ask_rotation_info( + struct lttng_rotation_handle *rotation_handle, + struct lttng_rotation_get_info_return **info) +{ + /* lsm.get_rotation_state.rotation_id */ + struct lttcomm_session_msg lsm; + enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK; + int ret; + + if (!rotation_handle || !info) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto end; + } + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_ROTATION_GET_INFO; + lsm.u.get_rotation_info.rotation_id = rotation_handle->rotation_id; + + ret = lttng_strncpy(lsm.session.name, rotation_handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto end; + } + + ret = lttng_ctl_ask_sessiond(&lsm, (void **) info); + if (ret < 0) { + status = LTTNG_ROTATION_STATUS_ERROR; + goto end; + } +end: + return status; + +} + +enum lttng_rotation_status lttng_rotation_handle_get_state( + struct lttng_rotation_handle *rotation_handle, + enum lttng_rotation_state *state) +{ + enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK; + struct lttng_rotation_get_info_return *info = NULL; + int ret; + + if (!rotation_handle || !state) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto end; + } + + status = ask_rotation_info(rotation_handle, &info); + if (status != LTTNG_ROTATION_STATUS_OK) { + goto end; + } + + *state = (enum lttng_rotation_state) info->status; + if (rotation_handle->archive_location.is_set || + *state != LTTNG_ROTATION_STATE_COMPLETED) { + /* + * The path is only provided by the sessiond once + * the session rotation is completed, but not expired. + */ + goto end; + } + + /* + * Cache the location since the rotation may expire before the user + * has a chance to query it. + */ + ret = lttng_strncpy(rotation_handle->archive_location.path, + info->path, + sizeof(rotation_handle->archive_location.path)); + if (ret) { + status = LTTNG_ROTATION_STATUS_ERROR; + goto end; + } + rotation_handle->archive_location.is_set = true; +end: + free(info); + return status; +} + +enum lttng_rotation_status lttng_rotation_handle_get_completed_archive_location( + struct lttng_rotation_handle *rotation_handle, + const char **path) +{ + int ret; + enum lttng_rotation_status status = LTTNG_ROTATION_STATUS_OK; + struct lttng_rotation_get_info_return *info = NULL; + + if (!rotation_handle || !path) { + status = LTTNG_ROTATION_STATUS_INVALID; + goto end; + } + + /* Use the cached location we got from a previous query. */ + if (rotation_handle->archive_location.is_set) { + *path = rotation_handle->archive_location.path; + goto end; + } + + status = ask_rotation_info(rotation_handle, &info); + if (status != LTTNG_ROTATION_STATUS_OK) { + goto end; + } + + if ((enum lttng_rotation_state) info->status != + LTTNG_ROTATION_STATE_COMPLETED) { + status = LTTNG_ROTATION_STATUS_UNAVAILABLE; + goto end; + } + + ret = lttng_strncpy(rotation_handle->archive_location.path, + info->path, + sizeof(rotation_handle->archive_location.path)); + if (ret) { + status = LTTNG_ROTATION_STATUS_ERROR; + goto end; + } + rotation_handle->archive_location.is_set = true; +end: + free(info); + return status; +} + +void lttng_rotation_handle_destroy( + struct lttng_rotation_handle *rotation_handle) +{ + free(rotation_handle); +} + +static +int init_rotation_handle(struct lttng_rotation_handle *rotation_handle, + struct lttng_rotate_session_return *rotate_return, + struct lttng_rotation_immediate_attr *attr) +{ + int ret; + + ret = lttng_strncpy(rotation_handle->session_name, attr->session_name, + sizeof(rotation_handle->session_name)); + if (ret) { + goto end; + } + + rotation_handle->rotation_id = rotate_return->rotation_id; +end: + return ret; +} + +/* + * Rotate the output folder of the session. + * + * Return 0 on success else a negative LTTng error code. + */ +int lttng_rotate_session(struct lttng_rotation_immediate_attr *attr, + struct lttng_rotation_handle **rotation_handle) +{ + struct lttcomm_session_msg lsm; + struct lttng_rotate_session_return *rotate_return = NULL; + int ret; + + if (!attr) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_ROTATE_SESSION; + lttng_ctl_copy_string(lsm.session.name, attr->session_name, + sizeof(lsm.session.name)); + + ret = lttng_ctl_ask_sessiond(&lsm, (void **) &rotate_return); + if (ret < 0) { + *rotation_handle = NULL; + goto end; + } + + *rotation_handle = zmalloc(sizeof(struct lttng_rotation_handle)); + if (!*rotation_handle) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + init_rotation_handle(*rotation_handle, rotate_return, attr); + + ret = 0; + +end: + free(rotate_return); + return ret; +}