summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libsystemd/sd-json/sd-json.c20
-rw-r--r--src/systemd/sd-json.h6
-rw-r--r--src/test/test-json.c80
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;