diff --git a/tests/commandhelper.c b/tests/commandhelper.c index 32ebeeaef26e704a60369c3803eac49d3bde7b73..1312f3ee527c82c78e22d7e76e5d9a8c07e010d7 100644 --- a/tests/commandhelper.c +++ b/tests/commandhelper.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "internal.h" #define NO_LIBVIRT @@ -62,13 +63,27 @@ int main(int argc, char **argv) { char *cwd; FILE *log = fopen(abs_builddir "/commandhelper.log", "w"); int ret = EXIT_FAILURE; + int readfds[3] = { STDIN_FILENO, }; + int numreadfds = 1; + struct pollfd fds[3]; + int numpollfds = 0; + char *buffers[3] = {NULL, NULL, NULL}; + size_t buflen[3] = {0, 0, 0}; + char c; if (!log) return ret; - for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { fprintf(log, "ARG:%s\n", argv[i]); + if (STREQ(argv[i - 1], "--readfd") && + sscanf(argv[i], "%u%c", &readfds[numreadfds++], &c) != 1) { + printf("Could not parse fd %s\n", argv[i]); + goto cleanup; + } + } + origenv = environ; n = 0; while (*origenv != NULL) { @@ -134,15 +149,56 @@ int main(int argc, char **argv) { fprintf(stderr, "BEGIN STDERR\n"); fflush(stderr); + for (i = 0; i < numreadfds; i++) { + fds[numpollfds].fd = readfds[i]; + fds[numpollfds].events = POLLIN; + fds[numpollfds].revents = 0; + numpollfds++; + } + for (;;) { - got = read(STDIN_FILENO, buf, sizeof(buf)); - if (got < 0) + unsigned ctr = 0; + + if (poll(fds, numpollfds, -1) < 0) { + printf("poll failed: %s\n", strerror(errno)); goto cleanup; - if (got == 0) + } + + for (i = 0; i < numpollfds; i++) { + if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) { + fds[i].revents = 0; + + got = read(fds[i].fd, buf, sizeof(buf)); + if (got < 0) + goto cleanup; + if (got == 0) { + /* do not want to hear from this fd anymore */ + fds[i].events = 0; + } else { + buffers[i] = realloc(buffers[i], buflen[i] + got); + if (!buf[i]) { + fprintf(stdout, "Out of memory!\n"); + goto cleanup; + } + memcpy(buffers[i] + buflen[i], buf, got); + buflen[i] += got; + } + } + } + for (i = 0; i < numpollfds; i++) { + if (fds[i].events) { + ctr++; + break; + } + } + if (ctr == 0) break; - if (write(STDOUT_FILENO, buf, got) != got) + } + + for (i = 0; i < numpollfds; i++) { + if (write(STDOUT_FILENO, buffers[i], buflen[i]) != buflen[i]) goto cleanup; - if (write(STDERR_FILENO, buf, got) != got) + if (write(STDERR_FILENO, buffers[i], buflen[i]) != buflen[i]) goto cleanup; } @@ -154,6 +210,8 @@ int main(int argc, char **argv) { ret = EXIT_SUCCESS; cleanup: + for (i = 0; i < ARRAY_CARDINALITY(buffers); i++) + free(buffers[i]); fclose(log); free(newenv); return ret; diff --git a/tests/commandtest.c b/tests/commandtest.c index ce0832fb0c67991588dd2799c02db73f4f16471d..991c0572b072ec26e601145e9103b2c45f3f6bec 100644 --- a/tests/commandtest.c +++ b/tests/commandtest.c @@ -1139,6 +1139,118 @@ static int test26(const void *unused ATTRIBUTE_UNUSED) return ret; } +static int test27(const void *unused ATTRIBUTE_UNUSED) +{ + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + int pipe1[2]; + int pipe2[2]; + int ret = -1; + size_t buflen = 1024 * 128; + char *buffer0 = NULL; + char *buffer1 = NULL; + char *buffer2 = NULL; + char *outactual = NULL; + char *erractual = NULL; + char *outexpect = NULL; +# define TEST27_OUTEXPECT_TEMP "BEGIN STDOUT\n" \ + "%s%s%s" \ + "END STDOUT\n" + char *errexpect = NULL; +# define TEST27_ERREXPECT_TEMP "BEGIN STDERR\n" \ + "%s%s%s" \ + "END STDERR\n" + + if (VIR_ALLOC_N(buffer0, buflen) < 0 || + VIR_ALLOC_N(buffer1, buflen) < 0 || + VIR_ALLOC_N(buffer2, buflen) < 0) + goto cleanup; + + memset(buffer0, 'H', buflen - 2); + buffer0[buflen - 2] = '\n'; + buffer0[buflen - 1] = 0; + + memset(buffer1, '1', buflen - 2); + buffer1[buflen - 2] = '\n'; + buffer1[buflen - 1] = 0; + + memset(buffer2, '2', buflen - 2); + buffer2[buflen - 2] = '\n'; + buffer2[buflen - 1] = 0; + + if (virAsprintf(&outexpect, TEST27_OUTEXPECT_TEMP, + buffer0, buffer1, buffer2) < 0 || + virAsprintf(&errexpect, TEST27_ERREXPECT_TEMP, + buffer0, buffer1, buffer2) < 0) { + printf("Could not virAsprintf expected output\n"); + goto cleanup; + } + + if (pipe(pipe1) < 0 || pipe(pipe2) < 0) { + printf("Could not create pipe: %s\n", strerror(errno)); + goto cleanup; + } + + if (virCommandSetSendBuffer(cmd, pipe1[1], + (unsigned char *)buffer1, buflen - 1) < 0 || + virCommandSetSendBuffer(cmd, pipe2[1], + (unsigned char *)buffer2, buflen - 1) < 0) { + printf("Could not set send buffers\n"); + goto cleanup; + } + pipe1[1] = 0; + pipe2[1] = 0; + buffer1 = NULL; + buffer2 = NULL; + + virCommandAddArg(cmd, "--readfd"); + virCommandAddArgFormat(cmd, "%d", pipe1[0]); + virCommandPassFD(cmd, pipe1[0], 0); + + virCommandAddArg(cmd, "--readfd"); + virCommandAddArgFormat(cmd, "%d", pipe2[0]); + virCommandPassFD(cmd, pipe2[0], 0); + + virCommandSetInputBuffer(cmd, buffer0); + virCommandSetOutputBuffer(cmd, &outactual); + virCommandSetErrorBuffer(cmd, &erractual); + + if (virCommandRun(cmd, NULL) < 0) { + printf("Cannot run child %s\n", virGetLastErrorMessage()); + goto cleanup; + } + + virCommandFree(cmd); + + if (!outactual || !erractual) + goto cleanup; + + if (STRNEQ(outactual, outexpect)) { + virTestDifference(stderr, outexpect, outactual); + goto cleanup; + } + if (STRNEQ(erractual, errexpect)) { + virTestDifference(stderr, errexpect, erractual); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FORCE_CLOSE(pipe1[0]); + VIR_FORCE_CLOSE(pipe2[0]); + VIR_FORCE_CLOSE(pipe1[1]); + VIR_FORCE_CLOSE(pipe2[1]); + VIR_FREE(buffer0); + VIR_FREE(buffer1); + VIR_FREE(buffer2); + VIR_FREE(outactual); + VIR_FREE(erractual); + VIR_FREE(outexpect); + VIR_FREE(errexpect); + + return ret; +} + static void virCommandThreadWorker(void *opaque) { virCommandTestDataPtr test = opaque; @@ -1292,6 +1404,7 @@ mymain(void) DO_TEST(test23); DO_TEST(test25); DO_TEST(test26); + DO_TEST(test27); virMutexLock(&test->lock); if (test->running) {