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 #include <sys/types.h>
46 #define O_BINARY _O_BINARY
53 /* This file is an ugly hack to get GPGME working with glib on Windows
54 targets. On Windows, you can not select() on file descriptors.
55 The only way to check if there is something to read is to read
56 something. This means that GPGME can not let glib check for data
57 without letting glib also handle the data on Windows targets.
59 The ugly consequence is that we need to work on GIOChannels in
60 GPGME, creating a glib dependency. Also, we need to export an
61 interface for the application to get at GPGME's GIOChannel. There
62 is no good way to abstract all this with callbacks, because the
63 whole thing is also interconnected with the creation of pipes and
66 The following rule applies only to this I/O backend:
68 * ALL operations must use the user defined event loop. GPGME can
69 not anymore provide its own event loop. This is mostly a sanity
70 requirement: Although we have in theory all information we need to
71 make the GPGME W32 code for select still work, it would be a big
72 complication and require changes throughout GPGME.
74 Eventually, we probably have to bite the bullet and make some
75 really nice callback interfaces to let the user control all this at
76 a per-context level. */
85 /* If this is not -1, then it's a libc file descriptor. */
87 /* If fd is -1, this is the Windows socket handle. */
91 /* The boolean PRIMARY is true if this file descriptor caused the
92 allocation of CHAN. Only then should CHAN be destroyed when this
93 FD is closed. This, together with the fact that dup'ed file
94 descriptors are closed before the file descriptors from which
95 they are dup'ed are closed, ensures that CHAN is always valid,
96 and shared among all file descriptors refering to the same
99 The logic behind this is that there is only one reason for us to
100 dup file descriptors anyway: to allow simpler book-keeping of
101 file descriptors shared between GPGME and libassuan, which both
102 want to close something. Using the same channel for these
103 duplicates works just fine (and in fact, using different channels
104 does not work because the W32 backend in glib does not support
105 that: One would end up with several competing reader/writer
108 } giochannel_table[MAX_SLAFD];
112 find_channel (int fd)
114 if (fd < 0 || fd >= MAX_SLAFD)
117 return giochannel_table[fd].chan;
121 /* Returns the FD or -1 on resource limit. */
123 new_dummy_channel_from_fd (int cfd)
127 for (idx = 0; idx < MAX_SLAFD; idx++)
128 if (! giochannel_table[idx].used)
131 if (idx == MAX_SLAFD)
137 giochannel_table[idx].used = 1;
138 giochannel_table[idx].chan = NULL;
139 giochannel_table[idx].fd = cfd;
140 giochannel_table[idx].socket = INVALID_SOCKET;
141 giochannel_table[idx].primary = 1;
147 /* Returns the FD or -1 on resource limit. */
149 new_channel_from_fd (int cfd)
153 for (idx = 0; idx < MAX_SLAFD; idx++)
154 if (! giochannel_table[idx].used)
157 if (idx == MAX_SLAFD)
163 giochannel_table[idx].used = 1;
164 giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd);
165 giochannel_table[idx].fd = cfd;
166 giochannel_table[idx].socket = INVALID_SOCKET;
167 giochannel_table[idx].primary = 1;
169 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
170 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
176 /* Returns the FD or -1 on resource limit. */
178 new_channel_from_socket (int sock)
182 for (idx = 0; idx < MAX_SLAFD; idx++)
183 if (! giochannel_table[idx].used)
186 if (idx == MAX_SLAFD)
192 giochannel_table[idx].used = 1;
193 giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock);
194 giochannel_table[idx].fd = -1;
195 giochannel_table[idx].socket = sock;
196 giochannel_table[idx].primary = 1;
198 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
199 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
205 /* Compatibility interface. Obsolete. */
207 gpgme_get_giochannel (int fd)
209 return find_channel (fd);
213 /* Look up the giochannel for "file descriptor" FD. */
215 gpgme_get_fdptr (int fd)
217 return find_channel (fd);
221 /* Write the printable version of FD to the buffer BUF of length
222 BUFLEN. The printable version is the representation on the command
223 line that the child process expects. */
225 _gpgme_io_fd2str (char *buf, int buflen, int fd)
229 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
230 if (giochannel_table[fd].fd != -1)
231 hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd);
233 hndl = (HANDLE) giochannel_table[fd].socket;
235 TRACE_SUC1 ("syshd=%p", hndl);
237 return snprintf (buf, buflen, "%d", (int) hndl);
242 _gpgme_io_subsystem_init (void)
249 _gpgme_close_notify_handler_t handler;
251 } notify_table[MAX_SLAFD];
255 _gpgme_io_read (int fd, void *buffer, size_t count)
261 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
262 "buffer=%p, count=%u", buffer, count);
264 chan = find_channel (fd);
267 TRACE_LOG ("no channel registered");
269 return TRACE_SYSRES (-1);
271 TRACE_LOG1 ("channel %p", chan);
275 status = g_io_channel_read_chars (chan, (gchar *) buffer,
276 count, &nread, &err);
279 TRACE_LOG2 ("status %i, err %s", status, err->message);
284 if (status == G_IO_STATUS_EOF)
286 else if (status == G_IO_STATUS_AGAIN)
289 saved_errno = EAGAIN;
291 else if (status != G_IO_STATUS_NORMAL)
293 TRACE_LOG1 ("status %d", status);
298 if (nread != 0 && nread != -1)
299 TRACE_LOGBUF (buffer, nread);
302 return TRACE_SYSRES (nread);
307 _gpgme_io_write (int fd, const void *buffer, size_t count)
315 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
316 "buffer=%p, count=%u", buffer, count);
317 TRACE_LOGBUF (buffer, count);
319 chan = find_channel (fd);
322 TRACE_LOG ("fd %d: no channel registered");
327 status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
331 TRACE_LOG1 ("write error: %s", err->message);
335 if (status == G_IO_STATUS_AGAIN)
338 saved_errno = EAGAIN;
340 else if (status != G_IO_STATUS_NORMAL)
347 return TRACE_SYSRES (nwritten);
352 _gpgme_io_pipe (int filedes[2], int inherit_idx)
356 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
357 "inherit_idx=%i (GPGME uses it for %s)",
358 inherit_idx, inherit_idx ? "reading" : "writing");
360 #define PIPEBUF_SIZE 4096
361 if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
362 return TRACE_SYSRES (-1);
364 /* Make one end inheritable. */
365 if (inherit_idx == 0)
369 new_read = _dup (fds[0]);
376 return TRACE_SYSRES (-1);
379 else if (inherit_idx == 1)
383 new_write = _dup (fds[1]);
390 return TRACE_SYSRES (-1);
394 /* For _gpgme_io_close. */
395 filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
396 if (filedes[inherit_idx] < 0)
398 int saved_errno = errno;
403 return TRACE_SYSRES (-1);
406 /* Now we have a pipe with the correct end inheritable. The other end
407 should have a giochannel. */
408 filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
409 if (filedes[1 - inherit_idx] < 0)
411 int saved_errno = errno;
413 _gpgme_io_close (fds[inherit_idx]);
414 _close (fds[1 - inherit_idx]);
416 return TRACE_SYSRES (-1);
419 return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
421 (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
423 (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
424 giochannel_table[1 - inherit_idx].chan);
429 _gpgme_io_close (int fd)
431 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
433 if (fd < 0 || fd >= MAX_SLAFD)
436 return TRACE_SYSRES (-1);
439 assert (giochannel_table[fd].used);
441 /* First call the notify handler. */
442 if (notify_table[fd].handler)
444 notify_table[fd].handler (fd, notify_table[fd].value);
445 notify_table[fd].handler = NULL;
446 notify_table[fd].value = NULL;
449 /* Then do the close. */
450 if (giochannel_table[fd].chan)
452 if (giochannel_table[fd].primary)
453 g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
455 g_io_channel_unref (giochannel_table[fd].chan);
459 /* Dummy entry, just close. */
460 assert (giochannel_table[fd].fd != -1);
461 _close (giochannel_table[fd].fd);
464 giochannel_table[fd].used = 0;
465 giochannel_table[fd].fd = -1;
466 giochannel_table[fd].socket = INVALID_SOCKET;
467 giochannel_table[fd].chan = NULL;
468 giochannel_table[fd].primary = 0;
476 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
479 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
480 "close_handler=%p/%p", handler, value);
484 if (fd < 0 || fd >= (int) DIM (notify_table))
487 return TRACE_SYSRES (-1);
489 notify_table[fd].handler = handler;
490 notify_table[fd].value = value;
491 return TRACE_SYSRES (0);
496 _gpgme_io_set_nonblocking (int fd)
501 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
503 chan = find_channel (fd);
507 return TRACE_SYSRES (-1);
510 status = g_io_channel_set_flags (chan,
511 g_io_channel_get_flags (chan) |
512 G_IO_FLAG_NONBLOCK, NULL);
514 if (status != G_IO_STATUS_NORMAL)
517 /* glib 1.9.2 does not implement set_flags and returns an
520 return TRACE_SYSRES (-1);
522 TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
527 return TRACE_SYSRES (0);
532 build_commandline (char **argv)
539 /* We have to quote some things because under Windows the program
540 parses the commandline and does some unquoting. We enclose the
541 whole argument in double-quotes, and escape literal double-quotes
542 as well as backslashes with a backslash. We end up with a
543 trailing space at the end of the line, but that is harmless. */
544 for (i = 0; argv[i]; i++)
547 /* The leading double-quote. */
551 /* An extra one for each literal that must be escaped. */
552 if (*p == '\\' || *p == '"')
557 /* The trailing double-quote and the delimiter. */
560 /* And a trailing zero. */
563 buf = p = malloc (n);
566 for (i = 0; argv[i]; i++)
568 char *argvp = argv[i];
573 if (*argvp == '\\' || *argvp == '"')
587 _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
588 struct spawn_fd_item_s *fd_list, pid_t *r_pid)
590 SECURITY_ATTRIBUTES sec_attr;
591 PROCESS_INFORMATION pi =
593 NULL, /* returns process handle */
594 0, /* returns primary thread handle */
599 int cr_flags = (CREATE_DEFAULT_ERROR_MODE
600 | GetPriorityClass (GetCurrentProcess ()));
609 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
614 TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
618 /* We do not inherit any handles by default, and just insert those
619 handles we want the child to have afterwards. But some handle
620 values occur on the command line, and we need to move
621 stdin/out/err to the right location. So we use a wrapper program
622 which gets the information from a temporary file. */
623 if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
625 TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
626 return TRACE_SYSRES (-1);
628 TRACE_LOG1 ("tmp_name = %s", tmp_name);
630 args = calloc (2 + i + 1, sizeof (*args));
631 args[0] = (char *) _gpgme_get_w32spawn_path ();
634 memcpy (&args[3], &argv[1], i * sizeof (*args));
636 memset (&sec_attr, 0, sizeof sec_attr);
637 sec_attr.nLength = sizeof sec_attr;
638 sec_attr.bInheritHandle = FALSE;
640 arg_string = build_commandline (args);
645 DeleteFile (tmp_name);
646 return TRACE_SYSRES (-1);
649 memset (&si, 0, sizeof si);
651 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
652 si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
653 si.hStdInput = INVALID_HANDLE_VALUE;
654 si.hStdOutput = INVALID_HANDLE_VALUE;
655 si.hStdError = INVALID_HANDLE_VALUE;
657 cr_flags |= CREATE_SUSPENDED;
658 cr_flags |= DETACHED_PROCESS;
659 if (!CreateProcessA (_gpgme_get_w32spawn_path (),
661 &sec_attr, /* process security attributes */
662 &sec_attr, /* thread security attributes */
663 FALSE, /* inherit handles */
664 cr_flags, /* creation flags */
665 NULL, /* environment */
666 NULL, /* use current drive/directory */
667 &si, /* startup information */
668 &pi)) /* returns process information */
670 TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
673 DeleteFile (tmp_name);
675 /* FIXME: Should translate the error code. */
677 return TRACE_SYSRES (-1);
682 if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
683 _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
685 /* Insert the inherited handles. */
686 for (i = 0; fd_list[i].fd != -1; i++)
690 /* Make it inheritable for the wrapper process. */
691 if (!DuplicateHandle (GetCurrentProcess(),
692 _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
693 pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
695 TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
696 TerminateProcess (pi.hProcess, 0);
697 /* Just in case TerminateProcess didn't work, let the
698 process fail on its own. */
699 ResumeThread (pi.hThread);
700 CloseHandle (pi.hThread);
701 CloseHandle (pi.hProcess);
704 DeleteFile (tmp_name);
706 /* FIXME: Should translate the error code. */
708 return TRACE_SYSRES (-1);
710 /* Return the child name of this handle. */
711 fd_list[i].peer_name = (int) hd;
714 /* Write the handle translation information to the temporary
717 /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
718 notation: "0xFEDCBA9876543210" with an extra white space after
719 every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
720 for a time when a HANDLE is 64 bit. */
721 #define BUFFER_MAX 800
722 char line[BUFFER_MAX + 1];
727 if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
728 strcpy (line, "~1 \n");
731 for (i = 0; fd_list[i].fd != -1; i++)
733 /* Strip the newline. */
734 len = strlen (line) - 1;
736 /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
737 snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
738 fd_list[i].fd, fd_list[i].dup_to,
739 fd_list[i].peer_name, fd_list[i].arg_loc);
740 /* Rather safe than sorry. */
741 line[BUFFER_MAX - 1] = '\n';
742 line[BUFFER_MAX] = '\0';
748 res = write (tmp_fd, &line[written], len - written);
752 while (res > 0 || (res < 0 && errno == EAGAIN));
755 /* The temporary file is deleted by the gpgme-w32spawn process
758 TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
759 "dwProcessID=%d, dwThreadId=%d",
760 pi.hProcess, pi.hThread,
761 (int) pi.dwProcessId, (int) pi.dwThreadId);
764 *r_pid = (pid_t)pi.dwProcessId;
766 if (ResumeThread (pi.hThread) < 0)
767 TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
769 if (!CloseHandle (pi.hThread))
770 TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
771 (int) GetLastError ());
773 TRACE_LOG1 ("process=%p", pi.hProcess);
775 /* We don't need to wait for the process. */
776 if (!CloseHandle (pi.hProcess))
777 TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
778 (int) GetLastError ());
780 for (i = 0; fd_list[i].fd != -1; i++)
781 _gpgme_io_close (fd_list[i].fd);
783 for (i = 0; fd_list[i].fd != -1; i++)
784 if (fd_list[i].dup_to == -1)
785 TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
786 fd_list[i].peer_name);
788 TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
789 fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
790 ((fd_list[i].dup_to == 1) ? "out" : "err"));
792 return TRACE_SYSRES (0);
796 /* Select on the list of fds. Returns: -1 = error, 0 = timeout or
797 nothing to select, > 0 = number of signaled fds. */
799 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
809 /* Use a 1s timeout. */
811 void *dbg_help = NULL;
812 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
813 "nfds=%u, nonblock=%u", nfds, nonblock);
818 pollfds = calloc (nfds, sizeof *pollfds);
821 pollfds_map = calloc (nfds, sizeof *pollfds_map);
829 TRACE_SEQ (dbg_help, "select on [ ");
831 for (i = 0; i < nfds; i++)
833 GIOChannel *chan = NULL;
838 if ((fds[i].for_read || fds[i].for_write)
839 && !(chan = find_channel (fds[i].fd)))
841 TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
842 TRACE_END (dbg_help, "]");
843 assert (!"see log file");
845 else if (fds[i].for_read )
848 g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
849 pollfds_map[npollfds] = i;
850 TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
854 else if (fds[i].for_write)
857 g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
858 pollfds_map[npollfds] = i;
859 TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
865 TRACE_END (dbg_help, "]");
873 count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
876 int saved_errno = errno;
881 TRACE_SEQ (dbg_help, "select OK [ ");
882 if (TRACE_ENABLED (dbg_help))
884 for (i = 0; i < npollfds; i++)
886 if ((pollfds[i].revents & G_IO_IN))
887 TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
888 if ((pollfds[i].revents & G_IO_OUT))
889 TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
891 TRACE_END (dbg_help, "]");
894 /* COUNT is used to stop the lop as soon as possible. */
895 for (n = count, i = 0; i < npollfds && n; i++)
898 assert (j >= 0 && j < nfds);
901 else if (fds[j].for_read)
903 if ((pollfds[i].revents & G_IO_IN))
909 else if (fds[j].for_write)
911 if ((pollfds[i].revents & G_IO_OUT))
922 return TRACE_SYSRES (count);
927 _gpgme_io_dup (int fd)
932 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
934 if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
937 return TRACE_SYSRES (-1);
940 for (newfd = 0; newfd < MAX_SLAFD; newfd++)
941 if (! giochannel_table[newfd].used)
943 if (newfd == MAX_SLAFD)
946 return TRACE_SYSRES (-1);
949 chan = giochannel_table[fd].chan;
950 g_io_channel_ref (chan);
951 giochannel_table[newfd].used = 1;
952 giochannel_table[newfd].chan = chan;
953 giochannel_table[newfd].fd = -1;
954 giochannel_table[newfd].socket = INVALID_SOCKET;
955 giochannel_table[newfd].primary = 0;
957 return TRACE_SYSRES (newfd);
973 case ERROR_BROKEN_PIPE:
975 case WSANOTINITIALISED:
984 _gpgme_io_socket (int domain, int type, int proto)
989 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
990 "type=%i, protp=%i", type, proto);
992 res = socket (domain, type, proto);
993 if (res == INVALID_SOCKET)
995 errno = wsa2errno (WSAGetLastError ());
996 return TRACE_SYSRES (-1);
999 fd = new_channel_from_socket (res);
1002 int saved_errno = errno;
1004 errno = saved_errno;
1005 return TRACE_SYSRES (-1);
1008 TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
1015 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1024 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1025 "addr=%p, addrlen=%i", addr, addrlen);
1027 chan = find_channel (fd);
1031 return TRACE_SYSRES (-1);
1034 flags = g_io_channel_get_flags (chan);
1035 if (flags & G_IO_FLAG_NONBLOCK)
1037 status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
1040 TRACE_LOG1 ("setting flags error: %s", err->message);
1044 if (status != G_IO_STATUS_NORMAL)
1047 return TRACE_SYSRES (-1);
1051 sockfd = giochannel_table[fd].socket;
1052 if (sockfd == INVALID_SOCKET)
1055 return TRACE_SYSRES (-1);
1058 TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
1059 res = connect (sockfd, addr, addrlen);
1061 /* FIXME: Error ignored here. */
1062 if (! (flags & G_IO_FLAG_NONBLOCK))
1063 g_io_channel_set_flags (chan, flags, NULL);
1067 TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());
1069 errno = wsa2errno (WSAGetLastError ());
1070 return TRACE_SYSRES (-1);
1073 return TRACE_SUC ();