OSDN Git Service

* cygwin.din (_getutline): Remove.
authorChristopher Faylor <cgf@redhat.com>
Wed, 30 Jul 2008 04:58:23 +0000 (04:58 +0000)
committerChristopher Faylor <cgf@redhat.com>
Wed, 30 Jul 2008 04:58:23 +0000 (04:58 +0000)
* lib/bsdlib.cc (login): Make argument const as per linux.
(logout): Ditto.
* syscalls.cc (getutid): Ditto.
(getutline): Ditto.
(pututline): Ditto.
(getutxent): Add comment mentioning non-thread-safety.
(getutxid): Ditto.
(getutxline): Ditto.
(pututxline): Ditto.
* sys/utmp.h: Declare arguments to various functions as const as per linux.
Remove bogus _getutline definition.

winsup/cygwin/ChangeLog
winsup/cygwin/cygwin.din
winsup/cygwin/include/sys/utmp.h [new file with mode: 0644]
winsup/cygwin/libc/bsdlib.cc [new file with mode: 0644]
winsup/cygwin/select.cc [new file with mode: 0644]
winsup/cygwin/syscalls.cc

index 25f1601..cde34cf 100644 (file)
@@ -1,3 +1,18 @@
+2008-07-30  Christopher Faylor  <me+cygwin@cgf.cx>
+
+       * cygwin.din (_getutline): Remove.
+       * lib/bsdlib.cc (login): Make argument const as per linux.
+       (logout): Ditto.
+       * syscalls.cc (getutid): Ditto.
+       (getutline): Ditto.
+       (pututline): Ditto.
+       (getutxent): Add comment mentioning non-thread-safety.
+       (getutxid): Ditto.
+       (getutxline): Ditto.
+       (pututxline): Ditto.
+       * sys/utmp.h: Declare arguments to various functions as const as per
+       linux.  Remove bogus _getutline definition.
+
 2008-07-29  Christopher Faylor  <me+cygwin@cgf.cx>
 
        * include/cygwin/version.h: Add comment indicating that current API number
index 1dac226..1b5ac9f 100644 (file)
@@ -701,7 +701,6 @@ _getutent = getutent SIGFE
 getutid SIGFE
 _getutid = getutid SIGFE
 getutline SIGFE
-_getutline = getutline SIGFE
 getutxent SIGFE
 getutxid SIGFE
 getutxline SIGFE
