mirror of
https://github.com/systemd/systemd
synced 2024-10-02 22:37:25 +00:00
Merge pull request #33428 from poettering/graceful-varlinkctl
varlinkctl: add new --graceful= switch for treating selected errors like a success
This commit is contained in:
commit
3fbbff83b6
|
@ -250,6 +250,20 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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="no-pager" />
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
|
|
|
@ -1452,6 +1452,28 @@ static bool varlink_idl_comment_is_valid(const char *comment) {
|
||||||
return utf8_is_valid(comment);
|
return utf8_is_valid(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int varlink_idl_qualified_symbol_name_is_valid(const char *name) {
|
||||||
|
const char *dot;
|
||||||
|
|
||||||
|
/* Validates a qualified symbol name (i.e. interface name, followed by a dot, followed by a symbol name) */
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dot = strrchr(name, '.');
|
||||||
|
if (!dot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!varlink_idl_symbol_name_is_valid(dot + 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_cleanup_free_ char *iface = strndup(name, dot - name);
|
||||||
|
if (!iface)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return varlink_idl_interface_name_is_valid(iface);
|
||||||
|
}
|
||||||
|
|
||||||
static int varlink_idl_symbol_consistent(const VarlinkInterface *interface, const VarlinkSymbol *symbol, int level);
|
static int varlink_idl_symbol_consistent(const VarlinkInterface *interface, const VarlinkSymbol *symbol, int level);
|
||||||
|
|
||||||
static int varlink_idl_field_consistent(
|
static int varlink_idl_field_consistent(
|
||||||
|
|
|
@ -170,6 +170,8 @@ bool varlink_idl_field_name_is_valid(const char *name);
|
||||||
bool varlink_idl_symbol_name_is_valid(const char *name);
|
bool varlink_idl_symbol_name_is_valid(const char *name);
|
||||||
bool varlink_idl_interface_name_is_valid(const char *name);
|
bool varlink_idl_interface_name_is_valid(const char *name);
|
||||||
|
|
||||||
|
int varlink_idl_qualified_symbol_name_is_valid(const char *name);
|
||||||
|
|
||||||
int varlink_idl_consistent(const VarlinkInterface *interface, int level);
|
int varlink_idl_consistent(const VarlinkInterface *interface, int level);
|
||||||
|
|
||||||
const VarlinkSymbol* varlink_idl_find_symbol(const VarlinkInterface *interface, VarlinkSymbolType type, const char *name);
|
const VarlinkSymbol* varlink_idl_find_symbol(const VarlinkInterface *interface, VarlinkSymbolType type, const char *name);
|
||||||
|
|
|
@ -261,6 +261,17 @@ TEST(field_name_is_valid) {
|
||||||
assert_se(varlink_idl_field_name_is_valid("foo0foo"));
|
assert_se(varlink_idl_field_name_is_valid("foo0foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(qualified_symbol_name_is_valid) {
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid(NULL) == 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("") == 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("x") == 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("xxx") == 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("xxx.xxx") == 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("xxx.Xxx") > 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("xxx.xxx.XXX") > 0);
|
||||||
|
assert_se(varlink_idl_qualified_symbol_name_is_valid("xxx.xxx.0foo") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(validate_json) {
|
TEST(validate_json) {
|
||||||
|
|
||||||
_cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
|
_cleanup_(varlink_interface_freep) VarlinkInterface *parsed = NULL;
|
||||||
|
|
|
@ -22,6 +22,9 @@ static PagerFlags arg_pager_flags = 0;
|
||||||
static VarlinkMethodFlags arg_method_flags = 0;
|
static VarlinkMethodFlags arg_method_flags = 0;
|
||||||
static bool arg_collect = false;
|
static bool arg_collect = false;
|
||||||
static bool arg_quiet = false;
|
static bool arg_quiet = false;
|
||||||
|
static char **arg_graceful = NULL;
|
||||||
|
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep);
|
||||||
|
|
||||||
static int help(void) {
|
static int help(void) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
|
@ -58,6 +61,7 @@ static int help(void) {
|
||||||
" --json=MODE Output as JSON\n"
|
" --json=MODE Output as JSON\n"
|
||||||
" -j Same as --json=pretty on tty, --json=short otherwise\n"
|
" -j Same as --json=pretty on tty, --json=short otherwise\n"
|
||||||
" -q --quiet Do not output method reply\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",
|
"\nSee the %2$s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
link,
|
link,
|
||||||
|
@ -82,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_ONEWAY,
|
ARG_ONEWAY,
|
||||||
ARG_JSON,
|
ARG_JSON,
|
||||||
ARG_COLLECT,
|
ARG_COLLECT,
|
||||||
|
ARG_GRACEFUL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -93,6 +98,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "json", required_argument, NULL, ARG_JSON },
|
{ "json", required_argument, NULL, ARG_JSON },
|
||||||
{ "collect", no_argument, NULL, ARG_COLLECT },
|
{ "collect", no_argument, NULL, ARG_COLLECT },
|
||||||
{ "quiet", no_argument, NULL, 'q' },
|
{ "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;
|
arg_quiet = true;
|
||||||
break;
|
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 '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -153,6 +171,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
if (FLAGS_SET(arg_method_flags, VARLINK_METHOD_MORE))
|
if (FLAGS_SET(arg_method_flags, VARLINK_METHOD_MORE))
|
||||||
arg_json_format_flags |= SD_JSON_FORMAT_SEQ;
|
arg_json_format_flags |= SD_JSON_FORMAT_SEQ;
|
||||||
|
|
||||||
|
strv_sort_uniq(arg_graceful);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,6 +458,12 @@ static int reply_callback(
|
||||||
/* Propagate the error we received via sd_notify() */
|
/* Propagate the error we received via sd_notify() */
|
||||||
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
|
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%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);
|
r = *ret = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call failed: %s", error);
|
||||||
} else
|
} else
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -505,6 +531,12 @@ static int verb_call(int argc, char *argv[], void *userdata) {
|
||||||
/* Propagate the error we received via sd_notify() */
|
/* Propagate the error we received via sd_notify() */
|
||||||
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
|
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", 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);
|
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
|
||||||
} else
|
} else
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -570,6 +602,12 @@ static int verb_call(int argc, char *argv[], void *userdata) {
|
||||||
/* Propagate the error we received via sd_notify() */
|
/* Propagate the error we received via sd_notify() */
|
||||||
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", error);
|
(void) sd_notifyf(/* unset_environment= */ false, "VARLINKERROR=%s", 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);
|
r = log_error_errno(SYNTHETIC_ERRNO(EBADE), "Method call %s() failed: %s", method, error);
|
||||||
} else
|
} else
|
||||||
r = 0;
|
r = 0;
|
||||||
|
|
|
@ -264,7 +264,7 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
testcase_bootctl_varlink() {
|
testcase_bootctl_varlink() {
|
||||||
(varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}' ||:)
|
varlinkctl call --collect /run/systemd/io.systemd.BootControl io.systemd.BootControl.ListBootEntries '{}' --graceful=io.systemd.BootControl.NoSuchBootEntry
|
||||||
|
|
||||||
# We may have UEFI in the test environment.
|
# We may have UEFI in the test environment.
|
||||||
# If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
|
# If we don't have UEFI then we can test whether bootctl's varlink API fails cleanly.
|
||||||
|
@ -272,8 +272,8 @@ testcase_bootctl_varlink() {
|
||||||
if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
|
if ! (SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.GetRebootToFirmware '{}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
(SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
|
SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":true}' --graceful=io.systemd.BootControl.RebootToFirmwareNotSupported
|
||||||
(SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' || true) |& grep -q io.systemd.BootControl.RebootToFirmwareNotSupported
|
SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' --graceful=io.systemd.BootControl.RebootToFirmwareNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
run_testcases
|
run_testcases
|
||||||
|
|
|
@ -43,9 +43,9 @@ if command -v userdbctl >/dev/null; then
|
||||||
varlinkctl call -q /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
|
varlinkctl call -q /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'
|
||||||
varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
|
varlinkctl call -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' | jq .
|
||||||
# We ignore the return value of the following two calls, since if no memberships are defined at all this will return a NotFound error, which is OK
|
# We ignore the return value of the following two calls, since if no memberships are defined at all this will return a NotFound error, which is OK
|
||||||
(varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' ||:)
|
varlinkctl call --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' --graceful=io.systemd.UserDatabase.NoRecordFound
|
||||||
(varlinkctl call --quiet --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' ||:)
|
varlinkctl call --quiet --more /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' --graceful=io.systemd.UserDatabase.NoRecordFound
|
||||||
(varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' ||:) | jq --seq .
|
varlinkctl call --more -j /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' --graceful=io.systemd.UserDatabase.NoRecordFound | jq --seq .
|
||||||
varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
|
varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }'
|
||||||
(! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
|
(! varlinkctl call --oneway /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetMemberships '{ "service" : "io.systemd.Multiplexer" }' | grep .)
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Reference in a new issue