bin: compile lttng-sessiond as C++
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 6 Oct 2021 16:14:41 +0000 (12:14 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 17 Nov 2021 23:26:59 +0000 (18:26 -0500)
Same as commit 48a400056134 ("bin: compile lttng as C++"), but change
lttng-sessiond to be a C++ program. In addition to the categories of
changes already mentioned in that commit's message, here are some
interesting changes:

 - Add an include in trigger.h, an exported header, to fix:

      CXX      notification-thread.lo
    In file included from /home/simark/src/lttng-tools/src/bin/lttng-sessiond/notification-thread.cpp:9:
    /home/simark/src/lttng-tools/include/lttng/trigger/trigger.h:142:13: error: use of enum ‘lttng_error_code’ without previous declaration
      142 | extern enum lttng_error_code lttng_register_trigger_with_name(
          |             ^~~~~~~~~~~~~~~~

 - We get this with clang:

      CXX      lttng-conf.o
    In file included from /home/simark/src/lttng-tools/src/bin/lttng/conf.cpp:18:
    In file included from /home/simark/src/lttng-tools/src/common/common.h:14:
    In file included from /home/simark/src/lttng-tools/src/common/runas.h:17:
    In file included from /home/simark/src/lttng-tools/src/common/sessiond-comm/sessiond-comm.h:38:
    In file included from /home/simark/src/lttng-tools/src/common/unix.h:17:
    /home/simark/src/lttng-tools/src/common/payload-view.h:82:27: error: 'lttng_payload_view_from_payload' has C-linkage specified, but returns user-defined type 'struct lttng_payload_view' which is incompatible with C [-Werror,-Wreturn-type-c-linkage]
    struct lttng_payload_view lttng_payload_view_from_payload(
                              ^

    Turns out that because of the "const" field in lttng_payload_view,
    clang doesn't consider that type incompatible with C. I don't
    really want to remove the "const" for C code using that API, so
    conditionally remove it if we are compiling with clang in C++.

 - clang gives:

      CXX      event.lo
    In file included from /home/simark/src/lttng-tools/src/bin/lttng-sessiond/event.cpp:19:
    /home/simark/src/lttng-tools/src/common/bytecode/bytecode.h:50:1: error: struct has size 0 in C, size 1 in C++ [-Werror,-Wextern-c-compat]
    struct literal_string {
    ^

   It looks like that type isn't even used?  Remove it.

 - it's not possible to initialize some union members, for example with
   lttcomm_consumer_msg, in consumer.cpp. Initialize it in a separate
   statement.

 - It's not possible to use the transparent union trick when calling
   urcu function, for example in thread_application_registration, in
   register.cpp. We need to instantiate a cds_wfcq_head_ptr_t object,
   assign the appropriate field, and pass that object to the function.

 - the ALIGNED_CONST_PTR trick does not work in C++:

      CXX      consumer.lo
    In file included from /home/simark/src/lttng-tools/src/common/error.h:19,
                     from /home/simark/src/lttng-tools/src/common/common.h:12,
                     from /home/simark/src/lttng-tools/src/bin/lttng-sessiond/consumer.cpp:19:
    /home/simark/src/lttng-tools/src/bin/lttng-sessiond/consumer.cpp: In function ‘int consumer_send_relayd_socket(consumer_socket*, lttcomm_relayd_sock*, consumer_output*, lttng_stream_type, uint64_t, const char*, const char*, const char*, int, const uint64_t*, time_t, bool)’:
    /home/simark/src/lttng-tools/src/common/macros.h:116:58: error: expected primary-expression before ‘]’ token
      116 | #define ALIGNED_CONST_PTR(value) (((const typeof(value) []) { value }))
          |                                                          ^
    /home/simark/src/lttng-tools/src/bin/lttng-sessiond/consumer.cpp:1192:48: note: in expansion of macro ‘ALIGNED_CONST_PTR’
     1192 |         ret = consumer_send_fds(consumer_sock, ALIGNED_CONST_PTR(rsock->sock.fd), 1);
          |                                                ^~~~~~~~~~~~~~~~~

   Replace uses with copying the data in a local variable (which is
   properly aligned), and pass the address to that variable to the
   function.

 - In consumer.h, an array field in a structure is defined using
   the max macro. It can't be replaced with std::max, since std::max
   isn't constexpr in C++11. Define a max_constexpr function locally
   and use it.

 - g++ 7 doesn't support non-trivial designated initializers, leading to
   errors like:

         CXX      globals.lo
      /home/smarchi/src/lttng-tools/src/bin/lttng-sessiond/globals.cpp:44:1: sorry, unimplemented: non-trivial designated initializers not supported
      };
      ^

   Change consumer_data to have a constructor instead. Change
   initializations of some structures, such as lttcomm_lttng_msg, to
   initialize the fields separate from the variable declaration. This
   requires making these variable non-const which is not ideal. But
   once everything is C++, these types could get a fancy constructor,
   and then they can be made const again.

 - When compiling without UST support the stub versions of functions
   ust_app_rotate_session & co, in ust-app.h, are used. Some of them
   have the return type "enum lttng_error_code", but return 0, an invalid
   value, causing:

        CXX      main.o
      In file included from /home/smarchi/src/lttng-tools/src/bin/lttng-sessiond/lttng-sessiond.h:22:0,
                       from /home/smarchi/src/lttng-tools/src/bin/lttng-sessiond/main.cpp:45:
      /home/smarchi/src/lttng-tools/src/bin/lttng-sessiond/ust-app.h: In function ‘lttng_error_code ust_app_snapshot_record(ltt_ust_session*, const consumer_output*, int, uint64_t)’:
      /home/smarchi/src/lttng-tools/src/bin/lttng-sessiond/ust-app.h:575:9: error: invalid conversion from ‘int’ to ‘lttng_error_code’ [-fpermissive]
        return 0;
               ^

   Change these functions to return LTTNG_ERR_UNK. These functions are
   not supposed to be called if UST support is not included. But even
   if they were: all their callers check that the return value is not
   LTTNG_OK. The value 0 would be considered an error, so will be
   LTTNG_ERR_UNK.

Change-Id: I2cdd34459a54b1943087b43843ef20b35b7bf7d8
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
167 files changed:
include/lttng/action/action-internal.h
include/lttng/action/list-internal.h
include/lttng/condition/buffer-usage-internal.h
include/lttng/condition/event-rule-matches-internal.h
include/lttng/condition/session-consumed-size-internal.h
include/lttng/condition/session-rotation-internal.h
include/lttng/error-query-internal.h
include/lttng/event-internal.h
include/lttng/health-internal.h
include/lttng/location-internal.h
include/lttng/log-level-rule-internal.h
include/lttng/notification/notification-internal.h
include/lttng/session-descriptor-internal.h
include/lttng/trigger/trigger.h
include/lttng/userspace-probe-internal.h
src/bin/lttng-sessiond/Makefile.am
src/bin/lttng-sessiond/action-executor.c [deleted file]
src/bin/lttng-sessiond/action-executor.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/agent-thread.c [deleted file]
src/bin/lttng-sessiond/agent-thread.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/agent.c [deleted file]
src/bin/lttng-sessiond/agent.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/buffer-registry.c [deleted file]
src/bin/lttng-sessiond/buffer-registry.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/channel.c [deleted file]
src/bin/lttng-sessiond/channel.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/clear.c [deleted file]
src/bin/lttng-sessiond/clear.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/client.c [deleted file]
src/bin/lttng-sessiond/client.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/cmd.c [deleted file]
src/bin/lttng-sessiond/cmd.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/condition-internal.c [deleted file]
src/bin/lttng-sessiond/condition-internal.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/condition-internal.h
src/bin/lttng-sessiond/consumer.c [deleted file]
src/bin/lttng-sessiond/consumer.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/consumer.h
src/bin/lttng-sessiond/context.c [deleted file]
src/bin/lttng-sessiond/context.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/dispatch.c [deleted file]
src/bin/lttng-sessiond/dispatch.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/event-notifier-error-accounting.c [deleted file]
src/bin/lttng-sessiond/event-notifier-error-accounting.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/event.c [deleted file]
src/bin/lttng-sessiond/event.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/fd-limit.c [deleted file]
src/bin/lttng-sessiond/fd-limit.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/globals.c [deleted file]
src/bin/lttng-sessiond/globals.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/health.c [deleted file]
src/bin/lttng-sessiond/health.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ht-cleanup.c [deleted file]
src/bin/lttng-sessiond/ht-cleanup.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/kernel-consumer.c [deleted file]
src/bin/lttng-sessiond/kernel-consumer.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/kernel.c [deleted file]
src/bin/lttng-sessiond/kernel.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/lttng-syscall.c [deleted file]
src/bin/lttng-sessiond/lttng-syscall.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/main.c [deleted file]
src/bin/lttng-sessiond/main.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/manage-apps.c [deleted file]
src/bin/lttng-sessiond/manage-apps.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/manage-consumer.c [deleted file]
src/bin/lttng-sessiond/manage-consumer.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/manage-kernel.c [deleted file]
src/bin/lttng-sessiond/manage-kernel.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/modprobe.c [deleted file]
src/bin/lttng-sessiond/modprobe.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/notification-thread-commands.c [deleted file]
src/bin/lttng-sessiond/notification-thread-commands.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/notification-thread-events.c [deleted file]
src/bin/lttng-sessiond/notification-thread-events.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/notification-thread.c [deleted file]
src/bin/lttng-sessiond/notification-thread.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/notify-apps.c [deleted file]
src/bin/lttng-sessiond/notify-apps.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/process-utils.c [deleted file]
src/bin/lttng-sessiond/process-utils.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/register.c [deleted file]
src/bin/lttng-sessiond/register.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/rotate.c [deleted file]
src/bin/lttng-sessiond/rotate.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/rotation-thread.c [deleted file]
src/bin/lttng-sessiond/rotation-thread.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/save.c [deleted file]
src/bin/lttng-sessiond/save.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/session.c [deleted file]
src/bin/lttng-sessiond/session.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/sessiond-config.c [deleted file]
src/bin/lttng-sessiond/sessiond-config.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/snapshot.c [deleted file]
src/bin/lttng-sessiond/snapshot.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/thread-utils.c [deleted file]
src/bin/lttng-sessiond/thread-utils.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/thread.c [deleted file]
src/bin/lttng-sessiond/thread.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/timer.c [deleted file]
src/bin/lttng-sessiond/timer.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/trace-kernel.c [deleted file]
src/bin/lttng-sessiond/trace-kernel.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/trace-ust.c [deleted file]
src/bin/lttng-sessiond/trace-ust.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/tracker.c [deleted file]
src/bin/lttng-sessiond/tracker.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/trigger-error-query.c [deleted file]
src/bin/lttng-sessiond/trigger-error-query.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-app.c [deleted file]
src/bin/lttng-sessiond/ust-app.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-app.h
src/bin/lttng-sessiond/ust-consumer.c [deleted file]
src/bin/lttng-sessiond/ust-consumer.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-ctl-internal.h
src/bin/lttng-sessiond/ust-field-utils.c [deleted file]
src/bin/lttng-sessiond/ust-field-utils.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-metadata.c [deleted file]
src/bin/lttng-sessiond/ust-metadata.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-registry.c [deleted file]
src/bin/lttng-sessiond/ust-registry.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/ust-sigbus.c [deleted file]
src/bin/lttng-sessiond/ust-sigbus.cpp [new file with mode: 0644]
src/bin/lttng-sessiond/utils.c [deleted file]
src/bin/lttng-sessiond/utils.cpp [new file with mode: 0644]
src/common/buffer-view.h
src/common/bytecode/bytecode.h
src/common/compat/directory-handle.h
src/common/compat/poll.h
src/common/config/session-config.h
src/common/context.h
src/common/credentials.h
src/common/daemonize.h
src/common/defaults.h
src/common/dynamic-buffer.h
src/common/fd-handle.h
src/common/filter.h
src/common/futex.h
src/common/hashtable/hashtable.h
src/common/hashtable/utils.h
src/common/index-allocator.h
src/common/kernel-ctl/kernel-ctl.h
src/common/macros.h
src/common/optional.h
src/common/payload-view.h
src/common/payload.h
src/common/pipe.h
src/common/readwrite.h
src/common/relayd/relayd.h
src/common/runas.h
src/common/sessiond-comm/inet.c
src/common/sessiond-comm/inet.h
src/common/sessiond-comm/inet6.c
src/common/sessiond-comm/sessiond-comm.h
src/common/shm.h
src/common/testpoint/testpoint.h
src/common/trace-chunk.h
src/common/unix.h
src/common/uuid.h
src/common/waiter.h
tests/unit/Makefile.am
tests/unit/test_kernel_data.c [deleted file]
tests/unit/test_kernel_data.cpp [new file with mode: 0644]
tests/unit/test_session.c [deleted file]
tests/unit/test_session.cpp [new file with mode: 0644]
tests/unit/test_ust_data.c [deleted file]
tests/unit/test_ust_data.cpp [new file with mode: 0644]
tests/utils/tap/tap.h

index 71270a835cfd95a9d2d93141ef1d0d6c4542c66e..b55a6d59bc2dcde627f517b51436c163afe3d911 100644 (file)
 #include <sys/types.h>
 #include <urcu/ref.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_rate_policy;
 struct mi_writer;
 struct mi_lttng_error_query_callbacks;
@@ -128,4 +132,8 @@ enum lttng_error_code lttng_action_mi_serialize(const struct lttng_trigger *trig
                                *error_query_callbacks,
                struct lttng_dynamic_array *action_path_indexes);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_ACTION_INTERNAL_H */
index 231755fd6795b55d88eb2562115526aabb97ab51..3d7a5ba60f03d9b604a726347a1500259f0b166d 100644 (file)
 
 #include <common/macros.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_action;
 struct lttng_payload_view;
 struct mi_writer;
@@ -39,4 +43,8 @@ enum lttng_error_code lttng_action_list_mi_serialize(const struct lttng_trigger
                                *error_query_callbacks,
                struct lttng_dynamic_array *action_path_indexes);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_ACTION_LIST_INTERNAL_H */
index 1e6d5a509c40b8c518dc90e634c0cc76322dd0bc..6ea19e065f7e3fec613e60f4ce7f38fd1ebfdedb 100644 (file)
 #include "common/buffer-view.h"
 #include <common/macros.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_condition_buffer_usage {
        struct lttng_condition parent;
        struct {
@@ -77,4 +81,8 @@ ssize_t lttng_evaluation_buffer_usage_high_create_from_payload(
                struct lttng_payload_view *view,
                struct lttng_evaluation **evaluation);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_CONDITION_BUFFER_USAGE_INTERNAL_H */
index ef39f4f04f86b2a57d2e15a75c5609cc796495d8..4aac9f991e3517df7eac0395ce77dd19c2e63108 100644 (file)
 #include <common/dynamic-array.h>
 #include <lttng/event-field-value.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_capture_descriptor {
        struct lttng_event_expr *event_expression;
        struct lttng_bytecode *bytecode;
@@ -86,4 +90,8 @@ const struct lttng_bytecode *
 lttng_condition_event_rule_matches_get_capture_bytecode_at_index(
                const struct lttng_condition *condition, unsigned int index);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_CONDITION_EVENT_RULE_MATCHES_INTERNAL_H */
index 9340a5f233f6cb5566b0defac1edc57c2878cb70..07c5953e1d014965bd933ba6bbd821b918b095c8 100644 (file)
 #include <common/buffer-view.h>
 #include <common/macros.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_payload;
 struct lttng_payload_view;
 
@@ -53,4 +57,8 @@ ssize_t lttng_evaluation_session_consumed_size_create_from_payload(
                struct lttng_payload_view *view,
                struct lttng_evaluation **evaluation);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_CONDITION_SESSION_CONSUMED_SIZE_INTERNAL_H */
index c723c6d6a74638ac22452b5db71204ddc8574b15..e5993e9609b44f7a27712dcc1d0cc862c11d9077 100644 (file)
 #include <lttng/location.h>
 #include <common/macros.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_condition_session_rotation {
        struct lttng_condition parent;
        char *session_name;
@@ -61,4 +65,8 @@ ssize_t lttng_evaluation_session_rotation_completed_create_from_payload(
                struct lttng_payload_view *view,
                struct lttng_evaluation **evaluation);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_CONDITION_SESSION_ROTATION_INTERNAL_H */
index 07e2280d7486a283ebab703435f61a03cfd5338f..d3072f54b378a7ae862cccb0e4b15dcb449e4bd9 100644 (file)
 #include <common/payload.h>
 #include <common/payload-view.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct mi_writer;
 
 enum lttng_error_query_target_type {
@@ -78,4 +82,8 @@ enum lttng_error_code lttng_error_query_results_mi_serialize(
                const struct lttng_error_query_results *results,
                struct mi_writer *writer);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_ERROR_QUERY_INTERNAL_H */
index 8d03fbdec5680c10fd861b02dfe92bac8e5fd379..7370d0510730eb77d4f039bd62c828c911c5c47c 100644 (file)
 #include <common/macros.h>
 #include <lttng/event.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_userspace_probe_location;
 
 struct lttng_event_extended {
@@ -35,4 +39,8 @@ struct lttng_event_extended {
 
 struct lttng_event *lttng_event_copy(const struct lttng_event *event);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_EVENT_INTERNAL_H */
index ceda0a8dd5b027321c514885ea6d4124e717b710..875cc960b329a015de2af81962a4c4a6fba2563a 100644 (file)
 #include <lttng/health.h>
 #include <common/macros.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /*
  * These are the value added to the current state depending of the position in
  * the thread where is either waiting on a poll() or running in the code.
@@ -116,4 +120,8 @@ int health_check_state(struct health_app *ha, int type);
 void health_register(struct health_app *ha, int type);
 void health_unregister(struct health_app *ha);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* HEALTH_INTERNAL_H */
index 33498c90d5c0482b6663fdff5cb6ea68f547aa12..9eafd471a2d5273cb4aa2273cfc304ce8f9f0b02 100644 (file)
 #include <sys/types.h>
 #include <urcu/ref.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /*
  * The public API assumes that trace archive locations are always
  * provided as "constant". This means that the user of liblttng-ctl never
@@ -97,4 +101,8 @@ void lttng_trace_archive_location_get(
 void lttng_trace_archive_location_put(
                struct lttng_trace_archive_location *location);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_LOCATION_INTERNAL_H */
index c17e590b7264fa26db4c8fc584d3a393e59975eb..b02afafe0d8d563c86bfeca1c192df1c42a5d029 100644 (file)
 #include <lttng/event.h>
 #include <lttng/log-level-rule.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct mi_writer;
 
 /*
@@ -60,4 +64,8 @@ enum lttng_error_code lttng_log_level_rule_mi_serialize(
                const struct lttng_log_level_rule *rule,
                struct mi_writer *writer);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_LOG_LEVEL_RULE_INTERNAL_H */
index 7e601b0c7de82d5340b8686c932bff42e6b5b63d..1d337e07db6b18e62f2ab2bd58a89c3f40a351af 100644 (file)
 #include <stdbool.h>
 #include <sys/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_payload;
 struct lttng_payload_view;
 
@@ -40,4 +44,8 @@ ssize_t lttng_notification_create_from_payload(
                struct lttng_payload_view *view,
                struct lttng_notification **notification);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_NOTIFICATION_INTERNAL_H */
index f438d4812ff66a643d365626a383d2eae40eb789..97628bb881c4c2f2a21491f81216323af7006108 100644 (file)
 #include <common/buffer-view.h>
 #include <stdbool.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* Note that these enums are used as part of the lttnctl protocol. */
 enum lttng_session_descriptor_type {
        LTTNG_SESSION_DESCRIPTOR_TYPE_UNKNOWN = -1,
@@ -81,4 +85,8 @@ int lttng_session_descriptor_assign(
                struct lttng_session_descriptor *dst_descriptor,
                const struct lttng_session_descriptor *src_descriptor);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_SESSION_DESCRIPTOR_INTERNAL_H */
index c4899f936540ee970164a6091dd3b397e9b28974..842203ace0f145f17ecbee0389762671acbeea83 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/types.h>
 #include <lttng/constant.h>
 #include <inttypes.h>
+#include <lttng/lttng-error.h>
 
 struct lttng_action;
 struct lttng_condition;
index 5e960c106db1b853224d12c4da59f8c6f1695e18..fe96a4ea3b2b926fac04b232d562f68c6b641e6f 100644 (file)
 #include <common/fd-handle.h>
 #include <stdbool.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 struct lttng_payload;
 struct lttng_payload_view;
 struct lttng_dynamic_buffer;
@@ -159,4 +163,8 @@ enum lttng_error_code lttng_userspace_probe_location_mi_serialize(
                const struct lttng_userspace_probe_location *location,
                struct mi_writer *writer);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* LTTNG_USERSPACE_PROBE_INTERNAL_H */
index 11fb85e0b0968d69feca5682258ae18a5a002393..2de44a492d427564650fd0d5c4ec8cf181e88519 100644 (file)
@@ -9,62 +9,62 @@ endif
 
 noinst_LTLIBRARIES = liblttng-sessiond-common.la
 
-liblttng_sessiond_common_la_SOURCES = utils.c utils.h \
-                       trace-kernel.c trace-kernel.h \
-                       kernel.c kernel.h \
+liblttng_sessiond_common_la_SOURCES = utils.cpp utils.h \
+                       trace-kernel.cpp trace-kernel.h \
+                       kernel.cpp kernel.h \
                        ust-app.h ust-sigbus.h trace-ust.h notify-apps.h \
                        lttng-ust-ctl.h lttng-ust-abi.h lttng-ust-error.h \
                        ust-ctl-internal.h ust-abi-internal.h ust-error-internal.h \
                        ust-registry.h \
-                       condition-internal.c condition-internal.h \
-                       context.c context.h \
-                       channel.c channel.h \
-                       event.c event.h \
-                       consumer.c consumer.h \
-                       session.c session.h \
-                       modprobe.c modprobe.h kern-modules.h \
-                       fd-limit.c fd-limit.h \
-                       kernel-consumer.c kernel-consumer.h \
+                       condition-internal.cpp condition-internal.h \
+                       context.cpp context.h \
+                       channel.cpp channel.h \
+                       event.cpp event.h \
+                       consumer.cpp consumer.h \
+                       session.cpp session.h \
+                       modprobe.cpp modprobe.h kern-modules.h \
+                       fd-limit.cpp fd-limit.h \
+                       kernel-consumer.cpp kernel-consumer.h \
                        consumer.h \
                        health-sessiond.h \
-                       cmd.c cmd.h \
-                       buffer-registry.c buffer-registry.h \
-                       testpoint.h ht-cleanup.c ht-cleanup.h \
-                       snapshot.c snapshot.h \
-                       agent.c agent.h \
-                       save.h save.c \
-                       lttng-syscall.h lttng-syscall.c \
-                       notification-thread.h notification-thread.c \
+                       cmd.cpp cmd.h \
+                       buffer-registry.cpp buffer-registry.h \
+                       testpoint.h ht-cleanup.cpp ht-cleanup.h \
+                       snapshot.cpp snapshot.h \
+                       agent.cpp agent.h \
+                       save.h save.cpp \
+                       lttng-syscall.h lttng-syscall.cpp \
+                       notification-thread.h notification-thread.cpp \
                        notification-thread-internal.h \
-                       notification-thread-commands.h notification-thread-commands.c \
-                       notification-thread-events.h notification-thread-events.c \
-                       sessiond-config.h sessiond-config.c \
-                       rotate.h rotate.c \
-                       rotation-thread.h rotation-thread.c \
-                       timer.c timer.h \
-                       globals.c \
-                       thread-utils.c \
-                       process-utils.c \
-                       thread.c thread.h \
-                       health.c \
-                       client.c client.h \
-                       dispatch.c dispatch.h \
-                       register.c register.h \
-                       manage-apps.c manage-apps.h \
-                       manage-kernel.c manage-kernel.h \
-                       manage-consumer.c manage-consumer.h \
-                       clear.c clear.h \
-                       tracker.c tracker.h \
-                       event-notifier-error-accounting.c event-notifier-error-accounting.h \
-                       action-executor.c action-executor.h\
-                       trigger-error-query.c
+                       notification-thread-commands.h notification-thread-commands.cpp \
+                       notification-thread-events.h notification-thread-events.cpp \
+                       sessiond-config.h sessiond-config.cpp \
+                       rotate.h rotate.cpp \
+                       rotation-thread.h rotation-thread.cpp \
+                       timer.cpp timer.h \
+                       globals.cpp \
+                       thread-utils.cpp \
+                       process-utils.cpp \
+                       thread.cpp thread.h \
+                       health.cpp \
+                       client.cpp client.h \
+                       dispatch.cpp dispatch.h \
+                       register.cpp register.h \
+                       manage-apps.cpp manage-apps.h \
+                       manage-kernel.cpp manage-kernel.h \
+                       manage-consumer.cpp manage-consumer.h \
+                       clear.cpp clear.h \
+                       tracker.cpp tracker.h \
+                       event-notifier-error-accounting.cpp event-notifier-error-accounting.h \
+                       action-executor.cpp action-executor.h\
+                       trigger-error-query.cpp
 
 if HAVE_LIBLTTNG_UST_CTL
-liblttng_sessiond_common_la_SOURCES += trace-ust.c ust-registry.c ust-app.c \
-                       ust-consumer.c ust-consumer.h notify-apps.c \
-                       ust-metadata.c ust-clock.h agent-thread.c agent-thread.h \
-                       ust-field-utils.h ust-field-utils.c \
-                       ust-sigbus.c
+liblttng_sessiond_common_la_SOURCES += trace-ust.cpp ust-registry.cpp ust-app.cpp \
+                       ust-consumer.cpp ust-consumer.h notify-apps.cpp \
+                       ust-metadata.cpp ust-clock.h agent-thread.cpp agent-thread.h \
+                       ust-field-utils.h ust-field-utils.cpp \
+                       ust-sigbus.cpp
 endif
 
 # link on liblttngctl for check if sessiond is already alive.
@@ -88,7 +88,7 @@ endif
 
 bin_PROGRAMS = lttng-sessiond
 
-lttng_sessiond_SOURCES = lttng-sessiond.h main.c
+lttng_sessiond_SOURCES = lttng-sessiond.h main.cpp
 
 lttng_sessiond_LDFLAGS = -rdynamic
 
diff --git a/src/bin/lttng-sessiond/action-executor.c b/src/bin/lttng-sessiond/action-executor.c
deleted file mode 100644 (file)
index 94b819c..0000000
+++ /dev/null
@@ -1,1113 +0,0 @@
-/*
- * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#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 <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>
-#include <stdbool.h>
-#include <stddef.h>
-#include <urcu/list.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;
-       LTTNG_OPTIONAL(struct lttng_credentials) object_creds;
-       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;
-       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
- * executor.
- */
-typedef int (*action_executor_handler)(struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *item);
-
-static int action_executor_notify_handler(struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_start_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_stop_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_rotate_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_snapshot_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_list_handler(struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-static int action_executor_generic_handler(struct action_executor *executor,
-               const struct action_work_item *,
-               struct action_work_subitem *);
-
-static const action_executor_handler action_executors[] = {
-       [LTTNG_ACTION_TYPE_NOTIFY] = action_executor_notify_handler,
-       [LTTNG_ACTION_TYPE_START_SESSION] = action_executor_start_session_handler,
-       [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_LIST] = action_executor_list_handler,
-};
-
-/* 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)
-{
-       const enum lttng_action_type action_type = lttng_action_get_type(action);
-
-       LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
-
-       return lttng_action_type_string(action_type);
-}
-
-/* Check if this trigger allowed to interect with a given session. */
-static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger,
-               struct ltt_session *session)
-{
-       bool is_allowed = false;
-       const struct lttng_credentials session_creds = {
-               .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid),
-               .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid),
-       };
-       /* Can never be NULL. */
-       const struct lttng_credentials *trigger_creds =
-                       lttng_trigger_get_credentials(trigger);
-
-       is_allowed = (lttng_credentials_is_equal_uid(trigger_creds, &session_creds)) ||
-                       (lttng_credentials_get_uid(trigger_creds) == 0);
-       if (!is_allowed) {
-               WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld",
-                               session->name,
-                               (long int) session->uid,
-                               (long int) session->gid,
-                               (long int) lttng_credentials_get_uid(trigger_creds));
-       }
-
-       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,
-               void *user_data)
-{
-       int ret = 0;
-       struct action_executor *executor = user_data;
-       bool update_communication = true;
-
-       switch (status) {
-       case CLIENT_TRANSMISSION_STATUS_COMPLETE:
-               DBG("Successfully sent full notification to client, client_id = %" PRIu64,
-                               client->id);
-               update_communication = false;
-               break;
-       case CLIENT_TRANSMISSION_STATUS_QUEUED:
-               DBG("Queued notification in client outgoing buffer, client_id = %" PRIu64,
-                               client->id);
-               break;
-       case CLIENT_TRANSMISSION_STATUS_FAIL:
-               DBG("Communication error occurred while sending notification to client, client_id = %" PRIu64,
-                               client->id);
-               break;
-       default:
-               ERR("Fatal error encoutered while sending notification to client, client_id = %" PRIu64,
-                               client->id);
-               ret = -1;
-               goto end;
-       }
-
-       if (!update_communication) {
-               goto end;
-       }
-
-       /* Safe to read client's id without locking as it is immutable. */
-       ret = notification_thread_client_communication_update(
-                       executor->notification_thread_handle, client->id,
-                       status);
-end:
-       return ret;
-}
-
-static int action_executor_notify_handler(struct action_executor *executor,
-               const struct action_work_item *work_item,
-               struct action_work_subitem *item)
-{
-       return notification_client_list_send_evaluation(work_item->client_list,
-                       work_item->trigger,
-                       work_item->evaluation,
-                       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,
-               const struct action_work_item *work_item,
-               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);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               ERR("Failed to get session name from `%s` action",
-                               get_action_name(action));
-               ret = -1;
-               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 `%s`",
-                               session_name, get_action_name(action),
-                               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;
-       }
-
-       session_lock(session);
-       if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
-               goto error_dispose_session;
-       }
-
-       cmd_ret = cmd_start_trace(session);
-       switch (cmd_ret) {
-       case LTTNG_OK:
-               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 `%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 `%s`: %s",
-                               session_name, get_trigger_name(work_item->trigger),
-                               lttng_strerror(-cmd_ret));
-               lttng_action_increase_execution_failure_count(action);
-               break;
-       }
-
-error_dispose_session:
-       session_unlock(session);
-       session_put(session);
-error_unlock_list:
-       session_unlock_list();
-end:
-       return ret;
-}
-
-static int action_executor_stop_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *work_item,
-               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);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               ERR("Failed to get session name from `%s` action",
-                               get_action_name(action));
-               ret = -1;
-               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 `%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),
-                               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)) {
-               goto error_dispose_session;
-       }
-
-       cmd_ret = cmd_stop_trace(session);
-       switch (cmd_ret) {
-       case LTTNG_OK:
-               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 `%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 `%s`: %s",
-                               session_name, get_trigger_name(work_item->trigger),
-                               lttng_strerror(-cmd_ret));
-               lttng_action_increase_execution_failure_count(action);
-               break;
-       }
-
-error_dispose_session:
-       session_unlock(session);
-       session_put(session);
-error_unlock_list:
-       session_unlock_list();
-end:
-       return ret;
-}
-
-static int action_executor_rotate_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *work_item,
-               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);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               ERR("Failed to get session name from `%s` action",
-                               get_action_name(action));
-               ret = -1;
-               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 `%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),
-                               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)) {
-               goto error_dispose_session;
-       }
-
-       cmd_ret = cmd_rotate_session(session, NULL, false,
-                       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 `%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 `%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 `%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 `%s`: %s",
-                               session_name, get_trigger_name(work_item->trigger),
-                               lttng_strerror(-cmd_ret));
-               lttng_action_increase_execution_failure_count(action);
-               break;
-       }
-
-error_dispose_session:
-       session_unlock(session);
-       session_put(session);
-error_unlock_list:
-       session_unlock_list();
-end:
-       return ret;
-}
-
-static int action_executor_snapshot_session_handler(
-               struct action_executor *executor,
-               const struct action_work_item *work_item,
-               struct action_work_subitem *item)
-{
-       int ret = 0;
-       const char *session_name;
-       enum lttng_action_status action_status;
-       struct ltt_session *session;
-       const struct lttng_snapshot_output default_snapshot_output = {
-               .max_size = UINT64_MAX,
-       };
-       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);
-       if (action_status != LTTNG_ACTION_STATUS_OK) {
-               ERR("Failed to get session name from `%s` action",
-                               get_action_name(action));
-               ret = -1;
-               goto end;
-       }
-
-       action_status = lttng_action_snapshot_session_get_output(
-                       action, &snapshot_output);
-       if (action_status != LTTNG_ACTION_STATUS_OK &&
-                       action_status != LTTNG_ACTION_STATUS_UNSET) {
-               ERR("Failed to get output from `%s` action",
-                               get_action_name(action));
-               ret = -1;
-               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 `%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),
-                               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)) {
-               goto error_dispose_session;
-       }
-
-       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 `%s`",
-                               session_name, get_trigger_name(work_item->trigger));
-               break;
-       default:
-               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;
-       }
-
-error_dispose_session:
-       session_unlock(session);
-       session_put(session);
-error_unlock_list:
-       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)
-{
-       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,
-               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);
-
-       LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
-
-       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),
-                       get_trigger_name(work_item->trigger),
-                       work_item->id);
-       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;
-       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;
-}
-
-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);
-}
-
-static void *action_executor_thread(void *_data)
-{
-       struct action_executor *executor = _data;
-
-       LTTNG_ASSERT(executor);
-
-       health_register(the_health_sessiond,
-                       HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
-
-       rcu_register_thread();
-       rcu_thread_online();
-
-       DBG("Entering work execution loop");
-       pthread_mutex_lock(&executor->work.lock);
-       while (!executor->should_quit) {
-               int ret = 0;
-               struct action_work_item *work_item;
-
-               health_code_update();
-               if (executor->work.pending_count == 0) {
-                       health_poll_entry();
-                       DBG("No work items enqueued, entering wait");
-                       pthread_cond_wait(&executor->work.cond,
-                                       &executor->work.lock);
-                       DBG("Woke-up from wait");
-                       health_poll_exit();
-                       continue;
-               }
-
-               /* Pop item from front of the list with work lock held. */
-               work_item = cds_list_first_entry(&executor->work.list,
-                               struct action_work_item, list_node);
-               cds_list_del(&work_item->list_node);
-               executor->work.pending_count--;
-
-               /*
-                * Work can be performed without holding the work lock,
-                * 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);
-                       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",
-                                       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. */
-                       break;
-               }
-
-               health_code_update();
-               pthread_mutex_lock(&executor->work.lock);
-       }
-
-       if (executor->should_quit) {
-               pthread_mutex_unlock(&executor->work.lock);
-       }
-       DBG("Left work execution loop");
-
-       health_code_update();
-
-       rcu_thread_offline();
-       rcu_unregister_thread();
-       health_unregister(the_health_sessiond);
-
-       return NULL;
-}
-
-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;
-}
-
-static void clean_up_action_executor_thread(void *_data)
-{
-       struct action_executor *executor = _data;
-
-       LTTNG_ASSERT(cds_list_empty(&executor->work.list));
-
-       pthread_mutex_destroy(&executor->work.lock);
-       pthread_cond_destroy(&executor->work.cond);
-       free(executor);
-}
-
-struct action_executor *action_executor_create(
-               struct notification_thread_handle *handle)
-{
-       struct action_executor *executor = zmalloc(sizeof(*executor));
-
-       if (!executor) {
-               goto end;
-       }
-
-       CDS_INIT_LIST_HEAD(&executor->work.list);
-       pthread_cond_init(&executor->work.cond, NULL);
-       pthread_mutex_init(&executor->work.lock, NULL);
-       executor->notification_thread_handle = handle;
-
-       executor->thread = lttng_thread_create(THREAD_NAME,
-                       action_executor_thread, shutdown_action_executor_thread,
-                       clean_up_action_executor_thread, executor);
-end:
-       return executor;
-}
-
-void action_executor_destroy(struct action_executor *executor)
-{
-       struct action_work_item *work_item, *tmp;
-
-       /* TODO Wait for work list to drain? */
-       lttng_thread_shutdown(executor->thread);
-       pthread_mutex_lock(&executor->work.lock);
-       if (executor->work.pending_count != 0) {
-               WARN("%" PRIu64
-                       " trigger action%s still queued for execution and will be discarded",
-                               executor->work.pending_count,
-                               executor->work.pending_count == 1 ? " is" :
-                                                                   "s are");
-       }
-
-       cds_list_for_each_entry_safe (
-                       work_item, tmp, &executor->work.list, list_node) {
-               WARN("Discarding action work item %" PRIu64
-                               " 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);
-       }
-       pthread_mutex_unlock(&executor->work.lock);
-       lttng_thread_put(executor->thread);
-}
-
-/* RCU read-lock must be held by the caller. */
-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;
-
-       LTTNG_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 (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: trigger name = '%s'",
-                               get_trigger_name(trigger));
-               executor_status = ACTION_EXECUTOR_STATUS_ERROR;
-               goto error_unlock;
-       }
-
-       lttng_trigger_get(trigger);
-       if (client_list) {
-               const bool reference_acquired =
-                               notification_client_list_get(client_list);
-
-               LTTNG_ASSERT(reference_acquired);
-       }
-
-       *work_item = (typeof(*work_item)){
-                       .id = work_item_id,
-                       .trigger = trigger,
-                       /* Ownership transferred to the work item. */
-                       .evaluation = evaluation,
-                       .object_creds = {
-                               .is_set = !!object_creds,
-                               .value = object_creds ? *object_creds :
-                                       (typeof(work_item->object_creds.value)) {},
-                       },
-                       .client_list = client_list,
-                       .list_node = CDS_LIST_HEAD_INIT(work_item->list_node),
-       };
-
-       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: trigger name = `%s`, work item id = %" PRIu64,
-                       get_trigger_name(trigger), work_item_id);
-       signal = true;
-
-error_unlock:
-       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,
-               },
-       };
-
-       LTTNG_ASSERT(action);
-       LTTNG_ASSERT(subitems);
-
-       if (type == LTTNG_ACTION_TYPE_LIST) {
-               unsigned int count, i;
-
-               status = lttng_action_list_get_count(action, &count);
-               LTTNG_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);
-                       LTTNG_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);
-               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-               break;
-       case LTTNG_ACTION_TYPE_STOP_SESSION:
-               status = lttng_action_stop_session_get_session_name(
-                               action, &session_name);
-               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-               break;
-       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
-               status = lttng_action_rotate_session_get_session_name(
-                               action, &session_name);
-               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
-               break;
-       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
-               status = lttng_action_snapshot_session_get_session_name(
-                               action, &session_name);
-               LTTNG_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);
-       LTTNG_ASSERT(action);
-
-       return add_action_to_subitem_array(action, subitems);
-}
diff --git a/src/bin/lttng-sessiond/action-executor.cpp b/src/bin/lttng-sessiond/action-executor.cpp
new file mode 100644 (file)
index 0000000..cc7834f
--- /dev/null
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#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 <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>
+#include <stdbool.h>
+#include <stddef.h>
+#include <urcu/list.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;
+       LTTNG_OPTIONAL(struct lttng_credentials) object_creds;
+       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;
+       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
+ * executor.
+ */
+typedef int (*action_executor_handler)(struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *item);
+
+static int action_executor_notify_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_start_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_stop_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_rotate_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_snapshot_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_list_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+static int action_executor_generic_handler(struct action_executor *executor,
+               const struct action_work_item *,
+               struct action_work_subitem *);
+
+static const action_executor_handler action_executors[] = {
+       action_executor_notify_handler,
+       action_executor_start_session_handler,
+       action_executor_stop_session_handler,
+       action_executor_rotate_session_handler,
+       action_executor_snapshot_session_handler,
+       action_executor_list_handler,
+};
+
+/* 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 = (action_work_subitem *) element;
+
+       lttng_action_put(subitem->action);
+}
+
+static const char *get_action_name(const struct lttng_action *action)
+{
+       const enum lttng_action_type action_type = lttng_action_get_type(action);
+
+       LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
+
+       return lttng_action_type_string(action_type);
+}
+
+/* Check if this trigger allowed to interect with a given session. */
+static bool is_trigger_allowed_for_session(const struct lttng_trigger *trigger,
+               struct ltt_session *session)
+{
+       bool is_allowed = false;
+       const struct lttng_credentials session_creds = {
+               .uid = LTTNG_OPTIONAL_INIT_VALUE(session->uid),
+               .gid = LTTNG_OPTIONAL_INIT_VALUE(session->gid),
+       };
+       /* Can never be NULL. */
+       const struct lttng_credentials *trigger_creds =
+                       lttng_trigger_get_credentials(trigger);
+
+       is_allowed = (lttng_credentials_is_equal_uid(trigger_creds, &session_creds)) ||
+                       (lttng_credentials_get_uid(trigger_creds) == 0);
+       if (!is_allowed) {
+               WARN("Trigger is not allowed to interact with session `%s`: session uid = %ld, session gid = %ld, trigger uid = %ld",
+                               session->name,
+                               (long int) session->uid,
+                               (long int) session->gid,
+                               (long int) lttng_credentials_get_uid(trigger_creds));
+       }
+
+       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,
+               void *user_data)
+{
+       int ret = 0;
+       struct action_executor *executor = (action_executor *) user_data;
+       bool update_communication = true;
+
+       switch (status) {
+       case CLIENT_TRANSMISSION_STATUS_COMPLETE:
+               DBG("Successfully sent full notification to client, client_id = %" PRIu64,
+                               client->id);
+               update_communication = false;
+               break;
+       case CLIENT_TRANSMISSION_STATUS_QUEUED:
+               DBG("Queued notification in client outgoing buffer, client_id = %" PRIu64,
+                               client->id);
+               break;
+       case CLIENT_TRANSMISSION_STATUS_FAIL:
+               DBG("Communication error occurred while sending notification to client, client_id = %" PRIu64,
+                               client->id);
+               break;
+       default:
+               ERR("Fatal error encoutered while sending notification to client, client_id = %" PRIu64,
+                               client->id);
+               ret = -1;
+               goto end;
+       }
+
+       if (!update_communication) {
+               goto end;
+       }
+
+       /* Safe to read client's id without locking as it is immutable. */
+       ret = notification_thread_client_communication_update(
+                       executor->notification_thread_handle, client->id,
+                       status);
+end:
+       return ret;
+}
+
+static int action_executor_notify_handler(struct action_executor *executor,
+               const struct action_work_item *work_item,
+               struct action_work_subitem *item)
+{
+       return notification_client_list_send_evaluation(work_item->client_list,
+                       work_item->trigger,
+                       work_item->evaluation,
+                       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,
+               const struct action_work_item *work_item,
+               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);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from `%s` action",
+                               get_action_name(action));
+               ret = -1;
+               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 `%s`",
+                               session_name, get_action_name(action),
+                               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;
+       }
+
+       session_lock(session);
+       if (!is_trigger_allowed_for_session(work_item->trigger, session)) {
+               goto error_dispose_session;
+       }
+
+       cmd_ret = (lttng_error_code) cmd_start_trace(session);
+       switch (cmd_ret) {
+       case LTTNG_OK:
+               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 `%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 `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
+                               lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
+               break;
+       }
+
+error_dispose_session:
+       session_unlock(session);
+       session_put(session);
+error_unlock_list:
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_stop_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *work_item,
+               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);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from `%s` action",
+                               get_action_name(action));
+               ret = -1;
+               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 `%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),
+                               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)) {
+               goto error_dispose_session;
+       }
+
+       cmd_ret = (lttng_error_code) cmd_stop_trace(session);
+       switch (cmd_ret) {
+       case LTTNG_OK:
+               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 `%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 `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
+                               lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
+               break;
+       }
+
+error_dispose_session:
+       session_unlock(session);
+       session_put(session);
+error_unlock_list:
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_rotate_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *work_item,
+               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);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from `%s` action",
+                               get_action_name(action));
+               ret = -1;
+               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 `%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),
+                               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)) {
+               goto error_dispose_session;
+       }
+
+       cmd_ret = (lttng_error_code) cmd_rotate_session(session, NULL, false,
+                       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 `%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 `%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 `%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 `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
+                               lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
+               break;
+       }
+
+error_dispose_session:
+       session_unlock(session);
+       session_put(session);
+error_unlock_list:
+       session_unlock_list();
+end:
+       return ret;
+}
+
+static int action_executor_snapshot_session_handler(
+               struct action_executor *executor,
+               const struct action_work_item *work_item,
+               struct action_work_subitem *item)
+{
+       int ret = 0;
+       const char *session_name;
+       enum lttng_action_status action_status;
+       struct ltt_session *session;
+       lttng_snapshot_output default_snapshot_output;
+       const struct lttng_snapshot_output *snapshot_output =
+                       &default_snapshot_output;
+       enum lttng_error_code cmd_ret;
+       struct lttng_action *action = item->action;
+
+       default_snapshot_output.max_size = UINT64_MAX;
+
+       /*
+        * 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);
+       if (action_status != LTTNG_ACTION_STATUS_OK) {
+               ERR("Failed to get session name from `%s` action",
+                               get_action_name(action));
+               ret = -1;
+               goto end;
+       }
+
+       action_status = lttng_action_snapshot_session_get_output(
+                       action, &snapshot_output);
+       if (action_status != LTTNG_ACTION_STATUS_OK &&
+                       action_status != LTTNG_ACTION_STATUS_UNSET) {
+               ERR("Failed to get output from `%s` action",
+                               get_action_name(action));
+               ret = -1;
+               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 `%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),
+                               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)) {
+               goto error_dispose_session;
+       }
+
+       cmd_ret = (lttng_error_code) cmd_snapshot_record(session, snapshot_output, 0);
+       switch (cmd_ret) {
+       case LTTNG_OK:
+               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 `%s`: %s",
+                               session_name, get_trigger_name(work_item->trigger),
+                               lttng_strerror(-cmd_ret));
+               lttng_action_increase_execution_failure_count(action);
+               break;
+       }
+
+error_dispose_session:
+       session_unlock(session);
+       session_put(session);
+error_unlock_list:
+       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)
+{
+       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,
+               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);
+
+       LTTNG_ASSERT(action_type != LTTNG_ACTION_TYPE_UNKNOWN);
+
+       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),
+                       get_trigger_name(work_item->trigger),
+                       work_item->id);
+       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;
+       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 = (action_work_subitem *) 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;
+}
+
+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);
+}
+
+static void *action_executor_thread(void *_data)
+{
+       struct action_executor *executor = (action_executor *) _data;
+
+       LTTNG_ASSERT(executor);
+
+       health_register(the_health_sessiond,
+                       HEALTH_SESSIOND_TYPE_ACTION_EXECUTOR);
+
+       rcu_register_thread();
+       rcu_thread_online();
+
+       DBG("Entering work execution loop");
+       pthread_mutex_lock(&executor->work.lock);
+       while (!executor->should_quit) {
+               int ret = 0;
+               struct action_work_item *work_item;
+
+               health_code_update();
+               if (executor->work.pending_count == 0) {
+                       health_poll_entry();
+                       DBG("No work items enqueued, entering wait");
+                       pthread_cond_wait(&executor->work.cond,
+                                       &executor->work.lock);
+                       DBG("Woke-up from wait");
+                       health_poll_exit();
+                       continue;
+               }
+
+               /* Pop item from front of the list with work lock held. */
+               work_item = cds_list_first_entry(&executor->work.list,
+                               struct action_work_item, list_node);
+               cds_list_del(&work_item->list_node);
+               executor->work.pending_count--;
+
+               /*
+                * Work can be performed without holding the work lock,
+                * 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);
+                       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",
+                                       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. */
+                       break;
+               }
+
+               health_code_update();
+               pthread_mutex_lock(&executor->work.lock);
+       }
+
+       if (executor->should_quit) {
+               pthread_mutex_unlock(&executor->work.lock);
+       }
+       DBG("Left work execution loop");
+
+       health_code_update();
+
+       rcu_thread_offline();
+       rcu_unregister_thread();
+       health_unregister(the_health_sessiond);
+
+       return NULL;
+}
+
+static bool shutdown_action_executor_thread(void *_data)
+{
+       struct action_executor *executor = (action_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;
+}
+
+static void clean_up_action_executor_thread(void *_data)
+{
+       struct action_executor *executor = (action_executor *) _data;
+
+       LTTNG_ASSERT(cds_list_empty(&executor->work.list));
+
+       pthread_mutex_destroy(&executor->work.lock);
+       pthread_cond_destroy(&executor->work.cond);
+       free(executor);
+}
+
+struct action_executor *action_executor_create(
+               struct notification_thread_handle *handle)
+{
+       struct action_executor *executor = (action_executor *) zmalloc(sizeof(*executor));
+
+       if (!executor) {
+               goto end;
+       }
+
+       CDS_INIT_LIST_HEAD(&executor->work.list);
+       pthread_cond_init(&executor->work.cond, NULL);
+       pthread_mutex_init(&executor->work.lock, NULL);
+       executor->notification_thread_handle = handle;
+
+       executor->thread = lttng_thread_create(THREAD_NAME,
+                       action_executor_thread, shutdown_action_executor_thread,
+                       clean_up_action_executor_thread, executor);
+end:
+       return executor;
+}
+
+void action_executor_destroy(struct action_executor *executor)
+{
+       struct action_work_item *work_item, *tmp;
+
+       /* TODO Wait for work list to drain? */
+       lttng_thread_shutdown(executor->thread);
+       pthread_mutex_lock(&executor->work.lock);
+       if (executor->work.pending_count != 0) {
+               WARN("%" PRIu64
+                       " trigger action%s still queued for execution and will be discarded",
+                               executor->work.pending_count,
+                               executor->work.pending_count == 1 ? " is" :
+                                                                   "s are");
+       }
+
+       cds_list_for_each_entry_safe (
+                       work_item, tmp, &executor->work.list, list_node) {
+               WARN("Discarding action work item %" PRIu64
+                               " 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);
+       }
+       pthread_mutex_unlock(&executor->work.lock);
+       lttng_thread_put(executor->thread);
+}
+
+/* RCU read-lock must be held by the caller. */
+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;
+
+       LTTNG_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 (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 = (action_work_item *) zmalloc(sizeof(*work_item));
+       if (!work_item) {
+               PERROR("Failed to allocate action executor work item: trigger name = '%s'",
+                               get_trigger_name(trigger));
+               executor_status = ACTION_EXECUTOR_STATUS_ERROR;
+               goto error_unlock;
+       }
+
+       lttng_trigger_get(trigger);
+       if (client_list) {
+               const bool reference_acquired =
+                               notification_client_list_get(client_list);
+
+               LTTNG_ASSERT(reference_acquired);
+       }
+
+       work_item->id = work_item_id;
+       work_item->trigger = trigger;
+
+       /* Ownership transferred to the work item. */
+       work_item->evaluation = evaluation;
+       evaluation = NULL;
+
+       work_item->client_list = client_list;
+       work_item->object_creds.is_set = !!object_creds;
+       if (object_creds) {
+               work_item->object_creds.value = *object_creds;
+       }
+
+       CDS_INIT_LIST_HEAD(&work_item->list_node);
+
+       /* 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: trigger name = `%s`, work item id = %" PRIu64,
+                       get_trigger_name(trigger), work_item_id);
+       signal = true;
+
+error_unlock:
+       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,
+               },
+       };
+
+       LTTNG_ASSERT(action);
+       LTTNG_ASSERT(subitems);
+
+       if (type == LTTNG_ACTION_TYPE_LIST) {
+               unsigned int count, i;
+
+               status = lttng_action_list_get_count(action, &count);
+               LTTNG_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);
+                       LTTNG_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);
+               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_STOP_SESSION:
+               status = lttng_action_stop_session_get_session_name(
+                               action, &session_name);
+               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_ROTATE_SESSION:
+               status = lttng_action_rotate_session_get_session_name(
+                               action, &session_name);
+               LTTNG_ASSERT(status == LTTNG_ACTION_STATUS_OK);
+               break;
+       case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION:
+               status = lttng_action_snapshot_session_get_session_name(
+                               action, &session_name);
+               LTTNG_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);
+       LTTNG_ASSERT(action);
+
+       return add_action_to_subitem_array(action, subitems);
+}
diff --git a/src/bin/lttng-sessiond/agent-thread.c b/src/bin/lttng-sessiond/agent-thread.c
deleted file mode 100644 (file)
index f2ee4c0..0000000
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-
-#include <common/common.h>
-#include <common/sessiond-comm/sessiond-comm.h>
-#include <common/uri.h>
-#include <common/utils.h>
-
-#include <common/compat/endian.h>
-
-#include "fd-limit.h"
-#include "agent-thread.h"
-#include "agent.h"
-#include "lttng-sessiond.h"
-#include "session.h"
-#include "utils.h"
-#include "thread.h"
-
-struct thread_notifiers {
-       struct lttng_pipe *quit_pipe;
-       sem_t ready;
-};
-
-struct agent_app_id {
-       pid_t pid;
-       enum lttng_domain_type domain;
-};
-
-struct agent_protocol_version {
-       unsigned int major, minor;
-};
-
-static int agent_tracing_enabled = -1;
-
-/*
- * Note that there is not port here. It's set after this URI is parsed so we
- * can let the user define a custom one. However, localhost is ALWAYS the
- * default listening address.
- */
-static const char *default_reg_uri =
-       "tcp://" DEFAULT_NETWORK_VIEWER_BIND_ADDRESS;
-
-/*
- * Update agent application using the given socket. This is done just after
- * registration was successful.
- *
- * This will acquire the various sessions' lock; none must be held by the
- * caller.
- * The caller must hold the session list lock.
- */
-static void update_agent_app(const struct agent_app *app)
-{
-       struct ltt_session *session, *stmp;
-       struct ltt_session_list *list;
-       struct agent *trigger_agent;
-       struct lttng_ht_iter iter;
-
-       list = session_get_list();
-       LTTNG_ASSERT(list);
-
-       cds_list_for_each_entry_safe(session, stmp, &list->head, list) {
-               if (!session_get(session)) {
-                       continue;
-               }
-
-               session_lock(session);
-               if (session->ust_session) {
-                       const struct agent *agt;
-
-                       rcu_read_lock();
-                       agt = trace_ust_find_agent(session->ust_session, app->domain);
-                       if (agt) {
-                               agent_update(agt, app);
-                       }
-                       rcu_read_unlock();
-               }
-               session_unlock(session);
-               session_put(session);
-       }
-
-       rcu_read_lock();
-       /*
-        * We are protected against the addition of new events by the session
-        * list lock being held.
-        */
-       cds_lfht_for_each_entry(the_trigger_agents_ht_by_domain->ht,
-                       &iter.iter, trigger_agent, node.node) {
-               agent_update(trigger_agent, app);
-       }
-       rcu_read_unlock();
-}
-
-/*
- * Create and init socket from uri.
- */
-static struct lttcomm_sock *init_tcp_socket(void)
-{
-       int ret;
-       struct lttng_uri *uri = NULL;
-       struct lttcomm_sock *sock = NULL;
-       unsigned int port;
-       bool bind_succeeded = false;
-
-       /*
-        * This should never fail since the URI is hardcoded and the port is set
-        * before this thread is launched.
-        */
-       ret = uri_parse(default_reg_uri, &uri);
-       LTTNG_ASSERT(ret);
-       LTTNG_ASSERT(the_config.agent_tcp_port.begin > 0);
-       uri->port = the_config.agent_tcp_port.begin;
-
-       sock = lttcomm_alloc_sock_from_uri(uri);
-       uri_free(uri);
-       if (sock == NULL) {
-               ERR("agent allocating TCP socket");
-               goto error;
-       }
-
-       ret = lttcomm_create_sock(sock);
-       if (ret < 0) {
-               goto error;
-       }
-
-       for (port = the_config.agent_tcp_port.begin;
-                       port <= the_config.agent_tcp_port.end; port++) {
-               ret = lttcomm_sock_set_port(sock, (uint16_t) port);
-               if (ret) {
-                       ERR("Failed to set port %u on socket",
-                                       port);
-                       goto error;
-               }
-               DBG3("Trying to bind on port %u", port);
-               ret = sock->ops->bind(sock);
-               if (!ret) {
-                       bind_succeeded = true;
-                       break;
-               }
-
-               if (errno == EADDRINUSE) {
-                       DBG("Failed to bind to port %u since it is already in use",
-                                       port);
-               } else {
-                       PERROR("Failed to bind to port %u", port);
-                       goto error;
-               }
-       }
-
-       if (!bind_succeeded) {
-               if (the_config.agent_tcp_port.begin ==
-                               the_config.agent_tcp_port.end) {
-                       WARN("Another process is already using the agent port %i. "
-                            "Agent support will be deactivated.",
-                                       the_config.agent_tcp_port.begin);
-                       goto error;
-               } else {
-                       WARN("All ports in the range [%i, %i] are already in use. "
-                            "Agent support will be deactivated.",
-                                       the_config.agent_tcp_port.begin,
-                                       the_config.agent_tcp_port.end);
-                       goto error;
-               }
-       }
-
-       ret = sock->ops->listen(sock, -1);
-       if (ret < 0) {
-               goto error;
-       }
-
-       DBG("Listening on TCP port %u and socket %d",
-                       port, sock->fd);
-
-       return sock;
-
-error:
-       if (sock) {
-               lttcomm_destroy_sock(sock);
-       }
-       return NULL;
-}
-
-/*
- * Close and destroy the given TCP socket.
- */
-static void destroy_tcp_socket(struct lttcomm_sock *sock)
-{
-       int ret;
-       uint16_t port;
-
-       LTTNG_ASSERT(sock);
-
-       ret = lttcomm_sock_get_port(sock, &port);
-       if (ret) {
-               ERR("Failed to get port of agent TCP socket");
-               port = 0;
-       }
-
-       DBG3("Destroy TCP socket on port %" PRIu16,
-                       port);
-
-       /* This will return gracefully if fd is invalid. */
-       sock->ops->close(sock);
-       lttcomm_destroy_sock(sock);
-}
-
-static const char *domain_type_str(enum lttng_domain_type domain_type)
-{
-       switch (domain_type) {
-       case LTTNG_DOMAIN_NONE:
-               return "none";
-       case LTTNG_DOMAIN_KERNEL:
-               return "kernel";
-       case LTTNG_DOMAIN_UST:
-               return "ust";
-       case LTTNG_DOMAIN_JUL:
-               return "jul";
-       case LTTNG_DOMAIN_LOG4J:
-               return "log4j";
-       case LTTNG_DOMAIN_PYTHON:
-               return "python";
-       default:
-               return "unknown";
-       }
-}
-
-static bool is_agent_protocol_version_supported(
-               const struct agent_protocol_version *version)
-{
-       const bool is_supported = version->major == AGENT_MAJOR_VERSION &&
-                       version->minor == AGENT_MINOR_VERSION;
-
-       if (!is_supported) {
-               WARN("Refusing agent connection: unsupported protocol version %ui.%ui, expected %i.%i",
-                               version->major, version->minor,
-                               AGENT_MAJOR_VERSION, AGENT_MINOR_VERSION);
-       }
-
-       return is_supported;
-}
-
-/*
- * Handle a new agent connection on the registration socket.
- *
- * Returns 0 on success, or else a negative errno value.
- * On success, the resulting socket is returned through `agent_app_socket`
- * and the application's reported id is updated through `agent_app_id`.
- */
-static int accept_agent_connection(
-               struct lttcomm_sock *reg_sock,
-               struct agent_app_id *agent_app_id,
-               struct lttcomm_sock **agent_app_socket)
-{
-       int ret;
-       struct agent_protocol_version agent_version;
-       ssize_t size;
-       struct agent_register_msg msg;
-       struct lttcomm_sock *new_sock;
-
-       LTTNG_ASSERT(reg_sock);
-
-       new_sock = reg_sock->ops->accept(reg_sock);
-       if (!new_sock) {
-               ret = -ENOTCONN;
-               goto end;
-       }
-
-       size = new_sock->ops->recvmsg(new_sock, &msg, sizeof(msg), 0);
-       if (size < sizeof(msg)) {
-               if (size < 0) {
-                       PERROR("Failed to register new agent application");
-               } else if (size != 0) {
-                       ERR("Failed to register new agent application: invalid registration message length: expected length = %zu, message length = %zd",
-                                       sizeof(msg), size);
-               } else {
-                       DBG("Failed to register new agent application: connection closed");
-               }
-               ret = -EINVAL;
-               goto error_close_socket;
-       }
-
-       agent_version = (struct agent_protocol_version) {
-               be32toh(msg.major_version),
-               be32toh(msg.minor_version),
-       };
-
-       /* Test communication protocol version of the registering agent. */
-       if (!is_agent_protocol_version_supported(&agent_version)) {
-               ret = -EINVAL;
-               goto error_close_socket;
-       }
-
-       *agent_app_id = (struct agent_app_id) {
-               .domain = (enum lttng_domain_type) be32toh(msg.domain),
-               .pid = (pid_t) be32toh(msg.pid),
-       };
-
-       DBG2("New registration for agent application: pid = %ld, domain = %s, socket fd = %d",
-                       (long) agent_app_id->pid,
-                       domain_type_str(agent_app_id->domain), new_sock->fd);
-
-       *agent_app_socket = new_sock;
-       new_sock = NULL;
-       ret = 0;
-       goto end;
-
-error_close_socket:
-       new_sock->ops->close(new_sock);
-       lttcomm_destroy_sock(new_sock);
-end:
-       return ret;
-}
-
-bool agent_tracing_is_enabled(void)
-{
-       int enabled;
-
-       enabled = uatomic_read(&agent_tracing_enabled);
-       LTTNG_ASSERT(enabled != -1);
-       return enabled == 1;
-}
-
-/*
- * Write agent TCP port using the rundir.
- */
-static int write_agent_port(uint16_t port)
-{
-       return utils_create_pid_file(
-                       (pid_t) port, the_config.agent_port_file_path.value);
-}
-
-static
-void mark_thread_as_ready(struct thread_notifiers *notifiers)
-{
-       DBG("Marking agent management thread as ready");
-       sem_post(&notifiers->ready);
-}
-
-static
-void wait_until_thread_is_ready(struct thread_notifiers *notifiers)
-{
-       DBG("Waiting for agent management thread to be ready");
-       sem_wait(&notifiers->ready);
-       DBG("Agent management thread is ready");
-}
-
-/*
- * This thread manage application notify communication.
- */
-static void *thread_agent_management(void *data)
-{
-       int i, ret, pollfd;
-       uint32_t revents, nb_fd;
-       struct lttng_poll_event events;
-       struct lttcomm_sock *reg_sock;
-       struct thread_notifiers *notifiers = data;
-       const int quit_pipe_read_fd = lttng_pipe_get_readfd(
-                       notifiers->quit_pipe);
-
-       DBG("Manage agent application registration.");
-
-       rcu_register_thread();
-       rcu_thread_online();
-
-       /* Agent initialization call MUST be called before starting the thread. */
-       LTTNG_ASSERT(the_agent_apps_ht_by_sock);
-
-       /* Create pollset with size 2, quit pipe and registration socket. */
-       ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
-       if (ret < 0) {
-               goto error_poll_create;
-       }
-
-       ret = lttng_poll_add(&events, quit_pipe_read_fd,
-                       LPOLLIN | LPOLLERR);
-       if (ret < 0) {
-               goto error_tcp_socket;
-       }
-
-       reg_sock = init_tcp_socket();
-       if (reg_sock) {
-               uint16_t port;
-
-               ret = lttcomm_sock_get_port(reg_sock, &port);
-               LTTNG_ASSERT(ret == 0);
-
-               ret = write_agent_port(port);
-               if (ret) {
-                       ERR("Failed to create agent port file: agent tracing will be unavailable");
-                       /* Don't prevent the launch of the sessiond on error. */
-                       mark_thread_as_ready(notifiers);
-                       goto error;
-               }
-       } else {
-               /* Don't prevent the launch of the sessiond on error. */
-               mark_thread_as_ready(notifiers);
-               goto error_tcp_socket;
-       }
-
-       /*
-        * Signal that the agent thread is ready. The command thread
-        * may start to query whether or not agent tracing is enabled.
-        */
-       uatomic_set(&agent_tracing_enabled, 1);
-       mark_thread_as_ready(notifiers);
-
-       /* Add TCP socket to the poll set. */
-       ret = lttng_poll_add(&events, reg_sock->fd,
-                       LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP);
-       if (ret < 0) {
-               goto error;
-       }
-
-       while (1) {
-               DBG3("Manage agent polling");
-
-               /* Inifinite blocking call, waiting for transmission */
-restart:
-               ret = lttng_poll_wait(&events, -1);
-               DBG3("Manage agent return from poll on %d fds",
-                               LTTNG_POLL_GETNB(&events));
-               if (ret < 0) {
-                       /*
-                        * Restart interrupted system call.
-                        */
-                       if (errno == EINTR) {
-                               goto restart;
-                       }
-                       goto error;
-               }
-               nb_fd = ret;
-               DBG3("%d fd ready", nb_fd);
-
-               for (i = 0; i < nb_fd; i++) {
-                       /* Fetch once the poll data */
-                       revents = LTTNG_POLL_GETEV(&events, i);
-                       pollfd = LTTNG_POLL_GETFD(&events, i);
-
-                       /* Thread quit pipe has been closed. Killing thread. */
-                       if (pollfd == quit_pipe_read_fd) {
-                               goto exit;
-                       }
-
-                       /* Activity on the registration socket. */
-                       if (revents & LPOLLIN) {
-                               struct agent_app_id new_app_id;
-                               struct agent_app *new_app = NULL;
-                               struct lttcomm_sock *new_app_socket;
-                               int new_app_socket_fd;
-
-                               LTTNG_ASSERT(pollfd == reg_sock->fd);
-
-                               ret = accept_agent_connection(
-                                       reg_sock, &new_app_id, &new_app_socket);
-                               if (ret < 0) {
-                                       /* Errors are already logged. */
-                                       continue;
-                               }
-
-                               /*
-                                * new_app_socket's ownership has been
-                                * transferred to the new agent app.
-                                */
-                               new_app = agent_create_app(new_app_id.pid,
-                                               new_app_id.domain,
-                                               new_app_socket);
-                               if (!new_app) {
-                                       new_app_socket->ops->close(
-                                                       new_app_socket);
-                                       continue;
-                               }
-                               new_app_socket_fd = new_app_socket->fd;
-                               new_app_socket = NULL;
-
-                               /*
-                                * Since this is a command socket (write then
-                                * read), only add poll error event to only
-                                * detect shutdown.
-                                */
-                               ret = lttng_poll_add(&events, new_app_socket_fd,
-                                               LPOLLERR | LPOLLHUP | LPOLLRDHUP);
-                               if (ret < 0) {
-                                       agent_destroy_app(new_app);
-                                       continue;
-                               }
-
-                               /*
-                                * Prevent sessions from being modified while
-                                * the agent application's configuration is
-                                * updated.
-                                */
-                               session_lock_list();
-
-                               /*
-                                * Update the newly registered applications's
-                                * configuration.
-                                */
-                               update_agent_app(new_app);
-
-                               ret = agent_send_registration_done(new_app);
-                               if (ret < 0) {
-                                       agent_destroy_app(new_app);
-                                       /* Removing from the poll set. */
-                                       ret = lttng_poll_del(&events,
-                                                       new_app_socket_fd);
-                                       if (ret < 0) {
-                                               session_unlock_list();
-                                               goto error;
-                                       }
-                                       continue;
-                               }
-
-                               /* Publish the new agent app. */
-                               agent_add_app(new_app);
-
-                               session_unlock_list();
-                       } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
-                               /* Removing from the poll set */
-                               ret = lttng_poll_del(&events, pollfd);
-                               if (ret < 0) {
-                                       goto error;
-                               }
-                               agent_destroy_app_by_sock(pollfd);
-                       } else {
-                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
-                               goto error;
-                       }
-               }
-       }
-
-exit:
-       /* Whatever happens, try to delete it and exit. */
-       (void) lttng_poll_del(&events, reg_sock->fd);
-error:
-       destroy_tcp_socket(reg_sock);
-error_tcp_socket:
-       lttng_poll_clean(&events);
-error_poll_create:
-       uatomic_set(&agent_tracing_enabled, 0);
-       DBG("Cleaning up and stopping.");
-       rcu_thread_offline();
-       rcu_unregister_thread();
-       return NULL;
-}
-
-static bool shutdown_agent_management_thread(void *data)
-{
-       struct thread_notifiers *notifiers = data;
-       const int write_fd = lttng_pipe_get_writefd(notifiers->quit_pipe);
-
-       return notify_thread_pipe(write_fd) == 1;
-}
-
-static void cleanup_agent_management_thread(void *data)
-{
-       struct thread_notifiers *notifiers = data;
-
-       lttng_pipe_destroy(notifiers->quit_pipe);
-       sem_destroy(&notifiers->ready);
-       free(notifiers);
-}
-
-bool launch_agent_management_thread(void)
-{
-       struct thread_notifiers *notifiers;
-       struct lttng_thread *thread;
-
-       notifiers = zmalloc(sizeof(*notifiers));
-       if (!notifiers) {
-               goto error_alloc;
-       }
-
-       sem_init(&notifiers->ready, 0, 0);
-       notifiers->quit_pipe = lttng_pipe_open(FD_CLOEXEC);
-       if (!notifiers->quit_pipe) {
-               goto error;
-       }
-       thread = lttng_thread_create("Agent management",
-                       thread_agent_management,
-                       shutdown_agent_management_thread,
-                       cleanup_agent_management_thread,
-                       notifiers);
-       if (!thread) {
-               goto error;
-       }
-       wait_until_thread_is_ready(notifiers);
-       lttng_thread_put(thread);
-       return true;
-error:
-       cleanup_agent_management_thread(notifiers);
-error_alloc:
-       return false;
-}
diff --git a/src/bin/lttng-sessiond/agent-thread.cpp b/src/bin/lttng-sessiond/agent-thread.cpp
new file mode 100644 (file)
index 0000000..5e158b4
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+
+#include <common/common.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/uri.h>
+#include <common/utils.h>
+
+#include <common/compat/endian.h>
+
+#include "fd-limit.h"
+#include "agent-thread.h"
+#include "agent.h"
+#include "lttng-sessiond.h"
+#include "session.h"
+#include "utils.h"
+#include "thread.h"
+
+struct thread_notifiers {
+       struct lttng_pipe *quit_pipe;
+       sem_t ready;
+};
+
+struct agent_app_id {
+       pid_t pid;
+       enum lttng_domain_type domain;
+};
+
+struct agent_protocol_version {
+       unsigned int major, minor;
+};
+
+static int agent_tracing_enabled = -1;
+
+/*
+ * Note that there is not port here. It's set after this URI is parsed so we
+ * can let the user define a custom one. However, localhost is ALWAYS the
+ * default listening address.
+ */
+static const char *default_reg_uri =
+       "tcp://" DEFAULT_NETWORK_VIEWER_BIND_ADDRESS;
+
+/*
+ * Update agent application using the given socket. This is done just after
+ * registration was successful.
+ *
+ * This will acquire the various sessions' lock; none must be held by the
+ * caller.
+ * The caller must hold the session list lock.
+ */
+static void update_agent_app(const struct agent_app *app)
+{
+       struct ltt_session *session, *stmp;
+       struct ltt_session_list *list;
+       struct agent *trigger_agent;
+       struct lttng_ht_iter iter;
+
+       list = session_get_list();
+       LTTNG_ASSERT(list);
+
+       cds_list_for_each_entry_safe(session, stmp, &list->head, list) {
+               if (!session_get(session)) {
+                       continue;
+               }
+
+               session_lock(session);
+               if (session->ust_session) {
+                       const struct agent *agt;
+
+                       rcu_read_lock();
+                       agt = trace_ust_find_agent(session->ust_session, app->domain);
+                       if (agt) {
+                               agent_update(agt, app);
+                       }
+                       rcu_read_unlock();
+               }
+               session_unlock(session);
+               session_put(session);
+       }
+
+       rcu_read_lock();
+       /*
+        * We are protected against the addition of new events by the session
+        * list lock being held.
+        */
+       cds_lfht_for_each_entry(the_trigger_agents_ht_by_domain->ht,
+                       &iter.iter, trigger_agent, node.node) {
+               agent_update(trigger_agent, app);
+       }
+       rcu_read_unlock();
+}
+
+/*
+ * Create and init socket from uri.
+ */
+static struct lttcomm_sock *init_tcp_socket(void)
+{
+       int ret;
+       struct lttng_uri *uri = NULL;
+       struct lttcomm_sock *sock = NULL;
+       unsigned int port;
+       bool bind_succeeded = false;
+
+       /*
+        * This should never fail since the URI is hardcoded and the port is set
+        * before this thread is launched.
+        */
+       ret = uri_parse(default_reg_uri, &uri);
+       LTTNG_ASSERT(ret);
+       LTTNG_ASSERT(the_config.agent_tcp_port.begin > 0);
+       uri->port = the_config.agent_tcp_port.begin;
+
+       sock = lttcomm_alloc_sock_from_uri(uri);
+       uri_free(uri);
+       if (sock == NULL) {
+               ERR("agent allocating TCP socket");
+               goto error;
+       }
+
+       ret = lttcomm_create_sock(sock);
+       if (ret < 0) {
+               goto error;
+       }
+
+       for (port = the_config.agent_tcp_port.begin;
+                       port <= the_config.agent_tcp_port.end; port++) {
+               ret = lttcomm_sock_set_port(sock, (uint16_t) port);
+               if (ret) {
+                       ERR("Failed to set port %u on socket",
+                                       port);
+                       goto error;
+               }
+               DBG3("Trying to bind on port %u", port);
+               ret = sock->ops->bind(sock);
+               if (!ret) {
+                       bind_succeeded = true;
+                       break;
+               }
+
+               if (errno == EADDRINUSE) {
+                       DBG("Failed to bind to port %u since it is already in use",
+                                       port);
+               } else {
+                       PERROR("Failed to bind to port %u", port);
+                       goto error;
+               }
+       }
+
+       if (!bind_succeeded) {
+               if (the_config.agent_tcp_port.begin ==
+                               the_config.agent_tcp_port.end) {
+                       WARN("Another process is already using the agent port %i. "
+                            "Agent support will be deactivated.",
+                                       the_config.agent_tcp_port.begin);
+                       goto error;
+               } else {
+                       WARN("All ports in the range [%i, %i] are already in use. "
+                            "Agent support will be deactivated.",
+                                       the_config.agent_tcp_port.begin,
+                                       the_config.agent_tcp_port.end);
+                       goto error;
+               }
+       }
+
+       ret = sock->ops->listen(sock, -1);
+       if (ret < 0) {
+               goto error;
+       }
+
+       DBG("Listening on TCP port %u and socket %d",
+                       port, sock->fd);
+
+       return sock;
+
+error:
+       if (sock) {
+               lttcomm_destroy_sock(sock);
+       }
+       return NULL;
+}
+
+/*
+ * Close and destroy the given TCP socket.
+ */
+static void destroy_tcp_socket(struct lttcomm_sock *sock)
+{
+       int ret;
+       uint16_t port;
+
+       LTTNG_ASSERT(sock);
+
+       ret = lttcomm_sock_get_port(sock, &port);
+       if (ret) {
+               ERR("Failed to get port of agent TCP socket");
+               port = 0;
+       }
+
+       DBG3("Destroy TCP socket on port %" PRIu16,
+                       port);
+
+       /* This will return gracefully if fd is invalid. */
+       sock->ops->close(sock);
+       lttcomm_destroy_sock(sock);
+}
+
+static const char *domain_type_str(enum lttng_domain_type domain_type)
+{
+       switch (domain_type) {
+       case LTTNG_DOMAIN_NONE:
+               return "none";
+       case LTTNG_DOMAIN_KERNEL:
+               return "kernel";
+       case LTTNG_DOMAIN_UST:
+               return "ust";
+       case LTTNG_DOMAIN_JUL:
+               return "jul";
+       case LTTNG_DOMAIN_LOG4J:
+               return "log4j";
+       case LTTNG_DOMAIN_PYTHON:
+               return "python";
+       default:
+               return "unknown";
+       }
+}
+
+static bool is_agent_protocol_version_supported(
+               const struct agent_protocol_version *version)
+{
+       const bool is_supported = version->major == AGENT_MAJOR_VERSION &&
+                       version->minor == AGENT_MINOR_VERSION;
+
+       if (!is_supported) {
+               WARN("Refusing agent connection: unsupported protocol version %ui.%ui, expected %i.%i",
+                               version->major, version->minor,
+                               AGENT_MAJOR_VERSION, AGENT_MINOR_VERSION);
+       }
+
+       return is_supported;
+}
+
+/*
+ * Handle a new agent connection on the registration socket.
+ *
+ * Returns 0 on success, or else a negative errno value.
+ * On success, the resulting socket is returned through `agent_app_socket`
+ * and the application's reported id is updated through `agent_app_id`.
+ */
+static int accept_agent_connection(
+               struct lttcomm_sock *reg_sock,
+               struct agent_app_id *agent_app_id,
+               struct lttcomm_sock **agent_app_socket)
+{
+       int ret;
+       struct agent_protocol_version agent_version;
+       ssize_t size;
+       struct agent_register_msg msg;
+       struct lttcomm_sock *new_sock;
+
+       LTTNG_ASSERT(reg_sock);
+
+       new_sock = reg_sock->ops->accept(reg_sock);
+       if (!new_sock) {
+               ret = -ENOTCONN;
+               goto end;
+       }
+
+       size = new_sock->ops->recvmsg(new_sock, &msg, sizeof(msg), 0);
+       if (size < sizeof(msg)) {
+               if (size < 0) {
+                       PERROR("Failed to register new agent application");
+               } else if (size != 0) {
+                       ERR("Failed to register new agent application: invalid registration message length: expected length = %zu, message length = %zd",
+                                       sizeof(msg), size);
+               } else {
+                       DBG("Failed to register new agent application: connection closed");
+               }
+               ret = -EINVAL;
+               goto error_close_socket;
+       }
+
+       agent_version = (struct agent_protocol_version) {
+               be32toh(msg.major_version),
+               be32toh(msg.minor_version),
+       };
+
+       /* Test communication protocol version of the registering agent. */
+       if (!is_agent_protocol_version_supported(&agent_version)) {
+               ret = -EINVAL;
+               goto error_close_socket;
+       }
+
+       *agent_app_id = (struct agent_app_id) {
+               .pid = (pid_t) be32toh(msg.pid),
+               .domain = (lttng_domain_type) be32toh(msg.domain),
+       };
+
+       DBG2("New registration for agent application: pid = %ld, domain = %s, socket fd = %d",
+                       (long) agent_app_id->pid,
+                       domain_type_str(agent_app_id->domain), new_sock->fd);
+
+       *agent_app_socket = new_sock;
+       new_sock = NULL;
+       ret = 0;
+       goto end;
+
+error_close_socket:
+       new_sock->ops->close(new_sock);
+       lttcomm_destroy_sock(new_sock);
+end:
+       return ret;
+}
+
+bool agent_tracing_is_enabled(void)
+{
+       int enabled;
+
+       enabled = uatomic_read(&agent_tracing_enabled);
+       LTTNG_ASSERT(enabled != -1);
+       return enabled == 1;
+}
+
+/*
+ * Write agent TCP port using the rundir.
+ */
+static int write_agent_port(uint16_t port)
+{
+       return utils_create_pid_file(
+                       (pid_t) port, the_config.agent_port_file_path.value);
+}
+
+static
+void mark_thread_as_ready(struct thread_notifiers *notifiers)
+{
+       DBG("Marking agent management thread as ready");
+       sem_post(&notifiers->ready);
+}
+
+static
+void wait_until_thread_is_ready(struct thread_notifiers *notifiers)
+{
+       DBG("Waiting for agent management thread to be ready");
+       sem_wait(&notifiers->ready);
+       DBG("Agent management thread is ready");
+}
+
+/*
+ * This thread manage application notify communication.
+ */
+static void *thread_agent_management(void *data)
+{
+       int i, ret, pollfd;
+       uint32_t revents, nb_fd;
+       struct lttng_poll_event events;
+       struct lttcomm_sock *reg_sock;
+       struct thread_notifiers *notifiers = (thread_notifiers *) data;
+       const int quit_pipe_read_fd = lttng_pipe_get_readfd(
+                       notifiers->quit_pipe);
+
+       DBG("Manage agent application registration.");
+
+       rcu_register_thread();
+       rcu_thread_online();
+
+       /* Agent initialization call MUST be called before starting the thread. */
+       LTTNG_ASSERT(the_agent_apps_ht_by_sock);
+
+       /* Create pollset with size 2, quit pipe and registration socket. */
+       ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
+       if (ret < 0) {
+               goto error_poll_create;
+       }
+
+       ret = lttng_poll_add(&events, quit_pipe_read_fd,
+                       LPOLLIN | LPOLLERR);
+       if (ret < 0) {
+               goto error_tcp_socket;
+       }
+
+       reg_sock = init_tcp_socket();
+       if (reg_sock) {
+               uint16_t port;
+
+               ret = lttcomm_sock_get_port(reg_sock, &port);
+               LTTNG_ASSERT(ret == 0);
+
+               ret = write_agent_port(port);
+               if (ret) {
+                       ERR("Failed to create agent port file: agent tracing will be unavailable");
+                       /* Don't prevent the launch of the sessiond on error. */
+                       mark_thread_as_ready(notifiers);
+                       goto error;
+               }
+       } else {
+               /* Don't prevent the launch of the sessiond on error. */
+               mark_thread_as_ready(notifiers);
+               goto error_tcp_socket;
+       }
+
+       /*
+        * Signal that the agent thread is ready. The command thread
+        * may start to query whether or not agent tracing is enabled.
+        */
+       uatomic_set(&agent_tracing_enabled, 1);
+       mark_thread_as_ready(notifiers);
+
+       /* Add TCP socket to the poll set. */
+       ret = lttng_poll_add(&events, reg_sock->fd,
+                       LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP);
+       if (ret < 0) {
+               goto error;
+       }
+
+       while (1) {
+               DBG3("Manage agent polling");
+
+               /* Inifinite blocking call, waiting for transmission */
+restart:
+               ret = lttng_poll_wait(&events, -1);
+               DBG3("Manage agent return from poll on %d fds",
+                               LTTNG_POLL_GETNB(&events));
+               if (ret < 0) {
+                       /*
+                        * Restart interrupted system call.
+                        */
+                       if (errno == EINTR) {
+                               goto restart;
+                       }
+                       goto error;
+               }
+               nb_fd = ret;
+               DBG3("%d fd ready", nb_fd);
+
+               for (i = 0; i < nb_fd; i++) {
+                       /* Fetch once the poll data */
+                       revents = LTTNG_POLL_GETEV(&events, i);
+                       pollfd = LTTNG_POLL_GETFD(&events, i);
+
+                       /* Thread quit pipe has been closed. Killing thread. */
+                       if (pollfd == quit_pipe_read_fd) {
+                               goto exit;
+                       }
+
+                       /* Activity on the registration socket. */
+                       if (revents & LPOLLIN) {
+                               struct agent_app_id new_app_id;
+                               struct agent_app *new_app = NULL;
+                               struct lttcomm_sock *new_app_socket;
+                               int new_app_socket_fd;
+
+                               LTTNG_ASSERT(pollfd == reg_sock->fd);
+
+                               ret = accept_agent_connection(
+                                       reg_sock, &new_app_id, &new_app_socket);
+                               if (ret < 0) {
+                                       /* Errors are already logged. */
+                                       continue;
+                               }
+
+                               /*
+                                * new_app_socket's ownership has been
+                                * transferred to the new agent app.
+                                */
+                               new_app = agent_create_app(new_app_id.pid,
+                                               new_app_id.domain,
+                                               new_app_socket);
+                               if (!new_app) {
+                                       new_app_socket->ops->close(
+                                                       new_app_socket);
+                                       continue;
+                               }
+                               new_app_socket_fd = new_app_socket->fd;
+                               new_app_socket = NULL;
+
+                               /*
+                                * Since this is a command socket (write then
+                                * read), only add poll error event to only
+                                * detect shutdown.
+                                */
+                               ret = lttng_poll_add(&events, new_app_socket_fd,
+                                               LPOLLERR | LPOLLHUP | LPOLLRDHUP);
+                               if (ret < 0) {
+                                       agent_destroy_app(new_app);
+                                       continue;
+                               }
+
+                               /*
+                                * Prevent sessions from being modified while
+                                * the agent application's configuration is
+                                * updated.
+                                */
+                               session_lock_list();
+
+                               /*
+                                * Update the newly registered applications's
+                                * configuration.
+                                */
+                               update_agent_app(new_app);
+
+                               ret = agent_send_registration_done(new_app);
+                               if (ret < 0) {
+                                       agent_destroy_app(new_app);
+                                       /* Removing from the poll set. */
+                                       ret = lttng_poll_del(&events,
+                                                       new_app_socket_fd);
+                                       if (ret < 0) {
+                                               session_unlock_list();
+                                               goto error;
+                                       }
+                                       continue;
+                               }
+
+                               /* Publish the new agent app. */
+                               agent_add_app(new_app);
+
+                               session_unlock_list();
+                       } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+                               /* Removing from the poll set */
+                               ret = lttng_poll_del(&events, pollfd);
+                               if (ret < 0) {
+                                       goto error;
+                               }
+                               agent_destroy_app_by_sock(pollfd);
+                       } else {
+                               ERR("Unexpected poll events %u for sock %d", revents, pollfd);
+                               goto error;
+                       }
+               }
+       }
+
+exit:
+       /* Whatever happens, try to delete it and exit. */
+       (void) lttng_poll_del(&events, reg_sock->fd);
+error:
+       destroy_tcp_socket(reg_sock);
+error_tcp_socket:
+       lttng_poll_clean(&events);
+error_poll_create:
+       uatomic_set(&agent_tracing_enabled, 0);
+       DBG("Cleaning up and stopping.");
+       rcu_thread_offline();
+       rcu_unregister_thread();
+       return NULL;
+}
+
+static bool shutdown_agent_management_thread(void *data)
+{
+       struct thread_notifiers *notifiers = (thread_notifiers *) data;
+       const int write_fd = lttng_pipe_get_writefd(notifiers->quit_pipe);
+
+       return notify_thread_pipe(write_fd) == 1;
+}
+
+static void cleanup_agent_management_thread(void *data)
+{
+       struct thread_notifiers *notifiers = (thread_notifiers *) data;
+
+       lttng_pipe_destroy(notifiers->quit_pipe);
+       sem_destroy(&notifiers->ready);
+       free(notifiers);
+}
+
+bool launch_agent_management_thread(void)
+{
+       struct thread_notifiers *notifiers;
+       struct lttng_thread *thread;
+
+       notifiers = (thread_notifiers *) zmalloc(sizeof(*notifiers));
+       if (!notifiers) {
+               goto error_alloc;
+       }
+
+       sem_init(&notifiers->ready, 0, 0);
+       notifiers->quit_pipe = lttng_pipe_open(FD_CLOEXEC);
+       if (!notifiers->quit_pipe) {
+               goto error;
+       }
+       thread = lttng_thread_create("Agent management",
+                       thread_agent_management,
+                       shutdown_agent_management_thread,
+                       cleanup_agent_management_thread,
+                       notifiers);
+       if (!thread) {
+               goto error;
+       }
+       wait_until_thread_is_ready(notifiers);
+       lttng_thread_put(thread);
+       return true;
+error:
+       cleanup_agent_management_thread(notifiers);
+error_alloc:
+       return false;
+}
diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c
deleted file mode 100644 (file)
index 77846b4..0000000
+++ /dev/null
@@ -1,1624 +0,0 @@
-/*
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <urcu/uatomic.h>
-#include <urcu/rculist.h>
-
-#include <lttng/event-rule/event-rule.h>
-#include <lttng/event-rule/event-rule-internal.h>
-#include <lttng/event-rule/jul-logging.h>
-#include <lttng/event-rule/log4j-logging.h>
-#include <lttng/event-rule/python-logging.h>
-#include <lttng/condition/condition.h>
-#include <lttng/condition/event-rule-matches.h>
-#include <lttng/domain-internal.h>
-#include <lttng/log-level-rule-internal.h>
-
-#include <common/common.h>
-#include <common/sessiond-comm/agent.h>
-
-#include <common/compat/endian.h>
-
-#include "agent.h"
-#include "ust-app.h"
-#include "utils.h"
-#include "common/error.h"
-
-#define AGENT_RET_CODE_INDEX(code) (code - AGENT_RET_CODE_SUCCESS)
-
-typedef enum lttng_event_rule_status (*event_rule_logging_get_name_pattern)(
-               const struct lttng_event_rule *rule, const char **pattern);
-typedef enum lttng_event_rule_status (*event_rule_logging_get_log_level_rule)(
-               const struct lttng_event_rule *rule,
-               const struct lttng_log_level_rule **log_level_rule);
-
-/*
- * Agent application context representation.
- */
-struct agent_app_ctx {
-       char *provider_name;
-       char *ctx_name;
-
-       /* agent_app_ctx are part of the agent app_ctx_list. */
-       struct cds_list_head list_node;
-
-       /* For call_rcu teardown. */
-       struct rcu_head rcu_node;
-};
-
-/*
- * Human readable agent return code.
- */
-static const char *error_string_array[] = {
-       [ AGENT_RET_CODE_INDEX(AGENT_RET_CODE_SUCCESS) ] = "Success",
-       [ AGENT_RET_CODE_INDEX(AGENT_RET_CODE_INVALID) ] = "Invalid command",
-       [ AGENT_RET_CODE_INDEX(AGENT_RET_CODE_UNKNOWN_NAME) ] = "Unknown logger name",
-
-       /* Last element */
-       [ AGENT_RET_CODE_INDEX(AGENT_RET_CODE_NR) ] = "Unknown code",
-};
-
-static
-void log_reply_code(uint32_t in_reply_ret_code)
-{
-       int level = PRINT_DBG3;
-       /*
-        * reply_ret_code and in_reply_ret_code are kept separate to have a
-        * sanitized value (used to retrieve the human readable string) and the
-        * original value which is logged as-is.
-        */
-       uint32_t reply_ret_code = in_reply_ret_code;
-
-       if (reply_ret_code < AGENT_RET_CODE_SUCCESS ||
-                       reply_ret_code >= AGENT_RET_CODE_NR) {
-               reply_ret_code = AGENT_RET_CODE_NR;
-               level = PRINT_ERR;
-       }
-
-       LOG(level, "Agent replied with retcode: %s (%"PRIu32")",
-                       error_string_array[AGENT_RET_CODE_INDEX(
-                       reply_ret_code)],
-                       in_reply_ret_code);
-}
-
-/*
- * Match function for the events hash table lookup by name.
- */
-static int ht_match_event_by_name(struct cds_lfht_node *node,
-               const void *_key)
-{
-       struct agent_event *event;
-       const struct agent_ht_key *key;
-
-       LTTNG_ASSERT(node);
-       LTTNG_ASSERT(_key);
-
-       event = caa_container_of(node, struct agent_event, node.node);
-       key = _key;
-
-       /* Match 1 elements of the key: name. */
-
-       /* Event name */
-       if (strncmp(event->name, key->name, sizeof(event->name)) != 0) {
-               goto no_match;
-       }
-       /* Match. */
-       return 1;
-
-no_match:
-       return 0;
-}
-
-/*
- * Match function for the events hash table lookup by name, log level and
- * filter expression.
- */
-static int ht_match_event(struct cds_lfht_node *node,
-               const void *_key)
-{
-       struct agent_event *event;
-       const struct agent_ht_key *key;
-       int ll_match;
-
-       LTTNG_ASSERT(node);
-       LTTNG_ASSERT(_key);
-
-       event = caa_container_of(node, struct agent_event, node.node);
-       key = _key;
-
-       /* Match 2 elements of the key: name and loglevel. */
-
-       /* Event name */
-       if (strncmp(event->name, key->name, sizeof(event->name)) != 0) {
-               goto no_match;
-       }
-
-       /* Event loglevel value and type. */
-       ll_match = loglevels_match(event->loglevel_type,
-               event->loglevel_value, key->loglevel_type,
-               key->loglevel_value, LTTNG_EVENT_LOGLEVEL_ALL);
-
-       if (!ll_match) {
-               goto no_match;
-       }
-
-       /* Filter expression */
-       if (!!event->filter_expression != !!key->filter_expression) {
-               /* One has a filter expression, the other does not */
-               goto no_match;
-       }
-
-       if (event->filter_expression) {
-               if (strncmp(event->filter_expression, key->filter_expression,
-                               strlen(event->filter_expression)) != 0) {
-                       goto no_match;
-               }
-       }
-
-       return 1;
-
-no_match:
-       return 0;
-}
-
-/*
- * Add unique agent event based on the event name and loglevel.
- */
-static void add_unique_agent_event(struct lttng_ht *ht,
-               struct agent_event *event)
-{
-       struct cds_lfht_node *node_ptr;
-       struct agent_ht_key key;
-
-       LTTNG_ASSERT(ht);
-       LTTNG_ASSERT(ht->ht);
-       LTTNG_ASSERT(event);
-
-       key.name = event->name;
-       key.loglevel_value = event->loglevel_value;
-       key.loglevel_type = event->loglevel_type;
-       key.filter_expression = event->filter_expression;
-
-       node_ptr = cds_lfht_add_unique(ht->ht,
-                       ht->hash_fct(event->node.key, lttng_ht_seed),
-                       ht_match_event, &key, &event->node.node);
-       LTTNG_ASSERT(node_ptr == &event->node.node);
-}
-
-/*
- * URCU delayed agent event reclaim.
- */
-static void destroy_event_agent_rcu(struct rcu_head *head)
-{
-       struct lttng_ht_node_str *node =
-               caa_container_of(head, struct lttng_ht_node_str, head);
-       struct agent_event *event =
-               caa_container_of(node, struct agent_event, node);
-
-       agent_destroy_event(event);
-}
-
-/*
- * URCU delayed agent app reclaim.
- */
-static void destroy_app_agent_rcu(struct rcu_head *head)
-{
-       struct lttng_ht_node_ulong *node =
-               caa_container_of(head, struct lttng_ht_node_ulong, head);
-       struct agent_app *app =
-               caa_container_of(node, struct agent_app, node);
-
-       free(app);
-}
-
-/*
- * Communication with the agent. Send the message header to the given socket in
- * big endian.
- *
- * Return 0 on success or else a negative errno message of sendmsg() op.
- */
-static int send_header(struct lttcomm_sock *sock, uint64_t data_size,
-               uint32_t cmd, uint32_t cmd_version)
-{
-       int ret;
-       ssize_t size;
-       struct lttcomm_agent_hdr msg;
-
-       LTTNG_ASSERT(sock);
-
-       memset(&msg, 0, sizeof(msg));
-       msg.data_size = htobe64(data_size);
-       msg.cmd = htobe32(cmd);
-       msg.cmd_version = htobe32(cmd_version);
-
-       size = sock->ops->sendmsg(sock, &msg, sizeof(msg), 0);
-       if (size < sizeof(msg)) {
-               ret = -errno;
-               goto error;
-       }
-       ret = 0;
-
-error:
-       return ret;
-}
-
-/*
- * Communication call with the agent. Send the payload to the given socket. The
- * header MUST be sent prior to this call.
- *
- * Return 0 on success or else a negative errno value of sendmsg() op.
- */
-static int send_payload(struct lttcomm_sock *sock, const void *data,
-               size_t size)
-{
-       int ret;
-       ssize_t len;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(data);
-
-       len = sock->ops->sendmsg(sock, data, size, 0);
-       if (len < size) {
-               ret = -errno;
-               goto error;
-       }
-       ret = 0;
-
-error:
-       return ret;
-}
-
-/*
- * Communication call with the agent. Receive reply from the agent using the
- * given socket.
- *
- * Return 0 on success or else a negative errno value from recvmsg() op.
- */
-static int recv_reply(struct lttcomm_sock *sock, void *buf, size_t size)
-{
-       int ret;
-       ssize_t len;
-
-       LTTNG_ASSERT(sock);
-       LTTNG_ASSERT(buf);
-
-       len = sock->ops->recvmsg(sock, buf, size, 0);
-       if (len < size) {
-               ret = -errno;
-               goto error;
-       }
-       ret = 0;
-
-error:
-       return ret;
-}
-
-/*
- * Internal event listing for a given app. Populate events.
- *
- * Return number of element in the list or else a negative LTTNG_ERR* code.
- * On success, the caller is responsible for freeing the memory
- * allocated for "events".
- */
-static ssize_t list_events(struct agent_app *app, struct lttng_event **events)
-{
-       int ret, i, len = 0, offset = 0;
-       uint32_t nb_event;
-       size_t data_size;
-       uint32_t reply_ret_code;
-       struct lttng_event *tmp_events = NULL;
-       struct lttcomm_agent_list_reply *reply = NULL;
-       struct lttcomm_agent_list_reply_hdr reply_hdr;
-
-       LTTNG_ASSERT(app);
-       LTTNG_ASSERT(app->sock);
-       LTTNG_ASSERT(events);
-
-       DBG2("Agent listing events for app pid: %d and socket %d", app->pid,
-                       app->sock->fd);
-
-       ret = send_header(app->sock, 0, AGENT_CMD_LIST, 0);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       /* Get list header so we know how much we'll receive. */
-       ret = recv_reply(app->sock, &reply_hdr, sizeof(reply_hdr));
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       reply_ret_code = be32toh(reply_hdr.ret_code);
-       log_reply_code(reply_ret_code);
-       switch (reply_ret_code) {
-       case AGENT_RET_CODE_SUCCESS:
-               data_size = be32toh(reply_hdr.data_size) + sizeof(*reply);
-               break;
-       default:
-               ret = LTTNG_ERR_UNK;
-               goto error;
-       }
-
-       reply = zmalloc(data_size);
-       if (!reply) {
-               ret = LTTNG_ERR_NOMEM;
-               goto error;
-       }
-
-       /* Get the list with the appropriate data size. */
-       ret = recv_reply(app->sock, reply, data_size);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       nb_event = be32toh(reply->nb_event);
-       tmp_events = zmalloc(sizeof(*tmp_events) * nb_event);
-       if (!tmp_events) {
-               ret = LTTNG_ERR_NOMEM;
-               goto error;
-       }
-
-       for (i = 0; i < nb_event; i++) {
-               offset += len;
-               if (lttng_strncpy(tmp_events[i].name, reply->payload + offset,
-                               sizeof(tmp_events[i].name))) {
-                       ret = LTTNG_ERR_INVALID;
-                       goto error;
-               }
-               tmp_events[i].pid = app->pid;
-               tmp_events[i].enabled = -1;
-               len = strlen(reply->payload + offset) + 1;
-       }
-
-       *events = tmp_events;
-
-       free(reply);
-       return nb_event;
-
-error_io:
-       ret = LTTNG_ERR_UST_LIST_FAIL;
-error:
-       free(reply);
-       free(tmp_events);
-       return -ret;
-
-}
-
-/*
- * Internal enable agent event on a agent application. This function
- * communicates with the agent to enable a given event.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-static int enable_event(const struct agent_app *app, struct agent_event *event)
-{
-       int ret;
-       char *bytes_to_send;
-       uint64_t data_size;
-       size_t filter_expression_length;
-       uint32_t reply_ret_code;
-       struct lttcomm_agent_enable_event msg;
-       struct lttcomm_agent_generic_reply reply;
-
-       LTTNG_ASSERT(app);
-       LTTNG_ASSERT(app->sock);
-       LTTNG_ASSERT(event);
-
-       DBG2("Agent enabling event %s for app pid: %d and socket %d", event->name,
-                       app->pid, app->sock->fd);
-
-       /*
-        * Calculate the payload's size, which is the fixed-size struct followed
-        * by the variable-length filter expression (+1 for the ending \0).
-        */
-       if (!event->filter_expression) {
-               filter_expression_length = 0;
-       } else {
-               filter_expression_length = strlen(event->filter_expression) + 1;
-       }
-       data_size = sizeof(msg) + filter_expression_length;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.loglevel_value = htobe32(event->loglevel_value);
-       msg.loglevel_type = htobe32(event->loglevel_type);
-       if (lttng_strncpy(msg.name, event->name, sizeof(msg.name))) {
-               ret = LTTNG_ERR_INVALID;
-               goto error;
-       }
-       msg.filter_expression_length = htobe32(filter_expression_length);
-
-       ret = send_header(app->sock, data_size, AGENT_CMD_ENABLE, 0);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       bytes_to_send = zmalloc(data_size);
-       if (!bytes_to_send) {
-               ret = LTTNG_ERR_NOMEM;
-               goto error;
-       }
-
-       memcpy(bytes_to_send, &msg, sizeof(msg));
-       if (filter_expression_length > 0) {
-               memcpy(bytes_to_send + sizeof(msg), event->filter_expression,
-                               filter_expression_length);
-       }
-
-       ret = send_payload(app->sock, bytes_to_send, data_size);
-       free(bytes_to_send);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       ret = recv_reply(app->sock, &reply, sizeof(reply));
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       reply_ret_code = be32toh(reply.ret_code);
-       log_reply_code(reply_ret_code);
-       switch (reply_ret_code) {
-       case AGENT_RET_CODE_SUCCESS:
-               break;
-       case AGENT_RET_CODE_UNKNOWN_NAME:
-               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
-               goto error;
-       default:
-               ret = LTTNG_ERR_UNK;
-               goto error;
-       }
-
-       return LTTNG_OK;
-
-error_io:
-       ret = LTTNG_ERR_UST_ENABLE_FAIL;
-error:
-       return ret;
-}
-
-/*
- * Send Pascal-style string. Size is sent as a 32-bit big endian integer.
- */
-static
-int send_pstring(struct lttcomm_sock *sock, const char *str, uint32_t len)
-{
-       int ret;
-       uint32_t len_be;
-
-       len_be = htobe32(len);
-       ret = send_payload(sock, &len_be, sizeof(len_be));
-       if (ret) {
-               goto end;
-       }
-
-       ret = send_payload(sock, str, len);
-       if (ret) {
-               goto end;
-       }
-end:
-       return ret;
-}
-
-/*
- * Internal enable application context on an agent application. This function
- * communicates with the agent to enable a given application context.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-static int app_context_op(const struct agent_app *app,
-               const struct agent_app_ctx *ctx, enum lttcomm_agent_command cmd)
-{
-       int ret;
-       uint32_t reply_ret_code;
-       struct lttcomm_agent_generic_reply reply;
-       size_t app_ctx_provider_name_len, app_ctx_name_len, data_size;
-
-       LTTNG_ASSERT(app);
-       LTTNG_ASSERT(app->sock);
-       LTTNG_ASSERT(ctx);
-       LTTNG_ASSERT(cmd == AGENT_CMD_APP_CTX_ENABLE ||
-                       cmd == AGENT_CMD_APP_CTX_DISABLE);
-
-       DBG2("Agent %s application %s:%s for app pid: %d and socket %d",
-                       cmd == AGENT_CMD_APP_CTX_ENABLE ? "enabling" : "disabling",
-                       ctx->provider_name, ctx->ctx_name,
-                       app->pid, app->sock->fd);
-
-       /*
-        * Calculate the payload's size, which consists of the size (u32, BE)
-        * of the provider name, the NULL-terminated provider name string, the
-        * size (u32, BE) of the context name, followed by the NULL-terminated
-        * context name string.
-        */
-       app_ctx_provider_name_len = strlen(ctx->provider_name) + 1;
-       app_ctx_name_len = strlen(ctx->ctx_name) + 1;
-       data_size = sizeof(uint32_t) + app_ctx_provider_name_len +
-                       sizeof(uint32_t) + app_ctx_name_len;
-
-       ret = send_header(app->sock, data_size, cmd, 0);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       if (app_ctx_provider_name_len > UINT32_MAX ||
-                       app_ctx_name_len > UINT32_MAX) {
-               ERR("Application context name > MAX_UINT32");
-               ret = LTTNG_ERR_INVALID;
-               goto error;
-       }
-
-       ret = send_pstring(app->sock, ctx->provider_name,
-                       (uint32_t) app_ctx_provider_name_len);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       ret = send_pstring(app->sock, ctx->ctx_name,
-                       (uint32_t) app_ctx_name_len);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       ret = recv_reply(app->sock, &reply, sizeof(reply));
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       reply_ret_code = be32toh(reply.ret_code);
-       log_reply_code(reply_ret_code);
-       switch (reply_ret_code) {
-       case AGENT_RET_CODE_SUCCESS:
-               break;
-       default:
-               ret = LTTNG_ERR_UNK;
-               goto error;
-       }
-
-       return LTTNG_OK;
-
-error_io:
-       ret = LTTNG_ERR_UST_ENABLE_FAIL;
-error:
-       return ret;
-}
-
-/*
- * Internal disable agent event call on a agent application. This function
- * communicates with the agent to disable a given event.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-static int disable_event(struct agent_app *app, struct agent_event *event)
-{
-       int ret;
-       uint64_t data_size;
-       uint32_t reply_ret_code;
-       struct lttcomm_agent_disable_event msg;
-       struct lttcomm_agent_generic_reply reply;
-
-       LTTNG_ASSERT(app);
-       LTTNG_ASSERT(app->sock);
-       LTTNG_ASSERT(event);
-
-       DBG2("Agent disabling event %s for app pid: %d and socket %d", event->name,
-                       app->pid, app->sock->fd);
-
-       data_size = sizeof(msg);
-       memset(&msg, 0, sizeof(msg));
-       if (lttng_strncpy(msg.name, event->name, sizeof(msg.name))) {
-               ret = LTTNG_ERR_INVALID;
-               goto error;
-       }
-
-       ret = send_header(app->sock, data_size, AGENT_CMD_DISABLE, 0);
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       ret = send_payload(app->sock, &msg, sizeof(msg));
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       ret = recv_reply(app->sock, &reply, sizeof(reply));
-       if (ret < 0) {
-               goto error_io;
-       }
-
-       reply_ret_code = be32toh(reply.ret_code);
-       log_reply_code(reply_ret_code);
-       switch (reply_ret_code) {
-       case AGENT_RET_CODE_SUCCESS:
-               break;
-       case AGENT_RET_CODE_UNKNOWN_NAME:
-               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
-               goto error;
-       default:
-               ret = LTTNG_ERR_UNK;
-               goto error;
-       }
-
-       return LTTNG_OK;
-
-error_io:
-       ret = LTTNG_ERR_UST_DISABLE_FAIL;
-error:
-       return ret;
-}
-
-/*
- * Send back the registration DONE command to a given agent application.
- *
- * Return 0 on success or else a negative value.
- */
-int agent_send_registration_done(struct agent_app *app)
-{
-       LTTNG_ASSERT(app);
-       LTTNG_ASSERT(app->sock);
-
-       DBG("Agent sending registration done to app socket %d", app->sock->fd);
-
-       return send_header(app->sock, 0, AGENT_CMD_REG_DONE, 0);
-}
-
-/*
- * Enable agent event on every agent applications registered with the session
- * daemon.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-int agent_enable_event(struct agent_event *event,
-               enum lttng_domain_type domain)
-{
-       int ret;
-       struct agent_app *app;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(event);
-
-       rcu_read_lock();
-
-       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
-                       node.node) {
-               if (app->domain != domain) {
-                       continue;
-               }
-
-               /* Enable event on agent application through TCP socket. */
-               ret = enable_event(app, event);
-               if (ret != LTTNG_OK) {
-                       goto error;
-               }
-       }
-
-       event->enabled_count++;
-       ret = LTTNG_OK;
-
-error:
-       rcu_read_unlock();
-       return ret;
-}
-
-static
-void destroy_app_ctx(struct agent_app_ctx *ctx)
-{
-       free(ctx->provider_name);
-       free(ctx->ctx_name);
-       free(ctx);
-}
-
-static
-struct agent_app_ctx *create_app_ctx(const struct lttng_event_context *ctx)
-{
-       struct agent_app_ctx *agent_ctx = NULL;
-
-       if (!ctx) {
-               goto end;
-       }
-
-       LTTNG_ASSERT(ctx->ctx == LTTNG_EVENT_CONTEXT_APP_CONTEXT);
-       agent_ctx = zmalloc(sizeof(*ctx));
-       if (!agent_ctx) {
-               goto end;
-       }
-
-       agent_ctx->provider_name = strdup(ctx->u.app_ctx.provider_name);
-       agent_ctx->ctx_name = strdup(ctx->u.app_ctx.ctx_name);
-       if (!agent_ctx->provider_name || !agent_ctx->ctx_name) {
-               destroy_app_ctx(agent_ctx);
-               agent_ctx = NULL;
-       }
-end:
-       return agent_ctx;
-}
-
-/*
- * Enable agent context on every agent applications registered with the session
- * daemon.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-int agent_enable_context(const struct lttng_event_context *ctx,
-               enum lttng_domain_type domain)
-{
-       int ret;
-       struct agent_app *app;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(ctx);
-       if (ctx->ctx != LTTNG_EVENT_CONTEXT_APP_CONTEXT) {
-               ret = LTTNG_ERR_INVALID;
-               goto error;
-       }
-
-       rcu_read_lock();
-
-       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
-                       node.node) {
-               struct agent_app_ctx *agent_ctx;
-
-               if (app->domain != domain) {
-                       continue;
-               }
-
-               agent_ctx = create_app_ctx(ctx);
-               if (!agent_ctx) {
-                       ret = LTTNG_ERR_NOMEM;
-                       goto error_unlock;
-               }
-
-               /* Enable event on agent application through TCP socket. */
-               ret = app_context_op(app, agent_ctx, AGENT_CMD_APP_CTX_ENABLE);
-               destroy_app_ctx(agent_ctx);
-               if (ret != LTTNG_OK) {
-                       goto error_unlock;
-               }
-       }
-
-       ret = LTTNG_OK;
-
-error_unlock:
-       rcu_read_unlock();
-error:
-       return ret;
-}
-
-/*
- * Disable agent event on every agent application registered with the session
- * daemon.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-int agent_disable_event(struct agent_event *event,
-               enum lttng_domain_type domain)
-{
-       int ret = LTTNG_OK;
-       struct agent_app *app;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(event);
-       if (!AGENT_EVENT_IS_ENABLED(event)) {
-               goto end;
-       }
-
-       if (--event->enabled_count != 0) {
-               /*
-                * Agent event still enabled. Disable the agent event only when
-                * all "users" have disabled it (event notifiers, event rules,
-                * etc.).
-                */
-               ret = LTTNG_OK;
-               goto end;
-       }
-
-       rcu_read_lock();
-
-       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
-                       node.node) {
-               if (app->domain != domain) {
-                       continue;
-               }
-
-               /* Enable event on agent application through TCP socket. */
-               ret = disable_event(app, event);
-               if (ret != LTTNG_OK) {
-                       goto error;
-               }
-       }
-
-       /* event->enabled_count is now 0. */
-       LTTNG_ASSERT(!AGENT_EVENT_IS_ENABLED(event));
-
-error:
-       rcu_read_unlock();
-end:
-       return ret;
-}
-
-/*
- * Disable agent context on every agent application registered with the session
- * daemon.
- *
- * Return LTTNG_OK on success or else a LTTNG_ERR* code.
- */
-static int disable_context(struct agent_app_ctx *ctx,
-               enum lttng_domain_type domain)
-{
-       int ret = LTTNG_OK;
-       struct agent_app *app;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(ctx);
-
-       rcu_read_lock();
-       DBG2("Disabling agent application context %s:%s",
-                       ctx->provider_name, ctx->ctx_name);
-       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
-                       node.node) {
-               if (app->domain != domain) {
-                       continue;
-               }
-
-               ret = app_context_op(app, ctx, AGENT_CMD_APP_CTX_DISABLE);
-               if (ret != LTTNG_OK) {
-                       goto end;
-               }
-       }
-end:
-       rcu_read_unlock();
-       return ret;
-}
-
-/*
- * Ask every agent for the list of possible event. Events is allocated with the
- * events of every agent application.
- *
- * Return the number of events or else a negative value.
- */
-int agent_list_events(struct lttng_event **events,
-               enum lttng_domain_type domain)
-{
-       int ret;
-       size_t nbmem, count = 0;
-       struct agent_app *app;
-       struct lttng_event *tmp_events = NULL;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(events);
-
-       DBG2("Agent listing events for domain %d", domain);
-
-       nbmem = UST_APP_EVENT_LIST_SIZE;
-       tmp_events = zmalloc(nbmem * sizeof(*tmp_events));
-       if (!tmp_events) {
-               PERROR("zmalloc agent list events");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       rcu_read_lock();
-       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
-                       node.node) {
-               ssize_t nb_ev;
-               struct lttng_event *agent_events;
-
-               /* Skip domain not asked by the list. */
-               if (app->domain != domain) {
-                       continue;
-               }
-
-               nb_ev = list_events(app, &agent_events);
-               if (nb_ev < 0) {
-                       ret = nb_ev;
-                       goto error_unlock;
-               }
-
-               if (count + nb_ev > nbmem) {
-                       /* In case the realloc fails, we free the memory */
-                       struct lttng_event *new_tmp_events;
-                       size_t new_nbmem;
-
-                       new_nbmem = max_t(size_t, count + nb_ev, nbmem << 1);
-                       DBG2("Reallocating agent event list from %zu to %zu entries",
-                                       nbmem, new_nbmem);
-                       new_tmp_events = realloc(tmp_events,
-                               new_nbmem * sizeof(*new_tmp_events));
-                       if (!new_tmp_events) {
-                               PERROR("realloc agent events");
-                               ret = -ENOMEM;
-                               free(agent_events);
-                               goto error_unlock;
-                       }
-                       /* Zero the new memory */
-                       memset(new_tmp_events + nbmem, 0,
-                               (new_nbmem - nbmem) * sizeof(*new_tmp_events));
-                       nbmem = new_nbmem;
-                       tmp_events = new_tmp_events;
-               }
-               memcpy(tmp_events + count, agent_events,
-                       nb_ev * sizeof(*tmp_events));
-               free(agent_events);
-               count += nb_ev;
-       }
-       rcu_read_unlock();
-
-       ret = count;
-       *events = tmp_events;
-       return ret;
-
-error_unlock:
-       rcu_read_unlock();
-error:
-       free(tmp_events);
-       return ret;
-}
-
-/*
- * Create a agent app object using the given PID.
- *
- * Return newly allocated object or else NULL on error.
- */
-struct agent_app *agent_create_app(pid_t pid, enum lttng_domain_type domain,
-               struct lttcomm_sock *sock)
-{
-       struct agent_app *app;
-
-       LTTNG_ASSERT(sock);
-
-       app = zmalloc(sizeof(*app));
-       if (!app) {
-               PERROR("Failed to allocate agent application instance");
-               goto error;
-       }
-
-       app->pid = pid;
-       app->domain = domain;
-       app->sock = sock;
-       lttng_ht_node_init_ulong(&app->node, (unsigned long) app->sock->fd);
-
-error:
-       return app;
-}
-
-/*
- * Lookup agent app by socket in the global hash table.
- *
- * RCU read side lock MUST be acquired.
- *
- * Return object if found else NULL.
- */
-struct agent_app *agent_find_app_by_sock(int sock)
-{
-       struct lttng_ht_node_ulong *node;
-       struct lttng_ht_iter iter;
-       struct agent_app *app;
-
-       LTTNG_ASSERT(sock >= 0);
-
-       lttng_ht_lookup(the_agent_apps_ht_by_sock,
-                       (void *) ((unsigned long) sock), &iter);
-       node = lttng_ht_iter_get_node_ulong(&iter);
-       if (node == NULL) {
-               goto error;
-       }
-       app = caa_container_of(node, struct agent_app, node);
-
-       DBG3("Agent app pid %d found by sock %d.", app->pid, sock);
-       return app;
-
-error:
-       DBG3("Agent app NOT found by sock %d.", sock);
-       return NULL;
-}
-
-/*
- * Add agent application object to the global hash table.
- */
-void agent_add_app(struct agent_app *app)
-{
-       LTTNG_ASSERT(app);
-
-       DBG3("Agent adding app sock: %d and pid: %d to ht", app->sock->fd, app->pid);
-       lttng_ht_add_unique_ulong(the_agent_apps_ht_by_sock, &app->node);
-}
-
-/*
- * Delete agent application from the global hash table.
- *
- * rcu_read_lock() must be held by the caller.
- */
-void agent_delete_app(struct agent_app *app)
-{
-       int ret;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(app);
-
-       DBG3("Agent deleting app pid: %d and sock: %d", app->pid, app->sock->fd);
-
-       iter.iter.node = &app->node.node;
-       ret = lttng_ht_del(the_agent_apps_ht_by_sock, &iter);
-       LTTNG_ASSERT(!ret);
-}
-
-/*
- * Destroy an agent application object by detaching it from its corresponding
- * UST app if one is connected by closing the socket. Finally, perform a
- * delayed memory reclaim.
- */
-void agent_destroy_app(struct agent_app *app)
-{
-       LTTNG_ASSERT(app);
-
-       if (app->sock) {
-               app->sock->ops->close(app->sock);
-               lttcomm_destroy_sock(app->sock);
-       }
-
-       call_rcu(&app->node.head, destroy_app_agent_rcu);
-}
-
-/*
- * Initialize an already allocated agent object.
- *
- * Return 0 on success or else a negative errno value.
- */
-int agent_init(struct agent *agt)
-{
-       int ret;
-
-       LTTNG_ASSERT(agt);
-
-       agt->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
-       if (!agt->events) {
-               ret = -ENOMEM;
-               goto error;
-       }
-       lttng_ht_node_init_u64(&agt->node, agt->domain);
-
-       CDS_INIT_LIST_HEAD(&agt->app_ctx_list);
-       return 0;
-
-error:
-       return ret;
-}
-
-/*
- * Add agent object to the given hash table.
- */
-void agent_add(struct agent *agt, struct lttng_ht *ht)
-{
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(ht);
-
-       DBG3("Agent adding from domain %d", agt->domain);
-
-       lttng_ht_add_unique_u64(ht, &agt->node);
-}
-
-/*
- * Create an agent object for the given domain.
- *
- * Return the allocated agent or NULL on error.
- */
-struct agent *agent_create(enum lttng_domain_type domain)
-{
-       int ret;
-       struct agent *agt;
-
-       agt = zmalloc(sizeof(struct agent));
-       if (!agt) {
-               goto error;
-       }
-       agt->domain = domain;
-
-       ret = agent_init(agt);
-       if (ret < 0) {
-               free(agt);
-               agt = NULL;
-               goto error;
-       }
-
-error:
-       return agt;
-}
-
-/*
- * Create a newly allocated agent event data structure.
- * Ownership of filter_expression is taken.
- *
- * Return a new object else NULL on error.
- */
-struct agent_event *agent_create_event(const char *name,
-               enum lttng_loglevel_type loglevel_type, int loglevel_value,
-               struct lttng_bytecode *filter, char *filter_expression)
-{
-       struct agent_event *event = NULL;
-
-       DBG3("Agent create new event with name %s, loglevel type %d, \
-                       loglevel value %d and filter %s",
-                       name, loglevel_type, loglevel_value,
-                       filter_expression ? filter_expression : "NULL");
-
-       if (!name) {
-               ERR("Failed to create agent event; no name provided.");
-               goto error;
-       }
-
-       event = zmalloc(sizeof(*event));
-       if (!event) {
-               goto error;
-       }
-
-       strncpy(event->name, name, sizeof(event->name));
-       event->name[sizeof(event->name) - 1] = '\0';
-       lttng_ht_node_init_str(&event->node, event->name);
-
-       event->loglevel_value = loglevel_value;
-       event->loglevel_type = loglevel_type;
-       event->filter = filter;
-       event->filter_expression = filter_expression;
-error:
-       return event;
-}
-
-/*
- * Unique add of a agent event to an agent object.
- */
-void agent_add_event(struct agent_event *event, struct agent *agt)
-{
-       LTTNG_ASSERT(event);
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(agt->events);
-
-       DBG3("Agent adding event %s", event->name);
-       add_unique_agent_event(agt->events, event);
-       agt->being_used = 1;
-}
-
-/*
- * Unique add of a agent context to an agent object.
- */
-int agent_add_context(const struct lttng_event_context *ctx, struct agent *agt)
-{
-       int ret = LTTNG_OK;
-       struct agent_app_ctx *agent_ctx = NULL;
-
-       LTTNG_ASSERT(ctx);
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(agt->events);
-       LTTNG_ASSERT(ctx->ctx == LTTNG_EVENT_CONTEXT_APP_CONTEXT);
-
-       agent_ctx = create_app_ctx(ctx);
-       if (!agent_ctx) {
-               ret = LTTNG_ERR_NOMEM;
-               goto end;
-       }
-
-       DBG3("Agent adding context %s:%s", ctx->u.app_ctx.provider_name,
-                       ctx->u.app_ctx.ctx_name);
-       cds_list_add_tail_rcu(&agent_ctx->list_node, &agt->app_ctx_list);
-end:
-       return ret;
-}
-
-/*
- * Find multiple agent events sharing the given name.
- *
- * RCU read side lock MUST be acquired. It must be held for the
- * duration of the iteration.
- *
- * Sets the given iterator.
- */
-void agent_find_events_by_name(const char *name, struct agent *agt,
-               struct lttng_ht_iter* iter)
-{
-       struct lttng_ht *ht;
-       struct agent_ht_key key;
-
-       LTTNG_ASSERT(name);
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(agt->events);
-       LTTNG_ASSERT(iter);
-
-       ht = agt->events;
-       key.name = name;
-
-       cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed),
-                       ht_match_event_by_name, &key, &iter->iter);
-}
-
-/*
- * Find the agent event matching a trigger.
- *
- * RCU read side lock MUST be acquired. It must be held for as long as
- * the returned agent_event is used.
- *
- * Return object if found else NULL.
- */
-struct agent_event *agent_find_event_by_trigger(
-               const struct lttng_trigger *trigger, struct agent *agt)
-{
-       enum lttng_condition_status c_status;
-       enum lttng_event_rule_status er_status;
-       enum lttng_domain_type domain;
-       const struct lttng_condition *condition;
-       const struct lttng_event_rule *rule;
-       const char *name;
-       const char *filter_expression;
-       const struct lttng_log_level_rule *log_level_rule;
-       /* Unused when loglevel_type is 'ALL'. */
-       int loglevel_value = 0;
-       enum lttng_loglevel_type loglevel_type;
-       event_rule_logging_get_name_pattern logging_get_name_pattern;
-       event_rule_logging_get_log_level_rule logging_get_log_level_rule;
-
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(agt->events);
-
-       condition = lttng_trigger_get_const_condition(trigger);
-
-       LTTNG_ASSERT(lttng_condition_get_type(condition) ==
-                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
-
-       c_status = lttng_condition_event_rule_matches_get_rule(
-                       condition, &rule);
-       LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
-
-       switch (lttng_event_rule_get_type(rule)) {
-       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
-               logging_get_name_pattern =
-                               lttng_event_rule_jul_logging_get_name_pattern;
-               logging_get_log_level_rule =
-                               lttng_event_rule_jul_logging_get_log_level_rule;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
-               logging_get_name_pattern =
-                               lttng_event_rule_log4j_logging_get_name_pattern;
-               logging_get_log_level_rule =
-                               lttng_event_rule_log4j_logging_get_log_level_rule;
-               break;
-       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
-               logging_get_name_pattern =
-                               lttng_event_rule_python_logging_get_name_pattern;
-               logging_get_log_level_rule =
-                               lttng_event_rule_python_logging_get_log_level_rule;
-               break;
-       default:
-               abort();
-               break;
-       }
-
-       domain = lttng_event_rule_get_domain_type(rule);
-       LTTNG_ASSERT(domain == LTTNG_DOMAIN_JUL || domain == LTTNG_DOMAIN_LOG4J ||
-                       domain == LTTNG_DOMAIN_PYTHON);
-
-       /* Get the event's pattern name ('name' in the legacy terminology). */
-       er_status = logging_get_name_pattern(rule, &name);
-       LTTNG_ASSERT(er_status == LTTNG_EVENT_RULE_STATUS_OK);
-
-       /* Get the internal filter expression. */
-       filter_expression = lttng_event_rule_get_filter(rule);
-
-       /* Map log_level_rule to loglevel value. */
-       er_status = logging_get_log_level_rule(rule, &log_level_rule);
-       if (er_status == LTTNG_EVENT_RULE_STATUS_UNSET) {
-               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
-               loglevel_value = 0;
-       } else if (er_status == LTTNG_EVENT_RULE_STATUS_OK) {
-               lttng_log_level_rule_to_loglevel(log_level_rule, &loglevel_type, &loglevel_value);
-       } else {
-               abort();
-       }
-
-       return agent_find_event(name, loglevel_type, loglevel_value,
-                       filter_expression, agt);
-}
-
-/*
- * Get the next agent event duplicate by name. This should be called
- * after a call to agent_find_events_by_name() to iterate on events.
- *
- * The RCU read lock must be held during the iteration and for as long
- * as the object the iterator points to remains in use.
- */
-void agent_event_next_duplicate(const char *name,
-               struct agent *agt, struct lttng_ht_iter* iter)
-{
-       struct agent_ht_key key;
-
-       key.name = name;
-
-       cds_lfht_next_duplicate(agt->events->ht, ht_match_event_by_name,
-               &key, &iter->iter);
-}
-
-/*
- * Find a agent event in the given agent using name, loglevel and filter.
- *
- * RCU read side lock MUST be acquired. It must be kept for as long as
- * the returned agent_event is used.
- *
- * Return object if found else NULL.
- */
-struct agent_event *agent_find_event(const char *name,
-               enum lttng_loglevel_type loglevel_type,
-               int loglevel_value,
-               const char *filter_expression,
-               struct agent *agt)
-{
-       struct lttng_ht_node_str *node;
-       struct lttng_ht_iter iter;
-       struct lttng_ht *ht;
-       struct agent_ht_key key;
-
-       LTTNG_ASSERT(name);
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(agt->events);
-
-       ht = agt->events;
-       key.name = name;
-       key.loglevel_value = loglevel_value;
-       key.loglevel_type = loglevel_type;
-       key.filter_expression = filter_expression;
-
-       cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed),
-                       ht_match_event, &key, &iter.iter);
-       node = lttng_ht_iter_get_node_str(&iter);
-       if (node == NULL) {
-               goto error;
-       }
-
-       DBG3("Agent event found %s.", name);
-       return caa_container_of(node, struct agent_event, node);
-
-error:
-       DBG3("Agent event NOT found %s.", name);
-       return NULL;
-}
-
-/*
- * Free given agent event. This event must not be globally visible at this
- * point (only expected to be used on failure just after event creation). After
- * this call, the pointer is not usable anymore.
- */
-void agent_destroy_event(struct agent_event *event)
-{
-       LTTNG_ASSERT(event);
-
-       free(event->filter);
-       free(event->filter_expression);
-       free(event->exclusion);
-       free(event);
-}
-
-static
-void destroy_app_ctx_rcu(struct rcu_head *head)
-{
-       struct agent_app_ctx *ctx =
-                       caa_container_of(head, struct agent_app_ctx, rcu_node);
-
-       destroy_app_ctx(ctx);
-}
-
-/*
- * Destroy an agent completely.
- */
-void agent_destroy(struct agent *agt)
-{
-       struct lttng_ht_node_str *node;
-       struct lttng_ht_iter iter;
-       struct agent_app_ctx *ctx;
-
-       LTTNG_ASSERT(agt);
-
-       DBG3("Agent destroy");
-
-       rcu_read_lock();
-       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, node, node) {
-               int ret;
-               struct agent_event *event;
-
-               /*
-                * When destroying an event, we have to try to disable it on the
-                * agent side so the event stops generating data. The return
-                * value is not important since we have to continue anyway
-                * destroying the object.
-                */
-               event = caa_container_of(node, struct agent_event, node);
-               (void) agent_disable_event(event, agt->domain);
-
-               ret = lttng_ht_del(agt->events, &iter);
-               LTTNG_ASSERT(!ret);
-               call_rcu(&node->head, destroy_event_agent_rcu);
-       }
-
-       cds_list_for_each_entry_rcu(ctx, &agt->app_ctx_list, list_node) {
-               (void) disable_context(ctx, agt->domain);
-               cds_list_del(&ctx->list_node);
-               call_rcu(&ctx->rcu_node, destroy_app_ctx_rcu);
-       }
-       rcu_read_unlock();
-       ht_cleanup_push(agt->events);
-       free(agt);
-}
-
-/*
- * Allocate agent_apps_ht_by_sock.
- */
-int agent_app_ht_alloc(void)
-{
-       the_agent_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
-       return the_agent_apps_ht_by_sock ? 0 : -1;
-}
-
-/*
- * Destroy a agent application by socket.
- */
-void agent_destroy_app_by_sock(int sock)
-{
-       struct agent_app *app;
-
-       LTTNG_ASSERT(sock >= 0);
-
-       /*
-        * Not finding an application is a very important error that should NEVER
-        * happen. The hash table deletion is ONLY done through this call when the
-        * main sessiond thread is torn down.
-        */
-       rcu_read_lock();
-       app = agent_find_app_by_sock(sock);
-       LTTNG_ASSERT(app);
-
-       /* RCU read side lock is assumed to be held by this function. */
-       agent_delete_app(app);
-
-       /* The application is freed in a RCU call but the socket is closed here. */
-       agent_destroy_app(app);
-       rcu_read_unlock();
-}
-
-/*
- * Clean-up the agent app hash table and destroy it.
- */
-void agent_app_ht_clean(void)
-{
-       struct lttng_ht_node_ulong *node;
-       struct lttng_ht_iter iter;
-
-       if (!the_agent_apps_ht_by_sock) {
-               return;
-       }
-       rcu_read_lock();
-       cds_lfht_for_each_entry(
-                       the_agent_apps_ht_by_sock->ht, &iter.iter, node, node) {
-               struct agent_app *app;
-
-               app = caa_container_of(node, struct agent_app, node);
-               agent_destroy_app_by_sock(app->sock->fd);
-       }
-       rcu_read_unlock();
-
-       lttng_ht_destroy(the_agent_apps_ht_by_sock);
-}
-
-/*
- * Update a agent application (given socket) using the given agent.
- *
- * Note that this function is most likely to be used with a tracing session
- * thus the caller should make sure to hold the appropriate lock(s).
- */
-void agent_update(const struct agent *agt, const struct agent_app *app)
-{
-       int ret;
-       struct agent_event *event;
-       struct lttng_ht_iter iter;
-       struct agent_app_ctx *ctx;
-
-       LTTNG_ASSERT(agt);
-       LTTNG_ASSERT(app);
-
-       DBG("Agent updating app: pid = %ld", (long) app->pid);
-
-       rcu_read_lock();
-       /*
-        * We are in the registration path thus if the application is gone,
-        * there is a serious code flow error.
-        */
-
-       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, event, node.node) {
-               /* Skip event if disabled. */
-               if (!AGENT_EVENT_IS_ENABLED(event)) {
-                       continue;
-               }
-
-               ret = enable_event(app, event);
-               if (ret != LTTNG_OK) {
-                       DBG2("Agent update unable to enable event %s on app pid: %d sock %d",
-                                       event->name, app->pid, app->sock->fd);
-                       /* Let's try the others here and don't assume the app is dead. */
-                       continue;
-               }
-       }
-
-       cds_list_for_each_entry_rcu(ctx, &agt->app_ctx_list, list_node) {
-               ret = app_context_op(app, ctx, AGENT_CMD_APP_CTX_ENABLE);
-               if (ret != LTTNG_OK) {
-                       DBG2("Agent update unable to add application context %s:%s on app pid: %d sock %d",
-                                       ctx->provider_name, ctx->ctx_name,
-                                       app->pid, app->sock->fd);
-                       continue;
-               }
-       }
-
-       rcu_read_unlock();
-}
-
-/*
- * Allocate the per-event notifier domain agent hash table. It is lazily
- * populated as domains are used.
- */
-int agent_by_event_notifier_domain_ht_create(void)
-{
-       the_trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
-       return the_trigger_agents_ht_by_domain ? 0 : -1;
-}
-
-/*
- * Clean-up the per-event notifier domain agent hash table and destroy it.
- */
-void agent_by_event_notifier_domain_ht_destroy(void)
-{
-       struct lttng_ht_node_u64 *node;
-       struct lttng_ht_iter iter;
-
-       if (!the_trigger_agents_ht_by_domain) {
-               return;
-       }
-
-       rcu_read_lock();
-       cds_lfht_for_each_entry(the_trigger_agents_ht_by_domain->ht,
-                       &iter.iter, node, node) {
-               struct agent *agent =
-                               caa_container_of(node, struct agent, node);
-               const int ret = lttng_ht_del(
-                               the_trigger_agents_ht_by_domain, &iter);
-
-               LTTNG_ASSERT(ret == 0);
-               agent_destroy(agent);
-       }
-
-       rcu_read_unlock();
-       lttng_ht_destroy(the_trigger_agents_ht_by_domain);
-}
-
-struct agent *agent_find_by_event_notifier_domain(
-               enum lttng_domain_type domain_type)
-{
-       struct agent *agt = NULL;
-       struct lttng_ht_node_u64 *node;
-       struct lttng_ht_iter iter;
-       const uint64_t key = (uint64_t) domain_type;
-
-       LTTNG_ASSERT(the_trigger_agents_ht_by_domain);
-
-       DBG3("Per-event notifier domain agent lookup for domain '%s'",
-                       lttng_domain_type_str(domain_type));
-
-       lttng_ht_lookup(the_trigger_agents_ht_by_domain, &key, &iter);
-       node = lttng_ht_iter_get_node_u64(&iter);
-       if (!node) {
-               goto end;
-       }
-
-       agt = caa_container_of(node, struct agent, node);
-
-end:
-       return agt;
-}
diff --git a/src/bin/lttng-sessiond/agent.cpp b/src/bin/lttng-sessiond/agent.cpp
new file mode 100644 (file)
index 0000000..5be61dc
--- /dev/null
@@ -0,0 +1,1627 @@
+/*
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ * Copyright (C) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ *
+ */
+
+#define _LGPL_SOURCE
+#include <urcu/uatomic.h>
+#include <urcu/rculist.h>
+
+#include <lttng/event-rule/event-rule.h>
+#include <lttng/event-rule/event-rule-internal.h>
+#include <lttng/event-rule/jul-logging.h>
+#include <lttng/event-rule/log4j-logging.h>
+#include <lttng/event-rule/python-logging.h>
+#include <lttng/condition/condition.h>
+#include <lttng/condition/event-rule-matches.h>
+#include <lttng/domain-internal.h>
+#include <lttng/log-level-rule-internal.h>
+
+#include <common/common.h>
+#include <common/sessiond-comm/agent.h>
+
+#include <common/compat/endian.h>
+
+#include "agent.h"
+#include "ust-app.h"
+#include "utils.h"
+#include "common/error.h"
+
+typedef enum lttng_event_rule_status (*event_rule_logging_get_name_pattern)(
+               const struct lttng_event_rule *rule, const char **pattern);
+typedef enum lttng_event_rule_status (*event_rule_logging_get_log_level_rule)(
+               const struct lttng_event_rule *rule,
+               const struct lttng_log_level_rule **log_level_rule);
+
+/*
+ * Agent application context representation.
+ */
+struct agent_app_ctx {
+       char *provider_name;
+       char *ctx_name;
+
+       /* agent_app_ctx are part of the agent app_ctx_list. */
+       struct cds_list_head list_node;
+
+       /* For call_rcu teardown. */
+       struct rcu_head rcu_node;
+};
+
+/*
+ * Human readable agent return code.
+ */
+static
+const char *lttcomm_agent_ret_code_str(lttcomm_agent_ret_code code)
+{
+       switch (code) {
+       case AGENT_RET_CODE_SUCCESS:
+               return "Success";
+       case AGENT_RET_CODE_INVALID:
+               return "Invalid command";
+       case AGENT_RET_CODE_UNKNOWN_NAME:
+               return "Unknown logger name";
+       default:
+               return "Unknown code";
+       }
+};
+
+static
+void log_reply_code(uint32_t in_reply_ret_code)
+{
+       int level = PRINT_DBG3;
+       /*
+        * reply_ret_code and in_reply_ret_code are kept separate to have a
+        * sanitized value (used to retrieve the human readable string) and the
+        * original value which is logged as-is.
+        */
+       uint32_t reply_ret_code = in_reply_ret_code;
+
+       if (reply_ret_code < AGENT_RET_CODE_SUCCESS ||
+                       reply_ret_code >= AGENT_RET_CODE_NR) {
+               reply_ret_code = AGENT_RET_CODE_NR;
+               level = PRINT_ERR;
+       }
+
+       LOG(level, "Agent replied with retcode: %s (%" PRIu32 ")",
+                       lttcomm_agent_ret_code_str((lttcomm_agent_ret_code) reply_ret_code),
+                       in_reply_ret_code);
+}
+
+/*
+ * Match function for the events hash table lookup by name.
+ */
+static int ht_match_event_by_name(struct cds_lfht_node *node,
+               const void *_key)
+{
+       struct agent_event *event;
+       const struct agent_ht_key *key;
+
+       LTTNG_ASSERT(node);
+       LTTNG_ASSERT(_key);
+
+       event = caa_container_of(node, struct agent_event, node.node);
+       key = (agent_ht_key *) _key;
+
+       /* Match 1 elements of the key: name. */
+
+       /* Event name */
+       if (strncmp(event->name, key->name, sizeof(event->name)) != 0) {
+               goto no_match;
+       }
+       /* Match. */
+       return 1;
+
+no_match:
+       return 0;
+}
+
+/*
+ * Match function for the events hash table lookup by name, log level and
+ * filter expression.
+ */
+static int ht_match_event(struct cds_lfht_node *node,
+               const void *_key)
+{
+       struct agent_event *event;
+       const struct agent_ht_key *key;
+       int ll_match;
+
+       LTTNG_ASSERT(node);
+       LTTNG_ASSERT(_key);
+
+       event = caa_container_of(node, struct agent_event, node.node);
+       key = (agent_ht_key *) _key;
+
+       /* Match 2 elements of the key: name and loglevel. */
+
+       /* Event name */
+       if (strncmp(event->name, key->name, sizeof(event->name)) != 0) {
+               goto no_match;
+       }
+
+       /* Event loglevel value and type. */
+       ll_match = loglevels_match(event->loglevel_type,
+               event->loglevel_value, key->loglevel_type,
+               key->loglevel_value, LTTNG_EVENT_LOGLEVEL_ALL);
+
+       if (!ll_match) {
+               goto no_match;
+       }
+
+       /* Filter expression */
+       if (!!event->filter_expression != !!key->filter_expression) {
+               /* One has a filter expression, the other does not */
+               goto no_match;
+       }
+
+       if (event->filter_expression) {
+               if (strncmp(event->filter_expression, key->filter_expression,
+                               strlen(event->filter_expression)) != 0) {
+                       goto no_match;
+               }
+       }
+
+       return 1;
+
+no_match:
+       return 0;
+}
+
+/*
+ * Add unique agent event based on the event name and loglevel.
+ */
+static void add_unique_agent_event(struct lttng_ht *ht,
+               struct agent_event *event)
+{
+       struct cds_lfht_node *node_ptr;
+       struct agent_ht_key key;
+
+       LTTNG_ASSERT(ht);
+       LTTNG_ASSERT(ht->ht);
+       LTTNG_ASSERT(event);
+
+       key.name = event->name;
+       key.loglevel_value = event->loglevel_value;
+       key.loglevel_type = event->loglevel_type;
+       key.filter_expression = event->filter_expression;
+
+       node_ptr = cds_lfht_add_unique(ht->ht,
+                       ht->hash_fct(event->node.key, lttng_ht_seed),
+                       ht_match_event, &key, &event->node.node);
+       LTTNG_ASSERT(node_ptr == &event->node.node);
+}
+
+/*
+ * URCU delayed agent event reclaim.
+ */
+static void destroy_event_agent_rcu(struct rcu_head *head)
+{
+       struct lttng_ht_node_str *node =
+               caa_container_of(head, struct lttng_ht_node_str, head);
+       struct agent_event *event =
+               caa_container_of(node, struct agent_event, node);
+
+       agent_destroy_event(event);
+}
+
+/*
+ * URCU delayed agent app reclaim.
+ */
+static void destroy_app_agent_rcu(struct rcu_head *head)
+{
+       struct lttng_ht_node_ulong *node =
+               caa_container_of(head, struct lttng_ht_node_ulong, head);
+       struct agent_app *app =
+               caa_container_of(node, struct agent_app, node);
+
+       free(app);
+}
+
+/*
+ * Communication with the agent. Send the message header to the given socket in
+ * big endian.
+ *
+ * Return 0 on success or else a negative errno message of sendmsg() op.
+ */
+static int send_header(struct lttcomm_sock *sock, uint64_t data_size,
+               uint32_t cmd, uint32_t cmd_version)
+{
+       int ret;
+       ssize_t size;
+       struct lttcomm_agent_hdr msg;
+
+       LTTNG_ASSERT(sock);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.data_size = htobe64(data_size);
+       msg.cmd = htobe32(cmd);
+       msg.cmd_version = htobe32(cmd_version);
+
+       size = sock->ops->sendmsg(sock, &msg, sizeof(msg), 0);
+       if (size < sizeof(msg)) {
+               ret = -errno;
+               goto error;
+       }
+       ret = 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Communication call with the agent. Send the payload to the given socket. The
+ * header MUST be sent prior to this call.
+ *
+ * Return 0 on success or else a negative errno value of sendmsg() op.
+ */
+static int send_payload(struct lttcomm_sock *sock, const void *data,
+               size_t size)
+{
+       int ret;
+       ssize_t len;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(data);
+
+       len = sock->ops->sendmsg(sock, data, size, 0);
+       if (len < size) {
+               ret = -errno;
+               goto error;
+       }
+       ret = 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Communication call with the agent. Receive reply from the agent using the
+ * given socket.
+ *
+ * Return 0 on success or else a negative errno value from recvmsg() op.
+ */
+static int recv_reply(struct lttcomm_sock *sock, void *buf, size_t size)
+{
+       int ret;
+       ssize_t len;
+
+       LTTNG_ASSERT(sock);
+       LTTNG_ASSERT(buf);
+
+       len = sock->ops->recvmsg(sock, buf, size, 0);
+       if (len < size) {
+               ret = -errno;
+               goto error;
+       }
+       ret = 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Internal event listing for a given app. Populate events.
+ *
+ * Return number of element in the list or else a negative LTTNG_ERR* code.
+ * On success, the caller is responsible for freeing the memory
+ * allocated for "events".
+ */
+static ssize_t list_events(struct agent_app *app, struct lttng_event **events)
+{
+       int ret, i, len = 0, offset = 0;
+       uint32_t nb_event;
+       size_t data_size;
+       uint32_t reply_ret_code;
+       struct lttng_event *tmp_events = NULL;
+       struct lttcomm_agent_list_reply *reply = NULL;
+       struct lttcomm_agent_list_reply_hdr reply_hdr;
+
+       LTTNG_ASSERT(app);
+       LTTNG_ASSERT(app->sock);
+       LTTNG_ASSERT(events);
+
+       DBG2("Agent listing events for app pid: %d and socket %d", app->pid,
+                       app->sock->fd);
+
+       ret = send_header(app->sock, 0, AGENT_CMD_LIST, 0);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       /* Get list header so we know how much we'll receive. */
+       ret = recv_reply(app->sock, &reply_hdr, sizeof(reply_hdr));
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       reply_ret_code = be32toh(reply_hdr.ret_code);
+       log_reply_code(reply_ret_code);
+       switch (reply_ret_code) {
+       case AGENT_RET_CODE_SUCCESS:
+               data_size = be32toh(reply_hdr.data_size) + sizeof(*reply);
+               break;
+       default:
+               ret = LTTNG_ERR_UNK;
+               goto error;
+       }
+
+       reply = (lttcomm_agent_list_reply *) zmalloc(data_size);
+       if (!reply) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       /* Get the list with the appropriate data size. */
+       ret = recv_reply(app->sock, reply, data_size);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       nb_event = be32toh(reply->nb_event);
+       tmp_events = (lttng_event *) zmalloc(sizeof(*tmp_events) * nb_event);
+       if (!tmp_events) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       for (i = 0; i < nb_event; i++) {
+               offset += len;
+               if (lttng_strncpy(tmp_events[i].name, reply->payload + offset,
+                               sizeof(tmp_events[i].name))) {
+                       ret = LTTNG_ERR_INVALID;
+                       goto error;
+               }
+               tmp_events[i].pid = app->pid;
+               tmp_events[i].enabled = -1;
+               len = strlen(reply->payload + offset) + 1;
+       }
+
+       *events = tmp_events;
+
+       free(reply);
+       return nb_event;
+
+error_io:
+       ret = LTTNG_ERR_UST_LIST_FAIL;
+error:
+       free(reply);
+       free(tmp_events);
+       return -ret;
+
+}
+
+/*
+ * Internal enable agent event on a agent application. This function
+ * communicates with the agent to enable a given event.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int enable_event(const struct agent_app *app, struct agent_event *event)
+{
+       int ret;
+       char *bytes_to_send;
+       uint64_t data_size;
+       size_t filter_expression_length;
+       uint32_t reply_ret_code;
+       struct lttcomm_agent_enable_event msg;
+       struct lttcomm_agent_generic_reply reply;
+
+       LTTNG_ASSERT(app);
+       LTTNG_ASSERT(app->sock);
+       LTTNG_ASSERT(event);
+
+       DBG2("Agent enabling event %s for app pid: %d and socket %d", event->name,
+                       app->pid, app->sock->fd);
+
+       /*
+        * Calculate the payload's size, which is the fixed-size struct followed
+        * by the variable-length filter expression (+1 for the ending \0).
+        */
+       if (!event->filter_expression) {
+               filter_expression_length = 0;
+       } else {
+               filter_expression_length = strlen(event->filter_expression) + 1;
+       }
+       data_size = sizeof(msg) + filter_expression_length;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.loglevel_value = htobe32(event->loglevel_value);
+       msg.loglevel_type = htobe32(event->loglevel_type);
+       if (lttng_strncpy(msg.name, event->name, sizeof(msg.name))) {
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+       msg.filter_expression_length = htobe32(filter_expression_length);
+
+       ret = send_header(app->sock, data_size, AGENT_CMD_ENABLE, 0);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       bytes_to_send = (char *) zmalloc(data_size);
+       if (!bytes_to_send) {
+               ret = LTTNG_ERR_NOMEM;
+               goto error;
+       }
+
+       memcpy(bytes_to_send, &msg, sizeof(msg));
+       if (filter_expression_length > 0) {
+               memcpy(bytes_to_send + sizeof(msg), event->filter_expression,
+                               filter_expression_length);
+       }
+
+       ret = send_payload(app->sock, bytes_to_send, data_size);
+       free(bytes_to_send);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       ret = recv_reply(app->sock, &reply, sizeof(reply));
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       reply_ret_code = be32toh(reply.ret_code);
+       log_reply_code(reply_ret_code);
+       switch (reply_ret_code) {
+       case AGENT_RET_CODE_SUCCESS:
+               break;
+       case AGENT_RET_CODE_UNKNOWN_NAME:
+               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+               goto error;
+       default:
+               ret = LTTNG_ERR_UNK;
+               goto error;
+       }
+
+       return LTTNG_OK;
+
+error_io:
+       ret = LTTNG_ERR_UST_ENABLE_FAIL;
+error:
+       return ret;
+}
+
+/*
+ * Send Pascal-style string. Size is sent as a 32-bit big endian integer.
+ */
+static
+int send_pstring(struct lttcomm_sock *sock, const char *str, uint32_t len)
+{
+       int ret;
+       uint32_t len_be;
+
+       len_be = htobe32(len);
+       ret = send_payload(sock, &len_be, sizeof(len_be));
+       if (ret) {
+               goto end;
+       }
+
+       ret = send_payload(sock, str, len);
+       if (ret) {
+               goto end;
+       }
+end:
+       return ret;
+}
+
+/*
+ * Internal enable application context on an agent application. This function
+ * communicates with the agent to enable a given application context.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int app_context_op(const struct agent_app *app,
+               const struct agent_app_ctx *ctx, enum lttcomm_agent_command cmd)
+{
+       int ret;
+       uint32_t reply_ret_code;
+       struct lttcomm_agent_generic_reply reply;
+       size_t app_ctx_provider_name_len, app_ctx_name_len, data_size;
+
+       LTTNG_ASSERT(app);
+       LTTNG_ASSERT(app->sock);
+       LTTNG_ASSERT(ctx);
+       LTTNG_ASSERT(cmd == AGENT_CMD_APP_CTX_ENABLE ||
+                       cmd == AGENT_CMD_APP_CTX_DISABLE);
+
+       DBG2("Agent %s application %s:%s for app pid: %d and socket %d",
+                       cmd == AGENT_CMD_APP_CTX_ENABLE ? "enabling" : "disabling",
+                       ctx->provider_name, ctx->ctx_name,
+                       app->pid, app->sock->fd);
+
+       /*
+        * Calculate the payload's size, which consists of the size (u32, BE)
+        * of the provider name, the NULL-terminated provider name string, the
+        * size (u32, BE) of the context name, followed by the NULL-terminated
+        * context name string.
+        */
+       app_ctx_provider_name_len = strlen(ctx->provider_name) + 1;
+       app_ctx_name_len = strlen(ctx->ctx_name) + 1;
+       data_size = sizeof(uint32_t) + app_ctx_provider_name_len +
+                       sizeof(uint32_t) + app_ctx_name_len;
+
+       ret = send_header(app->sock, data_size, cmd, 0);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       if (app_ctx_provider_name_len > UINT32_MAX ||
+                       app_ctx_name_len > UINT32_MAX) {
+               ERR("Application context name > MAX_UINT32");
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       ret = send_pstring(app->sock, ctx->provider_name,
+                       (uint32_t) app_ctx_provider_name_len);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       ret = send_pstring(app->sock, ctx->ctx_name,
+                       (uint32_t) app_ctx_name_len);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       ret = recv_reply(app->sock, &reply, sizeof(reply));
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       reply_ret_code = be32toh(reply.ret_code);
+       log_reply_code(reply_ret_code);
+       switch (reply_ret_code) {
+       case AGENT_RET_CODE_SUCCESS:
+               break;
+       default:
+               ret = LTTNG_ERR_UNK;
+               goto error;
+       }
+
+       return LTTNG_OK;
+
+error_io:
+       ret = LTTNG_ERR_UST_ENABLE_FAIL;
+error:
+       return ret;
+}
+
+/*
+ * Internal disable agent event call on a agent application. This function
+ * communicates with the agent to disable a given event.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int disable_event(struct agent_app *app, struct agent_event *event)
+{
+       int ret;
+       uint64_t data_size;
+       uint32_t reply_ret_code;
+       struct lttcomm_agent_disable_event msg;
+       struct lttcomm_agent_generic_reply reply;
+
+       LTTNG_ASSERT(app);
+       LTTNG_ASSERT(app->sock);
+       LTTNG_ASSERT(event);
+
+       DBG2("Agent disabling event %s for app pid: %d and socket %d", event->name,
+                       app->pid, app->sock->fd);
+
+       data_size = sizeof(msg);
+       memset(&msg, 0, sizeof(msg));
+       if (lttng_strncpy(msg.name, event->name, sizeof(msg.name))) {
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       ret = send_header(app->sock, data_size, AGENT_CMD_DISABLE, 0);
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       ret = send_payload(app->sock, &msg, sizeof(msg));
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       ret = recv_reply(app->sock, &reply, sizeof(reply));
+       if (ret < 0) {
+               goto error_io;
+       }
+
+       reply_ret_code = be32toh(reply.ret_code);
+       log_reply_code(reply_ret_code);
+       switch (reply_ret_code) {
+       case AGENT_RET_CODE_SUCCESS:
+               break;
+       case AGENT_RET_CODE_UNKNOWN_NAME:
+               ret = LTTNG_ERR_UST_EVENT_NOT_FOUND;
+               goto error;
+       default:
+               ret = LTTNG_ERR_UNK;
+               goto error;
+       }
+
+       return LTTNG_OK;
+
+error_io:
+       ret = LTTNG_ERR_UST_DISABLE_FAIL;
+error:
+       return ret;
+}
+
+/*
+ * Send back the registration DONE command to a given agent application.
+ *
+ * Return 0 on success or else a negative value.
+ */
+int agent_send_registration_done(struct agent_app *app)
+{
+       LTTNG_ASSERT(app);
+       LTTNG_ASSERT(app->sock);
+
+       DBG("Agent sending registration done to app socket %d", app->sock->fd);
+
+       return send_header(app->sock, 0, AGENT_CMD_REG_DONE, 0);
+}
+
+/*
+ * Enable agent event on every agent applications registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int agent_enable_event(struct agent_event *event,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       struct agent_app *app;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(event);
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
+                       node.node) {
+               if (app->domain != domain) {
+                       continue;
+               }
+
+               /* Enable event on agent application through TCP socket. */
+               ret = enable_event(app, event);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+       }
+
+       event->enabled_count++;
+       ret = LTTNG_OK;
+
+error:
+       rcu_read_unlock();
+       return ret;
+}
+
+static
+void destroy_app_ctx(struct agent_app_ctx *ctx)
+{
+       free(ctx->provider_name);
+       free(ctx->ctx_name);
+       free(ctx);
+}
+
+static
+struct agent_app_ctx *create_app_ctx(const struct lttng_event_context *ctx)
+{
+       struct agent_app_ctx *agent_ctx = NULL;
+
+       if (!ctx) {
+               goto end;
+       }
+
+       LTTNG_ASSERT(ctx->ctx == LTTNG_EVENT_CONTEXT_APP_CONTEXT);
+       agent_ctx = (agent_app_ctx *) zmalloc(sizeof(*ctx));
+       if (!agent_ctx) {
+               goto end;
+       }
+
+       agent_ctx->provider_name = strdup(ctx->u.app_ctx.provider_name);
+       agent_ctx->ctx_name = strdup(ctx->u.app_ctx.ctx_name);
+       if (!agent_ctx->provider_name || !agent_ctx->ctx_name) {
+               destroy_app_ctx(agent_ctx);
+               agent_ctx = NULL;
+       }
+end:
+       return agent_ctx;
+}
+
+/*
+ * Enable agent context on every agent applications registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int agent_enable_context(const struct lttng_event_context *ctx,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       struct agent_app *app;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(ctx);
+       if (ctx->ctx != LTTNG_EVENT_CONTEXT_APP_CONTEXT) {
+               ret = LTTNG_ERR_INVALID;
+               goto error;
+       }
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
+                       node.node) {
+               struct agent_app_ctx *agent_ctx;
+
+               if (app->domain != domain) {
+                       continue;
+               }
+
+               agent_ctx = create_app_ctx(ctx);
+               if (!agent_ctx) {
+                       ret = LTTNG_ERR_NOMEM;
+                       goto error_unlock;
+               }
+
+               /* Enable event on agent application through TCP socket. */
+               ret = app_context_op(app, agent_ctx, AGENT_CMD_APP_CTX_ENABLE);
+               destroy_app_ctx(agent_ctx);
+               if (ret != LTTNG_OK) {
+                       goto error_unlock;
+               }
+       }
+
+       ret = LTTNG_OK;
+
+error_unlock:
+       rcu_read_unlock();
+error:
+       return ret;
+}
+
+/*
+ * Disable agent event on every agent application registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+int agent_disable_event(struct agent_event *event,
+               enum lttng_domain_type domain)
+{
+       int ret = LTTNG_OK;
+       struct agent_app *app;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(event);
+       if (!AGENT_EVENT_IS_ENABLED(event)) {
+               goto end;
+       }
+
+       if (--event->enabled_count != 0) {
+               /*
+                * Agent event still enabled. Disable the agent event only when
+                * all "users" have disabled it (event notifiers, event rules,
+                * etc.).
+                */
+               ret = LTTNG_OK;
+               goto end;
+       }
+
+       rcu_read_lock();
+
+       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
+                       node.node) {
+               if (app->domain != domain) {
+                       continue;
+               }
+
+               /* Enable event on agent application through TCP socket. */
+               ret = disable_event(app, event);
+               if (ret != LTTNG_OK) {
+                       goto error;
+               }
+       }
+
+       /* event->enabled_count is now 0. */
+       LTTNG_ASSERT(!AGENT_EVENT_IS_ENABLED(event));
+
+error:
+       rcu_read_unlock();
+end:
+       return ret;
+}
+
+/*
+ * Disable agent context on every agent application registered with the session
+ * daemon.
+ *
+ * Return LTTNG_OK on success or else a LTTNG_ERR* code.
+ */
+static int disable_context(struct agent_app_ctx *ctx,
+               enum lttng_domain_type domain)
+{
+       int ret = LTTNG_OK;
+       struct agent_app *app;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(ctx);
+
+       rcu_read_lock();
+       DBG2("Disabling agent application context %s:%s",
+                       ctx->provider_name, ctx->ctx_name);
+       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
+                       node.node) {
+               if (app->domain != domain) {
+                       continue;
+               }
+
+               ret = app_context_op(app, ctx, AGENT_CMD_APP_CTX_DISABLE);
+               if (ret != LTTNG_OK) {
+                       goto end;
+               }
+       }
+end:
+       rcu_read_unlock();
+       return ret;
+}
+
+/*
+ * Ask every agent for the list of possible event. Events is allocated with the
+ * events of every agent application.
+ *
+ * Return the number of events or else a negative value.
+ */
+int agent_list_events(struct lttng_event **events,
+               enum lttng_domain_type domain)
+{
+       int ret;
+       size_t nbmem, count = 0;
+       struct agent_app *app;
+       struct lttng_event *tmp_events = NULL;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(events);
+
+       DBG2("Agent listing events for domain %d", domain);
+
+       nbmem = UST_APP_EVENT_LIST_SIZE;
+       tmp_events = (lttng_event *) zmalloc(nbmem * sizeof(*tmp_events));
+       if (!tmp_events) {
+               PERROR("zmalloc agent list events");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(the_agent_apps_ht_by_sock->ht, &iter.iter, app,
+                       node.node) {
+               ssize_t nb_ev;
+               struct lttng_event *agent_events;
+
+               /* Skip domain not asked by the list. */
+               if (app->domain != domain) {
+                       continue;
+               }
+
+               nb_ev = list_events(app, &agent_events);
+               if (nb_ev < 0) {
+                       ret = nb_ev;
+                       goto error_unlock;
+               }
+
+               if (count + nb_ev > nbmem) {
+                       /* In case the realloc fails, we free the memory */
+                       struct lttng_event *new_tmp_events;
+                       size_t new_nbmem;
+
+                       new_nbmem = std::max(count + nb_ev, nbmem << 1);
+                       DBG2("Reallocating agent event list from %zu to %zu entries",
+                                       nbmem, new_nbmem);
+                       new_tmp_events = (lttng_event *) realloc(tmp_events,
+                               new_nbmem * sizeof(*new_tmp_events));
+                       if (!new_tmp_events) {
+                               PERROR("realloc agent events");
+                               ret = -ENOMEM;
+                               free(agent_events);
+                               goto error_unlock;
+                       }
+                       /* Zero the new memory */
+                       memset(new_tmp_events + nbmem, 0,
+                               (new_nbmem - nbmem) * sizeof(*new_tmp_events));
+                       nbmem = new_nbmem;
+                       tmp_events = new_tmp_events;
+               }
+               memcpy(tmp_events + count, agent_events,
+                       nb_ev * sizeof(*tmp_events));
+               free(agent_events);
+               count += nb_ev;
+       }
+       rcu_read_unlock();
+
+       ret = count;
+       *events = tmp_events;
+       return ret;
+
+error_unlock:
+       rcu_read_unlock();
+error:
+       free(tmp_events);
+       return ret;
+}
+
+/*
+ * Create a agent app object using the given PID.
+ *
+ * Return newly allocated object or else NULL on error.
+ */
+struct agent_app *agent_create_app(pid_t pid, enum lttng_domain_type domain,
+               struct lttcomm_sock *sock)
+{
+       struct agent_app *app;
+
+       LTTNG_ASSERT(sock);
+
+       app = (agent_app *) zmalloc(sizeof(*app));
+       if (!app) {
+               PERROR("Failed to allocate agent application instance");
+               goto error;
+       }
+
+       app->pid = pid;
+       app->domain = domain;
+       app->sock = sock;
+       lttng_ht_node_init_ulong(&app->node, (unsigned long) app->sock->fd);
+
+error:
+       return app;
+}
+
+/*
+ * Lookup agent app by socket in the global hash table.
+ *
+ * RCU read side lock MUST be acquired.
+ *
+ * Return object if found else NULL.
+ */
+struct agent_app *agent_find_app_by_sock(int sock)
+{
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
+       struct agent_app *app;
+
+       LTTNG_ASSERT(sock >= 0);
+
+       lttng_ht_lookup(the_agent_apps_ht_by_sock,
+                       (void *) ((unsigned long) sock), &iter);
+       node = lttng_ht_iter_get_node_ulong(&iter);
+       if (node == NULL) {
+               goto error;
+       }
+       app = caa_container_of(node, struct agent_app, node);
+
+       DBG3("Agent app pid %d found by sock %d.", app->pid, sock);
+       return app;
+
+error:
+       DBG3("Agent app NOT found by sock %d.", sock);
+       return NULL;
+}
+
+/*
+ * Add agent application object to the global hash table.
+ */
+void agent_add_app(struct agent_app *app)
+{
+       LTTNG_ASSERT(app);
+
+       DBG3("Agent adding app sock: %d and pid: %d to ht", app->sock->fd, app->pid);
+       lttng_ht_add_unique_ulong(the_agent_apps_ht_by_sock, &app->node);
+}
+
+/*
+ * Delete agent application from the global hash table.
+ *
+ * rcu_read_lock() must be held by the caller.
+ */
+void agent_delete_app(struct agent_app *app)
+{
+       int ret;
+       struct lttng_ht_iter iter;
+
+       LTTNG_ASSERT(app);
+
+       DBG3("Agent deleting app pid: %d and sock: %d", app->pid, app->sock->fd);
+
+       iter.iter.node = &app->node.node;
+       ret = lttng_ht_del(the_agent_apps_ht_by_sock, &iter);
+       LTTNG_ASSERT(!ret);
+}
+
+/*
+ * Destroy an agent application object by detaching it from its corresponding
+ * UST app if one is connected by closing the socket. Finally, perform a
+ * delayed memory reclaim.
+ */
+void agent_destroy_app(struct agent_app *app)
+{
+       LTTNG_ASSERT(app);
+
+       if (app->sock) {
+               app->sock->ops->close(app->sock);
+               lttcomm_destroy_sock(app->sock);
+       }
+
+       call_rcu(&app->node.head, destroy_app_agent_rcu);
+}
+
+/*
+ * Initialize an already allocated agent object.
+ *
+ * Return 0 on success or else a negative errno value.
+ */
+int agent_init(struct agent *agt)
+{
+       int ret;
+
+       LTTNG_ASSERT(agt);
+
+       agt->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
+       if (!agt->events) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       lttng_ht_node_init_u64(&agt->node, agt->domain);
+
+       CDS_INIT_LIST_HEAD(&agt->app_ctx_list);
+       return 0;
+
+error:
+       return ret;
+}
+
+/*
+ * Add agent object to the given hash table.
+ */
+void agent_add(struct agent *agt, struct lttng_ht *ht)
+{
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(ht);
+
+       DBG3("Agent adding from domain %d", agt->domain);
+
+       lttng_ht_add_unique_u64(ht, &agt->node);
+}
+
+/*
+ * Create an agent object for the given domain.
+ *
+ * Return the allocated agent or NULL on error.
+ */
+struct agent *agent_create(enum lttng_domain_type domain)
+{
+       int ret;
+       struct agent *agt;
+
+       agt = (agent *) zmalloc(sizeof(struct agent));
+       if (!agt) {
+               goto error;
+       }
+       agt->domain = domain;
+
+       ret = agent_init(agt);
+       if (ret < 0) {
+               free(agt);
+               agt = NULL;
+               goto error;
+       }
+
+error:
+       return agt;
+}
+
+/*
+ * Create a newly allocated agent event data structure.
+ * Ownership of filter_expression is taken.
+ *
+ * Return a new object else NULL on error.
+ */
+struct agent_event *agent_create_event(const char *name,
+               enum lttng_loglevel_type loglevel_type, int loglevel_value,
+               struct lttng_bytecode *filter, char *filter_expression)
+{
+       struct agent_event *event = NULL;
+
+       DBG3("Agent create new event with name %s, loglevel type %d, \
+                       loglevel value %d and filter %s",
+                       name, loglevel_type, loglevel_value,
+                       filter_expression ? filter_expression : "NULL");
+
+       if (!name) {
+               ERR("Failed to create agent event; no name provided.");
+               goto error;
+       }
+
+       event = (agent_event *) zmalloc(sizeof(*event));
+       if (!event) {
+               goto error;
+       }
+
+       strncpy(event->name, name, sizeof(event->name));
+       event->name[sizeof(event->name) - 1] = '\0';
+       lttng_ht_node_init_str(&event->node, event->name);
+
+       event->loglevel_value = loglevel_value;
+       event->loglevel_type = loglevel_type;
+       event->filter = filter;
+       event->filter_expression = filter_expression;
+error:
+       return event;
+}
+
+/*
+ * Unique add of a agent event to an agent object.
+ */
+void agent_add_event(struct agent_event *event, struct agent *agt)
+{
+       LTTNG_ASSERT(event);
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(agt->events);
+
+       DBG3("Agent adding event %s", event->name);
+       add_unique_agent_event(agt->events, event);
+       agt->being_used = 1;
+}
+
+/*
+ * Unique add of a agent context to an agent object.
+ */
+int agent_add_context(const struct lttng_event_context *ctx, struct agent *agt)
+{
+       int ret = LTTNG_OK;
+       struct agent_app_ctx *agent_ctx = NULL;
+
+       LTTNG_ASSERT(ctx);
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(agt->events);
+       LTTNG_ASSERT(ctx->ctx == LTTNG_EVENT_CONTEXT_APP_CONTEXT);
+
+       agent_ctx = create_app_ctx(ctx);
+       if (!agent_ctx) {
+               ret = LTTNG_ERR_NOMEM;
+               goto end;
+       }
+
+       DBG3("Agent adding context %s:%s", ctx->u.app_ctx.provider_name,
+                       ctx->u.app_ctx.ctx_name);
+       cds_list_add_tail_rcu(&agent_ctx->list_node, &agt->app_ctx_list);
+end:
+       return ret;
+}
+
+/*
+ * Find multiple agent events sharing the given name.
+ *
+ * RCU read side lock MUST be acquired. It must be held for the
+ * duration of the iteration.
+ *
+ * Sets the given iterator.
+ */
+void agent_find_events_by_name(const char *name, struct agent *agt,
+               struct lttng_ht_iter* iter)
+{
+       struct lttng_ht *ht;
+       struct agent_ht_key key;
+
+       LTTNG_ASSERT(name);
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(agt->events);
+       LTTNG_ASSERT(iter);
+
+       ht = agt->events;
+       key.name = name;
+
+       cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed),
+                       ht_match_event_by_name, &key, &iter->iter);
+}
+
+/*
+ * Find the agent event matching a trigger.
+ *
+ * RCU read side lock MUST be acquired. It must be held for as long as
+ * the returned agent_event is used.
+ *
+ * Return object if found else NULL.
+ */
+struct agent_event *agent_find_event_by_trigger(
+               const struct lttng_trigger *trigger, struct agent *agt)
+{
+       enum lttng_condition_status c_status;
+       enum lttng_event_rule_status er_status;
+       enum lttng_domain_type domain;
+       const struct lttng_condition *condition;
+       const struct lttng_event_rule *rule;
+       const char *name;
+       const char *filter_expression;
+       const struct lttng_log_level_rule *log_level_rule;
+       /* Unused when loglevel_type is 'ALL'. */
+       int loglevel_value = 0;
+       enum lttng_loglevel_type loglevel_type;
+       event_rule_logging_get_name_pattern logging_get_name_pattern;
+       event_rule_logging_get_log_level_rule logging_get_log_level_rule;
+
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(agt->events);
+
+       condition = lttng_trigger_get_const_condition(trigger);
+
+       LTTNG_ASSERT(lttng_condition_get_type(condition) ==
+                       LTTNG_CONDITION_TYPE_EVENT_RULE_MATCHES);
+
+       c_status = lttng_condition_event_rule_matches_get_rule(
+                       condition, &rule);
+       LTTNG_ASSERT(c_status == LTTNG_CONDITION_STATUS_OK);
+
+       switch (lttng_event_rule_get_type(rule)) {
+       case LTTNG_EVENT_RULE_TYPE_JUL_LOGGING:
+               logging_get_name_pattern =
+                               lttng_event_rule_jul_logging_get_name_pattern;
+               logging_get_log_level_rule =
+                               lttng_event_rule_jul_logging_get_log_level_rule;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_LOG4J_LOGGING:
+               logging_get_name_pattern =
+                               lttng_event_rule_log4j_logging_get_name_pattern;
+               logging_get_log_level_rule =
+                               lttng_event_rule_log4j_logging_get_log_level_rule;
+               break;
+       case LTTNG_EVENT_RULE_TYPE_PYTHON_LOGGING:
+               logging_get_name_pattern =
+                               lttng_event_rule_python_logging_get_name_pattern;
+               logging_get_log_level_rule =
+                               lttng_event_rule_python_logging_get_log_level_rule;
+               break;
+       default:
+               abort();
+               break;
+       }
+
+       domain = lttng_event_rule_get_domain_type(rule);
+       LTTNG_ASSERT(domain == LTTNG_DOMAIN_JUL || domain == LTTNG_DOMAIN_LOG4J ||
+                       domain == LTTNG_DOMAIN_PYTHON);
+
+       /* Get the event's pattern name ('name' in the legacy terminology). */
+       er_status = logging_get_name_pattern(rule, &name);
+       LTTNG_ASSERT(er_status == LTTNG_EVENT_RULE_STATUS_OK);
+
+       /* Get the internal filter expression. */
+       filter_expression = lttng_event_rule_get_filter(rule);
+
+       /* Map log_level_rule to loglevel value. */
+       er_status = logging_get_log_level_rule(rule, &log_level_rule);
+       if (er_status == LTTNG_EVENT_RULE_STATUS_UNSET) {
+               loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
+               loglevel_value = 0;
+       } else if (er_status == LTTNG_EVENT_RULE_STATUS_OK) {
+               lttng_log_level_rule_to_loglevel(log_level_rule, &loglevel_type, &loglevel_value);
+       } else {
+               abort();
+       }
+
+       return agent_find_event(name, loglevel_type, loglevel_value,
+                       filter_expression, agt);
+}
+
+/*
+ * Get the next agent event duplicate by name. This should be called
+ * after a call to agent_find_events_by_name() to iterate on events.
+ *
+ * The RCU read lock must be held during the iteration and for as long
+ * as the object the iterator points to remains in use.
+ */
+void agent_event_next_duplicate(const char *name,
+               struct agent *agt, struct lttng_ht_iter* iter)
+{
+       struct agent_ht_key key;
+
+       key.name = name;
+
+       cds_lfht_next_duplicate(agt->events->ht, ht_match_event_by_name,
+               &key, &iter->iter);
+}
+
+/*
+ * Find a agent event in the given agent using name, loglevel and filter.
+ *
+ * RCU read side lock MUST be acquired. It must be kept for as long as
+ * the returned agent_event is used.
+ *
+ * Return object if found else NULL.
+ */
+struct agent_event *agent_find_event(const char *name,
+               enum lttng_loglevel_type loglevel_type,
+               int loglevel_value,
+               const char *filter_expression,
+               struct agent *agt)
+{
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
+       struct lttng_ht *ht;
+       struct agent_ht_key key;
+
+       LTTNG_ASSERT(name);
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(agt->events);
+
+       ht = agt->events;
+       key.name = name;
+       key.loglevel_value = loglevel_value;
+       key.loglevel_type = loglevel_type;
+       key.filter_expression = filter_expression;
+
+       cds_lfht_lookup(ht->ht, ht->hash_fct((void *) name, lttng_ht_seed),
+                       ht_match_event, &key, &iter.iter);
+       node = lttng_ht_iter_get_node_str(&iter);
+       if (node == NULL) {
+               goto error;
+       }
+
+       DBG3("Agent event found %s.", name);
+       return caa_container_of(node, struct agent_event, node);
+
+error:
+       DBG3("Agent event NOT found %s.", name);
+       return NULL;
+}
+
+/*
+ * Free given agent event. This event must not be globally visible at this
+ * point (only expected to be used on failure just after event creation). After
+ * this call, the pointer is not usable anymore.
+ */
+void agent_destroy_event(struct agent_event *event)
+{
+       LTTNG_ASSERT(event);
+
+       free(event->filter);
+       free(event->filter_expression);
+       free(event->exclusion);
+       free(event);
+}
+
+static
+void destroy_app_ctx_rcu(struct rcu_head *head)
+{
+       struct agent_app_ctx *ctx =
+                       caa_container_of(head, struct agent_app_ctx, rcu_node);
+
+       destroy_app_ctx(ctx);
+}
+
+/*
+ * Destroy an agent completely.
+ */
+void agent_destroy(struct agent *agt)
+{
+       struct lttng_ht_node_str *node;
+       struct lttng_ht_iter iter;
+       struct agent_app_ctx *ctx;
+
+       LTTNG_ASSERT(agt);
+
+       DBG3("Agent destroy");
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, node, node) {
+               int ret;
+               struct agent_event *event;
+
+               /*
+                * When destroying an event, we have to try to disable it on the
+                * agent side so the event stops generating data. The return
+                * value is not important since we have to continue anyway
+                * destroying the object.
+                */
+               event = caa_container_of(node, struct agent_event, node);
+               (void) agent_disable_event(event, agt->domain);
+
+               ret = lttng_ht_del(agt->events, &iter);
+               LTTNG_ASSERT(!ret);
+               call_rcu(&node->head, destroy_event_agent_rcu);
+       }
+
+       cds_list_for_each_entry_rcu(ctx, &agt->app_ctx_list, list_node) {
+               (void) disable_context(ctx, agt->domain);
+               cds_list_del(&ctx->list_node);
+               call_rcu(&ctx->rcu_node, destroy_app_ctx_rcu);
+       }
+       rcu_read_unlock();
+       ht_cleanup_push(agt->events);
+       free(agt);
+}
+
+/*
+ * Allocate agent_apps_ht_by_sock.
+ */
+int agent_app_ht_alloc(void)
+{
+       the_agent_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+       return the_agent_apps_ht_by_sock ? 0 : -1;
+}
+
+/*
+ * Destroy a agent application by socket.
+ */
+void agent_destroy_app_by_sock(int sock)
+{
+       struct agent_app *app;
+
+       LTTNG_ASSERT(sock >= 0);
+
+       /*
+        * Not finding an application is a very important error that should NEVER
+        * happen. The hash table deletion is ONLY done through this call when the
+        * main sessiond thread is torn down.
+        */
+       rcu_read_lock();
+       app = agent_find_app_by_sock(sock);
+       LTTNG_ASSERT(app);
+
+       /* RCU read side lock is assumed to be held by this function. */
+       agent_delete_app(app);
+
+       /* The application is freed in a RCU call but the socket is closed here. */
+       agent_destroy_app(app);
+       rcu_read_unlock();
+}
+
+/*
+ * Clean-up the agent app hash table and destroy it.
+ */
+void agent_app_ht_clean(void)
+{
+       struct lttng_ht_node_ulong *node;
+       struct lttng_ht_iter iter;
+
+       if (!the_agent_apps_ht_by_sock) {
+               return;
+       }
+       rcu_read_lock();
+       cds_lfht_for_each_entry(
+                       the_agent_apps_ht_by_sock->ht, &iter.iter, node, node) {
+               struct agent_app *app;
+
+               app = caa_container_of(node, struct agent_app, node);
+               agent_destroy_app_by_sock(app->sock->fd);
+       }
+       rcu_read_unlock();
+
+       lttng_ht_destroy(the_agent_apps_ht_by_sock);
+}
+
+/*
+ * Update a agent application (given socket) using the given agent.
+ *
+ * Note that this function is most likely to be used with a tracing session
+ * thus the caller should make sure to hold the appropriate lock(s).
+ */
+void agent_update(const struct agent *agt, const struct agent_app *app)
+{
+       int ret;
+       struct agent_event *event;
+       struct lttng_ht_iter iter;
+       struct agent_app_ctx *ctx;
+
+       LTTNG_ASSERT(agt);
+       LTTNG_ASSERT(app);
+
+       DBG("Agent updating app: pid = %ld", (long) app->pid);
+
+       rcu_read_lock();
+       /*
+        * We are in the registration path thus if the application is gone,
+        * there is a serious code flow error.
+        */
+
+       cds_lfht_for_each_entry(agt->events->ht, &iter.iter, event, node.node) {
+               /* Skip event if disabled. */
+               if (!AGENT_EVENT_IS_ENABLED(event)) {
+                       continue;
+               }
+
+               ret = enable_event(app, event);
+               if (ret != LTTNG_OK) {
+                       DBG2("Agent update unable to enable event %s on app pid: %d sock %d",
+                                       event->name, app->pid, app->sock->fd);
+                       /* Let's try the others here and don't assume the app is dead. */
+                       continue;
+               }
+       }
+
+       cds_list_for_each_entry_rcu(ctx, &agt->app_ctx_list, list_node) {
+               ret = app_context_op(app, ctx, AGENT_CMD_APP_CTX_ENABLE);
+               if (ret != LTTNG_OK) {
+                       DBG2("Agent update unable to add application context %s:%s on app pid: %d sock %d",
+                                       ctx->provider_name, ctx->ctx_name,
+                                       app->pid, app->sock->fd);
+                       continue;
+               }
+       }
+
+       rcu_read_unlock();
+}
+
+/*
+ * Allocate the per-event notifier domain agent hash table. It is lazily
+ * populated as domains are used.
+ */
+int agent_by_event_notifier_domain_ht_create(void)
+{
+       the_trigger_agents_ht_by_domain = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+       return the_trigger_agents_ht_by_domain ? 0 : -1;
+}
+
+/*
+ * Clean-up the per-event notifier domain agent hash table and destroy it.
+ */
+void agent_by_event_notifier_domain_ht_destroy(void)
+{
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+
+       if (!the_trigger_agents_ht_by_domain) {
+               return;
+       }
+
+       rcu_read_lock();
+       cds_lfht_for_each_entry(the_trigger_agents_ht_by_domain->ht,
+                       &iter.iter, node, node) {
+               struct agent *agent =
+                               caa_container_of(node, struct agent, node);
+               const int ret = lttng_ht_del(
+                               the_trigger_agents_ht_by_domain, &iter);
+
+               LTTNG_ASSERT(ret == 0);
+               agent_destroy(agent);
+       }
+
+       rcu_read_unlock();
+       lttng_ht_destroy(the_trigger_agents_ht_by_domain);
+}
+
+struct agent *agent_find_by_event_notifier_domain(
+               enum lttng_domain_type domain_type)
+{
+       struct agent *agt = NULL;
+       struct lttng_ht_node_u64 *node;
+       struct lttng_ht_iter iter;
+       const uint64_t key = (uint64_t) domain_type;
+
+       LTTNG_ASSERT(the_trigger_agents_ht_by_domain);
+
+       DBG3("Per-event notifier domain agent lookup for domain '%s'",
+                       lttng_domain_type_str(domain_type));
+
+       lttng_ht_lookup(the_trigger_agents_ht_by_domain, &key, &iter);
+       node = lttng_ht_iter_get_node_u64(&iter);
+       if (!node) {
+               goto end;
+       }
+
+       agt = caa_container_of(node, struct agent, node);
+
+end:
+       return agt;
+}
diff --git a/src/bin/lttng-sessiond/buffer-registry.c b/src/bin/lttng-sessiond/buffer-registry.c
deleted file mode 100644 (file)
index 3390f87..0000000
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- *
- * SPDX-License-Identifier: GPL-2.0-only
- *
- */
-
-#define _LGPL_SOURCE
-#include <inttypes.h>
-
-#include <common/common.h>
-#include <common/hashtable/utils.h>
-
-#include "buffer-registry.h"
-#include "fd-limit.h"
-#include "ust-consumer.h"
-#include "lttng-ust-ctl.h"
-#include "lttng-ust-error.h"
-#include "utils.h"
-
-/*
- * Set in main.c during initialization process of the daemon. This contains
- * buffer_reg_uid object which are global registry for per UID buffer. Object
- * are indexed by session id and matched by the triplet
- * <session_id/bits_per_long/uid>.
- */
-static struct lttng_ht *buffer_registry_uid;
-
-/*
- * Initialized at the daemon start. This contains buffer_reg_pid object and
- * indexed by session id.
- */
-static struct lttng_ht *buffer_registry_pid;
-
-/*
- * Match function for the per UID registry hash table. It matches a registry
- * uid object with the triplet <session_id/abi/uid>.
- */
-static int ht_match_reg_uid(struct cds_lfht_node *node, const void *_key)
-{
-       struct buffer_reg_uid *reg;
-       const struct buffer_reg_uid *key;
-
-       LTTNG_ASSERT(node);
-       LTTNG_ASSERT(_key);
-
-       reg = caa_container_of(node, struct buffer_reg_uid, node.node);
-       LTTNG_ASSERT(reg);
-       key = _key;
-
-       if (key->session_id != reg->session_id ||
-                       key->bits_per_long != reg->bits_per_long ||
-                       key->uid != reg->uid) {
-               goto no_match;
-       }
-
-       /* Match */
-       return 1;
-no_match:
-       return 0;
-}
-
-/*
- * Hash function for the per UID registry hash table. This XOR the triplet
- * together.
- */
-static unsigned long ht_hash_reg_uid(const void *_key, unsigned long seed)
-{
-       uint64_t xored_key;
-       const struct buffer_reg_uid *key = _key;
-
-       LTTNG_ASSERT(key);
-
-       xored_key = (uint64_t)(key->session_id ^ key->bits_per_long ^ key->uid);
-       return hash_key_u64(&xored_key, seed);
-}
-
-/*
- * Initialize global buffer per UID registry. Should only be called ONCE!.
- */
-void buffer_reg_init_uid_registry(void)
-{
-       /* Should be called once. */
-       LTTNG_ASSERT(!buffer_registry_uid);
-       buffer_registry_uid = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
-       LTTNG_ASSERT(buffer_registry_uid);
-       buffer_registry_uid->match_fct = ht_match_reg_uid;
-       buffer_registry_uid->hash_fct = ht_hash_reg_uid;
-
-       DBG3("Global buffer per UID registry initialized");
-}
-
-/*
- * Allocate and initialize object. Set regp with the object pointer.
- *
- * Return 0 on success else a negative value and regp is untouched.
- */
-int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid,
-               enum lttng_domain_type domain, struct buffer_reg_uid **regp,
-               const char *root_shm_path, const char *shm_path)
-{
-       int ret = 0;
-       struct buffer_reg_uid *reg = NULL;
-
-       LTTNG_ASSERT(regp);
-
-       reg = zmalloc(sizeof(*reg));
-       if (!reg) {
-               PERROR("zmalloc buffer registry uid");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       reg->registry = zmalloc(sizeof(struct buffer_reg_session));
-       if (!reg->registry) {
-               PERROR("zmalloc buffer registry uid session");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       reg->session_id = session_id;
-       reg->bits_per_long = bits_per_long;
-       reg->uid = uid;
-       reg->domain = domain;
-       if (shm_path[0]) {
-               strncpy(reg->root_shm_path, root_shm_path, sizeof(reg->root_shm_path));
-               reg->root_shm_path[sizeof(reg->root_shm_path) - 1] = '\0';
-               strncpy(reg->shm_path, shm_path, sizeof(reg->shm_path));
-               reg->shm_path[sizeof(reg->shm_path) - 1] = '\0';
-               DBG3("shm path '%s' is assigned to uid buffer registry for session id %" PRIu64,
-                       reg->shm_path, session_id);
-       }
-       reg->registry->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
-       if (!reg->registry->channels) {
-               ret = -ENOMEM;
-               goto error_session;
-       }
-
-       cds_lfht_node_init(&reg->node.node);
-       *regp = reg;
-
-       DBG3("Buffer registry per UID created id: %" PRIu64 ", ABI: %u, uid: %d, domain: %d",
-                       session_id, bits_per_long, uid, domain);
-
-       return 0;
-
-error_session:
-       free(reg->registry);
-error:
-       free(reg);
-       return ret;
-}
-
-/*
- * Add a buffer registry per UID object to the global registry.
- */
-void buffer_reg_uid_add(struct buffer_reg_uid *reg)
-{
-       struct cds_lfht_node *nodep;
-       struct lttng_ht *ht = buffer_registry_uid;
-
-       LTTNG_ASSERT(reg);
-
-       DBG3("Buffer registry per UID adding to global registry with id: %" PRIu64 ,
-                       reg->session_id);
-
-       rcu_read_lock();
-       nodep = cds_lfht_add_unique(ht->ht, ht->hash_fct(reg, lttng_ht_seed),
-                       ht->match_fct, reg, &reg->node.node);
-       LTTNG_ASSERT(nodep == &reg->node.node);
-       rcu_read_unlock();
-}
-
-/*
- * Find a buffer registry per UID object with given params. RCU read side lock
- * MUST be acquired before calling this and hold on to protect the object.
- *
- * Return the object pointer or NULL on error.
- */
-struct buffer_reg_uid *buffer_reg_uid_find(uint64_t session_id,
-               uint32_t bits_per_long, uid_t uid)
-{
-       struct lttng_ht_node_u64 *node;
-       struct lttng_ht_iter iter;
-       struct buffer_reg_uid *reg = NULL, key;
-       struct lttng_ht *ht = buffer_registry_uid;
-
-       /* Setup key we are looking for. */
-       key.session_id = session_id;
-       key.bits_per_long = bits_per_long;
-       key.uid = uid;
-
-       DBG3("Buffer registry per UID find id: %" PRIu64 ", ABI: %u, uid: %d",
-                       session_id, bits_per_long, uid);
-
-       /* Custom lookup function since it's a different key. */
-       cds_lfht_lookup(ht->ht, ht->hash_fct(&key, lttng_ht_seed), ht->match_fct,
-                       &key, &iter.iter);
-       node = lttng_ht_iter_get_node_u64(&iter);
-       if (!node) {
-               goto end;
-       }
-       reg = caa_container_of(node, struct buffer_reg_uid, node);
-
-end:
-       return reg;
-}
-
-/*
- * Initialize global buffer per PID registry. Should only be called ONCE!.
- */
-void buffer_reg_init_pid_registry(void)
-{
-       /* Should be called once. */
-       LTTNG_ASSERT(!buffer_registry_pid);
-       buffer_registry_pid = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
-       LTTNG_ASSERT(buffer_registry_pid);
-
-       DBG3("Global buffer per PID registry initialized");
-}
-
-/*
- * Allocate and initialize object. Set regp with the object pointer.
- *
- * Return 0 on success else a negative value and regp is untouched.
- */
-int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp,
-               const char *root_shm_path, const char *shm_path)
-{
-       int ret = 0;
-       struct buffer_reg_pid *reg = NULL;
-
-       LTTNG_ASSERT(regp);
-
-       reg = zmalloc(sizeof(*reg));
-       if (!reg) {
-               PERROR("zmalloc buffer registry pid");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       reg->registry = zmalloc(sizeof(struct buffer_reg_session));
-       if (!reg->registry) {
-               PERROR("zmalloc buffer registry pid session");
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       /* A cast is done here so we can use the session ID as a u64 ht node. */
-       reg->session_id = session_id;
-       if (shm_path[0]) {
-               strncpy(reg->root_shm_path, root_shm_path, sizeof(reg->root_shm_path));
-               reg->root_shm_path[sizeof(reg->root_shm_path) - 1] = '\0';
-               strncpy(reg->shm_path, shm_path, sizeof(reg->shm_path));
-               reg->shm_path[sizeof(reg->shm_path) - 1] = '\0';
-               DBG3("shm path '%s' is assigned to pid buffer registry for session id %" PRIu64,
-                               reg->shm_path, session_id);
-       }
-       reg->registry->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
-       if (!reg->registry->channels) {
-               ret = -ENOMEM;
-               goto error_session;
-       }
-
-       lttng_ht_node_init_u64(&reg->node, reg->session_id);
-       *regp = reg;
-
-       DBG3("Buffer registry per PID created with session id: %" PRIu64,
-                       session_id);
-
-       return 0;
-
-error_session:
-       free(reg->registry);
-error:
-       free(reg);
-       return ret;
-}
-
-/*
- * Add a buffer registry per PID object to the global registry.
- */
-void buffer_reg_pid_add(struct buffer_reg_pid *reg)
-{
-       LTTNG_ASSERT(reg);
-
-       DBG3("Buffer registry per PID adding to global registry with id: %" PRIu64,
-                       reg->session_id);
-
-       rcu_read_lock();
-       lttng_ht_add_unique_u64(buffer_registry_pid, &reg->node);
-       rcu_read_unlock();
-}
-
-/*
- * Find a buffer registry per PID object with given params. RCU read side lock
- * MUST be acquired before calling this and hold on to protect the object.
- *
- * Return the object pointer or NULL on error.
- */
-struct buffer_reg_pid *buffer_reg_pid_find(uint64_t session_id)
-{
-       struct lttng_ht_node_u64 *node;
-       struct lttng_ht_iter iter;
-       struct buffer_reg_pid *reg = NULL;
-       struct lttng_ht *ht = buffer_registry_pid;
-
-       DBG3("Buffer registry per PID find id: %" PRIu64, session_id);
-
-       lttng_ht_lookup(ht, &session_id, &iter);
-       node = lttng_ht_iter_get_node_u64(&iter);
-       if (!node) {
-               goto end;
-       }
-       reg = caa_container_of(node, struct buffer_reg_pid, node);
-
-end:
-       return reg;
-}
-
-/*
- * Find the consumer channel key from a UST session per-uid channel key.
- *
- * Return the matching key or -1 if not found.
- */
-int buffer_reg_uid_consumer_channel_key(
-               struct cds_list_head *buffer_reg_uid_list,
-               uint64_t chan_key, uint64_t *consumer_chan_key)
-{
-       struct lttng_ht_iter iter;
-       struct buffer_reg_uid *uid_reg = NULL;
-       struct buffer_reg_session *session_reg = NULL;
-       struct buffer_reg_channel *reg_chan;
-       int ret = -1;
-
-       rcu_read_lock();
-       /*
-        * For the per-uid registry, we have to iterate since we don't have the
-        * uid and bitness key.
-        */
-       cds_list_for_each_entry(uid_reg, buffer_reg_uid_list, lnode) {
-               session_reg = uid_reg->registry;
-               cds_lfht_for_each_entry(session_reg->channels->ht,
-                               &iter.iter, reg_chan, node.node) {
-                       if (reg_chan->key == chan_key) {
-                               *consumer_chan_key = reg_chan->consumer_key;
-                               ret = 0;
-                               goto end;
-                       }
-               }
-       }
-
-end:
-       rcu_read_unlock();
-       return ret;
-}
-
-/*
- * Allocate and initialize a buffer registry channel with the given key. Set
- * regp with the object pointer.
- *
- * Return 0 on success or else a negative value keeping regp untouched.
- */
-int buffer_reg_channel_create(uint64_t key, struct buffer_reg_channel **regp)
-{
-       struct buffer_reg_channel *reg;
-
-       LTTNG_ASSERT(regp);
-
-       DBG3("Buffer registry channel create with key: %" PRIu64, key);
-
-       reg = zmalloc(sizeof(*reg));
-       if (!reg) {
-               PERROR("zmalloc buffer registry channel");
-               return -ENOMEM;
-       }
-
-       reg->key = key;
-       CDS_INIT_LIST_HEAD(&reg->streams);
-       pthread_mutex_init(&reg->stream_list_lock, NULL);
-
-       lttng_ht_node_init_u64(&reg->node, key);
-       *regp = reg;
-
-       return 0;
-}
-
-/*
- * Allocate and initialize a buffer registry stream. Set regp with the object
- * pointer.
- *
- * Return 0 on success or else a negative value keeping regp untouched.
- */
-int buffer_reg_stream_create(struct buffer_reg_stream **regp)
-{
-       struct buffer_reg_stream *reg;
-
-       LTTNG_ASSERT(regp);
-
-       DBG3("Buffer registry creating stream");
-
-       reg = zmalloc(sizeof(*reg));
-       if (!reg) {
-               PERROR("zmalloc buffer registry stream");
-               return -ENOMEM;
-       }
-
-       *regp = reg;
-
-       return 0;
-}
-
-/*
- * Add stream to the list in the channel.
- */
-void buffer_reg_stream_add(struct buffer_reg_stream *stream,
-               struct buffer_reg_channel *channel)
-{
-       LTTNG_ASSERT(stream);
-       LTTNG_ASSERT(channel);
-
-       pthread_mutex_lock(&channel->stream_list_lock);
-       cds_list_add_tail(&stream->lnode, &channel->streams);
-       channel->stream_count++;
-       pthread_mutex_unlock(&channel->stream_list_lock);
-}
-
-/*
- * Add a buffer registry channel object to the given session.
- */
-void buffer_reg_channel_add(struct buffer_reg_session *session,
-               struct buffer_reg_channel *channel)
-{
-       LTTNG_ASSERT(session);
-       LTTNG_ASSERT(channel);
-
-       rcu_read_lock();
-       lttng_ht_add_unique_u64(session->channels, &channel->node);
-       rcu_read_unlock();
-}
-
-/*
- * Find a buffer registry channel object with the given key. RCU read side lock
- * MUST be acquired and hold on until the object reference is not needed
- * anymore.
- *
- * Return the object pointer or NULL on error.
- */
-struct buffer_reg_channel *buffer_reg_channel_find(uint64_t key,
-               struct buffer_reg_uid *reg)
-{
-       struct lttng_ht_node_u64 *node;
-       struct lttng_ht_iter iter;
-       struct buffer_reg_channel *chan = NULL;
-       struct lttng_ht *ht;
-
-       LTTNG_ASSERT(reg);
-
-       switch (reg->domain) {
-       case LTTNG_DOMAIN_UST:
-               ht = reg->registry->channels;
-               break;
-       default:
-               abort();
-               goto end;
-       }
-
-       lttng_ht_lookup(ht, &key, &iter);
-       node = lttng_ht_iter_get_node_u64(&iter);
-       if (!node) {
-               goto end;
-       }
-       chan = caa_container_of(node, struct buffer_reg_channel, node);
-
-end:
-       return chan;
-}
-
-/*
- * Destroy a buffer registry stream with the given domain.
- */
-void buffer_reg_stream_destroy(struct buffer_reg_stream *regp,
-               enum lttng_domain_type domain)
-{
-       if (!regp) {
-               return;
-       }
-
-       DBG3("Buffer registry stream destroy with handle %d",
-                       regp->obj.ust->handle);
-
-       switch (domain) {
-       case LTTNG_DOMAIN_UST:
-       {
-               int ret;
-
-               ret = ust_app_release_object(NULL, regp->obj.ust);
-               if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
-                       ERR("Buffer reg stream release obj handle %d failed with ret %d",
-                                       regp->obj.ust->handle, ret);
-               }
-               free(regp->obj.ust);
-               lttng_fd_put(LTTNG_FD_APPS, 2);
-               break;
-       }
-       default:
-               abort();
-       }
-
-       free(regp);
-       return;
-}
-
-/*
- * Remove buffer registry channel object from the session hash table. RCU read
- * side lock MUST be acquired before calling this.
- */
-void buffer_reg_channel_remove(struct buffer_reg_session *session,
-               struct buffer_reg_channel *regp)
-{
-       int ret;
-       struct lttng_ht_iter iter;
-
-       LTTNG_ASSERT(session);
-       LTTNG_ASSERT(regp);
-
-       iter.iter.node =