}
_public_ int sd_varlink_error_errno(sd_varlink *v, int error) {
+
+ /* This generates a system error return that includes the Linux error number, and error name. The
+ * error number is kinda Linux specific (and to some degree the error name too), hence let's indicate
+ * the origin of the system error. This way interpretation of the error should not leave questions
+ * open, even to foreign systems. */
+
+ error = abs(error);
+ const char *name = errno_to_name(error);
+
return sd_varlink_errorbo(
v,
SD_VARLINK_ERROR_SYSTEM,
- SD_JSON_BUILD_PAIR("errno", SD_JSON_BUILD_INTEGER(abs(error))));
+ SD_JSON_BUILD_PAIR_STRING("origin", "linux"),
+ SD_JSON_BUILD_PAIR_INTEGER("errno", error),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("errnoName", name));
}
_public_ int sd_varlink_notify(sd_varlink *v, sd_json_variant *parameters) {
{ SD_VARLINK_ERROR_EXPECTED_MORE, -EBADE },
};
+ int r;
+
if (!error)
return 0;
if (streq(error, t->error))
return t->value;
- if (streq(error, SD_VARLINK_ERROR_SYSTEM) && parameters) {
- sd_json_variant *e;
+ /* This following tries to reverse the operation sd_varlink_error_errno() applies to turn errnos into
+ * varlink errors */
+ if (!streq(error, SD_VARLINK_ERROR_SYSTEM))
+ return -EBADR;
- e = sd_json_variant_by_key(parameters, "errno");
- if (sd_json_variant_is_integer(e)) {
- int64_t i;
+ if (!parameters)
+ return -EBADR;
- i = sd_json_variant_integer(e);
- if (i > 0 && i < ERRNO_MAX)
- return -i;
- }
+ /* If an origin is set, check if it's Linux, otherwise don't translate */
+ sd_json_variant *e = sd_json_variant_by_key(parameters, "origin");
+ if (e && (!sd_json_variant_is_string(e) ||
+ !streq(sd_json_variant_string(e), "linux")))
+ return -EBADR;
+
+ /* If a name is specified, go by name */
+ e = sd_json_variant_by_key(parameters, "errnoName");
+ if (e) {
+ if (!sd_json_variant_is_string(e))
+ return -EBADR;
+
+ r = errno_from_name(sd_json_variant_string(e));
+ if (r < 0)
+ return -EBADR;
+
+ assert(r > 0);
+ return -r;
}
- return -EBADR; /* Catch-all */
+ /* Finally, use the provided error number, if there is one */
+ e = sd_json_variant_by_key(parameters, "errno");
+ if (!e)
+ return -EBADR;
+ if (!sd_json_variant_is_integer(e))
+ return -EBADR;
+
+ int64_t i = sd_json_variant_integer(e);
+ if (i <= 0 || i > ERRNO_MAX)
+ return -EBADR;
+
+ return (int) -i;
}
_public_ int sd_varlink_error_is_invalid_parameter(const char *error, sd_json_variant *parameter, const char *name) {
/* This one we invented, and use for generically propagating system errors (errno) to clients */
static SD_VARLINK_DEFINE_ERROR(
System,
- SD_VARLINK_DEFINE_FIELD(errno, SD_VARLINK_INT, 0));
+ SD_VARLINK_FIELD_COMMENT("The origin of this system error, typically 'linux' to indicate Linux error numbers."),
+ SD_VARLINK_DEFINE_FIELD(origin, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The Linux error name, i.e. ENOENT, EHWPOISON or similar."),
+ SD_VARLINK_DEFINE_FIELD(errnoName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The numeric Linux error number. Typically the name is preferable, if specified."),
+ SD_VARLINK_DEFINE_FIELD(errno, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd,
"io.systemd",
+ SD_VARLINK_SYMBOL_COMMENT("Local error if a Varlink connection is disconnected (this never crosses the wire and is synthesized locally only)."),
&vl_error_Disconnected,
+ SD_VARLINK_SYMBOL_COMMENT("A method call time-out has been reached (also synthesized locally, does not cross wire)"),
&vl_error_TimedOut,
+ SD_VARLINK_SYMBOL_COMMENT("Some form of protocol error (also synthesized locally, does not cross wire)"),
&vl_error_Protocol,
+ SD_VARLINK_SYMBOL_COMMENT("A generic Linux system error (\"errno\"s)."),
&vl_error_System);