diff --git a/src/debug.h b/src/debug.h index ca65d9ed..ecd230f6 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,283 +1,294 @@ /* debug.h - interface to debugging functions Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef DEBUG_H #define DEBUG_H #include #ifdef HAVE_STDINT_H #include #endif #include "gpgme.h" /* Required for gpgme_error stuff. */ /* Indirect stringification, requires __STDC__ to work. */ #define STRINGIFY(v) #v #define XSTRINGIFY(v) STRINGIFY(v) -/* The debug levels. */ +/* + * The debug levels. + * + * Note that TRACE_LOGBUFX uses the current debug level + 1. + */ #define DEBUG_INIT 1 #define DEBUG_GLOBAL 2 #define DEBUG_CTX 3 #define DEBUG_ENGINE 4 #define DEBUG_DATA 5 #define DEBUG_ASSUAN 6 #define DEBUG_SYSIO 7 /* Remove path components from filenames (i.e. __FILE__) for cleaner logs. */ static inline const char *_gpgme_debug_srcname (const char *file) GPGME_GCC_A_PURE; static inline const char * _gpgme_debug_srcname (const char *file) { const char *s = strrchr (file, '/'); return s? s+1:file; } /* Initialization helper function; see debug.c. */ int _gpgme_debug_set_debug_envvar (const char *value); /* Called early to initialize the logging. */ void _gpgme_debug_subsystem_init (void); /* Log the formatted string FORMAT at debug level LEVEL or higher. */ int _gpgme_debug (int level, const char *format, ...); /* Start a new debug line in *LINE, logged at level LEVEL or higher, and starting with the formatted string FORMAT. */ void _gpgme_debug_begin (void **helper, int level, const char *format, ...); /* Add the formatted string FORMAT to the debug line *LINE. */ void _gpgme_debug_add (void **helper, const char *format, ...); /* Finish construction of *LINE and send it to the debug output stream. */ void _gpgme_debug_end (void **helper); void _gpgme_debug_buffer (int lvl, const char *const fmt, const char *const func, const char *const buffer, size_t len); void _gpgme_debug_frame_begin (void); int _gpgme_debug_frame_end (void); static inline gpgme_error_t _gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line) { _gpgme_debug (DEBUG_ENGINE, "%s:%d: returning error: %s\n", _gpgme_debug_srcname (file), line, gpgme_strerror (err)); return err; } /* Trace support. */ /* FIXME: For now. */ #define _gpgme_debug_trace() 1 #define _TRACE(lvl, name, tag) \ int _gpgme_trace_level = lvl; \ const char *const _gpgme_trace_func = name; \ const char *const _gpgme_trace_tagname = STRINGIFY (tag); \ void *_gpgme_trace_tag = (void *) (uintptr_t) tag; \ _gpgme_debug_frame_begin () #define TRACE_BEG(lvl, name, tag) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_BEG0(lvl, name, tag, fmt) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_BEG1(lvl, name, tag, fmt, arg1) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1) #define TRACE_BEG2(lvl, name, tag, fmt, arg1, arg2) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2) #define TRACE_BEG3(lvl, name, tag, fmt, arg1, arg2, arg3) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3) #define TRACE_BEG4(lvl, name, tag, fmt, arg1, arg2, arg3, arg4) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4) #define TRACE_BEG5(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4, arg5) #define TRACE_BEG7(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \ arg5, arg6, arg7) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4, arg5, arg6, arg7) #define TRACE_BEG8(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \ arg5, arg6, arg7, arg8) \ _TRACE (lvl, name, tag); \ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) #define TRACE(lvl, name, tag) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag), \ _gpgme_debug_frame_end () #define TRACE0(lvl, name, tag, fmt) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag), \ _gpgme_debug_frame_end () #define TRACE1(lvl, name, tag, fmt, arg1) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1), \ _gpgme_debug_frame_end () #define TRACE2(lvl, name, tag, fmt, arg1, arg2) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2), _gpgme_debug_frame_end () #define TRACE3(lvl, name, tag, fmt, arg1, arg2, arg3) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2, arg3), _gpgme_debug_frame_end () #define TRACE6(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ _gpgme_debug_frame_begin (), \ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ arg2, arg3, arg4, arg5, arg6), \ _gpgme_debug_frame_end () #define TRACE_ERR(err) \ err == 0 ? (TRACE_SUC ()) : \ (_gpgme_debug (_gpgme_trace_level, "%s:%d: error: %s <%s>\n", \ _gpgme_trace_func, __LINE__, gpgme_strerror (err), \ gpgme_strsource (err)), _gpgme_debug_frame_end (), (err)) /* The cast to void suppresses GCC warnings. */ #define TRACE_SYSRES(res) \ res >= 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ _gpgme_trace_func, strerror (errno)), _gpgme_debug_frame_end (), (res)) #define TRACE_SYSERR(res) \ res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \ _gpgme_trace_func, strerror (res)), \ _gpgme_debug_frame_end (), (res)) #define TRACE_SUC() \ _gpgme_debug (_gpgme_trace_level, "%s: leave\n", \ _gpgme_trace_func), _gpgme_debug_frame_end () #define TRACE_SUC0(fmt) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func), _gpgme_debug_frame_end () #define TRACE_SUC1(fmt, arg1) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func, arg1), _gpgme_debug_frame_end () #define TRACE_SUC2(fmt, arg1, arg2) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func, arg1, arg2), _gpgme_debug_frame_end () +#define TRACE_SUC3(fmt, arg1, arg2, arg3) \ + _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ + _gpgme_trace_func, arg1, arg2, arg3), _gpgme_debug_frame_end () #define TRACE_SUC4(fmt, arg1, arg2, arg3, arg4) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func, arg1, arg2, arg3, arg4), \ _gpgme_debug_frame_end () #define TRACE_SUC5(fmt, arg1, arg2, arg3, arg4, arg5) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5), \ _gpgme_debug_frame_end () #define TRACE_SUC6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \ _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5, arg6), \ _gpgme_debug_frame_end () #define TRACE_LOG(fmt) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_LOG1(fmt, arg1) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1) #define TRACE_LOG2(fmt, arg1, arg2) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2) #define TRACE_LOG3(fmt, arg1, arg2, arg3) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3) #define TRACE_LOG4(fmt, arg1, arg2, arg3, arg4) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4) #define TRACE_LOG5(fmt, arg1, arg2, arg3, arg4, arg5) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4, arg5) #define TRACE_LOG6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \ arg1, arg2, arg3, arg4, arg5, arg6) #define TRACE_LOGBUF(buf, len) \ _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \ _gpgme_trace_func, buf, len) +#define TRACE_LOGBUFX(buf, len) \ + _gpgme_debug_buffer (_gpgme_trace_level+1, "%s: check: %s", \ + _gpgme_trace_func, buf, len) + #define TRACE_SEQ(hlp,fmt) \ _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ "%s: check: %s=%p, " fmt, _gpgme_trace_func, \ _gpgme_trace_tagname, _gpgme_trace_tag) #define TRACE_ADD0(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ _gpgme_debug_add (&(hlp), fmt, (a)) #define TRACE_ADD2(hlp,fmt,a,b) \ _gpgme_debug_add (&(hlp), fmt, (a), (b)) #define TRACE_ADD3(hlp,fmt,a,b,c) \ _gpgme_debug_add (&(hlp), fmt, (a), (b), (c)) #define TRACE_END(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt); \ _gpgme_debug_end (&(hlp)) #define TRACE_ENABLED(hlp) (!!(hlp)) /* And finally a simple macro to trace the location of an error code. This macro is independent of the other trace macros and may be used without any preconditions. */ #define trace_gpg_error(e) \ _gpgme_trace_gpgme_error (gpg_error (e), __FILE__, __LINE__) #endif /* DEBUG_H */ diff --git a/src/posix-io.c b/src/posix-io.c index 0448d296..322c41a8 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -1,875 +1,875 @@ /* posix-io.c - Posix I/O functions Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_SYS_UIO_H # include #endif #include #include #ifdef USE_LINUX_GETDENTS # include # include # include #endif /*USE_LINUX_GETDENTS*/ #include "util.h" #include "priv-io.h" #include "sema.h" #include "ath.h" #include "debug.h" void _gpgme_io_subsystem_init (void) { struct sigaction act; sigaction (SIGPIPE, NULL, &act); if (act.sa_handler == SIG_DFL) { act.sa_handler = SIG_IGN; sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGPIPE, &act, NULL); } } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { return snprintf (buf, buflen, "%d", fd); } /* The table to hold notification handlers. We use a linear search and extend the table as needed. */ struct notify_table_item_s { int fd; /* -1 indicates an unused entry. */ _gpgme_close_notify_handler_t handler; void *value; }; typedef struct notify_table_item_s *notify_table_item_t; static notify_table_item_t notify_table; static size_t notify_table_size; DEFINE_STATIC_LOCK (notify_table_lock); int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); do { nread = _gpgme_ath_read (fd, buffer, count); } while (nread == -1 && errno == EINTR); - TRACE_LOGBUF (buffer, nread); + TRACE_LOGBUFX (buffer, nread); return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int nwritten; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); - TRACE_LOGBUF (buffer, count); + TRACE_LOGBUFX (buffer, count); do { nwritten = _gpgme_ath_write (fd, buffer, count); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int saved_errno; int err; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); err = pipe (filedes); if (err < 0) return TRACE_SYSRES (err); /* FIXME: Should get the old flags first. */ err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC); saved_errno = errno; if (err < 0) { close (filedes[0]); close (filedes[1]); } errno = saved_errno; if (err) return TRACE_SYSRES (err); return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]); } int _gpgme_io_close (int fd) { int res; _gpgme_close_notify_handler_t handler = NULL; void *handler_value; int idx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); if (fd == -1) { errno = EINVAL; return TRACE_SYSRES (-1); } /* First call the notify handler. */ LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) { if (notify_table[idx].fd == fd) { handler = notify_table[idx].handler; handler_value = notify_table[idx].value; notify_table[idx].handler = NULL; notify_table[idx].value = NULL; notify_table[idx].fd = -1; /* Mark slot as free. */ break; } } UNLOCK (notify_table_lock); if (handler) { TRACE_LOG2 ("invoking close handler %p/%p", handler, handler_value); handler (fd, handler_value); } /* Then do the close. */ res = close (fd); return TRACE_SYSRES (res); } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { int res = 0; int idx; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); LOCK (notify_table_lock); for (idx=0; idx < notify_table_size; idx++) if (notify_table[idx].fd == -1) break; if (idx == notify_table_size) { /* We need to increase the size of the table. The approach we take is straightforward to minimize the risk of bugs. */ notify_table_item_t newtbl; size_t newsize = notify_table_size + 64; newtbl = calloc (newsize, sizeof *newtbl); if (!newtbl) { res = -1; goto leave; } for (idx=0; idx < notify_table_size; idx++) newtbl[idx] = notify_table[idx]; for (; idx < newsize; idx++) { newtbl[idx].fd = -1; newtbl[idx].handler = NULL; newtbl[idx].value = NULL; } free (notify_table); notify_table = newtbl; idx = notify_table_size; notify_table_size = newsize; } notify_table[idx].fd = fd; notify_table[idx].handler = handler; notify_table[idx].value = value; leave: UNLOCK (notify_table_lock); return TRACE_SYSRES (res); } int _gpgme_io_set_nonblocking (int fd) { int flags; int res; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); flags = fcntl (fd, F_GETFL, 0); if (flags == -1) return TRACE_SYSRES (-1); flags |= O_NONBLOCK; res = fcntl (fd, F_SETFL, flags); return TRACE_SYSRES (res); } #ifdef USE_LINUX_GETDENTS /* This is not declared in public headers; getdents64(2) says that we must * define it ourselves. */ struct linux_dirent64 { ino64_t d_ino; off64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; # define DIR_BUF_SIZE 1024 #endif /*USE_LINUX_GETDENTS*/ static long int get_max_fds (void) { const char *source = NULL; long int fds = -1; int rc; /* Under Linux we can figure out the highest used file descriptor by * reading /proc/self/fd. This is in the common cases much faster * than for example doing 4096 close calls where almost all of them * will fail. * * We can't use the normal opendir/readdir/closedir interface between * fork and exec in a multi-threaded process because opendir uses * malloc and thus a mutex which may deadlock with a malloc in another * thread. However, the underlying getdents system call is safe. */ #ifdef USE_LINUX_GETDENTS { int dir_fd; char dir_buf[DIR_BUF_SIZE]; struct linux_dirent64 *dir_entry; int r, pos; const char *s; int x; dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); if (dir_fd != -1) { for (;;) { r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE); if (r == -1) { /* Fall back to other methods. */ fds = -1; break; } if (r == 0) break; for (pos = 0; pos < r; pos += dir_entry->d_reclen) { dir_entry = (struct linux_dirent64 *) (dir_buf + pos); s = dir_entry->d_name; if (*s < '0' || *s > '9') continue; /* atoi is not guaranteed to be async-signal-safe. */ for (x = 0; *s >= '0' && *s <= '9'; s++) x = x * 10 + (*s - '0'); if (!*s && x > fds && x != dir_fd) fds = x; } } close (dir_fd); } if (fds != -1) { fds++; source = "/proc"; } } #endif /*USE_LINUX_GETDENTS*/ #ifdef RLIMIT_NOFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_NOFILE, &rl); if (rc == 0) { source = "RLIMIT_NOFILE"; fds = rl.rlim_max; } } #endif #ifdef RLIMIT_OFILE if (fds == -1) { struct rlimit rl; rc = getrlimit (RLIMIT_OFILE, &rl); if (rc == 0) { source = "RLIMIT_OFILE"; fds = rl.rlim_max; } } #endif #ifdef _SC_OPEN_MAX if (fds == -1) { long int scres; scres = sysconf (_SC_OPEN_MAX); if (scres >= 0) { source = "_SC_OPEN_MAX"; return scres; } } #endif #ifdef OPEN_MAX if (fds == -1) { source = "OPEN_MAX"; fds = OPEN_MAX; } #endif #if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \ && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX) #warning "No known way to get the maximum number of file descriptors." #endif if (fds == -1) { source = "arbitrary"; /* Arbitrary limit. */ fds = 1024; } /* AIX returns INT32_MAX instead of a proper value. We assume that * this is always an error and use a more reasonable limit. */ #ifdef INT32_MAX if (fds == INT32_MAX) { source = "aix-fix"; fds = 1024; } #endif TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source); return fds; } int _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal) { int status; pid_t ret; *r_status = 0; *r_signal = 0; do ret = _gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG); while (ret == (pid_t)(-1) && errno == EINTR); if (ret == pid) { if (WIFSIGNALED (status)) { *r_status = 4; /* Need some value here. */ *r_signal = WTERMSIG (status); } else if (WIFEXITED (status)) *r_status = WEXITSTATUS (status); else *r_status = 4; /* Oops. */ return 1; } return 0; } /* Returns 0 on success, -1 on error. */ int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { pid_t pid; int i; int status; int signo; TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); i = 0; while (argv[i]) { TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); i++; } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd); else TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to); pid = fork (); if (pid == -1) return TRACE_SYSRES (-1); if (!pid) { /* Intermediate child to prevent zombie processes. */ if ((pid = fork ()) == 0) { /* Child. */ int max_fds = -1; int fd; int seen_stdin = 0; int seen_stdout = 0; int seen_stderr = 0; if (atfork) atfork (atforkvalue, 0); /* First close all fds which will not be inherited. If we * have closefrom(2) we first figure out the highest fd we * do not want to close, then call closefrom, and on success * use the regular code to close all fds up to the start * point of closefrom. Note that Solaris' and FreeBSD's closefrom do * not return errors. */ #ifdef HAVE_CLOSEFROM { fd = -1; for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd > fd) fd = fd_list[i].fd; fd++; #if defined(__sun) || defined(__FreeBSD__) closefrom (fd); max_fds = fd; #else /*!__sun */ while ((i = closefrom (fd)) && errno == EINTR) ; if (!i || errno == EBADF) max_fds = fd; #endif /*!__sun*/ } #endif /*HAVE_CLOSEFROM*/ if (max_fds == -1) max_fds = get_max_fds (); for (fd = 0; fd < max_fds; fd++) { for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].fd == fd) break; if (fd_list[i].fd == -1) close (fd); } /* And now dup and close those to be duplicated. */ for (i = 0; fd_list[i].fd != -1; i++) { int child_fd; int res; if (fd_list[i].dup_to != -1) child_fd = fd_list[i].dup_to; else child_fd = fd_list[i].fd; if (child_fd == 0) seen_stdin = 1; else if (child_fd == 1) seen_stdout = 1; else if (child_fd == 2) seen_stderr = 1; if (fd_list[i].dup_to == -1) continue; res = dup2 (fd_list[i].fd, fd_list[i].dup_to); if (res < 0) { #if 0 /* FIXME: The debug file descriptor is not dup'ed anyway, so we can't see this. */ TRACE_LOG1 ("dup2 failed in child: %s\n", strerror (errno)); #endif _exit (8); } close (fd_list[i].fd); } if (! seen_stdin || ! seen_stdout || !seen_stderr) { fd = open ("/dev/null", O_RDWR); if (fd == -1) { /* The debug file descriptor is not dup'ed, so we can't do a trace output. */ _exit (8); } /* Make sure that the process has connected stdin. */ if (! seen_stdin && fd != 0) { if (dup2 (fd, 0) == -1) _exit (8); } if (! seen_stdout && fd != 1) { if (dup2 (fd, 1) == -1) _exit (8); } if (! seen_stderr && fd != 2) { if (dup2 (fd, 2) == -1) _exit (8); } if (fd != 0 && fd != 1 && fd != 2) close (fd); } execv (path, (char *const *) argv); /* Hmm: in that case we could write a special status code to the status-pipe. */ _exit (8); /* End child. */ } if (pid == -1) _exit (1); else _exit (0); } TRACE_LOG1 ("waiting for child process pid=%i", pid); _gpgme_io_waitpid (pid, 1, &status, &signo); if (status) return TRACE_SYSRES (-1); for (i = 0; fd_list[i].fd != -1; i++) { if (! (flags & IOSPAWN_FLAG_NOCLOSE)) _gpgme_io_close (fd_list[i].fd); /* No handle translation. */ fd_list[i].peer_name = fd_list[i].fd; } if (r_pid) *r_pid = pid; return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { fd_set readfds; fd_set writefds; unsigned int i; int any; int max_fd; int n; int count; /* Use a 1s timeout. */ struct timeval timeout = { 1, 0 }; void *dbg_help = NULL; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); FD_ZERO (&readfds); FD_ZERO (&writefds); max_fd = 0; if (nonblock) timeout.tv_sec = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if (fds[i].for_read) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &readfds)); FD_SET (fds[i].fd, &readfds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd); any = 1; } else if (fds[i].for_write) { if (fds[i].fd >= FD_SETSIZE) { TRACE_END (dbg_help, " -BAD- ]"); gpg_err_set_errno (EMFILE); return TRACE_SYSRES (-1); } assert (!FD_ISSET (fds[i].fd, &writefds)); FD_SET (fds[i].fd, &writefds); if (fds[i].fd > max_fd) max_fd = fds[i].fd; TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); do { count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL, &timeout); } while (count < 0 && errno == EINTR); if (count < 0) return TRACE_SYSRES (-1); TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i <= max_fd; i++) { if (FD_ISSET (i, &readfds)) TRACE_ADD1 (dbg_help, "r0x%x ", i); if (FD_ISSET (i, &writefds)) TRACE_ADD1 (dbg_help, "w0x%x ", i); } TRACE_END (dbg_help, "]"); } /* The variable N is used to optimize it a little bit. */ for (n = count, i = 0; i < nfds && n; i++) { if (fds[i].fd == -1) ; else if (fds[i].for_read) { if (FD_ISSET (fds[i].fd, &readfds)) { fds[i].signaled = 1; n--; } } else if (fds[i].for_write) { if (FD_ISSET (fds[i].fd, &writefds)) { fds[i].signaled = 1; n--; } } } return TRACE_SYSRES (count); } int _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags) { int nread; int saved_errno; struct iovec *iov; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd, "msg=%p, flags=%i", msg, flags); nread = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nread += iov->iov_len; iov++; } TRACE_LOG1 ("about to receive %d bytes", nread); do { nread = _gpgme_ath_recvmsg (fd, msg, flags); } while (nread == -1 && errno == EINTR); saved_errno = errno; if (nread > 0) { int nr = nread; iov = msg->msg_iov; while (nr > 0) { int len = nr > iov->iov_len ? iov->iov_len : nr; - TRACE_LOGBUF (msg->msg_iov->iov_base, len); + TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nr -= len; } } errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags) { int nwritten; struct iovec *iov; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd, "msg=%p, flags=%i", msg, flags); nwritten = 0; iov = msg->msg_iov; while (iov < msg->msg_iov + msg->msg_iovlen) { nwritten += iov->iov_len; iov++; } TRACE_LOG1 ("about to receive %d bytes", nwritten); iov = msg->msg_iov; while (nwritten > 0) { int len = nwritten > iov->iov_len ? iov->iov_len : nwritten; - TRACE_LOGBUF (msg->msg_iov->iov_base, len); + TRACE_LOGBUFX (msg->msg_iov->iov_base, len); iov++; nwritten -= len; } do { nwritten = _gpgme_ath_sendmsg (fd, msg, flags); } while (nwritten == -1 && errno == EINTR); return TRACE_SYSRES (nwritten); } int _gpgme_io_dup (int fd) { int new_fd; do new_fd = dup (fd); while (new_fd == -1 && errno == EINTR); TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd); return new_fd; } int _gpgme_io_socket (int domain, int type, int proto) { int res; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, proto=%i", type, proto); res = socket (domain, type, proto); return TRACE_SYSRES (res); } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { int res; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); do res = ath_connect (fd, addr, addrlen); while (res == -1 && errno == EINTR); return TRACE_SYSRES (res); } diff --git a/src/w32-glib-io.c b/src/w32-glib-io.c index 66dc9bf7..967c64e8 100644 --- a/src/w32-glib-io.c +++ b/src/w32-glib-io.c @@ -1,1085 +1,1085 @@ /* w32-glib-io.c - W32 Glib I/O functions Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2004, 2005 g10 Code GmbH This file is part of GPGME. GPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. GPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #include #include #include #include "util.h" #include "priv-io.h" #include "sema.h" #include "debug.h" #ifndef O_BINARY #ifdef _O_BINARY #define O_BINARY _O_BINARY #else #define O_BINARY 0 #endif #endif /* This file is an ugly hack to get GPGME working with glib on Windows targets. On Windows, you can not select() on file descriptors. The only way to check if there is something to read is to read something. This means that GPGME can not let glib check for data without letting glib also handle the data on Windows targets. The ugly consequence is that we need to work on GIOChannels in GPGME, creating a glib dependency. Also, we need to export an interface for the application to get at GPGME's GIOChannel. There is no good way to abstract all this with callbacks, because the whole thing is also interconnected with the creation of pipes and child processes. The following rule applies only to this I/O backend: * ALL operations must use the user defined event loop. GPGME can not anymore provide its own event loop. This is mostly a sanity requirement: Although we have in theory all information we need to make the GPGME W32 code for select still work, it would be a big complication and require changes throughout GPGME. Eventually, we probably have to bite the bullet and make some really nice callback interfaces to let the user control all this at a per-context level. */ #define MAX_SLAFD 256 static struct { int used; /* If this is not -1, then it's a libc file descriptor. */ int fd; /* If fd is -1, this is the Windows socket handle. */ int socket; GIOChannel *chan; /* The boolean PRIMARY is true if this file descriptor caused the allocation of CHAN. Only then should CHAN be destroyed when this FD is closed. This, together with the fact that dup'ed file descriptors are closed before the file descriptors from which they are dup'ed are closed, ensures that CHAN is always valid, and shared among all file descriptors referring to the same underlying object. The logic behind this is that there is only one reason for us to dup file descriptors anyway: to allow simpler book-keeping of file descriptors shared between GPGME and libassuan, which both want to close something. Using the same channel for these duplicates works just fine (and in fact, using different channels does not work because the W32 backend in glib does not support that: One would end up with several competing reader/writer threads. */ int primary; } giochannel_table[MAX_SLAFD]; static GIOChannel * find_channel (int fd) { if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used) return NULL; return giochannel_table[fd].chan; } /* Returns the FD or -1 on resource limit. */ int new_dummy_channel_from_fd (int cfd) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = NULL; giochannel_table[idx].fd = cfd; giochannel_table[idx].socket = INVALID_SOCKET; giochannel_table[idx].primary = 1; return idx; } /* Returns the FD or -1 on resource limit. */ int new_channel_from_fd (int cfd) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd); giochannel_table[idx].fd = cfd; giochannel_table[idx].socket = INVALID_SOCKET; giochannel_table[idx].primary = 1; g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL); g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE); return idx; } /* Returns the FD or -1 on resource limit. */ int new_channel_from_socket (int sock) { int idx; for (idx = 0; idx < MAX_SLAFD; idx++) if (! giochannel_table[idx].used) break; if (idx == MAX_SLAFD) { errno = EIO; return -1; } giochannel_table[idx].used = 1; giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock); giochannel_table[idx].fd = -1; giochannel_table[idx].socket = sock; giochannel_table[idx].primary = 1; g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL); g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE); return idx; } /* Compatibility interface. Obsolete. */ void * gpgme_get_giochannel (int fd) { return find_channel (fd); } /* Look up the giochannel for "file descriptor" FD. */ void * gpgme_get_fdptr (int fd) { return find_channel (fd); } /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ int _gpgme_io_fd2str (char *buf, int buflen, int fd) { HANDLE hndl; TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd); if (giochannel_table[fd].fd != -1) hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd); else hndl = (HANDLE) giochannel_table[fd].socket; TRACE_SUC1 ("syshd=%p", hndl); return snprintf (buf, buflen, "%d", (int) hndl); } void _gpgme_io_subsystem_init (void) { } static struct { _gpgme_close_notify_handler_t handler; void *value; } notify_table[MAX_SLAFD]; int _gpgme_io_read (int fd, void *buffer, size_t count) { int saved_errno = 0; gsize nread; GIOChannel *chan; GIOStatus status; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); chan = find_channel (fd); if (!chan) { TRACE_LOG ("no channel registered"); errno = EINVAL; return TRACE_SYSRES (-1); } TRACE_LOG1 ("channel %p", chan); { GError *err = NULL; status = g_io_channel_read_chars (chan, (gchar *) buffer, count, &nread, &err); if (err) { TRACE_LOG2 ("status %i, err %s", status, err->message); g_error_free (err); } } if (status == G_IO_STATUS_EOF) nread = 0; else if (status == G_IO_STATUS_AGAIN) { nread = -1; saved_errno = EAGAIN; } else if (status != G_IO_STATUS_NORMAL) { TRACE_LOG1 ("status %d", status); nread = -1; saved_errno = EIO; } if (nread != 0 && nread != -1) - TRACE_LOGBUF (buffer, nread); + TRACE_LOGBUFX (buffer, nread); errno = saved_errno; return TRACE_SYSRES (nread); } int _gpgme_io_write (int fd, const void *buffer, size_t count) { int saved_errno = 0; gsize nwritten; GIOChannel *chan; GIOStatus status; GError *err = NULL; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); - TRACE_LOGBUF (buffer, count); + TRACE_LOGBUFX (buffer, count); chan = find_channel (fd); if (!chan) { TRACE_LOG ("fd %d: no channel registered"); errno = EINVAL; return -1; } status = g_io_channel_write_chars (chan, (gchar *) buffer, count, &nwritten, &err); if (err) { TRACE_LOG1 ("write error: %s", err->message); g_error_free (err); } if (status == G_IO_STATUS_AGAIN) { nwritten = -1; saved_errno = EAGAIN; } else if (status != G_IO_STATUS_NORMAL) { nwritten = -1; saved_errno = EIO; } errno = saved_errno; return TRACE_SYSRES (nwritten); } int _gpgme_io_pipe (int filedes[2], int inherit_idx) { int fds[2]; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); #define PIPEBUF_SIZE 4096 if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1) return TRACE_SYSRES (-1); /* Make one end inheritable. */ if (inherit_idx == 0) { int new_read; new_read = _dup (fds[0]); _close (fds[0]); fds[0] = new_read; if (new_read < 0) { _close (fds[1]); return TRACE_SYSRES (-1); } } else if (inherit_idx == 1) { int new_write; new_write = _dup (fds[1]); _close (fds[1]); fds[1] = new_write; if (new_write < 0) { _close (fds[0]); return TRACE_SYSRES (-1); } } /* For _gpgme_io_close. */ filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]); if (filedes[inherit_idx] < 0) { int saved_errno = errno; _close (fds[0]); _close (fds[1]); errno = saved_errno; return TRACE_SYSRES (-1); } /* Now we have a pipe with the correct end inheritable. The other end should have a giochannel. */ filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]); if (filedes[1 - inherit_idx] < 0) { int saved_errno = errno; _gpgme_io_close (fds[inherit_idx]); _close (fds[1 - inherit_idx]); errno = saved_errno; return TRACE_SYSRES (-1); } return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p", filedes[0], (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd), filedes[1], (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd), giochannel_table[1 - inherit_idx].chan); } int _gpgme_io_close (int fd) { TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); if (fd < 0 || fd >= MAX_SLAFD) { errno = EBADF; return TRACE_SYSRES (-1); } assert (giochannel_table[fd].used); /* First call the notify handler. */ if (notify_table[fd].handler) { notify_table[fd].handler (fd, notify_table[fd].value); notify_table[fd].handler = NULL; notify_table[fd].value = NULL; } /* Then do the close. */ if (giochannel_table[fd].chan) { if (giochannel_table[fd].primary) g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL); g_io_channel_unref (giochannel_table[fd].chan); } else { /* Dummy entry, just close. */ assert (giochannel_table[fd].fd != -1); _close (giochannel_table[fd].fd); } giochannel_table[fd].used = 0; giochannel_table[fd].fd = -1; giochannel_table[fd].socket = INVALID_SOCKET; giochannel_table[fd].chan = NULL; giochannel_table[fd].primary = 0; TRACE_SUC (); return 0; } int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); if (fd < 0 || fd >= (int) DIM (notify_table)) { errno = EINVAL; return TRACE_SYSRES (-1); } notify_table[fd].handler = handler; notify_table[fd].value = value; return TRACE_SYSRES (0); } int _gpgme_io_set_nonblocking (int fd) { GIOChannel *chan; GIOStatus status; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); chan = find_channel (fd); if (!chan) { errno = EIO; return TRACE_SYSRES (-1); } status = g_io_channel_set_flags (chan, g_io_channel_get_flags (chan) | G_IO_FLAG_NONBLOCK, NULL); if (status != G_IO_STATUS_NORMAL) { #if 0 /* glib 1.9.2 does not implement set_flags and returns an error. */ errno = EIO; return TRACE_SYSRES (-1); #else TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)", status); #endif } return TRACE_SYSRES (0); } static char * build_commandline (char **argv) { int i; int n = 0; char *buf; char *p; /* We have to quote some things because under Windows the program parses the commandline and does some unquoting. We enclose the whole argument in double-quotes, and escape literal double-quotes as well as backslashes with a backslash. We end up with a trailing space at the end of the line, but that is harmless. */ for (i = 0; argv[i]; i++) { p = argv[i]; /* The leading double-quote. */ n++; while (*p) { /* An extra one for each literal that must be escaped. */ if (*p == '\\' || *p == '"') n++; n++; p++; } /* The trailing double-quote and the delimiter. */ n += 2; } /* And a trailing zero. */ n++; buf = p = malloc (n); if (!buf) return NULL; for (i = 0; argv[i]; i++) { char *argvp = argv[i]; *(p++) = '"'; while (*argvp) { if (*argvp == '\\' || *argvp == '"') *(p++) = '\\'; *(p++) = *(argvp++); } *(p++) = '"'; *(p++) = ' '; } *(p++) = 0; return buf; } int _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags, struct spawn_fd_item_s *fd_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* returns process handle */ 0, /* returns primary thread handle */ 0, /* returns pid */ 0 /* returns tid */ }; STARTUPINFO si; int cr_flags = (CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ())); int i; char **args; char *arg_string; /* FIXME. */ int debug_me = 0; int tmp_fd; char *tmp_name; TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, "path=%s", path); i = 0; while (argv[i]) { TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); i++; } /* We do not inherit any handles by default, and just insert those handles we want the child to have afterwards. But some handle values occur on the command line, and we need to move stdin/out/err to the right location. So we use a wrapper program which gets the information from a temporary file. */ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) { TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); return TRACE_SYSRES (-1); } TRACE_LOG1 ("tmp_name = %s", tmp_name); args = calloc (2 + i + 1, sizeof (*args)); args[0] = (char *) _gpgme_get_w32spawn_path (); args[1] = tmp_name; args[2] = path; memcpy (&args[3], &argv[1], i * sizeof (*args)); memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; arg_string = build_commandline (args); free (args); if (!arg_string) { close (tmp_fd); DeleteFile (tmp_name); return TRACE_SYSRES (-1); } memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; si.hStdInput = INVALID_HANDLE_VALUE; si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdError = INVALID_HANDLE_VALUE; cr_flags |= CREATE_SUSPENDED; if ((flags & IOSPAWN_FLAG_DETACHED)) cr_flags |= DETACHED_PROCESS; if (!CreateProcessA (_gpgme_get_w32spawn_path (), arg_string, &sec_attr, /* process security attributes */ &sec_attr, /* thread security attributes */ FALSE, /* inherit handles */ cr_flags, /* creation flags */ NULL, /* environment */ NULL, /* use current drive/directory */ &si, /* startup information */ &pi)) /* returns process information */ { TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ()); free (arg_string); close (tmp_fd); DeleteFile (tmp_name); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } free (arg_string); if (flags & IOSPAWN_FLAG_ALLOW_SET_FG) _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId); /* Insert the inherited handles. */ for (i = 0; fd_list[i].fd != -1; i++) { HANDLE hd; /* Make it inheritable for the wrapper process. */ if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (giochannel_table[fd_list[i].fd].fd), pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); TerminateProcess (pi.hProcess, 0); /* Just in case TerminateProcess didn't work, let the process fail on its own. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); CloseHandle (pi.hProcess); close (tmp_fd); DeleteFile (tmp_name); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } /* Return the child name of this handle. */ fd_list[i].peer_name = (int) hd; } /* Write the handle translation information to the temporary file. */ { /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex notation: "0xFEDCBA9876543210" with an extra white space after every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead for a time when a HANDLE is 64 bit. */ #define BUFFER_MAX 800 char line[BUFFER_MAX + 1]; int res; int written; size_t len; if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG)) strcpy (line, "~1 \n"); else strcpy (line, "\n"); for (i = 0; fd_list[i].fd != -1; i++) { /* Strip the newline. */ len = strlen (line) - 1; /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n", fd_list[i].fd, fd_list[i].dup_to, fd_list[i].peer_name, fd_list[i].arg_loc); /* Rather safe than sorry. */ line[BUFFER_MAX - 1] = '\n'; line[BUFFER_MAX] = '\0'; } len = strlen (line); written = 0; do { res = write (tmp_fd, &line[written], len - written); if (res > 0) written += res; } while (res > 0 || (res < 0 && errno == EAGAIN)); } close (tmp_fd); /* The temporary file is deleted by the gpgme-w32spawn process (hopefully). */ TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, " "dwProcessID=%d, dwThreadId=%d", pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); if (r_pid) *r_pid = (pid_t)pi.dwProcessId; if (ResumeThread (pi.hThread) < 0) TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ()); if (!CloseHandle (pi.hThread)) TRACE_LOG1 ("CloseHandle of thread failed: ec=%d", (int) GetLastError ()); TRACE_LOG1 ("process=%p", pi.hProcess); /* We don't need to wait for the process. */ if (!CloseHandle (pi.hProcess)) TRACE_LOG1 ("CloseHandle of process failed: ec=%d", (int) GetLastError ()); if (! (flags & IOSPAWN_FLAG_NOCLOSE)) { for (i = 0; fd_list[i].fd != -1; i++) _gpgme_io_close (fd_list[i].fd); } for (i = 0; fd_list[i].fd != -1; i++) if (fd_list[i].dup_to == -1) TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].peer_name); else TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : ((fd_list[i].dup_to == 1) ? "out" : "err")); return TRACE_SYSRES (0); } /* Select on the list of fds. Returns: -1 = error, 0 = timeout or nothing to select, > 0 = number of signaled fds. */ int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) { int npollfds; GPollFD *pollfds; int *pollfds_map; int i; int j; int any; int n; int count; /* Use a 1s timeout. */ int timeout = 1000; void *dbg_help = NULL; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); if (nonblock) timeout = 0; pollfds = calloc (nfds, sizeof *pollfds); if (!pollfds) return -1; pollfds_map = calloc (nfds, sizeof *pollfds_map); if (!pollfds_map) { free (pollfds); return -1; } npollfds = 0; TRACE_SEQ (dbg_help, "select on [ "); any = 0; for (i = 0; i < nfds; i++) { GIOChannel *chan = NULL; if (fds[i].fd == -1) continue; if ((fds[i].for_read || fds[i].for_write) && !(chan = find_channel (fds[i].fd))) { TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd); TRACE_END (dbg_help, "]"); assert (!"see log file"); } else if (fds[i].for_read ) { assert(chan); g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds); pollfds_map[npollfds] = i; TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd); npollfds++; any = 1; } else if (fds[i].for_write) { assert(chan); g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds); pollfds_map[npollfds] = i; TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd); npollfds++; any = 1; } fds[i].signaled = 0; } TRACE_END (dbg_help, "]"); if (!any) { count = 0; goto leave; } count = g_io_channel_win32_poll (pollfds, npollfds, timeout); if (count < 0) { int saved_errno = errno; errno = saved_errno; goto leave; } TRACE_SEQ (dbg_help, "select OK [ "); if (TRACE_ENABLED (dbg_help)) { for (i = 0; i < npollfds; i++) { if ((pollfds[i].revents & G_IO_IN)) TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd); if ((pollfds[i].revents & G_IO_OUT)) TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd); } TRACE_END (dbg_help, "]"); } /* COUNT is used to stop the lop as soon as possible. */ for (n = count, i = 0; i < npollfds && n; i++) { j = pollfds_map[i]; assert (j >= 0 && j < nfds); if (fds[j].fd == -1) ; else if (fds[j].for_read) { if ((pollfds[i].revents & G_IO_IN)) { fds[j].signaled = 1; n--; } } else if (fds[j].for_write) { if ((pollfds[i].revents & G_IO_OUT)) { fds[j].signaled = 1; n--; } } } leave: free (pollfds); free (pollfds_map); return TRACE_SYSRES (count); } int _gpgme_io_dup (int fd) { int newfd; GIOChannel *chan; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd); if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used) { errno = EINVAL; return TRACE_SYSRES (-1); } for (newfd = 0; newfd < MAX_SLAFD; newfd++) if (! giochannel_table[newfd].used) break; if (newfd == MAX_SLAFD) { errno = EIO; return TRACE_SYSRES (-1); } chan = giochannel_table[fd].chan; g_io_channel_ref (chan); giochannel_table[newfd].used = 1; giochannel_table[newfd].chan = chan; giochannel_table[newfd].fd = -1; giochannel_table[newfd].socket = INVALID_SOCKET; giochannel_table[newfd].primary = 0; return TRACE_SYSRES (newfd); } static int wsa2errno (int err) { switch (err) { case WSAENOTSOCK: return EINVAL; case WSAEWOULDBLOCK: return EAGAIN; case ERROR_BROKEN_PIPE: return EPIPE; case WSANOTINITIALISED: return ENOSYS; default: return EIO; } } int _gpgme_io_socket (int domain, int type, int proto) { int res; int fd; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain, "type=%i, protp=%i", type, proto); res = socket (domain, type, proto); if (res == INVALID_SOCKET) { errno = wsa2errno (WSAGetLastError ()); return TRACE_SYSRES (-1); } fd = new_channel_from_socket (res); if (fd < 0) { int saved_errno = errno; closesocket (res); errno = saved_errno; return TRACE_SYSRES (-1); } TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res); return fd; } int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen) { GIOChannel *chan; int sockfd; int res; GIOFlags flags; GIOStatus status; GError *err = NULL; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd, "addr=%p, addrlen=%i", addr, addrlen); chan = find_channel (fd); if (! chan) { errno = EINVAL; return TRACE_SYSRES (-1); } flags = g_io_channel_get_flags (chan); if (flags & G_IO_FLAG_NONBLOCK) { status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err); if (err) { TRACE_LOG1 ("setting flags error: %s", err->message); g_error_free (err); err = NULL; } if (status != G_IO_STATUS_NORMAL) { errno = EIO; return TRACE_SYSRES (-1); } } sockfd = giochannel_table[fd].socket; if (sockfd == INVALID_SOCKET) { errno = EINVAL; return TRACE_SYSRES (-1); } TRACE_LOG1 ("connect sockfd=0x%x", sockfd); res = connect (sockfd, addr, addrlen); /* FIXME: Error ignored here. */ if (! (flags & G_IO_FLAG_NONBLOCK)) g_io_channel_set_flags (chan, flags, NULL); if (res) { TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ()); errno = wsa2errno (WSAGetLastError ()); return TRACE_SYSRES (-1); } return TRACE_SUC (); }