varlinkctl: add --graceful= option for optionally marking some errors as successes

This is generally useful, but in some cases particularly: when
implementing enumeration calls that use the "more" flag to return
multiple replies then for the first reply we need to return an error in
case the list of objects to enumerate is empty, usually so form of
"NoSuchXYZ" error. In many cases this shouldn't really be treated as
error, as an empty list probably more than not is as valid as a list
with one, two or more entries.
This commit is contained in:
Lennart Poettering 2024-05-27 22:32:51 +02:00
parent 296027122b
commit da213bb5c0
2 changed files with 55 additions and 3 deletions

View file

@ -250,6 +250,20 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--graceful=</option></term>
<listitem>
<para>Takes a qualified Varlink error name (i.e. an interface name, suffixed by an error name,
separated by a dot; e.g. <literal>org.varlink.service.InvalidParameter</literal>). Ensures that if
a method call fails with the specified error this will be treated as success, i.e. will cause the
<command>varlinkctl</command> invocation to exit with a zero exit status. This option may be used more
than once in order to treat multiple different errors as successes.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />

View file

@ -22,6 +22,9 @@ static PagerFlags arg_pager_flags = 0;
static VarlinkMethodFlags arg_method_flags = 0;
static bool arg_collect = false;
static bool arg_quiet = false;
static char **arg_graceful = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -58,6 +61,7 @@ static int help(void) {
" --json=MODE Output as JSON\n"
" -j Same as --json=pretty on tty, --json=short otherwise\n"
" -q --quiet Do not output method reply\n"
" --graceful=ERROR Treat specified Varlink error as success\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@ -82,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ONEWAY,
ARG_JSON,
ARG_COLLECT,
ARG_GRACEFUL,
};
static const struct option options[] = {
@ -93,6 +98,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "json", required_argument, NULL, ARG_JSON },
{ "collect", no_argument, NULL, ARG_COLLECT },
{ "quiet", no_argument, NULL, 'q' },
{ "graceful", required_argument, NULL, ARG_GRACEFUL },
{},
};
@ -142,6 +148,18 @@ static int parse_argv(int argc, char *argv[]) {
arg_quiet = true;
break;
case ARG_GRACEFUL:
r = varlink_idl_qualified_symbol_name_is_valid(optarg);
if (r < 0)
return log_error_errno(r, "Failed to validate Varlink error name '%s': %m", optarg);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid Varlink error name: %s", optarg);
if (strv_extend(&arg_graceful, optarg) < 0)
return log_oom();
break;
case '?':
return -EINVAL;
@ -153,6 +171,8 @@ static int parse_argv(int argc, char *argv[]) {
if (FLAGS_SET(arg_method_flags, VARLINK_METHOD_MORE))
arg_json_format_flags |= SD_JSON_FORMAT_SEQ;
strv_sort_uniq(arg_graceful);
return 1;
}
@ -438,7 +458,13 @@ static int reply_callback(
/* Propagate the error we received via sd_notify() */
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
r = *ret = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call failed: %s", error);
if (strv_contains(arg_graceful, error)) {
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
"Method call returned expected error: %s", error);
r = 0;
} else
r = *ret = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call failed: %s", error);
} else
r = 0;
@ -505,7 +531,13 @@ static int verb_call(int argc, char *argv[], void *userdata) {
/* Propagate the error we received via sd_notify() */
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
if (strv_contains(arg_graceful, error)) {
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
"Method call %s() returned expected error: %s", method, error);
r = 0;
} else
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
} else
r = 0;
@ -570,7 +602,13 @@ static int verb_call(int argc, char *argv[], void *userdata) {
/* Propagate the error we received via sd_notify() */
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
if (strv_contains(arg_graceful, error)) {
log_full(arg_quiet ? LOG_DEBUG : LOG_INFO,
"Method call %s() returned expected error: %s", method, error);
r = 0;
} else
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
} else
r = 0;