Fix: sessiond: ODR violation results in memory corruption
[lttng-tools.git] / src / bin / lttng-sessiond / action-executor.cpp
index 8c2374535195cc0cc59531a4d02133b3930c5e56..23a99a1acbbb7ec94458ad79a5dc7d9a85ba68ee 100644 (file)
@@ -5,29 +5,29 @@
  *
  */
 
-#include "action-executor.h"
-#include "cmd.h"
-#include "health-sessiond.h"
-#include "lttng-sessiond.h"
-#include "notification-thread-internal.h"
-#include "session.h"
-#include "thread.h"
-#include <common/dynamic-array.h>
-#include <common/macros.h>
-#include <common/optional.h>
-#include <lttng/action/action-internal.h>
-#include <lttng/action/list-internal.h>
+#include "action-executor.hpp"
+#include "cmd.hpp"
+#include "health-sessiond.hpp"
+#include "lttng-sessiond.hpp"
+#include "notification-thread-internal.hpp"
+#include "session.hpp"
+#include "thread.hpp"
+#include <common/dynamic-array.hpp>
+#include <common/macros.hpp>
+#include <common/optional.hpp>
+#include <lttng/action/action-internal.hpp>
+#include <lttng/action/list-internal.hpp>
 #include <lttng/action/list.h>
-#include <lttng/action/notify-internal.h>
+#include <lttng/action/notify-internal.hpp>
 #include <lttng/action/notify.h>
 #include <lttng/action/rotate-session.h>
 #include <lttng/action/snapshot-session.h>
 #include <lttng/action/start-session.h>
 #include <lttng/action/stop-session.h>
 #include <lttng/condition/evaluation.h>
-#include <lttng/condition/event-rule-matches-internal.h>
+#include <lttng/condition/event-rule-matches-internal.hpp>
 #include <lttng/lttng-error.h>
-#include <lttng/trigger/trigger-internal.h>
+#include <lttng/trigger/trigger-internal.hpp>
 #include <pthread.h>
 #include <stdbool.h>
 #include <stddef.h>
 #define THREAD_NAME "Action Executor"
 #define MAX_QUEUED_WORK_COUNT 8192
 
+struct action_executor {
+       struct lttng_thread *thread;
+       struct notification_thread_handle *notification_thread_handle;
+       struct {
+               uint64_t pending_count;
+               struct cds_list_head list;
+               pthread_cond_t cond;
+               pthread_mutex_t lock;
+       } work;
+       bool should_quit;
+       uint64_t next_work_item_id;
+};
+
+namespace {
 /*
  * A work item is composed of a dynamic array of sub-items which
  * represent a flattened, and augmented, version of a trigger's actions.
@@ -69,7 +83,6 @@
  * trigger object at the moment of execution, if the trigger is found to be
  * unregistered, the execution is skipped.
  */
-
 struct action_work_item {
        uint64_t id;
 
@@ -94,19 +107,8 @@ struct action_work_subitem {
                LTTNG_OPTIONAL(uint64_t) session_id;
        } context;
 };
+} /* namespace */
 
-struct action_executor {
-       struct lttng_thread *thread;
-       struct notification_thread_handle *notification_thread_handle;
-       struct {
-               uint64_t pending_count;
-               struct cds_list_head list;
-               pthread_cond_t cond;
-               pthread_mutex_t lock;
-       } work;
-       bool should_quit;
-       uint64_t next_work_item_id;
-};
 
 /*
  * Only return non-zero on a fatal error that should shut down the action
@@ -233,6 +235,15 @@ static int client_handle_transmission_status(
        case CLIENT_TRANSMISSION_STATUS_COMPLETE:
                DBG("Successfully sent full notification to client, client_id = %" PRIu64,
                                client->id);
+               /*
+                * There is no need to wake the (e)poll thread. If it was waiting for
+                * "out" events on the client's socket, it will see that no payload
+                * in queued and will unsubscribe from that event.
+                *
+                * In the other cases, we have to wake the the (e)poll thread to either
+                * handle the error on the client or to get it to monitor the client "out"
+                * events.
+                */
                update_communication = false;
                break;
        case CLIENT_TRANSMISSION_STATUS_QUEUED:
