diff options
-rw-r--r-- | src/libsystemd/sd-json/sd-json.c | 20 | ||||
-rw-r--r-- | src/systemd/sd-json.h | 6 | ||||
-rw-r--r-- | src/test/test-json.c | 80 |
3 files changed, 103 insertions, 3 deletions
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c index 1316498620..f7b36af880 100644 --- a/src/libsystemd/sd-json/sd-json.c +++ b/src/libsystemd/sd-json/sd-json.c @@ -4656,8 +4656,10 @@ _public_ int sd_json_dispatch_full( merged_flags = flags | p->flags; + /* If an explicit type is specified, verify it matches */ if (p->type != _SD_JSON_VARIANT_TYPE_INVALID && - !sd_json_variant_has_type(value, p->type)) { + !sd_json_variant_has_type(value, p->type) && + !(FLAGS_SET(merged_flags, SD_JSON_NULLABLE) && sd_json_variant_is_null(value))) { json_log(value, merged_flags, 0, "Object field '%s' has wrong type %s, expected %s.", sd_json_variant_string(key), @@ -4672,6 +4674,22 @@ _public_ int sd_json_dispatch_full( return -EINVAL; } + /* If the SD_JSON_REFUSE_NULL flag is specified, insist the field is not "null". Note + * that this provides overlapping functionality with the type check above. */ + if (FLAGS_SET(merged_flags, SD_JSON_REFUSE_NULL) && sd_json_variant_is_null(value)) { + + json_log(value, merged_flags, 0, + "Object field '%s' may not be null.", sd_json_variant_string(key)); + + if (merged_flags & SD_JSON_PERMISSIVE) + continue; + + if (reterr_bad_field) + *reterr_bad_field = p->name; + + return -EINVAL; + } + if (found[p-table]) { json_log(value, merged_flags, 0, "Duplicate object field '%s'.", sd_json_variant_string(key)); diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h index 434a6ffff3..d86f16106e 100644 --- a/src/systemd/sd-json.h +++ b/src/systemd/sd-json.h @@ -280,10 +280,12 @@ typedef enum sd_json_dispatch_flags_t { SD_JSON_STRICT = 1 << 3, /* Use slightly stricter validation than usually (means different things for different dispatchers, for example: don't accept "unsafe" strings in json_dispatch_string() + json_dispatch_string()) */ SD_JSON_RELAX = 1 << 4, /* Use slightly more relaxed validation than usually (similar, for example: relaxed user name checking in json_dispatch_user_group_name()) */ SD_JSON_ALLOW_EXTENSIONS = 1 << 5, /* Subset of JSON_PERMISSIVE: allow additional fields, but no other permissive handling */ + SD_JSON_NULLABLE = 1 << 6, /* Allow both specified type and null for this field */ + SD_JSON_REFUSE_NULL = 1 << 7, /* Never allow null, even if type is otherwise not specified */ /* The following two may be passed into log_json() in addition to those above */ - SD_JSON_DEBUG = 1 << 6, /* Indicates that this log message is a debug message */ - SD_JSON_WARNING = 1 << 7 /* Indicates that this log message is a warning message */ + SD_JSON_DEBUG = 1 << 8, /* Indicates that this log message is a debug message */ + SD_JSON_WARNING = 1 << 9 /* Indicates that this log message is a warning message */ } sd_json_dispatch_flags_t; typedef int (*sd_json_dispatch_callback_t)(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); diff --git a/src/test/test-json.c b/src/test/test-json.c index 4323bd5c97..a3e7fe29af 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1109,6 +1109,86 @@ TEST(json_iovec) { assert_se(iovec_memcmp(&iov1, &b) > 0); } +TEST(json_dispatch_nullable) { + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; + + assert_se(sd_json_build(&j, SD_JSON_BUILD_OBJECT( + SD_JSON_BUILD_PAIR("x1", JSON_BUILD_CONST_STRING("foo")), + SD_JSON_BUILD_PAIR("x2", JSON_BUILD_CONST_STRING("bar")), + SD_JSON_BUILD_PAIR("x3", JSON_BUILD_CONST_STRING("waldo")), + SD_JSON_BUILD_PAIR("x4", JSON_BUILD_CONST_STRING("foo2")), + SD_JSON_BUILD_PAIR("x5", JSON_BUILD_CONST_STRING("bar2")), + SD_JSON_BUILD_PAIR("x6", JSON_BUILD_CONST_STRING("waldo2")), + SD_JSON_BUILD_PAIR("x7", SD_JSON_BUILD_NULL), + SD_JSON_BUILD_PAIR("x8", SD_JSON_BUILD_NULL), + SD_JSON_BUILD_PAIR("x9", SD_JSON_BUILD_NULL))) >= 0); + + struct data { + const char *x1, *x2, *x3, *x4, *x5, *x6, *x7, *x8, *x9; + } data = { + .x1 = POINTER_MAX, + .x2 = POINTER_MAX, + .x3 = POINTER_MAX, + .x4 = POINTER_MAX, + .x5 = POINTER_MAX, + .x6 = POINTER_MAX, + .x7 = POINTER_MAX, + .x8 = POINTER_MAX, + .x9 = POINTER_MAX, + }; + + assert_se(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "x1", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x1), SD_JSON_NULLABLE }, + { "x2", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x2), SD_JSON_REFUSE_NULL }, + { "x3", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x3), 0 }, + { "x4", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x4), SD_JSON_NULLABLE }, + { "x5", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x5), SD_JSON_REFUSE_NULL }, + { "x6", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x6), 0 }, + { "x7", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x7), SD_JSON_NULLABLE }, + { "x8", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x8), 0 }, + { "x9", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x9), SD_JSON_NULLABLE }, + {}, + }, + /* flags= */ 0, + &data) >= 0); + + assert_se(streq_ptr(data.x1, "foo")); + assert_se(streq_ptr(data.x2, "bar")); + assert_se(streq_ptr(data.x3, "waldo")); + assert_se(streq_ptr(data.x4, "foo2")); + assert_se(streq_ptr(data.x5, "bar2")); + assert_se(streq_ptr(data.x6, "waldo2")); + assert_se(!data.x7); + assert_se(!data.x8); + assert_se(!data.x9); + + assert_se(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "x7", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_const_string, offsetof(struct data, x7), SD_JSON_REFUSE_NULL }, + {}, + }, + /* flags= */ SD_JSON_ALLOW_EXTENSIONS, + &data) == -EINVAL); + + assert_se(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "x7", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x7), SD_JSON_REFUSE_NULL }, + {}, + }, + /* flags= */ SD_JSON_ALLOW_EXTENSIONS, + &data) == -EINVAL); + + assert_se(sd_json_dispatch(j, + (const sd_json_dispatch_field[]) { + { "x7", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct data, x7), 0 }, + {}, + }, + /* flags= */ SD_JSON_ALLOW_EXTENSIONS, + &data) == -EINVAL); +} + TEST(parse_continue) { unsigned line = 23, column = 43; |