mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
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:
parent
f5656ff363
commit
1b0ce9918c
@ -647,6 +647,7 @@ static struct foreign_object_vtable es_object_vtable =
|
|||||||
es_object_to_string,
|
es_object_to_string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
static pointer
|
static pointer
|
||||||
es_wrap (scheme *sc, estream_t stream)
|
es_wrap (scheme *sc, estream_t stream)
|
||||||
{
|
{
|
||||||
@ -658,6 +659,7 @@ es_wrap (scheme *sc, estream_t stream)
|
|||||||
box->closed = 0;
|
box->closed = 0;
|
||||||
return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box);
|
return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct es_object_box *
|
static struct es_object_box *
|
||||||
es_unwrap (scheme *sc, pointer object)
|
es_unwrap (scheme *sc, pointer object)
|
||||||
@ -818,22 +820,102 @@ proc_unwrap (scheme *sc, pointer object)
|
|||||||
#define IS_A_proc(SC, X) proc_unwrap (SC, X)
|
#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
|
static pointer
|
||||||
do_process_spawn (scheme *sc, pointer args)
|
do_process_spawn_io (scheme *sc, pointer args)
|
||||||
{
|
{
|
||||||
FFI_PROLOG ();
|
FFI_PROLOG ();
|
||||||
pointer arguments;
|
pointer arguments;
|
||||||
|
char *a_input;
|
||||||
char **argv;
|
char **argv;
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
gnupg_process_t proc = NULL;
|
gnupg_process_t proc = NULL;
|
||||||
estream_t infp;
|
estream_t infp;
|
||||||
estream_t outfp;
|
#ifdef HAVE_W32_SYSTEM
|
||||||
estream_t errfp;
|
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, pointer, arguments, list, args);
|
||||||
FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args);
|
FFI_ARG_OR_RETURN (sc, char *, a_input, string, args);
|
||||||
flags |= (GNUPG_PROCESS_STDIN_PIPE
|
flags = (GNUPG_PROCESS_STDIN_PIPE
|
||||||
| GNUPG_PROCESS_STDOUT_PIPE
|
| GNUPG_PROCESS_STDOUT_PIPE
|
||||||
| GNUPG_PROCESS_STDERR_PIPE);
|
| GNUPG_PROCESS_STDERR_PIPE);
|
||||||
FFI_ARGS_DONE_OR_RETURN (sc, args);
|
FFI_ARGS_DONE_OR_RETURN (sc, args);
|
||||||
@ -857,18 +939,208 @@ do_process_spawn (scheme *sc, pointer args)
|
|||||||
|
|
||||||
err = gnupg_process_spawn (argv[0], (const char **) &argv[1],
|
err = gnupg_process_spawn (argv[0], (const char **) &argv[1],
|
||||||
flags, NULL, NULL, &proc);
|
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);
|
xfree (argv);
|
||||||
#define IMP(A, B) \
|
FFI_RETURN_ERR (sc, err);
|
||||||
_cons (sc, proc_wrap (sc, (A)), (B), 1)
|
}
|
||||||
#define IMS(A, B) \
|
|
||||||
_cons (sc, es_wrap (sc, (A)), (B), 1)
|
#ifdef HAVE_W32_SYSTEM
|
||||||
FFI_RETURN_POINTER (sc, IMS (infp,
|
err = gnupg_process_ctl (proc, GNUPG_PROCESS_GET_HANDLES,
|
||||||
IMS (outfp,
|
NULL, &out_hd, &err_hd);
|
||||||
IMS (errfp,
|
#else
|
||||||
IMP (proc, sc->NIL)))));
|
err = gnupg_process_get_fds (proc, 0, NULL, &out_fd, &err_fd);
|
||||||
#undef IMS
|
#endif
|
||||||
#undef IMC
|
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);
|
||||||
|
|
||||||
|
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
|
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, pipe);
|
||||||
ffi_define_function (sc, inbound_pipe);
|
ffi_define_function (sc, inbound_pipe);
|
||||||
ffi_define_function (sc, outbound_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_spawn_fd);
|
||||||
ffi_define_function (sc, process_wait);
|
ffi_define_function (sc, process_wait);
|
||||||
|
|
||||||
|
@ -92,24 +92,16 @@
|
|||||||
(define :stdin car)
|
(define :stdin car)
|
||||||
(define :stdout cadr)
|
(define :stdout cadr)
|
||||||
(define :stderr caddr)
|
(define :stderr caddr)
|
||||||
(define :proc cadddr)
|
|
||||||
|
|
||||||
(define (call-with-io what in)
|
(define (call-with-io what in)
|
||||||
(let ((h (process-spawn what 0)))
|
(let ((proc-result (process-spawn-io what in)))
|
||||||
(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)
|
(if (> (*verbose*) 2)
|
||||||
(info "Child" (:proc h) "returned:"
|
(info "Child #proc returned:"
|
||||||
`((command ,(stringify what))
|
`((command ,(stringify what))
|
||||||
(status ,result)
|
(status ,(car proc-result))
|
||||||
(stdout ,out)
|
(stdout ,(cadr proc-result))
|
||||||
(stderr ,err))))
|
(stderr ,(caddr proc-result)))))
|
||||||
(list result out err))))
|
proc-result))
|
||||||
|
|
||||||
;; Accessor function for the results of 'call-with-io'. ':stdout' and
|
;; Accessor function for the results of 'call-with-io'. ':stdout' and
|
||||||
;; ':stderr' can also be used.
|
;; ':stderr' can also be used.
|
||||||
@ -128,17 +120,6 @@
|
|||||||
(:stdout result)
|
(:stdout result)
|
||||||
(throw (:stderr 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.
|
;; File management.
|
||||||
;;
|
;;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user