From: Simon Zolin <szo...@parallels.com> Create anonymous pipe that can be passed as a stdin/stdout/stderr handle to a child process spawned using forthcoming guest-file-exec command. Working with a pipe is done using the existing interfaces guest-file-*.
Signed-off-by: Simon Zolin <szo...@parallels.com> Signed-off-by: Denis V. Lunev <d...@openvz.org> CC: Michael Roth <mdr...@linux.vnet.ibm.com> --- qga/commands-win32.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 8fb29fc..4160c2b 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -37,6 +37,7 @@ typedef struct GuestFileHandle { int64_t id; HANDLE fh; + HANDLE pipe_other_end_fd; /* if set, it's a pipe fd of the other end. */ QTAILQ_ENTRY(GuestFileHandle) next; } GuestFileHandle; @@ -84,7 +85,8 @@ static OpenFlags *find_open_flag(const char *mode_str) return NULL; } -static int64_t guest_file_handle_add(HANDLE fh, Error **errp) +static int64_t guest_file_handle_add(HANDLE fh, HANDLE pipe_other_end_fd, + Error **errp) { GuestFileHandle *gfh; int64_t handle; @@ -96,6 +98,7 @@ static int64_t guest_file_handle_add(HANDLE fh, Error **errp) gfh = g_malloc0(sizeof(GuestFileHandle)); gfh->id = handle; gfh->fh = fh; + gfh->pipe_other_end_fd = pipe_other_end_fd; QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); return handle; @@ -143,7 +146,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, return -1; } - fd = guest_file_handle_add(fh, errp); + fd = guest_file_handle_add(fh, INVALID_HANDLE_VALUE, errp); if (fd < 0) { CloseHandle(&fh); error_setg(errp, "failed to add handle to qmp handle table"); @@ -154,9 +157,70 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, return fd; } +/* Create an anonymous pipe. + * Emulate non-blocking UNIX behavior using PIPE_NOWAIT flag: ReadFile() + * will fail with ERROR_NO_DATA; WriteFile() will return 0 bytes written. */ int64_t qmp_guest_pipe_open(const char *mode, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); + HANDLE fd[2]; + int this_end, other_end; + DWORD pipemode; + int64_t handle; + SECURITY_ATTRIBUTES sa = { + sizeof(SECURITY_ATTRIBUTES), NULL, 0 + }; + + slog("guest-pipe-open called"); + + if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0') { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "Only \"r\" or \"w\" are the valid modes to open a pipe"); + return -1; + } + + if (!CreatePipe(&fd[0], &fd[1], &sa, 0)) { + error_set_errno(errp, GetLastError(), QERR_QGA_COMMAND_FAILED, + "CreatePipe() failed"); + return -1; + } + + this_end = (mode[0] == 'w'); + other_end = !this_end; + + pipemode = PIPE_READMODE_BYTE | PIPE_NOWAIT; + if (!SetNamedPipeHandleState(fd[this_end], &pipemode, NULL, NULL)) { + error_set_errno(errp, GetLastError(), QERR_QGA_COMMAND_FAILED, + "SetNamedPipeHandleState() failed"); + goto fail; + } + + handle = guest_file_handle_add(fd[this_end], fd[other_end], errp); + if (handle == -1) { + goto fail; + } + + slog("guest-pipe-open: handle: %" PRId64, handle); + + return handle; + +fail: + CloseHandle(fd[0]); + CloseHandle(fd[1]); + + return -1; +} + +static int guest_pipe_close_other_end(GuestFileHandle *gfh) +{ + if (gfh->pipe_other_end_fd != INVALID_HANDLE_VALUE) { + + if (!CloseHandle(gfh->pipe_other_end_fd)) { + return 1; + } + + gfh->pipe_other_end_fd = INVALID_HANDLE_VALUE; + } + return 0; } @@ -168,6 +232,12 @@ void qmp_guest_file_close(int64_t handle, Error **errp) if (gfh == NULL) { return; } + + if (guest_pipe_close_other_end(gfh) != 0) { + error_setg_errno(errp, GetLastError(), "failed to close pipe handle"); + return; + } + ret = CloseHandle(gfh->fh); if (!ret) { error_setg_errno(errp, GetLastError(), "failed close handle"); @@ -707,7 +777,7 @@ GList *ga_command_blacklist_init(GList *blacklist) "guest-suspend-hybrid", "guest-network-get-interfaces", "guest-get-vcpus", "guest-set-vcpus", "guest-fsfreeze-freeze-list", "guest-get-fsinfo", - "guest-fstrim", "guest-pipe-open", "guest-exec-status", + "guest-fstrim", "guest-exec-status", "guest-exec", NULL}; char **p = (char **)list_unsupported; -- 1.9.1