@@ -264,7 +275,7 @@ end:
 
 static int action_executor_notify_handler(struct action_executor *executor,
                const struct action_work_item *work_item,
-               struct action_work_subitem *item)
+               struct action_work_subitem *item __attribute__((unused)))
 {
        return notification_client_list_send_evaluation(work_item->client_list,
                        work_item->trigger,
@@ -276,7 +287,7 @@ static int action_executor_notify_handler(struct action_executor *executor,
 }
 
 static int action_executor_start_session_handler(
-               struct action_executor *executor,
+               struct action_executor *executor __attribute__((unused)),
                const struct action_work_item *work_item,
                struct action_work_subitem *item)
 {
@@ -301,40 +312,33 @@ static int action_executor_start_session_handler(
         * existed. If not skip the action altogether.
         */
        if (!item->context.session_id.is_set) {
-               DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`",
+               DBG("Session `%s` was not present at the moment the work item was enqueued for `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                get_trigger_name(work_item->trigger));
                lttng_action_increase_execution_failure_count(action);
-               ret = 0;
                goto end;
        }
 
        session_lock_list();
-       session = session_find_by_name(session_name);
+       rcu_read_lock();
+       session = session_find_by_id(LTTNG_OPTIONAL_GET(item->context.session_id));
        if (!session) {
                DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                get_trigger_name(work_item->trigger));
+               lttng_action_increase_execution_failure_count(action);
                goto error_unlock_list;
        }
 
-       /*
-        * Check if the session id is the same as when the work item was
-        * enqueued.
-        */
-       if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) {
-               DBG("Session id for session `%s` (id: %" PRIu64
-                               " is not the same that was sampled (id: %" PRIu64
-                               " at the moment the work item was enqueued for %s` action of trigger `%s`",
-                               session_name, session->id,
-                               LTTNG_OPTIONAL_GET(item->context.session_id),
+       session_lock(session);
+       if (session->destroyed) {
+               DBG("Session `%s` with id = %" PRIu64 " is flagged as destroyed. Skipping: action = `%s`, trigger = `%s`",
+                               session->name, session->id,
                                get_action_name(action),
                                get_trigger_name(work_item->trigger));
-               ret = 0;
-               goto error_put_session;
+               goto error_unlock_session;
        }
 
-       session_lock(session);
        if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
                goto error_unlock_session;
        }
@@ -359,16 +363,16 @@ static int action_executor_start_session_handler(
 
 error_unlock_session:
        session_unlock(session);
-error_put_session:
        session_put(session);
 error_unlock_list:
+       rcu_read_unlock();
        session_unlock_list();
 end:
        return ret;
 }
 
 static int action_executor_stop_session_handler(
-               struct action_executor *executor,
+               struct action_executor *executor __attribute__((unused)),
                const struct action_work_item *work_item,
                struct action_work_subitem *item)
 {
@@ -393,16 +397,16 @@ static int action_executor_stop_session_handler(
         * existed. If not, skip the action altogether.
         */
        if (!item->context.session_id.is_set) {
-               DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`",
+               DBG("Session `%s` was not present at the moment the work item was enqueued for `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                get_trigger_name(work_item->trigger));
                lttng_action_increase_execution_failure_count(action);
-               ret = 0;
                goto end;
        }
 
        session_lock_list();
-       session = session_find_by_name(session_name);
+       rcu_read_lock();
+       session = session_find_by_id(LTTNG_OPTIONAL_GET(item->context.session_id));
        if (!session) {
                DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
@@ -411,23 +415,15 @@ static int action_executor_stop_session_handler(
                goto error_unlock_list;
        }
 
-       /*
-        * Check if the session id is the same as when the work item was
-        * enqueued
-        */
-       if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) {
-               DBG("Session id for session `%s` (id: %" PRIu64
-                               " is not the same that was sampled (id: %" PRIu64
-                               " at the moment the work item was enqueued for %s` action of trigger `%s`",
-                               session_name, session->id,
-                               LTTNG_OPTIONAL_GET(item->context.session_id),
+       session_lock(session);
+       if (session->destroyed) {
+               DBG("Session `%s` with id = %" PRIu64 " is flagged as destroyed. Skipping: action = `%s`, trigger = `%s`",
+                               session->name, session->id,
                                get_action_name(action),
                                get_trigger_name(work_item->trigger));
-               ret = 0;
-               goto error_put_session;
+               goto error_unlock_session;
        }
 
-       session_lock(session);
        if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
                goto error_unlock_session;
        }
@@ -452,16 +448,16 @@ static int action_executor_stop_session_handler(
 
 error_unlock_session:
        session_unlock(session);
-error_put_session:
        session_put(session);
 error_unlock_list:
+       rcu_read_unlock();
        session_unlock_list();
 end:
        return ret;
 }
 
 static int action_executor_rotate_session_handler(
-               struct action_executor *executor,
+               struct action_executor *executor __attribute__((unused)),
                const struct action_work_item *work_item,
                struct action_work_subitem *item)
 {
@@ -486,16 +482,16 @@ static int action_executor_rotate_session_handler(
         * existed. If not, skip the action altogether.
         */
        if (!item->context.session_id.is_set) {
-               DBG("Session `%s` was not present at the moment the work item was enqueued for %s` action of trigger `%s`",
+               DBG("Session `%s` was not present at the moment the work item was enqueued for `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
                                get_trigger_name(work_item->trigger));
                lttng_action_increase_execution_failure_count(action);
-               ret = 0;
                goto end;
        }
 
        session_lock_list();
-       session = session_find_by_name(session_name);
+       rcu_read_lock();
+       session = session_find_by_id(LTTNG_OPTIONAL_GET(item->context.session_id));
        if (!session) {
                DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
@@ -504,23 +500,15 @@ static int action_executor_rotate_session_handler(
                goto error_unlock_list;
        }
 
-       /*
-        * Check if the session id is the same as when the work item was
-        * enqueued.
-        */
-       if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) {
-               DBG("Session id for session `%s` (id: %" PRIu64
-                   " is not the same that was sampled (id: %" PRIu64
-                   " at the moment the work item was enqueued for %s` action of trigger `%s`",
-                               session_name, session->id,
-                               LTTNG_OPTIONAL_GET(item->context.session_id),
+       session_lock(session);
+       if (session->destroyed) {
+               DBG("Session `%s` with id = %" PRIu64 " is flagged as destroyed. Skipping: action = `%s`, trigger = `%s`",
+                               session->name, session->id,
                                get_action_name(action),
                                get_trigger_name(work_item->trigger));
-               ret = 0;
-               goto error_put_session;
+               goto error_unlock_session;
        }
 
-       session_lock(session);
        if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
                goto error_unlock_session;
        }
@@ -552,16 +540,16 @@ static int action_executor_rotate_session_handler(
 
 error_unlock_session:
        session_unlock(session);
-error_put_session:
        session_put(session);
 error_unlock_list:
+       rcu_read_unlock();
        session_unlock_list();
 end:
        return ret;
 }
 
 static int action_executor_snapshot_session_handler(
-               struct action_executor *executor,
+               struct action_executor *executor __attribute__((unused)),
                const struct action_work_item *work_item,
                struct action_work_subitem *item)
 {
@@ -582,11 +570,10 @@ static int action_executor_snapshot_session_handler(
         * existed. If not, skip the action altogether.
         */
        if (!item->context.session_id.is_set) {
-               DBG("Session was not present at the moment the work item was enqueued for %s` action of trigger `%s`",
+               DBG("Session was not present at the moment the work item was enqueued for `%s` action of trigger `%s`",
                                get_action_name(action),
                                get_trigger_name(work_item->trigger));
                lttng_action_increase_execution_failure_count(action);
-               ret = 0;
                goto end;
        }
 
@@ -610,7 +597,8 @@ static int action_executor_snapshot_session_handler(
        }
 
        session_lock_list();
-       session = session_find_by_name(session_name);
+       rcu_read_lock();
+       session = session_find_by_id(LTTNG_OPTIONAL_GET(item->context.session_id));
        if (!session) {
                DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
@@ -619,23 +607,15 @@ static int action_executor_snapshot_session_handler(
                goto error_unlock_list;
        }
 
-       /*
-        * Check if the session id is the same as when the work item was
-        * enqueued.
-        */
-       if (session->id != LTTNG_OPTIONAL_GET(item->context.session_id)) {
-               DBG("Session id for session `%s` (id: %" PRIu64
-                   " is not the same that was sampled (id: %" PRIu64
-                   " at the moment the work item was enqueued for %s` action of trigger `%s`",
-                               session_name, session->id,
-                               LTTNG_OPTIONAL_GET(item->context.session_id),
+       session_lock(session);
+       if (session->destroyed) {
+               DBG("Session `%s` with id = %" PRIu64 " is flagged as destroyed. Skipping: action = `%s`, trigger = `%s`",
+                               session->name, session->id,
                                get_action_name(action),
                                get_trigger_name(work_item->trigger));
-               ret = 0;
-               goto error_put_session;
+               goto error_unlock_session;
        }
 
-       session_lock(session);
        if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
                goto error_unlock_session;
        }
@@ -656,17 +636,18 @@ static int action_executor_snapshot_session_handler(
 
 error_unlock_session:
        session_unlock(session);
-error_put_session:
        session_put(session);
 error_unlock_list:
+       rcu_read_unlock();
        session_unlock_list();
 end:
        return ret;
 }
 
-static int action_executor_list_handler(struct action_executor *executor,
-               const struct action_work_item *work_item,
-               struct action_work_subitem *item)
+static int action_executor_list_handler(
+               struct action_executor *executor __attribute__((unused)),
+               const struct action_work_item *work_item __attribute__((unused)),
+               struct action_work_subitem *item __attribute__((unused)))
 {
        ERR("Execution of a list action by the action executor should never occur");
        abort();
@@ -791,7 +772,7 @@ static void *action_executor_thread(void *_data)
                                        work_item->trigger, &trigger_owner_uid);
                        LTTNG_ASSERT(trigger_status == LTTNG_TRIGGER_STATUS_OK);
 
-                       DBG("Work item skipped since the associated trigger is no longer registered: work item id = %" PRIu64 ", trigger name = '%s', trigger owner uid = %d",
+                       DBG("Work item skipped since the associated trigger is no longer registered: work item id = %" PRIu64 ", trigger name = `%s`, trigger owner uid = %d",
                                        work_item->id, trigger_name,
                                        (int) trigger_owner_uid);
                        ret = 0;
@@ -851,7 +832,7 @@ static void clean_up_action_executor_thread(void *_data)
 struct action_executor *action_executor_create(
                struct notification_thread_handle *handle)
 {
-       struct action_executor *executor = (action_executor *) zmalloc(sizeof(*executor));
+       struct action_executor *executor = zmalloc<action_executor>();
 
        if (!executor) {
                goto end;
@@ -911,6 +892,7 @@ enum action_executor_status action_executor_enqueue_trigger(
        bool signal = false;
 
        LTTNG_ASSERT(trigger);
+       ASSERT_RCU_READ_LOCKED();
 
        pthread_mutex_lock(&executor->work.lock);
        /* Check for queue overflow. */
@@ -922,9 +904,9 @@ enum action_executor_status action_executor_enqueue_trigger(
                goto error_unlock;
        }
 
-       work_item = (action_work_item *) zmalloc(sizeof(*work_item));
+       work_item = zmalloc<action_work_item>();
        if (!work_item) {
-               PERROR("Failed to allocate action executor work item: trigger name = '%s'",
+               PERROR("Failed to allocate action executor work item: trigger name = `%s`",
                                get_trigger_name(trigger));
                executor_status = ACTION_EXECUTOR_STATUS_ERROR;
                goto error_unlock;
This page took 0.029215 seconds and 4 git commands to generate.