From e62ab8f1f4f65459d52959afc912eaaf0bc2767c Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 12 Nov 2007 15:30:18 +0000 Subject: 2007-11-08 Christopher Faylor * dllfixdbg: Eliminate extra objcopy step. 2007-11-07 Pedro Alves * dllfixdbg: Pass --only-keep-debug to objcopy, instead of selecting the sections manually. 2007-11-06 Corinna Vinschen * thread.cc (pthread_key_create): Drop check for incoming valid object. 2007-11-06 Corinna Vinschen * 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 * 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 * 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 * 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 * termios.cc (setspeed): Support new baud rates introduced 2007-02-05. 2007-09-18 Corinna Vinschen * 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 * 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 * syscalls.cc (open): Don't follow symlinks if O_EXCL is given. 2007-08-09 Ernie Coskrey * gendef (sigbe): Reset "incyg" while the stack lock is active to avoid a potential race. 2007-08-01 Corinna Vinschen * localtime.cc (tzsetwall): Don't set TZ. 2007-07-17 Corinna Vinschen * fhandler.cc (fhandler_base::fhaccess): Add check for R/O file system. 2007-07-14 Christopher Faylor * 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 * init.cc (in_dllentry): Make NO_COPY to avoid spurious false positives. 2007-07-09 Christopher Faylor * dlfcn.cc (dlclose): Don't close handle returned from GetModuleHandle(NULL). 2007-07-06 Corinna Vinschen * times.cc (gettimeofday): Align definition to POSIX. 2007-07-04 Corinna Vinschen * times.cc: Define __timezonefunc__ before including time.h to protect definition of timezone function. 2007-07-04 Corinna Vinschen * include/cygwin/time.h: Switch to timezone variable by default. Add comment. 2007-06-27 Corinna Vinschen * 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 * signal.cc (usleep): Use useconds_t for the type as per POSIX. 2007-06-12 Corinna Vinschen * fhandler.cc (fhandler_base::fstat): Set pipe permission bits more correctly. 2007-05-29 Corinna Vinschen * 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 * fhandler_floppy.cc (fhandler_dev_floppy::lseek): Don't invalidate devbuf if new position is within buffered range. 2007-05-21 Eric Blake * include/search.h (hsearch_r): Provide declaration. 2007-05-21 Christian Franke Corinna Vinschen * 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 * 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 * cygwin.sc: Remove duplicated .debug_macinfo section. * dllfixdbg: Also copy DWARF-2 sections into .dbg file. 2007-04-06 Eric Blake * include/stdint.h (WINT_MIN): Fix sign. 2007-04-04 Eric Blake * include/stdint.h (WINT_MIN, WINT_MAX): Fix definition. 2007-03-28 Christopher Faylor * spawn.cc (spawn_guts): Start pure-windows processes in a suspended state to avoid potential DuplicateHandle problems. 2007-03-07 Christopher Faylor * signal.cc (handle_sigprocmask): Remove extraneous sig_dispatch_pending. 2007-02-26 Corinna Vinschen * fhandler.cc (fhandler_base::fstat): Set all file times to arbitrary fixed value. 2007-02-20 Christopher Faylor * 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 * fhandler_socket.cc (fhandler_socket::bind): Remove printing wrong errno in debug output. 2007-02-05 Corinna Vinschen * 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-04 Brian Ford Corinna Vinschen * 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. 2006-11-08 Corinna Vinschen * security.cc (get_token_group_sidlist): Always add the local group to the token. 2006-07-14 Corinna Vinschen * 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. --- winsup/cygwin/ChangeLog | 242 ++- winsup/cygwin/cygwin.sc | 1 - winsup/cygwin/dlfcn.cc | 167 ++ winsup/cygwin/dllfixdbg | 75 + winsup/cygwin/dtable.cc | 6 +- winsup/cygwin/exceptions.cc | 1394 +++++++++++++++ winsup/cygwin/fhandler.cc | 44 +- winsup/cygwin/fhandler.h | 11 +- winsup/cygwin/fhandler_disk_file.cc | 17 +- winsup/cygwin/fhandler_floppy.cc | 536 ++++++ winsup/cygwin/fhandler_mailslot.cc | 160 ++ winsup/cygwin/fhandler_raw.cc | 215 +++ winsup/cygwin/fhandler_serial.cc | 72 + winsup/cygwin/fhandler_socket.cc | 1694 ++++++++++++++++++ winsup/cygwin/gendef | 455 +++++ winsup/cygwin/include/cygwin/time.h | 45 + winsup/cygwin/include/search.h | 74 + winsup/cygwin/include/stdint.h | 182 ++ winsup/cygwin/include/sys/stdio.h | 44 + winsup/cygwin/include/sys/termios.h | 354 ++++ winsup/cygwin/init.cc | 163 ++ winsup/cygwin/localtime.cc | 2170 +++++++++++++++++++++++ winsup/cygwin/mmap.cc | 28 +- winsup/cygwin/security.cc | 18 +- winsup/cygwin/shared.cc | 9 +- winsup/cygwin/shared_info.h | 5 +- winsup/cygwin/shm.cc | 411 +++++ winsup/cygwin/signal.cc | 577 ++++++ winsup/cygwin/sigproc.cc | 2 +- winsup/cygwin/spawn.cc | 4 +- winsup/cygwin/sync.h | 73 + winsup/cygwin/syscalls.cc | 5 +- winsup/cygwin/termios.cc | 316 ++++ winsup/cygwin/thread.cc | 3309 +++++++++++++++++++++++++++++++++++ winsup/cygwin/times.cc | 750 ++++++++ winsup/cygwin/winsup.h | 3 +- 36 files changed, 13563 insertions(+), 68 deletions(-) create mode 100644 winsup/cygwin/dlfcn.cc create mode 100755 winsup/cygwin/dllfixdbg create mode 100644 winsup/cygwin/exceptions.cc create mode 100644 winsup/cygwin/fhandler_floppy.cc create mode 100644 winsup/cygwin/fhandler_mailslot.cc create mode 100644 winsup/cygwin/fhandler_raw.cc create mode 100644 winsup/cygwin/fhandler_socket.cc create mode 100755 winsup/cygwin/gendef create mode 100644 winsup/cygwin/include/cygwin/time.h create mode 100644 winsup/cygwin/include/search.h create mode 100644 winsup/cygwin/include/stdint.h create mode 100644 winsup/cygwin/include/sys/stdio.h create mode 100644 winsup/cygwin/include/sys/termios.h create mode 100644 winsup/cygwin/init.cc create mode 100644 winsup/cygwin/localtime.cc create mode 100644 winsup/cygwin/shm.cc create mode 100644 winsup/cygwin/signal.cc create mode 100644 winsup/cygwin/sync.h create mode 100644 winsup/cygwin/termios.cc create mode 100644 winsup/cygwin/thread.cc create mode 100644 winsup/cygwin/times.cc 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 + + * dllfixdbg: Eliminate extra objcopy step. + +2007-11-07 Pedro Alves + + * dllfixdbg: Pass --only-keep-debug to objcopy, instead of + selecting the sections manually. + +2007-11-06 Corinna Vinschen + + * thread.cc (pthread_key_create): Drop check for incoming valid object. + +2007-11-06 Corinna Vinschen + + * 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 + + * 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 + + * 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 + + * 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 + + * termios.cc (setspeed): Support new baud rates introduced 2007-02-05. + +2007-09-18 Corinna Vinschen + + * 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 + + * 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 + + * syscalls.cc (open): Don't follow symlinks if O_EXCL is given. + +2007-08-09 Ernie Coskrey + + * gendef (sigbe): Reset "incyg" while the stack lock is active to avoid + a potential race. + +2007-08-01 Corinna Vinschen + + * localtime.cc (tzsetwall): Don't set TZ. + +2007-07-17 Corinna Vinschen + + * fhandler.cc (fhandler_base::fhaccess): Add check for R/O file system. + +2007-07-14 Christopher Faylor + + * 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 + + * init.cc (in_dllentry): Make NO_COPY to avoid spurious false positives. + +2007-07-09 Christopher Faylor + + * dlfcn.cc (dlclose): Don't close handle returned from + GetModuleHandle(NULL). + +2007-07-06 Corinna Vinschen + + * times.cc (gettimeofday): Align definition to POSIX. + +2007-07-04 Corinna Vinschen + + * times.cc: Define __timezonefunc__ before including time.h to protect + definition of timezone function. + +2007-07-04 Corinna Vinschen + + * include/cygwin/time.h: Switch to timezone variable by default. Add + comment. + 2007-06-29 Brian Dessent * posix.sgml: List resolver functions in BSD section with reference @@ -11,9 +124,111 @@ 2007-06-28 Brian Dessent 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 + + * 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 + + * signal.cc (usleep): Use useconds_t for the type as per POSIX. + +2007-06-12 Corinna Vinschen + + * fhandler.cc (fhandler_base::fstat): Set pipe permission bits more + correctly. + +2007-05-29 Corinna Vinschen + + * 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 + + * fhandler_floppy.cc (fhandler_dev_floppy::lseek): Don't invalidate + devbuf if new position is within buffered range. + +2007-05-21 Eric Blake + + * include/search.h (hsearch_r): Provide declaration. + +2007-05-21 Christian Franke + Corinna Vinschen + + * 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 + + * 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 + + * cygwin.sc: Remove duplicated .debug_macinfo section. + * dllfixdbg: Also copy DWARF-2 sections into .dbg file. + +2007-04-06 Eric Blake + + * include/stdint.h (WINT_MIN): Fix sign. + +2007-04-04 Eric Blake + + * include/stdint.h (WINT_MIN, WINT_MAX): Fix definition. + +2007-03-28 Christopher Faylor + + * spawn.cc (spawn_guts): Start pure-windows processes in a suspended + state to avoid potential DuplicateHandle problems. + +2007-03-07 Christopher Faylor + + * signal.cc (handle_sigprocmask): Remove extraneous + sig_dispatch_pending. + +2007-02-26 Corinna Vinschen + + * fhandler.cc (fhandler_base::fstat): Set all file times to arbitrary + fixed value. + +2007-02-20 Christopher Faylor + + * 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 + + * fhandler_socket.cc (fhandler_socket::bind): Remove printing wrong + errno in debug output. + +2007-02-05 Corinna Vinschen + + * 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 @@ -68,6 +283,16 @@ * mmap.cc: Do bookkeeping in 4K pages, rather than in 64K chunks. +2007-01-04 Brian Ford + Corinna Vinschen + + * 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 * 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 + + * security.cc (get_token_group_sidlist): Always add the local + group to the token. + 2006-11-06 Corinna Vinschen * 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 + + * 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 * 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 +#include +#include +#include +#include +#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 () { + 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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include +#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, + §or_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 +#include + +#include +#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 +#include + +#include +#include +#include +#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 +#include +#include +#include + +#include +#define USE_SYS_TYPES_FD_SET +#include +#include +#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 +#include +#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 *) ∈ + 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 () { + push(@top, nocr $_); + last if /^\s*exports\s*$/i; +} +my $libline = nocr scalar(); +my @in = nocr ; +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 = < 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 < + * 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 +#include + +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 +#include + +/* 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 +#include "thread.h" +#include "perprocess.h" +#include "cygtls.h" +#include "pinfo.h" +#include +#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 +#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 '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 . +*/ +#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 +#ifdef USE_SERVER +#include +#include +#include +#include + +#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 + +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 +#include "cygerrno.h" +#include +#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 + +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 +#include +#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 + +/* 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 + Substantialy enhanced by Robert Collins + +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 +#include "cygerrno.h" +#include +#include +#include "pinfo.h" +#include "sigproc.h" +#include "perprocess.h" +#include "security.h" +#include "cygtls.h" +#include +#include +#include +#include +#include +#include + +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::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::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::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::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::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::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, ¤tvalue)) + 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 +#include +#include +#include +#include +#include +#include +#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; -- cgit v1.2.1