diff options
author | Corinna Vinschen <vinschen@redhat.com> | 2008-02-18 14:08:37 +0000 |
---|---|---|
committer | Corinna Vinschen <vinschen@redhat.com> | 2008-02-18 14:08:37 +0000 |
commit | 388a43e8fe989d72eea97a9336a6a5dfbde263d4 (patch) | |
tree | 482f1f453b310c8d276e810a9105d17f3d44bd56 | |
parent | 69a10b127727c274be899b08ddc994f794af27f7 (diff) | |
download | gdb-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/ChangeLog | 26 | ||||
-rw-r--r-- | winsup/cygwin/exceptions.cc | 25 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_process.cc | 979 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_termios.cc | 363 | ||||
-rw-r--r-- | winsup/cygwin/pinfo.h | 246 | ||||
-rw-r--r-- | winsup/cygwin/select.cc | 1730 | ||||
-rw-r--r-- | winsup/cygwin/signal.cc | 6 | ||||
-rw-r--r-- | winsup/cygwin/sigproc.cc | 8 | ||||
-rw-r--r-- | winsup/cygwin/thread.cc | 2 | ||||
-rw-r--r-- | winsup/cygwin/thread.h | 1 |
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 (); |