summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2008-02-18 14:08:37 +0000
committerCorinna Vinschen <vinschen@redhat.com>2008-02-18 14:08:37 +0000
commit388a43e8fe989d72eea97a9336a6a5dfbde263d4 (patch)
tree482f1f453b310c8d276e810a9105d17f3d44bd56
parent69a10b127727c274be899b08ddc994f794af27f7 (diff)
downloadgdb-388a43e8fe989d72eea97a9336a6a5dfbde263d4.tar.gz
* exceptions.cc (handle_sigsuspend): Eliminate thread signal mask and
use either main sigmask or current thread sigmask. (set_process_mask): Ditto. (sighold): Ditto. (sigrelse): Ditto. (sigset): Ditto. (set_process_mask_delta): Ditto. (_cygtls::call_signal_handler): Ditto. * fhandler_process.cc (format_process_status): Ditto. * fhandler_termios.cc (fhandler_termios::bg_check): Ditto. * pinfo.h (class pinfo): Ditto. * select.cc (pselect): Ditto. * signal.cc (sigprocmask): Ditto. (abort): Ditto. (sigpause): Ditto. (sigsend): Ditto. (wait_sig): Ditto. * sigproc.cc (sig_send): Ditto. (pending_signals::add): Ditto. (wait_sig): Ditto. * thread.h (pthread::parent_tls): New member. * thread.cc (pthread::pthread): Record parent_tls here. (pthread::thread_init_wrapper): Initialize sigmask from parent thread.
-rw-r--r--winsup/cygwin/ChangeLog26
-rw-r--r--winsup/cygwin/exceptions.cc25
-rw-r--r--winsup/cygwin/fhandler_process.cc979
-rw-r--r--winsup/cygwin/fhandler_termios.cc363
-rw-r--r--winsup/cygwin/pinfo.h246
-rw-r--r--winsup/cygwin/select.cc1730
-rw-r--r--winsup/cygwin/signal.cc6
-rw-r--r--winsup/cygwin/sigproc.cc8
-rw-r--r--winsup/cygwin/thread.cc2
-rw-r--r--winsup/cygwin/thread.h1
10 files changed, 3366 insertions, 20 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 8494c2218bf..eab0418f65c 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,29 @@
+2008-02-15 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * exceptions.cc (handle_sigsuspend): Eliminate thread signal mask and
+ use either main sigmask or current thread sigmask.
+ (set_process_mask): Ditto.
+ (sighold): Ditto.
+ (sigrelse): Ditto.
+ (sigset): Ditto.
+ (set_process_mask_delta): Ditto.
+ (_cygtls::call_signal_handler): Ditto.
+ * fhandler_process.cc (format_process_status): Ditto.
+ * fhandler_termios.cc (fhandler_termios::bg_check): Ditto.
+ * pinfo.h (class pinfo): Ditto.
+ * select.cc (pselect): Ditto.
+ * signal.cc (sigprocmask): Ditto.
+ (abort): Ditto.
+ (sigpause): Ditto.
+ (sigsend): Ditto.
+ (wait_sig): Ditto.
+ * sigproc.cc (sig_send): Ditto.
+ (pending_signals::add): Ditto.
+ (wait_sig): Ditto.
+ * thread.h (pthread::parent_tls): New member.
+ * thread.cc (pthread::pthread): Record parent_tls here.
+ (pthread::thread_init_wrapper): Initialize sigmask from parent thread.
+
2008-02-13 Christopher Faylor <me+cygwin@cgf.cx>
* thread.cc (pthread_kill): Deal with signal 0 as per POSIX and also
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index d4c14e29563..ad0af387166 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -687,9 +687,9 @@ handle_sigsuspend (sigset_t tempmask)
return -1;
}
- sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+ sigset_t oldmask = _my_tls.sigmask; // Remember for restoration
- set_signal_mask (tempmask, myself->getsigmask ());
+ set_signal_mask (tempmask, _my_tls.sigmask);
sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
pthread_testcancel ();
@@ -999,8 +999,7 @@ ctrl_c_handler (DWORD type)
extern "C" void __stdcall
set_process_mask (sigset_t newmask)
{
- set_signal_mask (newmask, myself->getsigmask ());
-sigproc_printf ("mask now %p\n", myself->getsigmask ());
+ set_signal_mask (newmask, _my_tls.sigmask);
}
extern "C" int
@@ -1014,9 +1013,9 @@ sighold (int sig)
return -1;
}
mask_sync.acquire (INFINITE);
- sigset_t mask = myself->getsigmask ();
+ sigset_t mask = _my_tls.sigmask;
sigaddset (&mask, sig);
- set_signal_mask (mask, myself->getsigmask ());
+ set_signal_mask (mask, _my_tls.sigmask);
mask_sync.release ();
return 0;
}
@@ -1032,9 +1031,9 @@ sigrelse (int sig)
return -1;
}
mask_sync.acquire (INFINITE);
- sigset_t mask = myself->getsigmask ();
+ sigset_t mask = _my_tls.sigmask;
sigdelset (&mask, sig);
- set_signal_mask (mask, myself->getsigmask ());
+ set_signal_mask (mask, _my_tls.sigmask);
mask_sync.release ();
return 0;
}
@@ -1054,7 +1053,7 @@ sigset (int sig, _sig_func_ptr func)
}
mask_sync.acquire (INFINITE);
- sigset_t mask = myself->getsigmask ();
+ sigset_t mask = _my_tls.sigmask;
/* If sig was in the signal mask return SIG_HOLD, otherwise return the
previous disposition. */
if (sigismember (&mask, sig))
@@ -1072,7 +1071,7 @@ sigset (int sig, _sig_func_ptr func)
signal (sig, func);
sigdelset (&mask, sig);
}
- set_signal_mask (mask, myself->getsigmask ());
+ set_signal_mask (mask, _my_tls.sigmask);
mask_sync.release ();
return prev;
}
@@ -1094,11 +1093,11 @@ set_process_mask_delta ()
if (_my_tls.deltamask & SIG_NONMASKABLE)
oldmask = _my_tls.oldmask; /* from handle_sigsuspend */
else
- oldmask = myself->getsigmask ();
+ oldmask = _my_tls.sigmask;
newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
_my_tls.deltamask);
- myself->setsigmask (newmask);
+ _my_tls.sigmask = newmask;
mask_sync.release ();
return oldmask;
}
@@ -1366,7 +1365,7 @@ _cygtls::call_signal_handler ()
sigact (thissig, &thissi, NULL);
}
incyg = 1;
- set_signal_mask (this_oldmask, myself->getsigmask ());
+ set_signal_mask (this_oldmask, _my_tls.sigmask);
if (this_errno >= 0)
set_errno (this_errno);
}
diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc
new file mode 100644
index 00000000000..b07f4015c23
--- /dev/null
+++ b/winsup/cygwin/fhandler_process.cc
@@ -0,0 +1,979 @@
+/* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
+
+ Copyright 2002, 2003, 2004, 2005, 2006 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. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "dtable.h"
+#include "cygtls.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include <sys/param.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <psapi.h>
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+static const int PROCESS_PPID = 2;
+static const int PROCESS_WINPID = 3;
+static const int PROCESS_WINEXENAME = 4;
+static const int PROCESS_STATUS = 5;
+static const int PROCESS_UID = 6;
+static const int PROCESS_GID = 7;
+static const int PROCESS_PGID = 8;
+static const int PROCESS_SID = 9;
+static const int PROCESS_CTTY = 10;
+static const int PROCESS_STAT = 11;
+static const int PROCESS_STATM = 12;
+static const int PROCESS_CMDLINE = 13;
+static const int PROCESS_MAPS = 14;
+static const int PROCESS_FD = 15;
+static const int PROCESS_EXENAME = 16;
+/* Keep symlinks always the last entries. */
+static const int PROCESS_ROOT = 17;
+static const int PROCESS_EXE = 18;
+static const int PROCESS_CWD = 19;
+
+/* The position of "root" defines the beginning of symlik entries. */
+#define is_symlink(nr) ((nr) >= PROCESS_ROOT)
+
+static const char * const process_listing[] =
+{
+ ".",
+ "..",
+ "ppid",
+ "winpid",
+ "winexename",
+ "status",
+ "uid",
+ "gid",
+ "pgid",
+ "sid",
+ "ctty",
+ "stat",
+ "statm",
+ "cmdline",
+ "maps",
+ "fd",
+ "exename",
+ /* Keep symlinks always the last entries. */
+ "root",
+ "exe",
+ "cwd",
+ NULL
+};
+
+static const int PROCESS_LINK_COUNT =
+ (sizeof (process_listing) / sizeof (const char *)) - 1;
+
+static _off64_t format_process_maps (_pinfo *p, char *&destbuf, size_t maxsize);
+static _off64_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize);
+static _off64_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize);
+static _off64_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize);
+static int get_process_state (DWORD dwProcessId);
+static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize,
+ unsigned long *vmrss, unsigned long *vmtext,
+ unsigned long *vmdata, unsigned long *vmlib,
+ unsigned long *vmshare);
+
+/* Returns 0 if path doesn't exist, >0 if path is a directory,
+ * -1 if path is a file, -2 if path is a symlink, -3 if path is a pipe,
+ * -4 if path is a socket.
+ */
+int
+fhandler_process::exists ()
+{
+ const char *path = get_name ();
+ debug_printf ("exists (%s)", path);
+ path += proc_len + 1;
+ while (*path != 0 && !isdirsep (*path))
+ path++;
+ if (*path == 0)
+ return 2;
+
+ for (int i = 0; process_listing[i]; i++)
+ if (pathmatch (path + 1, process_listing[i]))
+ {
+ fileid = i;
+ return is_symlink (i) ? -2 : (i == PROCESS_FD) ? 1 : -1;
+ }
+ if (pathnmatch (strchr (path, '/') + 1, "fd/", 3))
+ {
+ fileid = PROCESS_FD;
+ if (fill_filebuf ())
+ return -2;
+ /* Check for nameless device entries. */
+ path = strrchr (path, '/');
+ if (path && *++path)
+ {
+ if (!strncmp (path, "pipe:[", 6))
+ return -3;
+ else if (!strncmp (path, "socket:[", 8))
+ return -4;
+ }
+ }
+ return 0;
+}
+
+fhandler_process::fhandler_process ():
+ fhandler_proc ()
+{
+}
+
+int
+fhandler_process::fstat (struct __stat64 *buf)
+{
+ const char *path = get_name ();
+ int file_type = exists ();
+ fhandler_base::fstat (buf);
+ path += proc_len + 1;
+ pid = atoi (path);
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ buf->st_mode &= ~_IFMT & NO_W;
+
+ switch (file_type)
+ {
+ case 0:
+ set_errno (ENOENT);
+ return -1;
+ case 1:
+ case 2:
+ buf->st_ctime = buf->st_mtime = p->start_time;
+ buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec = 0;
+ time_as_timestruc_t (&buf->st_atim);
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ if (file_type == 1)
+ buf->st_nlink = 2;
+ else
+ buf->st_nlink = 3;
+ return 0;
+ case -2:
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+ return 0;
+ case -3:
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR;
+ return 0;
+ case -4:
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode = S_IFSOCK | S_IRUSR | S_IWUSR;
+ return 0;
+ case -1:
+ default:
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ return 0;
+ }
+}
+
+DIR *
+fhandler_process::opendir ()
+{
+ DIR *dir = fhandler_virtual::opendir ();
+ if (dir)
+ {
+ if (fileid == PROCESS_FD)
+ fill_filebuf ();
+ dir->__flags = 0;
+ }
+ return dir;
+}
+
+int
+fhandler_process::readdir (DIR *dir, dirent *de)
+{
+ int res = ENMFILE;
+ if (fileid == PROCESS_FD)
+ {
+ if (dir->__d_position >= 2 + filesize / sizeof (int))
+ goto out;
+ }
+ else if (dir->__d_position >= PROCESS_LINK_COUNT)
+ goto out;
+ if (fileid == PROCESS_FD && dir->__d_position > 1)
+ {
+ int *p = (int *) filebuf;
+ __small_sprintf (de->d_name, "%d", p[dir->__d_position++ - 2]);
+ }
+ else
+ strcpy (de->d_name, process_listing[dir->__d_position++]);
+ dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot;
+ res = 0;
+out:
+ syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, de, de->d_name);
+ return res;
+}
+
+int
+fhandler_process::open (int flags, mode_t mode)
+{
+ int process_file_no = -1;
+
+ int res = fhandler_virtual::open (flags, mode);
+ if (!res)
+ goto out;
+
+ nohandle (true);
+
+ const char *path;
+ path = get_name () + proc_len + 1;
+ pid = atoi (path);
+ while (*path != 0 && !isdirsep (*path))
+ path++;
+
+ if (*path == 0)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+
+ process_file_no = -1;
+ for (int i = 0; process_listing[i]; i++)
+ {
+ if (path_prefix_p
+ (process_listing[i], path + 1, strlen (process_listing[i])))
+ process_file_no = i;
+ }
+ if (process_file_no == -1)
+ {
+ if (flags & O_CREAT)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ set_errno (ENOENT);
+ res = 0;
+ goto out;
+ }
+ }
+ if (process_file_no == PROCESS_FD)
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ if (flags & O_WRONLY)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+
+ fileid = process_file_no;
+ if (!fill_filebuf ())
+ {
+ res = 0;
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ position = filesize;
+ else
+ position = 0;
+
+success:
+ res = 1;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+out:
+ syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
+ return res;
+}
+
+bool
+fhandler_process::fill_filebuf ()
+{
+ const char *path;
+ path = get_name () + proc_len + 1;
+ if (!pid)
+ pid = atoi (path);
+
+ pinfo p (pid);
+
+ if (!p)
+ {
+ set_errno (ENOENT);
+ return false;
+ }
+
+ switch (fileid)
+ {
+ case PROCESS_FD:
+ {
+ size_t fs;
+ char *fdp = strrchr (path, '/');
+ if (!fdp || *++fdp == 'f') /* The "fd" directory itself. */
+ {
+ if (filebuf)
+ cfree (filebuf);
+ filebuf = p->fds (fs);
+ }
+ else
+ {
+ if (filebuf)
+ cfree (filebuf);
+ int fd = atoi (fdp);
+ if (fd < 0 || (fd == 0 && !isdigit (*fdp)))
+ {
+ set_errno (ENOENT);
+ return false;
+ }
+ filebuf = p->fd (fd, fs);
+ if (!filebuf || !*filebuf)
+ {
+ set_errno (ENOENT);
+ return false;
+ }
+ }
+ filesize = fs;
+ break;
+ }
+ case PROCESS_UID:
+ case PROCESS_GID:
+ case PROCESS_PGID:
+ case PROCESS_SID:
+ case PROCESS_CTTY:
+ case PROCESS_PPID:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 40);
+ int num;
+ switch (fileid)
+ {
+ case PROCESS_PPID:
+ num = p->ppid;
+ break;
+ case PROCESS_UID:
+ num = p->uid;
+ break;
+ case PROCESS_PGID:
+ num = p->pgid;
+ break;
+ case PROCESS_SID:
+ num = p->sid;
+ break;
+ case PROCESS_GID:
+ num = p->gid;
+ break;
+ case PROCESS_CTTY:
+ num = p->ctty;
+ break;
+ default: // what's this here for?
+ num = 0;
+ break;
+ }
+ __small_sprintf (filebuf, "%d\n", num);
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_ROOT:
+ case PROCESS_CWD:
+ case PROCESS_CMDLINE:
+ {
+ if (filebuf)
+ {
+ cfree (filebuf);
+ filebuf = NULL;
+ }
+ size_t fs;
+ switch (fileid)
+ {
+ case PROCESS_ROOT:
+ filebuf = p->root (fs);
+ break;
+ case PROCESS_CWD:
+ filebuf = p->cwd (fs);
+ break;
+ case PROCESS_CMDLINE:
+ filebuf = p->cmdline (fs);
+ break;
+ }
+ filesize = fs;
+ if (!filebuf || !*filebuf)
+ {
+ filebuf = cstrdup ("<defunct>");
+ filesize = strlen (filebuf) + 1;
+ }
+ break;
+ }
+ case PROCESS_EXENAME:
+ case PROCESS_EXE:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = CYG_MAX_PATH);
+ if (p->process_state & PID_EXITED)
+ strcpy (filebuf, "<defunct>");
+ else
+ {
+ mount_table->conv_to_posix_path (p->progname, filebuf, 1);
+ /* If transparent_exe isn't set, the link keeps its suffix so that
+ an open(2) call will succeed on /proc/$PID/exe. */
+ if (transparent_exe)
+ {
+ int len = strlen (filebuf);
+ if (len > 4)
+ {
+ char *s = filebuf + len - 4;
+ if (strcasematch (s, ".exe"))
+ *s = 0;
+ }
+ }
+ }
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_WINPID:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 40);
+ __small_sprintf (filebuf, "%d\n", p->dwProcessId);
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_WINEXENAME:
+ {
+ int len = strlen (p->progname);
+ filebuf = (char *) crealloc (filebuf, bufalloc = (len + 2));
+ strcpy (filebuf, p->progname);
+ filebuf[len] = '\n';
+ filesize = len + 1;
+ break;
+ }
+ case PROCESS_STATUS:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 2048);
+ filesize = format_process_status (*p, filebuf, bufalloc);
+ break;
+ }
+ case PROCESS_STAT:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 2048);
+ filesize = format_process_stat (*p, filebuf, bufalloc);
+ break;
+ }
+ case PROCESS_STATM:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 2048);
+ filesize = format_process_statm (*p, filebuf, bufalloc);
+ break;
+ }
+ case PROCESS_MAPS:
+ {
+ filebuf = (char *) crealloc (filebuf, bufalloc = 2048);
+ filesize = format_process_maps (*p, filebuf, bufalloc);
+ break;
+ }
+ }
+
+ return true;
+}
+
+static _off64_t
+format_process_maps (_pinfo *p, char *&destbuf, size_t maxsize)
+{
+ if (!wincap.is_winnt ())
+ return 0;
+
+ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+ FALSE,
+ p->dwProcessId);
+ if (!proc)
+ return 0;
+
+ _off64_t len = 0;
+ HMODULE *modules;
+ DWORD needed, i;
+ DWORD_PTR wset_size;
+ DWORD_PTR *workingset = NULL;
+ MODULEINFO info;
+ char modname[CYG_MAX_PATH];
+ char posix_modname[CYG_MAX_PATH];
+
+ if (!EnumProcessModules (proc, NULL, 0, &needed))
+ {
+ __seterrno ();
+ len = -1;
+ goto out;
+ }
+ modules = (HMODULE*) alloca (needed);
+ if (!EnumProcessModules (proc, modules, needed, &needed))
+ {
+ __seterrno ();
+ len = -1;
+ goto out;
+ }
+
+ QueryWorkingSet (proc, (void *) &wset_size, sizeof wset_size);
+ if (GetLastError () == ERROR_BAD_LENGTH)
+ {
+ workingset = (DWORD_PTR *) alloca (sizeof (DWORD_PTR) * ++wset_size);
+ if (!QueryWorkingSet (proc, (void *) workingset,
+ sizeof (DWORD_PTR) * wset_size))
+ workingset = NULL;
+ }
+ for (i = 0; i < needed / sizeof (HMODULE); i++)
+ if (GetModuleInformation (proc, modules[i], &info, sizeof info)
+ && GetModuleFileNameEx (proc, modules[i], modname, sizeof modname))
+ {
+ char access[5];
+ strcpy (access, "r--p");
+ struct __stat64 st;
+ cygwin_conv_to_full_posix_path (modname, posix_modname);
+ if (stat64 (posix_modname, &st))
+ {
+ st.st_dev = 0;
+ st.st_ino = 0;
+ }
+ if (len + strlen (posix_modname) + 62 > maxsize - 1)
+ destbuf = (char *) crealloc (destbuf, maxsize += 2048);
+ if (workingset)
+ for (unsigned i = 1; i <= wset_size; ++i)
+ {
+ DWORD_PTR addr = workingset[i] & 0xfffff000UL;
+ if ((char *)addr >= info.lpBaseOfDll
+ && (char *)addr < (char *)info.lpBaseOfDll + info.SizeOfImage)
+ {
+ access[0] = (workingset[i] & 0x5) ? 'r' : '-';
+ access[1] = (workingset[i] & 0x4) ? 'w' : '-';
+ access[2] = (workingset[i] & 0x2) ? 'x' : '-';
+ access[3] = (workingset[i] & 0x100) ? 's' : 'p';
+ }
+ }
+ int written = __small_sprintf (destbuf + len,
+ "%08lx-%08lx %s %08lx %04x:%04x %U ",
+ info.lpBaseOfDll,
+ (unsigned long)info.lpBaseOfDll
+ + info.SizeOfImage,
+ access,
+ info.EntryPoint,
+ st.st_dev >> 16,
+ st.st_dev & 0xffff,
+ st.st_ino);
+ while (written < 62)
+ destbuf[len + written++] = ' ';
+ len += written;
+ len += __small_sprintf (destbuf + len, "%s\n", posix_modname);
+ }
+out:
+ CloseHandle (proc);
+ return len;
+}
+
+static _off64_t
+format_process_stat (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ char cmd[CYG_MAX_PATH];
+ int state = 'R';
+ unsigned long fault_count = 0UL,
+ utime = 0UL, stime = 0UL,
+ start_time = 0UL,
+ vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL;
+ int priority = 0;
+ if (p->process_state & PID_EXITED)
+ strcpy (cmd, "<defunct>");
+ else
+ {
+ strcpy (cmd, p->progname);
+ char *last_slash = strrchr (cmd, '\\');
+ if (last_slash != NULL)
+ strcpy (cmd, last_slash + 1);
+ int len = strlen (cmd);
+ if (len > 4)
+ {
+ char *s = cmd + len - 4;
+ if (strcasematch (s, ".exe"))
+ *s = 0;
+ }
+ }
+ /*
+ * Note: under Windows, a _process_ is always running - it's only _threads_
+ * that get suspended. Therefore the default state is R (runnable).
+ */
+ if (p->process_state & PID_EXITED)
+ state = 'Z';
+ else if (p->process_state & PID_STOPPED)
+ state = 'T';
+ else if (wincap.is_winnt ())
+ state = get_process_state (p->dwProcessId);
+ start_time = (GetTickCount () / 1000 - time (NULL) + p->start_time) * HZ;
+ if (wincap.is_winnt ())
+ {
+ NTSTATUS ret;
+ HANDLE hProcess;
+ VM_COUNTERS vmc;
+ KERNEL_USER_TIMES put;
+ PROCESS_BASIC_INFORMATION pbi;
+ QUOTA_LIMITS ql;
+ SYSTEM_TIME_OF_DAY_INFORMATION stodi;
+ SYSTEM_PROCESSOR_TIMES spt;
+ hProcess = OpenProcess (PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
+ FALSE, p->dwProcessId);
+ if (hProcess != NULL)
+ {
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessVmCounters,
+ (PVOID) &vmc,
+ sizeof vmc, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessTimes,
+ (PVOID) &put,
+ sizeof put, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessBasicInformation,
+ (PVOID) &pbi,
+ sizeof pbi, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessQuotaLimits,
+ (PVOID) &ql,
+ sizeof ql, NULL);
+ CloseHandle (hProcess);
+ }
+ else
+ {
+ DWORD error = GetLastError ();
+ __seterrno_from_win_error (error);
+ debug_printf ("OpenProcess: ret %d", error);
+ return 0;
+ }
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
+ (PVOID) &stodi,
+ sizeof stodi, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemProcessorTimes,
+ (PVOID) &spt,
+ sizeof spt, NULL);
+ if (ret != STATUS_SUCCESS)
+ {
+ __seterrno_from_nt_status (ret);
+ debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %E", ret);
+ return 0;
+ }
+ fault_count = vmc.PageFaultCount;
+ utime = put.UserTime.QuadPart * HZ / 10000000ULL;
+ stime = put.KernelTime.QuadPart * HZ / 10000000ULL;
+#if 0
+ if (stodi.CurrentTime.QuadPart > put.CreateTime.QuadPart)
+ start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart -
+ stodi.CurrentTime.QuadPart + put.CreateTime.QuadPart) * HZ / 10000000ULL;
+ else
+ /*
+ * sometimes stodi.CurrentTime is a bit behind
+ * Note: some older versions of procps are broken and can't cope
+ * with process start times > time(NULL).
+ */
+ start_time = (spt.KernelTme.QuadPart + spt.UserTime.QuadPart) * HZ / 10000000ULL;
+#endif
+ priority = pbi.BasePriority;
+ unsigned page_size = getsystempagesize ();
+ vmsize = vmc.PagefileUsage;
+ vmrss = vmc.WorkingSetSize / page_size;
+ vmmaxrss = ql.MaximumWorkingSetSize / page_size;
+ }
+
+ return __small_sprintf (destbuf, "%d (%s) %c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu %lu %lu "
+ "%ld %ld %ld %ld %ld %ld "
+ "%lu %lu "
+ "%ld "
+ "%lu",
+ p->pid, cmd,
+ state,
+ p->ppid, p->pgid, p->sid, makedev (FH_TTYS, p->ctty),
+ -1, 0, fault_count, fault_count, 0, 0, utime, stime,
+ utime, stime, priority, 0, 0, 0,
+ start_time, vmsize,
+ vmrss, vmmaxrss
+ );
+}
+
+static _off64_t
+format_process_status (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ char cmd[CYG_MAX_PATH];
+ int state = 'R';
+ const char *state_str = "unknown";
+ unsigned long vmsize = 0UL, vmrss = 0UL, vmdata = 0UL, vmlib = 0UL, vmtext = 0UL,
+ vmshare = 0UL;
+ if (p->process_state & PID_EXITED)
+ strcpy (cmd, "<defunct>");
+ else
+ {
+ strcpy (cmd, p->progname);
+ char *last_slash = strrchr (cmd, '\\');
+ if (last_slash != NULL)
+ strcpy (cmd, last_slash + 1);
+ int len = strlen (cmd);
+ if (len > 4)
+ {
+ char *s = cmd + len - 4;
+ if (strcasematch (s, ".exe"))
+ *s = 0;
+ }
+ }
+ /*
+ * Note: under Windows, a _process_ is always running - it's only _threads_
+ * that get suspended. Therefore the default state is R (runnable).
+ */
+ if (p->process_state & PID_EXITED)
+ state = 'Z';
+ else if (p->process_state & PID_STOPPED)
+ state = 'T';
+ else if (wincap.is_winnt ())
+ state = get_process_state (p->dwProcessId);
+ switch (state)
+ {
+ case 'O':
+ state_str = "running";
+ break;
+ case 'D':
+ case 'S':
+ state_str = "sleeping";
+ break;
+ case 'R':
+ state_str = "runnable";
+ break;
+ case 'Z':
+ state_str = "zombie";
+ break;
+ case 'T':
+ state_str = "stopped";
+ break;
+ }
+ if (wincap.is_winnt ())
+ {
+ if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata, &vmlib, &vmshare))
+ return 0;
+ unsigned page_size = getsystempagesize ();
+ vmsize *= page_size; vmrss *= page_size; vmdata *= page_size;
+ vmtext *= page_size; vmlib *= page_size;
+ }
+ // The real uid value for *this* process is stored at cygheap->user.real_uid
+ // but we can't get at the real uid value for any other process, so
+ // just fake it as p->uid. Similar for p->gid.
+ return __small_sprintf (destbuf, "Name:\t%s\n"
+ "State:\t%c (%s)\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "Uid:\t%d %d %d %d\n"
+ "Gid:\t%d %d %d %d\n"
+ "VmSize:\t%8d kB\n"
+ "VmLck:\t%8d kB\n"
+ "VmRSS:\t%8d kB\n"
+ "VmData:\t%8d kB\n"
+ "VmStk:\t%8d kB\n"
+ "VmExe:\t%8d kB\n"
+ "VmLib:\t%8d kB\n"
+ "SigPnd:\t%016x\n"
+ "SigBlk:\t%016x\n"
+ "SigIgn:\t%016x\n",
+ cmd,
+ state, state_str,
+ p->pgid,
+ p->pid,
+ p->ppid,
+ p->uid, p->uid, p->uid, p->uid,
+ p->gid, p->gid, p->gid, p->gid,
+ vmsize >> 10, 0, vmrss >> 10, vmdata >> 10, 0, vmtext >> 10, vmlib >> 10,
+ 0, 0, _my_tls.sigmask
+ );
+}
+
+static _off64_t
+format_process_statm (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL,
+ vmlib = 0UL, vmshare = 0UL;
+ if (wincap.is_winnt ())
+ {
+ if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
+ &vmlib, &vmshare))
+ return 0;
+ }
+ return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld",
+ vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0
+ );
+}
+
+static int
+get_process_state (DWORD dwProcessId)
+{
+ /*
+ * This isn't really heavy magic - just go through the processes'
+ * threads one by one and return a value accordingly
+ * Errors are silently ignored.
+ */
+ NTSTATUS ret;
+ SYSTEM_PROCESSES *sp;
+ ULONG n = 0x1000;
+ PULONG p = new ULONG[n];
+ int state =' ';
+ while (STATUS_INFO_LENGTH_MISMATCH ==
+ (ret = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ (PVOID) p,
+ n * sizeof *p, NULL)))
+ delete [] p, p = new ULONG[n *= 2];
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQuerySystemInformation: ret %d, Dos(ret) %d",
+ ret, RtlNtStatusToDosError (ret));
+ goto out;
+ }
+ state = 'Z';
+ sp = (SYSTEM_PROCESSES *) p;
+ for (;;)
+ {
+ if (sp->ProcessId == dwProcessId)
+ {
+ SYSTEM_THREADS *st;
+ if (wincap.has_process_io_counters ())
+ /*
+ * Windows 2000 and XP have an extra member in SYSTEM_PROCESSES
+ * which means the offset of the first SYSTEM_THREADS entry is
+ * different on these operating systems compared to NT 4.
+ */
+ st = &sp->Threads[0];
+ else
+ /*
+ * 136 is the offset of the first SYSTEM_THREADS entry on
+ * Windows NT 4.
+ */
+ st = (SYSTEM_THREADS *) ((char *) sp + 136);
+ state = 'S';
+ for (unsigned i = 0; i < sp->ThreadCount; i++)
+ {
+ if (st->State == StateRunning ||
+ st->State == StateReady)
+ {
+ state = 'R';
+ goto out;
+ }
+ st++;
+ }
+ break;
+ }
+ if (!sp->NextEntryDelta)
+ break;
+ sp = (SYSTEM_PROCESSES *) ((char *) sp + sp->NextEntryDelta);
+ }
+out:
+ delete [] p;
+ return state;
+}
+
+static bool
+get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss,
+ unsigned long *vmtext, unsigned long *vmdata,
+ unsigned long *vmlib, unsigned long *vmshare)
+{
+ bool res = true;
+ NTSTATUS ret;
+ HANDLE hProcess;
+ VM_COUNTERS vmc;
+ MEMORY_WORKING_SET_LIST *mwsl;
+ ULONG n = 0x1000, length;
+ PULONG p = (PULONG) malloc (sizeof (ULONG) * n);
+ unsigned page_size = getsystempagesize ();
+ hProcess = OpenProcess (PROCESS_QUERY_INFORMATION,
+ FALSE, dwProcessId);
+ if (hProcess == NULL)
+ {
+ DWORD error = GetLastError ();
+ __seterrno_from_win_error (error);
+ debug_printf ("OpenProcess: ret %d", error);
+ return false;
+ }
+ while ((ret = NtQueryVirtualMemory (hProcess, 0,
+ MemoryWorkingSetList,
+ (PVOID) p,
+ n * sizeof *p, &length)),
+ (ret == STATUS_SUCCESS || ret == STATUS_INFO_LENGTH_MISMATCH) &&
+ length >= (n * sizeof (*p)))
+ p = (PULONG) realloc (p, n *= (2 * sizeof (ULONG)));
+
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQueryVirtualMemory: ret %d, Dos(ret) %d",
+ ret, RtlNtStatusToDosError (ret));
+ res = false;
+ goto out;
+ }
+ mwsl = (MEMORY_WORKING_SET_LIST *) p;
+ for (unsigned long i = 0; i < mwsl->NumberOfPages; i++)
+ {
+ ++*vmrss;
+ unsigned flags = mwsl->WorkingSetList[i] & 0x0FFF;
+ if (flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE) == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
+ ++*vmlib;
+ else if (flags & WSLE_PAGE_SHAREABLE)
+ ++*vmshare;
+ else if (flags & WSLE_PAGE_EXECUTE)
+ ++*vmtext;
+ else
+ ++*vmdata;
+ }
+ ret = NtQueryInformationProcess (hProcess, ProcessVmCounters, (PVOID) &vmc,
+ sizeof vmc, NULL);
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQueryInformationProcess: ret %d, Dos(ret) %d",
+ ret, RtlNtStatusToDosError (ret));
+ res = false;
+ goto out;
+ }
+ *vmsize = vmc.PagefileUsage / page_size;
+out:
+ free (p);
+ CloseHandle (hProcess);
+ return res;
+}
diff --git a/winsup/cygwin/fhandler_termios.cc b/winsup/cygwin/fhandler_termios.cc
new file mode 100644
index 00000000000..3b51160862d
--- /dev/null
+++ b/winsup/cygwin/fhandler_termios.cc
@@ -0,0 +1,363 @@
+/* fhandler_termios.cc
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 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. */
+
+#include "winsup.h"
+#include <sys/termios.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "tty.h"
+#include "sys/cygwin.h"
+#include "cygtls.h"
+
+/* Common functions shared by tty/console */
+
+void
+fhandler_termios::tcinit (tty_min *this_tc, bool force)
+{
+ /* Initial termios values */
+
+ tc = this_tc;
+
+ if (force || !tc->initialized ())
+ {
+ tc->ti.c_iflag = BRKINT | ICRNL | IXON;
+ tc->ti.c_oflag = OPOST | ONLCR;
+ tc->ti.c_cflag = B38400 | CS8 | CREAD;
+ tc->ti.c_lflag = ISIG | ICANON | ECHO | IEXTEN;
+
+ tc->ti.c_cc[VDISCARD] = CFLUSH;
+ tc->ti.c_cc[VEOL] = CEOL;
+ tc->ti.c_cc[VEOL2] = CEOL2;
+ tc->ti.c_cc[VEOF] = CEOF;
+ tc->ti.c_cc[VERASE] = CERASE;
+ tc->ti.c_cc[VINTR] = CINTR;
+ tc->ti.c_cc[VKILL] = CKILL;
+ tc->ti.c_cc[VLNEXT] = CLNEXT;
+ tc->ti.c_cc[VMIN] = 1;
+ tc->ti.c_cc[VQUIT] = CQUIT;
+ tc->ti.c_cc[VREPRINT] = CRPRNT;
+ tc->ti.c_cc[VSTART] = CSTART;
+ tc->ti.c_cc[VSTOP] = CSTOP;
+ tc->ti.c_cc[VSUSP] = CSUSP;
+ tc->ti.c_cc[VSWTC] = CSWTCH;
+ tc->ti.c_cc[VTIME] = 0;
+ tc->ti.c_cc[VWERASE] = CWERASE;
+
+ tc->ti.c_ispeed = tc->ti.c_ospeed = B38400;
+ tc->pgid = myself->pgid;
+ tc->initialized (true);
+ }
+}
+
+int
+fhandler_termios::tcsetpgrp (const pid_t pgid)
+{
+ termios_printf ("tty %d pgid %d, sid %d, tsid %d", tc->ntty, pgid,
+ myself->sid, tc->getsid ());
+ if (myself->sid != tc->getsid ())
+ {
+ set_errno (EPERM);
+ return -1;
+ }
+ int res;
+ while (1)
+ {
+ res = bg_check (-SIGTTOU);
+
+ switch (res)
+ {
+ case bg_ok:
+ tc->setpgid (pgid);
+ init_console_handler (tc->gethwnd ());
+ res = 0;
+ break;
+ case bg_signalled:
+ if (_my_tls.call_signal_handler ())
+ continue;
+ set_errno (EINTR);
+ /* fall through intentionally */
+ default:
+ res = -1;
+ break;
+ }
+ break;
+ }
+ return res;
+}
+
+int
+fhandler_termios::tcgetpgrp ()
+{
+ return tc->pgid;
+}
+
+void
+tty_min::kill_pgrp (int sig)
+{
+ int killself = 0;
+ winpids pids ((DWORD) PID_MAP_RW);
+ siginfo_t si = {0};
+ si.si_signo = sig;
+ si.si_code = SI_KERNEL;
+ for (unsigned i = 0; i < pids.npids; i++)
+ {
+ _pinfo *p = pids[i];
+ if (!p->exists () || p->ctty != ntty || p->pgid != pgid)
+ continue;
+ if (p == myself)
+ killself++;
+ else
+ sig_send (p, si);
+ }
+ if (killself)
+ sig_send (myself, si);
+}
+
+bg_check_types
+fhandler_termios::bg_check (int sig)
+{
+ if (!myself->pgid || tc->getpgid () == myself->pgid ||
+ myself->ctty != tc->ntty ||
+ ((sig == SIGTTOU) && !(tc->ti.c_lflag & TOSTOP)))
+ return bg_ok;
+
+ if (sig < 0)
+ sig = -sig;
+
+ termios_printf ("bg I/O pgid %d, tpgid %d, %s, ntty tty%d", myself->pgid, tc->getpgid (),
+ myctty (), tc->ntty);
+
+ if (tc->getsid () == 0)
+ {
+ /* The pty has been closed by the master. Return an EOF
+ indication. FIXME: There is nothing to stop somebody
+ from reallocating this pty. I think this is the case
+ which is handled by unlockpt on a Unix system. */
+ termios_printf ("closed by master");
+ return bg_eof;
+ }
+
+ /* If the process group is no more or if process is ignoring or blocks 'sig',
+ return with error */
+ int pgid_gone = !pid_exists (myself->pgid);
+ int sigs_ignored =
+ ((void *) global_sigs[sig].sa_handler == (void *) SIG_IGN) ||
+ (_main_tls->sigmask & SIGTOMASK (sig));
+
+ if (pgid_gone)
+ goto setEIO;
+ else if (!sigs_ignored)
+ /* nothing */;
+ else if (sig == SIGTTOU)
+ return bg_ok; /* Just allow the output */
+ else
+ goto setEIO; /* This is an output error */
+
+ /* Don't raise a SIGTT* signal if we have already been interrupted
+ by another signal. */
+ if (WaitForSingleObject (signal_arrived, 0) != WAIT_OBJECT_0)
+ {
+ siginfo_t si = {0};
+ si.si_signo = sig;
+ si.si_code = SI_KERNEL;
+ kill_pgrp (myself->pgid, si);
+ }
+ return bg_signalled;
+
+setEIO:
+ set_errno (EIO);
+ return bg_error;
+}
+
+#define set_input_done(x) input_done = input_done || (x)
+
+inline void
+fhandler_termios::echo_erase (int force)
+{
+ if (force || tc->ti.c_lflag & ECHO)
+ doecho ("\b \b", 3);
+}
+
+line_edit_status
+fhandler_termios::line_edit (const char *rptr, int nread, termios& ti)
+{
+ line_edit_status ret = line_edit_ok;
+ char c;
+ int input_done = 0;
+ bool sawsig = false;
+ int iscanon = ti.c_lflag & ICANON;
+
+ while (nread-- > 0)
+ {
+ c = *rptr++;
+
+ termios_printf ("char %c", c);
+
+ /* Check for special chars */
+
+ if (c == '\r')
+ {
+ if (ti.c_iflag & IGNCR)
+ continue;
+ if (ti.c_iflag & ICRNL)
+ {
+ c = '\n';
+ set_input_done (iscanon);
+ }
+ }
+ else if (c == '\n')
+ {
+ if (ti.c_iflag & INLCR)
+ c = '\r';
+ else
+ set_input_done (iscanon);
+ }
+
+ if (ti.c_iflag & ISTRIP)
+ c &= 0x7f;
+ if (ti.c_lflag & ISIG)
+ {
+ int sig;
+ if (CCEQ (ti.c_cc[VINTR], c))
+ sig = SIGINT;
+ else if (CCEQ (ti.c_cc[VQUIT], c))
+ sig = SIGQUIT;
+ else if (CCEQ (ti.c_cc[VSUSP], c))
+ sig = SIGTSTP;
+ else
+ goto not_a_sig;
+
+ termios_printf ("got interrupt %d, sending signal %d", c, sig);
+ eat_readahead (-1);
+ tc->kill_pgrp (sig);
+ ti.c_lflag &= ~FLUSHO;
+ sawsig = true;
+ goto restart_output;
+ }
+ not_a_sig:
+ if (ti.c_iflag & IXON)
+ {
+ if (CCEQ (ti.c_cc[VSTOP], c))
+ {
+ if (!tc->output_stopped)
+ {
+ tc->output_stopped = 1;
+ acquire_output_mutex (INFINITE);
+ }
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VSTART], c))
+ {
+ restart_output:
+ tc->output_stopped = 0;
+ release_output_mutex ();
+ continue;
+ }
+ else if ((ti.c_iflag & IXANY) && tc->output_stopped)
+ goto restart_output;
+ }
+ if (iscanon && ti.c_lflag & IEXTEN && CCEQ (ti.c_cc[VDISCARD], c))
+ {
+ ti.c_lflag ^= FLUSHO;
+ continue;
+ }
+ if (!iscanon)
+ /* nothing */;
+ else if (CCEQ (ti.c_cc[VERASE], c))
+ {
+ if (eat_readahead (1))
+ echo_erase ();
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VWERASE], c))
+ {
+ int ch;
+ do
+ if (!eat_readahead (1))
+ break;
+ else
+ echo_erase ();
+ while ((ch = peek_readahead (1)) >= 0 && !isspace (ch));
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VKILL], c))
+ {
+ int nchars = eat_readahead (-1);
+ if (ti.c_lflag & ECHO)
+ while (nchars--)
+ echo_erase (1);
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VREPRINT], c))
+ {
+ if (ti.c_lflag & ECHO)
+ {
+ doecho ("\n\r", 2);
+ doecho (rabuf, ralen);
+ }
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VEOF], c))
+ {
+ termios_printf ("EOF");
+ accept_input ();
+ ret = line_edit_input_done;
+ continue;
+ }
+ else if (CCEQ (ti.c_cc[VEOL], c) ||
+ CCEQ (ti.c_cc[VEOL2], c) ||
+ c == '\n')
+ {
+ set_input_done (1);
+ termios_printf ("EOL");
+ }
+
+ if (ti.c_iflag & IUCLC && isupper (c))
+ c = cyg_tolower (c);
+
+ put_readahead (c);
+ if (ti.c_lflag & ECHO)
+ doecho (&c, 1);
+ if (!iscanon || input_done)
+ {
+ int status = accept_input ();
+ if (status != 1)
+ {
+ ret = status ? line_edit_error : line_edit_pipe_full;
+ eat_readahead (1);
+ break;
+ }
+ ret = line_edit_input_done;
+ input_done = 0;
+ }
+ }
+
+ if (!iscanon && ralen > 0)
+ ret = line_edit_input_done;
+
+ if (sawsig)
+ ret = line_edit_signalled;
+
+ return ret;
+}
+
+_off64_t
+fhandler_termios::lseek (_off64_t, int)
+{
+ set_errno (ESPIPE);
+ return -1;
+}
diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h
new file mode 100644
index 00000000000..e5abf12517d
--- /dev/null
+++ b/winsup/cygwin/pinfo.h
@@ -0,0 +1,246 @@
+/* pinfo.h: process table info
+
+ Copyright 2000, 2001, 2002, 2003, 2004, 2005 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. */
+
+#ifndef _PINFO_H
+#define _PINFO_H
+#include <sys/resource.h>
+#include "thread.h"
+
+struct commune_result
+{
+ char *s;
+ int n;
+ HANDLE handles[2];
+};
+
+enum picom
+{
+ PICOM_EXTRASTR = 0x80000000,
+ PICOM_CMDLINE = 1,
+ PICOM_FIFO = PICOM_EXTRASTR | 2,
+ PICOM_CWD = 3,
+ PICOM_ROOT = 4,
+ PICOM_FDS = 5,
+ PICOM_FD = 6,
+ PICOM_PIPE_FHANDLER = 7
+};
+
+#define EXITCODE_SET 0x8000000
+#define EXITCODE_NOSET 0x4000000
+#define EXITCODE_RETRY 0x2000000
+#define EXITCODE_OK 0x1000000
+
+class fhandler_pipe;
+
+class _pinfo
+{
+public:
+ /* Cygwin pid */
+ pid_t pid;
+
+ /* Various flags indicating the state of the process. See PID_
+ constants below. */
+ DWORD process_state;
+
+ DWORD exitcode; /* set when process exits */
+
+#define PINFO_REDIR_SIZE ((char *) &myself.procinfo->exitcode - (char *) myself.procinfo)
+
+ /* > 0 if started by a cygwin process */
+ DWORD cygstarted;
+
+ /* Parent process id. */
+ pid_t ppid;
+
+ /* dwProcessId contains the processid used for sending signals. It
+ will be reset in a child process when it is capable of receiving
+ signals. */
+ DWORD dwProcessId;
+
+ /* Used to spawn a child for fork(), among other things. */
+ char progname[CYG_MAX_PATH];
+
+ /* User information.
+ The information is derived from the GetUserName system call,
+ with the name looked up in /etc/passwd and assigned a default value
+ if not found. This data resides in the shared data area (allowing
+ tasks to store whatever they want here) so it's for informational
+ purposes only. */
+ __uid32_t uid; /* User ID */
+ __gid32_t gid; /* Group ID */
+ pid_t pgid; /* Process group ID */
+ pid_t sid; /* Session ID */
+ int ctty; /* Control tty */
+ bool has_pgid_children;/* True if we've forked or spawned children with our GID. */
+
+ /* Resources used by process. */
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+ int nice;
+
+ /* Non-zero if process was stopped by a signal. */
+ char stopsig;
+
+ inline void set_has_pgid_children ()
+ {
+ if (pgid == pid)
+ has_pgid_children = 1;
+ }
+
+ inline void set_has_pgid_children (bool val) {has_pgid_children = val;}
+
+ commune_result commune_request (__uint32_t, ...);
+ bool alive ();
+ fhandler_pipe *pipe_fhandler (HANDLE, size_t &);
+ char *fd (int fd, size_t &);
+ char *fds (size_t &);
+ char *root (size_t &);
+ char *cwd (size_t &);
+ char *cmdline (size_t &);
+ void set_ctty (class tty_min *, int, class fhandler_tty_slave *);
+ HANDLE dup_proc_pipe (HANDLE) __attribute__ ((regparm(2)));
+ void sync_proc_pipe ();
+ bool alert_parent (char);
+ int __stdcall kill (siginfo_t&) __attribute__ ((regparm (2)));
+ bool __stdcall exists () __attribute__ ((regparm (1)));
+ const char *_ctty (char *);
+
+ friend void __stdcall set_myself (HANDLE);
+
+ /* signals */
+ HANDLE sendsig;
+ HANDLE exec_sendsig;
+ DWORD exec_dwProcessId;
+public:
+ HANDLE wr_proc_pipe;
+ DWORD wr_proc_pipe_owner;
+ friend class pinfo;
+};
+
+DWORD WINAPI commune_process (void *);
+
+enum parent_alerter
+{
+ __ALERT_REPARENT = 111, // arbitrary non-signal value
+ __ALERT_ALIVE = 112
+};
+
+class pinfo
+{
+ HANDLE h;
+ _pinfo *procinfo;
+ bool destroy;
+public:
+ HANDLE rd_proc_pipe;
+ HANDLE hProcess;
+ bool waiter_ready;
+ class cygthread *wait_thread;
+ void init (pid_t, DWORD, HANDLE) __attribute__ ((regparm(3)));
+ pinfo () {}
+ pinfo (_pinfo *x): procinfo (x), hProcess (NULL) {}
+ pinfo (pid_t n) : rd_proc_pipe (NULL), hProcess (NULL) {init (n, 0, NULL);}
+ pinfo (pid_t n, DWORD flag) : rd_proc_pipe (NULL), hProcess (NULL), waiter_ready (0), wait_thread (NULL) {init (n, flag, NULL);}
+ void release ();
+ int wait () __attribute__ ((regparm (1)));
+ ~pinfo ()
+ {
+ if (destroy && procinfo)
+ release ();
+ }
+ void exit (DWORD n) __attribute__ ((noreturn, regparm(2)));
+ void maybe_set_exit_code_from_windows () __attribute__ ((regparm(1)));
+ _pinfo *operator -> () const {return procinfo;}
+ int operator == (pinfo *x) const {return x->procinfo == procinfo;}
+ int operator == (pinfo &x) const {return x.procinfo == procinfo;}
+ int operator == (_pinfo *x) const {return x == procinfo;}
+ int operator == (void *x) const {return procinfo == x;}
+ int operator == (int x) const {return (int) procinfo == (int) x;}
+ int operator == (char *x) const {return (char *) procinfo == x;}
+ _pinfo *operator * () const {return procinfo;}
+ operator _pinfo * () const {return procinfo;}
+ // operator bool () const {return (int) h;}
+ void preserve () { destroy = false; }
+#ifndef _SIGPROC_H
+ int remember () {system_printf ("remember is not here"); return 0;}
+#else
+ int remember (bool detach)
+ {
+ int res = proc_subproc (detach ? PROC_DETACHED_CHILD : PROC_ADDCHILD,
+ (DWORD) this);
+ destroy = res ? false : true;
+ return res;
+ }
+#endif
+ HANDLE shared_handle () {return h;}
+ void set_acl ();
+ void zap_cwd ();
+ friend class _pinfo;
+ friend class winpids;
+};
+
+#define ISSTATE(p, f) (!!((p)->process_state & f))
+#define NOTSTATE(p, f) (!((p)->process_state & f))
+
+class winpids
+{
+ bool make_copy;
+ DWORD npidlist;
+ DWORD *pidlist;
+ pinfo *pinfolist;
+ DWORD pinfo_access; // access type for pinfo open
+ DWORD (winpids::* enum_processes) (bool winpid);
+ DWORD enum_init (bool winpid);
+ DWORD enumNT (bool winpid);
+ DWORD enum9x (bool winpid);
+ void add (DWORD& nelem, bool, DWORD pid);
+public:
+ DWORD npids;
+ inline void reset () { release (); npids = 0;}
+ void set (bool winpid);
+ winpids (): make_copy (true), enum_processes (&winpids::enum_init) {}
+ winpids (int): make_copy (false), npidlist (0), pidlist (NULL), pinfolist (NULL),
+ pinfo_access (0), enum_processes (&winpids::enum_init), npids (0) {}
+ winpids (DWORD acc): make_copy (false), npidlist (0), pidlist (NULL), pinfolist (NULL),
+ pinfo_access (acc), enum_processes (&winpids::enum_init),
+ npids (0)
+ {
+ set (0);
+ }
+ inline DWORD& winpid (int i) const {return pidlist[i];}
+ inline _pinfo *operator [] (int i) const {return (_pinfo *) pinfolist[i];}
+ ~winpids ();
+ void release ();
+};
+
+extern __inline pid_t
+cygwin_pid (pid_t pid)
+{
+ return (pid_t) (wincap.has_negative_pids ()) ? -(int) pid : pid;
+}
+
+void __stdcall pinfo_init (char **, int);
+void __stdcall set_myself (HANDLE h);
+extern pinfo myself;
+
+#define _P_VFORK 0
+#define _P_SYSTEM 512
+
+#define __ctty() _ctty ((char *) alloca (sizeof ("ctty /dev/tty") + 20))
+#define myctty() myself->__ctty ()
+
+/* For mmaps across fork(). */
+int __stdcall fixup_mmaps_after_fork (HANDLE parent);
+/* for shm areas across fork (). */
+int __stdcall fixup_shms_after_fork ();
+
+void __stdcall fill_rusage (struct rusage *, HANDLE);
+void __stdcall add_rusage (struct rusage *, struct rusage *);
+#endif /*_PINFO_H*/
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
new file mode 100644
index 00000000000..4aafd5f701c
--- /dev/null
+++ b/winsup/cygwin/select.cc
@@ -0,0 +1,1730 @@
+/* select.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006 Red Hat, Inc.
+
+ Written by Christopher Faylor of Cygnus Solutions
+ cgf@cygnus.com
+
+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 <sys/socket.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <wingdi.h>
+#include <winuser.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <limits.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "cygerrno.h"
+#include "select.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "sigproc.h"
+#include "tty.h"
+#include "ntdll.h"
+#include "cygtls.h"
+#include <asm/byteorder.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 object (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 () && me->read_selected)
+ UNIX_FD_SET (me->fd, readfds);
+ sock->connect_state (connected);
+ }
+ ready++;
+ }
+ if ((me->except_selected || me->except_on_write) && me->except_ready)
+ {
+ if (me->except_on_write) /* Only on sockets */
+ {
+ UNIX_FD_SET (me->fd, writefds);
+ if ((sock = me->fh->is_socket ()))
+ sock->connect_state (connect_failed);
+ }
+ if (me->except_selected)
+ 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);
+
+ /* pipes require a guard mutex to guard against the situation where multiple
+ readers are attempting to read from the same pipe. In this scenario, it
+ is possible for PeekNamedPipe to report available data to two readers but
+ only one will actually get the data. This will result in the other reader
+ entering fhandler_base::raw_read and blocking indefinitely in an interruptible
+ state. This causes things like "make -j2" to hang. So, for the non-select case
+ we use the pipe mutex, if it is available. */
+ HANDLE guard_mutex = from_select ? NULL : fh->get_guard ();
+
+ /* 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;
+ }
+ else if (!n || !guard_mutex)
+ /* no guard mutex or nothing to read from the pipe. */;
+ else if (WaitForSingleObject (guard_mutex, 0) != WAIT_OBJECT_0)
+ {
+ select_printf ("%s, couldn't get mutex %p, %E", fh->get_name (),
+ guard_mutex);
+ n = 0;
+ }
+ else
+ {
+ /* Now that we have the mutex, make sure that no one else has snuck
+ in and grabbed the data that we originally saw. */
+ if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
+ n = -1;
+ }
+ if (n <= 0)
+ ReleaseMutex (guard_mutex); /* Oops. We lost the race. */
+ }
+
+ if (n < 0)
+ {
+ fh->set_eof (); /* Flag that other end of pipe is gone */
+ 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 0
+/* FIXME: This code is not quite correct. There's no better solution
+ so far but to always treat the write side of the pipe as writable. */
+
+ /* We don't worry about the guard mutex, because that only applies
+ when from_select is false, and peek_pipe is never called that
+ way for writes. */
+
+ 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 on Win9x, because
+ NtQueryInformationFile is not available, or 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;
+ }
+ /* Ensure that enough space is available for atomic writes,
+ as required by POSIX. Subsequent writes with size > PIPE_BUF
+ can still block, but most (all?) UNIX variants seem to work
+ this way (e.g., BSD, Linux, Solaris). */
+ else if (fpli.WriteQuotaAvailable >= PIPE_BUF)
+ {
+ 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 if (!get_guard ())
+ res = 1;
+ else
+ {
+ const HANDLE w4[2] = {get_guard (), signal_arrived};
+ switch (WaitForMultipleObjects (2, w4, 0, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ res = 1;
+ break;
+ case WAIT_OBJECT_0 + 1:
+ set_sig_errno (EINTR);
+ res = 0;
+ break;
+ default:
+ __seterrno ();
+ res = 0;
+ }
+ }
+ 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;
+ }
+ }
+
+ if (get_guard () && !avail && me.read_ready)
+ ReleaseMutex (get_guard ());
+
+ 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;
+}
+
+struct socketinf
+ {
+ cygthread *thread;
+ winsock_fd_set readfds, writefds, exceptfds;
+ SOCKET exitsock;
+ select_record *start;
+ };
+
+static int
+peek_socket (select_record *me, bool)
+{
+ winsock_fd_set ws_readfds, ws_writefds, ws_exceptfds;
+ struct timeval tv = {0, 0};
+ WINSOCK_FD_ZERO (&ws_readfds);
+ WINSOCK_FD_ZERO (&ws_writefds);
+ WINSOCK_FD_ZERO (&ws_exceptfds);
+
+ HANDLE h;
+ set_handle_or_return_if_not_open (h, me);
+ select_printf ("considering handle %p", h);
+
+ if (me->read_selected && !me->read_ready)
+ {
+ select_printf ("adding read fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_readfds);
+ }
+ if (me->write_selected && !me->write_ready)
+ {
+ select_printf ("adding write fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_writefds);
+ }
+ if ((me->except_selected || me->except_on_write) && !me->except_ready)
+ {
+ select_printf ("adding except fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_exceptfds);
+ }
+ int r;
+ if ((me->read_selected && !me->read_ready)
+ || (me->write_selected && !me->write_ready)
+ || ((me->except_selected || me->except_on_write) && !me->except_ready))
+ {
+ r = WINSOCK_SELECT (0, &ws_readfds, &ws_writefds, &ws_exceptfds, &tv);
+ select_printf ("WINSOCK_SELECT returned %d", r);
+ if (r == -1)
+ {
+ select_printf ("error %d", WSAGetLastError ());
+ set_winsock_errno ();
+ return 0;
+ }
+ if (WINSOCK_FD_ISSET (h, &ws_readfds))
+ me->read_ready = true;
+ if (WINSOCK_FD_ISSET (h, &ws_writefds))
+ me->write_ready = true;
+ if (WINSOCK_FD_ISSET (h, &ws_exceptfds))
+ me->except_ready = true;
+ }
+ return me->read_ready || me->write_ready || me->except_ready;
+}
+
+static int start_thread_socket (select_record *, select_stuff *);
+
+static DWORD WINAPI
+thread_socket (void *arg)
+{
+ socketinf *si = (socketinf *) arg;
+
+ select_printf ("stuff_start %p", &si->start);
+ int r = WINSOCK_SELECT (0, &si->readfds, &si->writefds, &si->exceptfds, NULL);
+ select_printf ("Win32 select returned %d", r);
+ if (r == -1)
+ select_printf ("error %d", WSAGetLastError ());
+ select_record *s = si->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_socket)
+ {
+ HANDLE h = s->fh->get_handle ();
+ select_printf ("s %p, testing fd %d (%s)", s, s->fd, s->fh->get_name ());
+ if (WINSOCK_FD_ISSET (h, &si->readfds))
+ {
+ select_printf ("read_ready");
+ s->read_ready = true;
+ }
+ if (WINSOCK_FD_ISSET (h, &si->writefds))
+ {
+ select_printf ("write_ready");
+ s->write_ready = true;
+ }
+ if (WINSOCK_FD_ISSET (h, &si->exceptfds))
+ {
+ select_printf ("except_ready");
+ s->except_ready = true;
+ }
+ }
+
+ if (WINSOCK_FD_ISSET (si->exitsock, &si->readfds))
+ select_printf ("saw exitsock read");
+ 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;
+ WINSOCK_FD_ZERO (&si->readfds);
+ WINSOCK_FD_ZERO (&si->writefds);
+ WINSOCK_FD_ZERO (&si->exceptfds);
+ select_record *s = &stuff->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_socket)
+ {
+ HANDLE h = s->fh->get_handle ();
+ select_printf ("Handle %p", h);
+ if (s->read_selected && !s->read_ready)
+ {
+ WINSOCK_FD_SET (h, &si->readfds);
+ select_printf ("Added to readfds");
+ }
+ if (s->write_selected && !s->write_ready)
+ {
+ WINSOCK_FD_SET (h, &si->writefds);
+ select_printf ("Added to writefds");
+ }
+ if ((s->except_selected || s->except_on_write) && !s->except_ready)
+ {
+ WINSOCK_FD_SET (h, &si->exceptfds);
+ select_printf ("Added to exceptfds");
+ }
+ }
+
+ if (_my_tls.locals.exitsock != INVALID_SOCKET)
+ si->exitsock = _my_tls.locals.exitsock;
+ else
+ {
+ si->exitsock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (si->exitsock == INVALID_SOCKET)
+ {
+ set_winsock_errno ();
+ select_printf ("cannot create socket, %E");
+ return 0;
+ }
+ int sin_len = sizeof (_my_tls.locals.exitsock_sin);
+ memset (&_my_tls.locals.exitsock_sin, 0, sin_len);
+ _my_tls.locals.exitsock_sin.sin_family = AF_INET;
+ _my_tls.locals.exitsock_sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (bind (si->exitsock, (struct sockaddr *) &_my_tls.locals.exitsock_sin, sin_len) < 0)
+ {
+ select_printf ("cannot bind socket %p, %E", si->exitsock);
+ goto err;
+ }
+
+ if (getsockname (si->exitsock, (struct sockaddr *) &_my_tls.locals.exitsock_sin, &sin_len) < 0)
+ {
+ select_printf ("getsockname error");
+ goto err;
+ }
+ if (wincap.has_set_handle_information ())
+ SetHandleInformation ((HANDLE) si->exitsock, HANDLE_FLAG_INHERIT, 0);
+ /* else
+ too bad? */
+ select_printf ("opened new socket %p", si->exitsock);
+ _my_tls.locals.exitsock = si->exitsock;
+ }
+
+ select_printf ("exitsock %p", si->exitsock);
+ WINSOCK_FD_SET ((HANDLE) si->exitsock, &si->readfds);
+ 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;
+
+err:
+ set_winsock_errno ();
+ closesocket (si->exitsock);
+ return 0;
+}
+
+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)
+ {
+ char buf[] = "";
+ int res = sendto (_my_tls.locals.exitsock, buf, 1, 0,
+ (sockaddr *) &_my_tls.locals.exitsock_sin,
+ sizeof (_my_tls.locals.exitsock_sin));
+ select_printf ("sent a byte to exitsock %p, res %d", _my_tls.locals.exitsock, res);
+ /* Wait for thread to go away */
+ si->thread->detach ();
+ /* empty the socket */
+ select_printf ("reading a byte from exitsock %p", si->exitsock);
+ res = recv (si->exitsock, buf, 1, 0);
+ select_printf ("recv returned %d", res);
+ stuff->device_specific_socket = NULL;
+ 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;
+}
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
index 3765cff2610..707ab0de00d 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -147,7 +147,7 @@ usleep (useconds_t useconds)
extern "C" int
sigprocmask (int how, const sigset_t *set, sigset_t *oldset)
{
- return handle_sigprocmask (how, set, oldset, myself->getsigmask ());
+ return handle_sigprocmask (how, set, oldset, _my_tls.sigmask);
}
int __stdcall
@@ -342,7 +342,7 @@ abort (void)
sigset_t sig_mask;
sigfillset (&sig_mask);
sigdelset (&sig_mask, SIGABRT);
- set_signal_mask (sig_mask, myself->getsigmask ());
+ set_signal_mask (sig_mask, _my_tls.sigmask);
raise (SIGABRT);
_my_tls.call_signal_handler (); /* Call any signal handler */
@@ -485,7 +485,7 @@ sigpause (int signal_mask)
extern "C" int
pause (void)
{
- return handle_sigsuspend (myself->getsigmask ());
+ return handle_sigsuspend (_my_tls.sigmask);
}
extern "C" int
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
index 4bc7594215f..5eaac043c6b 100644
--- a/winsup/cygwin/sigproc.cc
+++ b/winsup/cygwin/sigproc.cc
@@ -663,7 +663,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
else if (si.si_signo == __SIGPENDING)
pack.mask = &pending;
else if (si.si_signo == __SIGFLUSH || si.si_signo > 0)
- pack.mask = &myself->getsigmask ();
+ pack.mask = &_my_tls.sigmask;
else
pack.mask = NULL;
@@ -1118,7 +1118,7 @@ pending_signals::add (sigpacket& pack)
return;
se = sigs + pack.si.si_signo;
*se = pack;
- se->mask = &myself->getsigmask ();
+ se->mask = &pack.tls->sigmask;
se->next = NULL;
if (end)
end->next = se;
@@ -1199,7 +1199,7 @@ wait_sig (VOID *)
sigset_t dummy_mask;
if (!pack.mask)
{
- dummy_mask = myself->getsigmask ();
+ dummy_mask = _main_tls->sigmask;
pack.mask = &dummy_mask;
}
@@ -1218,7 +1218,7 @@ wait_sig (VOID *)
unsigned bit;
sigq.reset ();
while ((q = sigq.next ()))
- if (myself->getsigmask () & (bit = SIGTOMASK (q->si.si_signo)))
+ if (pack.tls->sigmask & (bit = SIGTOMASK (q->si.si_signo)))
*pack.mask |= bit;
break;
case __SIGHOLD:
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
index f5226a55275..f9501f86a53 100644
--- a/winsup/cygwin/thread.cc
+++ b/winsup/cygwin/thread.cc
@@ -374,6 +374,7 @@ pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0),
{
if (this != pthread_null::get_null_pthread ())
threads.insert (this);
+ parent_tls = &_my_tls;
}
pthread::~pthread ()
@@ -1909,6 +1910,7 @@ pthread::thread_init_wrapper (void *arg)
// if thread is detached force cleanup on exit
if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL)
thread->joiner = thread;
+ _my_tls.sigmask = thread->parent_tls->sigmask;
thread->mutex.unlock ();
thread_printf ("started thread %p %p %p %p %p %p", arg, &_my_tls.local_clib,
diff --git a/winsup/cygwin/thread.h b/winsup/cygwin/thread.h
index ed2ec540e71..92164fb8354 100644
--- a/winsup/cygwin/thread.h
+++ b/winsup/cygwin/thread.h
@@ -430,6 +430,7 @@ private:
DWORD thread_id;
__pthread_cleanup_handler *cleanup_stack;
pthread_mutex mutex;
+ _cygtls *parent_tls;
void suspend_except_self ();
void resume ();