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);
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.");
}
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));
}
}
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));
}
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));
}
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;
}
}
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);
}
}
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;
-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);
+ return varlink_idl_validate_symbol(method, v, SD_VARLINK_INPUT, 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_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_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(
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));
if (r < 0)
return r;
}
- invalid = true;
}
+
+ invalid = r < 0;
}
if (!invalid) {
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",
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));
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "sd-varlink.h"
#include "sd-varlink-idl.h"
#include "macro.h"
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);
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));
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"),
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"),
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"),
*
* 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);
#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(
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),
#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),
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),
} sd_varlink_symbol_type_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_symbol_flags_t) {
- _SD_VARLINK_SYMBOL_FLAGS_MAX = (1 << 0) - 1,
+ 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;
.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, \
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) {