diff --git a/winsup/cygwin/include/sys/utmp.h b/winsup/cygwin/include/sys/utmp.h
new file mode 100644 (file)
index 0000000..8a63b34
--- /dev/null
@@ -0,0 +1,54 @@
+/* sys/utmp.h
+
+   Copyright 2001, 2003, 2004, 2005, 2008 Red Hat, Inc.
+
+   This software is a copyrighted work licensed under the terms of the
+   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+   details. */
+
+#ifndef UTMP_H
+#define UTMP_H
+
+#include <cygwin/utmp.h>
+
+#define UTMP_FILE _PATH_UTMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ut_name
+#define ut_name                ut_user
+#endif
+
+
+struct utmp
+{
+ short ut_type;
+ pid_t ut_pid;
+ char  ut_line[UT_LINESIZE];
+ char  ut_id[UT_IDLEN];
+ time_t ut_time;
+ char  ut_user[UT_NAMESIZE];
+ char  ut_host[UT_HOSTSIZE];
+ long  ut_addr;
+};
+
+extern struct utmp *getutent (void);
+extern struct utmp *getutid (const struct utmp *);
+extern struct utmp *getutline (const struct utmp *);
+extern struct utmp *pututline (const struct utmp *);
+extern void endutent (void);
+extern void setutent (void);
+extern void utmpname (const char *);
+
+void login (const struct utmp *);
+int logout (const char *);
+int login_tty (int);
+void updwtmp (const char *, const struct utmp *);
+void logwtmp (const char *, const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* UTMP_H */
diff --git a/winsup/cygwin/libc/bsdlib.cc b/winsup/cygwin/libc/bsdlib.cc
new file mode 100644 (file)
index 0000000..9d488c3
--- /dev/null
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * CV 2003-09-10: Cygwin specific changes applied.  Code simplified just
+ *               for Cygwin alone.
+ */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include "cygerrno.h"
+#include "thread.h"
+#include "cygtls.h"
+
+extern "C" int
+daemon (int nochdir, int noclose)
+{
+  int fd;
+
+  switch (fork ())
+    {
+      case -1:
+       return -1;
+      case 0:
+       break;
+      default:
+       /* This sleep avoids a race condition which kills the
+          child process if parent is started by a NT/W2K service.
+          FIXME: Is that still true? */
+       Sleep (1000L);
+       _exit (0);
+    }
+  if (setsid () == -1)
+    return -1;
+  if (!nochdir)
+    chdir ("/");
+  if (!noclose && (fd = open (_PATH_DEVNULL, O_RDWR, 0)) >= 0)
+    {
+      dup2 (fd, STDIN_FILENO);
+      dup2 (fd, STDOUT_FILENO);
+      dup2 (fd, STDERR_FILENO);
+      if (fd > 2)
+       close (fd);
+    }
+  return 0;
+}
+
+extern "C" int
+login_tty (int fd)
+{
+  char *fdname;
+  int newfd;
+
+  if (setsid () == -1)
+    return -1;
+  if ((fdname = ttyname (fd)))
+    {
+      if (fd != STDIN_FILENO)
+       close (STDIN_FILENO);
+      if (fd != STDOUT_FILENO)
+       close (STDOUT_FILENO);
+      if (fd != STDERR_FILENO)
+       close (STDERR_FILENO);
+      newfd = open (fdname, O_RDWR);
+      close (newfd);
+    }
+  dup2 (fd, STDIN_FILENO);
+  dup2 (fd, STDOUT_FILENO);
+  dup2 (fd, STDERR_FILENO);
+  if (fd > 2)
+    close (fd);
+  return 0;
+}
+
+extern "C" int
+openpty (int *amaster, int *aslave, char *name, struct termios *termp,
+        struct winsize *winp)
+{
+  int master, slave;
+  char pts[TTY_NAME_MAX];
+
+  if ((master = open ("/dev/ptmx", O_RDWR | O_NOCTTY)) >= 0)
+    {
+      grantpt (master);
+      unlockpt (master);
+      strcpy (pts, ptsname (master));
+      revoke (pts);
+      if ((slave = open (pts, O_RDWR | O_NOCTTY)) >= 0)
+       {
+         if (amaster)
+           *amaster = master;
+         if (aslave)
+           *aslave = slave;
+         if (name)
+           strcpy (name, pts);
+         if (termp)
+           tcsetattr (slave, TCSAFLUSH, termp);
+         if (winp)
+           ioctl (slave, TIOCSWINSZ, (char *) winp);
+         return 0;
+       }
+      close (master);
+    }
+  set_errno (ENOENT);
+  return -1;
+}
+
+extern "C" int
+forkpty (int *amaster, char *name, struct termios *termp, struct winsize *winp)
+{
+  int master, slave, pid;
+
+  if (openpty (&master, &slave, name, termp, winp) == -1)
+    return -1;
+  switch (pid = fork ())
+    {
+      case -1:
+       return -1;
+      case 0:
+       close (master);
+       login_tty (slave);
+       return 0;
+    }
+  if (amaster)
+    *amaster = master;
+  close (slave);
+  return pid;
+}
+
+extern "C" char *__progname;
+
+static void
+_vwarnx (const char *fmt, va_list ap)
+{
+  fprintf (stderr, "%s: ", __progname);
+  vfprintf (stderr, fmt, ap);
+}
+
+extern "C" void
+vwarn (const char *fmt, va_list ap)
+{
+  _vwarnx (fmt, ap);
+  fprintf (stderr, ": %s", strerror (get_errno ()));
+  fputc ('\n', stderr);
+}
+
+extern "C" void
+vwarnx (const char *fmt, va_list ap)
+{
+  _vwarnx (fmt, ap);
+  fputc ('\n', stderr);
+}
+
+extern "C" void
+warn (const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vwarn (fmt, ap);
+}
+
+extern "C" void
+warnx (const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vwarnx (fmt, ap);
+}
+
+extern "C" void
+verr (int eval, const char *fmt, va_list ap)
+{
+  vwarn (fmt, ap);
+  exit (eval);
+}
+
+extern "C" void
+verrx (int eval, const char *fmt, va_list ap)
+{
+  vwarnx (fmt, ap);
+  exit (eval);
+}
+
+extern "C" void
+err (int eval, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vwarn (fmt, ap);
+  exit (eval);
+}
+
+extern "C" void
+errx (int eval, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vwarnx (fmt, ap);
+  exit (eval);
+}
+
+extern "C" const char *
+getprogname (void)
+{
+  return __progname;
+}
+
+extern "C" void
+setprogname (const char *newprogname)
+{
+  myfault efault;
+  if (!efault.faulted (EFAULT))
+    {
+      /* Per BSD man page, setprogname keeps a pointer to the last
+        path component of the argument.  It does *not* copy the
+        argument before. */
+      __progname = strrchr (newprogname, '/');
+      if (__progname)
+       ++__progname;
+      else
+       __progname = (char *)newprogname;
+    }
+}
+
+extern "C" void
+logwtmp (const char *line, const char *user, const char *host)
+{
+  struct utmp ut;
+  memset (&ut, 0, sizeof ut);
+  ut.ut_type = USER_PROCESS;
+  ut.ut_pid = getpid ();
+  if (line)
+    strncpy (ut.ut_line, line, sizeof ut.ut_line);
+  time (&ut.ut_time);
+  if (user)
+    strncpy (ut.ut_user, user, sizeof ut.ut_user);
+  if (host)
+    strncpy (ut.ut_host, host, sizeof ut.ut_host);
+  updwtmp (_PATH_WTMP, &ut);
+}
+
+extern "C" void
+login (const struct utmp *ut)
+{
+  pututline (ut);
+  endutent ();
+  updwtmp (_PATH_WTMP, ut);
+}
+
+extern "C" int
+logout (const char *line)
+{
+  struct utmp ut_buf, *ut;
+
+  memset (&ut_buf, 0, sizeof ut_buf);
+  strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line);
+  setutent ();
+  ut = getutline (&ut_buf);
+
+  if (ut)
+    {
+      ut->ut_type = DEAD_PROCESS;
+      memset (ut->ut_user, 0, sizeof ut->ut_user);
+      memset (ut->ut_host, 0, sizeof ut->ut_host);
+      time (&ut->ut_time);
+      debug_printf ("set logout time for %s", line);
+      pututline (ut);
+      endutent ();
+      return 1;
+    }
+  return 0;
+}
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
new file mode 100644 (file)
index 0000000..6be10ec
--- /dev/null
@@ -0,0 +1,1606 @@
+/* select.cc
+
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+   2005, 2006, 2007, 2008 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* The following line means that the BSD socket definitions for
+   fd_set, FD_ISSET etc. are used in this file.  */
+
+#define  __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <stdlib.h>
+#include "ntdll.h"
+
+#include <wingdi.h>
+#include <winuser.h>
+#include <netdb.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "sigproc.h"
+#include "cygtls.h"
+
+/*
+ * All these defines below should be in sys/types.h
+ * but because of the includes above, they may not have
+ * been included. We create special UNIX_xxxx versions here.
+ */
+
+#ifndef NBBY
+#define NBBY 8    /* number of bits in a byte */
+#endif /* NBBY */
+
+/*
+ * Select uses bit masks of file descriptors in longs.
+ * These macros manipulate such bit fields (the filesystem macros use chars).
+ * FD_SETSIZE may be defined by the user, but the default here
+ * should be >= NOFILE (param.h).
+ */
+
+typedef long fd_mask;
+#define UNIX_NFDBITS (sizeof (fd_mask) * NBBY)       /* bits per mask */
+#ifndef unix_howmany
+#define unix_howmany(x,y) (((x)+((y)-1))/(y))
+#endif
+
+#define unix_fd_set fd_set
+
+#define NULL_fd_set ((fd_set *) NULL)
+#define sizeof_fd_set(n) \
+  ((unsigned) (NULL_fd_set->fds_bits + unix_howmany ((n), UNIX_NFDBITS)))
+#define UNIX_FD_SET(n, p) \
+  ((p)->fds_bits[(n)/UNIX_NFDBITS] |= (1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_CLR(n, p) \
+  ((p)->fds_bits[(n)/UNIX_NFDBITS] &= ~(1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_ISSET(n, p) \
+  ((p)->fds_bits[(n)/UNIX_NFDBITS] & (1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_ZERO(p, n) \
+  bzero ((caddr_t)(p), sizeof_fd_set ((n)))
+
+#define allocfd_set(n) ((fd_set *) memset (alloca (sizeof_fd_set (n)), 0, sizeof_fd_set (n)))
+#define copyfd_set(to, from, n) memcpy (to, from, sizeof_fd_set (n));
+
+#define set_handle_or_return_if_not_open(h, s) \
+  h = (s)->fh->get_handle (); \
+  if (cygheap->fdtab.not_open ((s)->fd)) \
+    { \
+      (s)->thread_errno =  EBADF; \
+      return -1; \
+    } \
+
+/* The main select code.
+ */
+extern "C" int
+cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+              struct timeval *to)
+{
+  select_stuff sel;
+  fd_set *dummy_readfds = allocfd_set (maxfds);
+  fd_set *dummy_writefds = allocfd_set (maxfds);
+  fd_set *dummy_exceptfds = allocfd_set (maxfds);
+
+  select_printf ("%d, %p, %p, %p, %p", maxfds, readfds, writefds, exceptfds, to);
+
+  if (!readfds)
+    readfds = dummy_readfds;
+  if (!writefds)
+    writefds = dummy_writefds;
+  if (!exceptfds)
+    exceptfds = dummy_exceptfds;
+
+  for (int i = 0; i < maxfds; i++)
+    if (!sel.test_and_set (i, readfds, writefds, exceptfds))
+      {
+       select_printf ("aborting due to test_and_set error");
+       return -1;      /* Invalid fd, maybe? */
+      }
+
+  /* Convert to milliseconds or INFINITE if to == NULL */
+  DWORD ms = to ? (to->tv_sec * 1000) + (to->tv_usec / 1000) : INFINITE;
+  if (ms == 0 && to->tv_usec)
+    ms = 1;                    /* At least 1 ms granularity */
+
+  if (to)
+    select_printf ("to->tv_sec %d, to->tv_usec %d, ms %d", to->tv_sec, to->tv_usec, ms);
+  else
+    select_printf ("to NULL, ms %x", ms);
+
+  select_printf ("sel.always_ready %d", sel.always_ready);
+
+  int timeout = 0;
+  /* Allocate some fd_set structures using the number of fds as a guide. */
+  fd_set *r = allocfd_set (maxfds);
+  fd_set *w = allocfd_set (maxfds);
+  fd_set *e = allocfd_set (maxfds);
+
+  /* Degenerate case.  No fds to wait for.  Just wait. */
+  if (sel.start.next == NULL)
+    {
+      if (WaitForSingleObject (signal_arrived, ms) == WAIT_OBJECT_0)
+       {
+         select_printf ("signal received");
+         set_sig_errno (EINTR);
+         return -1;
+       }
+      timeout = 1;
+    }
+  else if (sel.always_ready || ms == 0)
+    /* Don't bother waiting. */;
+  else if ((timeout = sel.wait (r, w, e, ms) < 0))
+    return -1; /* some kind of error */
+
+  sel.cleanup ();
+  copyfd_set (readfds, r, maxfds);
+  copyfd_set (writefds, w, maxfds);
+  copyfd_set (exceptfds, e, maxfds);
+  return timeout ? 0 : sel.poll (readfds, writefds, exceptfds);
+}
+
+extern "C" int
+pselect(int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+       const struct timespec *ts, const sigset_t *set)
+{
+  struct timeval tv;
+  sigset_t oldset = _my_tls.sigmask;
+
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return -1;
+  if (ts)
+    {
+      tv.tv_sec = ts->tv_sec;
+      tv.tv_usec = ts->tv_nsec / 1000;
+    }
+  if (set)
+    set_signal_mask (*set, _my_tls.sigmask);
+  int ret = cygwin_select (maxfds, readfds, writefds, exceptfds,
+                          ts ? &tv : NULL);
+  if (set)
+    set_signal_mask (oldset, _my_tls.sigmask);
+  return ret;
+}
+
+/* Call cleanup functions for all inspected fds.  Gets rid of any
+   executing threads. */
+void
+select_stuff::cleanup ()
+{
+  select_record *s = &start;
+
+  select_printf ("calling cleanup routines");
+  while ((s = s->next))
+    if (s->cleanup)
+      {
+       s->cleanup (s, this);
+       s->cleanup = NULL;
+      }
+}
+
+/* Destroy all storage associated with select stuff. */
+select_stuff::~select_stuff ()
+{
+  cleanup ();
+  select_record *s = &start;
+  select_record *snext = start.next;
+
+  select_printf ("deleting select records");
+  while ((s = snext))
+    {
+      snext = s->next;
+      delete s;
+    }
+}
+
+/* Add a record to the select chain */
+int
+select_stuff::test_and_set (int i, fd_set *readfds, fd_set *writefds,
+                           fd_set *exceptfds)
+{
+  select_record *s = NULL;
+  if (UNIX_FD_ISSET (i, readfds) && (s = cygheap->fdtab.select_read (i, s)) == NULL)
+    return 0; /* error */
+  if (UNIX_FD_ISSET (i, writefds) && (s = cygheap->fdtab.select_write (i, s)) == NULL)
+    return 0; /* error */
+  if (UNIX_FD_ISSET (i, exceptfds) && (s = cygheap->fdtab.select_except (i, s)) == NULL)
+    return 0; /* error */
+  if (s == NULL)
+    return 1; /* nothing to do */
+
+  if (s->read_ready || s->write_ready || s->except_ready)
+    always_ready = true;
+
+  if (s->windows_handle)
+    windows_used = true;
+
+  s->next = start.next;
+  start.next = s;
+  return 1;
+}
+
+/* The heart of select.  Waits for an fd to do something interesting. */
+int
+select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+                   DWORD ms)
+{
+  int wait_ret;
+  HANDLE w4[MAXIMUM_WAIT_OBJECTS];
+  select_record *s = &start;
+  int m = 0;
+  int res = 0;
+
+  w4[m++] = signal_arrived;  /* Always wait for the arrival of a signal. */
+  /* Loop through the select chain, starting up anything appropriate and
+     counting the number of active fds. */
+  while ((s = s->next))
+    {
+      if (m >= MAXIMUM_WAIT_OBJECTS)
+       {
+         set_sig_errno (EINVAL);
+         return -1;
+       }
+      if (!s->startup (s, this))
+       {
+         s->set_select_errno ();
+         return -1;
+       }
+      if (s->h == NULL)
+       continue;
+      for (int i = 1; i < m; i++)
+       if (w4[i] == s->h)
+         goto next_while;
+      w4[m++] = s->h;
+  next_while:
+      continue;
+    }
+
+  LONGLONG start_time = gtod.msecs (); /* Record the current time for later use. */
+
+  debug_printf ("m %d, ms %u", m, ms);
+  for (;;)
+    {
+      if (!windows_used)
+       wait_ret = WaitForMultipleObjects (m, w4, FALSE, ms);
+      else
+       wait_ret = MsgWaitForMultipleObjects (m, w4, FALSE, ms, QS_ALLINPUT);
+
+      switch (wait_ret)
+      {
+       case WAIT_OBJECT_0:
+         select_printf ("signal received");
+         set_sig_errno (EINTR);
+         return -1;
+       case WAIT_FAILED:
+         select_printf ("WaitForMultipleObjects failed");
+         s->set_select_errno ();
+         return -1;
+       case WAIT_TIMEOUT:
+         select_printf ("timed out");
+         res = 1;
+         goto out;
+      }
+
+      select_printf ("woke up.  wait_ret %d.  verifying", wait_ret);
+      s = &start;
+      bool gotone = false;
+      /* Some types of objects (e.g., consoles) wake up on "inappropriate" events
+        like mouse movements.  The verify function will detect these situations.
+        If it returns false, then this wakeup was a false alarm and we should go
+        back to waiting. */
+      while ((s = s->next))
+       if (s->saw_error ())
+         {
+           set_errno (s->saw_error ());
+           return -1;          /* Somebody detected an error */
+         }
+       else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) &&
+           s->verify (s, readfds, writefds, exceptfds))
+         gotone = true;
+
+      select_printf ("gotone %d", gotone);
+      if (gotone)
+       goto out;
+
+      if (ms == INFINITE)
+       {
+         select_printf ("looping");
+         continue;
+       }
+      select_printf ("recalculating ms");
+
+      LONGLONG now = gtod.msecs ();
+      if (now > (start_time + ms))
+       {
+         select_printf ("timed out after verification");
+         goto out;
+       }
+      ms -= (now - start_time);
+      start_time = now;
+      select_printf ("ms now %u", ms);
+    }
+
+out:
+  select_printf ("returning %d", res);
+  return res;
+}
+
+static int
+set_bits (select_record *me, fd_set *readfds, fd_set *writefds,
+         fd_set *exceptfds)
+{
+  int ready = 0;
+  fhandler_socket *sock;
+  select_printf ("me %p, testing fd %d (%s)", me, me->fd, me->fh->get_name ());
+  if (me->read_selected && me->read_ready)
+    {
+      UNIX_FD_SET (me->fd, readfds);
+      ready++;
+    }
+  if (me->write_selected && me->write_ready)
+    {
+      UNIX_FD_SET (me->fd, writefds);
+      if (me->except_on_write && (sock = me->fh->is_socket ()))
+       {
+         /* Special AF_LOCAL handling. */
+         if (!me->read_ready && sock->connect_state () == connect_pending
+             && sock->af_local_connect ())
+           {
+             if (me->read_selected)
+               UNIX_FD_SET (me->fd, readfds);
+             sock->connect_state (connect_failed);
+           }
+         else
+           sock->connect_state (connected);
+       }
+      ready++;
+    }
+  if (me->except_selected && me->except_ready)
+    {
+      UNIX_FD_SET (me->fd, exceptfds);
+      ready++;
+    }
+  select_printf ("ready %d", ready);
+  return ready;
+}
+
+/* Poll every fd in the select chain.  Set appropriate fd in mask. */
+int
+select_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
+{
+  int n = 0;
+  select_record *s = &start;
+  while ((s = s->next))
+    n += (!s->peek || s->peek (s, true)) ?
+        set_bits (s, readfds, writefds, exceptfds) : 0;
+  select_printf ("returning %d", n);
+  return n;
+}
+
+static int
+verify_true (select_record *, fd_set *, fd_set *, fd_set *)
+{
+  return 1;
+}
+
+static int
+verify_ok (select_record *me, fd_set *readfds, fd_set *writefds,
+          fd_set *exceptfds)
+{
+  return set_bits (me, readfds, writefds, exceptfds);
+}
+
+static int
+no_startup (select_record *, select_stuff *)
+{
+  return 1;
+}
+
+static int
+no_verify (select_record *, fd_set *, fd_set *, fd_set *)
+{
+  return 0;
+}
+
+static int
+peek_pipe (select_record *s, bool from_select)
+{
+  int n = 0;
+  int gotone = 0;
+  fhandler_base *fh = s->fh;
+
+  HANDLE h;
+  set_handle_or_return_if_not_open (h, s);
+
+  /* Don't perform complicated tests if we don't need to. */
+  if (!s->read_selected && !s->except_selected)
+    goto out;
+
+  if (s->read_selected)
+    {
+      if (s->read_ready)
+       {
+         select_printf ("%s, already ready for read", fh->get_name ());
+         gotone = 1;
+         goto out;
+       }
+
+      switch (fh->get_major ())
+       {
+       case DEV_TTYM_MAJOR:
+         if (((fhandler_pty_master *) fh)->need_nl)
+           {
+             gotone = s->read_ready = true;
+             goto out;
+           }
+         break;
+       default:
+         if (fh->get_readahead_valid ())
+           {
+             select_printf ("readahead");
+             gotone = s->read_ready = true;
+             goto out;
+           }
+       }
+
+      if (fh->bg_check (SIGTTIN) <= bg_eof)
+       {
+         gotone = s->read_ready = true;
+         goto out;
+       }
+    }
+
+  if (fh->get_device () == FH_PIPEW)
+    select_printf ("%s, select for read/except on write end of pipe",
+                  fh->get_name ());
+  else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
+    {
+      select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
+      n = -1;
+    }
+
+  if (n < 0)
+    {
+      select_printf ("%s, n %d", fh->get_name (), n);
+      if (s->except_selected)
+       gotone += s->except_ready = true;
+      if (s->read_selected)
+       gotone += s->read_ready = true;
+    }
+  if (n > 0 && s->read_selected)
+    {
+      select_printf ("%s, ready for read: avail %d", fh->get_name (), n);
+      gotone += s->read_ready = true;
+    }
+  if (!gotone && s->fh->hit_eof ())
+    {
+      select_printf ("%s, saw EOF", fh->get_name ());
+      if (s->except_selected)
+       gotone += s->except_ready = true;
+      if (s->read_selected)
+       gotone += s->read_ready = true;
+    }
+
+out:
+  if (s->write_selected)
+    {
+      if (s->write_ready)
+       {
+         select_printf ("%s, already ready for write", fh->get_name ());
+         gotone++;
+       }
+      /* Do we need to do anything about SIGTTOU here? */
+      else if (fh->get_device () == FH_PIPER)
+       select_printf ("%s, select for write on read end of pipe",
+                      fh->get_name ());
+      else
+       {
+#if 1
+/* FIXME: This code is not quite correct.  There's no better solution
+   so far but to make simple assumptions based on WriteQuotaAvailable. */
+
+         IO_STATUS_BLOCK iosb = {0};
+         FILE_PIPE_LOCAL_INFORMATION fpli = {0};
+
+         if (NtQueryInformationFile (h,
+                                     &iosb,
+                                     &fpli,
+                                     sizeof (fpli),
+                                     FilePipeLocalInformation))
+           {
+             /* If NtQueryInformationFile fails, optimistically assume the
+                pipe is writable.  This could happen if we somehow
+                inherit a pipe that doesn't permit FILE_READ_ATTRIBUTES
+                access on the write end.  */
+             select_printf ("%s, NtQueryInformationFile failed",
+                            fh->get_name ());
+             gotone += s->write_ready = true;
+           }
+         /* If there is anything available in the pipe buffer then signal
+            that.  This means that a pipe could still block since you could
+            be trying to write more to the pipe than is available in the
+            buffer but that is the hazard of select().  */
+         else if (fpli.WriteQuotaAvailable = (fpli.OutboundQuota - fpli.ReadDataAvailable))
+           {
+             select_printf ("%s, ready for write: size %lu, avail %lu",
+                            fh->get_name (),
+                            fpli.OutboundQuota,
+                            fpli.WriteQuotaAvailable);
+             gotone += s->write_ready = true;
+           }
+         /* If we somehow inherit a tiny pipe (size < PIPE_BUF), then consider
+            the pipe writable only if it is completely empty, to minimize the
+            probability that a subsequent write will block.  */
+         else if (fpli.OutboundQuota < PIPE_BUF &&
+                  fpli.WriteQuotaAvailable == fpli.OutboundQuota)
+           {
+             select_printf ("%s, tiny pipe: size %lu, avail %lu",
+                            fh->get_name (),
+                            fpli.OutboundQuota,
+                            fpli.WriteQuotaAvailable);
+             gotone += s->write_ready = true;
+           }
+#else
+         gotone += s->write_ready = true;
+#endif
+       }
+    }
+
+  return gotone;
+}
+
+static int start_thread_pipe (select_record *me, select_stuff *stuff);
+
+struct pipeinf
+  {
+    cygthread *thread;
+    bool stop_thread_pipe;
+    select_record *start;
+  };
+
+static DWORD WINAPI
+thread_pipe (void *arg)
+{
+  pipeinf *pi = (pipeinf *) arg;
+  bool gotone = false;
+  DWORD sleep_time = 0;
+
+  for (;;)
+    {
+      select_record *s = pi->start;
+      while ((s = s->next))
+       if (s->startup == start_thread_pipe)
+         {
+           if (peek_pipe (s, true))
+             gotone = true;
+           if (pi->stop_thread_pipe)
+             {
+               select_printf ("stopping");
+               goto out;
+             }
+         }
+      /* Paranoid check */
+      if (pi->stop_thread_pipe)
+       {
+         select_printf ("stopping from outer loop");
+         break;
+       }
+      if (gotone)
+       break;
+      Sleep (sleep_time >> 3);
+      if (sleep_time < 80)
+       ++sleep_time;
+    }
+out:
+  return 0;
+}
+
+static int
+start_thread_pipe (select_record *me, select_stuff *stuff)
+{
+  if (stuff->device_specific_pipe)
+    {
+      me->h = *((pipeinf *) stuff->device_specific_pipe)->thread;
+      return 1;
+    }
+  pipeinf *pi = new pipeinf;
+  pi->start = &stuff->start;
+  pi->stop_thread_pipe = false;
+  pi->thread = new cygthread (thread_pipe, 0, pi, "select_pipe");
+  me->h = *pi->thread;
+  if (!me->h)
+    return 0;
+  stuff->device_specific_pipe = (void *) pi;
+  return 1;
+}
+
+static void
+pipe_cleanup (select_record *, select_stuff *stuff)
+{
+  pipeinf *pi = (pipeinf *) stuff->device_specific_pipe;
+  if (pi && pi->thread)
+    {
+      pi->stop_thread_pipe = true;
+      pi->thread->detach ();
+      delete pi;
+      stuff->device_specific_pipe = NULL;
+    }
+}
+
+int
+fhandler_pipe::ready_for_read (int fd, DWORD howlong)
+{
+  int res;
+  if (!howlong)
+    res = fhandler_base::ready_for_read (fd, howlong);
+  else
+    res = 1;
+  return res;
+}
+
+select_record *
+fhandler_pipe::select_read (select_record *s)
+{
+  if (!s)
+    s = new select_record;
+  s->startup = start_thread_pipe;
+  s->peek = peek_pipe;
+  s->verify = verify_ok;
+  s->cleanup = pipe_cleanup;
+  s->read_selected = true;
+  s->read_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_pipe::select_write (select_record *s)
+{
+  if (!s)
+    s = new select_record;
+  s->startup = start_thread_pipe;
+  s->peek = peek_pipe;
+  s->verify = verify_ok;
+  s->cleanup = pipe_cleanup;
+  s->write_selected = true;
+  s->write_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_pipe::select_except (select_record *s)
+{
+  if (!s)
+    s = new select_record;
+  s->startup = start_thread_pipe;
+  s->peek = peek_pipe;
+  s->verify = verify_ok;
+  s->cleanup = pipe_cleanup;
+  s->except_selected = true;
+  s->except_ready = false;
+  return s;
+}
+
+static int
+peek_console (select_record *me, bool)
+{
+  extern const char * get_nonascii_key (INPUT_RECORD& input_rec, char *);
+  fhandler_console *fh = (fhandler_console *) me->fh;
+
+  if (!me->read_selected)
+    return me->write_ready;
+
+  if (fh->get_readahead_valid ())
+    {
+      select_printf ("readahead");
+      return me->read_ready = true;
+    }
+
+  if (me->read_ready)
+    {
+      select_printf ("already ready");
+      return 1;
+    }
+
+  INPUT_RECORD irec;
+  DWORD events_read;
+  HANDLE h;
+  char tmpbuf[17];
+  set_handle_or_return_if_not_open (h, me);
+
+  for (;;)
+    if (fh->bg_check (SIGTTIN) <= bg_eof)
+      return me->read_ready = true;
+    else if (!PeekConsoleInput (h, &irec, 1, &events_read) || !events_read)
+      break;
+    else
+      {
+       if (irec.EventType == KEY_EVENT)
+         {
+           if (irec.Event.KeyEvent.bKeyDown
+               && (irec.Event.KeyEvent.uChar.AsciiChar
+                   || get_nonascii_key (irec, tmpbuf)))
+             return me->read_ready = true;
+         }
+       else
+         {
+           fh->send_winch_maybe ();
+           if (irec.EventType == MOUSE_EVENT
+               && fh->mouse_aware ()
+               && (irec.Event.MouseEvent.dwEventFlags == 0
+                   || irec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
+               return me->read_ready = true;
+         }
+
+       /* Read and discard the event */
+       ReadConsoleInput (h, &irec, 1, &events_read);
+      }
+
+  return me->write_ready;
+}
+
+static int
+verify_console (select_record *me, fd_set *rfds, fd_set *wfds,
+             fd_set *efds)
+{
+  return peek_console (me, true);
+}
+
+
+select_record *
+fhandler_console::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_console;
+      set_cursor_maybe ();
+    }
+
+  s->peek = peek_console;
+  s->h = get_handle ();
+  s->read_selected = true;
+  s->read_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_console::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = no_verify;
+      set_cursor_maybe ();
+    }
+
+  s->peek = peek_console;
+  s->write_selected = true;
+  s->write_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_console::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = no_verify;
+      set_cursor_maybe ();
+    }
+
+  s->peek = peek_console;
+  s->except_selected = true;
+  s->except_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_tty_common::select_read (select_record *s)
+{
+  return ((fhandler_pipe *) this)->fhandler_pipe::select_read (s);
+}
+
+select_record *
+fhandler_tty_common::select_write (select_record *s)
+{
+  return ((fhandler_pipe *) this)->fhandler_pipe::select_write (s);
+}
+
+select_record *
+fhandler_tty_common::select_except (select_record *s)
+{
+  return ((fhandler_pipe *) this)->fhandler_pipe::select_except (s);
+}
+
+static int
+verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds,
+          fd_set *exceptfds)
+{
+  if (WaitForSingleObject (me->h, 0) == WAIT_OBJECT_0)
+    me->read_ready = true;
+  return set_bits (me, readfds, writefds, exceptfds);
+}
+
+select_record *
+fhandler_tty_slave::select_read (select_record *s)
+{
+  if (!s)
+    s = new select_record;
+  s->h = input_available_event;
+  s->startup = no_startup;
+  s->peek = peek_pipe;
+  s->verify = verify_tty_slave;
+  s->read_selected = true;
+  s->read_ready = false;
+  s->cleanup = NULL;
+  return s;
+}
+
+select_record *
+fhandler_dev_null::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = no_verify;
+    }
+  s->h = get_handle ();
+  s->read_selected = true;
+  s->read_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_dev_null::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = no_verify;
+    }
+  s->h = get_handle ();
+  s->write_selected = true;
+  s->write_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_dev_null::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = no_verify;
+    }
+  s->h = get_handle ();
+  s->except_selected = true;
+  s->except_ready = false;
+  return s;
+}
+
+static int start_thread_serial (select_record *me, select_stuff *stuff);
+
+struct serialinf
+  {
+    cygthread *thread;
+    bool stop_thread_serial;
+    select_record *start;
+  };
+
+static int
+peek_serial (select_record *s, bool)
+{
+  COMSTAT st;
+
+  fhandler_serial *fh = (fhandler_serial *) s->fh;
+
+  if (fh->get_readahead_valid () || fh->overlapped_armed < 0)
+    return s->read_ready = true;
+
+  select_printf ("fh->overlapped_armed %d", fh->overlapped_armed);
+
+  HANDLE h;
+  set_handle_or_return_if_not_open (h, s);
+  int ready = 0;
+
+  if (s->read_selected && s->read_ready || (s->write_selected && s->write_ready))
+    {
+      select_printf ("already ready");
+      ready = 1;
+      goto out;
+    }
+
+  SetCommMask (h, EV_RXCHAR);
+
+  if (!fh->overlapped_armed)
+    {
+      COMSTAT st;
+
+      ResetEvent (fh->io_status.hEvent);
+
+      if (!ClearCommError (h, &fh->ev, &st))
+       {
+         debug_printf ("ClearCommError");
+         goto err;
+       }
+      else if (st.cbInQue)
+       return s->read_ready = true;
+      else if (WaitCommEvent (h, &fh->ev, &fh->io_status))
+       return s->read_ready = true;
+      else if (GetLastError () == ERROR_IO_PENDING)
+       fh->overlapped_armed = 1;
+      else
+       {
+         debug_printf ("WaitCommEvent");
+         goto err;
+       }
+    }
+
+  HANDLE w4[2];
+  DWORD to;
+
+  w4[0] = fh->io_status.hEvent;
+  w4[1] = signal_arrived;
+  to = 10;
+
+  switch (WaitForMultipleObjects (2, w4, FALSE, to))
+    {
+    case WAIT_OBJECT_0:
+      if (!ClearCommError (h, &fh->ev, &st))
+       {
+         debug_printf ("ClearCommError");
+         goto err;
+       }
+      else if (!st.cbInQue)
+       Sleep (to);
+      else
+       {
+         return s->read_ready = true;
+         select_printf ("got something");
+       }
+      break;
+    case WAIT_OBJECT_0 + 1:
+      select_printf ("interrupt");
+      set_sig_errno (EINTR);
+      ready = -1;
+      break;
+    case WAIT_TIMEOUT:
+      break;
+    default:
+      debug_printf ("WaitForMultipleObjects");
+      goto err;
+    }
+
+out:
+  return ready;
+
+err:
+  if (GetLastError () == ERROR_OPERATION_ABORTED)
+    {
+      select_printf ("operation aborted");
+      return ready;
+    }
+
+  s->set_select_errno ();
+  select_printf ("error %E");
+  return -1;
+}
+
+static DWORD WINAPI
+thread_serial (void *arg)
+{
+  serialinf *si = (serialinf *) arg;
+  bool gotone = false;
+
+  for (;;)
+    {
+      select_record *s = si->start;
+      while ((s = s->next))
+       if (s->startup == start_thread_serial)
+         {
+           if (peek_serial (s, true))
+             gotone = true;
+         }
+      if (si->stop_thread_serial)
+       {
+         select_printf ("stopping");
+         break;
+       }
+      if (gotone)
+       break;
+    }
+
+  select_printf ("exiting");
+  return 0;
+}
+
+static int
+start_thread_serial (select_record *me, select_stuff *stuff)
+{
+  if (stuff->device_specific_serial)
+    {
+      me->h = *((serialinf *) stuff->device_specific_serial)->thread;
+      return 1;
+    }
+  serialinf *si = new serialinf;
+  si->start = &stuff->start;
+  si->stop_thread_serial = false;
+  si->thread = new cygthread (thread_serial, 0,  si, "select_serial");
+  me->h = *si->thread;
+  stuff->device_specific_serial = (void *) si;
+  return 1;
+}
+
+static void
+serial_cleanup (select_record *, select_stuff *stuff)
+{
+  serialinf *si = (serialinf *) stuff->device_specific_serial;
+  if (si && si->thread)
+    {
+      si->stop_thread_serial = true;
+      si->thread->detach ();
+      delete si;
+      stuff->device_specific_serial = NULL;
+    }
+}
+
+select_record *
+fhandler_serial::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = start_thread_serial;
+      s->verify = verify_ok;
+      s->cleanup = serial_cleanup;
+    }
+  s->peek = peek_serial;
+  s->read_selected = true;
+  s->read_ready = false;
+  return s;
+}
+
+select_record *
+fhandler_serial::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->peek = peek_serial;
+  s->h = get_handle ();
+  s->write_selected = true;
+  s->write_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_serial::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->h = NULL;
+  s->peek = peek_serial;
+  s->except_selected = false;  // Can't do this
+  s->except_ready = false;
+  return s;
+}
+
+int
+fhandler_base::ready_for_read (int fd, DWORD howlong)
+{
+  bool avail = false;
+  select_record me (this);
+  me.fd = fd;
+  while (!avail)
+    {
+      select_read (&me);
+      avail = me.read_ready ?: me.peek (&me, false);
+
+      if (fd >= 0 && cygheap->fdtab.not_open (fd))
+       {
+         set_sig_errno (EBADF);
+         avail = false;
+         break;
+       }
+
+      if (howlong != INFINITE)
+       {
+         if (!avail)
+           set_sig_errno (EAGAIN);
+         break;
+       }
+
+      if (WaitForSingleObject (signal_arrived, avail ? 0 : 10) == WAIT_OBJECT_0)
+       {
+         debug_printf ("interrupted");
+         set_sig_errno (EINTR);
+         avail = false;
+         break;
+       }
+    }
+
+  select_printf ("read_ready %d, avail %d", me.read_ready, avail);
+  return avail;
+}
+
+select_record *
+fhandler_base::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->h = get_handle ();
+  s->read_selected = true;
+  s->read_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_base::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->h = get_handle ();
+  s->write_selected = true;
+  s->write_ready = true;
+  return s;
+}
+
+select_record *
+fhandler_base::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->h = NULL;
+  s->except_selected = true;
+  s->except_ready = false;
+  return s;
+}
+
+static int
+peek_socket (select_record *me, bool)
+{
+  fhandler_socket *fh = (fhandler_socket *) me->fh;
+  long events;
+  /* Don't play with the settings again, unless having taken a deep look into
+     Richard W. Stevens Network Programming book.  Thank you. */
+  long evt_mask = (me->read_selected ? (FD_READ | FD_ACCEPT | FD_CLOSE) : 0)
+               | (me->write_selected ? (FD_WRITE | FD_CONNECT | FD_CLOSE) : 0)
+               | (me->except_selected ? FD_OOB : 0);
+  int ret = fh->evaluate_events (evt_mask, events, false);
+  if (me->read_selected)
+    me->read_ready |= ret || !!(events & (FD_READ | FD_ACCEPT | FD_CLOSE));
+  if (me->write_selected)
+    me->write_ready |= ret || !!(events & (FD_WRITE | FD_CONNECT | FD_CLOSE));
+  if (me->except_selected)
+    me->except_ready |= !!(events & FD_OOB);
+
+  select_printf ("read_ready: %d, write_ready: %d, except_ready: %d",
+                me->read_ready, me->write_ready, me->except_ready);
+  return me->read_ready || me->write_ready || me->except_ready;
+}
+
+static int start_thread_socket (select_record *, select_stuff *);
+
+struct socketinf
+  {
+    cygthread *thread;
+    int max_w4;
+    int num_w4;
+    LONG *ser_num;
+    HANDLE *w4;
+    select_record *start;
+  };
+
+static DWORD WINAPI
+thread_socket (void *arg)
+{
+  socketinf *si = (socketinf *) arg;
+  DWORD timeout = 64 / (si->max_w4 / MAXIMUM_WAIT_OBJECTS);
+  bool event = false;
+
+  select_printf ("stuff_start %p", si->start);
+  while (!event)
+    {
+      for (select_record *s = si->start; (s = s->next); )
+       if (s->startup == start_thread_socket)
+         if (peek_socket (s, false))
+           event = true;
+      if (!event)
+       for (int i = 0; i < si->max_w4; i += MAXIMUM_WAIT_OBJECTS)
+         switch (WaitForMultipleObjects (min (si->num_w4 - i,
+                                              MAXIMUM_WAIT_OBJECTS),
+                                         si->w4 + i, FALSE, timeout))
+           {
+           case WAIT_FAILED:
+             goto out;
+           case WAIT_OBJECT_0:
+             if (!i)   /* Socket event set. */
+               goto out;
+             break;
+           case WAIT_TIMEOUT:
+           default:
+             break;
+           }
+    }
+out:
+  select_printf ("leaving thread_socket");
+  return 0;
+}
+
+static int
+start_thread_socket (select_record *me, select_stuff *stuff)
+{
+  socketinf *si;
+
+  if ((si = (socketinf *) stuff->device_specific_socket))
+    {
+      me->h = *si->thread;
+      return 1;
+    }
+
+  si = new socketinf;
+  si->ser_num = (LONG *) malloc (MAXIMUM_WAIT_OBJECTS * sizeof (LONG));
+  si->w4 = (HANDLE *) malloc (MAXIMUM_WAIT_OBJECTS * sizeof (HANDLE));
+  if (!si->ser_num || !si->w4)
+    return 0;
+  si->max_w4 = MAXIMUM_WAIT_OBJECTS;
+  select_record *s = &stuff->start;
+  if (_my_tls.locals.select_sockevt != INVALID_HANDLE_VALUE)
+    si->w4[0] = _my_tls.locals.select_sockevt;
+  else if (!(si->w4[0] = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL)))
+    return 1;
+  else
+    _my_tls.locals.select_sockevt = si->w4[0];
+  si->num_w4 = 1;
+  while ((s = s->next))
+    if (s->startup == start_thread_socket)
+      {
+       /* No event/socket should show up multiple times.  Every socket
+          is uniquely identified by its serial number in the global
+          wsock_events record. */
+       const LONG ser_num = ((fhandler_socket *) s->fh)->serial_number ();
+       for (int i = 1; i < si->num_w4; ++i)
+         if (si->ser_num[i] == ser_num)
+           goto continue_outer_loop;
+       if (si->num_w4 >= si->max_w4)
+         {
+           LONG *nser = (LONG *) realloc (si->ser_num,
+                                          (si->max_w4 + MAXIMUM_WAIT_OBJECTS)
+                                          * sizeof (LONG));
+           if (!nser)
+             return 0;
+           si->ser_num = nser;
+           HANDLE *nw4 = (HANDLE *) realloc (si->w4,
+                                          (si->max_w4 + MAXIMUM_WAIT_OBJECTS)
+                                          * sizeof (HANDLE));
+           if (!nw4)
+             return 0;
+           si->w4 = nw4;
+           si->max_w4 += MAXIMUM_WAIT_OBJECTS;
+         }
+       si->ser_num[si->num_w4] = ser_num;
+       si->w4[si->num_w4++] = ((fhandler_socket *) s->fh)->wsock_event ();
+      continue_outer_loop:
+       ;
+      }
+  stuff->device_specific_socket = (void *) si;
+  si->start = &stuff->start;
+  select_printf ("stuff_start %p", &stuff->start);
+  si->thread = new cygthread (thread_socket, 0,  si, "select_socket");
+  me->h = *si->thread;
+  return 1;
+}
+
+void
+socket_cleanup (select_record *, select_stuff *stuff)
+{
+  socketinf *si = (socketinf *) stuff->device_specific_socket;
+  select_printf ("si %p si->thread %p", si, si ? si->thread : NULL);
+  if (si && si->thread)
+    {
+      SetEvent (si->w4[0]);
+      /* Wait for thread to go away */
+      si->thread->detach ();
+      ResetEvent (si->w4[0]);
+      stuff->device_specific_socket = NULL;
+      if (si->ser_num)
+       free (si->ser_num);
+      if (si->w4)
+       free (si->w4);
+      delete si;
+    }
+  select_printf ("returning");
+}
+
+select_record *
+fhandler_socket::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = start_thread_socket;
+      s->verify = verify_true;
+      s->cleanup = socket_cleanup;
+    }
+  s->peek = peek_socket;
+  s->read_ready = saw_shutdown_read ();
+  s->read_selected = true;
+  return s;
+}
+
+select_record *
+fhandler_socket::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = start_thread_socket;
+      s->verify = verify_true;
+      s->cleanup = socket_cleanup;
+    }
+  s->peek = peek_socket;
+  s->write_ready = saw_shutdown_write () || connect_state () == unconnected;
+  s->write_selected = true;
+  if (connect_state () != unconnected)
+    {
+      s->except_ready = saw_shutdown_write () || saw_shutdown_read ();
+      s->except_on_write = true;
+    }
+  return s;
+}
+
+select_record *
+fhandler_socket::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = start_thread_socket;
+      s->verify = verify_true;
+      s->cleanup = socket_cleanup;
+    }
+  s->peek = peek_socket;
+  /* FIXME: Is this right?  Should these be used as criteria for except? */
+  s->except_ready = saw_shutdown_write () || saw_shutdown_read ();
+  s->except_selected = true;
+  return s;
+}
+
+static int
+peek_windows (select_record *me, bool)
+{
+  MSG m;
+  HANDLE h;
+  set_handle_or_return_if_not_open (h, me);
+
+  if (me->read_selected && me->read_ready)
+    return 1;
+
+  if (PeekMessage (&m, (HWND) h, 0, 0, PM_NOREMOVE))
+    {
+      me->read_ready = true;
+      select_printf ("window %d(%p) ready", me->fd, me->fh->get_handle ());
+      return 1;
+    }
+
+  select_printf ("window %d(%p) not ready", me->fd, me->fh->get_handle ());
+  return me->write_ready;
+}
+
+static int
+verify_windows (select_record *me, fd_set *rfds, fd_set *wfds,
+               fd_set *efds)
+{
+  return peek_windows (me, true);
+}
+
+select_record *
+fhandler_windows::select_read (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+    }
+  s->verify = verify_windows;
+  s->peek = peek_windows;
+  s->read_selected = true;
+  s->read_ready = false;
+  s->h = get_handle ();
+  s->windows_handle = true;
+  return s;
+}
+
+select_record *
+fhandler_windows::select_write (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->peek = peek_windows;
+  s->h = get_handle ();
+  s->write_selected = true;
+  s->write_ready = true;
+  s->windows_handle = true;
+  return s;
+}
+
+select_record *
+fhandler_windows::select_except (select_record *s)
+{
+  if (!s)
+    {
+      s = new select_record;
+      s->startup = no_startup;
+      s->verify = verify_ok;
+    }
+  s->peek = peek_windows;
+  s->h = get_handle ();
+  s->except_selected = true;
+  s->except_ready = false;
+  s->windows_handle = true;
+  return s;
+}
+
+static int
+peek_mailslot (select_record *me, bool)
+{
+  HANDLE h;
+  set_handle_or_return_if_not_open (h, me);
+
+  if (me->read_selected && me->read_ready)
+    return 1;
+  DWORD msgcnt = 0;
+  if (!GetMailslotInfo (h, NULL, NULL, &msgcnt, NULL))
+    {
+      select_printf ("mailslot %d(%p) error %E", me->fd, h);
+      return 1;
+    }
+  if (msgcnt > 0)
+    {
+      me->read_ready = true;
+      select_printf ("mailslot %d(%p) ready", me->fd, h);
+      return 1;
+    }
+  select_printf ("mailslot %d(%p) not ready", me->fd, h);
+  return 0;
+}
+
+static int
+verify_mailslot (select_record *me, fd_set *rfds, fd_set *wfds,
+                fd_set *efds)
+{
+  return peek_mailslot (me, true);
+}
+
+static int start_thread_mailslot (select_record *me, select_stuff *stuff);
+
+struct mailslotinf
+  {
+    cygthread *thread;
+    bool stop_thread_mailslot;
+    select_record *start;
+  };
+
+static DWORD WINAPI
+thread_mailslot (void *arg)
+{
+  mailslotinf *mi = (mailslotinf *) arg;
+  bool gotone = false;
+  DWORD sleep_time = 0;
+
+  for (;;)
+    {
+      select_record *s = mi->start;
+      while ((s = s->next))
+       if (s->startup == start_thread_mailslot)
+         {
+           if (peek_mailslot (s, true))
+             gotone = true;
+           if (mi->stop_thread_mailslot)
+             {
+               select_printf ("stopping");
+               goto out;
+             }
+         }
+      /* Paranoid check */
+      if (mi->stop_thread_mailslot)
+       {
+         select_printf ("stopping from outer loop");
+         break;
+       }
+      if (gotone)
+       break;
+      Sleep (sleep_time >> 3);
+      if (sleep_time < 80)
+       ++sleep_time;
+    }
+out:
+  return 0;
+}
+
+static int
+start_thread_mailslot (select_record *me, select_stuff *stuff)
+{
+  if (stuff->device_specific_mailslot)
+    {
+      me->h = *((mailslotinf *) stuff->device_specific_mailslot)->thread;
+      return 1;
+    }
+  mailslotinf *mi = new mailslotinf;
+  mi->start = &stuff->start;
+  mi->stop_thread_mailslot = false;
+  mi->thread = new cygthread (thread_mailslot, 0,  mi, "select_mailslot");
+  me->h = *mi->thread;
+  if (!me->h)
+    return 0;
+  stuff->device_specific_mailslot = (void *) mi;
+  return 1;
+}
+
+static void
+mailslot_cleanup (select_record *, select_stuff *stuff)
+{
+  mailslotinf *mi = (mailslotinf *) stuff->device_specific_mailslot;
+  if (mi && mi->thread)
+    {
+      mi->stop_thread_mailslot = true;
+      mi->thread->detach ();
+      delete mi;
+      stuff->device_specific_mailslot = NULL;
+    }
+}
+
+select_record *
+fhandler_mailslot::select_read (select_record *s)
+{
+  if (!s)
+    s = new select_record;
+  s->startup = start_thread_mailslot;
+  s->peek = peek_mailslot;
+  s->verify = verify_mailslot;
+  s->cleanup = mailslot_cleanup;
+  s->read_selected = true;
+  s->read_ready = false;
+  return s;
+}
index a8a66ec..92446bc 100644 (file)
@@ -3098,7 +3098,7 @@ getutent ()
 }
 
 extern "C" struct utmp *
