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 <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2023-06-08 14:39:50 +09:00
parent f5656ff363
commit 1b0ce9918c
No known key found for this signature in database
GPG Key ID: 640114AF89DE6054
2 changed files with 299 additions and 46 deletions

View File

@ -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);

View File

@ -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.
;;