aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMattias Nissler <122288598+mnissler-rivos@users.noreply.github.com>2023-08-31 10:53:22 +0200
committerGitHub <noreply@github.com>2023-08-31 09:53:22 +0100
commit8530d6c34d20eaddb3dcec78792cafccf1468f55 (patch)
treeb0f196323aef3c5ee4c0cce97a7b5e0039dd2aec
parentf981913abd9a5926d38a48c6ba9b1ba7dacb1c11 (diff)
downloadlibvfio-user-8530d6c34d20eaddb3dcec78792cafccf1468f55.zip
libvfio-user-8530d6c34d20eaddb3dcec78792cafccf1468f55.tar.gz
libvfio-user-8530d6c34d20eaddb3dcec78792cafccf1468f55.tar.bz2
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 <mnissler@rivosinc.com> Reviewed-by: John Levon <john.levon@nutanix.com> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
-rw-r--r--lib/tran.c131
1 files 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 <assert.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdlib.h>
@@ -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