/* Set the current position from where the next read or write starts in the data object with the handle DH to OFFSET, relativ to WHENCE. */ gpgme_off_t gpgme_data_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) { TRACE_BEG2 (DEBUG_DATA, "gpgme_data_seek", dh, "offset=%lli, whence=%i", offset, whence); if (!dh) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } if (!dh->cbs->seek) { gpg_err_set_errno (ENOSYS); return TRACE_SYSRES (-1); } /* For relative movement, we must take into account the actual position of the read counter. */ if (whence == SEEK_CUR) offset -= dh->pending_len; offset = (*dh->cbs->seek) (dh, offset, whence); if (offset >= 0) dh->pending_len = 0; return TRACE_SYSRES (offset); }
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); }
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); }
int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, void *value) { int i; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, "close_handler=%p/%p", handler, value); assert (fd != -1); LOCK (notify_table_lock); for (i=0; i < DIM (notify_table); i++) if (notify_table[i].inuse && notify_table[i].fd == fd) break; if (i == DIM (notify_table)) for (i = 0; i < DIM (notify_table); i++) if (!notify_table[i].inuse) break; if (i == DIM (notify_table)) { UNLOCK (notify_table_lock); errno = EINVAL; return TRACE_SYSRES (-1); } notify_table[i].fd = fd; notify_table[i].handler = handler; notify_table[i].value = value; notify_table[i].inuse = 1; UNLOCK (notify_table_lock); return TRACE_SYSRES (0); }
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; }
static ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { /* Due to the peculiarities of the W32 API we can't use write for a network socket and thus we try to use send first and fallback to write if send detects that it is not a network socket. */ int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_write", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); #ifdef HAVE_W32CE_SYSTEM /* This is a bit of a hack to support stdout over ssh. Note that fread buffers fully while getchar is line buffered. Weird, but that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are special handle values that shouldn't occur in the wild. */ if (fd == ASSUAN_STDOUT) { res = fwrite (buffer, 1, size, stdout); return TRACE_SYSRES (res); } #endif res = send ((int)fd, buffer, size, 0); if (res == -1 && WSAGetLastError () == WSAENOTSOCK) { DWORD nwrite; TRACE_LOG ("send call failed - trying WriteFile"); res = WriteFile (fd, buffer, size, &nwrite, NULL); if (! res) { TRACE_LOG1 ("WriteFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: gpg_err_set_errno (EPIPE); break; case ERROR_PIPE_NOT_CONNECTED: case ERROR_BUSY: gpg_err_set_errno (EAGAIN); break; default: gpg_err_set_errno (EIO); break; } res = -1; } else res = (int) nwrite; } else if (res == -1) TRACE_LOG1 ("send call failed: rc=%d", (int)GetLastError ()); return TRACE_SYSRES (res); }
static ssize_t old_user_read (gpgme_data_t dh, void *buffer, size_t size) { gpgme_error_t err; size_t amt; TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_read", dh, "buffer=%p, size=%u", buffer, size); err = (*dh->data.old_user.cb) (dh->data.old_user.handle, buffer, size, &amt); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES (amt); }
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); errno = saved_errno; return TRACE_SYSRES (nread); }
static off_t old_user_seek (gpgme_data_t dh, off_t offset, int whence) { gpgme_error_t err; TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_seek", dh, "offset=%llu, whence=%i", offset, whence); if (whence != SEEK_SET || offset) { errno = EINVAL; return TRACE_SYSRES (-1); } err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL); if (err) return TRACE_SYSRES (gpgme_error_to_errno (err)); return TRACE_SYSRES (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_close (int fd) { int i; _gpgme_close_notify_handler_t handler = NULL; void *value = NULL; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); if (fd == -1) { errno = EBADF; return TRACE_SYSRES (-1); } kill_reader (fd); kill_writer (fd); LOCK (notify_table_lock); for (i = 0; i < DIM (notify_table); i++) { if (notify_table[i].inuse && notify_table[i].fd == fd) { handler = notify_table[i].handler; value = notify_table[i].value; notify_table[i].handler = NULL; notify_table[i].value = NULL; notify_table[i].inuse = 0; break; } } UNLOCK (notify_table_lock); if (handler) handler (fd, value); if (!CloseHandle (fd_to_handle (fd))) { TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ()); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } return TRACE_SYSRES (0); }
/* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. */ gpgme_ssize_t gpgme_io_write (int fd, const void *buffer, size_t count) { int ret; TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); ret = _gpgme_io_write (fd, buffer, count); return TRACE_SYSRES (ret); }
/* Read up to SIZE bytes into buffer BUFFER from the data object with the handle DH. Return the number of characters read, 0 on EOF and -1 on error. If an error occurs, errno is set. */ gpgme_ssize_t gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size) { gpgme_ssize_t res; TRACE_BEG2 (DEBUG_DATA, "gpgme_data_read", dh, "buffer=%p, size=%u", buffer, size); if (!dh) { gpg_err_set_errno (EINVAL); return TRACE_SYSRES (-1); } if (!dh->cbs->read) { gpg_err_set_errno (ENOSYS); return TRACE_SYSRES (-1); } do res = (*dh->cbs->read) (dh, buffer, size); while (res < 0 && errno == EINTR); 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); res = connect (fd, addr, addrlen); if (!res) { errno = wsa2errno (WSAGetLastError ()); return TRACE_SYSRES (-1); } return TRACE_SUC (); }
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; }
/* This function provides access to the internal write function. It is to be used by user callbacks to return data to gpgme. See gpgme_passphrase_cb_t and gpgme_edit_cb_t. Note that this is a variant of gpgme_io_write which guarantees that all COUNT bytes are written or an error is return. Returns: 0 on success or -1 on error and the sets errno. */ int gpgme_io_writen (int fd, const void *buffer_arg, size_t count) { const char *buffer = buffer_arg; int ret = 0; TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_writen", fd, "buffer=%p, count=%u", buffer, count); while (count) { ret = _gpgme_io_write (fd, buffer, count); if (ret < 0) break; buffer += ret; count -= ret; ret = 0; } return TRACE_SYSRES (ret); }
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); 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_socket (int domain, int type, int proto) { int res; 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); } TRACE_SUC1 ("socket=0x%x", res); return res; }
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_pipe (int filedes[2], int inherit_idx) { HANDLE rh; HANDLE wh; SECURITY_ATTRIBUTES sec_attr; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); memset (&sec_attr, 0, sizeof (sec_attr)); sec_attr.nLength = sizeof (sec_attr); sec_attr.bInheritHandle = FALSE; if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE)) { TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ()); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } /* Make one end inheritable. */ if (inherit_idx == 0) { struct writer_context_s *ctx; HANDLE hd; if (!DuplicateHandle (GetCurrentProcess(), rh, GetCurrentProcess(), &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); CloseHandle (rh); CloseHandle (wh); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } CloseHandle (rh); rh = hd; ctx = find_writer (handle_to_fd (wh), 0); assert (ctx == NULL); ctx = find_writer (handle_to_fd (wh), 1); if (!ctx) { CloseHandle (rh); CloseHandle (wh); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } } else if (inherit_idx == 1) { struct reader_context_s *ctx; HANDLE hd; if (!DuplicateHandle( GetCurrentProcess(), wh, GetCurrentProcess(), &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); CloseHandle (rh); CloseHandle (wh); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } CloseHandle (wh); wh = hd; ctx = find_reader (handle_to_fd (rh), 0); assert (ctx == NULL); ctx = find_reader (handle_to_fd (rh), 1); if (!ctx) { CloseHandle (rh); CloseHandle (wh); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } } filedes[0] = handle_to_fd (rh); filedes[1] = handle_to_fd (wh); return TRACE_SUC2 ("read=%p, write=%p", rh, wh); }
int _gpgme_io_dup (int fd) { HANDLE handle = fd_to_handle (fd); HANDLE new_handle = fd_to_handle (fd); int i; struct reader_context_s *rd_ctx; struct writer_context_s *wt_ctx; TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd); if (!DuplicateHandle (GetCurrentProcess(), handle, GetCurrentProcess(), &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ()); /* FIXME: Translate error code. */ errno = EIO; return TRACE_SYSRES (-1); } rd_ctx = find_reader (fd, 0); if (rd_ctx) { /* No need for locking, as the only races are against the reader thread itself, which doesn't touch refcount. */ rd_ctx->refcount++; LOCK (reader_table_lock); for (i = 0; i < reader_table_size; i++) if (!reader_table[i].used) break; /* FIXME. */ assert (i != reader_table_size); reader_table[i].fd = handle_to_fd (new_handle); reader_table[i].context = rd_ctx; reader_table[i].used = 1; UNLOCK (reader_table_lock); } wt_ctx = find_writer (fd, 0); if (wt_ctx) { /* No need for locking, as the only races are against the writer thread itself, which doesn't touch refcount. */ wt_ctx->refcount++; LOCK (writer_table_lock); for (i = 0; i < writer_table_size; i++) if (!writer_table[i].used) break; /* FIXME. */ assert (i != writer_table_size); writer_table[i].fd = handle_to_fd (new_handle); writer_table[i].context = wt_ctx; writer_table[i].used = 1; UNLOCK (writer_table_lock); } return TRACE_SYSRES (handle_to_fd (new_handle)); }
int _gpgme_io_read (int fd, void *buffer, size_t count) { int nread; struct reader_context_s *ctx; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, "buffer=%p, count=%u", buffer, count); ctx = find_reader (fd, 1); if (!ctx) { errno = EBADF; return TRACE_SYSRES (-1); } if (ctx->eof_shortcut) return TRACE_SYSRES (0); LOCK (ctx->mutex); if (ctx->readpos == ctx->writepos && !ctx->error) { /* No data available. */ UNLOCK (ctx->mutex); TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd); WaitForSingleObject (ctx->have_data_ev, INFINITE); TRACE_LOG1 ("data from thread %p available", ctx->thread_hd); LOCK (ctx->mutex); } if (ctx->readpos == ctx->writepos || ctx->error) { UNLOCK (ctx->mutex); ctx->eof_shortcut = 1; if (ctx->eof) return TRACE_SYSRES (0); if (!ctx->error) { TRACE_LOG ("EOF but ctx->eof flag not set"); return 0; } errno = ctx->error_code; return TRACE_SYSRES (-1); } nread = ctx->readpos < ctx->writepos ? ctx->writepos - ctx->readpos : READBUF_SIZE - ctx->readpos; if (nread > count) nread = count; memcpy (buffer, ctx->buffer + ctx->readpos, nread); ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE; if (ctx->readpos == ctx->writepos && !ctx->eof) { if (!ResetEvent (ctx->have_data_ev)) { TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } } if (!SetEvent (ctx->have_space_ev)) { TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_space_ev, (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); TRACE_LOGBUF (buffer, nread); return TRACE_SYSRES (nread); }
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; 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); }
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 (); }
/* 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); }
/* 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) { HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS]; int waitidx[MAXIMUM_WAIT_OBJECTS]; int code; int nwait; int i; int any; int count; void *dbg_help; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, "nfds=%u, nonblock=%u", nfds, nonblock); restart: TRACE_SEQ (dbg_help, "select on [ "); any = 0; nwait = 0; count = 0; for (i=0; i < nfds; i++) { if (fds[i].fd == -1) continue; fds[i].signaled = 0; if (fds[i].for_read || fds[i].for_write) { if (fds[i].for_read) { struct reader_context_s *ctx = find_reader (fds[i].fd,0); if (!ctx) TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)", fds[i].fd); else { if (nwait >= DIM (waitbuf)) { TRACE_END (dbg_help, "oops ]"); TRACE_LOG ("Too many objects for WFMO!"); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } waitidx[nwait] = i; waitbuf[nwait++] = ctx->have_data_ev; } TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd); any = 1; } else if (fds[i].for_write) { struct writer_context_s *ctx = find_writer (fds[i].fd,0); if (!ctx) TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)", fds[i].fd); else { if (nwait >= DIM (waitbuf)) { TRACE_END (dbg_help, "oops ]"); TRACE_LOG ("Too many objects for WFMO!"); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } waitidx[nwait] = i; waitbuf[nwait++] = ctx->is_empty; } TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); any = 1; } } } TRACE_END (dbg_help, "]"); if (!any) return TRACE_SYSRES (0); code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000); if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait) { /* This WFMO is a really silly function: It does return either the index of the signaled object or if 2 objects have been signalled at the same time, the index of the object with the lowest object is returned - so and how do we find out how many objects have been signaled???. The only solution I can imagine is to test each object starting with the returned index individually - how dull. */ any = 0; for (i = code - WAIT_OBJECT_0; i < nwait; i++) { if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0) { assert (waitidx[i] >=0 && waitidx[i] < nfds); fds[waitidx[i]].signaled = 1; any = 1; count++; } } if (!any) { TRACE_LOG ("no signaled objects found after WFMO"); count = -1; } } else if (code == WAIT_TIMEOUT) TRACE_LOG ("WFMO timed out"); else if (code == WAIT_FAILED) { int le = (int) GetLastError (); if (le == ERROR_INVALID_HANDLE) { int k; int j = handle_to_fd (waitbuf[i]); TRACE_LOG1 ("WFMO invalid handle %d removed", j); for (k = 0 ; k < nfds; k++) { if (fds[k].fd == j) { fds[k].for_read = fds[k].for_write = 0; goto restart; } } TRACE_LOG (" oops, or not???"); } TRACE_LOG1 ("WFMO failed: %d", le); count = -1; } else { TRACE_LOG1 ("WFMO returned %d", code); count = -1; } if (count > 0) { TRACE_SEQ (dbg_help, "select OK [ "); for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) continue; if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled) TRACE_ADD2 (dbg_help, "%c0x%x ", fds[i].for_read ? 'r' : 'w', fds[i].fd); } TRACE_END (dbg_help, "]"); } if (count < 0) { /* FIXME: Should determine a proper error code. */ errno = EIO; } return TRACE_SYSRES (count); }
static ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { /* Due to the peculiarities of the W32 API we can't use read for a network socket and thus we try to use recv first and fallback to read if recv detects that it is not a network socket. */ int res; TRACE_BEG3 (ctx, ASSUAN_LOG_SYSIO, "__assuan_read", ctx, "fd=0x%x, buffer=%p, size=%i", fd, buffer, size); #ifdef HAVE_W32CE_SYSTEM /* This is a bit of a hack to support stdin over ssh. Note that fread buffers fully while getchar is line buffered. Weird, but that's the way it is. ASSUAN_STDIN and ASSUAN_STDOUT are special handle values that shouldn't occur in the wild. */ if (fd == ASSUAN_STDIN) { int i = 0; int chr; while (i < size) { chr = getchar(); if (chr == EOF) break; ((char*)buffer)[i++] = (char) chr; if (chr == '\n') break; } return TRACE_SYSRES (i); } #endif res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) { TRACE_LOG1 ("recv failed: rc=%d", (int)WSAGetLastError ()); switch (WSAGetLastError ()) { case WSAENOTSOCK: { DWORD nread = 0; res = ReadFile (fd, buffer, size, &nread, NULL); if (! res) { TRACE_LOG1 ("ReadFile failed: rc=%d", (int)GetLastError ()); switch (GetLastError ()) { case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; case ERROR_PIPE_NOT_CONNECTED: case ERROR_BUSY: gpg_err_set_errno (EAGAIN); break; default: gpg_err_set_errno (EIO); } res = -1; } else res = (int) nread; } break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return TRACE_SYSRES (res); }
int _gpgme_io_write (int fd, const void *buffer, size_t count) { struct writer_context_s *ctx; TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, "buffer=%p, count=%u", buffer, count); TRACE_LOGBUF (buffer, count); if (count == 0) return TRACE_SYSRES (0); ctx = find_writer (fd, 0); if (!ctx) return TRACE_SYSRES (-1); LOCK (ctx->mutex); if (!ctx->error && ctx->nbytes) { /* Bytes are pending for send. */ /* Reset the is_empty event. Better safe than sorry. */ if (!ResetEvent (ctx->is_empty)) { TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd); WaitForSingleObject (ctx->is_empty, INFINITE); TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd); LOCK (ctx->mutex); } if (ctx->error) { UNLOCK (ctx->mutex); if (ctx->error_code == ERROR_NO_DATA) errno = EPIPE; else errno = EIO; return TRACE_SYSRES (-1); } /* If no error occured, the number of bytes in the buffer must be zero. */ assert (!ctx->nbytes); if (count > WRITEBUF_SIZE) count = WRITEBUF_SIZE; memcpy (ctx->buffer, buffer, count); ctx->nbytes = count; /* We have to reset the is_empty event early, because it is also used by the select() implementation to probe the channel. */ if (!ResetEvent (ctx->is_empty)) { TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } if (!SetEvent (ctx->have_data)) { TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); UNLOCK (ctx->mutex); /* FIXME: Should translate the error code. */ errno = EIO; return TRACE_SYSRES (-1); } UNLOCK (ctx->mutex); return TRACE_SYSRES ((int) count); }