-getutid (struct utmp *id)
+getutid (const struct utmp *id)
 {
   myfault efault;
   if (efault.faulted (EFAULT))
@@ -3137,7 +3137,7 @@ getutid (struct utmp *id)
 }
 
 extern "C" struct utmp *
-getutline (struct utmp *line)
+getutline (const struct utmp *line)
 {
   myfault efault;
   if (efault.faulted (EFAULT))
@@ -3160,7 +3160,7 @@ getutline (struct utmp *line)
 }
 
 extern "C" struct utmp *
-pututline (struct utmp *ut)
+pututline (const struct utmp *ut)
 {
   myfault efault;
   if (efault.faulted (EFAULT))
@@ -3184,7 +3184,10 @@ pututline (struct utmp *ut)
     }
   else
     locked_append (utmp_fd, ut, sizeof *ut);
-  return ut;
+  /* The documentation says to return a pointer to this which implies that
+     this has to be cast from a const.  That doesn't seem right but the
+     documentation seems pretty clear on this.  */
+  return (struct utmp *) ut;
 }
 
 extern "C" void
@@ -3202,6 +3205,7 @@ endutxent ()
 extern "C" struct utmpx *
 getutxent ()
 {
+  /* UGH.  Not thread safe. */
   static struct utmpx utx;
   return copy_ut_to_utx (getutent (), &utx);
 }
@@ -3209,6 +3213,7 @@ getutxent ()
 extern "C" struct utmpx *
 getutxid (const struct utmpx *id)
 {
+  /* UGH.  Not thread safe. */
   static struct utmpx utx;
 
   myfault efault;
@@ -3221,6 +3226,7 @@ getutxid (const struct utmpx *id)
 extern "C" struct utmpx *
 getutxline (const struct utmpx *line)
 {
+  /* UGH.  Not thread safe. */
   static struct utmpx utx;
 
   myfault efault;
@@ -3233,6 +3239,7 @@ getutxline (const struct utmpx *line)
 extern "C" struct utmpx *
 pututxline (const struct utmpx *utmpx)
 {
+  /* UGH.  Not thread safe. */
   static struct utmpx utx;
 
   myfault efault;