X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fsession.c;h=dc6dea4439a6b77417863f0c1626ce89bfaf4490;hp=ee71533f9ff790e60e11fac522ac2c82c2f4e3c6;hb=3a5f70173aa04d11ccb22694d5d31a702cad33ab;hpb=5da88b0f58d7f838068037ea449ddfb25d3e85ad diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index ee71533f9..dc6dea443 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -1,18 +1,8 @@ /* - * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 David Goulet * - * 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. + * SPDX-License-Identifier: GPL-2.0-only * - * 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 @@ -46,11 +36,16 @@ struct ltt_session_destroy_notifier_element { void *user_data; }; +struct ltt_session_clear_notifier_element { + ltt_session_clear_notifier notifier; + void *user_data; +}; + /* * NOTES: * * No ltt_session.lock is taken here because those data structure are widely - * spread across the lttng-tools code base so before caling functions below + * spread across the lttng-tools code base so before calling functions below * that can read/write a session, the caller MUST acquire the session lock * using session_lock() and session_unlock(). */ @@ -72,6 +67,8 @@ static const char *forbidden_name_chars = "/"; /* Global hash table to keep the sessions, indexed by id. */ static struct lttng_ht *ltt_sessions_ht_by_id = NULL; +/* Global hash table to keep the sessions, indexed by name. */ +static struct lttng_ht *ltt_sessions_ht_by_name = NULL; /* * Validate the session name for forbidden characters. @@ -83,7 +80,7 @@ static int validate_name(const char *name) int ret; char *tok, *tmp_name; - assert(name); + LTTNG_ASSERT(name); tmp_name = strdup(name); if (!tmp_name) { @@ -114,7 +111,7 @@ error: */ static uint64_t add_session_list(struct ltt_session *ls) { - assert(ls); + LTTNG_ASSERT(ls); cds_list_add(&ls->list, <t_session_list.head); return ltt_session_list.next_uuid++; @@ -127,7 +124,7 @@ static uint64_t add_session_list(struct ltt_session *ls) */ static void del_session_list(struct ltt_session *ls) { - assert(ls); + LTTNG_ASSERT(ls); cds_list_del(&ls->list); } @@ -298,7 +295,7 @@ end: } /* - * Allocate the ltt_sessions_ht_by_id HT. + * Allocate the ltt_sessions_ht_by_id and ltt_sessions_ht_by_name HT. * * The session list lock must be held. */ @@ -313,6 +310,15 @@ static int ltt_sessions_ht_alloc(void) ERR("Failed to allocate ltt_sessions_ht_by_id"); goto end; } + + DBG("Allocating ltt_sessions_ht_by_name"); + ltt_sessions_ht_by_name = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + if (!ltt_sessions_ht_by_name) { + ret = -1; + ERR("Failed to allocate ltt_sessions_ht_by_name"); + goto end; + } + end: return ret; } @@ -324,23 +330,29 @@ end: */ static void ltt_sessions_ht_destroy(void) { - if (!ltt_sessions_ht_by_id) { - return; + if (ltt_sessions_ht_by_id) { + ht_cleanup_push(ltt_sessions_ht_by_id); + ltt_sessions_ht_by_id = NULL; + } + + if (ltt_sessions_ht_by_name) { + ht_cleanup_push(ltt_sessions_ht_by_name); + ltt_sessions_ht_by_name = NULL; } - ht_cleanup_push(ltt_sessions_ht_by_id); - ltt_sessions_ht_by_id = NULL; + + return; } /* - * Add a ltt_session to the ltt_sessions_ht_by_id. - * If unallocated, the ltt_sessions_ht_by_id HT is allocated. - * The session list lock must be held. + * Add a ltt_session to the ltt_sessions_ht_by_id and ltt_sessions_ht_by_name. + * If unallocated, the ltt_sessions_ht_by_id and ltt_sessions_ht_by_name. HTs + * are allocated. The session list lock must be held. */ static void add_session_ht(struct ltt_session *ls) { int ret; - assert(ls); + LTTNG_ASSERT(ls); if (!ltt_sessions_ht_by_id) { ret = ltt_sessions_ht_alloc(); @@ -349,35 +361,45 @@ static void add_session_ht(struct ltt_session *ls) goto end; } } + + /* Should always be present with ltt_sessions_ht_by_id. */ + LTTNG_ASSERT(ltt_sessions_ht_by_name); + lttng_ht_node_init_u64(&ls->node, ls->id); lttng_ht_add_unique_u64(ltt_sessions_ht_by_id, &ls->node); + lttng_ht_node_init_str(&ls->node_by_name, ls->name); + lttng_ht_add_unique_str(ltt_sessions_ht_by_name, &ls->node_by_name); + end: return; } /* - * Test if ltt_sessions_ht_by_id is empty. + * Test if ltt_sessions_ht_by_id/name are empty. * Return 1 if empty, 0 if not empty. * The session list lock must be held. */ static int ltt_sessions_ht_empty(void) { - int ret; + unsigned long count; if (!ltt_sessions_ht_by_id) { - ret = 1; + count = 0; goto end; } - ret = lttng_ht_get_count(ltt_sessions_ht_by_id) ? 0 : 1; + LTTNG_ASSERT(ltt_sessions_ht_by_name); + + count = lttng_ht_get_count(ltt_sessions_ht_by_id); + LTTNG_ASSERT(count == lttng_ht_get_count(ltt_sessions_ht_by_name)); end: - return ret; + return count ? 0 : 1; } /* - * Remove a ltt_session from the ltt_sessions_ht_by_id. - * If empty, the ltt_sessions_ht_by_id HT is freed. + * Remove a ltt_session from the ltt_sessions_ht_by_id/name. + * If empty, the ltt_sessions_ht_by_id/name HTs are freed. * The session list lock must be held. */ static void del_session_ht(struct ltt_session *ls) @@ -385,15 +407,20 @@ static void del_session_ht(struct ltt_session *ls) struct lttng_ht_iter iter; int ret; - assert(ls); - assert(ltt_sessions_ht_by_id); + LTTNG_ASSERT(ls); + LTTNG_ASSERT(ltt_sessions_ht_by_id); + LTTNG_ASSERT(ltt_sessions_ht_by_name); iter.iter.node = &ls->node.node; ret = lttng_ht_del(ltt_sessions_ht_by_id, &iter); - assert(!ret); + LTTNG_ASSERT(!ret); + + iter.iter.node = &ls->node_by_name.node; + ret = lttng_ht_del(ltt_sessions_ht_by_name, &iter); + LTTNG_ASSERT(!ret); if (ltt_sessions_ht_empty()) { - DBG("Empty ltt_sessions_ht_by_id, destroying it"); + DBG("Empty ltt_sessions_ht_by_id/name, destroying hast tables"); ltt_sessions_ht_destroy(); } } @@ -403,7 +430,7 @@ static void del_session_ht(struct ltt_session *ls) */ void session_lock(struct ltt_session *session) { - assert(session); + LTTNG_ASSERT(session); pthread_mutex_lock(&session->lock); } @@ -413,7 +440,7 @@ void session_lock(struct ltt_session *session) */ void session_unlock(struct ltt_session *session) { - assert(session); + LTTNG_ASSERT(session); pthread_mutex_unlock(&session->lock); } @@ -453,7 +480,7 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, goto end; } chunk_status = lttng_trace_chunk_get_id(new_trace_chunk, &chunk_id); - assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); + LTTNG_ASSERT(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); refs_to_acquire = 1; refs_to_acquire += !!session->ust_session; @@ -476,7 +503,7 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, CONSUMER_DST_LOCAL; session->ust_session->current_trace_chunk = new_trace_chunk; - if (is_local_trace) { + if (is_local_trace) { enum lttng_error_code ret_error_code; ret_error_code = ust_app_create_channel_subdirectories( @@ -484,7 +511,7 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, if (ret_error_code != LTTNG_OK) { goto error; } - } + } cds_lfht_for_each_entry( session->ust_session->consumer->socks->ht, &iter, socket, node.node) { @@ -494,11 +521,11 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, session->id, new_trace_chunk, DEFAULT_UST_TRACE_DIR); pthread_mutex_unlock(socket->lock); - if (ret) { + if (ret) { goto error; - } - } - } + } + } + } if (session->kernel_session) { const uint64_t relayd_id = session->kernel_session->consumer->net_seq_index; @@ -515,7 +542,7 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, if (ret_error_code != LTTNG_OK) { goto error; } - } + } cds_lfht_for_each_entry( session->kernel_session->consumer->socks->ht, &iter, socket, node.node) { @@ -525,11 +552,11 @@ int _session_set_trace_chunk_no_lock_check(struct ltt_session *session, session->id, new_trace_chunk, DEFAULT_KERNEL_TRACE_DIR); pthread_mutex_unlock(socket->lock); - if (ret) { + if (ret) { goto error; - } - } - } + } + } + } /* * Update local current trace chunk state last, only if all remote @@ -553,7 +580,7 @@ error: if (session->kernel_session) { session->kernel_session->current_trace_chunk = NULL; } - /* + /* * Release references taken in the case where all references could not * be acquired. */ @@ -579,16 +606,17 @@ struct lttng_trace_chunk *session_create_new_trace_chunk( const char *base_path; struct lttng_directory_handle *session_output_directory = NULL; const struct lttng_credentials session_credentials = { - .uid = session->uid, - .gid = session->gid, + .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid), + .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid), }; uint64_t next_chunk_id; const struct consumer_output *output; + const char *new_path; if (consumer_output_override) { output = consumer_output_override; } else { - assert(session->ust_session || session->kernel_session); + LTTNG_ASSERT(session->ust_session || session->kernel_session); output = session->ust_session ? session->ust_session->consumer : session->kernel_session->consumer; @@ -607,8 +635,26 @@ struct lttng_trace_chunk *session_create_new_trace_chunk( next_chunk_id = session->most_recent_chunk_id.is_set ? session->most_recent_chunk_id.value + 1 : 0; + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + goto error; + } + } + if (!session->current_trace_chunk) { + if (!session->rotated) { + new_path = ""; + } else { + new_path = NULL; + } + } else { + new_path = DEFAULT_CHUNK_TMP_NEW_DIRECTORY; + } + trace_chunk = lttng_trace_chunk_create(next_chunk_id, - chunk_creation_ts); + chunk_creation_ts, new_path); if (!trace_chunk) { goto error; } @@ -662,9 +708,9 @@ error: goto end; } -int session_close_trace_chunk(const struct ltt_session *session, +int session_close_trace_chunk(struct ltt_session *session, struct lttng_trace_chunk *trace_chunk, - const enum lttng_trace_chunk_command_type *close_command, + enum lttng_trace_chunk_command_type close_command, char *closed_trace_chunk_path) { int ret = 0; @@ -673,14 +719,13 @@ int session_close_trace_chunk(const struct ltt_session *session, struct consumer_socket *socket; enum lttng_trace_chunk_status chunk_status; const time_t chunk_close_timestamp = time(NULL); + const char *new_path; - if (close_command) { - chunk_status = lttng_trace_chunk_set_close_command( - trace_chunk, *close_command); - if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { - ret = -1; - goto end; - } + chunk_status = lttng_trace_chunk_set_close_command( + trace_chunk, close_command); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; } if (chunk_close_timestamp == (time_t) -1) { @@ -689,6 +734,44 @@ int session_close_trace_chunk(const struct ltt_session *session, ret = -1; goto end; } + + if (close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE && !session->rotated) { + /* New chunk stays in session output directory. */ + new_path = ""; + } else { + /* Use chunk name for new chunk. */ + new_path = NULL; + } + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + /* Rename new chunk path. */ + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + new_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } + if (!lttng_trace_chunk_get_name_overridden(trace_chunk) && + close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) { + const char *old_path; + + if (!session->rotated) { + old_path = ""; + } else { + old_path = NULL; + } + /* We need to move back the .tmp_old_chunk to its rightful place. */ + chunk_status = lttng_trace_chunk_rename_path(trace_chunk, + old_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } + if (close_command == LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED) { + session->rotated = true; + } chunk_status = lttng_trace_chunk_set_close_timestamp(trace_chunk, chunk_close_timestamp); if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { @@ -741,6 +824,83 @@ end: return ret; } +/* + * This function skips the metadata channel as the begin/end timestamps of a + * metadata packet are useless. + * + * Moreover, opening a packet after a "clear" will cause problems for live + * sessions as it will introduce padding that was not part of the first trace + * chunk. The relay daemon expects the content of the metadata stream of + * successive metadata trace chunks to be strict supersets of one another. + * + * For example, flushing a packet at the beginning of the metadata stream of + * a trace chunk resulting from a "clear" session command will cause the + * size of the metadata stream of the new trace chunk to not match the size of + * the metadata stream of the original chunk. This will confuse the relay + * daemon as the same "offset" in a metadata stream will no longer point + * to the same content. + */ +static +enum lttng_error_code session_kernel_open_packets(struct ltt_session *session) +{ + enum lttng_error_code ret = LTTNG_OK; + struct consumer_socket *socket; + struct lttng_ht_iter iter; + struct cds_lfht_node *node; + struct ltt_kernel_channel *chan; + + rcu_read_lock(); + + cds_lfht_first(session->kernel_session->consumer->socks->ht, &iter.iter); + node = cds_lfht_iter_get_node(&iter.iter); + socket = container_of(node, typeof(*socket), node.node); + + cds_list_for_each_entry(chan, + &session->kernel_session->channel_list.head, list) { + int open_ret; + + DBG("Open packet of kernel channel: channel key = %" PRIu64 + ", session name = %s, session_id = %" PRIu64, + chan->key, session->name, session->id); + + open_ret = consumer_open_channel_packets(socket, chan->key); + if (open_ret < 0) { + /* General error (no known error expected). */ + ret = LTTNG_ERR_UNK; + goto end; + } + } + +end: + rcu_read_unlock(); + return ret; +} + +enum lttng_error_code session_open_packets(struct ltt_session *session) +{ + enum lttng_error_code ret = LTTNG_OK; + + DBG("Opening packets of session channels: session name = %s, session id = %" PRIu64, + session->name, session->id); + + if (session->ust_session) { + ret = ust_app_open_packets(session); + if (ret != LTTNG_OK) { + goto end; + } + } + + if (session->kernel_session) { + ret = session_kernel_open_packets(session); + if (ret != LTTNG_OK) { + goto end; + } + } + +end: + return ret; +} + /* * Set a session's current trace chunk. * @@ -771,6 +931,25 @@ void session_notify_destruction(const struct ltt_session *session) } } +/* + * Fire each clear notifier once, and remove them from the array. + */ +void session_notify_clear(struct ltt_session *session) +{ + size_t i; + const size_t count = lttng_dynamic_array_get_count( + &session->clear_notifiers); + + for (i = 0; i < count; i++) { + const struct ltt_session_clear_notifier_element *element = + lttng_dynamic_array_get_element( + &session->clear_notifiers, i); + + element->notifier(session, element->user_data); + } + lttng_dynamic_array_clear(&session->clear_notifiers); +} + static void session_release(struct urcu_ref *ref) { @@ -780,12 +959,12 @@ void session_release(struct urcu_ref *ref) struct ltt_session *session = container_of(ref, typeof(*session), ref); const bool session_published = session->published; - assert(!session->chunk_being_archived); + LTTNG_ASSERT(!session->chunk_being_archived); usess = session->ust_session; ksess = session->kernel_session; - /* Clean kernel session teardown, keeping data for destroy notifier. */ + /* Clean kernel session teardown, keeping data for destroy notifier. */ kernel_destroy_session(ksess); /* UST session teardown, keeping data for destroy notifier. */ @@ -807,7 +986,7 @@ void session_release(struct urcu_ref *ref) * Must notify the kernel thread here to update it's poll set in order to * remove the channel(s)' fd just destroyed. */ - ret = notify_thread_pipe(kernel_poll_pipe[1]); + ret = notify_thread_pipe(the_kernel_poll_pipe[1]); if (ret < 0) { PERROR("write kernel poll pipe"); } @@ -833,6 +1012,7 @@ void session_release(struct urcu_ref *ref) session->ust_session = NULL; } lttng_dynamic_array_reset(&session->destroy_notifiers); + lttng_dynamic_array_reset(&session->clear_notifiers); free(session->last_archived_chunk_name); free(session->base_path); free(session); @@ -841,6 +1021,7 @@ void session_release(struct urcu_ref *ref) * Broadcast after free-ing to ensure the memory is * reclaimed before the main thread exits. */ + ASSERT_LOCKED(ltt_session_list.lock); pthread_cond_broadcast(<t_session_list.removal_cond); } } @@ -867,7 +1048,7 @@ void session_put(struct ltt_session *session) * may cause the removal of the session from the session_list. */ ASSERT_LOCKED(ltt_session_list.lock); - assert(session->ref.refcount); + LTTNG_ASSERT(session->ref.refcount); urcu_ref_put(&session->ref, session_release); } @@ -884,7 +1065,7 @@ void session_put(struct ltt_session *session) */ void session_destroy(struct ltt_session *session) { - assert(!session->destroyed); + LTTNG_ASSERT(!session->destroyed); session->destroyed = true; session_put(session); } @@ -901,6 +1082,18 @@ int session_add_destroy_notifier(struct ltt_session *session, &element); } +int session_add_clear_notifier(struct ltt_session *session, + ltt_session_clear_notifier notifier, void *user_data) +{ + const struct ltt_session_clear_notifier_element element = { + .notifier = notifier, + .user_data = user_data + }; + + return lttng_dynamic_array_add_element(&session->clear_notifiers, + &element); +} + /* * Return a ltt_session structure ptr that matches name. If no session found, * NULL is returned. This must be called with the session list lock held using @@ -911,7 +1104,7 @@ struct ltt_session *session_find_by_name(const char *name) { struct ltt_session *iter; - assert(name); + LTTNG_ASSERT(name); ASSERT_LOCKED(ltt_session_list.lock); DBG2("Trying to find session by name %s", name); @@ -992,6 +1185,9 @@ enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, lttng_dynamic_array_init(&new_session->destroy_notifiers, sizeof(struct ltt_session_destroy_notifier_element), NULL); + lttng_dynamic_array_init(&new_session->clear_notifiers, + sizeof(struct ltt_session_clear_notifier_element), + NULL); urcu_ref_init(&new_session->ref); pthread_mutex_init(&new_session->lock, NULL); @@ -1133,18 +1329,13 @@ error: } /* - * Check if the UID or GID match the session. Root user has access to all + * Check if the UID matches the session. Root user has access to all * sessions. */ -int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) +bool session_access_ok(struct ltt_session *session, uid_t uid) { - assert(session); - - if (uid != session->uid && gid != session->gid && uid != 0) { - return 0; - } else { - return 1; - } + LTTNG_ASSERT(session); + return (uid == session->uid) || uid == 0; } /* @@ -1181,11 +1372,56 @@ int session_reset_rotation_state(struct ltt_session *session, chunk_status = lttng_trace_chunk_get_id( session->chunk_being_archived, &chunk_id); - assert(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); + LTTNG_ASSERT(chunk_status == LTTNG_TRACE_CHUNK_STATUS_OK); LTTNG_OPTIONAL_SET(&session->last_archived_chunk_id, chunk_id); lttng_trace_chunk_put(session->chunk_being_archived); session->chunk_being_archived = NULL; + /* + * Fire the clear reply notifiers if we are completing a clear + * rotation. + */ + session_notify_clear(session); } return ret; } + +/* + * Sample the id of a session looked up via its name. + * Here the term "sampling" hint the caller that this return the id at a given + * point in time with no guarantee that the session for which the id was + * sampled still exist at that point. + * + * Return 0 when the session is not found, + * Return 1 when the session is found and set `id`. + */ +bool sample_session_id_by_name(const char *name, uint64_t *id) +{ + bool found = false; + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + struct ltt_session *ls; + + rcu_read_lock(); + + if (!ltt_sessions_ht_by_name) { + found = false; + goto end; + } + + lttng_ht_lookup(ltt_sessions_ht_by_name, name, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node == NULL) { + found = false; + goto end; + } + + ls = caa_container_of(node, struct ltt_session, node_by_name); + *id = ls->id; + found = true; + + DBG3("Session id `%" PRIu64 "` sampled for session `%s", *id, name); +end: + rcu_read_unlock(); + return found; +}