summaryrefslogtreecommitdiff
path: root/winsup
diff options
context:
space:
mode:
Diffstat (limited to 'winsup')
-rw-r--r--winsup/cygwin/ChangeLog242
-rw-r--r--winsup/cygwin/cygwin.sc1
-rw-r--r--winsup/cygwin/dlfcn.cc167
-rwxr-xr-xwinsup/cygwin/dllfixdbg75
-rw-r--r--winsup/cygwin/dtable.cc6
-rw-r--r--winsup/cygwin/exceptions.cc1394
-rw-r--r--winsup/cygwin/fhandler.cc44
-rw-r--r--winsup/cygwin/fhandler.h11
-rw-r--r--winsup/cygwin/fhandler_disk_file.cc17
-rw-r--r--winsup/cygwin/fhandler_floppy.cc536
-rw-r--r--winsup/cygwin/fhandler_mailslot.cc160
-rw-r--r--winsup/cygwin/fhandler_raw.cc215
-rw-r--r--winsup/cygwin/fhandler_serial.cc72
-rw-r--r--winsup/cygwin/fhandler_socket.cc1694
-rwxr-xr-xwinsup/cygwin/gendef455
-rw-r--r--winsup/cygwin/include/cygwin/time.h45
-rw-r--r--winsup/cygwin/include/search.h74
-rw-r--r--winsup/cygwin/include/stdint.h182
-rw-r--r--winsup/cygwin/include/sys/stdio.h44
-rw-r--r--winsup/cygwin/include/sys/termios.h354
-rw-r--r--winsup/cygwin/init.cc163
-rw-r--r--winsup/cygwin/localtime.cc2170
-rw-r--r--winsup/cygwin/mmap.cc28
-rw-r--r--winsup/cygwin/security.cc18
-rw-r--r--winsup/cygwin/shared.cc9
-rw-r--r--winsup/cygwin/shared_info.h5
-rw-r--r--winsup/cygwin/shm.cc411
-rw-r--r--winsup/cygwin/signal.cc577
-rw-r--r--winsup/cygwin/sigproc.cc2
-rw-r--r--winsup/cygwin/spawn.cc4
-rw-r--r--winsup/cygwin/sync.h73
-rw-r--r--winsup/cygwin/syscalls.cc5
-rw-r--r--winsup/cygwin/termios.cc316
-rw-r--r--winsup/cygwin/thread.cc3309
-rw-r--r--winsup/cygwin/times.cc750
-rw-r--r--winsup/cygwin/winsup.h3
36 files changed, 13563 insertions, 68 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index ee87bbff879..68ec045f1cd 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,116 @@
+2007-11-08 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * dllfixdbg: Eliminate extra objcopy step.
+
+2007-11-07 Pedro Alves <pedro_alves@portugalmail.pt>
+
+ * dllfixdbg: Pass --only-keep-debug to objcopy, instead of
+ selecting the sections manually.
+
+2007-11-06 Corinna Vinschen <corinna@vinschen.de>
+
+ * thread.cc (pthread_key_create): Drop check for incoming valid object.
+
+2007-11-06 Corinna Vinschen <corinna@vinschen.de>
+
+ * shm.cc: Include sync.h
+ (struct shm_shmid_list): Add ref_count member.
+ (struct shm_attached_list): Remove hdl and size members. Add a parent
+ member pointing to referenced shm_shmid_list entry.
+ (shm_guard): New muto.
+ (SLIST_LOCK): Define.
+ (SLIST_UNLOCK): Define.
+ (fixup_shms_after_fork): Use hdl and size members of parent
+ shm_shmid_list entry.
+ (shmat): Access sequential bookkeeping lists in a thread safe way.
+ Accommodate change in list element layout. Align comments.
+ (shmctl): Ditto.
+ (shmdt): Ditto.
+ (shmget): Ditto.
+
+2007-11-05 Corinna Vinschen <corinna@vinschen.de>
+
+ * shm.cc (shmctl): On IPC_RMID don't unmap views and don't close handle
+ if the map is still referenced to emulate Linux and BSD behaviour.
+
+2007-11-05 Corinna Vinschen <corinna@vinschen.de>
+
+ * shm.cc (shmctl): On IPC_RMID also unmap all views on shared mem
+ as well as connected shm_attached_list entry.
+
+2007-10-30 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_disk_file.cc (fhandler_base::fstat_helper): Don't remove
+ write bits for directories with R/O attribute.
+ (fhandler_base::fhaccess): Don't shortcircuit R/O attribute with W_OK
+ scenarios for directories.
+
+2007-09-26 Corinna Vinschen <corinna@vinschen.de>
+
+ * termios.cc (setspeed): Support new baud rates introduced 2007-02-05.
+
+2007-09-18 Corinna Vinschen <corinna@vinschen.de>
+
+ * mmap.cc (fh_disk_file): Delete as global static variable and...
+ (mmap64): ...define as local pointer to make mmap thread-safe.
+ Accommodate throughout. Only initialize fh_disk_file after file could
+ be opened with GENERIC_EXECUTE access.
+
+2007-09-06 Brian Dessent <brian@dessent.net>
+
+ * include/sys/stdio.h (_flockfile): Don't try to lock a FILE
+ that has the __SSTR flag set.
+ (_ftrylockfile): Likewise.
+ (_funlockfile): Likewise.
+
+2007-08-24 Corinna Vinschen <corinna@vinschen.de>
+
+ * syscalls.cc (open): Don't follow symlinks if O_EXCL is given.
+
+2007-08-09 Ernie Coskrey <Ernie.Coskrey@steeleye.com>
+
+ * gendef (sigbe): Reset "incyg" while the stack lock is active to avoid
+ a potential race.
+
+2007-08-01 Corinna Vinschen <corinna@vinschen.de>
+
+ * localtime.cc (tzsetwall): Don't set TZ.
+
+2007-07-17 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler.cc (fhandler_base::fhaccess): Add check for R/O file system.
+
+2007-07-14 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * init.cc (in_dllentry): Delete.
+ (dll_entry): Remove assignment to deleted variable.
+ * winsup.h (in_dllentry): Delete declaration.
+ * exceptions.cc (inside_kernel): Use another method to see if we are in
+ dll_entry phase.
+
+2007-07-14 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * init.cc (in_dllentry): Make NO_COPY to avoid spurious false positives.
+
+2007-07-09 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * dlfcn.cc (dlclose): Don't close handle returned from
+ GetModuleHandle(NULL).
+
+2007-07-06 Corinna Vinschen <corinna@vinschen.de>
+
+ * times.cc (gettimeofday): Align definition to POSIX.
+
+2007-07-04 Corinna Vinschen <corinna@vinschen.de>
+
+ * times.cc: Define __timezonefunc__ before including time.h to protect
+ definition of timezone function.
+
+2007-07-04 Corinna Vinschen <corinna@vinschen.de>
+
+ * include/cygwin/time.h: Switch to timezone variable by default. Add
+ comment.
+
2007-06-29 Brian Dessent <brian@dessent.net>
* posix.sgml: List resolver functions in BSD section with reference
@@ -11,9 +124,111 @@
2007-06-28 Brian Dessent <brian@dessent.net>
Backport documentation changes from HEAD.
- * ansi.sgml: Delete.
- * misc-std.sgml: Delete.
- * posix.sgml: Rework entirely.
+ * ansi.sgml: Delete.
+ * misc-std.sgml: Delete.
+ * posix.sgml: Rework entirely.
+
+2007-06-27 Corinna Vinschen <corinna@vinschen.de>
+
+ * shared_info.h (SHARED_INFO_CB): Accommodate change to shared_info.
+ (CURR_SHARED_MAGIC): Ditto.
+ (class shared_info): Add heap_slop_inited member.
+ * shared.cc (shared_info::heap_slop_size): Use heap_slop_inited to
+ track initializing heap_slop since 0 is a valid value for heap_slop.
+ Drop useless < 0 consideration.
+
+2007-06-12 Christopher Faylor <me+cygwin@cgf.cx>
+
+ * signal.cc (usleep): Use useconds_t for the type as per POSIX.
+
+2007-06-12 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler.cc (fhandler_base::fstat): Set pipe permission bits more
+ correctly.
+
+2007-05-29 Corinna Vinschen <corinna@vinschen.de>
+
+ * dtable.cc (dtable::set_file_pointers_for_exec): Call SetFilePointer
+ correctly for 64 bit file access. Comment out functionality.
+ * fhandler.cc (fhandler_base::open): Don't set append_mode.
+ (fhandler_base::write): Check for O_APPEND instead of append_mode.
+ Call SetFilePointer correctly for 64 bit file access. Handle
+ errors from SetFilePointer.
+ * fhandler.h (class fhandler_base): Drop append_mode status flag.
+ * fhandler_disk_file.cc (fhandler_base::fstat_helper): Handle
+ seeking correctly for 64 bit file access.
+
+2007-05-21 Christian Franke <franke@computer.org>
+
+ * fhandler_floppy.cc (fhandler_dev_floppy::lseek): Don't invalidate
+ devbuf if new position is within buffered range.
+
+2007-05-21 Eric Blake <ebb9@byu.net>
+
+ * include/search.h (hsearch_r): Provide declaration.
+
+2007-05-21 Christian Franke <franke@computer.org>
+ Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_floppy.cc (fhandler_dev_floppy::lseek): Set buf size to
+ sector size. Simplify non-sector aligned case. Handle errors from
+ raw_read.
+
+2007-05-15 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_socket.cc (adjust_socket_file_mode): New inline function.
+ (fhandler_socket::fchmod): Squeeze mode through adjust_socket_file_mode
+ before using it.
+ (fhandler_socket::bind): Ditto.
+
+2007-04-18 Brian Dessent <brian@dessent.net>
+
+ * cygwin.sc: Remove duplicated .debug_macinfo section.
+ * dllfixdbg: Also copy DWARF-2 sections into .dbg file.
+
+2007-04-06 Eric Blake <ebb9@byu.net>
+
+ * include/stdint.h (WINT_MIN): Fix sign.
+
+2007-04-04 Eric Blake <ebb9@byu.net>
+
+ * include/stdint.h (WINT_MIN, WINT_MAX): Fix definition.
+
+2007-03-28 Christopher Faylor <me@cgf.cx>
+
+ * spawn.cc (spawn_guts): Start pure-windows processes in a suspended
+ state to avoid potential DuplicateHandle problems.
+
+2007-03-07 Christopher Faylor <me@cgf.cx>
+
+ * signal.cc (handle_sigprocmask): Remove extraneous
+ sig_dispatch_pending.
+
+2007-02-26 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler.cc (fhandler_base::fstat): Set all file times to arbitrary
+ fixed value.
+
+2007-02-20 Christopher Faylor <me@cgf.cx>
+
+ * exceptions.cc (_cygtls::signal_exit): Only call myself.exit when when
+ exit_state indicates that we've visited do_exit.
+ * sync.h (lock_process::lock_process): Use renamed exit_state -
+ ES_PROCESS_LOCKED.
+ * winsup.h: Rename ES_MUTO_SET to ES_PROCESS_LOCKED.
+
+2007-02-20 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_socket.cc (fhandler_socket::bind): Remove printing wrong
+ errno in debug output.
+
+2007-02-05 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler_serial.cc (fhandler_serial::tcsetattr): Add support for
+ baud rates up to 3000000 baud. Add missing 128K and 256K cases.
+ (fhandler_serial::tcgetattr): Ditto.
+ * include/sys/termios.h: Add baud rate definitions from B460800 up to
+ B3000000.
2007-01-30 Corinna Vinschen <corinna@vinschen.de>
@@ -68,6 +283,16 @@
* mmap.cc: Do bookkeeping in 4K pages, rather than in 64K chunks.
+2007-01-04 Brian Ford <Brian.Ford@FlightSafety.com>
+ Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler.h (PREFERRED_IO_BLKSIZE): Define as 64K.
+ * fhandler.cc (fhandler_base::fstat): Set st_blksize to
+ PREFERRED_IO_BLKSIZE.
+ * fhandler_disk_file.cc (fhandler_base::fstat_helper): Ditto.
+ * fhandler_mailslot.cc (fhandler_mailslot::fstat): Ditto.
+ * fhandler_raw.cc (fhandler_dev_raw::fstat): Ditto.
+
2007-01-01 Christopher Faylor <me@cgf.cx>
* spawn.cc (spawn_guts): Don't expect synchronization from a non-cygwin
@@ -166,6 +391,11 @@
* include/cygwin/version.h: Bump DLL minor version number to 23.
+2006-11-08 Corinna Vinschen <corinna@vinschen.de>
+
+ * security.cc (get_token_group_sidlist): Always add the local
+ group to the token.
+
2006-11-06 Corinna Vinschen <corinna@vinschen.de>
* dtable.cc (build_fh_pc): Add missing DEV_SD1_MAJOR case (Thanks to
@@ -348,6 +578,12 @@
* dllfixdbg: Accommodate newer binutils which put the gnu_debuglink at
the end rather than at the beginning.
+2006-07-14 Corinna Vinschen <corinna@vinschen.de>
+
+ * security.cc (get_token_group_sidlist): Always add the interactive
+ group to the token. Create logon_id group SID by copying it from
+ incoming group list.
+
2006-07-13 Christopher Faylor <cgf@timesys.com>
* sigproc.cc (waitq_head): Don't initialize to zero.
diff --git a/winsup/cygwin/cygwin.sc b/winsup/cygwin/cygwin.sc
index 6247e86cc59..30b7b691abe 100644
--- a/winsup/cygwin/cygwin.sc
+++ b/winsup/cygwin/cygwin.sc
@@ -138,6 +138,5 @@ SECTIONS
.debug_str ALIGN(__section_alignment__) (NOLOAD) : { *(.debug_str) }
.debug_loc ALIGN(__section_alignment__) (NOLOAD) : { *(.debug_loc) }
.debug_macinfo ALIGN(__section_alignment__) (NOLOAD) : { *(.debug_macinfo) }
- .debug_macinfo ALIGN(__section_alignment__) (NOLOAD) : { *(.debug_macinfo) }
.debug_ranges ALIGN(__section_alignment__) (NOLOAD) : { *(.debug_ranges) }
}
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
new file mode 100644
index 00000000000..3893045b328
--- /dev/null
+++ b/winsup/cygwin/dlfcn.cc
@@ -0,0 +1,167 @@
+/* dlfcn.cc
+
+ Copyright 1998, 2000, 2001, 2002, 2003, 2004 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 <psapi.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "perprocess.h"
+#include "thread.h"
+#include "dlfcn.h"
+#include "dll_init.h"
+#include "cygtls.h"
+
+static void __stdcall
+set_dl_error (const char *str)
+{
+ strcpy (_my_tls.locals.dl_buffer, strerror (get_errno ()));
+ _my_tls.locals.dl_error = 1;
+}
+
+/* Look for an executable file given the name and the environment
+ variable to use for searching (eg., PATH); returns the full
+ pathname (static buffer) if found or NULL if not. */
+inline const char * __stdcall
+check_path_access (const char *mywinenv, const char *name, path_conv& buf)
+{
+ return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_CWD | FE_DLL);
+}
+
+/* Search LD_LIBRARY_PATH for dll, if it exists.
+ Return Windows version of given path. */
+static const char * __stdcall
+get_full_path_of_dll (const char* str, char *name)
+{
+ int len = strlen (str);
+
+ /* empty string or too long to be legal win32 pathname? */
+ if (len == 0 || len >= CYG_MAX_PATH)
+ return str; /* Yes. Let caller deal with it. */
+
+ const char *ret;
+
+ strcpy (name, str); /* Put it somewhere where we can manipulate it. */
+
+ /* Add extension if necessary */
+ if (str[len - 1] != '.')
+ {
+ /* Add .dll only if no extension provided. */
+ const char *p = strrchr (str, '.');
+ if (!p || strpbrk (p, "\\/"))
+ strcat (name, ".dll");
+ }
+
+ path_conv real_filename;
+
+ if (isabspath (name) ||
+ (ret = check_path_access ("LD_LIBRARY_PATH=", name, real_filename)
+ ?: check_path_access ("/usr/lib", name, real_filename)) == NULL)
+ real_filename.check (name, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY); /* Convert */
+
+ if (!real_filename.error)
+ ret = strcpy (name, real_filename);
+ else
+ {
+ set_errno (real_filename.error);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+void *
+dlopen (const char *name, int)
+{
+ void *ret;
+
+ if (name == NULL)
+ ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
+ else
+ {
+ char buf[CYG_MAX_PATH];
+ /* handle for the named library */
+ const char *fullpath = get_full_path_of_dll (name, buf);
+ if (!fullpath)
+ ret = NULL;
+ else
+ {
+ ret = (void *) LoadLibrary (fullpath);
+ if (ret == NULL)
+ __seterrno ();
+ }
+ }
+
+ if (!ret)
+ set_dl_error ("dlopen");
+ debug_printf ("ret %p", ret);
+
+ return ret;
+}
+
+void *
+dlsym (void *handle, const char *name)
+{
+ void *ret = NULL;
+ if (handle == RTLD_DEFAULT)
+ { /* search all modules */
+ HANDLE cur_proc = GetCurrentProcess ();
+ HMODULE *modules;
+ DWORD needed, i;
+ if (!EnumProcessModules (cur_proc, NULL, 0, &needed))
+ {
+ dlsym_fail:
+ set_dl_error ("dlsym");
+ return NULL;
+ }
+ modules = (HMODULE*) alloca (needed);
+ if (!EnumProcessModules (cur_proc, modules, needed, &needed))
+ goto dlsym_fail;
+ for (i = 0; i < needed / sizeof (HMODULE); i++)
+ if ((ret = (void *) GetProcAddress (modules[i], name)))
+ break;
+ }
+ else
+ ret = (void *) GetProcAddress ((HMODULE)handle, name);
+ if (!ret)
+ set_dl_error ("dlsym");
+ debug_printf ("ret %p", ret);
+ return ret;
+}
+
+int
+dlclose (void *handle)
+{
+ int ret = -1;
+ if (handle == GetModuleHandle (NULL) || FreeLibrary ((HMODULE) handle))
+ ret = 0;
+ if (ret)
+ set_dl_error ("dlclose");
+ return ret;
+}
+
+char *
+dlerror ()
+{
+ char *res;
+ if (!_my_tls.locals.dl_error)
+ res = NULL;
+ else
+ {
+ _my_tls.locals.dl_error = 0;
+ res = _my_tls.locals.dl_buffer;
+ }
+ return res;
+}
diff --git a/winsup/cygwin/dllfixdbg b/winsup/cygwin/dllfixdbg
new file mode 100755
index 00000000000..f97be0ca6b1
--- /dev/null
+++ b/winsup/cygwin/dllfixdbg
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+# Copyright 2006, 2007 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.
+#
+use integer;
+use strict;
+sub xit($@);
+my $strip = $ARGV[0] eq '-s';
+shift if $strip;
+my $objdump = shift;
+my @objcopy = ((shift));
+my $dll = shift;
+my $dbg = shift;
+xit 0, @objcopy, '-R', '.gnu_debuglink_overlay', '--only-keep-debug', $dll, $dbg;
+xit 0, @objcopy, '-g', '--add-gnu-debuglink=' . $dbg, $dll;
+open(OBJDUMP, '-|', "$objdump --headers $dll");
+my %section;
+while (<OBJDUMP>) {
+ my ($idx, $name, $size, $vma, $lma, $fileoff, $algn) = /^\s*(\d+)\s+(\.\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/;
+ if ($name eq '.gnu_debuglink') {
+ push(@objcopy, '--set-section-flag', '.gnu_debuglink=contents,readonly,debug,noload');
+ $idx = $section{'.gnu_debuglink'}{-idx} if defined($section{'.gnu_debuglink'}{-idx});
+ } elsif ($name eq '.gnu_debuglink_overlay') {
+ push (@objcopy, '-R', '.gnu_debuglink_overlay');
+ $section{'.gnu_debuglink'}{-idx} = $idx;
+ next;
+ }
+ defined($idx) and
+ $section{$name} = {-idx=>int($idx), -size=>hex($size), -vma=>hex($vma), -lma=>hex($lma), -fileoff=>hex($fileoff),
+ -algn=>0x00001000};
+}
+close OBJDUMP;
+my $vma;
+for my $k (sort {$section{$a}{-idx} <=> $section{$b}{-idx}} keys %section) {
+ if ($strip && $k =~ /\.(?:stab|debug)/o) {
+ push(@objcopy, '-R', $k);
+ next;
+ }
+ if (!defined($vma)) {
+ $vma = $section{$k}{-vma};
+ }
+ if ($vma != $section{$k}{-vma}) {
+ my $newvma = align($vma, $section{$k}{-algn});
+ if ($newvma != $vma) {
+ printf STDERR "$0: ERROR $k VMA 0x%08x != 0x%08x\n", $vma, $newvma;
+ exit 1;
+ }
+ push(@objcopy, '--change-section-address', sprintf "$k=0x%08x", $vma);
+ }
+ $vma = align($vma + $section{$k}{-size}, $section{$k}{-algn});
+}
+
+warn "$0: ERROR final VMA (" . sprintf("0x%08x", $vma) . ") not on 64K boundary\n" if $vma != align($vma, 64 * 1024);
+push(@objcopy, $dll, @ARGV);
+xit 1, @objcopy;
+sub align {
+ my $n = $_[0];
+ my $align = $_[1] - 1;
+ return ($n + $align) & ~$align;
+}
+
+sub xit($@) {
+ my $execit = shift;
+ print "+ @_\n";
+ if ($execit) {
+ exec @_ or die "$0: couldn't exec $_[0] - $!\n";
+ } else {
+ system @_ and die "$0: couldn't exec $_[0] - $!\n";
+ }
+}
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index e2fc34c93f1..8948033eaf8 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -681,12 +681,16 @@ dtable::fixup_before_exec (DWORD target_proc_id)
void
dtable::set_file_pointers_for_exec ()
{
+/* This is not POSIX-compliant. */
+#if 0
+ LONG off_high = 0;
lock ();
fhandler_base *fh;
for (size_t i = 0; i < size; i++)
if ((fh = fds[i]) != NULL && fh->get_flags () & O_APPEND)
- SetFilePointer (fh->get_handle (), 0, 0, FILE_END);
+ SetFilePointer (fh->get_handle (), 0, &off_high, FILE_END);
unlock ();
+#endif
}
void
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
new file mode 100644
index 00000000000..c05151d3273
--- /dev/null
+++ b/winsup/cygwin/exceptions.cc
@@ -0,0 +1,1394 @@
+/* exceptions.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007 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 <wingdi.h>
+#include <winuser.h>
+#include <imagehlp.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <assert.h>
+#include <syslog.h>
+
+#include "exceptions.h"
+#include "sync.h"
+#include "pinfo.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "cygerrno.h"
+#include "shared_info.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info.h"
+
+#define CALL_HANDLER_RETRY 20
+
+char debugger_command[2 * CYG_MAX_PATH + 20];
+
+extern "C" {
+extern void sigdelayed ();
+};
+
+extern child_info_spawn *chExeced;
+int NO_COPY sigExeced;
+
+static BOOL WINAPI ctrl_c_handler (DWORD);
+char windows_system_directory[1024];
+static size_t windows_system_directory_length;
+
+/* This is set to indicate that we have already exited. */
+
+static NO_COPY int exit_already = 0;
+static muto NO_COPY mask_sync;
+
+NO_COPY static struct
+{
+ unsigned int code;
+ const char *name;
+} status_info[] =
+{
+#define X(s) s, #s
+ { X (STATUS_ABANDONED_WAIT_0) },
+ { X (STATUS_ACCESS_VIOLATION) },
+ { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
+ { X (STATUS_BREAKPOINT) },
+ { X (STATUS_CONTROL_C_EXIT) },
+ { X (STATUS_DATATYPE_MISALIGNMENT) },
+ { X (STATUS_FLOAT_DENORMAL_OPERAND) },
+ { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
+ { X (STATUS_FLOAT_INEXACT_RESULT) },
+ { X (STATUS_FLOAT_INVALID_OPERATION) },
+ { X (STATUS_FLOAT_OVERFLOW) },
+ { X (STATUS_FLOAT_STACK_CHECK) },
+ { X (STATUS_FLOAT_UNDERFLOW) },
+ { X (STATUS_GUARD_PAGE_VIOLATION) },
+ { X (STATUS_ILLEGAL_INSTRUCTION) },
+ { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
+ { X (STATUS_INTEGER_OVERFLOW) },
+ { X (STATUS_INVALID_DISPOSITION) },
+ { X (STATUS_IN_PAGE_ERROR) },
+ { X (STATUS_NONCONTINUABLE_EXCEPTION) },
+ { X (STATUS_NO_MEMORY) },
+ { X (STATUS_PENDING) },
+ { X (STATUS_PRIVILEGED_INSTRUCTION) },
+ { X (STATUS_SINGLE_STEP) },
+ { X (STATUS_STACK_OVERFLOW) },
+ { X (STATUS_TIMEOUT) },
+ { X (STATUS_USER_APC) },
+ { X (STATUS_WAIT_0) },
+ { 0, 0 }
+#undef X
+};
+
+/* Initialization code. */
+
+BOOL WINAPI
+dummy_ctrl_c_handler (DWORD)
+{
+ return TRUE;
+}
+
+void
+init_console_handler (bool install_handler)
+{
+ BOOL res;
+
+ SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
+ if (wincap.has_null_console_handler_routine ())
+ SetConsoleCtrlHandler (NULL, FALSE);
+ if (install_handler)
+ res = SetConsoleCtrlHandler (ctrl_c_handler, TRUE);
+ else if (wincap.has_null_console_handler_routine ())
+ res = SetConsoleCtrlHandler (NULL, TRUE);
+ else
+ res = SetConsoleCtrlHandler (dummy_ctrl_c_handler, TRUE);
+ if (!res)
+ system_printf ("SetConsoleCtrlHandler failed, %E");
+}
+
+extern "C" void
+error_start_init (const char *buf)
+{
+ if (!buf || !*buf)
+ {
+ debugger_command[0] = '\0';
+ return;
+ }
+
+ char pgm[CYG_MAX_PATH];
+ if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
+ strcpy (pgm, "cygwin1.dll");
+ for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
+ *p = '/';
+
+ __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm);
+}
+
+static void
+open_stackdumpfile ()
+{
+ if (myself->progname[0])
+ {
+ const char *p;
+ /* write to progname.stackdump if possible */
+ if (!myself->progname[0])
+ p = "unknown";
+ else if ((p = strrchr (myself->progname, '\\')))
+ p++;
+ else
+ p = myself->progname;
+ char corefile[strlen (p) + sizeof (".stackdump")];
+ __small_sprintf (corefile, "%s.stackdump", p);
+ HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
+ CREATE_ALWAYS, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (!myself->cygstarted)
+ system_printf ("Dumping stack trace to %s", corefile);
+ else
+ debug_printf ("Dumping stack trace to %s", corefile);
+ SetStdHandle (STD_ERROR_HANDLE, h);
+ }
+ }
+}
+
+/* Utilities for dumping the stack, etc. */
+
+static void
+exception (EXCEPTION_RECORD *e, CONTEXT *in)
+{
+ const char *exception_name = NULL;
+
+ if (e)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ exception_name = status_info[i].name;
+ break;
+ }
+ }
+ }
+
+ if (exception_name)
+ small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
+ else
+ small_printf ("Signal %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
+ small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
+ in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
+ small_printf ("ebp=%08x esp=%08x program=%s, pid %u, thread %s\r\n",
+ in->Ebp, in->Esp, myself->progname, myself->pid, cygthread::name ());
+ small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
+ in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
+}
+
+/* A class for manipulating the stack. */
+class stack_info
+{
+ int walk (); /* Uses the "old" method */
+ char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
+ bool needargs;
+ DWORD dummy_frame;
+public:
+ STACKFRAME sf; /* For storing the stack information */
+ void init (DWORD, bool, bool); /* Called the first time that stack info is needed */
+
+ /* Postfix ++ iterates over the stack, returning zero when nothing is left. */
+ int operator ++(int) { return walk (); }
+};
+
+/* The number of parameters used in STACKFRAME */
+#define NPARAMS (sizeof (thestack.sf.Params) / sizeof (thestack.sf.Params[0]))
+
+/* This is the main stack frame info for this process. */
+static NO_COPY stack_info thestack;
+
+/* Initialize everything needed to start iterating. */
+void
+stack_info::init (DWORD ebp, bool wantargs, bool goodframe)
+{
+# define debp ((DWORD *) ebp)
+ memset (&sf, 0, sizeof (sf));
+ if (!goodframe)
+ sf.AddrFrame.Offset = ebp;
+ else
+ {
+ dummy_frame = ebp;
+ sf.AddrFrame.Offset = (DWORD) &dummy_frame;
+ }
+ sf.AddrReturn.Offset = debp[1];
+ sf.AddrFrame.Mode = AddrModeFlat;
+ needargs = wantargs;
+# undef debp
+}
+
+/* Walk the stack by looking at successive stored 'bp' frames.
+ This is not foolproof. */
+int
+stack_info::walk ()
+{
+ char **ebp;
+ if ((ebp = (char **) next_offset ()) == NULL)
+ return 0;
+
+ sf.AddrFrame.Offset = (DWORD) ebp;
+ sf.AddrPC.Offset = sf.AddrReturn.Offset;
+
+ if (!sf.AddrPC.Offset)
+ return 0; /* stack frames are exhausted */
+
+ /* The return address always follows the stack pointer */
+ sf.AddrReturn.Offset = (DWORD) *++ebp;
+
+ if (needargs)
+ /* The arguments follow the return address */
+ for (unsigned i = 0; i < NPARAMS; i++)
+ sf.Params[i] = (DWORD) *++ebp;
+
+ return 1;
+}
+
+static void
+stackdump (DWORD ebp, int open_file, bool isexception)
+{
+ extern unsigned long rlim_core;
+ static bool already_dumped;
+
+ if (rlim_core == 0UL || (open_file && already_dumped))
+ return;
+
+ if (open_file)
+ open_stackdumpfile ();
+
+ already_dumped = true;
+
+ int i;
+
+ thestack.init (ebp, 1, !isexception); /* Initialize from the input CONTEXT */
+ small_printf ("Stack trace:\r\nFrame Function Args\r\n");
+ for (i = 0; i < 16 && thestack++; i++)
+ {
+ small_printf ("%08x %08x ", thestack.sf.AddrFrame.Offset,
+ thestack.sf.AddrPC.Offset);
+ for (unsigned j = 0; j < NPARAMS; j++)
+ small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
+ small_printf (")\r\n");
+ }
+ small_printf ("End of stack trace%s\n",
+ i == 16 ? " (more stack frames may be present)" : "");
+}
+
+static bool
+inside_kernel (CONTEXT *cx)
+{
+ int res;
+ MEMORY_BASIC_INFORMATION m;
+
+ if (!_my_tls.isinitialized ())
+ return true;
+
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) cx->Eip, &m, sizeof m))
+ sigproc_printf ("couldn't get memory info, pc %p, %E", cx->Eip);
+
+ char *checkdir = (char *) alloca (windows_system_directory_length + 4);
+ memset (checkdir, 0, sizeof (checkdir));
+
+# define h ((HMODULE) m.AllocationBase)
+ /* Apparently Windows 95 can sometimes return bogus addresses from
+ GetThreadContext. These resolve to a strange allocation base.
+ These should *never* be treated as interruptible. */
+ if (!h || m.State != MEM_COMMIT)
+ res = true;
+ else if (h == user_data->hmodule)
+ res = false;
+ else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
+ res = false;
+ else
+ res = strncasematch (windows_system_directory, checkdir,
+ windows_system_directory_length);
+ sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->Eip, h, res);
+# undef h
+ return res;
+}
+
+/* Temporary (?) function for external callers to get a stack dump */
+extern "C" void
+cygwin_stackdump ()
+{
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (GetCurrentThread (), &c);
+ stackdump (c.Ebp, 0, 0);
+}
+
+#define TIME_TO_WAIT_FOR_DEBUGGER 10000
+
+extern "C" int
+try_to_debug (bool waitloop)
+{
+ debug_printf ("debugger_command '%s'", debugger_command);
+ if (*debugger_command == '\0')
+ return 0;
+ if (being_debugged ())
+ {
+ extern void break_here ();
+ break_here ();
+ return 0;
+ }
+
+ __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());
+
+ LONG prio = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = 0;
+ si.cb = sizeof (si);
+
+ /* FIXME: need to know handles of all running threads to
+ suspend_all_threads_except (current_thread_id);
+ */
+
+ /* if any of these mutexes is owned, we will fail to start any cygwin app
+ until trapped app exits */
+
+ lock_ttys::release ();
+
+ /* prevent recursive exception handling */
+ char* rawenv = GetEnvironmentStrings () ;
+ for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
+ {
+ if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
+ {
+ char* q = strstr (p, "error_start") ;
+ /* replace 'error_start=...' with '_rror_start=...' */
+ if (q)
+ {
+ *q = '_' ;
+ SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
+ }
+ break ;
+ }
+ }
+
+ console_printf ("*** starting debugger for pid %u, tid %u\n",
+ cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
+ BOOL dbg;
+ dbg = CreateProcess (NULL,
+ debugger_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL,
+ NULL,
+ &si,
+ &pi);
+
+ if (!dbg)
+ system_printf ("Failed to start debugger, %E");
+ else
+ {
+ if (!waitloop)
+ return dbg;
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
+ while (!being_debugged ())
+ low_priority_sleep (0);
+ Sleep (2000);
+ }
+
+ console_printf ("*** continuing pid %u from debugger call (%d)\n",
+ cygwin_pid (GetCurrentProcessId ()), dbg);
+
+ SetThreadPriority (GetCurrentThread (), prio);
+ return dbg;
+}
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+static void __stdcall rtl_unwind (exception_list *, PEXCEPTION_RECORD) __attribute__ ((noinline, regparm (3)));
+void __stdcall
+rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e)
+{
+ __asm__ ("\n\
+ pushl %%ebx \n\
+ pushl %%edi \n\
+ pushl %%esi \n\
+ pushl $0 \n\
+ pushl %1 \n\
+ pushl $1f \n\
+ pushl %0 \n\
+ call _RtlUnwind@16 \n\
+1: \n\
+ popl %%esi \n\
+ popl %%edi \n\
+ popl %%ebx \n\
+": : "r" (frame), "r" (e));
+}
+
+/* Main exception handler. */
+
+extern "C" char *__progname;
+int
+_cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *)
+{
+ static bool NO_COPY debugging;
+ static int NO_COPY recursed;
+ _cygtls& me = _my_tls;
+
+ if (debugging && ++debugging < 500000)
+ {
+ SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
+ return 0;
+ }
+
+ /* If we've already exited, don't do anything here. Returning 1
+ tells Windows to keep looking for an exception handler. */
+ if (exit_already || e->ExceptionFlags)
+ return 1;
+
+ siginfo_t si = {0};
+ si.si_code = SI_KERNEL;
+ /* Coerce win32 value to posix value. */
+ switch (e->ExceptionCode)
+ {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_STACK_CHECK:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTSUB;
+ break;
+ case STATUS_FLOAT_INEXACT_RESULT:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTRES;
+ break;
+ case STATUS_FLOAT_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTOVF;
+ break;
+ case STATUS_FLOAT_UNDERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_FLTUND;
+ break;
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_INTDIV;
+ break;
+ case STATUS_INTEGER_OVERFLOW:
+ si.si_signo = SIGFPE;
+ si.si_code = FPE_INTOVF;
+ break;
+
+ case STATUS_ILLEGAL_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_ILLOPC;
+ break;
+
+ case STATUS_PRIVILEGED_INSTRUCTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_PRVOPC;
+ break;
+
+ case STATUS_NONCONTINUABLE_EXCEPTION:
+ si.si_signo = SIGILL;
+ si.si_code = ILL_ILLADR;
+ break;
+
+ case STATUS_TIMEOUT:
+ si.si_signo = SIGALRM;
+ break;
+
+ case STATUS_GUARD_PAGE_VIOLATION:
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_OBJERR;
+ break;
+
+ case STATUS_DATATYPE_MISALIGNMENT:
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_ADRALN;
+ break;
+
+ case STATUS_ACCESS_VIOLATION:
+ switch (mmap_is_attached_or_noreserve_page (e->ExceptionInformation[1]))
+ {
+ case 2: /* MAP_NORESERVE page, now commited. */
+ return 0;
+ case 1: /* MAP_NORESERVE page, commit failed, or
+ access to mmap page beyond EOF. */
+ si.si_signo = SIGBUS;
+ si.si_code = BUS_OBJERR;
+ break;
+ default:
+ MEMORY_BASIC_INFORMATION m;
+ VirtualQuery ((PVOID) e->ExceptionInformation[1], &m, sizeof m);
+ si.si_signo = SIGSEGV;
+ si.si_code = m.State == MEM_FREE ? SEGV_MAPERR : SEGV_ACCERR;
+ break;
+ }
+ break;
+
+ case STATUS_ARRAY_BOUNDS_EXCEEDED:
+ case STATUS_IN_PAGE_ERROR:
+ case STATUS_NO_MEMORY:
+ case STATUS_INVALID_DISPOSITION:
+ case STATUS_STACK_OVERFLOW:
+ si.si_signo = SIGSEGV;
+ si.si_code = SEGV_MAPERR;
+ break;
+
+ case STATUS_CONTROL_C_EXIT:
+ si.si_signo = SIGINT;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ /* CloseHandle will throw this exception if it is given an
+ invalid handle. We don't care about the exception; we just
+ want CloseHandle to return an error. This can be revisited
+ if gcc ever supports Windows style structured exception
+ handling. */
+ return 0;
+
+ default:
+ /* If we don't recognize the exception, we have to assume that
+ we are doing structured exception handling, and we let
+ something else handle it. */
+ return 1;
+ }
+
+ rtl_unwind (frame, e);
+
+ debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
+ debug_printf ("In cygwin_except_handler sig %d at %p", si.si_signo, in->Eip);
+
+ if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo))
+ syscall_printf ("signal %d, masked %p", si.si_signo,
+ global_sigs[si.si_signo].sa_mask);
+
+ debug_printf ("In cygwin_except_handler calling %p",
+ global_sigs[si.si_signo].sa_handler);
+
+ DWORD *ebp = (DWORD *) in->Esp;
+ for (DWORD *bpend = (DWORD *) __builtin_frame_address (0); ebp > bpend; ebp--)
+ if (*ebp == in->SegCs && ebp[-1] == in->Eip)
+ {
+ ebp -= 2;
+ break;
+ }
+
+ if (me.fault_guarded ())
+ me.return_from_fault ();
+
+ me.copy_context (in);
+ if (!cygwin_finished_initializing
+ || &me == _sig_tls
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_DFL
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_IGN
+ || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_ERR)
+ {
+ /* Print the exception to the console */
+ if (!myself->cygstarted)
+ for (int i = 0; status_info[i].name; i++)
+ if (status_info[i].code == e->ExceptionCode)
+ {
+ system_printf ("Exception: %s", status_info[i].name);
+ break;
+ }
+
+ /* Another exception could happen while tracing or while exiting.
+ Only do this once. */
+ if (recursed++)
+ system_printf ("Error while dumping state (probably corrupted stack)");
+ else
+ {
+ if (try_to_debug (0))
+ {
+ debugging = true;
+ return 0;
+ }
+
+ open_stackdumpfile ();
+ exception (e, in);
+ stackdump ((DWORD) ebp, 0, 1);
+ }
+
+ if (e->ExceptionCode == STATUS_ACCESS_VIOLATION)
+ {
+ int error_code = 0;
+ if (si.si_code == SEGV_ACCERR) /* Address present */
+ error_code |= 1;
+ if (e->ExceptionInformation[0]) /* Write access */
+ error_code |= 2;
+ if (!inside_kernel (in)) /* User space */
+ error_code |= 4;
+ klog (LOG_INFO, "%s[%d]: segfault at %08x rip %08x rsp %08x error %d",
+ __progname, myself->pid,
+ e->ExceptionInformation[1], in->Eip, in->Esp,
+ ((in->Eip >= 0x61000000 && in->Eip < 0x61200000)
+ ? 0 : 4) | (e->ExceptionInformation[0] << 1));
+ }
+
+ me.signal_exit (0x80 | si.si_signo); // Flag signal + core dump
+ }
+
+ si.si_addr = (void *) in->Eip;
+ si.si_errno = si.si_pid = si.si_uid = 0;
+ me.incyg++;
+ sig_send (NULL, si, &me); // Signal myself
+ me.incyg--;
+ e->ExceptionFlags = 0;
+ return 0;
+}
+
+/* Utilities to call a user supplied exception handler. */
+
+#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))
+
+/* Non-raceable sigsuspend
+ * Note: This implementation is based on the Single UNIX Specification
+ * man page. This indicates that sigsuspend always returns -1 and that
+ * attempts to block unblockable signals will be silently ignored.
+ * This is counter to what appears to be documented in some UNIX
+ * man pages, e.g. Linux.
+ */
+int __stdcall
+handle_sigsuspend (sigset_t tempmask)
+{
+ if (&_my_tls != _main_tls)
+ {
+ cancelable_wait (signal_arrived, INFINITE, cw_cancel_self);
+ return -1;
+ }
+
+ sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+
+ set_signal_mask (tempmask, myself->getsigmask ());
+ sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
+
+ pthread_testcancel ();
+ cancelable_wait (signal_arrived, INFINITE);
+
+ set_sig_errno (EINTR); // Per POSIX
+
+ /* A signal dispatch function will have been added to our stack and will
+ be hit eventually. Set the old mask to be restored when the signal
+ handler returns and indicate its presence by modifying deltamask. */
+
+ _my_tls.deltamask |= SIG_NONMASKABLE;
+ _my_tls.oldmask = oldmask; // Will be restored by signal handler
+ return -1;
+}
+
+extern DWORD exec_exit; // Possible exit value for exec
+
+extern "C" {
+static void
+sig_handle_tty_stop (int sig)
+{
+ _my_tls.incyg = 1;
+ /* Silently ignore attempts to suspend if there is no accommodating
+ cygwin parent to deal with this behavior. */
+ if (!myself->cygstarted)
+ {
+ myself->process_state &= ~PID_STOPPED;
+ return;
+ }
+
+ myself->stopsig = sig;
+ myself->alert_parent (sig);
+ sigproc_printf ("process %d stopped by signal %d", myself->pid, sig);
+ HANDLE w4[2];
+ w4[0] = sigCONT;
+ w4[1] = signal_arrived;
+ switch (WaitForMultipleObjects (2, w4, TRUE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_OBJECT_0 + 1:
+ reset_signal_arrived ();
+ myself->alert_parent (SIGCONT);
+ break;
+ default:
+ api_fatal ("WaitSingleObject failed, %E");
+ break;
+ }
+ _my_tls.incyg = 0;
+}
+}
+
+bool
+_cygtls::interrupt_now (CONTEXT *cx, int sig, void *handler,
+ struct sigaction& siga)
+{
+ bool interrupted;
+
+ if (incyg || spinning || locked () || inside_kernel (cx))
+ interrupted = false;
+ else
+ {
+ push ((__stack_t) cx->Eip);
+ interrupt_setup (sig, handler, siga);
+ cx->Eip = pop ();
+ SetThreadContext (*this, cx); /* Restart the thread in a new location */
+ interrupted = true;
+ }
+ return interrupted;
+}
+
+void __stdcall
+_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga)
+{
+ push ((__stack_t) sigdelayed);
+ deltamask = siga.sa_mask & ~SIG_NONMASKABLE;
+ sa_flags = siga.sa_flags;
+ func = (void (*) (int)) handler;
+ if (siga.sa_flags & SA_RESETHAND)
+ siga.sa_handler = SIG_DFL;
+ saved_errno = -1; // Flag: no errno to save
+ if (handler == sig_handle_tty_stop)
+ {
+ myself->stopsig = 0;
+ myself->process_state |= PID_STOPPED;
+ }
+
+ this->sig = sig; // Should always be last thing set to avoid a race
+
+ if (!event)
+ threadkill = false;
+ else
+ {
+ HANDLE h = event;
+ event = NULL;
+ SetEvent (h);
+ }
+
+ /* Clear any waiting threads prior to dispatching to handler function */
+ int res = SetEvent (signal_arrived); // For an EINTR case
+ proc_subproc (PROC_CLEARWAIT, 1);
+ sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
+ sig, res);
+}
+
+extern "C" void __stdcall
+set_sig_errno (int e)
+{
+ *_my_tls.errno_addr = e;
+ _my_tls.saved_errno = e;
+ // sigproc_printf ("errno %d", e);
+}
+
+static int setup_handler (int, void *, struct sigaction&, _cygtls *tls)
+ __attribute__((regparm(3)));
+static int
+setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls)
+{
+ CONTEXT cx;
+ bool interrupted = false;
+
+ if (tls->sig)
+ {
+ sigproc_printf ("trying to send sig %d but signal %d already armed",
+ sig, tls->sig);
+ goto out;
+ }
+
+ for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+ {
+ tls->lock ();
+ if (tls->incyg)
+ {
+ sigproc_printf ("controlled interrupt. stackptr %p, stack %p, stackptr[-1] %p",
+ tls->stackptr, tls->stack, tls->stackptr[-1]);
+ tls->interrupt_setup (sig, handler, siga);
+ interrupted = true;
+ tls->unlock ();
+ break;
+ }
+
+ tls->unlock ();
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
+
+ /* Suspend the thread which will receive the signal.
+ For Windows 95, we also have to ensure that the addresses returned by
+ GetThreadContext are valid.
+ If one of these conditions is not true we loop for a fixed number of times
+ since we don't want to stall the signal handler. FIXME: Will this result in
+ noticeable delays?
+ If the thread is already suspended (which can occur when a program has called
+ SuspendThread on itself) then just queue the signal. */
+
+#ifndef DEBUGGING
+ sigproc_printf ("suspending mainthread");
+#else
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ memset (&cx, 0, sizeof cx);
+ sigproc_printf ("suspending mainthread PC %p", cx.Eip);
+#endif
+ res = SuspendThread (hth);
+ /* Just set pending if thread is already suspended */
+ if (res)
+ {
+ ResumeThread (hth);
+ break;
+ }
+ cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ if (!GetThreadContext (hth, &cx))
+ system_printf ("couldn't get context of main thread, %E");
+ else
+ interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+
+ res = ResumeThread (hth);
+ if (interrupted)
+ break;
+
+ sigproc_printf ("couldn't interrupt. trying again.");
+ low_priority_sleep (0);
+ }
+
+out:
+ sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
+ return interrupted;
+}
+
+static inline bool
+has_visible_window_station ()
+{
+ HWINSTA station_hdl;
+ USEROBJECTFLAGS uof;
+ DWORD len;
+
+ /* Check if the process is associated with a visible window station.
+ These are processes running on the local desktop as well as processes
+ running in terminal server sessions.
+ Processes running in a service session not explicitely associated
+ with the desktop (using the "Allow service to interact with desktop"
+ property) are running in an invisible window station. */
+ if ((station_hdl = GetProcessWindowStation ())
+ && GetUserObjectInformationA (station_hdl, UOI_FLAGS, &uof,
+ sizeof uof, &len)
+ && (uof.dwFlags & WSF_VISIBLE))
+ return true;
+ return false;
+}
+
+/* Keyboard interrupt handler. */
+static BOOL WINAPI
+ctrl_c_handler (DWORD type)
+{
+ static bool saw_close;
+
+ if (!cygwin_finished_initializing)
+ {
+ if (myself->cygstarted) /* Was this process created by a cygwin process? */
+ return TRUE; /* Yes. Let the parent eventually handle CTRL-C issues. */
+ debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT);
+ ExitProcess (STATUS_CONTROL_C_EXIT);
+ }
+
+ _my_tls.remove (INFINITE);
+
+#if 0
+ if (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
+ proc_subproc (PROC_KILLFORKED, 0);
+#endif
+
+ /* Return FALSE to prevent an "End task" dialog box from appearing
+ for each Cygwin process window that's open when the computer
+ is shut down or console window is closed. */
+
+ if (type == CTRL_SHUTDOWN_EVENT)
+ {
+#if 0
+ /* Don't send a signal. Only NT service applications and their child
+ processes will receive this event and the services typically already
+ handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN
+ control message. */
+ sig_send (NULL, SIGTERM);
+#endif
+ return FALSE;
+ }
+
+ if (myself->ctty != -1)
+ {
+ if (type == CTRL_CLOSE_EVENT)
+ {
+ sig_send (NULL, SIGHUP);
+ saw_close = true;
+ return FALSE;
+ }
+ if (!saw_close && type == CTRL_LOGOFF_EVENT)
+ {
+ /* The CTRL_LOGOFF_EVENT is sent when *any* user logs off.
+ The below code sends a SIGHUP only if it is not performing the
+ default activity for SIGHUP. Note that it is possible for two
+ SIGHUP signals to arrive if a process group leader is exiting
+ too. Getting this 100% right is saved for a future cygwin mailing
+ list goad. */
+ if (global_sigs[SIGHUP].sa_handler != SIG_DFL)
+ {
+ sig_send (myself_nowait, SIGHUP);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ }
+
+ if (chExeced)
+ {
+ chExeced->set_saw_ctrl_c ();
+ return TRUE;
+ }
+
+ /* We're only the process group leader when we have a valid pinfo structure.
+ If we don't have one, then the parent "stub" will handle the signal. */
+ if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
+ return TRUE;
+
+ tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
+ /* Ignore this if we're not the process group leader since it should be handled
+ *by* the process group leader. */
+ if (myself->ctty != -1 && t->getpgid () == myself->pid &&
+ (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
+ /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
+ that we have handled the signal). At this point, type should be
+ a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
+ {
+ int sig = SIGINT;
+ /* If intr and quit are both mapped to ^C, send SIGQUIT on ^BREAK */
+ if (type == CTRL_BREAK_EVENT
+ && t->ti.c_cc[VINTR] == 3 && t->ti.c_cc[VQUIT] == 3)
+ sig = SIGQUIT;
+ t->last_ctrl_c = GetTickCount ();
+ killsys (-myself->pid, sig);
+ t->last_ctrl_c = GetTickCount ();
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+/* Function used by low level sig wrappers. */
+extern "C" void __stdcall
+set_process_mask (sigset_t newmask)
+{
+ set_signal_mask (newmask, myself->getsigmask ());
+sigproc_printf ("mask now %p\n", myself->getsigmask ());
+}
+
+extern "C" int
+sighold (int sig)
+{
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ sigaddset (&mask, sig);
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return 0;
+}
+
+extern "C" int
+sigrelse (int sig)
+{
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", sig);
+ return -1;
+ }
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ sigdelset (&mask, sig);
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return 0;
+}
+
+extern "C" _sig_func_ptr
+sigset (int sig, _sig_func_ptr func)
+{
+ sig_dispatch_pending ();
+ _sig_func_ptr prev;
+
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigset (%d, %p)", sig, func);
+ return (_sig_func_ptr) SIG_ERR;
+ }
+
+ mask_sync.acquire (INFINITE);
+ sigset_t mask = myself->getsigmask ();
+ /* If sig was in the signal mask return SIG_HOLD, otherwise return the
+ previous disposition. */
+ if (sigismember (&mask, sig))
+ prev = SIG_HOLD;
+ else
+ prev = global_sigs[sig].sa_handler;
+ /* If func is SIG_HOLD, add sig to the signal mask, otherwise set the
+ disposition to func and remove sig from the signal mask. */
+ if (func == SIG_HOLD)
+ sigaddset (&mask, sig);
+ else
+ {
+ /* No error checking. The test which could return SIG_ERR has already
+ been made above. */
+ signal (sig, func);
+ sigdelset (&mask, sig);
+ }
+ set_signal_mask (mask, myself->getsigmask ());
+ mask_sync.release ();
+ return prev;
+}
+
+extern "C" int
+sigignore (int sig)
+{
+ return sigset (sig, SIG_IGN) == SIG_ERR ? -1 : 0;
+}
+
+/* Update the signal mask for this process and return the old mask.
+ Called from sigdelayed */
+extern "C" sigset_t
+set_process_mask_delta ()
+{
+ mask_sync.acquire (INFINITE);
+ sigset_t newmask, oldmask;
+
+ if (_my_tls.deltamask & SIG_NONMASKABLE)
+ oldmask = _my_tls.oldmask; /* from handle_sigsuspend */
+ else
+ oldmask = myself->getsigmask ();
+ newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
+ sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
+ _my_tls.deltamask);
+ myself->setsigmask (newmask);
+ mask_sync.release ();
+ return oldmask;
+}
+
+/* Set the signal mask for this process.
+ Note that some signals are unmaskable, as in UNIX. */
+extern "C" void __stdcall
+set_signal_mask (sigset_t newmask, sigset_t& oldmask)
+{
+#ifdef CGF
+ if (&_my_tls == _sig_tls)
+ small_printf ("********* waiting in signal thread\n");
+#endif
+ mask_sync.acquire (INFINITE);
+ newmask &= ~SIG_NONMASKABLE;
+ sigset_t mask_bits = oldmask & ~newmask;
+ sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask,
+ mask_bits);
+ oldmask = newmask;
+ if (mask_bits)
+ sig_dispatch_pending (true);
+ else
+ sigproc_printf ("not calling sig_dispatch_pending");
+ mask_sync.release ();
+}
+
+int __stdcall
+sigpacket::process ()
+{
+ DWORD continue_now;
+ struct sigaction dummy = global_sigs[SIGSTOP];
+
+ if (si.si_signo != SIGCONT)
+ continue_now = false;
+ else
+ {
+ continue_now = myself->process_state & PID_STOPPED;
+ myself->stopsig = 0;
+ myself->process_state &= ~PID_STOPPED;
+ /* Clear pending stop signals */
+ sig_clear (SIGSTOP);
+ sig_clear (SIGTSTP);
+ sig_clear (SIGTTIN);
+ sig_clear (SIGTTOU);
+ }
+
+ int rc = 1;
+
+ sigproc_printf ("signal %d processing", si.si_signo);
+ struct sigaction& thissig = global_sigs[si.si_signo];
+
+ myself->rusage_self.ru_nsignals++;
+
+ bool masked;
+ void *handler;
+ if (!hExeced || (void *) thissig.sa_handler == (void *) SIG_IGN)
+ handler = (void *) thissig.sa_handler;
+ else if (tls)
+ return 1;
+ else
+ handler = NULL;
+
+ if (si.si_signo == SIGKILL)
+ goto exit_sig;
+ if (si.si_signo == SIGSTOP)
+ {
+ sig_clear (SIGCONT);
+ if (!tls)
+ tls = _main_tls;
+ goto stop;
+ }
+
+ bool insigwait_mask;
+ if ((masked = ISSTATE (myself, PID_STOPPED)))
+ insigwait_mask = false;
+ else if (!tls)
+ insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo));
+ else
+ insigwait_mask = sigismember (&tls->sigwait_mask, si.si_signo);
+
+ if (insigwait_mask)
+ goto thread_specific;
+
+ if (masked)
+ /* nothing to do */;
+ else if (sigismember (mask, si.si_signo))
+ masked = true;
+ else if (tls)
+ masked = sigismember (&tls->sigmask, si.si_signo);
+
+ if (!tls)
+ tls = _main_tls;
+
+ if (masked)
+ {
+ sigproc_printf ("signal %d blocked", si.si_signo);
+ rc = -1;
+ goto done;
+ }
+
+ /* Clear pending SIGCONT on stop signals */
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ sig_clear (SIGCONT);
+
+#ifdef CGF
+ if (being_debugged ())
+ {
+ char sigmsg[sizeof (_CYGWIN_SIGNAL_STRING " 0xffffffff")];
+ __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %p", si.si_signo);
+ OutputDebugString (sigmsg);
+ }
+#endif
+
+ if (handler == (void *) SIG_DFL)
+ {
+ if (insigwait_mask)
+ goto thread_specific;
+ if (si.si_signo == SIGCHLD || si.si_signo == SIGIO || si.si_signo == SIGCONT || si.si_signo == SIGWINCH
+ || si.si_signo == SIGURG)
+ {
+ sigproc_printf ("default signal %d ignored", si.si_signo);
+ if (continue_now)
+ SetEvent (signal_arrived);
+ goto done;
+ }
+
+ if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
+ goto stop;
+
+ goto exit_sig;
+ }
+
+ if (handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("signal %d ignored", si.si_signo);
+ goto done;
+ }
+
+ if (handler == (void *) SIG_ERR)
+ goto exit_sig;
+
+ tls->set_siginfo (this);
+ goto dosig;
+
+stop:
+ /* Eat multiple attempts to STOP */
+ if (ISSTATE (myself, PID_STOPPED))
+ goto done;
+ handler = (void *) sig_handle_tty_stop;
+ thissig = dummy;
+
+dosig:
+ /* Dispatch to the appropriate function. */
+ sigproc_printf ("signal %d, about to call %p", si.si_signo, handler);
+ rc = setup_handler (si.si_signo, handler, thissig, tls);
+
+done:
+ if (continue_now)
+ SetEvent (sigCONT);
+ sigproc_printf ("returning %d", rc);
+ return rc;
+
+thread_specific:
+ tls->sig = si.si_signo;
+ tls->set_siginfo (this);
+ sigproc_printf ("releasing sigwait for thread");
+ SetEvent (tls->event);
+ goto done;
+
+exit_sig:
+ if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT)
+ {
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (hMainThread, &c);
+ tls->copy_context (&c);
+ si.si_signo |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
+ tls->signal_exit (si.si_signo); /* never returns */
+}
+
+/* Cover function to `do_exit' to handle exiting even in presence of more
+ exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
+ routines to run. */
+void
+_cygtls::signal_exit (int rc)
+{
+ if (hExeced)
+ {
+ sigproc_printf ("terminating captive process");
+ TerminateProcess (hExeced, sigExeced = rc);
+ }
+
+ signal_debugger (rc & 0x7f);
+ if ((rc & 0x80) && !try_to_debug ())
+ stackdump (thread_context.ebp, 1, 1);
+
+ lock_process until_exit (true);
+ if (hExeced || exit_state > ES_PROCESS_LOCKED)
+ myself.exit (rc);
+
+ /* Starve other threads in a vain attempt to stop them from doing something
+ stupid. */
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+
+ user_data->resourcelocks->Delete ();
+ user_data->resourcelocks->Init ();
+
+ sigproc_printf ("about to call do_exit (%x)", rc);
+ SetEvent (signal_arrived);
+ do_exit (rc);
+}
+
+void
+events_init ()
+{
+ mask_sync.init ("mask_sync");
+ windows_system_directory[0] = '\0';
+ GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
+ char *end = strchr (windows_system_directory, '\0');
+ if (end == windows_system_directory)
+ api_fatal ("can't find windows system directory");
+ if (end[-1] != '\\')
+ {
+ *end++ = '\\';
+ *end = '\0';
+ }
+ windows_system_directory_length = end - windows_system_directory;
+ debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
+ windows_system_directory, windows_system_directory_length);
+}
+
+void
+events_terminate ()
+{
+ exit_already = 1;
+}
+
+int
+_cygtls::call_signal_handler ()
+{
+ int this_sa_flags = 0;
+ /* Call signal handler. */
+ while (sig)
+ {
+ lock ();
+ this_sa_flags = sa_flags;
+ int thissig = sig;
+
+ pop ();
+ reset_signal_arrived ();
+ sigset_t this_oldmask = set_process_mask_delta ();
+ int this_errno = saved_errno;
+ sig = 0;
+ unlock (); // make sure synchronized
+ incyg = 0;
+ if (!(this_sa_flags & SA_SIGINFO))
+ {
+ void (*sigfunc) (int) = func;
+ sigfunc (thissig);
+ }
+ else
+ {
+ siginfo_t thissi = infodata;
+ void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) func;
+ /* no ucontext_t information provided yet */
+ sigact (thissig, &thissi, NULL);
+ }
+ incyg = 1;
+ set_signal_mask (this_oldmask, myself->getsigmask ());
+ if (this_errno >= 0)
+ set_errno (this_errno);
+ }
+
+ return this_sa_flags & SA_RESTART;
+}
+
+extern "C" void __stdcall
+reset_signal_arrived ()
+{
+ // NEEDED? WaitForSingleObject (signal_arrived, 10);
+ ResetEvent (signal_arrived);
+ sigproc_printf ("reset signal_arrived");
+ if (_my_tls.stackptr > _my_tls.stack)
+ debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]);
+}
+
+void
+_cygtls::copy_context (CONTEXT *c)
+{
+ memcpy (&thread_context, c, (&thread_context._internal - (unsigned char *) &thread_context));
+}
+
+void
+_cygtls::signal_debugger (int sig)
+{
+ if (isinitialized () && being_debugged ())
+ {
+ char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING " ffffffff ffffffff")];
+ __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %d %p %p", sig, thread_id, &thread_context);
+ OutputDebugString (sigmsg);
+ }
+}
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 4c8e431cbd7..505c8db9ec4 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -1,7 +1,7 @@
/* fhandler.cc. See console.cc for fhandler_console functions.
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005 Red Hat, Inc.
+ 2005, 2006, 2007 Red Hat, Inc.
This file is part of Cygwin.
@@ -365,7 +365,8 @@ fhandler_base::fhaccess (int flags)
if (is_fs_special ())
/* short circuit */;
- else if (has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK))
+ else if (has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK)
+ && !pc.isdir ())
goto eaccess_done;
else if (has_acls () && allow_ntsec)
{
@@ -431,6 +432,15 @@ fhandler_base::fhaccess (int flags)
eaccess_done:
set_errno (EACCES);
done:
+#ifndef FILE_READ_ONLY_VOLUME
+#define FILE_READ_ONLY_VOLUME 0x80000
+#endif
+ if (!res && (flags & W_OK) && get_device () == FH_FS
+ && (pc.fs_flags () & FILE_READ_ONLY_VOLUME))
+ {
+ set_errno (EROFS);
+ res = -1;
+ }
debug_printf ("returning %d", res);
return res;
}
@@ -483,9 +493,6 @@ fhandler_base::open_9x (int flags, mode_t mode)
if ((flags & O_EXCL) && (flags & O_CREAT))
creation_distribution = CREATE_NEW;
- if (flags & O_APPEND)
- append_mode (true);
-
/* These flags are host dependent. */
shared = wincap.shared ();
@@ -647,9 +654,6 @@ fhandler_base::open (int flags, mode_t mode)
if ((flags & O_EXCL) && (flags & O_CREAT))
create_disposition = FILE_CREATE;
- if (flags & O_APPEND)
- append_mode (true);
-
if (flags & O_CREAT && get_device () == FH_FS)
{
file_attributes = FILE_ATTRIBUTE_NORMAL;
@@ -817,8 +821,17 @@ fhandler_base::write (const void *ptr, size_t len)
{
int res;
- if (append_mode ())
- SetFilePointer (get_output_handle (), 0, 0, FILE_END);
+ if (get_flags () & O_APPEND)
+ {
+ LONG off_high = 0;
+ DWORD ret = SetFilePointer (get_output_handle (), 0, &off_high, FILE_END);
+ if (ret == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
+ {
+ debug_printf ("Seeking to EOF in append mode failed");
+ __seterrno ();
+ return -1;
+ }
+ }
else if (did_lseek ())
{
_off64_t actual_length, current_position;
@@ -1239,13 +1252,13 @@ fhandler_base::fstat (struct __stat64 *buf)
switch (get_device ())
{
case FH_PIPE:
- buf->st_mode = S_IFIFO | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+ buf->st_mode = S_IFIFO | S_IRUSR | S_IWUSR;
break;
case FH_PIPEW:
- buf->st_mode = S_IFIFO | STD_WBITS | S_IWGRP | S_IWOTH;
+ buf->st_mode = S_IFIFO | S_IWUSR;
break;
case FH_PIPER:
- buf->st_mode = S_IFIFO | STD_RBITS;
+ buf->st_mode = S_IFIFO | S_IRUSR;
break;
case FH_FULL:
buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
@@ -1258,8 +1271,9 @@ fhandler_base::fstat (struct __stat64 *buf)
buf->st_uid = geteuid32 ();
buf->st_gid = getegid32 ();
buf->st_nlink = 1;
- buf->st_blksize = S_BLKSIZE;
- time_as_timestruc_t (&buf->st_ctim);
+ buf->st_blksize = PREFERRED_IO_BLKSIZE;
+ buf->st_ctim.tv_sec = 1164931200L; /* Arbitrary value: 2006-12-01 */
+ buf->st_ctim.tv_nsec = 0L;
buf->st_atim = buf->st_mtim = buf->st_ctim;
return 0;
}
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 9aa991f57ef..afdaf0bca01 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1,7 +1,7 @@
/* fhandler.h
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006 Red Hat, Inc.
+ 2005, 2006, 2007 Red Hat, Inc.
This file is part of Cygwin.
@@ -25,6 +25,11 @@ details. */
both flags are set. */
#define O_NONBLOCK_MASK (O_NONBLOCK | OLD_O_NDELAY)
+/* It appears that 64K is the block size used for buffered I/O on NT.
+ Using this blocksize in read/write calls in the application results
+ in a much better performance than using smaller values. */
+#define PREFERRED_IO_BLKSIZE ((blksize_t) 65536)
+
extern const char *windows_device_names[];
extern struct __cygwin_perfile *perfile_table;
#define __fmode (*(user_data->fmode_ptr))
@@ -95,7 +100,6 @@ class fhandler_base
unsigned wbinset : 1; /* binary write mode explicitly set */
unsigned nohandle : 1; /* No handle associated with fhandler. */
unsigned uninterruptible_io : 1; /* Set if I/O should be uninterruptible. */
- unsigned append_mode : 1; /* always append */
unsigned did_lseek : 1; /* set when lseek is called as a flag that
_write should check if we've moved
beyond EOF, zero filling or making
@@ -109,7 +113,7 @@ class fhandler_base
public:
status_flags () :
rbinary (0), rbinset (0), wbinary (0), wbinset (0), nohandle (0),
- uninterruptible_io (0), append_mode (0), did_lseek (0),
+ uninterruptible_io (0), did_lseek (0),
query_open (no_query), close_on_exec (0), need_fork_fixup (0),
has_changed (0)
{}
@@ -187,7 +191,6 @@ class fhandler_base
IMPLEMENT_STATUS_FLAG (bool, rbinset)
IMPLEMENT_STATUS_FLAG (bool, nohandle)
IMPLEMENT_STATUS_FLAG (bool, uninterruptible_io)
- IMPLEMENT_STATUS_FLAG (bool, append_mode)
IMPLEMENT_STATUS_FLAG (bool, did_lseek)
IMPLEMENT_STATUS_FLAG (query_state, query_open)
IMPLEMENT_STATUS_FLAG (bool, close_on_exec)
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index d068114e097..82510c0bff8 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -1,7 +1,7 @@
/* fhandler_disk_file.cc
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006 Red Hat, Inc.
+ 2005, 2006, 2007 Red Hat, Inc.
This file is part of Cygwin.
@@ -428,7 +428,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
else
buf->st_ino = get_namehash ();
- buf->st_blksize = S_BLKSIZE;
+ buf->st_blksize = PREFERRED_IO_BLKSIZE;
if (nAllocSize >= 0LL)
/* A successful NtQueryInformationFile returns the allocation size
@@ -470,7 +470,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
{
/* If read-only attribute is set, modify ntsec return value */
if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
- && !pc.issymlink ())
+ && !pc.isdir () && !pc.issymlink ())
buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
if (buf->st_mode & S_IFMT)
@@ -489,7 +489,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
buf->st_mode |= STD_RBITS;
if (!::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
- && !pc.issymlink ())
+ && !pc.isdir () && !pc.issymlink ())
buf->st_mode |= STD_WBITS;
/* | S_IWGRP | S_IWOTH; we don't give write to group etc */
@@ -509,13 +509,14 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
if (pc.exec_state () == dont_know_if_executable)
{
DWORD cur, done;
+ LONG curhigh = 0;
char magic[3];
/* First retrieve current position, set to beginning
of file if not already there. */
- cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT);
- if (cur != INVALID_SET_FILE_POINTER
- && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN)
+ cur = SetFilePointer (get_handle (), 0, &curhigh, FILE_CURRENT);
+ if ((cur != INVALID_SET_FILE_POINTER || GetLastError () == NO_ERROR)
+ && ((!cur && !curhigh) || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN)
!= INVALID_SET_FILE_POINTER))
{
/* FIXME should we use /etc/magic ? */
@@ -526,7 +527,7 @@ fhandler_base::fstat_helper (struct __stat64 *buf,
pc.set_exec ();
buf->st_mode |= STD_XBITS;
}
- SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN);
+ SetFilePointer (get_handle (), cur, &curhigh, FILE_BEGIN);
}
}
}
diff --git a/winsup/cygwin/fhandler_floppy.cc b/winsup/cygwin/fhandler_floppy.cc
new file mode 100644
index 00000000000..bcd0c7df671
--- /dev/null
+++ b/winsup/cygwin/fhandler_floppy.cc
@@ -0,0 +1,536 @@
+/* fhandler_floppy.cc. See fhandler.h for a description of the
+ fhandler classes.
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004 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 <unistd.h>
+#include <winioctl.h>
+#include <asm/socket.h>
+#include <cygwin/rdevio.h>
+#include <cygwin/hdreg.h>
+#include <cygwin/fs.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include <ntdef.h>
+#include "ntdll.h"
+
+#define IS_EOM(err) ((err) == ERROR_INVALID_PARAMETER \
+ || (err) == ERROR_SEEK \
+ || (err) == ERROR_SECTOR_NOT_FOUND)
+
+/**********************************************************************/
+/* fhandler_dev_floppy */
+
+fhandler_dev_floppy::fhandler_dev_floppy ()
+ : fhandler_dev_raw (), status ()
+{
+}
+
+int
+fhandler_dev_floppy::get_drive_info (struct hd_geometry *geo)
+{
+ char dbuf[256];
+ char pbuf[256];
+
+ DISK_GEOMETRY *di = NULL;
+ PARTITION_INFORMATION_EX *pix = NULL;
+ PARTITION_INFORMATION *pi = NULL;
+ DWORD bytes_read = 0;
+
+ /* Always try using the new EX ioctls first (>= XP). If not available,
+ fall back to trying the old non-EX ioctls.
+ Unfortunately the EX ioctls are not implemented in the floppy driver. */
+ if (wincap.has_disk_ex_ioctls () && get_major () != DEV_FLOPPY_MAJOR)
+ {
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
+ dbuf, 256, &bytes_read, NULL))
+ __seterrno ();
+ else
+ {
+ di = &((DISK_GEOMETRY_EX *) dbuf)->Geometry;
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
+ pbuf, 256, &bytes_read, NULL))
+ __seterrno ();
+ else
+ pix = (PARTITION_INFORMATION_EX *) pbuf;
+ }
+ }
+ if (!di)
+ {
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ dbuf, 256, &bytes_read, NULL))
+ __seterrno ();
+ else
+ {
+ di = (DISK_GEOMETRY *) dbuf;
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_PARTITION_INFO, NULL, 0,
+ pbuf, 256, &bytes_read, NULL))
+ __seterrno ();
+ else
+ pi = (PARTITION_INFORMATION *) pbuf;
+ }
+ }
+ if (!di)
+ {
+ /* Up to Win2K, even IOCTL_DISK_GET_DRIVE_GEOMETRY fails when trying
+ it on CD or DVD drives. In that case fall back to requesting
+ simple file system information. */
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
+ FILE_FS_SIZE_INFORMATION ffsi;
+
+ status = NtQueryVolumeInformationFile (get_handle (), &io, &ffsi,
+ sizeof ffsi,
+ FileFsSizeInformation);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return -1;
+ }
+ debug_printf ("fsys geometry: (%D units)*(%u sec)*(%u bps)",
+ ffsi.TotalAllocationUnits.QuadPart,
+ ffsi.SectorsPerAllocationUnit,
+ ffsi.BytesPerSector);
+ bytes_per_sector = ffsi.BytesPerSector;
+ drive_size = ffsi.TotalAllocationUnits.QuadPart
+ * ffsi.SectorsPerAllocationUnit
+ * ffsi.BytesPerSector;
+ if (geo)
+ {
+ geo->heads = 1;
+ geo->sectors = ffsi.SectorsPerAllocationUnit;
+ geo->cylinders = ffsi.TotalAllocationUnits.LowPart;
+ geo->start = 0;
+ }
+ }
+ else
+ {
+ debug_printf ("disk geometry: (%D cyl)*(%u trk)*(%u sec)*(%u bps)",
+ di->Cylinders.QuadPart,
+ di->TracksPerCylinder,
+ di->SectorsPerTrack,
+ di->BytesPerSector);
+ bytes_per_sector = di->BytesPerSector;
+ if (pix)
+ {
+ debug_printf ("partition info: offset %D length %D",
+ pix->StartingOffset.QuadPart,
+ pix->PartitionLength.QuadPart);
+ drive_size = pix->PartitionLength.QuadPart;
+ }
+ else if (pi)
+ {
+ debug_printf ("partition info: offset %D length %D",
+ pi->StartingOffset.QuadPart,
+ pi->PartitionLength.QuadPart);
+ drive_size = pi->PartitionLength.QuadPart;
+ }
+ else
+ {
+ /* Getting the partition size by using the drive geometry information
+ looks wrong, but this is a historical necessity. NT4 didn't
+ maintain partition information for the whole drive (aka
+ "partition 0"), but returned ERROR_INVALID_HANDLE instead. That
+ got fixed in W2K. */
+ drive_size = di->Cylinders.QuadPart * di->TracksPerCylinder
+ * di->SectorsPerTrack * di->BytesPerSector;
+ }
+ if (geo)
+ {
+ geo->heads = di->TracksPerCylinder;
+ geo->sectors = di->SectorsPerTrack;
+ geo->cylinders = di->Cylinders.LowPart;
+ if (pix)
+ geo->start = pix->StartingOffset.QuadPart >> 9ULL;
+ else if (pi)
+ geo->start = pi->StartingOffset.QuadPart >> 9ULL;
+ else
+ geo->start = 0;
+ }
+ }
+ debug_printf ("drive size: %D", drive_size);
+
+ return 0;
+}
+
+/* Wrapper functions for ReadFile and WriteFile to simplify error handling. */
+BOOL
+fhandler_dev_floppy::read_file (void *buf, DWORD to_read, DWORD *read, int *err)
+{
+ BOOL ret;
+
+ *err = 0;
+ if (!(ret = ReadFile (get_handle (), buf, to_read, read, 0)))
+ *err = GetLastError ();
+ syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)",
+ ret, *err, get_handle (), buf, to_read, *read);
+ return ret;
+}
+
+BOOL
+fhandler_dev_floppy::write_file (const void *buf, DWORD to_write,
+ DWORD *written, int *err)
+{
+ BOOL ret;
+
+ *err = 0;
+ if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
+ *err = GetLastError ();
+ syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)",
+ ret, *err, get_handle (), buf, to_write, *written);
+ return ret;
+}
+
+int
+fhandler_dev_floppy::open (int flags, mode_t)
+{
+ /* The correct size of the buffer would be 512 bytes, which is the atomic
+ size, supported by WinNT. Unfortunately, the performance is worse than
+ access to file system on same device! Setting buffer size to a
+ relatively big value increases performance by means. The new ioctl call
+ with 'rdevio.h' header file supports changing this value.
+
+ As default buffer size, we're using some value which is a multiple of
+ the typical tar and cpio buffer sizes, Except O_DIRECT is set, in which
+ case we're not buffering at all. */
+ devbufsiz = (flags & O_DIRECT) ? 0L : 61440L;
+ int ret = fhandler_dev_raw::open (flags);
+
+ if (ret && get_drive_info (NULL))
+ {
+ close ();
+ return 0;
+ }
+
+ return ret;
+}
+
+int
+fhandler_dev_floppy::dup (fhandler_base *child)
+{
+ int ret = fhandler_dev_raw::dup (child);
+
+ if (!ret)
+ {
+ fhandler_dev_floppy *fhc = (fhandler_dev_floppy *) child;
+
+ fhc->drive_size = drive_size;
+ fhc->bytes_per_sector = bytes_per_sector;
+ fhc->eom_detected (eom_detected ());
+ }
+ return ret;
+}
+
+inline _off64_t
+fhandler_dev_floppy::get_current_position ()
+{
+ LARGE_INTEGER off = { QuadPart: 0LL };
+ off.LowPart = SetFilePointer (get_handle (), 0, &off.HighPart, FILE_CURRENT);
+ return off.QuadPart;
+}
+
+void
+fhandler_dev_floppy::raw_read (void *ptr, size_t& ulen)
+{
+ DWORD bytes_read = 0;
+ DWORD read2;
+ DWORD bytes_to_read;
+ int ret;
+ size_t len = ulen;
+ char *tgt;
+ char *p = (char *) ptr;
+
+ /* Checking a previous end of media */
+ if (eom_detected () && !lastblk_to_read ())
+ {
+ set_errno (ENOSPC);
+ goto err;
+ }
+
+ if (devbuf)
+ {
+ while (len > 0)
+ {
+ if (devbufstart < devbufend)
+ {
+ bytes_to_read = min (len, devbufend - devbufstart);
+ debug_printf ("read %d bytes from buffer (rest %d)",
+ bytes_to_read,
+ devbufend - devbufstart - bytes_to_read);
+ memcpy (p, devbuf + devbufstart, bytes_to_read);
+ len -= bytes_to_read;
+ p += bytes_to_read;
+ bytes_read += bytes_to_read;
+ devbufstart += bytes_to_read;
+
+ if (lastblk_to_read ())
+ {
+ lastblk_to_read (false);
+ break;
+ }
+ }
+ if (len > 0)
+ {
+ if (len >= devbufsiz)
+ {
+ bytes_to_read = (len / bytes_per_sector) * bytes_per_sector;
+ tgt = p;
+ }
+ else
+ {
+ tgt = devbuf;
+ bytes_to_read = devbufsiz;
+ }
+ _off64_t current_position = get_current_position ();
+ if (current_position + bytes_to_read >= drive_size)
+ bytes_to_read = drive_size - current_position;
+ if (!bytes_to_read)
+ {
+ eom_detected (true);
+ break;
+ }
+
+ debug_printf ("read %d bytes %s", bytes_to_read,
+ len < devbufsiz ? "into buffer" : "directly");
+ if (!read_file (tgt, bytes_to_read, &read2, &ret))
+ {
+ if (!IS_EOM (ret))
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ eom_detected (true);
+
+ if (!read2)
+ {
+ if (!bytes_read)
+ {
+ debug_printf ("return -1, set errno to ENOSPC");
+ set_errno (ENOSPC);
+ goto err;
+ }
+ break;
+ }
+ lastblk_to_read (true);
+ }
+ if (!read2)
+ break;
+ if (tgt == devbuf)
+ {
+ devbufstart = 0;
+ devbufend = read2;
+ }
+ else
+ {
+ len -= read2;
+ p += read2;
+ bytes_read += read2;
+ }
+ }
+ }
+ }
+ else if (!read_file (p, len, &bytes_read, &ret))
+ {
+ if (!IS_EOM (ret))
+ {
+ __seterrno ();
+ goto err;
+ }
+ if (bytes_read)
+ eom_detected (true);
+ else
+ {
+ debug_printf ("return -1, set errno to ENOSPC");
+ set_errno (ENOSPC);
+ goto err;
+ }
+ }
+
+ ulen = (size_t) bytes_read;
+ return;
+
+err:
+ ulen = (size_t) -1;
+}
+
+int
+fhandler_dev_floppy::raw_write (const void *ptr, size_t len)
+{
+ DWORD bytes_written = 0;
+ char *p = (char *) ptr;
+ int ret;
+
+ /* Checking a previous end of media on tape */
+ if (eom_detected ())
+ {
+ set_errno (ENOSPC);
+ return -1;
+ }
+
+ /* Invalidate buffer. */
+ devbufstart = devbufend = 0;
+
+ if (len > 0)
+ {
+ if (!write_file (p, len, &bytes_written, &ret))
+ {
+ if (!IS_EOM (ret))
+ {
+ __seterrno ();
+ return -1;
+ }
+ eom_detected (true);
+ if (!bytes_written)
+ {
+ set_errno (ENOSPC);
+ return -1;
+ }
+ }
+ }
+ return bytes_written;
+}
+
+_off64_t
+fhandler_dev_floppy::lseek (_off64_t offset, int whence)
+{
+ char buf[bytes_per_sector];
+ _off64_t lloffset = offset;
+ _off64_t current_pos = (_off64_t) -1;
+ LARGE_INTEGER sector_aligned_offset;
+ size_t bytes_left;
+
+ if (whence == SEEK_END)
+ {
+ lloffset += drive_size;
+ whence = SEEK_SET;
+ }
+ else if (whence == SEEK_CUR)
+ {
+ current_pos = get_current_position ();
+ lloffset += current_pos - (devbufend - devbufstart);
+ whence = SEEK_SET;
+ }
+
+ if (whence != SEEK_SET || lloffset < 0 || lloffset > drive_size)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* If new position is in buffered range, adjust buffer and return */
+ if (devbufstart < devbufend)
+ {
+ if (current_pos == (_off64_t) -1)
+ current_pos = get_current_position ();
+ if (current_pos - devbufend <= lloffset && lloffset <= current_pos)
+ {
+ devbufstart = devbufend - (current_pos - lloffset);
+ return lloffset;
+ }
+ }
+
+ sector_aligned_offset.QuadPart = (lloffset / bytes_per_sector)
+ * bytes_per_sector;
+ bytes_left = lloffset - sector_aligned_offset.QuadPart;
+
+ /* Invalidate buffer. */
+ devbufstart = devbufend = 0;
+
+ sector_aligned_offset.LowPart =
+ SetFilePointer (get_handle (),
+ sector_aligned_offset.LowPart,
+ &sector_aligned_offset.HighPart,
+ FILE_BEGIN);
+ if (sector_aligned_offset.LowPart == INVALID_SET_FILE_POINTER
+ && GetLastError ())
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ eom_detected (false);
+
+ if (bytes_left)
+ {
+ raw_read (buf, bytes_left);
+ if (bytes_left == (size_t) -1)
+ return -1;
+ }
+
+ return sector_aligned_offset.QuadPart + bytes_left;
+}
+
+int
+fhandler_dev_floppy::ioctl (unsigned int cmd, void *buf)
+{
+ DISK_GEOMETRY di;
+ DWORD bytes_read;
+ switch (cmd)
+ {
+ case HDIO_GETGEO:
+ {
+ debug_printf ("HDIO_GETGEO");
+ return get_drive_info ((struct hd_geometry *) buf);
+ }
+ case BLKGETSIZE:
+ case BLKGETSIZE64:
+ {
+ debug_printf ("BLKGETSIZE");
+ if (cmd == BLKGETSIZE)
+ *(long *)buf = drive_size >> 9UL;
+ else
+ *(_off64_t *)buf = drive_size;
+ return 0;
+ }
+ case BLKRRPART:
+ {
+ debug_printf ("BLKRRPART");
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_UPDATE_DRIVE_SIZE,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ get_drive_info (NULL);
+ return 0;
+ }
+ case BLKSSZGET:
+ {
+ debug_printf ("BLKSSZGET");
+ *(int *)buf = bytes_per_sector;
+ return 0;
+ }
+ case RDSETBLK:
+ /* Just check the restriction that blocksize must be a multiple
+ of the sector size of the underlying volume sector size,
+ then fall through to fhandler_dev_raw::ioctl. */
+ if (((struct rdop *) buf)->rd_parm % bytes_per_sector)
+ {
+ SetLastError (ERROR_INVALID_PARAMETER);
+ __seterrno ();
+ return -1;
+ }
+ /*FALLTHRU*/
+ default:
+ return fhandler_dev_raw::ioctl (cmd, buf);
+ }
+}
+
diff --git a/winsup/cygwin/fhandler_mailslot.cc b/winsup/cygwin/fhandler_mailslot.cc
new file mode 100644
index 00000000000..133df041497
--- /dev/null
+++ b/winsup/cygwin/fhandler_mailslot.cc
@@ -0,0 +1,160 @@
+/* fhandler_mailslot.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2005, 2007 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 <sys/termios.h>
+
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+
+/**********************************************************************/
+/* fhandler_mailslot */
+
+fhandler_mailslot::fhandler_mailslot ()
+ : fhandler_base ()
+{
+}
+
+int __stdcall
+fhandler_mailslot::fstat (struct __stat64 *buf)
+{
+ debug_printf ("here");
+
+ fhandler_base::fstat (buf);
+ if (is_auto_device ())
+ {
+ buf->st_mode = S_IFCHR | S_IRUSR | S_IWUSR;
+ buf->st_uid = geteuid32 ();
+ buf->st_gid = getegid32 ();
+ buf->st_nlink = 1;
+ buf->st_blksize = PREFERRED_IO_BLKSIZE;
+ time_as_timestruc_t (&buf->st_ctim);
+ buf->st_atim = buf->st_mtim = buf->st_ctim;
+ }
+ return 0;
+}
+
+int
+fhandler_mailslot::open (int flags, mode_t mode)
+{
+ int res = 0;
+ HANDLE x;
+
+ switch (flags & O_ACCMODE)
+ {
+ case O_RDONLY: /* Server */
+ x = CreateMailslot (get_win32_name (),
+ 0, /* Any message size */
+ (flags & O_NONBLOCK) ? 0 : MAILSLOT_WAIT_FOREVER,
+ &sec_none);
+ if (x == INVALID_HANDLE_VALUE)
+ {
+ /* FIXME: It's not possible to open the read side of an existing
+ mailslot using CreateFile. You'll get a handle, but using it
+ in ReadFile returns ERROR_INVALID_PARAMETER. On the other
+ hand, CreateMailslot returns with ERROR_ALREADY_EXISTS if the
+ mailslot has been created already.
+ So this is an exclusive open for now. *Duplicating* read side
+ handles works, though, so it might be an option to duplicate
+ the handle from the first process to the current process for
+ opening the mailslot. */
+#if 0
+ if (GetLastError () != ERROR_ALREADY_EXISTS)
+ {
+ __seterrno ();
+ break;
+ }
+ x = CreateFile (get_win32_name (), GENERIC_READ, wincap.shared (),
+ &sec_none, OPEN_EXISTING, 0, 0);
+#endif
+ if (x == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ break;
+ }
+ }
+ set_io_handle (x);
+ set_flags (flags, O_BINARY);
+ res = 1;
+ set_open_status ();
+ break;
+ case O_WRONLY: /* Client */
+ /* The client is the DLL exclusively. Don't allow opening from
+ application code. */
+ extern fhandler_mailslot *dev_kmsg;
+ if (this != dev_kmsg)
+ {
+ set_errno (EPERM); /* As on Linux. */
+ break;
+ }
+ x = CreateFile (get_win32_name (), GENERIC_WRITE, wincap.shared (),
+ &sec_none, OPEN_EXISTING, 0, 0);
+ if (x == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ break;
+ }
+ set_io_handle (x);
+ set_flags (flags, O_BINARY);
+ res = 1;
+ set_open_status ();
+ break;
+ default:
+ set_errno (EINVAL);
+ break;
+ }
+ return res;
+}
+
+int
+fhandler_mailslot::write (const void *ptr, size_t len)
+{
+ /* Check for 425/426 byte weirdness */
+ if (len == 425 || len == 426)
+ {
+ char buf[427];
+ buf[425] = buf[426] = '\0';
+ memcpy (buf, ptr, len);
+ return raw_write (buf, 427);
+ }
+ return raw_write (ptr, len);
+}
+
+int
+fhandler_mailslot::ioctl (unsigned int cmd, void *buf)
+{
+ int res = -1;
+
+ switch (cmd)
+ {
+ case FIONBIO:
+ {
+ DWORD timeout = buf ? 0 : MAILSLOT_WAIT_FOREVER;
+ if (!SetMailslotInfo (get_handle (), timeout))
+ {
+ debug_printf ("SetMailslotInfo (%u): %E", timeout);
+ break;
+ }
+ }
+ /*FALLTHRU*/
+ default:
+ res = fhandler_base::ioctl (cmd, buf);
+ break;
+ }
+ return res;
+}
diff --git a/winsup/cygwin/fhandler_raw.cc b/winsup/cygwin/fhandler_raw.cc
new file mode 100644
index 00000000000..9478ee78864
--- /dev/null
+++ b/winsup/cygwin/fhandler_raw.cc
@@ -0,0 +1,215 @@
+/* fhandler_raw.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 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 <unistd.h>
+
+#include <cygwin/rdevio.h>
+#include <sys/mtio.h>
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+
+/**********************************************************************/
+/* fhandler_dev_raw */
+
+fhandler_dev_raw::fhandler_dev_raw ()
+ : fhandler_base (), status ()
+{
+ need_fork_fixup (true);
+}
+
+fhandler_dev_raw::~fhandler_dev_raw ()
+{
+ if (devbufsiz > 1L)
+ delete [] devbuf;
+}
+
+int __stdcall
+fhandler_dev_raw::fstat (struct __stat64 *buf)
+{
+ debug_printf ("here");
+
+ fhandler_base::fstat (buf);
+ if (is_auto_device ())
+ {
+ if (get_major () == DEV_TAPE_MAJOR)
+ buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+ else
+ buf->st_mode = S_IFBLK | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+
+ buf->st_uid = geteuid32 ();
+ buf->st_gid = getegid32 ();
+ buf->st_nlink = 1;
+ buf->st_blksize = PREFERRED_IO_BLKSIZE;
+ time_as_timestruc_t (&buf->st_ctim);
+ buf->st_atim = buf->st_mtim = buf->st_ctim;
+ }
+ return 0;
+}
+
+int
+fhandler_dev_raw::open (int flags, mode_t)
+{
+ if (!wincap.has_raw_devices ())
+ {
+ set_errno (ENOENT);
+ debug_printf ("%s is accessible under NT/W2K only", get_win32_name ());
+ return 0;
+ }
+
+ /* Check for illegal flags. */
+ if (get_major () != DEV_TAPE_MAJOR && (flags & (O_APPEND | O_EXCL)))
+ {
+ set_errno (EINVAL);
+ return 0;
+ }
+
+ /* Always open a raw device existing and binary. */
+ flags &= ~(O_CREAT | O_TRUNC);
+ flags |= O_BINARY;
+
+ /* Write-only doesn't work well with raw devices */
+ if ((flags & O_ACCMODE) == O_WRONLY)
+ flags = ((flags & ~O_WRONLY) | O_RDWR);
+
+ int res = fhandler_base::open (flags, 0);
+ if (res && devbufsiz > 1L)
+ devbuf = new char [devbufsiz];
+
+ return res;
+}
+
+int
+fhandler_dev_raw::dup (fhandler_base *child)
+{
+ int ret = fhandler_base::dup (child);
+
+ if (!ret)
+ {
+ fhandler_dev_raw *fhc = (fhandler_dev_raw *) child;
+
+ fhc->devbufsiz = devbufsiz;
+ if (devbufsiz > 1L)
+ fhc->devbuf = new char [devbufsiz];
+ fhc->devbufstart = 0;
+ fhc->devbufend = 0;
+ fhc->lastblk_to_read (false);
+ }
+ return ret;
+}
+
+void
+fhandler_dev_raw::fixup_after_fork (HANDLE)
+{
+ devbufstart = 0;
+ devbufend = 0;
+ lastblk_to_read (false);
+}
+
+void
+fhandler_dev_raw::fixup_after_exec ()
+{
+ if (!close_on_exec ())
+ {
+ if (devbufsiz > 1L)
+ devbuf = new char [devbufsiz];
+ devbufstart = 0;
+ devbufend = 0;
+ lastblk_to_read (false);
+ }
+}
+
+int
+fhandler_dev_raw::ioctl (unsigned int cmd, void *buf)
+{
+ int ret = NO_ERROR;
+
+ if (cmd == RDIOCDOP)
+ {
+ struct rdop *op = (struct rdop *) buf;
+
+ if (!op)
+ ret = ERROR_INVALID_PARAMETER;
+ else
+ switch (op->rd_op)
+ {
+ case RDSETBLK:
+ if (get_major () == DEV_TAPE_MAJOR)
+ {
+ struct mtop mop;
+
+ mop.mt_op = MTSETBLK;
+ mop.mt_count = op->rd_parm;
+ ret = ioctl (MTIOCTOP, &mop);
+ }
+ else if ((devbuf && ((op->rd_parm <= 1 && (devbufend - devbufstart))
+ || op->rd_parm < devbufend - devbufstart))
+ || (op->rd_parm > 1 && (op->rd_parm % 512))
+ || (get_flags () & O_DIRECT))
+ /* The conditions for a *valid* parameter are these:
+ - If there's still data in the current buffer, it must
+ fit in the new buffer.
+ - The new size is either 0 or 1, both indicating unbufferd
+ I/O, or the new buffersize must be a multiple of 512.
+ - In the O_DIRECT case, the whole request is invalid. */
+ ret = ERROR_INVALID_PARAMETER;
+ else if (!devbuf || op->rd_parm != devbufsiz)
+ {
+ char *buf = NULL;
+ if (op->rd_parm > 1L)
+ buf = new char [op->rd_parm];
+ if (buf && devbufsiz > 1L)
+ {
+ memcpy (buf, devbuf + devbufstart, devbufend - devbufstart);
+ devbufend -= devbufstart;
+ }
+ else
+ devbufend = 0;
+
+ if (devbufsiz > 1L)
+ delete [] devbuf;
+
+ devbufstart = 0;
+ devbuf = buf;
+ devbufsiz = op->rd_parm ?: 1L;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (cmd == RDIOCGET)
+ {
+ struct rdget *get = (struct rdget *) buf;
+
+ if (!get)
+ ret = ERROR_INVALID_PARAMETER;
+ else
+ get->bufsiz = devbufsiz;
+ }
+ else
+ return fhandler_base::ioctl (cmd, buf);
+
+ if (ret != NO_ERROR)
+ {
+ SetLastError (ret);
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
diff --git a/winsup/cygwin/fhandler_serial.cc b/winsup/cygwin/fhandler_serial.cc
index 7378b3c4648..c7a905f700b 100644
--- a/winsup/cygwin/fhandler_serial.cc
+++ b/winsup/cygwin/fhandler_serial.cc
@@ -635,9 +635,45 @@ fhandler_serial::tcsetattr (int action, const struct termios *t)
case B115200:
state.BaudRate = CBR_115200;
break;
+ case B128000:
+ state.BaudRate = CBR_128000;
+ break;
case B230400:
state.BaudRate = 230400 /* CBR_230400 - not defined */;
break;
+ case B256000:
+ state.BaudRate = CBR_256000;
+ break;
+ case B460800:
+ state.BaudRate = 460800 /* CBR_460800 - not defined */;
+ break;
+ case B500000:
+ state.BaudRate = 500000 /* CBR_500000 - not defined */;
+ break;
+ case B576000:
+ state.BaudRate = 576000 /* CBR_576000 - not defined */;
+ break;
+ case B921600:
+ state.BaudRate = 921600 /* CBR_921600 - not defined */;
+ break;
+ case B1000000:
+ state.BaudRate = 1000000 /* CBR_1000000 - not defined */;
+ break;
+ case B1152000:
+ state.BaudRate = 1152000 /* CBR_1152000 - not defined */;
+ break;
+ case B1500000:
+ state.BaudRate = 1500000 /* CBR_1500000 - not defined */;
+ break;
+ case B2000000:
+ state.BaudRate = 2000000 /* CBR_2000000 - not defined */;
+ break;
+ case B2500000:
+ state.BaudRate = 2500000 /* CBR_2500000 - not defined */;
+ break;
+ case B3000000:
+ state.BaudRate = 3000000 /* CBR_3000000 - not defined */;
+ break;
default:
/* Unsupported baud rate! */
termios_printf ("Invalid t->c_ospeed %d", t->c_ospeed);
@@ -942,9 +978,45 @@ fhandler_serial::tcgetattr (struct termios *t)
case CBR_115200:
t->c_ospeed = t->c_ispeed = B115200;
break;
+ case CBR_128000:
+ t->c_ospeed = t->c_ispeed = B128000;
+ break;
case 230400: /* CBR_230400 - not defined */
t->c_ospeed = t->c_ispeed = B230400;
break;
+ case CBR_256000:
+ t->c_ospeed = t->c_ispeed = B256000;
+ break;
+ case 460800: /* CBR_460000 - not defined */
+ t->c_ospeed = t->c_ispeed = B460800;
+ break;
+ case 500000: /* CBR_500000 - not defined */
+ t->c_ospeed = t->c_ispeed = B500000;
+ break;
+ case 576000: /* CBR_576000 - not defined */
+ t->c_ospeed = t->c_ispeed = B576000;
+ break;
+ case 921600: /* CBR_921600 - not defined */
+ t->c_ospeed = t->c_ispeed = B921600;
+ break;
+ case 1000000: /* CBR_1000000 - not defined */
+ t->c_ospeed = t->c_ispeed = B1000000;
+ break;
+ case 1152000: /* CBR_1152000 - not defined */
+ t->c_ospeed = t->c_ispeed = B1152000;
+ break;
+ case 1500000: /* CBR_1500000 - not defined */
+ t->c_ospeed = t->c_ispeed = B1500000;
+ break;
+ case 2000000: /* CBR_2000000 - not defined */
+ t->c_ospeed = t->c_ispeed = B2000000;
+ break;
+ case 2500000: /* CBR_2500000 - not defined */
+ t->c_ospeed = t->c_ispeed = B2500000;
+ break;
+ case 3000000: /* CBR_3000000 - not defined */
+ t->c_ospeed = t->c_ispeed = B3000000;
+ break;
default:
/* Unsupported baud rate! */
termios_printf ("Invalid baud rate %d", state.BaudRate);
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
new file mode 100644
index 00000000000..db4d55040ce
--- /dev/null
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -0,0 +1,1694 @@
+/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2000, 2001, 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. */
+
+/* #define DEBUG_NEST_ON 1 */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <asm/byteorder.h>
+
+#include <stdlib.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "cygwin/version.h"
+#include "perprocess.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "cygthread.h"
+#include "select.h"
+#include "wininfo.h"
+#include <unistd.h>
+#include <sys/acl.h>
+#include "cygtls.h"
+
+#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT)
+
+extern bool fdsock (cygheap_fdmanip& fd, const device *, SOCKET soc);
+extern "C" {
+int sscanf (const char *, const char *, ...);
+} /* End of "C" section */
+
+fhandler_dev_random* entropy_source;
+
+static inline mode_t
+adjust_socket_file_mode (mode_t mode)
+{
+ /* Kludge: Don't allow to remove read bit on socket files for
+ user/group/other, if the accompanying write bit is set. It would
+ be nice to have exact permissions on a socket file, but it's
+ necessary that somebody able to access the socket can always read
+ the contents of the socket file to avoid spurious "permission
+ denied" messages. */
+ return mode | ((mode & (S_IWUSR | S_IWGRP | S_IWOTH)) << 1);
+}
+
+/* cygwin internal: map sockaddr into internet domain address */
+static int
+get_inet_addr (const struct sockaddr *in, int inlen,
+ struct sockaddr_in *out, int *outlen,
+ int *type = NULL, int *secret = NULL)
+{
+ int secret_buf [4];
+ int* secret_ptr = (secret ? : secret_buf);
+
+ if (in->sa_family == AF_INET)
+ {
+ *out = * (struct sockaddr_in *)in;
+ *outlen = inlen;
+ return 1;
+ }
+ else if (in->sa_family == AF_LOCAL)
+ {
+ path_conv pc (in->sa_data, PC_SYM_FOLLOW);
+ if (pc.error)
+ {
+ set_errno (pc.error);
+ return 0;
+ }
+ if (!pc.exists ())
+ {
+ set_errno (ENOENT);
+ return 0;
+ }
+ if (!pc.issocket ())
+ {
+ set_errno (EBADF);
+ return 0;
+ }
+ HANDLE fh = CreateFile (pc, GENERIC_READ, wincap.shared (), &sec_none,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fh == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return 0;
+ }
+ int ret = 0;
+ DWORD len = 0;
+ char buf[128];
+ memset (buf, 0, sizeof buf);
+ if (ReadFile (fh, buf, 128, &len, 0))
+ {
+ struct sockaddr_in sin;
+ char ctype;
+ sin.sin_family = AF_INET;
+ sscanf (buf + strlen (SOCKET_COOKIE), "%hu %c %08x-%08x-%08x-%08x",
+ &sin.sin_port,
+ &ctype,
+ secret_ptr, secret_ptr + 1, secret_ptr + 2, secret_ptr + 3);
+ sin.sin_port = htons (sin.sin_port);
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ *out = sin;
+ *outlen = sizeof sin;
+ if (type)
+ *type = (ctype == 's' ? SOCK_STREAM :
+ ctype == 'd' ? SOCK_DGRAM
+ : 0);
+ ret = 1;
+ }
+ else
+ __seterrno ();
+ CloseHandle (fh);
+ return ret;
+ }
+ else
+ {
+ set_errno (EAFNOSUPPORT);
+ return 0;
+ }
+}
+
+/**********************************************************************/
+/* fhandler_socket */
+
+fhandler_socket::fhandler_socket () :
+ fhandler_base (),
+ sun_path (NULL),
+ status ()
+{
+ need_fork_fixup (true);
+ prot_info_ptr = (LPWSAPROTOCOL_INFOA) cmalloc (HEAP_BUF,
+ sizeof (WSAPROTOCOL_INFOA));
+#if 0
+ if (pc.is_fs_special ())
+ {
+ fhandler_socket * fhs = (fhandler_socket *) fh;
+ fhs->set_addr_family (AF_LOCAL);
+ fhs->set_sun_path (posix_path);
+ }
+#endif
+}
+
+fhandler_socket::~fhandler_socket ()
+{
+ if (prot_info_ptr)
+ cfree (prot_info_ptr);
+ if (sun_path)
+ cfree (sun_path);
+}
+
+char *
+fhandler_socket::get_proc_fd_name (char *buf)
+{
+ __small_sprintf (buf, "socket:[%d]", get_socket ());
+ return buf;
+}
+
+int
+fhandler_socket::open (int flags, mode_t mode)
+{
+ set_errno (ENXIO);
+ return 0;
+}
+
+void
+fhandler_socket::af_local_set_sockpair_cred ()
+{
+ sec_pid = sec_peer_pid = getpid ();
+ sec_uid = sec_peer_uid = geteuid32 ();
+ sec_gid = sec_peer_gid = getegid32 ();
+}
+
+void
+fhandler_socket::af_local_setblocking (bool &async, bool &nonblocking)
+{
+ async = async_io ();
+ nonblocking = is_nonblocking ();
+ if (async || nonblocking)
+ WSAAsyncSelect (get_socket (), winmsg, 0, 0);
+ unsigned long p = 0;
+ ioctlsocket (get_socket (), FIONBIO, &p);
+ set_nonblocking (false);
+ async_io (false);
+}
+
+void
+fhandler_socket::af_local_unsetblocking (bool async, bool nonblocking)
+{
+ if (nonblocking)
+ {
+ unsigned long p = 1;
+ ioctlsocket (get_socket (), FIONBIO, &p);
+ set_nonblocking (true);
+ }
+ if (async)
+ {
+ WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK);
+ async_io (true);
+ }
+}
+
+bool
+fhandler_socket::af_local_recv_secret ()
+{
+ int out[4] = { 0, 0, 0, 0 };
+ int rest = sizeof out;
+ char *ptr = (char *) out;
+ while (rest > 0)
+ {
+ int ret = recvfrom (ptr, rest, 0, NULL, NULL);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ {
+ debug_printf ("Received af_local secret: %08x-%08x-%08x-%08x",
+ out[0], out[1], out[2], out[3]);
+ if (out[0] != connect_secret[0] || out[1] != connect_secret[1]
+ || out[2] != connect_secret[2] || out[3] != connect_secret[3])
+ {
+ debug_printf ("Receiving af_local secret mismatch");
+ return false;
+ }
+ }
+ else
+ debug_printf ("Receiving af_local secret failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_send_secret ()
+{
+ int rest = sizeof connect_secret;
+ char *ptr = (char *) connect_secret;
+ while (rest > 0)
+ {
+ int ret = sendto (ptr, rest, 0, NULL, 0);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ debug_printf ("Sending af_local secret %s", rest == 0 ? "succeeded"
+ : "failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_recv_cred ()
+{
+ struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 };
+ int rest = sizeof out;
+ char *ptr = (char *) &out;
+ while (rest > 0)
+ {
+ int ret = recvfrom (ptr, rest, 0, NULL, NULL);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ {
+ debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d",
+ out.pid, out.uid, out.gid);
+ sec_peer_pid = out.pid;
+ sec_peer_uid = out.uid;
+ sec_peer_gid = out.gid;
+ }
+ else
+ debug_printf ("Receiving eid credentials failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_send_cred ()
+{
+ struct ucred in = { sec_pid, sec_uid, sec_gid };
+ int rest = sizeof in;
+ char *ptr = (char *) &in;
+ while (rest > 0)
+ {
+ int ret = sendto (ptr, rest, 0, NULL, 0);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ debug_printf ("Sending eid credentials succeeded");
+ else
+ debug_printf ("Sending eid credentials failed");
+ return rest == 0;
+}
+
+int
+fhandler_socket::af_local_connect ()
+{
+ /* This keeps the test out of select. */
+ if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM)
+ return 0;
+
+ debug_printf ("af_local_connect called");
+ bool orig_async_io, orig_is_nonblocking;
+ af_local_setblocking (orig_async_io, orig_is_nonblocking);
+ if (!af_local_send_secret () || !af_local_recv_secret ()
+ || !af_local_send_cred () || !af_local_recv_cred ())
+ {
+ debug_printf ("accept from unauthorized server");
+ ::shutdown (get_socket (), SD_BOTH);
+ WSASetLastError (WSAECONNREFUSED);
+ return -1;
+ }
+ af_local_unsetblocking (orig_async_io, orig_is_nonblocking);
+ return 0;
+}
+
+int
+fhandler_socket::af_local_accept ()
+{
+ debug_printf ("af_local_accept called");
+ bool orig_async_io, orig_is_nonblocking;
+ af_local_setblocking (orig_async_io, orig_is_nonblocking);
+ if (!af_local_recv_secret () || !af_local_send_secret ()
+ || !af_local_recv_cred () || !af_local_send_cred ())
+ {
+ debug_printf ("connect from unauthorized client");
+ ::shutdown (get_socket (), SD_BOTH);
+ ::closesocket (get_socket ());
+ WSASetLastError (WSAECONNABORTED);
+ return -1;
+ }
+ af_local_unsetblocking (orig_async_io, orig_is_nonblocking);
+ return 0;
+}
+
+void
+fhandler_socket::af_local_set_cred ()
+{
+ sec_pid = getpid ();
+ sec_uid = geteuid32 ();
+ sec_gid = getegid32 ();
+ sec_peer_pid = (pid_t) 0;
+ sec_peer_uid = (__uid32_t) -1;
+ sec_peer_gid = (__gid32_t) -1;
+}
+
+void
+fhandler_socket::af_local_copy (fhandler_socket *sock)
+{
+ sock->connect_secret[0] = connect_secret[0];
+ sock->connect_secret[1] = connect_secret[1];
+ sock->connect_secret[2] = connect_secret[2];
+ sock->connect_secret[3] = connect_secret[3];
+ sock->sec_pid = sec_pid;
+ sock->sec_uid = sec_uid;
+ sock->sec_gid = sec_gid;
+ sock->sec_peer_pid = sec_peer_pid;
+ sock->sec_peer_uid = sec_peer_uid;
+ sock->sec_peer_gid = sec_peer_gid;
+}
+
+void
+fhandler_socket::af_local_set_secret (char *buf)
+{
+ if (!entropy_source)
+ {
+ void *buf = malloc (sizeof (fhandler_dev_random));
+ entropy_source = new (buf) fhandler_dev_random ();
+ entropy_source->dev () = *urandom_dev;
+ }
+ if (entropy_source &&
+ !entropy_source->open (O_RDONLY))
+ {
+ delete entropy_source;
+ entropy_source = NULL;
+ }
+ if (entropy_source)
+ {
+ size_t len = sizeof (connect_secret);
+ entropy_source->read (connect_secret, len);
+ if (len != sizeof (connect_secret))
+ bzero ((char*) connect_secret, sizeof (connect_secret));
+ }
+ __small_sprintf (buf, "%08x-%08x-%08x-%08x",
+ connect_secret [0], connect_secret [1],
+ connect_secret [2], connect_secret [3]);
+}
+
+void
+fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id)
+{
+ if (!WSADuplicateSocketA (get_socket (), win_proc_id, prot_info_ptr))
+ debug_printf ("WSADuplicateSocket went fine, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ else
+ {
+ debug_printf ("WSADuplicateSocket error, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ set_winsock_errno ();
+ }
+}
+
+void
+fhandler_socket::fixup_after_fork (HANDLE parent)
+{
+ SOCKET new_sock;
+
+ debug_printf ("WSASocket begin, dwServiceFlags1=%d",
+ prot_info_ptr->dwServiceFlags1);
+
+ if ((new_sock = WSASocketA (FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ prot_info_ptr, 0, 0)) == INVALID_SOCKET)
+ {
+ debug_printf ("WSASocket error");
+ set_io_handle ((HANDLE)INVALID_SOCKET);
+ set_winsock_errno ();
+ }
+ else
+ {
+ debug_printf ("WSASocket went fine new_sock %p, old_sock %p", new_sock, get_socket ());
+
+ /* Go figure! Even though the original socket was not inheritable,
+ the duplicated socket is inheritable again. This can lead to all
+ sorts of trouble, apparently. Note that there's no way to prevent
+ this on 9x, not even by trying to reset socket inheritance using
+ DuplicateHandle and closing the original socket. */
+ if (wincap.has_set_handle_information ())
+ SetHandleInformation ((HANDLE) new_sock, HANDLE_FLAG_INHERIT, 0);
+
+ set_io_handle ((HANDLE) new_sock);
+ }
+}
+
+void
+fhandler_socket::fixup_after_exec ()
+{
+ if (!close_on_exec ())
+ fixup_after_fork (NULL);
+}
+
+int
+fhandler_socket::dup (fhandler_base *child)
+{
+ HANDLE nh;
+
+ debug_printf ("here");
+ fhandler_socket *fhs = (fhandler_socket *) child;
+ fhs->addr_family = addr_family;
+ fhs->set_socket_type (get_socket_type ());
+ if (get_addr_family () == AF_LOCAL)
+ {
+ fhs->set_sun_path (get_sun_path ());
+ if (get_socket_type () == SOCK_STREAM)
+ {
+ fhs->sec_pid = sec_pid;
+ fhs->sec_uid = sec_uid;
+ fhs->sec_gid = sec_gid;
+ fhs->sec_peer_pid = sec_peer_pid;
+ fhs->sec_peer_uid = sec_peer_uid;
+ fhs->sec_peer_gid = sec_peer_gid;
+ }
+ }
+ fhs->connect_state (connect_state ());
+
+ /* Since WSADuplicateSocket() fails on NT systems when the process
+ is currently impersonating a non-privileged account, we revert
+ to the original account before calling WSADuplicateSocket() and
+ switch back afterwards as it's also in fork().
+ If WSADuplicateSocket() still fails for some reason, we fall back
+ to DuplicateHandle(). */
+ WSASetLastError (0);
+ cygheap->user.deimpersonate ();
+ fhs->set_io_handle (get_io_handle ());
+ fhs->fixup_before_fork_exec (GetCurrentProcessId ());
+ cygheap->user.reimpersonate ();
+ if (!WSAGetLastError ())
+ {
+ fhs->fixup_after_fork (hMainProc);
+ if (fhs->get_io_handle() != (HANDLE) INVALID_SOCKET)
+ {
+ cygheap->fdtab.inc_need_fixup_before ();
+ return 0;
+ }
+ }
+ debug_printf ("WSADuplicateSocket failed, trying DuplicateHandle");
+
+ /* We don't call fhandler_base::dup here since that requires
+ having winsock called from fhandler_base and it creates only
+ inheritable sockets which is wrong for winsock2. */
+
+ if (!DuplicateHandle (hMainProc, get_io_handle (), hMainProc, &nh, 0,
+ FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ());
+ __seterrno ();
+ return -1;
+ }
+ VerifyHandle (nh);
+ fhs->set_io_handle (nh);
+ cygheap->fdtab.inc_need_fixup_before ();
+ return 0;
+}
+
+int __stdcall
+fhandler_socket::fstat (struct __stat64 *buf)
+{
+ int res;
+ if (get_device () == FH_UNIX)
+ {
+ res = fhandler_base::fstat_fs (buf);
+ if (!res)
+ {
+ buf->st_mode = (buf->st_mode & ~S_IFMT) | S_IFSOCK;
+ }
+ }
+ else
+ {
+ res = fhandler_base::fstat (buf);
+ if (!res)
+ {
+ buf->st_dev = 0;
+ buf->st_ino = (__ino64_t) ((DWORD) get_handle ());
+ buf->st_mode = S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ }
+ return res;
+}
+
+int
+fhandler_socket::fchmod (mode_t mode)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ fh.get_device () = FH_FS;
+ int ret = fh.fchmod (adjust_socket_file_mode (mode));
+ SetFileAttributes (pc, GetFileAttributes (pc) | FILE_ATTRIBUTE_SYSTEM);
+ return ret;
+ }
+ return 0;
+}
+
+int
+fhandler_socket::fchown (__uid32_t uid, __gid32_t gid)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.fchown (uid, gid);
+ }
+ return 0;
+}
+
+int
+fhandler_socket::facl (int cmd, int nentries, __aclent32_t *aclbufp)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.facl (cmd, nentries, aclbufp);
+ }
+ return fhandler_base::facl (cmd, nentries, aclbufp);
+}
+
+int
+fhandler_socket::link (const char *newpath)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.link (newpath);
+ }
+ return fhandler_base::link (newpath);
+}
+
+static inline bool
+address_in_use (struct sockaddr_in *addr)
+{
+ PMIB_TCPTABLE tab;
+ PMIB_TCPROW entry;
+ DWORD size = 0, i;
+
+ if (GetTcpTable (NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ tab = (PMIB_TCPTABLE) alloca (size);
+ if (!GetTcpTable (tab, &size, FALSE))
+ {
+ for (i = tab->dwNumEntries, entry = tab->table; i > 0; --i, ++entry)
+ if (entry->dwLocalAddr == addr->sin_addr.s_addr
+ && entry->dwLocalPort == addr->sin_port
+ && entry->dwState >= MIB_TCP_STATE_LISTEN
+ && entry->dwState <= MIB_TCP_STATE_LAST_ACK)
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+fhandler_socket::bind (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+
+ if (name->sa_family == AF_LOCAL)
+ {
+#define un_addr ((struct sockaddr_un *) name)
+ struct sockaddr_in sin;
+ int len = sizeof sin;
+
+ if (strlen (un_addr->sun_path) >= UNIX_PATH_LEN)
+ {
+ set_errno (ENAMETOOLONG);
+ goto out;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (::bind (get_socket (), (sockaddr *) &sin, len))
+ {
+ syscall_printf ("AF_LOCAL: bind failed");
+ set_winsock_errno ();
+ goto out;
+ }
+ if (::getsockname (get_socket (), (sockaddr *) &sin, &len))
+ {
+ syscall_printf ("AF_LOCAL: getsockname failed");
+ set_winsock_errno ();
+ goto out;
+ }
+
+ sin.sin_port = ntohs (sin.sin_port);
+ debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port);
+
+ path_conv pc (un_addr->sun_path, PC_SYM_FOLLOW);
+ if (pc.error)
+ {
+ set_errno (pc.error);
+ goto out;
+ }
+ if (pc.exists ())
+ {
+ set_errno (EADDRINUSE);
+ goto out;
+ }
+ mode_t mode = adjust_socket_file_mode ((S_IRWXU | S_IRWXG | S_IRWXO)
+ & ~cygheap->umask);
+ DWORD attr = FILE_ATTRIBUTE_SYSTEM;
+ if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ attr |= FILE_ATTRIBUTE_READONLY;
+ SECURITY_ATTRIBUTES sa = sec_none;
+ security_descriptor sd;
+ if (allow_ntsec && pc.has_acls ())
+ set_security_attribute (mode, &sa, sd);
+ HANDLE fh = CreateFile (pc, GENERIC_WRITE, 0, &sa, CREATE_NEW, attr, 0);
+ if (fh == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError () == ERROR_ALREADY_EXISTS)
+ set_errno (EADDRINUSE);
+ else
+ __seterrno ();
+ }
+
+ char buf[sizeof (SOCKET_COOKIE) + 80];
+ __small_sprintf (buf, "%s%u %c ", SOCKET_COOKIE, sin.sin_port, get_socket_type () == SOCK_STREAM ? 's' : get_socket_type () == SOCK_DGRAM ? 'd' : '-');
+ af_local_set_secret (strchr (buf, '\0'));
+ DWORD blen = strlen (buf) + 1;
+ if (!WriteFile (fh, buf, blen, &blen, 0))
+ {
+ __seterrno ();
+ CloseHandle (fh);
+ DeleteFile (pc);
+ }
+ else
+ {
+ CloseHandle (fh);
+ set_sun_path (un_addr->sun_path);
+ res = 0;
+ }
+#undef un_addr
+ }
+ else
+ {
+ /* If the application didn't explicitely call setsockopt (SO_REUSEADDR),
+ enforce exclusive local address use using the SO_EXCLUSIVEADDRUSE
+ socket option, to emulate POSIX socket behaviour more closely.
+
+ KB 870562: Note that this option is only available since NT4 SP4.
+ Also note that a bug in Win2K SP1-3 and XP up to SP1 only enables
+ this option for users in the local administrators group. */
+ if (wincap.has_exclusiveaddruse ())
+ {
+ if (!saw_reuseaddr ())
+ {
+ int on = 1;
+ int ret = ::setsockopt (get_socket (), SOL_SOCKET,
+ ~(SO_REUSEADDR),
+ (const char *) &on, sizeof on);
+ debug_printf ("%d = setsockopt (SO_EXCLUSIVEADDRUSE), %E", ret);
+ }
+ else
+ {
+ debug_printf ("SO_REUSEADDR set");
+ /* There's a bug in SO_REUSEADDR handling in WinSock.
+ Per standards, we must not be able to reuse a complete
+ duplicate of a local TCP address (same IP, same port),
+ even if SO_REUSEADDR has been set. That's unfortunately
+ possible in WinSock. So we're testing here if the local
+ address is already in use and don't bind, if so. This
+ only works for OSes with IP Helper support. */
+ if (get_socket_type () == SOCK_STREAM
+ && wincap.has_ip_helper_lib ()
+ && address_in_use ((struct sockaddr_in *) name))
+ {
+ debug_printf ("Local address in use, don't bind");
+ set_errno (EADDRINUSE);
+ goto out;
+ }
+ }
+ }
+ if (::bind (get_socket (), name, namelen))
+ set_winsock_errno ();
+ else
+ res = 0;
+ }
+
+out:
+ return res;
+}
+
+int
+fhandler_socket::connect (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+ bool in_progress = false;
+ struct sockaddr_in sin;
+ DWORD err;
+ int type;
+
+ if (!get_inet_addr (name, namelen, &sin, &namelen, &type, connect_secret))
+ return -1;
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () != type)
+ {
+ WSASetLastError (WSAEPROTOTYPE);
+ set_winsock_errno ();
+ return -1;
+ }
+
+ res = ::connect (get_socket (), (struct sockaddr *) &sin, namelen);
+
+ if (!res)
+ err = 0;
+ else
+ {
+ err = WSAGetLastError ();
+ /* Special handling for connect to return the correct error code
+ when called on a non-blocking socket. */
+ if (is_nonblocking ())
+ {
+ if (err == WSAEWOULDBLOCK || err == WSAEALREADY)
+ in_progress = true;
+
+ if (err == WSAEWOULDBLOCK)
+ WSASetLastError (err = WSAEINPROGRESS);
+ }
+ if (err == WSAEINVAL)
+ WSASetLastError (err = WSAEISCONN);
+ set_winsock_errno ();
+ }
+
+ if (get_addr_family () == AF_LOCAL && (!res || in_progress))
+ set_sun_path (name->sa_data);
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ af_local_set_cred (); /* Don't move into af_local_connect since
+ af_local_connect is called from select,
+ possibly running under another identity. */
+ if (!res && af_local_connect ())
+ {
+ set_winsock_errno ();
+ return -1;
+ }
+ }
+
+ if (err == WSAEINPROGRESS || err == WSAEALREADY)
+ connect_state (connect_pending);
+ else if (err)
+ connect_state (connect_failed);
+ else
+ connect_state (connected);
+
+ return res;
+}
+
+int
+fhandler_socket::listen (int backlog)
+{
+ int res = ::listen (get_socket (), backlog);
+ if (res && WSAGetLastError () == WSAEINVAL && get_addr_family () == AF_INET)
+ {
+ /* It's perfectly valid to call listen on an unbound INET socket.
+ In this case the socket is automatically bound to an unused
+ port number, listening on all interfaces. On Winsock, listen
+ fails with WSAEINVAL when it's called on an unbound socket.
+ So we have to bind manually here to have POSIX semantics. */
+ struct sockaddr_in sa;
+ sa.sin_family = AF_INET;
+ sa.sin_port = 0;
+ sa.sin_addr.s_addr = INADDR_ANY;
+ if (!::bind (get_socket (), (struct sockaddr *) &sa, sizeof sa))
+ res = ::listen (get_socket (), backlog);
+ }
+ if (!res)
+ {
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ af_local_set_cred ();
+ connect_state (connected);
+ }
+ else
+ set_winsock_errno ();
+ return res;
+}
+
+int
+fhandler_socket::accept (struct sockaddr *peer, int *len)
+{
+ int res = -1;
+
+ /* Allows NULL peer and len parameters. */
+ struct sockaddr_in peer_dummy;
+ int len_dummy;
+ if (!peer)
+ peer = (struct sockaddr *) &peer_dummy;
+ if (!len)
+ {
+ len_dummy = sizeof (struct sockaddr_in);
+ len = &len_dummy;
+ }
+
+ /* accept on NT fails if len < sizeof (sockaddr_in)
+ * some programs set len to
+ * sizeof (name.sun_family) + strlen (name.sun_path) for UNIX domain
+ */
+ if (len && ((unsigned) *len < sizeof (struct sockaddr_in)))
+ *len = sizeof (struct sockaddr_in);
+
+ res = ::accept (get_socket (), peer, len);
+
+ if (res == (int) INVALID_SOCKET)
+ set_winsock_errno ();
+ else
+ {
+ cygheap_fdnew res_fd;
+ if (res_fd >= 0 && fdsock (res_fd, &dev (), res))
+ {
+ fhandler_socket *sock = (fhandler_socket *) res_fd;
+ sock->set_addr_family (get_addr_family ());
+ sock->set_socket_type (get_socket_type ());
+ sock->async_io (async_io ());
+ sock->set_nonblocking (is_nonblocking ());
+ if (get_addr_family () == AF_LOCAL)
+ {
+ sock->set_sun_path (get_sun_path ());
+ if (get_socket_type () == SOCK_STREAM)
+ {
+ /* Don't forget to copy credentials from accepting
+ socket to accepted socket and start transaction
+ on accepted socket! */
+ af_local_copy (sock);
+ res = sock->af_local_accept ();
+ if (res == -1)
+ {
+ res_fd.release ();
+ set_winsock_errno ();
+ goto out;
+ }
+ }
+ }
+ sock->connect_state (connected);
+ res = res_fd;
+ }
+ else
+ {
+ closesocket (res);
+ res = -1;
+ }
+ }
+
+out:
+ debug_printf ("res %d", res);
+ return res;
+}
+
+int
+fhandler_socket::getsockname (struct sockaddr *name, int *namelen)
+{
+ int res = -1;
+
+ if (get_addr_family () == AF_LOCAL)
+ {
+ struct sockaddr_un *sun = (struct sockaddr_un *) name;
+ memset (sun, 0, *namelen);
+ sun->sun_family = AF_LOCAL;
+
+ if (!get_sun_path ())
+ sun->sun_path[0] = '\0';
+ else
+ /* According to SUSv2 "If the actual length of the address is
+ greater than the length of the supplied sockaddr structure, the
+ stored address will be truncated." We play it save here so
+ that the path always has a trailing 0 even if it's truncated. */
+ strncpy (sun->sun_path, get_sun_path (),
+ *namelen - sizeof *sun + sizeof sun->sun_path - 1);
+
+ *namelen = sizeof *sun - sizeof sun->sun_path
+ + strlen (sun->sun_path) + 1;
+ res = 0;
+ }
+ else
+ {
+ res = ::getsockname (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::getpeername (struct sockaddr *name, int *namelen)
+{
+ int res = ::getpeername (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+
+ return res;
+}
+
+bool
+fhandler_socket::prepare (HANDLE &event, long event_mask)
+{
+ WSASetLastError (0);
+ closed (false);
+ if ((event = WSACreateEvent ()) == WSA_INVALID_EVENT)
+ {
+ debug_printf ("WSACreateEvent, %E");
+ return false;
+ }
+ if (WSAEventSelect (get_socket (), event, event_mask) == SOCKET_ERROR)
+ {
+ debug_printf ("WSAEventSelect(evt), %d", WSAGetLastError ());
+ return false;
+ }
+ return true;
+}
+
+int
+fhandler_socket::wait (HANDLE event, int flags, DWORD timeout)
+{
+ int ret = SOCKET_ERROR;
+ int wsa_err = 0;
+ WSAEVENT ev[2] = { event, signal_arrived };
+ WSANETWORKEVENTS evts;
+
+#if 0 /* Not yet. Not this way. */
+/* If WSAWaitForMultipleEvents is interrupted by a signal, and the signal
+ has the SA_RESTART flag set, return to this label and... restart. */
+sa_restart:
+#endif
+
+ switch (WSAWaitForMultipleEvents (2, ev, FALSE, timeout, FALSE))
+ {
+ case WSA_WAIT_TIMEOUT:
+ ret = 0;
+ break;
+ case WSA_WAIT_EVENT_0:
+ if (!WSAEnumNetworkEvents (get_socket (), event, &evts))
+ {
+ if (!evts.lNetworkEvents)
+ {
+ ret = 0;
+ break;
+ }
+ if (evts.lNetworkEvents & FD_OOB)
+ {
+ if (evts.iErrorCode[FD_OOB_BIT])
+ wsa_err = evts.iErrorCode[FD_OOB_BIT];
+ else if (flags & MSG_OOB)
+ ret = 0;
+ else
+ {
+ raise (SIGURG);
+ WSASetLastError (WSAEINTR);
+ break;
+ }
+ }
+ if (evts.lNetworkEvents & FD_ACCEPT)
+ {
+ if (evts.iErrorCode[FD_ACCEPT_BIT])
+ wsa_err = evts.iErrorCode[FD_ACCEPT_BIT];
+ else
+ ret = 0;
+ }
+ if (evts.lNetworkEvents & FD_CONNECT)
+ {
+ if (evts.iErrorCode[FD_CONNECT_BIT])
+ wsa_err = evts.iErrorCode[FD_CONNECT_BIT];
+ else
+ ret = 0;
+ }
+ else if (evts.lNetworkEvents & FD_READ)
+ {
+ if (evts.iErrorCode[FD_READ_BIT])
+ wsa_err = evts.iErrorCode[FD_READ_BIT];
+ else
+ ret = 0;
+ }
+ else if (evts.lNetworkEvents & FD_WRITE)
+ {
+ if (evts.iErrorCode[FD_WRITE_BIT])
+ wsa_err = evts.iErrorCode[FD_WRITE_BIT];
+ else
+ ret = 0;
+ }
+ if (evts.lNetworkEvents & FD_CLOSE)
+ {
+ closed (true);
+ if (!wsa_err)
+ {
+ if (evts.iErrorCode[FD_CLOSE_BIT])
+ wsa_err = evts.iErrorCode[FD_CLOSE_BIT];
+ else
+ ret = 0;
+ }
+ }
+ if (wsa_err)
+ WSASetLastError (wsa_err);
+ }
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+#if 0 /* Not yet. Not this way. */
+ if (_my_tls.call_signal_handler ())
+ {
+ sig_dispatch_pending ();
+ goto sa_restart;
+ }
+#endif
+ WSASetLastError (WSAEINTR);
+ break;
+ default:
+ WSASetLastError (WSAEFAULT);
+ break;
+ }
+ return ret;
+}
+
+void
+fhandler_socket::release (HANDLE event)
+{
+ int last_err = WSAGetLastError ();
+ /* KB 168349: NT4 fails if the event parameter is not NULL. */
+ if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR)
+ debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ());
+ WSACloseEvent (event);
+ unsigned long non_block = 0;
+ if (ioctlsocket (get_socket (), FIONBIO, &non_block))
+ debug_printf ("return to blocking failed: %d", WSAGetLastError ());
+ else
+ WSASetLastError (last_err);
+}
+
+int
+fhandler_socket::readv (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_control: NULL,
+ msg_controllen: 0,
+ msg_flags: 0
+ };
+
+ return recvmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::recvfrom (void *ptr, size_t len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res = SOCKET_ERROR;
+ DWORD ret = 0;
+
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking () || closed () || async_io ())
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, &lflags,
+ from, fromlen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !closed ()
+ && !(res = wait (evt, flags)));
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ /* According to SUSv3, errno isn't set in that case and no error
+ condition is returned. */
+ if (WSAGetLastError () == WSAEMSGSIZE)
+ return len;
+
+ /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned
+ in this case. */
+ if (WSAGetLastError () == WSAESHUTDOWN)
+ return 0;
+
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (CYGWIN_VERSION_CHECK_FOR_USING_ANCIENT_MSGHDR)
+ ((struct OLD_msghdr *) msg)->msg_accrightslen = 0;
+ else
+ {
+ msg->msg_controllen = 0;
+ msg->msg_flags = 0;
+ }
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* On AF_LOCAL sockets the (fixed-size) name of the shared memory
+ area used for descriptor passing is transmitted first.
+ If this string is empty, no descriptors are passed and we can
+ go ahead recv'ing the normal data blocks. Otherwise start
+ special handling for descriptor passing. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ struct sockaddr *from = (struct sockaddr *) msg->msg_name;
+ int *fromlen = from ? &msg->msg_namelen : NULL;
+
+ int res = SOCKET_ERROR;
+
+ WSABUF wsabuf[iovcnt];
+ unsigned long len = 0L;
+
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ len += wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+
+ DWORD ret = 0;
+
+ if (is_nonblocking () || closed () || async_io ())
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !closed ()
+ && !(res = wait (evt, flags)));
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ /* According to SUSv3, errno isn't set in that case and no error
+ condition is returned. */
+ if (WSAGetLastError () == WSAEMSGSIZE)
+ return len;
+
+ /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned
+ in this case. */
+ if (WSAGetLastError () == WSAESHUTDOWN)
+ return 0;
+
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::writev (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_control: NULL,
+ msg_controllen: 0,
+ msg_flags: 0
+ };
+
+ return sendmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::sendto (const void *ptr, size_t len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ struct sockaddr_in sin;
+
+ if (to && !get_inet_addr (to, tolen, &sin, &tolen))
+ return SOCKET_ERROR;
+
+ int res = SOCKET_ERROR;
+ DWORD ret = 0;
+
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking () || closed () || async_io ())
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen,
+ NULL, NULL);
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL),
+ tolen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !(res = wait (evt, 0))
+ && !closed ());
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ else
+ res = ret;
+
+ /* Special handling for EPIPE and SIGPIPE.
+
+ EPIPE is generated if the local end has been shut down on a connection
+ oriented socket. In this case the process will also receive a SIGPIPE
+ unless MSG_NOSIGNAL is set. */
+ if (res == SOCKET_ERROR && get_errno () == ESHUTDOWN
+ && get_socket_type () == SOCK_STREAM)
+ {
+ set_errno (EPIPE);
+ if (! (flags & MSG_NOSIGNAL))
+ raise (SIGPIPE);
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* For AF_LOCAL/AF_UNIX sockets, if descriptors are given, start
+ the special handling for descriptor passing. Otherwise just
+ transmit an empty string to tell the receiver that no
+ descriptor passing is done. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ int res = SOCKET_ERROR;
+
+ WSABUF wsabuf[iovcnt];
+
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+
+ DWORD ret = 0;
+
+ if (is_nonblocking () || closed () || async_io ())
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret,
+ flags & MSG_WINMASK, (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen, NULL, NULL);
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ res = WSASendTo (get_socket (), wsabuf, iovcnt,
+ &ret, flags & MSG_WINMASK,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !(res = wait (evt, 0))
+ && !closed ());
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ else
+ res = ret;
+
+ /* Special handling for EPIPE and SIGPIPE.
+
+ EPIPE is generated if the local end has been shut down on a connection
+ oriented socket. In this case the process will also receive a SIGPIPE
+ unless MSG_NOSIGNAL is set. */
+ if (res == SOCKET_ERROR && get_errno () == ESHUTDOWN
+ && get_socket_type () == SOCK_STREAM)
+ {
+ set_errno (EPIPE);
+ if (! (flags & MSG_NOSIGNAL))
+ raise (SIGPIPE);
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::shutdown (int how)
+{
+ int res = ::shutdown (get_socket (), how);
+
+ if (res)
+ set_winsock_errno ();
+ else
+ switch (how)
+ {
+ case SHUT_RD:
+ saw_shutdown_read (true);
+ break;
+ case SHUT_WR:
+ saw_shutdown_write (true);
+ break;
+ case SHUT_RDWR:
+ saw_shutdown_read (true);
+ saw_shutdown_write (true);
+ break;
+ }
+ return res;
+}
+
+int
+fhandler_socket::close ()
+{
+ int res = 0;
+
+ /* HACK to allow a graceful shutdown even if shutdown() hasn't been
+ called by the application. Note that this isn't the ultimate
+ solution but it helps in many cases. */
+ struct linger linger;
+ linger.l_onoff = 1;
+ linger.l_linger = 240; /* secs. default 2MSL value according to MSDN. */
+ setsockopt (get_socket (), SOL_SOCKET, SO_LINGER,
+ (const char *)&linger, sizeof linger);
+
+ while ((res = closesocket (get_socket ())) != 0)
+ {
+ if (WSAGetLastError () != WSAEWOULDBLOCK)
+ {
+ set_winsock_errno ();
+ res = -1;
+ break;
+ }
+ if (WaitForSingleObject (signal_arrived, 10) == WAIT_OBJECT_0)
+ {
+ set_errno (EINTR);
+ res = -1;
+ break;
+ }
+ WSASetLastError (0);
+ }
+
+ debug_printf ("%d = fhandler_socket::close()", res);
+ return res;
+}
+
+int
+fhandler_socket::ioctl (unsigned int cmd, void *p)
+{
+ extern int get_ifconf (struct ifconf *ifc, int what); /* net.cc */
+ int res;
+ struct ifconf ifc, *ifcp;
+ struct ifreq *ifr, *ifrp;
+
+ switch (cmd)
+ {
+ case SIOCGIFCONF:
+ ifcp = (struct ifconf *) p;
+ if (!ifcp)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ res = get_ifconf (ifcp, cmd);
+ if (res)
+ debug_printf ("error in get_ifconf");
+ break;
+ case SIOCGIFFLAGS:
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ ifr->ifr_flags = IFF_NOTRAILERS | IFF_UP | IFF_RUNNING;
+ if (!strncmp(ifr->ifr_name, "lo", 2)
+ || ntohl (((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr)
+ == INADDR_LOOPBACK)
+ ifr->ifr_flags |= IFF_LOOPBACK;
+ else
+ ifr->ifr_flags |= IFF_BROADCAST;
+ res = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFADDR:
+ case SIOCGIFHWADDR:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ {
+ ifc.ifc_len = 2048;
+ ifc.ifc_buf = (char *) alloca (2048);
+
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ debug_printf ("ifr == NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ res = get_ifconf (&ifc, cmd);
+ if (res)
+ {
+ debug_printf ("error in get_ifconf");
+ break;
+ }
+
+ debug_printf (" name: %s", ifr->ifr_name);
+ for (ifrp = ifc.ifc_req;
+ (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len;
+ ++ifrp)
+ {
+ debug_printf ("testname: %s", ifrp->ifr_name);
+ if (! strcmp (ifrp->ifr_name, ifr->ifr_name))
+ {
+ switch (cmd)
+ {
+ case SIOCGIFADDR:
+ ifr->ifr_addr = ifrp->ifr_addr;
+ break;
+ case SIOCGIFBRDADDR:
+ ifr->ifr_broadaddr = ifrp->ifr_broadaddr;
+ break;
+ case SIOCGIFNETMASK:
+ ifr->ifr_netmask = ifrp->ifr_netmask;
+ break;
+ case SIOCGIFHWADDR:
+ ifr->ifr_hwaddr = ifrp->ifr_hwaddr;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = ifrp->ifr_metric;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifrp->ifr_mtu;
+ break;
+ }
+ break;
+ }
+ }
+ if ((caddr_t) ifrp >= ifc.ifc_buf + ifc.ifc_len)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ break;
+ }
+ case FIOASYNC:
+ res = WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO,
+ *(int *) p ? ASYNC_MASK : 0);
+ syscall_printf ("Async I/O on socket %s",
+ *(int *) p ? "started" : "cancelled");
+ async_io (*(int *) p != 0);
+ break;
+ case FIONREAD:
+ res = ioctlsocket (get_socket (), FIONREAD, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ break;
+ default:
+ /* We must cancel WSAAsyncSelect (if any) before setting socket to
+ * blocking mode
+ */
+ if (cmd == FIONBIO && *(int *) p == 0)
+ {
+ if (async_io ())
+ WSAAsyncSelect (get_socket (), winmsg, 0, 0);
+ if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR)
+ debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ());
+ }
+ res = ioctlsocket (get_socket (), cmd, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ if (cmd == FIONBIO)
+ {
+ if (!res)
+ {
+ syscall_printf ("socket is now %sblocking",
+ *(int *) p ? "non" : "");
+ set_nonblocking (*(int *) p);
+ }
+ /* Start AsyncSelect if async socket unblocked */
+ if (*(int *) p && async_io ())
+ WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK);
+ }
+ break;
+ }
+ syscall_printf ("%d = ioctl_socket (%x, %x)", res, cmd, p);
+ return res;
+}
+
+int
+fhandler_socket::fcntl (int cmd, void *arg)
+{
+ int res = 0;
+ int request, current;
+
+ switch (cmd)
+ {
+ case F_SETOWN:
+ {
+ /* Urgh! Bad hack! */
+ pid_t pid = (pid_t) arg;
+ owner (pid == getpid ());
+ debug_printf ("owner set to %d", owner ());
+ }
+ break;
+ case F_SETFL:
+ {
+ /* Carefully test for the O_NONBLOCK or deprecated OLD_O_NDELAY flag.
+ Set only the flag that has been passed in. If both are set, just
+ record O_NONBLOCK. */
+ int new_flags = (int) arg & O_NONBLOCK_MASK;
+ if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK))
+ new_flags = O_NONBLOCK;
+ current = get_flags () & O_NONBLOCK_MASK;
+ request = new_flags ? 1 : 0;
+ if (!!current != !!new_flags && (res = ioctl (FIONBIO, &request)))
+ break;
+ set_flags ((get_flags () & ~O_NONBLOCK_MASK) | new_flags);
+ break;
+ }
+ default:
+ res = fhandler_base::fcntl (cmd, arg);
+ break;
+ }
+ return res;
+}
+
+void
+fhandler_socket::set_close_on_exec (bool val)
+{
+ close_on_exec (val);
+ debug_printf ("set close_on_exec for %s to %d", get_name (), val);
+}
+
+void
+fhandler_socket::set_sun_path (const char *path)
+{
+ sun_path = path ? cstrdup (path) : NULL;
+}
+
+int
+fhandler_socket::getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid)
+{
+ if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (connect_state () != connected)
+ {
+ set_errno (ENOTCONN);
+ return -1;
+ }
+ if (sec_peer_pid == (pid_t) 0)
+ {
+ set_errno (ENOTCONN); /* Usually when calling getpeereid on
+ accepting (instead of accepted) socket. */
+ return -1;
+ }
+
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
+ if (pid)
+ *pid = sec_peer_pid;
+ if (euid)
+ *euid = sec_peer_uid;
+ if (egid)
+ *egid = sec_peer_gid;
+ return 0;
+}
diff --git a/winsup/cygwin/gendef b/winsup/cygwin/gendef
new file mode 100755
index 00000000000..0d7df4b090c
--- /dev/null
+++ b/winsup/cygwin/gendef
@@ -0,0 +1,455 @@
+#!/usr/bin/perl
+# Copyright 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.
+#
+use strict;
+sub nocr(@);
+my $in = shift;
+my $tls_offsets = shift;
+my $out = shift;
+my $sigfe = shift;
+
+$main::first = 0;
+if (!defined($in) || !defined($out) || !defined($sigfe)) {
+ die "usage: $0 deffile.in cygtls.h deffile.def sigfe.s\n";
+}
+
+require $tls_offsets;
+
+open(IN, $in) or die "$0: couldn't open \"$in\" - $!\n";
+my @top = ();
+while (<IN>) {
+ push(@top, nocr $_);
+ last if /^\s*exports\s*$/i;
+}
+my $libline = nocr scalar(<IN>);
+my @in = nocr <IN>;
+close(IN);
+
+my %sigfe = ();
+my @data = ();
+my @nosigfuncs = ();
+my @out = ();
+for (@in) {
+ /\sDATA$/o and do {
+ push(@data, $_);
+ next;
+ };
+ chomp;
+ if (/=/o) {
+ if (s/\s+NOSIGFE\s*$//) {
+ # nothing
+ } elsif (s/ SIGFE(_MAYBE)?$//) {
+ my $func = (split(' '))[2];
+ my $maybe = lc $1 . '_';
+ $sigfe{$func} = '_sigfe' . $maybe . $func;
+ }
+ } else {
+ my ($func, $sigfe) = m%^\s*(\S+)(?:\s+((?:NO)?SIGFE(?:_MAYBE)?))?$%o;
+ if (defined($sigfe) && $sigfe =~ /^NO/o) {
+ $_ = $func;
+ } else {
+ $sigfe ||= 'sigfe';
+ $_ = '_' . lc($sigfe) . '_' . $func;
+ $sigfe{$func} = $_;
+ $_ = $func . ' = ' . $_;
+ }
+ }
+ s/(\S)\s+(\S)/$1 $2/go;
+ s/(\S)\s+$/$1/o;
+ s/^\s+(\S)/$1/o;
+ push(@out, $_ . "\n");
+}
+
+for (@out) {
+ my ($alias, $func) = /^(\S+) = (\S+)\s*$/o;
+ $_ = $alias . ' = ' . $sigfe{$func} . "\n"
+ if defined($func) && $sigfe{$func};
+}
+open(OUT, '>', $out) or die "$0: couldn't open \"$out\" - $!\n";
+print OUT @top, @data, @out;
+close OUT;
+
+open(SIGFE, '>', $sigfe) or die "$0: couldn't open sigfe file \"$sigfe\" - $!\n";
+
+for my $k (sort keys %sigfe) {
+ print SIGFE fefunc($k, $sigfe{$k});
+}
+close SIGFE;
+
+sub fefunc {
+ my $func = '_' . shift;
+ my $fe = '_' . shift;
+ my $sigfe_func = ($fe =~ /^(.*)$func/)[0];
+ my $extra;
+ my $res = <<EOF;
+ .extern $func
+ .global $fe
+$fe:
+ pushl \$$func
+ jmp $sigfe_func
+
+EOF
+ if (!$main::first++) {
+ $res = <<EOF . longjmp () . $res;
+ .text
+
+ .stabs "_sigfe:F(0,1)",36,0,0,__sigfe
+__sigfe_maybe:
+ pushl %ebx
+ pushl %edx
+ movl %fs:4,%ebx # location of bottom of stack
+ addl \$$tls::initialized,%ebx # where we will be looking
+ cmpl %ebx,%esp # stack loc > than tls
+ jge 0f # yep. we don't have a tls.
+ subl \$$tls::initialized,%ebx # where we will be looking
+ movl $tls::initialized(%ebx),%eax
+ cmpl \$0xc763173f,%eax # initialized?
+ je 1f
+0: popl %edx
+ popl %ebx
+ ret
+
+__sigfe:
+ pushl %ebx
+ pushl %edx
+ movl %fs:4,%ebx # location of bottom of stack
+1: movl \$1,%eax # potential lock value
+ lock xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it
+ movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock
+ testl %eax,%eax # it will be zero
+ jz 2f # if so
+ xorl %eax,%eax # nope. It was not zero
+ call _low_priority_sleep # should be a short-time thing, so
+ jmp 1b # sleep and loop
+2: movl \$4,%eax # have the lock, now increment the
+ xadd %eax,$tls::stackptr(%ebx) # stack pointer and get pointer
+ leal __sigbe,%edx # new place to return to
+ xchgl %edx,12(%esp) # exchange with real return value
+ movl %edx,(%eax) # store real return value on alt stack
+ incl $tls::incyg(%ebx)
+ decl $tls::stacklock(%ebx) # remove lock
+ popl %edx # restore saved value
+ popl %ebx
+ ret
+
+ .global __sigbe
+ .stabs "_sigbe:F(0,1)",36,0,0,__sigbe
+__sigbe: # return here after cygwin syscall
+ pushl %edx
+ pushl %ebx
+ pushl %eax # don't clobber
+1: movl %fs:4,%ebx # address of bottom of tls
+ movl \$1,%eax # potential lock value
+ lock xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it
+ movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock
+ testl %eax,%eax # it will be zero
+ jz 2f # if so
+ xorl %eax,%eax # nope. not zero
+ call _low_priority_sleep # sleep
+ jmp 1b # and loop
+2: movl \$-4,%eax # now decrement aux stack
+ xadd %eax,$tls::stackptr(%ebx) # and get pointer
+ xorl %edx,%edx
+ xchgl %edx,-4(%eax) # get return address from signal stack
+ xchgl %edx,8(%esp) # restore edx/real return address
+ decl $tls::incyg(%ebx)
+ decl $tls::stacklock(%ebx) # release lock
+ popl %eax
+ popl %ebx
+ ret
+
+ .global _sigreturn
+ .stabs "sigreturn:F(0,1)",36,0,0,_sigreturn
+_sigreturn:
+ movl %fs:4,%ebx
+ incl $tls::incyg(%ebx)
+ addl \$12,%esp # remove arguments
+ call _set_process_mask\@4
+
+1: movl \$1,%eax # potential lock value
+ lock xchgl %eax,$tls::stacklock(%ebx) # see if we can grab it
+ movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock
+ testl %eax,%eax # it will be zero
+ jz 2f # if so
+ xorl %eax,%eax # nope. not zero
+ call _low_priority_sleep # sleep
+ jmp 1b # and loop
+2: popl %edx # saved errno
+ testl %edx,%edx # Is it < 0
+ jl 3f # yup. ignore it
+ movl $tls::errno_addr(%ebx),%eax
+ movl %edx,(%eax)
+3: movl \$-4,%eax # now decrement aux stack
+ xadd %eax,$tls::stackptr(%ebx) # and get pointer
+ xorl %ebp,%ebp
+ xchgl %ebp,-4(%eax) # get return address from signal stack
+ xchgl %ebp,28(%esp) # store real return address
+ decl $tls::incyg(%ebx)
+ decl $tls::stacklock(%ebx) # unlock
+
+ popl %eax
+ popl %ebx
+ popl %ecx
+ popl %edx
+ popl %edi
+ popl %esi
+ popf
+ ret
+
+ .global _sigdelayed
+ .stabs "sigdelayed:F(0,1)",36,0,0,_sigdelayed
+_sigdelayed:
+ pushl %ebp
+ movl %esp,%ebp
+ pushf
+ pushl %esi
+ pushl %edi
+ pushl %edx
+ pushl %ecx
+ pushl %ebx
+ pushl %eax
+ movl %fs:4,%ebx
+ incl $tls::incyg(%ebx)
+ pushl $tls::saved_errno(%ebx) # saved errno
+ call _set_process_mask_delta
+ pushl %eax
+
+ # fill out handler arguments
+ xorl %eax,%eax # ucontext_t (currently not set)
+ pushl %eax
+ leal $tls::infodata(%ebx),%eax
+ pushl %eax # siginfo
+ pushl $tls::sig(%ebx) # signal number
+
+ call _reset_signal_arrived\@0
+ pushl \$_sigreturn # where to return
+ pushl $tls::func(%ebx) # signal func
+ cmpl \$0,$tls::threadkill(%ebx)#pthread_kill signal?
+ jnz 4f # yes. callee clears signal number
+ movl \$0,$tls::sig(%ebx) # zero the signal number as a
+ # flag to the signal handler thread
+ # that it is ok to set up sigsave
+4: decl $tls::incyg(%ebx)
+ ret
+
+ .global __ZN7_cygtls3popEv
+__ZN7_cygtls3popEv:
+1: pushl %ebx
+ pushl %edx # FIXME: needed?
+ movl %eax,%ebx
+ movl \$-4,%edx
+ xadd %edx,$tls::pstackptr(%ebx)
+ xorl %eax,%eax
+ xchgl %eax,-4(%edx)
+ popl %edx # FIXME: needed?
+ popl %ebx
+ ret
+
+ .global __ZN7_cygtls4lockEv
+__ZN7_cygtls4lockEv:
+ pushl %ebx
+ movl %eax,%ebx
+1: movl \$1,%eax
+ lock xchgl %eax,$tls::pstacklock(%ebx)
+ testl %eax,%eax
+ jz 2f
+ xorl %eax,%eax
+ call _low_priority_sleep
+ jmp 1b
+2: popl %ebx
+ ret
+
+ .global __ZN7_cygtls6unlockEv
+__ZN7_cygtls6unlockEv:
+ decl $tls::pstacklock(%eax)
+ ret
+
+ .global __ZN7_cygtls6lockedEv
+__ZN7_cygtls6lockedEv:
+ movl $tls::pstacklock(%eax),%eax
+ ret
+
+ .extern __ZN7_cygtls19call_signal_handlerEv
+stabilize_sig_stack:
+ movl %fs:4,%ebx
+ incl $tls::incyg(%ebx)
+1: movl \$1,%eax
+ lock xchgl %eax,$tls::stacklock(%ebx)
+ movl %eax,$tls::spinning(%ebx) # flag if we are waiting for lock
+ testl %eax,%eax
+ jz 2f
+ xorl %eax,%eax
+ call _low_priority_sleep
+ jmp 1b
+2: cmpl \$0,$tls::sig(%ebx)
+ jz 3f
+ decl $tls::stacklock(%ebx) # unlock
+ movl \$-$tls::sizeof__cygtls,%eax # point to beginning
+ addl %ebx,%eax # of tls block
+ call __ZN7_cygtls19call_signal_handlerEv
+ jmp 1b
+3: decl $tls::incyg(%ebx)
+ ret
+EOF
+ }
+ return $res;
+}
+
+sub longjmp {
+ return <<EOF;
+
+ .globl _setjmp
+_setjmp:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ movl 8(%ebp),%edi
+ movl %eax,0(%edi)
+ movl %ebx,4(%edi)
+ movl %ecx,8(%edi)
+ movl %edx,12(%edi)
+ movl %esi,16(%edi)
+ movl -4(%ebp),%eax
+ movl %eax,20(%edi)
+ movl 0(%ebp),%eax
+ movl %eax,24(%edi)
+ movl %esp,%eax
+ addl \$12,%eax
+ movl %eax,28(%edi)
+ movl 4(%ebp),%eax
+ movl %eax,32(%edi)
+ movw %es,%ax
+ movw %ax,36(%edi)
+ movw %fs,%ax
+ movw %ax,38(%edi)
+ movw %gs,%ax
+ movw %ax,40(%edi)
+ movw %ss,%ax
+ movw %ax,42(%edi)
+ pushl %ebx
+ call stabilize_sig_stack
+ movl $tls::stackptr(%ebx),%eax # save stack pointer contents
+ decl $tls::stacklock(%ebx)
+ popl %ebx
+ movl %eax,44(%edi)
+ popl %edi
+ movl \$0,%eax
+ leave
+ ret
+
+ .globl ___sjfault
+___sjfault:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ movl 8(%ebp),%edi
+ movl %eax,0(%edi)
+ movl %ebx,4(%edi)
+ movl %ecx,8(%edi)
+ movl %edx,12(%edi)
+ movl %esi,16(%edi)
+ movl -4(%ebp),%eax
+ movl %eax,20(%edi)
+ movl 0(%ebp),%eax
+ movl %eax,24(%edi)
+ movl %esp,%eax
+ addl \$12,%eax
+ movl %eax,28(%edi)
+ movl 4(%ebp),%eax
+ movl %eax,32(%edi)
+ movw %es,%ax
+ movw %ax,36(%edi)
+ movw %fs,%ax
+ movw %ax,38(%edi)
+ movw %gs,%ax
+ movw %ax,40(%edi)
+ movw %ss,%ax
+ movw %ax,42(%edi)
+ popl %edi
+ movl \$0,%eax
+ leave
+ ret
+
+ .global ___ljfault
+___ljfault:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%edi
+
+ movl 12(%ebp),%eax
+ testl %eax,%eax
+ jne 0f
+ incl %eax
+
+0: movl %eax,0(%edi)
+ movl 24(%edi),%ebp
+ pushfl
+ popl %ebx
+ movw 42(%edi),%ax
+ movw %ax,%ss
+ movl 28(%edi),%esp
+ pushl 32(%edi)
+ pushl %ebx
+ movw 36(%edi),%ax
+ movw %ax,%es
+ movw 40(%edi),%ax
+ movw %ax,%gs
+ movl 0(%edi),%eax
+ movl 4(%edi),%ebx
+ movl 8(%edi),%ecx
+ movl 16(%edi),%esi
+ movl 12(%edi),%edx
+ movl 20(%edi),%edi
+ popfl
+ ret
+
+ .globl _longjmp
+_longjmp:
+ pushl %ebp
+ movl %esp,%ebp
+ movl 8(%ebp),%edi # address of buffer
+ call stabilize_sig_stack
+ movl 44(%edi),%eax # get old signal stack
+ movl %eax,$tls::stackptr(%ebx) # restore
+ decl $tls::stacklock(%ebx) # relinquish lock
+ xorl %eax,%eax
+ movl %eax,$tls::incyg(%ebx) # we're definitely not in cygwin anymore
+
+ movl 12(%ebp),%eax
+ testl %eax,%eax
+ jne 3f
+ incl %eax
+
+3: movl %eax,0(%edi)
+ movl 24(%edi),%ebp
+ pushfl
+ popl %ebx
+ movw 42(%edi),%ax
+ movw %ax,%ss
+ movl 28(%edi),%esp
+ pushl 32(%edi)
+ pushl %ebx
+ movw 36(%edi),%ax
+ movw %ax,%es
+ movw 40(%edi),%ax
+ movw %ax,%gs
+ movl 0(%edi),%eax
+ movl 4(%edi),%ebx
+ movl 8(%edi),%ecx
+ movl 16(%edi),%esi
+ movl 12(%edi),%edx
+ movl 20(%edi),%edi
+ popfl
+ ret
+EOF
+}
+
+sub nocr(@) {
+ map {s/\r//g; $_} @_;
+}
diff --git a/winsup/cygwin/include/cygwin/time.h b/winsup/cygwin/include/cygwin/time.h
new file mode 100644
index 00000000000..61d385e67c2
--- /dev/null
+++ b/winsup/cygwin/include/cygwin/time.h
@@ -0,0 +1,45 @@
+/* time.h
+
+ Copyright 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 _CYGWIN_TIME_H
+#define _CYGWIN_TIME_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int nanosleep (const struct timespec *, struct timespec *);
+int clock_setres (clockid_t, struct timespec *);
+int clock_getres (clockid_t, struct timespec *);
+
+/* GNU extensions. */
+time_t timelocal (struct tm *);
+time_t timegm (struct tm *);
+
+#define TIMER_RELTIME 0 /* For compatibility with HP/UX, Solaris, others? */
+
+#ifndef __STRICT_ANSI__
+# ifndef daylight
+# define daylight _daylight
+# endif
+
+/* The timezone function is only kept for backward compatibility.
+ POSIX expects the timezone variable as XSI extension. */
+# ifdef __timezonefunc__
+char __cdecl *timezone (void);
+# elif !defined(timezone)
+# define timezone _timezone
+# endif
+#endif /*__STRICT_ANSI__*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*_CYGWIN_TIME_H*/
diff --git a/winsup/cygwin/include/search.h b/winsup/cygwin/include/search.h
new file mode 100644
index 00000000000..bfadb23e095
--- /dev/null
+++ b/winsup/cygwin/include/search.h
@@ -0,0 +1,74 @@
+/*-
+ * Written by J.T. Conklin <jtc@netbsd.org>
+ * Public domain.
+ *
+ * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $
+ * $FreeBSD: src/include/search.h,v 1.10 2002/10/16 14:29:23 robert Exp $
+ */
+
+#ifndef _SEARCH_H_
+#define _SEARCH_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+typedef struct entry
+{
+ char *key;
+ void *data;
+} ENTRY;
+
+typedef enum
+{
+ FIND, ENTER
+} ACTION;
+
+typedef enum
+{
+ preorder,
+ postorder,
+ endorder,
+ leaf
+} VISIT;
+
+#ifdef _SEARCH_PRIVATE
+typedef struct node
+{
+ char *key;
+ struct node *llink, *rlink;
+} node_t;
+
+struct que_elem
+{
+ struct que_elem *next;
+ struct que_elem *prev;
+};
+#endif
+
+struct hsearch_data
+{
+ struct internal_head *htable;
+ size_t htablesize;
+};
+
+__BEGIN_DECLS
+int hcreate (size_t);
+void hdestroy (void);
+ENTRY *hsearch (ENTRY, ACTION);
+int hcreate_r (size_t, struct hsearch_data *);
+void hdestroy_r (struct hsearch_data *);
+int hsearch_r (ENTRY, ACTION, ENTRY **, struct hsearch_data *);
+void *tdelete (const void * __restrict, void ** __restrict,
+ int (*) (const void *, const void *));
+void tdestroy (void *, void (*)(void *));
+void *tfind (const void *, void **,
+ int (*) (const void *, const void *));
+void *tsearch (const void *, void **, int (*) (const void *, const void *));
+void twalk (const void *, void (*) (const void *, VISIT, int));
+void *lfind (const void *, const void *, size_t *, size_t,
+ int (*) (const void *, const void *));
+void *lsearch (const void *, void *, size_t *, size_t,
+ int (*) (const void *, const void *));
+__END_DECLS
+
+#endif /* !_SEARCH_H_ */
diff --git a/winsup/cygwin/include/stdint.h b/winsup/cygwin/include/stdint.h
new file mode 100644
index 00000000000..14115812340
--- /dev/null
+++ b/winsup/cygwin/include/stdint.h
@@ -0,0 +1,182 @@
+/* stdint.h - integer types
+
+ Copyright 2003, 2006, 2007 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 _STDINT_H
+#define _STDINT_H
+
+/* Exact-width integer types */
+
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef signed char int8_t;
+typedef short int16_t;
+typedef long int32_t;
+typedef long long int64_t;
+#endif
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+#ifndef __uint32_t_defined
+#define __uint32_t_defined
+typedef unsigned long uint32_t;
+#endif
+typedef unsigned long long uint64_t;
+
+/* Minimum-width integer types */
+
+typedef signed char int_least8_t;
+typedef short int_least16_t;
+typedef long int_least32_t;
+typedef long long int_least64_t;
+
+typedef unsigned char uint_least8_t;
+typedef unsigned short uint_least16_t;
+typedef unsigned long uint_least32_t;
+typedef unsigned long long uint_least64_t;
+
+/* Fastest minimum-width integer types */
+
+typedef signed char int_fast8_t;
+typedef long int_fast16_t;
+typedef long int_fast32_t;
+typedef long long int_fast64_t;
+
+typedef unsigned char uint_fast8_t;
+typedef unsigned long uint_fast16_t;
+typedef unsigned long uint_fast32_t;
+typedef unsigned long long uint_fast64_t;
+
+/* Integer types capable of holding object pointers */
+
+#ifndef __intptr_t_defined
+#define __intptr_t_defined
+typedef long intptr_t;
+#endif
+typedef unsigned long uintptr_t;
+
+/* Greatest-width integer types */
+
+typedef long long intmax_t;
+typedef unsigned long long uintmax_t;
+
+/* Limits of exact-width integer types */
+
+#define INT8_MIN (-128)
+#define INT16_MIN (-32768)
+#define INT32_MIN (-2147483647 - 1)
+#define INT64_MIN (-9223372036854775807LL - 1LL)
+
+#define INT8_MAX (127)
+#define INT16_MAX (32767)
+#define INT32_MAX (2147483647)
+#define INT64_MAX (9223372036854775807LL)
+
+#define UINT8_MAX (255)
+#define UINT16_MAX (65535)
+#define UINT32_MAX (4294967295UL)
+#define UINT64_MAX (18446744073709551615ULL)
+
+/* Limits of minimum-width integer types */
+
+#define INT_LEAST8_MIN (-128)
+#define INT_LEAST16_MIN (-32768)
+#define INT_LEAST32_MIN (-2147483647 - 1)
+#define INT_LEAST64_MIN (-9223372036854775807LL - 1LL)
+
+#define INT_LEAST8_MAX (127)
+#define INT_LEAST16_MAX (32767)
+#define INT_LEAST32_MAX (2147483647)
+#define INT_LEAST64_MAX (9223372036854775807LL)
+
+#define UINT_LEAST8_MAX (255)
+#define UINT_LEAST16_MAX (65535)
+#define UINT_LEAST32_MAX (4294967295UL)
+#define UINT_LEAST64_MAX (18446744073709551615ULL)
+
+/* Limits of fastest minimum-width integer types */
+
+#define INT_FAST8_MIN (-128)
+#define INT_FAST16_MIN (-2147483647 - 1)
+#define INT_FAST32_MIN (-2147483647 - 1)
+#define INT_FAST64_MIN (-9223372036854775807LL - 1LL)
+
+#define INT_FAST8_MAX (127)
+#define INT_FAST16_MAX (2147483647)
+#define INT_FAST32_MAX (2147483647)
+#define INT_FAST64_MAX (9223372036854775807LL)
+
+#define UINT_FAST8_MAX (255)
+#define UINT_FAST16_MAX (4294967295UL)
+#define UINT_FAST32_MAX (4294967295UL)
+#define UINT_FAST64_MAX (18446744073709551615ULL)
+
+/* Limits of integer types capable of holding object pointers */
+
+#define INTPTR_MIN (-2147483647 - 1)
+#define INTPTR_MAX (2147483647)
+#define UINTPTR_MAX (4294967295UL)
+
+/* Limits of greatest-width integer types */
+
+#define INTMAX_MIN (-9223372036854775807LL - 1LL)
+#define INTMAX_MAX (9223372036854775807LL)
+#define UINTMAX_MAX (18446744073709551615ULL)
+
+/* Limits of other integer types */
+
+#ifndef PTRDIFF_MIN
+#define PTRDIFF_MIN (-2147483647 - 1)
+#define PTRDIFF_MAX (2147483647)
+#endif
+
+#ifndef SIG_ATOMIC_MIN
+#define SIG_ATOMIC_MIN (-2147483647 - 1)
+#endif
+#ifndef SIG_ATOMIC_MAX
+#define SIG_ATOMIC_MAX (2147483647)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (4294967295UL)
+#endif
+
+#ifndef WCHAR_MIN
+#ifdef __WCHAR_MIN__
+#define WCHAR_MIN __WCHAR_MIN__
+#define WCHAR_MAX __WCHAR_MAX__
+#else
+#define WCHAR_MIN (0)
+#define WCHAR_MAX (65535)
+#endif
+#endif
+
+#ifndef WINT_MIN
+#define WINT_MIN 0U
+#define WINT_MAX UINT_MAX
+#endif
+
+/* Macros for minimum-width integer constant expressions */
+
+#define INT8_C(x) x
+#define INT16_C(x) x
+#define INT32_C(x) x ## L
+#define INT64_C(x) x ## LL
+
+#define UINT8_C(x) x
+#define UINT16_C(x) x
+#define UINT32_C(x) x ## UL
+#define UINT64_C(x) x ## ULL
+
+/* Macros for greatest-width integer constant expressions */
+
+#define INTMAX_C(x) x ## LL
+#define UINTMAX_C(x) x ## ULL
+
+#endif /* _STDINT_H */
diff --git a/winsup/cygwin/include/sys/stdio.h b/winsup/cygwin/include/sys/stdio.h
new file mode 100644
index 00000000000..40b18b3be2c
--- /dev/null
+++ b/winsup/cygwin/include/sys/stdio.h
@@ -0,0 +1,44 @@
+/* sys/stdio.h
+
+ Copyright 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 _SYS_STDIO_H_
+#define _SYS_STDIO_H_
+
+#include <sys/cdefs.h>
+#include <sys/lock.h>
+
+/* These definitions should be kept in sync with those in the newlib
+ header of the same name (newlib/libc/include/sys/stdio.h). */
+
+#if !defined(__SINGLE_THREAD__)
+# if !defined(_flockfile)
+# define _flockfile(fp) ({ if (!((fp)->_flags & __SSTR)) \
+ __cygwin_lock_lock ((_LOCK_T *)&(fp)->_lock); })
+# endif
+# if !defined(_ftrylockfile)
+# define _ftrylockfile(fp) (((fp)->_flags & __SSTR) ? 0 : \
+ __cygwin_lock_trylock ((_LOCK_T *)&(fp)->_lock))
+# endif
+# if !defined(_funlockfile)
+# define _funlockfile(fp) ({ if (!((fp)->_flags & __SSTR)) \
+ __cygwin_lock_unlock ((_LOCK_T *)&(fp)->_lock); })
+# endif
+#endif
+
+__BEGIN_DECLS
+
+#ifdef _GNU_SOURCE
+ssize_t _EXFUN(getline, (char **, size_t *, FILE *));
+ssize_t _EXFUN(getdelim, (char **, size_t *, int, FILE *));
+#endif /* _GNU_SOURCE */
+
+__END_DECLS
+
+#endif
diff --git a/winsup/cygwin/include/sys/termios.h b/winsup/cygwin/include/sys/termios.h
new file mode 100644
index 00000000000..e2fd1321a49
--- /dev/null
+++ b/winsup/cygwin/include/sys/termios.h
@@ -0,0 +1,354 @@
+/* sys/termios.h
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 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. */
+
+/* sys/termios.h */
+
+#ifndef _SYS_TERMIOS_H
+#define _SYS_TERMIOS_H
+
+#define TIOCMGET 0x5415
+#define TIOCMBIS 0x5416
+#define TIOCMBIC 0x5417
+#define TIOCMSET 0x5418
+#define TIOCINQ 0x541B
+
+/* TIOCINQ is utilized instead of FIONREAD which has been
+accupied for other purposes under CYGWIN.
+Other UNIX ioctl requests has been omited because
+effects of their work one can achive by standard
+POSIX commands */
+
+#define TIOCSBRK 0x5427
+#define TIOCCBRK 0x5428
+
+#define TIOCM_DTR 0x002
+#define TIOCM_RTS 0x004
+#define TIOCM_CTS 0x020
+#define TIOCM_CAR 0x040
+#define TIOCM_RNG 0x080
+#define TIOCM_DSR 0x100
+#define TIOCM_CD TIOCM_CAR
+#define TIOCM_RI TIOCM_RNG
+
+#define TCOOFF 0
+#define TCOON 1
+#define TCIOFF 2
+#define TCION 3
+
+#define TCGETA 5
+#define TCSETA 6
+#define TCSETAW 7
+#define TCSETAF 8
+
+#define TCIFLUSH 0
+#define TCOFLUSH 1
+#define TCIOFLUSH 2
+#define TCFLSH 3
+
+#define TCSAFLUSH 1
+#define TCSANOW 2
+#define TCSADRAIN 3
+#define TCSADFLUSH 4
+
+#define TIOCPKT 6
+
+#define TIOCPKT_DATA 0
+#define TIOCPKT_FLUSHREAD 1
+#define TIOCPKT_FLUSHWRITE 2
+#define TIOCPKT_STOP 4
+#define TIOCPKT_START 8
+#define TIOCPKT_NOSTOP 16
+#define TIOCPKT_DOSTOP 32
+
+#define FIONBIO 0x8004667e /* To be compatible with socket version */
+
+#define CTRL(ch) ((ch)&0x1F)
+
+#define CNUL 0
+#define CDEL 0x0007f
+#define CESC '\\'
+#define CINTR CTRL('C')
+#define CQUIT 0x0001c
+#define CERASE CTRL('H')
+#define CKILL CTRL('U')
+#define CEOT CTRL('D')
+#define CEOL 0
+#define CEOL2 0
+#define CEOF CTRL('D')
+#define CSTART CTRL('Q')
+#define CSTOP CTRL('S')
+#define CSWTCH 0x0001a
+#define NSWTCH 0
+#define CSUSP CTRL('Z')
+#define CDSUSP CTRL('Y')
+#define CRPRNT CTRL('R')
+#define CFLUSH CTRL('O')
+#define CWERASE CTRL('W')
+#define CLNEXT CTRL('V')
+
+/* iflag bits */
+#define IGNBRK 0x00001
+#define BRKINT 0x00002
+#define IGNPAR 0x00004
+#define IMAXBEL 0x00008
+#define INPCK 0x00010
+#define ISTRIP 0x00020
+#define INLCR 0x00040
+#define IGNCR 0x00080
+#define ICRNL 0x00100
+#define IXON 0x00400
+#define IXOFF 0x01000
+#define IUCLC 0x04000
+#define IXANY 0x08000
+#define PARMRK 0x10000
+
+/* oflag bits */
+
+#define OPOST 0x00001
+#define OLCUC 0x00002
+#define OCRNL 0x00004
+#define ONLCR 0x00008
+#define ONOCR 0x00010
+#define ONLRET 0x00020
+#define OFILL 0x00040
+#define CRDLY 0x00180
+#define CR0 0x00000
+#define CR1 0x00080
+#define CR2 0x00100
+#define CR3 0x00180
+#define NLDLY 0x00200
+#define NL0 0x00000
+#define NL1 0x00200
+#define BSDLY 0x00400
+#define BS0 0x00000
+#define BS1 0x00400
+#define TABDLY 0x01800
+#define TAB0 0x00000
+#define TAB1 0x00800
+#define TAB2 0x01000
+#define TAB3 0x01800
+#define XTABS 0x01800
+#define VTDLY 0x02000
+#define VT0 0x00000
+#define VT1 0x02000
+#define FFDLY 0x04000
+#define FF0 0x00000
+#define FF1 0x04000
+#define OFDEL 0x08000
+
+/* cflag bits */
+
+/* Baud rate values. These must fit in speed_t, which is unsigned
+ char. See also the extended baud rates below. These baud rates
+ set an additional bit. */
+#define CBAUD 0x0100f
+#define B0 0x00000
+#define B50 0x00001
+#define B75 0x00002
+#define B110 0x00003
+#define B134 0x00004
+#define B150 0x00005
+#define B200 0x00006
+#define B300 0x00007
+#define B600 0x00008
+#define B1200 0x00009
+#define B1800 0x0000a
+#define B2400 0x0000b
+#define B4800 0x0000c
+#define B9600 0x0000d
+#define B19200 0x0000e
+#define B38400 0x0000f
+
+#define CSIZE 0x00030
+#define CS5 0x00000
+#define CS6 0x00010
+#define CS7 0x00020
+#define CS8 0x00030
+#define CSTOPB 0x00040
+#define CREAD 0x00080
+#define PARENB 0x00100
+#define PARODD 0x00200
+#define HUPCL 0x00400
+#define CLOCAL 0x00800
+
+/* Extended baud rates above 37K. */
+#define CBAUDEX 0x0100f
+#define B57600 0x01001
+#define B115200 0x01002
+#define B128000 0x01003
+#define B230400 0x01004
+#define B256000 0x01005
+#define B460800 0x01006
+#define B500000 0x01007
+#define B576000 0x01008
+#define B921600 0x01009
+#define B1000000 0x0100a
+#define B1152000 0x0100b
+#define B1500000 0x0100c
+#define B2000000 0x0100d
+#define B2500000 0x0100e
+#define B3000000 0x0100f
+
+#define CRTSXOFF 0x04000
+#define CRTSCTS 0x08000
+
+/* lflag bits */
+#define ISIG 0x0001
+#define ICANON 0x0002
+#define ECHO 0x0004
+#define ECHOE 0x0008
+#define ECHOK 0x0010
+#define ECHONL 0x0020
+#define NOFLSH 0x0040
+#define TOSTOP 0x0080
+#define IEXTEN 0x0100
+#define FLUSHO 0x0200
+#define ECHOKE 0x0400
+#define ECHOCTL 0x0800
+
+#define VDISCARD 1
+#define VEOL 2
+#define VEOL2 3
+#define VEOF 4
+#define VERASE 5
+#define VINTR 6
+#define VKILL 7
+#define VLNEXT 8
+#define VMIN 9
+#define VQUIT 10
+#define VREPRINT 11
+#define VSTART 12
+#define VSTOP 13
+#define VSUSP 14
+#define VSWTC 15
+#define VTIME 16
+#define VWERASE 17
+
+#define NCCS 18
+
+/* `c_cc' member of 'struct termios' structure can be disabled by
+ using the value _POSIX_VDISABLE. */
+#define _POSIX_VDISABLE '\0'
+
+/* Compare a character C to a value VAL from the `c_cc' array in a
+ `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it. */
+#define CCEQ(val, c) ((c) == (val) && (val) != _POSIX_VDISABLE)
+
+typedef unsigned char cc_t;
+typedef unsigned int tcflag_t;
+typedef unsigned int speed_t;
+typedef unsigned short otcflag_t;
+typedef unsigned char ospeed_t;
+
+struct __oldtermios
+{
+ otcflag_t c_iflag;
+ otcflag_t c_oflag;
+ otcflag_t c_cflag;
+ otcflag_t c_lflag;
+ char c_line;
+ cc_t c_cc[NCCS];
+ ospeed_t c_ispeed;
+ ospeed_t c_ospeed;
+};
+
+struct termios
+{
+ tcflag_t c_iflag;
+ tcflag_t c_oflag;
+ tcflag_t c_cflag;
+ tcflag_t c_lflag;
+ char c_line;
+ cc_t c_cc[NCCS];
+ speed_t c_ispeed;
+ speed_t c_ospeed;
+};
+
+#ifdef CYGWIN_VERSION_DLL_IS_OLD_TERMIOS
+#ifdef __GNUC__
+# define __tonew_termios(ti) \
+ ({ \
+ struct termios *__newti; \
+ \
+ if (!CYGWIN_VERSION_DLL_IS_OLD_TERMIOS) \
+ __newti = (struct termios *) ti; \
+ else \
+ { \
+ __newti = (struct termios *) alloca(sizeof(struct termios)); \
+ __newti->c_iflag = ((struct __oldtermios *)ti)->c_iflag; \
+ __newti->c_oflag = ((struct __oldtermios *)ti)->c_oflag; \
+ __newti->c_cflag = ((struct __oldtermios *)ti)->c_cflag; \
+ __newti->c_lflag = ((struct __oldtermios *)ti)->c_lflag; \
+ __newti->c_line = ((struct __oldtermios *)ti)->c_line; \
+ __newti->c_ispeed = ((struct __oldtermios *)ti)->c_ispeed; \
+ __newti->c_ospeed = ((struct __oldtermios *)ti)->c_ospeed; \
+ memcpy (__newti->c_cc, ((struct __oldtermios *)ti)->c_cc, sizeof(__newti->c_cc)); \
+ } \
+ __newti; \
+ })
+
+# define __makenew_termios(ti) \
+ (CYGWIN_VERSION_DLL_IS_OLD_TERMIOS ? \
+ (struct termios *) alloca (sizeof (struct termios)) : (ti))
+
+# define __toapp_termios(toti, fromti) \
+ ({ \
+ if (!CYGWIN_VERSION_DLL_IS_OLD_TERMIOS) \
+ toti = fromti; \
+ else \
+ { \
+ ((struct __oldtermios *)toti)->c_iflag = fromti->c_iflag; \
+ ((struct __oldtermios *)toti)->c_oflag = fromti->c_oflag; \
+ ((struct __oldtermios *)toti)->c_cflag = fromti->c_cflag; \
+ ((struct __oldtermios *)toti)->c_lflag = fromti->c_lflag; \
+ ((struct __oldtermios *)toti)->c_line = fromti->c_line; \
+ ((struct __oldtermios *)toti)->c_ispeed = fromti->c_ispeed; \
+ ((struct __oldtermios *)toti)->c_ospeed = fromti->c_ospeed; \
+ memcpy (((struct __oldtermios*)toti)->c_cc, fromti->c_cc, sizeof(fromti->c_cc)); \
+ } \
+ toti; \
+ })
+#endif /*__GNUC__*/
+#endif
+
+#define termio termios
+
+#define cfgetospeed(tp) ((tp)->c_ospeed)
+#define cfgetispeed(tp) ((tp)->c_ispeed)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int tcgetattr (int, struct termios *);
+int tcsetattr (int, int, const struct termios *);
+int tcsendbreak (int, int);
+int tcdrain (int);
+int tcflush (int, int);
+int tcflow (int, int);
+int cfsetispeed (struct termios *, speed_t);
+int cfsetospeed (struct termios *, speed_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Extra stuff to make porting stuff easier. */
+struct winsize
+{
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+
+#define TIOCGWINSZ (('T' << 8) | 1)
+#define TIOCSWINSZ (('T' << 8) | 2)
+#define TIOCLINUX (('T' << 8) | 3)
+
+#endif /* _SYS_TERMIOS_H */
diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc
new file mode 100644
index 00000000000..08deb5635a8
--- /dev/null
+++ b/winsup/cygwin/init.cc
@@ -0,0 +1,163 @@
+/* init.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 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 <stdlib.h>
+#include "thread.h"
+#include "perprocess.h"
+#include "cygtls.h"
+#include "pinfo.h"
+#include <ntdef.h>
+#include "ntdll.h"
+
+static DWORD _my_oldfunc;
+
+int NO_COPY dynamically_loaded;
+static char NO_COPY *search_for = (char *) cygthread::stub;
+unsigned threadfunc_ix[8] __attribute__((section (".cygwin_dll_common"), shared));
+extern cygthread *hwait_sig;
+
+#define OLDFUNC_OFFSET -1
+
+static void WINAPI
+threadfunc_fe (VOID *arg)
+{
+ (void)__builtin_return_address(1);
+ asm volatile ("andl $-16,%%esp" ::: "%esp");
+ _cygtls::call ((DWORD (*) (void *, void *)) TlsGetValue (_my_oldfunc), arg);
+}
+
+/* If possible, redirect the thread entry point to a cygwin routine which
+ adds tls stuff to the stack. */
+static void
+munge_threadfunc ()
+{
+ int i;
+ char **ebp = (char **) __builtin_frame_address (0);
+ if (!threadfunc_ix[0])
+ {
+ char **peb;
+ char **top = (char **) _tlsbase;
+ for (peb = ebp, i = 0; peb < top && i < 7; peb++)
+ if (*peb == search_for)
+ threadfunc_ix[i++] = peb - ebp;
+ if (0 && !threadfunc_ix[0])
+ {
+ try_to_debug ();
+ return;
+ }
+ }
+
+ if (threadfunc_ix[0])
+ {
+ char *threadfunc = ebp[threadfunc_ix[0]];
+ if (!search_for || threadfunc == search_for)
+ {
+ search_for = NULL;
+ for (i = 0; threadfunc_ix[i]; i++)
+ ebp[threadfunc_ix[i]] = (char *) threadfunc_fe;
+ TlsSetValue (_my_oldfunc, threadfunc);
+ }
+ }
+}
+
+inline static void
+respawn_wow64_process ()
+{
+ NTSTATUS ret;
+ PROCESS_BASIC_INFORMATION pbi;
+ HANDLE parent;
+
+ BOOL is_wow64_proc = TRUE; /* Opt on the safe side. */
+
+ /* Unfortunately there's no simpler way to retrieve the
+ parent process in NT, as far as I know. Hints welcome. */
+ ret = NtQueryInformationProcess (GetCurrentProcess (),
+ ProcessBasicInformation,
+ (PVOID) &pbi,
+ sizeof pbi, NULL);
+ if (ret == STATUS_SUCCESS
+ && (parent = OpenProcess (PROCESS_QUERY_INFORMATION,
+ FALSE,
+ pbi.InheritedFromUniqueProcessId)))
+ {
+ IsWow64Process (parent, &is_wow64_proc);
+ CloseHandle (parent);
+ }
+
+ /* The parent is a real 64 bit process? Respawn! */
+ if (!is_wow64_proc)
+ {
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ DWORD ret = 0;
+
+ GetStartupInfo (&si);
+ if (!CreateProcessA (NULL, GetCommandLineA (), NULL, NULL, TRUE,
+ CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ()),
+ NULL, NULL, &si, &pi))
+ api_fatal ("Failed to create process <%s>, %E", GetCommandLineA ());
+ CloseHandle (pi.hThread);
+ if (WaitForSingleObject (pi.hProcess, INFINITE) == WAIT_FAILED)
+ api_fatal ("Waiting for process %d failed, %E", pi.dwProcessId);
+ GetExitCodeProcess (pi.hProcess, &ret);
+ CloseHandle (pi.hProcess);
+ ExitProcess (ret);
+ }
+}
+
+extern void __stdcall dll_crt0_0 ();
+
+HMODULE NO_COPY cygwin_hmodule;
+
+extern "C" BOOL WINAPI
+dll_entry (HANDLE h, DWORD reason, void *static_load)
+{
+ BOOL wow64_test_stack_marker;
+
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ wincap.init ();
+ init_console_handler (false);
+
+ cygwin_hmodule = (HMODULE) h;
+ dynamically_loaded = (static_load == NULL);
+
+ /* Is the stack at an unusual address? This is, an address which
+ is in the usual space occupied by the process image, but below
+ the auto load address of DLLs?
+ Check if we're running in WOW64 on a 64 bit machine *and* are
+ spawned by a genuine 64 bit process. If so, respawn. */
+ if (wincap.is_wow64 ()
+ && &wow64_test_stack_marker >= (PBOOL) 0x400000
+ && &wow64_test_stack_marker <= (PBOOL) 0x10000000)
+ respawn_wow64_process ();
+
+ dll_crt0_0 ();
+ _my_oldfunc = TlsAlloc ();
+ break;
+ case DLL_PROCESS_DETACH:
+ break;
+ case DLL_THREAD_ATTACH:
+ if (hwait_sig)
+ munge_threadfunc ();
+ break;
+ case DLL_THREAD_DETACH:
+ if (hwait_sig && (void *) &_my_tls > (void *) &wow64_test_stack_marker
+ && _my_tls.isinitialized ())
+ _my_tls.remove (0);
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/winsup/cygwin/localtime.cc b/winsup/cygwin/localtime.cc
new file mode 100644
index 00000000000..1dab62793f4
--- /dev/null
+++ b/winsup/cygwin/localtime.cc
@@ -0,0 +1,2170 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+/* Temporarily merged private.h and tzfile.h for ease of management - DJ */
+
+#include "winsup.h"
+#include "cygerrno.h"
+#include <windows.h>
+#define STD_INSPIRED
+#define lint
+
+#define USG_COMPAT
+
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)localtime.c 7.66";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
+** POSIX-style TZ environment variable handling from Guy Harris
+** (guy@auspex.com).
+*/
+
+/*LINTLIBRARY*/
+
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char privatehid[] = "@(#)private.h 7.48";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME 1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY 3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 0
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H 0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "limits.h" /* for CHAR_BIT */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT - 0
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#if HAVE_UNISTD_H - 0
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H - 0 */
+
+#if !(HAVE_UNISTD_H - 0)
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !(HAVE_UNISTD_H - 0) */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** SunOS 4.1.1 cc lacks const.
+*/
+
+#ifndef const
+#ifndef __STDC__
+#define const
+#endif /* !defined __STDC__ */
+#endif /* !defined const */
+
+/*
+** SunOS 4.1.1 cc lacks prototypes.
+*/
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x) ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX 1024 /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
+
+#endif /* !defined PRIVATE_H */
+
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char tzfilehid[] = "@(#)tzfile.h 7.14";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_reserved[16]; /* reserved for future use */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if true, transition
+** time is standard time, if false,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if true, transition
+** time is UTC, if false,
+** transition time is local time
+** if absent, transition times are
+** assumed to be local time
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+/*
+** The TZ_MAX_TIMES value below is enough to handle a bit more than a
+** year's worth of solar time (corrected daily to the nearest second) or
+** 138 years of Pacific Presidential Election time
+** (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES 370
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew <earl@hpato.aus.hp.com>.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+/*
+** Accurate only for the past couple of centuries;
+** that will probably do.
+*/
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+#ifndef USG
+
+/*
+** Use of the underscored variants may cause problems if you move your code to
+** certain System-V-based systems; for maximum portability, use the
+** underscore-free variants. The underscored variants are provided for
+** backward compatibility only; they may disappear from future versions of
+** this file.
+*/
+
+#define SECS_PER_MIN SECSPERMIN
+#define MINS_PER_HOUR MINSPERHOUR
+#define HOURS_PER_DAY HOURSPERDAY
+#define DAYS_PER_WEEK DAYSPERWEEK
+#define DAYS_PER_NYEAR DAYSPERNYEAR
+#define DAYS_PER_LYEAR DAYSPERLYEAR
+#define SECS_PER_HOUR SECSPERHOUR
+#define SECS_PER_DAY SECSPERDAY
+#define MONS_PER_YEAR MONSPERYEAR
+
+#endif /* !defined USG */
+
+#endif /* !defined TZFILE_H */
+
+#include "fcntl.h"
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+** 1. They might reference tzname[0] before calling tzset (explicitly
+** or implicitly).
+** 2. They might reference tzname[1] before calling tzset (explicitly
+** or implicitly).
+** 3. They might reference tzname[1] after setting to a time zone
+** in which Daylight Saving Time is never observed.
+** 4. They might reference tzname[0] after setting to a time zone
+** in which Standard Time is never observed.
+** 5. They might reference tm.TM_ZONE after calling offtime.
+** What's best to do in the above cases is open to debate;
+** for now, we just set things up so that in any of the five cases
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
+** string "tzname[0] used before set", and similarly for the other cases.
+** And another: initialize tzname[0] to "ERA", with an explanation in the
+** manual page of what this "time zone abbreviation" means (doing this so
+** that tzname[0] has the "normal" length of three characters).
+*/
+#define WILDABBR " "
+#endif /* !defined WILDABBR */
+
+static char wildabbr[] NO_COPY = WILDABBR;
+
+static char gmt[] NO_COPY = "GMT";
+
+struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* true if transition is std time */
+ int tt_ttisgmt; /* true if transition is UTC */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long detzcode P((const char * codep));
+static const char * getzname P((const char * strp));
+static const char * getnum P((const char * strp, int * nump, int min,
+ int max));
+static const char * getsecs P((const char * strp, long * secsp));
+static const char * getoffset P((const char * strp, long * offsetp));
+static const char * getrule P((const char * strp, struct rule * rulep));
+static void gmtload P((struct state * sp));
+static void gmtsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static void localsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static int increment_overflow P((int * number, int delta));
+static int normalize_overflow P((int * tensptr, int * unitsptr,
+ int base));
+static void settzname P((void));
+static time_t time1 P((struct tm * tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm *)),
+ long offset));
+static time_t time2 P((struct tm *tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm*)),
+ long offset, int * okayp));
+static time_t time2sub P((struct tm *tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm*)),
+ long offset, int * okayp, int do_norm_secs));
+static void timesub P((const time_t * timep, long offset,
+ const struct state * sp, struct tm * tmp));
+static int tmcomp P((const struct tm * atmp,
+ const struct tm * btmp));
+static time_t transtime P((time_t janfirst, int year,
+ const struct rule * rulep, long offset));
+static int tzload P((const char * name, struct state * sp));
+static int tzparse P((const char * name, struct state * sp,
+ int lastditch));
+
+#ifdef ALL_STATE
+static struct state * lclptr;
+static struct state * gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state lclmem;
+static struct state gmtmem;
+#define lclptr (&lclmem)
+#define gmtptr (&gmtmem)
+#endif /* State Farm */
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+static char lcl_TZname[TZ_STRLEN_MAX + 1];
+static int lcl_is_set;
+static int gmt_is_set;
+
+#define tzname _tzname
+#undef _tzname
+
+char * tzname[2] = {
+ wildabbr,
+ wildabbr
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+** Except for the strftime function, these functions [asctime,
+** ctime, gmtime, localtime] return values in one of two static
+** objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+*/
+
+static struct tm tm;
+
+
+/* These variables are initialized by tzset. The macro versions are
+ defined in time.h, and indirect through the __imp_ pointers. */
+
+#define timezone _timezone
+#define daylight _daylight
+#undef _timezone
+#undef _daylight
+
+#ifdef USG_COMPAT
+long timezone; /* was time_t but POSIX requires long. */
+int daylight;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t altzone;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(const char *codep)
+{
+ register long result;
+ register int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0L;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static void
+settzname P((void))
+{
+ register struct state * const sp = lclptr;
+ register int i;
+
+ tzname[0] = wildabbr;
+ tzname[1] = wildabbr;
+#ifdef USG_COMPAT
+ daylight = 0;
+ timezone = 0;
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ altzone = 0;
+#endif /* defined ALTZONE */
+#ifdef ALL_STATE
+ if (sp == NULL) {
+ tzname[0] = tzname[1] = gmt;
+ return;
+ }
+#endif /* defined ALL_STATE */
+ for (i = 0; i < sp->typecnt; ++i) {
+ register const struct ttinfo * const ttisp = &sp->ttis[i];
+
+ tzname[ttisp->tt_isdst] =
+ &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+ if (ttisp->tt_isdst)
+ daylight = 1;
+ if (i == 0 || !ttisp->tt_isdst)
+ timezone = -(ttisp->tt_gmtoff);
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ if (i == 0 || ttisp->tt_isdst)
+ altzone = -(ttisp->tt_gmtoff);
+#endif /* defined ALTZONE */
+ }
+ /*
+ ** And to get the latest zone names into tzname. . .
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ register const struct ttinfo * const ttisp =
+ &sp->ttis[
+ sp->types[i]];
+
+ tzname[ttisp->tt_isdst] =
+ &sp->chars[ttisp->tt_abbrind];
+ }
+}
+
+#include "tz_posixrules.h"
+
+static int
+tzload(const char *name, struct state *sp)
+{
+ register const char * p;
+ register int i;
+ register int fid;
+ save_errno save;
+
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ register int doaccess;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ strcpy(fullname, p);
+ strcat(fullname, "/");
+ strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = true;
+ name = fullname;
+ }
+#if 0
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+#endif
+ if ((fid = open(name, OPEN_MODE)) == -1)
+ {
+ const char *base = strrchr(name, '/');
+ if (base)
+ base++;
+ else
+ base = name;
+ if (strcmp(base, "posixrules"))
+ return -1;
+
+ /* We've got a built-in copy of posixrules just in case */
+ fid = -2;
+ }
+ }
+ {
+ struct tzhead * tzhp;
+ union {
+ struct tzhead tzhead;
+ char buf[sizeof *sp + sizeof *tzhp];
+ } u;
+ int ttisstdcnt;
+ int ttisgmtcnt;
+
+ if (fid == -2)
+ {
+ memcpy(u.buf, _posixrules_data, sizeof (_posixrules_data));
+ i = sizeof (_posixrules_data);
+ }
+ else
+ {
+ i = read(fid, u.buf, sizeof u.buf);
+ if (close(fid) != 0)
+ return -1;
+ }
+ ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+ ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+ sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+ sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+ sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+ sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+ p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return -1;
+ if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * (4 + 2) + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (4 + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->ats[i] = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->types[i] = (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return -1;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ register struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = detzcode(p);
+ p += 4;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = false;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != true &&
+ ttisp->tt_ttisstd != false)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = false;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != true &&
+ ttisp->tt_ttisgmt != false)
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char *
+getzname(const char *strp)
+{
+ register char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(const char *strp, int *nump, const int min, const int max)
+{
+ register char c;
+ register int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(const char *strp, long *secsp)
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(const char *strp, long *offsetp)
+{
+ register int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(const char *strp, struct rule *rulep)
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t
+transtime(const time_t janfirst, const int year, const struct rule *rulep,
+ long offset)
+{
+ register int leapyear;
+ register time_t value;
+ register int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "dow" is the day-of-week of the first day of the month. Get
+ ** the day-of-month (zero-origin) of the first "dow" day of the
+ ** month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(const char *name, struct state *sp, const int lastditch)
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ register time_t * atp;
+ register unsigned char * typep;
+ register char * cp;
+ register int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ if (stdlen < 3)
+ return -1;
+ if (*name == '\0')
+ return -1;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ if (dstlen < 3)
+ return -1;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR to 2037.
+ */
+ sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
+ if (sp->timecnt > TZ_MAX_TIMES)
+ return -1;
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ for (year = EPOCH_YEAR; year <= 2037; ++year) {
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ janfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ }
+ __gettzinfo ()->__tzrule[0].offset
+ = -sp->ttis[1].tt_gmtoff;
+ __gettzinfo ()->__tzrule[1].offset
+ = -sp->ttis[0].tt_gmtoff;
+ } else {
+ register long theirstdoffset;
+ register long theirdstoffset;
+ register long theiroffset;
+ register int isdst;
+ register int i;
+ register int j;
+
+ if (*name != '\0')
+ return -1;
+ if (load_result != 0)
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = false;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = false;
+ sp->ttis[0].tt_abbrind = 0;
+ sp->ttis[1].tt_gmtoff = -dstoffset;
+ sp->ttis[1].tt_isdst = true;
+ sp->ttis[1].tt_abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ __gettzinfo ()->__tzrule[0].offset
+ = -sp->ttis[0].tt_gmtoff;
+ __gettzinfo ()->__tzrule[1].offset
+ = -sp->ttis[1].tt_gmtoff;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ __gettzinfo ()->__tzrule[0].offset = -sp->ttis[0].tt_gmtoff;
+ __gettzinfo ()->__tzrule[1].offset = -sp->ttis[0].tt_gmtoff;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static void
+gmtload(struct state *sp)
+{
+ if (tzload(gmt, sp) != 0)
+ tzparse(gmt, sp, true);
+}
+
+#ifndef STD_INSPIRED
+/*
+** A non-static declaration of tzsetwall in a system header file
+** may cause a warning about this upcoming static declaration...
+*/
+static
+#endif /* !defined STD_INSPIRED */
+void
+tzsetwall P((void))
+{
+ if (lcl_is_set < 0)
+ return;
+ lcl_is_set = -1;
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+#if defined (_WIN32) || defined (__CYGWIN__)
+#define is_upper(c) ((unsigned)(c) - 'A' <= 26)
+ {
+ TIME_ZONE_INFORMATION tz;
+ char buf[BUFSIZ];
+ char *cp, *dst;
+ wchar_t *src;
+ div_t d;
+ GetTimeZoneInformation(&tz);
+ dst = cp = buf;
+ for (src = tz.StandardName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if ((dst - cp) < 3)
+ {
+ /* In non-english Windows, converted tz.StandardName
+ may not contain a valid standard timezone name. */
+ strcpy(cp, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.StandardBias, 60);
+ sprintf(cp, "%d", d.quot);
+ if (d.rem)
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ if(tz.StandardDate.wMonth) {
+ cp = strchr(cp, 0);
+ dst = cp;
+ for (src = tz.DaylightName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if ((dst - cp) < 3)
+ {
+ /* In non-english Windows, converted tz.DaylightName
+ may not contain a valid daylight timezone name. */
+ strcpy(cp, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.DaylightBias, 60);
+ sprintf(cp, "%d", d.quot);
+ if (d.rem)
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.DaylightDate.wMonth,
+ tz.DaylightDate.wDay,
+ tz.DaylightDate.wDayOfWeek,
+ tz.DaylightDate.wHour);
+ if (tz.DaylightDate.wMinute || tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wMinute);
+ if (tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wSecond);
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.StandardDate.wMonth,
+ tz.StandardDate.wDay,
+ tz.StandardDate.wDayOfWeek,
+ tz.StandardDate.wHour);
+ if (tz.StandardDate.wMinute || tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wMinute);
+ if (tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wSecond);
+ }
+ /* printf("TZ deduced as `%s'\n", buf); */
+ if (tzparse(buf, lclptr, false) == 0) {
+ settzname();
+ lcl_is_set = 1;
+ strlcpy(lcl_TZname, buf, sizeof (lcl_TZname));
+#if 0
+ /* Huh? POSIX doesn't mention anywhere that tzset should
+ set $TZ. That's not right. */
+ setenv("TZ", lcl_TZname, 1);
+#endif
+ return;
+ }
+ }
+#endif
+ if (tzload((char *) NULL, lclptr) != 0)
+ gmtload(lclptr);
+ settzname();
+}
+
+extern "C" void
+tzset P((void))
+{
+ const char * name = getenv("TZ");
+
+ if (name == NULL) {
+ tzsetwall();
+ return;
+ }
+
+ if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
+ return;
+ lcl_is_set = (strlen(name) < sizeof (lcl_TZname));
+ if (lcl_is_set)
+ strcpy(lcl_TZname, name);
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (*name == '\0') {
+ /*
+ ** User wants it fast rather than right.
+ */
+ lclptr->leapcnt = 0; /* so, we're off a little */
+ lclptr->timecnt = 0;
+ lclptr->ttis[0].tt_gmtoff = 0;
+ lclptr->ttis[0].tt_abbrind = 0;
+ strcpy(lclptr->chars, gmt);
+ } else if (tzload(name, lclptr) != 0) {
+ if (name[0] == ':' || tzparse(name, lclptr, false) != 0)
+ gmtload(lclptr);
+ }
+ settzname();
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static void
+localsub (const time_t * const timep,
+ const long offset,
+ struct tm * const tmp)
+{
+ register struct state * sp;
+ register const struct ttinfo * ttisp;
+ register int i;
+ const time_t t = *timep;
+
+ sp = lclptr;
+#ifdef ALL_STATE
+ if (sp == NULL) {
+ gmtsub(timep, offset, tmp);
+ return;
+ }
+#endif /* defined ALL_STATE */
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ } else {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (t < sp->ats[i])
+ break;
+ i = sp->types[i - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->tt_isdst;
+ tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+}
+
+extern "C" struct tm *
+localtime(const time_t *timep)
+{
+ tzset();
+ localsub(timep, 0L, &tm);
+ return &tm;
+}
+
+/*
+ * Re-entrant version of localtime
+ */
+extern "C" struct tm *
+localtime_r(const time_t *timep, struct tm *tm)
+{
+ tzset();
+ localsub(timep, 0L, tm);
+ return tm;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static void
+gmtsub(const time_t *timep, const long offset, struct tm *tmp)
+{
+ if (!gmt_is_set) {
+ gmt_is_set = true;
+#ifdef ALL_STATE
+ gmtptr = (struct state *) malloc(sizeof *gmtptr);
+ if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+ gmtload(gmtptr);
+ }
+ timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = wildabbr;
+ else {
+#ifdef ALL_STATE
+ if (gmtptr == NULL)
+ tmp->TM_ZONE = gmt;
+ else tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+ }
+#endif /* defined TM_ZONE */
+}
+
+extern "C" struct tm *
+gmtime(const time_t *timep)
+{
+ gmtsub(timep, 0L, &tm);
+ return &tm;
+}
+
+/*
+ * Re-entrant version of gmtime
+ */
+extern "C" struct tm *
+gmtime_r(const time_t *timep, struct tm *tm)
+{
+ gmtsub(timep, 0L, tm);
+ return tm;
+}
+
+#ifdef STD_INSPIRED
+
+extern "C" struct tm *
+offtime(const time_t *timep, const long offset)
+{
+ gmtsub(timep, offset, &tm);
+ return &tm;
+}
+
+#endif /* defined STD_INSPIRED */
+
+static void
+timesub(const time_t *timep, const long offset, const struct state *sp,
+ struct tm *tmp)
+{
+ register const struct lsinfo * lp;
+ register long days;
+ register long rem;
+ register int y;
+ register int yleap;
+ register const int * ip;
+ register long corr;
+ register int hit;
+ register int i;
+
+ corr = 0;
+ hit = 0;
+#ifdef ALL_STATE
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ i = sp->leapcnt;
+#endif /* State Farm */
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ if (*timep == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ days = *timep / SECSPERDAY;
+ rem = *timep % SECSPERDAY;
+#ifdef mc68k
+ if (*timep == 0x80000000) {
+ /*
+ ** A 3B1 muffs the division on the most negative number.
+ */
+ days = -24855;
+ rem = -11648;
+ }
+#endif /* defined mc68k */
+ rem += (offset - corr);
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --days;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++days;
+ }
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem = rem % SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ y = EPOCH_YEAR;
+#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+ while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
+ register int newy;
+
+ newy = y + days / DAYSPERNYEAR;
+ if (days < 0)
+ --newy;
+ days -= (newy - y) * DAYSPERNYEAR +
+ LEAPS_THRU_END_OF(newy - 1) -
+ LEAPS_THRU_END_OF(y - 1);
+ y = newy;
+ }
+ tmp->tm_year = y - TM_YEAR_BASE;
+ tmp->tm_yday = (int) days;
+ ip = mon_lengths[yleap];
+ for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ days = days - (long) ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (days + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+}
+
+extern "C" char *
+ctime(const time_t *timep)
+{
+/*
+** Section 4.12.3.2 of X3.159-1989 requires that
+** The ctime function converts the calendar time pointed to by timer
+** to local time in the form of a string. It is equivalent to
+** asctime(localtime(timer))
+*/
+ return asctime(localtime(timep));
+}
+
+extern "C" char *
+ctime_r(const time_t *timep, char *buf)
+{
+ struct tm tm;
+
+ return asctime_r(localtime_r(timep, &tm), buf);
+}
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** [kridle@xinet.com as of 1996-01-16.]
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+*/
+
+static int
+increment_overflow(int *number, int delta)
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
+normalize_overflow(int *tensptr, int *unitsptr, const int base)
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+tmcomp(register const struct tm *atmp, register const struct tm *btmp)
+{
+ register int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+static time_t
+time2sub(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)),
+ const long offset, int *okayp, const int do_norm_secs)
+{
+ register const struct state * sp;
+ register int dir;
+ register int bits;
+ register int i, j ;
+ register int saved_seconds;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
+
+ *okayp = false;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn yourtm.tm_year into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (increment_overflow(&yourtm.tm_year, -1))
+ return WRONG;
+ i = yourtm.tm_year + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(i)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ i = yourtm.tm_year + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(i)];
+ if (increment_overflow(&yourtm.tm_year, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (increment_overflow(&yourtm.tm_year, 1))
+ return WRONG;
+ }
+ }
+ if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+ return WRONG;
+ if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Divide the search space in half
+ ** (this works whether time_t is signed or unsigned).
+ */
+ bits = TYPE_BIT(time_t) - 1;
+ /*
+ ** If time_t is signed, then 0 is just above the median,
+ ** assuming two's complement arithmetic.
+ ** If time_t is unsigned, then (1 << bits) is just above the median.
+ */
+ t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
+ for ( ; ; ) {
+ (*funcp)(&t, offset, &mytm);
+ dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (bits-- < 0)
+ return WRONG;
+ if (bits < 0)
+ --t; /* may be needed if new t is minimal */
+ else if (dir > 0)
+ t -= ((time_t) 1) << bits;
+ else t += ((time_t) 1) << bits;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ sp = (const struct state *)
+ (((void *) funcp == (void *) localsub) ?
+ lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = t + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ (*funcp)(&newt, offset, &mytm);
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ (*funcp)(&t, offset, tmp);
+ *okayp = true;
+ return t;
+}
+
+static time_t
+time2(struct tm *tmp, void (*funcp) P((const time_t*, long, struct tm*)),
+ const long offset, int *okayp)
+{
+ time_t t;
+
+ /*
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, offset, okayp, false);
+ return *okayp ? t : time2sub(tmp, funcp, offset, okayp, true);
+}
+
+static time_t
+time1(struct tm *tmp, void (*funcp) P((const time_t *, long, struct tm *)),
+ const long offset)
+{
+ register time_t t;
+ register const struct state * sp;
+ register int samei, otheri;
+ int okay;
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, offset, &okay);
+#ifdef PCTS
+ /*
+ ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
+ lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (samei = sp->typecnt - 1; samei >= 0; --samei) {
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+extern "C" time_t
+mktime(struct tm *tmp)
+{
+ tzset();
+ return time1(tmp, localsub, 0L);
+}
+
+#ifdef STD_INSPIRED
+
+extern "C" time_t
+timelocal(struct tm *tmp)
+{
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
+}
+
+extern "C" time_t
+timegm(struct tm *tmp)
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, 0L);
+}
+
+extern "C" time_t
+timeoff(struct tm *tmp, const long offset)
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, offset);
+}
+
+#endif /* defined STD_INSPIRED */
+
+#ifdef CMUCS
+
+/*
+** The following is supplied for compatibility with
+** previous versions of the CMUCS runtime library.
+*/
+
+extern "C" long
+gtime(struct tm *tmp)
+{
+ const time_t t = mktime(tmp);
+
+ if (t == WRONG)
+ return -1;
+ return t;
+}
+
+#endif /* defined CMUCS */
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/*
+** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
+** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which
+** is not the case if we are accounting for leap seconds.
+** So, we provide the following conversion routines for use
+** when exchanging timestamps with POSIX conforming systems.
+*/
+
+static long
+leapcorr(time_t *timep)
+{
+ register struct state * sp;
+ register struct lsinfo * lp;
+ register int i;
+
+ sp = lclptr;
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+extern "C" time_t
+time2posix(time_t t)
+{
+ tzset();
+ return t - leapcorr(&t);
+}
+
+extern "C" time_t
+posix2time(time_t t)
+{
+ time_t x;
+ time_t y;
+
+ tzset();
+ /*
+ ** For a positive leap second hit, the result
+ ** is not unique. For a negative leap second
+ ** hit, the corresponding time doesn't exist,
+ ** so we return an adjacent second.
+ */
+ x = t + leapcorr(&t);
+ y = x - leapcorr(&x);
+ if (y < t) {
+ do {
+ x++;
+ y = x - leapcorr(&x);
+ } while (y < t);
+ if (t != y)
+ return x - 1;
+ } else if (y > t) {
+ do {
+ --x;
+ y = x - leapcorr(&x);
+ } while (y > t);
+ if (t != y)
+ return x + 1;
+ }
+ return x;
+}
+
+#endif /* defined STD_INSPIRED */
diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc
index d7eed339362..88b98523339 100644
--- a/winsup/cygwin/mmap.cc
+++ b/winsup/cygwin/mmap.cc
@@ -49,8 +49,6 @@ details. */
/* Used for anonymous mappings. */
static fhandler_dev_zero fh_anonymous;
-/* Used for reopening a disk file when necessary. */
-static fhandler_disk_file fh_disk_file;
/* Small helpers to avoid having lots of flag bit tests in the code. */
static inline bool
@@ -983,6 +981,8 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
caddr_t ret = (caddr_t) MAP_FAILED;
fhandler_base *fh = NULL;
+ fhandler_disk_file *fh_disk_file = NULL; /* Used for reopening a disk file
+ when necessary. */
list *map_list = NULL;
size_t orig_len = 0;
caddr_t base = NULL;
@@ -992,7 +992,6 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
fh_anonymous.set_io_handle (INVALID_HANDLE_VALUE);
fh_anonymous.set_access (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
- fh_disk_file.set_io_handle (NULL);
SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
@@ -1078,12 +1077,13 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
set_errno (EACCES);
goto out;
}
- fh_disk_file.set_io_handle (h);
- fh_disk_file.set_access (fh->get_access () | GENERIC_WRITE);
+ fh_disk_file = new (alloca (sizeof *fh_disk_file)) fhandler_disk_file;
path_conv pc;
pc.set_name (fh->get_win32_name (), "");
- fh_disk_file.set_name (pc);
- fh = &fh_disk_file;
+ fh_disk_file->set_name (pc);
+ fh_disk_file->set_io_handle (h);
+ fh_disk_file->set_access (fh->get_access () | GENERIC_WRITE);
+ fh = fh_disk_file;
}
/* On NT you can't create mappings with PAGE_EXECUTE protection if
@@ -1096,9 +1096,13 @@ mmap64 (void *addr, size_t len, int prot, int flags, int fd, _off64_t off)
OPEN_EXISTING, 0, NULL);
if (h != INVALID_HANDLE_VALUE)
{
- fh_disk_file.set_io_handle (h);
- fh_disk_file.set_access (fh->get_access () | GENERIC_EXECUTE);
- fh = &fh_disk_file;
+ fh_disk_file = new (alloca (sizeof *fh_disk_file)) fhandler_disk_file;
+ path_conv pc;
+ pc.set_name (fh->get_win32_name (), "");
+ fh_disk_file->set_name (pc);
+ fh_disk_file->set_io_handle (h);
+ fh_disk_file->set_access (fh->get_access () | GENERIC_EXECUTE);
+ fh = fh_disk_file;
}
else if (prot & PROT_EXEC)
{
@@ -1294,8 +1298,8 @@ out:
ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
- if (fh_disk_file.get_handle ())
- CloseHandle (fh_disk_file.get_handle ());
+ if (fh_disk_file)
+ CloseHandle (fh_disk_file->get_handle ());
syscall_printf ("%p = mmap() ", ret);
return ret;
diff --git a/winsup/cygwin/security.cc b/winsup/cygwin/security.cc
index cab8a2baba0..ad12975b79d 100644
--- a/winsup/cygwin/security.cc
+++ b/winsup/cygwin/security.cc
@@ -465,16 +465,14 @@ get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps,
auth_pos = -1;
if (my_grps)
{
- if (sid_in_token_groups (my_grps, well_known_local_sid))
- grp_list += well_known_local_sid;
+ grp_list += well_known_local_sid;
if (sid_in_token_groups (my_grps, well_known_dialup_sid))
grp_list += well_known_dialup_sid;
if (sid_in_token_groups (my_grps, well_known_network_sid))
grp_list += well_known_network_sid;
if (sid_in_token_groups (my_grps, well_known_batch_sid))
grp_list += well_known_batch_sid;
- if (sid_in_token_groups (my_grps, well_known_interactive_sid))
- grp_list += well_known_interactive_sid;
+ grp_list += well_known_interactive_sid;
if (sid_in_token_groups (my_grps, well_known_service_sid))
grp_list += well_known_service_sid;
}
@@ -485,11 +483,13 @@ get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps,
}
if (get_ll (auth_luid) != 999LL) /* != SYSTEM_LUID */
{
- char buf[64];
- __small_sprintf (buf, "S-1-5-5-%u-%u", auth_luid.HighPart,
- auth_luid.LowPart);
- grp_list += buf;
- auth_pos = grp_list.count - 1;
+ for (DWORD i = 0; i < my_grps->GroupCount; ++i)
+ if (my_grps->Groups[i].Attributes & SE_GROUP_LOGON_ID)
+ {
+ grp_list += my_grps->Groups[i].Sid;
+ auth_pos = grp_list.count - 1;
+ break;
+ }
}
}
diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc
index c66912b3f24..820c4cf7650 100644
--- a/winsup/cygwin/shared.cc
+++ b/winsup/cygwin/shared.cc
@@ -262,7 +262,7 @@ memory_init ()
unsigned
shared_info::heap_slop_size ()
{
- if (!heap_slop)
+ if (!heap_slop_inited)
{
/* Fetch from registry, first user then local machine. */
for (int i = 0; i < 2; i++)
@@ -273,11 +273,8 @@ shared_info::heap_slop_size ()
break;
heap_slop = wincap.heapslop ();
}
-
- if (heap_slop < 0)
- heap_slop = 0;
- else
- heap_slop <<= 20;
+ heap_slop <<= 20;
+ heap_slop_inited = true;
}
return heap_slop;
diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h
index db9fc97e6ba..6711c2de96a 100644
--- a/winsup/cygwin/shared_info.h
+++ b/winsup/cygwin/shared_info.h
@@ -142,9 +142,9 @@ public:
cygwin_version.api_minor)
#define SHARED_VERSION_MAGIC CYGWIN_VERSION_MAGIC (SHARED_MAGIC, SHARED_VERSION)
-#define SHARED_INFO_CB 19988
+#define SHARED_INFO_CB 19992
-#define CURR_SHARED_MAGIC 0x87c42d1eU
+#define CURR_SHARED_MAGIC 0xb7048a88U
/* NOTE: Do not make gratuitous changes to the names or organization of the
below class. The layout is checksummed to determine compatibility between
@@ -155,6 +155,7 @@ class shared_info
DWORD cb;
public:
unsigned heap_chunk;
+ bool heap_slop_inited;
unsigned heap_slop;
DWORD sys_mount_table_counter;
diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc
new file mode 100644
index 00000000000..3170fcae3f4
--- /dev/null
+++ b/winsup/cygwin/shm.cc
@@ -0,0 +1,411 @@
+/* shm.cc: XSI IPC interface for Cygwin.
+
+ Copyright 2003 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 "cygerrno.h"
+#include <signal.h>
+#ifdef USE_SERVER
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "pinfo.h"
+#include "sigproc.h"
+
+#include "cygserver_ipc.h"
+#include "cygserver_shm.h"
+#include "cygtls.h"
+#include "sync.h"
+
+/*
+ * client_request_shm Constructors
+ */
+
+client_request_shm::client_request_shm (int shmid,
+ const void *shmaddr,
+ int shmflg)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmat;
+ ipc_set_proc_info (_parameters.in.ipcblk);
+
+ _parameters.in.atargs.shmid = shmid;
+ _parameters.in.atargs.shmaddr = shmaddr;
+ _parameters.in.atargs.shmflg = shmflg;
+
+ msglen (sizeof (_parameters.in));
+}
+
+client_request_shm::client_request_shm (int shmid,
+ int cmd,
+ struct shmid_ds *buf)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmctl;
+ ipc_set_proc_info (_parameters.in.ipcblk);
+
+ _parameters.in.ctlargs.shmid = shmid;
+ _parameters.in.ctlargs.cmd = cmd;
+ _parameters.in.ctlargs.buf = buf;
+
+ msglen (sizeof (_parameters.in));
+}
+
+client_request_shm::client_request_shm (const void *shmaddr)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmdt;
+ ipc_set_proc_info (_parameters.in.ipcblk);
+
+ _parameters.in.dtargs.shmaddr = shmaddr;
+
+ msglen (sizeof (_parameters.in));
+}
+
+client_request_shm::client_request_shm (key_t key,
+ size_t size,
+ int shmflg)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmget;
+ ipc_set_proc_info (_parameters.in.ipcblk);
+
+ _parameters.in.getargs.key = key;
+ _parameters.in.getargs.size = size;
+ _parameters.in.getargs.shmflg = shmflg;
+
+ msglen (sizeof (_parameters.in));
+}
+
+client_request_shm::client_request_shm (proc *p1)
+ : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters))
+{
+ _parameters.in.shmop = SHMOP_shmfork;
+ ipc_set_proc_info (_parameters.in.ipcblk);
+
+ _parameters.in.forkargs = *p1;
+}
+
+/* List of shmid's with file mapping HANDLE and size, returned by shmget. */
+struct shm_shmid_list {
+ SLIST_ENTRY (shm_shmid_list) ssh_next;
+ int shmid;
+ vm_object_t hdl;
+ size_t size;
+ int ref_count;
+};
+
+static SLIST_HEAD (, shm_shmid_list) ssh_list;
+
+/* List of attached mappings, as returned by shmat. */
+struct shm_attached_list {
+ SLIST_ENTRY (shm_attached_list) sph_next;
+ vm_object_t ptr;
+ shm_shmid_list *parent;
+ int access;
+};
+
+static SLIST_HEAD (, shm_attached_list) sph_list;
+
+static NO_COPY muto shm_guard;
+#define SLIST_LOCK() (shm_guard.init ("shm_guard")->acquire ())
+#define SLIST_UNLOCK() (shm_guard.release ())
+
+int __stdcall
+fixup_shms_after_fork ()
+{
+ if (!SLIST_FIRST (&sph_list))
+ return 0;
+ pinfo p (myself->ppid);
+ proc parent = { myself->ppid, p->dwProcessId, p->uid, p->gid };
+
+ client_request_shm request (&parent);
+ if (request.make_request () == -1 || request.retval () == -1)
+ {
+ syscall_printf ("-1 [%d] = fixup_shms_after_fork ()", request.error_code ());
+ set_errno (request.error_code ());
+ return 0;
+ }
+ shm_attached_list *sph_entry;
+ /* Remove map from list... */
+ SLIST_FOREACH (sph_entry, &sph_list, sph_next)
+ {
+ vm_object_t ptr = MapViewOfFileEx (sph_entry->parent->hdl,
+ sph_entry->access, 0, 0,
+ sph_entry->parent->size,
+ sph_entry->ptr);
+ if (ptr != sph_entry->ptr)
+ api_fatal ("MapViewOfFileEx (%p), %E. Terminating.", sph_entry->ptr);
+ }
+ return 0;
+}
+#endif /* USE_SERVER */
+
+/*
+ * XSI shmaphore API. These are exported by the DLL.
+ */
+
+extern "C" void *
+shmat (int shmid, const void *shmaddr, int shmflg)
+{
+#ifdef USE_SERVER
+ syscall_printf ("shmat (shmid = %d, shmaddr = %p, shmflg = 0x%x)",
+ shmid, shmaddr, shmflg);
+
+ SLIST_LOCK ();
+ shm_shmid_list *ssh_entry;
+ SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
+ {
+ if (ssh_entry->shmid == shmid)
+ break;
+ }
+ if (!ssh_entry)
+ {
+ /* The shmid is unknown to this process so far. Try to get it from
+ the server if it exists. Use special internal call to shmget,
+ which interprets the key as a shmid and only returns a valid
+ shmid if one exists. Since shmctl inserts a new entry for this
+ shmid into ssh_list automatically, we just have to go through
+ that list again. If that still fails, well, bad luck. */
+ if (shmid && shmget ((key_t) shmid, 0, IPC_KEY_IS_SHMID) != -1)
+ {
+ SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
+ {
+ if (ssh_entry->shmid == shmid)
+ break;
+ }
+ }
+ if (!ssh_entry)
+ {
+ /* Invalid shmid */
+ set_errno (EINVAL);
+ SLIST_UNLOCK ();
+ return (void *) -1;
+ }
+ }
+ /* Early increment ref counter. This allows further actions to run with
+ unlocked lists, because shmdt or shmctl(IPC_RMID) won't delete this
+ ssh_entry. */
+ ++ssh_entry->ref_count;
+ SLIST_UNLOCK ();
+
+ vm_object_t attach_va = NULL;
+ if (shmaddr)
+ {
+ if (shmflg & SHM_RND)
+ attach_va = (vm_object_t)((vm_offset_t)shmaddr & ~(SHMLBA-1));
+ else
+ attach_va = (vm_object_t)shmaddr;
+ /* Don't even bother to call anything if shmaddr is NULL or
+ not aligned. */
+ if (!attach_va || (vm_offset_t)attach_va % SHMLBA)
+ {
+ set_errno (EINVAL);
+ --ssh_entry->ref_count;
+ return (void *) -1;
+ }
+ }
+ /* Try allocating memory before calling cygserver. */
+ shm_attached_list *sph_entry = new (shm_attached_list);
+ if (!sph_entry)
+ {
+ set_errno (ENOMEM);
+ --ssh_entry->ref_count;
+ return (void *) -1;
+ }
+ DWORD access = (shmflg & SHM_RDONLY) ? FILE_MAP_READ : FILE_MAP_WRITE;
+ vm_object_t ptr = MapViewOfFileEx (ssh_entry->hdl, access, 0, 0,
+ ssh_entry->size, attach_va);
+ if (!ptr)
+ {
+ __seterrno ();
+ delete sph_entry;
+ --ssh_entry->ref_count;
+ return (void *) -1;
+ }
+ /* Use returned ptr address as is, so it's stored using the exact value
+ in cygserver. */
+ client_request_shm request (shmid, ptr, shmflg & ~SHM_RND);
+ if (request.make_request () == -1 || request.ptrval () == NULL)
+ {
+ syscall_printf ("-1 [%d] = shmat ()", request.error_code ());
+ UnmapViewOfFile (ptr);
+ delete sph_entry;
+ set_errno (request.error_code ());
+ --ssh_entry->ref_count;
+ if (request.error_code () == ENOSYS)
+ raise (SIGSYS);
+ return (void *) -1;
+ }
+ sph_entry->ptr = ptr;
+ sph_entry->parent = ssh_entry;
+ sph_entry->access = access;
+ SLIST_LOCK ();
+ SLIST_INSERT_HEAD (&sph_list, sph_entry, sph_next);
+ SLIST_UNLOCK ();
+ return ptr;
+#else
+ set_errno (ENOSYS);
+ raise (SIGSYS);
+ return (void *) -1;
+#endif
+}
+
+extern "C" int
+shmctl (int shmid, int cmd, struct shmid_ds *buf)
+{
+#ifdef USE_SERVER
+ syscall_printf ("shmctl (shmid = %d, cmd = %d, buf = 0x%x)",
+ shmid, cmd, buf);
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
+ client_request_shm request (shmid, cmd, buf);
+ if (request.make_request () == -1 || request.retval () == -1)
+ {
+ syscall_printf ("-1 [%d] = shmctl ()", request.error_code ());
+ set_errno (request.error_code ());
+ if (request.error_code () == ENOSYS)
+ raise (SIGSYS);
+ return -1;
+ }
+ if (cmd == IPC_RMID)
+ {
+ /* Cleanup */
+ shm_shmid_list *ssh_entry, *ssh_next_entry;
+ SLIST_LOCK ();
+ SLIST_FOREACH_SAFE (ssh_entry, &ssh_list, ssh_next, ssh_next_entry)
+ {
+ if (ssh_entry->shmid == shmid)
+ {
+ /* Remove this entry from the list and close the handle
+ only if it's not in use anymore. */
+ if (ssh_entry->ref_count <= 0)
+ {
+ SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
+ CloseHandle (ssh_entry->hdl);
+ delete ssh_entry;
+ }
+ break;
+ }
+ }
+ SLIST_UNLOCK ();
+ }
+ return request.retval ();
+#else
+ set_errno (ENOSYS);
+ raise (SIGSYS);
+ return -1;
+#endif
+}
+
+extern "C" int
+shmdt (const void *shmaddr)
+{
+#ifdef USE_SERVER
+ syscall_printf ("shmdt (shmaddr = %p)", shmaddr);
+ client_request_shm request (shmaddr);
+ if (request.make_request () == -1 || request.retval () == -1)
+ {
+ syscall_printf ("-1 [%d] = shmdt ()", request.error_code ());
+ set_errno (request.error_code ());
+ if (request.error_code () == ENOSYS)
+ raise (SIGSYS);
+ return -1;
+ }
+ shm_attached_list *sph_entry, *sph_next_entry;
+ /* Remove map from list... */
+ SLIST_LOCK ();
+ SLIST_FOREACH_SAFE (sph_entry, &sph_list, sph_next, sph_next_entry)
+ {
+ if (sph_entry->ptr == shmaddr)
+ {
+ SLIST_REMOVE (&sph_list, sph_entry, shm_attached_list, sph_next);
+ /* ...unmap view... */
+ UnmapViewOfFile (sph_entry->ptr);
+ /* ...and, if this was the last reference to this shared section... */
+ shm_shmid_list *ssh_entry = sph_entry->parent;
+ if (--ssh_entry->ref_count <= 0)
+ {
+ /* ...delete parent entry and close handle. */
+ SLIST_REMOVE (&ssh_list, ssh_entry, shm_shmid_list, ssh_next);
+ CloseHandle (ssh_entry->hdl);
+ delete ssh_entry;
+ }
+ delete sph_entry;
+ break;
+ }
+ }
+ SLIST_UNLOCK ();
+ return request.retval ();
+#else
+ set_errno (ENOSYS);
+ raise (SIGSYS);
+ return -1;
+#endif
+}
+
+extern "C" int
+shmget (key_t key, size_t size, int shmflg)
+{
+#ifdef USE_SERVER
+ syscall_printf ("shmget (key = %U, size = %d, shmflg = 0x%x)",
+ key, size, shmflg);
+ /* Try allocating memory before calling cygserver. */
+ shm_shmid_list *ssh_new_entry = new (shm_shmid_list);
+ if (!ssh_new_entry)
+ {
+ set_errno (ENOMEM);
+ return -1;
+ }
+ client_request_shm request (key, size, shmflg);
+ if (request.make_request () == -1 || request.retval () == -1)
+ {
+ syscall_printf ("-1 [%d] = shmget ()", request.error_code ());
+ delete ssh_new_entry;
+ set_errno (request.error_code ());
+ if (request.error_code () == ENOSYS)
+ raise (SIGSYS);
+ return -1;
+ }
+ int shmid = request.retval (); /* Shared mem ID */
+ vm_object_t hdl = request.objval (); /* HANDLE associated with it. */
+ shm_shmid_list *ssh_entry;
+ SLIST_LOCK ();
+ SLIST_FOREACH (ssh_entry, &ssh_list, ssh_next)
+ {
+ if (ssh_entry->shmid == shmid)
+ {
+ /* We already maintain an entry for this shmid. That means,
+ the hdl returned by cygserver is a superfluous duplicate
+ of the original hdl maintained by cygserver. We can safely
+ delete it. */
+ CloseHandle (hdl);
+ delete ssh_new_entry;
+ SLIST_UNLOCK ();
+ return shmid;
+ }
+ }
+ /* We arrive here only if shmid is a new one for this process. Add the
+ shmid and hdl value to the list. */
+ ssh_new_entry->shmid = shmid;
+ ssh_new_entry->hdl = hdl;
+ ssh_new_entry->size = size;
+ ssh_new_entry->ref_count = 0;
+ SLIST_INSERT_HEAD (&ssh_list, ssh_new_entry, ssh_next);
+ SLIST_UNLOCK ();
+ return shmid;
+#else
+ set_errno (ENOSYS);
+ raise (SIGSYS);
+ return -1;
+#endif
+}
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
new file mode 100644
index 00000000000..3765cff2610
--- /dev/null
+++ b/winsup/cygwin/signal.cc
@@ -0,0 +1,577 @@
+/* signal.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005 Red Hat, Inc.
+
+ Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
+ Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
+
+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 <stdlib.h>
+#include "cygerrno.h"
+#include <sys/cygwin.h>
+#include "pinfo.h"
+#include "sigproc.h"
+#include "hires.h"
+#include "security.h"
+#include "cygtls.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "cygtls.h"
+
+int sigcatchers; /* FIXME: Not thread safe. */
+
+#define _SA_NORESTART 0x8000
+
+static int sigaction_worker (int, const struct sigaction *, struct sigaction *, bool)
+ __attribute__ ((regparm (3)));
+
+#define sigtrapped(func) ((func) != SIG_IGN && (func) != SIG_DFL)
+
+static inline void
+set_sigcatchers (void (*oldsig) (int), void (*cursig) (int))
+{
+#ifdef DEBUGGING
+ int last_sigcatchers = sigcatchers;
+#endif
+ if (!sigtrapped (oldsig) && sigtrapped (cursig))
+ sigcatchers++;
+ else if (sigtrapped (oldsig) && !sigtrapped (cursig))
+ sigcatchers--;
+#ifdef DEBUGGING
+ if (last_sigcatchers != sigcatchers)
+ sigproc_printf ("last %d, old %d, cur %p, cur %p", last_sigcatchers,
+ sigcatchers, oldsig, cursig);
+#endif
+}
+
+extern "C" _sig_func_ptr
+signal (int sig, _sig_func_ptr func)
+{
+ sig_dispatch_pending ();
+ _sig_func_ptr prev;
+
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = signal (%d, %p)", sig, func);
+ return (_sig_func_ptr) SIG_ERR;
+ }
+
+ prev = global_sigs[sig].sa_handler;
+ struct sigaction& gs = global_sigs[sig];
+ if (gs.sa_flags & _SA_NORESTART)
+ gs.sa_flags &= ~SA_RESTART;
+ else
+ gs.sa_flags |= SA_RESTART;
+
+ gs.sa_mask = SIGTOMASK (sig);
+ gs.sa_handler = func;
+ gs.sa_flags &= ~SA_SIGINFO;
+
+ set_sigcatchers (prev, func);
+
+ syscall_printf ("%p = signal (%d, %p)", prev, sig, func);
+ return prev;
+}
+
+extern "C" int
+nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
+{
+ int res = 0;
+ sig_dispatch_pending ();
+ pthread_testcancel ();
+
+ if ((unsigned int) rqtp->tv_sec > (HIRES_DELAY_MAX / 1000 - 1)
+ || (unsigned int) rqtp->tv_nsec > 999999999)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ DWORD resolution = gtod.resolution ();
+ DWORD req = ((rqtp->tv_sec * 1000 + (rqtp->tv_nsec + 999999) / 1000000
+ + resolution - 1) / resolution) * resolution;
+ DWORD end_time = gtod.dmsecs () + req;
+ syscall_printf ("nanosleep (%ld)", req);
+
+ int rc = cancelable_wait (signal_arrived, req);
+ DWORD rem;
+ if ((rem = end_time - gtod.dmsecs ()) > HIRES_DELAY_MAX)
+ rem = 0;
+ if (rc == WAIT_OBJECT_0)
+ {
+ _my_tls.call_signal_handler ();
+ set_errno (EINTR);
+ res = -1;
+ }
+
+ if (rmtp)
+ {
+ rmtp->tv_sec = rem / 1000;
+ rmtp->tv_nsec = (rem % 1000) * 1000000;
+ }
+
+ syscall_printf ("%d = nanosleep (%ld, %ld)", res, req, rem);
+ return res;
+}
+
+extern "C" unsigned int
+sleep (unsigned int seconds)
+{
+ struct timespec req, rem;
+ req.tv_sec = seconds;
+ req.tv_nsec = 0;
+ nanosleep (&req, &rem);
+ return rem.tv_sec + (rem.tv_nsec > 0);
+}
+
+extern "C" unsigned int
+usleep (useconds_t useconds)
+{
+ struct timespec req;
+ req.tv_sec = useconds / 1000000;
+ req.tv_nsec = (useconds % 1000000) * 1000;
+ int res = nanosleep (&req, 0);
+ return res;
+}
+
+extern "C" int
+sigprocmask (int how, const sigset_t *set, sigset_t *oldset)
+{
+ return handle_sigprocmask (how, set, oldset, myself->getsigmask ());
+}
+
+int __stdcall
+handle_sigprocmask (int how, const sigset_t *set, sigset_t *oldset, sigset_t& opmask)
+{
+ /* check that how is in right range */
+ if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
+ {
+ syscall_printf ("Invalid how value %d", how);
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
+
+ if (oldset)
+ *oldset = opmask;
+
+ if (set)
+ {
+ sigset_t newmask = opmask;
+ switch (how)
+ {
+ case SIG_BLOCK:
+ /* add set to current mask */
+ newmask |= *set;
+ break;
+ case SIG_UNBLOCK:
+ /* remove set from current mask */
+ newmask &= ~*set;
+ break;
+ case SIG_SETMASK:
+ /* just set it */
+ newmask = *set;
+ break;
+ }
+ set_signal_mask (newmask, opmask);
+ }
+ return 0;
+}
+
+int __stdcall
+_pinfo::kill (siginfo_t& si)
+{
+ sig_dispatch_pending ();
+
+ int res = 0;
+ bool sendSIGCONT;
+
+ if (!exists ())
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+
+ if ((sendSIGCONT = (si.si_signo < 0)))
+ si.si_signo = -si.si_signo;
+
+ DWORD this_process_state = process_state;
+ if (si.si_signo == 0)
+ /* ok */;
+ else if ((res = sig_send (this, si)))
+ {
+ sigproc_printf ("%d = sig_send, %E ", res);
+ res = -1;
+ }
+ else if (sendSIGCONT)
+ {
+ siginfo_t si2 = {0};
+ si2.si_signo = SIGCONT;
+ si2.si_code = SI_KERNEL;
+ sig_send (this, si2);
+ }
+
+ syscall_printf ("%d = _pinfo::kill (%d, %d), process_state %p", res, pid,
+ si.si_signo, this_process_state);
+ return res;
+}
+
+int
+raise (int sig)
+{
+ return kill (myself->pid, sig);
+}
+
+static int
+kill0 (pid_t pid, siginfo_t& si)
+{
+ syscall_printf ("kill (%d, %d)", pid, si.si_signo);
+ /* check that sig is in right range */
+ if (si.si_signo < 0 || si.si_signo >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("signal %d out of range", si.si_signo);
+ return -1;
+ }
+
+ /* Silently ignore stop signals from a member of orphaned process group.
+ FIXME: Why??? */
+ if (ISSTATE (myself, PID_ORPHANED) &&
+ (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU))
+ si.si_signo = 0;
+
+ return (pid > 0) ? pinfo (pid)->kill (si) : kill_pgrp (-pid, si);
+}
+
+int
+killsys (pid_t pid, int sig)
+{
+ siginfo_t si = {0};
+ si.si_signo = sig;
+ si.si_code = SI_KERNEL;
+ return kill0 (pid, si);
+}
+
+int
+kill (pid_t pid, int sig)
+{
+ siginfo_t si = {0};
+ si.si_signo = sig;
+ si.si_code = SI_USER;
+ return kill0 (pid, si);
+}
+
+int
+kill_pgrp (pid_t pid, siginfo_t& si)
+{
+ int res = 0;
+ int found = 0;
+ int killself = 0;
+
+ sigproc_printf ("pid %d, signal %d", pid, si.si_signo);
+
+ winpids pids ((DWORD) PID_MAP_RW);
+ for (unsigned i = 0; i < pids.npids; i++)
+ {
+ _pinfo *p = pids[i];
+
+ if (!p->exists ())
+ continue;
+
+ /* Is it a process we want to kill? */
+ if ((pid == 0 && (p->pgid != myself->pgid || p->ctty != myself->ctty)) ||
+ (pid > 1 && p->pgid != pid) ||
+ (si.si_signo < 0 && NOTSTATE (p, PID_STOPPED)))
+ continue;
+ sigproc_printf ("killing pid %d, pgrp %d, p->%s, %s", p->pid, p->pgid,
+ p->__ctty (), myctty ());
+ if (p == myself)
+ killself++;
+ else if (p->kill (si))
+ res = -1;
+ found++;
+ }
+
+ if (killself && !exit_state && myself->kill (si))
+ res = -1;
+
+ if (!found)
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ syscall_printf ("%d = kill (%d, %d)", res, pid, si.si_signo);
+ return res;
+}
+
+extern "C" int
+killpg (pid_t pgrp, int sig)
+{
+ return kill (-pgrp, sig);
+}
+
+extern "C" void
+abort (void)
+{
+ _my_tls.incyg++;
+ sig_dispatch_pending ();
+ /* Flush all streams as per SUSv2.
+ From my reading of this document, this isn't strictly correct.
+ The streams are supposed to be flushed prior to exit. However,
+ if there is I/O in any signal handler that will not necessarily
+ be flushed.
+ However this is the way FreeBSD does it, and it is much easier to
+ do things this way, so... */
+ if (_GLOBAL_REENT->__cleanup)
+ _GLOBAL_REENT->__cleanup (_GLOBAL_REENT);
+
+ /* Ensure that SIGABRT can be caught regardless of blockage. */
+ sigset_t sig_mask;
+ sigfillset (&sig_mask);
+ sigdelset (&sig_mask, SIGABRT);
+ set_signal_mask (sig_mask, myself->getsigmask ());
+
+ raise (SIGABRT);
+ _my_tls.call_signal_handler (); /* Call any signal handler */
+ do_exit (SIGABRT); /* signal handler didn't exit. Goodbye. */
+}
+
+static int
+sigaction_worker (int sig, const struct sigaction *newact, struct sigaction *oldact, bool isinternal)
+{
+ sig_dispatch_pending ();
+ /* check that sig is in right range */
+ if (sig < 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ sigproc_printf ("signal %d, newact %p, oldact %p", sig, newact, oldact);
+ syscall_printf ("SIG_ERR = sigaction signal %d out of range", sig);
+ return -1;
+ }
+
+ struct sigaction oa = global_sigs[sig];
+
+ if (!newact)
+ sigproc_printf ("signal %d, newact %p, oa %p", sig, newact, oa, oa.sa_handler);
+ else
+ {
+ sigproc_printf ("signal %d, newact %p (handler %p), oa %p", sig, newact, newact->sa_handler, oa, oa.sa_handler);
+ if (sig == SIGKILL || sig == SIGSTOP)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ struct sigaction na = *newact;
+ struct sigaction& gs = global_sigs[sig];
+ if (!isinternal)
+ na.sa_flags &= ~_SA_INTERNAL_MASK;
+ gs = na;
+ if (!(gs.sa_flags & SA_NODEFER))
+ gs.sa_mask |= SIGTOMASK(sig);
+ if (gs.sa_handler == SIG_IGN)
+ sig_clear (sig);
+ if (gs.sa_handler == SIG_DFL && sig == SIGCHLD)
+ sig_clear (sig);
+ set_sigcatchers (oa.sa_handler, gs.sa_handler);
+ if (sig == SIGCHLD)
+ {
+ myself->process_state &= ~PID_NOCLDSTOP;
+ if (gs.sa_flags & SA_NOCLDSTOP)
+ myself->process_state |= PID_NOCLDSTOP;
+ }
+ }
+
+ if (oldact)
+ {
+ *oldact = oa;
+ oa.sa_flags &= ~_SA_INTERNAL_MASK;
+ }
+
+ return 0;
+}
+
+extern "C" int
+sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
+{
+ return sigaction_worker (sig, newact, oldact, false);
+}
+
+extern "C" int
+sigaddset (sigset_t *set, const int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigaddset signal %d out of range", sig);
+ return -1;
+ }
+
+ *set |= SIGTOMASK (sig);
+ return 0;
+}
+
+extern "C" int
+sigdelset (sigset_t *set, const int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
+ return -1;
+ }
+
+ *set &= ~SIGTOMASK (sig);
+ return 0;
+}
+
+extern "C" int
+sigismember (const sigset_t *set, int sig)
+{
+ /* check that sig is in right range */
+ if (sig <= 0 || sig >= NSIG)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
+ return -1;
+ }
+
+ if (*set & SIGTOMASK (sig))
+ return 1;
+ else
+ return 0;
+}
+
+extern "C" int
+sigemptyset (sigset_t *set)
+{
+ *set = (sigset_t) 0;
+ return 0;
+}
+
+extern "C" int
+sigfillset (sigset_t *set)
+{
+ *set = ~((sigset_t) 0);
+ return 0;
+}
+
+extern "C" int
+sigsuspend (const sigset_t *set)
+{
+ return handle_sigsuspend (*set);
+}
+
+extern "C" int
+sigpause (int signal_mask)
+{
+ return handle_sigsuspend ((sigset_t) signal_mask);
+}
+
+extern "C" int
+pause (void)
+{
+ return handle_sigsuspend (myself->getsigmask ());
+}
+
+extern "C" int
+siginterrupt (int sig, int flag)
+{
+ struct sigaction act;
+ sigaction (sig, NULL, &act);
+ if (flag)
+ {
+ act.sa_flags &= ~SA_RESTART;
+ act.sa_flags |= _SA_NORESTART;
+ }
+ else
+ {
+ act.sa_flags &= ~_SA_NORESTART;
+ act.sa_flags |= SA_RESTART;
+ }
+ return sigaction_worker (sig, &act, NULL, true);
+}
+
+extern "C" int
+sigwait (const sigset_t *set, int *sig_ptr)
+{
+ int sig = sigwaitinfo (set, NULL);
+ if (sig > 0)
+ *sig_ptr = sig;
+ return sig > 0 ? 0 : -1;
+}
+
+extern "C" int
+sigwaitinfo (const sigset_t *set, siginfo_t *info)
+{
+ pthread_testcancel ();
+ HANDLE h;
+ h = _my_tls.event = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ if (!h)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ _my_tls.sigwait_mask = *set;
+ sig_dispatch_pending (true);
+
+ int res;
+ switch (WaitForSingleObject (h, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ if (!sigismember (set, _my_tls.infodata.si_signo))
+ {
+ set_errno (EINTR);
+ res = -1;
+ }
+ else
+ {
+ if (info)
+ *info = _my_tls.infodata;
+ res = _my_tls.infodata.si_signo;
+ InterlockedExchange ((LONG *) &_my_tls.sig, (LONG) 0);
+ }
+ break;
+ default:
+ __seterrno ();
+ res = -1;
+ }
+ CloseHandle (h);
+ sigproc_printf ("returning sig %d", res);
+ return res;
+}
+
+/* FIXME: SUSv3 says that this function should block until the signal has
+ actually been delivered. Currently, this will only happen when sending
+ signals to the current process. It will not happen when sending signals
+ to other processes. */
+extern "C" int
+sigqueue (pid_t pid, int sig, const union sigval value)
+{
+ siginfo_t si = {0};
+ pinfo dest (pid);
+ if (!dest)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ si.si_signo = sig;
+ si.si_code = SI_QUEUE;
+ si.si_value = value;
+ return sig_send (dest, si);
+}
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
index 9eba1263fe1..4bc7594215f 100644
--- a/winsup/cygwin/sigproc.cc
+++ b/winsup/cygwin/sigproc.cc
@@ -39,7 +39,7 @@ details. */
#define WSSC 60000 // Wait for signal completion
#define WPSP 40000 // Wait for proc_subproc mutex
-#define no_signals_available(x) (!hwait_sig || hwait_sig == INVALID_HANDLE_VALUE || ((x) && myself->exitcode & EXITCODE_SET) || &_my_tls == _sig_tls || in_dllentry)
+#define no_signals_available(x) (!hwait_sig || hwait_sig == INVALID_HANDLE_VALUE || ((x) && myself->exitcode & EXITCODE_SET) || &_my_tls == _sig_tls)
#define NPROCS 256
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 5be10141964..d2749d5a930 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -460,8 +460,8 @@ spawn_guts (const char * prog_arg, const char *const *argv,
So we have to start the child in suspend state, unfortunately, to avoid
a race condition. */
if (!newargv.win16_exe
- && (wincap.start_proc_suspended () || mode != _P_OVERLAY
- || cygheap->fdtab.need_fixup_before ()))
+ && (wincap.start_proc_suspended () || !ch.iscygwin ()
+ || mode != _P_OVERLAY || cygheap->fdtab.need_fixup_before ()))
c_flags |= CREATE_SUSPENDED;
runpath = null_app_name ? NULL : (const char *) real_path;
diff --git a/winsup/cygwin/sync.h b/winsup/cygwin/sync.h
new file mode 100644
index 00000000000..dfe2c392112
--- /dev/null
+++ b/winsup/cygwin/sync.h
@@ -0,0 +1,73 @@
+/* sync.h: Header file for cygwin synchronization primitives.
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
+
+ Written by Christopher Faylor <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. */
+
+#ifndef _SYNC_H
+#define _SYNC_H
+/* FIXME: Note that currently this class cannot be allocated via `new' since
+ there are issues with malloc and fork. */
+class muto
+{
+public:
+ const char *name;
+private:
+ static DWORD exiting_thread;
+ LONG sync; /* Used to serialize access to this class. */
+ LONG waiters; /* Number of threads waiting for lock. */
+ HANDLE bruteforce; /* event handle used to control waiting for lock. */
+public:
+ LONG visits; /* Count of number of times a thread has called acquire. */
+ void *tls; /* Tls of lock owner. */
+ // class muto *next;
+
+ /* The real constructor. */
+ muto *init (const char *) __attribute__ ((regparm (2)));
+
+#if 0 /* FIXME: See comment in sync.cc */
+ ~muto ()
+#endif
+ int acquire (DWORD ms = INFINITE) __attribute__ ((regparm (2))); /* Acquire the lock. */
+ int release () __attribute__ ((regparm (1))); /* Release the lock. */
+
+ bool acquired () __attribute__ ((regparm (1)));
+ void upforgrabs () {tls = this;} // just set to an invalid address
+ void grab () __attribute__ ((regparm (1)));
+ operator int () const {return !!name;}
+ static void set_exiting_thread () {exiting_thread = GetCurrentThreadId ();}
+};
+
+class lock_process
+{
+ bool skip_unlock;
+ static muto locker;
+public:
+ static void init () {locker.init ("lock_process");}
+ void dont_bother () {skip_unlock = true;}
+ lock_process (bool exiting = false)
+ {
+ locker.acquire ();
+ skip_unlock = exiting;
+ if (exiting && exit_state < ES_PROCESS_LOCKED)
+ {
+ exit_state = ES_PROCESS_LOCKED;
+ muto::set_exiting_thread ();
+ }
+ }
+ ~lock_process ()
+ {
+ if (!skip_unlock)
+ locker.release ();
+ }
+ friend class dtable;
+ friend class fhandler_fifo;
+};
+
+#endif /*_SYNC_H*/
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
index 6637f0f84e0..7ff99b5f918 100644
--- a/winsup/cygwin/syscalls.cc
+++ b/winsup/cygwin/syscalls.cc
@@ -602,8 +602,9 @@ open (const char *unix_path, int flags, ...)
if (fd >= 0)
{
- if (!(fh = build_fh_name (unix_path, NULL, (flags & O_NOFOLLOW) ?
- PC_SYM_NOFOLLOW : PC_SYM_FOLLOW,
+ if (!(fh = build_fh_name (unix_path, NULL,
+ (flags & (O_NOFOLLOW | O_EXCL))
+ ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW,
transparent_exe ? stat_suffixes : NULL)))
res = -1; // errno already set
else if ((flags & O_NOFOLLOW) && fh->issymlink ())
diff --git a/winsup/cygwin/termios.cc b/winsup/cygwin/termios.cc
new file mode 100644
index 00000000000..34afe4658ad
--- /dev/null
+++ b/winsup/cygwin/termios.cc
@@ -0,0 +1,316 @@
+/* termios.cc: termios for WIN32.
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Doug Evans and Steve Chamberlain of Cygnus Support
+ dje@cygnus.com, sac@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. */
+
+#include "winsup.h"
+#include <signal.h>
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "cygwin/version.h"
+#include "perprocess.h"
+#include "sigproc.h"
+#include "cygtls.h"
+#include <sys/termios.h>
+
+/* tcsendbreak: POSIX 7.2.2.1 */
+extern "C" int
+tcsendbreak (int fd, int duration)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcsendbreak (duration);
+
+out:
+ syscall_printf ("%d = tcsendbreak (%d, %d)", res, fd, duration);
+ return res;
+}
+
+/* tcdrain: POSIX 7.2.2.1 */
+extern "C" int
+tcdrain (int fd)
+{
+ int res = -1;
+
+ termios_printf ("tcdrain");
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcdrain ();
+
+out:
+ syscall_printf ("%d = tcdrain (%d)", res, fd);
+ return res;
+}
+
+/* tcflush: POSIX 7.2.2.1 */
+extern "C" int
+tcflush (int fd, int queue)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if (queue != TCIFLUSH && queue != TCOFLUSH && queue != TCIOFLUSH)
+ set_errno (EINVAL);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcflush (queue);
+
+out:
+ termios_printf ("%d = tcflush (%d, %d)", res, fd, queue);
+ return res;
+}
+
+/* tcflow: POSIX 7.2.2.1 */
+extern "C" int
+tcflow (int fd, int action)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto out;
+
+ if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof)
+ res = cfd->tcflow (action);
+
+out:
+ syscall_printf ("%d = tcflow (%d, %d)", res, fd, action);
+ return res;
+}
+
+/* tcsetattr: POSIX96 7.2.1.1 */
+extern "C" int
+tcsetattr (int fd, int a, const struct termios *t)
+{
+ int res;
+ t = __tonew_termios (t);
+ int e = get_errno ();
+
+ while (1)
+ {
+ res = -1;
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ e = get_errno ();
+ break;
+ }
+
+ if (!cfd->is_tty ())
+ {
+ e = ENOTTY;
+ break;
+ }
+
+ res = cfd->bg_check (-SIGTTOU);
+
+ switch (res)
+ {
+ case bg_eof:
+ e = get_errno ();
+ break;
+ case bg_ok:
+ if (cfd.isopen ())
+ res = cfd->tcsetattr (a, t);
+ e = get_errno ();
+ break;
+ case bg_signalled:
+ if (_my_tls.call_signal_handler ())
+ continue;
+ res = -1;
+ /* fall through intentionally */
+ default:
+ e = get_errno ();
+ break;
+ }
+ break;
+ }
+
+ set_errno (e);
+ termios_printf ("iflag %p, oflag %p, cflag %p, lflag %p, VMIN %d, VTIME %d",
+ t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN],
+ t->c_cc[VTIME]);
+ termios_printf ("%d = tcsetattr (%d, %d, %x)", res, fd, a, t);
+ return res;
+}
+
+/* tcgetattr: POSIX 7.2.1.1 */
+extern "C" int
+tcgetattr (int fd, struct termios *in_t)
+{
+ int res = -1;
+ struct termios *t = __makenew_termios (in_t);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else if ((res = cfd->tcgetattr (t)) == 0)
+ __toapp_termios (in_t, t);
+
+ if (res)
+ termios_printf ("%d = tcgetattr (%d, %p)", res, fd, in_t);
+ else
+ termios_printf ("iflag %x, oflag %x, cflag %x, lflag %x, VMIN %d, VTIME %d",
+ t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN],
+ t->c_cc[VTIME]);
+
+ return res;
+}
+
+/* tcgetpgrp: POSIX 7.2.3.1 */
+extern "C" int
+tcgetpgrp (int fd)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else
+ res = cfd->tcgetpgrp ();
+
+ termios_printf ("%d = tcgetpgrp (%d)", res, fd);
+ return res;
+}
+
+/* tcsetpgrp: POSIX 7.2.4.1 */
+extern "C" int
+tcsetpgrp (int fd, pid_t pgid)
+{
+ int res = -1;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ /* saw an error */;
+ else if (!cfd->is_tty ())
+ set_errno (ENOTTY);
+ else
+ res = cfd->tcsetpgrp (pgid);
+
+ termios_printf ("%d = tcsetpgrp (%d, %x)", res, fd, pgid);
+ return res;
+}
+
+/* NIST PCTS requires not macro-only implementation */
+#undef cfgetospeed
+#undef cfgetispeed
+#undef cfsetospeed
+#undef cfsetispeed
+
+/* cfgetospeed: POSIX96 7.1.3.1 */
+extern "C" speed_t
+cfgetospeed (struct termios *tp)
+{
+ return __tonew_termios (tp)->c_ospeed;
+}
+
+/* cfgetispeed: POSIX96 7.1.3.1 */
+extern "C" speed_t
+cfgetispeed (struct termios *tp)
+{
+ return __tonew_termios (tp)->c_ispeed;
+}
+
+static inline int
+setspeed (speed_t &set_speed, speed_t from_speed)
+{
+ int res;
+ switch (from_speed)
+ {
+ case B0:
+ case B50:
+ case B75:
+ case B110:
+ case B134:
+ case B150:
+ case B200:
+ case B300:
+ case B600:
+ case B1200:
+ case B1800:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B128000:
+ case B230400:
+ case B256000:
+ case B460800:
+ case B500000:
+ case B576000:
+ case B921600:
+ case B1000000:
+ case B1152000:
+ case B1500000:
+ case B2000000:
+ case B2500000:
+ case B3000000:
+ set_speed = from_speed;
+ res = 0;
+ break;
+ default:
+ set_errno (EINVAL);
+ res = -1;
+ break;
+ }
+ return res;
+}
+
+/* cfsetospeed: POSIX96 7.1.3.1 */
+extern "C" int
+cfsetospeed (struct termios *in_tp, speed_t speed)
+{
+ struct termios *tp = __tonew_termios (in_tp);
+ int res = setspeed (tp->c_ospeed, speed);
+ __toapp_termios (in_tp, tp);
+ return res;
+}
+
+/* cfsetispeed: POSIX96 7.1.3.1 */
+extern "C" int
+cfsetispeed (struct termios *in_tp, speed_t speed)
+{
+ struct termios *tp = __tonew_termios (in_tp);
+ int res = setspeed (tp->c_ispeed, speed);
+ __toapp_termios (in_tp, tp);
+ return res;
+}
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
new file mode 100644
index 00000000000..12fe5992271
--- /dev/null
+++ b/winsup/cygwin/thread.cc
@@ -0,0 +1,3309 @@
+/* thread.cc: Locking and threading module functions
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+
+ Originally written by Marco Fuykschot <marco@ddi.nl>
+ Substantialy enhanced by Robert Collins <rbtcollins@hotmail.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. */
+
+/* Implementation overview and caveats:
+
+ Win32 puts some contraints on what can and cannot be implemented. Where
+ possible we work around those contrainsts. Where we cannot work around
+ the constraints we either pretend to be conformant, or return an error
+ code.
+
+ Some caveats: PROCESS_SHARED objects while they pretend to be process
+ shared, may not actually work. Some test cases are needed to determine
+ win32's behaviour. My suspicion is that the win32 handle needs to be
+ opened with different flags for proper operation.
+
+ R.Collins, April 2001. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "winsup.h"
+#include <limits.h>
+#include "cygerrno.h"
+#include <assert.h>
+#include <stdlib.h>
+#include "pinfo.h"
+#include "sigproc.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygtls.h"
+#include <semaphore.h>
+#include <stdio.h>
+#include <sys/timeb.h>
+#include <exceptions.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+
+extern "C" void __fp_lock_all ();
+extern "C" void __fp_unlock_all ();
+static inline verifyable_object_state
+ verifyable_object_isvalid (void const * objectptr, long magic,
+ void *static_ptr1 = NULL,
+ void *static_ptr2 = NULL,
+ void *static_ptr3 = NULL);
+
+extern int threadsafe;
+
+#undef __getreent
+extern "C" struct _reent *
+__getreent ()
+{
+ return &_my_tls.local_clib;
+}
+
+extern "C" void
+__cygwin_lock_init (_LOCK_T *lock)
+{
+ *lock = _LOCK_T_INITIALIZER;
+}
+
+extern "C" void
+__cygwin_lock_init_recursive (_LOCK_T *lock)
+{
+ *lock = _LOCK_T_RECURSIVE_INITIALIZER;
+}
+
+extern "C" void
+__cygwin_lock_fini (_LOCK_T *lock)
+{
+ pthread_mutex_destroy ((pthread_mutex_t*) lock);
+}
+
+extern "C" void
+__cygwin_lock_lock (_LOCK_T *lock)
+{
+ if (MT_INTERFACE->threadcount <= 1)
+ paranoid_printf ("threadcount %d. not locking", MT_INTERFACE->threadcount);
+ else
+ {
+ paranoid_printf ("threadcount %d. locking", MT_INTERFACE->threadcount);
+ pthread_mutex_lock ((pthread_mutex_t*) lock);
+ }
+}
+
+extern "C" int
+__cygwin_lock_trylock (_LOCK_T *lock)
+{
+ return pthread_mutex_trylock ((pthread_mutex_t*) lock);
+}
+
+
+extern "C" void
+__cygwin_lock_unlock (_LOCK_T *lock)
+{
+ if (MT_INTERFACE->threadcount <= 1)
+ paranoid_printf ("threadcount %d. not unlocking", MT_INTERFACE->threadcount);
+ else
+ {
+ pthread_mutex_unlock ((pthread_mutex_t*) lock);
+ paranoid_printf ("threadcount %d. unlocked", MT_INTERFACE->threadcount);
+ }
+}
+
+static inline verifyable_object_state
+verifyable_object_isvalid (void const *objectptr, long magic, void *static_ptr1,
+ void *static_ptr2, void *static_ptr3)
+{
+ myfault efault;
+ /* Check for NULL pointer specifically since it is a cheap test and avoids the
+ overhead of setting up the fault handler. */
+ if (!objectptr || efault.faulted ())
+ return INVALID_OBJECT;
+
+ verifyable_object **object = (verifyable_object **) objectptr;
+
+ if ((static_ptr1 && *object == static_ptr1) ||
+ (static_ptr2 && *object == static_ptr2) ||
+ (static_ptr3 && *object == static_ptr3))
+ return VALID_STATIC_OBJECT;
+ if ((*object)->magic != magic)
+ return INVALID_OBJECT;
+ return VALID_OBJECT;
+}
+
+/* static members */
+inline bool
+pthread_attr::is_good_object (pthread_attr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_ATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_condattr::is_good_object (pthread_condattr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_CONDATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_rwlockattr::is_good_object (pthread_rwlockattr_t const *attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_RWLOCKATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_key::is_good_object (pthread_key_t const *key)
+{
+ if (verifyable_object_isvalid (key, PTHREAD_KEY_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_mutex::is_good_object (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_mutex::is_good_initializer (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC,
+ PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+ PTHREAD_NORMAL_MUTEX_INITIALIZER_NP,
+ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_mutex::is_good_initializer_or_object (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC,
+ PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+ PTHREAD_NORMAL_MUTEX_INITIALIZER_NP,
+ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_mutex::can_be_unlocked (pthread_mutex_t const *mutex)
+{
+ pthread_t self = pthread::self ();
+
+ if (!is_good_object (mutex))
+ return false;
+ /* Check if the mutex is owned by the current thread and can be unlocked.
+ * Also check for the ANONYMOUS owner to cover NORMAL mutexes as well. */
+ return ((*mutex)->recursion_counter == 1
+ && ((*mutex)->owner == MUTEX_OWNER_ANONYMOUS
+ || pthread::equal ((*mutex)->owner, self)));
+}
+
+inline bool
+pthread_mutexattr::is_good_object (pthread_mutexattr_t const * attr)
+{
+ if (verifyable_object_isvalid (attr, PTHREAD_MUTEXATTR_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool __attribute__ ((used))
+pthread::is_good_object (pthread_t const *thread)
+{
+ if (verifyable_object_isvalid (thread, PTHREAD_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+/* Thread synchronisation */
+inline bool
+pthread_cond::is_good_object (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_cond::is_good_initializer (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_cond::is_good_initializer_or_object (pthread_cond_t const *cond)
+{
+ if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+/* RW locks */
+inline bool
+pthread_rwlock::is_good_object (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_rwlock::is_good_initializer (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+pthread_rwlock::is_good_initializer_or_object (pthread_rwlock_t const *rwlock)
+{
+ if (verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+inline bool
+semaphore::is_good_object (sem_t const * sem)
+{
+ if (verifyable_object_isvalid (sem, SEM_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+LPCRITICAL_SECTION
+ResourceLocks::Lock (int _resid)
+{
+ return &lock;
+}
+
+void
+SetResourceLock (int _res_id, int _mode, const char *_function)
+{
+ EnterCriticalSection (user_data->resourcelocks->Lock (_res_id));
+}
+
+void
+ReleaseResourceLock (int _res_id, int _mode, const char *_function)
+{
+ LeaveCriticalSection (user_data->resourcelocks->Lock (_res_id));
+}
+
+void
+ResourceLocks::Init ()
+{
+ InitializeCriticalSection (&lock);
+ inited = true;
+ thread_printf ("lock %p inited by %p , %d", &lock, user_data, myself->pid);
+}
+
+void
+ResourceLocks::Delete ()
+{
+ if (inited)
+ {
+ thread_printf ("Close Resource Locks %p ", &lock);
+ DeleteCriticalSection (&lock);
+ inited = false;
+ }
+}
+
+void
+MTinterface::Init ()
+{
+ pthread_mutex::init_mutex ();
+ pthread_cond::init_mutex ();
+ pthread_rwlock::init_mutex ();
+}
+
+void
+MTinterface::fixup_before_fork ()
+{
+ pthread_key::fixup_before_fork ();
+}
+
+/* This function is called from a single threaded process */
+void
+MTinterface::fixup_after_fork ()
+{
+ pthread_key::fixup_after_fork ();
+
+ threadcount = 0;
+ pthread::init_mainthread ();
+
+ pthread::fixup_after_fork ();
+ pthread_mutex::fixup_after_fork ();
+ pthread_cond::fixup_after_fork ();
+ pthread_rwlock::fixup_after_fork ();
+ semaphore::fixup_after_fork ();
+}
+
+/* pthread calls */
+
+/* static methods */
+void
+pthread::init_mainthread ()
+{
+ pthread *thread = get_tls_self_pointer ();
+ if (!thread)
+ {
+ thread = new pthread ();
+ if (!thread)
+ api_fatal ("failed to create mainthread object");
+ }
+
+ set_tls_self_pointer (thread);
+ thread->thread_id = GetCurrentThreadId ();
+ if (!DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &thread->win32_obj_id, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ api_fatal ("failed to create mainthread handle");
+ if (!thread->create_cancel_event ())
+ api_fatal ("couldn't create cancel event for main thread");
+ VerifyHandle (thread->win32_obj_id);
+ thread->postcreate ();
+}
+
+pthread *
+pthread::self ()
+{
+ pthread *thread = get_tls_self_pointer ();
+ if (!thread)
+ {
+ thread = pthread_null::get_null_pthread ();
+ set_tls_self_pointer (thread);
+ }
+ return thread;
+}
+
+pthread *
+pthread::get_tls_self_pointer ()
+{
+ return _my_tls.tid;
+}
+
+void
+pthread::set_tls_self_pointer (pthread *thread)
+{
+ thread->cygtls = &_my_tls;
+ _my_tls.tid = thread;
+}
+
+List<pthread> pthread::threads;
+
+/* member methods */
+pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0),
+ valid (false), suspended (false),
+ cancelstate (0), canceltype (0), cancel_event (0),
+ joiner (NULL), next (NULL), cleanup_stack (NULL)
+{
+ if (this != pthread_null::get_null_pthread ())
+ threads.insert (this);
+}
+
+pthread::~pthread ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+ if (cancel_event)
+ CloseHandle (cancel_event);
+
+ if (this != pthread_null::get_null_pthread ())
+ threads.remove (this);
+}
+
+bool
+pthread::create_cancel_event ()
+{
+ cancel_event = ::CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ if (!cancel_event)
+ {
+ system_printf ("couldn't create cancel event, %E");
+ /* we need the event for correct behaviour */
+ return false;
+ }
+ return true;
+}
+
+void
+pthread::precreate (pthread_attr *newattr)
+{
+ pthread_mutex *verifyable_mutex_obj = &mutex;
+
+ /* already running ? */
+ if (win32_obj_id)
+ return;
+
+ if (newattr)
+ {
+ attr.joinable = newattr->joinable;
+ attr.contentionscope = newattr->contentionscope;
+ attr.inheritsched = newattr->inheritsched;
+ attr.stacksize = newattr->stacksize;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("New thread object access mutex is not valid. this %p",
+ this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mutex.type = PTHREAD_MUTEX_NORMAL;
+ if (!create_cancel_event ())
+ magic = 0;
+}
+
+bool
+pthread::create (void *(*func) (void *), pthread_attr *newattr,
+ void *threadarg)
+{
+ bool retval;
+
+ precreate (newattr);
+ if (!magic)
+ return false;
+
+ function = func;
+ arg = threadarg;
+
+ mutex.lock ();
+ win32_obj_id = ::CreateThread (&sec_none_nih, attr.stacksize,
+ thread_init_wrapper, this, 0, &thread_id);
+
+ if (!win32_obj_id)
+ {
+ thread_printf ("CreateThread failed: this %p, %E", this);
+ magic = 0;
+ }
+ else
+ {
+ postcreate ();
+ while (!cygtls)
+ low_priority_sleep (0);
+ }
+ retval = magic;
+ mutex.unlock ();
+ return retval;
+}
+
+void
+pthread::postcreate ()
+{
+ valid = true;
+
+ InterlockedIncrement (&MT_INTERFACE->threadcount);
+ /* FIXME: set the priority appropriately for system contention scope */
+ if (attr.inheritsched == PTHREAD_EXPLICIT_SCHED)
+ {
+ /* FIXME: set the scheduling settings for the new thread */
+ /* sched_thread_setparam (win32_obj_id, attr.schedparam); */
+ }
+}
+
+void
+pthread::exit (void *value_ptr)
+{
+ class pthread *thread = this;
+
+ // run cleanup handlers
+ pop_all_cleanup_handlers ();
+
+ pthread_key::run_all_destructors ();
+
+ mutex.lock ();
+ // cleanup if thread is in detached state and not joined
+ if (equal (joiner, thread))
+ delete this;
+ else
+ {
+ valid = false;
+ return_ptr = value_ptr;
+ mutex.unlock ();
+ }
+
+ if (_my_tls.local_clib.__sdidinit < 0)
+ _my_tls.local_clib.__sdidinit = 0;
+ (_reclaim_reent) (_REENT);
+
+
+ if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0)
+ ::exit (0);
+ else
+ {
+ _my_tls.remove (INFINITE);
+ ExitThread (0);
+ }
+}
+
+int
+pthread::cancel ()
+{
+ class pthread *thread = this;
+ class pthread *self = pthread::self ();
+
+ mutex.lock ();
+
+ if (!valid)
+ {
+ mutex.unlock ();
+ return 0;
+ }
+
+ if (canceltype == PTHREAD_CANCEL_DEFERRED ||
+ cancelstate == PTHREAD_CANCEL_DISABLE)
+ {
+ // cancel deferred
+ mutex.unlock ();
+ SetEvent (cancel_event);
+ return 0;
+ }
+ else if (equal (thread, self))
+ {
+ mutex.unlock ();
+ cancel_self ();
+ return 0; // Never reached
+ }
+
+ // cancel asynchronous
+ SuspendThread (win32_obj_id);
+ if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL;
+ GetThreadContext (win32_obj_id, &context);
+ context.Eip = (DWORD) pthread::static_cancel_self;
+ SetThreadContext (win32_obj_id, &context);
+ }
+ mutex.unlock ();
+ ResumeThread (win32_obj_id);
+
+ return 0;
+/*
+ TODO: insert pthread_testcancel into the required functions
+ the required function list is: *indicates done, X indicates not present in cygwin.
+aio_suspend ()
+*close ()
+*creat ()
+fcntl ()
+fsync ()
+getmsg ()
+getpmsg ()
+lockf ()
+mq_receive ()
+mq_send ()
+msgrcv ()
+msgsnd ()
+msync ()
+nanosleep ()
+open ()
+*pause ()
+poll ()
+pread ()
+*pthread_cond_timedwait ()
+*pthread_cond_wait ()
+*pthread_join ()
+*pthread_testcancel ()
+putmsg ()
+putpmsg ()
+pwrite ()
+read ()
+readv ()
+select ()
+*sem_wait ()
+*sigpause ()
+*sigsuspend ()
+sigtimedwait ()
+sigwait ()
+sigwaitinfo ()
+*sleep ()
+*system ()
+tcdrain ()
+*usleep ()
+*wait ()
+*wait3()
+waitid ()
+*waitpid ()
+write ()
+writev ()
+
+the optional list is:
+catclose ()
+catgets ()
+catopen ()
+closedir ()
+closelog ()
+ctermid ()
+dbm_close ()
+dbm_delete ()
+dbm_fetch ()
+dbm_nextkey ()
+dbm_open ()
+dbm_store ()
+dlclose ()
+dlopen ()
+endgrent ()
+endpwent ()
+endutxent ()
+fclose ()
+fcntl ()
+fflush ()
+fgetc ()
+fgetpos ()
+fgets ()
+fgetwc ()
+fgetws ()
+fopen ()
+fprintf ()
+fputc ()
+fputs ()
+fputwc ()
+fputws ()
+fread ()
+freopen ()
+fscanf ()
+fseek ()
+fseeko ()
+fsetpos ()
+ftell ()
+ftello ()
+ftw ()
+fwprintf ()
+fwrite ()
+fwscanf ()
+getc ()
+getc_unlocked ()
+getchar ()
+getchar_unlocked ()
+getcwd ()
+getdate ()
+getgrent ()
+getgrgid ()
+getgrgid_r ()
+getgrnam ()
+getgrnam_r ()
+getlogin ()
+getlogin_r ()
+getpwent ()
+*getpwnam ()
+*getpwnam_r ()
+*getpwuid ()
+*getpwuid_r ()
+gets ()
+getutxent ()
+getutxid ()
+getutxline ()
+getw ()
+getwc ()
+getwchar ()
+getwd ()
+glob ()
+iconv_close ()
+iconv_open ()
+ioctl ()
+lseek ()
+mkstemp ()
+nftw ()
+opendir ()
+openlog ()
+pclose ()
+perror ()
+popen ()
+printf ()
+putc ()
+putc_unlocked ()
+putchar ()
+putchar_unlocked ()
+puts ()
+pututxline ()
+putw ()
+putwc ()
+putwchar ()
+readdir ()
+readdir_r ()
+remove ()
+rename ()
+rewind ()
+rewinddir ()
+scanf ()
+seekdir ()
+semop ()
+setgrent ()
+setpwent ()
+setutxent ()
+strerror ()
+syslog ()
+tmpfile ()
+tmpnam ()
+ttyname ()
+ttyname_r ()
+ungetc ()
+ungetwc ()
+unlink ()
+vfprintf ()
+vfwprintf ()
+vprintf ()
+vwprintf ()
+wprintf ()
+wscanf ()
+
+Note, that for fcntl (), for any value of the cmd argument.
+
+And we must not introduce cancellation points anywhere else that's part of the posix or
+opengroup specs.
+ */
+}
+
+void
+pthread::testcancel ()
+{
+ if (cancelstate == PTHREAD_CANCEL_DISABLE)
+ return;
+
+ if (WaitForSingleObject (cancel_event, 0) == WAIT_OBJECT_0)
+ cancel_self ();
+}
+
+void
+pthread::static_cancel_self ()
+{
+ pthread::self ()->cancel_self ();
+}
+
+DWORD
+cancelable_wait (HANDLE object, DWORD timeout, const cw_cancel_action cancel_action,
+ const enum cw_sig_wait sig_wait)
+{
+ DWORD res;
+ DWORD num = 0;
+ HANDLE wait_objects[3];
+ pthread_t thread = pthread::self ();
+
+ /* Do not change the wait order.
+ The object must have higher priority than the cancel event,
+ because WaitForMultipleObjects will return the smallest index
+ if both objects are signaled. */
+ wait_objects[num++] = object;
+ DWORD cancel_n;
+ if (cancel_action == cw_no_cancel || !pthread::is_good_object (&thread) ||
+ thread->cancelstate == PTHREAD_CANCEL_DISABLE)
+ cancel_n = (DWORD) -1;
+ else
+ {
+ cancel_n = WAIT_OBJECT_0 + num++;
+ wait_objects[cancel_n] = thread->cancel_event;
+ }
+
+ DWORD sig_n;
+ if (sig_wait == cw_sig_nosig || &_my_tls != _main_tls)
+ sig_n = (DWORD) -1;
+ else
+ {
+ sig_n = WAIT_OBJECT_0 + num++;
+ wait_objects[sig_n] = signal_arrived;
+ }
+
+ while (1)
+ {
+ res = WaitForMultipleObjects (num, wait_objects, FALSE, timeout);
+ if (res == cancel_n)
+ {
+ if (cancel_action == cw_cancel_self)
+ pthread::static_cancel_self ();
+ res = WAIT_CANCELED;
+ }
+ else if (res != sig_n)
+ /* all set */;
+ else if (sig_wait == cw_sig_eintr)
+ res = WAIT_SIGNALED;
+ else
+ {
+ _my_tls.call_signal_handler ();
+ continue;
+ }
+ break;
+ }
+ return res;
+}
+
+int
+pthread::setcancelstate (int state, int *oldstate)
+{
+ int result = 0;
+
+ mutex.lock ();
+
+ if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE)
+ result = EINVAL;
+ else
+ {
+ if (oldstate)
+ *oldstate = cancelstate;
+ cancelstate = state;
+ }
+
+ mutex.unlock ();
+
+ return result;
+}
+
+int
+pthread::setcanceltype (int type, int *oldtype)
+{
+ int result = 0;
+
+ mutex.lock ();
+
+ if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS)
+ result = EINVAL;
+ else
+ {
+ if (oldtype)
+ *oldtype = canceltype;
+ canceltype = type;
+ }
+
+ mutex.unlock ();
+
+ return result;
+}
+
+void
+pthread::push_cleanup_handler (__pthread_cleanup_handler *handler)
+{
+ if (this != self ())
+ // TODO: do it?
+ api_fatal ("Attempt to push a cleanup handler across threads");
+ handler->next = cleanup_stack;
+ cleanup_stack = handler;
+}
+
+void
+pthread::pop_cleanup_handler (int const execute)
+{
+ if (this != self ())
+ // TODO: send a signal or something to the thread ?
+ api_fatal ("Attempt to execute a cleanup handler across threads");
+
+ mutex.lock ();
+
+ if (cleanup_stack != NULL)
+ {
+ __pthread_cleanup_handler *handler = cleanup_stack;
+
+ if (execute)
+ (*handler->function) (handler->arg);
+ cleanup_stack = handler->next;
+ }
+
+ mutex.unlock ();
+}
+
+void
+pthread::pop_all_cleanup_handlers ()
+{
+ while (cleanup_stack != NULL)
+ pop_cleanup_handler (1);
+}
+
+void
+pthread::cancel_self ()
+{
+ exit (PTHREAD_CANCELED);
+}
+
+DWORD
+pthread::get_thread_id ()
+{
+ return thread_id;
+}
+
+void
+pthread::_fixup_after_fork ()
+{
+ /* set thread to not running if it is not the forking thread */
+ if (this != pthread::self ())
+ {
+ magic = 0;
+ valid = false;
+ win32_obj_id = NULL;
+ cancel_event = NULL;
+ }
+}
+
+void
+pthread::suspend_except_self ()
+{
+ if (valid && this != pthread::self ())
+ SuspendThread (win32_obj_id);
+}
+
+void
+pthread::resume ()
+{
+ if (valid)
+ ResumeThread (win32_obj_id);
+}
+
+/* instance members */
+
+pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC),
+joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS),
+inheritsched (PTHREAD_INHERIT_SCHED), stacksize (0)
+{
+ schedparam.sched_priority = 0;
+}
+
+pthread_attr::~pthread_attr ()
+{
+}
+
+pthread_condattr::pthread_condattr ():verifyable_object
+ (PTHREAD_CONDATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_condattr::~pthread_condattr ()
+{
+}
+
+List<pthread_cond> pthread_cond::conds;
+
+/* This is used for cond creation protection within a single process only */
+fast_mutex NO_COPY pthread_cond::cond_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_cond::init_mutex ()
+{
+ if (!cond_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread cond static initializer support.");
+}
+
+pthread_cond::pthread_cond (pthread_condattr *attr) :
+ verifyable_object (PTHREAD_COND_MAGIC),
+ shared (0), waiting (0), pending (0), sem_wait (NULL),
+ mtx_cond(NULL), next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj;
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ verifyable_mutex_obj = &mtx_in;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /*
+ * Change the mutex type to NORMAL.
+ * This mutex MUST be of type normal
+ */
+ mtx_in.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_mutex_obj = &mtx_out;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx_out.type = PTHREAD_MUTEX_NORMAL;
+
+ sem_wait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!sem_wait)
+ {
+ debug_printf ("CreateSemaphore failed. %E");
+ magic = 0;
+ return;
+ }
+
+ conds.insert (this);
+}
+
+pthread_cond::~pthread_cond ()
+{
+ if (sem_wait)
+ CloseHandle (sem_wait);
+
+ conds.remove (this);
+}
+
+void
+pthread_cond::unblock (const bool all)
+{
+ unsigned long releaseable;
+
+ /*
+ * Block outgoing threads (and avoid simultanous unblocks)
+ */
+ mtx_out.lock ();
+
+ releaseable = waiting - pending;
+ if (releaseable)
+ {
+ unsigned long released;
+
+ if (!pending)
+ {
+ /*
+ * Block incoming threads until all waiting threads are released.
+ */
+ mtx_in.lock ();
+
+ /*
+ * Calculate releaseable again because threads can enter until
+ * the semaphore has been taken, but they can not leave, therefore pending
+ * is unchanged and releaseable can only get higher
+ */
+ releaseable = waiting - pending;
+ }
+
+ released = all ? releaseable : 1;
+ pending += released;
+ /*
+ * Signal threads
+ */
+ ::ReleaseSemaphore (sem_wait, released, NULL);
+ }
+
+ /*
+ * And let the threads release.
+ */
+ mtx_out.unlock ();
+}
+
+int
+pthread_cond::wait (pthread_mutex_t mutex, DWORD dwMilliseconds)
+{
+ DWORD rv;
+
+ mtx_in.lock ();
+ if (InterlockedIncrement ((long *)&waiting) == 1)
+ mtx_cond = mutex;
+ else if (mtx_cond != mutex)
+ {
+ InterlockedDecrement ((long *)&waiting);
+ mtx_in.unlock ();
+ return EINVAL;
+ }
+ mtx_in.unlock ();
+
+ /*
+ * Release the mutex and wait on semaphore
+ */
+ ++mutex->condwaits;
+ mutex->unlock ();
+
+ rv = cancelable_wait (sem_wait, dwMilliseconds, cw_no_cancel_self, cw_sig_eintr);
+
+ mtx_out.lock ();
+
+ if (rv != WAIT_OBJECT_0)
+ {
+ /*
+ * It might happen that a signal is sent while the thread got canceled
+ * or timed out. Try to take one.
+ * If the thread gets one than a signal|broadcast is in progress.
+ */
+ if (WaitForSingleObject (sem_wait, 0) == WAIT_OBJECT_0)
+ /*
+ * thread got cancelled ot timed out while a signalling is in progress.
+ * Set wait result back to signaled
+ */
+ rv = WAIT_OBJECT_0;
+ }
+
+ InterlockedDecrement ((long *)&waiting);
+
+ if (rv == WAIT_OBJECT_0 && --pending == 0)
+ /*
+ * All signaled threads are released,
+ * new threads can enter Wait
+ */
+ mtx_in.unlock ();
+
+ mtx_out.unlock ();
+
+ mutex->lock ();
+ --mutex->condwaits;
+
+ if (rv == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ else if (rv == WAIT_SIGNALED)
+ /* SUSv3 states: If a signal is delivered to a thread waiting for a
+ condition variable, upon return from the signal handler the thread
+ resumes waiting for the condition variable as if it was not
+ interrupted, or it shall return zero due to spurious wakeup.
+ We opt for the latter choice here. */
+ return 0;
+ else if (rv == WAIT_TIMEOUT)
+ return ETIMEDOUT;
+
+ return 0;
+}
+
+void
+pthread_cond::_fixup_after_fork ()
+{
+ waiting = pending = 0;
+ mtx_cond = NULL;
+
+ /* Unlock eventually locked mutexes */
+ mtx_in.unlock ();
+ mtx_out.unlock ();
+
+ sem_wait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!sem_wait)
+ api_fatal ("pthread_cond::_fixup_after_fork () failed to recreate win32 semaphore");
+}
+
+pthread_rwlockattr::pthread_rwlockattr ():verifyable_object
+ (PTHREAD_RWLOCKATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_rwlockattr::~pthread_rwlockattr ()
+{
+}
+
+List<pthread_rwlock> pthread_rwlock::rwlocks;
+
+/* This is used for rwlock creation protection within a single process only */
+fast_mutex NO_COPY pthread_rwlock::rwlock_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_rwlock::init_mutex ()
+{
+ if (!rwlock_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread rwlock static initializer support.");
+}
+
+pthread_rwlock::pthread_rwlock (pthread_rwlockattr *attr) :
+ verifyable_object (PTHREAD_RWLOCK_MAGIC),
+ shared (0), waiting_readers (0), waiting_writers (0), writer (NULL),
+ readers (NULL), readers_mx (), mtx (NULL), cond_readers (NULL), cond_writers (NULL),
+ next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj = &mtx;
+ pthread_cond *verifyable_cond_obj;
+
+ if (!readers_mx.init ())
+ {
+ thread_printf ("Internal rwlock synchronisation mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal rwlock mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_cond_obj = &cond_readers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock readers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ verifyable_cond_obj = &cond_writers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock writers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+
+ rwlocks.insert (this);
+}
+
+pthread_rwlock::~pthread_rwlock ()
+{
+ rwlocks.remove (this);
+}
+
+int
+pthread_rwlock::rdlock ()
+{
+ int result = 0;
+ struct RWLOCK_READER *reader;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ reader = new struct RWLOCK_READER;
+ if (!reader)
+ {
+ result = EAGAIN;
+ goto DONE;
+ }
+
+ while (writer || waiting_writers)
+ {
+ pthread_cleanup_push (pthread_rwlock::rdlock_cleanup, this);
+
+ ++waiting_readers;
+ cond_readers.wait (&mtx);
+ --waiting_readers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ reader->thread = self;
+ add_reader (reader);
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::tryrdlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || waiting_writers || lookup_reader (self))
+ result = EBUSY;
+ else
+ {
+ struct RWLOCK_READER *reader = new struct RWLOCK_READER;
+ if (reader)
+ {
+ reader->thread = self;
+ add_reader (reader);
+ }
+ else
+ result = EAGAIN;
+ }
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::wrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer == self || lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ while (writer || readers)
+ {
+ pthread_cleanup_push (pthread_rwlock::wrlock_cleanup, this);
+
+ ++waiting_writers;
+ cond_writers.wait (&mtx);
+ --waiting_writers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ writer = self;
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::trywrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || readers)
+ result = EBUSY;
+ else
+ writer = self;
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::unlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer)
+ {
+ if (writer != self)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ writer = NULL;
+ }
+ else
+ {
+ struct RWLOCK_READER *reader = lookup_reader (self);
+
+ if (!reader)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ remove_reader (reader);
+ delete reader;
+ }
+
+ release ();
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+void
+pthread_rwlock::add_reader (struct RWLOCK_READER *rd)
+{
+ List_insert (readers, rd);
+}
+
+void
+pthread_rwlock::remove_reader (struct RWLOCK_READER *rd)
+{
+ List_remove (readers_mx, readers, rd);
+}
+
+struct pthread_rwlock::RWLOCK_READER *
+pthread_rwlock::lookup_reader (pthread_t thread)
+{
+ readers_mx.lock ();
+
+ struct RWLOCK_READER *cur = readers;
+
+ while (cur && cur->thread != thread)
+ cur = cur->next;
+
+ readers_mx.unlock ();
+
+ return cur;
+}
+
+void
+pthread_rwlock::rdlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_readers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::wrlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_writers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::_fixup_after_fork ()
+{
+ pthread_t self = pthread::self ();
+ struct RWLOCK_READER **temp = &readers;
+
+ waiting_readers = 0;
+ waiting_writers = 0;
+
+ if (!readers_mx.init ())
+ api_fatal ("pthread_rwlock::_fixup_after_fork () failed to recreate mutex");
+
+ /* Unlock eventually locked mutex */
+ mtx.unlock ();
+ /*
+ * Remove all readers except self
+ */
+ while (*temp)
+ {
+ if ((*temp)->thread == self)
+ temp = &((*temp)->next);
+ else
+ {
+ struct RWLOCK_READER *cur = *temp;
+ *temp = (*temp)->next;
+ delete cur;
+ }
+ }
+}
+
+/* pthread_key */
+/* static members */
+/* This stores pthread_key information across fork() boundaries */
+List<pthread_key> pthread_key::keys;
+
+/* non-static members */
+
+pthread_key::pthread_key (void (*aDestructor) (void *)):verifyable_object (PTHREAD_KEY_MAGIC), destructor (aDestructor)
+{
+ tls_index = TlsAlloc ();
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ magic = 0;
+ else
+ keys.insert (this);
+}
+
+pthread_key::~pthread_key ()
+{
+ /* We may need to make the list code lock the list during operations
+ */
+ if (magic != 0)
+ {
+ keys.remove (this);
+ TlsFree (tls_index);
+ }
+}
+
+void
+pthread_key::_fixup_before_fork ()
+{
+ fork_buf = get ();
+}
+
+void
+pthread_key::_fixup_after_fork ()
+{
+ tls_index = TlsAlloc ();
+ if (tls_index == TLS_OUT_OF_INDEXES)
+ api_fatal ("pthread_key::recreate_key_from_buffer () failed to reallocate Tls storage");
+ set (fork_buf);
+}
+
+void
+pthread_key::run_destructor ()
+{
+ if (destructor)
+ {
+ void *oldValue = get ();
+ if (oldValue)
+ {
+ set (NULL);
+ destructor (oldValue);
+ }
+ }
+}
+
+/* pshared mutexs:
+
+ REMOVED FROM CURRENT. These can be reinstated with the daemon, when all the
+ gymnastics can be a lot easier.
+
+ the mutex_t (size 4) is not used as a verifyable object because we cannot
+ guarantee the same address space for all processes.
+ we use the following:
+ high bit set (never a valid address).
+ second byte is reserved for the priority.
+ third byte is reserved
+ fourth byte is the mutex id. (max 255 cygwin mutexs system wide).
+ creating mutex's does get slower and slower, but as creation is a one time
+ job, it should never become an issue
+
+ And if you're looking at this and thinking, why not an array in cygwin for all mutexs,
+ - you incur a penalty on _every_ mutex call and you have toserialise them all.
+ ... Bad karma.
+
+ option 2? put everything in userspace and update the ABI?
+ - bad karma as well - the HANDLE, while identical across process's,
+ Isn't duplicated, it's reopened. */
+
+/* static members */
+
+List<pthread_mutex> pthread_mutex::mutexes;
+
+/* This is used for mutex creation protection within a single process only */
+fast_mutex NO_COPY pthread_mutex::mutex_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_mutex::init_mutex ()
+{
+ if (!mutex_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread mutex static initializer support.");
+}
+
+pthread_mutex::pthread_mutex (pthread_mutexattr *attr) :
+ verifyable_object (PTHREAD_MUTEX_MAGIC),
+ lock_counter (0),
+ win32_obj_id (NULL), recursion_counter (0),
+ condwaits (0), owner (NULL), type (PTHREAD_MUTEX_ERRORCHECK),
+ pshared (PTHREAD_PROCESS_PRIVATE)
+{
+ win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!win32_obj_id)
+ {
+ magic = 0;
+ return;
+ }
+ /*attr checked in the C call */
+ if (attr)
+ {
+ if (attr->pshared == PTHREAD_PROCESS_SHARED)
+ {
+ // fail
+ magic = 0;
+ return;
+ }
+
+ type = attr->mutextype;
+ }
+
+ mutexes.insert (this);
+}
+
+pthread_mutex::~pthread_mutex ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+
+ mutexes.remove (this);
+}
+
+int
+pthread_mutex::_lock (pthread_t self)
+{
+ int result = 0;
+
+ if (InterlockedIncrement ((long *)&lock_counter) == 1)
+ set_owner (self);
+ else if (type == PTHREAD_MUTEX_NORMAL || !pthread::equal (owner, self))
+ {
+ cancelable_wait (win32_obj_id, INFINITE, cw_no_cancel, cw_sig_resume);
+ set_owner (self);
+ }
+ else
+ {
+ InterlockedDecrement ((long *) &lock_counter);
+ if (type == PTHREAD_MUTEX_RECURSIVE)
+ result = lock_recursive ();
+ else
+ result = EDEADLK;
+ }
+
+ return result;
+}
+
+int
+pthread_mutex::_trylock (pthread_t self)
+{
+ int result = 0;
+
+ if (InterlockedCompareExchange ((long *) &lock_counter, 1, 0) == 0)
+ set_owner (self);
+ else if (type == PTHREAD_MUTEX_RECURSIVE && pthread::equal (owner, self))
+ result = lock_recursive ();
+ else
+ result = EBUSY;
+
+ return result;
+}
+
+int
+pthread_mutex::_unlock (pthread_t self)
+{
+ if (!pthread::equal (owner, self))
+ return EPERM;
+
+ if (--recursion_counter == 0)
+ {
+ owner = NULL;
+ if (InterlockedDecrement ((long *)&lock_counter))
+ // Another thread is waiting
+ ::ReleaseSemaphore (win32_obj_id, 1, NULL);
+ }
+
+ return 0;
+}
+
+int
+pthread_mutex::_destroy (pthread_t self)
+{
+ if (condwaits || _trylock (self))
+ // Do not destroy a condwaited or locked mutex
+ return EBUSY;
+ else if (recursion_counter != 1)
+ {
+ // Do not destroy a recursive locked mutex
+ --recursion_counter;
+ return EBUSY;
+ }
+
+ delete this;
+ return 0;
+}
+
+void
+pthread_mutex::_fixup_after_fork ()
+{
+ debug_printf ("mutex %x in _fixup_after_fork", this);
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ api_fatal ("pthread_mutex::_fixup_after_fork () doesn'tunderstand PROCESS_SHARED mutex's");
+
+ if (owner == NULL)
+ /* mutex has no owner, reset to initial */
+ lock_counter = 0;
+ else if (lock_counter != 0)
+ /* All waiting threads are gone after a fork */
+ lock_counter = 1;
+
+ win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!win32_obj_id)
+ api_fatal ("pthread_mutex::_fixup_after_fork () failed to recreate win32 semaphore for mutex");
+
+ condwaits = 0;
+}
+
+pthread_mutexattr::pthread_mutexattr ():verifyable_object (PTHREAD_MUTEXATTR_MAGIC),
+pshared (PTHREAD_PROCESS_PRIVATE), mutextype (PTHREAD_MUTEX_ERRORCHECK)
+{
+}
+
+pthread_mutexattr::~pthread_mutexattr ()
+{
+}
+
+List<semaphore> semaphore::semaphores;
+
+semaphore::semaphore (int pshared, unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (pshared),
+ currentvalue (value),
+ name (NULL)
+{
+ SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE)
+ ? sec_all : sec_none_nih;
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, NULL);
+ if (!this->win32_obj_id)
+ magic = 0;
+
+ semaphores.insert (this);
+}
+
+semaphore::semaphore (const char *sem_name, int oflag, mode_t mode,
+ unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (PTHREAD_PROCESS_SHARED),
+ currentvalue (value), /* Unused for named semaphores. */
+ name (NULL)
+{
+ if (oflag & O_CREAT)
+ {
+ SECURITY_ATTRIBUTES sa = sec_all;
+ security_descriptor sd;
+ if (allow_ntsec)
+ set_security_attribute (mode, &sa, sd);
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, sem_name);
+ if (!this->win32_obj_id)
+ magic = 0;
+ if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL))
+ {
+ __seterrno ();
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ }
+ else
+ {
+ this->win32_obj_id = ::OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+ sem_name);
+ if (!this->win32_obj_id)
+ {
+ __seterrno ();
+ magic = 0;
+ }
+ }
+ if (magic)
+ {
+ name = new char [strlen (sem_name + 1)];
+ if (!name)
+ {
+ set_errno (ENOSPC);
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ else
+ strcpy (name, sem_name);
+ }
+
+ semaphores.insert (this);
+}
+
+semaphore::~semaphore ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+
+ delete [] name;
+
+ semaphores.remove (this);
+}
+
+void
+semaphore::_post ()
+{
+ if (ReleaseSemaphore (win32_obj_id, 1, &currentvalue))
+ currentvalue++;
+}
+
+int
+semaphore::_getvalue (int *sval)
+{
+ long val;
+
+ switch (WaitForSingleObject (win32_obj_id, 0))
+ {
+ case WAIT_OBJECT_0:
+ ReleaseSemaphore (win32_obj_id, 1, &val);
+ *sval = val + 1;
+ break;
+ case WAIT_TIMEOUT:
+ *sval = 0;
+ break;
+ default:
+ set_errno (EAGAIN);
+ return -1;
+ }
+ return 0;
+}
+
+int
+semaphore::_trywait ()
+{
+ /* FIXME: signals should be able to interrupt semaphores...
+ We probably need WaitForMultipleObjects here. */
+ if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ set_errno (EAGAIN);
+ return -1;
+ }
+ currentvalue--;
+ return 0;
+}
+
+int
+semaphore::_timedwait (const struct timespec *abstime)
+{
+ struct timeval tv;
+ long waitlength;
+
+ myfault efault;
+ if (efault.faulted ())
+ {
+ /* According to SUSv3, abstime need not be checked for validity,
+ if the semaphore can be locked immediately. */
+ if (!_trywait ())
+ return 0;
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ gettimeofday (&tv, NULL);
+ waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
+ waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
+ if (waitlength < 0)
+ waitlength = 0;
+ switch (cancelable_wait (win32_obj_id, waitlength, cw_cancel_self, cw_sig_eintr))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ case WAIT_SIGNALED:
+ set_errno (EINTR);
+ return -1;
+ case WAIT_TIMEOUT:
+ set_errno (ETIMEDOUT);
+ return -1;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+int
+semaphore::_wait ()
+{
+ switch (cancelable_wait (win32_obj_id, INFINITE, cw_cancel_self, cw_sig_eintr))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ case WAIT_SIGNALED:
+ set_errno (EINTR);
+ return -1;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ break;
+ }
+ return 0;
+}
+
+void
+semaphore::_fixup_after_fork ()
+{
+ if (shared == PTHREAD_PROCESS_PRIVATE)
+ {
+ debug_printf ("sem %x in _fixup_after_fork", this);
+ /* FIXME: duplicate code here and in the constructor. */
+ this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue,
+ LONG_MAX, NULL);
+ if (!win32_obj_id)
+ api_fatal ("failed to create new win32 semaphore, error %d");
+ }
+}
+
+verifyable_object::verifyable_object (long verifyer):
+magic (verifyer)
+{
+}
+
+verifyable_object::~verifyable_object ()
+{
+ magic = 0;
+}
+
+DWORD WINAPI
+pthread::thread_init_wrapper (void *arg)
+{
+ pthread *thread = (pthread *) arg;
+ set_tls_self_pointer (thread);
+
+ thread->mutex.lock ();
+
+ // if thread is detached force cleanup on exit
+ if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL)
+ thread->joiner = thread;
+ thread->mutex.unlock ();
+
+ thread_printf ("started thread %p %p %p %p %p %p", arg, &_my_tls.local_clib,
+ _impure_ptr, thread, thread->function, thread->arg);
+
+ // call the user's thread
+ void *ret = thread->function (thread->arg);
+
+ thread->exit (ret);
+
+ return 0; // just for show. Never returns.
+}
+
+unsigned long
+pthread::getsequence_np ()
+{
+ return get_thread_id ();
+}
+
+int
+pthread::create (pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ if (attr && !pthread_attr::is_good_object (attr))
+ return EINVAL;
+
+ *thread = new pthread ();
+ if (!(*thread)->create (start_routine, attr ? *attr : NULL, arg))
+ {
+ delete (*thread);
+ *thread = NULL;
+ return EAGAIN;
+ }
+
+ return 0;
+}
+
+int
+pthread::once (pthread_once_t *once_control, void (*init_routine) (void))
+{
+ // already done ?
+ if (once_control->state)
+ return 0;
+
+ pthread_mutex_lock (&once_control->mutex);
+ /* Here we must set a cancellation handler to unlock the mutex if needed */
+ /* but a cancellation handler is not the right thing. We need this in the thread
+ *cleanup routine. Assumption: a thread can only be in one pthread_once routine
+ *at a time. Stote a mutex_t *in the pthread_structure. if that's non null unlock
+ *on pthread_exit ();
+ */
+ if (!once_control->state)
+ {
+ init_routine ();
+ once_control->state = 1;
+ }
+ /* Here we must remove our cancellation handler */
+ pthread_mutex_unlock (&once_control->mutex);
+ return 0;
+}
+
+int
+pthread::cancel (pthread_t thread)
+{
+ if (!is_good_object (&thread))
+ return ESRCH;
+
+ return thread->cancel ();
+}
+
+void
+pthread::atforkprepare ()
+{
+ callback *cb = MT_INTERFACE->pthread_prepare;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+
+ __fp_lock_all ();
+
+ MT_INTERFACE->fixup_before_fork ();
+}
+
+void
+pthread::atforkparent ()
+{
+ __fp_unlock_all ();
+
+ callback *cb = MT_INTERFACE->pthread_parent;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+void
+pthread::atforkchild ()
+{
+ MT_INTERFACE->fixup_after_fork ();
+
+ __fp_unlock_all ();
+
+ callback *cb = MT_INTERFACE->pthread_child;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+/* Register a set of functions to run before and after fork.
+ prepare calls are called in LI-FC order.
+ parent and child calls are called in FI-FC order. */
+int
+pthread::atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+ callback *prepcb = NULL, *parentcb = NULL, *childcb = NULL;
+ if (prepare)
+ {
+ prepcb = new callback;
+ if (!prepcb)
+ return ENOMEM;
+ }
+ if (parent)
+ {
+ parentcb = new callback;
+ if (!parentcb)
+ {
+ if (prepcb)
+ delete prepcb;
+ return ENOMEM;
+ }
+ }
+ if (child)
+ {
+ childcb = new callback;
+ if (!childcb)
+ {
+ if (prepcb)
+ delete prepcb;
+ if (parentcb)
+ delete parentcb;
+ return ENOMEM;
+ }
+ }
+
+ if (prepcb)
+ {
+ prepcb->cb = prepare;
+ List_insert (MT_INTERFACE->pthread_prepare, prepcb);
+ }
+ if (parentcb)
+ {
+ parentcb->cb = parent;
+ callback **t = &MT_INTERFACE->pthread_parent;
+ while (*t)
+ t = &(*t)->next;
+ /* t = pointer to last next in the list */
+ List_insert (*t, parentcb);
+ }
+ if (childcb)
+ {
+ childcb->cb = child;
+ callback **t = &MT_INTERFACE->pthread_child;
+ while (*t)
+ t = &(*t)->next;
+ /* t = pointer to last next in the list */
+ List_insert (*t, childcb);
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_attr_init (pthread_attr_t *attr)
+{
+ if (pthread_attr::is_good_object (attr))
+ return EBUSY;
+
+ *attr = new pthread_attr;
+ if (!pthread_attr::is_good_object (attr))
+ {
+ delete (*attr);
+ *attr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getinheritsched (const pthread_attr_t *attr,
+ int *inheritsched)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *inheritsched = (*attr)->inheritsched;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getschedparam (const pthread_attr_t *attr,
+ struct sched_param *param)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *param = (*attr)->schedparam;
+ return 0;
+}
+
+/* From a pure code point of view, this should call a helper in sched.cc,
+ to allow for someone adding scheduler policy changes to win32 in the future.
+ However that's extremely unlikely, so short and sweet will do us */
+extern "C" int
+pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *policy = SCHED_FIFO;
+ return 0;
+}
+
+
+extern "C" int
+pthread_attr_getscope (const pthread_attr_t *attr, int *contentionscope)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *contentionscope = (*attr)->contentionscope;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (detachstate < 0 || detachstate > 1)
+ return EINVAL;
+ (*attr)->joinable = detachstate;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *detachstate = (*attr)->joinable;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setinheritsched (pthread_attr_t *attr, int inheritsched)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (inheritsched != PTHREAD_INHERIT_SCHED
+ && inheritsched != PTHREAD_EXPLICIT_SCHED)
+ return ENOTSUP;
+ (*attr)->inheritsched = inheritsched;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setschedparam (pthread_attr_t *attr,
+ const struct sched_param *param)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (!valid_sched_parameters (param))
+ return ENOTSUP;
+ (*attr)->schedparam = *param;
+ return 0;
+}
+
+/* See __pthread_attr_getschedpolicy for some notes */
+extern "C" int
+pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (policy != SCHED_FIFO)
+ return ENOTSUP;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setscope (pthread_attr_t *attr, int contentionscope)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ if (contentionscope != PTHREAD_SCOPE_SYSTEM
+ && contentionscope != PTHREAD_SCOPE_PROCESS)
+ return EINVAL;
+ /* In future, we may be able to support system scope by escalating the thread
+ priority to exceed the priority class. For now we only support PROCESS scope. */
+ if (contentionscope != PTHREAD_SCOPE_PROCESS)
+ return ENOTSUP;
+ (*attr)->contentionscope = contentionscope;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_setstacksize (pthread_attr_t *attr, size_t size)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ (*attr)->stacksize = size;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ *size = (*attr)->stacksize;
+ return 0;
+}
+
+extern "C" int
+pthread_attr_destroy (pthread_attr_t *attr)
+{
+ if (!pthread_attr::is_good_object (attr))
+ return EINVAL;
+ delete (*attr);
+ *attr = NULL;
+ return 0;
+}
+
+int
+pthread::join (pthread_t *thread, void **return_val)
+{
+ pthread_t joiner = self ();
+
+ joiner->testcancel ();
+
+ // Initialize return val with NULL
+ if (return_val)
+ *return_val = NULL;
+
+ if (!is_good_object (&joiner))
+ return EINVAL;
+
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if (equal (*thread,joiner))
+ return EDEADLK;
+
+ (*thread)->mutex.lock ();
+
+ if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED)
+ {
+ (*thread)->mutex.unlock ();
+ return EINVAL;
+ }
+ else
+ {
+ (*thread)->joiner = joiner;
+ (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED;
+ (*thread)->mutex.unlock ();
+
+ switch (cancelable_wait ((*thread)->win32_obj_id, INFINITE, cw_no_cancel_self, cw_sig_resume))
+ {
+ case WAIT_OBJECT_0:
+ if (return_val)
+ *return_val = (*thread)->return_ptr;
+ delete (*thread);
+ break;
+ case WAIT_CANCELED:
+ // set joined thread back to joinable since we got canceled
+ (*thread)->joiner = NULL;
+ (*thread)->attr.joinable = PTHREAD_CREATE_JOINABLE;
+ joiner->cancel_self ();
+ // never reached
+ break;
+ default:
+ // should never happen
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int
+pthread::detach (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ (*thread)->mutex.lock ();
+ if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED)
+ {
+ (*thread)->mutex.unlock ();
+ return EINVAL;
+ }
+
+ // check if thread is still alive
+ if ((*thread)->valid && WaitForSingleObject ((*thread)->win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ // force cleanup on exit
+ (*thread)->joiner = *thread;
+ (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED;
+ (*thread)->mutex.unlock ();
+ }
+ else
+ {
+ // thread has already terminated.
+ (*thread)->mutex.unlock ();
+ delete (*thread);
+ }
+
+ return 0;
+}
+
+int
+pthread::suspend (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if ((*thread)->suspended == false)
+ {
+ (*thread)->suspended = true;
+ SuspendThread ((*thread)->win32_obj_id);
+ }
+
+ return 0;
+}
+
+
+int
+pthread::resume (pthread_t *thread)
+{
+ if (!is_good_object (thread))
+ return ESRCH;
+
+ if ((*thread)->suspended == true)
+ ResumeThread ((*thread)->win32_obj_id);
+ (*thread)->suspended = false;
+
+ return 0;
+}
+
+/* provided for source level compatability.
+ See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html
+*/
+extern "C" int
+pthread_getconcurrency ()
+{
+ return MT_INTERFACE->concurrency;
+}
+
+/* keep this in sync with sched.cc */
+extern "C" int
+pthread_getschedparam (pthread_t thread, int *policy,
+ struct sched_param *param)
+{
+ if (!pthread::is_good_object (&thread))
+ return ESRCH;
+ *policy = SCHED_FIFO;
+ /* we don't return the current effective priority, we return the current
+ requested priority */
+ *param = thread->attr.schedparam;
+ return 0;
+}
+
+/* Thread Specific Data */
+extern "C" int
+pthread_key_create (pthread_key_t *key, void (*destructor) (void *))
+{
+ *key = new pthread_key (destructor);
+
+ if (!pthread_key::is_good_object (key))
+ {
+ delete (*key);
+ *key = NULL;
+ return EAGAIN;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_key_delete (pthread_key_t key)
+{
+ if (!pthread_key::is_good_object (&key))
+ return EINVAL;
+
+ delete (key);
+ return 0;
+}
+
+/* provided for source level compatability. See
+http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html
+*/
+extern "C" int
+pthread_setconcurrency (int new_level)
+{
+ if (new_level < 0)
+ return EINVAL;
+ MT_INTERFACE->concurrency = new_level;
+ return 0;
+}
+
+/* keep syncronised with sched.cc */
+extern "C" int
+pthread_setschedparam (pthread_t thread, int policy,
+ const struct sched_param *param)
+{
+ if (!pthread::is_good_object (&thread))
+ return ESRCH;
+ if (policy != SCHED_FIFO)
+ return ENOTSUP;
+ if (!param)
+ return EINVAL;
+ int rv =
+ sched_set_thread_priority (thread->win32_obj_id, param->sched_priority);
+ if (!rv)
+ thread->attr.schedparam.sched_priority = param->sched_priority;
+ return rv;
+}
+
+
+extern "C" int
+pthread_setspecific (pthread_key_t key, const void *value)
+{
+ if (!pthread_key::is_good_object (&key))
+ return EINVAL;
+ (key)->set (value);
+ return 0;
+}
+
+extern "C" void *
+pthread_getspecific (pthread_key_t key)
+{
+ if (!pthread_key::is_good_object (&key))
+ return NULL;
+
+ return (key)->get ();
+
+}
+
+extern "C" int
+pthread_cond_destroy (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ /* reads are atomic */
+ if ((*cond)->waiting)
+ return EBUSY;
+
+ delete (*cond);
+ *cond = NULL;
+
+ return 0;
+}
+
+int
+pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr)
+{
+ pthread_cond_t new_cond;
+
+ if (attr && !pthread_condattr::is_good_object (attr))
+ return EINVAL;
+
+ cond_initialization_lock.lock ();
+
+ new_cond = new pthread_cond (attr ? (*attr) : NULL);
+ if (!is_good_object (&new_cond))
+ {
+ delete new_cond;
+ cond_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+
+ myfault efault;
+ if (efault.faulted ())
+ {
+ delete new_cond;
+ cond_initialization_lock.unlock ();
+ return EINVAL;
+ }
+
+ *cond = new_cond;
+ cond_initialization_lock.unlock ();
+
+ return 0;
+}
+
+extern "C" int
+pthread_cond_broadcast (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ (*cond)->unblock (true);
+
+ return 0;
+}
+
+extern "C" int
+pthread_cond_signal (pthread_cond_t *cond)
+{
+ if (pthread_cond::is_good_initializer (cond))
+ return 0;
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ (*cond)->unblock (false);
+
+ return 0;
+}
+
+static int
+__pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ DWORD waitlength)
+{
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ if (!pthread_mutex::can_be_unlocked (mutex))
+ return EPERM;
+
+ if (pthread_cond::is_good_initializer (cond))
+ pthread_cond::init (cond, NULL);
+ if (!pthread_cond::is_good_object (cond))
+ return EINVAL;
+
+ return (*cond)->wait (*mutex, waitlength);
+}
+
+extern "C" int
+pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ const struct timespec *abstime)
+{
+ struct timeval tv;
+ DWORD waitlength;
+
+ myfault efault;
+ if (efault.faulted ())
+ return EINVAL;
+
+ pthread_testcancel ();
+
+ /* According to SUSv3, the abstime value must be checked for validity. */
+ if (abstime->tv_sec < 0
+ || abstime->tv_nsec < 0
+ || abstime->tv_nsec > 999999999)
+ return EINVAL;
+
+ gettimeofday (&tv, NULL);
+ /* Check for immediate timeout before converting to microseconds, since
+ the resulting value can easily overflow long. This also allows to
+ evaluate microseconds directly in DWORD. */
+ if (tv.tv_sec > abstime->tv_sec
+ || (tv.tv_sec == abstime->tv_sec
+ && tv.tv_usec > abstime->tv_nsec / 1000))
+ return ETIMEDOUT;
+
+ waitlength = (abstime->tv_sec - tv.tv_sec) * 1000;
+ waitlength += (abstime->tv_nsec / 1000 - tv.tv_usec) / 1000;
+ return __pthread_cond_dowait (cond, mutex, waitlength);
+}
+
+extern "C" int
+pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+ pthread_testcancel ();
+
+ return __pthread_cond_dowait (cond, mutex, INFINITE);
+}
+
+extern "C" int
+pthread_condattr_init (pthread_condattr_t *condattr)
+{
+ if (pthread_condattr::is_good_object (condattr))
+ return EBUSY;
+
+ *condattr = new pthread_condattr;
+ if (!pthread_condattr::is_good_object (condattr))
+ {
+ delete (*condattr);
+ *condattr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared)
+{
+ if (!pthread_condattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->shared;
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared)
+{
+ if (!pthread_condattr::is_good_object (attr))
+ return EINVAL;
+ if ((pshared < 0) || (pshared > 1))
+ return EINVAL;
+ /* shared cond vars not currently supported */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->shared = pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_condattr_destroy (pthread_condattr_t *condattr)
+{
+ if (!pthread_condattr::is_good_object (condattr))
+ return EINVAL;
+ delete (*condattr);
+ *condattr = NULL;
+ return 0;
+}
+
+extern "C" int
+pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ return 0;
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ if ((*rwlock)->writer || (*rwlock)->readers ||
+ (*rwlock)->waiting_readers || (*rwlock)->waiting_writers)
+ return EBUSY;
+
+ delete (*rwlock);
+ *rwlock = NULL;
+
+ return 0;
+}
+
+int
+pthread_rwlock::init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
+{
+ pthread_rwlock_t new_rwlock;
+
+ if (attr && !pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+
+ rwlock_initialization_lock.lock ();
+
+ new_rwlock = new pthread_rwlock (attr ? (*attr) : NULL);
+ if (!is_good_object (&new_rwlock))
+ {
+ delete new_rwlock;
+ rwlock_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+
+ myfault efault;
+ if (efault.faulted ())
+ {
+ delete new_rwlock;
+ rwlock_initialization_lock.unlock ();
+ return EINVAL;
+ }
+
+ *rwlock = new_rwlock;
+ rwlock_initialization_lock.unlock ();
+
+ return 0;
+}
+
+extern "C" int
+pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
+{
+ pthread_testcancel ();
+
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->rdlock ();
+}
+
+extern "C" int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->tryrdlock ();
+}
+
+extern "C" int
+pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
+{
+ pthread_testcancel ();
+
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->wrlock ();
+}
+
+extern "C" int
+pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ pthread_rwlock::init (rwlock, NULL);
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->trywrlock ();
+}
+
+extern "C" int
+pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
+{
+ if (pthread_rwlock::is_good_initializer (rwlock))
+ return 0;
+ if (!pthread_rwlock::is_good_object (rwlock))
+ return EINVAL;
+
+ return (*rwlock)->unlock ();
+}
+
+extern "C" int
+pthread_rwlockattr_init (pthread_rwlockattr_t *rwlockattr)
+{
+ if (pthread_rwlockattr::is_good_object (rwlockattr))
+ return EBUSY;
+
+ *rwlockattr = new pthread_rwlockattr;
+ if (!pthread_rwlockattr::is_good_object (rwlockattr))
+ {
+ delete (*rwlockattr);
+ *rwlockattr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared)
+{
+ if (!pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->shared;
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared)
+{
+ if (!pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+ if ((pshared < 0) || (pshared > 1))
+ return EINVAL;
+ /* shared rwlock vars not currently supported */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->shared = pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_rwlockattr_destroy (pthread_rwlockattr_t *rwlockattr)
+{
+ if (!pthread_rwlockattr::is_good_object (rwlockattr))
+ return EINVAL;
+ delete (*rwlockattr);
+ *rwlockattr = NULL;
+ return 0;
+}
+
+/* Thread signal */
+extern "C" int
+pthread_kill (pthread_t thread, int sig)
+{
+ // lock myself, for the use of thread2signal
+ // two different kills might clash: FIXME
+
+ if (!pthread::is_good_object (&thread))
+ return EINVAL;
+
+ siginfo_t si = {0};
+ si.si_signo = sig;
+ si.si_code = SI_USER;
+ si.si_pid = myself->pid;
+ si.si_uid = myself->uid;
+ thread->cygtls->set_threadkill ();
+ int rval = sig ? sig_send (NULL, si, thread->cygtls) : 0;
+
+ // unlock myself
+ return rval;
+}
+
+extern "C" int
+pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
+{
+ return handle_sigprocmask (operation, set, old_set, _my_tls.sigmask);
+}
+
+/* ID */
+
+extern "C" int
+pthread_equal (pthread_t t1, pthread_t t2)
+{
+ return pthread::equal (t1, t2);
+}
+
+/* Mutexes */
+
+int
+pthread_mutex::init (pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr,
+ const pthread_mutex_t initializer)
+{
+ pthread_mutex_t new_mutex;
+
+ if (attr && !pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+
+ mutex_initialization_lock.lock ();
+
+ new_mutex = new pthread_mutex (attr ? (*attr) : NULL);
+ if (!is_good_object (&new_mutex))
+ {
+ delete new_mutex;
+ mutex_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+
+ if (!attr && initializer)
+ {
+ if (initializer == PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
+ new_mutex->type = PTHREAD_MUTEX_RECURSIVE;
+ else if (initializer == PTHREAD_NORMAL_MUTEX_INITIALIZER_NP)
+ new_mutex->type = PTHREAD_MUTEX_NORMAL;
+ else if (initializer == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP)
+ new_mutex->type = PTHREAD_MUTEX_ERRORCHECK;
+ }
+
+ myfault efault;
+ if (efault.faulted ())
+ {
+ delete new_mutex;
+ mutex_initialization_lock.unlock ();
+ return EINVAL;
+ }
+
+ *mutex = new_mutex;
+ mutex_initialization_lock.unlock ();
+
+ return 0;
+}
+
+extern "C" int
+pthread_mutex_getprioceiling (const pthread_mutex_t *mutex,
+ int *prioceiling)
+{
+ /* We don't define _POSIX_THREAD_PRIO_PROTECT because we do't currently support
+ mutex priorities.
+
+ We can support mutex priorities in the future though:
+ Store a priority with each mutex.
+ When the mutex is optained, set the thread priority as appropriate
+ When the mutex is released, reset the thread priority. */
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutex_lock (pthread_mutex_t *mutex)
+{
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL, *mutex);
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ return (*mutex)->lock ();
+}
+
+extern "C" int
+pthread_mutex_trylock (pthread_mutex_t *mutex)
+{
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL, *mutex);
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ return (*mutex)->trylock ();
+}
+
+extern "C" int
+pthread_mutex_unlock (pthread_mutex_t *mutex)
+{
+ if (pthread_mutex::is_good_initializer (mutex))
+ return EPERM;
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+ return (*mutex)->unlock ();
+}
+
+extern "C" int
+pthread_mutex_destroy (pthread_mutex_t *mutex)
+{
+ int rv;
+
+ if (pthread_mutex::is_good_initializer (mutex))
+ return 0;
+ if (!pthread_mutex::is_good_object (mutex))
+ return EINVAL;
+
+ rv = (*mutex)->destroy ();
+ if (rv)
+ return rv;
+
+ *mutex = NULL;
+ return 0;
+}
+
+extern "C" int
+pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling,
+ int *old_ceiling)
+{
+ return ENOSYS;
+}
+
+/* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling
+ for more detail */
+extern "C" int
+pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr,
+ int *protocol)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr,
+ int *pshared)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ *pshared = (*attr)->pshared;
+ return 0;
+}
+
+extern "C" int
+pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ *type = (*attr)->mutextype;
+ return 0;
+}
+
+/* FIXME: write and test process shared mutex's. */
+extern "C" int
+pthread_mutexattr_init (pthread_mutexattr_t *attr)
+{
+ if (pthread_mutexattr::is_good_object (attr))
+ return EBUSY;
+
+ *attr = new pthread_mutexattr ();
+ if (!pthread_mutexattr::is_good_object (attr))
+ {
+ delete (*attr);
+ *attr = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+extern "C" int
+pthread_mutexattr_destroy (pthread_mutexattr_t *attr)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ delete (*attr);
+ *attr = NULL;
+ return 0;
+}
+
+
+/* Win32 doesn't support mutex priorities */
+extern "C" int
+pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+/* Win32 doesn't support mutex priorities */
+extern "C" int
+pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr,
+ int prioceiling)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr,
+ int *prioceiling)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ return ENOSYS;
+}
+
+extern "C" int
+pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+ /* we don't use pshared for anything as yet. We need to test PROCESS_SHARED
+ *functionality
+ */
+ if (pshared != PTHREAD_PROCESS_PRIVATE)
+ return EINVAL;
+ (*attr)->pshared = pshared;
+ return 0;
+}
+
+/* see pthread_mutex_gettype */
+extern "C" int
+pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type)
+{
+ if (!pthread_mutexattr::is_good_object (attr))
+ return EINVAL;
+
+ switch (type)
+ {
+ case PTHREAD_MUTEX_ERRORCHECK:
+ case PTHREAD_MUTEX_RECURSIVE:
+ case PTHREAD_MUTEX_NORMAL:
+ (*attr)->mutextype = type;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Semaphores */
+
+/* static members */
+
+int
+semaphore::init (sem_t *sem, int pshared, unsigned int value)
+{
+ /* opengroup calls this undefined */
+ if (is_good_object (sem))
+ return EBUSY;
+
+ if (value > SEM_VALUE_MAX)
+ return EINVAL;
+
+ *sem = new semaphore (pshared, value);
+
+ if (!is_good_object (sem))
+ {
+ delete (*sem);
+ *sem = NULL;
+ return EAGAIN;
+ }
+ return 0;
+}
+
+int
+semaphore::destroy (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ return EINVAL;
+
+ /* FIXME - new feature - test for busy against threads... */
+
+ delete (*sem);
+ *sem = NULL;
+ return 0;
+}
+
+sem_t *
+semaphore::open (const char *name, int oflag, mode_t mode, unsigned int value)
+{
+ if (value > SEM_VALUE_MAX)
+ {
+ set_errno (EINVAL);
+ return NULL;
+ }
+
+ sem_t *sem = new sem_t;
+ if (!sem)
+ {
+ set_errno (ENOMEM);
+ return NULL;
+ }
+
+ *sem = new semaphore (name, oflag, mode, value);
+
+ if (!is_good_object (sem))
+ {
+ delete *sem;
+ delete sem;
+ return NULL;
+ }
+ return sem;
+}
+
+int
+semaphore::wait (sem_t *sem)
+{
+ pthread_testcancel ();
+
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_wait ();
+}
+
+int
+semaphore::trywait (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_trywait ();
+}
+
+int
+semaphore::timedwait (sem_t *sem, const struct timespec *abstime)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_timedwait (abstime);
+}
+
+int
+semaphore::post (sem_t *sem)
+{
+ if (!is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ (*sem)->_post ();
+ return 0;
+}
+
+int
+semaphore::getvalue (sem_t *sem, int *sval)
+{
+ myfault efault;
+ if (efault.faulted () || !is_good_object (sem))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return (*sem)->_getvalue (sval);
+}
+
+/* pthread_null */
+pthread *
+pthread_null::get_null_pthread ()
+{
+ /* because of weird entry points */
+ _instance.magic = 0;
+ return &_instance;
+}
+
+pthread_null::pthread_null ()
+{
+ attr.joinable = PTHREAD_CREATE_DETACHED;
+ /* Mark ourselves as invalid */
+ magic = 0;
+}
+
+pthread_null::~pthread_null ()
+{
+}
+
+bool
+pthread_null::create (void *(*)(void *), pthread_attr *, void *)
+{
+ return true;
+}
+
+void
+pthread_null::exit (void *value_ptr)
+{
+ _my_tls.remove (INFINITE);
+ ExitThread (0);
+}
+
+int
+pthread_null::cancel ()
+{
+ return 0;
+}
+
+void
+pthread_null::testcancel ()
+{
+}
+
+int
+pthread_null::setcancelstate (int state, int *oldstate)
+{
+ return EINVAL;
+}
+
+int
+pthread_null::setcanceltype (int type, int *oldtype)
+{
+ return EINVAL;
+}
+
+void
+pthread_null::push_cleanup_handler (__pthread_cleanup_handler *handler)
+{
+}
+
+void
+pthread_null::pop_cleanup_handler (int const execute)
+{
+}
+
+unsigned long
+pthread_null::getsequence_np ()
+{
+ return 0;
+}
+
+pthread_null pthread_null::_instance;
diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc
new file mode 100644
index 00000000000..0dfc316ae85
--- /dev/null
+++ b/winsup/cygwin/times.cc
@@ -0,0 +1,750 @@
+/* times.cc
+
+ Copyright 1996, 1997, 1998, 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"
+#define __timezonefunc__
+#include <time.h>
+#include <sys/times.h>
+#include <sys/timeb.h>
+#include <utime.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "hires.h"
+#include "cygtls.h"
+#include "sigproc.h"
+#include "sync.h"
+
+#define FACTOR (0x19db1ded53e8000LL)
+#define NSPERSEC 10000000LL
+
+static inline LONGLONG
+systime ()
+{
+ LARGE_INTEGER x;
+ FILETIME ft;
+ GetSystemTimeAsFileTime (&ft);
+ x.HighPart = ft.dwHighDateTime;
+ x.LowPart = ft.dwLowDateTime;
+ x.QuadPart -= FACTOR; /* Add conversion factor for UNIX vs. Windows base time */
+ x.QuadPart /= 10; /* Convert to microseconds */
+ return x.QuadPart;
+}
+
+/* Cygwin internal */
+static unsigned long long __stdcall
+__to_clock_t (FILETIME *src, int flag)
+{
+ unsigned long long total = ((unsigned long long) src->dwHighDateTime << 32) + ((unsigned)src->dwLowDateTime);
+ syscall_printf ("dwHighDateTime %u, dwLowDateTime %u", src->dwHighDateTime, src->dwLowDateTime);
+
+ /* Convert into clock ticks - the total is in 10ths of a usec. */
+ if (flag)
+ total -= FACTOR;
+
+ total /= (unsigned long long) (NSPERSEC / CLOCKS_PER_SEC);
+ syscall_printf ("total %08x %08x", (unsigned) (total>>32), (unsigned) (total));
+ return total;
+}
+
+/* times: POSIX 4.5.2.1 */
+extern "C" clock_t
+times (struct tms *buf)
+{
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return ((clock_t) -1);
+
+ LONGLONG ticks = gtod.uptime ();
+ /* Ticks is in milliseconds, convert to our ticks. Use long long to prevent
+ overflow. */
+ clock_t tc = (clock_t) (ticks * CLOCKS_PER_SEC / 1000);
+ if (wincap.has_get_process_times ())
+ {
+ GetProcessTimes (hMainProc, &creation_time, &exit_time,
+ &kernel_time, &user_time);
+
+ syscall_printf ("ticks %d, CLOCKS_PER_SEC %d", ticks, CLOCKS_PER_SEC);
+ syscall_printf ("user_time %d, kernel_time %d, creation_time %d, exit_time %d",
+ user_time, kernel_time, creation_time, exit_time);
+ buf->tms_stime = __to_clock_t (&kernel_time, 0);
+ buf->tms_utime = __to_clock_t (&user_time, 0);
+ timeval_to_filetime (&myself->rusage_children.ru_stime, &kernel_time);
+ buf->tms_cstime = __to_clock_t (&kernel_time, 1);
+ timeval_to_filetime (&myself->rusage_children.ru_utime, &user_time);
+ buf->tms_cutime = __to_clock_t (&user_time, 1);
+ }
+ else
+ /* GetProcessTimes() does not work for non-NT versions of Windows. The
+ return values are undefined, so instead just copy the ticks value
+ into utime so that clock() will work properly on these systems */
+ {
+ buf->tms_utime = tc;
+ buf->tms_stime = 0;
+ buf->tms_cstime = 0;
+ buf->tms_cutime = 0;
+ }
+
+ return tc;
+}
+
+EXPORT_ALIAS (times, _times)
+
+/* settimeofday: BSD */
+extern "C" int
+settimeofday (const struct timeval *tv, const struct timezone *tz)
+{
+ SYSTEMTIME st;
+ struct tm *ptm;
+ int res;
+
+ tz = tz; /* silence warning about unused variable */
+
+ ptm = gmtime (&tv->tv_sec);
+ st.wYear = ptm->tm_year + 1900;
+ st.wMonth = ptm->tm_mon + 1;
+ st.wDayOfWeek = ptm->tm_wday;
+ st.wDay = ptm->tm_mday;
+ st.wHour = ptm->tm_hour;
+ st.wMinute = ptm->tm_min;
+ st.wSecond = ptm->tm_sec;
+ st.wMilliseconds = tv->tv_usec / 1000;
+
+ res = !SetSystemTime (&st);
+
+ syscall_printf ("%d = settimeofday (%x, %x)", res, tv, tz);
+
+ return res;
+}
+
+/* timezone: standards? */
+extern "C" char *
+timezone (void)
+{
+ char *b = _my_tls.locals.timezone_buf;
+
+ tzset ();
+ __small_sprintf (b,"GMT%+d:%02d", (int) (-_timezone / 3600), (int) (abs (_timezone / 60) % 60));
+ return b;
+}
+
+/* Cygwin internal */
+void __stdcall
+totimeval (struct timeval *dst, FILETIME *src, int sub, int flag)
+{
+ long long x = __to_clock_t (src, flag);
+
+ x *= (int) (1e6) / CLOCKS_PER_SEC; /* Turn x into usecs */
+ x -= (long long) sub * (int) (1e6);
+
+ dst->tv_usec = x % (long long) (1e6); /* And split */
+ dst->tv_sec = x / (long long) (1e6);
+}
+
+hires_ms NO_COPY gtod;
+
+/* FIXME: Make thread safe */
+extern "C" int
+gettimeofday (struct timeval *tv, void *tzvp)
+{
+ struct timezone *tz = (struct timezone *) tzvp;
+ static bool tzflag;
+ LONGLONG now = gtod.usecs ();
+
+ if (now == (LONGLONG) -1)
+ return -1;
+
+ tv->tv_sec = now / 1000000;
+ tv->tv_usec = now % 1000000;
+
+ if (tz != NULL)
+ {
+ if (!tzflag)
+ {
+ tzset ();
+ tzflag = true;
+ }
+ tz->tz_minuteswest = _timezone / 60;
+ tz->tz_dsttime = _daylight;
+ }
+
+ return 0;
+}
+
+EXPORT_ALIAS (gettimeofday, _gettimeofday)
+
+/* Cygwin internal */
+void
+time_t_to_filetime (time_t time_in, FILETIME *out)
+{
+ long long x = time_in * NSPERSEC + FACTOR;
+ out->dwHighDateTime = x >> 32;
+ out->dwLowDateTime = x;
+}
+
+/* Cygwin internal */
+void __stdcall
+timeval_to_filetime (const struct timeval *time_in, FILETIME *out)
+{
+ long long x = time_in->tv_sec * NSPERSEC +
+ time_in->tv_usec * (NSPERSEC/1000000) + FACTOR;
+ out->dwHighDateTime = x >> 32;
+ out->dwLowDateTime = x;
+}
+
+/* Cygwin internal */
+static timeval __stdcall
+time_t_to_timeval (time_t in)
+{
+ timeval res;
+ res.tv_sec = in;
+ res.tv_usec = 0;
+ return res;
+}
+
+/* Cygwin internal */
+/* Convert a Win32 time to "UNIX" format. */
+long __stdcall
+to_time_t (FILETIME *ptr)
+{
+ /* A file time is the number of 100ns since jan 1 1601
+ stuffed into two long words.
+ A time_t is the number of seconds since jan 1 1970. */
+
+ long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned)ptr->dwLowDateTime);
+
+ /* pass "no time" as epoch */
+ if (x == 0)
+ return 0;
+
+ x -= FACTOR; /* number of 100ns between 1601 and 1970 */
+ x /= (long long) NSPERSEC; /* number of 100ns in a second */
+ return x;
+}
+
+/* Cygwin internal */
+/* Convert a Win32 time to "UNIX" timestruc_t format. */
+void __stdcall
+to_timestruc_t (FILETIME *ptr, timestruc_t *out)
+{
+ /* A file time is the number of 100ns since jan 1 1601
+ stuffed into two long words.
+ A timestruc_t is the number of seconds and microseconds since jan 1 1970
+ stuffed into a time_t and a long. */
+
+ long rem;
+ long long x = ((long long) ptr->dwHighDateTime << 32) + ((unsigned)ptr->dwLowDateTime);
+
+ /* pass "no time" as epoch */
+ if (x == 0)
+ {
+ out->tv_sec = 0;
+ out->tv_nsec = 0;
+ return;
+ }
+
+ x -= FACTOR; /* number of 100ns between 1601 and 1970 */
+ rem = x % ((long long)NSPERSEC);
+ x /= (long long) NSPERSEC; /* number of 100ns in a second */
+ out->tv_nsec = rem * 100; /* as tv_nsec is in nanoseconds */
+ out->tv_sec = x;
+}
+
+/* Cygwin internal */
+/* Get the current time as a "UNIX" timestruc_t format. */
+void __stdcall
+time_as_timestruc_t (timestruc_t * out)
+{
+ FILETIME filetime;
+
+ GetSystemTimeAsFileTime (&filetime);
+ to_timestruc_t (&filetime, out);
+}
+
+/* time: POSIX 4.5.1.1, C 4.12.2.4 */
+/* Return number of seconds since 00:00 UTC on jan 1, 1970 */
+extern "C" time_t
+time (time_t * ptr)
+{
+ time_t res;
+ FILETIME filetime;
+
+ GetSystemTimeAsFileTime (&filetime);
+ res = to_time_t (&filetime);
+ if (ptr)
+ *ptr = res;
+
+ syscall_printf ("%d = time (%x)", res, ptr);
+
+ return res;
+}
+
+/*
+ * localtime_r.c
+ * Original Author: Adapted from tzcode maintained by Arthur David Olson.
+ *
+ * Converts the calendar time pointed to by tim_p into a broken-down time
+ * expressed as local time. Returns a pointer to a structure containing the
+ * broken-down time.
+ */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
+#define DAYSPERWEEK 7
+#define MONSPERYEAR 12
+
+#define YEAR_BASE 1900
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY 4
+
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+#if 0 /* POSIX_LOCALTIME */
+
+static _CONST int mon_lengths[2][MONSPERYEAR] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static _CONST int year_lengths[2] = {
+ 365,
+ 366
+};
+
+/*
+ * Convert a time_t into a struct tm *.
+ * Does NO timezone conversion.
+ */
+
+/* Cygwin internal */
+static struct tm * __stdcall
+corelocaltime (const time_t * tim_p)
+{
+ long days, rem;
+ int y;
+ int yleap;
+ _CONST int *ip;
+ struct tm &localtime_buf=_my_tls.locals.localtime_buf;
+
+ time_t tim = *tim_p;
+ struct tm *res = &localtime_buf;
+
+ days = ((long) tim) / SECSPERDAY;
+ rem = ((long) tim) % SECSPERDAY;
+
+ while (rem < 0)
+ {
+ rem += SECSPERDAY;
+ --days;
+ }
+ while (rem >= SECSPERDAY)
+ {
+ rem -= SECSPERDAY;
+ ++days;
+ }
+
+ /* compute hour, min, and sec */
+ res->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ res->tm_min = (int) (rem / SECSPERMIN);
+ res->tm_sec = (int) (rem % SECSPERMIN);
+
+ /* compute day of week */
+ if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
+ res->tm_wday += DAYSPERWEEK;
+
+ /* compute year & day of year */
+ y = EPOCH_YEAR;
+ if (days >= 0)
+ {
+ for (;;)
+ {
+ yleap = isleap (y);
+ if (days < year_lengths[yleap])
+ break;
+ y++;
+ days -= year_lengths[yleap];
+ }
+ }
+ else
+ {
+ do
+ {
+ --y;
+ yleap = isleap (y);
+ days += year_lengths[yleap];
+ } while (days < 0);
+ }
+
+ res->tm_year = y - YEAR_BASE;
+ res->tm_yday = days;
+ ip = mon_lengths[yleap];
+ for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
+ days -= ip[res->tm_mon];
+ res->tm_mday = days + 1;
+
+ /* set daylight saving time flag */
+ res->tm_isdst = -1;
+
+ syscall_printf ("%d = corelocaltime (%x)", res, tim_p);
+
+ return (res);
+}
+
+/* localtime: POSIX 8.1.1, C 4.12.3.4 */
+/*
+ * localtime takes a time_t (which is in UTC)
+ * and formats it into a struct tm as a local time.
+ */
+extern "C" struct tm *
+localtime (const time_t *tim_p)
+{
+ time_t tim = *tim_p;
+ struct tm *rtm;
+
+ tzset ();
+
+ tim -= _timezone;
+
+ rtm = corelocaltime (&tim);
+
+ rtm->tm_isdst = _daylight;
+
+ syscall_printf ("%x = localtime (%x)", rtm, tim_p);
+
+ return rtm;
+}
+
+/* gmtime: C 4.12.3.3 */
+/*
+ * gmtime takes a time_t (which is already in UTC)
+ * and just puts it into a struct tm.
+ */
+extern "C" struct tm *
+gmtime (const time_t *tim_p)
+{
+ time_t tim = *tim_p;
+
+ struct tm *rtm = corelocaltime (&tim);
+ /* UTC has no daylight savings time */
+ rtm->tm_isdst = 0;
+
+ syscall_printf ("%x = gmtime (%x)", rtm, tim_p);
+
+ return rtm;
+}
+
+#endif /* POSIX_LOCALTIME */
+
+static int
+utimes_worker (const char *path, const struct timeval *tvp, int nofollow)
+{
+ int res = -1;
+ path_conv win32 (path, PC_POSIX | (nofollow ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW));
+
+ if (win32.error)
+ set_errno (win32.error);
+ else
+ {
+ fhandler_base *fh = NULL;
+ bool fromfd = false;
+
+ cygheap_fdenum cfd (true);
+ while (cfd.next () >= 0)
+ if (cfd->get_access () & (FILE_WRITE_ATTRIBUTES | GENERIC_WRITE)
+ && strcmp (cfd->get_win32_name (), win32) == 0)
+ {
+ fh = cfd;
+ fromfd = true;
+ break;
+ }
+
+ if (!fh)
+ {
+ if (!(fh = build_fh_pc (win32)))
+ goto error;
+
+ if (fh->error ())
+ {
+ debug_printf ("got %d error from build_fh_name", fh->error ());
+ set_errno (fh->error ());
+ }
+ }
+
+ res = fh->utimes (tvp);
+
+ if (!fromfd)
+ delete fh;
+ }
+
+error:
+ syscall_printf ("%d = utimes (%s, %p)", res, path, tvp);
+ return res;
+}
+
+/* utimes: POSIX/SUSv3 */
+extern "C" int
+utimes (const char *path, const struct timeval *tvp)
+{
+ return utimes_worker (path, tvp, 0);
+}
+
+/* BSD */
+extern "C" int
+lutimes (const char *path, const struct timeval *tvp)
+{
+ return utimes_worker (path, tvp, 1);
+}
+
+/* BSD */
+extern "C" int
+futimes (int fd, const struct timeval *tvp)
+{
+ int res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = -1;
+ else if (cfd->get_access () & (FILE_WRITE_ATTRIBUTES | GENERIC_WRITE))
+ res = cfd->utimes (tvp);
+ else
+ res = utimes_worker (cfd->get_win32_name (), tvp, 1);
+ syscall_printf ("%d = futimes (%d, %p)", res, fd, tvp);
+ return res;
+}
+
+/* utime: POSIX 5.6.6.1 */
+extern "C" int
+utime (const char *path, const struct utimbuf *buf)
+{
+ struct timeval tmp[2];
+
+ if (buf == 0)
+ return utimes (path, 0);
+
+ debug_printf ("incoming utime act %x", buf->actime);
+ tmp[0] = time_t_to_timeval (buf->actime);
+ tmp[1] = time_t_to_timeval (buf->modtime);
+
+ return utimes (path, tmp);
+}
+
+/* ftime: standards? */
+extern "C" int
+ftime (struct timeb *tp)
+{
+ struct timeval tv;
+ struct timezone tz;
+
+ if (gettimeofday (&tv, &tz) < 0)
+ return -1;
+
+ tp->time = tv.tv_sec;
+ tp->millitm = tv.tv_usec / 1000;
+ tp->timezone = tz.tz_minuteswest;
+ tp->dstflag = tz.tz_dsttime;
+
+ return 0;
+}
+
+/* obsolete, changed to cygwin_tzset when localtime.c was added - dj */
+extern "C" void
+cygwin_tzset ()
+{
+}
+
+#define stupid_printf if (cygwin_finished_initializing) debug_printf
+void
+hires_us::prime ()
+{
+ LARGE_INTEGER ifreq;
+ if (!QueryPerformanceFrequency (&ifreq))
+ {
+ inited = -1;
+ return;
+ }
+
+ int priority = GetThreadPriority (GetCurrentThread ());
+
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+ if (!QueryPerformanceCounter (&primed_pc))
+ {
+ SetThreadPriority (GetCurrentThread (), priority);
+ inited = -1;
+ return;
+ }
+
+ primed_ft.QuadPart = systime ();
+ freq = (double) ((double) 1000000. / (double) ifreq.QuadPart);
+ inited = true;
+ SetThreadPriority (GetCurrentThread (), priority);
+}
+
+LONGLONG
+hires_us::usecs (bool justdelta)
+{
+ if (!inited)
+ prime ();
+ if (inited < 0)
+ {
+ set_errno (ENOSYS);
+ return (long long) -1;
+ }
+
+ LARGE_INTEGER now;
+ if (!QueryPerformanceCounter (&now))
+ {
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ // FIXME: Use round() here?
+ now.QuadPart = (LONGLONG) (freq * (double) (now.QuadPart - primed_pc.QuadPart));
+ LONGLONG res = justdelta ? now.QuadPart : primed_ft.QuadPart + now.QuadPart;
+ return res;
+}
+
+void
+hires_ms::prime ()
+{
+ if (!inited)
+ {
+ int priority = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+ initime_us = systime () - (((LONGLONG) timeGetTime ()) * 1000LL);
+ inited = true;
+ SetThreadPriority (GetCurrentThread (), priority);
+ }
+ return;
+}
+
+LONGLONG
+hires_ms::usecs ()
+{
+ if (!inited)
+ prime ();
+
+ LONGLONG t = systime ();
+ LONGLONG res = initime_us + (((LONGLONG) timeGetTime ()) * 1000LL);
+ if (res < (t - 40000LL))
+ {
+ inited = false;
+ prime ();
+ res = initime_us + (((LONGLONG) timeGetTime ()) * 1000LL);
+ }
+ return res;
+}
+
+extern "C" int
+clock_gettime (clockid_t clk_id, struct timespec *tp)
+{
+ if (clk_id != CLOCK_REALTIME)
+ {
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ LONGLONG now = gtod.usecs ();
+ if (now == (LONGLONG) -1)
+ return -1;
+
+ tp->tv_sec = now / 1000000;
+ tp->tv_nsec = (now % 1000000) * 1000;
+ return 0;
+}
+
+static DWORD minperiod; // FIXME: Maintain period after a fork.
+
+UINT
+hires_ms::resolution ()
+{
+ if (!minperiod)
+ {
+ /* Try to empirically determine current timer resolution */
+ int priority = GetThreadPriority (GetCurrentThread ());
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+ DWORD period = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ DWORD now;
+ DWORD then = timeGetTime ();
+ while ((now = timeGetTime ()) == then)
+ continue;
+ then = now;
+ while ((now = timeGetTime ()) == then)
+ continue;
+ period += now - then;
+ }
+ SetThreadPriority (GetCurrentThread (), priority);
+ period /= 4;
+ minperiod = period;
+ }
+ return minperiod;
+}
+
+extern "C" int
+clock_getres (clockid_t clk_id, struct timespec *tp)
+{
+ if (clk_id != CLOCK_REALTIME)
+ {
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ DWORD period = gtod.resolution ();
+
+ tp->tv_sec = period / 1000;
+ tp->tv_nsec = (period % 1000) * 1000000;
+
+ return 0;
+}
+
+extern "C" int
+clock_setres (clockid_t clk_id, struct timespec *tp)
+{
+ static NO_COPY bool period_set;
+ if (clk_id != CLOCK_REALTIME)
+ {
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ if (period_set)
+ timeEndPeriod (minperiod);
+
+ DWORD period = (tp->tv_sec * 1000) + ((tp->tv_nsec) / 1000000);
+
+ if (timeBeginPeriod (period))
+ {
+ minperiod = period;
+ period_set = true;
+ }
+ else
+ {
+ __seterrno ();
+ timeBeginPeriod (minperiod);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h
index 26c115b5bfa..a7a42d86b84 100644
--- a/winsup/cygwin/winsup.h
+++ b/winsup/cygwin/winsup.h
@@ -186,7 +186,7 @@ extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *);
enum exit_states
{
ES_NOT_EXITING = 0,
- ES_SET_MUTO,
+ ES_PROCESS_LOCKED,
ES_GLOBAL_DTORS,
ES_EVENTS_TERMINATE,
ES_THREADTERM,
@@ -349,7 +349,6 @@ extern bool display_title;
extern bool transparent_exe;
extern bool in_forkee;
-extern bool in_dllentry;
extern HANDLE hMainThread;
extern HANDLE hMainProc;