Silence warnings on GCC 4.8 with -Wmaybe-uninitialized
[lttng-tools.git] / src / bin / lttng-sessiond / action-executor.c
index d1369375d6f70d71244059040f64a1a6456db438..14ac8103b1fe0c7738314c7ac962ca538234cb29 100644 (file)
 #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/group.h>
+#include <lttng/action/list-internal.h>
+#include <lttng/action/list.h>
+#include <lttng/action/notify-internal.h>
 #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/lttng-error.h>
 #include <lttng/trigger/trigger-internal.h>
 #include <pthread.h>
 #define THREAD_NAME "Action Executor"
 #define MAX_QUEUED_WORK_COUNT 8192
 
+/*
+ * A work item is composed of a dynamic array of sub-items which
+ * represent a flattened, and augmented, version of a trigger's actions.
+ *
+ * We cannot rely solely on the trigger's actions since each action can have an
+ * execution context we need to comply with.
+ *
+ * The notion of execution context is required since for some actions the
+ * associated object are referenced by name and not by id. This can lead to
+ * a number of ambiguities when executing an action work item.
+ *
+ * For example, let's take a simple trigger such as:
+ *   - condition: ust event a
+ *   - action: start session S
+ *
+ * At time T, session S exists.
+ * At T + 1, the event A is hit.
+ * At T + 2, the tracer event notification is received and the work item is
+ * queued. Here session S have an id of 1.
+ * At T + 3, the session S is destroyed and a new session S is created, with a
+ * resulting id of 200.
+ * At T +4, the work item is popped from the queue and begin execution and will
+ * start session S with an id of 200 instead of the session S id 1 that was
+ * present at the queuing phase.
+ *
+ * The context to be respected is the one when the work item is queued. If the
+ * execution context is not the same at the moment of execution, we skip the
+ * execution of that sub-item.
+ *
+ * It is the same policy in regards to the validity of the associated
+ * 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;
+
+       /*
+        * The actions to be executed with their respective execution context.
+        * See struct `action_work_subitem`.
+        */
+       struct lttng_dynamic_array subitems;
+
+       /* Execution context data */
        struct lttng_trigger *trigger;
        struct lttng_evaluation *evaluation;
        struct notification_client_list *client_list;
@@ -41,6 +87,14 @@ struct action_work_item {
        struct cds_list_head list_node;
 };
 
