diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index 0c1dea566a..d49a5d97f9 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "devnum-util.h" #include "glyph-util.h" #include "in-addr-util.h" #include "iovec-util.h" @@ -294,3 +295,46 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis return 0; } + +int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum) { + if (devnum == 0) + return sd_json_variant_new_null(ret); + + return sd_json_buildo( + ret, + SD_JSON_BUILD_PAIR_UNSIGNED("major", major(devnum)), + SD_JSON_BUILD_PAIR_UNSIGNED("minor", minor(devnum))); +} + +int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { + dev_t *ret = ASSERT_PTR(userdata); + int r; + + assert(variant); + + if (sd_json_variant_is_null(variant)) { + *ret = 0; + return 0; + } + + struct { + uint32_t major; + uint32_t minor; + } data; + + static const sd_json_dispatch_field dispatch_table[] = { + { "major", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, major), SD_JSON_MANDATORY }, + { "minor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, minor), SD_JSON_MANDATORY }, + {} + }; + + r = sd_json_dispatch(variant, dispatch_table, flags, &data); + if (r < 0) + return r; + + if (!DEVICE_MAJOR_VALID(data.major) || !DEVICE_MINOR_VALID(data.minor)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid device number.", strna(name)); + + *ret = makedev(data.major, data.minor); + return 0; +} diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h index 6b72d0d93d..1bc5f63aba 100644 --- a/src/libsystemd/sd-json/json-util.h +++ b/src/libsystemd/sd-json/json-util.h @@ -114,6 +114,7 @@ int json_dispatch_const_user_group_name(const char *name, sd_json_variant *varia int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); +int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) { return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL); @@ -147,6 +148,7 @@ enum { _JSON_BUILD_RATELIMIT, _JSON_BUILD_TRISTATE, _JSON_BUILD_PIDREF, + _JSON_BUILD_DEVNUM, _JSON_BUILD_PAIR_INTEGER_NON_ZERO, _JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE, @@ -172,6 +174,7 @@ enum { _JSON_BUILD_PAIR_OCTESCAPE_NON_EMPTY, _JSON_BUILD_PAIR_TRISTATE_NON_NULL, _JSON_BUILD_PAIR_PIDREF_NON_NULL, + _JSON_BUILD_PAIR_DEVNUM, _SD_JSON_BUILD_REALLYMAX, }; @@ -192,6 +195,7 @@ enum { #define JSON_BUILD_RATELIMIT(rl) _JSON_BUILD_RATELIMIT, (const RateLimit*) { rl } #define JSON_BUILD_TRISTATE(i) _JSON_BUILD_TRISTATE, (int) { i } #define JSON_BUILD_PIDREF(p) _JSON_BUILD_PIDREF, (const PidRef*) { p } +#define JSON_BUILD_DEVNUM(d) _JSON_BUILD_DEVNUM, (dev_t) { d } #define JSON_BUILD_PAIR_INTEGER_NON_ZERO(name, i) _JSON_BUILD_PAIR_INTEGER_NON_ZERO, (const char*) { name }, (int64_t) { i } #define JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE(name, i) _JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE, (const char*) { name }, (int64_t) { i } @@ -230,5 +234,7 @@ enum { #define JSON_BUILD_PAIR_RATELIMIT(name, rl) SD_JSON_BUILD_PAIR(name, JSON_BUILD_RATELIMIT(rl)) #define JSON_BUILD_PAIR_TRISTATE(name, i) SD_JSON_BUILD_PAIR(name, JSON_BUILD_TRISTATE(i)) #define JSON_BUILD_PAIR_PIDREF(name, p) SD_JSON_BUILD_PAIR(name, JSON_BUILD_PIDREF(p)) +#define JSON_BUILD_PAIR_DEVNUM(name, d) SD_JSON_BUILD_PAIR(name, JSON_BUILD_DEVNUM(d)) int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref); +int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum); diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c index 2028f222cb..297052bdb4 100644 --- a/src/libsystemd/sd-json/sd-json.c +++ b/src/libsystemd/sd-json/sd-json.c @@ -4224,6 +4224,34 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) { break; } + case _JSON_BUILD_DEVNUM: { + dev_t devnum; + + if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { + r = -EINVAL; + goto finish; + } + + devnum = va_arg(ap, dev_t); + + if (current->n_suppress == 0) { + r = json_variant_new_devnum(&add, devnum); + if (r < 0) + goto finish; + } + + n_subtract = 1; + + if (current->expect == EXPECT_TOPLEVEL) + current->expect = EXPECT_END; + else if (current->expect == EXPECT_OBJECT_VALUE) + current->expect = EXPECT_OBJECT_KEY; + else + assert(current->expect == EXPECT_ARRAY_ELEMENT); + + break; + } + case _JSON_BUILD_TRISTATE: { int tristate; diff --git a/src/test/test-json.c b/src/test/test-json.c index 32cd285841..fdf69ac2f7 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1307,4 +1307,27 @@ TEST(pidref) { pidref_done(&data.pid1); } +TEST(devnum) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + dev_t dev = makedev(123, 456), parsed; + + ASSERT_OK(json_variant_new_devnum(&v, dev)); + ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL)); + ASSERT_OK(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed)); + ASSERT_EQ(major(parsed), major(dev)); + ASSERT_EQ(minor(parsed), minor(dev)); + v = sd_json_variant_unref(v); + + dev = makedev(1 << 12, 456); + ASSERT_OK(json_variant_new_devnum(&v, dev)); + ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL)); + ASSERT_FAIL(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed)); + v = sd_json_variant_unref(v); + + dev = makedev(123, 1 << 20); + ASSERT_OK(json_variant_new_devnum(&v, dev)); + ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL)); + ASSERT_FAIL(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed)); +} + DEFINE_TEST_MAIN(LOG_DEBUG);