Write an alternative implementation of our virJSON functions,
using json-c instead of yajl.
Signed-off-by: Ján Tomko <jtomko@redhat.com>
---
src/util/virjson.c | 167 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 165 insertions(+), 2 deletions(-)
diff --git a/src/util/virjson.c b/src/util/virjson.c
index 0edf86cd1c..8e0ba47fc9 100644
--- a/src/util/virjson.c
+++ b/src/util/virjson.c
@@ -32,7 +32,9 @@
#include "virenum.h"
#include "virbitmap.h"
-#if WITH_YAJL
+#if WITH_JSON_C
+# include <json.h>
+#elif WITH_YAJL
# include <yajl/yajl_gen.h>
# include <yajl/yajl_parse.h>
@@ -1390,7 +1392,168 @@ virJSONValueCopy(const virJSONValue *in)
}
-#if WITH_YAJL
+#if WITH_JSON_C
+static virJSONValue *
+virJSONValueFromJsonC(json_object *jobj)
+{
+ enum json_type type = json_object_get_type(jobj);
+ virJSONValue *ret = NULL;
+
+ switch (type) {
+ case json_type_null:
+ ret = virJSONValueNewNull();
+ break;
+ case json_type_boolean:
+ ret = virJSONValueNewBoolean(json_object_get_boolean(jobj));
+ break;
+ case json_type_double:
+ case json_type_int: {
+ ret = virJSONValueNewNumber(g_strdup(json_object_get_string(jobj)));
+ break;
+ }
+ case json_type_object:
+ ret = virJSONValueNewObject();
+ {
+ json_object_object_foreach(jobj, key, val) {
+ virJSONValue *cur = virJSONValueFromJsonC(val);
+
+ if (virJSONValueObjectAppend(ret, key, &cur) < 0) {
+ g_free(ret);
+ return NULL;
+ }
+ }
+ }
+ break;
+ case json_type_array: {
+ size_t len;
+ size_t i;
+
+ ret = virJSONValueNewArray();
+ len = json_object_array_length(jobj);
+
+ for (i = 0; i < len; i++) {
+ virJSONValue *cur = NULL;
+ json_object *val = NULL;
+
+ val = json_object_array_get_idx(jobj, i);
+
+ cur = virJSONValueFromJsonC(val);
+
+ virJSONValueArrayAppend(ret, &cur);
+ }
+ break;
+ }
+ case json_type_string:
+ ret = virJSONValueNewString(g_strdup(json_object_get_string(jobj)));
+ break;
+ }
+
+ return ret;
+}
+
+
+virJSONValue *
+virJSONValueFromString(const char *jsonstring)
+{
+ json_object *jobj = NULL;
+ virJSONValue *ret = NULL;
+ json_tokener *tok = NULL;
+ enum json_tokener_error jerr;
+ int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8;
+
+ VIR_DEBUG("string=%s", jsonstring);
+
+ tok = json_tokener_new();
+ json_tokener_set_flags(tok, jsonflags);
+ jobj = json_tokener_parse_ex(tok, jsonstring, strlen(jsonstring));
+ jerr = json_tokener_get_error(tok);
+ if (jerr != json_tokener_success) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", json_tokener_error_desc(jerr));
+ goto cleanup;
+ }
+ ret = virJSONValueFromJsonC(jobj);
+
+ cleanup:
+ json_object_put(jobj);
+ json_tokener_free(tok);
+ return ret;
+}
+
+static json_object *
+virJSONValueToJsonC(virJSONValue *object)
+{
+ json_object *ret = NULL;
+ size_t i;
+
+ VIR_DEBUG("object=%p type=%d", object, object->type);
+
+ switch (object->type) {
+ case VIR_JSON_TYPE_OBJECT:
+ ret = json_object_new_object();
+ for (i = 0; i < object->data.object.npairs; i++) {
+ json_object *child = virJSONValueToJsonC(object->data.object.pairs[i].value);
+ json_object_object_add(ret, object->data.object.pairs[i].key, child);
+ }
+ return ret;
+ case VIR_JSON_TYPE_ARRAY:
+ /* json_object_new_array_ext was introduced in json-c 0.16 */
+# if JSON_C_VERSION_NUM < ((0 << 16) | (16 << 8))
+ ret = json_object_new_array();
+# else
+ ret = json_object_new_array_ext(object->data.array.nvalues);
+# endif
+ for (i = 0; i < object->data.array.nvalues; i++) {
+ json_object_array_add(ret,
+ virJSONValueToJsonC(object->data.array.values[i]));
+ }
+ return ret;
+
+ case VIR_JSON_TYPE_STRING:
+ return json_object_new_string(object->data.string);
+
+ case VIR_JSON_TYPE_NUMBER: {
+ /* Yes. That's a random value. We only care about the string. */
+ return json_object_new_double_s(299792458, object->data.number);
+ }
+ case VIR_JSON_TYPE_BOOLEAN:
+ return json_object_new_boolean(object->data.boolean);
+
+ case VIR_JSON_TYPE_NULL:
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+
+int
+virJSONValueToBuffer(virJSONValue *object,
+ virBuffer *buf,
+ bool pretty)
+{
+ json_object *jobj = NULL;
+ const char *str;
+ size_t len;
+ int jsonflags = JSON_C_TO_STRING_NOSLASHESCAPE;
+
+ if (pretty)
+ jsonflags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
+
+ jobj = virJSONValueToJsonC(object);
+
+ str = json_object_to_json_string_length(jobj, jsonflags, &len);
+
+ virBufferAdd(buf, str, len);
+ if (pretty)
+ virBufferAddLit(buf, "\n");
+
+ json_object_put(jobj);
+ return 0;
+}
+
+
+#elif WITH_YAJL
static int
virJSONParserInsertValue(virJSONParser *parser,
virJSONValue **value)
--
2.46.0
On Thu, Sep 05, 2024 at 15:49:36 +0200, Ján Tomko wrote:
> Write an alternative implementation of our virJSON functions,
> using json-c instead of yajl.
>
> Signed-off-by: Ján Tomko <jtomko@redhat.com>
> ---
> src/util/virjson.c | 167 ++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 165 insertions(+), 2 deletions(-)
>
> diff --git a/src/util/virjson.c b/src/util/virjson.c
> index 0edf86cd1c..8e0ba47fc9 100644
> --- a/src/util/virjson.c
> +++ b/src/util/virjson.c
> @@ -32,7 +32,9 @@
> #include "virenum.h"
> #include "virbitmap.h"
>
> -#if WITH_YAJL
> +#if WITH_JSON_C
> +# include <json.h>
> +#elif WITH_YAJL
> # include <yajl/yajl_gen.h>
> # include <yajl/yajl_parse.h>
>
> @@ -1390,7 +1392,168 @@ virJSONValueCopy(const virJSONValue *in)
> }
>
>
> -#if WITH_YAJL
> +#if WITH_JSON_C
> +static virJSONValue *
> +virJSONValueFromJsonC(json_object *jobj)
> +{
> + enum json_type type = json_object_get_type(jobj);
> + virJSONValue *ret = NULL;
> +
> + switch (type) {
> + case json_type_null:
> + ret = virJSONValueNewNull();
> + break;
> + case json_type_boolean:
> + ret = virJSONValueNewBoolean(json_object_get_boolean(jobj));
> + break;
> + case json_type_double:
> + case json_type_int: {
> + ret = virJSONValueNewNumber(g_strdup(json_object_get_string(jobj)));
> + break;
> + }
> + case json_type_object:
> + ret = virJSONValueNewObject();
> + {
Unaddressed from previous review. Either everything inside the block or
no block at all.
> + json_object_object_foreach(jobj, key, val) {
> + virJSONValue *cur = virJSONValueFromJsonC(val);
> +
> + if (virJSONValueObjectAppend(ret, key, &cur) < 0) {
> + g_free(ret);
> + return NULL;
> + }
> + }
> + }
> + break;
> + case json_type_array: {
> + size_t len;
> + size_t i;
> +
> + ret = virJSONValueNewArray();
> + len = json_object_array_length(jobj);
> +
> + for (i = 0; i < len; i++) {
> + virJSONValue *cur = NULL;
> + json_object *val = NULL;
> +
> + val = json_object_array_get_idx(jobj, i);
> +
> + cur = virJSONValueFromJsonC(val);
> +
> + virJSONValueArrayAppend(ret, &cur);
> + }
> + break;
> + }
> + case json_type_string:
> + ret = virJSONValueNewString(g_strdup(json_object_get_string(jobj)));
> + break;
> + }
> +
> + return ret;
> +}
> +
> +
> +virJSONValue *
> +virJSONValueFromString(const char *jsonstring)
> +{
> + json_object *jobj = NULL;
> + virJSONValue *ret = NULL;
> + json_tokener *tok = NULL;
> + enum json_tokener_error jerr;
> + int jsonflags = JSON_TOKENER_STRICT | JSON_TOKENER_VALIDATE_UTF8;
> +
> + VIR_DEBUG("string=%s", jsonstring);
> +
> + tok = json_tokener_new();
> + json_tokener_set_flags(tok, jsonflags);
> + jobj = json_tokener_parse_ex(tok, jsonstring, strlen(jsonstring));
> + jerr = json_tokener_get_error(tok);
> + if (jerr != json_tokener_success) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + "%s", json_tokener_error_desc(jerr));
> + goto cleanup;
> + }
> + ret = virJSONValueFromJsonC(jobj);
> +
> + cleanup:
> + json_object_put(jobj);
> + json_tokener_free(tok);
> + return ret;
> +}
> +
> +static json_object *
> +virJSONValueToJsonC(virJSONValue *object)
> +{
> + json_object *ret = NULL;
> + size_t i;
> +
> + VIR_DEBUG("object=%p type=%d", object, object->type);
> +
> + switch (object->type) {
> + case VIR_JSON_TYPE_OBJECT:
> + ret = json_object_new_object();
> + for (i = 0; i < object->data.object.npairs; i++) {
> + json_object *child = virJSONValueToJsonC(object->data.object.pairs[i].value);
> + json_object_object_add(ret, object->data.object.pairs[i].key, child);
> + }
> + return ret;
> + case VIR_JSON_TYPE_ARRAY:
> + /* json_object_new_array_ext was introduced in json-c 0.16 */
> +# if JSON_C_VERSION_NUM < ((0 << 16) | (16 << 8))
> + ret = json_object_new_array();
> +# else
> + ret = json_object_new_array_ext(object->data.array.nvalues);
> +# endif
> + for (i = 0; i < object->data.array.nvalues; i++) {
> + json_object_array_add(ret,
> + virJSONValueToJsonC(object->data.array.values[i]));
> + }
> + return ret;
> +
> + case VIR_JSON_TYPE_STRING:
> + return json_object_new_string(object->data.string);
> +
> + case VIR_JSON_TYPE_NUMBER: {
> + /* Yes. That's a random value. We only care about the string. */
Unaddressed from previous review:
/* Yes. That's a random value. json-c will use the provided
string representation in the JSON document it outputs. The
'json_object' tree will not be used outside of this function
and thus the actual numeric value is irrelevant. */
> + return json_object_new_double_s(299792458, object->data.number);
> + }
> + case VIR_JSON_TYPE_BOOLEAN:
> + return json_object_new_boolean(object->data.boolean);
> +
> + case VIR_JSON_TYPE_NULL:
return json_object_new_null();
Which should be available in 0.14 we require. But yes I'm aware that
it'd be correct without:
struct json_object *json_object_new_null(void)
{
return NULL;
}
In case you'd want to stick with 'return NULL' this will require a
comment and I'll like to see it before comitting.
> + default:
> + return NULL;
> + }
> + return NULL;
> +}
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
On a Friday in 2024, Peter Krempa wrote:
>On Thu, Sep 05, 2024 at 15:49:36 +0200, Ján Tomko wrote:
>> Write an alternative implementation of our virJSON functions,
>> using json-c instead of yajl.
>>
>> Signed-off-by: Ján Tomko <jtomko@redhat.com>
>> ---
>> src/util/virjson.c | 167 ++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 165 insertions(+), 2 deletions(-)
>>
>> diff --git a/src/util/virjson.c b/src/util/virjson.c
>> index 0edf86cd1c..8e0ba47fc9 100644
>> --- a/src/util/virjson.c
>> +++ b/src/util/virjson.c
>> @@ -32,7 +32,9 @@
>> #include "virenum.h"
>> #include "virbitmap.h"
>>
>> -#if WITH_YAJL
>> +#if WITH_JSON_C
>> +# include <json.h>
>> +#elif WITH_YAJL
>> # include <yajl/yajl_gen.h>
>> # include <yajl/yajl_parse.h>
>>
>> @@ -1390,7 +1392,168 @@ virJSONValueCopy(const virJSONValue *in)
>> }
>>
>>
>> -#if WITH_YAJL
>> +#if WITH_JSON_C
>> +static virJSONValue *
>> +virJSONValueFromJsonC(json_object *jobj)
>> +{
>> + enum json_type type = json_object_get_type(jobj);
>> + virJSONValue *ret = NULL;
>> +
>> + switch (type) {
>> + case json_type_null:
>> + ret = virJSONValueNewNull();
>> + break;
>> + case json_type_boolean:
>> + ret = virJSONValueNewBoolean(json_object_get_boolean(jobj));
>> + break;
>> + case json_type_double:
>> + case json_type_int: {
>> + ret = virJSONValueNewNumber(g_strdup(json_object_get_string(jobj)));
>> + break;
>> + }
>> + case json_type_object:
>> + ret = virJSONValueNewObject();
>> + {
>
>Unaddressed from previous review. Either everything inside the block or
>no block at all.
>
Previous review:
> This block-in-case style is very punk.
I think leaving it very-punkish addressed your comments just fine :P
>> + json_object_object_foreach(jobj, key, val) {
>> + virJSONValue *cur = virJSONValueFromJsonC(val);
>> +
>> + if (virJSONValueObjectAppend(ret, key, &cur) < 0) {
>> + g_free(ret);
>> + return NULL;
>> + }
>> + }
>> + }
>> + break;
>> + case json_type_array: {
[...]
>> + case VIR_JSON_TYPE_STRING:
>> + return json_object_new_string(object->data.string);
>> +
>> + case VIR_JSON_TYPE_NUMBER: {
>> + /* Yes. That's a random value. We only care about the string. */
>
>Unaddressed from previous review:
>
> /* Yes. That's a random value. json-c will use the provided
> string representation in the JSON document it outputs. The
> 'json_object' tree will not be used outside of this function
> and thus the actual numeric value is irrelevant. */
>
That doesn't sound right. The json_object will be used outside of this
function, but the formatter will use the string instead of the numeric
value.
>> + return json_object_new_double_s(299792458, object->data.number);
>> + }
>> + case VIR_JSON_TYPE_BOOLEAN:
>> + return json_object_new_boolean(object->data.boolean);
>> +
>> + case VIR_JSON_TYPE_NULL:
> return json_object_new_null();
>
>Which should be available in 0.14 we require. But yes I'm aware that
>it'd be correct without:
>
>struct json_object *json_object_new_null(void)
>{
> return NULL;
>}
>
>In case you'd want to stick with 'return NULL' this will require a
>comment and I'll like to see it before comitting.
>
I am confused here, do you prefer json_object_new_null() or returning
NULL directly? And why would either of those require a comment?
Jano
>
>> + default:
>> + return NULL;
>> + }
>> + return NULL;
>> +}
>
>Reviewed-by: Peter Krempa <pkrempa@redhat.com>
>
On Fri, Sep 06, 2024 at 11:46:16 +0200, Ján Tomko wrote:
> On a Friday in 2024, Peter Krempa wrote:
> > On Thu, Sep 05, 2024 at 15:49:36 +0200, Ján Tomko wrote:
> > > Write an alternative implementation of our virJSON functions,
> > > using json-c instead of yajl.
> > >
> > > Signed-off-by: Ján Tomko <jtomko@redhat.com>
> > > ---
> > > src/util/virjson.c | 167 ++++++++++++++++++++++++++++++++++++++++++++-
> > > 1 file changed, 165 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/src/util/virjson.c b/src/util/virjson.c
> > > index 0edf86cd1c..8e0ba47fc9 100644
> > > --- a/src/util/virjson.c
> > > +++ b/src/util/virjson.c
> > > @@ -32,7 +32,9 @@
> > > #include "virenum.h"
> > > #include "virbitmap.h"
> > >
> > > -#if WITH_YAJL
> > > +#if WITH_JSON_C
> > > +# include <json.h>
> > > +#elif WITH_YAJL
> > > # include <yajl/yajl_gen.h>
> > > # include <yajl/yajl_parse.h>
> > >
> > > @@ -1390,7 +1392,168 @@ virJSONValueCopy(const virJSONValue *in)
> > > }
> > >
> > >
> > > -#if WITH_YAJL
> > > +#if WITH_JSON_C
> > > +static virJSONValue *
> > > +virJSONValueFromJsonC(json_object *jobj)
> > > +{
> > > + enum json_type type = json_object_get_type(jobj);
> > > + virJSONValue *ret = NULL;
> > > +
> > > + switch (type) {
> > > + case json_type_null:
> > > + ret = virJSONValueNewNull();
> > > + break;
> > > + case json_type_boolean:
> > > + ret = virJSONValueNewBoolean(json_object_get_boolean(jobj));
> > > + break;
> > > + case json_type_double:
> > > + case json_type_int: {
> > > + ret = virJSONValueNewNumber(g_strdup(json_object_get_string(jobj)));
> > > + break;
> > > + }
> > > + case json_type_object:
> > > + ret = virJSONValueNewObject();
> > > + {
> >
> > Unaddressed from previous review. Either everything inside the block or
> > no block at all.
> >
>
> Previous review:
>
> > This block-in-case style is very punk.
>
> I think leaving it very-punkish addressed your comments just fine :P
Sigh. Okay I'll be more direct next time.
> > > + json_object_object_foreach(jobj, key, val) {
> > > + virJSONValue *cur = virJSONValueFromJsonC(val);
> > > +
> > > + if (virJSONValueObjectAppend(ret, key, &cur) < 0) {
> > > + g_free(ret);
> > > + return NULL;
> > > + }
> > > + }
> > > + }
> > > + break;
> > > + case json_type_array: {
>
> [...]
>
> > > + case VIR_JSON_TYPE_STRING:
> > > + return json_object_new_string(object->data.string);
> > > +
> > > + case VIR_JSON_TYPE_NUMBER: {
> > > + /* Yes. That's a random value. We only care about the string. */
> >
> > Unaddressed from previous review:
> >
>
> That doesn't sound right. The json_object will be used outside of this
> function, but the formatter will use the string instead of the numeric
> value.
You are technically correct. 'json_object' will be used outside, but
strictly to format the json. So:
/* Yes. That's a random value. json-c will use the provided
string representation in the JSON document it outputs. The
'json_object' tree created here is not used for anything
else besides the JSON string output, thus the actual numeric
value doesn't matter. */
> > > + return json_object_new_double_s(299792458, object->data.number);
> > > + }
> > > + case VIR_JSON_TYPE_BOOLEAN:
> > > + return json_object_new_boolean(object->data.boolean);
> > > +
> > > + case VIR_JSON_TYPE_NULL:
> > return json_object_new_null();
> >
> > Which should be available in 0.14 we require. But yes I'm aware that
> > it'd be correct without:
> >
> > struct json_object *json_object_new_null(void)
> > {
> > return NULL;
> > }
> >
> > In case you'd want to stick with 'return NULL' this will require a
> > comment and I'll like to see it before comitting.
> >
>
> I am confused here, do you prefer json_object_new_null() or returning
> NULL directly?
I prefer json_object_new_null()
> And why would either of those require a comment?
Returning NULL directly does require a comment. Espeically since it's
right next to the 'default:' case which would signify a programming
error which returns exactly the same value.
It looked wrong and made me look it up.
> Jano
>
> >
> > > + default:
> > > + return NULL;
> > > + }
> > > + return NULL;
> > > +}
> >
> > Reviewed-by: Peter Krempa <pkrempa@redhat.com>
> >
On a Friday in 2024, Peter Krempa wrote:
>On Fri, Sep 06, 2024 at 11:46:16 +0200, Ján Tomko wrote:
>> On a Friday in 2024, Peter Krempa wrote:
>> > On Thu, Sep 05, 2024 at 15:49:36 +0200, Ján Tomko wrote:
>> > > Write an alternative implementation of our virJSON functions,
>> > > using json-c instead of yajl.
>> > >
>> > > Signed-off-by: Ján Tomko <jtomko@redhat.com>
>> > > ---
>> > > src/util/virjson.c | 167 ++++++++++++++++++++++++++++++++++++++++++++-
>> > > 1 file changed, 165 insertions(+), 2 deletions(-)
>> > >
>> > > diff --git a/src/util/virjson.c b/src/util/virjson.c
>> > > index 0edf86cd1c..8e0ba47fc9 100644
>> > > --- a/src/util/virjson.c
>> > > +++ b/src/util/virjson.c
>> > > @@ -32,7 +32,9 @@
>> > > #include "virenum.h"
>> > > #include "virbitmap.h"
>> > >
>> > > -#if WITH_YAJL
>> > > +#if WITH_JSON_C
>> > > +# include <json.h>
>> > > +#elif WITH_YAJL
>> > > # include <yajl/yajl_gen.h>
>> > > # include <yajl/yajl_parse.h>
>> > >
>> > > @@ -1390,7 +1392,168 @@ virJSONValueCopy(const virJSONValue *in)
>> > > }
>> > >
>> > >
>> > > -#if WITH_YAJL
>> > > +#if WITH_JSON_C
>> > > +static virJSONValue *
>> > > +virJSONValueFromJsonC(json_object *jobj)
>> > > +{
>> > > + enum json_type type = json_object_get_type(jobj);
>> > > + virJSONValue *ret = NULL;
>> > > +
>> > > + switch (type) {
>> > > + case json_type_null:
>> > > + ret = virJSONValueNewNull();
>> > > + break;
>> > > + case json_type_boolean:
>> > > + ret = virJSONValueNewBoolean(json_object_get_boolean(jobj));
>> > > + break;
>> > > + case json_type_double:
>> > > + case json_type_int: {
>> > > + ret = virJSONValueNewNumber(g_strdup(json_object_get_string(jobj)));
>> > > + break;
>> > > + }
>> > > + case json_type_object:
>> > > + ret = virJSONValueNewObject();
>> > > + {
>> >
>> > Unaddressed from previous review. Either everything inside the block or
>> > no block at all.
>> >
>>
>> Previous review:
>>
>> > This block-in-case style is very punk.
>>
>> I think leaving it very-punkish addressed your comments just fine :P
>
>Sigh. Okay I'll be more direct next time.
>
I've changed it to use the less punk-ish C version:
case json_type_object: {
json_object_iter iter;
ret = virJSONValueNewObject();
json_object_object_foreachC(jobj, iter) {
virJSONValue *cur = virJSONValueFromJsonC(iter.val);
if (virJSONValueObjectAppend(ret, iter.key, &cur) < 0) {
g_free(ret);
return NULL;
}
}
break;
}
[..]
>> > > + return json_object_new_double_s(299792458, object->data.number);
>> > > + }
>> > > + case VIR_JSON_TYPE_BOOLEAN:
>> > > + return json_object_new_boolean(object->data.boolean);
>> > > +
>> > > + case VIR_JSON_TYPE_NULL:
>> > return json_object_new_null();
>> >
>> > Which should be available in 0.14 we require. But yes I'm aware that
>> > it'd be correct without:
>> >
>> > struct json_object *json_object_new_null(void)
>> > {
>> > return NULL;
>> > }
>> >
>> > In case you'd want to stick with 'return NULL' this will require a
>> > comment and I'll like to see it before comitting.
>> >
>>
>> I am confused here, do you prefer json_object_new_null() or returning
>> NULL directly?
>
>I prefer json_object_new_null()
>
Done.
Jano
>
>> And why would either of those require a comment?
>
>Returning NULL directly does require a comment. Espeically since it's
>right next to the 'default:' case which would signify a programming
>error which returns exactly the same value.
>
>It looked wrong and made me look it up.
>
>> Jano
>>
>> >
>> > > + default:
>> > > + return NULL;
>> > > + }
>> > > + return NULL;
>> > > +}
>> >
>> > Reviewed-by: Peter Krempa <pkrempa@redhat.com>
>> >
>
>
© 2016 - 2026 Red Hat, Inc.