diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c index 7f5073a658bf..4d2b6ef2b5a1 100644 --- a/lib/mgmt_be_client.c +++ b/lib/mgmt_be_client.c @@ -786,8 +786,8 @@ static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree, struct be_client_tree_data_batch_args *args = arg; struct mgmt_be_client *client = args->client; struct mgmt_msg_tree_data *tree_msg = NULL; - uint8_t *buf = NULL; bool more = false; + uint8_t **darrp; LY_ERR err; if (ret == NB_YIELD) { @@ -797,26 +797,26 @@ static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree, if (ret != NB_OK) goto done; - darr_append_nz(buf, offsetof(typeof(*tree_msg), result)); - tree_msg = (typeof(tree_msg))buf; + tree_msg = mgmt_msg_appendable_alloc_msg(struct mgmt_msg_tree_data); tree_msg->refer_id = args->txn_id; tree_msg->req_id = args->req_id; tree_msg->code = MGMT_MSG_CODE_TREE_DATA; tree_msg->result_type = args->result_type; tree_msg->more = more; - err = yang_print_tree_append(&buf, tree, args->result_type, + + darrp = mgmt_msg_appendable_get_darrp(tree_msg); + err = yang_print_tree_append(darrp, tree, args->result_type, (LYD_PRINT_WD_EXPLICIT | LYD_PRINT_WITHSIBLINGS)); - /* buf may have been reallocated and moved */ - tree_msg = (typeof(tree_msg))buf; - if (err) { ret = NB_ERR; goto done; } - (void)be_client_send_native_msg(client, buf, darr_len(buf), false); + (void)be_client_send_native_msg(client, tree_msg, + mgmt_msg_appendable_get_msg_len(tree_msg), + false); done: - darr_free(buf); + mgmt_msg_appendable_free_msg(tree_msg); if (ret) be_client_send_error(client, args->txn_id, args->req_id, false, -EINVAL, diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c index dccfb1dae620..f21c7edb39f2 100644 --- a/lib/mgmt_fe_client.c +++ b/lib/mgmt_fe_client.c @@ -35,7 +35,6 @@ DECLARE_LIST(mgmt_sessions, struct mgmt_fe_client_session, list_linkage); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT, "frontend client"); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT_NAME, "frontend client name"); -DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_GET_DATA_MSG, "FE get data msg"); DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "frontend session"); struct mgmt_fe_client { @@ -324,7 +323,7 @@ int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client, size_t mlen = sizeof(*msg) + xplen + 1; int ret; - msg = XCALLOC(MTYPE_MGMTD_FE_GET_DATA_MSG, mlen); + msg = XCALLOC(MTYPE_MSG_NATIVE_GET_TREE, mlen); msg->refer_id = session_id; msg->req_id = req_id; msg->code = MGMT_MSG_CODE_GET_TREE; @@ -336,7 +335,7 @@ int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client, session_id, req_id, xpath); ret = fe_client_send_native_msg(client, msg, mlen, false); - XFREE(MTYPE_MGMTD_FE_GET_DATA_MSG, msg); + XFREE(MTYPE_MSG_NATIVE_GET_TREE, msg); return ret; } diff --git a/lib/mgmt_msg_native.c b/lib/mgmt_msg_native.c index 61ed9f732540..024c0bcfafad 100644 --- a/lib/mgmt_msg_native.c +++ b/lib/mgmt_msg_native.c @@ -11,6 +11,7 @@ DEFINE_MGROUP(MSG_NATIVE, "Native message allocations"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_MSG, "native mgmt msg"); DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_ERROR, "native mgmt error msg"); +DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg"); int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id, uint64_t req_id, bool short_circuit_ok, diff --git a/lib/mgmt_msg_native.h b/lib/mgmt_msg_native.h index 472c84dea337..faca4b792419 100644 --- a/lib/mgmt_msg_native.h +++ b/lib/mgmt_msg_native.h @@ -23,30 +23,112 @@ extern "C" { #include -DECLARE_MTYPE(MSG_NATIVE_MSG); -DECLARE_MTYPE(MSG_NATIVE_ERROR); - /* - * Adding New Messages Types - * ------------------------- + * ================== + * Native Message API + * ================== + * + * ----------------------- + * Defining A New Message: + * ----------------------- + * + * 1) Start with struct mgmt_msg_header as the first (unnamed) field. + * 2) Add fixed-width fields on natural aligned boundaries. + * 3) Add an optional variable field aligned on a 64-bit boundary, this is + * done so that: `value = (HDR + 1)` to works. + * + * These rules are so the messages may be read from and written directly to + * "the wire", easily, using common programming languages (e.g., C, rust, go, + * python, ...) + * + * Fixed And Known Variable Length Messages: + * ----------------------------------------- + * + * For fixed-length and variable length message where the length is known + * up-front one should allocate new messages with the XCALLOC() and XFREE() + * functions with an MTYPE dedicated for that message type. The new MTYPE type + * should be declared below. + * + * Unknown Variable Length Messages: + * --------------------------------- + * + * If using a variable length field and the length is not known at message + * creation time, you can use the `appendable` API functions to allocate and + * manipulate the message. + * + * Notable Appendable API Functions: + * --------------------------------- + * + * mgmt_msg_appendable_alloc_msg() - Allocate an appendable msg. + * mgmt_msg_appendable_free_msg() - Free an appendable native msg. + * mgmt_msg_appendable_append() - Append data to the end of the msg. + * mgmt_msg_appendable_get_msg_len() - Get the total length of the msg. + * + * + * ------------------------------------- + * [Advanced Use] Dynamic Array Messages + * ------------------------------------- + * + * NOTE: Most users can simply use mgmt_msg_appendable_append() and skip this + * section. + * + * This section is only important to understand if you wish to utilize the fact + * that native messages allocated with mgmt_msg_appendable_alloc_msg are + * actually allocated as uint8_t dynamic arrays (`darr`). * - * Native messages structs have 2 simple rules: + * You can utilize all the darr_xxxx() API to manipulate the variable length + * message data in a native message. To do so you simply need to understand that + * the native message is actually a `uint8_t *` darr. So, for example, to append + * data to the end of a message one could do the following: * - * 1) All fields should be naturally aligned. - * 2) Any required padding should be explicitly reserved. + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * msg = (struct mggm_msg_my_msg *) + * darr_strcat((uint8_t *)msg, "/metric"); * - * This is so for all intents and purposes the messages may be read and written - * direct from "the wire", easily, using common programming languages (e.g., - * C, rust, go, python, ...) + * // ... + * } * - * Additionally by design fixed fields precede the variable length data which - * comes at the end. The zero length arrays fields are aligned such that this is so: + * NOTE: If reallocs happen the original passed in pointer will be updated; + * however, any other pointers into the message will become invalid, and so they + * should always be discarded or reinitialized after using any reallocating + * darr_xxx() API functions. * - * sizeof(struct mgmt_msg_foo) == offsetof(struct mgmt_msg_foo, field) + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * char *xpath = msg->xpath; // pointer into message + * + * darr_in_strcat((uint8_t *)msg, "/metric"); + * // msg may have been updated to point at new memory + * + * xpath = NULL; // now invalid + * xpath = msg->xpath; // reinitialize + * // ... + * } + * + * Rather than worry about this, it's typical when using dynamic arrays to always + * work from the main pointer to the dynamic array, rather than caching multiple + * pointers into the data. Modern compilers will optimize the code so that it + * adds no extra execution cost. + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * darr_in_strcat((uint8_t *)msg, "/metric"); + * + * // Use `msg->xpath` directly rather creating and using an + * // `xpath = msg->xpath` local variable. + * + * if (strcmp(msg->xpath, "foobar/metric")) { + * // ... + * } + * } * - * This allows things like `value = (HDR + 1)` to work. */ +DECLARE_MTYPE(MSG_NATIVE_MSG); +DECLARE_MTYPE(MSG_NATIVE_ERROR); +DECLARE_MTYPE(MSG_NATIVE_GET_TREE); + /* * Native message codes */ @@ -78,6 +160,7 @@ _Static_assert(sizeof(struct mgmt_msg_header) == /** * struct mgmt_msg_error - Common error message. + * * @error: An error value. * @errst: Description of error can be 0 length. * @@ -97,6 +180,7 @@ _Static_assert(sizeof(struct mgmt_msg_error) == /** * struct mgmt_msg_get_tree - Message carrying xpath query request. + * * @result_type: ``LYD_FORMAT`` for the returned result. * @xpath: the query for the data to return. */ @@ -113,6 +197,7 @@ _Static_assert(sizeof(struct mgmt_msg_get_tree) == /** * struct mgmt_msg_tree_data - Message carrying tree data. + * * @partial_error: If the full result could not be returned do to this error. * @result_type: ``LYD_FORMAT`` for format of the @result value. * @more: if this is a partial return and there will be more coming. @@ -162,6 +247,103 @@ extern int vmgmt_msg_native_send_error(struct msg_conn *conn, const char *errfmt, va_list ap) PRINTFRR(6, 0); +/** + * mgmt_msg_appendable_alloc_msg() - Create an appendable msg. + * @msg_type: The message structure type. + * + * This function takes a C type (e.g., `struct mgmt_msg_get_tree`) as an + * argument and returns a new appendable message. The newly allocated message + * should be used with the other `appendable` functions. + * + * Importantly the mgmt_msg_appendable_append() function can be used to add data + * to the end of the message, and mgmt_msg_get_appendable_msg_len() can be used + * to obtain the total length of the message (i.e., the fixed sized header plus + * the variable length data that has been appended). + * + * Additionally, a dynamic array (darr) pointer can be obtained using + * mgmt_msg_get_appendable_darr() which allows adding and manipulating the + * variable data that follows the fixed sized header. + * + * Return: A `msg_type` object created using a dynamic_array. + */ +#define mgmt_msg_appendable_alloc_msg(msg_type) \ + ({ \ + uint8_t *buf = NULL; \ + (typeof(msg_type) *)darr_append_nz(buf, sizeof(msg_type)); \ + }) + +/** + * mgmt_msg_free_appendable_msg() - Free an appendable native msg. + * @msg - pointer to message allocated by mgmt_msg_create_appendable_msg(). + */ +#define mgmt_msg_appendable_free_msg(msg) darr_free(msg) + +/** + * mgmt_msg_appendable_get_msg_len() - Get the total length of the msg. + * @msg: the appendable message. + * + * Return: the total length of the message, fixed + variable length. + */ +#define mgmt_msg_appendable_get_msg_len(msg) (darr_len((uint8_t *)(msg))) + +/** + * mgmt_msg_appendable_append() - Append data to the end of the msg. + * @msg: (IN/OUT) Pointer to the appendable message, variable may be updated. + * @data: data to append. + * @len: length of data to append. + * + * Append @data of length @len to the appendable message @msg. + * + * NOTE: Be aware @msg pointer may change as a result of reallocating the + * message to fit the new data. Any other pointers into the old message should + * be discarded. + * + * Return: a pointer to the newly appended data. + */ +#define mgmt_msg_appendable_append(msg, data, len) \ + memcpy(darr_append(mgmt_msg_get_appendable_darr(msg), len), data, len) + + + +/** + * mgmt_msg_appendable_get_darrp() - Return a ptr to the dynamic array ptr. + * @msg: Pointer to the appendable message. + * + * NOTE: Most users can simply use mgmt_msg_appendable_append() instead of this. + * + * This function obtains a pointer to the dynamic byte array for this message, + * this array actually includes the message header if one is going to look at + * the length value. With that in mind any of the `darr_*()` functions/API may + * be used to manipulate the variable data at the end of the message. + * + * NOTE: The pointer returned is actually a pointer to the message pointer + * passed in to this function. This pointer to pointer is required so that + * realloc can be done inside the darr API. + * + * NOTE: If reallocs happen the original passed in pointer will be updated; + * however, any other pointers into the message will become invalid and so they + * should always be discarded after using the returned value. + * + * Example: + * + * void append_metric_path(struct mgmt_msg_my_msg *msg) + * { + * char *xpath = msg->xpath; // pointer into message + * uint8_t **darp; + * + * darrp = mgmt_msg_appendable_get_darrp(msg); + * darr_in_strcat(*darrp, "/metric"); + * + * xpath = NULL; // now invalid + * xpath = msg->xpath; + * } + * + * + * Return: A pointer to the first argument -- which is a pointer to a pointer to + * a dynamic array. + */ +#define mgmt_msg_appendable_get_darrp(msg) ((uint8_t **)&(msg)) + #ifdef __cplusplus } #endif