From nobody Mon May 6 14:28:06 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1534141964905847.5404616223285; Sun, 12 Aug 2018 23:32:44 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0B74E9C0A2; Mon, 13 Aug 2018 06:32:42 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 969F060BE7; Mon, 13 Aug 2018 06:32:41 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A6DFA18037EF; Mon, 13 Aug 2018 06:32:40 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w7D6WaTo012311 for ; Mon, 13 Aug 2018 02:32:36 -0400 Received: by smtp.corp.redhat.com (Postfix) id C147D2010CC6; Mon, 13 Aug 2018 06:32:36 +0000 (UTC) Received: from mx1.redhat.com (ext-mx01.extmail.prod.ext.phx2.redhat.com [10.5.110.25]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B6DCA2010CC4 for ; Mon, 13 Aug 2018 06:32:33 +0000 (UTC) Received: from mout-xforward.gmx.net (mout-xforward.gmx.net [82.165.159.40]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0C95A81DE8 for ; Mon, 13 Aug 2018 06:32:30 +0000 (UTC) Received: from localhost.localdomain ([58.56.27.130]) by mail.gmx.com (mrgmx103 [212.227.17.174]) with ESMTPSA (Nemesis) id 0MZD0K-1fasMx0F0G-00Ku9j; Mon, 13 Aug 2018 08:32:28 +0200 From: Shi Lei To: libvir-list@redhat.com Date: Mon, 13 Aug 2018 14:32:18 +0800 Message-Id: <1534141939-5199-2-git-send-email-shilei.massclouds@gmx.com> In-Reply-To: <1534141939-5199-1-git-send-email-shilei.massclouds@gmx.com> References: <1534141939-5199-1-git-send-email-shilei.massclouds@gmx.com> X-Provags-ID: V03:K1:r7SIjFtspaEXNUQ8TdYKDPuPm2jvlPVSsIpIZeBKnfjtdNMGLL3 Er9Q/1ivf2lnV3HhbeXVA/uMXxpQCAkoryMlr6zsQWmEdnP4uglKyNwDhuARo6AIws8ssvT EcL9Y/HTsvro/xHdtJcuA6n5Lfj+wrcmsWAIOtA4lgxKj/OhHffxojBgso7VXkHubUMuSEp Gl+Z5zgcNjrFo7HgOVfuA== X-UI-Out-Filterresults: junk:10;V01:K0:QCa8fdXbOoQ=:lnn0xHElB2ozXDKwpMylccjb zc/LDaWqoyD9fPgswZPrhgikwNsrl3Irp6iq17vNlQh3OyrT42xiSRGKbdyVOx8PLRpUO+kNR ldaBogkrvVk4iIMHFp+8t5R5lx6GmAd7E/vZ4EERRWo4vY43cdq/V4w7+7ZrLLubfNkQ3M42a DWCvMTi/4vtckJDN2zhoIGjwcLJSBXlxOHbu8jru/YgVjoASlltOOoLSUEInPTw0JV7xYQGg5 6cbGeJKT8PhYZMuivqt/mFomu2VZR3QGq+Oy7maQoTE9SezXjyBxrd7tDjSUTOhgFkbCC/Hd8 YPIHVO1iS/xyZnsnx4DUfTcbF8apZx+Mv2sm2ZvSME6CjI3EZKcixkBY1NKHDlaUNRDA08BoX 06UX8OFwTavk/2JsjoGS/LKvL7Mqv/JngssIRrM+h55LVb1dq3+6MST0aNGq4BFoizDFgXgdI tW9B0XryXSD9xvYeZYQd4NZomEed4ADwTo3ciDGxGxakK2sUIIwonPC6BmDDtO2Xk/0RvlLXK z4ZTKSJJelIpXVCnqiAoCG4QX/XE+C3YK/jpg3M7hD266PS6kU9rJXk3AgYWNscwCK80m4jg9 Mbx9rMVh+aEHKiv3s8Lir3f3xe4XMaJTWHm6AT9EMOHil7N9BKRItHrFDA5lJ0j8kT9mnR/X2 aHEs+gnRkWufw8sjv9RZnIMaVbTGHcBfU/5f7+DJaxOCnqwc5Mrr1DCQAvF+hbQnElF5Q8BlI ceYa92yPVnOI4L48ErZMb8ieLLEFkEjJN7wYtZNXF439mHuZWAKtMSUl/Aqgqqwxrj8Y+6x7h nIZpH7Gx/yblDnSKrLC1lCysnf1yRYBm+KQHdM3yJyXeNIuuBQ8QZ8DmKbqnra44DItKsNVgs g6RgMa1OFWLzzrVTnNyTD6+8m1SdOwW0QPgJHaVdSMiteEVOPgtHNxequSRo X-Greylist: Sender passed SPF test, ACL 232 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Mon, 13 Aug 2018 06:32:31 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Mon, 13 Aug 2018 06:32:31 +0000 (UTC) for IP:'82.165.159.40' DOMAIN:'mout-xforward.gmx.net' HELO:'mout-xforward.gmx.net' FROM:'shilei.massclouds@gmx.com' RCPT:'' X-RedHat-Spam-Score: 0 (FREEMAIL_FROM, SPF_PASS) 82.165.159.40 mout-xforward.gmx.net 82.165.159.40 mout-xforward.gmx.net X-Scanned-By: MIMEDefang 2.83 on 10.5.110.25 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.25 X-loop: libvir-list@redhat.com Cc: Shi Lei Subject: [libvirt] [RFC 1/2] introduce timeout mode in virCommandRun/Async X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Mon, 13 Aug 2018 06:32:42 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Shi Lei --- docs/internals/command.html.in | 68 +++++++ src/libvirt_private.syms | 2 + src/util/vircommand.c | 110 ++++++++++- src/util/vircommand.h | 4 + src/util/virprocess.c | 107 ++++++++--- src/util/virprocess.h | 4 + tests/commandtest.c | 411 +++++++++++++++++++++++++++++++++++++= +--- 7 files changed, 651 insertions(+), 55 deletions(-) diff --git a/docs/internals/command.html.in b/docs/internals/command.html.in index 43f51a4..e064019 100644 --- a/docs/internals/command.html.in +++ b/docs/internals/command.html.in @@ -530,6 +530,74 @@ if (WEXITSTATUS(status)...) { virCommandAbort to reap the process.

=20 +

Set timeout for commands

+ +

+ With virCommandSetTimeout, commands can be set + timeout in milliseconds. It can be used under one of these condition= s: +

+ +

+ 1. In synchronous mode, using virCommandRun NOT in + daemon mode. By default, virCommandRun waits for the + command to complete & exit and then checks its exit status. If t= he + caller uses virCommandSetTimeout before it, the command= will be + aborted internally when timeout and there is no need to use + virCommandAbort to reap the child process. +

+ +
+int timeout =3D 3000; /* in milliseconds */
+virCommandSetTimeout(cmd, timeout);
+if (virCommandRun(cmd, NULL) < 0) {
+    if (virCommandGetErr(cmd) =3D=3D ETIME)
+        ... do something for timeout, e.g. report error ...
+
+    return -1;
+}
+
+ +

+ The argument timeout of the virCommandSetTimeout<= /code> + accepts a positive value. When timeout, virCommandRun r= eturn + -1 and the caller can use virCommandGetErr to check err= orcode. And + ETIME means command timeout. +

+

+ Note: virCommandSetTimeout cannot mix = with + virCommandDaemonize. Or virCommandRun fail= s directly. +

+ +

+ 2. In asynchronous mode, the caller can also call virCommandSe= tTimeout + before virCommandRunAsync to limit the running time of = the command. + The caller uses virCommandWait to wait for the child pr= ocess to complete + & exit. virCommandWait returns -1 if timeout and th= e caller can use + virCommandGetErr to check the reason. +

+ +
+char *outbuf =3D NULL;
+char *errbuf =3D NULL;
+int timeout =3D 3000; /* in milliseconds */
+virCommandSetTimeout(cmd, timeout);
+
+virCommandSetOutputBuffer(cmd, &outbuf);
+virCommandSetErrorBuffer(cmd, &errbuf);
+virCommandDoAsyncIO(cmd);
+if (virCommandRunAsync(cmd, NULL) < 0)
+   return -1;
+
+... do something ...
+
+if (virCommandWait(cmd, NULL) < 0) {
+    if (virCommandGetErr(cmd) =3D=3D ETIME)
+        ... do something for timeout, e.g. report error ...
+
+    return -1;
+}
+
+

Releasing resources

=20

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 32ed5a0..7a44d19 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1604,6 +1604,7 @@ virCommandDaemonize; virCommandDoAsyncIO; virCommandExec; virCommandFree; +virCommandGetErr; virCommandGetGID; virCommandGetUID; virCommandHandshakeNotify; @@ -1638,6 +1639,7 @@ virCommandSetOutputFD; virCommandSetPidFile; virCommandSetPreExecHook; virCommandSetSELinuxLabel; +virCommandSetTimeout; virCommandSetUID; virCommandSetUmask; virCommandSetWorkingDirectory; diff --git a/src/util/vircommand.c b/src/util/vircommand.c index 8be3fdf..1966648 100644 --- a/src/util/vircommand.c +++ b/src/util/vircommand.c @@ -53,6 +53,7 @@ #include "virbuffer.h" #include "virthread.h" #include "virstring.h" +#include "virtime.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -136,6 +137,8 @@ struct _virCommand { char *appArmorProfile; #endif int mask; + + int timeout; }; =20 /* See virCommandSetDryRun for description for this variable */ @@ -906,6 +909,8 @@ virCommandNewArgs(const char *const*args) cmd->uid =3D -1; cmd->gid =3D -1; =20 + cmd->timeout =3D -1; + virCommandAddArgSet(cmd, args); =20 return cmd; @@ -1077,6 +1082,13 @@ virCommandGetUID(virCommandPtr cmd) } =20 =20 +int +virCommandGetErr(virCommandPtr cmd) +{ + return cmd ? cmd->has_error : -1; +} + + void virCommandSetGID(virCommandPtr cmd, gid_t gid) { @@ -2016,6 +2028,8 @@ virCommandProcessIO(virCommandPtr cmd) size_t inlen =3D 0, outlen =3D 0, errlen =3D 0; size_t inoff =3D 0; int ret =3D 0; + int timeout =3D -1; + unsigned long long start; =20 if (dryRunBuffer || dryRunCallback) { VIR_DEBUG("Dry run requested, skipping I/O processing"); @@ -2041,6 +2055,11 @@ virCommandProcessIO(virCommandPtr cmd) if (VIR_REALLOC_N(*cmd->errbuf, 1) < 0) ret =3D -1; } + if (cmd->timeout > 0) { + timeout =3D cmd->timeout; + if (virTimeMillisNow(&start) < 0) + ret =3D -1; + } if (ret =3D=3D -1) goto cleanup; ret =3D -1; @@ -2069,17 +2088,45 @@ virCommandProcessIO(virCommandPtr cmd) nfds++; } =20 - if (nfds =3D=3D 0) + if (nfds =3D=3D 0) { + /* + * Timeout can be changed during loops. + * Set cmd->timeout with rest of timeout, + * and virCommandWait may uses it later + */ + if (timeout !=3D -1 && timeout !=3D cmd->timeout) + cmd->timeout =3D timeout; break; + } + + ret =3D poll(fds, nfds, timeout); + if (ret < 0) { + if (errno =3D=3D EAGAIN || errno =3D=3D EINTR) { + if (cmd->timeout > 0) { + unsigned long long now; + if (virTimeMillisNow(&now) < 0) + goto cleanup; =20 - if (poll(fds, nfds, -1) < 0) { - if (errno =3D=3D EAGAIN || errno =3D=3D EINTR) + if (timeout > (int)(now - start)) + timeout -=3D (int)(now - start); + else + timeout =3D 0; + } continue; + } virReportSystemError(errno, "%s", _("unable to poll on child")); goto cleanup; } =20 + if (ret =3D=3D 0) { + /* Timeout to abort command and return failure */ + virCommandAbort(cmd); + cmd->has_error =3D ETIME; + ret =3D -1; + goto cleanup; + } + for (i =3D 0; i < nfds; i++) { if (fds[i].revents & (POLLIN | POLLHUP | POLLERR) && (fds[i].fd =3D=3D errfd || fds[i].fd =3D=3D outfd)) { @@ -2126,6 +2173,7 @@ virCommandProcessIO(virCommandPtr cmd) =20 done =3D write(cmd->inpipe, cmd->inbuf + inoff, inlen - inoff); + if (done < 0) { if (errno =3D=3D EPIPE) { VIR_DEBUG("child closed stdin early, ignoring EPIP= E " @@ -2348,7 +2396,9 @@ virCommandDoAsyncIOHelper(void *opaque) virCommandPtr cmd =3D opaque; if (virCommandProcessIO(cmd) < 0) { /* If something went wrong, save errno or -1*/ - cmd->has_error =3D errno ? errno : -1; + /* except ETIME which means timeout */ + if (cmd->has_error !=3D ETIME) + cmd->has_error =3D errno ? errno : -1; } } =20 @@ -2438,6 +2488,13 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) _("creation of pid file requires daemonized command= ")); goto cleanup; } + if (cmd->timeout > 0) { + if (cmd->flags & VIR_EXEC_DAEMON) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("daemonized command cannot use virCommandSetT= imeout")); + goto cleanup; + } + } =20 str =3D virCommandToString(cmd); if (dryRunBuffer || dryRunCallback) { @@ -2531,6 +2588,11 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) virReportOOMError(); return -1; } + if (cmd->has_error =3D=3D ETIME) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("timeout waiting for child io")); + return -1; + } if (cmd->has_error) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid use of command API")); @@ -2559,7 +2621,17 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) * message is not as detailed as what we can provide. So, we * guarantee that virProcessWait only fails due to failure to wait, * and repeat the exitstatus check code ourselves. */ - ret =3D virProcessWait(cmd->pid, &status, true); + if (cmd->timeout >=3D 0) { + ret =3D virProcessWaitTimeout(cmd->pid, &status, true, &(cmd->time= out)); + if (ret =3D=3D 1) { + virCommandAbort(cmd); + cmd->has_error =3D ETIME; + ret =3D -1; + } + } else { + ret =3D virProcessWait(cmd->pid, &status, true); + } + if (cmd->flags & VIR_EXEC_ASYNC_IO) { cmd->flags &=3D ~VIR_EXEC_ASYNC_IO; virThreadJoin(cmd->asyncioThread); @@ -3163,3 +3235,31 @@ virCommandRunNul(virCommandPtr cmd ATTRIBUTE_UNUSED, return -1; } #endif /* WIN32 */ + + +/** + * virCommandSetTimeout: + * @cmd: the command to set timeout + * @timeout: the number of milliseconds for timeout. + * it should be a positive value. + * + * Set timeout for the command. When timeout, abort the command. + * By default, command without calling this function has no timeout. + * Note: virCommandSetTimeout should be used under one of the conditions: + * 1) virCommandRun WITHOUT setting VIR_EXEC_DAEMON + * 2) virCommandRunAsync and virCommandWait + */ +void +virCommandSetTimeout(virCommandPtr cmd, int timeout) +{ + if (!cmd || cmd->has_error) + return; + + if (timeout <=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("timeout can't be zero or negative")); + return; + } + + cmd->timeout =3D timeout; +} diff --git a/src/util/vircommand.h b/src/util/vircommand.h index 90bcc6c..99785af 100644 --- a/src/util/vircommand.h +++ b/src/util/vircommand.h @@ -77,6 +77,8 @@ void virCommandSetGID(virCommandPtr cmd, gid_t gid); =20 void virCommandSetUID(virCommandPtr cmd, uid_t uid); =20 +int virCommandGetErr(virCommandPtr cmd) ATTRIBUTE_NONNULL(1); + void virCommandSetMaxMemLock(virCommandPtr cmd, unsigned long long bytes); void virCommandSetMaxProcesses(virCommandPtr cmd, unsigned int procs); void virCommandSetMaxFiles(virCommandPtr cmd, unsigned int files); @@ -219,6 +221,8 @@ int virCommandRunNul(virCommandPtr cmd, virCommandRunNulFunc func, void *data); =20 +void virCommandSetTimeout(virCommandPtr cmd, int timeout); + VIR_DEFINE_AUTOPTR_FUNC(virCommand, virCommandFree) =20 #endif /* __VIR_COMMAND_H__ */ diff --git a/src/util/virprocess.c b/src/util/virprocess.c index ecea27a..e290c95 100644 --- a/src/util/virprocess.c +++ b/src/util/virprocess.c @@ -61,6 +61,7 @@ #include "virutil.h" #include "virstring.h" #include "vircommand.h" +#include "virtime.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -211,32 +212,12 @@ virProcessAbort(pid_t pid) #endif =20 =20 -/** - * virProcessWait: - * @pid: child to wait on - * @exitstatus: optional status collection - * @raw: whether to pass non-normal status back to caller - * - * Wait for a child process to complete. If @pid is -1, do nothing, but - * return -1 (useful for error cleanup, and assumes an earlier message was - * already issued). All other pids issue an error message on failure. - * - * If @exitstatus is NULL, then the child must exit normally with status 0. - * Otherwise, if @raw is false, the child must exit normally, and - * @exitstatus will contain the final exit status (no need for the caller - * to use WEXITSTATUS()). If @raw is true, then the result of waitpid() is - * returned in @exitstatus, and the caller must use WIFEXITED() and friends - * to decipher the child's status. - * - * Returns 0 on a successful wait. Returns -1 on any error waiting for - * completion, or if the command completed with a status that cannot be - * reflected via the choice of @exitstatus and @raw. - */ -int -virProcessWait(pid_t pid, int *exitstatus, bool raw) +static int +virProcessWaitHelper(pid_t pid, int *exitstatus, bool raw, const int *time= out) { int ret; int status; + unsigned long long start; VIR_AUTOFREE(char *) st =3D NULL; =20 if (pid <=3D 0) { @@ -246,9 +227,32 @@ virProcessWait(pid_t pid, int *exitstatus, bool raw) return -1; } =20 + if (virTimeMillisNow(&start) < 0) + return -1; + /* Wait for intermediate process to exit */ - while ((ret =3D waitpid(pid, &status, 0)) =3D=3D -1 && - errno =3D=3D EINTR); + if (timeout && *timeout >=3D 0) { + for (;;) { + while ((ret =3D waitpid(pid, &status, WNOHANG)) =3D=3D -1 && + errno =3D=3D EINTR); + + if (ret =3D=3D 0) { + unsigned long long now; + if (virTimeMillisNow(&now) < 0) + return -1; + + if ((int)(now - start) >=3D *timeout) + return 1; /* Timeout */ + + usleep(100 * 1000); + continue; + } + + break; + } + } else { + while ((ret =3D waitpid(pid, &status, 0)) =3D=3D -1 && errno =3D= =3D EINTR); + } =20 if (ret =3D=3D -1) { virReportSystemError(errno, _("unable to wait for process %lld"), @@ -278,6 +282,59 @@ virProcessWait(pid_t pid, int *exitstatus, bool raw) } =20 =20 +/** + * virProcessWait: + * @pid: child to wait on + * @exitstatus: optional status collection + * @raw: whether to pass non-normal status back to caller + * + * Wait for a child process to complete. If @pid is -1, do nothing, but + * return -1 (useful for error cleanup, and assumes an earlier message was + * already issued). All other pids issue an error message on failure. + * + * If @exitstatus is NULL, then the child must exit normally with status 0. + * Otherwise, if @raw is false, the child must exit normally, and + * @exitstatus will contain the final exit status (no need for the caller + * to use WEXITSTATUS()). If @raw is true, then the result of waitpid() is + * returned in @exitstatus, and the caller must use WIFEXITED() and friends + * to decipher the child's status. + * + * Returns 0 on a successful wait. Returns -1 on any error waiting for + * completion, or if the command completed with a status that cannot be + * reflected via the choice of @exitstatus and @raw. + */ +int +virProcessWait(pid_t pid, int *exitstatus, bool raw) +{ + return virProcessWaitHelper(pid, exitstatus, raw, NULL); +} + + +/** + * virProcessWaitTimeout: + * @pid: child to wait on + * @exitstatus: optional status collection + * @raw: whether to pass non-normal status back to caller + * @timeout: pointer to timeout. + * + * Just like virProcessWait, but it has timeout. + * Expect @timeout to be altered by outside, so passes pointer of it. + * + * Returns 0 on a successful wait. + * Returns 1 on timeout. + * Returns -1 on any error waiting for completion. + */ +int +virProcessWaitTimeout(pid_t pid, int *exitstatus, bool raw, const int *tim= eout) +{ + if (!timeout || *timeout < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("timeout invalid")); + return -1; + } + return virProcessWaitHelper(pid, exitstatus, raw, timeout); +} + + /* send signal to a single process */ int virProcessKill(pid_t pid, int sig) { diff --git a/src/util/virprocess.h b/src/util/virprocess.h index 3c5a882..bc4abd1 100644 --- a/src/util/virprocess.h +++ b/src/util/virprocess.h @@ -52,6 +52,10 @@ int virProcessWait(pid_t pid, int *exitstatus, bool raw) ATTRIBUTE_RETURN_CHECK; =20 +int +virProcessWaitTimeout(pid_t pid, int *exitstatus, bool raw, const int *tim= eout) + ATTRIBUTE_RETURN_CHECK; + int virProcessKill(pid_t pid, int sig); =20 int virProcessKillPainfully(pid_t pid, bool force); diff --git a/tests/commandtest.c b/tests/commandtest.c index 744a387..26c6b58 100644 --- a/tests/commandtest.c +++ b/tests/commandtest.c @@ -51,6 +51,10 @@ struct _virCommandTestData { bool running; }; =20 +struct testdata { + int timeout; /* milliseconds */ +}; + #ifdef WIN32 =20 int @@ -113,17 +117,22 @@ static int checkoutput(const char *testname, return ret; } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open * No slot for return status must log error. */ -static int test0(const void *unused ATTRIBUTE_UNUSED) +static int test0(const void *opaque) { virCommandPtr cmd; int ret =3D -1; + const struct testdata *data =3D opaque; =20 cmd =3D virCommandNew(abs_builddir "/commandhelper-doesnotexist"); + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) =3D=3D 0) goto cleanup; =20 @@ -138,18 +147,23 @@ static int test0(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open * Capturing return status must not log error. */ -static int test1(const void *unused ATTRIBUTE_UNUSED) +static int test1(const void *opaque) { virCommandPtr cmd; int ret =3D -1; int status; + const struct testdata *data =3D opaque; =20 cmd =3D virCommandNew(abs_builddir "/commandhelper-doesnotexist"); + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, &status) < 0) goto cleanup; if (status !=3D EXIT_ENOENT) @@ -167,14 +181,18 @@ static int test1(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Run program (twice), no args, inherit all ENV, keep CWD. * Only stdin/out/err open */ -static int test2(const void *unused ATTRIBUTE_UNUSED) +static int test2(const void *opaque) { - virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); int ret; + virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; + if (data) + virCommandSetTimeout(cmd, data->timeout); =20 if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); @@ -198,13 +216,15 @@ static int test2(const void *unused ATTRIBUTE_UNUSED) return checkoutput("test2", NULL); } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * stdin/out/err + two extra FD open */ -static int test3(const void *unused ATTRIBUTE_UNUSED) +static int test3(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; int newfd1 =3D dup(STDERR_FILENO); int newfd2 =3D dup(STDERR_FILENO); int newfd3 =3D dup(STDERR_FILENO); @@ -214,6 +234,9 @@ static int test3(const void *unused ATTRIBUTE_UNUSED) virCommandPassFD(cmd, newfd3, VIR_COMMAND_PASS_FD_CLOSE_PARENT); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -282,12 +305,16 @@ static int test4(const void *unused ATTRIBUTE_UNUSED) * Run program, no args, inherit filtered ENV, keep CWD. * Only stdin/out/err open */ -static int test5(const void *unused ATTRIBUTE_UNUSED) +static int test5(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; =20 virCommandAddEnvPassCommon(cmd); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -304,13 +331,17 @@ static int test5(const void *unused ATTRIBUTE_UNUSED) * Run program, no args, inherit filtered ENV, keep CWD. * Only stdin/out/err open */ -static int test6(const void *unused ATTRIBUTE_UNUSED) +static int test6(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; =20 virCommandAddEnvPassBlockSUID(cmd, "DISPLAY", NULL); virCommandAddEnvPassBlockSUID(cmd, "DOESNOTEXIST", NULL); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -327,14 +358,18 @@ static int test6(const void *unused ATTRIBUTE_UNUSED) * Run program, no args, inherit filtered ENV, keep CWD. * Only stdin/out/err open */ -static int test7(const void *unused ATTRIBUTE_UNUSED) +static int test7(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; =20 virCommandAddEnvPassCommon(cmd); virCommandAddEnvPassBlockSUID(cmd, "DISPLAY", NULL); virCommandAddEnvPassBlockSUID(cmd, "DOESNOTEXIST", NULL); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -346,19 +381,24 @@ static int test7(const void *unused ATTRIBUTE_UNUSED) return checkoutput("test7", NULL); } =20 + /* * Run program, no args, inherit filtered ENV, keep CWD. * Only stdin/out/err open */ -static int test8(const void *unused ATTRIBUTE_UNUSED) +static int test8(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; =20 virCommandAddEnvString(cmd, "USER=3Dbogus"); virCommandAddEnvString(cmd, "LANG=3DC"); virCommandAddEnvPair(cmd, "USER", "also bogus"); virCommandAddEnvPair(cmd, "USER", "test"); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -375,9 +415,10 @@ static int test8(const void *unused ATTRIBUTE_UNUSED) * Run program, some args, inherit all ENV, keep CWD. * Only stdin/out/err open */ -static int test9(const void *unused ATTRIBUTE_UNUSED) +static int test9(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; const char* const args[] =3D { "arg1", "arg2", NULL }; virBuffer buf =3D VIR_BUFFER_INITIALIZER; =20 @@ -396,6 +437,9 @@ static int test9(const void *unused ATTRIBUTE_UNUSED) return -1; } =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -412,15 +456,19 @@ static int test9(const void *unused ATTRIBUTE_UNUSED) * Run program, some args, inherit all ENV, keep CWD. * Only stdin/out/err open */ -static int test10(const void *unused ATTRIBUTE_UNUSED) +static int test10(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; const char *const args[] =3D { "-version", "-log=3Dbar.log", NULL, }; =20 virCommandAddArgSet(cmd, args); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -432,17 +480,22 @@ static int test10(const void *unused ATTRIBUTE_UNUSED) return checkoutput("test10", NULL); } =20 + /* * Run program, some args, inherit all ENV, keep CWD. * Only stdin/out/err open */ -static int test11(const void *unused ATTRIBUTE_UNUSED) +static int test11(const void *opaque) { const char *args[] =3D { abs_builddir "/commandhelper", "-version", "-log=3Dbar.log", NULL, }; virCommandPtr cmd =3D virCommandNewArgs(args); + const struct testdata *data =3D opaque; + + if (data) + virCommandSetTimeout(cmd, data->timeout); =20 if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); @@ -455,16 +508,21 @@ static int test11(const void *unused ATTRIBUTE_UNUSED) return checkoutput("test11", NULL); } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open. Set stdin data */ -static int test12(const void *unused ATTRIBUTE_UNUSED) +static int test12(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; =20 virCommandSetInputBuffer(cmd, "Hello World\n"); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); virCommandFree(cmd); @@ -476,13 +534,15 @@ static int test12(const void *unused ATTRIBUTE_UNUSED) return checkoutput("test12", NULL); } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open. Set stdin data */ -static int test13(const void *unused ATTRIBUTE_UNUSED) +static int test13(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; char *outactual =3D NULL; const char *outexpect =3D "BEGIN STDOUT\n" "Hello World\n" @@ -492,6 +552,9 @@ static int test13(const void *unused ATTRIBUTE_UNUSED) virCommandSetInputBuffer(cmd, "Hello World\n"); virCommandSetOutputBuffer(cmd, &outactual); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -515,13 +578,15 @@ static int test13(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open. Set stdin data */ -static int test14(const void *unused ATTRIBUTE_UNUSED) +static int test14(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; char *outactual =3D NULL; const char *outexpect =3D "BEGIN STDOUT\n" "Hello World\n" @@ -544,6 +609,9 @@ static int test14(const void *unused ATTRIBUTE_UNUSED) virCommandSetOutputBuffer(cmd, &outactual); virCommandSetErrorBuffer(cmd, &erractual); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -557,6 +625,9 @@ static int test14(const void *unused ATTRIBUTE_UNUSED) virCommandSetInputBuffer(cmd, "Hello World\n"); virCommandSetOutputBuffer(cmd, &jointactual); virCommandSetErrorBuffer(cmd, &jointactual); + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -592,9 +663,10 @@ static int test14(const void *unused ATTRIBUTE_UNUSED) * Run program, no args, inherit all ENV, change CWD. * Only stdin/out/err open */ -static int test15(const void *unused ATTRIBUTE_UNUSED) +static int test15(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; char *cwd =3D NULL; int ret =3D -1; =20 @@ -603,6 +675,9 @@ static int test15(const void *unused ATTRIBUTE_UNUSED) virCommandSetWorkingDirectory(cmd, cwd); virCommandSetUmask(cmd, 002); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -617,17 +692,22 @@ static int test15(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Don't run program; rather, log what would be run. */ -static int test16(const void *unused ATTRIBUTE_UNUSED) +static int test16(const void *opaque) { virCommandPtr cmd =3D virCommandNew("true"); + const struct testdata *data =3D opaque; char *outactual =3D NULL; const char *outexpect =3D "A=3DB C=3D'D E' true F 'G H'"; int ret =3D -1; int fd =3D -1; =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + virCommandAddEnvPair(cmd, "A", "B"); virCommandAddEnvPair(cmd, "C", "D E"); virCommandAddArg(cmd, "F"); @@ -662,16 +742,21 @@ static int test16(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Test string handling when no output is present. */ -static int test17(const void *unused ATTRIBUTE_UNUSED) +static int test17(const void *opaque) { virCommandPtr cmd =3D virCommandNew("true"); + const struct testdata *data =3D opaque; int ret =3D -1; char *outbuf; char *errbuf =3D NULL; =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + virCommandSetOutputBuffer(cmd, &outbuf); if (outbuf !=3D NULL) { puts("buffer not sanitized at registration"); @@ -718,6 +803,7 @@ static int test17(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Run long-running daemon, to ensure no hang. */ @@ -766,15 +852,20 @@ static int test18(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Asynchronously run long-running daemon, to ensure no hang. */ -static int test19(const void *unused ATTRIBUTE_UNUSED) +static int test19(const void *opaque) { + const struct testdata *data =3D opaque; virCommandPtr cmd =3D virCommandNewArgList("sleep", "100", NULL); pid_t pid; int ret =3D -1; =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + alarm(5); if (virCommandRunAsync(cmd, &pid) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); @@ -802,14 +893,16 @@ static int test19(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + /* * Run program, no args, inherit all ENV, keep CWD. * Ignore huge stdin data, to provoke SIGPIPE or EPIPE in parent. */ -static int test20(const void *unused ATTRIBUTE_UNUSED) +static int test20(const void *opaque) { virCommandPtr cmd =3D virCommandNewArgList(abs_builddir "/commandhelpe= r", "--close-stdin", NULL); + const struct testdata *data =3D opaque; char *buf; int ret =3D -1; =20 @@ -825,6 +918,9 @@ static int test20(const void *unused ATTRIBUTE_UNUSED) goto cleanup; virCommandSetInputBuffer(cmd, buf); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRun(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -837,6 +933,7 @@ static int test20(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + static const char *const newenv[] =3D { "PATH=3D/usr/bin:/bin", "HOSTNAME=3Dtest", @@ -849,9 +946,11 @@ static const char *const newenv[] =3D { NULL }; =20 -static int test21(const void *unused ATTRIBUTE_UNUSED) + +static int test21(const void *opaque) { virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; int ret =3D -1; const char *wrbuf =3D "Hello world\n"; char *outbuf =3D NULL, *errbuf =3D NULL; @@ -867,6 +966,9 @@ static int test21(const void *unused ATTRIBUTE_UNUSED) virCommandSetErrorBuffer(cmd, &errbuf); virCommandDoAsyncIO(cmd); =20 + if (data) + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRunAsync(cmd, NULL) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); goto cleanup; @@ -896,14 +998,17 @@ static int test21(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 -static int -test22(const void *unused ATTRIBUTE_UNUSED) + +static int test22(const void *opaque) { int ret =3D -1; virCommandPtr cmd; int status =3D -1; + const struct testdata *data =3D opaque; =20 cmd =3D virCommandNewArgList("/bin/sh", "-c", "exit 3", NULL); + if (data) + virCommandSetTimeout(cmd, data->timeout); =20 if (virCommandRun(cmd, &status) < 0) { printf("Cannot run child %s\n", virGetLastErrorMessage()); @@ -949,8 +1054,7 @@ test22(const void *unused ATTRIBUTE_UNUSED) } =20 =20 -static int -test23(const void *unused ATTRIBUTE_UNUSED) +static int test23(const void *unused ATTRIBUTE_UNUSED) { /* Not strictly a virCommand test, but this is the easiest place * to test this lower-level interface. It takes a double fork to @@ -1006,6 +1110,7 @@ test23(const void *unused ATTRIBUTE_UNUSED) return ret; } =20 + static int test24(const void *unused ATTRIBUTE_UNUSED) { char *pidfile =3D virPidFileBuildPath(abs_builddir, "commandhelper"); @@ -1138,6 +1243,211 @@ static int test25(const void *unused ATTRIBUTE_UNUS= ED) } =20 =20 +/* + * virCommandSetTimeout cannot mix with daemon + */ +static int test26(const void *opaque) +{ + const char *expect_msg =3D + "internal error: daemonized command cannot use virCommandSetTimeou= t"; + virCommandPtr cmd =3D virCommandNew(abs_builddir "/commandhelper"); + const struct testdata *data =3D opaque; + char *pidfile =3D virPidFileBuildPath(abs_builddir, "commandhelper"); + int ret =3D -1; + + if (!data) + goto cleanup; + if (!pidfile) + goto cleanup; + + virCommandSetPidFile(cmd, pidfile); + virCommandDaemonize(cmd); + virCommandSetTimeout(cmd, data->timeout); + + if (virCommandRun(cmd, NULL) !=3D -1 || + STRNEQ(virGetLastErrorMessage(), expect_msg)) { + printf("virCommandSetTimeout mixes with virCommandDaemonize %s\n", + virGetLastErrorMessage()); + goto cleanup; + } + + ret =3D 0; + cleanup: + virCommandFree(cmd); + if (pidfile) + unlink(pidfile); + VIR_FREE(pidfile); + return ret; +} + + +/* + * virCommandRunAsync without async string io when time-out + */ +static int test27(const void *opaque) +{ + int ret =3D -1; + virCommandPtr cmd =3D virCommandNewArgList("sleep", "100", NULL); + pid_t pid; + const struct testdata *data =3D opaque; + if (!data) + goto cleanup; + + virCommandSetTimeout(cmd, data->timeout); + if (virCommandRunAsync(cmd, &pid) < 0) { + printf("Cannot run child %s\n", virGetLastErrorMessage()); + goto cleanup; + } + + if (virCommandWait(cmd, NULL) !=3D -1 || + virCommandGetErr(cmd) !=3D ETIME) { + printf("Timeout doesn't work %s:%d\n", + virGetLastErrorMessage(), + virCommandGetErr(cmd)); + goto cleanup; + } + + ret =3D 0; + cleanup: + virCommandFree(cmd); + return ret; +} + + +/* + * synchronous mode: abort command when time-out + */ +static int test28(const void *opaque) +{ + const char *expect_msg1 =3D "internal error: timeout waiting for child= io"; + const char *expect_msg2 =3D "internal error: invalid use of command AP= I"; + virCommandPtr cmd =3D virCommandNewArgList("sleep", "100", NULL); + const struct testdata *data =3D opaque; + if (!data) { + printf("opaque arg NULL\n"); + virCommandFree(cmd); + return -1; + } + + virCommandSetTimeout(cmd, data->timeout); + + if (virCommandRun(cmd, NULL) !=3D -1 || + virCommandGetErr(cmd) !=3D ETIME || + STRNEQ(virGetLastErrorMessage(), expect_msg1)) { + printf("Timeout doesn't work %s (first)\n", virGetLastErrorMessage= ()); + virCommandFree(cmd); + return -1; + } + + if (virCommandRun(cmd, NULL) !=3D -1 || + virCommandGetErr(cmd) !=3D ETIME || + STRNEQ(virGetLastErrorMessage(), expect_msg2)) { + printf("Timeout doesn't work %s (second)\n", virGetLastErrorMessag= e()); + virCommandFree(cmd); + return -1; + } + + virCommandFree(cmd); + return 0; +} + + +/* + * asynchronous mode with async string io: + * abort command when time-out + */ +static int test29(const void *opaque) +{ + int ret =3D -1; + const char *wrbuf =3D "Hello world\n"; + char *outbuf =3D NULL; + char *errbuf =3D NULL; + const char *expect_msg =3D + "Error while processing command's IO: Timer expired:"; + virCommandPtr cmd =3D virCommandNewArgList("sleep", "100", NULL); + const struct testdata *data =3D opaque; + if (!data) { + printf("opaque arg NULL\n"); + ret =3D -1; + goto cleanup; + } + + virCommandSetTimeout(cmd, data->timeout); + + virCommandSetInputBuffer(cmd, wrbuf); + virCommandSetOutputBuffer(cmd, &outbuf); + virCommandSetErrorBuffer(cmd, &errbuf); + virCommandDoAsyncIO(cmd); + + if (virCommandRunAsync(cmd, NULL) < 0) { + printf("Cannot run child %s\n", virGetLastErrorMessage()); + ret =3D -1; + goto cleanup; + } + + if (virCommandWait(cmd, NULL) !=3D -1 || + virCommandGetErr(cmd) !=3D ETIME || + STRPREFIX(virGetLastErrorMessage(), expect_msg)) { + printf("Timeout doesn't work %s:%d\n", + virGetLastErrorMessage(), + virCommandGetErr(cmd)); + goto cleanup; + } + + ret =3D 0; + cleanup: + VIR_FREE(outbuf); + VIR_FREE(errbuf); + virCommandFree(cmd); + return ret; +} + + +/* + * asynchronous mode only with input buffer + * abort command when time-out + */ +static int test30(const void *opaque) +{ + int ret =3D -1; + const char *wrbuf =3D "Hello world\n"; + const char *expect_msg =3D + "Error while processing command's IO: Timer expired:"; + virCommandPtr cmd =3D virCommandNewArgList("sleep", "10", NULL); + const struct testdata *data =3D opaque; + if (!data) { + printf("opaque arg NULL\n"); + ret =3D -1; + goto cleanup; + } + + virCommandSetTimeout(cmd, data->timeout); + + virCommandSetInputBuffer(cmd, wrbuf); + virCommandDoAsyncIO(cmd); + + if (virCommandRunAsync(cmd, NULL) < 0) { + printf("Cannot run child %s\n", virGetLastErrorMessage()); + ret =3D -1; + goto cleanup; + } + + if (virCommandWait(cmd, NULL) !=3D -1 || + virCommandGetErr(cmd) !=3D ETIME || + STRPREFIX(virGetLastErrorMessage(), expect_msg)) { + printf("Timeout doesn't work %s:%d\n", + virGetLastErrorMessage(), + virCommandGetErr(cmd)); + goto cleanup; + } + + ret =3D 0; + cleanup: + virCommandFree(cmd); + return ret; +} + + static void virCommandThreadWorker(void *opaque) { virCommandTestDataPtr test =3D opaque; @@ -1161,6 +1471,7 @@ static void virCommandThreadWorker(void *opaque) return; } =20 + static void virCommandTestFreeTimer(int timer ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) @@ -1168,6 +1479,7 @@ virCommandTestFreeTimer(int timer ATTRIBUTE_UNUSED, /* nothing to be done here */ } =20 + static int mymain(void) { @@ -1176,6 +1488,7 @@ mymain(void) virCommandTestDataPtr test =3D NULL; int timer =3D -1; int virinitret; + struct testdata data; =20 if (virThreadInitialize() < 0) return EXIT_FAILURE; @@ -1292,6 +1605,54 @@ mymain(void) DO_TEST(test24); DO_TEST(test25); =20 + + /* tests for virCommandSetTimeout */ + /* 1) NO time-out */ + data.timeout =3D 3*1000; + +# define DO_TEST_SET_TIMEOUT(NAME) \ + if (virTestRun("Command Exec " #NAME " test", \ + NAME, &data) < 0) \ + ret =3D -1 + + /* Exclude test4, test18 and test24 for they're in daemon mode. + * Exclude test25, there's no meaning to set timeout + */ + DO_TEST_SET_TIMEOUT(test0); + DO_TEST_SET_TIMEOUT(test1); + DO_TEST_SET_TIMEOUT(test2); + DO_TEST_SET_TIMEOUT(test3); + DO_TEST_SET_TIMEOUT(test5); + DO_TEST_SET_TIMEOUT(test6); + DO_TEST_SET_TIMEOUT(test7); + DO_TEST_SET_TIMEOUT(test8); + DO_TEST_SET_TIMEOUT(test9); + DO_TEST_SET_TIMEOUT(test10); + DO_TEST_SET_TIMEOUT(test11); + DO_TEST_SET_TIMEOUT(test12); + DO_TEST_SET_TIMEOUT(test13); + DO_TEST_SET_TIMEOUT(test14); + DO_TEST_SET_TIMEOUT(test15); + DO_TEST_SET_TIMEOUT(test16); + DO_TEST_SET_TIMEOUT(test17); + DO_TEST_SET_TIMEOUT(test19); + DO_TEST_SET_TIMEOUT(test20); + DO_TEST_SET_TIMEOUT(test21); + DO_TEST_SET_TIMEOUT(test22); + DO_TEST_SET_TIMEOUT(test23); + + /* 2) unsupported usage */ + /* Failure: virCommandSetTimeout mixes with daemon */ + DO_TEST_SET_TIMEOUT(test26); + + /* 3) when time-out */ + data.timeout =3D 100; + DO_TEST_SET_TIMEOUT(test27); /* timeout in async mode without async st= ring io */ + DO_TEST_SET_TIMEOUT(test28); /* timeout in sync mode */ + DO_TEST_SET_TIMEOUT(test29); /* timeout in async mode with async strin= g io */ + DO_TEST_SET_TIMEOUT(test30); /* timeout in async mode with only input = buffer */ + + virMutexLock(&test->lock); if (test->running) { test->quit =3D true; --=20 2.7.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list From nobody Mon May 6 14:28:06 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1534141982284947.227108695789; Sun, 12 Aug 2018 23:33:02 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 03F65307D847; Mon, 13 Aug 2018 06:33:00 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C0530AC307; Mon, 13 Aug 2018 06:32:59 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 7270918037F4; Mon, 13 Aug 2018 06:32:59 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w7D6WZgx012306 for ; Mon, 13 Aug 2018 02:32:35 -0400 Received: by smtp.corp.redhat.com (Postfix) id A067A1754D; Mon, 13 Aug 2018 06:32:35 +0000 (UTC) Received: from mx1.redhat.com (ext-mx09.extmail.prod.ext.phx2.redhat.com [10.5.110.38]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 941194AF for ; Mon, 13 Aug 2018 06:32:33 +0000 (UTC) Received: from mout.gmx.net (mout.gmx.net [212.227.17.20]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 521034E90A for ; Mon, 13 Aug 2018 06:32:31 +0000 (UTC) Received: from localhost.localdomain ([58.56.27.130]) by mail.gmx.com (mrgmx103 [212.227.17.174]) with ESMTPSA (Nemesis) id 0MUpI8-1fNewT0zQy-00Y9hj; Mon, 13 Aug 2018 08:32:30 +0200 From: Shi Lei To: libvir-list@redhat.com Date: Mon, 13 Aug 2018 14:32:19 +0800 Message-Id: <1534141939-5199-3-git-send-email-shilei.massclouds@gmx.com> In-Reply-To: <1534141939-5199-1-git-send-email-shilei.massclouds@gmx.com> References: <1534141939-5199-1-git-send-email-shilei.massclouds@gmx.com> X-Provags-ID: V03:K1:NWOpp++rm/w/x6r4wJmWjVnWGpXsBlZ7v7REzEY3n+rbV8cEL2Q 6coDTENErmyg78ZH0NN+a118B0vpJRvoc2Sun7XDoH0FiSdRcCZ2hCLa/riLVUHV31ey48X 25xlRn099xKh7LsFZSFlNY+abMX4lbZuPnt6wn8SPvycsnTkDQZm6BuDE7Puwx8gCsBJV+4 a0dpvsSq/dwJ8jawC73Jw== X-UI-Out-Filterresults: notjunk:1;V01:K0:UvBqNZ6gcb8=:tkICTdWmgtpr6eBwhjNkYK uWr1JVvX69H94UdeiSUeFYgF6NVBcrmqh40Gz22SAHvNAuN3LbKfJs1FEjJh/n0v0vMJVuZFH VKRAOoY4bDcmmto4r8n88BpjqeOp+9EpgeyTvF9k1We8uDX0VDG56ZPZ8PzcxMmz4vr4wvBGg Erz6798LMz6yIerhM59L5QcLHna1hcRar0F6jwuwFs2q+1Xpfjq4ARKPTZeyBMet4S/m8B/zp u2W2tb2Gv5331NHg768cl8R1e8hiz8Dc0RqM+znKdILpjvkMqRFpBTkoEMq/E+bpH27bf0egA vqzoJl0Y1i9CzvNpC89rbFU7ibG1wSEXVPhKD5h4NTjpVaXfHHDaFiPdK7hZzjINIByrfDoyC RxB6Q6Ws3jhMr228DJqCHPzCjcxbgxdrt8MagbplG61026Cc3HDqLg8Jb4LYlbz5Z+kj50Ol1 zMsZ0rZtWINVDstFT67jvTe+JobVxqEPAIX7T0OlDL5qgCvtgDtnSHGNoC/p1nWichv9vCpDG YGB/zpgFi/83jqb8cyQjTnT2wSu8V/Di+gnlppDG9lE1sv6/QTmyNkbao+fH6/XZV6PDzXX21 rX95sc0zr6lsei04OPDy+xvQVm4sCKtAl7DR6AZhxjvXsJ/jkYQIWQpq0StPHCDITAa5wH/UF QFeVRMLJr8Bm1Cj0Xn8GvaNTv+4qWL5e+NNX7lJa4hc+AdkTYohcdyX01ydkcJHc52pse3OyL mJaNuh7AXjafWbCWn83Hj2xcgwClcPCku5Ndp8wplM1I7JOp9u7QEtZ38/I= X-Greylist: Sender passed SPF test, Sender IP whitelisted by DNSRBL, ACL 212 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Mon, 13 Aug 2018 06:32:31 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Mon, 13 Aug 2018 06:32:31 +0000 (UTC) for IP:'212.227.17.20' DOMAIN:'mout.gmx.net' HELO:'mout.gmx.net' FROM:'shilei.massclouds@gmx.com' RCPT:'' X-RedHat-Spam-Score: -0.7 (FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, SPF_PASS) 212.227.17.20 mout.gmx.net 212.227.17.20 mout.gmx.net X-Scanned-By: MIMEDefang 2.78 on 10.5.110.38 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-loop: libvir-list@redhat.com Cc: Shi Lei Subject: [libvirt] [RFC 2/2] introduce timeout mode in virKModLoad X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.27 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.48]); Mon, 13 Aug 2018 06:33:00 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Shi Lei --- src/Makefile.am | 4 +++- src/util/virkmod.c | 43 ++++++++++++++++++++++++++++++++++++++++++- src/virt-command.conf | 10 ++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/virt-command.conf diff --git a/src/Makefile.am b/src/Makefile.am index 61876cf..e0af476 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -150,7 +150,7 @@ lib_LTLIBRARIES =3D libvirt.la libvirt-qemu.la libvirt-= lxc.la moddir =3D $(libdir)/libvirt/connection-driver =20 confdir =3D $(sysconfdir)/libvirt -conf_DATA +=3D libvirt.conf libvirt-admin.conf +conf_DATA +=3D libvirt.conf libvirt-admin.conf virt-command.conf =20 augeasdir =3D $(datadir)/augeas/lenses =20 @@ -952,6 +952,8 @@ libvirt_nss_la_SOURCES =3D \ util/virbuffer.h \ util/vircommand.c \ util/vircommand.h \ + util/virconf.c \ + util/virconf.h \ util/virerror.c \ util/virerror.h \ util/virfile.c \ diff --git a/src/util/virkmod.c b/src/util/virkmod.c index 9d0375b..99cce4c 100644 --- a/src/util/virkmod.c +++ b/src/util/virkmod.c @@ -24,12 +24,45 @@ #include "virkmod.h" #include "vircommand.h" #include "virstring.h" +#include "virthread.h" +#include "virconf.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +/* + * Kernel module load timeout (milliseconds) + * >0, the number of milliseconds before loading expires + * =3D0, waiting indefinitely + */ +static int virKModLoadTimeout =3D 3*1000; + +static int virKModOnceInit(void) +{ + int timeout =3D 0; + virConfPtr conf =3D NULL; + if (virConfLoadConfig(&conf, "virt-command.conf") < 0) + goto cleanup; + if (virConfGetValueInt(conf, "kmod_load_timeout", &timeout) <=3D 0) + goto cleanup; + + if (timeout >=3D 0) + virKModLoadTimeout =3D timeout; + + cleanup: + virConfFree(conf); + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virKMod) =20 static int doModprobe(const char *opts, const char *module, char **outbuf, char **err= buf) { VIR_AUTOPTR(virCommand) cmd =3D NULL; =20 + if (virKModInitialize() < 0) + return -1; + cmd =3D virCommandNew(MODPROBE); if (opts) virCommandAddArg(cmd, opts); @@ -40,8 +73,16 @@ doModprobe(const char *opts, const char *module, char **= outbuf, char **errbuf) if (errbuf) virCommandSetErrorBuffer(cmd, errbuf); =20 - if (virCommandRun(cmd, NULL) < 0) + if (virKModLoadTimeout > 0) + virCommandSetTimeout(cmd, virKModLoadTimeout); + + if (virCommandRun(cmd, NULL) < 0) { + if (virCommandGetErr(cmd) =3D=3D ETIME) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("%s loading timeout. Check kmod_load_timeout = from virt-command.conf"), + module); return -1; + } =20 return 0; } diff --git a/src/virt-command.conf b/src/virt-command.conf new file mode 100644 index 0000000..d88d24a --- /dev/null +++ b/src/virt-command.conf @@ -0,0 +1,10 @@ +# +# This can be used to set timeout for kernel module loading internally +# +# >0, the number of milliseconds for loading timeout +# =3D0, waiting indefinitely until loading finished, i.e. no timeout. +# +# The default 3 seconds is adequate since most kernel module loading +# with microsecond latency or millisecond latency +# +# kmod_load_timeout =3D 3000 --=20 2.7.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list