diff --git a/tests/gpgscm/ffi.c b/tests/gpgscm/ffi.c index b46d5cb61..cac052138 100644 --- a/tests/gpgscm/ffi.c +++ b/tests/gpgscm/ffi.c @@ -647,6 +647,7 @@ static struct foreign_object_vtable es_object_vtable = es_object_to_string, }; +#if 0 static pointer es_wrap (scheme *sc, estream_t stream) { @@ -658,6 +659,7 @@ es_wrap (scheme *sc, estream_t stream) box->closed = 0; return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box); } +#endif static struct es_object_box * es_unwrap (scheme *sc, pointer object) @@ -818,24 +820,104 @@ proc_unwrap (scheme *sc, pointer object) #define IS_A_proc(SC, X) proc_unwrap (SC, X) +#define SPAWN_IO_BUFSIZE 4096 + +#ifdef HAVE_W32_SYSTEM +struct rfp { + HANDLE hd; + char *buf; + size_t len; + off_t off; +}; + +static DWORD __attribute__((stdcall)) +read_from_pipe (void *arg) +{ + struct rfp *rfp = arg; + DWORD bytes_read; + + if (rfp->hd == INVALID_HANDLE_VALUE) + goto errout; + + while (1) + { + if (!ReadFile (rfp->hd, rfp->buf + rfp->off, rfp->len - rfp->off, + &bytes_read, NULL)) + { + DWORD ec = GetLastError (); + + if (ec == ERROR_BROKEN_PIPE) + { + CloseHandle (rfp->hd); + rfp->hd = INVALID_HANDLE_VALUE; + break; + } + + goto errout; + } + + if (bytes_read == 0) + /* It may occur, when it writes WriteFile with zero-byte on + the other end of the pipe. */ + continue; + else + { + rfp->off += bytes_read; + if (rfp->off == rfp->len) + { + rfp->len += SPAWN_IO_BUFSIZE; + rfp->buf = xtryrealloc (rfp->buf, rfp->len); + if (rfp->buf == NULL) + goto errout; + } + } + } + + return 0; + + errout: + if (rfp->hd != INVALID_HANDLE_VALUE) + { + CloseHandle (rfp->hd); + rfp->hd = INVALID_HANDLE_VALUE; + } + xfree (rfp->buf); + rfp->buf = NULL; + return 1; +} +#endif + + static pointer -do_process_spawn (scheme *sc, pointer args) +do_process_spawn_io (scheme *sc, pointer args) { FFI_PROLOG (); pointer arguments; + char *a_input; char **argv; size_t len; unsigned int flags; gnupg_process_t proc = NULL; estream_t infp; - estream_t outfp; - estream_t errfp; +#ifdef HAVE_W32_SYSTEM + HANDLE out_hd, err_hd; +#else + int out_fd, err_fd; +#endif + char *out_string = NULL; + char *err_string = NULL; + size_t out_len = SPAWN_IO_BUFSIZE; + size_t err_len = SPAWN_IO_BUFSIZE; + off_t out_off = 0; + off_t err_off = 0; + int retcode = -1; + pointer p0, p1, p2; FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args); - FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args); - flags |= (GNUPG_PROCESS_STDIN_PIPE - | GNUPG_PROCESS_STDOUT_PIPE - | GNUPG_PROCESS_STDERR_PIPE); + FFI_ARG_OR_RETURN (sc, char *, a_input, string, args); + flags = (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE); FFI_ARGS_DONE_OR_RETURN (sc, args); err = ffi_list2argv (sc, arguments, &argv, &len); @@ -857,18 +939,208 @@ do_process_spawn (scheme *sc, pointer args) err = gnupg_process_spawn (argv[0], (const char **) &argv[1], flags, NULL, NULL, &proc); - err = gnupg_process_get_streams (proc, 0, &infp, &outfp, &errfp); + err = gnupg_process_get_streams (proc, 0, &infp, NULL, NULL); + + err = es_write (infp, a_input, strlen (a_input), NULL); + es_fclose (infp); + if (err) + { + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); + } + +#ifdef HAVE_W32_SYSTEM + err = gnupg_process_ctl (proc, GNUPG_PROCESS_GET_HANDLES, + NULL, &out_hd, &err_hd); +#else + err = gnupg_process_get_fds (proc, 0, NULL, &out_fd, &err_fd); +#endif + if (err) + { + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); + } + + out_string = xtrymalloc (out_len); + if (out_string == NULL) + goto errout; + + err_string = xtrymalloc (err_len); + if (err_string == NULL) + goto errout; + +#ifdef HAVE_W32_SYSTEM + { + HANDLE h_thread_rfp_err; + struct rfp rfp_out; + struct rfp rfp_err; + DWORD thread_exit_code; + + rfp_err.hd = err_hd; + rfp_err.buf = err_string; + rfp_err.len = err_len; + rfp_err.off = 0; + err_hd = INVALID_HANDLE_VALUE; + err_string = NULL; + + h_thread_rfp_err = CreateThread (NULL, 0, read_from_pipe, (void *)&rfp_err, + 0, NULL); + if (h_thread_rfp_err == NULL) + { + xfree (rfp_err.buf); + CloseHandle (rfp_err.hd); + goto errout; + } + + rfp_out.hd = out_hd; + rfp_out.buf = out_string; + rfp_out.len = out_len; + rfp_out.off = 0; + out_hd = INVALID_HANDLE_VALUE; + out_string = NULL; + + if (read_from_pipe (&rfp_out)) + { + CloseHandle (h_thread_rfp_err); + xfree (rfp_err.buf); + goto errout; + } + + out_string = rfp_out.buf; + out_off = rfp_out.off; + + WaitForSingleObject (h_thread_rfp_err, INFINITE); + GetExitCodeThread (h_thread_rfp_err, &thread_exit_code); + CloseHandle (h_thread_rfp_err); + if (thread_exit_code) + goto errout; + + err_string = rfp_err.buf; + err_off = rfp_err.off; + } +#else + { + fd_set read_fdset; + ssize_t bytes_read; + + if (out_fd < 0) + goto errout; + + if (err_fd < 0) + goto errout; + + FD_ZERO (&read_fdset); + + while (1) + { + int nfd; + int ret; + + if (out_fd >= 0) + FD_SET (out_fd, &read_fdset); + + if (err_fd >= 0) + FD_SET (err_fd, &read_fdset); + + if (out_fd > err_fd) + nfd = out_fd; + else + nfd = err_fd; + + if (nfd == -1) + break; + + ret = select (nfd+1, &read_fdset, NULL, NULL, NULL); + if (ret < 0) + break; + + if (FD_ISSET (out_fd, &read_fdset)) + { + bytes_read = read (out_fd, out_string + out_off, + out_len - out_off); + if (bytes_read == 0) + { + close (out_fd); + out_fd = -1; + } + else if (bytes_read < 0) + goto errout; + else + { + out_off += bytes_read; + if (out_off == out_len) + { + out_len += SPAWN_IO_BUFSIZE; + out_string = xtryrealloc (out_string, out_len); + if (out_string == NULL) + goto errout; + } + } + } + + if (FD_ISSET (err_fd, &read_fdset)) + { + bytes_read = read (err_fd, err_string + err_off, + err_len - err_off); + if (bytes_read == 0) + { + close (err_fd); + err_fd = -1; + } + else if (bytes_read < 0) + goto errout; + else + { + err_off += bytes_read; + if (err_off == err_len) + { + err_len += SPAWN_IO_BUFSIZE; + err_string = xtryrealloc (err_string, err_len); + if (err_string == NULL) + goto errout; + } + } + } + } + } +#endif + + err = gnupg_process_wait (proc, 1); + if (!err) + err = gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &retcode); + + gnupg_process_release (proc); xfree (argv); -#define IMP(A, B) \ - _cons (sc, proc_wrap (sc, (A)), (B), 1) -#define IMS(A, B) \ - _cons (sc, es_wrap (sc, (A)), (B), 1) - FFI_RETURN_POINTER (sc, IMS (infp, - IMS (outfp, - IMS (errfp, - IMP (proc, sc->NIL))))); -#undef IMS -#undef IMC + + p0 = sc->vptr->mk_integer (sc, (unsigned long)retcode); + p1 = sc->vptr->mk_counted_string (sc, out_string, out_off); + p2 = sc->vptr->mk_counted_string (sc, err_string, err_off); + + xfree (out_string); + xfree (err_string); + + FFI_RETURN_POINTER (sc, _cons (sc, p0, + _cons (sc, p1, + _cons (sc, p2, sc->NIL, 1), 1), 1)); + errout: + xfree (out_string); + xfree (err_string); +#ifdef HAVE_W32_SYSTEM + if (out_hd != INVALID_HANDLE_VALUE) + CloseHandle (out_hd); + if (err_hd != INVALID_HANDLE_VALUE) + CloseHandle (err_hd); +#else + if (out_fd >= 0) + close (out_fd); + if (err_fd >= 0) + close (err_fd); +#endif + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); } static void @@ -1410,7 +1682,7 @@ ffi_init (scheme *sc, const char *argv0, const char *scriptname, ffi_define_function (sc, pipe); ffi_define_function (sc, inbound_pipe); ffi_define_function (sc, outbound_pipe); - ffi_define_function (sc, process_spawn); + ffi_define_function (sc, process_spawn_io); ffi_define_function (sc, process_spawn_fd); ffi_define_function (sc, process_wait); diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index 6a11e55f1..1e6d7fea0 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -92,24 +92,16 @@ (define :stdin car) (define :stdout cadr) (define :stderr caddr) -(define :proc cadddr) (define (call-with-io what in) - (let ((h (process-spawn what 0))) - (es-write (:stdin h) in) - (es-fclose (:stdin h)) - (let* ((out (es-read-all (:stdout h))) - (err (es-read-all (:stderr h))) - (result (process-wait (:proc h) #t))) - (es-fclose (:stdout h)) - (es-fclose (:stderr h)) - (if (> (*verbose*) 2) - (info "Child" (:proc h) "returned:" - `((command ,(stringify what)) - (status ,result) - (stdout ,out) - (stderr ,err)))) - (list result out err)))) + (let ((proc-result (process-spawn-io what in))) + (if (> (*verbose*) 2) + (info "Child #proc returned:" + `((command ,(stringify what)) + (status ,(car proc-result)) + (stdout ,(cadr proc-result)) + (stderr ,(caddr proc-result))))) + proc-result)) ;; Accessor function for the results of 'call-with-io'. ':stdout' and ;; ':stderr' can also be used. @@ -128,17 +120,6 @@ (:stdout result) (throw (:stderr result))))) -;; -;; estream helpers. -;; - -(define (es-read-all stream) - (let loop - ((acc "")) - (if (es-feof stream) - acc - (loop (string-append acc (es-read stream 4096)))))) - ;; ;; File management. ;;