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 #include <sys/types.h>
49 #define O_BINARY _O_BINARY
56 /* This file is an ugly hack to get GPGME working with glib on Windows
57 targets. On Windows, you can not select() on file descriptors.
58 The only way to check if there is something to read is to read
59 something. This means that GPGME can not let glib check for data
60 without letting glib also handle the data on Windows targets.
62 The ugly consequence is that we need to work on GIOChannels in
63 GPGME, creating a glib dependency. Also, we need to export an
64 interface for the application to get at GPGME's GIOChannel. There
65 is no good way to abstract all this with callbacks, because the
66 whole thing is also interconnected with the creation of pipes and
69 The following rule applies only to this I/O backend:
71 * ALL operations must use the user defined event loop. GPGME can
72 not anymore provide its own event loop. This is mostly a sanity
73 requirement: Although we have in theory all information we need to
74 make the GPGME W32 code for select still work, it would be a big
75 complication and require changes throughout GPGME.
77 Eventually, we probably have to bite the bullet and make some
78 really nice callback interfaces to let the user control all this at
79 a per-context level. */
88 /* If this is not -1, then it's a libc file descriptor. */
90 /* If fd is -1, this is the Windows socket handle. */
94 /* The boolean PRIMARY is true if this file descriptor caused the
95 allocation of CHAN. Only then should CHAN be destroyed when this
96 FD is closed. This, together with the fact that dup'ed file
97 descriptors are closed before the file descriptors from which
98 they are dup'ed are closed, ensures that CHAN is always valid,
99 and shared among all file descriptors refering to the same
102 The logic behind this is that there is only one reason for us to
103 dup file descriptors anyway: to allow simpler book-keeping of
104 file descriptors shared between GPGME and libassuan, which both
105 want to close something. Using the same channel for these
106 duplicates works just fine (and in fact, using different channels
107 does not work because the W32 backend in glib does not support
108 that: One would end up with several competing reader/writer
111 } giochannel_table[MAX_SLAFD];
115 find_channel (int fd)
117 if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
120 return giochannel_table[fd].chan;
124 /* Returns the FD or -1 on resource limit. */
126 new_dummy_channel_from_fd (int cfd)
130 for (idx = 0; idx < MAX_SLAFD; idx++)
131 if (! giochannel_table[idx].used)
134 if (idx == MAX_SLAFD)
140 giochannel_table[idx].used = 1;
141 giochannel_table[idx].chan = NULL;
142 giochannel_table[idx].fd = cfd;
143 giochannel_table[idx].socket = INVALID_SOCKET;
144 giochannel_table[idx].primary = 1;
150 /* Returns the FD or -1 on resource limit. */
152 new_channel_from_fd (int cfd)
156 for (idx = 0; idx < MAX_SLAFD; idx++)
157 if (! giochannel_table[idx].used)
160 if (idx == MAX_SLAFD)
166 giochannel_table[idx].used = 1;
167 giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd);
168 giochannel_table[idx].fd = cfd;
169 giochannel_table[idx].socket = INVALID_SOCKET;
170 giochannel_table[idx].primary = 1;
172 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
173 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
179 /* Returns the FD or -1 on resource limit. */
181 new_channel_from_socket (int sock)
185 for (idx = 0; idx < MAX_SLAFD; idx++)
186 if (! giochannel_table[idx].used)
189 if (idx == MAX_SLAFD)
195 giochannel_table[idx].used = 1;
196 giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock);
197 giochannel_table[idx].fd = -1;
198 giochannel_table[idx].socket = sock;
199 giochannel_table[idx].primary = 1;
201 g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
202 g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
208 /* Compatibility interface. Obsolete. */
210 gpgme_get_giochannel (int fd)
212 return find_channel (fd);
216 /* Look up the giochannel for "file descriptor" FD. */
218 gpgme_get_fdptr (int fd)
220 return find_channel (fd);
224 /* Write the printable version of FD to the buffer BUF of length
225 BUFLEN. The printable version is the representation on the command
226 line that the child process expects. */
228 _gpgme_io_fd2str (char *buf, int buflen, int fd)
232 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
233 if (giochannel_table[fd].fd != -1)
234 hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd);
236 hndl = (HANDLE) giochannel_table[fd].socket;
238 TRACE_SUC1 ("syshd=%p", hndl);
240 return snprintf (buf, buflen, "%d", (int) hndl);
245 _gpgme_io_subsystem_init (void)
252 _gpgme_close_notify_handler_t handler;
254 } notify_table[MAX_SLAFD];
258 _gpgme_io_read (int fd, void *buffer, size_t count)
264 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
265 "buffer=%p, count=%u", buffer, count);
267 chan = find_channel (fd);
270 TRACE_LOG ("no channel registered");
272 return TRACE_SYSRES (-1);
274 TRACE_LOG1 ("channel %p", chan);
278 status = g_io_channel_read_chars (chan, (gchar *) buffer,
279 count, &nread, &err);
282 TRACE_LOG2 ("status %i, err %s", status, err->message);
287 if (status == G_IO_STATUS_EOF)
289 else if (status == G_IO_STATUS_AGAIN)
292 saved_errno = EAGAIN;
294 else if (status != G_IO_STATUS_NORMAL)
296 TRACE_LOG1 ("status %d", status);
301 if (nread != 0 && nread != -1)
302 TRACE_LOGBUF (buffer, nread);
305 return TRACE_SYSRES (nread);
310 _gpgme_io_write (int fd, const void *buffer, size_t count)
318 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
319 "buffer=%p, count=%u", buffer, count);
320 TRACE_LOGBUF (buffer, count);
322 chan = find_channel (fd);
325 TRACE_LOG ("fd %d: no channel registered");
330 status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
334 TRACE_LOG1 ("write error: %s", err->message);
338 if (status == G_IO_STATUS_AGAIN)
341 saved_errno = EAGAIN;
343 else if (status != G_IO_STATUS_NORMAL)
350 return TRACE_SYSRES (nwritten);
355 _gpgme_io_pipe (int filedes[2], int inherit_idx)
359 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
360 "inherit_idx=%i (GPGME uses it for %s)",
361 inherit_idx, inherit_idx ? "reading" : "writing");
363 #define PIPEBUF_SIZE 4096
364 if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
365 return TRACE_SYSRES (-1);
367 /* Make one end inheritable. */
368 if (inherit_idx == 0)
372 new_read = _dup (fds[0]);
379 return TRACE_SYSRES (-1);
382 else if (inherit_idx == 1)
386 new_write = _dup (fds[1]);
393 return TRACE_SYSRES (-1);
397 /* For _gpgme_io_close. */
398 filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
399 if (filedes[inherit_idx] < 0)
401 int saved_errno = errno;
406 return TRACE_SYSRES (-1);
409 /* Now we have a pipe with the correct end inheritable. The other end
410 should have a giochannel. */
411 filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
412 if (filedes[1 - inherit_idx] < 0)
414 int saved_errno = errno;
416 _gpgme_io_close (fds[inherit_idx]);
417 _close (fds[1 - inherit_idx]);
419 return TRACE_SYSRES (-1);
422 return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
424 (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
426 (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
427 giochannel_table[1 - inherit_idx].chan);
432 _gpgme_io_close (int fd)
434 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
436 if (fd < 0 || fd >= MAX_SLAFD)
439 return TRACE_SYSRES (-1);
442 assert (giochannel_table[fd].used);
444 /* First call the notify handler. */
445 if (notify_table[fd].handler)
447 notify_table[fd].handler (fd, notify_table[fd].value);
448 notify_table[fd].handler = NULL;
449 notify_table[fd].value = NULL;
452 /* Then do the close. */
453 if (giochannel_table[fd].chan)
455 if (giochannel_table[fd].primary)
456 g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
458 g_io_channel_unref (giochannel_table[fd].chan);
462 /* Dummy entry, just close. */
463 assert (giochannel_table[fd].fd != -1);
464 _close (giochannel_table[fd].fd);
467 giochannel_table[fd].used = 0;
468 giochannel_table[fd].fd = -1;
469 giochannel_table[fd].socket = INVALID_SOCKET;
470 giochannel_table[fd].chan = NULL;
471 giochannel_table[fd].primary = 0;
479 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
482 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
483 "close_handler=%p/%p", handler, value);
487 if (fd < 0 || fd >= (int) DIM (notify_table))
490 return TRACE_SYSRES (-1);
492 notify_table[fd].handler = handler;
493 notify_table[fd].value = value;
494 return TRACE_SYSRES (0);
499 _gpgme_io_set_nonblocking (int fd)
504 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
506 chan = find_channel (fd);
510 return TRACE_SYSRES (-1);
513 status = g_io_channel_set_flags (chan,
514 g_io_channel_get_flags (chan) |
515 G_IO_FLAG_NONBLOCK, NULL);
517 if (status != G_IO_STATUS_NORMAL)
520 /* glib 1.9.2 does not implement set_flags and returns an
523 return TRACE_SYSRES (-1);
525 TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
530 return TRACE_SYSRES (0);
535 build_commandline (char **argv)
542 /* We have to quote some things because under Windows the program
543 parses the commandline and does some unquoting. We enclose the
544 whole argument in double-quotes, and escape literal double-quotes
545 as well as backslashes with a backslash. We end up with a
546 trailing space at the end of the line, but that is harmless. */
547 for (i = 0; argv[i]; i++)
550 /* The leading double-quote. */
554 /* An extra one for each literal that must be escaped. */
555 if (*p == '\\' || *p == '"')
560 /* The trailing double-quote and the delimiter. */
563 /* And a trailing zero. */
566 buf = p = malloc (n);
569 for (i = 0; argv[i]; i++)
571 char *argvp = argv[i];
576 if (*argvp == '\\' || *argvp == '"')
590 _gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
591 struct spawn_fd_item_s *fd_list,
592 void (*atfork) (void *opaque, int reserved),
593 void *atforkvalue, pid_t *r_pid)
595 SECURITY_ATTRIBUTES sec_attr;
596 PROCESS_INFORMATION pi =
598 NULL, /* returns process handle */
599 0, /* returns primary thread handle */
604 int cr_flags = (CREATE_DEFAULT_ERROR_MODE
605 | GetPriorityClass (GetCurrentProcess ()));
614 TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
619 TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
623 /* We do not inherit any handles by default, and just insert those
624 handles we want the child to have afterwards. But some handle
625 values occur on the command line, and we need to move
626 stdin/out/err to the right location. So we use a wrapper program
627 which gets the information from a temporary file. */
628 if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
630 TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
631 return TRACE_SYSRES (-1);
633 TRACE_LOG1 ("tmp_name = %s", tmp_name);
635 args = calloc (2 + i + 1, sizeof (*args));
636 args[0] = (char *) _gpgme_get_w32spawn_path ();
639 memcpy (&args[3], &argv[1], i * sizeof (*args));
641 memset (&sec_attr, 0, sizeof sec_attr);
642 sec_attr.nLength = sizeof sec_attr;
643 sec_attr.bInheritHandle = FALSE;
645 arg_string = build_commandline (args);
650 DeleteFile (tmp_name);
651 return TRACE_SYSRES (-1);
654 memset (&si, 0, sizeof si);
656 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
657 si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
658 si.hStdInput = INVALID_HANDLE_VALUE;
659 si.hStdOutput = INVALID_HANDLE_VALUE;
660 si.hStdError = INVALID_HANDLE_VALUE;
662 cr_flags |= CREATE_SUSPENDED;
663 cr_flags |= DETACHED_PROCESS;
664 if (!CreateProcessA (_gpgme_get_w32spawn_path (),
666 &sec_attr, /* process security attributes */
667 &sec_attr, /* thread security attributes */
668 FALSE, /* inherit handles */
669 cr_flags, /* creation flags */
670 NULL, /* environment */
671 NULL, /* use current drive/directory */
672 &si, /* startup information */
673 &pi)) /* returns process information */
675 TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
678 DeleteFile (tmp_name);
680 /* FIXME: Should translate the error code. */
682 return TRACE_SYSRES (-1);
687 if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
688 _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
690 /* Insert the inherited handles. */
691 for (i = 0; fd_list[i].fd != -1; i++)
695 /* Make it inheritable for the wrapper process. */
696 if (!DuplicateHandle (GetCurrentProcess(),
697 _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
698 pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
700 TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
701 TerminateProcess (pi.hProcess, 0);
702 /* Just in case TerminateProcess didn't work, let the
703 process fail on its own. */
704 ResumeThread (pi.hThread);
705 CloseHandle (pi.hThread);
706 CloseHandle (pi.hProcess);
709 DeleteFile (tmp_name);
711 /* FIXME: Should translate the error code. */
713 return TRACE_SYSRES (-1);
715 /* Return the child name of this handle. */
716 fd_list[i].peer_name = (int) hd;
719 /* Write the handle translation information to the temporary
722 /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
723 notation: "0xFEDCBA9876543210" with an extra white space after
724 every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
725 for a time when a HANDLE is 64 bit. */
726 #define BUFFER_MAX 800
727 char line[BUFFER_MAX + 1];
732 if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
733 strcpy (line, "~1 \n");
736 for (i = 0; fd_list[i].fd != -1; i++)
738 /* Strip the newline. */
739 len = strlen (line) - 1;
741 /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
742 snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
743 fd_list[i].fd, fd_list[i].dup_to,
744 fd_list[i].peer_name, fd_list[i].arg_loc);
745 /* Rather safe than sorry. */
746 line[BUFFER_MAX - 1] = '\n';
747 line[BUFFER_MAX] = '\0';
753 res = write (tmp_fd, &line[written], len - written);
757 while (res > 0 || (res < 0 && errno == EAGAIN));
760 /* The temporary file is deleted by the gpgme-w32spawn process
763 TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
764 "dwProcessID=%d, dwThreadId=%d",
765 pi.hProcess, pi.hThread,
766 (int) pi.dwProcessId, (int) pi.dwThreadId);
769 *r_pid = (pid_t)pi.dwProcessId;
771 if (ResumeThread (pi.hThread) < 0)
772 TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
774 if (!CloseHandle (pi.hThread))
775 TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
776 (int) GetLastError ());
778 TRACE_LOG1 ("process=%p", pi.hProcess);
780 /* We don't need to wait for the process. */
781 if (!CloseHandle (pi.hProcess))
782 TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
783 (int) GetLastError ());
785 if (! (flags & IOSPAWN_FLAG_NOCLOSE))
787 for (i = 0; fd_list[i].fd != -1; i++)
788 _gpgme_io_close (fd_list[i].fd);
791 for (i = 0; fd_list[i].fd != -1; i++)
792 if (fd_list[i].dup_to == -1)
793 TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
794 fd_list[i].peer_name);
796 TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
797 fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
798 ((fd_list[i].dup_to == 1) ? "out" : "err"));
800 return TRACE_SYSRES (0);
804 /* Select on the list of fds. Returns: -1 = error, 0 = timeout or
805 nothing to select, > 0 = number of signaled fds. */
807 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
817 /* Use a 1s timeout. */
819 void *dbg_help = NULL;
820 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
821 "nfds=%u, nonblock=%u", nfds, nonblock);
826 pollfds = calloc (nfds, sizeof *pollfds);
829 pollfds_map = calloc (nfds, sizeof *pollfds_map);
837 TRACE_SEQ (dbg_help, "select on [ ");
839 for (i = 0; i < nfds; i++)
841 GIOChannel *chan = NULL;
846 if ((fds[i].for_read || fds[i].for_write)
847 && !(chan = find_channel (fds[i].fd)))
849 TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
850 TRACE_END (dbg_help, "]");
851 assert (!"see log file");
853 else if (fds[i].for_read )
856 g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
857 pollfds_map[npollfds] = i;
858 TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
862 else if (fds[i].for_write)
865 g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
866 pollfds_map[npollfds] = i;
867 TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
873 TRACE_END (dbg_help, "]");
881 count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
884 int saved_errno = errno;
889 TRACE_SEQ (dbg_help, "select OK [ ");
890 if (TRACE_ENABLED (dbg_help))
892 for (i = 0; i < npollfds; i++)
894 if ((pollfds[i].revents & G_IO_IN))
895 TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
896 if ((pollfds[i].revents & G_IO_OUT))
897 TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
899 TRACE_END (dbg_help, "]");
902 /* COUNT is used to stop the lop as soon as possible. */
903 for (n = count, i = 0; i < npollfds && n; i++)
906 assert (j >= 0 && j < nfds);
909 else if (fds[j].for_read)
911 if ((pollfds[i].revents & G_IO_IN))
917 else if (fds[j].for_write)
919 if ((pollfds[i].revents & G_IO_OUT))
930 return TRACE_SYSRES (count);
935 _gpgme_io_dup (int fd)
940 TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
942 if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
945 return TRACE_SYSRES (-1);
948 for (newfd = 0; newfd < MAX_SLAFD; newfd++)
949 if (! giochannel_table[newfd].used)
951 if (newfd == MAX_SLAFD)
954 return TRACE_SYSRES (-1);
957 chan = giochannel_table[fd].chan;
958 g_io_channel_ref (chan);
959 giochannel_table[newfd].used = 1;
960 giochannel_table[newfd].chan = chan;
961 giochannel_table[newfd].fd = -1;
962 giochannel_table[newfd].socket = INVALID_SOCKET;
963 giochannel_table[newfd].primary = 0;
965 return TRACE_SYSRES (newfd);
981 case ERROR_BROKEN_PIPE:
983 case WSANOTINITIALISED:
992 _gpgme_io_socket (int domain, int type, int proto)
997 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
998 "type=%i, protp=%i", type, proto);
1000 res = socket (domain, type, proto);
1001 if (res == INVALID_SOCKET)
1003 errno = wsa2errno (WSAGetLastError ());
1004 return TRACE_SYSRES (-1);
1007 fd = new_channel_from_socket (res);
1010 int saved_errno = errno;
1012 errno = saved_errno;
1013 return TRACE_SYSRES (-1);
1016 TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
1023 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1032 TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1033 "addr=%p, addrlen=%i", addr, addrlen);
1035 chan = find_channel (fd);
1039 return TRACE_SYSRES (-1);
1042 flags = g_io_channel_get_flags (chan);
1043 if (flags & G_IO_FLAG_NONBLOCK)
1045 status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
1048 TRACE_LOG1 ("setting flags error: %s", err->message);
1052 if (status != G_IO_STATUS_NORMAL)
1055 return TRACE_SYSRES (-1);
1059 sockfd = giochannel_table[fd].socket;
1060 if (sockfd == INVALID_SOCKET)
1063 return TRACE_SYSRES (-1);
1066 TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
1067 res = connect (sockfd, addr, addrlen);
1069 /* FIXME: Error ignored here. */
1070 if (! (flags & G_IO_FLAG_NONBLOCK))
1071 g_io_channel_set_flags (chan, flags, NULL);
1075 TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());
1077 errno = wsa2errno (WSAGetLastError ());
1078 return TRACE_SYSRES (-1);
1081 return TRACE_SUC ();