aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2023-06-08 05:39:50 +0000
committerNIIBE Yutaka <[email protected]>2023-06-08 05:39:50 +0000
commit1b0ce9918c321a5060fb7c59a234ab683187e8c1 (patch)
treeec7b4e3edeb030c6d5ff751cab445c7488b41a3c /tests
parentkbx: Fix datastream_thread and use the data pipe. (diff)
downloadgnupg-1b0ce9918c321a5060fb7c59a234ab683187e8c1.tar.gz
gnupg-1b0ce9918c321a5060fb7c59a234ab683187e8c1.zip
tests: Fix call-with-io deadlock.
* tests/gpgscm/ffi.c (es_wrap): Ifdef-out. [HAVE_W32_SYSTEM] (read_from_pipe): New. (do_process_spawn_io): Rename from do_process_spawn. Do I/O with no deadlock. * tests/gpgscm/tests.scm (call-with-io): Use process-spawn-io. (es-read-all): Remove. -- GnuPG-bug-id: 6523 Signed-off-by: NIIBE Yutaka <[email protected]>
Diffstat (limited to 'tests')
-rw-r--r--tests/gpgscm/ffi.c310
-rw-r--r--tests/gpgscm/tests.scm35
2 files changed, 299 insertions, 46 deletions
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.
@@ -129,17 +121,6 @@
(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.
;;
(define (file-exists? name)