1 /* w32-glib-io.c - W32 Glib I/O functions
2 Copyright (C) 2000 Werner Koch (dd9jn)
3 Copyright (C) 2001, 2002, 2004, 2005 g10 Code GmbH
5 This file is part of GPGME.
7 GPGME is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of
10 the License, or (at your option) any later version.
12 GPGME is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
51 #define O_BINARY _O_BINARY
58 /* This file is an ugly hack to get GPGME working with glib on Windows
59 targets. On Windows, you can not select() on file descriptors.
60 The only way to check if there is something to read is to read
61 something. This means that GPGME can not let glib check for data
62 without letting glib also handle the data on Windows targets.
64 The ugly consequence is that we need to work on GIOChannels in
65 GPGME, creating a glib dependency. Also, we need to export an
66 interface for the application to get at GPGME's GIOChannel. There
67 is no good way to abstract all this with callbacks, because the
68 whole thing is also interconnected with the creation of pipes and
71 The following rule applies only to this I/O backend:
73 * ALL operations must use the user defined event loop. GPGME can
74 not anymore provide its own event loop. This is mostly a sanity
75 requirement: Although we have in theory all information we need to
76 make the GPGME W32 code for select still work, it would be a big
77 complication and require changes throughout GPGME.
79 Eventually, we probably have to bite the bullet and make some
80 really nice callback interfaces to let the user control all this at
81 a per-context level. */
90 /* If this is not -1, then it's a libc file descriptor. */
92 /* If fd is -1, this is the Windows socket handle. */
96 /* The boolean PRIMARY is true if this file descriptor caused the
97 allocation of CHAN. Only then should CHAN be destroyed when this
98 FD is closed. This, together with the fact that dup'ed file
99 descriptors are closed before the file descriptors from which
100 they are dup'ed are closed, ensures that CHAN is always valid,
101 and shared among all file descriptors refering to the same
104 The logic behind this is that there is only one reason for us to
105 dup file descriptors anyway: to allow simpler book-keeping of
106 file descriptors shared between GPGME and libassuan, which both
107 want to close something. Using the same channel for these
108 duplicates works just fine (and in fact, using different channels
109 does not work because the W32 backend in glib does not support
110 that: One would end up with several competing reader/writer
113 } giochannel_table[MAX_SLAFD];
117 find_channel (int fd)
119 if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
122 return giochannel_table[fd].chan;
126 /* Returns the FD or -1 on resource limit. */
128 new_dummy_channel_from_fd (int cfd)
132 for (idx = 0; idx < MAX_SLAFD; idx++)
133 if (! giochannel_table[idx].used)
136 if (idx == MAX_SLAFD)
142 giochannel_table[idx].used = 1;
143 giochannel_table[idx].chan = NULL;
144 giochannel_table[idx].fd = cfd;
145 giochannel_table[idx].socket = INVALID_SOCKET;
146 giochannel_table[idx].primary = 1;
152 /* Returns the FD or -1 on resource limit. */
154 new_channel_from_fd (int cfd)
158 for (idx = 0; idx < MAX_SLAFD; idx++)
159 if (! giochannel_table[idx].used)
162 if (idx == MAX_SLAFD)
168 giochannel_table[idx].used = 1;
169 giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd);
170 giochannel_table[idx].fd = cfd;
171 giochannel_table[idx].socket = INVALID_SOCKET;
172 giochannel_table[idx].primary = 1;
174 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
175 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
181 /* Returns the FD or -1 on resource limit. */
183 new_channel_from_socket (int sock)
187 for (idx = 0; idx < MAX_SLAFD; idx++)
188 if (! giochannel_table[idx].used)
191 if (idx == MAX_SLAFD)
197 giochannel_table[idx].used = 1;
198 giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock);
199 giochannel_table[idx].fd = -1;
200 giochannel_table[idx].socket = sock;
201 giochannel_table[idx].primary = 1;
203 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
204 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
210 /* Compatibility interface. Obsolete. */
212 gpgme_get_giochannel (int fd)
214 return find_channel (fd);
218 /* Look up the giochannel for "file descriptor" FD. */
220 gpgme_get_fdptr (int fd)
222 return find_channel (fd);
226 /* Write the printable version of FD to the buffer BUF of length
227 BUFLEN. The printable version is the representation on the command
228 line that the child process expects. */
230 _gpgme_io_fd2str (char *buf, int buflen, int fd)
234 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
235 if (giochannel_table[fd].fd != -1)
236 hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd);
238 hndl = (HANDLE) giochannel_table[fd].socket;
240 TRACE_SUC1 ("syshd=%p", hndl);
242 return snprintf (buf, buflen, "%d", (int) hndl);
247 _gpgme_io_subsystem_init (void)
254 _gpgme_close_notify_handler_t handler;
256 } notify_table[MAX_SLAFD];
260 _gpgme_io_read (int fd, void *buffer, size_t count)
266 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
267 "buffer=%p, count=%u", buffer, count);
269 chan = find_channel (fd);
272 TRACE_LOG ("no channel registered");
274 return TRACE_SYSRES (-1);
276 TRACE_LOG1 ("channel %p", chan);
280 status = g_io_channel_read_chars (chan, (gchar *) buffer,
281 count, &nread, &err);
284 TRACE_LOG2 ("status %i, err %s", status, err->message);
289 if (status == G_IO_STATUS_EOF)
291 else if (status == G_IO_STATUS_AGAIN)
294 saved_errno = EAGAIN;
296 else if (status != G_IO_STATUS_NORMAL)
298 TRACE_LOG1 ("status %d", status);
303 if (nread != 0 && nread != -1)
304 TRACE_LOGBUF (buffer, nread);
307 return TRACE_SYSRES (nread);
312 _gpgme_io_write (int fd, const void *buffer, size_t count)
320 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
321 "buffer=%p, count=%u", buffer, count);
322 TRACE_LOGBUF (buffer, count);
324 chan = find_channel (fd);
327 TRACE_LOG ("fd %d: no channel registered");
332 status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
336 TRACE_LOG1 ("write error: %s", err->message);
340 if (status == G_IO_STATUS_AGAIN)
343 saved_errno = EAGAIN;
345 else if (status != G_IO_STATUS_NORMAL)
352 return TRACE_SYSRES (nwritten);
357 _gpgme_io_pipe (int filedes[2], int inherit_idx)
361 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
362 "inherit_idx=%i (GPGME uses it for %s)",
363 inherit_idx, inherit_idx ? "reading" : "writing");
365 #define PIPEBUF_SIZE 4096
366 if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
367 return TRACE_SYSRES (-1);
369 /* Make one end inheritable. */
370 if (inherit_idx == 0)
374 new_read = _dup (fds[0]);
381 return TRACE_SYSRES (-1);
384 else if (inherit_idx == 1)
388 new_write = _dup (fds[1]);
395 return TRACE_SYSRES (-1);
399 /* For _gpgme_io_close. */
400 filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
401 if (filedes[inherit_idx] < 0)
403 int saved_errno = errno;
408 return TRACE_SYSRES (-1);
411 /* Now we have a pipe with the correct end inheritable. The other end
412 should have a giochannel. */
413 filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
414 if (filedes[1 - inherit_idx] < 0)
416 int saved_errno = errno;
418 _gpgme_io_close (fds[inherit_idx]);
419 _close (fds[1 - inherit_idx]);
421 return TRACE_SYSRES (-1);
424 return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
426 (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
428 (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
429 giochannel_table[1 - inherit_idx].chan);
434 _gpgme_io_close (int fd)
436 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
438 if (fd < 0 || fd >= MAX_SLAFD)
441 return TRACE_SYSRES (-1);
444 assert (giochannel_table[fd].used);
446 /* First call the notify handler. */
447 if (notify_table[fd].handler)
449 notify_table[fd].handler (fd, notify_table[fd].value);
450 notify_table[fd].handler = NULL;
451 notify_table[fd].value = NULL;
454 /* Then do the close. */
455 if (giochannel_table[fd].chan)
457 if (giochannel_table[fd].primary)
458 g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
460 g_io_channel_unref (giochannel_table[fd].chan);
464 /* Dummy entry, just close. */
465 assert (giochannel_table[fd].fd != -1);
466 _close (giochannel_table[fd].fd);
469 giochannel_table[fd].used = 0;
470 giochannel_table[fd].fd = -1;
471 giochannel_table[fd].socket = INVALID_SOCKET;
472 giochannel_table[fd].chan = NULL;
473 giochannel_table[fd].primary = 0;
481 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
484 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
485 "close_handler=%p/%p", handler, value);
489 if (fd < 0 || fd >= (int) DIM (notify_table))
492 return TRACE_SYSRES (-1);
494 notify_table[fd].handler = handler;
495 notify_table[fd].value = value;
496 return TRACE_SYSRES (0);
501 _gpgme_io_set_nonblocking (int fd)
506 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
508 chan = find_channel (fd);
512 return TRACE_SYSRES (-1);
515 status = g_io_channel_set_flags (chan,
516 g_io_channel_get_flags (chan) |
517 G_IO_FLAG_NONBLOCK, NULL);
519 if (status != G_IO_STATUS_NORMAL)
522 /* glib 1.9.2 does not implement set_flags and returns an
525 return TRACE_SYSRES (-1);
527 TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
532 return TRACE_SYSRES (0);
537 build_commandline (char **argv)
544 /* We have to quote some things because under Windows the program
545 parses the commandline and does some unquoting. We enclose the
546 whole argument in double-quotes, and escape literal double-quotes
547 as well as backslashes with a backslash. We end up with a
548 trailing space at the end of the line, but that is harmless. */
549 for (i = 0; argv[i]; i++)
552 /* The leading double-quote. */
556 /* An extra one for each literal that must be escaped. */
557 if (*p == '\\' || *p == '"')
562 /* The trailing double-quote and the delimiter. */
565 /* And a trailing zero. */
568 buf = p = malloc (n);
571 for (i = 0; argv[i]; i++)
573 char *argvp = argv[i];
578 if (*argvp == '\\' || *argvp == '"')
592 _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
593 struct spawn_fd_item_s *fd_list,
594 void (*atfork) (void *opaque, int reserved),
595 void *atforkvalue, pid_t *r_pid)
597 SECURITY_ATTRIBUTES sec_attr;
598 PROCESS_INFORMATION pi =
600 NULL, /* returns process handle */
601 0, /* returns primary thread handle */
606 int cr_flags = (CREATE_DEFAULT_ERROR_MODE
607 | GetPriorityClass (GetCurrentProcess ()));
616 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
621 TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
625 /* We do not inherit any handles by default, and just insert those
626 handles we want the child to have afterwards. But some handle
627 values occur on the command line, and we need to move
628 stdin/out/err to the right location. So we use a wrapper program
629 which gets the information from a temporary file. */
630 if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
632 TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
633 return TRACE_SYSRES (-1);
635 TRACE_LOG1 ("tmp_name = %s", tmp_name);
637 args = calloc (2 + i + 1, sizeof (*args));
638 args[0] = (char *) _gpgme_get_w32spawn_path ();
641 memcpy (&args[3], &argv[1], i * sizeof (*args));
643 memset (&sec_attr, 0, sizeof sec_attr);
644 sec_attr.nLength = sizeof sec_attr;
645 sec_attr.bInheritHandle = FALSE;
647 arg_string = build_commandline (args);
652 DeleteFile (tmp_name);
653 return TRACE_SYSRES (-1);
656 memset (&si, 0, sizeof si);
658 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
659 si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
660 si.hStdInput = INVALID_HANDLE_VALUE;
661 si.hStdOutput = INVALID_HANDLE_VALUE;
662 si.hStdError = INVALID_HANDLE_VALUE;
664 cr_flags |= CREATE_SUSPENDED;
665 cr_flags |= DETACHED_PROCESS;
666 if (!CreateProcessA (_gpgme_get_w32spawn_path (),
668 &sec_attr, /* process security attributes */
669 &sec_attr, /* thread security attributes */
670 FALSE, /* inherit handles */
671 cr_flags, /* creation flags */
672 NULL, /* environment */
673 NULL, /* use current drive/directory */
674 &si, /* startup information */
675 &pi)) /* returns process information */
677 TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
680 DeleteFile (tmp_name);
682 /* FIXME: Should translate the error code. */
684 return TRACE_SYSRES (-1);
689 if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
690 _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
692 /* Insert the inherited handles. */
693 for (i = 0; fd_list[i].fd != -1; i++)
697 /* Make it inheritable for the wrapper process. */
698 if (!DuplicateHandle (GetCurrentProcess(),
699 _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
700 pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
702 TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
703 TerminateProcess (pi.hProcess, 0);
704 /* Just in case TerminateProcess didn't work, let the
705 process fail on its own. */
706 ResumeThread (pi.hThread);
707 CloseHandle (pi.hThread);
708 CloseHandle (pi.hProcess);
711 DeleteFile (tmp_name);
713 /* FIXME: Should translate the error code. */
715 return TRACE_SYSRES (-1);
717 /* Return the child name of this handle. */
718 fd_list[i].peer_name = (int) hd;
721 /* Write the handle translation information to the temporary
724 /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
725 notation: "0xFEDCBA9876543210" with an extra white space after
726 every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
727 for a time when a HANDLE is 64 bit. */
728 #define BUFFER_MAX 800
729 char line[BUFFER_MAX + 1];
734 if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
735 strcpy (line, "~1 \n");
738 for (i = 0; fd_list[i].fd != -1; i++)
740 /* Strip the newline. */
741 len = strlen (line) - 1;
743 /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
744 snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
745 fd_list[i].fd, fd_list[i].dup_to,
746 fd_list[i].peer_name, fd_list[i].arg_loc);
747 /* Rather safe than sorry. */
748 line[BUFFER_MAX - 1] = '\n';
749 line[BUFFER_MAX] = '\0';
755 res = write (tmp_fd, &line[written], len - written);
759 while (res > 0 || (res < 0 && errno == EAGAIN));
762 /* The temporary file is deleted by the gpgme-w32spawn process
765 TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
766 "dwProcessID=%d, dwThreadId=%d",
767 pi.hProcess, pi.hThread,
768 (int) pi.dwProcessId, (int) pi.dwThreadId);
771 *r_pid = (pid_t)pi.dwProcessId;
773 if (ResumeThread (pi.hThread) < 0)
774 TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
776 if (!CloseHandle (pi.hThread))
777 TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
778 (int) GetLastError ());
780 TRACE_LOG1 ("process=%p", pi.hProcess);
782 /* We don't need to wait for the process. */
783 if (!CloseHandle (pi.hProcess))
784 TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
785 (int) GetLastError ());
787 if (! (flags & IOSPAWN_FLAG_NOCLOSE))
789 for (i = 0; fd_list[i].fd != -1; i++)
790 _gpgme_io_close (fd_list[i].fd);
793 for (i = 0; fd_list[i].fd != -1; i++)
794 if (fd_list[i].dup_to == -1)
795 TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
796 fd_list[i].peer_name);
798 TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
799 fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
800 ((fd_list[i].dup_to == 1) ? "out" : "err"));
802 return TRACE_SYSRES (0);
806 /* Select on the list of fds. Returns: -1 = error, 0 = timeout or
807 nothing to select, > 0 = number of signaled fds. */
809 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
819 /* Use a 1s timeout. */
821 void *dbg_help = NULL;
822 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
823 "nfds=%u, nonblock=%u", nfds, nonblock);
828 pollfds = calloc (nfds, sizeof *pollfds);
831 pollfds_map = calloc (nfds, sizeof *pollfds_map);
839 TRACE_SEQ (dbg_help, "select on [ ");
841 for (i = 0; i < nfds; i++)
843 GIOChannel *chan = NULL;
848 if ((fds[i].for_read || fds[i].for_write)
849 && !(chan = find_channel (fds[i].fd)))
851 TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
852 TRACE_END (dbg_help, "]");
853 assert (!"see log file");
855 else if (fds[i].for_read )
858 g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
859 pollfds_map[npollfds] = i;
860 TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
864 else if (fds[i].for_write)
867 g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
868 pollfds_map[npollfds] = i;
869 TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
875 TRACE_END (dbg_help, "]");
883 count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
886 int saved_errno = errno;
891 TRACE_SEQ (dbg_help, "select OK [ ");
892 if (TRACE_ENABLED (dbg_help))
894 for (i = 0; i < npollfds; i++)
896 if ((pollfds[i].revents & G_IO_IN))
897 TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
898 if ((pollfds[i].revents & G_IO_OUT))
899 TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
901 TRACE_END (dbg_help, "]");
904 /* COUNT is used to stop the lop as soon as possible. */
905 for (n = count, i = 0; i < npollfds && n; i++)
908 assert (j >= 0 && j < nfds);
911 else if (fds[j].for_read)
913 if ((pollfds[i].revents & G_IO_IN))
919 else if (fds[j].for_write)
921 if ((pollfds[i].revents & G_IO_OUT))
932 return TRACE_SYSRES (count);
937 _gpgme_io_dup (int fd)
942 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
944 if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
947 return TRACE_SYSRES (-1);
950 for (newfd = 0; newfd < MAX_SLAFD; newfd++)
951 if (! giochannel_table[newfd].used)
953 if (newfd == MAX_SLAFD)
956 return TRACE_SYSRES (-1);
959 chan = giochannel_table[fd].chan;
960 g_io_channel_ref (chan);
961 giochannel_table[newfd].used = 1;
962 giochannel_table[newfd].chan = chan;
963 giochannel_table[newfd].fd = -1;
964 giochannel_table[newfd].socket = INVALID_SOCKET;
965 giochannel_table[newfd].primary = 0;
967 return TRACE_SYSRES (newfd);
983 case ERROR_BROKEN_PIPE:
985 case WSANOTINITIALISED:
994 _gpgme_io_socket (int domain, int type, int proto)
999 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
1000 "type=%i, protp=%i", type, proto);
1002 res = socket (domain, type, proto);
1003 if (res == INVALID_SOCKET)
1005 errno = wsa2errno (WSAGetLastError ());
1006 return TRACE_SYSRES (-1);
1009 fd = new_channel_from_socket (res);
1012 int saved_errno = errno;
1014 errno = saved_errno;
1015 return TRACE_SYSRES (-1);
1018 TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
1025 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1034 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1035 "addr=%p, addrlen=%i", addr, addrlen);
1037 chan = find_channel (fd);
1041 return TRACE_SYSRES (-1);
1044 flags = g_io_channel_get_flags (chan);
1045 if (flags & G_IO_FLAG_NONBLOCK)
1047 status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
1050 TRACE_LOG1 ("setting flags error: %s", err->message);
1054 if (status != G_IO_STATUS_NORMAL)
1057 return TRACE_SYSRES (-1);
1061 sockfd = giochannel_table[fd].socket;
1062 if (sockfd == INVALID_SOCKET)
1065 return TRACE_SYSRES (-1);
1068 TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
1069 res = connect (sockfd, addr, addrlen);
1071 /* FIXME: Error ignored here. */
1072 if (! (flags & G_IO_FLAG_NONBLOCK))
1073 g_io_channel_set_flags (chan, flags, NULL);
1077 TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());
1079 errno = wsa2errno (WSAGetLastError ());
1080 return TRACE_SYSRES (-1);
1083 return TRACE_SUC ();