From a58c490f0bff52a73717d31d04d1472629180de2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 4 May 2017 23:08:47 -0400 Subject: [PATCH] Add client notification API MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérémie Galarneau --- configure.ac | 14 +- include/Makefile.am | 28 +- include/lttng/action/action-internal.h | 52 ++ include/lttng/action/action.h | 41 + include/lttng/action/notify-internal.h | 28 + include/lttng/action/notify.h | 33 + .../lttng/condition/buffer-usage-internal.h | 99 +++ include/lttng/condition/buffer-usage.h | 102 +++ include/lttng/condition/condition-internal.h | 72 ++ include/lttng/condition/condition.h | 52 ++ include/lttng/condition/evaluation-internal.h | 50 ++ include/lttng/condition/evaluation.h | 46 + include/lttng/endpoint-internal.h | 32 + include/lttng/endpoint.h | 32 + include/lttng/lttng-error.h | 4 + include/lttng/lttng.h | 9 + include/lttng/notification/channel-internal.h | 99 +++ include/lttng/notification/channel.h | 68 ++ .../notification/notification-internal.h | 68 ++ include/lttng/notification/notification.h | 46 + include/lttng/trigger/trigger-internal.h | 49 + include/lttng/trigger/trigger.h | 54 ++ src/common/Makefile.am | 3 + src/common/action.c | 119 +++ src/common/buffer-usage.c | 838 ++++++++++++++++++ src/common/condition.c | 169 ++++ src/common/defaults.h | 16 +- src/common/endpoint.c | 26 + src/common/error.c | 4 + src/common/evaluation.c | 113 +++ src/common/notification.c | 176 ++++ src/common/notify.c | 49 + src/common/trigger.c | 183 ++++ src/lib/lttng-ctl/Makefile.am | 3 +- src/lib/lttng-ctl/channel.c | 572 ++++++++++++ src/lib/lttng-ctl/lttng-ctl.c | 91 ++ 36 files changed, 3433 insertions(+), 7 deletions(-) create mode 100644 include/lttng/action/action-internal.h create mode 100644 include/lttng/action/action.h create mode 100644 include/lttng/action/notify-internal.h create mode 100644 include/lttng/action/notify.h create mode 100644 include/lttng/condition/buffer-usage-internal.h create mode 100644 include/lttng/condition/buffer-usage.h create mode 100644 include/lttng/condition/condition-internal.h create mode 100644 include/lttng/condition/condition.h create mode 100644 include/lttng/condition/evaluation-internal.h create mode 100644 include/lttng/condition/evaluation.h create mode 100644 include/lttng/endpoint-internal.h create mode 100644 include/lttng/endpoint.h create mode 100644 include/lttng/notification/channel-internal.h create mode 100644 include/lttng/notification/channel.h create mode 100644 include/lttng/notification/notification-internal.h create mode 100644 include/lttng/notification/notification.h create mode 100644 include/lttng/trigger/trigger-internal.h create mode 100644 include/lttng/trigger/trigger.h create mode 100644 src/common/action.c create mode 100644 src/common/buffer-usage.c create mode 100644 src/common/condition.c create mode 100644 src/common/endpoint.c create mode 100644 src/common/evaluation.c create mode 100644 src/common/notification.c create mode 100644 src/common/notify.c create mode 100644 src/common/trigger.c create mode 100644 src/lib/lttng-ctl/channel.c diff --git a/configure.ac b/configure.ac index 0a7034e24..9bc394123 100644 --- a/configure.ac +++ b/configure.ac @@ -948,8 +948,20 @@ CFLAGS="-Wall $CFLAGS -g -fno-strict-aliasing" DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_builddir) -I\$(top_builddir)/src -I\$(top_builddir)/include -include config.h" lttngincludedir="${includedir}/lttng" - AC_SUBST(lttngincludedir) + +lttngactionincludedir="${includedir}/lttng/action" +AC_SUBST(lttngactionincludedir) + +lttngconditionincludedir="${includedir}/lttng/condition" +AC_SUBST(lttngconditionincludedir) + +lttngnotificationincludedir="${includedir}/lttng/notification" +AC_SUBST(lttngnotificationincludedir) + +lttngtriggerincludedir="${includedir}/lttng/trigger" +AC_SUBST(lttngtriggerincludedir) + AC_SUBST(DEFAULT_INCLUDES) lttnglibexecdir="${libdir}/lttng/libexec" diff --git a/include/Makefile.am b/include/Makefile.am index 0536dcdf0..bc105b064 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -77,10 +77,36 @@ lttnginclude_HEADERS = \ lttng/snapshot.h \ lttng/save.h \ lttng/load.h \ + lttng/endpoint.h \ version.h.tmpl +lttngactioninclude_HEADERS= \ + lttng/action/action.h \ + lttng/action/notify.h + +lttngconditioninclude_HEADERS= \ + lttng/condition/condition.h \ + lttng/condition/buffer-usage.h \ + lttng/condition/evaluation.h + +lttngnotificationinclude_HEADERS= \ + lttng/notification/channel.h \ + lttng/notification/notification.h + +lttngtriggerinclude_HEADERS= \ + lttng/trigger/trigger.h + noinst_HEADERS = \ lttng/snapshot-internal.h \ lttng/health-internal.h \ lttng/save-internal.h \ - lttng/load-internal.h + lttng/load-internal.h \ + lttng/action/action-internal.h \ + lttng/action/notify-internal.h \ + lttng/condition/condition-internal.h \ + lttng/condition/buffer-usage-internal.h \ + lttng/condition/evaluation-internal.h \ + lttng/notification/notification-internal.h \ + lttng/trigger/trigger-internal.h \ + lttng/endpoint-internal.h \ + lttng/notification/channel-internal.h diff --git a/include/lttng/action/action-internal.h b/include/lttng/action/action-internal.h new file mode 100644 index 000000000..746b0a4d8 --- /dev/null +++ b/include/lttng/action/action-internal.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_INTERNAL_H +#define LTTNG_ACTION_INTERNAL_H + +#include +#include +#include +#include + +typedef bool (*action_validate_cb)(struct lttng_action *action); +typedef void (*action_destroy_cb)(struct lttng_action *action); +typedef ssize_t (*action_serialize_cb)(struct lttng_action *action, char *buf); + +struct lttng_action { + enum lttng_action_type type; + action_validate_cb validate; + action_serialize_cb serialize; + action_destroy_cb destroy; +}; + +struct lttng_action_comm { + /* enum lttng_action_type */ + int8_t action_type; +} LTTNG_PACKED; + +LTTNG_HIDDEN +bool lttng_action_validate(struct lttng_action *action); + +LTTNG_HIDDEN +ssize_t lttng_action_serialize(struct lttng_action *action, char *buf); + +LTTNG_HIDDEN +ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_INTERNAL_H */ diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h new file mode 100644 index 000000000..311f5a0fb --- /dev/null +++ b/include/lttng/action/action.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_H +#define LTTNG_ACTION_H + +struct lttng_action; + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_action_type { + LTTNG_ACTION_TYPE_UNKNOWN = -1, + LTTNG_ACTION_TYPE_NOTIFY = 0, +}; + +extern enum lttng_action_type lttng_action_get_type( + struct lttng_action *action); + +extern void lttng_action_destroy(struct lttng_action *action); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_H */ diff --git a/include/lttng/action/notify-internal.h b/include/lttng/action/notify-internal.h new file mode 100644 index 000000000..509bd36a9 --- /dev/null +++ b/include/lttng/action/notify-internal.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_NOTIFY_INTERNAL_H +#define LTTNG_ACTION_NOTIFY_INTERNAL_H + +#include +#include + +struct lttng_action_notify { + struct lttng_action parent; +}; + +#endif /* LTTNG_ACTION_NOTIFY_INTERNAL_H */ diff --git a/include/lttng/action/notify.h b/include/lttng/action/notify.h new file mode 100644 index 000000000..6c0d0ca25 --- /dev/null +++ b/include/lttng/action/notify.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ACTION_NOTIFY_H +#define LTTNG_ACTION_NOTIFY_H + +struct lttng_action; + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct lttng_action *lttng_action_notify_create(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_NOTIFY_H */ diff --git a/include/lttng/condition/buffer-usage-internal.h b/include/lttng/condition/buffer-usage-internal.h new file mode 100644 index 000000000..4b605d664 --- /dev/null +++ b/include/lttng/condition/buffer-usage-internal.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_BUFFER_USAGE_INTERNAL_H +#define LTTNG_CONDITION_BUFFER_USAGE_INTERNAL_H + +#include +#include +#include +#include +#include "common/buffer-view.h" + +struct lttng_condition_buffer_usage { + struct lttng_condition parent; + struct { + bool set; + uint64_t value; + } threshold_bytes; + struct { + bool set; + double value; + } threshold_ratio; + char *session_name; + char *channel_name; + struct { + bool set; + enum lttng_domain_type type; + } domain; +}; + +struct lttng_condition_buffer_usage_comm { + uint8_t threshold_set_in_bytes; + /* + * Expressed in bytes if "threshold_set_in_bytes" is not 0. + * Otherwise, it is expressed a ratio in the interval [0.0, 1.0] + * that is mapped to the range on a 32-bit unsigned integer. + * The ratio is obtained by (threshold / UINT32_MAX). + */ + uint32_t threshold; + /* Both lengths include the trailing \0. */ + uint32_t session_name_len; + uint32_t channel_name_len; + /* enum lttng_domain_type */ + int8_t domain_type; + /* session and channel names. */ + char names[]; +} LTTNG_PACKED; + +struct lttng_evaluation_buffer_usage { + struct lttng_evaluation parent; + uint64_t buffer_use; + uint64_t buffer_capacity; +}; + +struct lttng_evaluation_buffer_usage_comm { + uint64_t buffer_use; + uint64_t buffer_capacity; +} LTTNG_PACKED; + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_buffer_usage_create( + enum lttng_condition_type type, uint64_t use, + uint64_t capacity); + +LTTNG_HIDDEN +ssize_t lttng_condition_buffer_usage_low_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +ssize_t lttng_condition_buffer_usage_high_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **condition); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **evaluation); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **evaluation); + +#endif /* LTTNG_CONDITION_BUFFER_USAGE_INTERNAL_H */ diff --git a/include/lttng/condition/buffer-usage.h b/include/lttng/condition/buffer-usage.h new file mode 100644 index 000000000..1463ef823 --- /dev/null +++ b/include/lttng/condition/buffer-usage.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_BUFFER_USAGE_H +#define LTTNG_CONDITION_BUFFER_USAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_condition; +struct lttng_evaluation; + +extern struct lttng_condition * +lttng_condition_buffer_usage_low_create(void); + +extern struct lttng_condition * +lttng_condition_buffer_usage_high_create(void); + +/* threshold_ratio expressed as [0.0, 1.0]. */ +extern enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold_ratio( + const struct lttng_condition *condition, + double *threshold_ratio); + +/* threshold_ratio expressed as [0.0, 1.0]. */ +extern enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold_ratio( + struct lttng_condition *condition, + double threshold_ratio); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold( + const struct lttng_condition *condition, + uint64_t *threshold_bytes); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold( + struct lttng_condition *condition, + uint64_t threshold_bytes); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_get_session_name( + const struct lttng_condition *condition, + const char **session_name); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_set_session_name( + struct lttng_condition *condition, + const char *session_name); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_get_channel_name( + const struct lttng_condition *condition, + const char **channel_name); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_set_channel_name( + struct lttng_condition *condition, + const char *channel_name); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_get_domain_type( + const struct lttng_condition *condition, + enum lttng_domain_type *type); + +extern enum lttng_condition_status +lttng_condition_buffer_usage_set_domain_type( + struct lttng_condition *condition, + enum lttng_domain_type type); + + +/* LTTng Condition Evaluation */ +extern enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage_ratio( + const struct lttng_evaluation *evaluation, + double *usage_ratio); + +extern enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage( + const struct lttng_evaluation *evaluation, + uint64_t *usage_bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_CONDITION_BUFFER_USAGE_H */ diff --git a/include/lttng/condition/condition-internal.h b/include/lttng/condition/condition-internal.h new file mode 100644 index 000000000..72a8922b0 --- /dev/null +++ b/include/lttng/condition/condition-internal.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_INTERNAL_H +#define LTTNG_CONDITION_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +typedef void (*condition_destroy_cb)(struct lttng_condition *condition); +typedef bool (*condition_validate_cb)(const struct lttng_condition *condition); +typedef ssize_t (*condition_serialize_cb)( + const struct lttng_condition *condition, char *buf); +typedef bool (*condition_equal_cb)(const struct lttng_condition *a, + const struct lttng_condition *b); +typedef ssize_t (*condition_create_from_buffer_cb)( + const struct lttng_buffer_view *view, + struct lttng_condition **condition); + +struct lttng_condition { + enum lttng_condition_type type; + condition_validate_cb validate; + condition_serialize_cb serialize; + condition_equal_cb equal; + condition_destroy_cb destroy; +}; + +struct lttng_condition_comm { + /* enum lttng_condition_type */ + int8_t condition_type; + char payload[]; +}; + +LTTNG_HIDDEN +void lttng_condition_init(struct lttng_condition *condition, + enum lttng_condition_type type); + +LTTNG_HIDDEN +bool lttng_condition_validate(const struct lttng_condition *condition); + +LTTNG_HIDDEN +ssize_t lttng_condition_create_from_buffer( + const struct lttng_buffer_view *buffer, + struct lttng_condition **condition); + +LTTNG_HIDDEN +ssize_t lttng_condition_serialize(const struct lttng_condition *condition, + char *buf); + +LTTNG_HIDDEN +bool lttng_condition_is_equal(const struct lttng_condition *a, + const struct lttng_condition *b); + +#endif /* LTTNG_CONDITION_INTERNAL_H */ diff --git a/include/lttng/condition/condition.h b/include/lttng/condition/condition.h new file mode 100644 index 000000000..38a071e63 --- /dev/null +++ b/include/lttng/condition/condition.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CONDITION_H +#define LTTNG_CONDITION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_condition; + +enum lttng_condition_type { + LTTNG_CONDITION_TYPE_UNKNOWN = -1, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW = 102, + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH = 101, +}; + +enum lttng_condition_status { + LTTNG_CONDITION_STATUS_OK = 0, + LTTNG_CONDITION_STATUS_ERROR = -1, + LTTNG_CONDITION_STATUS_UNKNOWN = -2, + LTTNG_CONDITION_STATUS_INVALID = -3, + LTTNG_CONDITION_STATUS_UNSET = -4, +}; + +extern enum lttng_condition_type lttng_condition_get_type( + const struct lttng_condition *condition); + +extern void lttng_condition_destroy(struct lttng_condition *condition); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_CONDITION_H */ diff --git a/include/lttng/condition/evaluation-internal.h b/include/lttng/condition/evaluation-internal.h new file mode 100644 index 000000000..414760e05 --- /dev/null +++ b/include/lttng/condition/evaluation-internal.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVALUATION_INTERNAL_H +#define LTTNG_EVALUATION_INTERNAL_H + +#include +#include +#include +#include + +typedef void (*evaluation_destroy_cb)(struct lttng_evaluation *evaluation); +typedef ssize_t (*evaluation_serialize_cb)(struct lttng_evaluation *evaluation, + char *buf); + +struct lttng_evaluation_comm { + /* enum lttng_condition_type type */ + int8_t type; + char payload[]; +} LTTNG_PACKED; + +struct lttng_evaluation { + enum lttng_condition_type type; + evaluation_serialize_cb serialize; + evaluation_destroy_cb destroy; +}; + +LTTNG_HIDDEN +ssize_t lttng_evaluation_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_evaluation **evaluation); + +LTTNG_HIDDEN +ssize_t lttng_evaluation_serialize(struct lttng_evaluation *evaluation, + char *buf); + +#endif /* LTTNG_EVALUATION_INTERNAL_H */ diff --git a/include/lttng/condition/evaluation.h b/include/lttng/condition/evaluation.h new file mode 100644 index 000000000..987fce32b --- /dev/null +++ b/include/lttng/condition/evaluation.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_EVALUATION_H +#define LTTNG_EVALUATION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_evaluation; + +enum lttng_evaluation_status { + LTTNG_EVALUATION_STATUS_OK = 0, + LTTNG_EVALUATION_STATUS_ERROR = -1, + LTTNG_EVALUATION_STATUS_INVALID = -2, + LTTNG_EVALUATION_STATUS_UNKNOWN = -2, + LTTNG_EVALUATION_STATUS_UNSET = -3, +}; + +extern enum lttng_condition_type lttng_evaluation_get_type( + const struct lttng_evaluation *evaluation); + +extern void lttng_evaluation_destroy(struct lttng_evaluation *evaluation); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_EVALUATION_H */ diff --git a/include/lttng/endpoint-internal.h b/include/lttng/endpoint-internal.h new file mode 100644 index 000000000..de6024b42 --- /dev/null +++ b/include/lttng/endpoint-internal.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ENDPOINT_INTERNAL_H +#define LTTNG_ENDPOINT_INTERNAL_H + +#include +#include + +enum lttng_endpoint_type { + LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION = 0, +}; + +struct lttng_endpoint { + enum lttng_endpoint_type type; +}; + +#endif /* LTTNG_ENDPOINT_INTERNAL_H */ diff --git a/include/lttng/endpoint.h b/include/lttng/endpoint.h new file mode 100644 index 000000000..b93ed697e --- /dev/null +++ b/include/lttng/endpoint.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_ENDPOINT_H +#define LTTNG_ENDPOINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Default LTTng session daemon endpoint singleton. */ +extern struct lttng_endpoint *lttng_session_daemon_notification_endpoint; + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ENDPOINT_H */ diff --git a/include/lttng/lttng-error.h b/include/lttng/lttng-error.h index db6fe73c2..1b5ea699a 100644 --- a/include/lttng/lttng-error.h +++ b/include/lttng/lttng-error.h @@ -145,6 +145,10 @@ enum lttng_error_code { LTTNG_ERR_REGEN_STATEDUMP_FAIL = 122, /* Failed to regenerate the state dump */ LTTNG_ERR_REGEN_STATEDUMP_NOMEM = 123, /* Failed to regenerate the state dump, not enough memory */ LTTNG_ERR_NOT_SNAPSHOT_SESSION = 124, /* Session is not in snapshot mode. */ + LTTNG_ERR_INVALID_TRIGGER = 125, /* Invalid trigger provided. */ + LTTNG_ERR_TRIGGER_EXISTS = 126, /* Trigger already registered. */ + LTTNG_ERR_TRIGGER_NOT_FOUND = 127, /* Trigger not found. */ + LTTNG_ERR_COMMAND_CANCELLED = 128, /* Command cancelled. */ /* MUST be last element */ LTTNG_ERR_NR, /* Last element */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index 5478641b2..72e8dcf05 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -34,6 +34,15 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/include/lttng/notification/channel-internal.h b/include/lttng/notification/channel-internal.h new file mode 100644 index 000000000..efe502fe4 --- /dev/null +++ b/include/lttng/notification/channel-internal.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_NOTIFICATION_CHANNEL_INTERNAL_H +#define LTTNG_NOTIFICATION_CHANNEL_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +#define LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR 1 +#define LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR 0 + +enum lttng_notification_channel_message_type { + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNKNOWN = -1, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE = 0, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE = 1, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE = 2, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_COMMAND_REPLY = 3, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION = 4, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED = 5, +}; + +struct lttng_notification_channel_message { + /* enum lttng_notification_channel_message_type */ + int8_t type; + /* Size of the payload following this field. */ + uint32_t size; + char payload[]; +} LTTNG_PACKED; + +struct lttng_notification_channel_command_handshake { + uint8_t major; + uint8_t minor; +} LTTNG_PACKED; + +struct lttng_notification_channel_command_reply { + /* enum lttng_notification_channel_status */ + int8_t status; +} LTTNG_PACKED; + +struct pending_notification { + /* NULL means "notification dropped". */ + struct lttng_notification *notification; + struct cds_list_head node; +}; + +/* + * The notification channel protocol is bidirectional and accomodates + * synchronous and asynchronous communication modes: + * + * - Synchronous: commands emitted by the client to which a reply is expected + * (e.g. subscribing/unsubscribing to conditions), + * - Asynchronous: notifications which are sent by the lttng_endpoint to the + * client as one of the subscribed condition has occured. + * + * The nature of this hybrid communication mode means that asynchronous messages + * (e.g. notifications) may be interleaved between synchronous messages (e.g. a + * command and its reply). + * + * Notifications that are received between a command and its reply and enqueued + * in the pending_notifications list. + */ +struct lttng_notification_channel { + pthread_mutex_t lock; + int socket; + struct { + /* Count of pending notifications. */ + unsigned int count; + /* List of struct pending_notification. */ + struct cds_list_head list; + } pending_notifications; + struct lttng_dynamic_buffer reception_buffer; + /* Sessiond notification protocol version. */ + struct { + bool set; + int8_t major, minor; + } version; +}; + +#endif /* LTTNG_NOTIFICATION_CHANNEL_INTERNAL_H */ diff --git a/include/lttng/notification/channel.h b/include/lttng/notification/channel.h new file mode 100644 index 000000000..50334c58e --- /dev/null +++ b/include/lttng/notification/channel.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_NOTIFICATION_CHANNEL_H +#define LTTNG_NOTIFICATION_CHANNEL_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_endpoint; +struct lttng_condition; +struct lttng_notification; +struct lttng_notification_channel; + +/* LTTng Notification channel */ +enum lttng_notification_channel_status { + LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED = 1, + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK = 0, + LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR = -1, + LTTNG_NOTIFICATION_CHANNEL_STATUS_CLOSED = -2, + LTTNG_NOTIFICATION_CHANNEL_STATUS_ALREADY_SUBSCRIBED = -3, + /* Condition unknown. */ + LTTNG_NOTIFICATION_CHANNEL_STATUS_UNKNOWN_CONDITION = -4, + LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID = -5, + LTTNG_NOTIFICATION_CHANNEL_STATUS_UNSUPPORTED_VERSION = -6, +}; + +extern struct lttng_notification_channel *lttng_notification_channel_create( + struct lttng_endpoint *endpoint); + +extern enum lttng_notification_channel_status +lttng_notification_channel_get_next_notification( + struct lttng_notification_channel *channel, + struct lttng_notification **notification); + +extern enum lttng_notification_channel_status +lttng_notification_channel_subscribe( + struct lttng_notification_channel *channel, + const struct lttng_condition *condition); + +extern enum lttng_notification_channel_status +lttng_notification_channel_unsubscribe( + struct lttng_notification_channel *channel, + const struct lttng_condition *condition); + +extern void lttng_notification_channel_destroy( + struct lttng_notification_channel *channel); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_NOTIFICATION_CHANNEL_H */ diff --git a/include/lttng/notification/notification-internal.h b/include/lttng/notification/notification-internal.h new file mode 100644 index 000000000..df9e8cb50 --- /dev/null +++ b/include/lttng/notification/notification-internal.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_NOTIFICATION_INTERNAL_H +#define LTTNG_NOTIFICATION_INTERNAL_H + +#include +#include +#include +#include +#include + +struct lttng_notification { + struct lttng_condition *condition; + struct lttng_evaluation *evaluation; + /* + * The ownership of the notification's inner-elements depends + * on the way it was created. The notification owns both + * the condition and evaluation if it was obtained from a notification + * channel (i.e. created using lttng_notification_create_from_buffer) + * as the user may never access the condition and evaluation, + * thus never getting a chance to free them. + * + * However, when the _private_ lttng_notification_create() function + * is used, no ownership of condition and evaluation is assumed by + * the notification object. The main reason for this change in + * behavior is that internal users of this API only use the object + * to use its serialization facilities. + */ + bool owns_elements; +}; + +struct lttng_notification_comm { + /* Size of the payload following this field. */ + uint32_t length; + /* Condition and evaluation objects follow. */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +struct lttng_notification *lttng_notification_create( + struct lttng_condition *condition, + struct lttng_evaluation *evaluation); + +LTTNG_HIDDEN +ssize_t lttng_notification_serialize(struct lttng_notification *notification, + char *buf); + +LTTNG_HIDDEN +ssize_t lttng_notification_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_notification **notification); + +#endif /* LTTNG_NOTIFICATION_INTERNAL_H */ diff --git a/include/lttng/notification/notification.h b/include/lttng/notification/notification.h new file mode 100644 index 000000000..dcaf9745a --- /dev/null +++ b/include/lttng/notification/notification.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_NOTIFICATION_H +#define LTTNG_NOTIFICATION_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_condition; +struct lttng_evaluation; +struct lttng_notification; + +/* + * The notification retains ownership of both the condition and evaluation. + * Destroying the notification will also destroy the notification and evaluation + * objects. + */ +extern const struct lttng_condition *lttng_notification_get_condition( + struct lttng_notification *notification); + +extern const struct lttng_evaluation *lttng_notification_get_evaluation( + struct lttng_notification *notification); + +extern void lttng_notification_destroy(struct lttng_notification *notification); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_NOTIFICATION_H */ diff --git a/include/lttng/trigger/trigger-internal.h b/include/lttng/trigger/trigger-internal.h new file mode 100644 index 000000000..c92fa7d38 --- /dev/null +++ b/include/lttng/trigger/trigger-internal.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_TRIGGER_INTERNAL_H +#define LTTNG_TRIGGER_INTERNAL_H + +#include +#include +#include +#include +#include + +struct lttng_trigger { + struct lttng_condition *condition; + struct lttng_action *action; +}; + +struct lttng_trigger_comm { + /* length excludes its own length. */ + uint32_t length; + /* A condition and action object follow. */ + char payload[]; +} LTTNG_PACKED; + +LTTNG_HIDDEN +ssize_t lttng_trigger_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_trigger **trigger); + +LTTNG_HIDDEN +ssize_t lttng_trigger_serialize(struct lttng_trigger *trigger, char *buf); + +LTTNG_HIDDEN +bool lttng_trigger_validate(struct lttng_trigger *trigger); + +#endif /* LTTNG_TRIGGER_INTERNAL_H */ diff --git a/include/lttng/trigger/trigger.h b/include/lttng/trigger/trigger.h new file mode 100644 index 000000000..b2eab0e3d --- /dev/null +++ b/include/lttng/trigger/trigger.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_TRIGGER_H +#define LTTNG_TRIGGER_H + +struct lttng_action; +struct lttng_condition; +struct lttng_trigger; + +#ifdef __cplusplus +extern "C" { +#endif + +enum lttng_register_trigger_status { + LTTNG_REGISTER_TRIGGER_STATUS_OK = 0, + LTTNG_REGISTER_TRIGGER_STATUS_INVALID = -1, +}; + +/* The caller retains the ownership of both condition and action. */ +extern struct lttng_trigger *lttng_trigger_create( + struct lttng_condition *condition, struct lttng_action *action); + +extern struct lttng_condition *lttng_trigger_get_condition( + struct lttng_trigger *trigger); + +extern struct lttng_action *lttng_trigger_get_action( + struct lttng_trigger *trigger); + +extern void lttng_trigger_destroy(struct lttng_trigger *trigger); + +extern int lttng_register_trigger(struct lttng_trigger *trigger); + +extern int lttng_unregister_trigger(struct lttng_trigger *trigger); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_TRIGGER_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 83354d070..2c734d7ec 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -73,6 +73,9 @@ libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.c runas.h \ mi-lttng.h mi-lttng.c \ daemonize.c daemonize.h \ unix.c unix.h \ + filter.c filter.h context.c context.h \ + action.c notify.c condition.c buffer-usage.c \ + evaluation.c notification.c trigger.c endpoint.c \ dynamic-buffer.h dynamic-buffer.c \ buffer-view.h buffer-view.c diff --git a/src/common/action.c b/src/common/action.c new file mode 100644 index 000000000..3abdfaf21 --- /dev/null +++ b/src/common/action.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +enum lttng_action_type lttng_action_get_type(struct lttng_action *action) +{ + return action ? action->type : LTTNG_ACTION_TYPE_UNKNOWN; +} + +void lttng_action_destroy(struct lttng_action *action) +{ + if (!action) { + return; + } + + assert(action->destroy); + action->destroy(action); +} + +LTTNG_HIDDEN +bool lttng_action_validate(struct lttng_action *action) +{ + bool valid; + + if (!action) { + valid = false; + goto end; + } + + if (!action->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = action->validate(action); +end: + return valid; +} + +LTTNG_HIDDEN +ssize_t lttng_action_serialize(struct lttng_action *action, char *buf) +{ + ssize_t ret, action_size; + struct lttng_action_comm action_comm; + + if (!action) { + ret = -1; + goto end; + } + + action_comm.action_type = (int8_t) action->type; + ret = sizeof(struct lttng_action_comm); + if (buf) { + memcpy(buf, &action_comm, ret); + buf += ret; + } + + action_size = action->serialize(action, buf); + if (action_size < 0) { + ret = action_size; + goto end; + } + ret += action_size; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_action_create_from_buffer(const struct lttng_buffer_view *view, + struct lttng_action **_action) +{ + ssize_t ret, action_size = sizeof(struct lttng_action_comm); + struct lttng_action *action; + const struct lttng_action_comm *action_comm; + + if (!view || !_action) { + ret = -1; + goto end; + } + + action_comm = (const struct lttng_action_comm *) view->data; + DBG("Deserializing action from buffer"); + switch (action_comm->action_type) { + case LTTNG_ACTION_TYPE_NOTIFY: + action = lttng_action_notify_create(); + break; + default: + ret = -1; + goto end; + } + + if (!action) { + ret = -1; + goto end; + } + ret = action_size; + *_action = action; +end: + return ret; +} diff --git a/src/common/buffer-usage.c b/src/common/buffer-usage.c new file mode 100644 index 000000000..49696a33f --- /dev/null +++ b/src/common/buffer-usage.c @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_USAGE_CONDITION(condition) ( \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || \ + lttng_condition_get_type(condition) == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH \ + ) + +static +double fixed_to_double(uint32_t val) +{ + return (double) val / (double) UINT32_MAX; +} + +static +uint64_t double_to_fixed(double val) +{ + return (val * (double) UINT32_MAX); +} + +static +bool is_usage_evaluation(const struct lttng_evaluation *evaluation) +{ + enum lttng_condition_type type = lttng_evaluation_get_type(evaluation); + + return type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW || + type == LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH; +} + +static +void lttng_condition_buffer_usage_destroy(struct lttng_condition *condition) +{ + struct lttng_condition_buffer_usage *usage; + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + + free(usage->session_name); + free(usage->channel_name); + free(usage); +} + +static +bool lttng_condition_buffer_usage_validate( + const struct lttng_condition *condition) +{ + bool valid = false; + struct lttng_condition_buffer_usage *usage; + + if (!condition) { + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->session_name) { + ERR("Invalid buffer condition: a target session name must be set."); + goto end; + } + if (!usage->channel_name) { + ERR("Invalid buffer condition: a target channel name must be set."); + goto end; + } + if (!usage->threshold_ratio.set && !usage->threshold_bytes.set) { + ERR("Invalid buffer condition: a threshold must be set."); + goto end; + } + + valid = true; +end: + return valid; +} + +static +ssize_t lttng_condition_buffer_usage_serialize( + const struct lttng_condition *condition, char *buf) +{ + struct lttng_condition_buffer_usage *usage; + ssize_t ret, size; + size_t session_name_len, channel_name_len; + + if (!condition || !IS_USAGE_CONDITION(condition)) { + ret = -1; + goto end; + } + + DBG("Serializing buffer usage condition"); + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + size = sizeof(struct lttng_condition_buffer_usage_comm); + session_name_len = strlen(usage->session_name) + 1; + channel_name_len = strlen(usage->channel_name) + 1; + if (session_name_len > LTTNG_NAME_MAX || + channel_name_len > LTTNG_NAME_MAX) { + ret = -1; + goto end; + } + size += session_name_len + channel_name_len; + if (buf) { + struct lttng_condition_buffer_usage_comm usage_comm = { + .threshold_set_in_bytes = usage->threshold_bytes.set ? 1 : 0, + .session_name_len = session_name_len, + .channel_name_len = channel_name_len, + .domain_type = (int8_t) usage->domain.type, + }; + + if (usage->threshold_bytes.set) { + usage_comm.threshold = usage->threshold_bytes.value; + } else { + uint64_t val = double_to_fixed( + usage->threshold_ratio.value); + + if (val > UINT32_MAX) { + /* overflow. */ + ret = -1; + goto end; + } + usage_comm.threshold = val; + } + + memcpy(buf, &usage_comm, sizeof(usage_comm)); + buf += sizeof(usage_comm); + memcpy(buf, usage->session_name, session_name_len); + buf += session_name_len; + memcpy(buf, usage->channel_name, channel_name_len); + buf += channel_name_len; + } + ret = size; +end: + return ret; +} + +static +bool lttng_condition_buffer_usage_is_equal(const struct lttng_condition *_a, + const struct lttng_condition *_b) +{ + bool is_equal = false; + struct lttng_condition_buffer_usage *a, *b; + + a = container_of(_a, struct lttng_condition_buffer_usage, parent); + b = container_of(_b, struct lttng_condition_buffer_usage, parent); + + if ((a->threshold_ratio.set && !b->threshold_ratio.set) || + (a->threshold_bytes.set && !b->threshold_bytes.set)) { + goto end; + } + + if (a->threshold_ratio.set && b->threshold_ratio.set) { + double a_value, b_value, diff; + + a_value = a->threshold_ratio.value; + b_value = b->threshold_ratio.value; + diff = fabs(a_value - b_value); + + if (diff > DBL_EPSILON) { + goto end; + } + } else if (a->threshold_bytes.set && b->threshold_bytes.set) { + uint64_t a_value, b_value; + + a_value = a->threshold_bytes.value; + b_value = b->threshold_bytes.value; + if (a_value != b_value) { + goto end; + } + } + + if ((a->session_name && !b->session_name) || + (!a->session_name && b->session_name)) { + goto end; + } + + if (a->channel_name && b->channel_name) { + if (strcmp(a->channel_name, b->channel_name)) { + goto end; + } + } if ((a->channel_name && !b->channel_name) || + (!a->channel_name && b->channel_name)) { + goto end; + } + + if (a->channel_name && b->channel_name) { + if (strcmp(a->channel_name, b->channel_name)) { + goto end; + } + } + + if ((a->domain.set && !b->domain.set) || + (!a->domain.set && b->domain.set)) { + goto end; + } + + if (a->domain.set && b->domain.set) { + if (a->domain.type != b->domain.type) { + goto end; + } + } + is_equal = true; +end: + return is_equal; +} + +static +struct lttng_condition *lttng_condition_buffer_usage_create( + enum lttng_condition_type type) +{ + struct lttng_condition_buffer_usage *condition; + + condition = zmalloc(sizeof(struct lttng_condition_buffer_usage)); + if (!condition) { + goto end; + } + + lttng_condition_init(&condition->parent, type); + condition->parent.validate = lttng_condition_buffer_usage_validate; + condition->parent.serialize = lttng_condition_buffer_usage_serialize; + condition->parent.equal = lttng_condition_buffer_usage_is_equal; + condition->parent.destroy = lttng_condition_buffer_usage_destroy; +end: + return &condition->parent; +} + +struct lttng_condition *lttng_condition_buffer_usage_low_create(void) +{ + return lttng_condition_buffer_usage_create( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW); +} + +struct lttng_condition *lttng_condition_buffer_usage_high_create(void) +{ + return lttng_condition_buffer_usage_create( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH); +} + +static +ssize_t init_condition_from_buffer(struct lttng_condition *condition, + const struct lttng_buffer_view *src_view) +{ + ssize_t ret, condition_size; + enum lttng_condition_status status; + enum lttng_domain_type domain_type; + const struct lttng_condition_buffer_usage_comm *condition_comm; + const char *session_name, *channel_name; + struct lttng_buffer_view names_view; + + if (src_view->size < sizeof(*condition_comm)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain header"); + ret = -1; + goto end; + } + + condition_comm = (const struct lttng_condition_buffer_usage_comm *) src_view->data; + names_view = lttng_buffer_view_from_view(src_view, + sizeof(*condition_comm), -1); + + if (condition_comm->session_name_len > LTTNG_NAME_MAX || + condition_comm->channel_name_len > LTTNG_NAME_MAX) { + ERR("Failed to initialize from malformed condition buffer: name exceeds LTTNG_MAX_NAME"); + ret = -1; + goto end; + } + + if (names_view.size < + (condition_comm->session_name_len + + condition_comm->channel_name_len)) { + ERR("Failed to initialize from malformed condition buffer: buffer too short to contain element names"); + ret = -1; + goto end; + } + + if (condition_comm->threshold_set_in_bytes) { + status = lttng_condition_buffer_usage_set_threshold(condition, + condition_comm->threshold); + } else { + status = lttng_condition_buffer_usage_set_threshold_ratio( + condition, + fixed_to_double(condition_comm->threshold)); + } + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to initialize buffer usage condition threshold"); + ret = -1; + goto end; + } + + if (condition_comm->domain_type <= LTTNG_DOMAIN_NONE || + condition_comm->domain_type > LTTNG_DOMAIN_PYTHON) { + /* Invalid domain value. */ + ERR("Invalid domain type value (%i) found in condition buffer", + (int) condition_comm->domain_type); + ret = -1; + goto end; + } + + domain_type = (enum lttng_domain_type) condition_comm->domain_type; + status = lttng_condition_buffer_usage_set_domain_type(condition, + domain_type); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage condition domain"); + ret = -1; + goto end; + } + + session_name = names_view.data; + if (*(session_name + condition_comm->session_name_len - 1) != '\0') { + ERR("Malformed session name encountered in condition buffer"); + ret = -1; + goto end; + } + + channel_name = session_name + condition_comm->session_name_len; + if (*(channel_name + condition_comm->channel_name_len - 1) != '\0') { + ERR("Malformed channel name encountered in condition buffer"); + ret = -1; + goto end; + } + + status = lttng_condition_buffer_usage_set_session_name(condition, + session_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage session name"); + ret = -1; + goto end; + } + + status = lttng_condition_buffer_usage_set_channel_name(condition, + channel_name); + if (status != LTTNG_CONDITION_STATUS_OK) { + ERR("Failed to set buffer usage channel name"); + ret = -1; + goto end; + } + + if (!lttng_condition_validate(condition)) { + ret = -1; + goto end; + } + + condition_size = sizeof(*condition_comm) + + (ssize_t) condition_comm->session_name_len + + (ssize_t) condition_comm->channel_name_len; + ret = condition_size; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_buffer_usage_low_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **_condition) +{ + ssize_t ret; + struct lttng_condition *condition = + lttng_condition_buffer_usage_low_create(); + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_buffer(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_buffer_usage_high_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_condition **_condition) +{ + ssize_t ret; + struct lttng_condition *condition = + lttng_condition_buffer_usage_high_create(); + + if (!_condition || !condition) { + ret = -1; + goto error; + } + + ret = init_condition_from_buffer(condition, view); + if (ret < 0) { + goto error; + } + + *_condition = condition; + return ret; +error: + lttng_condition_destroy(condition); + return ret; +} + +static +struct lttng_evaluation *create_evaluation_from_buffer( + enum lttng_condition_type type, + const struct lttng_buffer_view *view) +{ + const struct lttng_evaluation_buffer_usage_comm *comm = + (const struct lttng_evaluation_buffer_usage_comm *) view->data; + struct lttng_evaluation *evaluation = NULL; + + if (view->size < sizeof(*comm)) { + goto end; + } + + evaluation = lttng_evaluation_buffer_usage_create(type, + comm->buffer_use, comm->buffer_capacity); +end: + return evaluation; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_buffer_usage_low_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + evaluation = create_evaluation_from_buffer( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW, view); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + ret = sizeof(struct lttng_evaluation_buffer_usage_comm); + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_buffer_usage_high_create_from_buffer( + const struct lttng_buffer_view *view, + struct lttng_evaluation **_evaluation) +{ + ssize_t ret; + struct lttng_evaluation *evaluation = NULL; + + if (!_evaluation) { + ret = -1; + goto error; + } + + evaluation = create_evaluation_from_buffer( + LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH, view); + if (!evaluation) { + ret = -1; + goto error; + } + + *_evaluation = evaluation; + ret = sizeof(struct lttng_evaluation_buffer_usage_comm); + return ret; +error: + lttng_evaluation_destroy(evaluation); + return ret; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold_ratio( + const struct lttng_condition *condition, + double *threshold_ratio) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + !threshold_ratio) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->threshold_ratio.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *threshold_ratio = usage->threshold_ratio.value; +end: + return status; +} + +/* threshold_ratio expressed as [0.0, 1.0]. */ +enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold_ratio( + struct lttng_condition *condition, double threshold_ratio) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + threshold_ratio < 0.0 || + threshold_ratio > 1.0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->threshold_ratio.set = true; + usage->threshold_bytes.set = false; + usage->threshold_ratio.value = threshold_ratio; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_threshold( + const struct lttng_condition *condition, + uint64_t *threshold_bytes) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !threshold_bytes) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->threshold_bytes.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *threshold_bytes = usage->threshold_bytes.value; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_threshold( + struct lttng_condition *condition, uint64_t threshold_bytes) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition)) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->threshold_ratio.set = false; + usage->threshold_bytes.set = true; + usage->threshold_bytes.value = threshold_bytes; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_session_name( + const struct lttng_condition *condition, + const char **session_name) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !session_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->session_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *session_name = usage->session_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_session_name( + struct lttng_condition *condition, const char *session_name) +{ + char *session_name_copy; + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + session_name_copy = strdup(session_name); + if (!session_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + if (usage->session_name) { + free(usage->session_name); + } + usage->session_name = session_name_copy; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_channel_name( + const struct lttng_condition *condition, + const char **channel_name) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->channel_name) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *channel_name = usage->channel_name; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_channel_name( + struct lttng_condition *condition, const char *channel_name) +{ + char *channel_name_copy; + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !channel_name || + strlen(channel_name) == 0) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + channel_name_copy = strdup(channel_name); + if (!channel_name_copy) { + status = LTTNG_CONDITION_STATUS_ERROR; + goto end; + } + + if (usage->channel_name) { + free(usage->channel_name); + } + usage->channel_name = channel_name_copy; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_get_domain_type( + const struct lttng_condition *condition, + enum lttng_domain_type *type) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || !type) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + if (!usage->domain.set) { + status = LTTNG_CONDITION_STATUS_UNSET; + goto end; + } + *type = usage->domain.type; +end: + return status; +} + +enum lttng_condition_status +lttng_condition_buffer_usage_set_domain_type( + struct lttng_condition *condition, enum lttng_domain_type type) +{ + struct lttng_condition_buffer_usage *usage; + enum lttng_condition_status status = LTTNG_CONDITION_STATUS_OK; + + if (!condition || !IS_USAGE_CONDITION(condition) || + type == LTTNG_DOMAIN_NONE) { + status = LTTNG_CONDITION_STATUS_INVALID; + goto end; + } + + usage = container_of(condition, struct lttng_condition_buffer_usage, + parent); + usage->domain.set = true; + usage->domain.type = type; +end: + return status; +} + +static +ssize_t lttng_evaluation_buffer_usage_serialize( + struct lttng_evaluation *evaluation, char *buf) +{ + ssize_t ret; + struct lttng_evaluation_buffer_usage *usage; + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + if (buf) { + struct lttng_evaluation_buffer_usage_comm comm = { + .buffer_use = usage->buffer_use, + .buffer_capacity = usage->buffer_capacity, + }; + + memcpy(buf, &comm, sizeof(comm)); + } + + ret = sizeof(struct lttng_evaluation_buffer_usage_comm); + return ret; +} + +static +void lttng_evaluation_buffer_usage_destroy( + struct lttng_evaluation *evaluation) +{ + struct lttng_evaluation_buffer_usage *usage; + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + free(usage); +} + +LTTNG_HIDDEN +struct lttng_evaluation *lttng_evaluation_buffer_usage_create( + enum lttng_condition_type type, uint64_t use, uint64_t capacity) +{ + struct lttng_evaluation_buffer_usage *usage; + + usage = zmalloc(sizeof(struct lttng_evaluation_buffer_usage)); + if (!usage) { + goto end; + } + + usage->parent.type = type; + usage->buffer_use = use; + usage->buffer_capacity = capacity; + usage->parent.serialize = lttng_evaluation_buffer_usage_serialize; + usage->parent.destroy = lttng_evaluation_buffer_usage_destroy; +end: + return &usage->parent; +} + +/* + * Get the sampled buffer usage which caused the associated condition to + * evaluate to "true". + */ +enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage_ratio( + const struct lttng_evaluation *evaluation, double *usage_ratio) +{ + struct lttng_evaluation_buffer_usage *usage; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_usage_evaluation(evaluation) || !usage_ratio) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + *usage_ratio = (double) usage->buffer_use / + (double) usage->buffer_capacity; +end: + return status; +} + +enum lttng_evaluation_status +lttng_evaluation_buffer_usage_get_usage( + const struct lttng_evaluation *evaluation, + uint64_t *usage_bytes) +{ + struct lttng_evaluation_buffer_usage *usage; + enum lttng_evaluation_status status = LTTNG_EVALUATION_STATUS_OK; + + if (!evaluation || !is_usage_evaluation(evaluation) || !usage_bytes) { + status = LTTNG_EVALUATION_STATUS_INVALID; + goto end; + } + + usage = container_of(evaluation, struct lttng_evaluation_buffer_usage, + parent); + *usage_bytes = usage->buffer_use; +end: + return status; +} diff --git a/src/common/condition.c b/src/common/condition.c new file mode 100644 index 000000000..0b184422a --- /dev/null +++ b/src/common/condition.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum lttng_condition_type lttng_condition_get_type( + const struct lttng_condition *condition) +{ + return condition ? condition->type : LTTNG_CONDITION_TYPE_UNKNOWN; +} + +void lttng_condition_destroy(struct lttng_condition *condition) +{ + if (!condition) { + return; + } + + assert(condition->destroy); + condition->destroy(condition); +} + +LTTNG_HIDDEN +bool lttng_condition_validate(const struct lttng_condition *condition) +{ + bool valid; + + if (!condition) { + valid = false; + goto end; + } + + if (!condition->validate) { + /* Sub-class guarantees that it can never be invalid. */ + valid = true; + goto end; + } + + valid = condition->validate(condition); +end: + return valid; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_serialize(const struct lttng_condition *condition, + char *buf) +{ + ssize_t ret, condition_size; + struct lttng_condition_comm condition_comm; + + if (!condition) { + ret = -1; + goto end; + } + + condition_comm.condition_type = (int8_t) condition->type; + ret = sizeof(struct lttng_condition_comm); + if (buf) { + memcpy(buf, &condition_comm, ret); + buf += ret; + } + + condition_size = condition->serialize(condition, buf); + if (condition_size < 0) { + ret = condition_size; + goto end; + } + ret += condition_size; +end: + return ret; +} + +LTTNG_HIDDEN +bool lttng_condition_is_equal(const struct lttng_condition *a, + const struct lttng_condition *b) +{ + bool is_equal = false; + + if (!a || !b) { + goto end; + } + + if (a->type != b->type) { + goto end; + } + + is_equal = a->equal ? a->equal(a, b) : true; +end: + return is_equal; +} + +LTTNG_HIDDEN +ssize_t lttng_condition_create_from_buffer( + const struct lttng_buffer_view *buffer, + struct lttng_condition **condition) +{ + ssize_t ret, condition_size = 0; + const struct lttng_condition_comm *condition_comm; + condition_create_from_buffer_cb create_from_buffer = NULL; + + if (!buffer || !condition) { + ret = -1; + goto end; + } + + DBG("Deserializing condition from buffer"); + condition_comm = (const struct lttng_condition_comm *) buffer->data; + condition_size += sizeof(*condition_comm); + + switch ((enum lttng_condition_type) condition_comm->condition_type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + create_from_buffer = lttng_condition_buffer_usage_low_create_from_buffer; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + create_from_buffer = lttng_condition_buffer_usage_high_create_from_buffer; + break; + default: + ERR("Attempted to create condition of unknown type (%i)", + (int) condition_comm->condition_type); + ret = -1; + goto end; + } + + if (create_from_buffer) { + const struct lttng_buffer_view view = + lttng_buffer_view_from_view(buffer, + sizeof(*condition_comm), -1); + + ret = create_from_buffer(&view, condition); + if (ret < 0) { + goto end; + } + condition_size += ret; + + } else { + abort(); + } + + ret = condition_size; +end: + return ret; +} + +LTTNG_HIDDEN +void lttng_condition_init(struct lttng_condition *condition, + enum lttng_condition_type type) +{ + condition->type = type; +} diff --git a/src/common/defaults.h b/src/common/defaults.h index 37d222a78..818332f54 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -106,10 +106,12 @@ #define DEFAULT_LTTNG_EXTRA_KMOD_PROBES "LTTNG_EXTRA_KMOD_PROBES" /* Default unix socket path */ -#define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/client-lttng-sessiond" -#define DEFAULT_HOME_CLIENT_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/client-lttng-sessiond" -#define DEFAULT_GLOBAL_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/sessiond-health" -#define DEFAULT_HOME_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/sessiond-health" +#define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/client-lttng-sessiond" +#define DEFAULT_HOME_CLIENT_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/client-lttng-sessiond" +#define DEFAULT_GLOBAL_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/sessiond-health" +#define DEFAULT_HOME_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/sessiond-health" +#define DEFAULT_GLOBAL_NOTIFICATION_CHANNEL_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/sessiond-notification" +#define DEFAULT_HOME_NOTIFICATION_CHANNEL_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/sessiond-notification" /* Default consumer health unix socket path */ #define DEFAULT_GLOBAL_USTCONSUMER32_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/ustconsumerd32/health" @@ -313,6 +315,12 @@ /* Default thread stack size; the default mandated by pthread_create(3) */ #define DEFAULT_LTTNG_THREAD_STACK_SIZE 2097152 +/* Default maximal size of message notification channel message payloads. */ +#define DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE 65536 + +/* Default maximal size of message notification channel message payloads. */ +#define DEFAULT_CLIENT_MAX_QUEUED_NOTIFICATIONS_COUNT 100 + /* * Returns the default subbuf size. * diff --git a/src/common/endpoint.c b/src/common/endpoint.c new file mode 100644 index 000000000..89066de55 --- /dev/null +++ b/src/common/endpoint.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static +struct lttng_endpoint lttng_session_daemon_notification_endpoint_instance = { + .type = LTTNG_ENDPOINT_TYPE_DEFAULT_SESSIOND_NOTIFICATION +}; + +struct lttng_endpoint *lttng_session_daemon_notification_endpoint = + <tng_session_daemon_notification_endpoint_instance; diff --git a/src/common/error.c b/src/common/error.c index 938932cdc..2215886db 100644 --- a/src/common/error.c +++ b/src/common/error.c @@ -186,6 +186,10 @@ static const char *error_string_array[] = { [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_FAIL) ] = "Failed to regenerate the state dump", [ ERROR_INDEX(LTTNG_ERR_REGEN_STATEDUMP_NOMEM) ] = "Failed to regenerate the state dump, not enough memory", [ ERROR_INDEX(LTTNG_ERR_NOT_SNAPSHOT_SESSION) ] = "Snapshot command can't be applied to a non-snapshot session", + [ ERROR_INDEX(LTTNG_ERR_INVALID_TRIGGER) ] = "Invalid trigger", + [ ERROR_INDEX(LTTNG_ERR_TRIGGER_EXISTS) ] = "Trigger already registered", + [ ERROR_INDEX(LTTNG_ERR_TRIGGER_NOT_FOUND) ] = "Trigger not found", + [ ERROR_INDEX(LTTNG_ERR_COMMAND_CANCELLED) ] = "Command cancelled", /* Last element */ [ ERROR_INDEX(LTTNG_ERR_NR) ] = "Unknown error code" diff --git a/src/common/evaluation.c b/src/common/evaluation.c new file mode 100644 index 000000000..c6243d232 --- /dev/null +++ b/src/common/evaluation.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +LTTNG_HIDDEN +ssize_t lttng_evaluation_serialize(struct lttng_evaluation *evaluation, + char *buf) +{ + ssize_t ret, offset = 0; + struct lttng_evaluation_comm evaluation_comm; + + evaluation_comm.type = (int8_t) evaluation->type; + if (buf) { + memcpy(buf, &evaluation_comm, sizeof(evaluation_comm)); + } + offset += sizeof(evaluation_comm); + + if (evaluation->serialize) { + ret = evaluation->serialize(evaluation, + buf ? (buf + offset) : NULL); + if (ret < 0) { + goto end; + } + offset += ret; + } + + ret = offset; +end: + return ret; +} + +LTTNG_HIDDEN +ssize_t lttng_evaluation_create_from_buffer( + const struct lttng_buffer_view *src_view, + struct lttng_evaluation **evaluation) +{ + ssize_t ret, evaluation_size = 0; + const struct lttng_evaluation_comm *evaluation_comm; + const struct lttng_buffer_view evaluation_view = + lttng_buffer_view_from_view(src_view, + sizeof(*evaluation_comm), -1); + + if (!src_view || !evaluation) { + ret = -1; + goto end; + } + + evaluation_comm = (const struct lttng_evaluation_comm *) src_view->data; + evaluation_size += sizeof(*evaluation_comm); + + switch ((enum lttng_condition_type) evaluation_comm->type) { + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_LOW: + ret = lttng_evaluation_buffer_usage_low_create_from_buffer( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + case LTTNG_CONDITION_TYPE_BUFFER_USAGE_HIGH: + ret = lttng_evaluation_buffer_usage_high_create_from_buffer( + &evaluation_view, evaluation); + if (ret < 0) { + goto end; + } + evaluation_size += ret; + break; + default: + ERR("Attempted to create evaluation of unknown type (%i)", + (int) evaluation_comm->type); + ret = -1; + goto end; + } + ret = evaluation_size; +end: + return ret; +} + +enum lttng_condition_type lttng_evaluation_get_type( + const struct lttng_evaluation *evaluation) +{ + return evaluation ? evaluation->type : LTTNG_CONDITION_TYPE_UNKNOWN; +} + +void lttng_evaluation_destroy(struct lttng_evaluation *evaluation) +{ + if (!evaluation) { + return; + } + + assert(evaluation->destroy); + evaluation->destroy(evaluation); +} diff --git a/src/common/notification.c b/src/common/notification.c new file mode 100644 index 000000000..5062ca5b1 --- /dev/null +++ b/src/common/notification.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +LTTNG_HIDDEN +struct lttng_notification *lttng_notification_create( + struct lttng_condition *condition, + struct lttng_evaluation *evaluation) +{ + struct lttng_notification *notification = NULL; + + if (!condition || !evaluation) { + goto end; + } + + notification = zmalloc(sizeof(struct lttng_notification)); + if (!notification) { + goto end; + } + + notification->condition = condition; + notification->evaluation = evaluation; + notification->owns_elements = false; +end: + return notification; +} + +LTTNG_HIDDEN +ssize_t lttng_notification_serialize(struct lttng_notification *notification, + char *buf) +{ + ssize_t ret, condition_size, evaluation_size, offset = 0; + struct lttng_notification_comm notification_comm; + + if (!notification) { + ret = -1; + goto end; + } + + offset += sizeof(notification_comm); + condition_size = lttng_condition_serialize(notification->condition, + buf ? (buf + offset) : NULL); + if (condition_size < 0) { + ret = condition_size; + goto end; + } + offset += condition_size; + + evaluation_size = lttng_evaluation_serialize(notification->evaluation, + buf ? (buf + offset) : NULL); + if (evaluation_size < 0) { + ret = evaluation_size; + goto end; + } + offset += evaluation_size; + + if (buf) { + notification_comm.length = + (uint32_t) (condition_size + evaluation_size); + memcpy(buf, ¬ification_comm, sizeof(notification_comm)); + } + ret = offset; +end: + return ret; + +} + +LTTNG_HIDDEN +ssize_t lttng_notification_create_from_buffer( + const struct lttng_buffer_view *src_view, + struct lttng_notification **notification) +{ + ssize_t ret, notification_size = 0, condition_size, evaluation_size; + const struct lttng_notification_comm *notification_comm; + struct lttng_condition *condition; + struct lttng_evaluation *evaluation; + struct lttng_buffer_view condition_view; + struct lttng_buffer_view evaluation_view; + + if (!src_view || !notification) { + ret = -1; + goto end; + } + + notification_comm = + (const struct lttng_notification_comm *) src_view->data; + notification_size += sizeof(*notification_comm); + + /* struct lttng_condition */ + condition_view = lttng_buffer_view_from_view(src_view, + sizeof(*notification_comm), -1); + condition_size = lttng_condition_create_from_buffer(&condition_view, + &condition); + if (condition_size < 0) { + ret = condition_size; + goto end; + } + notification_size += condition_size; + + /* struct lttng_evaluation */ + evaluation_view = lttng_buffer_view_from_view(&condition_view, + condition_size, -1); + evaluation_size = lttng_evaluation_create_from_buffer(&evaluation_view, + &evaluation); + if (evaluation_size < 0) { + ret = evaluation_size; + goto end; + } + notification_size += evaluation_size; + + /* Unexpected size of inner-elements; the buffer is corrupted. */ + if ((ssize_t) notification_comm->length != + condition_size + evaluation_size) { + ret = -1; + goto error; + } + + *notification = lttng_notification_create(condition, evaluation); + if (!*notification) { + ret = -1; + goto error; + } + ret = notification_size; + (*notification)->owns_elements = true; +end: + return ret; +error: + lttng_condition_destroy(condition); + lttng_evaluation_destroy(evaluation); + return ret; +} + +void lttng_notification_destroy(struct lttng_notification *notification) +{ + if (!notification) { + return; + } + + if (notification->owns_elements) { + lttng_condition_destroy(notification->condition); + lttng_evaluation_destroy(notification->evaluation); + } + free(notification); +} + +const struct lttng_condition *lttng_notification_get_condition( + struct lttng_notification *notification) +{ + return notification ? notification->condition : NULL; +} + +const struct lttng_evaluation *lttng_notification_get_evaluation( + struct lttng_notification *notification) +{ + return notification ? notification->evaluation : NULL; +} diff --git a/src/common/notify.c b/src/common/notify.c new file mode 100644 index 000000000..956c0ecff --- /dev/null +++ b/src/common/notify.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +static +void lttng_action_notify_destroy(struct lttng_action *action) +{ + free(action); +} + +static +ssize_t lttng_action_notify_serialize(struct lttng_action *action, char *buf) +{ + return 0; +} + +struct lttng_action *lttng_action_notify_create(void) +{ + struct lttng_action_notify *notify; + + notify = zmalloc(sizeof(struct lttng_action_notify)); + if (!notify) { + goto end; + } + + notify->parent.type = LTTNG_ACTION_TYPE_NOTIFY; + notify->parent.serialize = lttng_action_notify_serialize; + notify->parent.destroy = lttng_action_notify_destroy; +end: + return ¬ify->parent; +} diff --git a/src/common/trigger.c b/src/common/trigger.c new file mode 100644 index 000000000..13f26d661 --- /dev/null +++ b/src/common/trigger.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +LTTNG_HIDDEN +bool lttng_trigger_validate(struct lttng_trigger *trigger) +{ + bool valid; + + if (!trigger) { + valid = false; + goto end; + } + + valid = lttng_condition_validate(trigger->condition) && + lttng_action_validate(trigger->action); +end: + return valid; +} + +struct lttng_trigger *lttng_trigger_create( + struct lttng_condition *condition, + struct lttng_action *action) +{ + struct lttng_trigger *trigger = NULL; + + if (!condition || !action) { + goto end; + } + + trigger = zmalloc(sizeof(struct lttng_trigger)); + if (!trigger) { + goto end; + } + + trigger->condition = condition; + trigger->action = action; +end: + return trigger; +} + +struct lttng_condition *lttng_trigger_get_condition( + struct lttng_trigger *trigger) +{ + return trigger ? trigger->condition : NULL; +} + +extern struct lttng_action *lttng_trigger_get_action( + struct lttng_trigger *trigger) +{ + return trigger ? trigger->action : NULL; +} + +void lttng_trigger_destroy(struct lttng_trigger *trigger) +{ + if (!trigger) { + return; + } + + free(trigger); +} + +LTTNG_HIDDEN +ssize_t lttng_trigger_create_from_buffer( + const struct lttng_buffer_view *src_view, + struct lttng_trigger **trigger) +{ + ssize_t ret, offset = 0, condition_size, action_size; + struct lttng_condition *condition = NULL; + struct lttng_action *action = NULL; + const struct lttng_trigger_comm *trigger_comm; + struct lttng_buffer_view condition_view; + struct lttng_buffer_view action_view; + + if (!src_view || !trigger) { + ret = -1; + goto end; + } + + /* lttng_trigger_comm header */ + trigger_comm = (const struct lttng_trigger_comm *) src_view->data; + offset += sizeof(*trigger_comm); + + condition_view = lttng_buffer_view_from_view(src_view, offset, -1); + + /* struct lttng_condition */ + condition_size = lttng_condition_create_from_buffer(&condition_view, + &condition); + if (condition_size < 0) { + ret = condition_size; + goto end; + } + offset += condition_size; + + /* struct lttng_action */ + action_view = lttng_buffer_view_from_view(src_view, offset, -1); + action_size = lttng_action_create_from_buffer(&action_view, &action); + if (action_size < 0) { + ret = action_size; + goto end; + } + offset += action_size; + + /* Unexpected size of inner-elements; the buffer is corrupted. */ + if ((ssize_t) trigger_comm->length != condition_size + action_size) { + ret = -1; + goto error; + } + + *trigger = lttng_trigger_create(condition, action); + if (!*trigger) { + ret = -1; + goto error; + } + ret = offset; +end: + return ret; +error: + lttng_condition_destroy(condition); + lttng_action_destroy(action); + return ret; +} + +/* + * Returns the size of a trigger (header + condition + action). + * Both elements are stored contiguously, see their "*_comm" structure + * for the detailed format. + */ +LTTNG_HIDDEN +ssize_t lttng_trigger_serialize(struct lttng_trigger *trigger, char *buf) +{ + struct lttng_trigger_comm trigger_comm; + ssize_t action_size, condition_size, offset = 0, ret; + + if (!trigger) { + ret = -1; + goto end; + } + + offset += sizeof(trigger_comm); + condition_size = lttng_condition_serialize(trigger->condition, + buf ? (buf + offset) : NULL); + if (condition_size < 0) { + ret = -1; + goto end; + } + offset += condition_size; + + action_size = lttng_action_serialize(trigger->action, + buf ? (buf + offset) : NULL); + if (action_size < 0) { + ret = -1; + goto end; + } + offset += action_size; + + if (buf) { + trigger_comm.length = (uint32_t) (condition_size + action_size); + memcpy(buf, &trigger_comm, sizeof(trigger_comm)); + } + ret = offset; +end: + return ret; +} diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index 6b6a6eed1..b5156caf9 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -5,7 +5,8 @@ SUBDIRS = filter lib_LTLIBRARIES = liblttng-ctl.la liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \ - lttng-ctl-health.c save.c load.c deprecated-symbols.c + lttng-ctl-health.c save.c load.c deprecated-symbols.c \ + channel.c liblttng_ctl_la_LDFLAGS = \ $(LT_NO_UNDEFINED) diff --git a/src/lib/lttng-ctl/channel.c b/src/lib/lttng-ctl/channel.c new file mode 100644 index 000000000..75a911f2a --- /dev/null +++ b/src/lib/lttng-ctl/channel.c @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2017 - Jérémie Galarneau + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lttng-ctl-helper.h" + +static +int handshake(struct lttng_notification_channel *channel); + +/* + * Populates the reception buffer with the next complete message. + * The caller must acquire the client's lock. + */ +static +int receive_message(struct lttng_notification_channel *channel) +{ + ssize_t ret; + struct lttng_notification_channel_message msg; + + ret = lttng_dynamic_buffer_set_size(&channel->reception_buffer, 0); + if (ret) { + goto error; + } + + ret = lttcomm_recv_unix_sock(channel->socket, &msg, sizeof(msg)); + if (ret <= 0) { + ret = -1; + goto error; + } + + if (msg.size > DEFAULT_MAX_NOTIFICATION_CLIENT_MESSAGE_PAYLOAD_SIZE) { + ret = -1; + goto error; + } + + /* Add message header at buffer's start. */ + ret = lttng_dynamic_buffer_append(&channel->reception_buffer, &msg, + sizeof(msg)); + if (ret) { + goto error; + } + + /* Reserve space for the payload. */ + ret = lttng_dynamic_buffer_set_size(&channel->reception_buffer, + channel->reception_buffer.size + msg.size); + if (ret) { + goto error; + } + + /* Receive message payload. */ + ret = lttcomm_recv_unix_sock(channel->socket, + channel->reception_buffer.data + sizeof(msg), msg.size); + if (ret < (ssize_t) msg.size) { + ret = -1; + goto error; + } + ret = 0; +end: + return ret; +error: + lttng_dynamic_buffer_set_size(&channel->reception_buffer, 0); + goto end; +} + +static +enum lttng_notification_channel_message_type get_current_message_type( + struct lttng_notification_channel *channel) +{ + struct lttng_notification_channel_message *msg; + + assert(channel->reception_buffer.size >= sizeof(*msg)); + + msg = (struct lttng_notification_channel_message *) + channel->reception_buffer.data; + return (enum lttng_notification_channel_message_type) msg->type; +} + +static +struct lttng_notification *create_notification_from_current_message( + struct lttng_notification_channel *channel) +{ + ssize_t ret; + struct lttng_notification *notification = NULL; + struct lttng_buffer_view view; + + if (channel->reception_buffer.size <= + sizeof(struct lttng_notification_channel_message)) { + goto end; + } + + view = lttng_buffer_view_from_dynamic_buffer(&channel->reception_buffer, + sizeof(struct lttng_notification_channel_message), -1); + + ret = lttng_notification_create_from_buffer(&view, ¬ification); + if (ret != channel->reception_buffer.size - + sizeof(struct lttng_notification_channel_message)) { + lttng_notification_destroy(notification); + notification = NULL; + goto end; + } +end: + return notification; +} + +struct lttng_notification_channel *lttng_notification_channel_create( + struct lttng_endpoint *endpoint) +{ + int fd, ret; + bool is_in_tracing_group = false, is_root = false; + char *sock_path = NULL; + struct lttng_notification_channel *channel = NULL; + + if (!endpoint || + endpoint != lttng_session_daemon_notification_endpoint) { + goto end; + } + + sock_path = zmalloc(LTTNG_PATH_MAX); + if (!sock_path) { + goto end; + } + + channel = zmalloc(sizeof(struct lttng_notification_channel)); + if (!channel) { + goto end; + } + channel->socket = -1; + pthread_mutex_init(&channel->lock, NULL); + lttng_dynamic_buffer_init(&channel->reception_buffer); + CDS_INIT_LIST_HEAD(&channel->pending_notifications.list); + + is_root = (getuid() == 0); + if (!is_root) { + is_in_tracing_group = lttng_check_tracing_group(); + } + + if (is_root || is_in_tracing_group) { + lttng_ctl_copy_string(sock_path, + DEFAULT_GLOBAL_NOTIFICATION_CHANNEL_UNIX_SOCK, + LTTNG_PATH_MAX); + ret = lttcomm_connect_unix_sock(sock_path); + if (ret >= 0) { + fd = ret; + goto set_fd; + } + } + + /* Fallback to local session daemon. */ + ret = snprintf(sock_path, LTTNG_PATH_MAX, + DEFAULT_HOME_NOTIFICATION_CHANNEL_UNIX_SOCK, + utils_get_home_dir()); + if (ret < 0 || ret >= LTTNG_PATH_MAX) { + goto error; + } + + ret = lttcomm_connect_unix_sock(sock_path); + if (ret < 0) { + goto error; + } + fd = ret; + +set_fd: + channel->socket = fd; + + ret = handshake(channel); + if (ret) { + goto error; + } +end: + free(sock_path); + return channel; +error: + lttng_notification_channel_destroy(channel); + channel = NULL; + goto end; +} + +enum lttng_notification_channel_status +lttng_notification_channel_get_next_notification( + struct lttng_notification_channel *channel, + struct lttng_notification **_notification) +{ + int ret; + struct lttng_notification *notification = NULL; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + + if (!channel || !_notification) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; + goto end; + } + + if (channel->pending_notifications.count) { + struct pending_notification *pending_notification; + + assert(!cds_list_empty(&channel->pending_notifications.list)); + + /* Deliver one of the pending notifications. */ + pending_notification = cds_list_first_entry( + &channel->pending_notifications.list, + struct pending_notification, + node); + notification = pending_notification->notification; + if (!notification) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED; + } + cds_list_del(&pending_notification->node); + channel->pending_notifications.count--; + free(pending_notification); + goto end; + } + + pthread_mutex_lock(&channel->lock); + + ret = receive_message(channel); + if (ret) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + + switch (get_current_message_type(channel)) { + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION: + notification = create_notification_from_current_message( + channel); + if (!notification) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + break; + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED: + /* No payload to consume. */ + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_NOTIFICATIONS_DROPPED; + break; + default: + /* Protocol error. */ + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + +end_unlock: + pthread_mutex_unlock(&channel->lock); +end: + if (_notification) { + *_notification = notification; + } + return status; +} + +static +int enqueue_dropped_notification( + struct lttng_notification_channel *channel) +{ + int ret = 0; + struct pending_notification *pending_notification; + struct cds_list_head *last_element = + channel->pending_notifications.list.prev; + + pending_notification = caa_container_of(last_element, + struct pending_notification, node); + if (!pending_notification->notification) { + /* + * The last enqueued notification indicates dropped + * notifications; there is nothing to do as we group + * dropped notifications together. + */ + goto end; + } + + if (channel->pending_notifications.count >= + DEFAULT_CLIENT_MAX_QUEUED_NOTIFICATIONS_COUNT && + pending_notification->notification) { + /* + * Discard the last enqueued notification to indicate + * that notifications were dropped at this point. + */ + lttng_notification_destroy( + pending_notification->notification); + pending_notification->notification = NULL; + goto end; + } + + pending_notification = zmalloc(sizeof(*pending_notification)); + if (!pending_notification) { + ret = -1; + goto end; + } + CDS_INIT_LIST_HEAD(&pending_notification->node); + cds_list_add(&pending_notification->node, + &channel->pending_notifications.list); + channel->pending_notifications.count++; +end: + return ret; +} + +static +int enqueue_notification_from_current_message( + struct lttng_notification_channel *channel) +{ + int ret = 0; + struct lttng_notification *notification; + struct pending_notification *pending_notification; + + if (channel->pending_notifications.count >= + DEFAULT_CLIENT_MAX_QUEUED_NOTIFICATIONS_COUNT) { + /* Drop the notification. */ + ret = enqueue_dropped_notification(channel); + goto end; + } + + pending_notification = zmalloc(sizeof(*pending_notification)); + if (!pending_notification) { + ret = -1; + goto error; + } + CDS_INIT_LIST_HEAD(&pending_notification->node); + + notification = create_notification_from_current_message(channel); + if (!notification) { + ret = -1; + goto error; + } + + pending_notification->notification = notification; + cds_list_add(&pending_notification->node, + &channel->pending_notifications.list); + channel->pending_notifications.count++; +end: + return ret; +error: + free(pending_notification); + goto end; +} + +static +int receive_command_reply(struct lttng_notification_channel *channel, + enum lttng_notification_channel_status *status) +{ + int ret; + struct lttng_notification_channel_command_reply *reply; + + while (true) { + enum lttng_notification_channel_message_type msg_type; + + ret = receive_message(channel); + if (ret) { + goto end; + } + + msg_type = get_current_message_type(channel); + switch (msg_type) { + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_COMMAND_REPLY: + goto exit_loop; + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION: + ret = enqueue_notification_from_current_message( + channel); + if (ret) { + goto end; + } + break; + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_NOTIFICATION_DROPPED: + ret = enqueue_dropped_notification(channel); + if (ret) { + goto end; + } + break; + case LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE: + { + struct lttng_notification_channel_command_handshake *handshake; + + handshake = (struct lttng_notification_channel_command_handshake *) + (channel->reception_buffer.data + + sizeof(struct lttng_notification_channel_message)); + channel->version.major = handshake->major; + channel->version.minor = handshake->minor; + channel->version.set = true; + break; + } + default: + ret = -1; + goto end; + } + } + +exit_loop: + if (channel->reception_buffer.size < + (sizeof(struct lttng_notification_channel_message) + + sizeof(*reply))) { + /* Invalid message received. */ + ret = -1; + goto end; + } + + reply = (struct lttng_notification_channel_command_reply *) + (channel->reception_buffer.data + + sizeof(struct lttng_notification_channel_message)); + *status = (enum lttng_notification_channel_status) reply->status; +end: + return ret; +} + +static +int handshake(struct lttng_notification_channel *channel) +{ + ssize_t ret; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + struct lttng_notification_channel_command_handshake handshake = { + .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR, + .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR, + }; + struct lttng_notification_channel_message msg_header = { + .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE, + .size = sizeof(handshake), + }; + char send_buffer[sizeof(msg_header) + sizeof(handshake)]; + + memcpy(send_buffer, &msg_header, sizeof(msg_header)); + memcpy(send_buffer + sizeof(msg_header), &handshake, sizeof(handshake)); + + pthread_mutex_lock(&channel->lock); + + ret = lttcomm_send_unix_sock(channel->socket, send_buffer, + sizeof(send_buffer)); + if (ret < 0) { + goto end_unlock; + } + + /* Receive handshake info from the sessiond. */ + ret = receive_command_reply(channel, &status); + if (ret < 0) { + goto end_unlock; + } + + if (!channel->version.set) { + ret = -1; + goto end_unlock; + } + + if (channel->version.major != LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) { + ret = -1; + goto end_unlock; + } + +end_unlock: + pthread_mutex_unlock(&channel->lock); + return ret; +} + +static +enum lttng_notification_channel_status send_condition_command( + struct lttng_notification_channel *channel, + enum lttng_notification_channel_message_type type, + const struct lttng_condition *condition) +{ + int socket; + ssize_t command_size, ret; + enum lttng_notification_channel_status status = + LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; + char *command_buffer = NULL; + struct lttng_notification_channel_message cmd_message = { + .type = type, + }; + + if (!channel) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; + goto end; + } + + assert(type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE || + type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE); + + pthread_mutex_lock(&channel->lock); + socket = channel->socket; + if (!lttng_condition_validate(condition)) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; + goto end_unlock; + } + + ret = lttng_condition_serialize(condition, NULL); + if (ret < 0) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; + goto end_unlock; + } + assert(ret < UINT32_MAX); + cmd_message.size = (uint32_t) ret; + command_size = ret + sizeof( + struct lttng_notification_channel_message); + command_buffer = zmalloc(command_size); + if (!command_buffer) { + goto end_unlock; + } + + memcpy(command_buffer, &cmd_message, sizeof(cmd_message)); + ret = lttng_condition_serialize(condition, + command_buffer + sizeof(cmd_message)); + if (ret < 0) { + goto end_unlock; + } + + ret = lttcomm_send_unix_sock(socket, command_buffer, command_size); + if (ret < 0) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } + + ret = receive_command_reply(channel, &status); + if (ret < 0) { + status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; + goto end_unlock; + } +end_unlock: + pthread_mutex_unlock(&channel->lock); +end: + free(command_buffer); + return status; +} + +enum lttng_notification_channel_status lttng_notification_channel_subscribe( + struct lttng_notification_channel *channel, + const struct lttng_condition *condition) +{ + return send_condition_command(channel, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE, + condition); +} + +enum lttng_notification_channel_status lttng_notification_channel_unsubscribe( + struct lttng_notification_channel *channel, + const struct lttng_condition *condition) +{ + return send_condition_command(channel, + LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE, + condition); +} + +void lttng_notification_channel_destroy( + struct lttng_notification_channel *channel) +{ + if (!channel) { + return; + } + + if (channel->socket >= 0) { + (void) lttcomm_close_unix_sock(channel->socket); + } + pthread_mutex_destroy(&channel->lock); + lttng_dynamic_buffer_reset(&channel->reception_buffer); + free(channel); +} diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index f0b211c7b..4e1665f40 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include "filter/filter-ast.h" #include "filter/filter-parser.h" @@ -2435,6 +2438,94 @@ end: return ret; } +int lttng_register_trigger(struct lttng_trigger *trigger) +{ + int ret; + struct lttcomm_session_msg lsm; + char *trigger_buf = NULL; + ssize_t trigger_size; + + if (!trigger) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (!lttng_trigger_validate(trigger)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + trigger_size = lttng_trigger_serialize(trigger, NULL); + if (trigger_size < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + trigger_buf = zmalloc(trigger_size); + if (!trigger_buf) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_REGISTER_TRIGGER; + if (lttng_trigger_serialize(trigger, trigger_buf) < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + lsm.u.trigger.length = (uint32_t) trigger_size; + ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, trigger_buf, + trigger_size, NULL); +end: + free(trigger_buf); + return ret; +} + +int lttng_unregister_trigger(struct lttng_trigger *trigger) +{ + int ret; + struct lttcomm_session_msg lsm; + char *trigger_buf = NULL; + ssize_t trigger_size; + + if (!trigger) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + if (!lttng_trigger_validate(trigger)) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + trigger_size = lttng_trigger_serialize(trigger, NULL); + if (trigger_size < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + trigger_buf = zmalloc(trigger_size); + if (!trigger_buf) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_UNREGISTER_TRIGGER; + if (lttng_trigger_serialize(trigger, trigger_buf) < 0) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + lsm.u.trigger.length = (uint32_t) trigger_size; + ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, trigger_buf, + trigger_size, NULL); +end: + free(trigger_buf); + return ret; +} + /* * lib constructor. */ -- 2.34.1