[PATCH 5/6] virprocess: Passthru error from virProcessRunInForkHelper

Pavel Mores pmores at redhat.com
Thu Mar 19 18:46:25 UTC 2020


On Wed, Mar 18, 2020 at 06:32:15PM +0100, Michal Privoznik wrote:
> When running a function in a forked child, so far the only thing
> we could report is exit status of the child and the error
> message. However, it may be beneficial to the caller to know the
> actual error that happened in the child.
> 
> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
> ---
>  build-aux/syntax-check.mk |  2 +-
>  src/util/virprocess.c     | 51 ++++++++++++++++++++++++++++++++++++---
>  tests/commandtest.c       | 43 +++++++++++++++++++++++++++++++++
>  3 files changed, 91 insertions(+), 5 deletions(-)
> 
> diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
> index 3020921be8..2a38c03ba9 100644
> --- a/build-aux/syntax-check.mk
> +++ b/build-aux/syntax-check.mk
> @@ -1987,7 +1987,7 @@ exclude_file_name_regexp--sc_flags_usage = \
>  exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \
>    ^(src/rpc/gendispatch\.pl$$|tests/)
>  
> -exclude_file_name_regexp--sc_po_check = ^(docs/|src/rpc/gendispatch\.pl$$)
> +exclude_file_name_regexp--sc_po_check = ^(docs/|src/rpc/gendispatch\.pl$$|tests/commandtest.c$$)
>  
>  exclude_file_name_regexp--sc_prohibit_VIR_ERR_NO_MEMORY = \
>    ^(build-aux/syntax-check\.mk|include/libvirt/virterror\.h|src/remote/remote_daemon_dispatch\.c|src/util/virerror\.c|docs/internals/oomtesting\.html\.in)$$
> diff --git a/src/util/virprocess.c b/src/util/virprocess.c
> index 24135070b7..e8674402f9 100644
> --- a/src/util/virprocess.c
> +++ b/src/util/virprocess.c
> @@ -1126,6 +1126,23 @@ virProcessRunInMountNamespace(pid_t pid G_GNUC_UNUSED,
>  
>  
>  #ifndef WIN32
> +typedef struct {
> +    int code;
> +    int domain;
> +    char message[VIR_ERROR_MAX_LENGTH];
> +    virErrorLevel level;
> +    char str1[VIR_ERROR_MAX_LENGTH];
> +    char str2[VIR_ERROR_MAX_LENGTH];
> +    /* str3 doesn't seem to be used. Ignore it. */
> +    int int1;
> +    int int2;
> +} errorData;
> +
> +typedef union {
> +    errorData data;
> +    char bindata[sizeof(errorData)];
> +} errorDataBin;
> +
>  static int
>  virProcessRunInForkHelper(int errfd,
>                            pid_t ppid,
> @@ -1134,9 +1151,19 @@ virProcessRunInForkHelper(int errfd,
>  {
>      if (cb(ppid, opaque) < 0) {
>          virErrorPtr err = virGetLastError();
> +        errorDataBin bin = { 0 };
> +
>          if (err) {
> -            size_t len = strlen(err->message) + 1;
> -            ignore_value(safewrite(errfd, err->message, len));
> +            bin.data.code = err->code;
> +            bin.data.domain = err->domain;
> +            ignore_value(virStrcpy(bin.data.message, err->message, sizeof(bin.data.message)));
> +            bin.data.level = err->level;
> +            ignore_value(virStrcpy(bin.data.str1, err->str1, sizeof(bin.data.str1)));
> +            ignore_value(virStrcpy(bin.data.str2, err->str2, sizeof(bin.data.str2)));
> +            bin.data.int1 = err->int1;
> +            bin.data.int2 = err->int2;
> +
> +            ignore_value(safewrite(errfd, bin.bindata, sizeof(bin)));
>          }
>  
>          return -1;
> @@ -1188,16 +1215,32 @@ virProcessRunInFork(virProcessForkCallback cb,
>      } else {
>          int status;
>          g_autofree char *buf = NULL;
> +        errorDataBin bin;
> +        int nread;
>  
>          VIR_FORCE_CLOSE(errfd[1]);
> -        ignore_value(virFileReadHeaderFD(errfd[0], 1024, &buf));
> +        nread = virFileReadHeaderFD(errfd[0], sizeof(bin), &buf);
>          ret = virProcessWait(child, &status, false);
>          if (ret == 0) {
>              ret = status == EXIT_CANCELED ? -1 : status;
>              if (ret) {
>                  virReportError(VIR_ERR_INTERNAL_ERROR,
>                                 _("child reported (status=%d): %s"),
> -                               status, NULLSTR(buf));
> +                               status, NULLSTR(bin.data.message));
> +
> +                if (nread == sizeof(bin)) {
> +                    memcpy(bin.bindata, buf, sizeof(bin));
> +                    virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__,
> +                                      bin.data.domain,
> +                                      bin.data.code,
> +                                      bin.data.level,
> +                                      bin.data.str1,
> +                                      bin.data.str2,
> +                                      NULL,
> +                                      bin.data.int1,
> +                                      bin.data.int2,
> +                                      "%s", bin.data.message);
> +                }
>              }
>          }
>      }
> diff --git a/tests/commandtest.c b/tests/commandtest.c
> index a64aa9ad33..f4a2c67c05 100644
> --- a/tests/commandtest.c
> +++ b/tests/commandtest.c
> @@ -1257,6 +1257,48 @@ static int test27(const void *unused G_GNUC_UNUSED)
>  }
>  
>  
> +static int
> +test28Callback(pid_t pid G_GNUC_UNUSED,
> +               void *opaque G_GNUC_UNUSED)
> +{
> +    virReportSystemError(ENODATA, "%s", "some error message");
> +    return -1;
> +}
> +
> +
> +static int
> +test28(const void *unused G_GNUC_UNUSED)
> +{
> +    /* Not strictly a virCommand test, but this is the easiest place
> +     * to test this lower-level interface. */
> +    virErrorPtr err;
> +
> +    if (virProcessRunInFork(test28Callback, NULL) != -1) {
> +        fprintf(stderr, "virProcessRunInFork did not fail\n");
> +        return -1;
> +    }
> +
> +    if (!(err = virGetLastError())) {
> +        fprintf(stderr, "Expected error but got nothing\n");
> +        return -1;
> +    }
> +
> +    if (!(err->code == VIR_ERR_SYSTEM_ERROR &&
> +          err->domain == 0 &&
> +          STREQ(err->message, "some error message: No data available") &&
> +          err->level == VIR_ERR_ERROR &&
> +          STREQ(err->str1, "%s") &&
> +          STREQ(err->str2, "some error message: No data available") &&
> +          err->int1 == ENODATA &&
> +          err->int2 == -1)) {
> +        fprintf(stderr, "Unexpected error object\n");
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +
>  static int
>  mymain(void)
>  {
> @@ -1354,6 +1396,7 @@ mymain(void)
>      DO_TEST(test25);
>      DO_TEST(test26);
>      DO_TEST(test27);
> +    DO_TEST(test28);
>  
>      return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
>  }
> -- 
> 2.24.1
> 

Reviewed-by: Pavel Mores <pmores at redhat.com>




More information about the libvir-list mailing list