From 15330f36a72a98db06e7e3e8ea2204645a5c470b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 20 Aug 2010 12:18:38 +0000 Subject: Reworked the posix and w32 exechelpers. --- common/ChangeLog | 28 +++ common/estream.c | 471 ++++++++++++++++++++++++++++++++++++++++++++---- common/estream.h | 29 +++ common/exechelp-posix.c | 140 ++++++++++---- common/exechelp-w32.c | 221 +++++++++++++++-------- common/exechelp-w32ce.c | 14 +- common/exechelp.h | 39 ++-- 7 files changed, 776 insertions(+), 166 deletions(-) (limited to 'common') diff --git a/common/ChangeLog b/common/ChangeLog index f53a19293..1fdd7de5d 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,31 @@ +2010-08-20 Werner Koch + + * exechelp-w32.c (create_inheritable_pipe): Change arg to HANDLE. + + * estream.h (es_sysopen_t): New. + * estream.c (es_func_w32_create, es_func_w32_read) + (es_func_w32_write, es_func_w32_seek, es_func_w32_destroy) + (estream_functions_w32, estream_cookie_fd): New. Only for W32. + (es_sysopen, es_sysopen_nc): New. + (do_w32open, do_sysopen): New. + (es_syshd, es_syshd_unlocked): New. + (struct estream_internal): Replace filed FD by SYSHD. + (es_initialize): Clear SYSHD_VALID. + (map_w32_to_errno): New. + (es_get_fd): Remove. + (es_fileno_unlocked): Re-implement using es_syshd. + (es_initialize, es_create): Replace arg FD by SYSHD. + (es_fopen, es_mopen, es_fopenmem, do_fdopen, do_fpopen) + (es_tmpfile): Use SYSHD instead of FD. + (es_destroy): Rename to do_close. + +2010-08-19 Werner Koch + + * exechelp-posix.c (create_pipe_and_estream): New. + (gnupg_spawn_process): Rework this function and its calling + convention; it is not used anyway. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + 2010-08-18 Werner Koch * logging.c (writen): Add arg IS_SOCKET. diff --git a/common/estream.c b/common/estream.c index ea5d4d00f..00f40d24d 100644 --- a/common/estream.c +++ b/common/estream.c @@ -126,9 +126,9 @@ int _setmode (int handle, int mode); #endif #ifdef HAVE_W32_SYSTEM -# define IS_INVALID_FD(a) ((void*)(a) == (void*)(-1)) +# define IS_INVALID_FD(a) ((void*)(a) == (void*)(-1)) /* ?? FIXME. */ #else -# define IS_INVALID_FD(a) ((a) == -1) +# define IS_INVALID_FD(a) ((a) == -1) #endif @@ -197,6 +197,7 @@ dummy_mutex_call_int (estream_mutex_t mutex) # define ESTREAM_SYS_YIELD() do { } while (0) #endif + /* Misc definitions. */ #define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR) @@ -218,7 +219,7 @@ struct estream_internal es_cookie_seek_function_t func_seek; es_cookie_close_function_t func_close; int strategy; - int fd; /* Value to return by es_fileno(). */ + es_syshd_t syshd; /* A copy of the sytem handle. */ struct { unsigned int err: 1; @@ -317,7 +318,39 @@ mem_free (void *p) free (p); } +#ifdef HAVE_W32_SYSTEM +static int +map_w32_to_errno (DWORD w32_err) +{ + switch (w32_err) + { + case 0: + return 0; + + case ERROR_FILE_NOT_FOUND: + return ENOENT; + + case ERROR_PATH_NOT_FOUND: + return ENOENT; + + case ERROR_ACCESS_DENIED: + return EPERM; + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_BLOCK: + return EINVAL; + + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + + case ERROR_NO_DATA: + return EPIPE; + + default: + return EIO; + } +} +#endif /*HAVE_W32_SYSTEM*/ /* * List manipulation. @@ -744,7 +777,7 @@ static es_cookie_io_functions_t estream_functions_mem = -/* Implementation of fd I/O. */ +/* Implementation of file descriptor based I/O. */ /* Cookie for fd objects. */ typedef struct estream_cookie_fd @@ -886,6 +919,217 @@ static es_cookie_io_functions_t estream_functions_fd = + +#ifdef HAVE_W32_SYSTEM +/* Implementation of W32 handle based I/O. */ + +/* Cookie for fd objects. */ +typedef struct estream_cookie_w32 +{ + HANDLE hd; /* The handle we are using for actual output. */ + int no_close; /* If set we won't close the handle. */ +} *estream_cookie_w32_t; + + +/* Create function for w32 handle objects. */ +static int +es_func_w32_create (void **cookie, HANDLE hd, + unsigned int modeflags, int no_close) +{ + estream_cookie_w32_t w32_cookie; + int err; + + w32_cookie = mem_alloc (sizeof (*w32_cookie)); + if (!w32_cookie) + err = -1; + else + { + /* CR/LF translations are not supported when using the bare W32 + API. If that is really required we need to implemented that + in the upper layer. */ + (void)modeflags; + + w32_cookie->hd = hd; + w32_cookie->no_close = no_close; + *cookie = w32_cookie; + err = 0; + } + + return err; +} + +/* Read function for W32 handle objects. */ +static ssize_t +es_func_w32_read (void *cookie, void *buffer, size_t size) +{ + estream_cookie_w32_t w32_cookie = cookie; + ssize_t bytes_read; + + if (w32_cookie->hd == INVALID_HANDLE_VALUE) + { + ESTREAM_SYS_YIELD (); + bytes_read = 0; + } + else + { + do + { +#ifdef HAVE_PTH + /* Note: Our pth_read actually uses HANDLE! */ + bytes_read = pth_read ((int)w32_cookie->hd, buffer, size); +#else + DWORD nread, ec; + + if (!ReadFile (w32_cookie->hd, buffer, size, &nread, NULL)) + { + ec = GetLastError (); + if (ec == ERROR_BROKEN_PIPE) + bytes_read = 0; /* Like our pth_read we handle this as EOF. */ + else + { + _set_errno (map_w32_to_errno (ec)); + log_debug ("estream: ReadFile returned %d\n", + (int)GetLastError ()); + bytes_read = -1; + } + } + else + bytes_read = (int)nread; +#endif + } + while (bytes_read == -1 && errno == EINTR); + } + + return bytes_read; +} + +/* Write function for W32 handle objects. */ +static ssize_t +es_func_w32_write (void *cookie, const void *buffer, size_t size) +{ + estream_cookie_w32_t w32_cookie = cookie; + ssize_t bytes_written; + + if (w32_cookie->hd == INVALID_HANDLE_VALUE) + { + ESTREAM_SYS_YIELD (); + bytes_written = size; /* Yeah: Success writing to the bit bucket. */ + } + else + { + do + { +#ifdef HAVE_PTH + /* Note: Our pth_write actually uses HANDLE! */ + bytes_written = pth_write ((int)w32_cookie->hd, buffer, size); +#else + DWORD nwritten; + + if (!WriteFile (w32_cookie->hd, buffer, size, &nwritten, NULL)) + { + _set_errno (map_w32_to_errno (GetLastError ())); + bytes_written = -1; + } + else + bytes_written = (int)nwritten; +#endif + } + while (bytes_written == -1 && errno == EINTR); + } + + return bytes_written; +} + +/* Seek function for W32 handle objects. */ +static int +es_func_w32_seek (void *cookie, off_t *offset, int whence) +{ + estream_cookie_w32_t w32_cookie = cookie; + DWORD method; + LARGE_INTEGER distance, newoff; + + if (w32_cookie->hd == INVALID_HANDLE_VALUE) + { + _set_errno (ESPIPE); + return -1; + } + + if (whence == SEEK_SET) + { + method = FILE_BEGIN; + distance.QuadPart = (unsigned long long)(*offset); + } + else if (whence == SEEK_CUR) + { + method = FILE_CURRENT; + distance.QuadPart = (long long)(*offset); + } + else if (whence == SEEK_END) + { + method = FILE_END; + distance.QuadPart = (long long)(*offset); + } + else + { + _set_errno (EINVAL); + return -1; + } +#ifdef HAVE_W32CE_SYSTEM +# warning need to use SetFilePointer +#else + if (!SetFilePointerEx (w32_cookie->hd, distance, &newoff, method)) + { + _set_errno (map_w32_to_errno (GetLastError ())); + return -1; + } +#endif + *offset = (unsigned long long)newoff.QuadPart; + return 0; +} + +/* Destroy function for W32 handle objects. */ +static int +es_func_w32_destroy (void *cookie) +{ + estream_cookie_w32_t w32_cookie = cookie; + int err; + + if (w32_cookie) + { + if (w32_cookie->hd == INVALID_HANDLE_VALUE) + err = 0; + else if (w32_cookie->no_close) + err = 0; + else + { + if (!CloseHandle (w32_cookie->hd)) + { + _set_errno (map_w32_to_errno (GetLastError ())); + err = -1; + } + else + err = 0; + } + mem_free (w32_cookie); + } + else + err = 0; + + return err; +} + + +static es_cookie_io_functions_t estream_functions_w32 = + { + es_func_w32_read, + es_func_w32_write, + es_func_w32_seek, + es_func_w32_destroy + }; +#endif /*HAVE_W32_SYSTEM*/ + + + /* Implementation of FILE* I/O. */ @@ -1049,7 +1293,7 @@ static es_cookie_io_functions_t estream_functions_fp = /* Implementation of file I/O. */ -/* Create function for file objects. */ +/* Create function for fd objects. */ static int es_func_file_create (void **cookie, int *filedes, const char *path, unsigned int modeflags) @@ -1269,7 +1513,8 @@ es_empty (estream_t stream) /* Initialize STREAM. */ static void es_initialize (estream_t stream, - void *cookie, int fd, es_cookie_io_functions_t functions, + void *cookie, es_syshd_t *syshd, + es_cookie_io_functions_t functions, unsigned int modeflags) { stream->intern->cookie = cookie; @@ -1280,7 +1525,7 @@ es_initialize (estream_t stream, stream->intern->func_seek = functions.func_seek; stream->intern->func_close = functions.func_close; stream->intern->strategy = _IOFBF; - stream->intern->fd = fd; + stream->intern->syshd = *syshd; stream->intern->print_ntotal = 0; stream->intern->indicators.err = 0; stream->intern->indicators.eof = 0; @@ -1329,7 +1574,7 @@ es_deinitialize (estream_t stream) /* Create a new stream object, initialize it. */ static int -es_create (estream_t *stream, void *cookie, int fd, +es_create (estream_t *stream, void *cookie, es_syshd_t *syshd, es_cookie_io_functions_t functions, unsigned int modeflags, int with_locked_list) { @@ -1361,7 +1606,7 @@ es_create (estream_t *stream, void *cookie, int fd, stream_new->intern = stream_internal_new; ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock); - es_initialize (stream_new, cookie, fd, functions, modeflags); + es_initialize (stream_new, cookie, syshd, functions, modeflags); err = es_list_add (stream_new, with_locked_list); if (err) @@ -1385,9 +1630,9 @@ es_create (estream_t *stream, void *cookie, int fd, /* Deinitialize a stream object and destroy it. */ static int -es_destroy (estream_t stream, int with_locked_list) +do_close (estream_t stream, int with_locked_list) { - int err = 0; + int err; if (stream) { @@ -1396,6 +1641,8 @@ es_destroy (estream_t stream, int with_locked_list) mem_free (stream->intern); mem_free (stream); } + else + err = 0; return err; } @@ -1897,6 +2144,7 @@ doreadline (estream_t ES__RESTRICT stream, size_t max_length, unsigned char *data; size_t data_len; int err; + es_syshd_t syshd; line_new = NULL; line_stream = NULL; @@ -1910,7 +2158,8 @@ doreadline (estream_t ES__RESTRICT stream, size_t max_length, if (err) goto out; - err = es_create (&line_stream, line_stream_cookie, -1, + memset (&syshd, 0, sizeof syshd); + err = es_create (&line_stream, line_stream_cookie, &syshd, estream_functions_mem, O_RDWR, 0); if (err) goto out; @@ -1996,7 +2245,7 @@ doreadline (estream_t ES__RESTRICT stream, size_t max_length, out: if (line_stream) - es_destroy (line_stream, 0); + do_close (line_stream, 0); else if (line_stream_cookie) es_func_mem_destroy (line_stream_cookie); @@ -2152,12 +2401,6 @@ es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new, } -static int -es_get_fd (estream_t stream) -{ - return stream->intern->fd; -} - /* API. */ @@ -2172,6 +2415,8 @@ es_init (void) return err; } + + estream_t es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode) { @@ -2181,6 +2426,7 @@ es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode) void *cookie; int err; int fd; + es_syshd_t syshd; stream = NULL; cookie = NULL; @@ -2193,9 +2439,12 @@ es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode) err = es_func_file_create (&cookie, &fd, path, modeflags); if (err) goto out; + + syshd.type = ES_SYSHD_FD; + syshd.u.fd = fd; create_called = 1; - err = es_create (&stream, cookie, fd, estream_functions_fd, modeflags, 0); + err = es_create (&stream, cookie, &syshd, estream_functions_fd, modeflags, 0); if (err) goto out; @@ -2211,6 +2460,7 @@ es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode) } + estream_t es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, unsigned int grow, @@ -2222,6 +2472,7 @@ es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, estream_t stream; void *cookie; int err; + es_syshd_t syshd; cookie = 0; stream = NULL; @@ -2237,8 +2488,10 @@ es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, if (err) goto out; + memset (&syshd, 0, sizeof syshd); create_called = 1; - err = es_create (&stream, cookie, -1, estream_functions_mem, modeflags, 0); + err = es_create (&stream, cookie, &syshd, + estream_functions_mem, modeflags, 0); out: @@ -2249,12 +2502,14 @@ es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len, } + estream_t es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode) { unsigned int modeflags; estream_t stream = NULL; void *cookie = NULL; + es_syshd_t syshd; /* Memory streams are always read/write. We use MODE only to get the append flag. */ @@ -2269,14 +2524,15 @@ es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode) memlimit)) return NULL; - if (es_create (&stream, cookie, -1, estream_functions_mem, modeflags, 0)) + memset (&syshd, 0, sizeof syshd); + if (es_create (&stream, cookie, &syshd, estream_functions_mem, modeflags, 0)) (*estream_functions_mem.func_close) (cookie); return stream; } - + estream_t es_fopencookie (void *ES__RESTRICT cookie, const char *ES__RESTRICT mode, @@ -2285,6 +2541,7 @@ es_fopencookie (void *ES__RESTRICT cookie, unsigned int modeflags; estream_t stream; int err; + es_syshd_t syshd; stream = NULL; modeflags = 0; @@ -2293,16 +2550,17 @@ es_fopencookie (void *ES__RESTRICT cookie, if (err) goto out; - err = es_create (&stream, cookie, -1, functions, modeflags, 0); + memset (&syshd, 0, sizeof syshd); + err = es_create (&stream, cookie, &syshd, functions, modeflags, 0); if (err) goto out; out: - return stream; } + estream_t do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list) { @@ -2311,6 +2569,7 @@ do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list) estream_t stream; void *cookie; int err; + es_syshd_t syshd; stream = NULL; cookie = NULL; @@ -2324,12 +2583,13 @@ do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list) if (err) goto out; + syshd.type = ES_SYSHD_FD; + syshd.u.fd = filedes; create_called = 1; - err = es_create (&stream, cookie, filedes, estream_functions_fd, + err = es_create (&stream, cookie, &syshd, estream_functions_fd, modeflags, with_locked_list); out: - if (err && create_called) (*estream_functions_fd.func_close) (cookie); @@ -2350,6 +2610,7 @@ es_fdopen_nc (int filedes, const char *mode) } + estream_t do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list) { @@ -2358,6 +2619,7 @@ do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list) estream_t stream; void *cookie; int err; + es_syshd_t syshd; stream = NULL; cookie = NULL; @@ -2372,9 +2634,11 @@ do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list) err = es_func_fp_create (&cookie, fp, modeflags, no_close); if (err) goto out; - + + syshd.type = ES_SYSHD_FD; + syshd.u.fd = fp? fileno (fp): -1; create_called = 1; - err = es_create (&stream, cookie, fp? fileno (fp):-1, estream_functions_fp, + err = es_create (&stream, cookie, &syshd, estream_functions_fp, modeflags, with_locked_list); out: @@ -2409,6 +2673,87 @@ es_fpopen_nc (FILE *fp, const char *mode) } + +#ifdef HAVE_W32_SYSTEM +estream_t +do_w32open (HANDLE hd, const char *mode, + int no_close, int with_locked_list) +{ + unsigned int modeflags; + int create_called = 0; + estream_t stream = NULL; + void *cookie = NULL; + int err; + es_syshd_t syshd; + + err = es_convert_mode (mode, &modeflags); + if (err) + goto leave; + + err = es_func_w32_create (&cookie, hd, modeflags, no_close); + if (err) + goto leave; + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = hd; + create_called = 1; + err = es_create (&stream, cookie, &syshd, estream_functions_w32, + modeflags, with_locked_list); + + leave: + if (err && create_called) + (*estream_functions_w32.func_close) (cookie); + + return stream; +} +#endif /*HAVE_W32_SYSTEM*/ + +static estream_t +do_sysopen (es_syshd_t *syshd, const char *mode, int no_close) +{ + estream_t stream; + + switch (syshd->type) + { + case ES_SYSHD_FD: + case ES_SYSHD_SOCK: + stream = do_fdopen (syshd->u.fd, mode, no_close, 0); + break; + +#ifdef HAVE_W32_SYSTEM + case ES_SYSHD_HANDLE: + stream = do_w32open (syshd->u.handle, mode, no_close, 0); + break; +#endif + + /* FIXME: Support RVIDs under Wince? */ + + default: + _set_errno (EINVAL); + stream = NULL; + } + return stream; +} + +/* On POSIX systems this function is an alias for es_fdopen. Under + Windows it uses the bare W32 API and thus a HANDLE instead of a + file descriptor. */ +estream_t +es_sysopen (es_syshd_t *syshd, const char *mode) +{ + return do_sysopen (syshd, mode, 0); +} + +/* Same as es_sysopen but the handle/fd will not be closed by + es_fclose. */ +estream_t +es_sysopen_nc (es_syshd_t *syshd, const char *mode) +{ + return do_sysopen (syshd, mode, 1); +} + + + /* Set custom standard descriptors to be used for stdin, stdout and stderr. This function needs to be called before any of the standard streams are accessed. */ @@ -2500,6 +2845,7 @@ es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, int create_called; void *cookie; int fd; + es_syshd_t syshd; cookie = NULL; create_called = 0; @@ -2516,8 +2862,10 @@ es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, if (err) goto leave; + syshd.type = ES_SYSHD_FD; + syshd.u.fd = fd; create_called = 1; - es_initialize (stream, cookie, fd, estream_functions_fd, modeflags); + es_initialize (stream, cookie, &syshd, estream_functions_fd, modeflags); leave: @@ -2526,7 +2874,7 @@ es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, if (create_called) es_func_fd_destroy (cookie); - es_destroy (stream, 0); + do_close (stream, 0); stream = NULL; } else @@ -2541,7 +2889,7 @@ es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode, /* FIXME? We don't support re-opening at the moment. */ _set_errno (EINVAL); es_deinitialize (stream); - es_destroy (stream, 0); + do_close (stream, 0); stream = NULL; } @@ -2554,15 +2902,47 @@ es_fclose (estream_t stream) { int err; - err = es_destroy (stream, 0); + err = do_close (stream, 0); return err; } + int es_fileno_unlocked (estream_t stream) { - return es_get_fd (stream); + es_syshd_t syshd; + + if (es_syshd (stream, &syshd)) + return -1; + switch (syshd.type) + { + case ES_SYSHD_FD: return syshd.u.fd; + case ES_SYSHD_SOCK: return syshd.u.sock; + default: + _set_errno (EINVAL); + return -1; + } +} + + +/* Return the handle of a stream which has been opened by es_sysopen. + The caller needs to pass a structure which will be filled with the + sys handle. Return 0 on success or true on error and sets errno. + This is the unlocked version. */ +int +es_syshd_unlocked (estream_t stream, es_syshd_t *syshd) +{ + if (!stream || !syshd || stream->intern->syshd.type == ES_SYSHD_NONE) + { + if (syshd) + syshd->type = ES_SYSHD_NONE; + _set_errno (EINVAL); + return -1; + } + + *syshd = stream->intern->syshd; + return 0; } @@ -2600,6 +2980,23 @@ es_fileno (estream_t stream) } +/* Return the handle of a stream which has been opened by es_sysopen. + The caller needs to pass a structure which will be filled with the + sys handle. Return 0 on success or true on error and sets errno. + This is the unlocked version. */ +int +es_syshd (estream_t stream, es_syshd_t *syshd) +{ + int ret; + + ESTREAM_LOCK (stream); + ret = es_syshd_unlocked (stream, syshd); + ESTREAM_UNLOCK (stream); + + return ret; +} + + int es_feof_unlocked (estream_t stream) { @@ -3371,6 +3768,7 @@ es_tmpfile (void) void *cookie; int err; int fd; + es_syshd_t syshd; create_called = 0; stream = NULL; @@ -3388,11 +3786,12 @@ es_tmpfile (void) if (err) goto out; + syshd.type = ES_SYSHD_FD; + syshd.u.fd = fd; create_called = 1; - err = es_create (&stream, cookie, fd, estream_functions_fd, modeflags, 0); + err = es_create (&stream, cookie, &syshd, estream_functions_fd, modeflags, 0); out: - if (err) { if (create_called) diff --git a/common/estream.h b/common/estream.h index 34ff5ce77..c5dc19c07 100644 --- a/common/estream.h +++ b/common/estream.h @@ -78,6 +78,8 @@ #define es_fopenmem _ESTREAM_PREFIX(es_fopenmem) #define es_fdopen _ESTREAM_PREFIX(es_fdopen) #define es_fdopen_nc _ESTREAM_PREFIX(es_fdopen_nc) +#define es_sysopen _ESTREAM_PREFIX(es_sysopen) +#define es_sysopen_nc _ESTREAM_PREFIX(es_sysopen_nc) #define es_fpopen _ESTREAM_PREFIX(es_fpopen) #define es_fpopen_nc _ESTREAM_PREFIX(es_fpopen_nc) #define _es_set_std_fd _ESTREAM_PREFIX(_es_set_std_fd) @@ -211,6 +213,29 @@ typedef struct es_cookie_io_functions es_cookie_close_function_t func_close; } es_cookie_io_functions_t; + +enum es_syshd_types + { + ES_SYSHD_NONE, /* No system handle available. */ + ES_SYSHD_FD, /* A file descriptor as returned by open(). */ + ES_SYSHD_SOCK, /* A socket as returned by socket(). */ + ES_SYSHD_RVID, /* A rendevous id (see libassuan's gpgcedev.c). */ + ES_SYSHD_HANDLE /* A HANDLE object (Windows). */ + }; + +typedef struct +{ + enum es_syshd_types type; + union { + int fd; + int sock; + int rvid; + void *handle; + } u; +} es_syshd_t; + + + #ifndef _ESTREAM_GCC_A_PRINTF #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) @@ -245,6 +270,8 @@ estream_t es_mopen (unsigned char *ES__RESTRICT data, estream_t es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode); estream_t es_fdopen (int filedes, const char *mode); estream_t es_fdopen_nc (int filedes, const char *mode); +estream_t es_sysopen (es_syshd_t *syshd, const char *mode); +estream_t es_sysopen_nc (es_syshd_t *syshd, const char *mode); estream_t es_fpopen (FILE *fp, const char *mode); estream_t es_fpopen_nc (FILE *fp, const char *mode); estream_t es_freopen (const char *ES__RESTRICT path, @@ -256,6 +283,8 @@ estream_t es_fopencookie (void *ES__RESTRICT cookie, int es_fclose (estream_t stream); int es_fileno (estream_t stream); int es_fileno_unlocked (estream_t stream); +int es_syshd (estream_t stream, es_syshd_t *syshd); +int es_syshd_unlocked (estream_t stream, es_syshd_t *syshd); void _es_set_std_fd (int no, int fd); estream_t _es_get_std_stream (int fd); diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c index 5a8e0289c..6ffc562ca 100644 --- a/common/exechelp-posix.c +++ b/common/exechelp-posix.c @@ -299,45 +299,95 @@ gnupg_create_outbound_pipe (int filedes[2]) } + +static gpg_error_t +create_pipe_and_estream (int filedes[2], estream_t *r_fp, + gpg_err_source_t errsource) +{ + gpg_error_t err; + + if (pipe (filedes) == -1) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + filedes[0] = filedes[1] = -1; + *r_fp = NULL; + return err; + } + + *r_fp = es_fdopen (filedes[0], "r"); + if (!*r_fp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + return 0; +} + + + /* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], - estream_t infile, estream_t outfile, + gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, - estream_t *statusfile, pid_t *pid) + estream_t infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) { gpg_error_t err; - int fd, fdout, rp[2]; + int infd = -1; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + estream_t outfp = NULL; + estream_t errfp = NULL; (void)flags; /* Currently not used. */ - *statusfile = NULL; - *pid = (pid_t)(-1); + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ - if (infile) + if (infp) { - es_fflush (infile); - es_rewind (infile); - fd = es_fileno (infile); + es_fflush (infp); + es_rewind (infp); + infd = es_fileno (infp); + if (infd == -1) + return gpg_err_make (errsource, GPG_ERR_INV_VALUE); } - else - fd = -1; - if (outfile) - fdout = es_fileno (outfile); - else - fdout = -1; - - if ((infile && fd == -1) || (outfile && fdout == -1)) - log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n"); + if (r_outfp) + { + err = create_pipe_and_estream (outpipe, &outfp, errsource); + if (err) + return err; + } - if (pipe (rp) == -1) + if (r_errfp) { - err = gpg_error_from_syserror (); - log_error (_("error creating a pipe: %s\n"), strerror (errno)); - return err; + err = create_pipe_and_estream (errpipe, &errfp, errsource); + if (err) + { + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + return err; + } } + #ifdef USE_GNU_PTH *pid = pth_fork? pth_fork () : fork (); #else @@ -345,33 +395,45 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], #endif if (*pid == (pid_t)(-1)) { - err = gpg_error_from_syserror (); - log_error (_("error forking process: %s\n"), strerror (errno)); - close (rp[0]); - close (rp[1]); + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error forking process: %s\n"), gpg_strerror (err)); + + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != -1) + close (errpipe[0]); + if (errpipe[1] != -1) + close (errpipe[1]); return err; } if (!*pid) { + /* This is the child. */ gcry_control (GCRYCTL_TERM_SECMEM); - /* Run child. */ - do_exec (pgmname, argv, fd, fdout, rp[1], preexec); + es_fclose (outfp); + es_fclose (errfp); + do_exec (pgmname, argv, infd, outpipe[1], errpipe[1], preexec); /*NOTREACHED*/ } - /* Parent. */ - close (rp[1]); + /* This is the parent. */ + if (outpipe[1] != -1) + close (outpipe[1]); + if (errpipe[1] != -1) + close (errpipe[1]); - *statusfile = es_fdopen (rp[0], "r"); - if (!*statusfile) - { - err = gpg_error_from_syserror (); - log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno)); - kill (*pid, SIGTERM); - *pid = (pid_t)(-1); - return err; - } + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; return 0; } diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c index 297f6f854..13f600716 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -257,7 +257,7 @@ build_w32_commandline (const char *pgmname, const char * const *argv, /* Create pipe where one end is inheritable: With an INHERIT_IDX of 0 the read end is inheritable, with 1 the write end is inheritable. */ static int -create_inheritable_pipe (int filedes[2], int inherit_idx) +create_inheritable_pipe (HANDLE filedes[2], int inherit_idx) { HANDLE r, w, h; SECURITY_ATTRIBUTES sec_attr; @@ -290,8 +290,8 @@ create_inheritable_pipe (int filedes[2], int inherit_idx) r = h; } - filedes[0] = handle_to_fd (r); - filedes[1] = handle_to_fd (w); + filedes[0] = r; + filedes[1] = w; return 0; } @@ -315,27 +315,27 @@ static gpg_error_t do_create_pipe (int filedes[2], int inherit_idx) { gpg_error_t err = 0; - int fds[2]; + HANDLE fds[2]; filedes[0] = filedes[1] = -1; err = gpg_error (GPG_ERR_GENERAL); if (!create_inheritable_pipe (fds, inherit_idx)) { - filedes[0] = _open_osfhandle (fds[0], 0); + filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), 0); if (filedes[0] == -1) { - log_error ("failed to translate osfhandle %p\n", (void*)fds[0]); - CloseHandle (fd_to_handle (fds[1])); + log_error ("failed to translate osfhandle %p\n", fds[0]); + CloseHandle (fds[1]); } else { - filedes[1] = _open_osfhandle (fds[1], 1); + filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), 1); if (filedes[1] == -1) { - log_error ("failed to translate osfhandle %p\n", (void*)fds[1]); + log_error ("failed to translate osfhandle %p\n", fds[1]); close (filedes[0]); filedes[0] = -1; - CloseHandle (fd_to_handle (fds[1])); + CloseHandle (fds[1]); } else err = 0; @@ -365,9 +365,12 @@ gnupg_create_outbound_pipe (int filedes[2]) /* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], - estream_t infile, estream_t outfile, + gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, - estream_t *statusfile, pid_t *pid) + estream_t infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) { gpg_error_t err; SECURITY_ATTRIBUTES sec_attr; @@ -381,32 +384,103 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], STARTUPINFO si; int cr_flags; char *cmdline; - int fd, fdout, rp[2]; - HANDLE nullhd[2]; + HANDLE inhandle = INVALID_HANDLE_VALUE; + HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + estream_t outfp = NULL; + estream_t errfp = NULL; + HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE}; int i; + es_syshd_t syshd; - (void)preexec; - - /* Setup return values. */ - *statusfile = NULL; - *pid = (pid_t)(-1); + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ - if (infile) + if (infp) { - es_fflush (infile); - es_rewind (infile); - fd = _get_osfhandle (es_fileno (infile)); + es_fflush (infp); + es_rewind (infp); + es_syshd (infp, &syshd); + switch (syshd.type) + { + case ES_SYSHD_FD: + inhandle = (HANDLE)_get_osfhandle (syshd.u.fd); + break; + case ES_SYSHD_SOCK: + inhandle = (HANDLE)_get_osfhandle (syshd.u.sock); + break; + case ES_SYSHD_HANDLE: + inhandle = syshd.u.handle; + break; + default: + inhandle = INVALID_HANDLE_VALUE; + break; + } + if (inhandle == INVALID_HANDLE_VALUE) + return gpg_err_make (errsource, GPG_ERR_INV_VALUE); + /* FIXME: In case we can't get a system handle (e.g. due to + es_fopencookie we should create a piper and a feeder + thread. */ } - else - fd = -1; - if (outfile) - fdout = _get_osfhandle (es_fileno (outfile)); - else - fdout = -1; + if (r_outfp) + { + if (create_inheritable_pipe (outpipe, 1)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } - if ( (infile && fd == -1) || (outfile && fdout == -1)) - log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n"); + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = outpipe[0]; + outfp = es_sysopen (&syshd, "r"); + if (!outfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (outpipe[0]); + CloseHandle (outpipe[1]); + outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; + return err; + } + } + + if (r_errfp) + { + if (create_inheritable_pipe (errpipe, 1)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = errpipe[0]; + errfp = es_sysopen (&syshd, "r"); + if (!errfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (errpipe[0]); + CloseHandle (errpipe[1]); + errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + return err; + } + } /* Prepare security attributes. */ memset (&sec_attr, 0, sizeof sec_attr ); @@ -418,27 +492,24 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], if (err) return err; - /* Create a pipe. */ - if (create_inheritable_pipe (rp, 1)) - { - err = gpg_error (GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - xfree (cmdline); - return err; - } - - nullhd[0] = fd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; - nullhd[1] = fdout == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; + if (inhandle != INVALID_HANDLE_VALUE) + nullhd[0] = w32_open_null (0); + if (outpipe[1] != INVALID_HANDLE_VALUE) + nullhd[1] = w32_open_null (0); + if (errpipe[1] != INVALID_HANDLE_VALUE) + nullhd[2] = w32_open_null (0); /* Start the process. Note that we can't run the PREEXEC function - because this would change our own environment. */ + because this might change our own environment. */ + (void)preexec; + memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; - si.hStdInput = fd == -1? nullhd[0] : fd_to_handle (fd); - si.hStdOutput = fdout == -1? nullhd[1] : fd_to_handle (fdout); - si.hStdError = fd_to_handle (rp[1]); + si.hStdInput = inhandle == INVALID_HANDLE_VALUE? nullhd[0] : inhandle; + si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; + si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; cr_flags = (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) @@ -459,9 +530,19 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], { log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); xfree (cmdline); - CloseHandle (fd_to_handle (rp[0])); - CloseHandle (fd_to_handle (rp[1])); - return gpg_error (GPG_ERR_GENERAL); + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[0]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + return gpg_err_make (errsource, GPG_ERR_GENERAL); } xfree (cmdline); cmdline = NULL; @@ -471,17 +552,21 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], if (nullhd[i] != INVALID_HANDLE_VALUE) CloseHandle (nullhd[i]); - /* Close the other end of the pipe. */ - CloseHandle (fd_to_handle (rp[1])); - -/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ -/* " dwProcessID=%d dwThreadId=%d\n", */ -/* pi.hProcess, pi.hThread, */ -/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + /* Close the inherited ends of the pipes. */ + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ + /* Fixme: For unknown reasons AllowSetForegroundWindow returns an - invalid argument error if we pass the correct processID to - it. As a workaround we use -1 (ASFW_ANY). */ + invalid argument error if we pass it the correct processID. As a + workaround we use -1 (ASFW_ANY). */ if ( (flags & 64) ) gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); @@ -489,22 +574,10 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], ResumeThread (pi.hThread); CloseHandle (pi.hThread); - { - int x; - - x = _open_osfhandle (rp[0], 0); - if (x == -1) - log_error ("failed to translate osfhandle %p\n", (void*)rp[0] ); - else - *statusfile = es_fdopen (x, "r"); - } - if (!*statusfile) - { - err = gpg_error_from_syserror (); - log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err)); - CloseHandle (pi.hProcess); - return err; - } + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; *pid = handle_to_pid (pi.hProcess); return 0; diff --git a/common/exechelp-w32ce.c b/common/exechelp-w32ce.c index 5a84c9f1d..f49a442d9 100644 --- a/common/exechelp-w32ce.c +++ b/common/exechelp-w32ce.c @@ -83,7 +83,7 @@ struct feeder_thread_parms }; -/* The thread started by start_feeded. */ +/* The thread started by start_feede3. */ static void * feeder_thread (void *arg) { @@ -485,10 +485,14 @@ create_process (const char *pgmname, const char *cmdline, /* Fork and exec the PGMNAME, see exechelp.h for details. */ gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], - estream_t infile, estream_t outfile, + gpg_err_source_t errsource, void (*preexec)(void), unsigned int flags, - estream_t *statusfile, pid_t *pid) + estream_t infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) { +#if 0 gpg_error_t err; PROCESS_INFORMATION pi = {NULL }; char *cmdline; @@ -598,7 +602,9 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], *pid = handle_to_pid (pi.hProcess); return 0; - +#else + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif } diff --git a/common/exechelp.h b/common/exechelp.h index 3a5b9e2b8..e6a0ee5e1 100644 --- a/common/exechelp.h +++ b/common/exechelp.h @@ -52,15 +52,24 @@ gpg_error_t gnupg_create_inbound_pipe (int filedes[2]); gpg_error_t gnupg_create_outbound_pipe (int filedes[2]); -/* Fork and exec the PGMNAME, connect the file descriptor of INFILE to - stdin, write the output to OUTFILE. INFILE or PUTFILE may be NULL - to connect thenm to /dev/null. Returns a new stream in STATUSFILE - for stderr and the pid of the process in PID. The arguments for the - process are expected in the NULL terminated array ARGV. The - program name itself should not be included there. If PREEXEC is - not NULL, that function will be called right before the exec. - Calling gnupg_wait_process and gnupg_release_process is required. - Returns 0 on success or an error code. +/* Fork and exec the PGMNAME. If INFP is NULL connect /dev/null to + stdin of the new process; if it is not NULL connect the file + descriptor retrieved from INFP to stdin. If R_OUTFP is NULL + connect stdout of the new process to /dev/null; if it is not NULL + store the address of a pointer to a new estream there. If R_ERRFP + is NULL connect stderr of the new process to /dev/null; if it is + not NULL store the address of a pointer to a new estream there. On + success the pid of the new process is stored at PID. On error -1 + is stored at PID and if R_OUTFP or R_ERRFP are not NULL, NULL is + stored there. + + The arguments for the process are expected in the NULL terminated + array ARGV. The program name itself should not be included there. + If PREEXEC is not NULL, the given function will be called right + before the exec. + + Returns 0 on success or an error code. Calling gnupg_wait_process + and gnupg_release_process is required if the function succeeded. FLAGS is a bit vector: @@ -74,10 +83,14 @@ gpg_error_t gnupg_create_outbound_pipe (int filedes[2]); allows SetForegroundWindow for all childs of this process. */ -gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[], - estream_t infile, estream_t outfile, - void (*preexec)(void), unsigned int flags, - estream_t *statusfile, pid_t *pid); +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + gpg_err_source_t errsource, + void (*preexec)(void), unsigned int flags, + estream_t infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid); /* Simplified version of gnupg_spawn_process. This function forks and -- cgit v1.2.3