X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fsession.c;h=491de61759825f0485326cae5b2d7e72db72c6ad;hp=38dbf6128c31639b668910ede62fc4a51f19d7c0;hb=facc1162cc341da6eb82a6aef22e6ebf0e97c460;hpb=92816cc33a1add3c8276839bd6335e17423577dd diff --git a/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c index 38dbf6128..491de6175 100644 --- a/src/bin/lttng-sessiond/session.c +++ b/src/bin/lttng-sessiond/session.c @@ -25,14 +25,18 @@ #include #include #include +#include #include #include #include +#include "lttng-sessiond.h" +#include "kernel.h" #include "session.h" #include "utils.h" #include "trace-ust.h" +#include "timer.h" /* * NOTES: @@ -51,6 +55,7 @@ static struct ltt_session_list ltt_session_list = { .head = CDS_LIST_HEAD_INIT(ltt_session_list.head), .lock = PTHREAD_MUTEX_INITIALIZER, + .removal_cond = PTHREAD_COND_INITIALIZER, .next_uuid = 0, }; @@ -127,6 +132,19 @@ struct ltt_session_list *session_get_list(void) return <t_session_list; } +/* + * Returns once the session list is empty. + */ +void session_list_wait_empty(void) +{ + pthread_mutex_lock(<t_session_list.lock); + while (!cds_list_empty(<t_session_list.head)) { + pthread_cond_wait(<t_session_list.removal_cond, + <t_session_list.lock); + } + pthread_mutex_unlock(<t_session_list.lock); +} + /* * Acquire session list lock */ @@ -382,29 +400,128 @@ void session_unlock(struct ltt_session *session) pthread_mutex_unlock(&session->lock); } +static +void session_release(struct urcu_ref *ref) +{ + int ret; + struct ltt_ust_session *usess; + struct ltt_kernel_session *ksess; + struct ltt_session *session = container_of(ref, typeof(*session), ref); + + usess = session->ust_session; + ksess = session->kernel_session; + + /* Clean kernel session teardown */ + kernel_destroy_session(ksess); + + /* UST session teardown */ + if (usess) { + /* Close any relayd session */ + consumer_output_send_destroy_relayd(usess->consumer); + + /* Destroy every UST application related to this session. */ + ret = ust_app_destroy_trace_all(usess); + if (ret) { + ERR("Error in ust_app_destroy_trace_all"); + } + + /* Clean up the rest. */ + trace_ust_destroy_session(usess); + } + + /* + * 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]); + if (ret < 0) { + PERROR("write kernel poll pipe"); + } + + DBG("Destroying session %s (id %" PRIu64 ")", session->name, session->id); + pthread_mutex_destroy(&session->lock); + + consumer_output_put(session->consumer); + snapshot_destroy(&session->snapshot); + + if (session->published) { + ASSERT_LOCKED(ltt_session_list.lock); + del_session_list(session); + del_session_ht(session); + pthread_cond_broadcast(<t_session_list.removal_cond); + } + free(session); +} + +/* + * Acquire a reference to a session. + * This function may fail (return false); its return value must be checked. + */ +bool session_get(struct ltt_session *session) +{ + return urcu_ref_get_unless_zero(&session->ref); +} + +/* + * Release a reference to a session. + */ +void session_put(struct ltt_session *session) +{ + if (!session) { + return; + } + /* + * The session list lock must be held as any session_put() + * may cause the removal of the session from the session_list. + */ + ASSERT_LOCKED(ltt_session_list.lock); + assert(session->ref.refcount); + urcu_ref_put(&session->ref, session_release); +} + +/* + * Destroy a session. + * + * This method does not immediately release/free the session as other + * components may still hold a reference to the session. However, + * the session should no longer be presented to the user. + * + * Releases the session list's reference to the session + * and marks it as destroyed. Iterations on the session list should be + * mindful of the "destroyed" flag. + */ +void session_destroy(struct ltt_session *session) +{ + assert(!session->destroyed); + session->destroyed = true; + session_put(session); +} + /* * 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 * session_lock_list and session_unlock_list. + * A reference to the session is implicitly acquired by this function. */ struct ltt_session *session_find_by_name(const char *name) { struct ltt_session *iter; assert(name); + ASSERT_LOCKED(ltt_session_list.lock); DBG2("Trying to find session by name %s", name); cds_list_for_each_entry(iter, <t_session_list.head, list) { - if (strncmp(iter->name, name, NAME_MAX) == 0) { + if (!strncmp(iter->name, name, NAME_MAX) && + !iter->destroyed) { goto found; } } - iter = NULL; - + return NULL; found: - return iter; + return session_get(iter) ? iter : NULL; } /* @@ -418,6 +535,8 @@ struct ltt_session *session_find_by_id(uint64_t id) struct lttng_ht_iter iter; struct ltt_session *ls; + ASSERT_LOCKED(ltt_session_list.lock); + if (!ltt_sessions_ht_by_id) { goto end; } @@ -430,7 +549,7 @@ struct ltt_session *session_find_by_id(uint64_t id) ls = caa_container_of(node, struct ltt_session, node); DBG3("Session %" PRIu64 " found by id.", id); - return ls; + return session_get(ls) ? ls : NULL; end: DBG3("Session %" PRIu64 " NOT found by id", id); @@ -438,98 +557,144 @@ end: } /* - * Delete session from the session list and free the memory. - * - * Return -1 if no session is found. On success, return 1; - * Should *NOT* be called with RCU read-side lock held. + * Create a new session and add it to the session list. + * Session list lock must be held by the caller. */ -int session_destroy(struct ltt_session *session) -{ - /* Safety check */ - assert(session); - - DBG("Destroying session %s (id %" PRIu64 ")", session->name, session->id); - del_session_list(session); - pthread_mutex_destroy(&session->lock); - del_session_ht(session); - - consumer_output_put(session->consumer); - snapshot_destroy(&session->snapshot); - free(session); - - return LTTNG_OK; -} - -/* - * Create a brand new session and add it to the session list. - */ -int session_create(char *name, uid_t uid, gid_t gid) +enum lttng_error_code session_create(const char *name, uid_t uid, gid_t gid, + struct ltt_session **out_session) { int ret; - struct ltt_session *new_session; + enum lttng_error_code ret_code; + struct ltt_session *new_session = NULL; - /* Allocate session data structure */ + ASSERT_LOCKED(ltt_session_list.lock); + if (name) { + struct ltt_session *clashing_session; + + clashing_session = session_find_by_name(name); + if (clashing_session) { + session_put(clashing_session); + ret_code = LTTNG_ERR_EXIST_SESS; + goto error; + } + } new_session = zmalloc(sizeof(struct ltt_session)); - if (new_session == NULL) { - PERROR("zmalloc"); - ret = LTTNG_ERR_FATAL; - goto error_malloc; + if (!new_session) { + PERROR("Failed to allocate an ltt_session structure"); + ret_code = LTTNG_ERR_NOMEM; + goto error; } - /* Define session name */ - if (name != NULL) { - if (snprintf(new_session->name, NAME_MAX, "%s", name) < 0) { - ret = LTTNG_ERR_FATAL; - goto error_asprintf; - } - } else { - ERR("No session name given"); - ret = LTTNG_ERR_FATAL; + urcu_ref_init(&new_session->ref); + pthread_mutex_init(&new_session->lock, NULL); + + new_session->creation_time = time(NULL); + if (new_session->creation_time == (time_t) -1) { + PERROR("Failed to sample session creation time"); + ret_code = LTTNG_ERR_SESSION_FAIL; goto error; } - ret = validate_name(name); - if (ret < 0) { - ret = LTTNG_ERR_SESSION_INVALID_CHAR; + /* Create default consumer output. */ + new_session->consumer = consumer_create_output(CONSUMER_DST_LOCAL); + if (new_session->consumer == NULL) { + ret_code = LTTNG_ERR_NOMEM; goto error; } + if (name) { + ret = lttng_strncpy(new_session->name, name, sizeof(new_session->name)); + if (ret) { + ret_code = LTTNG_ERR_SESSION_INVALID_CHAR; + goto error; + } + ret = validate_name(name); + if (ret < 0) { + ret_code = LTTNG_ERR_SESSION_INVALID_CHAR; + goto error; + } + } else { + int i = 0; + bool found_name = false; + char datetime[16]; + struct tm *timeinfo; + + timeinfo = localtime(&new_session->creation_time); + if (!timeinfo) { + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); + for (i = 0; i < INT_MAX; i++) { + struct ltt_session *clashing_session; + + if (i == 0) { + ret = snprintf(new_session->name, + sizeof(new_session->name), + "%s-%s", + DEFAULT_SESSION_NAME, + datetime); + } else { + ret = snprintf(new_session->name, + sizeof(new_session->name), + "%s%d-%s", + DEFAULT_SESSION_NAME, i, + datetime); + } + if (ret == -1 || ret >= sizeof(new_session->name)) { + /* + * Null-terminate in case the name is used + * in logging statements. + */ + new_session->name[sizeof(new_session->name) - 1] = '\0'; + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + + clashing_session = + session_find_by_name(new_session->name); + session_put(clashing_session); + if (!clashing_session) { + found_name = true; + break; + } + } + if (found_name) { + DBG("Generated session name \"%s\"", new_session->name); + new_session->has_auto_generated_name = true; + } else { + ERR("Failed to auto-generate a session name"); + ret_code = LTTNG_ERR_SESSION_FAIL; + goto error; + } + } + ret = gethostname(new_session->hostname, sizeof(new_session->hostname)); if (ret < 0) { if (errno == ENAMETOOLONG) { new_session->hostname[sizeof(new_session->hostname) - 1] = '\0'; + ERR("Hostname exceeds the maximal permitted length and has been truncated to %s", + new_session->hostname); } else { - ret = LTTNG_ERR_FATAL; + ret_code = LTTNG_ERR_SESSION_FAIL; goto error; } } - /* Init kernel session */ - new_session->kernel_session = NULL; - new_session->ust_session = NULL; - - /* Init lock */ - pthread_mutex_init(&new_session->lock, NULL); - new_session->uid = uid; new_session->gid = gid; ret = snapshot_init(&new_session->snapshot); if (ret < 0) { - ret = LTTNG_ERR_NOMEM; + ret_code = LTTNG_ERR_NOMEM; goto error; } - new_session->rotation_pending_local = false; - new_session->rotation_pending_relay = false; new_session->rotation_state = LTTNG_ROTATION_STATE_NO_ROTATION; - new_session->rotation_pending_check_timer_enabled = false; - new_session->rotation_schedule_timer_enabled = false; - - /* Add new session to the session list */ - session_lock_list(); + /* Add new session to the session list. */ new_session->id = add_session_list(new_session); + /* * Add the new session to the ltt_sessions_ht_by_id. * No ownership is taken by the hash table; it is merely @@ -537,23 +702,26 @@ int session_create(char *name, uid_t uid, gid_t gid) * by session id. */ add_session_ht(new_session); - session_unlock_list(); + new_session->published = true; /* - * Consumer is let to NULL since the create_session_uri command will set it - * up and, if valid, assign it to the session. + * Consumer is left to NULL since the create_session_uri command will + * set it up and, if valid, assign it to the session. */ - DBG("Tracing session %s created with ID %" PRIu64 " by UID %d GID %d", - name, new_session->id, new_session->uid, new_session->gid); - - return LTTNG_OK; - + DBG("Tracing session %s created with ID %" PRIu64 " by uid = %d, gid = %d", + new_session->name, new_session->id, new_session->uid, + new_session->gid); + ret_code = LTTNG_OK; +end: + if (new_session) { + (void) session_get(new_session); + *out_session = new_session; + } + return ret_code; error: -error_asprintf: - free(new_session); - -error_malloc: - return ret; + session_put(new_session); + new_session = NULL; + goto end; } /* @@ -570,3 +738,36 @@ int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) return 1; } } + +/* + * Set a session's rotation state and reset all associated state. + * + * This function resets the rotation state (check timers, pending + * flags, etc.) and sets the result of the last rotation. The result + * can be queries by a liblttng-ctl client. + * + * Be careful of the result passed to this function. For instance, + * on failure to launch a rotation, a client will expect the rotation + * state to be set to "NO_ROTATION". If an error occured while the + * rotation was "ONGOING", result should be set to "ERROR", which will + * allow a client to report it. + * + * Must be called with the session and session_list locks held. + */ +int session_reset_rotation_state(struct ltt_session *session, + enum lttng_rotation_state result) +{ + int ret = 0; + + ASSERT_LOCKED(ltt_session_list.lock); + ASSERT_LOCKED(session->lock); + + session->rotation_pending_local = false; + session->rotation_pending_relay = false; + session->rotated_after_last_stop = false; + session->rotation_state = result; + if (session->rotation_pending_check_timer_enabled) { + ret = timer_session_rotation_pending_check_stop(session); + } + return ret; +}