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,
|
||||
};
|
||||
|
||||
#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);
|
||||
|
||||
|
@ -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.
|
||||
;;
|
||||
|
Loading…
x
Reference in New Issue
Block a user