+struct action_work_subitem {
+       struct lttng_action *action;
+       struct {
+               /* Used by actions targeting a session. */
+               LTTNG_OPTIONAL(uint64_t) session_id;
+       } context;
+};
+
 struct action_executor {
        struct lttng_thread *thread;
        struct notification_thread_handle *notification_thread_handle;
@@ -60,29 +114,33 @@ struct action_executor {
  */
 typedef int (*action_executor_handler)(struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *action);
+               struct action_work_subitem *item);
 
 static int action_executor_notify_handler(struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
-static int action_executor_start_session_handler(struct action_executor *executor,
+               struct action_work_subitem *);
+static int action_executor_start_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
-static int action_executor_stop_session_handler(struct action_executor *executor,
+               struct action_work_subitem *);
+static int action_executor_stop_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
-static int action_executor_rotate_session_handler(struct action_executor *executor,
+               struct action_work_subitem *);
+static int action_executor_rotate_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
-static int action_executor_snapshot_session_handler(struct action_executor *executor,
+               struct action_work_subitem *);
+static int action_executor_snapshot_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
-static int action_executor_group_handler(struct action_executor *executor,
+               struct action_work_subitem *);
+static int action_executor_list_handler(struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
+               struct action_work_subitem *);
 static int action_executor_generic_handler(struct action_executor *executor,
                const struct action_work_item *,
-               const struct lttng_action *);
+               struct action_work_subitem *);
 
 static const action_executor_handler action_executors[] = {
        [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler,
@@ -90,17 +148,22 @@ static const action_executor_handler action_executors[] = {
        [LTTNG_ACTION_TYPE_STOP_SESSION] = action_executor_stop_session_handler,
        [LTTNG_ACTION_TYPE_ROTATE_SESSION] = action_executor_rotate_session_handler,
        [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = action_executor_snapshot_session_handler,
-       [LTTNG_ACTION_TYPE_GROUP] = action_executor_group_handler,
+       [LTTNG_ACTION_TYPE_LIST] = action_executor_list_handler,
 };
 
-static const char *action_type_names[] = {
-       [LTTNG_ACTION_TYPE_NOTIFY] = "Notify",
-       [LTTNG_ACTION_TYPE_START_SESSION] = "Start session",
-       [LTTNG_ACTION_TYPE_STOP_SESSION] = "Stop session",
-       [LTTNG_ACTION_TYPE_ROTATE_SESSION] = "Rotate session",
-       [LTTNG_ACTION_TYPE_SNAPSHOT_SESSION] = "Snapshot session",
-       [LTTNG_ACTION_TYPE_GROUP] = "Group",
-};
+/* Forward declaration */
+static int add_action_to_subitem_array(struct lttng_action *action,
+               struct lttng_dynamic_array *subitems);
+
+static int populate_subitem_array_from_trigger(struct lttng_trigger *trigger,
+               struct lttng_dynamic_array *subitems);
+
+static void action_work_subitem_destructor(void *element)
+{
+       struct action_work_subitem *subitem = element;
+
+       lttng_action_put(subitem->action);
+}
 
 static const char *get_action_name(const struct lttng_action *action)
 {
@@ -108,7 +171,7 @@ static const char *get_action_name(const struct lttng_action *action)
 
        assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
 
-       return action_type_names[action_type];
+       return lttng_action_type_string(action_type);
 }
 
 /* Check if this trigger allowed to interect with a given session. */
@@ -137,6 +200,26 @@ static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger,
        return is_allowed;
 }
 
+static const char *get_trigger_name(const struct lttng_trigger *trigger)
+{
+       const char *trigger_name;
+       enum lttng_trigger_status trigger_status;
+
+       trigger_status = lttng_trigger_get_name(trigger, &trigger_name);
+       switch (trigger_status) {
+       case LTTNG_TRIGGER_STATUS_OK:
+               break;
+       case LTTNG_TRIGGER_STATUS_UNSET:
+               trigger_name = "(anonymous)";
+               break;
+       default:
+               trigger_name = "(failed to get name)";
+               break;
+       }
+
+       return trigger_name;
+}
+
 static int client_handle_transmission_status(
                struct notification_client *client,
                enum client_transmission_status status,
@@ -181,26 +264,28 @@ end:
 
 static int action_executor_notify_handler(struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
        return notification_client_list_send_evaluation(work_item->client_list,
-                       lttng_trigger_get_const_condition(work_item->trigger),
+                       work_item->trigger,
                        work_item->evaluation,
-                       lttng_trigger_get_credentials(work_item->trigger),
-                       LTTNG_OPTIONAL_GET_PTR(work_item->object_creds),
-                       client_handle_transmission_status,
-                       executor);
+                       work_item->object_creds.is_set ?
+                                       &(work_item->object_creds.value) :
+                                       NULL,
+                       client_handle_transmission_status, executor);
 }
 
-static int action_executor_start_session_handler(struct action_executor *executor,
+static int action_executor_start_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
        int ret = 0;
        const char *session_name;
        enum lttng_action_status action_status;
        struct ltt_session *session;
        enum lttng_error_code cmd_ret;
+       struct lttng_action *action = item->action;
 
        action_status = lttng_action_start_session_get_session_name(
                        action, &session_name);
@@ -211,12 +296,41 @@ static int action_executor_start_session_handler(struct action_executor *executo
                goto end;
        }
 
+       /*
+        * Validate if at the moment of the action was queued the session
+        * 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`",
+                               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);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               get_trigger_name(work_item->trigger));
+               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),
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+               ret = 0;
                goto error_unlock_list;
        }
 
@@ -228,17 +342,18 @@ static int action_executor_start_session_handler(struct action_executor *executo
        cmd_ret = cmd_start_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully started session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               DBG("Successfully started session `%s` on behalf of trigger `%s`",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        case LTTNG_ERR_TRACE_ALREADY_STARTED:
-               DBG("Attempted to start session `%s` on behalf of trigger `%p` but it was already started",
-                               session_name, work_item->trigger);
+               DBG("Attempted to start session `%s` on behalf of trigger `%s` but it was already started",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        default:
-               WARN("Failed to start session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               WARN("Failed to start session `%s` on behalf of trigger `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
                                lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
                break;
        }
 
@@ -251,15 +366,17 @@ end:
        return ret;
 }
 
-static int action_executor_stop_session_handler(struct action_executor *executor,
+static int action_executor_stop_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
        int ret = 0;
        const char *session_name;
        enum lttng_action_status action_status;
        struct ltt_session *session;
        enum lttng_error_code cmd_ret;
+       struct lttng_action *action = item->action;
 
        action_status = lttng_action_stop_session_get_session_name(
                        action, &session_name);
@@ -270,12 +387,42 @@ static int action_executor_stop_session_handler(struct action_executor *executor
                goto end;
        }
 
+       /*
+        * Validate if, at the moment the action was queued, the target session
+        * 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`",
+                               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);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               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),
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+               ret = 0;
                goto error_unlock_list;
        }
 
@@ -287,17 +434,18 @@ static int action_executor_stop_session_handler(struct action_executor *executor
        cmd_ret = cmd_stop_trace(session);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully stopped session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               DBG("Successfully stopped session `%s` on behalf of trigger `%s`",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        case LTTNG_ERR_TRACE_ALREADY_STOPPED:
-               DBG("Attempted to stop session `%s` on behalf of trigger `%p` but it was already stopped",
-                               session_name, work_item->trigger);
+               DBG("Attempted to stop session `%s` on behalf of trigger `%s` but it was already stopped",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        default:
-               WARN("Failed to stop session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               WARN("Failed to stop session `%s` on behalf of trigger `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
                                lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
                break;
        }
 
@@ -310,15 +458,17 @@ end:
        return ret;
 }
 
-static int action_executor_rotate_session_handler(struct action_executor *executor,
+static int action_executor_rotate_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
        int ret = 0;
        const char *session_name;
        enum lttng_action_status action_status;
        struct ltt_session *session;
        enum lttng_error_code cmd_ret;
+       struct lttng_action *action = item->action;
 
        action_status = lttng_action_rotate_session_get_session_name(
                        action, &session_name);
@@ -329,12 +479,42 @@ static int action_executor_rotate_session_handler(struct action_executor *execut
                goto end;
        }
 
+       /*
+        * Validate if, at the moment the action was queued, the target session
+        * 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`",
+                               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);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               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),
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+               ret = 0;
                goto error_unlock_list;
        }
 
@@ -347,22 +527,24 @@ static int action_executor_rotate_session_handler(struct action_executor *execut
                        LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully started rotation of session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               DBG("Successfully started rotation of session `%s` on behalf of trigger `%s`",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        case LTTNG_ERR_ROTATION_PENDING:
-               DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation is already ongoing",
-                               session_name, work_item->trigger);
+               DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation is already ongoing",
+                               session_name, get_trigger_name(work_item->trigger));
+               lttng_action_increase_execution_failure_count(action);
                break;
        case LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP:
        case LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR:
-               DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%p` but a rotation has already been completed since the last stop or clear",
-                               session_name, work_item->trigger);
+               DBG("Attempted to start a rotation of session `%s` on behalf of trigger `%s` but a rotation has already been completed since the last stop or clear",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        default:
-               WARN("Failed to start a rotation of session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               WARN("Failed to start a rotation of session `%s` on behalf of trigger `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
                                lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
                break;
        }
 
@@ -375,9 +557,10 @@ end:
        return ret;
 }
 
-static int action_executor_snapshot_session_handler(struct action_executor *executor,
+static int action_executor_snapshot_session_handler(
+               struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
        int ret = 0;
        const char *session_name;
@@ -389,6 +572,20 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec
        const struct lttng_snapshot_output *snapshot_output =
                        &default_snapshot_output;
        enum lttng_error_code cmd_ret;
+       struct lttng_action *action = item->action;
+
+       /*
+        * Validate if, at the moment the action was queued, the target session
+        * 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`",
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+               lttng_action_increase_execution_failure_count(action);
+               ret = 0;
+               goto end;
+       }
 
        action_status = lttng_action_snapshot_session_get_session_name(
                        action, &session_name);
@@ -412,12 +609,28 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec
        session_lock_list();
        session = session_find_by_name(session_name);
        if (!session) {
-               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%p`",
+               DBG("Failed to find session `%s` by name while executing `%s` action of trigger `%s`",
                                session_name, get_action_name(action),
-                   work_item->trigger);
+                               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),
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger));
+               ret = 0;
+               goto error_unlock_list;
+       }
 
        session_lock(session);
        if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
@@ -427,13 +640,14 @@ static int action_executor_snapshot_session_handler(struct action_executor *exec
        cmd_ret = cmd_snapshot_record(session, snapshot_output, 0);
        switch (cmd_ret) {
        case LTTNG_OK:
-               DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%p`",
-                               session_name, work_item->trigger);
+               DBG("Successfully recorded snapshot of session `%s` on behalf of trigger `%s`",
+                               session_name, get_trigger_name(work_item->trigger));
                break;
        default:
-               WARN("Failed to record snapshot of session `%s` on behalf of trigger `%p`: %s",
-                               session_name, work_item->trigger,
+               WARN("Failed to record snapshot of session `%s` on behalf of trigger `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
                                lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
                break;
        }
 
@@ -446,69 +660,67 @@ end:
        return ret;
 }
 
-static int action_executor_group_handler(struct action_executor *executor,
+static int action_executor_list_handler(struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action_group)
+               struct action_work_subitem *item)
 {
-       int ret = 0;
-       unsigned int i, count;
-       enum lttng_action_status action_status;
-
-       action_status = lttng_action_group_get_count(action_group, &count);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               /* Fatal error. */
-               ERR("Failed to get count of action in action group");
-               ret = -1;
-               goto end;
-       }
-
-       DBG("Action group has %u action%s", count, count != 1 ? "s" : "");
-       for (i = 0; i < count; i++) {
-               const struct lttng_action *action =
-                               lttng_action_group_get_at_index(
-                                               action_group, i);
-
-               ret = action_executor_generic_handler(
-                               executor, work_item, action);
-               if (ret) {
-                       ERR("Stopping the execution of the action group of trigger `%p` following a fatal error",
-                                       work_item->trigger);
-                       goto end;
-               }
-       }
-end:
-       return ret;
+       ERR("Execution of a list action by the action executor should never occur");
+       abort();
 }
 
 static int action_executor_generic_handler(struct action_executor *executor,
                const struct action_work_item *work_item,
-               const struct lttng_action *action)
+               struct action_work_subitem *item)
 {
+       int ret;
+       struct lttng_action *action = item->action;
        const enum lttng_action_type action_type = lttng_action_get_type(action);
 
        assert(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
 
-       DBG("Executing action `%s` of trigger `%p` action work item %" PRIu64,
+       lttng_action_increase_execution_request_count(action);
+       if (!lttng_action_should_execute(action)) {
+               DBG("Policy prevented execution of action `%s` of trigger `%s` action work item %" PRIu64,
+                               get_action_name(action),
+                               get_trigger_name(work_item->trigger),
+                               work_item->id);
+               ret = 0;
+               goto end;
+       }
+
+       lttng_action_increase_execution_count(action);
+       DBG("Executing action `%s` of trigger `%s` action work item %" PRIu64,
                        get_action_name(action),
-                       work_item->trigger,
+                       get_trigger_name(work_item->trigger),
                        work_item->id);
-
-       return action_executors[action_type](
-                       executor, work_item, action);
+       ret = action_executors[action_type](executor, work_item, item);
+end:
+       return ret;
 }
 
 static int action_work_item_execute(struct action_executor *executor,
                struct action_work_item *work_item)
 {
        int ret;
-       const struct lttng_action *action =
-                       lttng_trigger_get_const_action(work_item->trigger);
-
-       DBG("Starting execution of action work item %" PRIu64 " of trigger `%p`",
-                       work_item->id, work_item->trigger);
-       ret = action_executor_generic_handler(executor, work_item, action);
-       DBG("Completed execution of action work item %" PRIu64 " of trigger `%p`",
-                       work_item->id, work_item->trigger);
+       size_t count, i;
+
+       DBG("Starting execution of action work item %" PRIu64 " of trigger `%s`",
+                       work_item->id, get_trigger_name(work_item->trigger));
+
+       count = lttng_dynamic_array_get_count(&work_item->subitems);
+       for (i = 0; i < count; i++) {
+               struct action_work_subitem *item;
+
+               item = lttng_dynamic_array_get_element(&work_item->subitems, i);
+               ret = action_executor_generic_handler(
+                               executor, work_item, item);
+               if (ret) {
+                       goto end;
+               }
+       }
+end:
+       DBG("Completed execution of action work item %" PRIu64 " of trigger `%s`",
+                       work_item->id, get_trigger_name(work_item->trigger));
        return ret;
 }
 
@@ -517,6 +729,7 @@ static void action_work_item_destroy(struct action_work_item *work_item)
        lttng_trigger_put(work_item->trigger);
        lttng_evaluation_destroy(work_item->evaluation);
        notification_client_list_put(work_item->client_list);
+       lttng_dynamic_array_reset(&work_item->subitems);
        free(work_item);
 }
 
@@ -526,7 +739,8 @@ static void *action_executor_thread(void *_data)
 
        assert(executor);
 
-       health_register(health_sessiond, HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
+       health_register(the_health_sessiond,
+                       HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
 
        rcu_register_thread();
        rcu_thread_online();
@@ -534,7 +748,7 @@ static void *action_executor_thread(void *_data)
        DBG("Entering work execution loop");
        pthread_mutex_lock(&executor->work.lock);
        while (!executor->should_quit) {
-               int ret;
+               int ret = 0;
                struct action_work_item *work_item;
 
                health_code_update();
@@ -559,7 +773,31 @@ static void *action_executor_thread(void *_data)
                 * allowing new items to be queued.
                 */
                pthread_mutex_unlock(&executor->work.lock);
+
+               /* Execute item only if a trigger is registered. */
+               lttng_trigger_lock(work_item->trigger);
+               if (!lttng_trigger_is_registered(work_item->trigger)) {
+                       const char *trigger_name = NULL;
+                       uid_t trigger_owner_uid;
+                       enum lttng_trigger_status trigger_status;
+
+                       trigger_name = get_trigger_name(work_item->trigger);
+
+                       trigger_status = lttng_trigger_get_owner_uid(
+                                       work_item->trigger, &trigger_owner_uid);
+                       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",
+                                       work_item->id, trigger_name,
+                                       (int) trigger_owner_uid);
+                       ret = 0;
+                       goto skip_execute;
+               }
+
                ret = action_work_item_execute(executor, work_item);
+
+       skip_execute:
+               lttng_trigger_unlock(work_item->trigger);
                action_work_item_destroy(work_item);
                if (ret) {
                        /* Fatal error. */
@@ -579,7 +817,7 @@ static void *action_executor_thread(void *_data)
 
        rcu_thread_offline();
        rcu_unregister_thread();
-       health_unregister(health_sessiond);
+       health_unregister(the_health_sessiond);
 
        return NULL;
 }
@@ -588,8 +826,10 @@ static bool shutdown_action_executor_thread(void *_data)
 {
        struct action_executor *executor = _data;
 
+       pthread_mutex_lock(&executor->work.lock);
        executor->should_quit = true;
        pthread_cond_signal(&executor->work.cond);
+       pthread_mutex_unlock(&executor->work.lock);
        return true;
 }
 
@@ -643,8 +883,8 @@ void action_executor_destroy(struct action_executor *executor)
        cds_list_for_each_entry_safe (
                        work_item, tmp, &executor->work.list, list_node) {
                WARN("Discarding action work item %" PRIu64
-                               " associated to trigger `%p`",
-                               work_item->id, work_item->trigger);
+                               " associated to trigger `%s`",
+                               work_item->id, get_trigger_name(work_item->trigger));
                cds_list_del(&work_item->list_node);
                action_work_item_destroy(work_item);
        }
@@ -653,32 +893,35 @@ void action_executor_destroy(struct action_executor *executor)
 }
 
 /* RCU read-lock must be held by the caller. */
-enum action_executor_status action_executor_enqueue(
+enum action_executor_status action_executor_enqueue_trigger(
                struct action_executor *executor,
                struct lttng_trigger *trigger,
                struct lttng_evaluation *evaluation,
                const struct lttng_credentials *object_creds,
                struct notification_client_list *client_list)
 {
+       int ret;
        enum action_executor_status executor_status = ACTION_EXECUTOR_STATUS_OK;
        const uint64_t work_item_id = executor->next_work_item_id++;
        struct action_work_item *work_item;
        bool signal = false;
 
+       assert(trigger);
+
        pthread_mutex_lock(&executor->work.lock);
        /* Check for queue overflow. */
        if (executor->work.pending_count >= MAX_QUEUED_WORK_COUNT) {
                /* Most likely spammy, remove if it is the case. */
-               DBG("Refusing to enqueue action for trigger `%p` as work item %" PRIu64
-                       " (overflow)", trigger, work_item_id);
+               DBG("Refusing to enqueue action for trigger (overflow): trigger name = `%s`, work item id = %" PRIu64,
+                               get_trigger_name(trigger), work_item_id);
                executor_status = ACTION_EXECUTOR_STATUS_OVERFLOW;
                goto error_unlock;
        }
 
        work_item = zmalloc(sizeof(*work_item));
        if (!work_item) {
-               PERROR("Failed to allocate action executor work item on behalf of trigger `%p`",
-                               trigger);
+               PERROR("Failed to allocate action executor work item: trigger name = '%s'",
+                               get_trigger_name(trigger));
                executor_status = ACTION_EXECUTOR_STATUS_ERROR;
                goto error_unlock;
        }
@@ -706,18 +949,165 @@ enum action_executor_status action_executor_enqueue(
        };
 
        evaluation = NULL;
+
+       /* Build the array of action work subitems for the passed trigger. */
+       lttng_dynamic_array_init(&work_item->subitems,
+                       sizeof(struct action_work_subitem),
+                       action_work_subitem_destructor);
+
+       ret = populate_subitem_array_from_trigger(
+                       trigger, &work_item->subitems);
+       if (ret) {
+               ERR("Failed to populate work item sub items on behalf of trigger: trigger name = `%s`",
+                               get_trigger_name(trigger));
+               executor_status = ACTION_EXECUTOR_STATUS_ERROR;
+               goto error_unlock;
+       }
+
        cds_list_add_tail(&work_item->list_node, &executor->work.list);
        executor->work.pending_count++;
-       DBG("Enqueued action for trigger `%p` as work item %" PRIu64,
-                       trigger, work_item_id);
+       DBG("Enqueued action for trigger: trigger name = `%s`, work item id = %" PRIu64,
+                       get_trigger_name(trigger), work_item_id);
        signal = true;
 
 error_unlock:
-       pthread_mutex_unlock(&executor->work.lock);
        if (signal) {
                pthread_cond_signal(&executor->work.cond);
        }
 
+       pthread_mutex_unlock(&executor->work.lock);
        lttng_evaluation_destroy(evaluation);
        return executor_status;
 }
+
+static int add_action_to_subitem_array(struct lttng_action *action,
+               struct lttng_dynamic_array *subitems)
+{
+       int ret = 0;
+       enum lttng_action_type type = lttng_action_get_type(action);
+       const char *session_name = NULL;
+       enum lttng_action_status status;
+       struct action_work_subitem subitem = {
+               .action = NULL,
+               .context = {
+                       .session_id = LTTNG_OPTIONAL_INIT_UNSET,
+               },
+       };
+
+       assert(action);
+       assert(subitems);
+
+       if (type == LTTNG_ACTION_TYPE_LIST) {
+               unsigned int count, i;
+
+               status = lttng_action_list_get_count(action, &count);
+               assert(status == LTTNG_ACTION_STATUS_OK);
+
+               for (i = 0; i < count; i++) {
+                       struct lttng_action *inner_action = NULL;
+
+                       inner_action = lttng_action_list_borrow_mutable_at_index(
+                                       action, i);
+                       assert(inner_action);
+                       ret = add_action_to_subitem_array(
+                                       inner_action, subitems);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               /*
+                * Go directly to the end since there is no need to add the
+                * list action by itself to the subitems array.
+                */
+               goto end;
+       }
+
+       /* Gather execution context. */
+       switch (type) {
+       case LTTNG_ACTION_TYPE_NOTIFY:
+               break;
+       case LTTNG_ACTION_TYPE_START_SESSION:
+               status = lttng_action_start_session_get_session_name(
+                               action, &session_name);
+               assert(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               status = lttng_action_stop_session_get_session_name(
+                               action, &session_name);
+               assert(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               status = lttng_action_rotate_session_get_session_name(
+                               action, &session_name);
+               assert(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               status = lttng_action_snapshot_session_get_session_name(
+                               action, &session_name);
+               assert(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_LIST:
+       case LTTNG_ACTION_TYPE_UNKNOWN:
+               /* Fallthrough */
+       default:
+               abort();
+               break;
+       }
+
+       /*
+        * Fetch the session execution context info as needed.
+        * Note that we could decide to not add an action for which we know the
+        * execution will not happen (i.e no session exists for that name). For
+        * now we leave the decision to skip to the action executor for sake of
+        * simplicity and consistency.
+        */
+       if (session_name != NULL) {
+               uint64_t session_id;
+
+               /*
+                * Instantaneous sampling of the session id if present.
+                *
+                * This method is preferred over `sessiond_find_by_name` then
+                * fetching the session'd id since `sessiond_find_by_name`
+                * requires the session list lock to be taken.
+                *
+                * Taking the session list lock can lead to a deadlock
+                * between the action executor and the notification thread
+                * (caller of add_action_to_subitem_array). It is okay if the
+                * session state changes between the enqueuing time and the
+                * execution time. The execution context is validated at
+                * execution time.
+                */
+               if (sample_session_id_by_name(session_name, &session_id)) {
+                       LTTNG_OPTIONAL_SET(&subitem.context.session_id,
+                                       session_id);
+               }
+       }
+
+       /* Get a reference to the action. */
+       lttng_action_get(action);
+       subitem.action = action;
+
+       ret = lttng_dynamic_array_add_element(subitems, &subitem);
+       if (ret) {
+               ERR("Failed to add work subitem to the subitem array");
+               lttng_action_put(action);
+               ret = -1;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static int populate_subitem_array_from_trigger(struct lttng_trigger *trigger,
+               struct lttng_dynamic_array *subitems)
+{
+       struct lttng_action *action;
+
+       action = lttng_trigger_get_action(trigger);
+       assert(action);
+
+       return add_action_to_subitem_array(action, subitems);
+}
This page took 0.036129 seconds and 4 git commands to generate.