Merge pull request #34600 from poettering/varlink-idl-add-flags-everywhere

sd-varlink: maintain "more" flag support in the IDL structures
This commit is contained in:
Luca Boccassi
2024-10-03 21:02:22 +01:00
committed by GitHub
15 changed files with 116 additions and 51 deletions
+46 -24
View File
@@ -374,6 +374,20 @@ static int varlink_idl_format_symbol(
break;
case SD_VARLINK_METHOD:
/* Sooner or later we want to export this in a proper IDL language construct, see
* https://github.com/varlink/varlink.github.io/issues/26 – but for now export this as a
* comment. */
if ((symbol->symbol_flags & (SD_VARLINK_REQUIRES_MORE|SD_VARLINK_SUPPORTS_MORE)) != 0) {
fputs(colors[COLOR_COMMENT], f);
if (FLAGS_SET(symbol->symbol_flags, SD_VARLINK_REQUIRES_MORE))
fputs("# [Requires 'more' flag]", f);
else
fputs("# [Supports 'more' flag]", f);
fputs(colors[COLOR_RESET], f);
fputs("\n", f);
}
fputs(colors[COLOR_SYMBOL_TYPE], f);
fputs("method ", f);
fputs(colors[COLOR_IDENTIFIER], f);
@@ -1761,15 +1775,15 @@ static int varlink_idl_validate_field(const sd_varlink_field *field, sd_json_var
return 0;
}
static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_variant *v, sd_varlink_field_direction_t direction, const char **bad_field) {
static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_variant *v, sd_varlink_field_direction_t direction, const char **reterr_bad_field) {
int r;
assert(symbol);
assert(!IN_SET(symbol->symbol_type, _SD_VARLINK_SYMBOL_COMMENT, _SD_VARLINK_INTERFACE_COMMENT));
if (!v) {
if (bad_field)
*bad_field = NULL;
if (reterr_bad_field)
*reterr_bad_field = NULL;
return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Null object passed, refusing.");
}
@@ -1780,8 +1794,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
const char *s;
if (!sd_json_variant_is_string(v)) {
if (bad_field)
*bad_field = symbol->name;
if (reterr_bad_field)
*reterr_bad_field = symbol->name;
return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-string to enum field '%s', refusing.", strna(symbol->name));
}
@@ -1801,8 +1815,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
}
if (!found) {
if (bad_field)
*bad_field = s;
if (reterr_bad_field)
*reterr_bad_field = s;
return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed unrecognized string '%s' to enum field '%s', refusing.", s, strna(symbol->name));
}
@@ -1813,8 +1827,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
case SD_VARLINK_METHOD:
case SD_VARLINK_ERROR: {
if (!sd_json_variant_is_object(v)) {
if (bad_field)
*bad_field = symbol->name;
if (reterr_bad_field)
*reterr_bad_field = symbol->name;
return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Passed non-object to field '%s', refusing.", strna(symbol->name));
}
@@ -1828,8 +1842,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
r = varlink_idl_validate_field(field, sd_json_variant_by_key(v, field->name));
if (r < 0) {
if (bad_field)
*bad_field = field->name;
if (reterr_bad_field)
*reterr_bad_field = field->name;
return r;
}
}
@@ -1838,8 +1852,8 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
const char *name;
JSON_VARIANT_OBJECT_FOREACH(name, e, v) {
if (!varlink_idl_find_field(symbol, name)) {
if (bad_field)
*bad_field = name;
if (reterr_bad_field)
*reterr_bad_field = name;
return varlink_idl_log(SYNTHETIC_ERRNO(EBUSY), "Field '%s' not defined for object, refusing.", name);
}
}
@@ -1858,32 +1872,40 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
return 1; /* validated */
}
static int varlink_idl_validate_method(const sd_varlink_symbol *method, sd_json_variant *v, sd_varlink_field_direction_t direction, const char **bad_field) {
assert(IN_SET(direction, SD_VARLINK_INPUT, SD_VARLINK_OUTPUT));
int varlink_idl_validate_method_call(const sd_varlink_symbol *method, sd_json_variant *v, sd_varlink_method_flags_t flags, const char **reterr_bad_field) {
if (!method)
return 0; /* Can't validate */
if (method->symbol_type != SD_VARLINK_METHOD)
return -EBADMSG;
return varlink_idl_validate_symbol(method, v, direction, bad_field);
/* If method calls require the "more" flag, but none is given, return a recognizable error */
if (FLAGS_SET(method->symbol_flags, SD_VARLINK_REQUIRES_MORE) && !FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return -EBADE;
return varlink_idl_validate_symbol(method, v, SD_VARLINK_INPUT, reterr_bad_field);
}
int varlink_idl_validate_method_call(const sd_varlink_symbol *method, sd_json_variant *v, const char **bad_field) {
return varlink_idl_validate_method(method, v, SD_VARLINK_INPUT, bad_field);
int varlink_idl_validate_method_reply(const sd_varlink_symbol *method, sd_json_variant *v, sd_varlink_reply_flags_t flags, const char **reterr_bad_field) {
if (!method)
return 0; /* Can't validate */
if (method->symbol_type != SD_VARLINK_METHOD)
return -EBADMSG;
/* If method replies have the "continues" flag set, but the method is not allowed to generate that, return a recognizable error */
if (FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES) && (method->symbol_type & (SD_VARLINK_SUPPORTS_MORE|SD_VARLINK_REQUIRES_MORE)) == 0)
return -EBADE;
return varlink_idl_validate_symbol(method, v, SD_VARLINK_OUTPUT, reterr_bad_field);
}
int varlink_idl_validate_method_reply(const sd_varlink_symbol *method, sd_json_variant *v, const char **bad_field) {
return varlink_idl_validate_method(method, v, SD_VARLINK_OUTPUT, bad_field);
}
int varlink_idl_validate_error(const sd_varlink_symbol *error, sd_json_variant *v, const char **bad_field) {
int varlink_idl_validate_error(const sd_varlink_symbol *error, sd_json_variant *v, const char **reterr_bad_field) {
if (!error)
return 0; /* Can't validate */
if (error->symbol_type != SD_VARLINK_ERROR)
return -EBADMSG;
return varlink_idl_validate_symbol(error, v, SD_VARLINK_REGULAR, bad_field);
return varlink_idl_validate_symbol(error, v, SD_VARLINK_REGULAR, reterr_bad_field);
}
const sd_varlink_symbol* varlink_idl_find_symbol(
+19 -6
View File
@@ -1354,8 +1354,17 @@ static int varlink_dispatch_method(sd_varlink *v) {
else {
const char *bad_field;
r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field);
if (r < 0) {
r = varlink_idl_validate_method_call(v->current_method, parameters, flags, &bad_field);
if (r == -EBADE) {
varlink_log_errno(v, r, "Method %s() called without 'more' flag, but flag needs to be set: %m",
method);
if (v->state == VARLINK_PROCESSING_METHOD) {
r = sd_varlink_error(v, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
if (r < 0)
return r;
}
} else if (r < 0) {
/* Please adjust test/units/end.sh when updating the log message. */
varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m",
method, strna(bad_field));
@@ -1365,8 +1374,9 @@ static int varlink_dispatch_method(sd_varlink *v) {
if (r < 0)
return r;
}
invalid = true;
}
invalid = r < 0;
}
if (!invalid) {
@@ -2439,7 +2449,7 @@ _public_ int sd_varlink_reply(sd_varlink *v, sd_json_variant *parameters) {
if (v->current_method) {
const char *bad_field = NULL;
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
r = varlink_idl_validate_method_reply(v->current_method, parameters, /* flags= */ 0, &bad_field);
if (r < 0)
/* Please adjust test/units/end.sh when updating the log message. */
varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m",
@@ -2652,8 +2662,11 @@ _public_ int sd_varlink_notify(sd_varlink *v, sd_json_variant *parameters) {
if (v->current_method) {
const char *bad_field = NULL;
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
if (r < 0)
r = varlink_idl_validate_method_reply(v->current_method, parameters, SD_VARLINK_REPLY_CONTINUES, &bad_field);
if (r == -EBADE)
varlink_log_errno(v, r, "Method reply for %s() has 'continues' flag set, but IDL structure doesn't allow that, ignoring: %m",
v->current_method->name);
else if (r < 0)
/* Please adjust test/units/end.sh when updating the log message. */
varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m",
v->current_method->name, strna(bad_field));
+3 -2
View File
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink.h"
#include "sd-varlink-idl.h"
#include "macro.h"
@@ -20,6 +21,6 @@ int varlink_idl_consistent(const sd_varlink_interface *interface, int level);
const sd_varlink_symbol* varlink_idl_find_symbol(const sd_varlink_interface *interface, sd_varlink_symbol_type_t type, const char *name);
const sd_varlink_field* varlink_idl_find_field(const sd_varlink_symbol *symbol, const char *name);
int varlink_idl_validate_method_call(const sd_varlink_symbol *method, sd_json_variant *v, const char **bad_field);
int varlink_idl_validate_method_reply(const sd_varlink_symbol *method, sd_json_variant *v, const char **bad_field);
int varlink_idl_validate_method_call(const sd_varlink_symbol *method, sd_json_variant *v, sd_varlink_method_flags_t flags, const char **reterr_bad_field);
int varlink_idl_validate_method_reply(const sd_varlink_symbol *method, sd_json_variant *v, sd_varlink_reply_flags_t flags, const char **reterr_bad_field);
int varlink_idl_validate_error(const sd_varlink_symbol *error, sd_json_variant *v, const char **bad_field);
+2 -1
View File
@@ -43,8 +43,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("Indicates whether this entry has been booted."),
SD_VARLINK_DEFINE_FIELD(isSelected, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
ListBootEntries,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_FIELD_COMMENT("A boot menu entry structure"),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(entry, BootEntry, SD_VARLINK_NULLABLE));
+4 -2
View File
@@ -53,8 +53,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("The priority of the log message, using the BSD syslog priority levels"),
SD_VARLINK_DEFINE_FIELD(priority, SD_VARLINK_INT, 0));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
ListTransfers,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_FIELD_COMMENT("Image class to filter by"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("A unique numeric identifier for the ongoing transfer"),
@@ -70,8 +71,9 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_FIELD_COMMENT("Progress in percent"),
SD_VARLINK_DEFINE_OUTPUT(percent, SD_VARLINK_FLOAT, 0));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
Pull,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_FIELD_COMMENT("The remote URL to download from"),
SD_VARLINK_DEFINE_INPUT(remote, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("The local image name to download to"),
+2 -1
View File
@@ -28,8 +28,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_FIELD_COMMENT("Timestamp in µs in the CLOCK_MONOTONIC clock"),
SD_VARLINK_DEFINE_FIELD(monotonic, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
List,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_FIELD_COMMENT("If non-null the name of a running machine to report details on. If null/unspecified enumerates all running machines."),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Name of the machine"),
+2 -1
View File
@@ -9,8 +9,9 @@
*
* Compare with io.systemd.oom where the client/server roles of oomd and the service manager are swapped! */
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
SubscribeManagedOOMCGroups,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(cgroups, ControlGroup, SD_VARLINK_ARRAY));
static SD_VARLINK_DEFINE_ERROR(SubscriptionTaken);
+2 -1
View File
@@ -2,8 +2,9 @@
#include "varlink-io.systemd.PCRLock.h"
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
ReadEventLog,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_DEFINE_OUTPUT(record, SD_VARLINK_OBJECT, 0));
static SD_VARLINK_DEFINE_METHOD(
@@ -17,8 +17,9 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(raw, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
SubscribeQueryResults,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
/* First reply */
SD_VARLINK_DEFINE_OUTPUT(ready, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+6 -3
View File
@@ -2,24 +2,27 @@
#include "varlink-io.systemd.UserDatabase.h"
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
GetUserRecord,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_DEFINE_INPUT(uid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(userName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(service, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_OUTPUT(record, SD_VARLINK_OBJECT, 0),
SD_VARLINK_DEFINE_OUTPUT(incomplete, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
GetGroupRecord,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_DEFINE_INPUT(gid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(groupName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(service, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_OUTPUT(record, SD_VARLINK_OBJECT, 0),
SD_VARLINK_DEFINE_OUTPUT(incomplete, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
GetMemberships,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_DEFINE_INPUT(userName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(groupName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(service, SD_VARLINK_STRING, 0),
+2 -1
View File
@@ -33,8 +33,9 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
static SD_VARLINK_DEFINE_METHOD_FULL(
List,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(Class, ImageClass, 0),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(Type, ImageType, 0),
+24
View File
@@ -34,6 +34,12 @@ _SD_BEGIN_DECLARATIONS;
* format on-the-fly. Can also parse the textual format back to C structures. Validates the interface
* definitions for internal consistency and validates JSON objects against the interface definitions. */
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_interface_flags_t) {
_SD_VARLINK_INTERFACE_FLAGS_MAX = (1 << 0) - 1,
_SD_VARLINK_INTERFACE_FLAGS_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(SD_VARLINK_INTERFACE_FLAGS)
} sd_varlink_interface_flags_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_symbol_type_t) {
SD_VARLINK_ENUM_TYPE,
SD_VARLINK_STRUCT_TYPE,
@@ -46,6 +52,14 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_symbol_type_t) {
_SD_ENUM_FORCE_S64(SD_VARLINK_SYMBOL)
} sd_varlink_symbol_type_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_symbol_flags_t) {
SD_VARLINK_SUPPORTS_MORE = 1 << 0, /* Call supports "more" flag */
SD_VARLINK_REQUIRES_MORE = 1 << 1, /* Call requires "more" flag */
_SD_VARLINK_SYMBOL_FLAGS_MAX = (1 << 2) - 1,
_SD_VARLINK_SYMBOL_FLAGS_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(SD_VARLINK_SYMBOL_FLAGS)
} sd_varlink_symbol_flags_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_field_type_t) {
_SD_VARLINK_FIELD_TYPE_END_MARKER = 0, /* zero type means: this is the last entry in the fields[] array of VarlinkSymbol */
SD_VARLINK_STRUCT,
@@ -99,6 +113,7 @@ struct sd_varlink_field {
struct sd_varlink_symbol {
const char *name; /* most symbols have a name, but sometimes they are created on-the-fly for fields, in which case they are anonymous */
sd_varlink_symbol_type_t symbol_type;
sd_varlink_symbol_flags_t symbol_flags;
#if __STDC_VERSION__ >= 199901L
sd_varlink_field fields[];
#else
@@ -109,6 +124,7 @@ struct sd_varlink_symbol {
/* An interface definition has a name and consist of symbols */
struct sd_varlink_interface {
const char *name;
sd_varlink_interface_flags_t interface_flags;
#if __STDC_VERSION__ >= 199901L
const sd_varlink_symbol *symbols[];
#else
@@ -149,6 +165,14 @@ struct sd_varlink_interface {
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
}
#define SD_VARLINK_DEFINE_METHOD_FULL(_name, _flags, ...) \
const sd_varlink_symbol vl_method_ ## _name = { \
.name = #_name, \
.symbol_type = SD_VARLINK_METHOD, \
.symbol_flags = _flags, \
.fields = { __VA_ARGS__ __VA_OPT__(,) {}}, \
}
#define SD_VARLINK_DEFINE_ERROR(_name, ...) \
const sd_varlink_symbol vl_error_ ## _name = { \
.name = #_name, \
+1 -1
View File
@@ -305,7 +305,7 @@ TEST(validate_json) {
const sd_varlink_symbol* symbol = ASSERT_PTR(varlink_idl_find_symbol(parsed, SD_VARLINK_METHOD, "Mymethod"));
assert_se(varlink_idl_validate_method_call(symbol, v, NULL) >= 0);
assert_se(varlink_idl_validate_method_call(symbol, v, /* flags= */ 0, /* reterr_bad_field= */ NULL) >= 0);
}
static int test_recursive_one(unsigned depth) {