summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2004-01-24 01:53:51 +0000
committerChristopher Faylor <cgf@redhat.com>2004-01-24 01:53:51 +0000
commit78db4ae60f67ce0505e77b0a1c834bd5f616dd7f (patch)
tree3b7b842e734daf64b320d54133f604c86eba951d
parentca7888f92cb353634fff66e026d66707f64a31e6 (diff)
downloadgdb-78db4ae60f67ce0505e77b0a1c834bd5f616dd7f.tar.gz
* fhandler_socket.cc (fhandler_socket::create_secret_event): Avoid creating
multiple handles. Always allow event inheritance but set the handle inheritance appropriately. Improve error handling. (fhandler_socket::check_peer_secret_event): Improve error handling. (fhandler_socket::close_secret_event): Simply call CloseHandle. (fhandler_socket::set_close_on_exec): Set secret event inheritance. * configure.in: Remove NEWVFORK default. * configure: Regenerate. * dcrt0.cc: Conditionalize vfork stuff throughout. * dtable.cc: Ditto. * perthread.h: Ditto. * pipe.cc (fhandler_pipe::close): Ditto. * spawn.cc (spawnve): Ditto. * syscalls.cc (setsid): Ditto. * exceptions.cc (sigpacket::process): Use macro to refer to vfork pid. * debug.cc (verify_handle): Define new function. * debug.h (VerifyHandle): Define new macro. (verify_handle): Declare new function * fhandler.cc (fhandler_base::dup): Verify that dup'ed handle is not supposed to be in use. (fhandler_base::set_inheritance): Ditto. (fhandler_base::fork_fixup): Ditto. * fhandler_socket.cc (fhandler_socket::dup): Ditto. * fhandler_tty.cc (fhandler_tty_slave::open): Ditto. * net.cc (set_socket_inheritance): Ditto. * pinfo.cc (pinfo_fixup_after_exec): Ditto. * sigproc.cc (proc_subproc): Ditto. (sig_send): Ditto. * spawn.cc (spawn_guts): Ditto. * thread.cc (pthread::init_mainthread): Ditto. * pipe.cc (fhandler_pipe::close): Close read_state with ForceCloseHandle since it was protected. (fhandler_pipe::fixup_after_exec): Protect read_state handle. (fhandler_pipe::dup): Correctly close open handles on error condition. Verify that dup'ed handle is not supposed to be in use. (fhandler_pipe::create): Protect read_state.
-rw-r--r--winsup/cygwin/ChangeLog43
-rwxr-xr-xwinsup/cygwin/configure2337
-rw-r--r--winsup/cygwin/configure.in186
-rw-r--r--winsup/cygwin/cygheap.cc472
-rw-r--r--winsup/cygwin/cygheap.h377
-rw-r--r--winsup/cygwin/dcrt0.cc11
-rw-r--r--winsup/cygwin/debug.cc241
-rw-r--r--winsup/cygwin/debug.h90
-rw-r--r--winsup/cygwin/dtable.cc904
-rw-r--r--winsup/cygwin/dtable.h102
-rw-r--r--winsup/cygwin/exceptions.cc2
-rw-r--r--winsup/cygwin/fhandler.cc16
-rw-r--r--winsup/cygwin/fhandler_socket.cc1314
-rw-r--r--winsup/cygwin/fhandler_tty.cc2
-rw-r--r--winsup/cygwin/net.cc2321
-rw-r--r--winsup/cygwin/perthread.h98
-rw-r--r--winsup/cygwin/pinfo.cc803
-rw-r--r--winsup/cygwin/pipe.cc323
-rw-r--r--winsup/cygwin/sigproc.cc5
-rw-r--r--winsup/cygwin/spawn.cc1119
-rw-r--r--winsup/cygwin/syscalls.cc2996
-rw-r--r--winsup/cygwin/thread.cc3224
22 files changed, 16974 insertions, 12 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index aaac6a0a2ba..5e4f35bae4c 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,46 @@
+2004-01-23 Pierre Humblet <pierre.humblet@ieee.org>
+
+ * fhandler_socket.cc (fhandler_socket::create_secret_event): Avoid
+ creating multiple handles. Always allow event inheritance but set the
+ handle inheritance appropriately. Improve error handling.
+ (fhandler_socket::check_peer_secret_event): Improve error handling.
+ (fhandler_socket::close_secret_event): Simply call CloseHandle.
+ (fhandler_socket::set_close_on_exec): Set secret event inheritance.
+
+2004-01-23 Christopher Faylor <cgf@redhat.com>
+
+ * configure.in: Remove NEWVFORK default.
+ * configure: Regenerate.
+ * dcrt0.cc: Conditionalize vfork stuff throughout.
+ * dtable.cc: Ditto.
+ * perthread.h: Ditto.
+ * pipe.cc (fhandler_pipe::close): Ditto.
+ * spawn.cc (spawnve): Ditto.
+ * syscalls.cc (setsid): Ditto.
+ * exceptions.cc (sigpacket::process): Use macro to refer to vfork pid.
+
+ * debug.cc (verify_handle): Define new function.
+ * debug.h (VerifyHandle): Define new macro.
+ (verify_handle): Declare new function
+ * fhandler.cc (fhandler_base::dup): Verify that dup'ed handle is not
+ supposed to be in use.
+ (fhandler_base::set_inheritance): Ditto.
+ (fhandler_base::fork_fixup): Ditto.
+ * fhandler_socket.cc (fhandler_socket::dup): Ditto.
+ * fhandler_tty.cc (fhandler_tty_slave::open): Ditto.
+ * net.cc (set_socket_inheritance): Ditto.
+ * pinfo.cc (pinfo_fixup_after_exec): Ditto.
+ * sigproc.cc (proc_subproc): Ditto.
+ (sig_send): Ditto.
+ * spawn.cc (spawn_guts): Ditto.
+ * thread.cc (pthread::init_mainthread): Ditto.
+ * pipe.cc (fhandler_pipe::close): Close read_state with
+ ForceCloseHandle since it was protected.
+ (fhandler_pipe::fixup_after_exec): Protect read_state handle.
+ (fhandler_pipe::dup): Correctly close open handles on error condition.
+ Verify that dup'ed handle is not supposed to be in use.
+ (fhandler_pipe::create): Protect read_state.
+
2004-01-23 Christopher Faylor <cgf@redhat.com>
* exceptions.cc (sig_handle_tty_stop): Fix boneheaded mistake by using
diff --git a/winsup/cygwin/configure b/winsup/cygwin/configure
new file mode 100755
index 00000000000..9f778b8469f
--- /dev/null
+++ b/winsup/cygwin/configure
@@ -0,0 +1,2337 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --enable-debugging Build a cygwin DLL which has more consistency checking for debugging"
+ac_help="$ac_help
+ --enable-server Build a cygwin DLL which can communicate with cygserver"
+ac_help="$ac_help
+ --enable-malloc-debugging Build a cygwin DLL with heap sanity checking (this is very slow, use only if you have heap corruption problems)"
+ac_help="$ac_help
+ --enable-vfork Build a cygwin DLL which uses experimental vfork code"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=init.cc
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+INSTALL="/bin/sh "`cd $srcdir/../..; echo $(pwd)/install-sh -c`
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:568: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+
+
+
+
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:654: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:675: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:693: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+
+if test $host != $build; then
+ ac_tool_prefix=${host_alias}-
+else
+ ac_tool_prefix=
+fi
+
+# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:725: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_CC"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:757: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="gcc"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ CC="gcc"
+fi
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:793: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:842: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:851: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ ac_test_CFLAGS="${CFLAGS+set}"
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS=
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:866: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+ if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+ elif test $ac_cv_prog_cc_g = yes; then
+ CFLAGS="-gstabs+ -O2"
+ else
+ CFLAGS="-O2"
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+# Extract the first word of "${ac_tool_prefix}g++", so it can be a program name with args.
+set dummy ${ac_tool_prefix}g++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:896: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="${ac_tool_prefix}g++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_CXX"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "g++", so it can be a program name with args.
+set dummy g++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:928: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="g++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CXX" && ac_cv_prog_CXX="g++"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ CXX="g++"
+fi
+fi
+
+if test -z "$CXX"; then
+ # Extract the first word of "c++", so it can be a program name with args.
+set dummy c++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:964: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="c++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+CXXFLAGS='$(CFLAGS)'
+
+
+case "$with_cross_host" in
+ ""|*cygwin*)
+ all_host="all_host"
+ install_host="install_host"
+ LIBSERVER='$(bupdir)/cygserver/libcygserver.a'
+ ;;
+ *)
+ all_host=
+ install_host=
+ LIBSERVER=
+ ;;
+esac
+
+test -n "$LIBSERVER" && cat >> confdefs.h <<\EOF
+#define USE_SERVER 1
+EOF
+
+
+
+
+
+# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1020: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_AR"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1052: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AR="ar"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_AR" && ac_cv_prog_AR="ar"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ AR="ar"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args.
+set dummy ${ac_tool_prefix}as; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1087: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AS'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AS"; then
+ ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AS="${ac_tool_prefix}as"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AS="$ac_cv_prog_AS"
+if test -n "$AS"; then
+ echo "$ac_t""$AS" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_AS"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "as", so it can be a program name with args.
+set dummy as; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1119: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AS'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AS"; then
+ ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AS="as"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_AS" && ac_cv_prog_AS="as"
+fi
+fi
+AS="$ac_cv_prog_AS"
+if test -n "$AS"; then
+ echo "$ac_t""$AS" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ AS="as"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1154: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_RANLIB"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1186: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB="ranlib"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ RANLIB="ranlib"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ld; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1221: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_LD="${ac_tool_prefix}ld"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+LD="$ac_cv_prog_LD"
+if test -n "$LD"; then
+ echo "$ac_t""$LD" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_LD"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ld", so it can be a program name with args.
+set dummy ld; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1253: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_LD="ld"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_LD" && ac_cv_prog_LD="ld"
+fi
+fi
+LD="$ac_cv_prog_LD"
+if test -n "$LD"; then
+ echo "$ac_t""$LD" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ LD="ld"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}nm", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1288: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_NM'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$NM"; then
+ ac_cv_prog_NM="$NM" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_NM="${ac_tool_prefix}nm"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+NM="$ac_cv_prog_NM"
+if test -n "$NM"; then
+ echo "$ac_t""$NM" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_NM"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "nm", so it can be a program name with args.
+set dummy nm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1320: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_NM'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$NM"; then
+ ac_cv_prog_NM="$NM" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_NM="nm"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_NM" && ac_cv_prog_NM="nm"
+fi
+fi
+NM="$ac_cv_prog_NM"
+if test -n "$NM"; then
+ echo "$ac_t""$NM" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ NM="nm"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1355: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_DLLTOOL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+DLLTOOL="$ac_cv_prog_DLLTOOL"
+if test -n "$DLLTOOL"; then
+ echo "$ac_t""$DLLTOOL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_DLLTOOL"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1387: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_DLLTOOL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_DLLTOOL="dlltool"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_DLLTOOL" && ac_cv_prog_DLLTOOL="dlltool"
+fi
+fi
+DLLTOOL="$ac_cv_prog_DLLTOOL"
+if test -n "$DLLTOOL"; then
+ echo "$ac_t""$DLLTOOL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ DLLTOOL="dlltool"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args.
+set dummy ${ac_tool_prefix}windres; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1422: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$WINDRES"; then
+ ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_WINDRES="${ac_tool_prefix}windres"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+WINDRES="$ac_cv_prog_WINDRES"
+if test -n "$WINDRES"; then
+ echo "$ac_t""$WINDRES" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_WINDRES"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "windres", so it can be a program name with args.
+set dummy windres; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1454: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$WINDRES"; then
+ ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_WINDRES="windres"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_WINDRES" && ac_cv_prog_WINDRES="windres"
+fi
+fi
+WINDRES="$ac_cv_prog_WINDRES"
+if test -n "$WINDRES"; then
+ echo "$ac_t""$WINDRES" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ WINDRES="windres"
+fi
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1488: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1503 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1509: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1520 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1526: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1537 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1543: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6
+echo "configure:1570: checking for working alloca.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1575 "configure"
+#include "confdefs.h"
+#include <alloca.h>
+int main() {
+char *p = alloca(2 * sizeof(int));
+; return 0; }
+EOF
+if { (eval echo configure:1582: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_header_alloca_h=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_alloca_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_alloca_h" 1>&6
+if test $ac_cv_header_alloca_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA_H 1
+EOF
+
+fi
+
+echo $ac_n "checking for alloca""... $ac_c" 1>&6
+echo "configure:1603: checking for alloca" >&5
+if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1608 "configure"
+#include "confdefs.h"
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+int main() {
+char *p = (char *) alloca(1);
+; return 0; }
+EOF
+if { (eval echo configure:1636: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_func_alloca_works=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_func_alloca_works=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_func_alloca_works" 1>&6
+if test $ac_cv_func_alloca_works = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA 1
+EOF
+
+fi
+
+if test $ac_cv_func_alloca_works = no; then
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+ # that cause trouble. Some versions do not even contain alloca or
+ # contain a buggy version. If you still want to use their alloca,
+ # use ar to extract alloca.o from them instead of compiling alloca.c.
+ ALLOCA=alloca.${ac_objext}
+ cat >> confdefs.h <<\EOF
+#define C_ALLOCA 1
+EOF
+
+
+echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6
+echo "configure:1668: checking whether alloca needs Cray hooks" >&5
+if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1673 "configure"
+#include "confdefs.h"
+#if defined(CRAY) && ! defined(CRAY2)
+webecray
+#else
+wenotbecray
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "webecray" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_os_cray=yes
+else
+ rm -rf conftest*
+ ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_os_cray" 1>&6
+if test $ac_cv_os_cray = yes; then
+for ac_func in _getb67 GETB67 getb67; do
+ echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1698: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1703 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1726: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<EOF
+#define CRAY_STACKSEG_END $ac_func
+EOF
+
+ break
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+done
+fi
+
+echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6
+echo "configure:1753: checking stack direction for C alloca" >&5
+if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_stack_direction=0
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1761 "configure"
+#include "confdefs.h"
+find_stack_direction ()
+{
+ static char *addr = 0;
+ auto char dummy;
+ if (addr == 0)
+ {
+ addr = &dummy;
+ return find_stack_direction ();
+ }
+ else
+ return (&dummy > addr) ? 1 : -1;
+}
+main ()
+{
+ exit (find_stack_direction() < 0);
+}
+EOF
+if { (eval echo configure:1780: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_c_stack_direction=1
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_c_stack_direction=-1
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_c_stack_direction" 1>&6
+cat >> confdefs.h <<EOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+EOF
+
+fi
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:1802: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+# Test for builtin mem* functions.
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext <<EOF
+#line 1839 "configure"
+#include "confdefs.h"
+
+#include <string.h>
+void foo(char *s, int c, size_t n)
+{
+ __builtin_memset(s, c, n);
+}
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1852: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ use_builtin_memset=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ use_builtin_memset=no
+fi
+rm -f conftest*
+if test $use_builtin_memset = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_BUILTIN_MEMSET 1
+EOF
+
+fi
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+
+# Check whether --enable-debugging or --disable-debugging was given.
+if test "${enable_debugging+set}" = set; then
+ enableval="$enable_debugging"
+ case "${enableval}" in
+yes) cat >> confdefs.h <<\EOF
+#define DEBUGGING 1
+EOF
+ ;;
+no) ;;
+esac
+
+fi
+
+
+# Check whether --enable-server or --disable-server was given.
+if test "${enable_server+set}" = set; then
+ enableval="$enable_server"
+ case "${enableval}" in
+yes) ;;
+no) LIBSERVER=;;
+esac
+
+fi
+
+
+MALLOC_OFILES=malloc.o
+# Check whether --enable-malloc-debugging or --disable-malloc-debugging was given.
+if test "${enable_malloc_debugging+set}" = set; then
+ enableval="$enable_malloc_debugging"
+ case "${enableval}" in
+yes) cat >> confdefs.h <<\EOF
+#define MALLOC_DEBUG 1
+EOF
+
+ MALLOC_OFILES=dlmalloc.o
+ ;;
+no) ;;
+esac
+
+fi
+
+
+# Check whether --enable-vfork or --disable-vfork was given.
+if test "${enable_vfork+set}" = set; then
+ enableval="$enable_vfork"
+ vfork="${enableval}"
+]
+fi
+
+
+case "$vfork" in
+no) ;;
+yes) cat >> confdefs.h <<EOF
+#define NEWVFORK 1
+EOF
+ ;;
+esac
+
+
+
+case "$target_cpu" in
+ i?86) DLL_ENTRY="_dll_entry@12"
+ DEF_DLL_ENTRY="dll_entry@12"
+ ALLOCA="_alloca"
+ CONFIG_DIR="i386" ;;
+ *) { echo "configure: error: Invalid target processor \"$target_cpu\"" 1>&2; exit 1; } ;;
+esac
+
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@all_host@%$all_host%g
+s%@install_host@%$install_host%g
+s%@AR@%$AR%g
+s%@AS@%$AS%g
+s%@RANLIB@%$RANLIB%g
+s%@LD@%$LD%g
+s%@NM@%$NM%g
+s%@DLLTOOL@%$DLLTOOL%g
+s%@WINDRES@%$WINDRES%g
+s%@CPP@%$CPP%g
+s%@ALLOCA@%$ALLOCA%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@MALLOC_OFILES@%$MALLOC_OFILES%g
+s%@LIBSERVER@%$LIBSERVER%g
+s%@DLL_ENTRY@%$DLL_ENTRY%g
+s%@DEF_DLL_ENTRY@%$DEF_DLL_ENTRY%g
+s%@CONFIG_DIR@%$CONFIG_DIR%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/winsup/cygwin/configure.in b/winsup/cygwin/configure.in
new file mode 100644
index 00000000000..db7dc454c72
--- /dev/null
+++ b/winsup/cygwin/configure.in
@@ -0,0 +1,186 @@
+dnl Autoconf configure script for Cygwin.
+dnl Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003 Red Hat, Inc.
+dnl
+dnl This file is part of Cygwin.
+dnl
+dnl This software is a copyrighted work licensed under the terms of the
+dnl Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+dnl details.
+dnl
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.12)dnl
+AC_INIT(init.cc)
+AC_CONFIG_HEADER(config.h)
+
+INSTALL="/bin/sh "`cd $srcdir/../..; echo $(pwd)/install-sh -c`
+
+AC_PROG_INSTALL
+
+dnl FIXME: We temporarily define our own version of AC_PROG_CC. This is
+dnl copied from autoconf 2.12, but does not call AC_PROG_CC_WORKS. We
+dnl are probably using a cross compiler, which will not be able to fully
+dnl link an executable. This should really be fixed in autoconf
+dnl itself.
+
+AC_DEFUN(LIB_AC_PROG_CC,
+[AC_BEFORE([$0], [AC_PROG_CPP])dnl
+AC_CHECK_TOOL(CC, gcc, gcc)
+if test -z "$CC"; then
+ AC_CHECK_PROG(CC, cc, cc, , , /usr/ucb/cc)
+ test -z "$CC" && AC_MSG_ERROR([no acceptable cc found in \$PATH])
+fi
+
+AC_PROG_CC_GNU
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+dnl Check whether -g works, even if CFLAGS is set, in case the package
+dnl plays around with CFLAGS (such as to build both debugging and
+dnl normal versions of a library), tasteless as that idea is.
+ ac_test_CFLAGS="${CFLAGS+set}"
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS=
+ AC_PROG_CC_G
+ if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+ elif test $ac_cv_prog_cc_g = yes; then
+ CFLAGS="-gstabs+ -O2"
+ else
+ CFLAGS="-O2"
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+])
+
+AC_DEFUN(LIB_AC_PROG_CXX,
+[AC_BEFORE([$0], [AC_PROG_CPP])dnl
+AC_CHECK_TOOL(CXX, g++, g++)
+if test -z "$CXX"; then
+ AC_CHECK_PROG(CXX, c++, c++, , , )
+ test -z "$CC" && AC_MSG_ERROR([no acceptable cc found in \$PATH])
+fi
+
+CXXFLAGS='$(CFLAGS)'
+])
+
+AC_CANONICAL_SYSTEM
+
+LIB_AC_PROG_CC
+LIB_AC_PROG_CXX
+
+case "$with_cross_host" in
+ ""|*cygwin*)
+ all_host="all_host"
+ install_host="install_host"
+ LIBSERVER='$(bupdir)/cygserver/libcygserver.a'
+ ;;
+ *)
+ all_host=
+ install_host=
+ LIBSERVER=
+ ;;
+esac
+
+test -n "$LIBSERVER" && AC_DEFINE(USE_SERVER)
+
+AC_SUBST(all_host)
+AC_SUBST(install_host)
+
+AC_CHECK_TOOL(AR, ar, ar)
+AC_CHECK_TOOL(AS, as, as)
+AC_CHECK_TOOL(RANLIB, ranlib, ranlib)
+AC_CHECK_TOOL(LD, ld, ld)
+AC_CHECK_TOOL(NM, nm, nm)
+AC_CHECK_TOOL(DLLTOOL, dlltool, dlltool)
+AC_CHECK_TOOL(WINDRES, windres, windres)
+
+AC_ALLOCA
+AC_PROG_MAKE_SET
+
+dnl check whether gcc supports __builtin_memset.
+# Test for builtin mem* functions.
+AC_LANG_SAVE
+AC_LANG_CPLUSPLUS
+AC_TRY_COMPILE([
+#include <string.h>
+void foo(char *s, int c, size_t n)
+{
+ __builtin_memset(s, c, n);
+}
+], [ ],
+use_builtin_memset=yes, use_builtin_memset=no)
+if test $use_builtin_memset = "yes"; then
+ AC_DEFINE(HAVE_BUILTIN_MEMSET)
+fi
+AC_LANG_RESTORE
+
+AC_ARG_ENABLE(debugging,
+[ --enable-debugging Build a cygwin DLL which has more consistency checking for debugging],
+[case "${enableval}" in
+yes) AC_DEFINE(DEBUGGING) ;;
+no) ;;
+esac
+])
+
+AC_ARG_ENABLE(server,
+[ --enable-server Build a cygwin DLL which can communicate with cygserver],
+[case "${enableval}" in
+yes) ;;
+no) LIBSERVER=;;
+esac
+])
+
+MALLOC_OFILES=malloc.o
+AC_ARG_ENABLE(malloc-debugging,
+[ --enable-malloc-debugging Build a cygwin DLL with heap sanity checking (this is very slow, use only if you have heap corruption problems)],
+[case "${enableval}" in
+yes) AC_DEFINE(MALLOC_DEBUG)
+ MALLOC_OFILES=dlmalloc.o
+ ;;
+no) ;;
+esac
+])
+
+AC_ARG_ENABLE(vfork,
+[ --enable-vfork Build a cygwin DLL which uses experimental vfork code],
+vfork="${enableval}"
+])
+
+case "$vfork" in
+no) ;;
+yes) AC_DEFINE_UNQUOTED(NEWVFORK) ;;
+esac
+
+dnl The only time we might want to transform the install names
+dnl is for unix x cygwin. Otherwise we don't. For now we don't
+dnl transform names.
+
+dnl if test "x$cross_compiling" = "xno" -a ; then
+dnl if test "x$program_transform_name" = "xs,x,x,"; then
+dnl program_transform_name=""
+dnl fi
+dnl if test "x$program_transform_name" = "x"; then
+dnl program_transform_name="s,^,$target_alias-,"
+dnl else
+dnl program_transform_name="$program_transform_name -e s,^,$target_alias-,"
+dnl fi
+dnl fi
+
+case "$target_cpu" in
+ i?86) DLL_ENTRY="_dll_entry@12"
+ DEF_DLL_ENTRY="dll_entry@12"
+ ALLOCA="_alloca"
+ CONFIG_DIR="i386" ;;
+ *) AC_MSG_ERROR(Invalid target processor \"$target_cpu\") ;;
+esac
+
+AC_SUBST(MALLOC_OFILES)
+AC_SUBST(LIBSERVER)
+AC_SUBST(DLL_ENTRY)
+AC_SUBST(DEF_DLL_ENTRY)
+AC_SUBST(ALLOCA)
+AC_SUBST(CONFIG_DIR)
+AC_OUTPUT(Makefile)
diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc
new file mode 100644
index 00000000000..e057f2fee8c
--- /dev/null
+++ b/winsup/cygwin/cygheap.cc
@@ -0,0 +1,472 @@
+/* cygheap.cc: Cygwin heap manager.
+
+ Copyright 2000, 2001, 2002, 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 <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "cygheap.h"
+#include "child_info.h"
+#include "heap.h"
+#include "sync.h"
+#include "shared_info.h"
+#include "sigproc.h"
+
+init_cygheap NO_COPY *cygheap;
+void NO_COPY *cygheap_max;
+
+static NO_COPY muto *cygheap_protect;
+
+struct cygheap_entry
+ {
+ int type;
+ struct cygheap_entry *next;
+ char data[0];
+ };
+
+#define NBUCKETS (sizeof (cygheap->buckets) / sizeof (cygheap->buckets[0]))
+#define N0 ((_cmalloc_entry *) NULL)
+#define to_cmalloc(s) ((_cmalloc_entry *) (((char *) (s)) - (unsigned) (N0->data)))
+
+#define CFMAP_OPTIONS (SEC_RESERVE | PAGE_READWRITE)
+#define MVMAP_OPTIONS (FILE_MAP_WRITE)
+
+extern "C" {
+static void __stdcall _cfree (void *ptr) __attribute__((regparm(1)));
+}
+
+static void
+init_cheap ()
+{
+ cygheap = (init_cygheap *) VirtualAlloc ((void *) &_cygheap_start, CYGHEAPSIZE, MEM_RESERVE, PAGE_NOACCESS);
+ if (!cygheap)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ if (!VirtualQuery ((LPCVOID) &_cygheap_start, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+ system_printf ("Couldn't reserve space for cygwin's heap, %E");
+ api_fatal ("AllocationBase %p, BaseAddress %p, RegionSize %p, State %p\n",
+ m.AllocationBase, m.BaseAddress, m.RegionSize, m.State);
+ }
+ cygheap_max = cygheap;
+}
+
+static void dup_now (void *, child_info *, unsigned) __attribute__ ((regparm(3)));
+static void
+dup_now (void *newcygheap, child_info *ci, unsigned n)
+{
+ if (!VirtualAlloc (newcygheap, n, MEM_COMMIT, PAGE_READWRITE))
+ api_fatal ("couldn't allocate new cygwin heap %p, %d for child, %E",
+ newcygheap, n);
+ memcpy (newcygheap, cygheap, n);
+}
+
+void *__stdcall
+cygheap_setup_for_child (child_info *ci, bool dup_later)
+{
+ void *newcygheap;
+ cygheap_protect->acquire ();
+ unsigned n = (char *) cygheap_max - (char *) cygheap;
+ unsigned size = CYGHEAPSIZE;
+ if (size < n)
+ size = n + (128 * 1024);
+ ci->cygheap_h = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_none,
+ CFMAP_OPTIONS, 0, size, NULL);
+ if (!ci->cygheap_h)
+ api_fatal ("Couldn't create heap for child, size %d, %E", CYGHEAPSIZE);
+ newcygheap = MapViewOfFileEx (ci->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, NULL);
+ ProtectHandle1INH (ci->cygheap_h, passed_cygheap_h);
+ if (!dup_later)
+ dup_now (newcygheap, ci, n);
+ cygheap_protect->release ();
+ ci->cygheap = cygheap;
+ ci->cygheap_max = cygheap_max;
+ return newcygheap;
+}
+
+void __stdcall
+cygheap_setup_for_child_cleanup (void *newcygheap, child_info *ci,
+ bool dup_it_now)
+{
+ if (dup_it_now)
+ {
+ /* NOTE: There is an assumption here that cygheap_max has not changed
+ between the time that cygheap_setup_for_child was called and now.
+ Make sure that this is a correct assumption. */
+ cygheap_protect->acquire ();
+ dup_now (newcygheap, ci, (char *) cygheap_max - (char *) cygheap);
+ cygheap_protect->release ();
+ }
+ UnmapViewOfFile (newcygheap);
+ ForceCloseHandle1 (ci->cygheap_h, passed_cygheap_h);
+}
+
+/* Called by fork or spawn to reallocate cygwin heap */
+void __stdcall
+cygheap_fixup_in_child (bool execed)
+{
+ cygheap = child_proc_info->cygheap;
+ cygheap_max = child_proc_info->cygheap_max;
+ void *addr = !wincap.map_view_of_file_ex_sucks () ? cygheap : NULL;
+ void *newaddr;
+
+ newaddr = MapViewOfFileEx (child_proc_info->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, addr);
+ if (newaddr != cygheap)
+ {
+ if (!newaddr)
+ newaddr = MapViewOfFileEx (child_proc_info->cygheap_h, MVMAP_OPTIONS, 0, 0, 0, NULL);
+ DWORD n = (DWORD) cygheap_max - (DWORD) cygheap;
+ /* Reserve cygwin heap in same spot as parent */
+ if (!VirtualAlloc (cygheap, CYGHEAPSIZE, MEM_RESERVE, PAGE_NOACCESS))
+ {
+ MEMORY_BASIC_INFORMATION m;
+ memset (&m, 0, sizeof m);
+ if (!VirtualQuery ((LPCVOID) cygheap, &m, sizeof m))
+ system_printf ("couldn't get memory info, %E");
+
+ system_printf ("Couldn't reserve space for cygwin's heap (%p <%p>) in child, %E", cygheap, newaddr);
+ api_fatal ("m.AllocationBase %p, m.BaseAddress %p, m.RegionSize %p, m.State %p\n",
+ m.AllocationBase, m.BaseAddress, m.RegionSize, m.State);
+ }
+
+ /* Allocate same amount of memory as parent */
+ if (!VirtualAlloc (cygheap, n, MEM_COMMIT, PAGE_READWRITE))
+ api_fatal ("Couldn't allocate space for child's heap %p, size %d, %E",
+ cygheap, n);
+ memcpy (cygheap, newaddr, n);
+ UnmapViewOfFile (newaddr);
+ }
+
+ ForceCloseHandle1 (child_proc_info->cygheap_h, passed_cygheap_h);
+
+ cygheap_init ();
+ debug_fixup_after_fork_exec ();
+
+ if (execed)
+ {
+ cygheap->user_heap.base = NULL; /* We can allocate the heap anywhere */
+ /* Walk the allocated memory chain looking for orphaned memory from
+ previous execs */
+ for (_cmalloc_entry *rvc = cygheap->chain; rvc; rvc = rvc->prev)
+ {
+ cygheap_entry *ce = (cygheap_entry *) rvc->data;
+ if (!rvc->ptr || rvc->b >= NBUCKETS || ce->type <= HEAP_1_START)
+ continue;
+ else if (ce->type < HEAP_1_MAX)
+ ce->type += HEAP_1_MAX; /* Mark for freeing after next exec */
+ else
+ _cfree (ce); /* Marked by parent for freeing in child */
+ }
+ }
+}
+
+void
+init_cygheap::close_ctty ()
+{
+ debug_printf ("closing cygheap->ctty %p", cygheap->ctty);
+ int usecount = cygheap->ctty->usecount;
+ cygheap->ctty->close ();
+#ifndef NEWVFORK
+ cygheap->ctty = NULL;
+#else // FIXME: This code ain't right
+ if (cygheap->ctty_on_hold == cygheap->ctty)
+ cygheap->ctty_on_hold = NULL;
+ if (usecount == 1)
+ {
+ cygheap->ctty = NULL;
+ debug_printf ("setting cygheap->ctty to NULL");
+ }
+#endif
+}
+
+#define pagetrunc(x) ((void *) (((DWORD) (x)) & ~(4096 - 1)))
+
+static void *__stdcall
+_csbrk (int sbs)
+{
+ void *prebrk = cygheap_max;
+ void *prebrka = pagetrunc (prebrk);
+ cygheap_max = (char *) cygheap_max + sbs;
+ if (!sbs || (prebrk != prebrka && prebrka == pagetrunc (cygheap_max)))
+ /* nothing to do */;
+ else if (!VirtualAlloc (prebrk, (DWORD) sbs, MEM_COMMIT, PAGE_READWRITE))
+ {
+ malloc_printf ("couldn't commit memory for cygwin heap, %E");
+ __seterrno ();
+ cygheap_max = (char *) cygheap_max - sbs;
+ return NULL;
+ }
+
+ return prebrk;
+}
+
+extern "C" void __stdcall
+cygheap_init ()
+{
+ new_muto (cygheap_protect);
+ if (!cygheap)
+ {
+ init_cheap ();
+ (void) _csbrk (sizeof (*cygheap));
+ }
+ if (!cygheap->fdtab)
+ cygheap->fdtab.init ();
+ if (!cygheap->sigs)
+ sigalloc ();
+
+ if (!cygheap->shared_prefix)
+ cygheap->shared_prefix = cstrdup (
+ wincap.has_terminal_services ()
+ && (set_process_privilege (SE_CREATE_GLOBAL_NAME, true) >= 0
+ || GetLastError () == ERROR_NO_SUCH_PRIVILEGE)
+ ? "Global\\" : "");
+}
+
+/* Copyright (C) 1997, 2000 DJ Delorie */
+
+static void *_cmalloc (unsigned size) __attribute ((regparm(1)));
+static void *__stdcall _crealloc (void *ptr, unsigned size) __attribute ((regparm(2)));
+
+static void *__stdcall
+_cmalloc (unsigned size)
+{
+ _cmalloc_entry *rvc;
+ unsigned b, sz;
+
+ /* Calculate "bit bucket" and size as a power of two. */
+ for (b = 3, sz = 8; sz && sz < size; b++, sz <<= 1)
+ continue;
+
+ cygheap_protect->acquire ();
+ if (cygheap->buckets[b])
+ {
+ rvc = (_cmalloc_entry *) cygheap->buckets[b];
+ cygheap->buckets[b] = rvc->ptr;
+ rvc->b = b;
+ }
+ else
+ {
+ rvc = (_cmalloc_entry *) _csbrk (sz + sizeof (_cmalloc_entry));
+ if (!rvc)
+ {
+ cygheap_protect->release ();
+ return NULL;
+ }
+
+ rvc->b = b;
+ rvc->prev = cygheap->chain;
+ cygheap->chain = rvc;
+ }
+ cygheap_protect->release ();
+ return rvc->data;
+}
+
+static void __stdcall
+_cfree (void *ptr)
+{
+ cygheap_protect->acquire ();
+ _cmalloc_entry *rvc = to_cmalloc (ptr);
+ DWORD b = rvc->b;
+ rvc->ptr = cygheap->buckets[b];
+ cygheap->buckets[b] = (char *) rvc;
+ cygheap_protect->release ();
+}
+
+static void *__stdcall
+_crealloc (void *ptr, unsigned size)
+{
+ void *newptr;
+ if (ptr == NULL)
+ newptr = _cmalloc (size);
+ else
+ {
+ unsigned oldsize = 1 << to_cmalloc (ptr)->b;
+ if (size <= oldsize)
+ return ptr;
+ newptr = _cmalloc (size);
+ memcpy (newptr, ptr, oldsize);
+ _cfree (ptr);
+ }
+ return newptr;
+}
+
+/* End Copyright (C) 1997 DJ Delorie */
+
+#define sizeof_cygheap(n) ((n) + sizeof (cygheap_entry))
+
+#define N ((cygheap_entry *) NULL)
+#define tocygheap(s) ((cygheap_entry *) (((char *) (s)) - (int) (N->data)))
+
+inline static void *
+creturn (cygheap_types x, cygheap_entry * c, unsigned len)
+{
+ if (!c)
+ {
+ set_errno (ENOMEM);
+ return NULL;
+ }
+ c->type = x;
+ char *cend = ((char *) c + sizeof (*c) + len);
+ if (cygheap_max < cend)
+ cygheap_max = cend;
+ MALLOC_CHECK;
+ return (void *) c->data;
+}
+
+extern "C" void *__stdcall
+cmalloc (cygheap_types x, DWORD n)
+{
+ cygheap_entry *c;
+ MALLOC_CHECK;
+ c = (cygheap_entry *) _cmalloc (sizeof_cygheap (n));
+ if (!c)
+ system_printf ("cmalloc returned NULL");
+ return creturn (x, c, n);
+}
+
+extern "C" void *__stdcall
+crealloc (void *s, DWORD n)
+{
+ MALLOC_CHECK;
+ if (s == NULL)
+ return cmalloc (HEAP_STR, n); // kludge
+
+ assert (!inheap (s));
+ cygheap_entry *c = tocygheap (s);
+ cygheap_types t = (cygheap_types) c->type;
+ c = (cygheap_entry *) _crealloc (c, sizeof_cygheap (n));
+ if (!c)
+ system_printf ("crealloc returned NULL");
+ return creturn (t, c, n);
+}
+
+extern "C" void __stdcall
+cfree (void *s)
+{
+ assert (!inheap (s));
+ (void) _cfree (tocygheap (s));
+ MALLOC_CHECK;
+}
+
+extern "C" void __stdcall
+cfree_and_set (char *&s, char *what)
+{
+ if (s && s != almost_null)
+ cfree (s);
+ s = what;
+}
+
+extern "C" void *__stdcall
+ccalloc (cygheap_types x, DWORD n, DWORD size)
+{
+ cygheap_entry *c;
+ MALLOC_CHECK;
+ n *= size;
+ c = (cygheap_entry *) _cmalloc (sizeof_cygheap (n));
+ if (c)
+ memset (c->data, 0, n);
+#ifdef DEBUGGING
+ if (!c)
+ system_printf ("ccalloc returned NULL");
+#endif
+ return creturn (x, c, n);
+}
+
+extern "C" char *__stdcall
+cstrdup (const char *s)
+{
+ MALLOC_CHECK;
+ char *p = (char *) cmalloc (HEAP_STR, strlen (s) + 1);
+ if (!p)
+ return NULL;
+ strcpy (p, s);
+ MALLOC_CHECK;
+ return p;
+}
+
+extern "C" char *__stdcall
+cstrdup1 (const char *s)
+{
+ MALLOC_CHECK;
+ char *p = (char *) cmalloc (HEAP_1_STR, strlen (s) + 1);
+ if (!p)
+ return NULL;
+ strcpy (p, s);
+ MALLOC_CHECK;
+ return p;
+}
+
+void
+cygheap_root::set (const char *posix, const char *native)
+{
+ if (*posix == '/' && posix[1] == '\0')
+ {
+ if (m)
+ {
+ cfree (m);
+ m = NULL;
+ }
+ return;
+ }
+ if (!m)
+ m = (struct cygheap_root_mount_info *) ccalloc (HEAP_MOUNT, 1, sizeof (*m));
+ strcpy (m->posix_path, posix);
+ m->posix_pathlen = strlen (posix);
+ if (m->posix_pathlen >= 1 && m->posix_path[m->posix_pathlen - 1] == '/')
+ m->posix_path[--m->posix_pathlen] = '\0';
+
+ strcpy (m->native_path, native);
+ m->native_pathlen = strlen (native);
+ if (m->native_pathlen >= 1 && m->native_path[m->native_pathlen - 1] == '\\')
+ m->native_path[--m->native_pathlen] = '\0';
+}
+
+cygheap_user::~cygheap_user ()
+{
+#if 0
+ if (pname)
+ cfree (pname);
+ if (plogsrv)
+ cfree (plogsrv - 2);
+ if (pdomain)
+ cfree (pdomain);
+ if (psid)
+ cfree (psid);
+#endif
+}
+
+void
+cygheap_user::set_name (const char *new_name)
+{
+ bool allocated = !!pname;
+
+ if (allocated)
+ {
+ if (strcasematch (new_name, pname))
+ return;
+ cfree (pname);
+ }
+
+ pname = cstrdup (new_name ? new_name : "");
+ if (!allocated)
+ return; /* Initializing. Don't bother with other stuff. */
+
+ cfree_and_set (homedrive);
+ cfree_and_set (homepath);
+ cfree_and_set (plogsrv);
+ cfree_and_set (pdomain);
+ cfree_and_set (pwinname);
+}
+
diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h
new file mode 100644
index 00000000000..f1c80e1afa6
--- /dev/null
+++ b/winsup/cygwin/cygheap.h
@@ -0,0 +1,377 @@
+/* cygheap.h: Cygwin heap manager.
+
+ Copyright 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. */
+
+#undef cfree
+
+enum cygheap_types
+{
+ HEAP_FHANDLER,
+ HEAP_STR,
+ HEAP_ARGV,
+ HEAP_BUF,
+ HEAP_MOUNT,
+ HEAP_SIGS,
+ HEAP_ARCHETYPES,
+ HEAP_TLS,
+ HEAP_1_START,
+ HEAP_1_STR,
+ HEAP_1_ARGV,
+ HEAP_1_BUF,
+ HEAP_1_EXEC,
+ HEAP_1_MAX = 100,
+ HEAP_MMAP = 200
+};
+
+#define incygheap(s) (cygheap && ((char *) (s) >= (char *) cygheap) && ((char *) (s) <= ((char *) cygheap_max)))
+
+struct _cmalloc_entry
+{
+ union
+ {
+ DWORD b;
+ char *ptr;
+ };
+ struct _cmalloc_entry *prev;
+ char data[0];
+};
+
+struct cygheap_root_mount_info
+{
+ char posix_path[CYG_MAX_PATH];
+ unsigned posix_pathlen;
+ char native_path[CYG_MAX_PATH];
+ unsigned native_pathlen;
+};
+
+/* CGF: FIXME This doesn't belong here */
+
+class cygheap_root
+{
+ /* Root directory information.
+ This is used after a chroot is called. */
+ struct cygheap_root_mount_info *m;
+
+public:
+ bool posix_ok (const char *path)
+ {
+ if (!m)
+ return 1;
+ return path_prefix_p (m->posix_path, path, m->posix_pathlen);
+ }
+ bool ischroot_native (const char *path)
+ {
+ if (!m)
+ return 1;
+ return strncasematch (m->native_path, path, m->native_pathlen)
+ && (path[m->native_pathlen] == '\\' || !path[m->native_pathlen]);
+ }
+ const char *unchroot (const char *path)
+ {
+ if (!m)
+ return path;
+ const char *p = path + m->posix_pathlen;
+ if (!*p)
+ p = "/";
+ return p;
+ }
+ bool exists () {return !!m;}
+ void set (const char *posix, const char *native);
+ size_t posix_length () const { return m->posix_pathlen; }
+ const char *posix_path () const { return m->posix_path; }
+ size_t native_length () const { return m->native_pathlen; }
+ const char *native_path () const { return m->native_path; }
+};
+
+enum homebodies
+{
+ CH_HOMEDRIVE,
+ CH_HOMEPATH,
+ CH_HOME
+};
+
+class cygheap_user
+{
+ /* Extendend user information.
+ The information is derived from the internal_getlogin call
+ when on a NT system. */
+ char *pname; /* user's name */
+ char *plogsrv; /* Logon server, may be FQDN */
+ char *pdomain; /* Logon domain of the user */
+ char *homedrive; /* User's home drive */
+ char *homepath; /* User's home path */
+ char *pwinname; /* User's name as far as Windows knows it */
+ char *puserprof; /* User profile */
+ cygsid effec_cygsid; /* buffer for user's SID */
+ cygsid saved_cygsid; /* Remains intact even after impersonation */
+public:
+ __uid32_t saved_uid; /* Remains intact even after impersonation */
+ __gid32_t saved_gid; /* Ditto */
+ __uid32_t real_uid; /* Remains intact on seteuid, replaced by setuid */
+ __gid32_t real_gid; /* Ditto */
+ user_groups groups; /* Primary and supp SIDs */
+
+ /* token is needed if set(e)uid should be called. It can be set by a call
+ to `set_impersonation_token()'. */
+ HANDLE external_token;
+ HANDLE internal_token;
+ HANDLE current_token;
+
+ /* CGF 2002-06-27. I removed the initializaton from this constructor
+ since this class is always allocated statically. That means that everything
+ is zero anyway so there is no need to initialize it to zero. Since the
+ token initialization is always handled during process startup as well,
+ I've removed the constructor entirely. Please reinstate this if this
+ situation ever changes.
+ cygheap_user () : pname (NULL), plogsrv (NULL), pdomain (NULL),
+ homedrive (NULL), homepath (NULL),
+ token (INVALID_HANDLE_VALUE) {}
+ */
+
+ ~cygheap_user ();
+
+ void init ();
+ void set_name (const char *new_name);
+ const char *name () const { return pname; }
+
+ const char *env_logsrv (const char *, size_t);
+ const char *env_homepath (const char *, size_t);
+ const char *env_homedrive (const char *, size_t);
+ const char *env_userprofile (const char *, size_t);
+ const char *env_domain (const char *, size_t);
+ const char *env_name (const char *, size_t);
+
+ const char *logsrv ()
+ {
+ const char *p = env_logsrv ("LOGONSERVER=", sizeof ("LOGONSERVER=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ const char *winname ()
+ {
+ const char *p = env_name ("USERNAME=", sizeof ("USERNAME=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ const char *domain ()
+ {
+ const char *p = env_domain ("USERDOMAIN=", sizeof ("USERDOMAIN=") - 1);
+ return (p == almost_null) ? NULL : p;
+ }
+ BOOL set_sid (PSID new_sid) {return (BOOL) (effec_cygsid = new_sid);}
+ BOOL set_saved_sid () { return (BOOL) (saved_cygsid = effec_cygsid); }
+ PSID sid () { return effec_cygsid; }
+ PSID saved_sid () { return saved_cygsid; }
+ const char *ontherange (homebodies what, struct passwd * = NULL);
+ bool issetuid () const { return current_token != INVALID_HANDLE_VALUE; }
+ HANDLE token () { return current_token; }
+ void deimpersonate ()
+ {
+ if (issetuid ())
+ RevertToSelf ();
+ }
+ void reimpersonate ()
+ {
+ if (issetuid ()
+ && !ImpersonateLoggedOnUser (token ()))
+ system_printf ("ImpersonateLoggedOnUser: %E");
+ }
+ bool has_impersonation_tokens ()
+ { return external_token != INVALID_HANDLE_VALUE
+ || internal_token != INVALID_HANDLE_VALUE
+ || current_token != INVALID_HANDLE_VALUE; }
+ void close_impersonation_tokens ()
+ {
+ if (current_token != INVALID_HANDLE_VALUE)
+ {
+ if( current_token != external_token && current_token != internal_token)
+ CloseHandle (current_token);
+ current_token = INVALID_HANDLE_VALUE;
+ }
+ if (external_token != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (external_token);
+ external_token = INVALID_HANDLE_VALUE;
+ }
+ if (internal_token != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (internal_token);
+ internal_token = INVALID_HANDLE_VALUE;
+ }
+ }
+ const char *cygheap_user::test_uid (char *&, const char *, size_t)
+ __attribute__ ((regparm (3)));
+};
+
+/* cwd cache stuff. */
+
+class muto;
+
+struct cwdstuff
+{
+ char *posix;
+ char *win32;
+ DWORD hash;
+ muto *cwd_lock;
+ char *get (char *buf, int need_posix = 1, int with_chroot = 0, unsigned ulen = CYG_MAX_PATH);
+ DWORD get_hash ();
+ void init ();
+ void fixup_after_exec (char *win32, char *posix, DWORD hash);
+ bool get_initial ();
+ void set (const char *win32_cwd, const char *posix_cwd = NULL);
+};
+
+#ifdef DEBUGGING
+struct cygheap_debug
+{
+ handle_list starth;
+ handle_list *endh;
+ handle_list freeh[500];
+};
+#endif
+
+struct user_heap_info
+{
+ void *base;
+ void *ptr;
+ void *top;
+ void *max;
+ unsigned chunk;
+};
+
+struct init_cygheap
+{
+ _cmalloc_entry *chain;
+ char *buckets[32];
+ cygheap_root root;
+ cygheap_user user;
+ user_heap_info user_heap;
+ mode_t umask;
+ HANDLE shared_h;
+ HANDLE console_h;
+ char *cygwin_regname;
+ cwdstuff cwd;
+ dtable fdtab;
+ const char *shared_prefix;
+#ifdef DEBUGGING
+ cygheap_debug debug;
+#endif
+ struct sigaction *sigs;
+
+ fhandler_tty_slave *ctty; /* Current tty */
+#ifdef NEWVFORK
+ fhandler_tty_slave *ctty_on_hold;
+#endif
+ struct _threadinfo **threadlist;
+ size_t sthreads;
+ int open_fhs;
+ void close_ctty ();
+};
+
+#define CYGHEAPSIZE (sizeof (init_cygheap) + (20000 * sizeof (fhandler_union)) + (5 * 65536))
+
+extern init_cygheap *cygheap;
+extern void *cygheap_max;
+
+class cygheap_fdmanip
+{
+ protected:
+ int fd;
+ fhandler_base **fh;
+ bool locked;
+ public:
+ cygheap_fdmanip (): fh (NULL) {}
+ virtual ~cygheap_fdmanip ()
+ {
+ if (locked)
+ cygheap->fdtab.unlock ();
+ }
+ void release ()
+ {
+ cygheap->fdtab.release (fd);
+ }
+ operator int &() {return fd;}
+ operator fhandler_base* &() {return *fh;}
+ operator fhandler_socket* () const {return reinterpret_cast<fhandler_socket *> (*fh);}
+ void operator = (fhandler_base *fh) {*this->fh = fh;}
+ fhandler_base *operator -> () const {return *fh;}
+ bool isopen () const
+ {
+ if (*fh)
+ return true;
+ set_errno (EBADF);
+ return false;
+ }
+};
+
+class cygheap_fdnew : public cygheap_fdmanip
+{
+ public:
+ cygheap_fdnew (int seed_fd = -1, bool lockit = true)
+ {
+ if (lockit)
+ cygheap->fdtab.lock ();
+ if (seed_fd < 0)
+ fd = cygheap->fdtab.find_unused_handle ();
+ else
+ fd = cygheap->fdtab.find_unused_handle (seed_fd + 1);
+ if (fd >= 0)
+ {
+ locked = lockit;
+ fh = cygheap->fdtab + fd;
+ }
+ else
+ {
+ set_errno (EMFILE);
+ if (lockit)
+ cygheap->fdtab.unlock ();
+ locked = false;
+ }
+ }
+ void operator = (fhandler_base *fh) {*this->fh = fh;}
+};
+
+class cygheap_fdget : public cygheap_fdmanip
+{
+ public:
+ cygheap_fdget (int fd, bool lockit = false, bool do_set_errno = true)
+ {
+ if (lockit)
+ cygheap->fdtab.lock ();
+ if (fd >= 0 && fd < (int) cygheap->fdtab.size
+ && *(fh = cygheap->fdtab + fd) != NULL)
+ {
+ this->fd = fd;
+ locked = lockit;
+ }
+ else
+ {
+ this->fd = -1;
+ if (do_set_errno)
+ set_errno (EBADF);
+ if (lockit)
+ cygheap->fdtab.unlock ();
+ locked = false;
+ }
+ }
+};
+
+class child_info;
+void *__stdcall cygheap_setup_for_child (child_info *ci, bool dup_later) __attribute__ ((regparm(2)));
+void __stdcall cygheap_setup_for_child_cleanup (void *, child_info *, bool) __attribute__ ((regparm(3)));
+void __stdcall cygheap_fixup_in_child (bool);
+extern "C" {
+void __stdcall cfree (void *) __attribute__ ((regparm(1)));
+void *__stdcall cmalloc (cygheap_types, DWORD) __attribute__ ((regparm(2)));
+void *__stdcall crealloc (void *, DWORD) __attribute__ ((regparm(2)));
+void *__stdcall ccalloc (cygheap_types, DWORD, DWORD) __attribute__ ((regparm(3)));
+char *__stdcall cstrdup (const char *) __attribute__ ((regparm(1)));
+char *__stdcall cstrdup1 (const char *) __attribute__ ((regparm(1)));
+void __stdcall cfree_and_set (char *&, char * = NULL) __attribute__ ((regparm(2)));
+void __stdcall cygheap_init ();
+extern DWORD _cygheap_start;
+}
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index 746b5f5bf18..763677e3f5f 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -45,10 +45,14 @@ HANDLE NO_COPY hMainProc = (HANDLE) -1;
HANDLE NO_COPY hMainThread;
per_thread_waitq NO_COPY waitq_storage;
+#ifdef NEWVFORK
per_thread_vfork NO_COPY vfork_storage;
+#endif
per_thread NO_COPY *threadstuff[] = {&waitq_storage,
+#ifdef NEWVFORK
&vfork_storage,
+#endif
NULL};
bool display_title;
@@ -59,7 +63,9 @@ codepage_type current_codepage = ansi_cp;
int __argc_safe;
int _declspec(dllexport) __argc;
char _declspec(dllexport) **__argv;
+#ifdef NEWVFORK
vfork_save NO_COPY *main_vfork;
+#endif
static int NO_COPY envc;
char NO_COPY **envp;
@@ -759,9 +765,10 @@ dll_crt0_1 (char *)
Need to do this before any helper threads start. */
debug_init ();
+#ifdef NEWVFORK
cygheap->fdtab.vfork_child_fixup ();
-
main_vfork = vfork_storage.create ();
+#endif
cygbench ("pre-forkee");
if (user_data->forkee)
@@ -988,12 +995,14 @@ do_exit (int status)
{
syscall_printf ("do_exit (%d), exit_state %d", status, exit_state);
+#ifdef NEWVFORK
vfork_save *vf = vfork_storage.val ();
if (vf != NULL && vf->pid < 0)
{
exit_state = ES_NOT_EXITING;
vf->restore_exit (status);
}
+#endif
EnterCriticalSection (&exit_lock);
muto::set_exiting_thread ();
diff --git a/winsup/cygwin/debug.cc b/winsup/cygwin/debug.cc
new file mode 100644
index 00000000000..10db804237e
--- /dev/null
+++ b/winsup/cygwin/debug.cc
@@ -0,0 +1,241 @@
+/* debug.cc
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include "sync.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "perthread.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygerrno.h"
+#ifdef DEBUGGING
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#endif
+
+#undef CloseHandle
+
+#ifdef DEBUGGING
+/* Here lies extra debugging routines which help track down internal
+ Cygwin problems when compiled with -DDEBUGGING . */
+#include <stdlib.h>
+#define NFREEH (sizeof (cygheap->debug.freeh) / sizeof (cygheap->debug.freeh[0]))
+
+class lock_debug
+{
+ static muto *locker;
+ bool acquired;
+ public:
+ lock_debug () : acquired (0)
+ {
+ if (locker && !exit_state)
+ acquired = !!locker->acquire (INFINITE);
+ }
+ void unlock ()
+ {
+ if (locker && acquired)
+ {
+ locker->release ();
+ acquired = false;
+ }
+ }
+ ~lock_debug () {unlock ();}
+ friend void debug_init ();
+};
+
+muto NO_COPY *lock_debug::locker = NULL;
+
+static bool __stdcall mark_closed (const char *, int, HANDLE, const char *, bool);
+
+void
+debug_init ()
+{
+ muto *debug_lock_muto;
+ lock_debug::locker = new_muto (debug_lock_muto);
+}
+
+/* Find a registered handle in the linked list of handles. */
+static handle_list * __stdcall
+find_handle (HANDLE h)
+{
+ handle_list *hl;
+ for (hl = &cygheap->debug.starth; hl->next != NULL; hl = hl->next)
+ if (hl->next->h == h)
+ goto out;
+ cygheap->debug.endh = hl;
+ hl = NULL;
+
+out:
+ return hl;
+}
+
+void
+verify_handle (const char *func, int ln, HANDLE h)
+{
+ handle_list *hl = find_handle (h);
+ if (!hl)
+ return;
+ system_printf ("%s:%d - multiple attempts to add handle %p", func, ln, h);
+
+ system_printf (" previously allocated by %s:%d(%s<%p>) winpid %d",
+ hl->func, hl->ln, hl->name, hl->h, hl->pid);
+}
+
+void
+setclexec (HANDLE oh, HANDLE nh, bool not_inheriting)
+{
+ handle_list *hl = find_handle (oh);
+ if (hl)
+ {
+ hl = hl->next;
+ hl->inherited = !not_inheriting;
+ hl->h = nh;
+ }
+}
+
+/* Create a new handle record */
+static handle_list * __stdcall
+newh ()
+{
+ handle_list *hl;
+ lock_debug here;
+
+ for (hl = cygheap->debug.freeh; hl < cygheap->debug.freeh + NFREEH; hl++)
+ if (hl->name == NULL)
+ return hl;
+
+ return NULL;
+}
+
+/* Add a handle to the linked list of known handles. */
+void __stdcall
+add_handle (const char *func, int ln, HANDLE h, const char *name, bool inh)
+{
+ handle_list *hl;
+ lock_debug here;
+
+ if (!cygheap)
+ return;
+
+ if ((hl = find_handle (h)))
+ {
+ hl = hl->next;
+ if (hl->name == name && hl->func == func && hl->ln == ln)
+ return;
+ system_printf ("%s:%d - multiple attempts to add handle %s<%p>", func,
+ ln, name, h);
+ system_printf (" previously allocated by %s:%d(%s<%p>) winpid %d",
+ hl->func, hl->ln, hl->name, hl->h, hl->pid);
+ return;
+ }
+
+ if ((hl = newh ()) == NULL)
+ {
+ here.unlock ();
+ debug_printf ("couldn't allocate memory for %s(%d): %s(%p)",
+ func, ln, name, h);
+ return;
+ }
+ hl->h = h;
+ hl->name = name;
+ hl->func = func;
+ hl->ln = ln;
+ hl->next = NULL;
+ hl->inherited = inh;
+ hl->pid = GetCurrentProcessId ();
+ cygheap->debug.endh->next = hl;
+ cygheap->debug.endh = hl;
+ debug_printf ("protecting handle '%s', inherited flag %d", hl->name, hl->inherited);
+
+ return;
+}
+
+static void __stdcall
+delete_handle (handle_list *hl)
+{
+ handle_list *hnuke = hl->next;
+ debug_printf ("nuking handle '%s'", hnuke->name);
+ hl->next = hl->next->next;
+ memset (hnuke, 0, sizeof (*hnuke));
+}
+
+void
+debug_fixup_after_fork_exec ()
+{
+ /* No lock needed at this point */
+ handle_list *hl;
+ for (hl = &cygheap->debug.starth; hl->next != NULL; /* nothing */)
+ if (hl->next->inherited)
+ hl = hl->next;
+ else
+ delete_handle (hl); // removes hl->next
+}
+
+static bool __stdcall
+mark_closed (const char *func, int ln, HANDLE h, const char *name, bool force)
+{
+ handle_list *hl;
+ lock_debug here;
+
+ if (!cygheap)
+ return true;
+
+ if ((hl = find_handle (h)) && !force)
+ {
+ hl = hl->next;
+ here.unlock (); // race here
+ system_printf ("attempt to close protected handle %s:%d(%s<%p>) winpid %d",
+ hl->func, hl->ln, hl->name, hl->h, hl->pid);
+ system_printf (" by %s:%d(%s<%p>)", func, ln, name, h);
+ return false;
+ }
+
+ handle_list *hln;
+ if (hl && (hln = hl->next) && strcmp (name, hln->name))
+ {
+ system_printf ("closing protected handle %s:%d(%s<%p>)",
+ hln->func, hln->ln, hln->name, hln->h);
+ system_printf (" by %s:%d(%s<%p>)", func, ln, name, h);
+ }
+
+ if (hl)
+ delete_handle (hl);
+
+ return true;
+}
+
+/* Close a known handle. Complain if !force and closing a known handle or
+ if the name of the handle being closed does not match the registered name. */
+bool __stdcall
+close_handle (const char *func, int ln, HANDLE h, const char *name, bool force)
+{
+ bool ret;
+ lock_debug here;
+
+ if (!mark_closed (func, ln, h, name, force))
+ return false;
+
+ ret = CloseHandle (h);
+
+#if 0 /* Uncomment to see CloseHandle failures */
+ if (!ret)
+ small_printf ("CloseHandle(%s) failed %s:%d\n", name, func, ln);
+#endif
+ return ret;
+}
+
+int __stdcall
+__set_errno (const char *func, int ln, int val)
+{
+ debug_printf ("%s:%d val %d", func, ln, val);
+ return errno = val;
+}
+#endif /*DEBUGGING*/
diff --git a/winsup/cygwin/debug.h b/winsup/cygwin/debug.h
new file mode 100644
index 00000000000..f4f00c60e81
--- /dev/null
+++ b/winsup/cygwin/debug.h
@@ -0,0 +1,90 @@
+/* debug.h
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef MALLOC_DEBUG
+#define MALLOC_CHECK do {} while (0)
+#else
+#include <stdlib.h>
+#include "dlmalloc.h"
+#define MALLOC_CHECK ({\
+ debug_printf ("checking malloc pool");\
+ (void) mallinfo ();\
+})
+#endif
+
+#if !defined(_DEBUG_H_)
+#define _DEBUG_H_
+
+#define being_debugged() \
+ (IsDebuggerPresent () /* || GetLastError () == ERROR_PROC_NOT_FOUND*/)
+
+#ifndef DEBUGGING
+# define cygbench(s)
+# define ForceCloseHandle CloseHandle
+# define ForceCloseHandle1(h, n) CloseHandle (h)
+# define ForceCloseHandle2(h, n) CloseHandle (h)
+# define ProtectHandle(h) do {} while (0)
+# define ProtectHandle1(h,n) do {} while (0)
+# define ProtectHandle2(h,n) do {} while (0)
+# define ProtectHandleINH(h) do {} while (0)
+# define ProtectHandle1INH(h,n) do {} while (0)
+# define ProtectHandle2INH(h,n) do {} while (0)
+# define debug_init() do {} while (0)
+# define setclexec(h, nh, b) do {} while (0)
+# define debug_fixup_after_fork_exec() do {} while (0)
+# define VerifyHandle(h) do {} while (0)
+
+#else
+
+# ifdef NO_DEBUG_DEFINES
+# undef NO_DEBUG_DEFINES
+# else
+# define CloseHandle(h) \
+ close_handle (__PRETTY_FUNCTION__, __LINE__, (h), #h, FALSE)
+# define ForceCloseHandle(h) \
+ close_handle (__PRETTY_FUNCTION__, __LINE__, (h), #h, TRUE)
+# define ForceCloseHandle1(h,n) \
+ close_handle (__PRETTY_FUNCTION__, __LINE__, (h), #n, TRUE)
+# define ForceCloseHandle2(h,n) \
+ close_handle (__PRETTY_FUNCTION__, __LINE__, (h), n, TRUE)
+# endif
+
+# define ProtectHandle(h) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), #h)
+# define ProtectHandle1(h, n) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), #n)
+# define ProtectHandle2(h, n) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), n)
+# define ProtectHandleINH(h) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), #h, 1)
+# define ProtectHandle1INH(h, n) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), #n, 1)
+# define ProtectHandle2INH(h, n) add_handle (__PRETTY_FUNCTION__, __LINE__, (h), n, 1)
+# define VerifyHandle(h) verify_handle (__PRETTY_FUNCTION__, __LINE__, (h))
+
+void debug_init ();
+void __stdcall add_handle (const char *, int, HANDLE, const char *, bool = false)
+ __attribute__ ((regparm (3)));
+void __stdcall verify_handle (const char *, int, HANDLE)
+ __attribute__ ((regparm (3)));
+bool __stdcall close_handle (const char *, int, HANDLE, const char *, bool)
+ __attribute__ ((regparm (3)));
+void __stdcall cygbench (const char *s) __attribute__ ((regparm (1)));
+extern "C" void console_printf (const char *fmt,...);
+void setclexec (HANDLE, HANDLE, bool);
+void debug_fixup_after_fork_exec ();
+extern int pinger;
+
+struct handle_list
+ {
+ HANDLE h;
+ const char *name;
+ const char *func;
+ int ln;
+ bool inherited;
+ DWORD pid;
+ struct handle_list *next;
+ };
+
+#endif /*DEBUGGING*/
+#endif /*_DEBUG_H_*/
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
new file mode 100644
index 00000000000..e7e6f1732fa
--- /dev/null
+++ b/winsup/cygwin/dtable.cc
@@ -0,0 +1,904 @@
+/* dtable.cc: file descriptor support.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 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. */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <ntdef.h>
+#include <winnls.h>
+
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "pinfo.h"
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include "tty.h"
+
+static const char NO_COPY unknown_file[] = "some disk file";
+
+static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+ STD_ERROR_HANDLE};
+
+static const char *handle_to_fn (HANDLE, char *);
+
+/* Set aside space for the table of fds */
+void
+dtable_init ()
+{
+ if (!cygheap->fdtab.size)
+ cygheap->fdtab.extend (NOFILE_INCR);
+ cygheap->fdtab.init_lock ();
+
+}
+
+void __stdcall
+set_std_handle (int fd)
+{
+ if (fd == 0)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_handle ());
+ else if (fd <= 2)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_output_handle ());
+}
+
+void
+dtable::init_lock ()
+{
+ new_muto (lock_cs);
+}
+
+int
+dtable::extend (int howmuch)
+{
+ int new_size = size + howmuch;
+ fhandler_base **newfds;
+
+ if (howmuch <= 0)
+ return 0;
+
+ if (new_size > (100 * NOFILE_INCR))
+ {
+ set_errno (EMFILE);
+ return 0;
+ }
+
+ /* Try to allocate more space for fd table. We can't call realloc ()
+ here to preserve old table if memory allocation fails */
+
+ if (!(newfds = (fhandler_base **) ccalloc (HEAP_ARGV, new_size, sizeof newfds[0])))
+ {
+ debug_printf ("calloc failed");
+ set_errno (ENOMEM);
+ return 0;
+ }
+ if (fds)
+ {
+ cfree (fds);
+ memcpy (newfds, fds, size * sizeof (fds[0]));
+ }
+
+ size = new_size;
+ fds = newfds;
+ debug_printf ("size %d, fds %p", size, fds);
+ return 1;
+}
+
+void
+dtable::get_debugger_info ()
+{
+ if (being_debugged ())
+ {
+ char std[3][sizeof ("/dev/ttyNNNN")];
+ std[0][0] = std[1][0] = std [2][0] = '\0';
+ char buf[sizeof ("cYgstd %x") + 32];
+ sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3);
+ OutputDebugString (buf);
+ for (int i = 0; i < 3; i++)
+ if (std[i][0])
+ {
+ HANDLE h = GetStdHandle (std_consts[i]);
+ fhandler_base *fh = build_fh_name (std[i]);
+ if (!fh)
+ continue;
+ fds[i] = fh;
+ if (!fh->open ((i ? O_WRONLY : O_RDONLY) | O_BINARY, 0777))
+ release (i);
+ else
+ CloseHandle (h);
+ }
+ }
+}
+
+/* Initialize the file descriptor/handle mapping table.
+ This function should only be called when a cygwin function is invoked
+ by a non-cygwin function, i.e., it should only happen very rarely. */
+
+void
+dtable::stdio_init ()
+{
+ extern void set_console_ctty ();
+ /* Set these before trying to output anything from strace.
+ Also, always set them even if we're to pick up our parent's fds
+ in case they're missed. */
+
+ if (myself->ppid_handle || ISSTATE (myself, PID_CYGPARENT))
+ return;
+
+ HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
+ HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
+
+ init_std_file_from_handle (0, in);
+
+ /* STD_ERROR_HANDLE has been observed to be the same as
+ STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes
+ to pass data from child to parent). */
+ if (out == err)
+ {
+ /* Since this code is not invoked for forked tasks, we don't have
+ to worry about the close-on-exec flag here. */
+ if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0,
+ 1, DUPLICATE_SAME_ACCESS))
+ {
+ /* If that fails, do this as a fall back. */
+ err = out;
+ system_printf ("couldn't make stderr distinct from stdout");
+ }
+ }
+
+ init_std_file_from_handle (1, out);
+ init_std_file_from_handle (2, err);
+ /* Assign the console as the controlling tty for this process if we actually
+ have a console and no other controlling tty has been assigned. */
+ if (myself->ctty < 0 && GetConsoleCP () > 0)
+ set_console_ctty ();
+}
+
+const int dtable::initial_archetype_size;
+
+fhandler_base *
+dtable::find_archetype (device& dev)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (archetypes[i]->get_device () == (unsigned) dev)
+ return archetypes[i];
+ return NULL;
+}
+
+fhandler_base **
+dtable::add_archetype ()
+{
+ if (farchetype++ >= narchetypes)
+ archetypes = (fhandler_base **) crealloc (archetypes, (narchetypes += initial_archetype_size) * sizeof archetypes[0]);
+ return archetypes + farchetype - 1;
+}
+
+void
+dtable::delete_archetype (fhandler_base *fh)
+{
+ for (unsigned i = 0; i < farchetype; i++)
+ if (fh == archetypes[i])
+ {
+ debug_printf ("deleting element %d for %s", i, fh->get_name ());
+ if (i < --farchetype)
+ archetypes[i] = archetypes[farchetype];
+ break;
+ }
+
+ delete fh;
+}
+
+int
+dtable::find_unused_handle (int start)
+{
+ do
+ {
+ for (size_t i = start; i < size; i++)
+ /* See if open -- no need for overhead of not_open */
+ if (fds[i] == NULL)
+ return i;
+ }
+ while (extend (NOFILE_INCR));
+ return -1;
+}
+
+void
+dtable::release (int fd)
+{
+ if (!not_open (fd))
+ {
+ if (fds[fd]->need_fixup_before ())
+ dec_need_fixup_before ();
+ fhandler_base *arch = fds[fd]->archetype;
+ delete fds[fd];
+ if (arch && !arch->usecount)
+ cygheap->fdtab.delete_archetype (arch);
+ fds[fd] = NULL;
+ }
+}
+
+extern "C" int
+cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
+ DWORD myaccess)
+{
+ if (fd == -1)
+ fd = cygheap->fdtab.find_unused_handle ();
+ fhandler_base *fh = build_fh_name (name);
+ cygheap->fdtab[fd] = fh;
+ fh->init (handle, myaccess, bin ?: fh->pc_binmode ());
+ return fd;
+}
+
+void
+dtable::init_std_file_from_handle (int fd, HANDLE handle)
+{
+ const char *name = NULL;
+ CONSOLE_SCREEN_BUFFER_INFO buf;
+ struct sockaddr sa;
+ int sal = sizeof (sa);
+ DCB dcb;
+ unsigned bin = O_BINARY;
+ device dev;
+
+ dev.devn = 0; /* FIXME: device */
+ first_fd_for_open = 0;
+
+ if (!not_open (fd))
+ return;
+
+ SetLastError (0);
+ DWORD ft = GetFileType (handle);
+ if (ft != FILE_TYPE_UNKNOWN || GetLastError () != ERROR_INVALID_HANDLE)
+ {
+ /* See if we can consoleify it */
+ if (GetConsoleScreenBufferInfo (handle, &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (ft == FILE_TYPE_PIPE)
+ {
+ if (fd == 0)
+ dev = *piper_dev;
+ else
+ dev = *pipew_dev;
+ }
+ else if (wsock_started && getpeername ((SOCKET) handle, &sa, &sal) == 0)
+ dev = *tcp_dev;
+ else if (GetCommState (handle, &dcb))
+ dev.parse ("/dev/ttyS0");
+ else
+ {
+ name = handle_to_fn (handle, (char *) alloca (CYG_MAX_PATH + 100));
+ bin = 0;
+ }
+ }
+
+ if (!name && !dev)
+ fds[fd] = NULL;
+ else
+ {
+ fhandler_base *fh;
+
+ if (dev)
+ fh = build_fh_dev (dev);
+ else
+ fh = build_fh_name (name);
+
+ if (fh)
+ cygheap->fdtab[fd] = fh;
+
+ if (!bin)
+ {
+ bin = fh->get_default_fmode (O_RDWR);
+ if (bin)
+ /* nothing */;
+ else if (dev)
+ bin = O_BINARY;
+ else if (name != unknown_file)
+ bin = fh->pc_binmode ();
+ }
+
+ fh->init (handle, GENERIC_READ | GENERIC_WRITE, bin);
+ set_std_handle (fd);
+ paranoid_printf ("fd %d, handle %p", fd, handle);
+ }
+}
+
+#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name
+fhandler_base *
+build_fh_name (const char *name, HANDLE h, unsigned opt, suffix_info *si)
+{
+ path_conv pc (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si);
+ if (pc.error)
+ {
+ fhandler_base *fh = cnew (fhandler_nodevice) ();
+ fh->set_error (pc.error);
+ set_errno (pc.error);
+ return fh;
+ }
+
+ if (!pc.exists () && h)
+ pc.fillin (h);
+
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_dev (const device& dev, const char *unix_name)
+{
+ path_conv pc (dev);
+ if (unix_name)
+ pc.set_normalized_path (unix_name);
+ else
+ pc.set_normalized_path (dev.name);
+ return build_fh_pc (pc);
+}
+
+fhandler_base *
+build_fh_pc (path_conv& pc)
+{
+ fhandler_base *fh = NULL;
+
+ switch (pc.dev.major)
+ {
+ case DEV_TTYS_MAJOR:
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ case DEV_TTYM_MAJOR:
+ fh = cnew (fhandler_tty_master) ();
+ break;
+ case DEV_CYGDRIVE_MAJOR:
+ fh = cnew (fhandler_cygdrive) ();
+ break;
+ case DEV_FLOPPY_MAJOR:
+ case DEV_CDROM_MAJOR:
+ case DEV_SD_MAJOR:
+ fh = cnew (fhandler_dev_floppy) ();
+ break;
+ case DEV_TAPE_MAJOR:
+ fh = cnew (fhandler_dev_tape) ();
+ break;
+ case DEV_SERIAL_MAJOR:
+ fh = cnew (fhandler_serial) ();
+ break;
+ default:
+ switch (pc.dev)
+ {
+ case FH_CONSOLE:
+ case FH_CONIN:
+ case FH_CONOUT:
+ fh = cnew (fhandler_console) ();
+ break;
+ case FH_PTYM:
+ fh = cnew (fhandler_pty_master) ();
+ break;
+ case FH_WINDOWS:
+ fh = cnew (fhandler_windows) ();
+ break;
+ case FH_FIFO:
+ fh = cnew (fhandler_fifo) ();
+ break;
+ case FH_PIPE:
+ case FH_PIPER:
+ case FH_PIPEW:
+ fh = cnew (fhandler_pipe) ();
+ break;
+ case FH_TCP:
+ case FH_UDP:
+ case FH_ICMP:
+ case FH_UNIX:
+ case FH_STREAM:
+ case FH_DGRAM:
+ fh = cnew (fhandler_socket) ();
+ break;
+ case FH_FS:
+ fh = cnew (fhandler_disk_file) ();
+ break;
+ case FH_NULL:
+ fh = cnew (fhandler_dev_null) ();
+ break;
+ case FH_ZERO:
+ fh = cnew (fhandler_dev_zero) ();
+ break;
+ case FH_RANDOM:
+ case FH_URANDOM:
+ fh = cnew (fhandler_dev_random) ();
+ break;
+ case FH_MEM:
+ case FH_PORT:
+ fh = cnew (fhandler_dev_mem) ();
+ break;
+ case FH_CLIPBOARD:
+ fh = cnew (fhandler_dev_clipboard) ();
+ break;
+ case FH_OSS_DSP:
+ fh = cnew (fhandler_dev_dsp) ();
+ break;
+ case FH_PROC:
+ fh = cnew (fhandler_proc) ();
+ break;
+ case FH_REGISTRY:
+ fh = cnew (fhandler_registry) ();
+ break;
+ case FH_PROCESS:
+ fh = cnew (fhandler_process) ();
+ break;
+ case FH_TTY:
+ {
+ if (myself->ctty == TTY_CONSOLE)
+ fh = cnew (fhandler_console) ();
+ else if (myself->ctty >= 0)
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ }
+ }
+ }
+
+ if (!fh)
+ fh = cnew (fhandler_nodevice) ();
+
+ fh->set_name (pc);
+
+ debug_printf ("fh %p", fh);
+ return fh;
+}
+
+fhandler_base *
+dtable::dup_worker (fhandler_base *oldfh)
+{
+ fhandler_base *newfh = build_fh_pc (oldfh->pc);
+ *newfh = *oldfh;
+ newfh->set_io_handle (NULL);
+ if (oldfh->dup (newfh))
+ {
+ cfree (newfh);
+ newfh = NULL;
+ return NULL;
+ }
+
+ newfh->set_close_on_exec_flag (0);
+ MALLOC_CHECK;
+ debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (), oldfh->get_io_handle (), newfh->get_io_handle ());
+ return newfh;
+}
+
+int
+dtable::dup2 (int oldfd, int newfd)
+{
+ int res = -1;
+ fhandler_base *newfh = NULL; // = NULL to avoid an incorrect warning
+
+ MALLOC_CHECK;
+ debug_printf ("dup2 (%d, %d)", oldfd, newfd);
+ lock ();
+
+ if (not_open (oldfd))
+ {
+ syscall_printf ("fd %d not open", oldfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd < 0)
+ {
+ syscall_printf ("new fd out of bounds: %d", newfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd == oldfd)
+ {
+ res = 0;
+ goto done;
+ }
+
+ if ((newfh = dup_worker (fds[oldfd])) == NULL)
+ {
+ res = -1;
+ goto done;
+ }
+
+ debug_printf ("newfh->io_handle %p, oldfh->io_handle %p",
+ newfh->get_io_handle (), fds[oldfd]->get_io_handle ());
+
+ if (!not_open (newfd))
+ close (newfd);
+ else if ((size_t) newfd < size)
+ /* nothing to do */;
+ else if (find_unused_handle (newfd) < 0)
+ {
+ newfh->close ();
+ res = -1;
+ goto done;
+ }
+
+ fds[newfd] = newfh;
+
+ if ((res = newfd) <= 2)
+ set_std_handle (res);
+
+done:
+ MALLOC_CHECK;
+ unlock ();
+ syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd);
+
+ return res;
+}
+
+fhandler_fifo *
+dtable::find_fifo (ATOM hill)
+{
+ lock ();
+ for (unsigned i = 0; i < size; i++)
+ {
+ fhandler_base *fh = fds[i];
+ if (fh && fh->isfifo () && ((fhandler_fifo *) fh)->get_atom () == hill)
+ return (fhandler_fifo *) fh;
+ }
+ return NULL;
+}
+
+select_record *
+dtable::select_read (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_read (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_write (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_write (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_except (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_except (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+/* Function to walk the fd table after an exec and perform
+ per-fhandler type fixups. */
+void
+dtable::fixup_before_fork (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::fixup_before_exec (DWORD target_proc_id)
+{
+ lock ();
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && !fh->get_close_on_exec ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ unlock ();
+}
+
+void
+dtable::set_file_pointers_for_exec ()
+{
+ 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);
+ unlock ();
+}
+
+void
+dtable::fixup_after_exec (HANDLE parent)
+{
+ first_fd_for_open = 0;
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (fh->get_close_on_exec ())
+ {
+ if (fh->archetype)
+ fh->close ();
+ release (i);
+ }
+ else
+ {
+ fh->fixup_after_exec (parent);
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+ }
+}
+
+void
+dtable::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base *fh;
+ cygheap->fdtab.init_lock ();
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ if (fh->get_close_on_exec () || fh->get_need_fork_fixup ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_after_fork (parent);
+ }
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+}
+
+#ifdef NEWVFORK
+int
+dtable::vfork_child_dup ()
+{
+ fhandler_base **newtable;
+ lock ();
+ newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0]));
+ int res = 1;
+
+ /* Remove impersonation */
+ cygheap->user.deimpersonate ();
+ if (cygheap->ctty)
+ {
+ cygheap->ctty->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "vfork dup", "incremented ", "");
+ }
+
+ for (size_t i = 0; i < size; i++)
+ if (not_open (i))
+ continue;
+ else if ((newtable[i] = dup_worker (fds[i])) != NULL)
+ newtable[i]->set_close_on_exec (fds[i]->get_close_on_exec ());
+ else
+ {
+ res = 0;
+ set_errno (EBADF);
+ goto out;
+ }
+
+ fds_on_hold = fds;
+ fds = newtable;
+
+out:
+ /* Restore impersonation */
+ cygheap->user.reimpersonate ();
+
+ unlock ();
+ return 1;
+}
+
+void
+dtable::vfork_parent_restore ()
+{
+ lock ();
+
+ fhandler_tty_slave *ctty_on_hold = cygheap->ctty_on_hold;
+ close_all_files ();
+ fhandler_base **deleteme = fds;
+ fds = fds_on_hold;
+ fds_on_hold = NULL;
+ cfree (deleteme);
+ unlock ();
+
+ if (cygheap->ctty != ctty_on_hold)
+ {
+ cygheap->ctty = ctty_on_hold; // revert
+ cygheap->ctty->close (); // Undo previous bump of this archetype
+ }
+ cygheap->ctty_on_hold = NULL;
+
+ return;
+}
+
+void
+dtable::vfork_child_fixup ()
+{
+ if (!fds_on_hold)
+ return;
+ debug_printf ("here");
+ fhandler_base **saveme = fds;
+ fds = fds_on_hold;
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (!fh->archetype && fh->get_close_on_exec ())
+ release (i);
+ else
+ {
+ fh->close ();
+ release (i);
+ }
+ }
+
+ fds = saveme;
+ cfree (fds_on_hold);
+ fds_on_hold = NULL;
+
+ if (cygheap->ctty_on_hold)
+ {
+ cygheap->ctty_on_hold->close ();
+ cygheap->ctty_on_hold = NULL;
+ }
+
+ return;
+}
+#endif /*NEWVFORK*/
+
+#define DEVICE_PREFIX "\\device\\"
+#define DEVICE_PREFIX_LEN sizeof (DEVICE_PREFIX) - 1
+#define REMOTE "\\Device\\LanmanRedirector\\"
+#define REMOTE_LEN sizeof (REMOTE) - 1
+
+static const char *
+handle_to_fn (HANDLE h, char *posix_fn)
+{
+ OBJECT_NAME_INFORMATION *ntfn;
+ char fnbuf[32768];
+
+ memset (fnbuf, 0, sizeof (fnbuf));
+ ntfn = (OBJECT_NAME_INFORMATION *) fnbuf;
+ ntfn->Name.MaximumLength = sizeof (fnbuf) - sizeof (*ntfn);
+ ntfn->Name.Buffer = (WCHAR *) (ntfn + 1);
+
+ DWORD res = NtQueryObject (h, ObjectNameInformation, ntfn, sizeof (fnbuf), NULL);
+
+ if (res)
+ {
+ strcpy (posix_fn, unknown_file);
+ debug_printf ("NtQueryObject failed");
+ return unknown_file;
+ }
+
+ // NT seems to do this on an unopened file
+ if (!ntfn->Name.Buffer)
+ {
+ debug_printf ("nt->Name.Buffer == NULL");
+ return NULL;
+ }
+
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = 0;
+
+ char win32_fn[CYG_MAX_PATH + 100];
+ sys_wcstombs (win32_fn, ntfn->Name.Buffer, ntfn->Name.Length);
+ debug_printf ("nt name '%s'", win32_fn);
+ if (!strncasematch (win32_fn, DEVICE_PREFIX, DEVICE_PREFIX_LEN)
+ || !QueryDosDevice (NULL, fnbuf, sizeof (fnbuf)))
+ return strcpy (posix_fn, win32_fn);
+
+ char *p = strechr (win32_fn + DEVICE_PREFIX_LEN, '\\');
+
+ int n = p - win32_fn;
+ int maxmatchlen = 0;
+ char *maxmatchdos = NULL;
+ for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1)
+ {
+ char device[CYG_MAX_PATH + 10];
+ device[CYG_MAX_PATH + 9] = '\0';
+ if (strchr (s, ':') == NULL)
+ continue;
+ if (!QueryDosDevice (s, device, sizeof (device) - 1))
+ continue;
+ char *q = strrchr (device, ';');
+ if (q)
+ {
+ char *r = strchr (q, '\\');
+ if (r)
+ strcpy (q, r + 1);
+ }
+ int devlen = strlen (device);
+ if (device[devlen - 1] == '\\')
+ device[--devlen] = '\0';
+ if (devlen < maxmatchlen)
+ continue;
+ if (!strncasematch (device, win32_fn, devlen) ||
+ (win32_fn[devlen] != '\0' && win32_fn[devlen] != '\\'))
+ continue;
+ maxmatchlen = devlen;
+ maxmatchdos = s;
+ debug_printf ("current match '%s'", device);
+ }
+
+ char *w32 = win32_fn;
+ if (maxmatchlen)
+ {
+ n = strlen (maxmatchdos);
+ if (maxmatchdos[n - 1] == '\\')
+ n--;
+ w32 += maxmatchlen - n;
+ memcpy (w32, maxmatchdos, n);
+ w32[n] = '\\';
+ }
+ else if (strncasematch (w32, REMOTE, REMOTE_LEN))
+ {
+ w32 += REMOTE_LEN - 2;
+ *w32 = '\\';
+ debug_printf ("remote drive");
+ }
+
+
+ debug_printf ("derived path '%s'", w32);
+ cygwin_conv_to_full_posix_path (w32, posix_fn);
+ return posix_fn;
+}
diff --git a/winsup/cygwin/dtable.h b/winsup/cygwin/dtable.h
new file mode 100644
index 00000000000..fcf29e1f4a1
--- /dev/null
+++ b/winsup/cygwin/dtable.h
@@ -0,0 +1,102 @@
+/* dtable.h: fd table definition.
+
+ Copyright 2000, 2001, 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. */
+
+/* Initial and increment values for cygwin's fd table */
+#define NOFILE_INCR 32
+
+#include "thread.h"
+#include "sync.h"
+
+class suffix_info;
+class fhandler_fifo;
+
+#define BFH_OPTS (PC_NULLEMPTY | PC_FULL | PC_POSIX)
+class dtable
+{
+ muto *lock_cs;
+ fhandler_base **fds;
+#ifdef NEWVFORK
+ fhandler_base **fds_on_hold;
+#endif
+ fhandler_base **archetypes;
+ unsigned narchetypes;
+ unsigned farchetype;
+ static const int initial_archetype_size = 8;
+ int first_fd_for_open;
+ int cnt_need_fixup_before;
+ void lock () {lock_cs->acquire ();}
+ void unlock () {lock_cs->release ();}
+ void init_lock ();
+public:
+ size_t size;
+
+ dtable () : archetypes (NULL), narchetypes (0), farchetype (0), first_fd_for_open(3), cnt_need_fixup_before(0) {}
+ void init () {first_fd_for_open = 3;}
+
+ void dec_need_fixup_before ()
+ { if (cnt_need_fixup_before > 0) --cnt_need_fixup_before; }
+ void inc_need_fixup_before ()
+ { cnt_need_fixup_before++; }
+ bool need_fixup_before ()
+ { return cnt_need_fixup_before > 0; }
+
+ int vfork_child_dup ();
+ void vfork_parent_restore ();
+ void vfork_child_fixup ();
+ fhandler_base *dup_worker (fhandler_base *oldfh);
+ int extend (int howmuch);
+ void fixup_before_exec (DWORD win_proc_id);
+ void fixup_before_fork (DWORD win_proc_id);
+ void fixup_after_fork (HANDLE);
+ inline int not_open (int fd)
+ {
+ lock ();
+ int res = fd < 0 || fd >= (int) size || fds[fd] == NULL;
+ unlock ();
+ return res;
+ }
+ int find_unused_handle (int start);
+ int find_unused_handle () { return find_unused_handle (first_fd_for_open);}
+ void release (int fd);
+ void init_std_file_from_handle (int fd, HANDLE handle);
+ int dup2 (int oldfd, int newfd);
+ void fixup_after_exec (HANDLE);
+ inline fhandler_base *&operator [](int fd) const { return fds[fd]; }
+ select_record *select_read (int fd, select_record *s);
+ select_record *select_write (int fd, select_record *s);
+ select_record *select_except (int fd, select_record *s);
+ operator fhandler_base **() {return fds;}
+ void stdio_init ();
+ void get_debugger_info ();
+ void set_file_pointers_for_exec ();
+#ifdef NEWVFORK
+ bool in_vfork_cleanup () {return fds_on_hold == fds;}
+#endif
+ fhandler_fifo *find_fifo (ATOM);
+ fhandler_base *find_archetype (device& dev);
+ fhandler_base **add_archetype ();
+ void delete_archetype (fhandler_base *);
+ friend void dtable_init ();
+ friend void __stdcall close_all_files ();
+ friend class cygheap_fdmanip;
+ friend class cygheap_fdget;
+ friend class cygheap_fdnew;
+};
+
+fhandler_base *build_fh_dev (const device&, const char * = NULL);
+fhandler_base *build_fh_name (const char *unix_name, HANDLE = NULL, unsigned = 0, suffix_info * = NULL);
+fhandler_base *build_fh_pc (path_conv& pc);
+
+void dtable_init (void);
+void stdio_init (void);
+extern dtable fdtab;
+
+extern "C" int getfdtabsize ();
+extern "C" void setfdtabsize (int);
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index fe765ecb1bb..11107a4cca8 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -906,7 +906,7 @@ sig_handle (int sig, sigset_t mask, int pid, _threadinfo *tls)
int rc = 1;
bool insigwait_mask = tls ? sigismember (&tls->sigwait_mask, sig) : false;
- bool special_case = ISSTATE (myself, PID_STOPPED) || main_vfork->pid;
+ bool special_case = ISSTATE (myself, PID_STOPPED) || VFORKPID;
bool masked = sigismember (&mask, sig);
if (sig != SIGKILL && sig != SIGSTOP
&& (special_case || main_vfork->pid || masked || insigwait_mask
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 0aad893a39c..d5619bd637f 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -1044,6 +1044,7 @@ fhandler_base::dup (fhandler_base *child)
return -1;
}
+ VerifyHandle (nh);
child->set_io_handle (nh);
}
return 0;
@@ -1209,17 +1210,17 @@ fhandler_dev_null::dump (void)
void
fhandler_base::set_inheritance (HANDLE &h, int not_inheriting)
{
-#ifdef DEBUGGING_AND_FDS_PROTECTED
HANDLE oh = h;
-#endif
/* Note that we could use SetHandleInformation here but it is not available
on all platforms. Test cases seem to indicate that using DuplicateHandle
in this fashion does not actually close the original handle, which is
what we want. If this changes in the future, we may be forced to use
SetHandleInformation on newer OS's */
- if (!DuplicateHandle (hMainProc, h, hMainProc, &h, 0, !not_inheriting,
+ if (!DuplicateHandle (hMainProc, oh, hMainProc, &h, 0, !not_inheriting,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
debug_printf ("DuplicateHandle failed, %E");
+ if (oh != h)
+ VerifyHandle (h);
#ifdef DEBUGGING_AND_FDS_PROTECTED
if (h)
setclexec (oh, h, not_inheriting);
@@ -1229,17 +1230,14 @@ fhandler_base::set_inheritance (HANDLE &h, int not_inheriting)
void
fhandler_base::fork_fixup (HANDLE parent, HANDLE &h, const char *name)
{
+ HANDLE oh = h;
if (/* !is_socket () && */ !get_close_on_exec ())
debug_printf ("handle %p already opened", h);
else if (!DuplicateHandle (parent, h, hMainProc, &h, 0, !get_close_on_exec (),
DUPLICATE_SAME_ACCESS))
system_printf ("%s - %E, handle %s<%p>", get_name (), name, h);
-#ifdef DEBUGGING_AND_FDS_PROTECTED
- else if (get_close_on_exec ())
- ProtectHandle (h); /* would have to be fancier than this */
- else
- /* ProtectHandleINH (h) */; /* Should already be protected */
-#endif
+ else if (oh != h)
+ VerifyHandle (h);
}
void
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
new file mode 100644
index 00000000000..22d322ff7f2
--- /dev/null
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -0,0 +1,1314 @@
+/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+/* #define DEBUG_NEST_ON 1 */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <asm/byteorder.h>
+
+#include <stdlib.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include "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 "wsock_event.h"
+#include "cygthread.h"
+#include "select.h"
+#include <unistd.h>
+
+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 void
+secret_event_name (char *buf, short port, int *secret_ptr)
+{
+ __small_sprintf (buf, "%scygwin.local_socket.secret.%d.%08x-%08x-%08x-%08x",
+ wincap.has_terminal_services () ? "Global\\" : "",
+ port,
+ secret_ptr [0], secret_ptr [1],
+ secret_ptr [2], secret_ptr [3]);
+}
+
+/* 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* secret = 0)
+{
+ int secret_buf [4];
+ int* secret_ptr = (secret ? : secret_buf);
+
+ if (in->sa_family == AF_INET)
+ {
+ *out = * (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))
+ {
+ sockaddr_in sin;
+ sin.sin_family = AF_INET;
+ sscanf (buf + strlen (SOCKET_COOKIE), "%hu %08x-%08x-%08x-%08x",
+ &sin.sin_port,
+ 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;
+ ret = 1;
+ }
+ CloseHandle (fh);
+ return ret;
+ }
+ else
+ {
+ set_errno (EAFNOSUPPORT);
+ return 0;
+ }
+}
+
+/**********************************************************************/
+/* fhandler_socket */
+
+fhandler_socket::fhandler_socket ()
+ : fhandler_base (), sun_path (NULL)
+{
+ set_need_fork_fixup ();
+ 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);
+}
+
+void
+fhandler_socket::set_connect_secret ()
+{
+ 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));
+ }
+}
+
+void
+fhandler_socket::get_connect_secret (char* buf)
+{
+ __small_sprintf (buf, "%08x-%08x-%08x-%08x",
+ connect_secret [0], connect_secret [1],
+ connect_secret [2], connect_secret [3]);
+}
+
+HANDLE
+fhandler_socket::create_secret_event (int* secret)
+{
+ struct sockaddr_in sin;
+ int sin_len = sizeof (sin);
+
+ if (secret_event)
+ return secret_event;
+
+ if (::getsockname (get_socket (), (struct sockaddr*) &sin, &sin_len))
+ {
+ debug_printf ("error getting local socket name (%d)", WSAGetLastError ());
+ return NULL;
+ }
+
+ char event_name[CYG_MAX_PATH];
+ secret_event_name (event_name, sin.sin_port, secret ?: connect_secret);
+ secret_event = CreateEvent (&sec_all, FALSE, FALSE, event_name);
+
+ if (!secret_event)
+ debug_printf("create event %E");
+ else if (get_close_on_exec ())
+ /* Event allows inheritance, but handle will not be inherited */
+ set_inheritance (secret_event, 1);
+
+ return secret_event;
+}
+
+void
+fhandler_socket::signal_secret_event ()
+{
+ if (!secret_event)
+ debug_printf ("no secret event?");
+ else
+ {
+ SetEvent (secret_event);
+ debug_printf ("signaled secret_event");
+ }
+}
+
+void
+fhandler_socket::close_secret_event ()
+{
+ if (secret_event)
+ CloseHandle (secret_event);
+ secret_event = NULL;
+}
+
+int
+fhandler_socket::check_peer_secret_event (struct sockaddr_in* peer, int* secret)
+{
+
+ char event_name[CYG_MAX_PATH];
+
+ secret_event_name (event_name, peer->sin_port, secret ?: connect_secret);
+ HANDLE ev = CreateEvent (&sec_all_nih, FALSE, FALSE, event_name);
+ if (!ev)
+ debug_printf("create event %E");
+
+ signal_secret_event ();
+
+ if (ev)
+ {
+ DWORD rc = WaitForSingleObject (ev, 10000);
+ debug_printf ("WFSO rc=%d", rc);
+ CloseHandle (ev);
+ return (rc == WAIT_OBJECT_0 ? 1 : 0);
+ }
+ else
+ return 0;
+}
+
+void
+fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id)
+{
+ if (!winsock2_active)
+ {
+ fhandler_base::fixup_before_fork_exec (win_proc_id);
+ debug_printf ("Without Winsock 2.0");
+ }
+ else 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 ();
+ }
+}
+
+extern "C" void __stdcall load_wsock32 ();
+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 if (!new_sock && !winsock2_active)
+ {
+ load_wsock32 ();
+ fhandler_base::fixup_after_fork (parent);
+ debug_printf ("Without Winsock 2.0");
+ }
+ else
+ {
+ debug_printf ("WSASocket went fine new_sock %p, old_sock %p", new_sock, get_io_handle ());
+ set_io_handle ((HANDLE) new_sock);
+ }
+
+ if (secret_event)
+ fork_fixup (parent, secret_event, "secret_event");
+}
+
+void
+fhandler_socket::fixup_after_exec (HANDLE parent)
+{
+ debug_printf ("here");
+ if (!get_close_on_exec ())
+ fixup_after_fork (parent);
+#if 0
+ else if (!winsock2_active)
+ closesocket (get_socket ());
+#endif
+}
+
+int
+fhandler_socket::dup (fhandler_base *child)
+{
+ debug_printf ("here");
+ fhandler_socket *fhs = (fhandler_socket *) child;
+ fhs->addr_family = addr_family;
+ if (get_addr_family () == AF_LOCAL)
+ fhs->set_sun_path (get_sun_path ());
+ fhs->set_socket_type (get_socket_type ());
+ fhs->set_connect_state (get_connect_state ());
+
+ if (winsock2_active)
+ {
+ /* 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)
+ return 0;
+ }
+ debug_printf ("WSADuplicateSocket failed, trying DuplicateHandle");
+ }
+
+ /* We don't call fhandler_base::dup here since that requires to
+ have winsock called from fhandler_base and it creates only
+ inheritable sockets which is wrong for winsock2. */
+
+ HANDLE nh;
+ if (!DuplicateHandle (hMainProc, get_io_handle (), hMainProc, &nh, 0,
+ !winsock2_active, DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ());
+ __seterrno ();
+ return -1;
+ }
+ VerifyHandle (nh);
+ fhs->set_io_handle (nh);
+ return 0;
+}
+
+int __stdcall
+fhandler_socket::fstat (struct __stat64 *buf)
+{
+ int res = fhandler_base::fstat (buf);
+ if (!res)
+ {
+ if (get_socket_type ()) /* fstat */
+ {
+ buf->st_dev = 0;
+ buf->st_ino = (__ino64_t) ((DWORD) get_handle ());
+ buf->st_mode = S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ else
+ {
+ path_conv spc ("/dev", PC_SYM_NOFOLLOW | PC_NULLEMPTY, NULL);
+ buf->st_dev = spc.volser ();
+ buf->st_ino = get_namehash ();
+ buf->st_mode &= ~S_IRWXO;
+ buf->st_rdev = (get_device () << 16) | get_unit ();
+ }
+ }
+ return res;
+}
+
+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 %d", get_errno ());
+ set_winsock_errno ();
+ goto out;
+ }
+ if (::getsockname (get_socket (), (sockaddr *) &sin, &len))
+ {
+ syscall_printf ("AF_LOCAL: getsockname failed %d", get_errno ());
+ 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 = (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 ();
+ }
+
+ set_connect_secret ();
+
+ char buf[sizeof (SOCKET_COOKIE) + 80];
+ __small_sprintf (buf, "%s%u ", SOCKET_COOKIE, sin.sin_port);
+ get_connect_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 (::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 secret_check_failed = false;
+ bool in_progress = false;
+ sockaddr_in sin;
+ int secret [4];
+ DWORD err;
+
+ if (!get_inet_addr (name, namelen, &sin, &namelen, secret))
+ return -1;
+
+ res = ::connect (get_socket (), (sockaddr *) &sin, namelen);
+
+ if (res)
+ {
+ /* Special handling for connect to return the correct error code
+ when called on a non-blocking socket. */
+ if (is_nonblocking () || is_connect_pending ())
+ {
+ err = WSAGetLastError ();
+ if (err == WSAEWOULDBLOCK || err == WSAEALREADY)
+ in_progress = true;
+
+ if (err == WSAEWOULDBLOCK)
+ WSASetLastError (WSAEINPROGRESS);
+ else if (err == WSAEINVAL)
+ WSASetLastError (WSAEISCONN);
+ }
+ set_winsock_errno ();
+ }
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ if (!res || in_progress)
+ {
+ if (!create_secret_event (secret))
+ {
+ secret_check_failed = true;
+ }
+ else if (in_progress)
+ signal_secret_event ();
+ }
+
+ if (!secret_check_failed && !res)
+ {
+ if (!check_peer_secret_event (&sin, secret))
+ {
+ debug_printf ("accept from unauthorized server");
+ secret_check_failed = true;
+ }
+ }
+
+ if (secret_check_failed)
+ {
+ close_secret_event ();
+ if (res)
+ closesocket (res);
+ set_errno (ECONNREFUSED);
+ res = -1;
+ }
+ }
+
+ err = WSAGetLastError ();
+ if (err == WSAEINPROGRESS || err == WSAEALREADY)
+ set_connect_state (CONNECT_PENDING);
+ else
+ set_connect_state (CONNECTED);
+
+ return res;
+}
+
+int
+fhandler_socket::listen (int backlog)
+{
+ int res = ::listen (get_socket (), backlog);
+ if (res)
+ set_winsock_errno ();
+ else
+ set_connect_state (CONNECTED);
+ return res;
+}
+
+int
+fhandler_socket::accept (struct sockaddr *peer, int *len)
+{
+ int res = -1;
+ bool secret_check_failed = false;
+ bool in_progress = false;
+
+ /* 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 ((SOCKET) res == INVALID_SOCKET && WSAGetLastError () == WSAEWOULDBLOCK)
+ in_progress = true;
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ if ((SOCKET) res != INVALID_SOCKET || in_progress)
+ {
+ if (!create_secret_event ())
+ secret_check_failed = true;
+ else if (in_progress)
+ signal_secret_event ();
+ }
+
+ if (!secret_check_failed &&
+ (SOCKET) res != INVALID_SOCKET)
+ {
+ if (!check_peer_secret_event ((struct sockaddr_in*) peer))
+ {
+ debug_printf ("connect from unauthorized client");
+ secret_check_failed = true;
+ }
+ }
+
+ if (secret_check_failed)
+ {
+ close_secret_event ();
+ if ((SOCKET) res != INVALID_SOCKET)
+ closesocket (res);
+ set_errno (ECONNABORTED);
+ return -1;
+ }
+ }
+
+ if ((SOCKET) res == INVALID_SOCKET)
+ set_winsock_errno ();
+ else
+ {
+ cygheap_fdnew res_fd;
+ if (res_fd >= 0 && fdsock (res_fd, &dev (), res))
+ {
+ if (get_addr_family () == AF_LOCAL)
+ ((fhandler_socket *) res_fd)->set_sun_path (get_sun_path ());
+ ((fhandler_socket *) res_fd)->set_addr_family (get_addr_family ());
+ ((fhandler_socket *) res_fd)->set_socket_type (get_socket_type ());
+ ((fhandler_socket *) res_fd)->set_connect_state (CONNECTED);
+ res = res_fd;
+ }
+ else
+ {
+ closesocket (res);
+ res = -1;
+ }
+ }
+
+ 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;
+}
+
+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_accrights: NULL,
+ msg_accrightslen: 0
+ };
+
+ return recvmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::recvfrom (void *ptr, size_t len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res;
+ DWORD ret;
+
+ flags &= MSG_WINMASK;
+ if (!winsock2_active)
+ ret = res = ::recvfrom (get_socket (),
+ (char *) ptr, len, flags,
+ from, fromlen);
+ else
+ {
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking ())
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
+ from, fromlen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
+ from, fromlen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+ }
+
+ 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;
+
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
+{
+ 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;
+
+ if (!winsock2_active)
+ {
+ if (iovcnt == 1)
+ res = recvfrom (iov->iov_base, iov->iov_len, flags,
+ from, fromlen);
+ else
+ {
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ char *buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ res = -1;
+ }
+ else
+ {
+ res = recvfrom (buf, tot, flags,
+ from, fromlen);
+
+ const struct iovec *iovptr = iov;
+ int nbytes = res;
+
+ while (nbytes > 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (iovptr->iov_base, buf, frag);
+ buf += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+ }
+ }
+ }
+ else
+ {
+ 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;
+
+ if (is_nonblocking ())
+ res = WSARecvFrom (get_socket (),
+ wsabuf, iovcnt, &ret, (DWORD *) &flags,
+ from, fromlen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSARecvFrom (get_socket (),
+ wsabuf, iovcnt, &ret, (DWORD *) &flags,
+ from, fromlen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+
+ 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;
+
+ res = -1;
+ 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_accrights: NULL,
+ msg_accrightslen: 0
+ };
+
+ return sendmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::sendto (const void *ptr, size_t len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ sockaddr_in sin;
+
+ if (to && !get_inet_addr (to, tolen, &sin, &tolen))
+ return -1;
+
+ int res;
+ DWORD ret;
+
+ if (!winsock2_active)
+ ret = res = ::sendto (get_socket (), (const char *) ptr, len,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen);
+ else
+ {
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking ())
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ 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 == -1 && 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;
+
+ if (!winsock2_active)
+ {
+ if (iovcnt == 1)
+ res = sendto (iov->iov_base, iov->iov_len, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen);
+ else
+ {
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ char *const buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ res = -1;
+ }
+ else
+ {
+ char *bufptr = buf;
+ const struct iovec *iovptr = iov;
+ int nbytes = tot;
+
+ while (nbytes != 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (bufptr, iovptr->iov_base, frag);
+ bufptr += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+
+ res = sendto (buf, tot, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen);
+ }
+ }
+ }
+ else
+ {
+ 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;
+
+ if (is_nonblocking ())
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+ }
+
+ 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:
+ set_shutdown_read ();
+ break;
+ case SHUT_WR:
+ set_shutdown_write ();
+ break;
+ case SHUT_RDWR:
+ set_shutdown_read ();
+ set_shutdown_write ();
+ 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);
+ }
+
+ close_secret_event ();
+
+ debug_printf ("%d = fhandler_socket::close()", res);
+ return res;
+}
+
+#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT)
+
+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 (), gethwnd (), WM_ASYNCIO,
+ *(int *) p ? ASYNC_MASK : 0);
+ syscall_printf ("Async I/O on socket %s",
+ *(int *) p ? "started" : "cancelled");
+ set_async (*(int *) p);
+ break;
+ default:
+ /* We must cancel WSAAsyncSelect (if any) before setting socket to
+ * blocking mode
+ */
+ if (cmd == FIONBIO && *(int *) p == 0)
+ WSAAsyncSelect (get_socket (), gethwnd (), 0, 0);
+ res = ioctlsocket (get_socket (), cmd, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ if (cmd == FIONBIO)
+ {
+ syscall_printf ("socket is now %sblocking",
+ *(int *) p ? "non" : "");
+ /* Start AsyncSelect if async socket unblocked */
+ if (*(int *) p && get_async ())
+ WSAAsyncSelect (get_socket (), gethwnd (), WM_ASYNCIO, ASYNC_MASK);
+
+ set_nonblocking (*(int *) p);
+ }
+ 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_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 (int val)
+{
+ if (secret_event)
+ set_inheritance (secret_event, val);
+ if (!winsock2_active) /* < Winsock 2.0 */
+ set_inheritance (get_handle (), val);
+ set_close_on_exec_flag (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;
+}
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 122f3f33d5d..cb1422f6842 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -553,6 +553,7 @@ fhandler_tty_slave::open (int flags, mode_t)
return 0;
}
+ VerifyHandle (from_master_local);
if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
hMainProc, &to_master_local, 0, TRUE,
DUPLICATE_SAME_ACCESS))
@@ -561,6 +562,7 @@ fhandler_tty_slave::open (int flags, mode_t)
__seterrno ();
return 0;
}
+ VerifyHandle (to_master_local);
CloseHandle (tty_owner);
}
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc
new file mode 100644
index 00000000000..f3c0c8c9250
--- /dev/null
+++ b/winsup/cygwin/net.cc
@@ -0,0 +1,2321 @@
+/* net.cc: network-related routines.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 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. */
+
+/* #define DEBUG_NEST_ON 1 */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <iphlpapi.h>
+
+#include <stdlib.h>
+#define gethostname cygwin_gethostname
+#include <unistd.h>
+#undef gethostname
+#include <netdb.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include <assert.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "registry.h"
+#include "wsock_event.h"
+#include "cygtls.h"
+
+extern "C"
+{
+ int h_errno;
+
+ int __stdcall rcmd (char **ahost, unsigned short inport, char *locuser,
+ char *remuser, char *cmd, SOCKET * fd2p);
+ int __stdcall rexec (char **ahost, unsigned short inport, char *locuser,
+ char *password, char *cmd, SOCKET * fd2p);
+ int __stdcall rresvport (int *);
+ int sscanf (const char *, const char *, ...);
+} /* End of "C" section */
+
+LPWSAOVERLAPPED
+wsock_event::prepare ()
+{
+ LPWSAOVERLAPPED ret = NULL;
+
+ SetLastError (0);
+ if ((event = WSACreateEvent ()) != WSA_INVALID_EVENT)
+ {
+ memset (&ovr, 0, sizeof ovr);
+ ovr.hEvent = event;
+ ret = &ovr;
+ }
+ else if (GetLastError () == ERROR_PROC_NOT_FOUND) /* winsock2 not available */
+ WSASetLastError (0);
+
+ debug_printf ("%d = wsock_event::prepare ()", ret);
+ return ret;
+}
+
+int
+wsock_event::wait (int socket, LPDWORD flags)
+{
+ int ret = -1;
+ WSAEVENT ev[2] = { event, signal_arrived };
+
+ switch (WSAWaitForMultipleEvents (2, ev, FALSE, WSA_INFINITE, FALSE))
+ {
+ case WSA_WAIT_EVENT_0:
+ DWORD len;
+ if (WSAGetOverlappedResult (socket, &ovr, &len, FALSE, flags))
+ ret = (int) len;
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+ if (!CancelIo ((HANDLE) socket))
+ {
+ debug_printf ("CancelIo() %E, fallback to blocking io");
+ WSAGetOverlappedResult (socket, &ovr, &len, TRUE, flags);
+ }
+ else
+ WSASetLastError (WSAEINTR);
+ break;
+ case WSA_WAIT_FAILED:
+ break;
+ default: /* Should be impossible. *LOL* */
+ WSASetLastError (WSAEFAULT);
+ break;
+ }
+ WSACloseEvent (event);
+ event = NULL;
+ return ret;
+}
+
+WSADATA wsadata;
+
+static fhandler_socket *
+get (const int fd)
+{
+ cygheap_fdget cfd (fd);
+
+ if (cfd < 0)
+ return 0;
+
+ fhandler_socket *const fh = cfd->is_socket ();
+
+ if (!fh)
+ set_errno (ENOTSOCK);
+
+ return fh;
+}
+
+static SOCKET __stdcall
+set_socket_inheritance (SOCKET sock)
+{
+ SOCKET osock = sock;
+
+ if (!DuplicateHandle (hMainProc, (HANDLE) sock, hMainProc, (HANDLE *) &sock,
+ 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ system_printf ("DuplicateHandle failed %E");
+ else
+ debug_printf ("DuplicateHandle succeeded osock %p, sock %p", osock, sock);
+ VerifyHandle ((HANDLE) sock);
+ return sock;
+}
+
+/* htonl: standards? */
+extern "C" unsigned long int
+htonl (unsigned long int x)
+{
+ return ((((x & 0x000000ffU) << 24) |
+ ((x & 0x0000ff00U) << 8) |
+ ((x & 0x00ff0000U) >> 8) |
+ ((x & 0xff000000U) >> 24)));
+}
+
+/* ntohl: standards? */
+extern "C" unsigned long int
+ntohl (unsigned long int x)
+{
+ return htonl (x);
+}
+
+/* htons: standards? */
+extern "C" unsigned short
+htons (unsigned short x)
+{
+ return ((((x & 0x000000ffU) << 8) |
+ ((x & 0x0000ff00U) >> 8)));
+}
+
+/* ntohs: standards? */
+extern "C" unsigned short
+ntohs (unsigned short x)
+{
+ return htons (x);
+}
+
+static void
+dump_protoent (struct protoent *p)
+{
+ if (p)
+ debug_printf ("protoent %s %x %x", p->p_name, p->p_aliases, p->p_proto);
+}
+
+/* exported as inet_ntoa: BSD 4.3 */
+extern "C" char *
+cygwin_inet_ntoa (struct in_addr in)
+{
+ char *res = inet_ntoa (in);
+
+ if (_my_tls.locals.ntoa_buf)
+ {
+ free (_my_tls.locals.ntoa_buf);
+ _my_tls.locals.ntoa_buf = NULL;
+ }
+ if (res)
+ _my_tls.locals.ntoa_buf = strdup (res);
+ return _my_tls.locals.ntoa_buf;
+}
+
+/* exported as inet_addr: BSD 4.3 */
+extern "C" unsigned long
+cygwin_inet_addr (const char *cp)
+{
+ if (check_null_str_errno (cp))
+ return INADDR_NONE;
+ unsigned long res = inet_addr (cp);
+
+ return res;
+}
+
+/* exported as inet_aton: BSD 4.3
+ inet_aton is not exported by wsock32 and ws2_32,
+ so it has to be implemented here. */
+extern "C" int
+cygwin_inet_aton (const char *cp, struct in_addr *inp)
+{
+ if (check_null_str_errno (cp) || check_null_invalid_struct_errno (inp))
+ return 0;
+
+ unsigned long res = inet_addr (cp);
+
+ if (res == INADDR_NONE && strcmp (cp, "255.255.255.255"))
+ return 0;
+ if (inp)
+ inp->s_addr = res;
+ return 1;
+}
+
+/* undocumented in wsock32.dll */
+extern "C" unsigned int WINAPI inet_network (const char *);
+
+extern "C" unsigned int
+cygwin_inet_network (const char *cp)
+{
+ if (check_null_str_errno (cp))
+ return INADDR_NONE;
+ unsigned int res = inet_network (cp);
+
+ return res;
+}
+
+/* inet_netof is in the standard BSD sockets library. It is useless
+ for modern networks, since it assumes network values which are no
+ longer meaningful, but some existing code calls it. */
+
+extern "C" unsigned long
+inet_netof (struct in_addr in)
+{
+ unsigned long i, res;
+
+ i = ntohl (in.s_addr);
+ if (IN_CLASSA (i))
+ res = (i & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT;
+ else if (IN_CLASSB (i))
+ res = (i & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT;
+ else
+ res = (i & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT;
+
+
+ return res;
+}
+
+/* inet_makeaddr is in the standard BSD sockets library. It is
+ useless for modern networks, since it assumes network values which
+ are no longer meaningful, but some existing code calls it. */
+
+extern "C" struct in_addr
+inet_makeaddr (int net, int lna)
+{
+ unsigned long i;
+ struct in_addr in;
+
+ if (net < IN_CLASSA_MAX)
+ i = (net << IN_CLASSA_NSHIFT) | (lna & IN_CLASSA_HOST);
+ else if (net < IN_CLASSB_MAX)
+ i = (net << IN_CLASSB_NSHIFT) | (lna & IN_CLASSB_HOST);
+ else if (net < 0x1000000)
+ i = (net << IN_CLASSC_NSHIFT) | (lna & IN_CLASSC_HOST);
+ else
+ i = net | lna;
+
+ in.s_addr = htonl (i);
+
+
+ return in;
+}
+
+struct tl
+{
+ int w;
+ const char *s;
+ int e;
+};
+
+static NO_COPY struct tl errmap[] = {
+ {WSAEINTR, "WSAEINTR", EINTR},
+ {WSAEWOULDBLOCK, "WSAEWOULDBLOCK", EWOULDBLOCK},
+ {WSAEINPROGRESS, "WSAEINPROGRESS", EINPROGRESS},
+ {WSAEALREADY, "WSAEALREADY", EALREADY},
+ {WSAENOTSOCK, "WSAENOTSOCK", ENOTSOCK},
+ {WSAEDESTADDRREQ, "WSAEDESTADDRREQ", EDESTADDRREQ},
+ {WSAEMSGSIZE, "WSAEMSGSIZE", EMSGSIZE},
+ {WSAEPROTOTYPE, "WSAEPROTOTYPE", EPROTOTYPE},
+ {WSAENOPROTOOPT, "WSAENOPROTOOPT", ENOPROTOOPT},
+ {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT", EPROTONOSUPPORT},
+ {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT", ESOCKTNOSUPPORT},
+ {WSAEOPNOTSUPP, "WSAEOPNOTSUPP", EOPNOTSUPP},
+ {WSAEPFNOSUPPORT, "WSAEPFNOSUPPORT", EPFNOSUPPORT},
+ {WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT", EAFNOSUPPORT},
+ {WSAEADDRINUSE, "WSAEADDRINUSE", EADDRINUSE},
+ {WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL", EADDRNOTAVAIL},
+ {WSAENETDOWN, "WSAENETDOWN", ENETDOWN},
+ {WSAENETUNREACH, "WSAENETUNREACH", ENETUNREACH},
+ {WSAENETRESET, "WSAENETRESET", ENETRESET},
+ {WSAECONNABORTED, "WSAECONNABORTED", ECONNABORTED},
+ {WSAECONNRESET, "WSAECONNRESET", ECONNRESET},
+ {WSAENOBUFS, "WSAENOBUFS", ENOBUFS},
+ {WSAEISCONN, "WSAEISCONN", EISCONN},
+ {WSAENOTCONN, "WSAENOTCONN", ENOTCONN},
+ {WSAESHUTDOWN, "WSAESHUTDOWN", ESHUTDOWN},
+ {WSAETOOMANYREFS, "WSAETOOMANYREFS", ETOOMANYREFS},
+ {WSAETIMEDOUT, "WSAETIMEDOUT", ETIMEDOUT},
+ {WSAECONNREFUSED, "WSAECONNREFUSED", ECONNREFUSED},
+ {WSAELOOP, "WSAELOOP", ELOOP},
+ {WSAENAMETOOLONG, "WSAENAMETOOLONG", ENAMETOOLONG},
+ {WSAEHOSTDOWN, "WSAEHOSTDOWN", EHOSTDOWN},
+ {WSAEHOSTUNREACH, "WSAEHOSTUNREACH", EHOSTUNREACH},
+ {WSAENOTEMPTY, "WSAENOTEMPTY", ENOTEMPTY},
+ {WSAEPROCLIM, "WSAEPROCLIM", EPROCLIM},
+ {WSAEUSERS, "WSAEUSERS", EUSERS},
+ {WSAEDQUOT, "WSAEDQUOT", EDQUOT},
+ {WSAESTALE, "WSAESTALE", ESTALE},
+ {WSAEREMOTE, "WSAEREMOTE", EREMOTE},
+ {WSAEINVAL, "WSAEINVAL", EINVAL},
+ {WSAEFAULT, "WSAEFAULT", EFAULT},
+ {0, "NOERROR", 0},
+ {0, NULL, 0}
+};
+
+static int
+find_winsock_errno (int why)
+{
+ for (int i = 0; errmap[i].s != NULL; ++i)
+ if (why == errmap[i].w)
+ return errmap[i].e;
+
+ return EPERM;
+}
+
+void
+__set_winsock_errno (const char *fn, int ln)
+{
+ DWORD werr = WSAGetLastError ();
+ int err = find_winsock_errno (werr);
+
+ set_errno (err);
+ syscall_printf ("%s:%d - winsock error %d -> errno %d", fn, ln, werr, err);
+}
+
+/*
+ * Since the member `s' isn't used for debug output we can use it
+ * for the error text returned by herror and hstrerror.
+ */
+static NO_COPY struct tl host_errmap[] = {
+ {WSAHOST_NOT_FOUND, "Unknown host", HOST_NOT_FOUND},
+ {WSATRY_AGAIN, "Host name lookup failure", TRY_AGAIN},
+ {WSANO_RECOVERY, "Unknown server error", NO_RECOVERY},
+ {WSANO_DATA, "No address associated with name", NO_DATA},
+ {0, NULL, 0}
+};
+
+static void
+set_host_errno ()
+{
+ int i;
+
+ int why = WSAGetLastError ();
+
+ for (i = 0; host_errmap[i].w != 0; ++i)
+ if (why == host_errmap[i].w)
+ break;
+
+ if (host_errmap[i].w != 0)
+ h_errno = host_errmap[i].e;
+ else
+ h_errno = NETDB_INTERNAL;
+}
+
+inline int
+DWORD_round (int n)
+{
+ return sizeof (DWORD) * (((n + sizeof (DWORD) - 1)) / sizeof (DWORD));
+}
+
+inline int
+strlen_round (const char *s)
+{
+ if (!s)
+ return 0;
+ return DWORD_round (strlen (s) + 1);
+}
+
+#pragma pack(push,2)
+struct pservent
+{
+ char *s_name;
+ char **s_aliases;
+ short s_port;
+ char *s_proto;
+};
+#pragma pack(pop)
+
+struct unionent
+{
+ char *name;
+ char **list;
+ short port_proto_addrtype;
+ short h_len;
+ union
+ {
+ char *s_proto;
+ char **h_addr_list;
+ };
+};
+
+enum struct_type
+{
+ is_hostent, is_protoent, is_servent
+};
+
+static const char *entnames[] = {"host", "proto", "serv"};
+
+/* Generic "dup a {host,proto,serv}ent structure" function.
+ This is complicated because we need to be able to free the
+ structure at any point and we can't rely on the pointer contents
+ being untouched by callers. So, we allocate a chunk of memory
+ large enough to hold the structure and all of the stuff it points
+ to then we copy the source into this new block of memory.
+ The 'unionent' struct is a union of all of the currently used
+ *ent structure. */
+
+#ifdef DEBUGGING
+static void *
+#else
+static inline void *
+#endif
+dup_ent (void *old, void *src0, struct_type type)
+{
+ if (old)
+ {
+ debug_printf ("freeing old %sent structure \"%s\" %p\n", entnames[type],
+ ((unionent *) old)->name, old);
+ free (old);
+ }
+
+ if (!src0)
+ return NULL;
+
+ unionent *src = (unionent *) src0;
+ debug_printf ("duping %sent \"%s\", %p", entnames[type],
+ src ? src->name : "<null!>", src);
+
+ /* Find the size of the raw structure minus any character strings, etc. */
+ int sz, struct_sz;
+ switch (type)
+ {
+ case is_protoent:
+ struct_sz = sizeof (protoent);
+ break;
+ case is_servent:
+ struct_sz = sizeof (servent);
+ break;
+ case is_hostent:
+ struct_sz = sizeof (hostent);
+ break;
+ default:
+ api_fatal ("called with invalid value %d", type);
+ break;
+ }
+
+ /* Every *ent begins with a name. Calculate it's length. */
+ int namelen = strlen_round (src->name);
+ sz = struct_sz + namelen;
+
+ char **av;
+ /* The next field in every *ent is an argv list of "something".
+ Calculate the number of components and how much space the
+ character strings will take. */
+ int list_len = 0;
+ for (av = src->list; av && *av; av++)
+ {
+ list_len++;
+ sz += sizeof (char **) + strlen_round (*av);
+ }
+
+ /* NULL terminate if there actually was a list */
+ if (av)
+ {
+ sz += sizeof (char **);
+ list_len++;
+ }
+
+ /* Do servent/hostent specific processing */
+ int protolen = 0;
+ int addr_list_len = 0;
+ char *s_proto = NULL;
+ if (type == is_servent)
+ {
+ if (src->s_proto)
+ {
+ /* Windows 95 idiocy. Structure is misaligned on Windows 95.
+ Kludge around this by trying a different pointer alignment. */
+ if (!IsBadStringPtr (src->s_proto, INT32_MAX))
+ s_proto = src->s_proto;
+ else if (!IsBadStringPtr (((pservent *) src)->s_proto, INT32_MAX))
+ s_proto = ((pservent *) src)->s_proto;
+ sz += (protolen = strlen_round (s_proto));
+ }
+ }
+ else if (type == is_hostent)
+ {
+ /* Calculate the length and storage used for h_addr_list */
+ for (av = src->h_addr_list; av && *av; av++)
+ {
+ addr_list_len++;
+ sz += sizeof (char **) + DWORD_round (src->h_len);
+ }
+ if (av)
+ {
+ sz += sizeof (char **);
+ addr_list_len++;
+ }
+ }
+
+ /* Allocate the storage needed */
+ unionent *dst = (unionent *) calloc (1, sz);
+
+ /* Hopefully, this worked. */
+ if (dst)
+ {
+ /* This field is common to all *ent structures but named differently
+ in each, of course. */
+ dst->port_proto_addrtype = src->port_proto_addrtype;
+
+ /* Copy the name field to dst, using space just beyond the end of
+ the dst structure. */
+ char *dp = ((char *) dst) + struct_sz;
+ strcpy (dst->name = dp, src->name);
+ dp += namelen;
+
+ /* Copy the 'list' type to dst, using space beyond end of structure
+ + storage for name. */
+ if (src->list)
+ {
+ char **dav = dst->list = (char **) dp;
+ dp += sizeof (char **) * list_len;
+ for (av = src->list; av && *av; av++)
+ {
+ int len = strlen (*av) + 1;
+ memcpy (*dav++ = dp, *av, len);
+ dp += DWORD_round (len);
+ }
+ }
+
+ /* Do servent/hostent specific processing. */
+ if (type == is_servent)
+ {
+ if (s_proto)
+ {
+ strcpy (dst->s_proto = dp, s_proto);
+ dp += protolen;
+ }
+ }
+ else if (type == is_hostent)
+ {
+ /* Transfer h_len and duplicate contents of h_addr_list, using
+ memory after 'list' allocation. */
+ dst->h_len = src->h_len;
+ char **dav = dst->h_addr_list = (char **) dp;
+ dp += sizeof (char **) * addr_list_len;
+ for (av = src->h_addr_list; av && *av; av++)
+ {
+ memcpy (*dav++ = dp, *av, src->h_len);
+ dp += DWORD_round (src->h_len);
+ }
+ }
+ /* Sanity check that we did our bookkeeping correctly. */
+ assert ((dp - (char *) dst) == sz);
+ }
+ debug_printf ("duped %sent \"%s\", %p", entnames[type], dst ? dst->name : "<null!>", dst);
+ return dst;
+}
+
+/* exported as getprotobyname: standards? */
+extern "C" struct protoent *
+cygwin_getprotobyname (const char *p)
+{
+ if (check_null_str_errno (p))
+ return NULL;
+ _my_tls.locals.protoent_buf =
+ (protoent *) dup_ent (_my_tls.locals.protoent_buf, getprotobyname (p),
+ is_protoent);
+ if (!_my_tls.locals.protoent_buf)
+ set_winsock_errno ();
+
+ dump_protoent (_my_tls.locals.protoent_buf);
+ return _my_tls.locals.protoent_buf;
+}
+
+/* exported as getprotobynumber: standards? */
+extern "C" struct protoent *
+cygwin_getprotobynumber (int number)
+{
+ _my_tls.locals.protoent_buf =
+ (protoent *) dup_ent (_my_tls.locals.protoent_buf,
+ getprotobynumber (number), is_protoent);
+ if (!_my_tls.locals.protoent_buf)
+ set_winsock_errno ();
+
+ dump_protoent (_my_tls.locals.protoent_buf);
+ return _my_tls.locals.protoent_buf;
+}
+
+bool
+fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc)
+{
+ if (!winsock2_active)
+ soc = set_socket_inheritance (soc);
+ else if (wincap.has_set_handle_information ())
+ {
+ /* NT systems apparently set sockets to inheritable by default */
+ SetHandleInformation ((HANDLE) soc, HANDLE_FLAG_INHERIT, 0);
+ debug_printf ("reset socket inheritance since winsock2_active %d",
+ winsock2_active);
+ }
+ else
+ debug_printf ("not setting socket inheritance since winsock2_active %d",
+ winsock2_active);
+ fd = build_fh_dev (*dev);
+ if (!fd.isopen ())
+ return false;
+ fd->set_io_handle ((HANDLE) soc);
+ fd->set_flags (O_RDWR | O_BINARY);
+ fd->set_r_no_interrupt (winsock2_active);
+ cygheap->fdtab.inc_need_fixup_before ();
+ debug_printf ("fd %d, name '%s', soc %p", (int) fd, dev->name, soc);
+ return true;
+}
+
+/* exported as socket: standards? */
+extern "C" int
+cygwin_socket (int af, int type, int protocol)
+{
+ int res = -1;
+ SOCKET soc = 0;
+
+ debug_printf ("socket (%d, %d, %d)", af, type, protocol);
+
+ soc = socket (AF_INET, type, af == AF_LOCAL ? 0 : protocol);
+
+ if (soc == INVALID_SOCKET)
+ {
+ set_winsock_errno ();
+ goto done;
+ }
+
+ const device *dev;
+
+ if (af == AF_INET)
+ dev = type == SOCK_STREAM ? tcp_dev : udp_dev;
+ else
+ dev = type == SOCK_STREAM ? stream_dev : dgram_dev;
+
+ {
+ cygheap_fdnew fd;
+ if (fd < 0 || !fdsock (fd, dev, soc))
+ closesocket (soc);
+ else
+ {
+ ((fhandler_socket *) fd)->set_addr_family (af);
+ ((fhandler_socket *) fd)->set_socket_type (type);
+ res = fd;
+ }
+ }
+
+done:
+ syscall_printf ("%d = socket (%d, %d, %d)", res, af, type, protocol);
+ return res;
+}
+
+/* exported as sendto: standards? */
+extern "C" int
+cygwin_sendto (int fd, const void *buf, int len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if ((len && __check_invalid_read_ptr_errno (buf, (unsigned) len))
+ || (to && __check_invalid_read_ptr_errno (to, tolen))
+ || !fh)
+ res = -1;
+ else if ((res = len) != 0)
+ res = fh->sendto (buf, len, flags, to, tolen);
+
+ syscall_printf ("%d = sendto (%d, %p, %d, %x, %p, %d)",
+ res, fd, buf, len, flags, to, tolen);
+
+ return res;
+}
+
+/* exported as recvfrom: standards? */
+extern "C" int
+cygwin_recvfrom (int fd, void *buf, int len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if ((len && __check_null_invalid_struct_errno (buf, (unsigned) len))
+ || (from
+ && (check_null_invalid_struct_errno (fromlen)
+ || __check_null_invalid_struct_errno (from, (unsigned) *fromlen)))
+ || !fh)
+ res = -1;
+ else if ((res = len) != 0)
+ res = fh->recvfrom (buf, len, flags, from, fromlen);
+
+ syscall_printf ("%d = recvfrom (%d, %p, %d, %x, %p, %p)",
+ res, fd, buf, len, flags, from, fromlen);
+
+ return res;
+}
+
+/* exported as setsockopt: standards? */
+extern "C" int
+cygwin_setsockopt (int fd, int level, int optname, const void *optval,
+ int optlen)
+{
+ int res;
+ fhandler_socket *fh = get (fd);
+ const char *name = "error";
+
+ /* For the following debug_printf */
+ switch (optname)
+ {
+ case SO_DEBUG:
+ name = "SO_DEBUG";
+ break;
+ case SO_ACCEPTCONN:
+ name = "SO_ACCEPTCONN";
+ break;
+ case SO_REUSEADDR:
+ name = "SO_REUSEADDR";
+ break;
+ case SO_KEEPALIVE:
+ name = "SO_KEEPALIVE";
+ break;
+ case SO_DONTROUTE:
+ name = "SO_DONTROUTE";
+ break;
+ case SO_BROADCAST:
+ name = "SO_BROADCAST";
+ break;
+ case SO_USELOOPBACK:
+ name = "SO_USELOOPBACK";
+ break;
+ case SO_LINGER:
+ name = "SO_LINGER";
+ break;
+ case SO_OOBINLINE:
+ name = "SO_OOBINLINE";
+ break;
+ case SO_ERROR:
+ name = "SO_ERROR";
+ break;
+ }
+
+ if ((optval && __check_invalid_read_ptr_errno (optval, optlen)) || !fh)
+ res = -1;
+ else
+ {
+ res = setsockopt (fh->get_socket (), level, optname,
+ (const char *) optval, optlen);
+
+ if (optlen == 4)
+ syscall_printf ("setsockopt optval=%x", *(long *) optval);
+
+ if (res)
+ set_winsock_errno ();
+ }
+
+ syscall_printf ("%d = setsockopt (%d, %d, %x (%s), %p, %d)",
+ res, fd, level, optname, name, optval, optlen);
+ return res;
+}
+
+/* exported as getsockopt: standards? */
+extern "C" int
+cygwin_getsockopt (int fd, int level, int optname, void *optval, int *optlen)
+{
+ int res;
+ fhandler_socket *fh = get (fd);
+ const char *name = "error";
+
+ /* For the following debug_printf */
+ switch (optname)
+ {
+ case SO_DEBUG:
+ name = "SO_DEBUG";
+ break;
+ case SO_ACCEPTCONN:
+ name = "SO_ACCEPTCONN";
+ break;
+ case SO_REUSEADDR:
+ name = "SO_REUSEADDR";
+ break;
+ case SO_KEEPALIVE:
+ name = "SO_KEEPALIVE";
+ break;
+ case SO_DONTROUTE:
+ name = "SO_DONTROUTE";
+ break;
+ case SO_BROADCAST:
+ name = "SO_BROADCAST";
+ break;
+ case SO_USELOOPBACK:
+ name = "SO_USELOOPBACK";
+ break;
+ case SO_LINGER:
+ name = "SO_LINGER";
+ break;
+ case SO_OOBINLINE:
+ name = "SO_OOBINLINE";
+ break;
+ case SO_ERROR:
+ name = "SO_ERROR";
+ break;
+ }
+
+ if ((optval
+ && (check_null_invalid_struct_errno (optlen)
+ || __check_null_invalid_struct_errno (optval, (unsigned) *optlen)))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = getsockopt (fh->get_socket (), level, optname, (char *) optval,
+ (int *) optlen);
+
+ if (optname == SO_ERROR)
+ {
+ int *e = (int *) optval;
+
+ *e = find_winsock_errno (*e);
+ }
+
+ if (res)
+ set_winsock_errno ();
+ }
+
+ syscall_printf ("%d = getsockopt (%d, %d, %x (%s), %p, %p)",
+ res, fd, level, optname, name, optval, optlen);
+ return res;
+}
+
+/* exported as connect: standards? */
+extern "C" int
+cygwin_connect (int fd, const struct sockaddr *name, int namelen)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (name, namelen) || !fh)
+ res = -1;
+ else
+ {
+ bool was_blocking = false;
+ if (!fh->is_nonblocking ())
+ {
+ int nonblocking = 1;
+ fh->ioctl (FIONBIO, &nonblocking);
+ was_blocking = true;
+ }
+ res = fh->connect (name, namelen);
+ if (was_blocking)
+ {
+ if (res == -1 && get_errno () == EINPROGRESS)
+ {
+ size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask);
+ fd_set *write_fds = (fd_set *) alloca (fds_size);
+ fd_set *except_fds = (fd_set *) alloca (fds_size);
+ memset (write_fds, 0, fds_size);
+ memset (except_fds, 0, fds_size);
+ FD_SET (fd, write_fds);
+ FD_SET (fd, except_fds);
+ res = cygwin_select (fd + 1, NULL, write_fds, except_fds, NULL);
+ if (res > 0 && FD_ISSET (fd, except_fds))
+ {
+ res = -1;
+ for (;;)
+ {
+ int err;
+ int len = sizeof err;
+ cygwin_getsockopt (fd, SOL_SOCKET, SO_ERROR,
+ (void *) &err, &len);
+ if (err)
+ {
+ set_errno (err);
+ break;
+ }
+ low_priority_sleep (0);
+ }
+ }
+ else if (res > 0)
+ res = 0;
+ else
+ {
+ WSASetLastError (WSAEINPROGRESS);
+ set_winsock_errno ();
+ }
+ }
+ int nonblocking = 0;
+ fh->ioctl (FIONBIO, &nonblocking);
+ }
+ }
+
+ syscall_printf ("%d = connect (%d, %p, %d)", res, fd, name, namelen);
+
+ return res;
+}
+
+/* exported as getservbyname: standards? */
+extern "C" struct servent *
+cygwin_getservbyname (const char *name, const char *proto)
+{
+ sig_dispatch_pending ();
+ if (check_null_str_errno (name)
+ || (proto != NULL && check_null_str_errno (proto)))
+ return NULL;
+
+ _my_tls.locals.servent_buf = (servent *) dup_ent (_my_tls.locals.servent_buf, getservbyname (name, proto),
+ is_servent);
+ if (!_my_tls.locals.servent_buf)
+ set_winsock_errno ();
+
+ syscall_printf ("%x = getservbyname (%s, %s)", _my_tls.locals.servent_buf, name, proto);
+ return _my_tls.locals.servent_buf;
+}
+
+/* exported as getservbyport: standards? */
+extern "C" struct servent *
+cygwin_getservbyport (int port, const char *proto)
+{
+ sig_dispatch_pending ();
+ if (proto != NULL && check_null_str_errno (proto))
+ return NULL;
+
+ _my_tls.locals.servent_buf = (servent *) dup_ent (_my_tls.locals.servent_buf, getservbyport (port, proto),
+ is_servent);
+ if (!_my_tls.locals.servent_buf)
+ set_winsock_errno ();
+
+ syscall_printf ("%x = getservbyport (%d, %s)", _my_tls.locals.servent_buf, port, proto);
+ return _my_tls.locals.servent_buf;
+}
+
+extern "C" int
+cygwin_gethostname (char *name, size_t len)
+{
+ sig_dispatch_pending ();
+ if (__check_null_invalid_struct_errno (name, len))
+ return -1;
+
+ if (gethostname (name, len))
+ {
+ DWORD local_len = len;
+
+ if (!GetComputerNameA (name, &local_len))
+ {
+ set_winsock_errno ();
+ return -1;
+ }
+ }
+ debug_printf ("name %s", name);
+ h_errno = 0;
+ return 0;
+}
+
+/* exported as gethostbyname: standards? */
+extern "C" struct hostent *
+cygwin_gethostbyname (const char *name)
+{
+ static unsigned char tmp_addr[4];
+ static struct hostent tmp;
+ static char *tmp_aliases[1];
+ static char *tmp_addr_list[2];
+ static int a, b, c, d;
+
+ sig_dispatch_pending ();
+ if (check_null_str_errno (name))
+ return NULL;
+
+ if (sscanf (name, "%d.%d.%d.%d", &a, &b, &c, &d) == 4)
+ {
+ /* In case you don't have DNS, at least x.x.x.x still works */
+ memset (&tmp, 0, sizeof (tmp));
+ tmp_addr[0] = a;
+ tmp_addr[1] = b;
+ tmp_addr[2] = c;
+ tmp_addr[3] = d;
+ tmp_addr_list[0] = (char *) tmp_addr;
+ tmp.h_name = name;
+ tmp.h_aliases = tmp_aliases;
+ tmp.h_addrtype = 2;
+ tmp.h_length = 4;
+ tmp.h_addr_list = tmp_addr_list;
+ return &tmp;
+ }
+
+ _my_tls.locals.hostent_buf = (hostent *) dup_ent (_my_tls.locals.hostent_buf, gethostbyname (name),
+ is_hostent);
+ if (!_my_tls.locals.hostent_buf)
+ {
+ set_winsock_errno ();
+ set_host_errno ();
+ }
+ else
+ {
+ debug_printf ("h_name %s", _my_tls.locals.hostent_buf->h_name);
+ h_errno = 0;
+ }
+ return _my_tls.locals.hostent_buf;
+}
+
+/* exported as gethostbyaddr: standards? */
+extern "C" struct hostent *
+cygwin_gethostbyaddr (const char *addr, int len, int type)
+{
+ sig_dispatch_pending ();
+ if (__check_invalid_read_ptr_errno (addr, len))
+ return NULL;
+
+ _my_tls.locals.hostent_buf = (hostent *) dup_ent (_my_tls.locals.hostent_buf,
+ gethostbyaddr (addr, len, type),
+ is_hostent);
+ if (!_my_tls.locals.hostent_buf)
+ {
+ set_winsock_errno ();
+ set_host_errno ();
+ }
+ else
+ {
+ debug_printf ("h_name %s", _my_tls.locals.hostent_buf->h_name);
+ h_errno = 0;
+ }
+ return _my_tls.locals.hostent_buf;
+}
+
+/* exported as accept: standards? */
+extern "C" int
+cygwin_accept (int fd, struct sockaddr *peer, int *len)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if ((peer && (check_null_invalid_struct_errno (len)
+ || __check_null_invalid_struct_errno (peer, (unsigned) *len)))
+ || !fh)
+ res = -1;
+ else
+ {
+ if (!fh->is_nonblocking ())
+ {
+ size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask);
+ fd_set *read_fds = (fd_set *) alloca (fds_size);
+ memset (read_fds, 0, fds_size);
+ FD_SET (fd, read_fds);
+ res = cygwin_select (fd + 1, read_fds, NULL, NULL, NULL);
+ if (res == -1)
+ return -1;
+ }
+ res = fh->accept (peer, len);
+ }
+
+ syscall_printf ("%d = accept (%d, %p, %p)", res, fd, peer, len);
+ return res;
+}
+
+/* exported as bind: standards? */
+extern "C" int
+cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen)
+{
+ int res;
+ sig_dispatch_pending ();
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (my_addr, addrlen) || !fh)
+ res = -1;
+ else
+ res = fh->bind (my_addr, addrlen);
+
+ syscall_printf ("%d = bind (%d, %p, %d)", res, fd, my_addr, addrlen);
+ return res;
+}
+
+/* exported as getsockname: standards? */
+extern "C" int
+cygwin_getsockname (int fd, struct sockaddr *addr, int *namelen)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (namelen)
+ || __check_null_invalid_struct_errno (addr, (unsigned) *namelen)
+ || !fh)
+ res = -1;
+ else
+ res = fh->getsockname (addr, namelen);
+
+ syscall_printf ("%d = getsockname (%d, %p, %p)", res, fd, addr, namelen);
+ return res;
+}
+
+/* exported as listen: standards? */
+extern "C" int
+cygwin_listen (int fd, int backlog)
+{
+ int res;
+ sig_dispatch_pending ();
+ fhandler_socket *fh = get (fd);
+
+ if (!fh)
+ res = -1;
+ else
+ res = fh->listen (backlog);
+
+ syscall_printf ("%d = listen (%d, %d)", res, fd, backlog);
+ return res;
+}
+
+/* exported as shutdown: standards? */
+extern "C" int
+cygwin_shutdown (int fd, int how)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (!fh)
+ res = -1;
+ else
+ res = fh->shutdown (how);
+
+ syscall_printf ("%d = shutdown (%d, %d)", res, fd, how);
+ return res;
+}
+
+/* exported as hstrerror: BSD 4.3 */
+extern "C" const char *
+cygwin_hstrerror (int err)
+{
+ int i;
+
+ for (i = 0; host_errmap[i].e != 0; ++i)
+ if (err == host_errmap[i].e)
+ break;
+
+ return host_errmap[i].s;
+}
+
+/* exported as herror: BSD 4.3 */
+extern "C" void
+cygwin_herror (const char *s)
+{
+ if (s && check_null_str (s))
+ return;
+ if (cygheap->fdtab.not_open (2))
+ return;
+
+ if (s)
+ {
+ write (2, s, strlen (s));
+ write (2, ": ", 2);
+ }
+
+ const char *h_errstr = cygwin_hstrerror (h_errno);
+
+ if (!h_errstr)
+ switch (h_errno)
+ {
+ case NETDB_INTERNAL:
+ h_errstr = "Resolver internal error";
+ break;
+ case NETDB_SUCCESS:
+ h_errstr = "Resolver error 0 (no error)";
+ break;
+ default:
+ h_errstr = "Unknown resolver error";
+ break;
+ }
+ write (2, h_errstr, strlen (h_errstr));
+ write (2, "\n", 1);
+}
+
+/* exported as getpeername: standards? */
+extern "C" int
+cygwin_getpeername (int fd, struct sockaddr *name, int *len)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (len)
+ || __check_null_invalid_struct_errno (name, (unsigned) *len)
+ || !fh)
+ res = -1;
+ else
+ res = fh->getpeername (name, len);
+
+ syscall_printf ("%d = getpeername %d", res, (fh ? fh->get_socket () : -1));
+ return res;
+}
+
+/* exported as recv: standards? */
+extern "C" int
+cygwin_recv (int fd, void *buf, int len, int flags)
+{
+ return cygwin_recvfrom (fd, buf, len, flags, NULL, NULL);
+}
+
+/* exported as send: standards? */
+extern "C" int
+cygwin_send (int fd, const void *buf, int len, int flags)
+{
+ return cygwin_sendto (fd, buf, len, flags, NULL, 0);
+}
+
+/* getdomainname: standards? */
+extern "C" int
+getdomainname (char *domain, size_t len)
+{
+ /*
+ * This works for Win95 only if the machine is configured to use MS-TCP.
+ * If a third-party TCP is being used this will fail.
+ * FIXME: On Win95, is there a way to portably check the TCP stack
+ * in use and include paths for the Domain name in each ?
+ * Punt for now and assume MS-TCP on Win95.
+ */
+ sig_dispatch_pending ();
+ if (__check_null_invalid_struct_errno (domain, len))
+ return -1;
+
+ PFIXED_INFO info = NULL;
+ ULONG size = 0;
+
+ if (GetNetworkParams(info, &size) == ERROR_BUFFER_OVERFLOW
+ && (info = (PFIXED_INFO) alloca(size))
+ && GetNetworkParams(info, &size) == ERROR_SUCCESS)
+ {
+ strncpy(domain, info->DomainName, len);
+ return 0;
+ }
+
+ /* This is only used by Win95 and NT <= 4.0.
+ The registry names are language independent.
+ FIXME: Handle DHCP on Win95. The DhcpDomain(s) may be available
+ in ..VxD\DHCP\DhcpInfoXX\OptionInfo, RFC 1533 format */
+
+ reg_key r (HKEY_LOCAL_MACHINE, KEY_READ,
+ (!wincap.is_winnt ()) ? "System" : "SYSTEM",
+ "CurrentControlSet", "Services",
+ (!wincap.is_winnt ()) ? "VxD" : "Tcpip",
+ (!wincap.is_winnt ()) ? "MSTCP" : "Parameters", NULL);
+
+ if (!r.error ())
+ {
+ int res1, res2 = 0; /* Suppress compiler warning */
+ res1 = r.get_string ("Domain", domain, len, "");
+ if (res1 != ERROR_SUCCESS || !domain[0])
+ res2 = r.get_string ("DhcpDomain", domain, len, "");
+ if (res1 == ERROR_SUCCESS || res2 == ERROR_SUCCESS)
+ return 0;
+ }
+ __seterrno ();
+ return -1;
+}
+
+/* Fill out an ifconf struct. */
+
+/*
+ * IFCONF 98/ME, NTSP4, W2K:
+ * Use IP Helper Library
+ */
+static void
+get_2k_ifconf (struct ifconf *ifc, int what)
+{
+ int cnt = 0;
+ int ethId = 0, pppId = 0, slpId = 0, tokId = 0;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ DWORD ip_cnt, lip, lnp;
+ DWORD siz_ip_table = 0;
+ PMIB_IPADDRTABLE ipt;
+ PMIB_IFROW ifrow;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+
+ typedef struct ifcount_t
+ {
+ DWORD ifIndex;
+ size_t count;
+ unsigned int enumerated; // for eth0:1
+ unsigned int classId; // for eth0, tok0 ...
+
+ };
+ ifcount_t *iflist, *ifEntry;
+
+ if (GetIpAddrTable (NULL, &siz_ip_table, TRUE) == ERROR_INSUFFICIENT_BUFFER
+ && (ifrow = (PMIB_IFROW) alloca (sizeof (MIB_IFROW)))
+ && (ipt = (PMIB_IPADDRTABLE) alloca (siz_ip_table))
+ && !GetIpAddrTable (ipt, &siz_ip_table, TRUE))
+ {
+ iflist =
+ (ifcount_t *) alloca (sizeof (ifcount_t) * (ipt->dwNumEntries + 1));
+ memset (iflist, 0, sizeof (ifcount_t) * (ipt->dwNumEntries + 1));
+ for (ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt)
+ {
+ ifEntry = iflist;
+ /* search for matching entry (and stop at first free entry) */
+ while (ifEntry->count != 0)
+ {
+ if (ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex)
+ break;
+ ifEntry++;
+ }
+ if (ifEntry->count == 0)
+ {
+ ifEntry->count = 1;
+ ifEntry->ifIndex = ipt->table[ip_cnt].dwIndex;
+ }
+ else
+ {
+ ifEntry->count++;
+ }
+ }
+ // reset the last element. This is just the stopper for the loop.
+ iflist[ipt->dwNumEntries].count = 0;
+
+ /* Iterate over all configured IP-addresses */
+ for (ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt)
+ {
+ memset (ifrow, 0, sizeof (MIB_IFROW));
+ ifrow->dwIndex = ipt->table[ip_cnt].dwIndex;
+ if (GetIfEntry (ifrow) != NO_ERROR)
+ continue;
+
+ ifcount_t *ifEntry = iflist;
+
+ /* search for matching entry (and stop at first free entry) */
+ while (ifEntry->count != 0)
+ {
+ if (ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex)
+ break;
+ ifEntry++;
+ }
+
+ /* Setup the interface name */
+ switch (ifrow->dwType)
+ {
+ case MIB_IF_TYPE_TOKENRING:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = tokId++;
+ __small_sprintf (ifr->ifr_name, "tok%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "tok%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_ETHERNET:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = ethId++;
+ __small_sprintf (ifr->ifr_name, "eth%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "eth%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_PPP:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = pppId++;
+ __small_sprintf (ifr->ifr_name, "ppp%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "ppp%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_SLIP:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = slpId++;
+ __small_sprintf (ifr->ifr_name, "slp%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "slp%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_LOOPBACK:
+ strcpy (ifr->ifr_name, "lo");
+ break;
+ default:
+ continue;
+ }
+ /* setup sockaddr struct */
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwAddr;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+#if 0
+ /* Unfortunately, the field returns only crap. */
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwBCastAddr;
+#else
+ lip = ipt->table[ip_cnt].dwAddr;
+ lnp = ipt->table[ip_cnt].dwMask;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+#endif
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwMask;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ for (UINT i = 0; i < IFHWADDRLEN; ++i)
+ if (i >= ifrow->dwPhysAddrLen)
+ so->sa_data[i] = '\0';
+ else
+ so->sa_data[i] = ifrow->bPhysAddr[i];
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifrow->dwMtu;
+ break;
+ }
+ ++cnt;
+ if ((caddr_t)++ ifr >
+ ifc->ifc_buf + ifc->ifc_len - sizeof (struct ifreq))
+ goto done;
+ }
+ }
+
+done:
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+/*
+ * IFCONF Windows NT < SP4:
+ * Look at the Bind value in
+ * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage\
+ * This is a REG_MULTI_SZ with strings of the form:
+ * \Device\<Netcard>, where netcard is the name of the net device.
+ * Then look under:
+ * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<NetCard>\
+ * Parameters\Tcpip
+ * at the IPAddress, Subnetmask and DefaultGateway values for the
+ * required values.
+ */
+static void
+get_nt_ifconf (struct ifconf *ifc, int what)
+{
+ HKEY key;
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+ DWORD size;
+ int cnt = 1;
+ char *binding = (char *) 0;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+ "SYSTEM\\"
+ "CurrentControlSet\\"
+ "Services\\"
+ "Tcpip\\" "Linkage",
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
+ {
+ if (RegQueryValueEx (key, "Bind",
+ NULL, NULL,
+ NULL, &size) == ERROR_SUCCESS)
+ {
+ binding = (char *) alloca (size);
+ if (RegQueryValueEx (key, "Bind",
+ NULL, NULL,
+ (unsigned char *) binding,
+ &size) != ERROR_SUCCESS)
+ {
+ binding = NULL;
+ }
+ }
+ RegCloseKey (key);
+ }
+
+ if (binding)
+ {
+ char *bp, eth[2] = "/";
+ char cardkey[256], ipaddress[256], netmask[256];
+
+ for (bp = binding; *bp; bp += strlen (bp) + 1)
+ {
+ bp += strlen ("\\Device\\");
+ strcpy (cardkey, "SYSTEM\\CurrentControlSet\\Services\\");
+ strcat (cardkey, bp);
+ strcat (cardkey, "\\Parameters\\Tcpip");
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, cardkey,
+ 0, KEY_READ, &key) != ERROR_SUCCESS)
+ continue;
+
+ if (RegQueryValueEx (key, "IPAddress",
+ NULL, NULL,
+ (unsigned char *) ipaddress,
+ (size = 256, &size)) == ERROR_SUCCESS
+ && RegQueryValueEx (key, "SubnetMask",
+ NULL, NULL,
+ (unsigned char *) netmask,
+ (size = 256, &size)) == ERROR_SUCCESS)
+ {
+ char *ip, *np;
+ char dhcpaddress[256], dhcpnetmask[256];
+
+ for (ip = ipaddress, np = netmask;
+ *ip && *np;
+ ip += strlen (ip) + 1, np += strlen (np) + 1)
+ {
+ if ((caddr_t)++ ifr > ifc->ifc_buf
+ + ifc->ifc_len - sizeof (struct ifreq))
+ break;
+
+ if (!strncmp (bp, "NdisWan", 7))
+ {
+ strcpy (ifr->ifr_name, "ppp");
+ strcat (ifr->ifr_name, bp + 7);
+ }
+ else
+ {
+ ++*eth;
+ strcpy (ifr->ifr_name, "eth");
+ strcat (ifr->ifr_name, eth);
+ }
+ memset (&ifr->ifr_addr, '\0', sizeof ifr->ifr_addr);
+ if (cygwin_inet_addr (ip) == 0L
+ && RegQueryValueEx (key, "DhcpIPAddress",
+ NULL, NULL,
+ (unsigned char *) dhcpaddress,
+ (size = 256, &size))
+ == ERROR_SUCCESS
+ && RegQueryValueEx (key, "DhcpSubnetMask",
+ NULL, NULL,
+ (unsigned char *) dhcpnetmask,
+ (size = 256, &size))
+ == ERROR_SUCCESS)
+ {
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr =
+ cygwin_inet_addr (dhcpaddress);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (dhcpaddress);
+ lnp = cygwin_inet_addr (dhcpnetmask);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr =
+ cygwin_inet_addr (dhcpnetmask);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+ else
+ {
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = cygwin_inet_addr (ip);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (ip);
+ lnp = cygwin_inet_addr (np);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr (np);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+ ++cnt;
+ }
+ }
+ RegCloseKey (key);
+ }
+ }
+
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+/*
+ * IFCONF Windows 95:
+ * HKLM/Enum/Network/MSTCP/"*"
+ * -> Value "Driver" enthält Subkey relativ zu
+ * HKLM/System/CurrentControlSet/Class/
+ * -> In Subkey "Bindings" die Values aufzählen
+ * -> Enthält Subkeys der Form "VREDIR\*"
+ * Das * ist ein Subkey relativ zu
+ * HKLM/System/CurrentControlSet/Class/Net/
+ * HKLM/System/CurrentControlSet/Class/"Driver"
+ * -> Value "IPAddress"
+ * -> Value "IPMask"
+ * HKLM/System/CurrentControlSet/Class/Net/"*"(aus "VREDIR\*")
+ * -> Wenn Value "AdapterName" == "MS$PPP" -> ppp interface
+ * -> Value "DriverDesc" enthält den Namen
+ *
+ */
+static void
+get_95_ifconf (struct ifconf *ifc, int what)
+{
+ HKEY key;
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+ FILETIME update;
+ LONG res;
+ DWORD size;
+ int cnt = 1;
+ char ifname[256];
+ char eth[2] = "/";
+ char ppp[2] = "/";
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Enum\\Network\\MSTCP",
+ 0, KEY_READ, &key) != ERROR_SUCCESS)
+ {
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+ return;
+ }
+
+ for (int i = 0;
+ (res = RegEnumKeyEx (key, i, ifname,
+ (size = sizeof ifname, &size),
+ 0, 0, 0, &update)) != ERROR_NO_MORE_ITEMS;
+ ++i)
+ {
+ HKEY ifkey, subkey;
+ char driver[256], classname[256], netname[256];
+ char adapter[256], ip[256], np[256];
+
+ if (res != ERROR_SUCCESS
+ || RegOpenKeyEx (key, ifname, 0, KEY_READ, &ifkey) != ERROR_SUCCESS)
+ continue;
+
+ if (RegQueryValueEx (ifkey, "Driver", 0,
+ NULL, (unsigned char *) driver,
+ (size = sizeof driver, &size)) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ continue;
+ }
+
+ strcpy (classname, "System\\CurrentControlSet\\Services\\Class\\");
+ strcat (classname, driver);
+ if ((res = RegOpenKeyEx (HKEY_LOCAL_MACHINE, classname,
+ 0, KEY_READ, &subkey)) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ continue;
+ }
+
+ if (RegQueryValueEx (subkey, "IPAddress", 0,
+ NULL, (unsigned char *) ip,
+ (size = sizeof ip, &size)) == ERROR_SUCCESS
+ && RegQueryValueEx (subkey, "IPMask", 0,
+ NULL, (unsigned char *) np,
+ (size = sizeof np, &size)) == ERROR_SUCCESS)
+ {
+ if ((caddr_t)++ ifr > ifc->ifc_buf
+ + ifc->ifc_len - sizeof (struct ifreq))
+ goto out;
+
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = cygwin_inet_addr (ip);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (ip);
+ lnp = cygwin_inet_addr (np);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr (np);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+
+ RegCloseKey (subkey);
+
+ strcpy (netname, "System\\CurrentControlSet\\Services\\Class\\Net\\");
+ strcat (netname, ifname);
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, netname,
+ 0, KEY_READ, &subkey) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ --ifr;
+ continue;
+ }
+
+ if (RegQueryValueEx (subkey, "AdapterName", 0,
+ NULL, (unsigned char *) adapter,
+ (size = sizeof adapter, &size)) == ERROR_SUCCESS
+ && strcasematch (adapter, "MS$PPP"))
+ {
+ ++*ppp;
+ strcpy (ifr->ifr_name, "ppp");
+ strcat (ifr->ifr_name, ppp);
+ }
+ else
+ {
+ ++*eth;
+ strcpy (ifr->ifr_name, "eth");
+ strcat (ifr->ifr_name, eth);
+ }
+
+ RegCloseKey (subkey);
+ RegCloseKey (ifkey);
+
+ ++cnt;
+ }
+
+out:
+
+ RegCloseKey (key);
+
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+int
+get_ifconf (struct ifconf *ifc, int what)
+{
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa;
+
+ sig_dispatch_pending ();
+ if (check_null_invalid_struct_errno (ifc))
+ return -1;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ /* Ensure we have space for two struct ifreqs, fail if not. */
+ if (ifc->ifc_len < (int) (2 * sizeof (struct ifreq)))
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ /* Set up interface lo0 first */
+ strcpy (ifr->ifr_name, "lo");
+ memset (&ifr->ifr_addr, '\0', sizeof (ifr->ifr_addr));
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = htonl (INADDR_LOOPBACK);
+ lnp = cygwin_inet_addr ("255.0.0.0");
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr ("255.0.0.0");
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ ifr->ifr_hwaddr.sa_family = AF_INET;
+ memset (ifr->ifr_hwaddr.sa_data, 0, IFHWADDRLEN);
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ /* This funny value is returned by `ifconfig lo' on Linux 2.2 kernel. */
+ ifr->ifr_mtu = 3924;
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ OSVERSIONINFO os_version_info;
+
+ memset (&os_version_info, 0, sizeof os_version_info);
+ os_version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&os_version_info);
+ if (wincap.has_ip_helper_lib ())
+ get_2k_ifconf (ifc, what);
+ else if (wincap.is_winnt ())
+ get_nt_ifconf (ifc, what);
+ else
+ get_95_ifconf (ifc, what);
+ return 0;
+}
+
+/* exported as rcmd: standards? */
+extern "C" int
+cygwin_rcmd (char **ahost, unsigned short inport, char *locuser,
+ char *remuser, char *cmd, int *fd2p)
+{
+ int res = -1;
+ SOCKET fd2s;
+
+ sig_dispatch_pending ();
+
+ if (check_null_invalid_struct_errno (ahost) ||
+ check_null_empty_str_errno (*ahost) ||
+ (locuser && check_null_empty_str_errno (locuser)) ||
+ (remuser && check_null_str_errno (remuser)))
+ return (int) INVALID_SOCKET;
+
+ res = rcmd (ahost, inport, locuser, remuser, cmd, fd2p ? &fd2s : NULL);
+ if (res != (int) INVALID_SOCKET)
+ {
+ cygheap_fdnew res_fd;
+
+ if (res_fd >= 0 && fdsock (res_fd, tcp_dev, res))
+ {
+ ((fhandler_socket *) res_fd)->set_connect_state (CONNECTED);
+ res = res_fd;
+ }
+ else
+ {
+ closesocket (res);
+ res = -1;
+ }
+
+ if (res >= 0 && fd2p)
+ {
+ cygheap_fdnew newfd (res_fd, false);
+ cygheap_fdget fd (*fd2p);
+
+ if (newfd >= 0 && fdsock (newfd, tcp_dev, fd2s))
+ {
+ *fd2p = newfd;
+ ((fhandler_socket *) fd2p)->set_connect_state (CONNECTED);
+ }
+ else
+ {
+ closesocket (res);
+ closesocket (fd2s);
+ res = -1;
+ }
+ }
+ }
+
+ syscall_printf ("%d = rcmd (...)", res);
+ return res;
+}
+
+/* exported as rresvport: standards? */
+extern "C" int
+cygwin_rresvport (int *port)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ if (check_null_invalid_struct_errno (port))
+ return -1;
+
+ res = rresvport (port);
+
+ if (res != (int) INVALID_SOCKET)
+ {
+ cygheap_fdnew res_fd;
+
+ if (res_fd >= 0 && fdsock (res_fd, tcp_dev, res))
+ res = res_fd;
+ else
+ res = -1;
+ }
+
+ syscall_printf ("%d = rresvport (%d)", res, port ? *port : 0);
+ return res;
+}
+
+/* exported as rexec: standards? */
+extern "C" int
+cygwin_rexec (char **ahost, unsigned short inport, char *locuser,
+ char *password, char *cmd, int *fd2p)
+{
+ int res = -1;
+ SOCKET fd2s;
+ sig_dispatch_pending ();
+
+ if (check_null_invalid_struct_errno (ahost) ||
+ check_null_empty_str_errno (*ahost) ||
+ (locuser && check_null_empty_str_errno (locuser)) ||
+ (password && check_null_str_errno (password)))
+ return (int) INVALID_SOCKET;
+
+ res = rexec (ahost, inport, locuser, password, cmd, fd2p ? &fd2s : NULL);
+ if (res != (int) INVALID_SOCKET)
+ {
+ cygheap_fdnew res_fd;
+
+ if (res_fd >= 0 && fdsock (res_fd, tcp_dev, res))
+ {
+ ((fhandler_socket *) res_fd)->set_connect_state (CONNECTED);
+ res = res_fd;
+ }
+ else
+ {
+ closesocket (res);
+ res = -1;
+ }
+
+ if (res >= 0 && fd2p)
+ {
+ cygheap_fdnew newfd (res_fd, false);
+ cygheap_fdget fd (*fd2p);
+
+ if (newfd >= 0 && fdsock (newfd, tcp_dev, fd2s))
+ {
+ ((fhandler_socket *) fd2p)->set_connect_state (CONNECTED);
+ *fd2p = newfd;
+ }
+ else
+ {
+ closesocket (res);
+ closesocket (fd2s);
+ res = -1;
+ }
+ }
+ }
+
+ syscall_printf ("%d = rexec (...)", res);
+ return res;
+}
+
+/* socketpair: standards? */
+/* Win32 supports AF_INET only, so ignore domain and protocol arguments */
+extern "C" int
+socketpair (int family, int type, int protocol, int *sb)
+{
+ int res = -1;
+ SOCKET insock, outsock, newsock;
+ struct sockaddr_in sock_in, sock_out;
+ int len;
+
+ sig_dispatch_pending ();
+ if (__check_null_invalid_struct_errno (sb, 2 * sizeof (int)))
+ return -1;
+
+ if (family != AF_LOCAL && family != AF_INET)
+ {
+ set_errno (EAFNOSUPPORT);
+ goto done;
+ }
+ if (type != SOCK_STREAM && type != SOCK_DGRAM)
+ {
+ set_errno (EPROTOTYPE);
+ goto done;
+ }
+ if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL)
+ || (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET))
+ {
+ set_errno (EPROTONOSUPPORT);
+ goto done;
+ }
+
+ /* create the first socket */
+ newsock = socket (AF_INET, type, 0);
+ if (newsock == INVALID_SOCKET)
+ {
+ debug_printf ("first socket call failed");
+ set_winsock_errno ();
+ goto done;
+ }
+
+ /* bind the socket to any unused port */
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_port = 0;
+ sock_in.sin_addr.s_addr = INADDR_ANY;
+ if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
+ {
+ debug_printf ("bind failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+ len = sizeof (sock_in);
+ if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0)
+ {
+ debug_printf ("getsockname error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+
+ /* For stream sockets, create a listener */
+ if (type == SOCK_STREAM)
+ listen (newsock, 2);
+
+ /* create a connecting socket */
+ outsock = socket (AF_INET, type, 0);
+ if (outsock == INVALID_SOCKET)
+ {
+ debug_printf ("second socket call failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+
+ /* For datagram sockets, bind the 2nd socket to an unused address, too */
+ if (type == SOCK_DGRAM)
+ {
+ sock_out.sin_family = AF_INET;
+ sock_out.sin_port = 0;
+ sock_out.sin_addr.s_addr = INADDR_ANY;
+ if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0)
+ {
+ debug_printf ("bind failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ len = sizeof (sock_out);
+ if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0)
+ {
+ debug_printf ("getsockname error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ }
+
+ /* Force IP address to loopback */
+ sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (type == SOCK_DGRAM)
+ sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ /* Do a connect */
+ if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
+ {
+ debug_printf ("connect error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+
+ if (type == SOCK_STREAM)
+ {
+ /* For stream sockets, accept the connection and close the listener */
+ len = sizeof (sock_in);
+ insock = accept (newsock, (struct sockaddr *) &sock_in, &len);
+ if (insock == INVALID_SOCKET)
+ {
+ debug_printf ("accept error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ closesocket (newsock);
+ }
+ else
+ {
+ /* For datagram sockets, connect the 2nd socket */
+ if (connect (newsock, (struct sockaddr *) &sock_out,
+ sizeof (sock_out)) < 0)
+ {
+ debug_printf ("connect error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ insock = newsock;
+ }
+
+ {
+ cygheap_fdnew sb0;
+ const device *dev;
+
+ if (family == AF_INET)
+ dev = (type == SOCK_STREAM ? tcp_dev : udp_dev);
+ else
+ dev = (type == SOCK_STREAM ? stream_dev : dgram_dev);
+
+ if (sb0 >= 0 && fdsock (sb0, dev, insock))
+ {
+ ((fhandler_socket *) sb0)->set_sun_path ("");
+ ((fhandler_socket *) sb0)->set_addr_family (family);
+ ((fhandler_socket *) sb0)->set_socket_type (type);
+ ((fhandler_socket *) sb0)->set_connect_state (CONNECTED);
+
+ cygheap_fdnew sb1 (sb0, false);
+
+ if (sb1 >= 0 && fdsock (sb1, dev, outsock))
+ {
+ ((fhandler_socket *) sb1)->set_sun_path ("");
+ ((fhandler_socket *) sb1)->set_addr_family (family);
+ ((fhandler_socket *) sb1)->set_socket_type (type);
+ ((fhandler_socket *) sb1)->set_connect_state (CONNECTED);
+
+ sb[0] = sb0;
+ sb[1] = sb1;
+ res = 0;
+ }
+ }
+
+ if (res == -1)
+ {
+ closesocket (insock);
+ closesocket (outsock);
+ }
+ }
+
+done:
+ syscall_printf ("%d = socketpair (...)", res);
+ return res;
+}
+
+/* sethostent: standards? */
+extern "C" void
+sethostent (int)
+{
+}
+
+/* endhostent: standards? */
+extern "C" void
+endhostent (void)
+{
+}
+
+/* exported as recvmsg: standards? */
+extern "C" int
+cygwin_recvmsg (int fd, struct msghdr *msg, int flags)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (msg)
+ || (msg->msg_name
+ && __check_null_invalid_struct_errno (msg->msg_name,
+ (unsigned) msg->msg_namelen))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = check_iovec_for_read (msg->msg_iov, msg->msg_iovlen);
+ if (res > 0)
+ res = fh->recvmsg (msg, flags, res); // res == iovec tot
+ }
+
+ syscall_printf ("%d = recvmsg (%d, %p, %x)", res, fd, msg, flags);
+ return res;
+}
+
+/* exported as sendmsg: standards? */
+extern "C" int
+cygwin_sendmsg (int fd, const struct msghdr *msg, int flags)
+{
+ int res;
+ sig_dispatch_pending ();
+
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (msg, sizeof msg)
+ || (msg->msg_name
+ && __check_invalid_read_ptr_errno (msg->msg_name,
+ (unsigned) msg->msg_namelen))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = check_iovec_for_write (msg->msg_iov, msg->msg_iovlen);
+ if (res > 0)
+ res = fh->sendmsg (msg, flags, res); // res == iovec tot
+ }
+
+ syscall_printf ("%d = sendmsg (%d, %p, %x)", res, fd, msg, flags);
+ return res;
+}
diff --git a/winsup/cygwin/perthread.h b/winsup/cygwin/perthread.h
new file mode 100644
index 00000000000..8c4c649c9b4
--- /dev/null
+++ b/winsup/cygwin/perthread.h
@@ -0,0 +1,98 @@
+/* perthread.h: Header file for cygwin thread-local storage.
+
+ Copyright 2000, 2001, 2002, 2004 Red Hat, Inc.
+
+ Written by Christopher Faylor <cgf@cygnus.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#define PTMAGIC 0x77366377
+
+#define PER_THREAD_FORK_CLEAR ((void *)UINT32_MAX)
+class per_thread
+{
+ DWORD tls;
+ int clear_on_fork_p;
+public:
+ per_thread (int forkval = 1) {tls = TlsAlloc (); clear_on_fork_p = forkval;}
+ DWORD get_tls () {return tls;}
+ int clear_on_fork () {return clear_on_fork_p;}
+
+ virtual void *get () {return TlsGetValue (get_tls ());}
+ virtual size_t size () {return 0;}
+ virtual void set (void *s = NULL);
+ virtual void set (int n) {TlsSetValue (get_tls (), (void *)n);}
+ virtual void *create ()
+ {
+ void *s = new char [size ()];
+ memset (s, 0, size ());
+ set (s);
+ return s;
+ }
+};
+
+class per_thread_waitq : public per_thread
+{
+public:
+ per_thread_waitq () : per_thread (0) {}
+ void *get () {return (waitq *) per_thread::get ();}
+ void *create () {return (waitq *) per_thread::create ();}
+ size_t size () {return sizeof (waitq);}
+};
+
+#ifdef NEED_VFORK
+#include "cygtls.h"
+#endif
+
+#ifndef NEWVFORK
+#define VFORKPID 0
+#else
+#if defined (NEED_VFORK)
+class vfork_save
+{
+ jmp_buf j;
+ int exitval;
+ public:
+ int pid;
+ DWORD frame[100];
+ _threadinfo tls;
+ char **vfork_ebp;
+ char **vfork_esp;
+ int ctty;
+ pid_t sid;
+ pid_t pgid;
+ int open_fhs;
+ int is_active () { return pid < 0; }
+ void restore_pid (int val)
+ {
+ pid = val;
+ longjmp (j, 1);
+ }
+ void restore_exit (int val)
+ {
+ exitval = val;
+ longjmp (j, 1);
+ }
+ friend int vfork ();
+};
+
+class per_thread_vfork : public per_thread
+{
+public:
+ vfork_save *val () { return (vfork_save *) per_thread::get (); }
+ vfork_save *create () {return (vfork_save *) per_thread::create ();}
+ size_t size () {return sizeof (vfork_save);}
+};
+extern per_thread_vfork vfork_storage;
+extern vfork_save *main_vfork;
+#define VFORKPID main_vfork->pid
+#endif
+#endif /*NEWVFORK*/
+
+extern per_thread_waitq waitq_storage;
+
+extern per_thread *threadstuff[];
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
new file mode 100644
index 00000000000..4a01193ae8b
--- /dev/null
+++ b/winsup/cygwin/pinfo.cc
@@ -0,0 +1,803 @@
+/* pinfo.cc: process table support
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 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 <stdlib.h>
+#include <time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygwin_version.h"
+#include "perprocess.h"
+#include "environ.h"
+#include <assert.h>
+#include <ntdef.h>
+#include "ntdll.h"
+#include "cygthread.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#include "fhandler.h"
+
+static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0};
+
+pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks
+
+HANDLE hexec_proc;
+
+void __stdcall
+pinfo_fixup_after_fork ()
+{
+ if (hexec_proc)
+ CloseHandle (hexec_proc);
+ /* Keeps the cygpid from being reused. No rights required */
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0,
+ TRUE, 0))
+ {
+ system_printf ("couldn't save current process handle %p, %E", hMainProc);
+ hexec_proc = NULL;
+ }
+ VerifyHandle (hexec_proc);
+}
+
+/* Initialize the process table.
+ This is done once when the dll is first loaded. */
+
+void __stdcall
+set_myself (pid_t pid, HANDLE h)
+{
+ DWORD winpid = GetCurrentProcessId ();
+ if (pid == 1)
+ pid = cygwin_pid (winpid);
+ myself.init (pid, PID_IN_USE | PID_MYSELF, h);
+ myself->dwProcessId = winpid;
+ myself->process_state |= PID_IN_USE;
+ myself->start_time = time (NULL); /* Register our starting time. */
+
+ (void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname));
+ if (!strace.active)
+ strace.hello ();
+ InitializeCriticalSection (&myself->lock);
+ return;
+}
+
+/* Initialize the process table entry for the current task.
+ This is not called for forked tasks, only execed ones. */
+void __stdcall
+pinfo_init (char **envp, int envc)
+{
+ if (envp)
+ {
+ environ_init (envp, envc);
+ /* spawn has already set up a pid structure for us so we'll use that */
+ myself->process_state |= PID_CYGPARENT;
+ }
+ else
+ {
+ /* Invent our own pid. */
+
+ set_myself (1);
+ myself->ppid = 1;
+ myself->pgid = myself->sid = myself->pid;
+ myself->ctty = -1;
+ myself->uid = ILLEGAL_UID;
+ myself->gid = UNKNOWN_GID;
+ environ_init (NULL, 0); /* call after myself has been set up */
+ }
+
+ debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
+}
+
+void
+_pinfo::exit (UINT n, bool norecord)
+{
+ if (this)
+ {
+ if (!norecord)
+ process_state = PID_EXITED;
+
+ /* FIXME: There is a potential race between an execed process and its
+ parent here. I hated to add a mutex just for this, though. */
+ struct rusage r;
+ fill_rusage (&r, hMainProc);
+ add_rusage (&rusage_self, &r);
+ }
+
+ cygthread::terminate ();
+ sigproc_printf ("Calling ExitProcess %d", n);
+ ExitProcess (n);
+}
+
+void
+pinfo::init (pid_t n, DWORD flag, HANDLE in_h)
+{
+ if (myself && n == myself->pid)
+ {
+ procinfo = myself;
+ destroy = 0;
+ h = NULL;
+ return;
+ }
+
+ void *mapaddr;
+ if (!(flag & PID_MYSELF))
+ mapaddr = NULL;
+ else
+ {
+ flag &= ~PID_MYSELF;
+ HANDLE hdummy;
+ mapaddr = open_shared (NULL, 0, hdummy, 0, SH_MYSELF);
+ }
+
+ int createit = flag & (PID_IN_USE | PID_EXECED);
+ DWORD access = FILE_MAP_READ
+ | (flag & (PID_IN_USE | PID_EXECED | PID_MAP_RW) ? FILE_MAP_WRITE : 0);
+ for (int i = 0; i < 10; i++)
+ {
+ int created;
+ char mapname[CYG_MAX_PATH]; /* XXX Not a path */
+ shared_name (mapname, "cygpid", n);
+
+ int mapsize;
+ if (flag & PID_EXECED)
+ mapsize = PINFO_REDIR_SIZE;
+ else
+ mapsize = sizeof (_pinfo);
+
+ if (in_h)
+ {
+ h = in_h;
+ created = 0;
+ }
+ else if (!createit)
+ {
+ h = OpenFileMappingA (access, FALSE, mapname);
+ created = 0;
+ }
+ else
+ {
+ char sa_buf[1024];
+ PSECURITY_ATTRIBUTES sec_attribs =
+ sec_user_nih (sa_buf, cygheap->user.sid(), well_known_world_sid,
+ FILE_MAP_READ);
+ h = CreateFileMapping (INVALID_HANDLE_VALUE, sec_attribs,
+ PAGE_READWRITE, 0, mapsize, mapname);
+ created = h && GetLastError () != ERROR_ALREADY_EXISTS;
+ }
+
+ if (!h)
+ {
+ if (createit)
+ __seterrno ();
+ procinfo = NULL;
+ return;
+ }
+
+ procinfo = (_pinfo *) MapViewOfFileEx (h, access, 0, 0, 0, mapaddr);
+ ProtectHandle1 (h, pinfo_shared_handle);
+
+ if ((procinfo->process_state & PID_INITIALIZING) && (flag & PID_NOREDIR)
+ && cygwin_pid (procinfo->dwProcessId) != procinfo->pid)
+ {
+ release ();
+ set_errno (ENOENT);
+ return;
+ }
+
+ if (procinfo->process_state & PID_EXECED)
+ {
+ assert (!i);
+ pid_t realpid = procinfo->pid;
+ debug_printf ("execed process windows pid %d, cygwin pid %d", n, realpid);
+ if (realpid == n)
+ api_fatal ("retrieval of execed process info for pid %d failed due to recursion.", n);
+ n = realpid;
+ release ();
+ if (flag & PID_ALLPIDS)
+ {
+ set_errno (ENOENT);
+ break;
+ }
+ continue;
+ }
+
+ /* In certain rare, pathological cases, it is possible for the shared
+ memory region to exist for a while after a process has exited. This
+ should only be a brief occurrence, so rather than introduce some kind
+ of locking mechanism, just loop. FIXME: I'm sure I'll regret doing it
+ this way at some point. */
+ if (i < 9 && !created && createit && (procinfo->process_state & PID_EXITED))
+ {
+ low_priority_sleep (5);
+ release ();
+ continue;
+ }
+
+ if (!created)
+ /* nothing */;
+ else if (!(flag & PID_EXECED))
+ procinfo->pid = n;
+ else
+ {
+ procinfo->process_state |= PID_IN_USE | PID_EXECED;
+ procinfo->pid = myself->pid;
+ }
+ break;
+ }
+ destroy = 1;
+}
+
+void
+pinfo::set_acl()
+{
+ char sa_buf[1024];
+ SECURITY_DESCRIPTOR sd;
+
+ sec_acl ((PACL) sa_buf, true, true, cygheap->user.sid (),
+ well_known_world_sid, FILE_MAP_READ);
+ if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+ debug_printf ("InitializeSecurityDescriptor %E");
+ else if (!SetSecurityDescriptorDacl (&sd, TRUE, (PACL) sa_buf, FALSE))
+ debug_printf ("SetSecurityDescriptorDacl %E");
+ else if (!SetKernelObjectSecurity (h, DACL_SECURITY_INFORMATION, &sd))
+ debug_printf ("SetKernelObjectSecurity %E");
+}
+
+void
+_pinfo::set_ctty (tty_min *tc, int flags, fhandler_tty_slave *arch)
+{
+ debug_printf ("checking if /dev/tty%d differs from input", ctty);
+ if ((ctty < 0 || ctty == tc->ntty) && !(flags & O_NOCTTY))
+ {
+ ctty = tc->ntty;
+ syscall_printf ("attached tty%d sid %d, pid %d, tty->pgid %d, tty->sid %d",
+ tc->ntty, sid, pid, pgid, tc->getsid ());
+
+ pinfo p (tc->getsid ());
+ if (sid == pid && (!p || p->pid == pid || !proc_exists (p)))
+ {
+ paranoid_printf ("resetting tty%d sid. Was %d, now %d. pgid was %d, now %d.",
+ tc->ntty, tc->getsid (), sid, tc->getpgid (), pgid);
+ /* We are the session leader */
+ tc->setsid (sid);
+ tc->setpgid (pgid);
+ }
+ else
+ sid = tc->getsid ();
+ if (tc->getpgid () == 0)
+ tc->setpgid (pgid);
+ if (cygheap->ctty != arch)
+ {
+ debug_printf ("cygheap->ctty %p, arch %p", cygheap->ctty, arch);
+ if (!cygheap->ctty)
+ syscall_printf ("ctty NULL");
+ else
+ {
+ syscall_printf ("ctty %p, usecount %d", cygheap->ctty,
+ cygheap->ctty->usecount);
+ cygheap->ctty->close ();
+ }
+ cygheap->ctty = arch;
+ if (arch)
+ {
+ arch->usecount++;
+ cygheap->open_fhs++;
+ report_tty_counts (cygheap->ctty, "ctty", "incremented ", "");
+ }
+ }
+ }
+}
+
+bool
+_pinfo::alive ()
+{
+ HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, false, dwProcessId);
+ if (h)
+ CloseHandle (h);
+ return !!h;
+}
+
+extern char **__argv;
+
+void
+_pinfo::commune_recv ()
+{
+ DWORD nr;
+ DWORD code;
+ HANDLE hp;
+ HANDLE __fromthem = NULL;
+ HANDLE __tothem = NULL;
+
+ hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("couldn't open handle for pid %d(%u)", pid, dwProcessId);
+ hello_pid = -1;
+ return;
+ }
+ if (!DuplicateHandle (hp, fromthem, hMainProc, &__fromthem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate fromthem, %E");
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ if (!DuplicateHandle (hp, tothem, hMainProc, &__tothem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate tothem, %E");
+ CloseHandle (__fromthem);
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ CloseHandle (hp);
+ hello_pid = 0;
+
+ if (!ReadFile (__fromthem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ {
+ unsigned n = 1;
+ CloseHandle (__fromthem); __fromthem = NULL;
+ extern int __argc_safe;
+ const char *argv[__argc_safe + 1];
+ for (int i = 0; i < __argc_safe; i++)
+ {
+ if (IsBadStringPtr (__argv[i], INT32_MAX))
+ argv[i] = "";
+ else
+ argv[i] = __argv[i];
+ n += strlen (argv[i]) + 1;
+ }
+ argv[__argc_safe] = NULL;
+ if (!WriteFile (__tothem, &n, sizeof n, &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile sizeof argv failed, %E");
+ }
+ else
+ for (const char **a = argv; *a; a++)
+ if (!WriteFile (__tothem, *a, strlen (*a) + 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile arg %d failed, %E", a - argv);
+ break;
+ }
+ if (!WriteFile (__tothem, "", 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile null failed, %E");
+ break;
+ }
+ break;
+ }
+ case PICOM_FIFO:
+ {
+ int formic;
+ if (!ReadFile (__fromthem, &formic, sizeof formic, &nr, NULL)
+ || nr != sizeof formic)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ fhandler_fifo *fh = cygheap->fdtab.find_fifo ((ATOM) formic);
+ HANDLE it[] = {(fh->get_handle ()), (fh->get_output_handle ())};
+
+ if (!WriteFile (__tothem, it, sizeof (it), &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile read handle failed, %E");
+ }
+
+ (void) ReadFile (__fromthem, &nr, sizeof (nr), &nr, NULL);
+ break;
+ }
+ }
+
+out:
+ if (__fromthem)
+ CloseHandle (__fromthem);
+ if (__tothem)
+ CloseHandle (__tothem);
+}
+
+#define PIPEBUFSIZE (16 * sizeof (DWORD))
+
+commune_result
+_pinfo::commune_send (DWORD code, ...)
+{
+ HANDLE fromthem = NULL, tome = NULL;
+ HANDLE fromme = NULL, tothem = NULL;
+ DWORD nr;
+ commune_result res;
+ va_list args;
+
+ va_start (args, code);
+
+ res.s = NULL;
+ res.n = 0;
+
+ if (!this || !pid)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+ if (!CreatePipe (&fromthem, &tome, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ if (!CreatePipe (&fromme, &tothem, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ EnterCriticalSection (&myself->lock);
+ myself->tothem = tome;
+ myself->fromthem = fromme;
+ myself->hello_pid = pid;
+ if (!WriteFile (tothem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ switch (code)
+ {
+ case PICOM_FIFO:
+ {
+ int formic = va_arg (args, int);
+ if (!WriteFile (tothem, &formic, sizeof formic, &nr, NULL) || nr != sizeof formic)
+ {
+ __seterrno ();
+ goto err;
+ }
+ break;
+ }
+ }
+
+ if (sig_send (this, __SIGCOMMUNE))
+ goto err;
+
+ /* FIXME: Need something better than an busy loop here */
+ bool isalive;
+ for (int i = 0; (isalive = alive ()) && (i < 10000); i++)
+ if (myself->hello_pid <= 0)
+ break;
+ else
+ low_priority_sleep (0);
+
+ CloseHandle (tome);
+ tome = NULL;
+ CloseHandle (fromme);
+ fromme = NULL;
+
+ if (!isalive)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+
+ if (myself->hello_pid < 0)
+ {
+ set_errno (ENOSYS);
+ goto err;
+ }
+
+ size_t n;
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ if (!ReadFile (fromthem, &n, sizeof n, &nr, NULL) || nr != sizeof n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.s = (char *) malloc (n);
+ char *p;
+ for (p = res.s; ReadFile (fromthem, p, n, &nr, NULL); p += nr)
+ continue;
+ if ((unsigned) (p - res.s) != n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.n = n;
+ break;
+ case PICOM_FIFO:
+ {
+ DWORD x = ReadFile (fromthem, res.handles, sizeof (res.handles), &nr, NULL);
+ WriteFile (tothem, &x, sizeof (x), &x, NULL);
+ if (!x)
+ goto err;
+ if (nr != sizeof (res.handles))
+ {
+ set_errno (EPIPE);
+ goto err;
+ }
+ break;
+ }
+ }
+ CloseHandle (tothem);
+ CloseHandle (fromthem);
+ goto out;
+
+err:
+ if (tome)
+ CloseHandle (tome);
+ if (fromthem)
+ CloseHandle (fromthem);
+ if (tothem)
+ CloseHandle (tothem);
+ if (fromme)
+ CloseHandle (fromme);
+ memset (&res, 0, sizeof (res));
+
+out:
+ myself->hello_pid = 0;
+ LeaveCriticalSection (&myself->lock);
+ return res;
+}
+
+char *
+_pinfo::cmdline (size_t& n)
+{
+ char *s;
+ if (!this || !pid)
+ return NULL;
+ if (pid != myself->pid)
+ {
+ commune_result cr = commune_send (PICOM_CMDLINE);
+ s = cr.s;
+ n = cr.n;
+ }
+ else
+ {
+ n = 1;
+ for (char **a = __argv; *a; a++)
+ n += strlen (*a) + 1;
+ char *p;
+ p = s = (char *) malloc (n);
+ for (char **a = __argv; *a; a++)
+ {
+ strcpy (p, *a);
+ p = strchr (p, '\0') + 1;
+ }
+ *p = '\0';
+ }
+ return s;
+}
+
+void
+pinfo::release ()
+{
+ if (h)
+ {
+#ifdef DEBUGGING
+ if (((DWORD) procinfo & 0x77000000) == 0x61000000)
+ try_to_debug ();
+#endif
+ UnmapViewOfFile (procinfo);
+ procinfo = NULL;
+ ForceCloseHandle1 (h, pinfo_shared_handle);
+ h = NULL;
+ }
+}
+
+/* DOCTOOL-START
+
+<sect1 id="func-cygwin-winpid-to-pid">
+ <title>cygwin_winpid_to_pid</title>
+
+ <funcsynopsis>
+ <funcdef>extern "C" pid_t
+ <function>cygwin_winpid_to_pid</function>
+ </funcdef>
+ <paramdef>int <parameter>winpid</parameter></paramdef>
+ </funcsynopsis>
+
+ <para>Given a windows pid, converts to the corresponding Cygwin
+pid, if any. Returns -1 if windows pid does not correspond to
+a cygwin pid.</para>
+ <example>
+ <title>Example use of cygwin_winpid_to_pid</title>
+ <programlisting>
+ extern "C" cygwin_winpid_to_pid (int winpid);
+ pid_t mypid;
+ mypid = cygwin_winpid_to_pid (windows_pid);
+ </programlisting>
+ </example>
+</sect1>
+
+ DOCTOOL-END */
+
+extern "C" pid_t
+cygwin_winpid_to_pid (int winpid)
+{
+ pinfo p (cygwin_pid (winpid));
+ if (p)
+ return p->pid;
+
+ set_errno (ESRCH);
+ return (pid_t) -1;
+}
+
+#include <tlhelp32.h>
+
+#define slop_pidlist 200
+#define size_pidlist(i) (sizeof (pidlist[0]) * ((i) + 1))
+#define size_pinfolist(i) (sizeof (pinfolist[0]) * ((i) + 1))
+
+inline void
+winpids::add (DWORD& nelem, bool winpid, DWORD pid)
+{
+ pid_t cygpid = cygwin_pid (pid);
+ if (nelem >= npidlist)
+ {
+ npidlist += slop_pidlist;
+ pidlist = (DWORD *) realloc (pidlist, size_pidlist (npidlist + 1));
+ pinfolist = (pinfo *) realloc (pinfolist, size_pinfolist (npidlist + 1));
+ }
+
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0)
+ | pinfo_access);
+ if (winpid)
+ goto out;
+
+ if (!pinfolist[nelem])
+ {
+ if (!pinfo_access)
+ return;
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0));
+ if (!pinfolist[nelem])
+ return;
+ }
+
+ /* Scan list of previously recorded pids to make sure that this pid hasn't
+ shown up before. This can happen when a process execs. */
+ for (unsigned i = 0; i < nelem; i++)
+ if (pinfolist[i]->pid == pinfolist[nelem]->pid)
+ {
+ if ((_pinfo *) pinfolist[nelem] != (_pinfo *) myself)
+ pinfolist[nelem].release ();
+ return;
+ }
+
+out:
+ pidlist[nelem++] = pid;
+}
+
+DWORD
+winpids::enumNT (bool winpid)
+{
+ static DWORD szprocs;
+ static SYSTEM_PROCESSES *procs;
+
+ DWORD nelem = 0;
+ if (!szprocs)
+ procs = (SYSTEM_PROCESSES *) malloc (sizeof (*procs) + (szprocs = 200 * sizeof (*procs)));
+
+ NTSTATUS res;
+ for (;;)
+ {
+ res = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ procs, szprocs, NULL);
+ if (res == 0)
+ break;
+
+ if (res == STATUS_INFO_LENGTH_MISMATCH)
+ procs = (SYSTEM_PROCESSES *) realloc (procs, szprocs += 200 * sizeof (*procs));
+ else
+ {
+ system_printf ("error %p reading system process information", res);
+ return 0;
+ }
+ }
+
+ SYSTEM_PROCESSES *px = procs;
+ for (;;)
+ {
+ if (px->ProcessId)
+ add (nelem, winpid, px->ProcessId);
+ if (!px->NextEntryDelta)
+ break;
+ px = (SYSTEM_PROCESSES *) ((char *) px + px->NextEntryDelta);
+ }
+
+ return nelem;
+}
+
+DWORD
+winpids::enum9x (bool winpid)
+{
+ DWORD nelem = 0;
+
+ HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
+ if (!h)
+ {
+ system_printf ("Couldn't create process snapshot, %E");
+ return 0;
+ }
+
+ PROCESSENTRY32 proc;
+ proc.dwSize = sizeof (proc);
+
+ if (Process32First (h, &proc))
+ do
+ {
+ if (proc.th32ProcessID)
+ add (nelem, winpid, proc.th32ProcessID);
+ }
+ while (Process32Next (h, &proc));
+
+ CloseHandle (h);
+ return nelem;
+}
+
+NO_COPY CRITICAL_SECTION winpids::cs;
+
+void
+winpids::set (bool winpid)
+{
+ EnterCriticalSection (&cs);
+ npids = (this->*enum_processes) (winpid);
+ if (pidlist)
+ pidlist[npids] = 0;
+ LeaveCriticalSection (&cs);
+}
+
+void
+winpids::init ()
+{
+ InitializeCriticalSection (&cs);
+}
+
+DWORD
+winpids::enum_init (bool winpid)
+{
+ if (wincap.is_winnt ())
+ enum_processes = &winpids::enumNT;
+ else
+ enum_processes = &winpids::enum9x;
+
+ return (this->*enum_processes) (winpid);
+}
+
+void
+winpids::release ()
+{
+ for (unsigned i = 0; i < npids; i++)
+ if (pinfolist[i] && (_pinfo *) pinfolist[i] != (_pinfo *) myself)
+ pinfolist[i].release ();
+}
+
+winpids::~winpids ()
+{
+ if (npidlist)
+ {
+ release ();
+ free (pidlist);
+ free (pinfolist);
+ }
+}
diff --git a/winsup/cygwin/pipe.cc b/winsup/cygwin/pipe.cc
new file mode 100644
index 00000000000..82313c492f1
--- /dev/null
+++ b/winsup/cygwin/pipe.cc
@@ -0,0 +1,323 @@
+/* pipe.cc: pipe for Cygwin.
+
+ Copyright 1996, 1998, 1999, 2000, 2001, 2002, 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. */
+
+/* FIXME: Should this really be fhandler_pipe.cc? */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <sys/socket.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "thread.h"
+#include "pinfo.h"
+#include "cygthread.h"
+
+static unsigned pipecount;
+static const NO_COPY char pipeid_fmt[] = "stupid_pipe.%u.%u";
+
+fhandler_pipe::fhandler_pipe ()
+ : fhandler_base (), guard (NULL), broken_pipe (false), writepipe_exists(0),
+ orig_pid (0), id (0)
+{
+}
+
+_off64_t
+fhandler_pipe::lseek (_off64_t offset, int whence)
+{
+ debug_printf ("(%d, %d)", offset, whence);
+ set_errno (ESPIPE);
+ return -1;
+}
+
+void
+fhandler_pipe::set_close_on_exec (int val)
+{
+ fhandler_base::set_close_on_exec (val);
+ if (guard)
+ set_inheritance (guard, val);
+ if (writepipe_exists)
+ set_inheritance (writepipe_exists, val);
+}
+
+struct pipeargs
+{
+ fhandler_base *fh;
+ void *ptr;
+ size_t *len;
+};
+
+static DWORD WINAPI
+read_pipe (void *arg)
+{
+ pipeargs *pi = (pipeargs *) arg;
+ pi->fh->fhandler_base::read (pi->ptr, *pi->len);
+ return 0;
+}
+
+void __stdcall
+fhandler_pipe::read (void *in_ptr, size_t& in_len)
+{
+ if (broken_pipe)
+ in_len = 0;
+ else
+ {
+ pipeargs pi = {dynamic_cast<fhandler_base *>(this), in_ptr, &in_len};
+ ResetEvent (read_state);
+ cygthread *th = new cygthread (read_pipe, &pi, "read_pipe");
+ if (th->detach (read_state) && !in_len)
+ in_len = (size_t) -1; /* received a signal */
+ }
+ (void) ReleaseMutex (guard);
+ return;
+}
+
+int
+fhandler_pipe::close ()
+{
+ if (guard)
+ CloseHandle (guard);
+ if (writepipe_exists)
+ CloseHandle (writepipe_exists);
+#ifndef NEWVFORK
+ if (read_state)
+#else
+ // FIXME is this vfork_cleanup test right? Is it responsible for some of
+ // the strange pipe behavior that has been reported in the cygwin mailing
+ // list?
+ if (read_state && !cygheap->fdtab.in_vfork_cleanup ())
+#endif
+ ForceCloseHandle (read_state);
+ if (get_handle ())
+ {
+ CloseHandle (get_handle ());
+ set_io_handle (NULL);
+ }
+ return 0;
+}
+
+bool
+fhandler_pipe::hit_eof ()
+{
+ char buf[80];
+ HANDLE ev;
+ if (broken_pipe)
+ return 1;
+ if (!orig_pid)
+ return false;
+ __small_sprintf (buf, pipeid_fmt, orig_pid, id);
+ if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
+ CloseHandle (ev);
+ debug_printf ("%s %p", buf, ev);
+ return ev == NULL;
+}
+
+void
+fhandler_pipe::fixup_after_exec (HANDLE parent)
+{
+ if (read_state)
+ {
+ read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ ProtectHandle (read_state);
+ }
+}
+
+void
+fhandler_pipe::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base::fixup_after_fork (parent);
+ if (guard)
+ fork_fixup (parent, guard, "guard");
+ if (writepipe_exists)
+ fork_fixup (parent, writepipe_exists, "guard");
+ fixup_after_exec (parent);
+}
+
+int
+fhandler_pipe::dup (fhandler_base *child)
+{
+ int res = -1;
+ fhandler_pipe *ftp = (fhandler_pipe *) child;
+ ftp->guard = ftp->writepipe_exists = ftp->read_state = NULL;
+
+ if (get_handle ())
+ {
+ res = fhandler_base::dup (child);
+ if (res)
+ goto err;
+ }
+
+ /* FIXME: This leaks handles in the failing condition */
+ if (guard == NULL)
+ ftp->guard = NULL;
+ else if (!DuplicateHandle (hMainProc, guard, hMainProc, &ftp->guard, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate guard %p, %E", guard);
+ goto err;
+ }
+
+ if (writepipe_exists == NULL)
+ ftp->writepipe_exists = NULL;
+ else if (!DuplicateHandle (hMainProc, writepipe_exists, hMainProc,
+ &ftp->writepipe_exists, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate writepipe_exists %p, %E", writepipe_exists);
+ goto err;
+ }
+
+ if (read_state == NULL)
+ ftp->read_state = NULL;
+ else if (!DuplicateHandle (hMainProc, read_state, hMainProc,
+ &ftp->read_state, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate read_state %p, %E", writepipe_exists);
+ goto err;
+ }
+
+ res = 0;
+ goto out;
+
+err:
+ if (!ftp->guard)
+ CloseHandle (ftp->guard);
+ if (!ftp->writepipe_exists)
+ CloseHandle (ftp->writepipe_exists);
+ if (!ftp->read_state)
+ CloseHandle (ftp->read_state);
+ goto leave;
+
+out:
+ ftp->id = id;
+ ftp->orig_pid = orig_pid;
+ VerifyHandle (ftp->guard);
+ VerifyHandle (ftp->writepipe_exists);
+ VerifyHandle (ftp->read_state);
+
+leave:
+ debug_printf ("res %d", res);
+ return res;
+}
+
+int
+fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode, bool fifo)
+{
+ HANDLE r, w;
+ SECURITY_ATTRIBUTES *sa = (mode & O_NOINHERIT) ? &sec_none_nih : &sec_none;
+ int res = -1;
+
+ if (!CreatePipe (&r, &w, sa, psize))
+ __seterrno ();
+ else
+ {
+ fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev);
+ fhs[1] = (fhandler_pipe *) build_fh_dev (*pipew_dev);
+
+ int binmode = mode & O_TEXT ?: O_BINARY;
+ fhs[0]->init (r, GENERIC_READ, binmode);
+ fhs[1]->init (w, GENERIC_WRITE, binmode);
+ if (mode & O_NOINHERIT)
+ {
+ fhs[0]->set_close_on_exec_flag (1);
+ fhs[1]->set_close_on_exec_flag (1);
+ }
+
+ fhs[0]->read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ fhs[0]->set_need_fork_fixup ();
+ ProtectHandle1 (fhs[0]->read_state, read_state);
+
+ res = 0;
+ fhs[0]->create_guard (sa);
+ if (wincap.has_unreliable_pipes ())
+ {
+ char buf[80];
+ int count = pipecount++; /* FIXME: Should this be InterlockedIncrement? */
+ __small_sprintf (buf, pipeid_fmt, myself->pid, count);
+ fhs[1]->writepipe_exists = CreateEvent (sa, TRUE, FALSE, buf);
+ fhs[0]->orig_pid = myself->pid;
+ fhs[0]->id = count;
+ }
+ }
+
+ syscall_printf ("%d = ([%p, %p], %d, %p)", res, fhs[0], fhs[1], psize, mode);
+ return res;
+}
+
+int
+fhandler_pipe::ioctl (unsigned int cmd, void *p)
+{
+ int n;
+
+ switch (cmd)
+ {
+ case FIONREAD:
+ if (get_device () == FH_PIPEW)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (!PeekNamedPipe (get_handle (), NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ break;
+ default:
+ return fhandler_base::ioctl (cmd, p);
+ break;
+ }
+ *(int *) p = n;
+ return 0;
+}
+
+extern "C" int
+pipe (int filedes[2])
+{
+ extern DWORD binmode;
+ fhandler_pipe *fhs[2];
+ int res = fhandler_pipe::create (fhs, 16384, (!binmode || binmode == O_BINARY)
+ ? O_BINARY : O_TEXT);
+ if (res == 0)
+ {
+ cygheap_fdnew fdin;
+ cygheap_fdnew fdout (fdin, false);
+ fdin = fhs[0];
+ fdout = fhs[1];
+ filedes[0] = fdin;
+ filedes[1] = fdout;
+ }
+
+ return res;
+}
+
+extern "C" int
+_pipe (int filedes[2], unsigned int psize, int mode)
+{
+ fhandler_pipe *fhs[2];
+ int res = fhandler_pipe::create (fhs, psize, mode);
+ /* This type of pipe is not interruptible so set the appropriate flag. */
+ if (!res)
+ {
+ cygheap_fdnew fdin;
+ cygheap_fdnew fdout (fdin, false);
+ fhs[0]->set_r_no_interrupt (1);
+ fdin = fhs[0];
+ fdout = fhs[1];
+ filedes[0] = fdin;
+ filedes[1] = fdout;
+ }
+
+ return res;
+}
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
index 4ecadda4e94..ad0a242cf2c 100644
--- a/winsup/cygwin/sigproc.cc
+++ b/winsup/cygwin/sigproc.cc
@@ -339,6 +339,7 @@ proc_subproc (DWORD what, DWORD val)
if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle,
SYNCHRONIZE | PROCESS_DUP_HANDLE, TRUE, 0))
system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid);
+ VerifyHandle (vchild->ppid_handle);
vchild->ppid = myself->pid;
vchild->uid = myself->uid;
vchild->gid = myself->gid;
@@ -726,17 +727,19 @@ sig_send (_pinfo *p, int sig, void *tls)
__seterrno ();
goto out;
}
+ VerifyHandle (hp);
for (int i = 0; !p->sendsig && i < 10000; i++)
low_priority_sleep (0);
if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0,
DUPLICATE_SAME_ACCESS) || !sendsig)
{
+ CloseHandle (hp);
sigproc_printf ("DuplicateHandle failed, %E");
__seterrno ();
goto out;
}
CloseHandle (hp);
- pack.wakeup = NULL;
+ VerifyHandle (sendsig);
}
sigproc_printf ("sendsig %p, pid %d, signal %d, its_me %d", sendsig, p->pid,
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
new file mode 100644
index 00000000000..653fdd5e1ae
--- /dev/null
+++ b/winsup/cygwin/spawn.cc
@@ -0,0 +1,1119 @@
+/* spawn.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 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 <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <process.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <ctype.h>
+#include "cygerrno.h"
+#include <sys/cygwin.h>
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "cygheap.h"
+#include "child_info.h"
+#include "shared_info.h"
+#include "pinfo.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "registry.h"
+#include "environ.h"
+#include "cygthread.h"
+
+#define LINE_BUF_CHUNK (CYG_MAX_PATH * 2)
+
+static suffix_info std_suffixes[] =
+{
+ suffix_info (".exe", 1), suffix_info ("", 1),
+ suffix_info (".com"), suffix_info (".cmd"),
+ suffix_info (".bat"), suffix_info (".dll"),
+ suffix_info (NULL)
+};
+
+HANDLE hExeced;
+DWORD dwExeced;
+
+/* Add .exe to PROG if not already present and see if that exists.
+ If not, return PROG (converted from posix to win32 rules if necessary).
+ The result is always BUF.
+
+ Returns (possibly NULL) suffix */
+
+static const char *
+perhaps_suffix (const char *prog, path_conv& buf)
+{
+ char *ext;
+
+ debug_printf ("prog '%s'", prog);
+ buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes);
+
+ if (!buf.exists () || buf.isdir ())
+ ext = NULL;
+ else if (buf.known_suffix)
+ ext = (char *) buf + (buf.known_suffix - buf.get_win32 ());
+ else
+ ext = strchr (buf, '\0');
+
+ debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext);
+ return ext;
+}
+
+/* Find an executable name, possibly by appending known executable
+ suffixes to it. The win32-translated name is placed in 'buf'.
+ Any found suffix is returned in known_suffix.
+
+ If the file is not found and !null_if_not_found then the win32 version
+ of name is placed in buf and returned. Otherwise the contents of buf
+ is undefined and NULL is returned. */
+
+const char * __stdcall
+find_exec (const char *name, path_conv& buf, const char *mywinenv,
+ unsigned opt, const char **known_suffix)
+{
+ const char *suffix = "";
+ debug_printf ("find_exec (%s)", name);
+ const char *retval = buf;
+ char tmp[CYG_MAX_PATH];
+ const char *posix = (opt & FE_NATIVE) ? NULL : name;
+ bool has_slash = strchr (name, '/');
+
+ /* Check to see if file can be opened as is first.
+ Win32 systems always check . first, but PATH may not be set up to
+ do this. */
+ if ((has_slash || opt & FE_CWD)
+ && (suffix = perhaps_suffix (name, buf)) != NULL)
+ {
+ if (posix && !has_slash)
+ {
+ tmp[0] = '.';
+ tmp[1] = '/';
+ strcpy (tmp + 2, name);
+ posix = tmp;
+ }
+ goto out;
+ }
+
+ win_env *winpath;
+ const char *path;
+ const char *posix_path;
+
+ /* Return the error condition if this is an absolute path or if there
+ is no PATH to search. */
+ if (strchr (name, '/') || strchr (name, '\\') ||
+ isdrive (name) ||
+ !(winpath = getwinenv (mywinenv)) ||
+ !(path = winpath->get_native ()) ||
+ *path == '\0')
+ goto errout;
+
+ debug_printf ("%s%s", mywinenv, path);
+
+ posix = (opt & FE_NATIVE) ? NULL : tmp;
+ posix_path = winpath->get_posix () - 1;
+ /* Iterate over the specified path, looking for the file with and
+ without executable extensions. */
+ do
+ {
+ posix_path++;
+ char *eotmp = strccpy (tmp, &path, ';');
+ /* An empty path or '.' means the current directory, but we've
+ already tried that. */
+ if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0')))
+ continue;
+
+ *eotmp++ = '\\';
+ strcpy (eotmp, name);
+
+ debug_printf ("trying %s", tmp);
+
+ if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
+ {
+ if (posix == tmp)
+ {
+ eotmp = strccpy (tmp, &posix_path, ':');
+ if (eotmp == tmp)
+ *eotmp++ = '.';
+ *eotmp++ = '/';
+ strcpy (eotmp, name);
+ }
+ goto out;
+ }
+ }
+ while (*path && *++path && (posix_path = strchr (posix_path, ':')));
+
+ errout:
+ posix = NULL;
+ /* Couldn't find anything in the given path.
+ Take the appropriate action based on null_if_not_found. */
+ if (opt & FE_NNF)
+ retval = NULL;
+ else if (opt & FE_NATIVE)
+ buf.check (name);
+ else
+ retval = name;
+
+ out:
+ if (posix)
+ buf.set_path (posix);
+ debug_printf ("%s = find_exec (%s)", (char *) buf, name);
+ if (known_suffix)
+ *known_suffix = suffix ?: strchr (buf, '\0');
+ return retval;
+}
+
+/* Utility for spawn_guts. */
+
+static HANDLE
+handle (int n, int direction)
+{
+ fhandler_base *fh = cygheap->fdtab[n];
+
+ if (!fh)
+ return INVALID_HANDLE_VALUE;
+ if (fh->get_close_on_exec ())
+ return INVALID_HANDLE_VALUE;
+ if (direction == 0)
+ return fh->get_handle ();
+ return fh->get_output_handle ();
+}
+
+int
+iscmd (const char *argv0, const char *what)
+{
+ int n;
+ n = strlen (argv0) - strlen (what);
+ if (n >= 2 && argv0[1] != ':')
+ return 0;
+ return n >= 0 && strcasematch (argv0 + n, what) &&
+ (n == 0 || isdirsep (argv0[n - 1]));
+}
+
+class linebuf
+{
+ public:
+ size_t ix;
+ char *buf;
+ size_t alloced;
+ linebuf () : ix (0), buf (NULL), alloced (0) {}
+ ~linebuf () {if (buf) free (buf);}
+ void add (const char *what, int len);
+ void add (const char *what) {add (what, strlen (what));}
+ void prepend (const char *what, int len);
+};
+
+void
+linebuf::add (const char *what, int len)
+{
+ size_t newix;
+ if ((newix = ix + len) >= alloced || !buf)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ }
+ memcpy (buf + ix, what, len);
+ ix = newix;
+ buf[ix] = '\0';
+}
+
+void
+linebuf::prepend (const char *what, int len)
+{
+ int buflen;
+ size_t newix;
+ if ((newix = ix + len) >= alloced)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ buf[ix] = '\0';
+ }
+ if ((buflen = strlen (buf)))
+ memmove (buf + len, buf, buflen + 1);
+ else
+ buf[newix] = '\0';
+ memcpy (buf, what, len);
+ ix = newix;
+}
+
+class av
+{
+ char **argv;
+ int calloced;
+ public:
+ int error;
+ int argc;
+ av (int ac, const char * const *av) : calloced (0), error (false), argc (ac)
+ {
+ argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *));
+ memcpy (argv, av, (argc + 1) * sizeof (char *));
+ }
+ ~av ()
+ {
+ if (argv)
+ {
+ for (int i = 0; i < calloced; i++)
+ if (argv[i])
+ cfree (argv[i]);
+ cfree (argv);
+ }
+ }
+ int unshift (const char *what, int conv = 0);
+ operator char **() {return argv;}
+ void all_calloced () {calloced = argc;}
+ void replace0_maybe (const char *arg0)
+ {
+ /* Note: Assumes that argv array has not yet been "unshifted" */
+ if (!calloced
+ && (argv[0] = cstrdup1 (arg0)))
+ calloced = true;
+ else
+ error = errno;
+ }
+ void dup_maybe (int i)
+ {
+ if (i >= calloced
+ && !(argv[i] = cstrdup1 (argv[i])))
+ error = errno;
+ }
+ void dup_all ()
+ {
+ for (int i = calloced; i < argc; i++)
+ if (!(argv[i] = cstrdup1 (argv[i])))
+ error = errno;
+ }
+};
+
+int
+av::unshift (const char *what, int conv)
+{
+ char **av;
+ av = (char **) crealloc (argv, (argc + 2) * sizeof (char *));
+ if (!av)
+ return 0;
+
+ argv = av;
+ memmove (argv + 1, argv, (argc + 1) * sizeof (char *));
+ char buf[CYG_MAX_PATH + 1];
+ if (conv)
+ {
+ cygwin_conv_to_posix_path (what, buf);
+ char *p = strchr (buf, '\0') - 4;
+ if (p > buf && strcasematch (p, ".exe"))
+ *p = '\0';
+ what = buf;
+ }
+ if (!(*argv = cstrdup1 (what)))
+ error = errno;
+ argc++;
+ calloced++;
+ return 1;
+}
+
+struct pthread_cleanup
+{
+ _sig_func_ptr oldint;
+ _sig_func_ptr oldquit;
+ sigset_t oldmask;
+ pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask (0) {}
+};
+
+static void
+do_cleanup (void *args)
+{
+# define cleanup ((pthread_cleanup *) args)
+ if (cleanup->oldint)
+ signal (SIGINT, cleanup->oldint);
+ if (cleanup->oldquit)
+ signal (SIGQUIT, cleanup->oldquit);
+ if (cleanup->oldmask)
+ sigprocmask (SIG_SETMASK, &(cleanup->oldmask), NULL);
+# undef cleanup
+}
+
+
+static int __stdcall
+spawn_guts (const char * prog_arg, const char *const *argv,
+ const char *const envp[], int mode)
+{
+ bool rc;
+ pid_t cygpid;
+
+ MALLOC_CHECK;
+
+ if (prog_arg == NULL)
+ {
+ syscall_printf ("prog_arg is NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ syscall_printf ("spawn_guts (%d, %.9500s)", mode, prog_arg);
+
+ if (argv == NULL)
+ {
+ syscall_printf ("argv is NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ path_conv real_path;
+
+ linebuf one_line;
+
+ STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+
+ child_info_spawn ciresrv;
+ si.lpReserved2 = (LPBYTE) &ciresrv;
+ si.cbReserved2 = sizeof (ciresrv);
+
+ DWORD chtype;
+ if (mode != _P_OVERLAY)
+ chtype = PROC_SPAWN;
+ else
+ chtype = PROC_EXEC;
+
+ HANDLE subproc_ready;
+ if (chtype != PROC_EXEC)
+ subproc_ready = NULL;
+ else
+ {
+ subproc_ready = CreateEvent (&sec_all, TRUE, FALSE, NULL);
+ ProtectHandleINH (subproc_ready);
+ }
+
+ init_child_info (chtype, &ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1,
+ subproc_ready);
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &ciresrv.parent, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("couldn't create handle to myself for child, %E");
+ return -1;
+ }
+
+ VerifyHandle (ciresrv.parent);
+ ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info));
+ ciresrv.moreinfo->old_title = NULL;
+
+ /* CreateProcess takes one long string that is the command line (sigh).
+ We need to quote any argument that has whitespace or embedded "'s. */
+
+ int ac;
+ for (ac = 0; argv[ac]; ac++)
+ /* nothing */;
+
+ av newargv (ac, argv);
+
+ int null_app_name = 0;
+ if (ac == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
+ (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
+ {
+ real_path.check (prog_arg);
+ one_line.add ("\"");
+ if (!real_path.error)
+ one_line.add (real_path);
+ else
+ one_line.add (argv[0]);
+ one_line.add ("\"");
+ one_line.add (" ");
+ one_line.add (argv[1]);
+ one_line.add (" ");
+ one_line.add (argv[2]);
+ strcpy (real_path, argv[0]);
+ null_app_name = 1;
+ goto skip_arg_parsing;
+ }
+
+ const char *ext;
+ if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ MALLOC_CHECK;
+
+ /* If the file name ends in either .exe, .com, .bat, or .cmd we assume
+ that it is NOT a script file */
+ while (*ext == '\0')
+ {
+ HANDLE hnd = CreateFile (real_path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ DWORD done;
+
+ char buf[2 * CYG_MAX_PATH + 1];
+ buf[0] = buf[1] = buf[2] = buf[sizeof (buf) - 1] = '\0';
+ if (!ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
+ {
+ CloseHandle (hnd);
+ __seterrno ();
+ return -1;
+ }
+
+ CloseHandle (hnd);
+
+ if (buf[0] == 'M' && buf[1] == 'Z')
+ break;
+
+ debug_printf ("%s is a script", (char *) real_path);
+
+ char *pgm, *arg1;
+
+ if (buf[0] != '#' || buf[1] != '!')
+ {
+ pgm = (char *) "/bin/sh";
+ arg1 = NULL;
+ }
+ else
+ {
+ char *ptr;
+ pgm = buf + 2;
+ pgm += strspn (pgm, " \t");
+ for (ptr = pgm, arg1 = NULL;
+ *ptr && *ptr != '\r' && *ptr != '\n';
+ ptr++)
+ if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
+ {
+ /* Null terminate the initial command and step over
+ any additional white space. If we've hit the
+ end of the line, exit the loop. Otherwise, we've
+ found the first argument. Position the current
+ pointer on the last known white space. */
+ *ptr = '\0';
+ char *newptr = ptr + 1;
+ newptr += strspn (newptr, " \t");
+ if (!*newptr || *newptr == '\r' || *newptr == '\n')
+ break;
+ arg1 = newptr;
+ ptr = newptr - 1;
+ }
+
+ *ptr = '\0';
+ }
+
+ /* Replace argv[0] with the full path to the script if this is the
+ first time through the loop. */
+ newargv.replace0_maybe (prog_arg);
+
+ /* pointers:
+ * pgm interpreter name
+ * arg1 optional string
+ */
+ if (arg1)
+ newargv.unshift (arg1);
+
+ /* FIXME: This should not be using FE_NATIVE. It should be putting
+ the posix path on the argv list. */
+ find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext);
+ newargv.unshift (real_path, 1);
+ }
+
+ if (real_path.iscygexec ())
+ newargv.dup_all ();
+ else
+ {
+ for (int i = 0; i < newargv.argc; i++)
+ {
+ char *p = NULL;
+ const char *a;
+
+ newargv.dup_maybe (i);
+ a = i ? newargv[i] : (char *) real_path;
+ int len = strlen (a);
+ if (len != 0 && !strpbrk (a, " \t\n\r\""))
+ one_line.add (a, len);
+ else
+ {
+ one_line.add ("\"", 1);
+ /* Handle embedded special characters " and \.
+ A " is always preceded by a \.
+ A \ is not special unless it precedes a ". If it does,
+ then all preceding \'s must be doubled to avoid having
+ the Windows command line parser interpret the \ as quoting
+ the ". This rule applies to a string of \'s before the end
+ of the string, since cygwin/windows uses a " to delimit the
+ argument. */
+ for (; (p = strpbrk (a, "\"\\")); a = ++p)
+ {
+ one_line.add (a, p - a);
+ /* Find length of string of backslashes */
+ int n = strspn (p, "\\");
+ if (!n)
+ one_line.add ("\\\"", 2); /* No backslashes, so it must be a ".
+ The " has to be protected with a backslash. */
+ else
+ {
+ one_line.add (p, n); /* Add the run of backslashes */
+ /* Need to double up all of the preceding
+ backslashes if they precede a quote or EOS. */
+ if (!p[n] || p[n] == '"')
+ one_line.add (p, n);
+ p += n - 1; /* Point to last backslash */
+ }
+ }
+ if (*a)
+ one_line.add (a);
+ one_line.add ("\"", 1);
+ }
+ MALLOC_CHECK;
+ one_line.add (" ", 1);
+ MALLOC_CHECK;
+ }
+
+ MALLOC_CHECK;
+ if (one_line.ix)
+ one_line.buf[one_line.ix - 1] = '\0';
+ else
+ one_line.add ("", 1);
+ MALLOC_CHECK;
+
+ if (one_line.ix > 32767)
+ {
+ debug_printf ("Command line too long (>32K), return E2BIG");
+ set_errno (E2BIG);
+ return -1;
+ }
+ }
+
+ char *envblock;
+ newargv.all_calloced ();
+ if (newargv.error)
+ {
+ set_errno (newargv.error);
+ return -1;
+ }
+
+ ciresrv.moreinfo->argc = newargv.argc;
+ ciresrv.moreinfo->argv = newargv;
+ ciresrv.hexec_proc = hexec_proc;
+
+ if (mode != _P_OVERLAY ||
+ !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc,
+ &ciresrv.moreinfo->myself_pinfo, 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ ciresrv.moreinfo->myself_pinfo = NULL;
+ else
+ VerifyHandle (ciresrv.moreinfo->myself_pinfo);
+
+ skip_arg_parsing:
+ PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = handle (0, 0); /* Get input handle */
+ si.hStdOutput = handle (1, 1); /* Get output handle */
+ si.hStdError = handle (2, 1); /* Get output handle */
+ si.cb = sizeof (si);
+
+ int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc);
+
+ if (mode == _P_DETACH || !set_console_state_for_spawn ())
+ flags |= DETACHED_PROCESS;
+ if (mode != _P_OVERLAY)
+ flags |= CREATE_SUSPENDED;
+
+ /* Some file types (currently only sockets) need extra effort in the
+ parent after CreateProcess and before copying the datastructures
+ to the child. So we have to start the child in suspend state,
+ unfortunately, to avoid a race condition. */
+ if (cygheap->fdtab.need_fixup_before ())
+ flags |= CREATE_SUSPENDED;
+
+
+ const char *runpath = null_app_name ? NULL : (const char *) real_path;
+
+ syscall_printf ("null_app_name %d (%s, %.9500s)", null_app_name, runpath, one_line.buf);
+
+ void *newheap;
+ /* Preallocated buffer for `sec_user' call */
+ char sa_buf[1024];
+
+ cygbench ("spawn-guts");
+
+ cygheap->fdtab.set_file_pointers_for_exec ();
+ cygheap->user.deimpersonate ();
+ /* When ruid != euid we create the new process under the current original
+ account and impersonate in child, this way maintaining the different
+ effective vs. real ids.
+ FIXME: If ruid != euid and ruid != saved_uid we currently give
+ up on ruid. The new process will have ruid == euid. */
+ if (!cygheap->user.issetuid ()
+ || (cygheap->user.saved_uid == cygheap->user.real_uid
+ && cygheap->user.saved_gid == cygheap->user.real_gid
+ && !cygheap->user.groups.issetgroups ()))
+ {
+ PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf);
+ ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
+ real_path.iscygexec ());
+ newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcess (runpath, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ sec_attribs, /* process security attrs */
+ sec_attribs, /* thread security attrs */
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock, /* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+ }
+ else
+ {
+ PSID sid = cygheap->user.sid ();
+ /* Give access to myself */
+ if (mode == _P_OVERLAY)
+ myself.set_acl();
+
+ /* Set security attributes with sid */
+ PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, sid);
+
+ /* allow the child to interact with our window station/desktop */
+ HANDLE hwst, hdsk;
+ SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
+ DWORD n;
+ char wstname[1024];
+ char dskname[1024];
+
+ hwst = GetProcessWindowStation ();
+ SetUserObjectSecurity (hwst, &dsi, get_null_sd ());
+ GetUserObjectInformation (hwst, UOI_NAME, wstname, 1024, &n);
+ hdsk = GetThreadDesktop (GetCurrentThreadId ());
+ SetUserObjectSecurity (hdsk, &dsi, get_null_sd ());
+ GetUserObjectInformation (hdsk, UOI_NAME, dskname, 1024, &n);
+ strcat (wstname, "\\");
+ strcat (wstname, dskname);
+ si.lpDesktop = wstname;
+
+ ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc,
+ real_path.iscygexec ());
+ newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ());
+ rc = CreateProcessAsUser (cygheap->user.token (),
+ runpath, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ sec_attribs, /* process security attrs */
+ sec_attribs, /* thread security attrs */
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock, /* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+ }
+
+ /* Restore impersonation. In case of _P_OVERLAY this isn't
+ allowed since it would overwrite child data. */
+ if (mode != _P_OVERLAY || !rc)
+ cygheap->user.reimpersonate ();
+
+ MALLOC_CHECK;
+ if (envblock)
+ free (envblock);
+ MALLOC_CHECK;
+
+ /* Set errno now so that debugging messages from it appear before our
+ final debugging message [this is a general rule for debugging
+ messages]. */
+ if (!rc)
+ {
+ __seterrno ();
+ syscall_printf ("CreateProcess failed, %E");
+ if (subproc_ready)
+ ForceCloseHandle (subproc_ready);
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
+ return -1;
+ }
+
+ /* FIXME: There is a small race here */
+
+ int res;
+ pthread_cleanup cleanup;
+ pthread_cleanup_push (do_cleanup, (void *) &cleanup);
+ if (mode == _P_SYSTEM)
+ {
+ sigset_t child_block;
+ cleanup.oldint = signal (SIGINT, SIG_IGN);
+ cleanup.oldquit = signal (SIGQUIT, SIG_IGN);
+ sigemptyset (&child_block);
+ sigaddset (&child_block, SIGCHLD);
+ (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask);
+ }
+
+ /* Fixup the parent datastructure if needed and resume the child's
+ main thread. */
+ if (!cygheap->fdtab.need_fixup_before ())
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0);
+ else
+ {
+ cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
+ cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1);
+ if (mode == _P_OVERLAY)
+ {
+ ResumeThread (pi.hThread);
+ cygthread::terminate ();
+ }
+ }
+
+ if (mode != _P_OVERLAY)
+ cygpid = cygwin_pid (pi.dwProcessId);
+ else
+ cygpid = myself->pid;
+
+ /* We print the original program name here so the user can see that too. */
+ syscall_printf ("%d = spawn_guts (%s, %.9500s)",
+ rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf);
+
+ /* Name the handle similarly to proc_subproc. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+
+ if (mode == _P_OVERLAY)
+ {
+ /* These are both duplicated in the child code. We do this here,
+ primarily for strace. */
+ strace.execing = 1;
+ hExeced = pi.hProcess;
+ dwExeced = pi.dwProcessId;
+ strcpy (myself->progname, real_path);
+ close_all_files ();
+ }
+ else
+ {
+ myself->set_has_pgid_children ();
+ ProtectHandle (pi.hThread);
+ pinfo child (cygpid, PID_IN_USE);
+ if (!child)
+ {
+ syscall_printf ("pinfo failed");
+ if (get_errno () != ENOMEM)
+ set_errno (EAGAIN);
+ res = -1;
+ goto out;
+ }
+ child->dwProcessId = pi.dwProcessId;
+ child->hProcess = pi.hProcess;
+ if (!child.remember ())
+ {
+ syscall_printf ("process table full");
+ set_errno (EAGAIN);
+ res = -1;
+ goto out;
+ }
+
+ strcpy (child->progname, real_path);
+ /* FIXME: This introduces an unreferenced, open handle into the child.
+ The purpose is to keep the pid shared memory open so that all of
+ the fields filled out by child.remember do not disappear and so there
+ is not a brief period during which the pid is not available.
+ However, we should try to find another way to do this eventually. */
+ (void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess,
+ NULL, 0, 0, DUPLICATE_SAME_ACCESS);
+ /* Start the child running */
+ ResumeThread (pi.hThread);
+ }
+
+ ForceCloseHandle (pi.hThread);
+
+ sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
+
+ bool exited;
+
+ res = 0;
+ exited = false;
+ MALLOC_CHECK;
+ if (mode == _P_OVERLAY)
+ {
+ int nwait = 3;
+ HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, subproc_ready};
+ for (int i = 0; i < 100; i++)
+ {
+ switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ sigproc_printf ("subprocess exited");
+ DWORD exitcode;
+ if (!GetExitCodeProcess (pi.hProcess, &exitcode))
+ exitcode = 1;
+ res |= exitcode;
+ exited = true;
+ break;
+ case WAIT_OBJECT_0 + 1:
+ sigproc_printf ("signal arrived");
+ reset_signal_arrived ();
+ continue;
+ case WAIT_OBJECT_0 + 2:
+ if (my_parent_is_alive ())
+ res |= EXIT_REPARENTING;
+ else if (!myself->ppid_handle)
+ {
+ nwait = 2;
+ sigproc_terminate ();
+ continue;
+ }
+ break;
+ case WAIT_FAILED:
+ system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
+ nwait, myself->pid, myself->dwProcessId);
+ system_printf ("waitbuf[0] %p %d", waitbuf[0],
+ WaitForSingleObject (waitbuf[0], 0));
+ system_printf ("waitbuf[1] %p = %d", waitbuf[1],
+ WaitForSingleObject (waitbuf[1], 0));
+ system_printf ("waitbuf[w] %p = %d", waitbuf[2],
+ WaitForSingleObject (waitbuf[2], 0));
+ set_errno (ECHILD);
+ try_to_debug ();
+ return -1;
+ }
+ break;
+ }
+
+ ForceCloseHandle (subproc_ready);
+
+ sigproc_printf ("res = %x", res);
+
+ if (res & EXIT_REPARENTING)
+ {
+ /* Try to reparent child process.
+ * Make handles to child available to parent process and exit with
+ * EXIT_REPARENTING status. Wait() syscall in parent will then wait
+ * for newly created child.
+ */
+ HANDLE oldh = myself->hProcess;
+ HANDLE h = myself->ppid_handle;
+ sigproc_printf ("parent handle %p", h);
+ int rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p",
+ rc, oldh, myself->hProcess);
+ VerifyHandle (myself->hProcess);
+ if (!rc && my_parent_is_alive ())
+ {
+ system_printf ("Reparent failed, parent handle %p, %E", h);
+ system_printf ("my dwProcessId %d, myself->dwProcessId %d",
+ GetCurrentProcessId (), myself->dwProcessId);
+ system_printf ("old hProcess %p, hProcess %p", oldh, myself->hProcess);
+ }
+ }
+
+ }
+
+ MALLOC_CHECK;
+
+ switch (mode)
+ {
+ case _P_OVERLAY:
+ ForceCloseHandle1 (pi.hProcess, childhProc);
+ proc_terminate ();
+ myself->exit (res, 1);
+ break;
+ case _P_WAIT:
+ case _P_SYSTEM:
+ waitpid (cygpid, (int *) &res, 0);
+ break;
+ case _P_DETACH:
+ res = 0; /* Lose all memory of this child. */
+ break;
+ case _P_NOWAIT:
+ case _P_NOWAITO:
+ case _P_VFORK:
+ res = cygpid;
+ break;
+ default:
+ break;
+ }
+
+out:
+ pthread_cleanup_pop (1);
+ return (int) res;
+}
+
+extern "C" int
+cwait (int *result, int pid, int)
+{
+ return waitpid (pid, result, 0);
+}
+
+/*
+ * Helper function for spawn runtime calls.
+ * Doesn't search the path.
+ */
+
+extern "C" int
+spawnve (int mode, const char *path, const char *const *argv,
+ const char *const *envp)
+{
+ int ret;
+#ifdef NEWVFORK
+ vfork_save *vf = vfork_storage.val ();
+
+ if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
+ mode = _P_NOWAIT;
+ else
+ vf = NULL;
+#endif
+
+ syscall_printf ("spawnve (%s, %s, %x)", path, argv[0], envp);
+
+ switch (mode)
+ {
+ case _P_OVERLAY:
+ /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
+ /* Just act as an exec if _P_OVERLAY set. */
+ spawn_guts (path, argv, envp, mode);
+ /* Errno should be set by spawn_guts. */
+ ret = -1;
+ break;
+ case _P_VFORK:
+ case _P_NOWAIT:
+ case _P_NOWAITO:
+ case _P_WAIT:
+ case _P_DETACH:
+ case _P_SYSTEM:
+ subproc_init ();
+ ret = spawn_guts (path, argv, envp, mode);
+#ifdef NEWVFORK
+ if (vf)
+ {
+ if (ret > 0)
+ {
+ debug_printf ("longjmping due to vfork");
+ vf->restore_pid (ret);
+ }
+ }
+#endif
+ break;
+ default:
+ set_errno (EINVAL);
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * spawn functions as implemented in the MS runtime library.
+ * Most of these based on (and copied from) newlib/libc/posix/execXX.c
+ */
+
+extern "C" int
+spawnl (int mode, const char *path, const char *arg0, ...)
+{
+ int i;
+ va_list args;
+ const char *argv[256];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ va_end (args);
+
+ return spawnve (mode, path, (char * const *) argv, cur_environ ());
+}
+
+extern "C" int
+spawnle (int mode, const char *path, const char *arg0, ...)
+{
+ int i;
+ va_list args;
+ const char * const *envp;
+ const char *argv[256];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ envp = va_arg (args, const char * const *);
+ va_end (args);
+
+ return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
+}
+
+extern "C" int
+spawnlp (int mode, const char *path, const char *arg0, ...)
+{
+ int i;
+ va_list args;
+ const char *argv[256];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ va_end (args);
+
+ return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
+}
+
+extern "C" int
+spawnlpe (int mode, const char *path, const char *arg0, ...)
+{
+ int i;
+ va_list args;
+ const char * const *envp;
+ const char *argv[256];
+
+ va_start (args, arg0);
+ argv[0] = arg0;
+ i = 1;
+
+ do
+ argv[i] = va_arg (args, const char *);
+ while (argv[i++] != NULL);
+
+ envp = va_arg (args, const char * const *);
+ va_end (args);
+
+ return spawnvpe (mode, path, (char * const *) argv, envp);
+}
+
+extern "C" int
+spawnv (int mode, const char *path, const char * const *argv)
+{
+ return spawnve (mode, path, argv, cur_environ ());
+}
+
+extern "C" int
+spawnvp (int mode, const char *path, const char * const *argv)
+{
+ return spawnvpe (mode, path, argv, cur_environ ());
+}
+
+extern "C" int
+spawnvpe (int mode, const char *file, const char * const *argv,
+ const char * const *envp)
+{
+ path_conv buf;
+ return spawnve (mode, find_exec (file, buf), argv, envp);
+}
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
new file mode 100644
index 00000000000..9e2d4dd8f41
--- /dev/null
+++ b/winsup/cygwin/syscalls.cc
@@ -0,0 +1,2996 @@
+/* syscalls.cc: syscalls
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 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. */
+
+#define fstat __FOOfstat__
+#define lstat __FOOlstat__
+#define stat __FOOstat__
+#define _close __FOO_close__
+#define _lseek __FOO_lseek__
+#define _open __FOO_open__
+#define _read __FOO_read__
+#define _write __FOO_write__
+#define _open64 __FOO_open64__
+#define _lseek64 __FOO_lseek64__
+#define _fstat64 __FOO_fstat64__
+
+#include "winsup.h"
+#include <sys/stat.h>
+#include <sys/vfs.h> /* needed for statfs */
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h>
+#include <utmp.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <winnls.h>
+#include <wininet.h>
+#include <lmcons.h> /* for UNLEN */
+#include <rpc.h>
+
+#undef fstat
+#undef lstat
+#undef stat
+
+#include <cygwin/version.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#define NEED_VFORK
+#include "perthread.h"
+#include "pwdgrp.h"
+#include "cpuid.h"
+#include "registry.h"
+
+#undef _close
+#undef _lseek
+#undef _open
+#undef _read
+#undef _write
+#undef _open64
+#undef _lseek64
+#undef _fstat64
+
+SYSTEM_INFO system_info;
+
+static int __stdcall mknod_worker (const char *, mode_t, mode_t, _major_t,
+ _minor_t);
+
+static int __stdcall stat_worker (const char *name, struct __stat64 *buf,
+ int nofollow) __attribute__ ((regparm (3)));
+
+/* Close all files and process any queued deletions.
+ Lots of unix style applications will open a tmp file, unlink it,
+ but never call close. This function is called by _exit to
+ ensure we don't leave any such files lying around. */
+
+void __stdcall
+close_all_files (void)
+{
+ cygheap->fdtab.lock ();
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL)
+ {
+#ifdef DEBUGGING
+ debug_printf ("closing fd %d", i);
+#endif
+ fh->close ();
+ cygheap->fdtab.release (i);
+ }
+
+ if (cygheap->ctty)
+ cygheap->close_ctty ();
+
+ cygheap->fdtab.unlock ();
+ user_shared->delqueue.process_queue ();
+}
+
+int
+dup (int fd)
+{
+ return cygheap->fdtab.dup2 (fd, cygheap_fdnew ());
+}
+
+int
+dup2 (int oldfd, int newfd)
+{
+ return cygheap->fdtab.dup2 (oldfd, newfd);
+}
+
+extern "C" int
+unlink (const char *ourname)
+{
+ int res = -1;
+ DWORD devn;
+
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ goto done;
+ }
+
+ if ((devn = win32_name.get_devn ()) == FH_PROC || devn == FH_REGISTRY
+ || devn == FH_PROCESS)
+ {
+ set_errno (EROFS);
+ goto done;
+ }
+
+ syscall_printf ("_unlink (%s)", win32_name.get_win32 ());
+
+ if (!win32_name.exists ())
+ {
+ syscall_printf ("unlinking a nonexistent file");
+ set_errno (ENOENT);
+ goto done;
+ }
+ else if (win32_name.isdir ())
+ {
+ syscall_printf ("unlinking a directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ /* Windows won't check the directory mode, so we do that ourselves. */
+ if (!writable_directory (win32_name))
+ {
+ syscall_printf ("non-writable directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ bool setattrs;
+ if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
+ setattrs = false;
+ else
+ {
+ /* Allow us to delete even if read-only */
+ setattrs = SetFileAttributes (win32_name,
+ (DWORD) win32_name
+ & ~(FILE_ATTRIBUTE_READONLY
+ | FILE_ATTRIBUTE_SYSTEM));
+ }
+ /* Attempt to use "delete on close" semantics to handle removing
+ a file which may be open. */
+ if (wincap.has_delete_on_close ())
+ {
+ HANDLE h;
+ h = CreateFile (win32_name, 0, FILE_SHARE_READ, &sec_none_nih,
+ OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ if (wincap.has_hard_links () && setattrs)
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+ BOOL res = CloseHandle (h);
+ syscall_printf ("%d = CloseHandle (%p)", res, h);
+ if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES
+ || !win32_name.isremote ())
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) succeeded");
+ goto ok;
+ }
+ else
+ {
+ syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) failed");
+ if (setattrs)
+ SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM));
+ }
+ }
+ }
+
+ /* Try a delete with attributes reset */
+ if (DeleteFile (win32_name))
+ {
+ syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded");
+ goto ok;
+ }
+
+ DWORD lasterr;
+ lasterr = GetLastError ();
+
+ (void) SetFileAttributes (win32_name, (DWORD) win32_name);
+
+ /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
+ violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
+ to simplify tests. */
+ if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
+ && !win32_name.isremote ())
+ lasterr = ERROR_SHARING_VIOLATION;
+
+ /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
+ violation, then queue the file for deletion when the process
+ exits. Otherwise, punt. */
+ if (lasterr != ERROR_SHARING_VIOLATION)
+ goto err;
+
+ syscall_printf ("couldn't delete file, err %d", lasterr);
+
+ /* Add file to the "to be deleted" queue. */
+ user_shared->delqueue.queue_file (win32_name);
+
+ /* Success condition. */
+ ok:
+ res = 0;
+ goto done;
+
+ /* Error condition. */
+ err:
+ __seterrno ();
+ res = -1;
+
+ done:
+ syscall_printf ("%d = unlink (%s)", res, ourname);
+ return res;
+}
+
+extern "C" int
+_remove_r (struct _reent *, const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" int
+remove (const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" pid_t
+getpid ()
+{
+ return myself->pid;
+}
+
+extern "C" pid_t
+_getpid_r (struct _reent *)
+{
+ return getpid ();
+}
+
+/* getppid: POSIX 4.1.1.1 */
+extern "C" pid_t
+getppid ()
+{
+ return myself->ppid;
+}
+
+/* setsid: POSIX 4.3.2.1 */
+extern "C" pid_t
+setsid (void)
+{
+#ifdef NEWVFORK
+ vfork_save *vf = vfork_storage.val ();
+ /* This is a horrible, horrible kludge */
+ if (vf && vf->pid < 0)
+ {
+ pid_t pid = fork ();
+ if (pid > 0)
+ {
+ syscall_printf ("longjmping due to vfork");
+ vf->restore_pid (pid);
+ }
+ /* assuming that fork was successful */
+ }
+#endif
+
+ if (myself->pgid == myself->pid)
+ syscall_printf ("hmm. pgid %d pid %d", myself->pgid, myself->pid);
+ else
+ {
+ if (myself->ctty >= 0 && cygheap->open_fhs <= 0)
+ {
+ syscall_printf ("freeing console");
+ FreeConsole ();
+ }
+ myself->ctty = -1;
+ myself->sid = getpid ();
+ myself->pgid = getpid ();
+ syscall_printf ("sid %d, pgid %d, ctty %d, open_fhs %d", myself->sid,
+ myself->pgid, myself->ctty, cygheap->open_fhs);
+ if (cygheap->ctty)
+ cygheap->close_ctty ();
+ return myself->sid;
+ }
+
+ set_errno (EPERM);
+ return -1;
+}
+
+extern "C" pid_t
+getsid (pid_t pid)
+{
+ pid_t res;
+ if (!pid)
+ res = myself->sid;
+ else
+ {
+ pinfo p (pid);
+ if (p)
+ res = p->sid;
+ else
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ }
+ return res;
+}
+
+extern "C" ssize_t
+read (int fd, void *ptr, size_t len)
+{
+ const iovec iov =
+ {
+ iov_base: ptr,
+ iov_len: len
+ };
+
+ return readv (fd, &iov, 1);
+}
+
+extern "C" ssize_t _read (int, void *, size_t)
+ __attribute__ ((alias ("read")));
+
+extern "C" ssize_t
+write (int fd, const void *ptr, size_t len)
+{
+ const struct iovec iov =
+ {
+ iov_base: (void *) ptr, // const_cast
+ iov_len: len
+ };
+
+ return writev (fd, &iov, 1);
+}
+
+extern "C" ssize_t _write (int fd, const void *ptr, size_t len)
+ __attribute__ ((alias ("write")));
+
+extern "C" ssize_t
+readv (int fd, const struct iovec *const iov, const int iovcnt)
+{
+ extern int sigcatchers;
+ const int e = get_errno ();
+
+ int res = -1;
+
+ const ssize_t tot = check_iovec_for_read (iov, iovcnt);
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ while (1)
+ {
+ sig_dispatch_pending ();
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ break;
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY)
+ {
+ set_errno (EBADF);
+ break;
+ }
+
+ DWORD wait = cfd->is_nonblocking () ? 0 : INFINITE;
+
+ /* Could block, so let user know we at least got here. */
+ syscall_printf ("readv (%d, %p, %d) %sblocking, sigcatchers %d",
+ fd, iov, iovcnt, wait ? "" : "non", sigcatchers);
+
+ if (wait && (!cfd->is_slow () || cfd->get_r_no_interrupt ()))
+ debug_printf ("no need to call ready_for_read");
+ else if (!cfd->ready_for_read (fd, wait))
+ {
+ res = -1;
+ goto out;
+ }
+
+ /* FIXME: This is not thread safe. We need some method to
+ ensure that an fd, closed in another thread, aborts I/O
+ operations. */
+ if (!cfd.isopen ())
+ break;
+
+ /* Check to see if this is a background read from a "tty",
+ sending a SIGTTIN, if appropriate */
+ res = cfd->bg_check (SIGTTIN);
+
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYIN;
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+ res = cfd->readv (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYIN;
+ }
+
+ out:
+ if (res >= 0 || get_errno () != EINTR || !call_signal_handler_now ())
+ break;
+ set_errno (e);
+ }
+
+done:
+ syscall_printf ("%d = readv (%d, %p, %d), errno %d", res, fd, iov, iovcnt,
+ get_errno ());
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" ssize_t
+writev (const int fd, const struct iovec *const iov, const int iovcnt)
+{
+ int res = -1;
+ sig_dispatch_pending ();
+ const ssize_t tot = check_iovec_for_write (iov, iovcnt);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto done;
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY)
+ {
+ set_errno (EBADF);
+ goto done;
+ }
+
+ /* Could block, so let user know we at least got here. */
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+ else
+ syscall_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+
+ res = cfd->bg_check (SIGTTOU);
+
+ if (res > (int) bg_eof)
+ {
+ myself->process_state |= PID_TTYOU;
+ res = cfd->writev (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYOU;
+ }
+
+done:
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+ else
+ syscall_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+
+ MALLOC_CHECK;
+ return res;
+}
+
+/* _open */
+/* newlib's fcntl.h defines _open as taking variable args so we must
+ correspond. The third arg if it exists is: mode_t mode. */
+extern "C" int
+open (const char *unix_path, int flags, ...)
+{
+ int res = -1;
+ va_list ap;
+ mode_t mode = 0;
+ sig_dispatch_pending ();
+
+ syscall_printf ("open (%s, %p)", unix_path, flags);
+ if (!check_null_empty_str_errno (unix_path))
+ {
+ /* check for optional mode argument */
+ va_start (ap, flags);
+ mode = va_arg (ap, mode_t);
+ va_end (ap);
+
+ fhandler_base *fh;
+ cygheap_fdnew fd;
+
+ if (fd >= 0)
+ {
+ if (!(fh = build_fh_name (unix_path, NULL, PC_SYM_FOLLOW)))
+ res = -1; // errno already set
+ else if (fh->is_fs_special () && fh->device_access_denied (flags))
+ {
+ delete fh;
+ res = -1;
+ }
+ else if (!fh->open (flags, (mode & 07777) & ~cygheap->umask))
+ {
+ delete fh;
+ res = -1;
+ }
+ else
+ {
+ cygheap->fdtab[fd] = fh;
+ if ((res = fd) <= 2)
+ set_std_handle (res);
+ }
+ }
+ }
+
+ syscall_printf ("%d = open (%s, %p)", res, unix_path, flags);
+ return res;
+}
+
+extern "C" int _open (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" int _open64 (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" _off64_t
+lseek64 (int fd, _off64_t pos, int dir)
+{
+ _off64_t res;
+
+ if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END)
+ {
+ set_errno (EINVAL);
+ res = -1;
+ }
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->lseek (pos, dir);
+ else
+ res = -1;
+ }
+ syscall_printf ("%d = lseek (%d, %D, %d)", res, fd, pos, dir);
+
+ return res;
+}
+
+extern "C" int _lseek64 (int fd, _off64_t pos, int dir)
+ __attribute__ ((alias ("lseek64")));
+
+extern "C" _off_t
+lseek (int fd, _off_t pos, int dir)
+{
+ return lseek64 (fd, (_off64_t) pos, dir);
+}
+
+extern "C" _off_t _lseek (int, _off_t, int)
+ __attribute__ ((alias ("lseek")));
+
+extern "C" int
+close (int fd)
+{
+ int res;
+
+ syscall_printf ("close (%d)", fd);
+
+ MALLOC_CHECK;
+ cygheap_fdget cfd (fd, true);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ res = cfd->close ();
+ cfd.release ();
+ }
+
+ syscall_printf ("%d = close (%d)", res, fd);
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" int _close (int) __attribute__ ((alias ("close")));
+
+extern "C" int
+isatty (int fd)
+{
+ int res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = 0;
+ else
+ res = cfd->is_tty ();
+ syscall_printf ("%d = isatty (%d)", res, fd);
+ return res;
+}
+
+/* Under NT, try to make a hard link using backup API. If that
+ fails or we are Win 95, just copy the file.
+ FIXME: We should actually be checking partition type, not OS.
+ Under NTFS, we should support hard links. On FAT partitions,
+ we should just copy the file.
+*/
+
+extern "C" int
+link (const char *a, const char *b)
+{
+ int res = -1;
+ path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL);
+ path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL);
+ extern bool allow_winsymlinks;
+
+ if (real_a.error)
+ {
+ set_errno (real_a.error);
+ goto done;
+ }
+
+ if (real_b.error)
+ {
+ set_errno (real_b.case_clash ? ECASECLASH : real_b.error);
+ goto done;
+ }
+
+ if (real_b.exists ())
+ {
+ syscall_printf ("file '%s' exists?", (char *) real_b);
+ set_errno (EEXIST);
+ goto done;
+ }
+
+ if (real_b[strlen (real_b) - 1] == '.')
+ {
+ syscall_printf ("trailing dot, bailing out");
+ set_errno (EINVAL);
+ goto done;
+ }
+
+ /* Shortcut hack. */
+ char new_lnk_buf[CYG_MAX_PATH + 5];
+ if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash)
+ {
+ strcpy (new_lnk_buf, b);
+ strcat (new_lnk_buf, ".lnk");
+ b = new_lnk_buf;
+ real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL);
+ }
+ /* Try to make hard link first on Windows NT */
+ if (wincap.has_hard_links ())
+ {
+ if (CreateHardLinkA (real_b, real_a, NULL))
+ goto success;
+
+ HANDLE hFileSource;
+
+ WIN32_STREAM_ID StreamId;
+ DWORD dwBytesWritten;
+ LPVOID lpContext;
+ DWORD cbPathLen;
+ DWORD StreamSize;
+ WCHAR wbuf[CYG_MAX_PATH];
+
+ BOOL bSuccess;
+
+ hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/,
+ &sec_none_nih, // sa
+ OPEN_EXISTING, 0, NULL);
+
+ if (hFileSource == INVALID_HANDLE_VALUE)
+ {
+ syscall_printf ("cannot open source, %E");
+ goto docopy;
+ }
+
+ cbPathLen = sys_mbstowcs (wbuf, real_b, CYG_MAX_PATH) * sizeof (WCHAR);
+
+ StreamId.dwStreamId = BACKUP_LINK;
+ StreamId.dwStreamAttributes = 0;
+ StreamId.dwStreamNameSize = 0;
+ StreamId.Size.HighPart = 0;
+ StreamId.Size.LowPart = cbPathLen;
+
+ StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) +
+ StreamId.dwStreamNameSize;
+
+ lpContext = NULL;
+ /* Write the WIN32_STREAM_ID */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) &StreamId, // buffer to write
+ StreamSize, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext);
+
+ if (bSuccess)
+ {
+ /* write the buffer containing the path */
+ /* FIXME: BackupWrite sometimes traps if linkname is invalid.
+ Need to handle. */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) wbuf, // buffer to write
+ cbPathLen, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext
+ );
+
+ if (!bSuccess)
+ syscall_printf ("cannot write linkname, %E");
+
+ /* Free context */
+ BackupWrite (
+ hFileSource,
+ NULL, // buffer to write
+ 0, // number of bytes to write
+ &dwBytesWritten,
+ TRUE, // abort
+ FALSE, // don't process security
+ &lpContext);
+ }
+ else
+ syscall_printf ("cannot write streamId, %E");
+
+ CloseHandle (hFileSource);
+
+ if (!bSuccess)
+ goto docopy;
+
+ success:
+ res = 0;
+ if (!allow_winsymlinks && real_a.is_lnk_symlink ())
+ SetFileAttributes (real_b, (DWORD) real_a
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_READONLY);
+
+ goto done;
+ }
+docopy:
+ /* do this with a copy */
+ if (CopyFileA (real_a, real_b, 1))
+ res = 0;
+ else
+ __seterrno ();
+
+done:
+ syscall_printf ("%d = link (%s, %s)", res, a, b);
+ return res;
+}
+
+/* chown: POSIX 5.6.5.1 */
+/*
+ * chown () is only implemented for Windows NT. Under other operating
+ * systems, it is only a stub that always returns zero.
+ */
+static int
+chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid)
+{
+ int res;
+
+ if (check_null_empty_str_errno (name))
+ return -1;
+
+ if (!wincap.has_security ()) // real chown only works on NT
+ res = 0; // return zero (and do nothing) under Windows 9x
+ else
+ {
+ /* we need Win32 path names because of usage of Win32 API functions */
+ path_conv win32_path (PC_NONULLEMPTY, name, fmode);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ res = -1;
+ goto done;
+ }
+
+ /* FIXME: This makes chown on a device succeed always. Someday we'll want
+ to actually allow chown to work properly on devices. */
+ if (win32_path.is_auto_device () && !win32_path.issocket ())
+ {
+ res = 0;
+ goto done;
+ }
+
+ mode_t attrib = 0;
+ if (win32_path.isdir ())
+ attrib |= S_IFDIR;
+ res = get_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ &attrib);
+ if (!res)
+ res = set_file_attribute (win32_path.has_acls (), win32_path, uid,
+ gid, attrib);
+ if (res != 0 && (!win32_path.has_acls () || !allow_ntsec))
+ {
+ /* fake - if not supported, pretend we're like win95
+ where it just works */
+ res = 0;
+ }
+ }
+
+done:
+ syscall_printf ("%d = %schown (%s,...)",
+ res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
+ return res;
+}
+
+extern "C" int
+chown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+chown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ return chown_worker (name, PC_SYM_FOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+lchown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
+}
+
+extern "C" int
+lchown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ return chown_worker (name, PC_SYM_NOFOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+fchown32 (int fd, __uid32_t uid, __gid32_t gid)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchown (%d,...)", fd);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchown (%d,...) (no name)", fd);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchown (%d,...): calling chown_worker (%s,FOLLOW,...)",
+ fd, path);
+ return chown_worker (path, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+fchown (int fd, __uid16_t uid, __gid16_t gid)
+{
+ return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid));
+}
+
+/* umask: POSIX 5.3.3.1 */
+extern "C" mode_t
+umask (mode_t mask)
+{
+ mode_t oldmask;
+
+ oldmask = cygheap->umask;
+ cygheap->umask = mask & 0777;
+ return oldmask;
+}
+
+int
+chmod_device (path_conv& pc, mode_t mode)
+{
+ return mknod_worker (pc, pc.dev.mode & S_IFMT, mode, pc.dev.major, pc.dev.minor);
+}
+
+/* chmod: POSIX 5.6.4.1 */
+extern "C" int
+chmod (const char *path, mode_t mode)
+{
+ int res = -1;
+
+ path_conv win32_path (path);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ goto done;
+ }
+
+ /* FIXME: This makes chmod on a device succeed always. Someday we'll want
+ to actually allow chmod to work properly on devices. */
+ if (win32_path.is_auto_device ())
+ {
+ res = 0;
+ goto done;
+ }
+ if (win32_path.is_fs_special ())
+ {
+ res = chmod_device (win32_path, mode);
+ goto done;
+ }
+
+ if (!win32_path.exists ())
+ __seterrno ();
+ else
+ {
+ /* temporary erase read only bit, to be able to set file security */
+ SetFileAttributes (win32_path, (DWORD) win32_path & ~FILE_ATTRIBUTE_READONLY);
+
+ if (win32_path.isdir ())
+ mode |= S_IFDIR;
+ if (!set_file_attribute (win32_path.has_acls (), win32_path,
+ ILLEGAL_UID, ILLEGAL_GID, mode)
+ && allow_ntsec)
+ res = 0;
+
+ /* if the mode we want has any write bits set, we can't
+ be read only. */
+ if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
+ (DWORD) win32_path &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY;
+
+ if (!win32_path.is_lnk_symlink () && S_ISLNK (mode) || S_ISSOCK (mode))
+ (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM;
+
+ if (!SetFileAttributes (win32_path, win32_path))
+ __seterrno ();
+ else if (!allow_ntsec)
+ /* Correct NTFS security attributes have higher priority */
+ res = 0;
+ }
+
+done:
+ syscall_printf ("%d = chmod (%s, %p)", res, path, mode);
+ return res;
+}
+
+/* fchmod: P96 5.6.4.1 */
+
+extern "C" int
+fchmod (int fd, mode_t mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o) (no name)", fd, mode);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchmod (%d, 0%o): calling chmod (%s, 0%o)",
+ fd, mode, path, mode);
+ return chmod (path, mode);
+}
+
+static void
+stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst)
+{
+ dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff);
+ dst->st_ino = ((unsigned) (src->st_ino >> 32)) | (unsigned) src->st_ino;
+ dst->st_mode = src->st_mode;
+ dst->st_nlink = src->st_nlink;
+ dst->st_uid = src->st_uid;
+ dst->st_gid = src->st_gid;
+ dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff);
+ dst->st_size = src->st_size;
+ dst->st_atim = src->st_atim;
+ dst->st_mtim = src->st_mtim;
+ dst->st_ctim = src->st_ctim;
+ dst->st_blksize = src->st_blksize;
+ dst->st_blocks = src->st_blocks;
+}
+
+extern "C" int
+fstat64 (int fd, struct __stat64 *buf)
+{
+ int res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ memset (buf, 0, sizeof (struct __stat64));
+ res = cfd->fstat (buf);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, cfd->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = cfd->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ syscall_printf ("%d = fstat (%d, %p)", res, fd, buf);
+ return res;
+}
+
+extern "C" int
+_fstat64_r (struct _reent *ptr, int fd, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat64 (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+fstat (int fd, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = fstat64 (fd, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_fstat_r (struct _reent *ptr, int fd, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = fstat (fd, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* fsync: P96 6.6.1.1 */
+extern "C" int
+fsync (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fsync (%d)", fd);
+ return -1;
+ }
+
+ if (FlushFileBuffers (cfd->get_handle ()) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+/* sync: standards? */
+extern "C" int
+sync ()
+{
+ return 0;
+}
+
+suffix_info stat_suffixes[] =
+{
+ suffix_info ("", 1),
+ suffix_info (".exe", 1),
+ suffix_info (NULL)
+};
+
+/* Cygwin internal */
+static int __stdcall
+stat_worker (const char *name, struct __stat64 *buf, int nofollow)
+{
+ int res = -1;
+ fhandler_base *fh = NULL;
+
+ if (check_null_invalid_struct_errno (buf))
+ goto done;
+
+ fh = build_fh_name (name, NULL, (nofollow ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW)
+ | PC_FULL, stat_suffixes);
+
+ if (fh->error ())
+ {
+ debug_printf ("got %d error from build_fh_name", fh->error ());
+ set_errno (fh->error ());
+ }
+ else
+ {
+ debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow,
+ fh, (DWORD) *fh);
+ memset (buf, 0, sizeof (*buf));
+ res = fh->fstat (buf);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, fh->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = fh->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ done:
+ if (fh)
+ delete fh;
+ MALLOC_CHECK;
+ syscall_printf ("%d = (%s, %p)", res, name, buf);
+ return res;
+}
+
+extern "C" int
+stat64 (const char *name, struct __stat64 *buf)
+{
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 0);
+}
+
+extern "C" int
+_stat64_r (struct _reent *ptr, const char *name, struct __stat64 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat64 (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+extern "C" int
+stat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = stat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern "C" int
+_stat_r (struct _reent *ptr, const char *name, struct __stat32 *buf)
+{
+ int ret;
+
+ set_errno (0);
+ if ((ret = stat (name, buf)) == -1 && get_errno () != 0)
+ ptr->_errno = get_errno ();
+ return ret;
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat64 (const char *name, struct __stat64 *buf)
+{
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 1);
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = lstat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+int
+access_worker (path_conv& real_path, int flags, fhandler_base *fh)
+{
+ if (real_path.error)
+ {
+ set_errno (real_path.error);
+ return -1;
+ }
+
+ if (!real_path.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ if (!(flags & (R_OK | W_OK | X_OK)))
+ return 0;
+
+ if (real_path.is_fs_special ())
+ /* short circuit */;
+ else if (real_path.has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK))
+ {
+ set_errno (EACCES);
+ return -1;
+ }
+ else if (real_path.has_acls () && allow_ntsec)
+ return check_file_access (real_path, flags);
+
+ struct __stat64 st;
+ int r = fh ? fh->fstat (&st) : stat_worker (real_path, &st, 0);
+ if (r)
+ return -1;
+ r = -1;
+ if (flags & R_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IRUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IRGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IROTH))
+ goto done;
+ }
+ if (flags & W_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IWUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IWGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IWOTH))
+ goto done;
+ }
+ if (flags & X_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IXUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IXGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IXOTH))
+ goto done;
+ }
+ r = 0;
+done:
+ if (r)
+ set_errno (EACCES);
+ return r;
+}
+
+extern "C" int
+access (const char *fn, int flags)
+{
+ // flags were incorrectly specified
+ if (flags & ~(F_OK|R_OK|W_OK|X_OK))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ path_conv pc (fn, PC_SYM_FOLLOW | PC_FULL, stat_suffixes);
+ return access_worker (pc, flags);
+}
+
+extern "C" int
+rename (const char *oldpath, const char *newpath)
+{
+ int res = 0;
+ char *lnk_suffix = NULL;
+
+ path_conv real_old (oldpath, PC_SYM_NOFOLLOW);
+
+ if (real_old.error)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_old.error);
+ return -1;
+ }
+
+ path_conv real_new (newpath, PC_SYM_NOFOLLOW);
+
+ /* Shortcut hack. */
+ char new_lnk_buf[CYG_MAX_PATH + 5];
+ if (real_old.is_lnk_symlink () && !real_new.error && !real_new.case_clash)
+ {
+ strcpy (new_lnk_buf, newpath);
+ strcat (new_lnk_buf, ".lnk");
+ newpath = new_lnk_buf;
+ real_new.check (newpath, PC_SYM_NOFOLLOW);
+ }
+
+ if (real_new.error || real_new.case_clash)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_new.case_clash ? ECASECLASH : real_new.error);
+ return -1;
+ }
+
+ if (!writable_directory (real_old) || !writable_directory (real_new))
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (EACCES);
+ return -1;
+ }
+
+ if (!real_old.exists ()) /* file to move doesn't exist */
+ {
+ syscall_printf ("file to move doesn't exist");
+ set_errno (ENOENT);
+ return (-1);
+ }
+
+ /* Destination file exists and is read only, change that or else
+ the rename won't work. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY);
+
+ /* Shortcut hack No. 2, part 1 */
+ if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_symlink ()
+ && (lnk_suffix = strrchr (real_new.get_win32 (), '.')))
+ *lnk_suffix = '\0';
+
+ if (!MoveFile (real_old, real_new))
+ res = -1;
+
+ if (res == 0 || (GetLastError () != ERROR_ALREADY_EXISTS
+ && GetLastError () != ERROR_FILE_EXISTS))
+ goto done;
+
+ if (wincap.has_move_file_ex ())
+ {
+ if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
+ MOVEFILE_REPLACE_EXISTING))
+ res = 0;
+ }
+ else
+ {
+ syscall_printf ("try win95 hack");
+ for (int i = 0; i < 2; i++)
+ {
+ if (!DeleteFileA (real_new.get_win32 ()) &&
+ GetLastError () != ERROR_FILE_NOT_FOUND)
+ {
+ syscall_printf ("deleting %s to be paranoid",
+ real_new.get_win32 ());
+ break;
+ }
+ else if (MoveFile (real_old.get_win32 (), real_new.get_win32 ()))
+ {
+ res = 0;
+ break;
+ }
+ }
+ }
+
+done:
+ if (res)
+ {
+ __seterrno ();
+ /* Reset R/O attributes if neccessary. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, real_new);
+ }
+ else
+ {
+ /* make the new file have the permissions of the old one */
+ DWORD attr = real_old;
+#ifdef HIDDEN_DOT_FILES
+ char *c = strrchr (real_old.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_old.get_win32 () == '.')
+ attr &= ~FILE_ATTRIBUTE_HIDDEN;
+ c = strrchr (real_new.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_new.get_win32 () == '.')
+ attr |= FILE_ATTRIBUTE_HIDDEN;
+#endif
+ SetFileAttributes (real_new, attr);
+
+ /* Shortcut hack, No. 2, part 2 */
+ /* if the new filename was an existing shortcut, remove it now if the
+ new filename is equal to the shortcut name without .lnk suffix. */
+ if (lnk_suffix)
+ {
+ *lnk_suffix = '.';
+ DeleteFile (real_new);
+ }
+ }
+
+ syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old,
+ (char *) real_new);
+
+ return res;
+}
+
+extern "C" int
+system (const char *cmdstring)
+{
+ pthread_testcancel ();
+
+ if (check_null_empty_str_errno (cmdstring))
+ return -1;
+
+ int res;
+ const char* command[4];
+
+ if (cmdstring == (const char *) NULL)
+ return 1;
+
+ command[0] = "sh";
+ command[1] = "-c";
+ command[2] = cmdstring;
+ command[3] = (const char *) NULL;
+
+ if ((res = spawnvp (_P_SYSTEM, "sh", command)) == -1)
+ {
+ // when exec fails, return value should be as if shell
+ // executed exit (127)
+ res = 127;
+ }
+
+ return res;
+}
+
+extern "C" int
+setdtablesize (int size)
+{
+ if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size))
+ return 0;
+
+ return -1;
+}
+
+extern "C" int
+getdtablesize ()
+{
+ return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX;
+}
+
+extern "C" size_t
+getpagesize ()
+{
+ if (!system_info.dwPageSize)
+ GetSystemInfo (&system_info);
+ return (int) system_info.dwPageSize;
+}
+
+size_t
+getshmlba ()
+{
+ if (!system_info.dwAllocationGranularity)
+ GetSystemInfo (&system_info);
+ return system_info.dwAllocationGranularity;
+}
+
+static int
+check_posix_perm (const char *fname, int v)
+{
+ /* Windows 95/98/ME don't support file system security at all. */
+ if (!wincap.has_security ())
+ return 0;
+
+ /* ntea is ok for supporting permission bits but it doesn't support
+ full POSIX security settings. */
+ if (v == _PC_POSIX_PERMISSIONS && allow_ntea)
+ return 1;
+
+ if (!allow_ntsec)
+ return 0;
+
+ char *root = rootdir (strcpy ((char *)alloca (strlen (fname)), fname));
+
+ if (!allow_smbntsec
+ && ((root[0] == '\\' && root[1] == '\\')
+ || GetDriveType (root) == DRIVE_REMOTE))
+ return 0;
+
+ DWORD vsn, len, flags;
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &len, &flags, NULL, 16))
+ {
+ __seterrno ();
+ return 0;
+ }
+
+ return (flags & FS_PERSISTENT_ACLS) ? 1 : 0;
+}
+
+/* FIXME: not all values are correct... */
+extern "C" long int
+fpathconf (int fd, int v)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ switch (v)
+ {
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ if (isatty (fd))
+ return _POSIX_MAX_CANON;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ return PATH_MAX;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ if (cfd->is_tty ())
+ return -1;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ if (cfd->get_device () == FH_FS)
+ return check_posix_perm (cfd->get_win32_name (), v);
+ set_errno (EINVAL);
+ return -1;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" long int
+pathconf (const char *file, int v)
+{
+ switch (v)
+ {
+ case _PC_PATH_MAX:
+ if (check_null_empty_str_errno (file))
+ return -1;
+ return PATH_MAX - strlen (file);
+ case _PC_NAME_MAX:
+ return PATH_MAX;
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ return _POSIX_MAX_CANON;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ return -1;
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ {
+ path_conv full_path (file, PC_SYM_FOLLOW | PC_FULL);
+ if (full_path.error)
+ {
+ set_errno (full_path.error);
+ return -1;
+ }
+ if (full_path.is_auto_device ())
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ return check_posix_perm (full_path, v);
+ }
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" char *
+ttyname (int fd)
+{
+ char *name;
+ cygheap_fdget cfd (fd);
+ if (cfd < 0 || !cfd->is_tty ())
+ return 0;
+ name = (char *) (cfd->ttyname ());
+ debug_printf ("returning %s", name);
+ return name;
+}
+
+extern "C" char *
+ctermid (char *str)
+{
+ static NO_COPY char buf[16];
+ if (str == NULL)
+ str = buf;
+ if (!real_tty_attached (myself))
+ strcpy (str, "/dev/conin");
+ else
+ __small_sprintf (str, "/dev/tty%d", myself->ctty);
+ return str;
+}
+
+/* Tells stdio if it should do the cr/lf conversion for this file */
+extern "C" int
+_cygwin_istext_for_stdio (int fd)
+{
+ syscall_printf ("_cygwin_istext_for_stdio (%d)", fd);
+ if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING)
+ {
+ syscall_printf (" _cifs: old API");
+ return 0; /* we do it for old apps, due to getc/putc macros */
+ }
+
+ cygheap_fdget cfd (fd, false, false);
+ if (cfd < 0)
+ {
+ syscall_printf (" _cifs: fd not open");
+ return 0;
+ }
+
+ if (cfd->get_device () != FH_FS)
+ {
+ syscall_printf (" _cifs: fd not disk file");
+ return 0;
+ }
+
+ if (cfd->get_w_binary () || cfd->get_r_binary ())
+ {
+ syscall_printf (" _cifs: get_*_binary");
+ return 0;
+ }
+
+ syscall_printf ("_cygwin_istext_for_stdio says yes");
+ return 1;
+}
+
+/* internal newlib function */
+extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *));
+
+static int setmode_mode;
+static int setmode_file;
+
+static int
+setmode_helper (FILE *f)
+{
+ if (fileno (f) != setmode_file)
+ return 0;
+ syscall_printf ("setmode: file was %s now %s",
+ f->_flags & __SCLE ? "text" : "raw",
+ setmode_mode & O_TEXT ? "text" : "raw");
+ if (setmode_mode & O_TEXT)
+ f->_flags |= __SCLE;
+ else
+ f->_flags &= ~__SCLE;
+ return 0;
+}
+
+extern "C" int
+getmode (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+
+ return cfd->get_flags () & (O_BINARY | O_TEXT);
+}
+
+/* Set a file descriptor into text or binary mode, returning the
+ previous mode. */
+
+extern "C" int
+setmode (int fd, int mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ if (mode != O_BINARY && mode != O_TEXT && mode != 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Note that we have no way to indicate the case that writes are
+ binary but not reads, or vice-versa. These cases can arise when
+ using the tty or console interface. People using those
+ interfaces should not use setmode. */
+
+ int res;
+ if (cfd->get_w_binary () && cfd->get_r_binary ())
+ res = O_BINARY;
+ else if (cfd->get_w_binset () && cfd->get_r_binset ())
+ res = O_TEXT; /* Specifically set O_TEXT */
+ else
+ res = 0;
+
+ if (!mode)
+ cfd->reset_to_open_binmode ();
+ else
+ cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
+
+ if (_cygwin_istext_for_stdio (fd))
+ setmode_mode = O_TEXT;
+ else
+ setmode_mode = O_BINARY;
+ setmode_file = fd;
+ _fwalk (_REENT, setmode_helper);
+
+ syscall_printf ("setmode (%d<%s>, %p) returns %s", fd, cfd->get_name (),
+ mode, res & O_TEXT ? "text" : "binary");
+ return res;
+}
+
+extern "C" int
+ftruncate64 (int fd, _off64_t length)
+{
+ int res = -1;
+
+ if (length < 0)
+ set_errno (EINVAL);
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ {
+ HANDLE h = cygheap->fdtab[fd]->get_handle ();
+
+ if (cfd->get_handle ())
+ {
+ /* remember curr file pointer location */
+ _off64_t prev_loc = cfd->lseek (0, SEEK_CUR);
+
+ cfd->lseek (length, SEEK_SET);
+ if (!SetEndOfFile (h))
+ __seterrno ();
+ else
+ res = 0;
+
+ /* restore original file pointer location */
+ cfd->lseek (prev_loc, SEEK_SET);
+ }
+ }
+ }
+
+ syscall_printf ("%d = ftruncate (%d, %D)", res, fd, length);
+ return res;
+}
+
+/* ftruncate: P96 5.6.7.1 */
+extern "C" int
+ftruncate (int fd, _off_t length)
+{
+ return ftruncate64 (fd, (_off64_t)length);
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate64 (const char *pathname, _off64_t length)
+{
+ int fd;
+ int res = -1;
+
+ fd = open (pathname, O_RDWR);
+
+ if (fd == -1)
+ set_errno (EBADF);
+ else
+ {
+ res = ftruncate64 (fd, length);
+ close (fd);
+ }
+ syscall_printf ("%d = truncate (%s, %d)", res, pathname, length);
+
+ return res;
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate (const char *pathname, _off_t length)
+{
+ return truncate64 (pathname, (_off64_t)length);
+}
+
+extern "C" long
+get_osfhandle (int fd)
+{
+ long res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = (long) cfd->get_handle ();
+ else
+ res = -1;
+
+ syscall_printf ("%d = get_osfhandle (%d)", res, fd);
+ return res;
+}
+
+extern "C" int
+statfs (const char *fname, struct statfs *sfs)
+{
+ if (!sfs)
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL);
+ const char *root = full_path.root_dir ();
+
+ syscall_printf ("statfs %s", root);
+
+ /* GetDiskFreeSpaceEx must be called before GetDiskFreeSpace on
+ WinME, to avoid the MS KB 314417 bug */
+ ULARGE_INTEGER availb, freeb, totalb;
+ BOOL status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb);
+
+ DWORD spc, bps, availc, freec, totalc;
+
+ if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ if (status)
+ {
+ availc = availb.QuadPart / (spc*bps);
+ totalc = totalb.QuadPart / (spc*bps);
+ freec = freeb.QuadPart / (spc*bps);
+ }
+ else
+ availc = freec;
+
+ DWORD vsn, maxlen, flags;
+
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0))
+ {
+ __seterrno ();
+ return -1;
+ }
+ sfs->f_type = flags;
+ sfs->f_bsize = spc*bps;
+ sfs->f_blocks = totalc;
+ sfs->f_bavail = availc;
+ sfs->f_bfree = freec;
+ sfs->f_files = -1;
+ sfs->f_ffree = -1;
+ sfs->f_fsid = vsn;
+ sfs->f_namelen = maxlen;
+ return 0;
+}
+
+extern "C" int
+fstatfs (int fd, struct statfs *sfs)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ return statfs (cfd->get_name (), sfs);
+}
+
+/* setpgid: POSIX 4.3.3.1 */
+extern "C" int
+setpgid (pid_t pid, pid_t pgid)
+{
+ int res = -1;
+ if (pid == 0)
+ pid = getpid ();
+ if (pgid == 0)
+ pgid = pid;
+
+ if (pgid < 0)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ else
+ {
+ pinfo p (pid, PID_MAP_RW);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ goto out;
+ }
+ /* A process may only change the process group of itself and its children */
+ if (p == myself || p->ppid == myself->pid)
+ {
+ p->pgid = pgid;
+ if (p->pid != p->pgid)
+ p->set_has_pgid_children (0);
+ res = 0;
+ }
+ else
+ {
+ set_errno (EPERM);
+ goto out;
+ }
+ }
+out:
+ syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
+ return res;
+}
+
+extern "C" pid_t
+getpgid (pid_t pid)
+{
+ if (pid == 0)
+ pid = getpid ();
+
+ pinfo p (pid);
+ if (p == 0)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ return p->pgid;
+}
+
+extern "C" int
+setpgrp (void)
+{
+ return setpgid (0, 0);
+}
+
+extern "C" pid_t
+getpgrp (void)
+{
+ return getpgid (0);
+}
+
+extern "C" char *
+ptsname (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return 0;
+ return (char *) (cfd->ptsname ());
+}
+
+/* FIXME: what is this? */
+extern "C" int __declspec(dllexport)
+regfree ()
+{
+ return 0;
+}
+
+static int __stdcall
+mknod_worker (const char *path, mode_t type, mode_t mode, _major_t major,
+ _minor_t minor)
+{
+ char buf[sizeof (":\\00000000:00000000:00000000") + CYG_MAX_PATH];
+ sprintf (buf, ":\\%x:%x:%x", major, minor,
+ type | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ return symlink_worker (buf, path, true, true);
+}
+
+extern "C" int
+mknod32 (const char *path, mode_t mode, __dev32_t dev)
+{
+ if (check_null_empty_str_errno (path))
+ return -1;
+
+ if (strlen (path) >= CYG_MAX_PATH)
+ return -1;
+
+ path_conv w32path (path, PC_SYM_NOFOLLOW | PC_FULL);
+ if (w32path.exists ())
+ {
+ set_errno (EEXIST);
+ return -1;
+ }
+
+ mode_t type = mode & S_IFMT;
+ _major_t major = _major (dev);
+ _minor_t minor = _minor (dev);
+ switch (type)
+ {
+ case S_IFCHR:
+ case S_IFBLK:
+ break;
+
+ case S_IFIFO:
+ major = _major (FH_FIFO);
+ minor = _minor (FH_FIFO);
+ break;
+
+ case 0:
+ case S_IFREG:
+ {
+ int fd = open (path, O_CREAT, mode);
+ if (fd < 0)
+ return -1;
+ close (fd);
+ return 0;
+ }
+
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return mknod_worker (w32path, type, mode, major, minor);
+}
+
+extern "C" int
+mknod (const char *_path, mode_t mode, __dev16_t dev)
+{
+ return mknod32 (_path, mode, (__dev32_t) dev);
+}
+
+extern "C" int
+mkfifo (const char *_path, mode_t mode)
+{
+ set_errno (ENOSYS); // FIXME
+ return -1;
+}
+
+/* seteuid: standards? */
+extern "C" int
+seteuid32 (__uid32_t uid)
+{
+ debug_printf ("uid: %u myself->gid: %u", uid, myself->gid);
+
+ if (uid == myself->uid && !cygheap->user.groups.ischanged)
+ {
+ debug_printf ("Nothing happens");
+ return 0;
+ }
+
+ cygsid usersid;
+ user_groups &groups = cygheap->user.groups;
+ HANDLE ptok, new_token = INVALID_HANDLE_VALUE;
+ struct passwd * pw_new;
+ bool token_is_internal, issamesid;
+ char dacl_buf[MAX_DACL_LEN (5)];
+ TOKEN_DEFAULT_DACL tdacl = {};
+
+ pw_new = internal_getpwuid (uid);
+ if (!wincap.has_security () && pw_new)
+ goto success_9x;
+ if (!usersid.getfrompw (pw_new))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ RevertToSelf ();
+ if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok))
+ {
+ __seterrno ();
+ goto failed_ptok;;
+ }
+
+ /* Verify if the process token is suitable. */
+ if (verify_token (ptok, usersid, groups))
+ new_token = ptok;
+ /* Verify if the external token is suitable */
+ else if (cygheap->user.external_token != INVALID_HANDLE_VALUE
+ && verify_token (cygheap->user.external_token, usersid, groups))
+ new_token = cygheap->user.external_token;
+ /* Verify if the current token (internal or former external) is suitable */
+ else if (cygheap->user.current_token != INVALID_HANDLE_VALUE
+ && cygheap->user.current_token != cygheap->user.external_token
+ && verify_token (cygheap->user.current_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.current_token;
+ /* Verify if the internal token is suitable */
+ else if (cygheap->user.internal_token != INVALID_HANDLE_VALUE
+ && cygheap->user.internal_token != cygheap->user.current_token
+ && verify_token (cygheap->user.internal_token, usersid, groups,
+ &token_is_internal))
+ new_token = cygheap->user.internal_token;
+
+ debug_printf ("Found token %d", new_token);
+
+ /* Set process def dacl to allow access to impersonated token */
+ if (sec_acl ((PACL) dacl_buf, true, true, usersid))
+ {
+ tdacl.DefaultDacl = (PACL) dacl_buf;
+ if (!SetTokenInformation (ptok, TokenDefaultDacl,
+ &tdacl, sizeof dacl_buf))
+ debug_printf ("SetTokenInformation"
+ "(TokenDefaultDacl): %E");
+ }
+
+ /* If no impersonation token is available, try to
+ authenticate using NtCreateToken () or subauthentication. */
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ new_token = create_token (usersid, groups, pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ {
+ /* create_token failed. Try subauthentication. */
+ debug_printf ("create token failed, try subauthentication.");
+ new_token = subauth (pw_new);
+ if (new_token == INVALID_HANDLE_VALUE)
+ goto failed;
+ }
+ /* Keep at most one internal token */
+ if (cygheap->user.internal_token != INVALID_HANDLE_VALUE)
+ CloseHandle (cygheap->user.internal_token);
+ cygheap->user.internal_token = new_token;
+ }
+ if (new_token != ptok)
+ {
+ /* Avoid having HKCU use default user */
+ load_registry_hive (usersid);
+
+ /* Try setting owner to same value as user. */
+ if (!SetTokenInformation (new_token, TokenOwner,
+ &usersid, sizeof usersid))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenOwner): %E");
+ /* Try setting primary group in token to current group */
+ if (!SetTokenInformation (new_token, TokenPrimaryGroup,
+ &groups.pgsid, sizeof (cygsid)))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenPrimaryGroup): %E");
+ /* Try setting default DACL */
+ if (tdacl.DefaultDacl
+ && !SetTokenInformation (new_token, TokenDefaultDacl,
+ &tdacl, sizeof (tdacl)))
+ debug_printf ("SetTokenInformation (TokenDefaultDacl): %E");
+ }
+
+ CloseHandle (ptok);
+ issamesid = (usersid == cygheap->user.sid ());
+ cygheap->user.set_sid (usersid);
+ cygheap->user.current_token = new_token == ptok ? INVALID_HANDLE_VALUE
+ : new_token;
+ if (!issamesid) /* MS KB 199190 */
+ RegCloseKey (HKEY_CURRENT_USER);
+ cygheap->user.reimpersonate ();
+ if (!issamesid)
+ user_shared_initialize (true);
+
+success_9x:
+ cygheap->user.set_name (pw_new->pw_name);
+ myself->uid = uid;
+ groups.ischanged = FALSE;
+ return 0;
+
+failed:
+ CloseHandle (ptok);
+failed_ptok:
+ cygheap->user.reimpersonate ();
+ return -1;
+}
+
+extern "C" int
+seteuid (__uid16_t uid)
+{
+ return seteuid32 (uid16touid32 (uid));
+}
+
+/* setuid: POSIX 4.2.2.1 */
+extern "C" int
+setuid32 (__uid32_t uid)
+{
+ int ret = seteuid32 (uid);
+ if (!ret)
+ cygheap->user.real_uid = myself->uid;
+ debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setuid (__uid16_t uid)
+{
+ return setuid32 (uid16touid32 (uid));
+}
+
+extern "C" int
+setreuid32 (__uid32_t ruid, __uid32_t euid)
+{
+ int ret = 0;
+ bool tried = false;
+ __uid32_t old_euid = myself->uid;
+
+ if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid)
+ tried = !(ret = seteuid32 (ruid));
+ if (!ret && euid != ILLEGAL_UID)
+ ret = seteuid32 (euid);
+ if (tried && (ret || euid == ILLEGAL_UID) && seteuid32 (old_euid))
+ system_printf ("Cannot restore original euid %u", old_euid);
+ if (!ret && ruid != ILLEGAL_UID)
+ cygheap->user.real_uid = ruid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setreuid (__uid16_t ruid, __uid16_t euid)
+{
+ return setreuid32 (uid16touid32 (ruid), uid16touid32 (euid));
+}
+
+/* setegid: from System V. */
+extern "C" int
+setegid32 (__gid32_t gid)
+{
+ debug_printf ("new egid: %u current: %u", gid, myself->gid);
+
+ if (gid == myself->gid || !wincap.has_security ())
+ {
+ myself->gid = gid;
+ return 0;
+ }
+
+ user_groups * groups = &cygheap->user.groups;
+ cygsid gsid;
+ HANDLE ptok;
+ struct __group32 * gr = internal_getgrgid (gid);
+
+ if (!gsid.getfromgr (gr))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ myself->gid = gid;
+
+ groups->update_pgrp (gsid);
+ /* If impersonated, update primary group and revert */
+ if (cygheap->user.issetuid ())
+ {
+ if (!SetTokenInformation (cygheap->user.token (),
+ TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(thread, "
+ "TokenPrimaryGroup): %E");
+ RevertToSelf ();
+ }
+ if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok))
+ debug_printf ("OpenProcessToken(): %E");
+ else
+ {
+ if (!SetTokenInformation (ptok, TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(process, "
+ "TokenPrimaryGroup): %E");
+ CloseHandle (ptok);
+ }
+ if (cygheap->user.issetuid ()
+ && !ImpersonateLoggedOnUser (cygheap->user.token ()))
+ system_printf ("Impersonating in setegid failed: %E");
+ return 0;
+}
+
+extern "C" int
+setegid (__gid16_t gid)
+{
+ return setegid32 (gid16togid32 (gid));
+}
+
+/* setgid: POSIX 4.2.2.1 */
+extern "C" int
+setgid32 (__gid32_t gid)
+{
+ int ret = setegid32 (gid);
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setgid (__gid16_t gid)
+{
+ int ret = setegid32 (gid16togid32 (gid));
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setregid32 (__gid32_t rgid, __gid32_t egid)
+{
+ int ret = 0;
+ bool tried = false;
+ __gid32_t old_egid = myself->gid;
+
+ if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid)
+ tried = !(ret = setegid32 (rgid));
+ if (!ret && egid != ILLEGAL_GID)
+ ret = setegid32 (egid);
+ if (tried && (ret || egid == ILLEGAL_GID) && setegid32 (old_egid))
+ system_printf ("Cannot restore original egid %u", old_egid);
+ if (!ret && rgid != ILLEGAL_GID)
+ cygheap->user.real_gid = rgid;
+ debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid);
+ return ret;
+}
+
+extern "C" int
+setregid (__gid16_t rgid, __gid16_t egid)
+{
+ return setregid32 (gid16togid32 (rgid), gid16togid32 (egid));
+}
+
+/* chroot: privileged Unix system call. */
+/* FIXME: Not privileged here. How should this be done? */
+extern "C" int
+chroot (const char *newroot)
+{
+ path_conv path (newroot, PC_SYM_FOLLOW | PC_FULL | PC_POSIX);
+
+ int ret;
+ if (path.error)
+ ret = -1;
+ else if (!path.exists ())
+ {
+ set_errno (ENOENT);
+ ret = -1;
+ }
+ else if (!path.isdir ())
+ {
+ set_errno (ENOTDIR);
+ ret = -1;
+ }
+ else
+ {
+ cygheap->root.set (path.normalized_path, path);
+ ret = 0;
+ }
+
+ syscall_printf ("%d = chroot (%s)", ret ? get_errno () : 0,
+ newroot ? newroot : "NULL");
+ if (!path.normalized_path_size && path.normalized_path)
+ cfree (path.normalized_path);
+ return ret;
+}
+
+extern "C" int
+creat (const char *path, mode_t mode)
+{
+ return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+extern "C" void
+__assertfail ()
+{
+ exit (99);
+}
+
+extern "C" int
+getw (FILE *fp)
+{
+ int w, ret;
+ ret = fread (&w, sizeof (int), 1, fp);
+ return ret != 1 ? EOF : w;
+}
+
+extern "C" int
+putw (int w, FILE *fp)
+{
+ int ret;
+ ret = fwrite (&w, sizeof (int), 1, fp);
+ if (feof (fp) || ferror (fp))
+ return -1;
+ return 0;
+}
+
+extern "C" int
+wcscmp (const wchar_t *s1, const wchar_t *s2)
+{
+ while (*s1 && *s1 == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ return (* (unsigned short *) s1) - (* (unsigned short *) s2);
+}
+
+extern "C" size_t
+wcslen (const wchar_t *s1)
+{
+ int l = 0;
+ while (s1[l])
+ l++;
+ return l;
+}
+
+/* FIXME: to do this right, maybe work out the usoft va_list machine
+ and use wsvprintfW instead?
+*/
+extern "C" int
+wprintf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = vprintf (fmt, ap);
+ va_end (ap);
+ return ret;
+}
+
+extern "C" int
+vhangup ()
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" _PTR
+memccpy (_PTR out, const _PTR in, int c, size_t len)
+{
+ const char *inc = (char *) in;
+ char *outc = (char *) out;
+
+ while (len)
+ {
+ char x = *inc++;
+ *outc++ = x;
+ if (x == c)
+ return outc;
+ len --;
+ }
+ return 0;
+}
+
+extern "C" int
+nice (int incr)
+{
+ DWORD priority[] =
+ {
+ IDLE_PRIORITY_CLASS,
+ IDLE_PRIORITY_CLASS,
+ NORMAL_PRIORITY_CLASS,
+ HIGH_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS
+ };
+ int curr = 2;
+
+ switch (GetPriorityClass (hMainProc))
+ {
+ case IDLE_PRIORITY_CLASS:
+ curr = 1;
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ curr = 2;
+ break;
+ case HIGH_PRIORITY_CLASS:
+ curr = 3;
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ curr = 4;
+ break;
+ }
+ if (incr > 0)
+ incr = -1;
+ else if (incr < 0)
+ incr = 1;
+
+ if (SetPriorityClass (hMainProc, priority[curr + incr]) == FALSE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first bit set in I.
+ */
+
+extern "C" int
+ffs (int i)
+{
+ static const unsigned char table[] =
+ {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ unsigned long int a;
+ unsigned long int x = i & -i;
+
+ a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24);
+
+ return table[x >> a] + a;
+}
+
+static void
+locked_append (int fd, const void * buf, size_t size)
+{
+ struct __flock64 lock_buffer = {F_WRLCK, SEEK_SET, 0, 0, 0};
+ int count = 0;
+
+ do
+ if ((lock_buffer.l_start = lseek64 (fd, 0, SEEK_END)) != (_off64_t)-1
+ && fcntl_worker (fd, F_SETLKW, &lock_buffer) != -1)
+ {
+ if (lseek64 (fd, 0, SEEK_END) != (_off64_t)-1)
+ write (fd, buf, size);
+ lock_buffer.l_type = F_UNLCK;
+ fcntl_worker (fd, F_SETLK, &lock_buffer);
+ break;
+ }
+ while (count++ < 1000
+ && (errno == EACCES || errno == EAGAIN)
+ && !usleep (1000));
+}
+
+extern "C" void
+updwtmp (const char *wtmp_file, const struct utmp *ut)
+{
+ int fd;
+
+ if ((fd = open (wtmp_file, O_WRONLY | O_BINARY, 0)) >= 0)
+ {
+ locked_append (fd, ut, sizeof *ut);
+ close (fd);
+ }
+}
+
+extern "C" void
+logwtmp (const char *line, const char *user, const char *host)
+{
+ struct utmp ut;
+ memset (&ut, 0, sizeof ut);
+ ut.ut_type = USER_PROCESS;
+ ut.ut_pid = getpid ();
+ if (line)
+ strncpy (ut.ut_line, line, sizeof ut.ut_line);
+ time (&ut.ut_time);
+ if (user)
+ strncpy (ut.ut_user, user, sizeof ut.ut_user);
+ if (host)
+ strncpy (ut.ut_host, host, sizeof ut.ut_host);
+ updwtmp (_PATH_WTMP, &ut);
+}
+
+extern "C" void
+login (struct utmp *ut)
+{
+ pututline (ut);
+ endutent ();
+ updwtmp (_PATH_WTMP, ut);
+}
+
+extern "C" int
+logout (char *line)
+{
+ struct utmp ut_buf, *ut;
+
+ memset (&ut_buf, 0, sizeof ut_buf);
+ strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line);
+ setutent ();
+ ut = getutline (&ut_buf);
+
+ if (ut)
+ {
+ ut->ut_type = DEAD_PROCESS;
+ memset (ut->ut_user, 0, sizeof ut->ut_user);
+ memset (ut->ut_host, 0, sizeof ut->ut_host);
+ time (&ut->ut_time);
+ debug_printf ("set logout time for %s", line);
+ pututline (ut);
+ endutent ();
+ return 1;
+ }
+ return 0;
+}
+
+static int utmp_fd = -1;
+static bool utmp_readonly = false;
+static char *utmp_file = (char *) _PATH_UTMP;
+
+static void
+internal_setutent (bool force_readwrite)
+{
+ if (force_readwrite && utmp_readonly)
+ endutent ();
+ if (utmp_fd < 0)
+ {
+ utmp_fd = open (utmp_file, O_RDWR | O_BINARY);
+ /* If open fails, we assume an unprivileged process (who?). In this
+ case we try again for reading only unless the process calls
+ pututline() (==force_readwrite) in which case opening just fails. */
+ if (utmp_fd < 0 && !force_readwrite)
+ {
+ utmp_fd = open (utmp_file, O_RDONLY | O_BINARY);
+ if (utmp_fd >= 0)
+ utmp_readonly = true;
+ }
+ }
+ else
+ lseek (utmp_fd, 0, SEEK_SET);
+}
+
+extern "C" void
+setutent ()
+{
+ internal_setutent (false);
+}
+
+extern "C" void
+endutent ()
+{
+ if (utmp_fd >= 0)
+ {
+ close (utmp_fd);
+ utmp_fd = -1;
+ utmp_readonly = false;
+ }
+}
+
+extern "C" void
+utmpname (_CONST char *file)
+{
+ if (check_null_empty_str (file))
+ {
+ debug_printf ("Invalid file");
+ return;
+ }
+ endutent ();
+ utmp_file = strdup (file);
+ debug_printf ("New UTMP file: %s", utmp_file);
+}
+
+/* Note: do not make NO_COPY */
+static struct utmp utmp_data_buf[16];
+static unsigned utix = 0;
+#define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0]))
+#define utmp_data ({ \
+ if (utix > nutdbuf) \
+ utix = 0; \
+ utmp_data_buf + utix++; \
+})
+
+extern "C" struct utmp *
+getutent ()
+{
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut)
+ return NULL;
+ return ut;
+}
+
+extern "C" struct utmp *
+getutid (struct utmp *id)
+{
+ if (check_null_invalid_struct_errno (id))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ {
+ switch (id->ut_type)
+ {
+ case RUN_LVL:
+ case BOOT_TIME:
+ case OLD_TIME:
+ case NEW_TIME:
+ if (id->ut_type == ut->ut_type)
+ return ut;
+ break;
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0)
+ return ut;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+extern "C" struct utmp *
+getutline (struct utmp *line)
+{
+ if (check_null_invalid_struct_errno (line))
+ return NULL;
+ if (utmp_fd < 0)
+ {
+ internal_setutent (false);
+ if (utmp_fd < 0)
+ return NULL;
+ }
+
+ utmp *ut = utmp_data;
+ while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut)
+ if ((ut->ut_type == LOGIN_PROCESS ||
+ ut->ut_type == USER_PROCESS) &&
+ !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line)))
+ return ut;
+
+ return NULL;
+}
+
+extern "C" void
+pututline (struct utmp *ut)
+{
+ if (check_null_invalid_struct (ut))
+ return;
+ internal_setutent (true);
+ if (utmp_fd < 0)
+ {
+ debug_printf ("error: utmp_fd %d", utmp_fd);
+ return;
+ }
+ debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n",
+ ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id);
+ debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n",
+ ut->ut_user, ut->ut_host);
+
+ struct utmp *u;
+ if ((u = getutid (ut)))
+ {
+ lseek (utmp_fd, -sizeof *ut, SEEK_CUR);
+ write (utmp_fd, ut, sizeof *ut);
+ }
+ else
+ locked_append (utmp_fd, ut, sizeof *ut);
+}
+
+extern "C"
+long gethostid (void)
+{
+ unsigned data[13] = {0x92895012,
+ 0x10293412,
+ 0x29602018,
+ 0x81928167,
+ 0x34601329,
+ 0x75630198,
+ 0x89860395,
+ 0x62897564,
+ 0x00194362,
+ 0x20548593,
+ 0x96839102,
+ 0x12219854,
+ 0x00290012};
+
+ bool has_cpuid = false;
+
+ DWORD opmask = SetThreadAffinityMask (GetCurrentThread (), 1);
+ if (!opmask)
+ debug_printf ("SetThreadAffinityMask to 1 failed, %E");
+
+ if (!can_set_flag (0x00040000))
+ debug_printf ("386 processor - no cpuid");
+ else
+ {
+ debug_printf ("486 processor");
+ if (can_set_flag (0x00200000))
+ {
+ debug_printf ("processor supports CPUID instruction");
+ has_cpuid = true;
+ }
+ else
+ debug_printf ("processor does not support CPUID instruction");
+ }
+ if (has_cpuid)
+ {
+ unsigned maxf, unused[3];
+ cpuid (&maxf, &unused[0], &unused[1], &unused[2], 0);
+ maxf &= 0xffff;
+ if (maxf >= 1)
+ {
+ unsigned features;
+ cpuid (&data[0], &unused[0], &unused[1], &features, 1);
+ if (features & (1 << 18))
+ {
+ debug_printf ("processor has psn");
+ if (maxf >= 3)
+ {
+ cpuid (&unused[0], &unused[1], &data[1], &data[2], 3);
+ debug_printf ("Processor PSN: %04x-%04x-%04x-%04x-%04x-%04x",
+ data[0] >> 16, data[0] & 0xffff, data[2] >> 16, data[2] & 0xffff, data[1] >> 16, data[1] & 0xffff);
+ }
+ }
+ else
+ debug_printf ("processor does not have psn");
+ }
+ }
+
+ UUID Uuid;
+ RPC_STATUS status = UuidCreateSequential (&Uuid);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ status = UuidCreate (&Uuid);
+ if (status == RPC_S_OK)
+ {
+ data[4] = *(unsigned *)&Uuid.Data4[2];
+ data[5] = *(unsigned short *)&Uuid.Data4[6];
+ // Unfortunately Windows will sometimes pick a virtual Ethernet card
+ // e.g. VMWare Virtual Ethernet Adaptor
+ debug_printf ("MAC address of first Ethernet card: %02x:%02x:%02x:%02x:%02x:%02x",
+ Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4],
+ Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]);
+ }
+ else
+ {
+ debug_printf ("no Ethernet card installed");
+ }
+
+ reg_key key (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", "Microsoft", "Windows", "CurrentVersion", NULL);
+ key.get_string ("ProductId", (char *)&data[6], 24, "00000-000-0000000-00000");
+ debug_printf ("Windows Product ID: %s", (char *)&data[6]);
+
+ /* Contrary to MSDN, NT4 requires the second argument
+ or a STATUS_ACCESS_VIOLATION is generated */
+ ULARGE_INTEGER availb;
+ GetDiskFreeSpaceEx ("C:\\", &availb, (PULARGE_INTEGER) &data[11], NULL);
+ if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ GetDiskFreeSpace ("C:\\", NULL, NULL, NULL, (DWORD *)&data[11]);
+
+ debug_printf ("hostid entropy: %08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x %08x %08x %08x "
+ "%08x",
+ data[0], data[1],
+ data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[8], data[9],
+ data[10], data[11],
+ data[12]);
+
+ long hostid = 0x40291372;
+ // a random hashing algorithm
+ // dependancy on md5 is probably too costly
+ for (int i=0;i<13;i++)
+ hostid ^= ((data[i] << (i << 2)) | (data[i] >> (32 - (i << 2))));
+
+ if (opmask && !SetThreadAffinityMask (GetCurrentThread (), opmask))
+ debug_printf ("SetThreadAffinityMask to %p failed, %E", opmask);
+
+ debug_printf ("hostid: %08x", hostid);
+
+ return hostid;
+}
+
+#define ETC_SHELLS "/etc/shells"
+static int shell_index;
+static struct __sFILE64 *shell_fp;
+
+extern "C" char *
+getusershell ()
+{
+ /* List of default shells if no /etc/shells exists, defined as on Linux.
+ FIXME: SunOS has a far longer list, containing all shells which
+ might be shipped with the OS. Should we do the same for the Cygwin
+ distro, adding bash, tcsh, ksh, pdksh and zsh? */
+ static NO_COPY const char *def_shells[] = {
+ "/bin/sh",
+ "/bin/csh",
+ "/usr/bin/sh",
+ "/usr/bin/csh",
+ NULL
+ };
+ static char buf[CYG_MAX_PATH];
+ int ch, buf_idx;
+
+ if (!shell_fp && !(shell_fp = fopen64 (ETC_SHELLS, "rt")))
+ {
+ if (def_shells[shell_index])
+ return strcpy (buf, def_shells[shell_index++]);
+ return NULL;
+ }
+ /* Skip white space characters. */
+ while ((ch = getc (shell_fp)) != EOF && isspace (ch))
+ ;
+ /* Get each non-whitespace character as part of the shell path as long as
+ it fits in buf. */
+ for (buf_idx = 0;
+ ch != EOF && !isspace (ch) && buf_idx < CYG_MAX_PATH;
+ buf_idx++, ch = getc (shell_fp))
+ buf[buf_idx] = ch;
+ /* Skip any trailing non-whitespace character not fitting in buf. If the
+ path is longer than CYG_MAX_PATH, it's invalid anyway. */
+ while (ch != EOF && !isspace (ch))
+ ch = getc (shell_fp);
+ if (buf_idx)
+ {
+ buf[buf_idx] = '\0';
+ return buf;
+ }
+ return NULL;
+}
+
+extern "C" void
+setusershell ()
+{
+ if (shell_fp)
+ fseek (shell_fp, 0L, SEEK_SET);
+ shell_index = 0;
+}
+
+extern "C" void
+endusershell ()
+{
+ if (shell_fp)
+ fclose (shell_fp);
+ shell_index = 0;
+}
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
new file mode 100644
index 00000000000..5d469ffac0b
--- /dev/null
+++ b/winsup/cygwin/thread.cc
@@ -0,0 +1,3224 @@
+/* thread.cc: Locking and threading module functions
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+ Originally written by Marco Fuykschot <marco@ddi.nl>
+ Substantialy enhanced by Robert Collins <rbtcollins@hotmail.com>
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* Implementation overview and caveats:
+
+ Win32 puts some contraints on what can and cannot be implemented. Where
+ possible we work around those contrainsts. Where we cannot work around
+ the constraints we either pretend to be conformant, or return an error
+ code.
+
+ Some caveats: PROCESS_SHARED objects while they pretend to be process
+ shared, may not actually work. Some test cases are needed to determine
+ win32's behaviour. My suspicion is that the win32 handle needs to be
+ opened with different flags for proper operation.
+
+ R.Collins, April 2001. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "winsup.h"
+#include <limits.h>
+#include "cygerrno.h"
+#include <assert.h>
+#include <stdlib.h>
+#include "pinfo.h"
+#include "sigproc.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygtls.h"
+#include <semaphore.h>
+#include <stdio.h>
+#include <sys/timeb.h>
+#include <exceptions.h>
+#include <sys/fcntl.h>
+
+extern int threadsafe;
+
+#undef __getreent
+extern "C" struct _reent *
+__getreent ()
+{
+ return &_my_tls.local_clib;
+}
+
+inline 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 (void)
+{
+ pthread_key::fixup_before_fork ();
+}
+
+/* This function is called from a single threaded process */
+void
+MTinterface::fixup_after_fork (void)
+{
+ 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");
+ }
+
+ thread->cygtls = &_my_tls;
+ _my_tls.tid = thread;
+ thread->thread_id = GetCurrentThreadId ();
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+ GetCurrentProcess (), &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)
+ return thread;
+ return pthread_null::get_null_pthread ();
+}
+
+pthread *
+pthread::get_tls_self_pointer ()
+{
+ return _my_tls.tid;
+}
+
+List<pthread> pthread::threads;
+
+/* member methods */
+pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0),
+ valid (false), suspended (false),
+ cancelstate (0), canceltype (0), cancel_event (0),
+ joiner (NULL), next (NULL), cleanup_stack (NULL)
+{
+ if (this != pthread_null::get_null_pthread ())
+ threads.insert (this);
+}
+
+pthread::~pthread ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+ if (cancel_event)
+ CloseHandle (cancel_event);
+
+ if (this != pthread_null::get_null_pthread ())
+ threads.remove (this);
+}
+
+bool
+pthread::create_cancel_event ()
+{
+ cancel_event = ::CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ if (!cancel_event)
+ {
+ system_printf ("couldn't create cancel event, %E");
+ /* we need the event for correct behaviour */
+ return false;
+ }
+ return true;
+}
+
+void
+pthread::precreate (pthread_attr *newattr)
+{
+ pthread_mutex *verifyable_mutex_obj = &mutex;
+
+ /* already running ? */
+ if (win32_obj_id)
+ return;
+
+ if (newattr)
+ {
+ attr.joinable = newattr->joinable;
+ attr.contentionscope = newattr->contentionscope;
+ attr.inheritsched = newattr->inheritsched;
+ attr.stacksize = newattr->stacksize;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("New thread object access mutex is not valid. this %p",
+ this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mutex.type = PTHREAD_MUTEX_NORMAL;
+ if (!create_cancel_event ())
+ magic = 0;
+}
+
+void
+pthread::create (void *(*func) (void *), pthread_attr *newattr,
+ void *threadarg)
+{
+ precreate (newattr);
+ if (!magic)
+ return;
+
+ function = func;
+ arg = threadarg;
+
+ win32_obj_id = ::CreateThread (&sec_none_nih, attr.stacksize,
+ thread_init_wrapper, this, CREATE_SUSPENDED,
+ &thread_id);
+
+ if (!win32_obj_id)
+ {
+ thread_printf ("CreateThread failed: this %p, %E", this);
+ magic = 0;
+ }
+ else
+ {
+ postcreate ();
+ ResumeThread (win32_obj_id);
+ }
+}
+
+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 ();
+ }
+
+ (_reclaim_reent) (_REENT);
+
+
+ if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0)
+ ::exit (0);
+ else
+ {
+ _my_tls.remove (INFINITE);
+ ExitThread (0);
+ }
+}
+
+int
+pthread::cancel (void)
+{
+ 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 (void)
+{
+ if (cancelstate == PTHREAD_CANCEL_DISABLE)
+ return;
+
+ if (WaitForSingleObject (cancel_event, 0) == WAIT_OBJECT_0)
+ cancel_self ();
+}
+
+void
+pthread::static_cancel_self (void)
+{
+ pthread::self ()->cancel_self ();
+}
+
+
+DWORD
+pthread::cancelable_wait (HANDLE object, DWORD timeout, const bool do_cancel)
+{
+ DWORD res;
+ HANDLE wait_objects[2];
+ pthread_t thread = self ();
+
+ if (!is_good_object (&thread) || thread->cancelstate == PTHREAD_CANCEL_DISABLE)
+ return WaitForSingleObject (object, timeout);
+
+ // 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[0] = object;
+ wait_objects[1] = thread->cancel_event;
+
+ res = WaitForMultipleObjects (2, wait_objects, FALSE, timeout);
+ if (do_cancel && res == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ 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);
+}
+
+/* static members */
+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;
+}
+
+/* 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 ()
+{
+}
+
+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;
+}
+
+pthread_condattr::pthread_condattr ():verifyable_object
+ (PTHREAD_CONDATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_condattr::~pthread_condattr ()
+{
+}
+
+List<pthread_cond> pthread_cond::conds;
+
+/* This is used for cond creation protection within a single process only */
+fast_mutex NO_COPY pthread_cond::cond_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_cond::init_mutex ()
+{
+ if (!cond_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread cond static initializer support.");
+}
+
+pthread_cond::pthread_cond (pthread_condattr *attr) :
+ verifyable_object (PTHREAD_COND_MAGIC),
+ shared (0), waiting (0), pending (0), sem_wait (NULL),
+ mtx_cond(NULL), next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj;
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ verifyable_mutex_obj = &mtx_in;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /*
+ * Change the mutex type to NORMAL.
+ * This mutex MUST be of type normal
+ */
+ mtx_in.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_mutex_obj = &mtx_out;
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx_out.type = PTHREAD_MUTEX_NORMAL;
+
+ sem_wait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!sem_wait)
+ {
+ debug_printf ("CreateSemaphore failed. %E");
+ magic = 0;
+ return;
+ }
+
+ conds.insert (this);
+}
+
+pthread_cond::~pthread_cond ()
+{
+ if (sem_wait)
+ CloseHandle (sem_wait);
+
+ conds.remove (this);
+}
+
+void
+pthread_cond::unblock (const bool all)
+{
+ unsigned long releaseable;
+
+ /*
+ * Block outgoing threads (and avoid simultanous unblocks)
+ */
+ mtx_out.lock ();
+
+ releaseable = waiting - pending;
+ if (releaseable)
+ {
+ unsigned long released;
+
+ if (!pending)
+ {
+ /*
+ * Block incoming threads until all waiting threads are released.
+ */
+ mtx_in.lock ();
+
+ /*
+ * Calculate releaseable again because threads can enter until
+ * the semaphore has been taken, but they can not leave, therefore pending
+ * is unchanged and releaseable can only get higher
+ */
+ releaseable = waiting - pending;
+ }
+
+ released = all ? releaseable : 1;
+ pending += released;
+ /*
+ * Signal threads
+ */
+ ::ReleaseSemaphore (sem_wait, released, NULL);
+ }
+
+ /*
+ * And let the threads release.
+ */
+ mtx_out.unlock ();
+}
+
+int
+pthread_cond::wait (pthread_mutex_t mutex, DWORD dwMilliseconds)
+{
+ DWORD rv;
+
+ mtx_in.lock ();
+ if (InterlockedIncrement ((long *)&waiting) == 1)
+ mtx_cond = mutex;
+ else if (mtx_cond != mutex)
+ {
+ InterlockedDecrement ((long *)&waiting);
+ mtx_in.unlock ();
+ return EINVAL;
+ }
+ mtx_in.unlock ();
+
+ /*
+ * Release the mutex and wait on semaphore
+ */
+ ++mutex->condwaits;
+ mutex->unlock ();
+
+ rv = pthread::cancelable_wait (sem_wait, dwMilliseconds, false);
+
+ 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_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");
+}
+
+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;
+}
+
+pthread_rwlockattr::pthread_rwlockattr ():verifyable_object
+ (PTHREAD_RWLOCKATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE)
+{
+}
+
+pthread_rwlockattr::~pthread_rwlockattr ()
+{
+}
+
+List<pthread_rwlock> pthread_rwlock::rwlocks;
+
+/* This is used for rwlock creation protection within a single process only */
+fast_mutex NO_COPY pthread_rwlock::rwlock_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_rwlock::init_mutex ()
+{
+ if (!rwlock_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread rwlock static initializer support.");
+}
+
+pthread_rwlock::pthread_rwlock (pthread_rwlockattr *attr) :
+ verifyable_object (PTHREAD_RWLOCK_MAGIC),
+ shared (0), waiting_readers (0), waiting_writers (0), writer (NULL),
+ readers (NULL), readers_mx (), mtx (NULL), cond_readers (NULL), cond_writers (NULL),
+ next (NULL)
+{
+ pthread_mutex *verifyable_mutex_obj = &mtx;
+ pthread_cond *verifyable_cond_obj;
+
+ if (!readers_mx.init ())
+ {
+ thread_printf ("Internal rwlock synchronisation mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ if (!pthread_mutex::is_good_object (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal rwlock mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtx.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_cond_obj = &cond_readers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock readers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+ verifyable_cond_obj = &cond_writers;
+ if (!pthread_cond::is_good_object (&verifyable_cond_obj))
+ {
+ thread_printf ("Internal rwlock writers cond is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+
+
+ rwlocks.insert (this);
+}
+
+pthread_rwlock::~pthread_rwlock ()
+{
+ rwlocks.remove (this);
+}
+
+int
+pthread_rwlock::rdlock ()
+{
+ int result = 0;
+ struct RWLOCK_READER *reader;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ reader = new struct RWLOCK_READER;
+ if (!reader)
+ {
+ result = EAGAIN;
+ goto DONE;
+ }
+
+ while (writer || waiting_writers)
+ {
+ pthread_cleanup_push (pthread_rwlock::rdlock_cleanup, this);
+
+ ++waiting_readers;
+ cond_readers.wait (&mtx);
+ --waiting_readers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ reader->thread = self;
+ add_reader (reader);
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::tryrdlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || waiting_writers || lookup_reader (self))
+ result = EBUSY;
+ else
+ {
+ struct RWLOCK_READER *reader = new struct RWLOCK_READER;
+ if (reader)
+ {
+ reader->thread = self;
+ add_reader (reader);
+ }
+ else
+ result = EAGAIN;
+ }
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::wrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer == self || lookup_reader (self))
+ {
+ result = EDEADLK;
+ goto DONE;
+ }
+
+ while (writer || readers)
+ {
+ pthread_cleanup_push (pthread_rwlock::wrlock_cleanup, this);
+
+ ++waiting_writers;
+ cond_writers.wait (&mtx);
+ --waiting_writers;
+
+ pthread_cleanup_pop (0);
+ }
+
+ writer = self;
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::trywrlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer || readers)
+ result = EBUSY;
+ else
+ writer = self;
+
+ mtx.unlock ();
+
+ return result;
+}
+
+int
+pthread_rwlock::unlock ()
+{
+ int result = 0;
+ pthread_t self = pthread::self ();
+
+ mtx.lock ();
+
+ if (writer)
+ {
+ if (writer != self)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ writer = NULL;
+ }
+ else
+ {
+ struct RWLOCK_READER *reader = lookup_reader (self);
+
+ if (!reader)
+ {
+ result = EPERM;
+ goto DONE;
+ }
+
+ remove_reader (reader);
+ delete reader;
+ }
+
+ release ();
+
+ DONE:
+ mtx.unlock ();
+
+ return result;
+}
+
+void
+pthread_rwlock::add_reader (struct RWLOCK_READER *rd)
+{
+ List_insert (readers, rd);
+}
+
+void
+pthread_rwlock::remove_reader (struct RWLOCK_READER *rd)
+{
+ List_remove (readers_mx, readers, rd);
+}
+
+struct pthread_rwlock::RWLOCK_READER *
+pthread_rwlock::lookup_reader (pthread_t thread)
+{
+ readers_mx.lock ();
+
+ struct RWLOCK_READER *cur = readers;
+
+ while (cur && cur->thread != thread)
+ cur = cur->next;
+
+ readers_mx.unlock ();
+
+ return cur;
+}
+
+void
+pthread_rwlock::rdlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_readers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::wrlock_cleanup (void *arg)
+{
+ pthread_rwlock *rwlock = (pthread_rwlock *) arg;
+
+ --(rwlock->waiting_writers);
+ rwlock->release ();
+ rwlock->mtx.unlock ();
+}
+
+void
+pthread_rwlock::_fixup_after_fork ()
+{
+ pthread_t self = pthread::self ();
+ struct RWLOCK_READER **temp = &readers;
+
+ waiting_readers = 0;
+ waiting_writers = 0;
+
+ if (!readers_mx.init ())
+ api_fatal ("pthread_rwlock::_fixup_after_fork () failed to recreate mutex");
+
+ /* Unlock eventually locked mutex */
+ mtx.unlock ();
+ /*
+ * Remove all readers except self
+ */
+ while (*temp)
+ {
+ if ((*temp)->thread == self)
+ temp = &((*temp)->next);
+ else
+ {
+ struct RWLOCK_READER *cur = *temp;
+ *temp = (*temp)->next;
+ delete cur;
+ }
+ }
+}
+
+/* pthread_key */
+/* static members */
+/* This stores pthread_key information across fork() boundaries */
+List<pthread_key> pthread_key::keys;
+
+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;
+}
+
+/* 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);
+ }
+}
+
+int
+pthread_key::set (const void *value)
+{
+ /* the OS function doesn't perform error checking */
+ TlsSetValue (tls_index, (void *) value);
+ return 0;
+}
+
+void *
+pthread_key::get () const
+{
+ int saved_error = ::GetLastError ();
+ void *result = TlsGetValue (tls_index);
+ ::SetLastError (saved_error);
+ return result;
+}
+
+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 */
+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;
+}
+
+bool
+pthread_mutex::is_good_initializer (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) != VALID_STATIC_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::is_good_initializer_or_object (pthread_mutex_t const *mutex)
+{
+ if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) == INVALID_OBJECT)
+ return false;
+ return true;
+}
+
+bool
+pthread_mutex::is_good_initializer_or_bad_object (pthread_mutex_t const *mutex)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+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
+ */
+ return ((*mutex)->recursion_counter == 1 && pthread::equal ((*mutex)->owner, self));
+}
+
+List<pthread_mutex> pthread_mutex::mutexes;
+
+/* This is used for mutex creation protection within a single process only */
+fast_mutex NO_COPY pthread_mutex::mutex_initialization_lock;
+
+/* We can only be called once.
+ TODO: (no rush) use a non copied memory section to
+ hold an initialization flag. */
+void
+pthread_mutex::init_mutex ()
+{
+ if (!mutex_initialization_lock.init ())
+ api_fatal ("Could not create win32 Mutex for pthread mutex static initializer support.");
+}
+
+pthread_mutex::pthread_mutex (pthread_mutexattr *attr) :
+ verifyable_object (PTHREAD_MUTEX_MAGIC),
+ lock_counter (0),
+ win32_obj_id (NULL), recursion_counter (0),
+ condwaits (0), owner (NULL), type (PTHREAD_MUTEX_DEFAULT),
+ 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))
+ {
+ InterlockedDecrement ((long *) &lock_counter);
+ if (type == PTHREAD_MUTEX_RECURSIVE)
+ result = lock_recursive ();
+ else
+ result = EDEADLK;
+ }
+ else
+ {
+ WaitForSingleObject (win32_obj_id, INFINITE);
+ set_owner (self);
+ }
+
+ 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;
+}
+
+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;
+}
+
+pthread_mutexattr::pthread_mutexattr ():verifyable_object (PTHREAD_MUTEXATTR_MAGIC),
+pshared (PTHREAD_PROCESS_PRIVATE), mutextype (PTHREAD_MUTEX_DEFAULT)
+{
+}
+
+pthread_mutexattr::~pthread_mutexattr ()
+{
+}
+
+List<semaphore> semaphore::semaphores;
+
+semaphore::semaphore (int pshared, unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (pshared),
+ currentvalue (value),
+ name (NULL)
+{
+ SECURITY_ATTRIBUTES sa = (pshared != PTHREAD_PROCESS_PRIVATE)
+ ? sec_all : sec_none_nih;
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, NULL);
+ if (!this->win32_obj_id)
+ magic = 0;
+
+ semaphores.insert (this);
+}
+
+semaphore::semaphore (const char *sem_name, int oflag, mode_t mode,
+ unsigned int value)
+: verifyable_object (SEM_MAGIC),
+ shared (PTHREAD_PROCESS_SHARED),
+ currentvalue (value), /* Unused for named semaphores. */
+ name (NULL)
+{
+ if (oflag & O_CREAT)
+ {
+ SECURITY_ATTRIBUTES sa = sec_all;
+ security_descriptor sd;
+ if (allow_ntsec)
+ set_security_attribute (mode, &sa, sd);
+ this->win32_obj_id = ::CreateSemaphore (&sa, value, LONG_MAX, sem_name);
+ if (!this->win32_obj_id)
+ magic = 0;
+ if (GetLastError () == ERROR_ALREADY_EXISTS && (oflag & O_EXCL))
+ {
+ __seterrno ();
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ }
+ else
+ {
+ this->win32_obj_id = ::OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+ sem_name);
+ if (!this->win32_obj_id)
+ {
+ __seterrno ();
+ magic = 0;
+ }
+ }
+ if (magic)
+ {
+ name = new char [strlen (sem_name + 1)];
+ if (!name)
+ {
+ set_errno (ENOSPC);
+ CloseHandle (this->win32_obj_id);
+ magic = 0;
+ }
+ else
+ strcpy (name, sem_name);
+ }
+
+ semaphores.insert (this);
+}
+
+semaphore::~semaphore ()
+{
+ if (win32_obj_id)
+ CloseHandle (win32_obj_id);
+
+ delete [] name;
+
+ semaphores.remove (this);
+}
+
+void
+semaphore::_post ()
+{
+ if (ReleaseSemaphore (win32_obj_id, 1, &currentvalue))
+ currentvalue++;
+}
+
+int
+semaphore::_getvalue (int *sval)
+{
+ long val;
+
+ switch (WaitForSingleObject (win32_obj_id, 0))
+ {
+ case WAIT_OBJECT_0:
+ ReleaseSemaphore (win32_obj_id, 1, &val);
+ *sval = val + 1;
+ break;
+ case WAIT_TIMEOUT:
+ *sval = 0;
+ break;
+ default:
+ set_errno (EAGAIN);
+ return -1;
+ }
+ return 0;
+}
+
+int
+semaphore::_trywait ()
+{
+ /* FIXME: signals should be able to interrupt semaphores...
+ *We probably need WaitForMultipleObjects here.
+ */
+ if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT)
+ {
+ set_errno (EAGAIN);
+ return -1;
+ }
+ currentvalue--;
+ return 0;
+}
+
+int
+semaphore::_timedwait (const struct timespec *abstime)
+{
+ struct timeval tv;
+ long waitlength;
+
+ if (__check_invalid_read_ptr (abstime, sizeof *abstime))
+ {
+ /* 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 (pthread::cancelable_wait (win32_obj_id, waitlength))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ case WAIT_TIMEOUT:
+ set_errno (ETIMEDOUT);
+ return -1;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+void
+semaphore::_wait ()
+{
+ switch (pthread::cancelable_wait (win32_obj_id, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ currentvalue--;
+ break;
+ default:
+ debug_printf ("cancelable_wait failed. %E");
+ return;
+ }
+}
+
+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;
+}
+
+/* Generic memory acccess routine - where should it live ? */
+int __stdcall
+check_valid_pointer (void const *pointer)
+{
+ if (!pointer || IsBadWritePtr ((void *) pointer, sizeof (verifyable_object)))
+ return EFAULT;
+ return 0;
+}
+
+verifyable_object_state
+verifyable_object_isvalid (void const * objectptr, long magic, void *static_ptr)
+{
+ verifyable_object **object = (verifyable_object **)objectptr;
+ if (check_valid_pointer (object))
+ return INVALID_OBJECT;
+ if (static_ptr && *object == static_ptr)
+ return VALID_STATIC_OBJECT;
+ if (!*object)
+ return INVALID_OBJECT;
+ if (check_valid_pointer (*object))
+ return INVALID_OBJECT;
+ if ((*object)->magic != magic)
+ return INVALID_OBJECT;
+ return VALID_OBJECT;
+}
+
+verifyable_object_state
+verifyable_object_isvalid (void const * objectptr, long magic)
+{
+ return verifyable_object_isvalid (objectptr, magic, NULL);
+}
+
+DWORD WINAPI
+pthread::thread_init_wrapper (void *arg)
+{
+ pthread *thread = (pthread *) arg;
+ _my_tls.tid = 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.
+}
+
+bool
+pthread::is_good_object (pthread_t const *thread)
+{
+ if (verifyable_object_isvalid (thread, PTHREAD_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+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 ();
+ (*thread)->create (start_routine, attr ? *attr : NULL, arg);
+ if (!is_good_object (thread))
+ {
+ 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 (void)
+{
+ MT_INTERFACE->fixup_before_fork ();
+
+ callback *cb = MT_INTERFACE->pthread_prepare;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+void
+pthread::atforkparent (void)
+{
+ callback *cb = MT_INTERFACE->pthread_parent;
+ while (cb)
+ {
+ cb->cb ();
+ cb = cb->next;
+ }
+}
+
+void
+pthread::atforkchild (void)
+{
+ MT_INTERFACE->fixup_after_fork ();
+
+ 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, false))
+ {
+ 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 (void)
+{
+ 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 *))
+{
+ /* The opengroup docs don't define if we should check this or not,
+ but creation is relatively rare. */
+ if (pthread_key::is_good_object (key))
+ return EBUSY;
+
+ *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 ();
+
+}
+
+/* Thread synchronisation */
+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;
+}
+
+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;
+}
+
+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;
+}
+
+bool
+pthread_cond::is_good_initializer_or_bad_object (pthread_cond_t const *cond)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+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)
+{
+ if (attr && !pthread_condattr::is_good_object (attr))
+ return EINVAL;
+
+ cond_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (cond))
+ {
+ cond_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *cond = new pthread_cond (attr ? (*attr) : NULL);
+ if (!is_good_object (cond))
+ {
+ delete (*cond);
+ *cond = NULL;
+ cond_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ 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;
+ long waitlength;
+
+ pthread_testcancel ();
+
+ if (check_valid_pointer (abstime))
+ return EINVAL;
+
+ 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)
+ return ETIMEDOUT;
+ 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;
+}
+
+/* RW locks */
+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;
+}
+
+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;
+}
+
+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;
+}
+
+bool
+pthread_rwlock::is_good_initializer_or_bad_object (pthread_rwlock_t const *rwlock)
+{
+ verifyable_object_state objectState = verifyable_object_isvalid (rwlock, PTHREAD_RWLOCK_MAGIC, PTHREAD_RWLOCK_INITIALIZER);
+ if (objectState == VALID_OBJECT)
+ return false;
+ return true;
+}
+
+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)
+{
+ if (attr && !pthread_rwlockattr::is_good_object (attr))
+ return EINVAL;
+
+ rwlock_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (rwlock))
+ {
+ rwlock_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *rwlock = new pthread_rwlock (attr ? (*attr) : NULL);
+ if (!is_good_object (rwlock))
+ {
+ delete (*rwlock);
+ *rwlock = NULL;
+ rwlock_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ 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;
+
+ int rval = sig ? sig_send (NULL, sig, 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 */
+
+/* FIXME: there's a potential race with PTHREAD_MUTEX_INITALIZER:
+ the mutex is not actually inited until the first use.
+ So two threads trying to lock/trylock may collide.
+ Solution: we need a global mutex on mutex creation, or possibly simply
+ on all constructors that allow INITIALIZER macros.
+ the lock should be very small: only around the init routine, not
+ every test, or all mutex access will be synchronised. */
+
+int
+pthread_mutex::init (pthread_mutex_t *mutex,
+ const pthread_mutexattr_t *attr)
+{
+ if (attr && !pthread_mutexattr::is_good_object (attr) || check_valid_pointer (mutex))
+ return EINVAL;
+
+ mutex_initialization_lock.lock ();
+
+ if (!is_good_initializer_or_bad_object (mutex))
+ {
+ mutex_initialization_lock.unlock ();
+ return EBUSY;
+ }
+
+ *mutex = new pthread_mutex (attr ? (*attr) : NULL);
+ if (!is_good_object (mutex))
+ {
+ delete (*mutex);
+ *mutex = NULL;
+ mutex_initialization_lock.unlock ();
+ return EAGAIN;
+ }
+ mutex_initialization_lock.unlock ();
+ return 0;
+}
+
+extern "C" int
+pthread_mutex_getprioceiling (const pthread_mutex_t *mutex,
+ int *prioceiling)
+{
+ pthread_mutex_t *themutex = (pthread_mutex_t *) mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init ((pthread_mutex_t *) mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ /* 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)
+{
+ pthread_mutex_t *themutex = mutex;
+ /* This could be simplified via is_good_initializer_or_object
+ and is_good_initializer, but in a performance critical call like this....
+ no. */
+ switch (verifyable_object_isvalid (themutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER))
+ {
+ case INVALID_OBJECT:
+ return EINVAL;
+ break;
+ case VALID_STATIC_OBJECT:
+ if (pthread_mutex::is_good_initializer (mutex))
+ {
+ int rv = pthread_mutex::init (mutex, NULL);
+ if (rv && rv != EBUSY)
+ return rv;
+ }
+ /* No else needed. If it's been initialized while we waited,
+ we can just attempt to lock it */
+ break;
+ case VALID_OBJECT:
+ break;
+ }
+ return (*themutex)->lock ();
+}
+
+extern "C" int
+pthread_mutex_trylock (pthread_mutex_t *mutex)
+{
+ pthread_mutex_t *themutex = mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ return (*themutex)->trylock ();
+}
+
+extern "C" int
+pthread_mutex_unlock (pthread_mutex_t *mutex)
+{
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ 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)
+{
+ pthread_mutex_t *themutex = mutex;
+ if (pthread_mutex::is_good_initializer (mutex))
+ pthread_mutex::init (mutex, NULL);
+ if (!pthread_mutex::is_good_object (themutex))
+ return EINVAL;
+ 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 */
+bool
+semaphore::is_good_object (sem_t const * sem)
+{
+ if (verifyable_object_isvalid (sem, SEM_MAGIC) != VALID_OBJECT)
+ return false;
+ return true;
+}
+
+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;
+ }
+
+ (*sem)->_wait ();
+ return 0;
+}
+
+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)
+{
+
+ if (!is_good_object (sem)
+ || __check_null_invalid_struct (sval, sizeof (int)))
+ {
+ 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 ()
+{
+}
+
+void
+pthread_null::create (void *(*)(void *), pthread_attr *, void *)
+{
+}
+
+void
+pthread_null::exit (void *value_ptr)
+{
+ 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;