From 8530d6c34d20eaddb3dcec78792cafccf1468f55 Mon Sep 17 00:00:00 2001 From: Mattias Nissler <122288598+mnissler-rivos@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:53:22 +0200 Subject: Construct server capabilities using json-c (#771) String formatting is hitting its limits: Adding another field is difficult given that we already branch on whether migration is enabled. This change constructs a JSON-C object instead so we can add what we need and serialize to a string afterwards. Signed-off-by: Mattias Nissler Reviewed-by: John Levon Reviewed-by: Thanos Makatos --- lib/tran.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 25 deletions(-) diff --git a/lib/tran.c b/lib/tran.c index dff47ef..3c8b25a 100644 --- a/lib/tran.c +++ b/lib/tran.c @@ -30,6 +30,7 @@ * */ +#include #include #include #include @@ -265,36 +266,114 @@ out: return 0; } +/* + * A json_object_object_add wrapper that takes ownership of *val + * unconditionally: Resets *val to NULL and makes sure *val gets dropped, even + * when an error occurs. Assumes key is new and a constant. + */ +static int +json_add(struct json_object *jso, const char *key, struct json_object **val) +{ + int ret = 0; + +#if JSON_C_MAJOR_VERSION > 0 || JSON_C_MINOR_VERSION >= 13 + ret = json_object_object_add_ex(jso, key, *val, + JSON_C_OBJECT_ADD_KEY_IS_NEW | + JSON_C_OBJECT_KEY_IS_CONSTANT); +#else + /* Earlier versions will abort() on allocation failure. */ + json_object_object_add(jso, key, *val); +#endif + + if (ret < 0) { + json_object_put(*val); + } + *val = NULL; + return ret; +} + +static int +json_add_uint64(struct json_object *jso, const char *key, uint64_t value) +{ + struct json_object *jo_tmp = NULL; + + /* + * Note that newer versions of the library have a json_object_new_uint64 + * function, but the int64 one is available also in older versions that we + * support, and our values don't require the full range anyways. + */ + assert(value <= INT64_MAX); + jo_tmp = json_object_new_int64(value); + return json_add(jso, key, &jo_tmp); +} + +/* + * Constructs the server's capabilities JSON string. The returned pointer must + * be freed by the caller. + */ +static char * +format_server_capabilities(vfu_ctx_t *vfu_ctx) +{ + struct json_object *jo_migration = NULL; + struct json_object *jo_caps = NULL; + struct json_object *jo_top = NULL; + char *caps_str = NULL; + + if ((jo_caps = json_object_new_object()) == NULL) { + goto out; + } + + if (json_add_uint64(jo_caps, "max_msg_fds", SERVER_MAX_FDS) < 0) { + goto out; + } + + if (json_add_uint64(jo_caps, "max_data_xfer_size", + SERVER_MAX_DATA_XFER_SIZE) < 0) { + goto out; + } + + if (vfu_ctx->migration != NULL) { + if ((jo_migration = json_object_new_object()) == NULL) { + goto out; + } + + size_t pgsize = migration_get_pgsize(vfu_ctx->migration); + if (json_add_uint64(jo_migration, "pgsize", pgsize) < 0) { + goto out; + } + + if (json_add(jo_caps, "migration", &jo_migration) < 0) { + goto out; + } + } + + if ((jo_top = json_object_new_object()) == NULL || + json_add(jo_top, "capabilities", &jo_caps) < 0) { + goto out; + } + + caps_str = strdup(json_object_to_json_string(jo_top)); + +out: + json_object_put(jo_migration); + json_object_put(jo_caps); + json_object_put(jo_top); + return caps_str; +} + static int send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id, struct vfio_user_version *cversion) { struct vfio_user_version sversion = { 0 }; struct iovec iovecs[2] = { { 0 } }; - char server_caps[1024]; vfu_msg_t msg = { { 0 } }; - int slen; - - if (vfu_ctx->migration == NULL) { - slen = snprintf(server_caps, sizeof(server_caps), - "{" - "\"capabilities\":{" - "\"max_msg_fds\":%u," - "\"max_data_xfer_size\":%u" - "}" - "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE); - } else { - slen = snprintf(server_caps, sizeof(server_caps), - "{" - "\"capabilities\":{" - "\"max_msg_fds\":%u," - "\"max_data_xfer_size\":%u," - "\"migration\":{" - "\"pgsize\":%zu" - "}" - "}" - "}", SERVER_MAX_FDS, SERVER_MAX_DATA_XFER_SIZE, - migration_get_pgsize(vfu_ctx->migration)); + char *server_caps = NULL; + int ret; + + if ((server_caps = format_server_capabilities(vfu_ctx)) == NULL) { + errno = ENOMEM; + return -1; } // FIXME: we should save the client minor here, and check that before trying @@ -306,14 +385,16 @@ send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id, iovecs[0].iov_len = sizeof(sversion); iovecs[1].iov_base = server_caps; /* Include the NUL. */ - iovecs[1].iov_len = slen + 1; + iovecs[1].iov_len = strlen(server_caps) + 1; msg.hdr.cmd = VFIO_USER_VERSION; msg.hdr.msg_id = msg_id; msg.out_iovecs = iovecs; msg.nr_out_iovecs = 2; - return vfu_ctx->tran->reply(vfu_ctx, &msg, 0); + ret = vfu_ctx->tran->reply(vfu_ctx, &msg, 0); + free(server_caps); + return ret; } int -- cgit v1.1