From: Lennart Poettering Date: Fri, 6 Sep 2024 19:44:51 +0000 (+0200) Subject: varlinkctl: add --timeout= switch to tweak time-out behaviour X-Git-Tag: v257-rc1~526 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=39ce86d19c7c3ac46b4afb546879ca8dcad1bdb2;p=thirdparty%2Fsystemd.git varlinkctl: add --timeout= switch to tweak time-out behaviour Fixes: #33772 --- diff --git a/man/varlinkctl.xml b/man/varlinkctl.xml index e936be2f43f..2a4bb160725 100644 --- a/man/varlinkctl.xml +++ b/man/varlinkctl.xml @@ -199,18 +199,35 @@ - When used with call: expect multiple method replies. If this flag is - set the method call is sent with the more flag set, which tells the service to - generate multiple replies, if needed. The command remains running until the service sends a reply - message that indicates it is the last in the series. This flag should be set only for method calls - that support this mechanism. + When used with call: expect multiple method replies. If this flag + is set the method call is sent with the more flag set, which tells the service + to generate multiple replies, if needed. The command remains running until the service sends a reply + message that indicates it is the last in the series (or if the configured time-out is reached, see + below). This flag should be set only for method calls that support this mechanism. If this mode is enabled output is automatically switched to JSON-SEQ mode, so that individual reply objects can be easily discerned. + This switch has no effect on the method call time-out applied by default: regardless if + is specified or not, the default time-out will be 45s. Use + (see below) to change or disable the time-out. When invoking a method + call that continously returns updates it is typically desirable to disable the time-out with + . On the other hand, when invoking a + method call for the purpose of enumerating objects (which likely will complete quickly) it is + typically beneficial to leave the time-out logic enabled, for robustness reasons. + + + + + A shortcut for . This switch is + useful for method calls that implement subscription to a continious stream of updates. + + + + @@ -279,6 +296,17 @@ + + + + + Expects a time-out in seconds as parameter. By default a time-out of 45s is enforced. To turn + off the time-out specify infinity or an empty string. + + + + + diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c index e99ea349641..885c1b1dc2b 100644 --- a/src/varlinkctl/varlinkctl.c +++ b/src/varlinkctl/varlinkctl.c @@ -26,6 +26,7 @@ static sd_varlink_method_flags_t arg_method_flags = 0; static bool arg_collect = false; static bool arg_quiet = false; static char **arg_graceful = NULL; +static usec_t arg_timeout = 0; STATIC_DESTRUCTOR_REGISTER(arg_graceful, strv_freep); @@ -65,6 +66,8 @@ static int help(void) { " -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" + " --timeout=SECS Maximum time to wait for method call completion\n" + " -E Short for --more --timeout=infinity\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -90,6 +93,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_COLLECT, ARG_GRACEFUL, + ARG_TIMEOUT, }; static const struct option options[] = { @@ -102,6 +106,7 @@ static int parse_argv(int argc, char *argv[]) { { "collect", no_argument, NULL, ARG_COLLECT }, { "quiet", no_argument, NULL, 'q' }, { "graceful", required_argument, NULL, ARG_GRACEFUL }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, {}, }; @@ -110,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hjq", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hjqE", options, NULL)) >= 0) switch (c) { @@ -124,6 +129,10 @@ static int parse_argv(int argc, char *argv[]) { arg_pager_flags |= PAGER_DISABLE; break; + case 'E': + arg_timeout = USEC_INFINITY; + _fallthrough_; + case ARG_MORE: arg_method_flags = (arg_method_flags & ~SD_VARLINK_METHOD_ONEWAY) | SD_VARLINK_METHOD_MORE; break; @@ -163,6 +172,21 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_TIMEOUT: + if (isempty(optarg)) { + arg_timeout = USEC_INFINITY; + break; + } + + r = parse_sec(optarg, &arg_timeout); + if (r < 0) + return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg); + + if (arg_timeout == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timeout cannot be zero."); + + break; + case '?': return -EINVAL; @@ -185,6 +209,8 @@ static int varlink_connect_auto(sd_varlink **ret, const char *where) { assert(ret); assert(where); + _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; + if (STARTSWITH_SET(where, "/", "./")) { /* If the string starts with a slash or dot slash we use it as a file system path */ _cleanup_close_ int fd = -EBADF; struct stat st; @@ -196,32 +222,35 @@ static int varlink_connect_auto(sd_varlink **ret, const char *where) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat '%s': %m", where); - /* Is this a socket in the fs? Then connect() to it. */ if (S_ISSOCK(st.st_mode)) { - r = sd_varlink_connect_address(ret, FORMAT_PROC_FD_PATH(fd)); + /* Is this a socket in the fs? Then connect() to it. */ + + r = sd_varlink_connect_address(&vl, FORMAT_PROC_FD_PATH(fd)); if (r < 0) return log_error_errno(r, "Failed to connect to '%s': %m", where); - return 0; - } + } else if (S_ISREG(st.st_mode) && (st.st_mode & 0111)) { + /* Is this an executable binary? Then fork it off. */ - /* Is this an executable binary? Then fork it off. */ - if (S_ISREG(st.st_mode) && (st.st_mode & 0111)) { - r = sd_varlink_connect_exec(ret, where, STRV_MAKE(where)); /* Ideally we'd use FORMAT_PROC_FD_PATH(fd) here too, but that breaks the #! logic */ + r = sd_varlink_connect_exec(&vl, where, STRV_MAKE(where)); /* Ideally we'd use FORMAT_PROC_FD_PATH(fd) here too, but that breaks the #! logic */ if (r < 0) return log_error_errno(r, "Failed to spawn '%s' process: %m", where); - - return 0; - } - - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unrecognized path '%s' is neither an AF_UNIX socket, nor an executable binary.", where); + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unrecognized path '%s' is neither an AF_UNIX socket, nor an executable binary.", where); + } else { + /* Otherwise assume this is an URL */ + r = sd_varlink_connect_url(&vl, where); + if (r < 0) + return log_error_errno(r, "Failed to connect to URL '%s': %m", where); } - /* Otherwise assume this is an URL */ - r = sd_varlink_connect_url(ret, where); - if (r < 0) - return log_error_errno(r, "Failed to connect to URL '%s': %m", where); + if (arg_timeout != 0) { + r = sd_varlink_set_relative_timeout(vl, arg_timeout); + if (r < 0) + log_error_errno(r, "Failed to set Varlink timeout: %m"); + } + *ret = TAKE_PTR(vl); return 0; }