diff options
author | Christopher Faylor <cgf@redhat.com> | 2003-01-16 01:27:30 +0000 |
---|---|---|
committer | Christopher Faylor <cgf@redhat.com> | 2003-01-16 01:27:30 +0000 |
commit | a91a64d228e9a4bdf1c5531119c42426bc719838 (patch) | |
tree | 3070c7daf6c77fdc92cef32ea9a2a7de5e2c2933 | |
parent | 0f38f19ae0747aec7a7c787bb5c23f91d8912b1e (diff) | |
download | gdb-a91a64d228e9a4bdf1c5531119c42426bc719838.tar.gz |
merge from trunk
39 files changed, 33270 insertions, 0 deletions
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in new file mode 100644 index 00000000000..ac2b254713e --- /dev/null +++ b/winsup/cygwin/Makefile.in @@ -0,0 +1,392 @@ +# Makefile.in for Cygwin. +# Copyright 1995, 1996, 1997, 1998, 1999, 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. + +# This makefile requires GNU make. + +SHELL:=@SHELL@ +srcdir:=@srcdir@ +objdir:=. + +CONFIG_DIR:=$(srcdir)/config/@CONFIG_DIR@ +VPATH:=$(srcdir):$(CONFIG_DIR):$(srcdir)/regex:$(srcdir)/regexp:$(srcdir)/lib + +target_alias:=@target_alias@ +build_alias:=@build_alias@ +host_alias:=@host_alias@ +prefix:=@prefix@ + +program_transform_name:=@program_transform_name@ +exec_prefix:=@exec_prefix@ +bindir:=@bindir@ +libdir:=@libdir@ +ifeq ($(target_alias),$(host_alias)) +ifeq ($(build_alias),$(host_alias)) +tooldir:=$(exec_prefix) +else +tooldir:=$(exec_prefix)/$(target_alias) +endif +else +tooldir:=$(exec_prefix)/$(target_alias) +endif +datadir:=@datadir@ +infodir:=@infodir@ +includedir:=@includedir@ + +INSTALL:=@INSTALL@ +INSTALL_PROGRAM:=@INSTALL_PROGRAM@ + +# +# --enable options from configure +# +MT_SAFE:=@MT_SAFE@ +DEFS:=@DEFS@ + +# cygheap_CFLAGS:=-fomit-frame-pointer +malloc_CFLAGS:=-fomit-frame-pointer +shared_CFLAGS:=-fomit-frame-pointer +cygthread_CFLAGS:=-fomit-frame-pointer +miscfuncs_CFLAGS:=-fomit-frame-pointer +fhandler_CFLAGS:=-fomit-frame_pointer +fhandler_clipboard_CFLAGS:=-fomit-frame-pointer +fhandler_console_CFLAGS:=-fomit-frame-pointer +fhandler_disk_file_CFLAGS:=-fomit-frame-pointer +fhandler_dsp_CFLAGS:=-fomit-frame-pointer +fhandler_floppy_CFLAGS:=-fomit-frame-pointer +fhandler_mem_CFLAGS:=-fomit-frame-pointer +fhandler_proc_CFLAGS:=-fomit-frame-pointer +fhandler_process_CFLAGS:=-fomit-frame-pointer +fhandler_random_CFLAGS:=-fomit-frame-pointer +fhandler_raw_CFLAGS:=-fomit-frame-pointer +fhandler_registry_CFLAGS:=-fomit-frame-pointer +fhandler_serial_CFLAGS:=-fomit-frame-pointer +fhandler_socket_CFLAGS:=-fomit-frame-pointer +fhandler_tape_CFLAGS:=-fomit-frame-pointer +fhandler_termios_CFLAGS:=-fomit-frame-pointer +fhandler_tty_CFLAGS:=-fomit-frame-pointer +fhandler_virtual_CFLAGS:=-fomit-frame-pointer +fhandler_windows_CFLAGS:=-fomit-frame-pointer +fhandler_zero_CFLAGS:=-fomit-frame-pointer + +CC:=@CC@ +# FIXME: Which is it, CC or CC_FOR_TARGET? +CC_FOR_TARGET:=$(CC) +CFLAGS=@CFLAGS@ +override CFLAGS+=-MMD ${$(*F)_CFLAGS} +CXX:=@CXX@ +CXXFLAGS=@CXXFLAGS@ + +# For linking mount, etc. crt0.o isn't accessable in a fresh build. +EXE_LDFLAGS:=@EXE_LDFLAGS@ + +AR:=@AR@ +AR_FLAGS:=qv +RANLIB:=@RANLIB@ +LD:=@LD@ +DLLTOOL:=@DLLTOOL@ +WINDRES:=@WINDRES@ +AS:=@AS@ +NM:=@NM@ +LDSCRIPT:=cygwin.sc + +# +# Include common definitions for winsup directory +# +include $(srcdir)/../Makefile.common + +INSTALL_DATA:=$(SHELL) $(updir1)/install-sh -c + +@SET_MAKE@ + +# Setup the testing framework, if you have one +EXPECT = `if [ -f $${rootme}/../../expect/expect$(EXEEXT) ] ; then \ + echo $${rootme}/../../expect/expect$(EXEEXT) ; \ + else echo expect ; fi` + +RUNTEST = `if [ -f $${srcdir}/../dejagnu/runtest ] ; then \ + echo $${srcdir}/../dejagnu/runtest ; \ + else echo runtest; fi` +RUNTESTFLAGS = + +# Parameters used in building the cygwin.dll. +# We build as new-cygwin.dll and rename at install time to overcome +# native rebuilding issues (we don't want the build tools to see a partially +# built cygwin.dll and attempt to use it instead of the old one). + +DLL_NAME:=cygwin1.dll +LIB_NAME:=libcygwin.a +DEF_FILE:=cygwin.def +DLL_ENTRY:=@DLL_ENTRY@ + +LIBGMON_A:=libgmon.a +GMON_START:=gcrt0.o + +# Some things want these from libc, but they have their own static +# data which apps can get to, which is a pain in the dll, so we +# include them directly into the library. + +LIBCOS:=${sort ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.c}}}} \ + ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.cc}}}}} + +# Build all source files in the config directory + +EXTRA_DLL_OFILES:=${addsuffix .o,${basename ${notdir ${wildcard $(CONFIG_DIR)/*.c}}}} + +EXTRA_OFILES=$(bupdir1)/libiberty/random.o $(bupdir1)/libiberty/strsignal.o + +MALLOC_OFILES=@MALLOC_OFILES@ + +DLL_IMPORTS:=$(w32api_lib)/libkernel32.a + +MT_SAFE_OBJECTS:= +# Please maintain this list in sorted order, with maximum files per 80 col line +DLL_OFILES:=assert.o autoload.o cxx.o cygheap.o cygserver_client.o \ + cygserver_transport.o cygserver_transport_pipes.o \ + cygserver_transport_sockets.o cygthread.o dcrt0.o debug.o \ + delqueue.o devices.o dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o \ + exceptions.o exec.o external.o fcntl.o fhandler.o \ + fhandler_clipboard.o fhandler_console.o fhandler_disk_file.o \ + fhandler_dsp.o fhandler_fifo.o fhandler_floppy.o fhandler_mem.o \ + fhandler_proc.o fhandler_process.o fhandler_random.o \ + fhandler_raw.o fhandler_registry.o fhandler_serial.o \ + fhandler_socket.o fhandler_tape.o fhandler_termios.o \ + fhandler_tty.o fhandler_virtual.o fhandler_windows.o \ + fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o ioctl.o \ + ipc.o localtime.o malloc.o malloc_wrapper.o miscfuncs.o mmap.o msg.o \ + net.o netdb.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o pthread.o \ + regcomp.o regerror.o regexec.o regfree.o registry.o resource.o \ + scandir.o sched.o sec_acl.o sec_helper.o security.o select.o sem.o \ + shared.o shm.o signal.o sigproc.o smallprint.o spawn.o strace.o \ + strsep.o sync.o syscalls.o sysconf.o syslog.o termios.o thread.o \ + times.o tty.o uinfo.o uname.o v8_regexp.o v8_regerror.o v8_regsub.o \ + wait.o wincap.o window.o \ + $(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS) + +GMON_OFILES:=gmon.o mcount.o profil.o + +OBSOLETE_FUNCTIONS:=regcomp regerror regexec regfree regsub +NEW_FUNCTIONS:=regcomp posix_regcomp \ + regerror posix_regerror \ + regexec posix_regexec \ + regfree posix_regfree + +API_VER:=$(srcdir)/include/cygwin/version.h + +PWD:=${shell pwd} +SUBLIBS:=libpthread.a $(PWD)/libm.a libc.a +EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a +INSTOBJS:=automode.o binmode.o textmode.o +TARGET_LIBS:=$(LIB_NAME) $(SUBLIBS) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS) +.PHONY: all force dll_ofiles install all_target install_target all_host install_host \ + install install-libs install-headers -lgcc + +.SUFFIXES: +.SUFFIXES: .c .cc .def .a .o .d + +all_host=@all_host@ +install_host=@install_host@ + +all: all_target $(all_host) + +all_target: $(TARGET_LIBS) cygserver.exe + +all_host: new-$(LIB_NAME) cygrun.exe + +force: + +install: install-libs install-headers install-man install_target \ + $(install_host) $(install_target) + +uninstall: uninstall-libs uninstall-headers uninstall-man + +install-libs: $(TARGET_LIBS) + $(INSTALL_DATA) new-$(DLL_NAME) $(bindir)/$(DLL_NAME); \ + for i in $^; do \ + $(INSTALL_DATA) $$i $(tooldir)/lib/`basename $$i` ; \ + done + +install-headers: + cd $(srcdir); \ + for sub in `find include -name '[a-z]*' -type d -print | sort`; do \ + for i in $$sub/*.h ; do \ + $(INSTALL_DATA) $$i $(tooldir)/$$sub/`basename $$i` ; \ + done ; \ + done ; \ + $(INSTALL_DATA) regex/regex.h $(tooldir)/include/regex.h + +install-man: + cd $(srcdir); \ + for i in `find . -type f -name '*.2'`; do \ + $(INSTALL_DATA) $$i $(tooldir)/man/man2/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.3'`; do \ + $(INSTALL_DATA) $$i $(tooldir)/man/man3/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.5'`; do \ + $(INSTALL_DATA) $$i $(tooldir)/man/man5/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.7'`; do \ + $(INSTALL_DATA) $$i $(tooldir)/man/man7/`basename $$i` ; \ + done + +install_target: cygserver.exe + $(INSTALL_PROGRAM) cygserver.exe $(bindir)/cygserver.exe + +install_host: + + +uninstall-libs: $(TARGET_LIBS) + rm -f $(bindir)/$(DLL_NAME); \ + for i in $^; do \ + rm -f $(tooldir)/lib/$$i ; \ + done + +uninstall-headers: + cd $(srcdir); \ + for sub in `find include -name '[a-z]*' -type d -print | sort`; do \ + for i in $$sub/*.h ; do \ + rm -f $(tooldir)/$$sub/`basename $$i` ; \ + done ; \ + done ; \ + rm -f $(tooldir)/include/regex.h + +uninstall-man: + cd $(srcdir); \ + for i in `find . -type f -name '*.2'`; do \ + rm -f $(tooldir)/man/man2/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.3'`; do \ + rm -f $(tooldir)/man/man3/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.5'`; do \ + rm -f $(tooldir)/man/man5/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.7'`; do \ + rm -f $(tooldir)/man/man7/`basename $$i` ; \ + done + +clean: + -rm -f *.o *.dll *.a *.exp junk *.base version.cc regexp/*.o winver_stamp *.exe *.d *stamp* *_magic.h + +maintainer-clean realclean: clean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + -rm -fr configure + + +# Rule to build cygwin.dll +new-$(DLL_NAME): $(LDSCRIPT) $(DLL_OFILES) $(DEF_FILE) $(DLL_IMPORTS) $(LIBC) $(LIBM) $(API_VER) Makefile winver_stamp + $(CXX) $(CXXFLAGS) -nostdlib -Wl,-T$(firstword $^) -Wl,--out-implib,cygdll.a -shared -o $@ \ + -e $(DLL_ENTRY) $(DEF_FILE) $(DLL_OFILES) version.o winver.o \ + $(MALLOC_OBJ) $(LIBM) $(LIBC) \ + -lgcc $(DLL_IMPORTS) + +# Rule to build libcygwin.a +$(LIB_NAME): rmsym newsym new-$(DLL_NAME) $(LIBCOS) + /bin/sh ${word 1,$^} ./cygdll.a "$(NM)" "$(AR)" "$(RANLIB)" $(OBSOLETE_FUNCTIONS) || exit 0 + /bin/sh ${word 2,$^} ./cygdll.a "$(AS)" "$(AR)" "$(RANLIB)" $(NEW_FUNCTIONS) || exit 0 + (echo create $(LIB_NAME); echo addmod $(LIBCOS); echo addlib cygdll.a; echo save) | $(AR) -M + +# Rule to make stub library used by testsuite +# dependency set to $(LIB_NAME) to accommodate make -j2. +# Otherwise dlltool gets confused. cgf (11-16-2000) +new-$(LIB_NAME): $(LIB_NAME) + $(DLLTOOL) --as=$(AS) --dllname new-$(DLL_NAME) --def $(DEF_FILE) --output-lib new-templib.a + $(AR) rcv new-templib.a $(LIBCOS) + mv new-templib.a new-$(LIB_NAME) + $(RANLIB) $@ + +dll_ofiles: $(DLL_OFILES) + +$(LIBGMON_A): $(GMON_OFILES) $(GMON_START) + $(AR) rcv $(LIBGMON_A) $(GMON_OFILES) + +$(API_VER): $(srcdir)/cygwin.din + @echo Error: Version info is older than DLL API! + @false + +version.cc winver.o: winver_stamp + @ : + +shared_info_magic.h: cygmagic shared_info.h + /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} MOUNT_MAGIC 'class mount_info' SHARED_MAGIC 'class shared_info' + +child_info_magic.h: cygmagic child_info.h + /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} CHILD_INFO_MAGIC 'class child_info' + +dcrt0.o sigproc.o: child_info_magic.h + +shared.o: shared_info_magic.h + +libpthread.a: speclib cygwin.def pthread.o thread.o + /bin/sh ${word 1, $^} $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^} + +$(PWD)/libm.a: speclib cygwin.def $(LIBM) + /bin/sh ${word 1, $^} $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^} + +$(PWD)/libc.a: speclib cygwin.def $(PWD)/libm.a libpthread.a + /bin/sh ${word 1, $^} -v $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^} + +lib%.a: %.o + $(AR) cru $@ $? + +winver_stamp: mkvers.sh include/cygwin/version.h winver.rc $(DLL_OFILES) + @echo "Making version.o and winver.o";\ + $(SHELL) ${word 1,$^} ${word 2,$^} ${word 3,$^} $(WINDRES) && \ + $(COMPILE_CXX) -o version.o version.cc && \ + touch $@ + +cygrun.o: cygrun.c + $(CC) $(MINGW_CFLAGS) -o $@ -c $< + +cygrun.exe : cygrun.o -lgcc $(w32api_lib)/libuser32.a \ + $(w32api_lib)/libshell32.a $(w32api_lib)/libkernel32.a + $(CC) ${MINGW_LDFLAGS} -mno-cygwin -o $@ $^ + +cygserver_transport_outside.o: cygserver_transport.cc + $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $< + +cygserver_transport_pipes_outside.o: cygserver_transport_pipes.cc + $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $< + +cygserver_transport_sockets_outside.o: cygserver_transport_sockets.cc + $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $< + +cygserver_client_outside.o: cygserver_client.cc + $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $< + +# gperf -c --key-positions='1-126' -r -t -C -E -L 'ANSI-C' -Hdevhash -N'device::lookup' -Z devstring -7 $? |\ + +$(srcdir)/devices.cc: devices.gperf devices.h + $(srcdir)/cygwin-gperf $^ > $@ + +cygserver.exe: cygserver.o cygserver_shm.o cygserver_transport_outside.o cygserver_transport_pipes_outside.o cygserver_transport_sockets_outside.o cygserver_client_outside.o cygserver_process.o threaded_queue.o wincap.o version.o smallprint.o + $(CXX) -o $@ $^ -lstdc++ +#ifdef VERBOSE +# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) +#else +# @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\ +# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS) +#endif + +-lgcc: + : + +# + +Makefile: cygwin.din + +$(DEF_FILE): cygwin.din config.status + $(SHELL) config.status + +winsup.h: config.h + +deps:=${wildcard *.d} +ifneq (,$(deps)) +include $(deps) +endif diff --git a/winsup/cygwin/configure b/winsup/cygwin/configure new file mode 100755 index 00000000000..05e791825b6 --- /dev/null +++ b/winsup/cygwin/configure @@ -0,0 +1,2415 @@ +#! /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-threadsafe=[runtime] Build a cygwin DLL which is thread safe" +ac_help="$ac_help + --enable-extra-threadsafe-checking Build a cygwin DLL which is thread safe with extra consistency checking" +ac_help="$ac_help + --enable-debugging Build a cygwin DLL which has more consistency checking for debugging" +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 + + + + +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" + ;; + *) + all_host= + install_host= + ;; +esac + + + + +# 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:1013: 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:1045: 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:1080: 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:1112: 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:1147: 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:1179: 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:1214: 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:1246: 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:1281: 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:1313: 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:1348: 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:1380: 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:1415: 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:1447: 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:1481: 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 1496 "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:1502: \"$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 1513 "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:1519: \"$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 1530 "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:1536: \"$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:1563: 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 1568 "configure" +#include "confdefs.h" +#include <alloca.h> +int main() { +char *p = alloca(2 * sizeof(int)); +; return 0; } +EOF +if { (eval echo configure:1575: \"$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:1596: 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 1601 "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:1629: \"$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:1661: 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 1666 "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:1691: 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 1696 "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:1719: \"$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:1746: 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 1754 "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:1773: \"$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:1795: 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 1832 "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:1845: \"$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 + + +mt_safe_val=1 +MT_SAFE=yes + +# Check whether --enable-threadsafe or --disable-threadsafe was given. +if test "${enable_threadsafe+set}" = set; then + enableval="$enable_threadsafe" + case "${enableval}" in +yes) + ;; +runtime) + mt_safe_val=2 + MT_SAFE=yes + ;; +no) + mt_safe_val=0 + MT_SAFE=no + ;; +esac + +fi + + +# Check whether --enable-extra-threadsafe-checking or --disable-extra-threadsafe-checking was given. +if test "${enable_extra_threadsafe_checking+set}" = set; then + enableval="$enable_extra_threadsafe_checking" + case "${enableval}" in +yes) + mt_safe_val=1 + MT_SAFE=yes + cat >> confdefs.h <<\EOF +#define _CYG_THREAD_FAILSAFE 1 +EOF + + ;; +no) + ;; +esac + +fi + + +if test "$MT_SAFE" = "yes"; then + cat >> confdefs.h <<EOF +#define _MT_SAFE $mt_safe_val +EOF + +fi + + + +# 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 + + +MALLOC_OFILES= +# 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 + + + + +echo $ac_n "checking if newlib is part of the build tree""... $ac_c" 1>&6 +echo "configure:1970: checking if newlib is part of the build tree" >&5 + +EXE_LDFLAGS= +if test -d ../newlib +then + echo "$ac_t""yes" 1>&6 + EXE_LDFLAGS="-B../../newlib/ -B../" +else + echo "$ac_t""no" 1>&6 +fi + + +if test x"$EXE_LDFLAGS" = x +then + echo $ac_n "checking if installed newlib needed""... $ac_c" 1>&6 +echo "configure:1985: checking if installed newlib needed" >&5 + cat > conftest.$ac_ext <<EOF +#line 1987 "configure" +#include "confdefs.h" + +int main() { +/* main already defined */ + +; return 0; } +EOF +if { (eval echo configure:1995: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + echo "$ac_t""no" 1>&6 + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""yes" 1>&6 + echo "configure: warning: newlib not found - utility .exe's may not link" 1>&2 +fi +rm -f conftest* +fi + + +case "$target_cpu" in + i386|i486|i586|i686) DLL_ENTRY="_dll_entry@12" + DEF_DLL_ENTRY="dll_entry@12" + ALLOCA="_alloca" + CONFIG_DIR="i386" ;; + powerpc*) DLL_ENTRY="dll_entry" + DEF_DLL_ENTRY="dll_entry" + ALLOCA=" __allocate_stack" + CONFIG_DIR="ppc" ;; + *) { 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 cygwin.def:cygwin.din 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%@MT_SAFE@%$MT_SAFE%g +s%@EXE_LDFLAGS@%$EXE_LDFLAGS%g +s%@MALLOC_OFILES@%$MALLOC_OFILES%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 cygwin.def:cygwin.din"} +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..864c04a273a --- /dev/null +++ b/winsup/cygwin/configure.in @@ -0,0 +1,249 @@ +dnl Autoconf configure script for Cygwin. +dnl Copyright 1996, 1997, 1998, 2000, 2001 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) + +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" + ;; + *) + all_host= + install_host= + ;; +esac + +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 + +dnl set default mt safeness and then process the options. +mt_safe_val=1 +MT_SAFE=yes + +AC_ARG_ENABLE(threadsafe, +[ --enable-threadsafe=[runtime] Build a cygwin DLL which is thread safe], +[case "${enableval}" in +yes) + dnl default. + ;; +runtime) + mt_safe_val=2 + MT_SAFE=yes + ;; +no) + mt_safe_val=0 + MT_SAFE=no + ;; +esac +]) + +AC_ARG_ENABLE(extra-threadsafe-checking, +[ --enable-extra-threadsafe-checking Build a cygwin DLL which is thread safe with extra consistency checking], +[case "${enableval}" in +yes) + mt_safe_val=1 + MT_SAFE=yes + AC_DEFINE(_CYG_THREAD_FAILSAFE) + ;; +no) + dnl Don't do anything here to avoid overriding --enable-threadsafe. + ;; +esac +]) + +if test "$MT_SAFE" = "yes"; then + AC_DEFINE_UNQUOTED(_MT_SAFE,$mt_safe_val) +fi + +dnl Makefile uses MT_SAFE, so we subst as well as defining it. +AC_SUBST(MT_SAFE) + +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 +]) + +MALLOC_OFILES= +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 + +dnl +dnl If newlib is part of build tree, always set EXE_LDFLAGS to point to +dnl it; this is important in cases where the installed newlib is perhaps +dnl not compatible. Check and warn for installed newlib only if it's not +dnl part of the build tree. +dnl + +AC_MSG_CHECKING([if newlib is part of the build tree]) + +EXE_LDFLAGS= +if test -d ../newlib +then + AC_MSG_RESULT(yes) + EXE_LDFLAGS="-B../../newlib/ -B../" +else + AC_MSG_RESULT(no) +fi + AC_SUBST(EXE_LDFLAGS) + +if test x"$EXE_LDFLAGS" = x +then + AC_MSG_CHECKING([if installed newlib needed]) + AC_TRY_LINK(, + [/* main already defined */] + , + AC_MSG_RESULT(no) + , + AC_MSG_RESULT(yes) + AC_MSG_WARN(newlib not found - utility .exe's may not link)) +fi +AC_SUBST(EXE_LDFLAGS) + +case "$target_cpu" in + i386|i486|i586|i686) DLL_ENTRY="_dll_entry@12" + DEF_DLL_ENTRY="dll_entry@12" + ALLOCA="_alloca" + CONFIG_DIR="i386" ;; + powerpc*) DLL_ENTRY="dll_entry" + DEF_DLL_ENTRY="dll_entry" + ALLOCA=" __allocate_stack" + CONFIG_DIR="ppc" ;; + *) AC_MSG_ERROR(Invalid target processor \"$target_cpu\") ;; +esac + +AC_SUBST(MALLOC_OFILES) +AC_SUBST(DLL_ENTRY) +AC_SUBST(DEF_DLL_ENTRY) +AC_SUBST(ALLOCA) +AC_SUBST(CONFIG_DIR) +AC_OUTPUT(Makefile cygwin.def:cygwin.din) diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc new file mode 100644 index 00000000000..4e4034bf663 --- /dev/null +++ b/winsup/cygwin/dcrt0.cc @@ -0,0 +1,1141 @@ +/* dcrt0.cc -- essentially the main() for the Cygwin dll + + 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 <unistd.h> +#include <stdlib.h> +#include "glob.h" +#include "exceptions.h" +#include <ctype.h> +#include <limits.h> +#include <wingdi.h> +#include <winuser.h> +#include <errno.h> +#include "sigproc.h" +#include "pinfo.h" +#include "cygerrno.h" +#define NEED_VFORK +#include "perprocess.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "child_info_magic.h" +#include "perthread.h" +#include "shared_info.h" +#include "cygwin_version.h" +#include "dll_init.h" +#include "cygthread.h" + +#define MAX_AT_FILE_LEVEL 10 + +#define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0])) + +HANDLE NO_COPY hMainProc; +HANDLE NO_COPY hMainThread; + +sigthread NO_COPY mainthread; // ID of the main thread + +per_thread_waitq NO_COPY waitq_storage; +per_thread_vfork NO_COPY vfork_storage; +per_thread_signal_dispatch NO_COPY signal_dispatch_storage; + +per_thread NO_COPY *threadstuff[] = {&waitq_storage, + &vfork_storage, + &signal_dispatch_storage, + NULL}; + +BOOL display_title; +BOOL strip_title_path; +BOOL allow_glob = TRUE; +codepage_type current_codepage = ansi_cp; + +int cygwin_finished_initializing; + +/* Used in SIGTOMASK for generating a bit for insertion into a sigset_t. + This is subtracted from the signal number prior to shifting the bit. + In older versions of cygwin, the signal was used as-is to shift the + bit for masking. So, we'll temporarily detect this and set it to zero + for programs that are linked using older cygwins. This is just a stopgap + measure to allow an orderly transfer to the new, correct sigmask method. */ +unsigned NO_COPY int signal_shift_subtract = 1; + +ResourceLocks _reslock NO_COPY; +MTinterface _mtinterf; + +bool NO_COPY _cygwin_testing; +unsigned NO_COPY _cygwin_testing_magic; + +char NO_COPY almost_null[1]; + +extern "C" +{ + /* This is an exported copy of environ which can be used by DLLs + which use cygwin.dll. */ + char **__cygwin_environ; + char ***main_environ; + /* __progname used in getopt error message */ + char *__progname; + struct _reent reent_data = _REENT_INIT(reent_data); + struct per_process __cygwin_user_data = + {/* initial_sp */ 0, /* magic_biscuit */ 0, + /* dll_major */ CYGWIN_VERSION_DLL_MAJOR, + /* dll_major */ CYGWIN_VERSION_DLL_MINOR, + /* impure_ptr_ptr */ NULL, /* envptr */ NULL, + /* malloc */ malloc, /* free */ free, + /* realloc */ realloc, + /* fmode_ptr */ NULL, /* main */ NULL, /* ctors */ NULL, + /* dtors */ NULL, /* data_start */ NULL, /* data_end */ NULL, + /* bss_start */ NULL, /* bss_end */ NULL, + /* calloc */ calloc, + /* premain */ {NULL, NULL, NULL, NULL}, + /* run_ctors_p */ 0, + /* unused */ {0, 0, 0, 0, 0, 0, 0}, + /* forkee */ 0, + /* hmodule */ NULL, + /* api_major */ CYGWIN_VERSION_API_MAJOR, + /* api_minor */ CYGWIN_VERSION_API_MINOR, + /* unused2 */ {0, 0, 0, 0, 0}, + /* resourcelocks */ &_reslock, /* threadinterface */ &_mtinterf, + /* impure_ptr */ &reent_data, + }; + bool ignore_case_with_glob; + int __declspec (dllexport) _check_for_executable = TRUE; +#ifdef DEBUGGING + int pinger; +#endif +}; + +char *old_title; +char title_buf[TITLESIZE + 1]; + +static void +do_global_dtors (void) +{ + if (user_data->dtors) + { + void (**pfunc)() = user_data->dtors; + while (*++pfunc) + (*pfunc) (); + } +} + +static void __stdcall +do_global_ctors (void (**in_pfunc)(), int force) +{ + if (!force) + { + if (user_data->forkee || user_data->run_ctors_p) + return; // inherit constructed stuff from parent pid + user_data->run_ctors_p = 1; + } + + /* Run ctors backwards, so skip the first entry and find how many + there are, then run them. */ + + void (**pfunc)() = in_pfunc; + + while (*++pfunc) + ; + while (--pfunc > in_pfunc) + (*pfunc) (); + + if (user_data->magic_biscuit == SIZEOF_PER_PROCESS) + atexit (do_global_dtors); +} + +/* + * Replaces @file in the command line with the contents of the file. + * There may be multiple @file's in a single command line + * A \@file is replaced with @file so that echo \@foo would print + * @foo and not the contents of foo. + */ +static int __stdcall +insert_file (char *name, char *&cmd) +{ + HANDLE f; + DWORD size; + + f = CreateFile (name + 1, + GENERIC_READ, /* open for reading */ + FILE_SHARE_READ, /* share for reading */ + &sec_none_nih, /* no security */ + OPEN_EXISTING, /* existing file only */ + FILE_ATTRIBUTE_NORMAL, /* normal file */ + NULL); /* no attr. template */ + + if (f == INVALID_HANDLE_VALUE) + { + debug_printf ("couldn't open file '%s', %E", name); + return FALSE; + } + + /* This only supports files up to about 4 billion bytes in + size. I am making the bold assumption that this is big + enough for this feature */ + size = GetFileSize (f, NULL); + if (size == 0xFFFFFFFF) + { + debug_printf ("couldn't get file size for '%s', %E", name); + return FALSE; + } + + int new_size = strlen (cmd) + size + 2; + char *tmp = (char *) malloc (new_size); + if (!tmp) + { + debug_printf ("malloc failed, %E"); + return FALSE; + } + + /* realloc passed as it should */ + DWORD rf_read; + BOOL rf_result; + rf_result = ReadFile (f, tmp, size, &rf_read, NULL); + CloseHandle (f); + if (!rf_result || (rf_read != size)) + { + debug_printf ("ReadFile failed, %E"); + return FALSE; + } + + tmp[size++] = ' '; + strcpy (tmp + size, cmd); + cmd = tmp; + return TRUE; +} + +static inline int +isquote (char c) +{ + char ch = c; + return ch == '"' || ch == '\''; +} + +/* Step over a run of characters delimited by quotes */ +static /*__inline*/ char * +quoted (char *cmd, int winshell) +{ + char *p; + char quote = *cmd; + + if (!winshell) + { + char *p; + strcpy (cmd, cmd + 1); + if ((p = strchr (cmd, quote)) != NULL) + strcpy (p, p + 1); + else + p = strchr (cmd, '\0'); + return p; + } + + const char *s = quote == '\'' ? "'" : "\\\""; + /* This must have been run from a Windows shell, so preserve + quotes for globify to play with later. */ + while (*cmd && *++cmd) + if ((p = strpbrk (cmd, s)) == NULL) + { + cmd = strchr (cmd, '\0'); // no closing quote + break; + } + else if (*p == '\\') + cmd = ++p; + else if (quote == '"' && p[1] == '"') + { + *p = '\\'; + cmd = ++p; // a quoted quote + } + else + { + cmd = p + 1; // point to after end + break; + } + return cmd; +} + +/* Perform a glob on word if it contains wildcard characters. + Also quote every character between quotes to force glob to + treat the characters literally. */ +static int __stdcall +globify (char *word, char **&argv, int &argc, int &argvlen) +{ + if (*word != '~' && strpbrk (word, "?*[\"\'(){}") == NULL) + return 0; + + int n = 0; + char *p, *s; + int dos_spec = isdrive (word); + if (!dos_spec && isquote (*word) && word[1] && word[2]) + dos_spec = isdrive (word + 1); + + /* We'll need more space if there are quoting characters in + word. If that is the case, doubling the size of the + string should provide more than enough space. */ + if (strpbrk (word, "'\"")) + n = strlen (word); + char pattern[strlen (word) + ((dos_spec + 1) * n) + 1]; + + /* Fill pattern with characters from word, quoting any + characters found within quotes. */ + for (p = pattern, s = word; *s != '\000'; s++, p++) + if (!isquote (*s)) + { + if (dos_spec && *s == '\\') + *p++ = '\\'; + *p = *s; + } + else + { + char quote = *s; + while (*++s && *s != quote) + { + if (dos_spec || *s != '\\') + /* nothing */; + else if (s[1] == quote || s[1] == '\\') + s++; + *p++ = '\\'; + *p++ = *s; + } + if (*s == quote) + p--; + if (*s == '\0') + break; + } + + *p = '\0'; + + glob_t gl; + gl.gl_offs = 0; + + /* Attempt to match the argument. Return just word (minus quoting) if no match. */ + if (glob (pattern, GLOB_TILDE | GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE, NULL, &gl) || !gl.gl_pathc) + return 0; + + /* Allocate enough space in argv for the matched filenames. */ + n = argc; + if ((argc += gl.gl_pathc) > argvlen) + { + argvlen = argc + 10; + argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0])); + } + + /* Copy the matched filenames to argv. */ + char **gv = gl.gl_pathv; + char **av = argv + n; + while (*gv) + { + debug_printf ("argv[%d] = '%s'", n++, *gv); + *av++ = *gv++; + } + + /* Clean up after glob. */ + free (gl.gl_pathv); + return 1; +} + +/* Build argv, argc from string passed from Windows. */ + +static void __stdcall +build_argv (char *cmd, char **&argv, int &argc, int winshell) +{ + int argvlen = 0; + int nesting = 0; // monitor "nesting" from insert_file + + argc = 0; + argvlen = 0; + argv = NULL; + + /* Scan command line until there is nothing left. */ + while (*cmd) + { + /* Ignore spaces */ + if (issep (*cmd)) + { + cmd++; + continue; + } + + /* Found the beginning of an argument. */ + char *word = cmd; + char *sawquote = NULL; + while (*cmd) + { + if (*cmd != '"' && (!winshell || *cmd != '\'')) + cmd++; // Skip over this character + else + /* Skip over characters until the closing quote */ + { + sawquote = cmd; + cmd = quoted (cmd, winshell && argc > 0); + } + if (issep (*cmd)) // End of argument if space + break; + } + if (*cmd) + *cmd++ = '\0'; // Terminate `word' + + /* Possibly look for @file construction assuming that this isn't + the very first argument and the @ wasn't quoted */ + if (argc && sawquote != word && *word == '@') + { + if (++nesting > MAX_AT_FILE_LEVEL) + api_fatal ("Too many levels of nesting for %s", word); + if (insert_file (word, cmd)) + continue; // There's new stuff in cmd now + } + + /* See if we need to allocate more space for argv */ + if (argc >= argvlen) + { + argvlen = argc + 10; + argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0])); + } + + /* Add word to argv file after (optional) wildcard expansion. */ + if (!winshell || !argc || !globify (word, argv, argc, argvlen)) + { + debug_printf ("argv[%d] = '%s'", argc, word); + argv[argc++] = word; + } + } + + argv[argc] = NULL; + + debug_printf ("argc %d", argc); +} + +/* sanity and sync check */ +void __stdcall +check_sanity_and_sync (per_process *p) +{ + /* Sanity check to make sure developers didn't change the per_process */ + /* struct without updating SIZEOF_PER_PROCESS [it makes them think twice */ + /* about changing it]. */ + if (sizeof (per_process) != SIZEOF_PER_PROCESS) + { + api_fatal ("per_process sanity check failed"); + } + + /* Make sure that the app and the dll are in sync. */ + + /* Complain if older than last incompatible change */ + if (p->dll_major < CYGWIN_VERSION_DLL_EPOCH) + api_fatal ("cygwin DLL and APP are out of sync -- DLL version mismatch %d < %d", + p->dll_major, CYGWIN_VERSION_DLL_EPOCH); + + /* magic_biscuit != 0 if using the old style version numbering scheme. */ + if (p->magic_biscuit != SIZEOF_PER_PROCESS) + api_fatal ("Incompatible cygwin .dll -- incompatible per_process info %d != %d", + p->magic_biscuit, SIZEOF_PER_PROCESS); + + /* Complain if incompatible API changes made */ + if (p->api_major != cygwin_version.api_major) + api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %d < %d", + p->api_major, cygwin_version.api_major); + + if (CYGWIN_VERSION_DLL_MAKE_COMBINED (p->dll_major, p->dll_minor) <= + CYGWIN_VERSION_DLL_BAD_SIGNAL_MASK) + signal_shift_subtract = 0; +} + +child_info NO_COPY *child_proc_info = NULL; +static MEMORY_BASIC_INFORMATION NO_COPY sm; + +#define CYGWIN_GUARD ((wincap.has_page_guard ()) ? \ + PAGE_EXECUTE_READWRITE|PAGE_GUARD : PAGE_NOACCESS) + +// __inline__ void +extern void +alloc_stack_hard_way (child_info_fork *ci, volatile char *b) +{ + void *new_stack_pointer; + MEMORY_BASIC_INFORMATION m; + void *newbase; + int newlen; + LPBYTE curbot = (LPBYTE) sm.BaseAddress + sm.RegionSize; + bool noguard; + + if (ci->stacktop > (LPBYTE) sm.AllocationBase && ci->stacktop < curbot) + { + newbase = curbot; + newlen = (LPBYTE) ci->stackbottom - (LPBYTE) curbot; + noguard = 1; + } + else + { + newbase = ci->stacktop; + newlen = (DWORD) ci->stackbottom - (DWORD) ci->stacktop; + noguard = 0; + } + if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS)) + api_fatal ("fork: can't reserve memory for stack %p - %p, %E", + ci->stacktop, ci->stackbottom); + + new_stack_pointer = (void *) ((LPBYTE) ci->stackbottom - ci->stacksize); + + if (!VirtualAlloc (new_stack_pointer, ci->stacksize, MEM_COMMIT, + PAGE_EXECUTE_READWRITE)) + api_fatal ("fork: can't commit memory for stack %p(%d), %E", + new_stack_pointer, ci->stacksize); + if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m)) + api_fatal ("fork: couldn't get new stack info, %E"); + if (!noguard) + { + m.BaseAddress = (LPVOID)((DWORD)m.BaseAddress - 1); + if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT, + CYGWIN_GUARD)) + api_fatal ("fork: couldn't allocate new stack guard page %p, %E", + m.BaseAddress); + } + if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m)) + api_fatal ("fork: couldn't get new stack info, %E"); + ci->stacktop = m.BaseAddress; + *b = 0; +} + +/* extend the stack prior to fork longjmp */ + +static void +alloc_stack (child_info_fork *ci) +{ + /* FIXME: adding 16384 seems to avoid a stack copy problem during + fork on Win95, but I don't know exactly why yet. DJ */ + volatile char b[ci->stacksize + 16384]; + + if (!VirtualQuery ((LPCVOID) &b, &sm, sizeof sm)) + api_fatal ("fork: couldn't get stack info, %E"); + + if (sm.AllocationBase != ci->stacktop) + alloc_stack_hard_way (ci, b + sizeof (b) - 1); + else + ci->stacksize = 0; + + return; +} + +static NO_COPY int mypid = 0; +int _declspec(dllexport) __argc; +char _declspec(dllexport) **__argv; +vfork_save NO_COPY *main_vfork = NULL; + +void +sigthread::init (const char *s) +{ + InitializeCriticalSection (&lock); + id = GetCurrentThreadId (); +} + +/* Take over from libc's crt0.o and start the application. Note the + various special cases when Cygwin DLL is being runtime loaded (as + opposed to being link-time loaded by Cygwin apps) from a non + cygwin app via LoadLibrary. */ +static void +dll_crt0_1 () +{ + /* According to onno@stack.urc.tue.nl, the exception handler record must + be on the stack. */ + /* FIXME: Verify forked children get their exception handler set up ok. */ + exception_list cygwin_except_entry; + + /* Initialize SIGSEGV handling, etc. */ + init_exceptions (&cygwin_except_entry); + + /* Set the os_being_run global. */ + wincap.init (); + device::init (); + check_sanity_and_sync (user_data); + + do_global_ctors (&__CTOR_LIST__, 1); + + /* Nasty static stuff needed by newlib -- point to a local copy of + the reent stuff. + Note: this MUST be done here (before the forkee code) as the + fork copy code doesn't copy the data in libccrt0.cc (that's why we + pass in the per_process struct into the .dll from libccrt0). */ + + _impure_ptr = &reent_data; + + user_data->resourcelocks->Init (); + user_data->threadinterface->Init (user_data->forkee); + + mainthread.init ("mainthread"); // For use in determining if signals + // should be blocked. + + winpids::init (); + + int envc = 0; + char **envp = NULL; + + if (!child_proc_info) + memory_init (); + else + { + bool close_ppid_handle = false; + bool close_hexec_proc = false; + switch (child_proc_info->type) + { + case _PROC_FORK: + alloc_stack (fork_info); + cygheap_fixup_in_child (0); + memory_init (); + set_myself (mypid); + close_ppid_handle = !!child_proc_info->pppid_handle; + break; + case _PROC_SPAWN: + /* Have to delay closes until after cygheap is setup */ + close_hexec_proc = !!spawn_info->hexec_proc; + close_ppid_handle = !!child_proc_info->pppid_handle; + goto around; + case _PROC_EXEC: + hexec_proc = spawn_info->hexec_proc; + around: + HANDLE h; + cygheap_fixup_in_child (1); + memory_init (); + if (!spawn_info->moreinfo->myself_pinfo || + !DuplicateHandle (hMainProc, spawn_info->moreinfo->myself_pinfo, + hMainProc, &h, 0, 0, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) + h = NULL; + set_myself (mypid, h); + __argc = spawn_info->moreinfo->argc; + __argv = spawn_info->moreinfo->argv; + envp = spawn_info->moreinfo->envp; + envc = spawn_info->moreinfo->envc; + cygheap->fdtab.fixup_after_exec (spawn_info->parent); + signal_fixup_after_exec (child_proc_info->type == PROC_SPAWN); + CloseHandle (spawn_info->parent); + if (spawn_info->moreinfo->old_title) + { + old_title = strcpy (title_buf, spawn_info->moreinfo->old_title); + cfree (spawn_info->moreinfo->old_title); + } + break; + } + if (close_hexec_proc) + CloseHandle (spawn_info->hexec_proc); + if (close_ppid_handle) + CloseHandle (child_proc_info->pppid_handle); + } + + ProtectHandle (hMainProc); + ProtectHandle (hMainThread); + cygthread::init (); + + pthread::initMainThread (!user_data->forkee); + + /* Initialize debug muto, if DLL is built with --enable-debugging. + Need to do this before any helper threads start. */ + debug_init (); + + cygheap->fdtab.vfork_child_fixup (); + + (void) SetErrorMode (SEM_FAILCRITICALERRORS); + + /* Initialize events. */ + events_init (); + + cygheap->cwd.init (); + main_vfork = vfork_storage.create (); + + cygbench ("pre-forkee"); + if (user_data->forkee) + { + /* If we've played with the stack, stacksize != 0. That means that + fork() was invoked from other than the main thread. Make sure that + frame pointer is referencing the new stack so that the OS knows what + to do when it needs to increase the size of the stack. + + NOTE: Don't do anything that involves the stack until you've completed + this step. */ + if (fork_info->stacksize) + { + asm ("movl %0,%%fs:4" : : "r" (fork_info->stackbottom)); + asm ("movl %0,%%fs:8" : : "r" (fork_info->stacktop)); + } + + longjmp (fork_info->jmp, fork_info->cygpid); + } + +#ifdef DEBUGGING + { + extern void fork_init (); + fork_init (); + } +#endif + + /* Initialize our process table entry. */ + pinfo_init (envp, envc); + + if (!old_title && GetConsoleTitle (title_buf, TITLESIZE)) + old_title = title_buf; + + /* Allocate cygheap->fdtab */ + dtable_init (); + + /* Init global well known SID objects */ + cygsid::init (); + + /* Initialize uid, gid if necessary. */ + if (child_proc_info == NULL || spawn_info->moreinfo->uid == ILLEGAL_UID) + uinfo_init (); + + /* Initialize signal/subprocess handling. */ + sigproc_init (); + + /* Connect to tty. */ + tty_init (); + + if (!__argc) + { + char *line = GetCommandLineA (); + line = strcpy ((char *) alloca (strlen (line) + 1), line); + + if (current_codepage == oem_cp) + CharToOemA (line, line); + + /* Scan the command line and build argv. Expand wildcards if not + called from another cygwin process. */ + build_argv (line, __argv, __argc, + NOTSTATE (myself, PID_CYGPARENT) && allow_glob); + + /* Convert argv[0] to posix rules if it's currently blatantly + win32 style. */ + if ((strchr (__argv[0], ':')) || (strchr (__argv[0], '\\'))) + { + char *new_argv0 = (char *) alloca (MAX_PATH); + cygwin_conv_to_posix_path (__argv[0], new_argv0); + __argv[0] = new_argv0; + } + } + + if (user_data->premain[0]) + for (unsigned int i = 0; i < PREMAIN_LEN / 2; i++) + user_data->premain[i] (__argc, __argv, user_data); + + /* Set up standard fds in file descriptor table. */ + cygheap->fdtab.stdio_init (); + + /* Set up __progname for getopt error call. */ + if (__argv[0] && (__progname = strrchr (__argv[0], '/'))) + ++__progname; + else + __progname = __argv[0]; + if (__progname) + { + char *cp = strchr (__progname, '\0') - 4; + if (cp > __progname && strcasematch (cp, ".exe")) + *cp = '\0'; + } + + /* Set new console title if appropriate. */ + + if (display_title && !dynamically_loaded) + { + char *cp = __progname; + if (strip_title_path) + for (char *ptr = cp; *ptr && *ptr != ' '; ptr++) + if (isdirsep (*ptr)) + cp = ptr + 1; + set_console_title (cp); + } + + cygwin_finished_initializing = 1; + /* Call init of loaded dlls. */ + dlls.init (); + + /* Execute any specified "premain" functions */ + if (user_data->premain[PREMAIN_LEN / 2]) + for (unsigned int i = PREMAIN_LEN / 2; i < PREMAIN_LEN; i++) + user_data->premain[i] (__argc, __argv, user_data); + + debug_printf ("user_data->main %p", user_data->main); + + if (dynamically_loaded) + { + set_errno (0); + return; + } + + /* Disable case-insensitive globbing */ + ignore_case_with_glob = FALSE; + + /* Flush signals and ensure that signal thread is up and running. Can't + do this for noncygwin case since the signal thread is blocked due to + LoadLibrary serialization. */ + wait_for_sigthread (); + + set_errno (0); + + MALLOC_CHECK; + cygbench (__progname); + if (user_data->main) + exit (user_data->main (__argc, __argv, *user_data->envptr)); +} + +#ifdef DEBUGGING +void +break_here () +{ + debug_printf ("break here"); +} +#endif + +void +initial_env () +{ + DWORD len; + char buf[MAX_PATH + 1]; +#ifdef DEBUGGING + if (GetEnvironmentVariable ("CYGWIN_SLEEP", buf, sizeof (buf) - 1)) + { + DWORD ms = atoi (buf); + buf[0] = '\0'; + len = GetModuleFileName (NULL, buf, MAX_PATH); + console_printf ("Sleeping %d, pid %u %s\n", ms, GetCurrentProcessId (), buf); + Sleep (ms); + } + if (GetEnvironmentVariable ("CYGWIN_DEBUG", buf, sizeof (buf) - 1)) + { + char buf1[MAX_PATH + 1]; + len = GetModuleFileName (NULL, buf1, MAX_PATH); + strlwr (buf1); + strlwr (buf); + char *p = strchr (buf, '='); + if (!p) + p = (char *) "gdb.exe -nw"; + else + *p++ = '\0'; + if (strstr (buf1, buf)) + { + error_start_init (p); + try_to_debug (); + break_here (); + } + } +#endif + + if (GetEnvironmentVariable ("CYGWIN_TESTING", buf, sizeof (buf) - 1)) + { + _cygwin_testing = 1; + if ((len = GetModuleFileName (cygwin_hmodule, buf, MAX_PATH)) + && len > sizeof ("new-cygwin1.dll") + && strcasematch (buf + len - sizeof ("new-cygwin1.dll"), + "\\new-cygwin1.dll")) + _cygwin_testing_magic = 0x10; + } +} + +/* Wrap the real one, otherwise gdb gets confused about + two symbols with the same name, but different addresses. + + UPTR is a pointer to global data that lives on the libc side of the + line [if one distinguishes the application from the dll]. */ + +extern "C" void __stdcall +_dll_crt0 () +{ + DECLARE_TLS_STORAGE; + initial_env (); + char zeros[sizeof (fork_info->zero)] = {0}; + static NO_COPY STARTUPINFO si; +#ifdef DEBUGGING + strace.microseconds (); +#endif + + main_environ = user_data->envptr; + *main_environ = NULL; + + early_stuff_init (); + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (), + GetCurrentProcess (), &hMainProc, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + hMainProc = GetCurrentProcess (); + + DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc, + &hMainThread, 0, false, DUPLICATE_SAME_ACCESS); + + GetStartupInfo (&si); + child_proc_info = (child_info *) si.lpReserved2; + if (si.cbReserved2 < EXEC_MAGIC_SIZE || !child_proc_info + || memcmp (child_proc_info->zero, zeros, sizeof (zeros)) != 0) + child_proc_info = NULL; + else + { + if ((child_proc_info->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC) + multiple_cygwin_problem ("proc", child_proc_info->intro, 0); + else if (child_proc_info->intro == PROC_MAGIC_GENERIC + && child_proc_info->magic != CHILD_INFO_MAGIC) + multiple_cygwin_problem ("proc", child_proc_info->magic, + CHILD_INFO_MAGIC); + else if (child_proc_info->cygheap != (void *) &_cygheap_start) + multiple_cygwin_problem ("cygheap", (DWORD) child_proc_info->cygheap, + (DWORD) &_cygheap_start); + unsigned should_be_cb = 0; + switch (child_proc_info->type) + { + case _PROC_FORK: + user_data->forkee = child_proc_info->cygpid; + should_be_cb = sizeof (child_info_fork); + /* fall through */; + case _PROC_SPAWN: + case _PROC_EXEC: + if (!should_be_cb) + should_be_cb = sizeof (child_info); + if (should_be_cb != child_proc_info->cb) + multiple_cygwin_problem ("proc size", child_proc_info->cb, should_be_cb); + else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb) + multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union)); + else + { + cygwin_mount_h = child_proc_info->mount_h; + mypid = child_proc_info->cygpid; + break; + } + default: + system_printf ("unknown exec type %d", child_proc_info->type); + /* intentionally fall through */ + case _PROC_WHOOPS: + child_proc_info = NULL; + break; + } + } + dll_crt0_1 (); +} + +void +dll_crt0 (per_process *uptr) +{ + DECLARE_TLS_STORAGE; + /* Set the local copy of the pointer into the user space. */ + if (uptr && uptr != user_data) + { + memcpy (user_data, uptr, per_process_overwrite); + *(user_data->impure_ptr_ptr) = &reent_data; + } + _dll_crt0 (); +} + +/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll */ +extern "C" void +cygwin_dll_init () +{ + static char **envp; + static int _fmode; + + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (), + GetCurrentProcess (), &hMainProc, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + hMainProc = GetCurrentProcess (); + + DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc, + &hMainThread, 0, FALSE, DUPLICATE_SAME_ACCESS); + user_data->magic_biscuit = sizeof (per_process); + + user_data->envptr = &envp; + user_data->fmode_ptr = &_fmode; + + dll_crt0_1 (); +} + +extern "C" void +__main (void) +{ + do_global_ctors (user_data->ctors, FALSE); +} + +enum exit_states + { + ES_NOT_EXITING = 0, + ES_THREADTERM, + ES_SIGNAL, + ES_CLOSEALL, + ES_SIGPROCTERMINATE, + ES_TITLE, + ES_HUP_PGRP, + ES_HUP_SID, + ES_TTY_TERMINATE, + ES_EVENTS_TERMINATE + }; + +exit_states NO_COPY exit_state; +extern CRITICAL_SECTION exit_lock; + +extern "C" void __stdcall +do_exit (int status) +{ + EnterCriticalSection (&exit_lock); + UINT n = (UINT) status; + + syscall_printf ("do_exit (%d)", n); + + vfork_save *vf = vfork_storage.val (); + if (vf != NULL && vf->pid < 0) + vf->restore_exit (status); + + if (exit_state < ES_THREADTERM) + { + exit_state = ES_THREADTERM; + cygthread::terminate (); + } + + if (exit_state < ES_SIGNAL) + { + exit_state = ES_SIGNAL; + if (!(n & EXIT_REPARENTING)) + { + signal (SIGCHLD, SIG_IGN); + signal (SIGHUP, SIG_IGN); + signal (SIGINT, SIG_IGN); + signal (SIGQUIT, SIG_IGN); + } + } + + if (exit_state < ES_CLOSEALL) + { + exit_state = ES_CLOSEALL; + close_all_files (); + } + + if (exit_state < ES_SIGPROCTERMINATE) + { + exit_state = ES_SIGPROCTERMINATE; + sigproc_terminate (); + } + + myself->stopsig = 0; + if (exit_state < ES_TITLE) + { + exit_state = ES_TITLE; + /* restore console title */ + if (old_title && display_title) + set_console_title (old_title); + } + + if (exit_state < ES_HUP_PGRP) + { + exit_state = ES_HUP_PGRP; + /* Kill orphaned children on group leader exit */ + if (myself->has_pgid_children && myself->pid == myself->pgid) + { + sigproc_printf ("%d == pgrp %d, send SIG{HUP,CONT} to stopped children", + myself->pid, myself->pgid); + kill_pgrp (myself->pgid, -SIGHUP); + } + } + + if (exit_state < ES_HUP_SID) + { + exit_state = ES_HUP_SID; + /* Kill the foreground process group on session leader exit */ + if (getpgrp () > 0 && myself->pid == myself->sid && real_tty_attached (myself)) + { + tty *tp = cygwin_shared->tty[myself->ctty]; + sigproc_printf ("%d == sid %d, send SIGHUP to children", + myself->pid, myself->sid); + + /* CGF FIXME: This can't be right. */ + if (tp->getsid () == myself->sid) + tp->kill_pgrp (SIGHUP); + } + + } + + if (exit_state < ES_TTY_TERMINATE) + { + exit_state = ES_TTY_TERMINATE; + tty_terminate (); + } + + if (exit_state < ES_EVENTS_TERMINATE) + { + exit_state = ES_EVENTS_TERMINATE; + events_terminate (); + } + + minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n); + myself->exit (n); +} + +extern "C" void +_exit (int n) +{ + do_exit ((DWORD) n & 0xffff); +} + +extern "C" void +__api_fatal (const char *fmt, ...) +{ + char buf[4096]; + va_list ap; + + va_start (ap, fmt); + __small_vsprintf (buf, fmt, ap); + va_end (ap); + strcat (buf, "\n"); + int len = strlen (buf); + DWORD done; + (void) WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0); + + /* Make sure that the message shows up on the screen, too, since this is + a serious error. */ + if (GetFileType (GetStdHandle (STD_ERROR_HANDLE)) != FILE_TYPE_CHAR) + { + HANDLE h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_WRITE, + &sec_none, OPEN_EXISTING, 0, 0); + if (h != INVALID_HANDLE_VALUE) + (void) WriteFile (h, buf, len, &done, 0); + } + + /* We are going down without mercy. Make sure we reset + our process_state. */ + sigproc_terminate (); +#ifdef DEBUGGING + (void) try_to_debug (); +#endif + myself->exit (1); +} + +void +multiple_cygwin_problem (const char *what, unsigned magic_version, unsigned version) +{ + if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap"))) + { + child_proc_info->type = _PROC_WHOOPS; + return; + } + + char buf[1024]; + if (GetEnvironmentVariable ("CYGWIN_MISMATCH_OK", buf, sizeof (buf))) + return; + + if (CYGWIN_VERSION_MAGIC_VERSION (magic_version) == version) + system_printf ("%s magic number mismatch detected - %p/%p", what, magic_version, version); + else + api_fatal ("%s version mismatch detected - %p/%p.\n\ +You have multiple copies of cygwin1.dll on your system.\n\ +Search for cygwin1.dll using the Windows Start->Find/Search facility\n\ +and delete all but the most recent version. The most recent version *should*\n\ +reside in x:\\cygwin\\bin, where 'x' is the drive on which you have\n\ +installed the cygwin distribution.", what, magic_version, version); +} + +#ifdef DEBUGGING +void __stdcall +cygbench (const char *s) +{ + char buf[1024]; + if (GetEnvironmentVariable ("CYGWIN_BENCH", buf, sizeof (buf))) + small_printf ("%05d ***** %s : %10d\n", GetCurrentProcessId (), s, strace.microseconds ()); +} +#endif diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc new file mode 100644 index 00000000000..6342e22231f --- /dev/null +++ b/winsup/cygwin/dtable.cc @@ -0,0 +1,883 @@ +/* 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 <errno.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 "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "ntdll.h" + +static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, + STD_ERROR_HANDLE}; + +static const char *handle_to_fn (HANDLE, char *); + +static const char NO_COPY unknown_file[] = "some disk file"; + +/* Set aside space for the table of fds */ +void +dtable_init (void) +{ + if (!cygheap->fdtab.size) + cygheap->fdtab.extend (NOFILE_INCR); +} + +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::dec_console_fds () +{ + if (console_fds > 0 && !--console_fds && + myself->ctty != TTY_CONSOLE && !check_pty_fds()) + FreeConsole (); +} + +int +dtable::extend (int howmuch) +{ + int new_size = size + howmuch; + fhandler_base **newfds; + + if (howmuch <= 0) + 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"); + return 0; + } + if (fds) + { + memcpy (newfds, fds, size * sizeof (fds[0])); + cfree (fds); + } + + 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]) + { + path_conv pc; + HANDLE h = GetStdHandle (std_consts[i]); + fhandler_base *fh = build_fhandler_from_name (i, std[i], NULL, pc); + if (!fh) + continue; + if (!fh->open (&pc, (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 (); +} + +int +dtable::find_unused_handle (int start) +{ + AssertResourceOwner (LOCK_FD_LIST, READ_LOCK); + 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)) + { + switch (fds[fd]->get_device ()) + { + case FH_SOCKET: + dec_need_fixup_before (); + break; + case FH_CONSOLE: + dec_console_fds (); + break; + } + delete fds[fd]; + 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 (); + path_conv pc; + fhandler_base *res = cygheap->fdtab.build_fhandler_from_name (fd, name, handle, + pc); + res->init (handle, myaccess, bin ?: 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 = *socket_dev; + else if (GetCommState (handle, &dcb)) + dev.parse ("/dev/ttyS0"); + else + { + name = handle_to_fn (handle, (char *) alloca (MAX_PATH + 100)); + bin = 0; + } + } + + if (!name && !dev) + fds[fd] = NULL; + else + { + path_conv pc; + fhandler_base *fh; + + if (dev) + fh = build_fhandler (fd, dev); + else + fh = build_fhandler_from_name (fd, name, handle, pc); + + if (!bin) + { + bin = fh->get_default_fmode (O_RDWR); + if (bin) + /* nothing */; + else if (dev) + bin = O_BINARY; + else if (name != unknown_file) + bin = pc.binmode (); + } + + fh->init (handle, GENERIC_READ | GENERIC_WRITE, bin); + set_std_handle (fd); + paranoid_printf ("fd %d, handle %p", fd, handle); + } +} + +fhandler_base * +dtable::build_fhandler_from_name (int fd, const char *name, HANDLE handle, + path_conv& pc, unsigned opt, suffix_info *si) +{ + pc.check (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si); + if (pc.error) + { + set_errno (pc.error); + return NULL; + } + + if (!pc.exists () && handle) + pc.fillin (handle); + + fhandler_base *fh = build_fhandler (fd, pc.dev, + pc.return_and_clear_normalized_path (), + pc); + return fh; +} + +fhandler_base * +dtable::build_fhandler (int fd, const device& dev, const char *unix_name, + const char *win32_name) +{ + return build_fhandler (fd, dev, cstrdup (unix_name), win32_name); +} + +#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name +fhandler_base * +dtable::build_fhandler (int fd, const device& dev, char *unix_name, + const char *win32_name) +{ + fhandler_base *fh = NULL; + + if (dev.upper) + switch (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: + case DEV_RAWDRIVE_MAJOR: + fh = cnew (fhandler_dev_floppy) (); + break; + case DEV_TAPE_MAJOR: + fh = cnew (fhandler_dev_tape) (); + break; + } + else + switch (dev) + { + case FH_CONSOLE: + case FH_CONIN: + case FH_CONOUT: + if ((fh = cnew (fhandler_console) ())) + inc_console_fds (); + break; + case FH_PTYM: + fh = cnew (fhandler_pty_master) (); + break; + case FH_WINDOWS: + fh = cnew (fhandler_windows) (); + break; + case FH_SERIAL: + fh = cnew (fhandler_serial) (); + 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_SOCKET: + if ((fh = cnew (fhandler_socket) ())) + inc_need_fixup_before (); + 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: + { + device newdev = dev; + newdev.tty_to_real_device (); + switch (newdev) + { + case FH_CONSOLE: + if ((fh = cnew (fhandler_console) ())) + inc_console_fds (); + break; + case FH_TTYS: + fh = cnew (fhandler_tty_slave) (); + break; + } + } + } + + if (!fh) + { + set_errno (ENODEV); +#ifdef DEBUGGING + system_printf ("unknown device - %p, '%s', upper %d", + (int) dev, dev.name, dev.upper); + return NULL; +#endif + } + + char w32buf[MAX_PATH + 1]; + if (!unix_name || !*unix_name) + { + if (!win32_name && dev.fmt && *dev.fmt) + { + sprintf (w32buf, dev.fmt, dev.minor); + win32_name = w32buf; + } + if (win32_name) + { + unix_name = cstrdup (w32buf); + for (char *p = strchr (unix_name, '\\'); p; p = strchr (p + 1, '\\')) + *p = '/'; + } + } + + fh->dev = dev; + if (unix_name) + { + if (!win32_name) + { + /* FIXME: ? Should we call win32_device_name here? + It seems like overkill, but... */ + win32_name = strcpy (w32buf, unix_name); + for (char *p = w32buf; (p = strchr (p, '/')); p++) + *p = '\\'; + } + fh->set_name (unix_name, win32_name); + } + + debug_printf ("fd %d, fh %p", fd, fh); + return fd >= 0 ? (fds[fd] = fh) : fh; +} + +fhandler_base * +dtable::dup_worker (fhandler_base *oldfh) +{ + fhandler_base *newfh = build_fhandler (-1, oldfh->dev); + *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); + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); + + 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; + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); + syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd); + + return res; +} + +fhandler_fifo * +dtable::find_fifo (ATOM hill) +{ + SetResourceLock (LOCK_FD_LIST, READ_LOCK, "dup"); + 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) +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_fork"); + 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); + } + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_fork"); +} + +void +dtable::fixup_before_exec (DWORD target_proc_id) +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec"); + 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); + } + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec"); +} + +void +dtable::set_file_pointers_for_exec () +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "set_file_pointers_for_exec"); + 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); + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec"); +} + +void +dtable::fixup_after_exec (HANDLE parent) +{ + first_fd_for_open = 0; + fhandler_base *fh; + for (size_t i = 0; i < size; i++) + if ((fh = fds[i]) != NULL) + { + fh->clear_readahead (); + if (fh->get_close_on_exec ()) + 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; + 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 ()); + } +} + +int +dtable::vfork_child_dup () +{ + fhandler_base **newtable; + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); + newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0])); + int res = 1; + + /* Remove impersonation */ + if (cygheap->user.issetuid ()) + RevertToSelf (); + + 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 */ + if (cygheap->user.issetuid ()) + ImpersonateLoggedOnUser (cygheap->user.token); + + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup"); + return 1; +} + +void +dtable::vfork_parent_restore () +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "restore"); + + close_all_files (); + fhandler_base **deleteme = fds; + fds = fds_on_hold; + fds_on_hold = NULL; + cfree (deleteme); + + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "restore"); + 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) cygheap->fdtab.size; i++) + if ((fh = cygheap->fdtab[i]) != NULL) + { + fh->clear_readahead (); + if (fh->get_close_on_exec ()) + release (i); + else + { + fh->close (); + cygheap->fdtab.release (i); + } + } + + fds = saveme; + cfree (fds_on_hold); + fds_on_hold = NULL; + + return; +} + +#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[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 = strchr (win32_fn + DEVICE_PREFIX_LEN, '\\'); + if (!p) + p = strchr (win32_fn + DEVICE_PREFIX_LEN, '\0'); + + int n = p - win32_fn; + int maxmatchlen = 0; + char *maxmatchdos = NULL; + for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1) + { + char device[MAX_PATH + 10]; + device[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..07c976e86b3 --- /dev/null +++ b/winsup/cygwin/dtable.h @@ -0,0 +1,93 @@ +/* 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" + +class suffix_info; +class fhandler_fifo; + +class dtable +{ + fhandler_base **fds; + fhandler_base **fds_on_hold; + int first_fd_for_open; + int cnt_need_fixup_before; + int console_fds; +public: + size_t size; + + dtable () : first_fd_for_open(3), cnt_need_fixup_before(0), console_fds(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; } + + void dec_console_fds (); + void inc_console_fds () + { console_fds++; } + BOOL has_console_fds () + { return console_fds > 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); + fhandler_base *build_fhandler (int fd, const device& dev, const char *unix_name, + const char *win32_name = NULL); + fhandler_base *build_fhandler (int fd, const device& dev, char *unix_name = NULL, + const char *win32_name = NULL); + fhandler_base *build_fhandler_from_name (int fd, const char *name, HANDLE h, + path_conv& pc, + unsigned opts = PC_SYM_FOLLOW, + suffix_info *si = NULL); + inline int not_open (int fd) + { + SetResourceLock (LOCK_FD_LIST, READ_LOCK, "not_open"); + + int res = fd < 0 || fd >= (int) size || fds[fd] == NULL; + + ReleaseResourceLock (LOCK_FD_LIST, READ_LOCK, "not open"); + 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 (); + bool in_vfork_cleanup () {return fds_on_hold == fds;} + fhandler_fifo *find_fifo (ATOM); +}; + +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/fhandler.cc b/winsup/cygwin/fhandler.cc new file mode 100644 index 00000000000..664cac0b610 --- /dev/null +++ b/winsup/cygwin/fhandler.cc @@ -0,0 +1,1329 @@ +/* fhandler.cc. See console.cc for fhandler_console functions. + + Copyright 1996, 1997, 1998, 1999, 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. */ + +#include "winsup.h" +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <sys/uio.h> +#include <signal.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "cygwin/version.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "shared_info.h" +#include "pinfo.h" +#include <assert.h> +#include <limits.h> + +static NO_COPY const int CHUNK_SIZE = 1024; /* Used for crlf conversions */ + +struct __cygwin_perfile *perfile_table; + +DWORD binmode; + +inline fhandler_base& +fhandler_base::operator =(fhandler_base &x) +{ + memcpy (this, &x, sizeof *this); + unix_path_name = x.unix_path_name ? cstrdup (x.unix_path_name) : NULL; + win32_path_name = x.win32_path_name ? cstrdup (x.win32_path_name) : NULL; + rabuf = NULL; + ralen = 0; + raixget = 0; + raixput = 0; + rabuflen = 0; + return *this; +} + +int +fhandler_base::puts_readahead (const char *s, size_t len) +{ + int success = 1; + while ((*s || (len != (size_t) -1 && len--)) + && (success = put_readahead (*s++) > 0)) + continue; + return success; +} + +int +fhandler_base::put_readahead (char value) +{ + char *newrabuf; + if (raixput < rabuflen) + /* Nothing to do */; + else if ((newrabuf = (char *) realloc (rabuf, rabuflen += 32))) + rabuf = newrabuf; + else + return 0; + + rabuf[raixput++] = value; + ralen++; + return 1; +} + +int +fhandler_base::get_readahead () +{ + int chret = -1; + if (raixget < ralen) + chret = ((unsigned char) rabuf[raixget++]) & 0xff; + /* FIXME - not thread safe */ + if (raixget >= ralen) + raixget = raixput = ralen = 0; + return chret; +} + +int +fhandler_base::peek_readahead (int queryput) +{ + int chret = -1; + if (!queryput && raixget < ralen) + chret = ((unsigned char) rabuf[raixget]) & 0xff; + else if (queryput && raixput > 0) + chret = ((unsigned char) rabuf[raixput - 1]) & 0xff; + return chret; +} + +void +fhandler_base::set_readahead_valid (int val, int ch) +{ + if (!val) + ralen = raixget = raixput = 0; + if (ch != -1) + put_readahead (ch); +} + +int +fhandler_base::eat_readahead (int n) +{ + int oralen = ralen; + if (n < 0) + n = ralen; + if (n > 0 && ralen) + { + if ((int) (ralen -= n) < 0) + ralen = 0; + + if (raixget >= ralen) + raixget = raixput = ralen = 0; + else if (raixput > ralen) + raixput = ralen; + } + + return oralen; +} + +int +fhandler_base::get_readahead_into_buffer (char *buf, size_t buflen) +{ + int ch; + int copied_chars = 0; + + while (buflen) + if ((ch = get_readahead ()) < 0) + break; + else + { + buf[copied_chars++] = (unsigned char)(ch & 0xff); + buflen--; + } + + return copied_chars; +} + +/* Record the file name. + Filenames are used mostly for debugging messages, and it's hoped that + in cases where the name is really required, the filename wouldn't ever + be too long (e.g. devices or some such). + The unix_path_name is also used by virtual fhandlers. */ +void +fhandler_base::set_name (const char *unix_path, const char *win32_path) +{ + if (unix_path == NULL || !*unix_path) + return; + + if (win32_path) + win32_path_name = cstrdup (win32_path); + else + { + const char *fmt = get_native_name (); + char *w = (char *) cmalloc (HEAP_STR, strlen (fmt) + 16); + __small_sprintf (w, fmt, get_unit ()); + win32_path_name = w; + } + + if (win32_path_name == NULL) + { + system_printf ("fatal error. strdup failed"); + exit (ENOMEM); + } + + assert (unix_path_name == NULL); + /* FIXME: This isn't really right. It ignores the first argument if we're + building names for a device and just converts the device name from the + win32 name since it has theoretically been previously detected by + path_conv. Ideally, we should pass in a format string and build the + unix_path, too. */ + if (!is_auto_device () || *win32_path_name != '\\') + unix_path_name = unix_path; + else + { + char *p = cstrdup (win32_path_name); + unix_path_name = p; + while ((p = strchr (p, '\\')) != NULL) + *p++ = '/'; + if (unix_path) + cfree ((void *) unix_path); + } + + if (unix_path_name == NULL) + { + system_printf ("fatal error. strdup failed"); + exit (ENOMEM); + } + namehash = hash_path_name (0, win32_path_name); +} + +/* Detect if we are sitting at EOF for conditions where Windows + returns an error but UNIX doesn't. */ +static int __stdcall +is_at_eof (HANDLE h, DWORD err) +{ + DWORD size, upper1, curr; + + size = GetFileSize (h, &upper1); + if (upper1 != 0xffffffff || GetLastError () == NO_ERROR) + { + LONG upper2 = 0; + curr = SetFilePointer (h, 0, &upper2, FILE_CURRENT); + if (curr == size && upper1 == (DWORD) upper2) + return 1; + } + + SetLastError (err); + return 0; +} + +void +fhandler_base::set_flags (int flags, int supplied_bin) +{ + int bin; + int fmode; + debug_printf ("flags %p, supplied_bin %p", flags, supplied_bin); + if ((bin = flags & (O_BINARY | O_TEXT))) + debug_printf ("O_TEXT/O_BINARY set in flags %p", bin); + else if (get_r_binset () && get_w_binset ()) + bin = get_r_binary () ? O_BINARY : O_TEXT; // FIXME: Not quite right + else if ((fmode = get_default_fmode (flags)) & O_BINARY) + bin = O_BINARY; + else if (fmode & O_TEXT) + bin = O_TEXT; + else if (supplied_bin) + bin = supplied_bin; + else + bin = get_w_binary () || get_r_binary () || (binmode != O_TEXT) + ? O_BINARY : O_TEXT; + + openflags = flags | bin; + + bin &= O_BINARY; + set_r_binary (bin); + set_w_binary (bin); + syscall_printf ("filemode set to %s", bin ? "binary" : "text"); +} + +/* Normal file i/o handlers. */ + +/* Cover function to ReadFile to achieve (as much as possible) Posix style + semantics and use of errno. */ +void +fhandler_base::raw_read (void *ptr, size_t& ulen) +{ +#define bytes_read ((ssize_t) ulen) + + HANDLE h = NULL; /* grumble */ + int prio = 0; /* ditto */ + DWORD len = ulen; + + (ssize_t) ulen = -1; + if (read_state) + { + h = GetCurrentThread (); + prio = GetThreadPriority (h); + (void) SetThreadPriority (h, THREAD_PRIORITY_TIME_CRITICAL); + SetEvent (read_state); + } + BOOL res = ReadFile (get_handle (), ptr, len, (DWORD *) &ulen, 0); + if (read_state) + { + SetEvent (read_state); + (void) SetThreadPriority (h, prio); + } + if (!res) + { + /* Some errors are not really errors. Detect such cases here. */ + + DWORD errcode = GetLastError (); + switch (errcode) + { + case ERROR_BROKEN_PIPE: + /* This is really EOF. */ + bytes_read = 0; + break; + case ERROR_MORE_DATA: + /* `bytes_read' is supposedly valid. */ + break; + case ERROR_NOACCESS: + if (is_at_eof (get_handle (), errcode)) + { + bytes_read = 0; + break; + } + case ERROR_INVALID_FUNCTION: + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_HANDLE: + if (openflags & O_DIROPEN) + { + set_errno (EISDIR); + bytes_read = -1; + break; + } + default: + syscall_printf ("ReadFile %s failed, %E", unix_path_name); + __seterrno_from_win_error (errcode); + bytes_read = -1; + break; + } + } +#undef bytes_read +} + +/* Cover function to WriteFile to provide Posix interface and semantics + (as much as possible). */ +int +fhandler_base::raw_write (const void *ptr, size_t len) +{ + DWORD bytes_written; + + if (!WriteFile (get_output_handle (), ptr, len, &bytes_written, 0)) + { + if (GetLastError () == ERROR_DISK_FULL && bytes_written > 0) + return bytes_written; + __seterrno (); + if (get_errno () == EPIPE) + raise (SIGPIPE); + return -1; + } + return bytes_written; +} + +#define ACCFLAGS(x) (x & (O_RDONLY | O_WRONLY | O_RDWR)) +int +fhandler_base::get_default_fmode (int flags) +{ + int fmode = __fmode; + if (perfile_table) + { + size_t nlen = strlen (get_name ()); + unsigned accflags = ACCFLAGS (flags); + for (__cygwin_perfile *pf = perfile_table; pf->name; pf++) + if (!*pf->name && ACCFLAGS (pf->flags) == accflags) + { + fmode = pf->flags & ~(O_RDONLY | O_WRONLY | O_RDWR); + break; + } + else + { + size_t pflen = strlen (pf->name); + const char *stem = get_name () + nlen - pflen; + if (pflen > nlen || (stem != get_name () && !isdirsep (stem[-1]))) + continue; + else if (ACCFLAGS (pf->flags) == accflags && strcasematch (stem, pf->name)) + { + fmode = pf->flags & ~(O_RDONLY | O_WRONLY | O_RDWR); + break; + } + } + } + return fmode; +} + +bool +fhandler_base::device_access_denied (int flags) +{ + int mode = 0; + if (flags & O_RDWR) + mode |= R_OK | W_OK; + if (flags & (O_WRONLY | O_APPEND)) + mode |= W_OK; + if (!mode) + mode |= R_OK; + + return ::access (get_win32_name (), mode); +} + +/* Open system call handler function. */ +int +fhandler_base::open (path_conv *pc, int flags, mode_t mode) +{ + int res = 0; + HANDLE x; + int file_attributes; + int shared; + int creation_distribution; + SECURITY_ATTRIBUTES sa = sec_none; + + syscall_printf ("(%s, %p) query_open %d", get_win32_name (), flags, get_query_open ()); + + if (get_win32_name () == NULL) + { + set_errno (ENOENT); + goto done; + } + + if (get_query_open ()) + access = 0; + else if (get_device () == FH_TAPE) + access = GENERIC_READ | GENERIC_WRITE; + else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY) + access = GENERIC_READ; + else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY) + access = GENERIC_WRITE; + else + access = GENERIC_READ | GENERIC_WRITE; + + /* Allow reliable lseek on disk devices. */ + if (get_device () == FH_FLOPPY) + access |= GENERIC_READ; + + /* FIXME: O_EXCL handling? */ + + if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY)) + { + if (flags & O_CREAT) + creation_distribution = CREATE_ALWAYS; + else + creation_distribution = TRUNCATE_EXISTING; + } + else if (flags & O_CREAT) + creation_distribution = OPEN_ALWAYS; + else + creation_distribution = OPEN_EXISTING; + + if ((flags & O_EXCL) && (flags & O_CREAT)) + creation_distribution = CREATE_NEW; + + if (flags & O_APPEND) + set_append_p (); + + /* These flags are host dependent. */ + shared = wincap.shared (); + + file_attributes = FILE_ATTRIBUTE_NORMAL; + if (flags & O_DIROPEN) + file_attributes |= FILE_FLAG_BACKUP_SEMANTICS; + if (get_device () == FH_SERIAL) + file_attributes |= FILE_FLAG_OVERLAPPED; + +#ifdef HIDDEN_DOT_FILES + if (flags & O_CREAT && get_device () == FH_FS) + { + char *c = strrchr (get_win32_name (), '\\'); + if ((c && c[1] == '.') || *get_win32_name () == '.') + file_attributes |= FILE_ATTRIBUTE_HIDDEN; + } +#endif + + /* CreateFile() with dwDesiredAccess == 0 when called on remote + share returns some handle, even if file doesn't exist. This code + works around this bug. */ + if (get_query_open () && isremote () && + creation_distribution == OPEN_EXISTING && pc && !pc->exists ()) + { + set_errno (ENOENT); + goto done; + } + + /* If mode has no write bits set, we set the R/O attribute. */ + if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + file_attributes |= FILE_ATTRIBUTE_READONLY; + + /* If the file should actually be created and ntsec is on, + set files attributes. */ + if (flags & O_CREAT && get_device () == FH_FS && allow_ntsec && has_acls ()) + set_security_attribute (mode, &sa, alloca (4096), 4096); + + x = CreateFile (get_win32_name (), access, shared, &sa, creation_distribution, + file_attributes, 0); + + syscall_printf ("%p = CreateFile (%s, %p, %p, %p, %p, %p, 0)", + x, get_win32_name (), access, shared, &sa, + creation_distribution, file_attributes); + + if (x == INVALID_HANDLE_VALUE) + { + if (!wincap.can_open_directories () && pc && pc->isdir ()) + { + if (flags & (O_CREAT | O_EXCL) == (O_CREAT | O_EXCL)) + set_errno (EEXIST); + else if (flags & (O_WRONLY | O_RDWR)) + set_errno (EISDIR); + else + set_nohandle (true); + } + else if (GetLastError () == ERROR_INVALID_HANDLE) + set_errno (ENOENT); + else + __seterrno (); + if (!get_nohandle ()) + goto done; + } + + /* Attributes may be set only if a file is _really_ created. + This code is now only used for ntea here since the files + security attributes are set in CreateFile () now. */ + if (flags & O_CREAT && get_device () == FH_FS + && GetLastError () != ERROR_ALREADY_EXISTS + && !allow_ntsec && allow_ntea) + set_file_attribute (has_acls (), get_win32_name (), mode); + + set_io_handle (x); + set_flags (flags, pc ? pc->binmode () : 0); + + res = 1; + set_open_status (); +done: + syscall_printf ("%d = fhandler_base::open (%s, %p)", res, get_win32_name (), + flags); + return res; +} + +/* states: + open buffer in binary mode? Just do the read. + + open buffer in text mode? Scan buffer for control zs and handle + the first one found. Then scan buffer, converting every \r\n into + an \n. If last char is an \r, look ahead one more char, if \n then + modify \r, if not, remember char. +*/ +void +fhandler_base::read (void *in_ptr, size_t& len) +{ + char *ptr = (char *) in_ptr; + ssize_t copied_chars = 0; + bool need_signal = !!read_state; + int c; + + while (len) + if ((c = get_readahead ()) < 0) + break; + else + { + ptr[copied_chars++] = (unsigned char) (c & 0xff); + len--; + } + + if (copied_chars && is_slow ()) + { + len = (size_t) copied_chars; + goto out; + } + + if (!len) + { + len = (size_t) copied_chars; + goto out; + } + + raw_read (ptr + copied_chars, len); + need_signal = false; + if (!copied_chars) + /* nothing */; + else if ((ssize_t) len > 0) + len += copied_chars; + else + len = copied_chars; + + if (get_r_binary () || len <= 0) + goto out; + + /* Scan buffer and turn \r\n into \n */ + char *src, *dst, *end; + src = (char *) ptr; + dst = (char *) ptr; + end = src + len - 1; + + /* Read up to the last but one char - the last char needs special handling */ + while (src < end) + { + if (*src == '\r' && src[1] == '\n') + src++; + *dst++ = *src++; + } + + /* If not beyond end and last char is a '\r' then read one more + to see if we should translate this one too */ + if (src > end) + /* nothing */; + else if (*src != '\r') + *dst++ = *src; + else + { + char c1; + size_t c1len = 1; + raw_read (&c1, c1len); + if (c1len <= 0) + /* nothing */; + else if (c1 == '\n') + *dst++ = '\n'; + else + { + set_readahead_valid (1, c1); + *dst++ = *src; + } + } + + len = dst - (char *) ptr; + +#ifndef NOSTRACE + if (strace.active) + { + char buf[16 * 6 + 1]; + char *p = buf; + + for (int i = 0; i < copied_chars && i < 16; ++i) + { + unsigned char c = ((unsigned char *) ptr)[i]; + /* >= 33 so space prints in hex */ + __small_sprintf (p, c >= 33 && c <= 127 ? " %c" : " %p", c); + p += strlen (p); + } + debug_printf ("read %d bytes (%s%s)", copied_chars, buf, + copied_chars > 16 ? " ..." : ""); + } +#endif + +out: + if (need_signal) + SetEvent (read_state); + + debug_printf ("returning %d, %s mode", len, + get_r_binary () ? "binary" : "text"); + return; +} + +int +fhandler_base::write (const void *ptr, size_t len) +{ + int res; + + if (get_append_p ()) + SetFilePointer (get_output_handle (), 0, 0, FILE_END); + else if (wincap.has_lseek_bug () && get_check_win95_lseek_bug ()) + { + /* Note: this bug doesn't happen on NT4, even though the documentation + for WriteFile() says that it *may* happen on any OS. */ + int actual_length, current_position; + set_check_win95_lseek_bug (0); /* don't do it again */ + actual_length = GetFileSize (get_output_handle (), NULL); + current_position = SetFilePointer (get_output_handle (), 0, 0, FILE_CURRENT); + if (current_position > actual_length) + { + /* Oops, this is the bug case - Win95 uses whatever is on the disk + instead of some known (safe) value, so we must seek back and + fill in the gap with zeros. - DJ */ + char zeros[512]; + int number_of_zeros_to_write = current_position - actual_length; + memset (zeros, 0, 512); + SetFilePointer (get_output_handle (), 0, 0, FILE_END); + while (number_of_zeros_to_write > 0) + { + DWORD zeros_this_time = (number_of_zeros_to_write > 512 + ? 512 : number_of_zeros_to_write); + DWORD written; + if (!WriteFile (get_output_handle (), zeros, zeros_this_time, &written, + NULL)) + { + __seterrno (); + if (get_errno () == EPIPE) + raise (SIGPIPE); + /* This might fail, but it's the best we can hope for */ + SetFilePointer (get_output_handle (), current_position, 0, FILE_BEGIN); + return -1; + + } + if (written < zeros_this_time) /* just in case */ + { + set_errno (ENOSPC); + /* This might fail, but it's the best we can hope for */ + SetFilePointer (get_output_handle (), current_position, 0, FILE_BEGIN); + return -1; + } + number_of_zeros_to_write -= written; + } + } + } + + if (get_w_binary ()) + { + debug_printf ("binary write"); + res = raw_write (ptr, len); + } + else + { + debug_printf ("text write"); + /* This is the Microsoft/DJGPP way. Still not ideal, but it's + compatible. + Modified slightly by CGF 2000-10-07 */ + + int left_in_data = len; + char *data = (char *)ptr; + res = 0; + + while (left_in_data > 0) + { + char buf[CHUNK_SIZE + 1], *buf_ptr = buf; + int left_in_buf = CHUNK_SIZE; + + while (left_in_buf > 0 && left_in_data > 0) + { + char ch = *data++; + if (ch == '\n') + { + *buf_ptr++ = '\r'; + left_in_buf--; + } + *buf_ptr++ = ch; + left_in_buf--; + left_in_data--; + if (left_in_data > 0 && ch == '\r' && *data == '\n') + { + *buf_ptr++ = *data++; + left_in_buf--; + left_in_data--; + } + } + + /* We've got a buffer-full, or we're out of data. Write it out */ + int nbytes; + int want = buf_ptr - buf; + if ((nbytes = raw_write (buf, want)) == want) + { + /* Keep track of how much written not counting additional \r's */ + res = data - (char *)ptr; + continue; + } + + if (nbytes == -1) + res = -1; /* Error */ + else + res += nbytes; /* Partial write. Return total bytes written. */ + break; /* All done */ + } + } + + debug_printf ("%d = write (%p, %d)", res, ptr, len); + return res; +} + +ssize_t +fhandler_base::readv (const struct iovec *const iov, const int iovcnt, + ssize_t tot) +{ + assert (iov); + assert (iovcnt >= 1); + + if (iovcnt == 1) + { + size_t len = iov->iov_len; + read (iov->iov_base, len); + return len; + } + + 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); + } + + assert (tot >= 0); + + if (tot == 0) + return 0; + + char *buf = (char *) alloca (tot); + + if (!buf) + { + set_errno (ENOMEM); + return -1; + } + + read (buf, (size_t) tot); + + const struct iovec *iovptr = iov; + int nbytes = tot; + + 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; + } + + return tot; +} + +ssize_t +fhandler_base::writev (const struct iovec *const iov, const int iovcnt, + ssize_t tot) +{ + assert (iov); + assert (iovcnt >= 1); + + if (iovcnt == 1) + return write (iov->iov_base, iov->iov_len); + + 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); + } + + assert (tot >= 0); + + if (tot == 0) + return 0; + + char *const buf = (char *) alloca (tot); + + if (!buf) + { + set_errno (ENOMEM); + return -1; + } + + 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; + } + + return write (buf, tot); +} + +__off64_t +fhandler_base::lseek (__off64_t offset, int whence) +{ + __off64_t res; + + /* 9x/Me doesn't support 64bit offsets. We trap that here and return + EINVAL. It doesn't make sense to simulate bigger offsets by a + SetFilePointer sequence since FAT and FAT32 don't support file + size >= 4GB anyway. */ + if (!wincap.has_64bit_file_access () + && (offset < LONG_MIN || offset > LONG_MAX)) + { + debug_printf ("Win9x, offset not 32 bit."); + set_errno (EINVAL); + return (__off64_t)-1; + } + + /* Seeks on text files is tough, we rewind and read till we get to the + right place. */ + + if (whence != SEEK_CUR || offset != 0) + { + if (whence == SEEK_CUR) + offset -= ralen - raixget; + set_readahead_valid (0); + } + + debug_printf ("lseek (%s, %D, %d)", unix_path_name, offset, whence); + + DWORD win32_whence = whence == SEEK_SET ? FILE_BEGIN + : (whence == SEEK_CUR ? FILE_CURRENT : FILE_END); + + LONG off_low = offset & 0xffffffff; + LONG *poff_high, off_high; + if (!wincap.has_64bit_file_access ()) + poff_high = NULL; + else + { + off_high = offset >> 32; + poff_high = &off_high; + } + + debug_printf ("setting file pointer to %u (high), %u (low)", off_high, off_low); + res = SetFilePointer (get_handle (), off_low, poff_high, win32_whence); + if (res == INVALID_SET_FILE_POINTER && GetLastError ()) + { + __seterrno (); + } + else + { + /* When next we write(), we will check to see if *this* seek went beyond + the end of the file, and back-seek and fill with zeros if so - DJ */ + set_check_win95_lseek_bug (); + + /* If this was a SEEK_CUR with offset 0, we still might have + readahead that we have to take into account when calculating + the actual position for the application. */ + if (whence == SEEK_CUR) + res -= ralen - raixget; + } + + return res; +} + +int +fhandler_base::close () +{ + int res = -1; + + syscall_printf ("closing '%s' handle %p", get_name (), get_handle ()); + if (get_nohandle () || CloseHandle (get_handle ())) + res = 0; + else + { + paranoid_printf ("CloseHandle (%d <%s>) failed", get_handle (), + get_name ()); + + __seterrno (); + } + return res; +} + +int +fhandler_base::ioctl (unsigned int cmd, void *buf) +{ + if (cmd == FIONBIO) + syscall_printf ("ioctl (FIONBIO, %p)", buf); + else + syscall_printf ("ioctl (%x, %p)", cmd, buf); + + set_errno (EINVAL); + return -1; +} + +int +fhandler_base::lock (int, struct flock *) +{ + set_errno (EINVAL); + return -1; +} + +extern "C" char * __stdcall +rootdir (char *full_path) +{ + /* Possible choices: + * d:... -> d:/ + * \\server\share... -> \\server\share\ + * else current drive. + */ + char *root = full_path; + + if (full_path[1] == ':') + strcpy (full_path + 2, "\\"); + else if (full_path[0] == '\\' && full_path[1] == '\\') + { + char *cp = full_path + 2; + while (*cp && *cp != '\\') + cp++; + if (!*cp) + { + set_errno (ENOTDIR); + return NULL; + } + cp++; + while (*cp && *cp != '\\') + cp++; + strcpy (cp, "\\"); + } + else + root = NULL; + + return root; +} + +int __stdcall +fhandler_base::fstat (struct __stat64 *buf, path_conv *pc) +{ + debug_printf ("here"); + + if (is_fs_special ()) + return fstat_fs (buf, pc); + + switch (get_device ()) + { + case FH_PIPE: + buf->st_mode = S_IFIFO | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; + break; + case FH_PIPEW: + buf->st_mode = S_IFIFO | STD_WBITS | S_IWGRP | S_IWOTH; + break; + case FH_PIPER: + buf->st_mode = S_IFIFO | STD_RBITS; + break; + case FH_FLOPPY: + buf->st_mode = S_IFBLK | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; + break; + default: + buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; + break; + } + + buf->st_nlink = 1; + buf->st_blksize = S_BLKSIZE; + time_as_timestruc_t (&buf->st_ctim); + buf->st_atim = buf->st_mtim = buf->st_ctim; + return 0; +} + +void +fhandler_base::init (HANDLE f, DWORD a, mode_t bin) +{ + set_io_handle (f); + access = a; + a &= GENERIC_READ | GENERIC_WRITE; + int flags = 0; + if (a == GENERIC_READ) + flags = O_RDONLY; + else if (a == GENERIC_WRITE) + flags = O_WRONLY; + else if (a == (GENERIC_READ | GENERIC_WRITE)) + flags = O_RDWR; + set_flags (flags | bin); + set_open_status (); + debug_printf ("created new fhandler_base for handle %p, bin %d", f, get_r_binary ()); +} + +void +fhandler_base::dump (void) +{ + paranoid_printf ("here"); +} + +int +fhandler_base::dup (fhandler_base *child) +{ + debug_printf ("in fhandler_base dup"); + + HANDLE nh; + if (!get_nohandle ()) + { + if (!DuplicateHandle (hMainProc, get_handle (), hMainProc, &nh, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + system_printf ("dup(%s) failed, handle %x, %E", + get_name (), get_handle ()); + __seterrno (); + return -1; + } + + child->set_io_handle (nh); + } + return 0; +} + +int fhandler_base::fcntl (int cmd, void *arg) +{ + int res; + + switch (cmd) + { + case F_GETFD: + res = get_close_on_exec () ? FD_CLOEXEC : 0; + break; + case F_SETFD: + set_close_on_exec ((int) arg); + res = 0; + break; + case F_GETFL: + res = get_flags (); + debug_printf ("GETFL: %d", res); + break; + case F_SETFL: + { + /* + * Only O_APPEND, O_ASYNC and O_NONBLOCK/O_NDELAY are allowed. + * Each other flag will be ignored. + * Since O_ASYNC isn't defined in fcntl.h it's currently + * ignored as well. + */ + const int allowed_flags = O_APPEND | O_NONBLOCK_MASK; + int new_flags = (int) arg & allowed_flags; + /* 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. */ + if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK)) + new_flags = O_NONBLOCK; + set_flags ((get_flags () & ~allowed_flags) | new_flags); + } + res = 0; + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + res = lock (cmd, (struct flock *) arg); + break; + default: + set_errno (EINVAL); + res = -1; + break; + } + return res; +} + +/* Base terminal handlers. These just return errors. */ + +int +fhandler_base::tcflush (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsendbreak (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcdrain (void) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcflow (int) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsetattr (int, const struct termios *) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcgetattr (struct termios *) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcsetpgrp (const pid_t) +{ + set_errno (ENOTTY); + return -1; +} + +int +fhandler_base::tcgetpgrp (void) +{ + set_errno (ENOTTY); + return -1; +} + +void +fhandler_base::operator delete (void *p) +{ + cfree (p); + return; +} + +/* Normal I/O constructor */ +fhandler_base::fhandler_base (): + status (0), + access (0), + io_handle (NULL), + namehash (0), + openflags (0), + rabuf (NULL), + ralen (0), + raixget (0), + raixput (0), + rabuflen (0), + unix_path_name (NULL), + win32_path_name (NULL), + open_status (0), + read_state (NULL) +{ +} + +/* Normal I/O destructor */ +fhandler_base::~fhandler_base (void) +{ + if (unix_path_name != NULL) + cfree ((void *) unix_path_name); + if (win32_path_name != NULL) + cfree ((void *) win32_path_name); + if (rabuf) + free (rabuf); + unix_path_name = win32_path_name = NULL; +} + +/**********************************************************************/ +/* /dev/null */ + +fhandler_dev_null::fhandler_dev_null () : + fhandler_base () +{ +} + +void +fhandler_dev_null::dump (void) +{ + paranoid_printf ("here"); +} + +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, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) + debug_printf ("DuplicateHandle failed, %E"); +#ifdef DEBUGGING_AND_FDS_PROTECTED + if (h) + setclexec (oh, h, not_inheriting); +#endif +} + +void +fhandler_base::fork_fixup (HANDLE parent, HANDLE &h, const char *name) +{ + 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 +} + +void +fhandler_base::set_close_on_exec (int val) +{ + if (!get_nohandle ()) + set_inheritance (io_handle, val); + set_close_on_exec_flag (val); + debug_printf ("set close_on_exec for %s to %d", get_name (), val); +} + +void +fhandler_base::fixup_after_fork (HANDLE parent) +{ + debug_printf ("inheriting '%s' from parent", get_name ()); + if (!get_nohandle ()) + fork_fixup (parent, io_handle, "io_handle"); +} + +bool +fhandler_base::is_nonblocking () +{ + return (openflags & O_NONBLOCK_MASK) != 0; +} + +void +fhandler_base::set_nonblocking (int yes) +{ + int current = openflags & O_NONBLOCK_MASK; + int new_flags = yes ? (!current ? O_NONBLOCK : current) : 0; + openflags = (openflags & ~O_NONBLOCK_MASK) | new_flags; +} + +DIR * +fhandler_base::opendir (path_conv&) +{ + set_errno (ENOTDIR); + return NULL; +} + +struct dirent * +fhandler_base::readdir (DIR *) +{ + set_errno (ENOTDIR); + return NULL; +} + +__off64_t +fhandler_base::telldir (DIR *) +{ + set_errno (ENOTDIR); + return -1; +} + +void +fhandler_base::seekdir (DIR *, __off64_t) +{ + set_errno (ENOTDIR); + return; +} + +void +fhandler_base::rewinddir (DIR *) +{ + set_errno (ENOTDIR); + return; +} + +int +fhandler_base::closedir (DIR *) +{ + set_errno (ENOTDIR); + return -1; +} diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h new file mode 100644 index 00000000000..337d40e5adf --- /dev/null +++ b/winsup/cygwin/fhandler.h @@ -0,0 +1,1250 @@ +/* fhandler.h + + 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. */ + +#ifndef _FHANDLER_H_ +#define _FHANDLER_H_ + +#include <sys/ioctl.h> +#include <fcntl.h> + +enum +{ + FH_RBINARY = 0x00001000, /* binary read mode */ + FH_WBINARY = 0x00002000, /* binary write mode */ + FH_CLOEXEC = 0x00004000, /* close-on-exec */ + FH_RBINSET = 0x00008000, /* binary read mode has been explicitly set */ + FH_WBINSET = 0x00010000, /* binary write mode has been explicitly set */ + FH_APPEND = 0x00020000, /* always append */ + FH_ASYNC = 0x00040000, /* async I/O */ + FH_SIGCLOSE = 0x00080000, /* signal handler should close fd on interrupt */ + + FH_SYMLINK = 0x00100000, /* is a symlink */ + FH_EXECABL = 0x00200000, /* file looked like it would run: + * ends in .exe or .bat or begins with #! */ + FH_W95LSBUG = 0x00400000, /* set when lseek is called as a flag that + * _write should check if we've moved beyond + * EOF, zero filling if so. */ + FH_NOHANDLE = 0x00800000, /* No handle associated with fhandler. */ + FH_NOEINTR = 0x01000000, /* Set if I/O should be uninterruptible. */ + FH_FFIXUP = 0x02000000, /* Set if need to fixup after fork. */ + FH_LOCAL = 0x04000000, /* File is unix domain socket */ + FH_SHUTRD = 0x08000000, /* Socket saw a SHUT_RD */ + FH_SHUTWR = 0x10000000, /* Socket saw a SHUT_WR */ + FH_ISREMOTE = 0x10000000, /* File is on a remote drive */ + FH_DCEXEC = 0x20000000, /* Don't care if this is executable */ + FH_HASACLS = 0x40000000, /* True if fs of file has ACLS */ + FH_QUERYOPEN = 0x80000000, /* open file without requesting either read + or write access */ +}; + +#include "devices.h" + +#define FHDEVN(n) (n) +#define FHISSETF(x) __ISSETF (this, x, FH) +#define FHSETF(x) __SETF (this, x, FH) +#define FHCLEARF(x) __CLEARF (this, x, FH) +#define FHCONDSETF(n, x) __CONDSETF(n, this, x, FH) + +#define FHSTATOFF 0 + +/* fcntl flags used only internaly. */ +#define O_NOSYMLINK 0x080000 +#define O_DIROPEN 0x100000 + +/* newlib used to define O_NDELAY differently from O_NONBLOCK. Now it + properly defines both to be the same. Unfortunately, we have to + behave properly the old version, too, to accommodate older executables. */ +#define OLD_O_NDELAY (CYGWIN_VERSION_CHECK_FOR_OLD_O_NONBLOCK ? 4 : 0) + +/* Care for the old O_NDELAY flag. If one of the flags is set, + both flags are set. */ +#define O_NONBLOCK_MASK (O_NONBLOCK | OLD_O_NDELAY) + +#define UNCONNECTED 0 +#define CONNECT_PENDING 1 +#define CONNECTED 2 + +extern const char *windows_device_names[]; +extern struct __cygwin_perfile *perfile_table; +#define __fmode (*(user_data->fmode_ptr)) +extern const char proc[]; +extern const int proc_len; + +class select_record; +class path_conv; +class fhandler_disk_file; +typedef struct __DIR DIR; +struct dirent; +struct iovec; + +enum line_edit_status +{ + line_edit_ok = 0, + line_edit_input_done = 1, + line_edit_signalled = 2, + line_edit_error = 3, + line_edit_pipe_full = 4 +}; + +enum bg_check_types +{ + bg_error = -1, + bg_eof = 0, + bg_ok = 1, + bg_signalled = 2 +}; + +enum executable_states +{ + is_executable, + dont_care_if_executable, + not_executable = dont_care_if_executable, + dont_know_if_executable +}; + +class fhandler_base +{ + protected: + DWORD status; + private: + int access; + HANDLE io_handle; + + unsigned long namehash; /* hashed filename, used as inode num */ + + protected: + /* Full unix path name of this file */ + /* File open flags from open () and fcntl () calls */ + int openflags; + + char *rabuf; /* used for crlf conversion in text files */ + size_t ralen; + size_t raixget; + size_t raixput; + size_t rabuflen; + + const char *unix_path_name; + const char *win32_path_name; + DWORD open_status; + HANDLE read_state; + + public: + device dev; + void set_name (const char *unix_path, const char *win32_path = NULL); + + virtual fhandler_base& operator =(fhandler_base &x); + fhandler_base (); + virtual ~fhandler_base (); + + /* Non-virtual simple accessor functions. */ + void set_io_handle (HANDLE x) { io_handle = x; } + + DWORD get_device () const { return dev.devn; } + DWORD get_major () const { return dev.major; } + DWORD get_minor () const { return dev.minor; } + virtual int get_unit () const { return dev.minor; } + + int get_access () const { return access; } + void set_access (int x) { access = x; } + + bool get_async () { return FHISSETF (ASYNC); } + void set_async (int x) { FHCONDSETF (x, ASYNC); } + + int get_flags () { return openflags; } + void set_flags (int x, int supplied_bin = 0); + + bool is_nonblocking (); + void set_nonblocking (int yes); + + bool get_w_binary () { return FHISSETF (WBINSET) ? FHISSETF (WBINARY) : 1; } + bool get_r_binary () { return FHISSETF (RBINSET) ? FHISSETF (RBINARY) : 1; } + + bool get_w_binset () { return FHISSETF (WBINSET); } + bool get_r_binset () { return FHISSETF (RBINSET); } + + void set_w_binary (int b) { FHCONDSETF (b, WBINARY); FHSETF (WBINSET); } + void set_r_binary (int b) { FHCONDSETF (b, RBINARY); FHSETF (RBINSET); } + void clear_w_binary () {FHCLEARF (WBINARY); FHCLEARF (WBINSET); } + void clear_r_binary () {FHCLEARF (RBINARY); FHCLEARF (RBINSET); } + + bool get_nohandle () { return FHISSETF (NOHANDLE); } + void set_nohandle (int x) { FHCONDSETF (x, NOHANDLE); } + + void set_open_status () {open_status = status;} + DWORD get_open_status () {return open_status;} + void reset_to_open_binmode () + { + set_flags ((get_flags () & ~(O_TEXT | O_BINARY)) + | ((open_status & (FH_WBINARY | FH_RBINARY) + ? O_BINARY : O_TEXT))); + } + + int get_default_fmode (int flags); + + bool get_r_no_interrupt () { return FHISSETF (NOEINTR); } + void set_r_no_interrupt (bool b) { FHCONDSETF (b, NOEINTR); } + + bool get_close_on_exec () { return FHISSETF (CLOEXEC); } + int set_close_on_exec_flag (int b) { return FHCONDSETF (b, CLOEXEC); } + + LPSECURITY_ATTRIBUTES get_inheritance (bool all = 0) + { + if (all) + return get_close_on_exec () ? &sec_all_nih : &sec_all; + else + return get_close_on_exec () ? &sec_none_nih : &sec_none; + } + + void set_check_win95_lseek_bug (int b = 1) { FHCONDSETF (b, W95LSBUG); } + bool get_check_win95_lseek_bug () { return FHISSETF (W95LSBUG); } + + bool get_need_fork_fixup () { return FHISSETF (FFIXUP); } + void set_need_fork_fixup () { FHSETF (FFIXUP); } + + virtual void set_close_on_exec (int val); + + virtual void fixup_before_fork_exec (DWORD) {} + virtual void fixup_after_fork (HANDLE); + virtual void fixup_after_exec (HANDLE) {} + + bool get_symlink_p () { return FHISSETF (SYMLINK); } + void set_symlink_p (int val) { FHCONDSETF (val, SYMLINK); } + void set_symlink_p () { FHSETF (SYMLINK); } + + bool get_socket_p () { return FHISSETF (LOCAL); } + void set_socket_p (int val) { FHCONDSETF (val, LOCAL); } + void set_socket_p () { FHSETF (LOCAL); } + + bool get_execable_p () { return FHISSETF (EXECABL); } + void set_execable_p (executable_states val) + { + FHCONDSETF (val == is_executable, EXECABL); + FHCONDSETF (val == dont_care_if_executable, DCEXEC); + } + void set_execable_p () { FHSETF (EXECABL); } + bool dont_care_if_execable () { return FHISSETF (DCEXEC); } + bool exec_state_isknown () { return FHISSETF (DCEXEC) || FHISSETF (EXECABL); } + + bool get_append_p () { return FHISSETF (APPEND); } + void set_append_p (int val) { FHCONDSETF (val, APPEND); } + void set_append_p () { FHSETF (APPEND); } + + bool get_query_open () { return FHISSETF (QUERYOPEN); } + void set_query_open (bool val) { FHCONDSETF (val, QUERYOPEN); } + + bool get_readahead_valid () { return raixget < ralen; } + int puts_readahead (const char *s, size_t len = (size_t) -1); + int put_readahead (char value); + + int get_readahead (); + int peek_readahead (int queryput = 0); + + int eat_readahead (int n); + + void set_readahead_valid (int val, int ch = -1); + + int get_readahead_into_buffer (char *buf, size_t buflen); + + bool has_acls () { return FHISSETF (HASACLS); } + void set_has_acls (int val) { FHCONDSETF (val, HASACLS); } + + bool isremote () { return FHISSETF (ISREMOTE); } + void set_isremote (int val) { FHCONDSETF (val, ISREMOTE); } + + const char *get_name () { return unix_path_name; } + const char *get_win32_name () { return win32_path_name; } + unsigned long get_namehash () { return namehash; } + + virtual void hclose (HANDLE h) {CloseHandle (h);} + virtual void set_inheritance (HANDLE &h, int not_inheriting); + + /* fixup fd possibly non-inherited handles after fork */ + void fork_fixup (HANDLE parent, HANDLE &h, const char *name); + + virtual int open (path_conv *real_path, int flags, mode_t mode = 0); + int open_fs (path_conv *real_path, int flags, mode_t mode = 0); + virtual int close (); + int close_fs (); + virtual int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + int __stdcall fstat_fs (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + int __stdcall fstat_helper (struct __stat64 *buf, path_conv *pc, + FILETIME ftCreateionTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime, + DWORD nFileSizeHigh, + DWORD nFileSizeLow, + DWORD nFileIndexHigh = 0, + DWORD nFileIndexLow = 0, + DWORD nNumberOfLinks = 1) + __attribute__ ((regparm (3))); + int __stdcall fstat_by_handle (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3))); + int __stdcall fstat_by_name (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3))); + virtual int ioctl (unsigned int cmd, void *); + virtual int fcntl (int cmd, void *); + virtual char const *ttyname () { return get_name(); } + virtual void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + virtual int write (const void *ptr, size_t len); + virtual ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1); + virtual ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1); + virtual __off64_t lseek (__off64_t offset, int whence); + virtual int lock (int, struct flock *); + virtual void dump (); + virtual int dup (fhandler_base *child); + + virtual HANDLE mmap (caddr_t *addr, size_t len, DWORD access, + int flags, __off64_t off); + virtual int munmap (HANDLE h, caddr_t addr, size_t len); + virtual int msync (HANDLE h, caddr_t addr, size_t len, int flags); + virtual BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address); + + void *operator new (size_t, void *p) {return p;} + + virtual void init (HANDLE, DWORD, mode_t); + + virtual int tcflush (int); + virtual int tcsendbreak (int); + virtual int tcdrain (); + virtual int tcflow (int); + virtual int tcsetattr (int a, const struct termios *t); + virtual int tcgetattr (struct termios *t); + virtual int tcsetpgrp (const pid_t pid); + virtual int tcgetpgrp (); + virtual int is_tty () { return 0; } + virtual bool isdevice () { return true; } + virtual bool isfifo () { return false; } + virtual char *ptsname () { return NULL;} + virtual class fhandler_socket *is_socket () { return 0; } + virtual class fhandler_console *is_console () { return 0; } + virtual int is_windows () {return 0; } + + virtual void raw_read (void *ptr, size_t& ulen); + virtual int raw_write (const void *ptr, size_t ulen); + + /* Virtual accessor functions to hide the fact + that some fd's have two handles. */ + virtual HANDLE& get_handle () { return io_handle; } + virtual HANDLE& get_io_handle () { return io_handle; } + virtual HANDLE& get_output_handle () { return io_handle; } + virtual bool hit_eof () {return FALSE;} + virtual select_record *select_read (select_record *s); + virtual select_record *select_write (select_record *s); + virtual select_record *select_except (select_record *s); + virtual int ready_for_read (int fd, DWORD howlong); + virtual const char *get_native_name () + { + return dev.fmt; + } + virtual bg_check_types bg_check (int) {return bg_ok;} + void clear_readahead () + { + raixput = raixget = ralen = rabuflen = 0; + rabuf = NULL; + } + void operator delete (void *); + virtual HANDLE get_guard () const {return NULL;} + virtual void set_eof () {} + virtual DIR *opendir (path_conv& pc); + virtual dirent *readdir (DIR *); + virtual __off64_t telldir (DIR *); + virtual void seekdir (DIR *, __off64_t); + virtual void rewinddir (DIR *); + virtual int closedir (DIR *); + virtual bool is_slow () {return 0;} + bool is_auto_device () {return isdevice () && !dev.isfs ();} + bool is_fs_special () {return dev.isfs ();} + bool device_access_denied (int) __attribute__ ((regparm (1))); +}; + +class fhandler_socket: public fhandler_base +{ + private: + int addr_family; + int type; + int connect_secret [4]; + HANDLE secret_event; + struct _WSAPROTOCOL_INFOA *prot_info_ptr; + char *sun_path; + int had_connect_or_listen; + + public: + fhandler_socket (); + ~fhandler_socket (); + int get_socket () { return (int) get_handle(); } + fhandler_socket *is_socket () { return this; } + + bool saw_shutdown_read () const {return FHISSETF (SHUTRD);} + bool saw_shutdown_write () const {return FHISSETF (SHUTWR);} + + void set_shutdown_read () {FHSETF (SHUTRD);} + void set_shutdown_write () {FHSETF (SHUTWR);} + + bool is_unconnected () const {return had_connect_or_listen == UNCONNECTED;} + bool is_connect_pending () const {return had_connect_or_listen == CONNECT_PENDING;} + bool is_connected () const {return had_connect_or_listen == CONNECTED;} + void set_connect_state (int newstate) { had_connect_or_listen = newstate; } + + int bind (const struct sockaddr *name, int namelen); + int connect (const struct sockaddr *name, int namelen); + int listen (int backlog); + int accept (struct sockaddr *peer, int *len); + int getsockname (struct sockaddr *name, int *namelen); + int getpeername (struct sockaddr *name, int *namelen); + + ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1); + int recvfrom (void *ptr, size_t len, int flags, + struct sockaddr *from, int *fromlen); + int recvmsg (struct msghdr *msg, int flags, ssize_t tot = -1); + + ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1); + int sendto (const void *ptr, size_t len, int flags, + const struct sockaddr *to, int tolen); + int sendmsg (const struct msghdr *msg, int flags, ssize_t tot = -1); + + int ioctl (unsigned int cmd, void *); + int fcntl (int cmd, void *); + __off64_t lseek (__off64_t, int) { return 0; } + int shutdown (int how); + int close (); + void hclose (HANDLE) {close ();} + int dup (fhandler_base *child); + + void set_close_on_exec (int val); + virtual void fixup_before_fork_exec (DWORD); + void fixup_after_fork (HANDLE); + void fixup_after_exec (HANDLE); + + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + void set_addr_family (int af) {addr_family = af;} + int get_addr_family () {return addr_family;} + void set_socket_type (int st) { type = st;} + int get_socket_type () {return type;} + void set_sun_path (const char *path); + char *get_sun_path () {return sun_path;} + void set_connect_secret (); + void get_connect_secret (char*); + HANDLE create_secret_event (int *secret = NULL); + int check_peer_secret_event (struct sockaddr_in *peer, int *secret = NULL); + void signal_secret_event (); + void close_secret_event (); + int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + bool is_slow () {return 1;} +}; + +class fhandler_pipe: public fhandler_base +{ +protected: + HANDLE guard; + bool broken_pipe; + HANDLE writepipe_exists; + DWORD orig_pid; + unsigned id; +public: + fhandler_pipe (); + __off64_t lseek (__off64_t offset, int whence); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + void set_close_on_exec (int val); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int close (); + void create_guard (SECURITY_ATTRIBUTES *sa) {guard = CreateMutex (sa, FALSE, NULL);} + int dup (fhandler_base *child); + int ioctl (unsigned int cmd, void *); + void fixup_after_fork (HANDLE); + void fixup_after_exec (HANDLE); + bool hit_eof (); + void set_eof () {broken_pipe = true;} + HANDLE get_guard () const {return guard;} + int ready_for_read (int fd, DWORD howlong); + static int create (fhandler_pipe *[2], unsigned, int, bool = false); + bool is_slow () {return 1;} + friend class fhandler_fifo; +}; + +class fhandler_fifo: public fhandler_pipe +{ + HANDLE output_handle; + HANDLE owner; // You can't have too many mutexes, now, can you? + ATOM upand; + long read_use; + long write_use; +public: + fhandler_fifo (); + int open (path_conv *, int flags, mode_t mode = 0); + int open_not_mine (int flags) __attribute__ ((regparm (2))); + int close (); + void set_use (int flags) __attribute__ ((regparm (2))); + bool isfifo () { return true; } + HANDLE& get_output_handle () { return output_handle; } + void set_output_handle (HANDLE h) { output_handle = h; } + void set_use (); + bool is_slow () {return 1;} + ATOM& get_atom () {return upand;} +}; + +class fhandler_dev_raw: public fhandler_base +{ + protected: + char *devbuf; + size_t devbufsiz; + size_t devbufstart; + size_t devbufend; + int eom_detected : 1; + int eof_detected : 1; + int lastblk_to_read : 1; + int is_writing : 1; + int has_written : 1; + int varblkop : 1; + + virtual void clear (void); + virtual int writebuf (void); + + /* returns not null, if `win_error' determines an end of media condition */ + virtual int is_eom(int win_error) = 0; + /* returns not null, if `win_error' determines an end of file condition */ + virtual int is_eof(int win_error) = 0; + + fhandler_dev_raw (); + + public: + ~fhandler_dev_raw (void); + + int open (path_conv *, int flags, mode_t mode = 0); + int close (void); + + void raw_read (void *ptr, size_t& ulen); + int raw_write (const void *ptr, size_t ulen); + + int dup (fhandler_base *child); + + int ioctl (unsigned int cmd, void *buf); + + void fixup_after_fork (HANDLE); + void fixup_after_exec (HANDLE); +}; + +class fhandler_dev_floppy: public fhandler_dev_raw +{ + protected: + virtual int is_eom (int win_error); + virtual int is_eof (int win_error); + + public: + fhandler_dev_floppy (); + + virtual int open (path_conv *, int flags, mode_t mode = 0); + virtual int close (void); + + virtual __off64_t lseek (__off64_t offset, int whence); + + virtual int ioctl (unsigned int cmd, void *buf); +}; + +class fhandler_dev_tape: public fhandler_dev_raw +{ + int lasterr; + + bool is_rewind_device () { return get_unit () < 128; } + + protected: + virtual void clear (void); + + virtual int is_eom (int win_error); + virtual int is_eof (int win_error); + + public: + fhandler_dev_tape (); + + virtual int open (path_conv *, int flags, mode_t mode = 0); + virtual int close (void); + + virtual __off64_t lseek (__off64_t offset, int whence); + + virtual int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + + virtual int dup (fhandler_base *child); + + virtual int ioctl (unsigned int cmd, void *buf); + + private: + int tape_write_marks (int marktype, DWORD len); + int tape_get_pos (unsigned long *ret); + int tape_set_pos (int mode, long count, BOOLEAN sfm_func = FALSE); + int tape_erase (int mode); + int tape_prepare (int action); + BOOLEAN tape_get_feature (DWORD parm); + int tape_get_blocksize (long *min, long *def, long *max, long *cur); + int tape_set_blocksize (long count); + int tape_status (struct mtget *get); + int tape_compression (long count); +}; + +/* Standard disk file */ + +class fhandler_disk_file: public fhandler_base +{ + public: + fhandler_disk_file (); + + int open (path_conv *real_path, int flags, mode_t mode); + int close (); + int lock (int, struct flock *); + bool isdevice () { return false; } + int __stdcall fstat (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3))); + + HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, __off64_t off); + int munmap (HANDLE h, caddr_t addr, size_t len); + int msync (HANDLE h, caddr_t addr, size_t len, int flags); + BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address); + DIR *opendir (path_conv& pc); + struct dirent *readdir (DIR *); + __off64_t telldir (DIR *); + void seekdir (DIR *, __off64_t); + void rewinddir (DIR *); + int closedir (DIR *); +}; + +class fhandler_cygdrive: public fhandler_disk_file +{ + int ndrives; + const char *pdrive; + void set_drives (); + public: + bool iscygdrive_root () const { return !dev.minor; } + fhandler_cygdrive (); + DIR *opendir (path_conv& pc); + struct dirent *readdir (DIR *); + __off64_t telldir (DIR *); + void seekdir (DIR *, __off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + int __stdcall fstat (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3))); +}; + +class fhandler_serial: public fhandler_base +{ + private: + size_t vmin_; /* from termios */ + unsigned int vtime_; /* from termios */ + pid_t pgrp_; + int rts; /* for Windows 9x purposes only */ + int dtr; /* for Windows 9x purposes only */ + + public: + int overlapped_armed; + OVERLAPPED io_status; + DWORD ev; + + /* Constructor */ + fhandler_serial (); + + int open (path_conv *, int flags, mode_t mode); + int close (); + void init (HANDLE h, DWORD a, mode_t flags); + void overlapped_setup (); + int dup (fhandler_base *child); + void raw_read (void *ptr, size_t& ulen); + int raw_write (const void *ptr, size_t ulen); + int tcsendbreak (int); + int tcdrain (); + int tcflow (int); + int ioctl (unsigned int cmd, void *); + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + __off64_t lseek (__off64_t, int) { return 0; } + int tcflush (int); + void dump (); + int is_tty () { return 1; } + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (HANDLE); + + /* We maintain a pgrp so that tcsetpgrp and tcgetpgrp work, but we + don't use it for permissions checking. fhandler_tty_slave does + permission checking on pgrps. */ + virtual int tcgetpgrp () { return pgrp_; } + virtual int tcsetpgrp (const pid_t pid) { pgrp_ = pid; return 0; } + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +#define acquire_output_mutex(ms) \ + __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms); + +#define release_output_mutex() \ + __release_output_mutex (__PRETTY_FUNCTION__, __LINE__); + +class tty; +class tty_min; +class fhandler_termios: public fhandler_base +{ + protected: + HANDLE output_handle; + virtual void doecho (const void *, DWORD) {}; + virtual int accept_input () {return 1;}; + public: + tty_min *tc; + fhandler_termios () : + fhandler_base () + { + set_need_fork_fixup (); + } + HANDLE& get_output_handle () { return output_handle; } + line_edit_status line_edit (const char *rptr, int nread, termios&); + void set_output_handle (HANDLE h) { output_handle = h; } + void tcinit (tty_min *this_tc, int force = FALSE); + virtual int is_tty () { return 1; } + int tcgetpgrp (); + int tcsetpgrp (int pid); + bg_check_types bg_check (int sig); + virtual DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms) {return 1;} + virtual void __release_output_mutex (const char *fn, int ln) {} + void fixup_after_fork (HANDLE); + void fixup_after_exec (HANDLE parent) { fixup_after_fork (parent); } + void echo_erase (int force = 0); + virtual __off64_t lseek (__off64_t, int); +}; + +enum ansi_intensity +{ + INTENSITY_INVISIBLE, + INTENSITY_DIM, + INTENSITY_NORMAL, + INTENSITY_BOLD +}; + +#define normal 0 +#define gotesc 1 +#define gotsquare 2 +#define gotarg1 3 +#define gotrsquare 4 +#define gotcommand 5 +#define gettitle 6 +#define eattitle 7 +#define MAXARGS 10 + +class dev_console +{ + WORD default_color, underline_color, dim_color; + + /* Used to determine if an input keystroke should be modified with META. */ + int meta_mask; + +/* Output state */ + int state_; + int args_[MAXARGS]; + int nargs_; + unsigned rarg; + bool saw_question_mark; + + char my_title_buf [TITLESIZE + 1]; + + WORD current_win32_attr; + ansi_intensity intensity; + bool underline, blink, reverse; + WORD fg, bg; + + /* saved cursor coordinates */ + int savex, savey; + + /* saved screen */ + COORD savebufsiz; + PCHAR_INFO savebuf; + + struct + { + short Top, Bottom; + } scroll_region; + struct + { + SHORT winTop; + SHORT winBottom; + COORD dwWinSize; + COORD dwBufferSize; + COORD dwCursorPosition; + WORD wAttributes; + } info; + + COORD dwLastCursorPosition; + DWORD dwLastButtonState; + int nModifiers; + + bool insert_mode; + bool use_mouse; + bool raw_win32_keyboard_mode; + friend class fhandler_console; +}; + +/* This is a input and output console handle */ +class fhandler_console: public fhandler_termios +{ + private: + static dev_console *dev_state; + +/* Output calls */ + void set_default_attr (); + WORD get_win32_attr (); + + BOOL fillin_info (); + void clear_screen (int, int, int, int); + void scroll_screen (int, int, int, int, int, int); + void cursor_set (BOOL, int, int); + void cursor_get (int *, int *); + void cursor_rel (int, int); + const unsigned char *write_normal (unsigned const char*, unsigned const char *); + void char_command (char); + BOOL set_raw_win32_keyboard_mode (BOOL); + int output_tcsetattr (int a, const struct termios *t); + +/* Input calls */ + int igncr_enabled (); + int input_tcsetattr (int a, const struct termios *t); + void set_cursor_maybe (); + + public: + + fhandler_console (); + + fhandler_console* is_console () { return this; } + + int open (path_conv *, int flags, mode_t mode = 0); + + int write (const void *ptr, size_t len); + void doecho (const void *str, DWORD len) { (void) write (str, len); } + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int close (); + + int tcflush (int); + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + + /* Special dup as we must dup two handles */ + int dup (fhandler_base *child); + + int ioctl (unsigned int cmd, void *); + void init (HANDLE, DWORD, mode_t); + bool mouse_aware () {return dev_state->use_mouse;} + + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + void fixup_after_exec (HANDLE); + void set_close_on_exec (int val); + void fixup_after_fork (HANDLE parent); + void set_input_state (); + void send_winch_maybe (); + static tty_min *get_tty_stuff (int); + bool is_slow () {return 1;} +}; + +class fhandler_tty_common: public fhandler_termios +{ + public: + fhandler_tty_common () + : fhandler_termios (), output_done_event (NULL), + ioctl_request_event (NULL), ioctl_done_event (NULL), output_mutex (NULL), + input_mutex (NULL), input_available_event (NULL), inuse (NULL) + { + // nothing to do + } + HANDLE output_done_event; // Raised by master when tty's output buffer + // written. Write status in tty::write_retval. + HANDLE ioctl_request_event; // Raised by slave to perform ioctl() request. + // Ioctl() request in tty::cmd/arg. + HANDLE ioctl_done_event; // Raised by master on ioctl() completion. + // Ioctl() status in tty::ioctl_retval. + HANDLE output_mutex, input_mutex; + HANDLE input_available_event; + HANDLE inuse; // used to indicate that a tty is in use + + DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms); + void __release_output_mutex (const char *fn, int ln); + + virtual int dup (fhandler_base *child); + + tty *get_ttyp () { return (tty *)tc; } + + int close (); + void set_close_on_exec (int val); + void fixup_after_fork (HANDLE parent); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +class fhandler_tty_slave: public fhandler_tty_common +{ + public: + /* Constructor */ + fhandler_tty_slave (); + + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + void init (HANDLE, DWORD, mode_t); + + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + int tcflush (int); + int ioctl (unsigned int cmd, void *); + + __off64_t lseek (__off64_t, int) { return 0; } + select_record *select_read (select_record *s); + int cygserver_attach_tty (HANDLE*, HANDLE*); + int get_unit () __attribute__ ((regparm (1))); +}; + +class fhandler_pty_master: public fhandler_tty_common +{ + int pktmode; // non-zero if pty in a packet mode. +protected: + device slave; // device type of slave +public: + int need_nl; // Next read should start with \n + + /* Constructor */ + fhandler_pty_master (); + + int process_slave_output (char *buf, size_t len, int pktmode_on); + void doecho (const void *str, DWORD len); + int accept_input (); + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int close (); + + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + int tcflush (int); + int ioctl (unsigned int cmd, void *); + + __off64_t lseek (__off64_t, int) { return 0; } + char *ptsname (); + + void set_close_on_exec (int val); + bool hit_eof (); + int get_unit () const { return slave.minor; } +}; + +class fhandler_tty_master: public fhandler_pty_master +{ + public: + /* Constructor */ + fhandler_console *console; // device handler to perform real i/o. + + fhandler_tty_master (); + int init (); + int init_console (); + void set_winsize (bool); + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (HANDLE); + bool is_slow () {return 1;} +}; + +class fhandler_dev_null: public fhandler_base +{ + public: + fhandler_dev_null (); + + void dump (); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); +}; + +class fhandler_dev_zero: public fhandler_base +{ + public: + fhandler_dev_zero (); + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + __off64_t lseek (__off64_t offset, int whence); + + void dump (); +}; + +class fhandler_dev_random: public fhandler_base +{ + protected: + HCRYPTPROV crypt_prov; + long pseudo; + + BOOL crypt_gen_random (void *ptr, size_t len); + int pseudo_write (const void *ptr, size_t len); + int pseudo_read (void *ptr, size_t len); + + public: + fhandler_dev_random (); + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + __off64_t lseek (__off64_t offset, int whence); + int close (void); + int dup (fhandler_base *child); + + void dump (); +}; + +class fhandler_dev_mem: public fhandler_base +{ + protected: + DWORD mem_size; + __off64_t pos; + + public: + fhandler_dev_mem (); + ~fhandler_dev_mem (void); + + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t ulen); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + __off64_t lseek (__off64_t offset, int whence); + int close (void); + int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + int dup (fhandler_base *child); + + HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, __off64_t off); + int munmap (HANDLE h, caddr_t addr, size_t len); + int msync (HANDLE h, caddr_t addr, size_t len, int flags); + BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address); + + void dump (); +} ; + +class fhandler_dev_clipboard: public fhandler_base +{ + public: + fhandler_dev_clipboard (); + int is_windows (void) { return 1; } + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + __off64_t lseek (__off64_t offset, int whence); + int close (void); + + int dup (fhandler_base *child); + + void dump (); + + private: + __off64_t pos; + void *membuffer; + size_t msize; + bool eof; +}; + +class fhandler_windows: public fhandler_base +{ + private: + HWND hWnd_; // the window whose messages are to be retrieved by read() call + int method_; // write method (Post or Send) + public: + fhandler_windows (); + int is_windows (void) { return 1; } + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int ioctl (unsigned int cmd, void *); + __off64_t lseek (__off64_t, int) { return 0; } + int close (void) { return 0; } + + void set_close_on_exec (int val); + void fixup_after_fork (HANDLE parent); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +class fhandler_dev_dsp : public fhandler_base +{ + private: + int audioformat_; + int audiofreq_; + int audiobits_; + int audiochannels_; + bool setupwav(const char *pData, int nBytes); + public: + fhandler_dev_dsp (); + ~fhandler_dev_dsp(); + + int open (path_conv *, int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int ioctl (unsigned int cmd, void *); + __off64_t lseek (__off64_t, int); + int close (void); + int dup (fhandler_base *child); + void dump (void); + void fixup_after_exec (HANDLE); +}; + +class fhandler_virtual : public fhandler_base +{ + protected: + char *filebuf; + size_t bufalloc, filesize; + __off64_t position; + int fileid; // unique within each class + public: + + fhandler_virtual (); + virtual ~fhandler_virtual(); + + virtual int exists(); + DIR *opendir (path_conv& pc); + __off64_t telldir (DIR *); + void seekdir (DIR *, __off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + __off64_t lseek (__off64_t, int); + int dup (fhandler_base *child); + int open (path_conv *, int flags, mode_t mode = 0); + int close (void); + int __stdcall fstat (struct stat *buf, path_conv *pc) __attribute__ ((regparm (3))); + virtual bool fill_filebuf (); + void fixup_after_exec (HANDLE); +}; + +class fhandler_proc: public fhandler_virtual +{ + public: + fhandler_proc (); + int exists(); + struct dirent *readdir (DIR *); + static DWORD get_proc_fhandler(const char *path); + + int open (path_conv *real_path, int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + bool fill_filebuf (); +}; + +class fhandler_registry: public fhandler_proc +{ + private: + char *value_name; + public: + fhandler_registry (); + int exists(); + struct dirent *readdir (DIR *); + __off64_t telldir (DIR *); + void seekdir (DIR *, __off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + + int open (path_conv *real_path, int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + bool fill_filebuf (); + int close (void); +}; + +class pinfo; +class fhandler_process: public fhandler_proc +{ + pid_t pid; + public: + fhandler_process (); + int exists(); + struct dirent *readdir (DIR *); + int open (path_conv *real_path, int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3))); + bool fill_filebuf (); +}; + +typedef union +{ + char __base[sizeof (fhandler_base)]; + char __console[sizeof (fhandler_console)]; + char __cygdrive[sizeof (fhandler_cygdrive)]; + char __dev_clipboard[sizeof (fhandler_dev_clipboard)]; + char __dev_dsp[sizeof (fhandler_dev_dsp)]; + char __dev_floppy[sizeof (fhandler_dev_floppy)]; + char __dev_mem[sizeof (fhandler_dev_mem)]; + char __dev_null[sizeof (fhandler_dev_null)]; + char __dev_random[sizeof (fhandler_dev_random)]; + char __dev_raw[sizeof (fhandler_dev_raw)]; + char __dev_tape[sizeof (fhandler_dev_tape)]; + char __dev_zero[sizeof (fhandler_dev_zero)]; + char __disk_file[sizeof (fhandler_disk_file)]; + char __pipe[sizeof (fhandler_pipe)]; + char __proc[sizeof (fhandler_proc)]; + char __process[sizeof (fhandler_process)]; + char __pty_master[sizeof (fhandler_pty_master)]; + char __registry[sizeof (fhandler_registry)]; + char __serial[sizeof (fhandler_serial)]; + char __socket[sizeof (fhandler_socket)]; + char __termios[sizeof (fhandler_termios)]; + char __tty_common[sizeof (fhandler_tty_common)]; + char __tty_master[sizeof (fhandler_tty_master)]; + char __tty_slave[sizeof (fhandler_tty_slave)]; + char __virtual[sizeof (fhandler_virtual)]; + char __windows[sizeof (fhandler_windows)]; +} fhandler_union; + +struct select_record +{ + int fd; + HANDLE h; + fhandler_base *fh; + bool saw_error; + bool windows_handle; + bool read_ready, write_ready, except_ready; + bool read_selected, write_selected, except_selected; + bool except_on_write; + int (*startup) (select_record *me, class select_stuff *stuff); + int (*peek) (select_record *, bool); + int (*verify) (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds); + void (*cleanup) (select_record *me, class select_stuff *stuff); + struct select_record *next; + + select_record (fhandler_base *in_fh = NULL) : fd (0), h (NULL), + fh (in_fh), saw_error (false), windows_handle (false), + read_ready (false), write_ready (false), except_ready (false), + read_selected (false), write_selected (false), + except_selected (false), except_on_write (false), + startup (NULL), peek (NULL), verify (NULL), cleanup (NULL), + next (NULL) {} +}; + +class select_stuff +{ + public: + ~select_stuff (); + bool always_ready, windows_used; + select_record start; + void *device_specific_pipe; + void *device_specific_socket; + void *device_specific_serial; + + int test_and_set (int i, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds); + int poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds); + int wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds, DWORD ms); + void cleanup (); + select_stuff (): always_ready (0), windows_used (0), start (0), + device_specific_pipe (0), + device_specific_socket (0), + device_specific_serial (0) {} +}; + +int __stdcall set_console_state_for_spawn (); + +#endif /* _FHANDLER_H_ */ diff --git a/winsup/cygwin/fhandler_clipboard.cc b/winsup/cygwin/fhandler_clipboard.cc new file mode 100644 index 00000000000..6f8559740be --- /dev/null +++ b/winsup/cygwin/fhandler_clipboard.cc @@ -0,0 +1,277 @@ +/* fhandler_dev_clipboard: code to access /dev/clipboard + + Copyright 2000, 2001, 2002 Red Hat, Inc + + Written by Charles Wilson (cwilson@ece.gatech.edu) + +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 <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <windows.h> +#include <wingdi.h> +#include <winuser.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" + +/* + * Robert Collins: + * FIXME: should we use GetClipboardSequenceNumber to tell if the clipboard has + * changed? How does /dev/clipboard operate under (say) linux? + */ + +static const NO_COPY char *CYGWIN_NATIVE = "CYGWIN_NATIVE_CLIPBOARD"; +/* this is MT safe because windows format id's are atomic */ +static UINT cygnativeformat; + +fhandler_dev_clipboard::fhandler_dev_clipboard () + : fhandler_base (), pos (0), membuffer (NULL), msize (0), + eof (true) +{ + /* FIXME: check for errors and loop until we can open the clipboard */ + OpenClipboard (NULL); + cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE); + CloseClipboard (); +} + +/* + * Special clipboard dup to duplicate input and output + * handles. + */ + +int +fhandler_dev_clipboard::dup (fhandler_base * child) +{ + fhandler_dev_clipboard *fhc = (fhandler_dev_clipboard *) child; + + if (!fhc->open (NULL, get_flags (), 0)) + system_printf ("error opening clipboard, %E"); + + fhc->membuffer = membuffer; + fhc->pos = pos; + fhc->msize = msize; + + return 0; +} + +int +fhandler_dev_clipboard::open (path_conv *, int flags, mode_t) +{ + set_flags (flags | O_TEXT); + eof = false; + pos = 0; + if (membuffer) + free (membuffer); + membuffer = NULL; + if (!cygnativeformat) + cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE); + set_nohandle (true); + set_open_status (); + return 1; +} + +static int +set_clipboard (const void *buf, size_t len) +{ + HGLOBAL hmem; + unsigned char *clipbuf; + /* Native CYGWIN format */ + OpenClipboard (0); + hmem = GlobalAlloc (GMEM_MOVEABLE, len + sizeof (size_t)); + if (!hmem) + { + system_printf ("Couldn't allocate global buffer for write"); + return -1; + } + clipbuf = (unsigned char *) GlobalLock (hmem); + memcpy (clipbuf + sizeof (size_t), buf, len); + *(size_t *) (clipbuf) = len; + GlobalUnlock (hmem); + EmptyClipboard (); + if (!cygnativeformat) + cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE); + if (!SetClipboardData (cygnativeformat, hmem)) + { + system_printf + ("Couldn't write native format to the clipboard %04x %x", + cygnativeformat, hmem); +/* FIXME: return an appriate error code &| set_errno(); */ + return -1; + } + CloseClipboard (); + if (GlobalFree (hmem)) + { + system_printf + ("Couldn't free global buffer after write to the clipboard"); +/* FIXME: return an appriate error code &| set_errno(); */ + return 0; + } + + /* CF_TEXT/CF_OEMTEXT for copying to wordpad and the like */ + + OpenClipboard (0); + hmem = GlobalAlloc (GMEM_MOVEABLE, len + 2); + if (!hmem) + { + system_printf ("Couldn't allocate global buffer for write"); + return -1; + } + clipbuf = (unsigned char *) GlobalLock (hmem); + memcpy (clipbuf, buf, len); + *(clipbuf + len) = '\0'; + *(clipbuf + len + 1) = '\0'; + GlobalUnlock (hmem); + if (!SetClipboardData + ((current_codepage == ansi_cp ? CF_TEXT : CF_OEMTEXT), hmem)) + { + system_printf ("Couldn't write to the clipboard"); +/* FIXME: return an appriate error code &| set_errno(); */ + return -1; + } + CloseClipboard (); + if (GlobalFree (hmem)) + { + system_printf + ("Couldn't free global buffer after write to the clipboard"); +/* FIXME: return an appriate error code &| set_errno(); */ + } + return 0; +} + +/* FIXME: arbitrary seeking is not handled */ +int +fhandler_dev_clipboard::write (const void *buf, size_t len) +{ + if (!eof) + { + /* write to our membuffer */ + size_t cursize = msize; + void *tempbuffer = realloc (membuffer, cursize + len); + if (!tempbuffer) + { + system_printf ("Couldn't realloc() clipboard buffer for write"); + return -1; + } + membuffer = tempbuffer; + msize = cursize + len; + memcpy ((unsigned char *) membuffer + cursize, buf, len); + + /* now pass to windows */ + if (set_clipboard (membuffer, msize)) + { + /* FIXME: membuffer is now out of sync with pos, but msize is used above */ + set_errno (ENOSPC); + return -1; + } + + pos = msize; + + set_errno (0); + eof = false; + return len; + } + else + { + /* FIXME: return 0 bytes written, file not open */ + return 0; + } +} + +void __stdcall +fhandler_dev_clipboard::read (void *ptr, size_t& len) +{ + HGLOBAL hglb; + size_t ret; + UINT formatlist[2]; + UINT format; + if (eof) + len = 0; + else + { + formatlist[0] = cygnativeformat; + formatlist[1] = current_codepage == ansi_cp ? CF_TEXT : CF_OEMTEXT; + OpenClipboard (0); + if ((format = GetPriorityClipboardFormat (formatlist, 2)) <= 0) + { + CloseClipboard (); +#if 0 + system_printf ("a non-accepted format! %d", format); +#endif + set_errno (0); + len = 0; + } + else + { + hglb = GetClipboardData (format); + if (format == cygnativeformat) + { + unsigned char *buf = (unsigned char *) GlobalLock (hglb); + size_t buflen = (*(size_t *) buf); + ret = ((len > (buflen - pos)) ? (buflen - pos) : len); + memcpy (ptr, buf + sizeof (size_t)+ pos , ret); + pos += ret; + if (pos + len - ret >= buflen) + eof = true; + GlobalUnlock (hglb); + } + else + { + LPSTR lpstr; + lpstr = (LPSTR) GlobalLock (hglb); + + ret = ((len > (strlen (lpstr) - pos)) ? (strlen (lpstr) - pos) + : len); + + memcpy (ptr, lpstr + pos, ret); + //ret = snprintf((char *) ptr, len, "%s", lpstr);//+pos); + pos += ret; + if (pos + len - ret >= strlen (lpstr)) + eof = true; + GlobalUnlock (hglb); + } + CloseClipboard (); + set_errno (0); + len = ret; + } + } +} + +__off64_t +fhandler_dev_clipboard::lseek (__off64_t offset, int whence) +{ + /* On reads we check this at read time, not seek time. + * On writes we use this to decide how to write - empty and write, or open, copy, empty + * and write + */ + pos = offset; + /* treat seek like rewind */ + if (membuffer) + free (membuffer); + msize = 0; + return 0; +} + +int +fhandler_dev_clipboard::close (void) +{ + eof = true; + pos = 0; + if (membuffer) + free (membuffer); + msize = 0; + return 0; +} + +void +fhandler_dev_clipboard::dump () +{ + paranoid_printf ("here, fhandler_dev_clipboard"); +} diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc new file mode 100644 index 00000000000..5032e133eb5 --- /dev/null +++ b/winsup/cygwin/fhandler_console.cc @@ -0,0 +1,1754 @@ +/* fhandler_console.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 <sys/termios.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <wingdi.h> +#include <winuser.h> +#include <wincon.h> +#include <winnls.h> +#include <ctype.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" +#include "pinfo.h" +#include "shared_info.h" +#include "cygthread.h" + +#define CONVERT_LIMIT 4096 + +static BOOL +cp_convert (UINT destcp, char *dest, UINT srccp, const char *src, DWORD size) +{ + if (!size) + /* no action */; + else if (destcp == srccp) + { + if (dest != src) + memcpy (dest, src, size); + } + else + { + WCHAR wbuffer[CONVERT_LIMIT]; /* same size as the maximum input, s.b. */ + if (!MultiByteToWideChar (srccp, 0, src, size, wbuffer, sizeof (wbuffer))) + return FALSE; + if (!WideCharToMultiByte (destcp, 0, wbuffer, size, dest, size, + NULL, NULL)) + return FALSE; + } + return TRUE; +} + +/* The results of GetConsoleCP() and GetConsoleOutputCP() cannot be + cached, because a program or the user can change these values at + any time. */ +inline BOOL +con_to_str (char *d, const char *s, DWORD sz) +{ + return cp_convert (get_cp (), d, GetConsoleCP (), s, sz); +} + +inline BOOL +str_to_con (char *d, const char *s, DWORD sz) +{ + return cp_convert (GetConsoleOutputCP (), d, get_cp (), s, sz); +} + +/* + * Scroll the screen context. + * x1, y1 - ul corner + * x2, y2 - dr corner + * xn, yn - new ul corner + * Negative values represents current screen dimensions + */ + +#define srTop (dev_state->info.winTop + dev_state->scroll_region.Top) +#define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom) + +#define use_tty ISSTATE (myself, PID_USETTY) + +const char * get_nonascii_key (INPUT_RECORD&, char *); + +static console_state NO_COPY *shared_console_info; + +dev_console NO_COPY *fhandler_console::dev_state; + +/* Allocate and initialize the shared record for the current console. + Returns a pointer to shared_console_info. */ +tty_min * +fhandler_console::get_tty_stuff (int flags = 0) +{ + if (dev_state) + return &shared_console_info->tty_min_state; + + shared_console_info = + (console_state *) open_shared (NULL, 0, cygheap->console_h, + sizeof (*shared_console_info), + SH_SHARED_CONSOLE); + dev_state = &shared_console_info->dev_state; + + ProtectHandleINH (cygheap->console_h); + if (!shared_console_info->tty_min_state.ntty) + { + shared_console_info->tty_min_state.setntty (TTY_CONSOLE); + shared_console_info->tty_min_state.setsid (myself->sid); + shared_console_info->tty_min_state.set_ctty (TTY_CONSOLE, flags); + + dev_state->scroll_region.Bottom = -1; + dev_state->dwLastCursorPosition.X = -1; + dev_state->dwLastCursorPosition.Y = -1; + dev_state->default_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; + dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + dev_state->meta_mask = LEFT_ALT_PRESSED; + /* Set the mask that determines if an input keystroke is modified by + META. We set this based on the keyboard layout language loaded + for the current thread. The left <ALT> key always generates + META, but the right <ALT> key only generates META if we are using + an English keyboard because many "international" keyboards + replace common shell symbols ('[', '{', etc.) with accented + language-specific characters (umlaut, accent grave, etc.). On + these keyboards right <ALT> (called AltGr) is used to produce the + shell symbols and should not be interpreted as META. */ + if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) + dev_state->meta_mask |= RIGHT_ALT_PRESSED; + } + + return &shared_console_info->tty_min_state; +} + +void +set_console_ctty () +{ + (void) fhandler_console::get_tty_stuff (); +} + +/* Return the tty structure associated with a given tty number. If the + tty number is < 0, just return a dummy record. */ +tty_min * +tty_list::get_tty (int n) +{ + static tty_min nada; + if (n == TTY_CONSOLE) + return fhandler_console::get_tty_stuff (); + else if (n >= 0) + return &cygwin_shared->tty.ttys[n]; + else + return &nada; +} + + +/* Determine if a console is associated with this process prior to a spawn. + If it is, then we'll return 1. If the console has been initialized, then + set it into a more friendly state for non-cygwin apps. */ +int __stdcall +set_console_state_for_spawn () +{ + HANDLE h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_WRITE, + &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + + if (h == INVALID_HANDLE_VALUE) + return 0; + + if (shared_console_info != NULL) + { + /* ACK. Temporarily define for use in TTYSETF macro */ +# define tc &shared_console_info->tty_min_state + SetConsoleMode (h, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + TTYSETF (RSTCONS); +# undef tc + } + + CloseHandle (h); + return 1; +} + +BOOL +fhandler_console::set_raw_win32_keyboard_mode (BOOL new_mode) +{ + BOOL old_mode = dev_state->raw_win32_keyboard_mode; + dev_state->raw_win32_keyboard_mode = new_mode; + syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis"); + return old_mode; +}; + +void +fhandler_console::set_cursor_maybe () +{ + CONSOLE_SCREEN_BUFFER_INFO now; + + if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) + return; + + if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X || + dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y) + { + SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition); + dev_state->dwLastCursorPosition = now.dwCursorPosition; + } +} + +void +fhandler_console::send_winch_maybe () +{ + SHORT y = dev_state->info.dwWinSize.Y; + SHORT x = dev_state->info.dwWinSize.X; + fillin_info (); + + if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X) + { + extern fhandler_tty_master *tty_master; + if (tty_master) + tty_master->set_winsize (true); + else + tc->kill_pgrp (SIGWINCH); + } +} + +void __stdcall +fhandler_console::read (void *pv, size_t& buflen) +{ + HANDLE h = get_io_handle (); + +#define buf ((char *) pv) + + int ch; + set_input_state (); + + int copied_chars = get_readahead_into_buffer (buf, buflen); + + if (copied_chars) + { + buflen = copied_chars; + return; + } + + HANDLE w4[2]; + DWORD nwait; + char tmp[60]; + + w4[0] = h; + if (cygthread::is ()) + nwait = 1; + else + { + w4[1] = signal_arrived; + nwait = 2; + } + + termios ti = tc->ti; + for (;;) + { + int bgres; + if ((bgres = bg_check (SIGTTIN)) <= bg_eof) + { + buflen = bgres; + return; + } + + set_cursor_maybe (); /* to make cursor appear on the screen immediately */ + switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + goto sig_exit; + default: + goto err; + } + + DWORD nread; + INPUT_RECORD input_rec; + const char *toadd = NULL; + + if (!ReadConsoleInput (h, &input_rec, 1, &nread)) + { + syscall_printf ("ReadConsoleInput failed, %E"); + goto err; /* seems to be failure */ + } + + /* check the event that occurred */ + switch (input_rec.EventType) + { + case KEY_EVENT: +#define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode) +#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState) + +#ifdef DEBUGGING + /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */ + if (input_rec.Event.KeyEvent.bKeyDown && + virtual_key_code == VK_SCROLL && + control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED) == LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED + ) + { + set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode); + continue; + } +#endif + + if (dev_state->raw_win32_keyboard_mode) + { + __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK", + input_rec.Event.KeyEvent.bKeyDown, + input_rec.Event.KeyEvent.wRepeatCount, + input_rec.Event.KeyEvent.wVirtualKeyCode, + input_rec.Event.KeyEvent.wVirtualScanCode, + input_rec.Event.KeyEvent.uChar.UnicodeChar, + input_rec.Event.KeyEvent.dwControlKeyState); + toadd = tmp; + nread = strlen (toadd); + break; + } + + if (!input_rec.Event.KeyEvent.bKeyDown) + continue; + +#define ich (input_rec.Event.KeyEvent.uChar.AsciiChar) +#define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar) +#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) +#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) + + if (wch == 0 || + /* arrow/function keys */ + (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) + { + toadd = get_nonascii_key (input_rec, tmp); + if (!toadd) + continue; + nread = strlen (toadd); + } + else + { + tmp[1] = ich; + /* Need this check since US code page seems to have a bug when + converting a CTRL-U. */ + if ((unsigned char) ich > 0x7f) + con_to_str (tmp + 1, tmp + 1, 1); + /* Determine if the keystroke is modified by META. The tricky + part is to distinguish whether the right Alt key should be + recognized as Alt, or as AltGr. */ + bool meta; + if (wincap.altgr_is_ctrl_alt ()) + /* WinNT: AltGr is reported as Ctrl+Alt, and Ctrl+Alt is + treated just like AltGr. However, if Ctrl+Alt+key generates + an ASCII control character, interpret is as META. */ + meta = (control_key_state & ALT_PRESSED) != 0 + && ((control_key_state & CTRL_PRESSED) == 0 + || (ich >= 0 && ich <= 0x1f || ich == 0x7f)); + else + /* Win9x: there's no way to distinguish Alt from AltGr, so rely + on dev_state->meta_mask heuristic (see fhandler_console constructor). */ + meta = (control_key_state & dev_state->meta_mask) != 0; + if (!meta) + toadd = tmp + 1; + else + { + tmp[0] = '\033'; + tmp[1] = cyg_tolower (tmp[1]); + toadd = tmp; + nread++; + } + } +#undef ich +#undef wch +#undef ALT_PRESSED +#undef CTRL_PRESSED + break; + + case MOUSE_EVENT: + send_winch_maybe (); + if (dev_state->use_mouse) + { + MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent; + + /* Treat the double-click event like a regular button press */ + if (mouse_event.dwEventFlags == DOUBLE_CLICK) + { + syscall_printf ("mouse: double-click -> click"); + mouse_event.dwEventFlags = 0; + } + + /* Did something other than a click occur? */ + if (mouse_event.dwEventFlags) + continue; + + /* If the mouse event occurred out of the area we can handle, + ignore it. */ + int x = mouse_event.dwMousePosition.X; + int y = mouse_event.dwMousePosition.Y; + if ((x + ' ' + 1 > 0xFF) || (y + ' ' + 1 > 0xFF)) + { + syscall_printf ("mouse: position out of range"); + continue; + } + + /* Ignore unimportant mouse buttons */ + mouse_event.dwButtonState &= 0x7; + + /* This code assumes Windows never reports multiple button + events at the same time. */ + int b = 0; + char sz[32]; + if (mouse_event.dwButtonState == dev_state->dwLastButtonState) + { + syscall_printf ("mouse: button state unchanged"); + continue; + } + else if (mouse_event.dwButtonState < dev_state->dwLastButtonState) + { + b = 3; + strcpy (sz, "btn up"); + } + else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1)) + { + b = 0; + strcpy (sz, "btn1 down"); + } + else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2)) + { + b = 2; + strcpy (sz, "btn2 down"); + } + else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4)) + { + b = 1; + strcpy (sz, "btn3 down"); + } + + /* Remember the current button state */ + dev_state->dwLastButtonState = mouse_event.dwButtonState; + + /* If a button was pressed, remember the modifiers */ + if (b != 3) + { + dev_state->nModifiers = 0; + if (mouse_event.dwControlKeyState & SHIFT_PRESSED) + dev_state->nModifiers |= 0x4; + if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) + dev_state->nModifiers |= 0x8; + if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) + dev_state->nModifiers |= 0x10; + } + + b |= dev_state->nModifiers; + + /* We can now create the code. */ + sprintf (tmp, "\033[M%c%c%c", b + ' ', x + ' ' + 1, y + ' ' + 1); + syscall_printf ("mouse: %s at (%d,%d)", sz, x, y); + + toadd = tmp; + nread = 6; + } + break; + + case FOCUS_EVENT: + case WINDOW_BUFFER_SIZE_EVENT: + send_winch_maybe (); + /* fall through */ + default: + continue; + } + + if (toadd) + { + line_edit_status res = line_edit (toadd, nread, ti); + if (res == line_edit_signalled) + goto sig_exit; + else if (res == line_edit_input_done) + break; + } +#undef ich + } + + while (buflen) + if ((ch = get_readahead ()) < 0) + break; + else + { + buf[copied_chars++] = (unsigned char)(ch & 0xff); + buflen--; + } +#undef buf + + buflen = copied_chars; + return; + +err: + __seterrno (); + (ssize_t) buflen = -1; + return; + + sig_exit: + set_sig_errno (EINTR); + (ssize_t) buflen = -1; + return; +} + +void +fhandler_console::set_input_state () +{ + if (TTYISSETF (RSTCONS)) + input_tcsetattr (0, &tc->ti); +} + +BOOL +fhandler_console::fillin_info (void) +{ + BOOL ret; + CONSOLE_SCREEN_BUFFER_INFO linfo; + + if ((ret = GetConsoleScreenBufferInfo (get_output_handle (), &linfo))) + { + dev_state->info.winTop = linfo.srWindow.Top; + dev_state->info.winBottom = linfo.srWindow.Bottom; + dev_state->info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top; + dev_state->info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left; + dev_state->info.dwBufferSize = linfo.dwSize; + dev_state->info.dwCursorPosition = linfo.dwCursorPosition; + dev_state->info.wAttributes = linfo.wAttributes; + } + else + { + memset (&dev_state->info, 0, sizeof dev_state->info); + dev_state->info.dwWinSize.Y = 25; + dev_state->info.dwWinSize.X = 80; + dev_state->info.winBottom = 24; + } + + return ret; +} + +void +fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn) +{ + SMALL_RECT sr1, sr2; + CHAR_INFO fill; + COORD dest; + + (void) fillin_info (); + sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1; + if (y1 == 0) + sr1.Top = dev_state->info.winTop; + else + sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom; + sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1; + if (y2 == 0) + sr1.Bottom = dev_state->info.winTop; + else + sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom; + sr2.Top = srTop; + sr2.Left = 0; + sr2.Bottom = srBottom; + sr2.Right = dev_state->info.dwWinSize.X - 1; + if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom) + sr1.Bottom = sr2.Bottom; + dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1; + if (yn == 0) + dest.Y = dev_state->info.winTop; + else + dest.Y = yn > 0 ? yn : dev_state->info.winBottom; + fill.Char.AsciiChar = ' '; + fill.Attributes = dev_state->current_win32_attr; + ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill); + + /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance + * is more than half of screen, filling doesn't work as expected */ + + if (sr1.Top != sr1.Bottom) + if (dest.Y <= sr1.Top) /* forward scroll */ + clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom); + else /* reverse scroll */ + clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1); +} + +int +fhandler_console::open (path_conv *, int flags, mode_t) +{ + HANDLE h; + + tcinit (get_tty_stuff (flags)); + + set_io_handle (NULL); + set_output_handle (NULL); + + set_flags ((flags & ~O_TEXT) | O_BINARY); + + /* Open the input handle as handle_ */ + h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none, + OPEN_EXISTING, 0, 0); + + if (h == INVALID_HANDLE_VALUE) + { + __seterrno (); + return 0; + } + set_io_handle (h); + set_r_no_interrupt (1); // Handled explicitly in read code + + h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none, + OPEN_EXISTING, 0, 0); + + if (h == INVALID_HANDLE_VALUE) + { + __seterrno (); + return 0; + } + set_output_handle (h); + + if (fillin_info ()) + dev_state->default_color = dev_state->info.wAttributes; + + set_default_attr (); + + DWORD cflags; + if (GetConsoleMode (get_io_handle (), &cflags)) + { + cflags |= ENABLE_PROCESSED_INPUT; + SetConsoleMode (get_io_handle (), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags); + } + + TTYCLEARF (RSTCONS); + set_open_status (); + debug_printf ("opened conin$ %p, conout$ %p", + get_io_handle (), get_output_handle ()); + + return 1; +} + +int +fhandler_console::close (void) +{ + CloseHandle (get_io_handle ()); + CloseHandle (get_output_handle ()); + set_io_handle (NULL); + set_output_handle (NULL); + return 0; +} + +/* + * Special console dup to duplicate input and output + * handles. + */ + +int +fhandler_console::dup (fhandler_base *child) +{ + fhandler_console *fhc = (fhandler_console *) child; + + if (!fhc->open (NULL, get_flags () & ~O_NOCTTY, 0)) + system_printf ("error opening console, %E"); + + return 0; +} + +int +fhandler_console::ioctl (unsigned int cmd, void *buf) +{ + switch (cmd) + { + case TIOCGWINSZ: + int st; + + st = fillin_info (); + if (st) + { + /* *not* the buffer size, the actual screen size... */ + /* based on Left Top Right Bottom of srWindow */ + ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y; + ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X; + syscall_printf ("WINSZ: (row=%d,col=%d)", + ((struct winsize *) buf)->ws_row, + ((struct winsize *) buf)->ws_col); + return 0; + } + else + { + syscall_printf ("WINSZ failed"); + __seterrno (); + return -1; + } + return 0; + case TIOCSWINSZ: + (void) bg_check (SIGTTOU); + return 0; + } + + return fhandler_base::ioctl (cmd, buf); +} + +int +fhandler_console::tcflush (int queue) +{ + int res = 0; + if (queue == TCIFLUSH + || queue == TCIOFLUSH) + { + if (!FlushConsoleInputBuffer (get_io_handle ())) + { + __seterrno (); + res = -1; + } + } + return res; +} + +int +fhandler_console::output_tcsetattr (int, struct termios const *t) +{ + /* All the output bits we can ignore */ + + DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + + int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; + syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)", + res, t, flags, t->c_lflag, t->c_oflag); + return res; +} + +int +fhandler_console::input_tcsetattr (int, struct termios const *t) +{ + /* Ignore the optional_actions stuff, since all output is emitted + instantly */ + + DWORD oflags; + + if (!GetConsoleMode (get_io_handle (), &oflags)) + oflags = 0; + DWORD flags = 0; + +#if 0 + /* Enable/disable LF -> CRLF conversions */ + set_r_binary ((t->c_iflag & INLCR) ? 0 : 1); +#endif + + /* There's some disparity between what we need and what's + available. We've got ECHO and ICANON, they've + got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */ + + tc->ti = *t; + + if (t->c_lflag & ECHO) + { + flags |= ENABLE_ECHO_INPUT; + } + if (t->c_lflag & ICANON) + { + flags |= ENABLE_LINE_INPUT; + } + + if (flags & ENABLE_ECHO_INPUT + && !(flags & ENABLE_LINE_INPUT)) + { + /* This is illegal, so turn off the echo here, and fake it + when we read the characters */ + + flags &= ~ENABLE_ECHO_INPUT; + } + + if (t->c_lflag & ISIG) + { + flags |= ENABLE_PROCESSED_INPUT; + } + + if (use_tty) + { + flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + tc->ti.c_iflag = 0; + tc->ti.c_lflag = 0; + } + + flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT; + + int res; + if (flags == oflags) + res = 0; + else + { + res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1; + if (res < 0) + __seterrno (); + syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p", + res, t, flags, t->c_lflag, t->c_iflag); + } + + TTYCLEARF (RSTCONS); + return res; +} + +int +fhandler_console::tcsetattr (int a, struct termios const *t) +{ + int res = output_tcsetattr (a, t); + if (res != 0) + return res; + return input_tcsetattr (a, t); +} + +int +fhandler_console::tcgetattr (struct termios *t) +{ + int res; + *t = tc->ti; + + t->c_cflag |= CS8; + + DWORD flags; + + if (!GetConsoleMode (get_io_handle (), &flags)) + { + __seterrno (); + res = -1; + } + else + { + if (flags & ENABLE_ECHO_INPUT) + t->c_lflag |= ECHO; + + if (flags & ENABLE_LINE_INPUT) + t->c_lflag |= ICANON; + + if (flags & ENABLE_PROCESSED_INPUT) + t->c_lflag |= ISIG; + + /* What about ENABLE_WINDOW_INPUT + and ENABLE_MOUSE_INPUT ? */ + + /* All the output bits we can ignore */ + res = 0; + } + syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p", + res, t, flags, t->c_lflag, t->c_iflag); + return res; +} + +fhandler_console::fhandler_console () : + fhandler_termios () +{ +} + +#define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \ + FOREGROUND_BLUE | FOREGROUND_INTENSITY) +#define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \ + BACKGROUND_BLUE | BACKGROUND_INTENSITY) +void +fhandler_console::set_default_attr () +{ + dev_state->blink = dev_state->underline = dev_state->reverse = FALSE; + dev_state->intensity = INTENSITY_NORMAL; + dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK; + dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK; + dev_state->current_win32_attr = get_win32_attr (); + SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr); +} + +WORD +fhandler_console::get_win32_attr () +{ + WORD win_fg = dev_state->fg; + WORD win_bg = dev_state->bg; + if (dev_state->reverse) + { + WORD save_fg = win_fg; + win_fg = (win_bg & BACKGROUND_RED ? FOREGROUND_RED : 0) | + (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) | + (win_bg & BACKGROUND_BLUE ? FOREGROUND_BLUE : 0) | + (win_fg & FOREGROUND_INTENSITY); + win_bg = (save_fg & FOREGROUND_RED ? BACKGROUND_RED : 0) | + (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) | + (save_fg & FOREGROUND_BLUE ? BACKGROUND_BLUE : 0) | + (win_bg & BACKGROUND_INTENSITY); + } + if (dev_state->underline) + win_fg = dev_state->underline_color; + /* emulate blink with bright background */ + if (dev_state->blink) + win_bg |= BACKGROUND_INTENSITY; + if (dev_state->intensity == INTENSITY_INVISIBLE) + win_fg = win_bg; + else if (dev_state->intensity == INTENSITY_BOLD) + win_fg |= FOREGROUND_INTENSITY; + return (win_fg | win_bg); +} + +/* + * Clear the screen context from x1/y1 to x2/y2 cell. + * Negative values represents current screen dimensions + */ +void +fhandler_console::clear_screen (int x1, int y1, int x2, int y2) +{ + COORD tlc; + DWORD done; + int num; + + (void)fillin_info (); + + if (x1 < 0) + x1 = dev_state->info.dwWinSize.X - 1; + if (y1 < 0) + y1 = dev_state->info.winBottom; + if (x2 < 0) + x2 = dev_state->info.dwWinSize.X - 1; + if (y2 < 0) + y2 = dev_state->info.winBottom; + + num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1; + + if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1)) + { + tlc.X = x1; + tlc.Y = y1; + } + else + { + tlc.X = x2; + tlc.Y = y2; + } + FillConsoleOutputCharacterA (get_output_handle (), ' ', + num, + tlc, + &done); + FillConsoleOutputAttribute (get_output_handle (), + dev_state->current_win32_attr, + num, + tlc, + &done); +} + +void +fhandler_console::cursor_set (BOOL rel_to_top, int x, int y) +{ + COORD pos; + + (void) fillin_info (); + if (y > dev_state->info.winBottom) + y = dev_state->info.winBottom; + else if (y < 0) + y = 0; + else if (rel_to_top) + y += dev_state->info.winTop; + + if (x > dev_state->info.dwWinSize.X) + x = dev_state->info.dwWinSize.X - 1; + else if (x < 0) + x = 0; + + pos.X = x; + pos.Y = y; + SetConsoleCursorPosition (get_output_handle (), pos); +} + +void +fhandler_console::cursor_rel (int x, int y) +{ + fillin_info (); + x += dev_state->info.dwCursorPosition.X; + y += dev_state->info.dwCursorPosition.Y; + cursor_set (FALSE, x, y); +} + +void +fhandler_console::cursor_get (int *x, int *y) +{ + fillin_info (); + *y = dev_state->info.dwCursorPosition.Y; + *x = dev_state->info.dwCursorPosition.X; +} + +#define BAK 1 +#define ESC 2 +#define NOR 0 +#define IGN 4 +#if 0 +#define ERR 5 +#else +#define ERR NOR +#endif +#define DWN 6 +#define BEL 7 +#define TAB 8 /* We should't let the console deal with these */ +#define CR 13 +#define LF 10 + +static const char base_chars[256] = +{ +/*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL, +/*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR, ERR, IGN, +/*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR, +/*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR, +/* ! " # $ % & ' */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*() * + , - . / */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*0 1 2 3 4 5 6 7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*8 9 : ; < = > ? */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*@ A B C D E F G */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*H I J K L M N O */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*P Q R S T U V W */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*X Y Z [ \ ] ^ _ */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*` a b c d e f g */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*h i j k l m n o */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*p q r s t u v w */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*x y z { | } ~ 7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, +/*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR }; + +void +fhandler_console::char_command (char c) +{ + int x, y; + char buf[40]; + + switch (c) + { + case 'm': /* Set Graphics Rendition */ + int i; + + for (i = 0; i <= dev_state->nargs_; i++) + switch (dev_state->args_[i]) + { + case 0: /* normal color */ + set_default_attr (); + break; + case 1: /* bold */ + dev_state->intensity = INTENSITY_BOLD; + break; + case 4: + dev_state->underline = 1; + break; + case 5: /* blink mode */ + dev_state->blink = TRUE; + break; + case 7: /* reverse */ + dev_state->reverse = TRUE; + break; + case 8: /* invisible */ + dev_state->intensity = INTENSITY_INVISIBLE; + break; + case 9: /* dim */ + dev_state->intensity = INTENSITY_DIM; + break; + case 24: + dev_state->underline = FALSE; + break; + case 27: + dev_state->reverse = FALSE; + break; + case 30: /* BLACK foreground */ + dev_state->fg = 0; + break; + case 31: /* RED foreground */ + dev_state->fg = FOREGROUND_RED; + break; + case 32: /* GREEN foreground */ + dev_state->fg = FOREGROUND_GREEN; + break; + case 33: /* YELLOW foreground */ + dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN; + break; + case 34: /* BLUE foreground */ + dev_state->fg = FOREGROUND_BLUE; + break; + case 35: /* MAGENTA foreground */ + dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE; + break; + case 36: /* CYAN foreground */ + dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN; + break; + case 37: /* WHITE foreg */ + dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + break; + case 39: + dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK; + break; + case 40: /* BLACK background */ + dev_state->bg = 0; + break; + case 41: /* RED background */ + dev_state->bg = BACKGROUND_RED; + break; + case 42: /* GREEN background */ + dev_state->bg = BACKGROUND_GREEN; + break; + case 43: /* YELLOW background */ + dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN; + break; + case 44: /* BLUE background */ + dev_state->bg = BACKGROUND_BLUE; + break; + case 45: /* MAGENTA background */ + dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE; + break; + case 46: /* CYAN background */ + dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN; + break; + case 47: /* WHITE background */ + dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; + break; + case 49: + dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK; + break; + } + dev_state->current_win32_attr = get_win32_attr (); + SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr); + break; + case 'h': + case 'l': + if (!dev_state->saw_question_mark) + { + switch (dev_state->args_[0]) + { + case 4: /* Insert mode */ + dev_state->insert_mode = (c == 'h') ? TRUE : FALSE; + syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis"); + break; + } + break; + } + switch (dev_state->args_[0]) + { + case 47: /* Save/Restore screen */ + if (c == 'h') /* save */ + { + CONSOLE_SCREEN_BUFFER_INFO now; + COORD cob = { 0, 0 }; + + if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) + break; + + dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1; + dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1; + + if (dev_state->savebuf) + cfree (dev_state->savebuf); + dev_state->savebuf = (PCHAR_INFO) cmalloc (HEAP_1_BUF, sizeof (CHAR_INFO) * + dev_state->savebufsiz.X * dev_state->savebufsiz.Y); + + ReadConsoleOutputA (get_output_handle (), dev_state->savebuf, + dev_state->savebufsiz, cob, &now.srWindow); + } + else /* restore */ + { + CONSOLE_SCREEN_BUFFER_INFO now; + COORD cob = { 0, 0 }; + + if (!GetConsoleScreenBufferInfo (get_output_handle (), &now)) + break; + + if (!dev_state->savebuf) + break; + + WriteConsoleOutputA (get_output_handle (), dev_state->savebuf, + dev_state->savebufsiz, cob, &now.srWindow); + + cfree (dev_state->savebuf); + dev_state->savebuf = NULL; + dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0; + } + break; + + case 1000: /* Mouse support */ + dev_state->use_mouse = (c == 'h') ? TRUE : FALSE; + syscall_printf ("mouse support %sabled", dev_state->use_mouse ? "en" : "dis"); + break; + + case 2000: /* Raw keyboard mode */ + set_raw_win32_keyboard_mode ((c == 'h') ? TRUE : FALSE); + break; + + default: /* Ignore */ + syscall_printf ("unknown h/l command: %d", dev_state->args_[0]); + break; + } + break; + case 'J': + switch (dev_state->args_[0]) + { + case 0: /* Clear to end of screen */ + cursor_get (&x, &y); + clear_screen (x, y, -1, -1); + break; + case 1: /* Clear from beginning of screen to cursor */ + cursor_get (&x, &y); + clear_screen (0, 0, x, y); + break; + case 2: /* Clear screen */ + clear_screen (0, 0, -1, -1); + cursor_set (TRUE, 0,0); + break; + default: + goto bad_escape; + } + break; + + case 'A': + cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1)); + break; + case 'B': + cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1); + break; + case 'C': + cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0); + break; + case 'D': + cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0); + break; + case 'K': + switch (dev_state->args_[0]) + { + case 0: /* Clear to end of line */ + cursor_get (&x, &y); + clear_screen (x, y, -1, y); + break; + case 2: /* Clear line */ + cursor_get (&x, &y); + clear_screen (0, y, -1, y); + break; + case 1: /* Clear from bol to cursor */ + cursor_get (&x, &y); + clear_screen (0, y, x, y); + break; + default: + goto bad_escape; + } + break; + case 'H': + case 'f': + cursor_set (TRUE, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1, + (dev_state->args_[0] ? dev_state->args_[0] : 1) - 1); + break; + case 'G': /* hpa - position cursor at column n - 1 */ + cursor_get (&x, &y); + cursor_set (FALSE, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y); + break; + case 'd': /* vpa - position cursor at line n */ + cursor_get (&x, &y); + cursor_set (TRUE, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0)); + break; + case 's': /* Save cursor position */ + cursor_get (&dev_state->savex, &dev_state->savey); + dev_state->savey -= dev_state->info.winTop; + break; + case 'u': /* Restore cursor position */ + cursor_set (TRUE, dev_state->savex, dev_state->savey); + break; + case 'I': /* TAB */ + cursor_get (&x, &y); + cursor_set (FALSE, 8 * (x / 8 + 1), y); + break; + case 'L': /* AL - insert blank lines */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + cursor_get (&x, &y); + scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]); + break; + case 'M': /* DL - delete lines */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + cursor_get (&x, &y); + scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y); + break; + case '@': /* IC - insert chars */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + cursor_get (&x, &y); + scroll_screen (x, y, -1, y, x + dev_state->args_[0], y); + break; + case 'P': /* DC - delete chars */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + cursor_get (&x, &y); + scroll_screen (x + dev_state->args_[0], y, -1, y, x, y); + break; + case 'S': /* SF - Scroll forward */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0); + break; + case 'T': /* SR - Scroll down */ + fillin_info (); + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]); + break; + case 'X': /* ec - erase chars */ + dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1; + cursor_get (&x, &y); + scroll_screen (x + dev_state->args_[0], y, -1, y, x, y); + scroll_screen (x, y, -1, y, x + dev_state->args_[0], y); + break; + case 'Z': /* Back tab */ + cursor_get (&x, &y); + cursor_set (FALSE, ((8 * (x / 8 + 1)) - 8), y); + break; + case 'b': /* Repeat char #1 #2 times */ + if (dev_state->insert_mode) + { + cursor_get (&x, &y); + scroll_screen (x, y, -1, y, x + dev_state->args_[1], y); + } + while (dev_state->args_[1]--) + WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0); + break; + case 'c': /* u9 - Terminal enquire string */ + strcpy (buf, "\033[?6c"); + puts_readahead (buf); + break; + case 'n': + switch (dev_state->args_[0]) + { + case 6: /* u7 - Cursor position request */ + cursor_get (&x, &y); + y -= dev_state->info.winTop; + /* x -= dev_state->info.winLeft; // not available yet */ + __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1); + puts_readahead (buf); + break; + default: + goto bad_escape; + } + break; + case 'r': /* Set Scroll region */ + dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0; + dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1; + cursor_set (TRUE, 0, 0); + break; + case 'g': /* TAB set/clear */ + break; + default: +bad_escape: + break; + } +} + +const unsigned char * +fhandler_console::write_normal (const unsigned char *src, + const unsigned char *end) +{ + /* Scan forward to see what a char which needs special treatment */ + DWORD done; + const unsigned char *found = src; + + while (found < end) + { + if (base_chars[*found] != NOR) + break; + found++; + } + + /* Print all the base ones out */ + if (found != src) + { + DWORD len = found - src; + do + { + DWORD buf_len; + char buf[CONVERT_LIMIT]; + done = buf_len = min (sizeof (buf), len); + if (!str_to_con (buf, (const char *) src, buf_len)) + { + debug_printf ("conversion error, handle %p", + get_output_handle ()); + __seterrno (); + return 0; + } + + if (dev_state->insert_mode) + { + int x, y; + cursor_get (&x, &y); + scroll_screen (x, y, -1, y, x + buf_len, y); + } + + if (!WriteFile (get_output_handle (), buf, buf_len, &done, 0)) + { + debug_printf ("write failed, handle %p", get_output_handle ()); + __seterrno (); + return 0; + } + len -= done; + src += done; + } + while (len > 0); + } + + if (src < end) + { + int x, y; + switch (base_chars[*src]) + { + case BEL: + Beep (412, 100); + break; + case ESC: + dev_state->state_ = gotesc; + break; + case DWN: + cursor_get (&x, &y); + if (y >= srBottom) + { + if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top) + WriteFile (get_output_handle (), "\n", 1, &done, 0); + else + { + scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop); + y--; + } + } + cursor_set (FALSE, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1); + break; + case BAK: + cursor_rel (-1, 0); + break; + case IGN: + cursor_rel (1, 0); + break; + case CR: + cursor_get (&x, &y); + cursor_set (FALSE, 0, y); + break; + case ERR: + WriteFile (get_output_handle (), src, 1, &done, 0); + break; + case TAB: + cursor_get (&x, &y); + cursor_set (FALSE, 8 * (x / 8 + 1), y); + break; + } + src ++; + } + return src; +} + +int +fhandler_console::write (const void *vsrc, size_t len) +{ + /* Run and check for ansi sequences */ + unsigned const char *src = (unsigned char *) vsrc; + unsigned const char *end = src + len; + + debug_printf ("%x, %d", vsrc, len); + + while (src < end) + { + debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ', + dev_state->state_); + switch (dev_state->state_) + { + case normal: + src = write_normal (src, end); + if (!src) /* write_normal failed */ + return -1; + break; + case gotesc: + if (*src == '[') + { + dev_state->state_ = gotsquare; + dev_state->saw_question_mark = FALSE; + for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++) + dev_state->args_[dev_state->nargs_] = 0; + dev_state->nargs_ = 0; + } + else if (*src == ']') + { + dev_state->rarg = 0; + dev_state->my_title_buf[0] = '\0'; + dev_state->state_ = gotrsquare; + } + else if (*src == 'M') /* Reverse Index */ + { + fillin_info (); + scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1); + dev_state->state_ = normal; + } + else if (*src == 'c') /* Reset Linux terminal */ + { + set_default_attr (); + clear_screen (0, 0, -1, -1); + cursor_set (TRUE, 0, 0); + dev_state->state_ = normal; + } + else if (*src == '8') /* Restore cursor position */ + { + cursor_set (TRUE, dev_state->savex, dev_state->savey); + dev_state->state_ = normal; + } + else if (*src == '7') /* Save cursor position */ + { + cursor_get (&dev_state->savex, &dev_state->savey); + dev_state->savey -= dev_state->info.winTop; + dev_state->state_ = normal; + } + else if (*src == 'R') + dev_state->state_ = normal; + else + { + dev_state->state_ = normal; + } + src++; + break; + case gotarg1: + if (isdigit (*src)) + { + dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0'; + src++; + } + else if (*src == ';') + { + src++; + dev_state->nargs_++; + if (dev_state->nargs_ >= MAXARGS) + dev_state->nargs_--; + } + else + { + dev_state->state_ = gotcommand; + } + break; + case gotcommand: + char_command (*src++); + dev_state->state_ = normal; + break; + case gotrsquare: + if (isdigit (*src)) + dev_state->rarg = dev_state->rarg * 10 + (*src - '0'); + else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0)) + dev_state->state_ = gettitle; + else + dev_state->state_ = eattitle; + src++; + break; + case eattitle: + case gettitle: + { + int n = strlen (dev_state->my_title_buf); + if (*src < ' ') + { + if (*src == '\007' && dev_state->state_ == gettitle) + { + if (old_title) + strcpy (old_title, dev_state->my_title_buf); + set_console_title (dev_state->my_title_buf); + } + dev_state->state_ = normal; + } + else if (n < TITLESIZE) + { + dev_state->my_title_buf[n++] = *src; + dev_state->my_title_buf[n] = '\0'; + } + src++; + break; + } + case gotsquare: + if (*src == ';') + { + dev_state->state_ = gotarg1; + dev_state->nargs_++; + src++; + } + else if (isalpha (*src)) + dev_state->state_ = gotcommand; + else if (*src != '@' && !isalpha (*src) && !isdigit (*src)) + { + if (*src == '?') + dev_state->saw_question_mark = TRUE; + /* ignore any extra chars between [ and first arg or command */ + src++; + } + else + dev_state->state_ = gotarg1; + break; + } + } + + syscall_printf ("%d = write_console (,..%d)", len, len); + + return len; +} + +static struct { + int vk; + const char *val[4]; +} keytable[] NO_COPY = { + /* NORMAL */ /* SHIFT */ /* CTRL */ /* ALT */ + {VK_LEFT, {"\033[D", "\033[D", "\033[D", "\033\033[D"}}, + {VK_RIGHT, {"\033[C", "\033[C", "\033[C", "\033\033[C"}}, + {VK_UP, {"\033[A", "\033[A", "\033[A", "\033\033[A"}}, + {VK_DOWN, {"\033[B", "\033[B", "\033[B", "\033\033[B"}}, + {VK_PRIOR, {"\033[5~", "\033[5~", "\033[5~", "\033\033[5~"}}, + {VK_NEXT, {"\033[6~", "\033[6~", "\033[6~", "\033\033[6~"}}, + {VK_HOME, {"\033[1~", "\033[1~", "\033[1~", "\033\033[1~"}}, + {VK_END, {"\033[4~", "\033[4~", "\033[4~", "\033\033[4~"}}, + {VK_INSERT, {"\033[2~", "\033[2~", "\033[2~", "\033\033[2~"}}, + {VK_DELETE, {"\033[3~", "\033[3~", "\033[3~", "\033\033[3~"}}, + {VK_F1, {"\033[[A", "\033[23~", NULL, NULL}}, + {VK_F2, {"\033[[B", "\033[24~", NULL, NULL}}, + {VK_F3, {"\033[[C", "\033[25~", NULL, NULL}}, + {VK_F4, {"\033[[D", "\033[26~", NULL, NULL}}, + {VK_F5, {"\033[[E", "\033[28~", NULL, NULL}}, + {VK_F6, {"\033[17~", "\033[29~", "\036", NULL}}, + {VK_F7, {"\033[18~", "\033[31~", NULL, NULL}}, + {VK_F8, {"\033[19~", "\033[32~", NULL, NULL}}, + {VK_F9, {"\033[20~", "\033[33~", NULL, NULL}}, + {VK_F10, {"\033[21~", "\033[34~", NULL, NULL}}, + {VK_F11, {"\033[23~", NULL, NULL, NULL}}, + {VK_F12, {"\033[24~", NULL, NULL, NULL}}, + {VK_NUMPAD5, {"\033[G", NULL, NULL, NULL}}, + {VK_CLEAR, {"\033[G", NULL, NULL, NULL}}, + {'6', {NULL, NULL, "\036", NULL}}, + {0, {"", NULL, NULL, NULL}} +}; + +const char * +get_nonascii_key (INPUT_RECORD& input_rec, char *tmp) +{ +#define NORMAL 0 +#define SHIFT 1 +#define CONTROL 2 +#define ALT 3 + int modifier_index = NORMAL; + + if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) + modifier_index = SHIFT; + else if (input_rec.Event.KeyEvent.dwControlKeyState & + (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + modifier_index = CONTROL; + else if (input_rec.Event.KeyEvent.dwControlKeyState & + (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + modifier_index = ALT; + + for (int i = 0; keytable[i].vk; i++) + if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk) + return keytable[i].val[modifier_index]; + + if (input_rec.Event.KeyEvent.uChar.AsciiChar) + { + tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar; + tmp[1] = '\0'; + return tmp; + } + return NULL; +} + +void +fhandler_console::init (HANDLE f, DWORD a, mode_t bin) +{ + // this->fhandler_termios::init (f, mode, bin); + /* Ensure both input and output console handles are open */ + int flags = 0; + + a &= GENERIC_READ | GENERIC_WRITE; + if (a == GENERIC_READ) + flags = O_RDONLY; + if (a == GENERIC_WRITE) + flags = O_WRONLY; + if (a == (GENERIC_READ | GENERIC_WRITE)) + flags = O_RDWR; + open ((path_conv *) NULL, flags | O_BINARY); + if (f != INVALID_HANDLE_VALUE) + CloseHandle (f); /* Reopened by open */ + + this->tcsetattr (0, &tc->ti); +} + +int +fhandler_console::igncr_enabled (void) +{ + return tc->ti.c_iflag & IGNCR; +} + +void +fhandler_console::set_close_on_exec (int val) +{ + this->fhandler_base::set_close_on_exec (val); + set_inheritance (output_handle, val); +} + +void +fhandler_console::fixup_after_fork (HANDLE) +{ + HANDLE h = get_handle (); + HANDLE oh = get_output_handle (); + + /* Windows does not allow duplication of console handles between processes + so open the console explicitly. */ + + if (!open (NULL, O_NOCTTY | get_flags (), 0)) + system_printf ("error opening console after fork, %E"); + + if (!get_close_on_exec ()) + { + CloseHandle (h); + CloseHandle (oh); + } +} + +void __stdcall +set_console_title (char *title) +{ + int rc; + char buf[257]; + strncpy (buf, title, sizeof (buf) - 1); + buf[sizeof (buf) - 1] = '\0'; + if ((rc = WaitForSingleObject (title_mutex, 15000)) != WAIT_OBJECT_0) + sigproc_printf ("wait for title mutex failed rc %d, %E", rc); + SetConsoleTitle (buf); + ReleaseMutex (title_mutex); + debug_printf ("title '%s'", buf); +} + +void +fhandler_console::fixup_after_exec (HANDLE) +{ + HANDLE h = get_handle (); + HANDLE oh = get_output_handle (); + + if (!open (NULL, O_NOCTTY | get_flags (), 0)) + { + int sawerr = 0; + if (!get_io_handle ()) + { + system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ()); + sawerr = 1; + } + if (!get_output_handle ()) + { + system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ()); + sawerr = 1; + } + + if (!sawerr) + system_printf ("error opening console after exec, errno %d, %E", get_errno ()); + } + + CloseHandle (h); + CloseHandle (oh); + return; +} diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc new file mode 100644 index 00000000000..17ad55cf78e --- /dev/null +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -0,0 +1,850 @@ +/* fhandler_disk_file.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 <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <signal.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "cygwin/version.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "shared_info.h" +#include "pinfo.h" +#include <assert.h> +#include <ctype.h> + +#define _COMPILING_NEWLIB +#include <dirent.h> + +/* The fhandler_base stat calls and helpers are actually only + applicable to files on disk. However, they are part of the + base class so that on-disk device files can also access them + as appropriate. */ + +static int __stdcall +num_entries (const char *win32_name) +{ + WIN32_FIND_DATA buf; + HANDLE handle; + char buf1[MAX_PATH]; + int count = 0; + + strcpy (buf1, win32_name); + int len = strlen (buf1); + if (len == 0 || isdirsep (buf1[len - 1])) + strcat (buf1, "*"); + else + strcat (buf1, "/*"); /* */ + + handle = FindFirstFileA (buf1, &buf); + + if (handle == INVALID_HANDLE_VALUE) + return 2; /* 2 is the minimum number of links to a dir, so... */ + count ++; + while (FindNextFileA (handle, &buf)) + { + if ((buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + count ++; + } + FindClose (handle); + return count; +} + +int __stdcall +fhandler_base::fstat_by_handle (struct __stat64 *buf, path_conv *pc) +{ + int res = 0; + BY_HANDLE_FILE_INFORMATION local; + + /* NT 3.51 seems to have a bug when attempting to get vol serial + numbers. This loop gets around this. */ + for (int i = 0; i < 2; i++) + { + if (!(res = GetFileInformationByHandle (get_handle (), &local))) + break; + if (local.dwVolumeSerialNumber && (long) local.dwVolumeSerialNumber != -1) + break; + } + + debug_printf ("%d = GetFileInformationByHandle (%s, %d)", + res, get_win32_name (), get_handle ()); + if (res == 0) + /* GetFileInformationByHandle will fail if it's given stdin/out/err + or a pipe*/ + { + memset (&local, 0, sizeof (local)); + local.nFileSizeLow = GetFileSize (get_handle (), &local.nFileSizeHigh); + } + + return fstat_helper (buf, pc, + local.ftCreationTime, + local.ftLastAccessTime, + local.ftLastWriteTime, + local.nFileSizeHigh, + local.nFileSizeLow, + local.nFileIndexHigh, + local.nFileIndexLow, + local.nNumberOfLinks); +} + +int __stdcall +fhandler_base::fstat_by_name (struct __stat64 *buf, path_conv *pc) +{ + int res; + + if (!pc->exists ()) + { + debug_printf ("already determined that pc does not exist"); + set_errno (ENOENT); + res = -1; + } + else + { + char drivebuf[5]; + char *name; + if ((*pc)[3] != '\0' || !isalpha ((*pc)[0]) || (*pc)[1] != ':' || (*pc)[2] != '\\') + name = *pc; + else + { + /* FIXME: Does this work on empty disks? */ + drivebuf[0] = (*pc)[0]; + drivebuf[1] = (*pc)[1]; + drivebuf[2] = (*pc)[2]; + drivebuf[3] = '*'; + drivebuf[4] = '\0'; + name = drivebuf; + } + + HANDLE h; + WIN32_FIND_DATA local; + if ((h = FindFirstFile (name, &local)) == INVALID_HANDLE_VALUE) + { + debug_printf ("FindFirstFile failed for '%s', %E", name); + __seterrno (); + res = -1; + } + else + { + FindClose (h); + res = fstat_helper (buf, pc, + local.ftCreationTime, + local.ftLastAccessTime, + local.ftLastWriteTime, + local.nFileSizeHigh, + local.nFileSizeLow); + } + } + return res; +} + +int __stdcall +fhandler_base::fstat_fs (struct __stat64 *buf, path_conv *pc) +{ + int res = -1; + int oret; + __uid32_t uid; + __gid32_t gid; + int open_flags = O_RDONLY | O_BINARY | O_DIROPEN; + bool query_open_already; + + if (get_io_handle ()) + { + if (get_nohandle ()) + return fstat_by_name (buf, pc); + else + return fstat_by_handle (buf, pc); + } + /* If we don't care if the file is executable or we already know if it is, + then just do a "query open" as it is apparently much faster. */ + if (pc->exec_state () != dont_know_if_executable) + set_query_open (query_open_already = true); + else + query_open_already = false; + + if (query_open_already && strncasematch (pc->volname (), "FAT", 3) + && !strpbrk (get_win32_name (), "?*|<>")) + oret = 0; + else if (!(oret = open_fs (pc, open_flags, 0))) + { + int ntsec_atts = 0; + /* If we couldn't open the file, try a "query open" with no permissions. + This will allow us to determine *some* things about the file, at least. */ + set_query_open (true); + if (!query_open_already && (oret = open (pc, open_flags, 0))) + /* ok */; + else if (allow_ntsec && pc->has_acls () && get_errno () == EACCES + && !get_file_attribute (TRUE, get_win32_name (), &ntsec_atts, &uid, &gid) + && !ntsec_atts && uid == myself->uid && gid == myself->gid) + { + /* Check a special case here. If ntsec is ON it happens + that a process creates a file using mode 000 to disallow + other processes access. In contrast to UNIX, this results + in a failing open call in the same process. Check that + case. */ + set_file_attribute (TRUE, get_win32_name (), 0400); + oret = open (pc, open_flags, 0); + set_file_attribute (TRUE, get_win32_name (), ntsec_atts); + } + } + + if (!oret || get_nohandle ()) + res = fstat_by_name (buf, pc); + else + { + res = fstat_by_handle (buf, pc); + close_fs (); + } + + return res; +} + +int __stdcall +fhandler_base::fstat_helper (struct __stat64 *buf, path_conv *pc, + FILETIME ftCreationTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime, + DWORD nFileSizeHigh, + DWORD nFileSizeLow, + DWORD nFileIndexHigh, + DWORD nFileIndexLow, + DWORD nNumberOfLinks) +{ + /* This is for FAT filesystems, which don't support atime/ctime */ + if (ftLastAccessTime.dwLowDateTime == 0 + && ftLastAccessTime.dwHighDateTime == 0) + ftLastAccessTime = ftLastWriteTime; + if (ftCreationTime.dwLowDateTime == 0 + && ftCreationTime.dwHighDateTime == 0) + ftCreationTime = ftLastWriteTime; + + to_timestruc_t (&ftLastAccessTime, &buf->st_atim); + to_timestruc_t (&ftLastWriteTime, &buf->st_mtim); + to_timestruc_t (&ftCreationTime, &buf->st_ctim); + buf->st_dev = pc->volser (); + buf->st_size = ((__off64_t)nFileSizeHigh << 32) + nFileSizeLow; + /* Unfortunately the count of 2 confuses `find (1)' command. So + let's try it with `1' as link count. */ + if (pc->isdir () && !pc->isremote () && nNumberOfLinks == 1) + buf->st_nlink = num_entries (pc->get_win32 ()); + else + buf->st_nlink = nNumberOfLinks; + + /* Assume that if a drive has ACL support it MAY have valid "inodes". + It definitely does not have valid inodes if it does not have ACL + support. */ + switch (pc->has_acls () && (nFileIndexHigh || nFileIndexLow) + ? pc->drive_type () : DRIVE_UNKNOWN) + { + case DRIVE_FIXED: + case DRIVE_REMOVABLE: + case DRIVE_CDROM: + case DRIVE_RAMDISK: + /* Although the documentation indicates otherwise, it seems like + "inodes" on these devices are persistent, at least across reboots. */ + buf->st_ino = nFileIndexHigh | nFileIndexLow; + break; + default: + /* Either the nFileIndex* fields are unreliable or unavailable. Use the + next best alternative. */ + buf->st_ino = get_namehash (); + break; + } + + buf->st_blksize = S_BLKSIZE; + buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE; + + buf->st_mode = 0; + /* Using a side effect: get_file_attibutes checks for + directory. This is used, to set S_ISVTX, if needed. */ + if (pc->isdir ()) + buf->st_mode = S_IFDIR; + else if (pc->issymlink ()) + buf->st_mode = S_IFLNK; + else if (pc->issocket ()) + buf->st_mode = S_IFSOCK; + + __uid32_t uid; + __gid32_t gid; + if (get_file_attribute (pc->has_acls (), get_win32_name (), &buf->st_mode, + &uid, &gid) == 0) + { + /* If read-only attribute is set, modify ntsec return value */ + if (pc->has_attribute (FILE_ATTRIBUTE_READONLY) && !get_symlink_p ()) + buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + + if (!(buf->st_mode & S_IFMT)) + buf->st_mode |= S_IFREG; + } + else + { + buf->st_mode |= STD_RBITS; + + if (!pc->has_attribute (FILE_ATTRIBUTE_READONLY)) + buf->st_mode |= STD_WBITS; + /* | S_IWGRP | S_IWOTH; we don't give write to group etc */ + + if (buf->st_mode & S_IFDIR) + buf->st_mode |= S_IFDIR | STD_XBITS; + else if (buf->st_mode & S_IFMT) + /* nothing */; + else if (pc->issocket ()) + buf->st_mode |= S_IFSOCK; + else if (is_fs_special ()) + { + buf->st_dev = dev; + buf->st_mode = dev.mode; + } + else + { + buf->st_mode |= S_IFREG; + if (pc->exec_state () == dont_know_if_executable) + { + DWORD cur, done; + char magic[3]; + + /* First retrieve current position, set to beginning + of file if not already there. */ + cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT); + if (cur != INVALID_SET_FILE_POINTER + && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN) + != INVALID_SET_FILE_POINTER)) + { + /* FIXME should we use /etc/magic ? */ + magic[0] = magic[1] = magic[2] = '\0'; + if (ReadFile (get_handle (), magic, 3, &done, NULL) + && has_exec_chars (magic, done)) + { + set_execable_p (); + pc->set_exec (); + buf->st_mode |= STD_XBITS; + } + (void) SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN); + } + } + } + + if (pc->exec_state () == is_executable) + buf->st_mode |= STD_XBITS; + } + + buf->st_uid = uid; + buf->st_gid = gid; + + /* The number of links to a directory includes the + number of subdirectories in the directory, since all + those subdirectories point to it. + This is too slow on remote drives, so we do without it and + set the number of links to 2. */ + + syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%d, sizeof=%d", + buf, buf->st_atime, buf->st_size, buf->st_mode, + (int) buf->st_ino, sizeof (*buf)); + return 0; +} + +int __stdcall +fhandler_disk_file::fstat (struct __stat64 *buf, path_conv *pc) +{ + return fstat_fs (buf, pc); +} + +fhandler_disk_file::fhandler_disk_file () : + fhandler_base () +{ +} + +int +fhandler_disk_file::open (path_conv *real_path, int flags, mode_t mode) +{ + return open_fs (real_path, flags, mode); +} + +int +fhandler_base::open_fs (path_conv *real_path, int flags, mode_t mode) +{ + if (real_path->case_clash && flags & O_CREAT) + { + debug_printf ("case clash detected"); + set_errno (ECASECLASH); + return 0; + } + + set_has_acls (real_path->has_acls ()); + set_isremote (real_path->isremote ()); + + int res = this->fhandler_base::open (real_path, flags | O_DIROPEN, mode); + if (!res) + goto out; + + /* This is for file systems known for having a buggy CreateFile call + which might return a valid HANDLE without having actually opened + the file. + The only known file system to date is the SUN NFS Solstice Client 3.1 + which returns a valid handle when trying to open a file in a nonexistent + directory. */ + if (real_path->has_buggy_open () && !real_path->exists ()) + { + debug_printf ("Buggy open detected."); + close_fs (); + set_errno (ENOENT); + return 0; + } + + set_symlink_p (real_path->issymlink ()); + set_execable_p (real_path->exec_state ()); + set_socket_p (real_path->issocket ()); + +out: + syscall_printf ("%d = fhandler_disk_file::open (%s, %p)", res, + get_win32_name (), flags); + return res; +} + +int +fhandler_disk_file::close () +{ + return close_fs (); +} + +int +fhandler_base::close_fs () +{ + int res = this->fhandler_base::close (); + if (!res) + cygwin_shared->delqueue.process_queue (); + return res; +} + +/* FIXME: The correct way to do this to get POSIX locking semantics is to + keep a linked list of posix lock requests and map them into Win32 locks. + he problem is that Win32 does not deal correctly with overlapping lock + requests. Also another pain is that Win95 doesn't do non-blocking or + non-exclusive locks at all. For '95 just convert all lock requests into + blocking,exclusive locks. This shouldn't break many apps but denying all + locking would. For now just convert to Win32 locks and hope for + the best. */ + +int +fhandler_disk_file::lock (int cmd, struct flock *fl) +{ + __off64_t win32_start; + int win32_len; + DWORD win32_upper; + __off64_t startpos; + + /* + * We don't do getlck calls yet. + */ + + if (cmd == F_GETLK) + { + set_errno (ENOSYS); + return -1; + } + + /* + * Calculate where in the file to start from, + * then adjust this by fl->l_start. + */ + + switch (fl->l_whence) + { + case SEEK_SET: + startpos = 0; + break; + case SEEK_CUR: + if ((startpos = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK) + return -1; + break; + case SEEK_END: + { + BY_HANDLE_FILE_INFORMATION finfo; + if (GetFileInformationByHandle (get_handle (), &finfo) == 0) + { + __seterrno (); + return -1; + } + startpos = ((__off64_t)finfo.nFileSizeHigh << 32) + + finfo.nFileSizeLow; + break; + } + default: + set_errno (EINVAL); + return -1; + } + + /* + * Now the fun starts. Adjust the start and length + * fields until they make sense. + */ + + win32_start = startpos + fl->l_start; + if (fl->l_len < 0) + { + win32_start -= fl->l_len; + win32_len = -fl->l_len; + } + else + win32_len = fl->l_len; + + if (win32_start < 0) + { + /* watch the signs! */ + win32_len -= -win32_start; + if (win32_len <= 0) + { + /* Failure ! */ + set_errno (EINVAL); + return -1; + } + win32_start = 0; + } + + /* + * Special case if len == 0 for POSIX means lock + * to the end of the entire file (and all future extensions). + */ + if (win32_len == 0) + { + win32_len = 0xffffffff; + win32_upper = wincap.lock_file_highword (); + } + else + win32_upper = 0; + + BOOL res; + + if (wincap.has_lock_file_ex ()) + { + DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0; + lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0; + + OVERLAPPED ov; + + ov.Internal = 0; + ov.InternalHigh = 0; + ov.Offset = (DWORD)win32_start; + ov.OffsetHigh = 0; + ov.hEvent = (HANDLE) 0; + + if (fl->l_type == F_UNLCK) + { + res = UnlockFileEx (get_handle (), 0, (DWORD)win32_len, win32_upper, &ov); + } + else + { + res = LockFileEx (get_handle (), lock_flags, 0, (DWORD)win32_len, + win32_upper, &ov); + /* Deal with the fail immediately case. */ + /* + * FIXME !! I think this is the right error to check for + * but I must admit I haven't checked.... + */ + if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) && + (GetLastError () == ERROR_LOCK_FAILED)) + { + set_errno (EAGAIN); + return -1; + } + } + } + else + { + /* Windows 95 -- use primitive lock call */ + if (fl->l_type == F_UNLCK) + res = UnlockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, + win32_upper); + else + res = LockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, win32_upper); + } + + if (res == 0) + { + __seterrno (); + return -1; + } + + return 0; +} + +DIR * +fhandler_disk_file::opendir (path_conv& real_name) +{ + DIR *dir; + DIR *res = NULL; + size_t len; + + if (!real_name.isdir ()) + set_errno (ENOTDIR); + else if ((len = strlen (real_name))> MAX_PATH - 3) + set_errno (ENAMETOOLONG); + else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) + set_errno (ENOMEM); + else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL) + { + free (dir); + set_errno (ENOMEM); + } + else if ((dir->__d_dirent = + (struct dirent *) malloc (sizeof (struct dirent))) == NULL) + { + free (dir->__d_dirname); + free (dir); + set_errno (ENOMEM); + } + else + { + strcpy (dir->__d_dirname, real_name.get_win32 ()); + dir->__d_dirent->d_version = __DIRENT_VERSION; + cygheap_fdnew fd; + fd = this; + fd->set_nohandle (true); + dir->__d_dirent->d_fd = fd; + dir->__d_u.__d_data.__fh = this; + /* FindFirstFile doesn't seem to like duplicate /'s. */ + len = strlen (dir->__d_dirname); + if (len == 0 || isdirsep (dir->__d_dirname[len - 1])) + strcat (dir->__d_dirname, "*"); + else + strcat (dir->__d_dirname, "\\*"); /**/ + dir->__d_cookie = __DIRENT_COOKIE; + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + dir->__d_position = 0; + dir->__d_dirhash = get_namehash (); + + res = dir; + } + + syscall_printf ("%p = opendir (%s)", res, get_name ()); + return res; +} + +struct dirent * +fhandler_disk_file::readdir (DIR *dir) +{ + WIN32_FIND_DATA buf; + HANDLE handle; + struct dirent *res = NULL; + + if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE + && dir->__d_position == 0) + { + handle = FindFirstFileA (dir->__d_dirname, &buf); + DWORD lasterr = GetLastError (); + dir->__d_u.__d_data.__handle = handle; + if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) + { + seterrno_from_win_error (__FILE__, __LINE__, lasterr); + return res; + } + } + else if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE) + { + return res; + } + else if (!FindNextFileA (dir->__d_u.__d_data.__handle, &buf)) + { + DWORD lasterr = GetLastError (); + (void) FindClose (dir->__d_u.__d_data.__handle); + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + /* POSIX says you shouldn't set errno when readdir can't + find any more files; so, if another error we leave it set. */ + if (lasterr != ERROR_NO_MORE_FILES) + seterrno_from_win_error (__FILE__, __LINE__, lasterr); + syscall_printf ("%p = readdir (%p)", res, dir); + return res; + } + + /* We get here if `buf' contains valid data. */ + strcpy (dir->__d_dirent->d_name, buf.cFileName); + + /* Check for Windows shortcut. If it's a Cygwin or U/WIN + symlink, drop the .lnk suffix. */ + if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + { + char *c = dir->__d_dirent->d_name; + int len = strlen (c); + if (strcasematch (c + len - 4, ".lnk")) + { + char fbuf[MAX_PATH + 1]; + strcpy (fbuf, dir->__d_dirname); + strcpy (fbuf + strlen (fbuf) - 1, dir->__d_dirent->d_name); + path_conv fpath (fbuf, PC_SYM_NOFOLLOW); + if (fpath.issymlink () || fpath.isspecial ()) + c[len - 4] = '\0'; + } + } + + dir->__d_position++; + res = dir->__d_dirent; + syscall_printf ("%p = readdir (%p) (%s)", + &dir->__d_dirent, dir, buf.cFileName); + return res; +} + +__off64_t +fhandler_disk_file::telldir (DIR *dir) +{ + return dir->__d_position; +} + +void +fhandler_disk_file::seekdir (DIR *dir, __off64_t loc) +{ + rewinddir (dir); + while (loc > dir->__d_position) + if (!readdir (dir)) + break; +} + +void +fhandler_disk_file::rewinddir (DIR *dir) +{ + if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE) + { + (void) FindClose (dir->__d_u.__d_data.__handle); + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + } + dir->__d_position = 0; +} + +int +fhandler_disk_file::closedir (DIR *dir) +{ + int res = 0; + if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE && + FindClose (dir->__d_u.__d_data.__handle) == 0) + { + __seterrno (); + res = -1; + } + syscall_printf ("%d = closedir (%p)", res, dir); + return 0; +} + +fhandler_cygdrive::fhandler_cygdrive () : + fhandler_disk_file (), ndrives (0), pdrive (NULL) +{ +} + +#define DRVSZ sizeof ("x:\\") +void +fhandler_cygdrive::set_drives () +{ + const int len = 1 + 26 * DRVSZ; + char *p = (char *) crealloc ((void *) win32_path_name, + sizeof (".") + sizeof ("..") + len); + + win32_path_name = pdrive = p; + strcpy (p, "."); + strcpy (p + sizeof ("."), ".."); + p += sizeof (".") + sizeof (".."); + ndrives = GetLogicalDriveStrings (len, p) / DRVSZ; +} + +int +fhandler_cygdrive::fstat (struct __stat64 *buf, path_conv *pc) +{ + if (!iscygdrive_root ()) + return fhandler_disk_file::fstat (buf, pc); + buf->st_mode = S_IFDIR | 0555; + if (!ndrives) + set_drives (); + buf->st_nlink = ndrives; + return 0; +} + +DIR * +fhandler_cygdrive::opendir (path_conv& real_name) +{ + DIR *dir; + + dir = fhandler_disk_file::opendir (real_name); + if (dir && iscygdrive_root () && !ndrives) + set_drives (); + + return dir; +} + +struct dirent * +fhandler_cygdrive::readdir (DIR *dir) +{ + if (!iscygdrive_root ()) + return fhandler_disk_file::readdir (dir); + if (!pdrive || !*pdrive) + { + set_errno (ENMFILE); + return NULL; + } + else if (dir->__d_position > 1 + && GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES) + { + pdrive = strchr (pdrive, '\0') + 1; + return readdir (dir); + } + else if (*pdrive == '.') + strcpy (dir->__d_dirent->d_name, pdrive); + else + { + *dir->__d_dirent->d_name = cyg_tolower (*pdrive); + dir->__d_dirent->d_name[1] = '\0'; + } + dir->__d_position++; + pdrive = strchr (pdrive, '\0') + 1; + syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir, + dir->__d_dirent->d_name); + return dir->__d_dirent; +} + +__off64_t +fhandler_cygdrive::telldir (DIR *dir) +{ + return fhandler_disk_file::telldir (dir); +} + +void +fhandler_cygdrive::seekdir (DIR *dir, __off64_t loc) +{ + if (!iscygdrive_root ()) + return fhandler_disk_file::seekdir (dir, loc); + + for (pdrive = win32_path_name, dir->__d_position = -1; *pdrive; + pdrive = strchr (pdrive, '\0') + 1) + if (++dir->__d_position >= loc) + break; + + return; +} + +void +fhandler_cygdrive::rewinddir (DIR *dir) +{ + if (!iscygdrive_root ()) + return fhandler_disk_file::rewinddir (dir); + pdrive = win32_path_name; + dir->__d_position = 0; + return; +} + +int +fhandler_cygdrive::closedir (DIR *dir) +{ + if (!iscygdrive_root ()) + return fhandler_disk_file::closedir (dir); + pdrive = win32_path_name; + return -1; +} diff --git a/winsup/cygwin/fhandler_dsp.cc b/winsup/cygwin/fhandler_dsp.cc new file mode 100644 index 00000000000..daa9a629807 --- /dev/null +++ b/winsup/cygwin/fhandler_dsp.cc @@ -0,0 +1,653 @@ +/* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp + + Copyright 2001, 2002, 2003 Red Hat, Inc + + Written by Andy Younger (andy@snoogie.demon.co.uk) + +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 <stdio.h> +#include <errno.h> +#include <windows.h> +#include <sys/soundcard.h> +#include <mmsystem.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" + +//------------------------------------------------------------------------ +// Simple encapsulation of the win32 audio device. +// +static void CALLBACK wave_callback (HWAVE hWave, UINT msg, DWORD instance, + DWORD param1, DWORD param2); +class Audio +{ +public: + enum + { + MAX_BLOCKS = 12, + BLOCK_SIZE = 16384, + TOT_BLOCK_SIZE = BLOCK_SIZE + sizeof (WAVEHDR) + }; + + Audio (); + ~Audio (); + + bool open (int rate, int bits, int channels, bool bCallback = false); + void close (); + int getvolume (); + void setvolume (int newVolume); + bool write (const void *pSampleData, int nBytes); + int blocks (); + void callback_sampledone (void *pData); + void setformat (int format) {formattype_ = format;} + int numbytesoutput (); + + void *operator new (size_t, void *p) {return p;} + +private: + char *initialisebuffer (); + void waitforcallback (); + bool flush (); + + HWAVEOUT dev_; + volatile int nBlocksInQue_; + int nBytesWritten_; + char *buffer_; + int bufferIndex_; + CRITICAL_SECTION lock_; + char *freeblocks_[MAX_BLOCKS]; + int formattype_; + + char bigwavebuffer_[MAX_BLOCKS * TOT_BLOCK_SIZE]; +}; + +static char audio_buf[sizeof (class Audio)]; + +Audio::Audio () +{ + InitializeCriticalSection (&lock_); + memset (bigwavebuffer_, 0, sizeof (bigwavebuffer_)); + for (int i = 0; i < MAX_BLOCKS; i++) + freeblocks_[i] = &bigwavebuffer_[i * TOT_BLOCK_SIZE]; +} + +Audio::~Audio () +{ + if (dev_) + close (); + DeleteCriticalSection (&lock_); +} + +bool +Audio::open (int rate, int bits, int channels, bool bCallback) +{ + WAVEFORMATEX format; + int nDevices = waveOutGetNumDevs (); + + nBytesWritten_ = 0L; + bufferIndex_ = 0; + buffer_ = 0L; + debug_printf ("number devices %d", nDevices); + if (nDevices <= 0) + return false; + + debug_printf ("trying to map device freq %d, bits %d, " + "channels %d, callback %d", rate, bits, channels, + bCallback); + + int bytesperSample = bits / 8; + + memset (&format, 0, sizeof (format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.wBitsPerSample = bits; + format.nChannels = channels; + format.nSamplesPerSec = rate; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels * + bytesperSample; + format.nBlockAlign = format.nChannels * bytesperSample; + + nBlocksInQue_ = 0; + HRESULT res = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) wave_callback, + (DWORD) this, bCallback ? CALLBACK_FUNCTION : 0); + if (res == S_OK) + { + debug_printf ("Sucessfully opened!"); + return true; + } + else + { + debug_printf ("failed to open"); + return false; + } +} + +void +Audio::close () +{ + if (dev_) + { + flush (); // force out last block whatever size.. + + while (blocks ()) // block till finished.. + waitforcallback (); + + waveOutReset (dev_); + waveOutClose (dev_); + dev_ = 0L; + } + nBytesWritten_ = 0L; +} + +int +Audio::numbytesoutput () +{ + return nBytesWritten_; +} + +int +Audio::getvolume () +{ + DWORD volume; + + waveOutGetVolume (dev_, &volume); + return ((volume >> 16) + (volume & 0xffff)) >> 1; +} + +void +Audio::setvolume (int newVolume) +{ + waveOutSetVolume (dev_, (newVolume << 16) | newVolume); +} + +char * +Audio::initialisebuffer () +{ + EnterCriticalSection (&lock_); + WAVEHDR *pHeader = 0L; + for (int i = 0; i < MAX_BLOCKS; i++) + { + char *pData = freeblocks_[i]; + if (pData) + { + pHeader = (WAVEHDR *) pData; + if (pHeader->dwFlags & WHDR_DONE) + { + waveOutUnprepareHeader (dev_, pHeader, sizeof (WAVEHDR)); + } + freeblocks_[i] = 0L; + break; + } + } + LeaveCriticalSection (&lock_); + + if (pHeader) + { + memset (pHeader, 0, sizeof (WAVEHDR)); + pHeader->dwBufferLength = BLOCK_SIZE; + pHeader->lpData = (LPSTR) (&pHeader[1]); + return (char *) pHeader->lpData; + } + return 0L; +} + +bool +Audio::write (const void *pSampleData, int nBytes) +{ + // split up big blocks into smaller BLOCK_SIZE chunks + while (nBytes > BLOCK_SIZE) + { + write (pSampleData, BLOCK_SIZE); + nBytes -= BLOCK_SIZE; + pSampleData = (void *) ((char *) pSampleData + BLOCK_SIZE); + } + + // Block till next sound is flushed + if (blocks () == MAX_BLOCKS) + waitforcallback (); + + // Allocate new wave buffer if necessary + if (buffer_ == 0L) + { + buffer_ = initialisebuffer (); + if (buffer_ == 0L) + return false; + } + + + // Handle gathering blocks into larger buffer + int sizeleft = BLOCK_SIZE - bufferIndex_; + if (nBytes < sizeleft) + { + memcpy (&buffer_[bufferIndex_], pSampleData, nBytes); + bufferIndex_ += nBytes; + nBytesWritten_ += nBytes; + return true; + } + + // flushing when we reach our limit of BLOCK_SIZE + memcpy (&buffer_[bufferIndex_], pSampleData, sizeleft); + bufferIndex_ += sizeleft; + nBytesWritten_ += sizeleft; + flush (); + + // change pointer to rest of sample, and size accordingly + pSampleData = (void *) ((char *) pSampleData + sizeleft); + nBytes -= sizeleft; + + // if we still have some sample left over write it out + if (nBytes) + return write (pSampleData, nBytes); + + return true; +} + +// return number of blocks back. +int +Audio::blocks () +{ + EnterCriticalSection (&lock_); + int ret = nBlocksInQue_; + LeaveCriticalSection (&lock_); + return ret; +} + +// This is called on an interupt so use locking.. Note nBlocksInQue_ is +// modified by it so we should wrap all references to it in locks. +void +Audio::callback_sampledone (void *pData) +{ + EnterCriticalSection (&lock_); + + nBlocksInQue_--; + for (int i = 0; i < MAX_BLOCKS; i++) + if (!freeblocks_[i]) + { + freeblocks_[i] = (char *) pData; + break; + } + + LeaveCriticalSection (&lock_); +} + +void +Audio::waitforcallback () +{ + int n = blocks (); + if (!n) + return; + do + { + Sleep (250); + } + while (n == blocks ()); +} + +bool +Audio::flush () +{ + if (!buffer_) + return false; + + // Send internal buffer out to the soundcard + WAVEHDR *pHeader = ((WAVEHDR *) buffer_) - 1; + pHeader->dwBufferLength = bufferIndex_; + + // Quick bit of sample buffer conversion + if (formattype_ == AFMT_S8) + { + unsigned char *p = ((unsigned char *) buffer_); + for (int i = 0; i < bufferIndex_; i++) + { + p[i] -= 0x7f; + } + } + + if (waveOutPrepareHeader (dev_, pHeader, sizeof (WAVEHDR)) == S_OK && + waveOutWrite (dev_, pHeader, sizeof (WAVEHDR)) == S_OK) + { + EnterCriticalSection (&lock_); + nBlocksInQue_++; + LeaveCriticalSection (&lock_); + bufferIndex_ = 0; + buffer_ = 0L; + return true; + } + else + { + EnterCriticalSection (&lock_); + for (int i = 0; i < MAX_BLOCKS; i++) + if (!freeblocks_[i]) + { + freeblocks_[i] = (char *) pHeader; + break; + } + LeaveCriticalSection (&lock_); + } + return false; +} + +//------------------------------------------------------------------------ +// Call back routine +static void CALLBACK +wave_callback (HWAVE hWave, UINT msg, DWORD instance, DWORD param1, + DWORD param2) +{ + if (msg == WOM_DONE) + { + Audio *ptr = (Audio *) instance; + ptr->callback_sampledone ((void *) param1); + } +} + +//------------------------------------------------------------------------ +// /dev/dsp handler +static Audio *s_audio; // static instance of the Audio handler + +//------------------------------------------------------------------------ +// wav file detection.. +#pragma pack(1) +struct wavchunk +{ + char id[4]; + unsigned int len; +}; +struct wavformat +{ + unsigned short wFormatTag; + unsigned short wChannels; + unsigned int dwSamplesPerSec; + unsigned int dwAvgBytesPerSec; + unsigned short wBlockAlign; + unsigned short wBitsPerSample; +}; +#pragma pack() + +bool +fhandler_dev_dsp::setupwav (const char *pData, int nBytes) +{ + int len; + const char *end = pData + nBytes; + + if (!(pData[0] == 'R' && pData[1] == 'I' && + pData[2] == 'F' && pData[3] == 'F')) + return false; + if (!(pData[8] == 'W' && pData[9] == 'A' && + pData[10] == 'V' && pData[11] == 'E')) + return false; + + len = *(int *) &pData[4]; + pData += 12; + while (len && pData < end) + { + wavchunk * pChunk = (wavchunk *) pData; + int blklen = pChunk-> len; + if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm' && + pChunk->id[2] == 't' && pChunk->id[3] == ' ') + { + wavformat *format = (wavformat *) (pChunk + 1); + if ((char *) (format + 1) > end) + return false; + + // Open up audio device with correct frequency for wav file + // + // FIXME: should through away all the header & not output + // it to the soundcard. + s_audio->close (); + if (s_audio->open (format->dwSamplesPerSec, format->wBitsPerSample, + format->wChannels) == false) + { + s_audio->open (audiofreq_, audiobits_, audiochannels_); + } + else + { + audiofreq_ = format->dwSamplesPerSec; + audiobits_ = format->wBitsPerSample; + audiochannels_ = format->wChannels; + } + return true; + } + + pData += blklen + sizeof (wavchunk); + } + return false; +} + +//------------------------------------------------------------------------ +fhandler_dev_dsp::fhandler_dev_dsp (): + fhandler_base () +{ +} + +fhandler_dev_dsp::~fhandler_dev_dsp () +{ +} + +int +fhandler_dev_dsp::open (path_conv *, int flags, mode_t mode) +{ + // currently we only support writing + if ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_WRONLY) + { + set_errno (EACCES); + return 0; + } + + set_flags ((flags & ~O_TEXT) | O_BINARY); + + if (!s_audio) + s_audio = new (audio_buf) Audio; + + // Work out initial sample format & frequency + // dev/dsp defaults + audioformat_ = AFMT_S8; + audiofreq_ = 8000; + audiobits_ = 8; + audiochannels_ = 1; + + int res; + if (!s_audio->open (audiofreq_, audiobits_, audiochannels_)) + res = 0; + else + { + set_open_status (); + res = 1; + } + + debug_printf ("returns %d", res); + return res; +} + +int +fhandler_dev_dsp::write (const void *ptr, size_t len) +{ + if (s_audio->numbytesoutput () == 0) + { + // check for wave file & setup frequencys properly if possible. + setupwav ((const char *) ptr, len); + + // Open audio device properly with callbacks. + s_audio->close (); + if (!s_audio->open (audiofreq_, audiobits_, audiochannels_, true)) + return 0; + } + + s_audio->write (ptr, len); + return len; +} + +void __stdcall +fhandler_dev_dsp::read (void *ptr, size_t& len) +{ + return; +} + +__off64_t +fhandler_dev_dsp::lseek (__off64_t offset, int whence) +{ + return 0; +} + +int +fhandler_dev_dsp::close (void) +{ + s_audio->close (); + return 0; +} + +int +fhandler_dev_dsp::dup (fhandler_base * child) +{ + fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child; + + fhc->set_flags (get_flags ()); + fhc->audiochannels_ = audiochannels_; + fhc->audiobits_ = audiobits_; + fhc->audiofreq_ = audiofreq_; + fhc->audioformat_ = audioformat_; + return 0; +} + +int +fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr) +{ + int *intptr = (int *) ptr; + switch (cmd) + { +#define CASE(a) case a : debug_printf("/dev/dsp: ioctl %s", #a); + + CASE (SNDCTL_DSP_RESET) + audioformat_ = AFMT_S8; + audiofreq_ = 8000; + audiobits_ = 8; + audiochannels_ = 1; + return 0; + + CASE (SNDCTL_DSP_GETBLKSIZE) + *intptr = Audio::BLOCK_SIZE; + return 0; + + CASE (SNDCTL_DSP_SETFMT) + { + int nBits = 0; + if (*intptr == AFMT_S16_LE) + nBits = 16; + else if (*intptr == AFMT_U8) + nBits = 8; + else if (*intptr == AFMT_S8) + nBits = 8; + if (nBits) + { + s_audio->setformat (*intptr); + s_audio->close (); + if (s_audio->open (audiofreq_, nBits, audiochannels_) == true) + { + audiobits_ = nBits; + return 0; + } + else + { + s_audio->open (audiofreq_, audiobits_, audiochannels_); + return -1; + } + } + } + break; + + CASE (SNDCTL_DSP_SPEED) + s_audio->close (); + if (s_audio->open (*intptr, audiobits_, audiochannels_) == true) + { + audiofreq_ = *intptr; + return 0; + } + else + { + s_audio->open (audiofreq_, audiobits_, audiochannels_); + return -1; + } + break; + + CASE (SNDCTL_DSP_STEREO) + { + int nChannels = *intptr + 1; + + s_audio->close (); + if (s_audio->open (audiofreq_, audiobits_, nChannels) == true) + { + audiochannels_ = nChannels; + return 0; + } + else + { + s_audio->open (audiofreq_, audiobits_, audiochannels_); + return -1; + } + } + break; + + CASE (SNDCTL_DSP_GETOSPACE) + { + audio_buf_info *p = (audio_buf_info *) ptr; + + int nBlocks = s_audio->blocks (); + int leftblocks = ((Audio::MAX_BLOCKS - nBlocks) - 1); + if (leftblocks < 0) + leftblocks = 0; + if (leftblocks > 1) + leftblocks = 1; + int left = leftblocks * Audio::BLOCK_SIZE; + + p->fragments = leftblocks; + p->fragstotal = Audio::MAX_BLOCKS; + p->fragsize = Audio::BLOCK_SIZE; + p->bytes = left; + + debug_printf ("ptr %p nblocks %d leftblocks %d left bytes %d ", + ptr, nBlocks, leftblocks, left); + + return 0; + } + break; + + CASE (SNDCTL_DSP_SETFRAGMENT) + { + // Fake!! esound & mikmod require this on non PowerPC platforms. + // + return 0; + } + break; + + CASE (SNDCTL_DSP_GETFMTS) + { + *intptr = AFMT_S16_LE | AFMT_U8 | AFMT_S8; // more? + return 0; + } + break; + + default: + debug_printf ("/dev/dsp: ioctl not handled yet! FIXME:"); + break; + +#undef CASE + }; + return -1; +} + +void +fhandler_dev_dsp::dump () +{ + paranoid_printf ("here, fhandler_dev_dsp"); +} + +void +fhandler_dev_dsp::fixup_after_exec (HANDLE) +{ + /* FIXME: Is there a better way to do this? */ + s_audio = new (audio_buf) Audio; +} diff --git a/winsup/cygwin/fhandler_floppy.cc b/winsup/cygwin/fhandler_floppy.cc new file mode 100644 index 00000000000..b8d5173e90c --- /dev/null +++ b/winsup/cygwin/fhandler_floppy.cc @@ -0,0 +1,316 @@ +/* fhandler_floppy.cc. See fhandler.h for a description of the + fhandler classes. + + Copyright 1999, 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. */ + +#include "winsup.h" +#include <sys/termios.h> +#include <errno.h> +#include <unistd.h> +#include <winioctl.h> +#include <asm/socket.h> +#include <cygwin/hdreg.h> +#include <cygwin/fs.h> +#include "security.h" +#include "fhandler.h" +#include "cygerrno.h" + +/**********************************************************************/ +/* fhandler_dev_floppy */ + +int +fhandler_dev_floppy::is_eom (int win_error) +{ + int ret = (win_error == ERROR_INVALID_PARAMETER); + if (ret) + debug_printf ("end of medium"); + return ret; +} + +int +fhandler_dev_floppy::is_eof (int) +{ + int ret = 0; + if (ret) + debug_printf ("end of file"); + return ret; +} + +fhandler_dev_floppy::fhandler_dev_floppy () + : fhandler_dev_raw () +{ +} + +int +fhandler_dev_floppy::open (path_conv *real_path, int flags, mode_t) +{ + /* The correct size of the buffer would be 512 bytes, + * which is the atomic size, supported by WinNT. + * Unfortunately, the performance is worse than + * access to file system on same device! + * Setting buffer size to a relatively big value + * increases performance by means. + * The new ioctl call with 'rdevio.h' header file + * supports changing this value. + * + * Let's be smart: Let's take a multiplier of typical tar + * and cpio buffer sizes by default! + */ + devbufsiz = 61440L; /* 512L; */ + return fhandler_dev_raw::open (real_path, flags); +} + +int +fhandler_dev_floppy::close (void) +{ + int ret; + + ret = writebuf (); + if (ret) + { + fhandler_dev_raw::close (); + return ret; + } + return fhandler_dev_raw::close (); +} + +__off64_t +fhandler_dev_floppy::lseek (__off64_t offset, int whence) +{ + int ret; + char buf[512]; + __off64_t drive_size = 0; + __off64_t lloffset = offset; + __off64_t current_position; + __off64_t sector_aligned_offset; + __off64_t bytes_left; + DWORD low; + LONG high = 0; + + DISK_GEOMETRY di; + PARTITION_INFORMATION pi; + DWORD bytes_read; + + if (!DeviceIoControl (get_handle (), + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, + &di, sizeof (di), + &bytes_read, NULL)) + { + __seterrno (); + return -1; + } + debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)", + di.Cylinders.LowPart, + di.TracksPerCylinder, + di.SectorsPerTrack, + di.BytesPerSector); + if (DeviceIoControl (get_handle (), + IOCTL_DISK_GET_PARTITION_INFO, + NULL, 0, + &pi, sizeof (pi), + &bytes_read, NULL)) + { + debug_printf ("partition info: %ld (%ld)", + pi.StartingOffset.LowPart, + pi.PartitionLength.LowPart); + drive_size = pi.PartitionLength.QuadPart; + } + else + { + drive_size = di.Cylinders.QuadPart * di.TracksPerCylinder * + di.SectorsPerTrack * di.BytesPerSector; + } + debug_printf ("drive size: %ld", drive_size); + + if (whence == SEEK_END && drive_size > 0) + { + lloffset = offset + drive_size; + whence = SEEK_SET; + } + + if (whence == SEEK_CUR) + { + low = SetFilePointer (get_handle (), 0, &high, FILE_CURRENT); + if (low == INVALID_SET_FILE_POINTER && GetLastError ()) + { + __seterrno (); + return -1; + } + current_position = low + ((__off64_t) high << 32); + if (is_writing) + current_position += devbufend - devbufstart; + else + current_position -= devbufend - devbufstart; + + lloffset += current_position; + whence = SEEK_SET; + } + + if (lloffset < 0 || + drive_size > 0 && lloffset > drive_size) + { + set_errno (EINVAL); + return -1; + } + + /* FIXME: sector can possibly be not 512 bytes long */ + sector_aligned_offset = (lloffset / 512) * 512; + bytes_left = lloffset - sector_aligned_offset; + + if (whence == SEEK_SET) + { + /* Invalidate buffer. */ + ret = writebuf (); + if (ret) + return ret; + devbufstart = devbufend = 0; + + low = sector_aligned_offset & 0xffffffff; + high = sector_aligned_offset >> 32; + if (SetFilePointer (get_handle (), low, &high, FILE_BEGIN) + == INVALID_SET_FILE_POINTER && GetLastError ()) + { + __seterrno (); + return -1; + } + + size_t len = bytes_left; + raw_read (buf, len); + return sector_aligned_offset + bytes_left; + } + + set_errno (EINVAL); + return -1; +} + +int +fhandler_dev_floppy::ioctl (unsigned int cmd, void *buf) +{ + DISK_GEOMETRY di; + PARTITION_INFORMATION pi; + DWORD bytes_read; + __off64_t drive_size = 0; + __off64_t start = 0; + switch (cmd) + { + case HDIO_GETGEO: + { + debug_printf ("HDIO_GETGEO"); + if (!DeviceIoControl (get_handle (), + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, + &di, sizeof (di), + &bytes_read, NULL)) + { + __seterrno (); + return -1; + } + debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)", + di.Cylinders.LowPart, + di.TracksPerCylinder, + di.SectorsPerTrack, + di.BytesPerSector); + if (DeviceIoControl (get_handle (), + IOCTL_DISK_GET_PARTITION_INFO, + NULL, 0, + &pi, sizeof (pi), + &bytes_read, NULL)) + { + debug_printf ("partition info: %ld (%ld)", + pi.StartingOffset.LowPart, + pi.PartitionLength.LowPart); + start = pi.StartingOffset.QuadPart >> 9ULL; + } + struct hd_geometry *geo = (struct hd_geometry *) buf; + geo->heads = di.TracksPerCylinder; + geo->sectors = di.SectorsPerTrack; + geo->cylinders = di.Cylinders.LowPart; + geo->start = start; + return 0; + } + case BLKGETSIZE: + case BLKGETSIZE64: + { + debug_printf ("BLKGETSIZE"); + if (!DeviceIoControl (get_handle (), + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, + &di, sizeof (di), + &bytes_read, NULL)) + { + __seterrno (); + return -1; + } + debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)", + di.Cylinders.LowPart, + di.TracksPerCylinder, + di.SectorsPerTrack, + di.BytesPerSector); + if (DeviceIoControl (get_handle (), + IOCTL_DISK_GET_PARTITION_INFO, + NULL, 0, + &pi, sizeof (pi), + &bytes_read, NULL)) + { + debug_printf ("partition info: %ld (%ld)", + pi.StartingOffset.LowPart, + pi.PartitionLength.LowPart); + drive_size = pi.PartitionLength.QuadPart; + } + else + { + drive_size = di.Cylinders.QuadPart * di.TracksPerCylinder * + di.SectorsPerTrack * di.BytesPerSector; + } + if (cmd == BLKGETSIZE) + *(long *)buf = drive_size >> 9UL; + else + *(__off64_t *)buf = drive_size; + return 0; + } + case BLKRRPART: + { + debug_printf ("BLKRRPART"); + if (!DeviceIoControl (get_handle (), + IOCTL_DISK_UPDATE_DRIVE_SIZE, + NULL, 0, + &di, sizeof (di), + &bytes_read, NULL)) + { + __seterrno (); + return -1; + } + return 0; + } + case BLKSSZGET: + { + debug_printf ("BLKSSZGET"); + if (!DeviceIoControl (get_handle (), + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, + &di, sizeof (di), + &bytes_read, NULL)) + { + __seterrno (); + return -1; + } + debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)", + di.Cylinders.LowPart, + di.TracksPerCylinder, + di.SectorsPerTrack, + di.BytesPerSector); + *(int *)buf = di.BytesPerSector; + return 0; + } + default: + return fhandler_dev_raw::ioctl (cmd, buf); + } +} + diff --git a/winsup/cygwin/fhandler_mem.cc b/winsup/cygwin/fhandler_mem.cc new file mode 100644 index 00000000000..710267ded37 --- /dev/null +++ b/winsup/cygwin/fhandler_mem.cc @@ -0,0 +1,439 @@ +/* fhandler_mem.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. */ + +#include "winsup.h" +#include <errno.h> +#include <unistd.h> +#include <sys/mman.h> +#include <ntdef.h> + +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "ntdll.h" + +/**********************************************************************/ +/* fhandler_dev_mem */ + +fhandler_dev_mem::fhandler_dev_mem () + : fhandler_base () +{ + /* Reading physical memory only supported on NT/W2K. */ + if (!wincap.has_physical_mem_access ()) + { + mem_size = 0; + return; + } + + if (dev == FH_MEM) /* /dev/mem */ + { + NTSTATUS ret; + SYSTEM_BASIC_INFORMATION sbi; + if ((ret = NtQuerySystemInformation (SystemBasicInformation, (PVOID) &sbi, + sizeof sbi, NULL)) != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + debug_printf("NtQuerySystemInformation: ret = %d, Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + mem_size = 0; + } + else + mem_size = sbi.PhysicalPageSize * sbi.NumberOfPhysicalPages; + debug_printf ("MemSize: %d MB", mem_size >> 20); + } + else if (dev == FH_KMEM) /* /dev/kmem - Not yet supported */ + { + mem_size = 0; + debug_printf ("KMemSize: %d MB", mem_size >> 20); + } + else if (dev == FH_ZERO) /* /dev/port == First 64K of /dev/mem */ + { + mem_size = 65536; + debug_printf ("PortSize: 64 KB"); + } + else + { + mem_size = 0; + debug_printf ("Illegal minor number!!!"); + } +} + +fhandler_dev_mem::~fhandler_dev_mem (void) +{ +} + +int +fhandler_dev_mem::open (path_conv *, int flags, mode_t) +{ + if (!wincap.has_physical_mem_access ()) + { + set_errno (ENOENT); + debug_printf ("%s is accessible under NT/W2K only", dev.name); + return 0; + } + + /* Check for illegal flags. */ + if (flags & (O_APPEND | O_TRUNC | O_EXCL)) + { + set_errno (EINVAL); + return 0; + } + + UNICODE_STRING memstr; + RtlInitUnicodeString (&memstr, L"\\device\\physicalmemory"); + + OBJECT_ATTRIBUTES attr; + InitializeObjectAttributes (&attr, &memstr, + OBJ_CASE_INSENSITIVE | OBJ_INHERIT, + NULL, NULL); + + ACCESS_MASK section_access; + if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY) + { + set_access (GENERIC_READ); + section_access = SECTION_MAP_READ; + } + else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY) + { + set_access (GENERIC_WRITE); + section_access = SECTION_MAP_READ | SECTION_MAP_WRITE; + } + else + { + set_access (GENERIC_READ | GENERIC_WRITE); + section_access = SECTION_MAP_READ | SECTION_MAP_WRITE; + } + + HANDLE mem; + NTSTATUS ret = NtOpenSection (&mem, section_access, &attr); + if (!NT_SUCCESS (ret)) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + set_io_handle (NULL); + return 0; + } + + set_io_handle (mem); + set_open_status (); + return 1; +} + +int +fhandler_dev_mem::write (const void *ptr, size_t ulen) +{ + if (!ulen || pos >= mem_size) + return 0; + + if (!(get_access () & GENERIC_WRITE)) + { + set_errno (EINVAL); + return -1; + } + + if (pos + ulen > mem_size) + ulen = mem_size - pos; + + PHYSICAL_ADDRESS phys; + NTSTATUS ret; + void *viewmem = NULL; + DWORD len = ulen + getpagesize () - 1; + + phys.QuadPart = (ULONGLONG) pos; + if ((ret = NtMapViewOfSection (get_handle (), + INVALID_HANDLE_VALUE, + &viewmem, + 0L, + len, + &phys, + &len, + ViewShare, + 0, + PAGE_READONLY)) != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + return -1; + } + + memcpy ((char *) viewmem + (pos - phys.QuadPart), ptr, ulen); + + if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, viewmem))) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + return -1; + } + + pos += ulen; + return ulen; +} + +void __stdcall +fhandler_dev_mem::read (void *ptr, size_t& ulen) +{ + if (!ulen || pos >= mem_size) + { + ulen = 0; + return; + } + + if (!(get_access () & GENERIC_READ)) + { + set_errno (EINVAL); + (ssize_t) ulen = -1; + return; + } + + if (pos + ulen > mem_size) + ulen = mem_size - pos; + + PHYSICAL_ADDRESS phys; + NTSTATUS ret; + void *viewmem = NULL; + DWORD len = ulen + getpagesize () - 1; + + phys.QuadPart = (ULONGLONG) pos; + if ((ret = NtMapViewOfSection (get_handle (), + INVALID_HANDLE_VALUE, + &viewmem, + 0L, + len, + &phys, + &len, + ViewShare, + 0, + PAGE_READONLY)) != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + (ssize_t) ulen = -1; + return; + } + + memcpy (ptr, (char *) viewmem + (pos - phys.QuadPart), ulen); + + if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, viewmem))) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + (ssize_t) ulen = -1; + return; + } + + pos += ulen; + return; +} + +int +fhandler_dev_mem::close (void) +{ + return fhandler_base::close (); +} + +__off64_t +fhandler_dev_mem::lseek (__off64_t offset, int whence) +{ + switch (whence) + { + case SEEK_SET: + pos = offset; + break; + + case SEEK_CUR: + pos += offset; + break; + + case SEEK_END: + pos = mem_size; + pos += offset; + break; + + default: + set_errno (EINVAL); + return ILLEGAL_SEEK; + } + + if (pos > mem_size) + { + set_errno (EINVAL); + return ILLEGAL_SEEK; + } + + return pos; +} + +HANDLE +fhandler_dev_mem::mmap (caddr_t *addr, size_t len, DWORD access, + int flags, __off64_t off) +{ + if (off >= mem_size + || (DWORD) len >= mem_size + || off + len >= mem_size) + { + set_errno (EINVAL); + syscall_printf ("-1 = mmap(): illegal parameter, set EINVAL"); + return INVALID_HANDLE_VALUE; + } + + UNICODE_STRING memstr; + RtlInitUnicodeString (&memstr, L"\\device\\physicalmemory"); + + OBJECT_ATTRIBUTES attr; + InitializeObjectAttributes (&attr, &memstr, + OBJ_CASE_INSENSITIVE | OBJ_INHERIT, + NULL, NULL); + + ACCESS_MASK section_access; + ULONG protect; + + if (access & FILE_MAP_COPY) + { + section_access = SECTION_MAP_READ | SECTION_MAP_WRITE; + protect = PAGE_WRITECOPY; + } + else if (access & FILE_MAP_WRITE) + { + section_access = SECTION_MAP_READ | SECTION_MAP_WRITE; + protect = PAGE_READWRITE; + } + else + { + section_access = SECTION_MAP_READ; + protect = PAGE_READONLY; + } + + HANDLE h; + NTSTATUS ret = NtOpenSection (&h, section_access, &attr); + if (!NT_SUCCESS (ret)) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + syscall_printf ("-1 = mmap(): NtOpenSection failed with %E"); + return INVALID_HANDLE_VALUE; + } + + PHYSICAL_ADDRESS phys; + void *base = *addr; + DWORD dlen = len; + + phys.QuadPart = (ULONGLONG) off; + + if ((ret = NtMapViewOfSection (h, + INVALID_HANDLE_VALUE, + &base, + 0L, + dlen, + &phys, + &dlen, + ViewShare /*??*/, + 0, + protect)) != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + syscall_printf ("-1 = mmap(): NtMapViewOfSection failed with %E"); + return INVALID_HANDLE_VALUE; + } + if ((flags & MAP_FIXED) && base != *addr) + { + set_errno (EINVAL); + syscall_printf ("-1 = mmap(): address shift with MAP_FIXED given"); + NtUnmapViewOfSection (INVALID_HANDLE_VALUE, base); + return INVALID_HANDLE_VALUE; + } + + *addr = (caddr_t) base; + return h; +} + +int +fhandler_dev_mem::munmap (HANDLE h, caddr_t addr, size_t len) +{ + NTSTATUS ret; + if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, addr))) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + return -1; + } + CloseHandle (h); + return 0; +} + +int +fhandler_dev_mem::msync (HANDLE h, caddr_t addr, size_t len, int flags) +{ + return 0; +} + +BOOL +fhandler_dev_mem::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address) +{ + DWORD ret; + PHYSICAL_ADDRESS phys; + void *base = address; + DWORD dlen = size; + ULONG protect; + + if (access & FILE_MAP_COPY) + protect = PAGE_WRITECOPY; + else if (access & FILE_MAP_WRITE) + protect = PAGE_READWRITE; + else + protect = PAGE_READONLY; + + phys.QuadPart = (ULONGLONG) offset; + + if ((ret = NtMapViewOfSection (h, + INVALID_HANDLE_VALUE, + &base, + 0L, + dlen, + &phys, + &dlen, + ViewShare /*??*/, + 0, + protect)) != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + syscall_printf ("-1 = fixup_mmap_after_fork(): NtMapViewOfSection failed with %E"); + return FALSE; + } + return base == address; +} + +int +fhandler_dev_mem::fstat (struct __stat64 *buf, path_conv *pc) +{ + this->fhandler_base::fstat (buf, pc); + buf->st_mode = S_IFCHR; + if (wincap.has_physical_mem_access ()) + buf->st_mode |= S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH; + buf->st_blksize = getpagesize (); + + return 0; +} + +int +fhandler_dev_mem::dup (fhandler_base *child) +{ + int ret = fhandler_base::dup (child); + + if (! ret) + { + fhandler_dev_mem *fhc = (fhandler_dev_mem *) child; + + fhc->mem_size = mem_size; + fhc->pos = pos; + } + return ret; +} + +void +fhandler_dev_mem::dump () +{ + paranoid_printf ("here, fhandler_dev_mem"); +} diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc new file mode 100644 index 00000000000..ce7c107a5e6 --- /dev/null +++ b/winsup/cygwin/fhandler_proc.cc @@ -0,0 +1,500 @@ +/* fhandler_proc.cc: fhandler for /proc virtual filesystem + + Copyright 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 <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <ntdef.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "pinfo.h" +#include "dtable.h" +#include "cygheap.h" +#include <assert.h> +#include <sys/utsname.h> +#include <sys/param.h> +#include "ntdll.h" + +#define _COMPILING_NEWLIB +#include <dirent.h> + +/* offsets in proc_listing */ +static const int PROC_LOADAVG = 2; // /proc/loadavg +static const int PROC_MEMINFO = 3; // /proc/meminfo +static const int PROC_REGISTRY = 4; // /proc/registry +static const int PROC_STAT = 5; // /proc/stat +static const int PROC_VERSION = 6; // /proc/version +static const int PROC_UPTIME = 7; // /proc/uptime + +/* names of objects in /proc */ +static const char *proc_listing[] = { + ".", + "..", + "loadavg", + "meminfo", + "registry", + "stat", + "version", + "uptime", + NULL +}; + +static const int PROC_LINK_COUNT = (sizeof (proc_listing) / sizeof (const char *)) - 1; + +/* FH_PROC in the table below means the file/directory is handles by + * fhandler_proc. + */ +static const DWORD proc_fhandlers[PROC_LINK_COUNT] = { + FH_PROC, + FH_PROC, + FH_PROC, + FH_PROC, + FH_REGISTRY, + FH_PROC, + FH_PROC, + FH_PROC +}; + +/* name of the /proc filesystem */ +const char proc[] = "/proc"; +const int proc_len = sizeof (proc) - 1; + +static off_t format_proc_meminfo (char *destbuf, size_t maxsize); +static off_t format_proc_stat (char *destbuf, size_t maxsize); +static off_t format_proc_uptime (char *destbuf, size_t maxsize); + +/* auxillary function that returns the fhandler associated with the given path + * this is where it would be nice to have pattern matching in C - polymorphism + * just doesn't cut it + */ +DWORD +fhandler_proc::get_proc_fhandler (const char *path) +{ + debug_printf ("get_proc_fhandler(%s)", path); + path += proc_len; + /* Since this method is called from path_conv::check we can't rely on + * it being normalised and therefore the path may have runs of slashes + * in it. + */ + while (isdirsep (*path)) + path++; + + /* Check if this is the root of the virtual filesystem (i.e. /proc). */ + if (*path == 0) + return FH_PROC; + + for (int i = 0; proc_listing[i]; i++) + { + if (path_prefix_p (proc_listing[i], path, strlen (proc_listing[i]))) + return proc_fhandlers[i]; + } + + if (pinfo (atoi (path))) + return FH_PROCESS; + + bool has_subdir = false; + while (*path) + if (isdirsep (*path++)) + { + has_subdir = true; + break; + } + + if (has_subdir) + /* The user is trying to access a non-existent subdirectory of /proc. */ + return FH_BAD; + else + /* Return FH_PROC so that we can return EROFS if the user is trying to create + a file. */ + return FH_PROC; +} + +/* Returns 0 if path doesn't exist, >0 if path is a directory, + * <0 if path is a file. + */ +int +fhandler_proc::exists () +{ + const char *path = get_name (); + debug_printf ("exists (%s)", path); + path += proc_len; + if (*path == 0) + return 2; + for (int i = 0; proc_listing[i]; i++) + if (pathmatch (path + 1, proc_listing[i])) + return (proc_fhandlers[i] == FH_PROC) ? -1 : 1; + return 0; +} + +fhandler_proc::fhandler_proc (): + fhandler_virtual () +{ +} + +int +fhandler_proc::fstat (struct __stat64 *buf, path_conv *pc) +{ + const char *path = get_name (); + debug_printf ("fstat (%s)", path); + + path += proc_len; + (void) fhandler_base::fstat (buf, pc); + + buf->st_mode &= ~_IFMT & NO_W; + + if (!*path) + { + buf->st_nlink = PROC_LINK_COUNT; + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + return 0; + } + else + { + path++; + for (int i = 0; proc_listing[i]; i++) + if (pathmatch (path, proc_listing[i])) + { + if (proc_fhandlers[i] != FH_PROC) + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + else + { + buf->st_mode &= NO_X; + buf->st_mode |= S_IFREG; + } + return 0; + } + } + set_errno (ENOENT); + return -1; +} + +struct dirent * +fhandler_proc::readdir (DIR * dir) +{ + if (dir->__d_position >= PROC_LINK_COUNT) + { + winpids pids; + int found = 0; + for (unsigned i = 0; i < pids.npids; i++) + if (found++ == dir->__d_position - PROC_LINK_COUNT) + { + __small_sprintf (dir->__d_dirent->d_name, "%d", pids[i]->pid); + dir->__d_position++; + return dir->__d_dirent; + } + set_errno (ENMFILE); + return NULL; + } + + strcpy (dir->__d_dirent->d_name, proc_listing[dir->__d_position++]); + syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir, + dir->__d_dirent->d_name); + return dir->__d_dirent; +} + +int +fhandler_proc::open (path_conv *pc, int flags, mode_t mode) +{ + int proc_file_no = -1; + + int res = fhandler_virtual::open (pc, flags, mode); + if (!res) + goto out; + + set_nohandle (true); + + const char *path; + + path = get_name () + proc_len; + + if (!*path) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + flags |= O_DIROPEN; + goto success; + } + } + + proc_file_no = -1; + for (int i = 0; proc_listing[i]; i++) + if (path_prefix_p (proc_listing[i], path + 1, strlen (proc_listing[i]))) + { + proc_file_no = i; + if (proc_fhandlers[i] != FH_PROC) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + flags |= O_DIROPEN; + goto success; + } + } + } + + if (proc_file_no == -1) + { + if (flags & O_CREAT) + { + set_errno (EROFS); + res = 0; + goto out; + } + else + { + set_errno (ENOENT); + res = 0; + goto out; + } + } + if (flags & O_WRONLY) + { + set_errno (EROFS); + res = 0; + goto out; + } + + fileid = proc_file_no; + if (!fill_filebuf ()) + { + res = 0; + goto out; + } + + if (flags & O_APPEND) + position = filesize; + else + position = 0; + +success: + res = 1; + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); +out: + syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode); + return res; +} + +bool +fhandler_proc::fill_filebuf () +{ + switch (fileid) + { + case PROC_VERSION: + { + if (!filebuf) + { + struct utsname uts_name; + uname (&uts_name); + bufalloc = strlen (uts_name.sysname) + 1 + strlen (uts_name.release) + + 1 + strlen (uts_name.version) + 2; + filebuf = (char *) realloc (filebuf, bufalloc); + filesize = __small_sprintf (filebuf, "%s %s %s\n", uts_name.sysname, + uts_name.release, uts_name.version); + } + break; + } + case PROC_UPTIME: + { + filebuf = (char *) realloc (filebuf, bufalloc = 80); + filesize = format_proc_uptime (filebuf, bufalloc); + break; + } + case PROC_STAT: + { + filebuf = (char *) realloc (filebuf, bufalloc = 2048); + filesize = format_proc_stat (filebuf, bufalloc); + break; + } + case PROC_LOADAVG: + { + /* + * not really supported - Windows doesn't keep track of these values + * Windows 95/98/me does have the KERNEL/CPUUsage performance counter + * which is similar. + */ + filebuf = (char *) realloc (filebuf, bufalloc = 16); + filesize = __small_sprintf (filebuf, "%u.%02u %u.%02u %u.%02u\n", + 0, 0, 0, 0, 0, 0); + break; + } + case PROC_MEMINFO: + { + filebuf = (char *) realloc (filebuf, bufalloc = 2048); + filesize = format_proc_meminfo (filebuf, bufalloc); + break; + } + } + return true; +} + +static +off_t +format_proc_meminfo (char *destbuf, size_t maxsize) +{ + unsigned long mem_total = 0UL, mem_free = 0UL, swap_total = 0UL, + swap_free = 0UL; + MEMORYSTATUS memory_status; + GlobalMemoryStatus (&memory_status); + mem_total = memory_status.dwTotalPhys; + mem_free = memory_status.dwAvailPhys; + swap_total = memory_status.dwTotalPageFile; + swap_free = memory_status.dwAvailPageFile; + return __small_sprintf (destbuf, " total: used: free:\n" + "Mem: %10lu %10lu %10lu\n" + "Swap: %10lu %10lu %10lu\n" + "MemTotal: %10lu kB\n" + "MemFree: %10lu kB\n" + "MemShared: 0 kB\n" + "HighTotal: 0 kB\n" + "HighFree: 0 kB\n" + "LowTotal: %10lu kB\n" + "LowFree: %10lu kB\n" + "SwapTotal: %10lu kB\n" + "SwapFree: %10lu kB\n", + mem_total, mem_total - mem_free, mem_free, + swap_total, swap_total - swap_free, swap_free, + mem_total >> 10, mem_free >> 10, + mem_total >> 10, mem_free >> 10, + swap_total >> 10, swap_free >> 10); +} + +static +off_t +format_proc_uptime (char *destbuf, size_t maxsize) +{ + unsigned long long uptime = 0ULL, idle_time = 0ULL; + SYSTEM_PROCESSOR_TIMES spt; + + NTSTATUS ret = NtQuerySystemInformation (SystemProcessorTimes, (PVOID) &spt, + sizeof spt, NULL); + if (!ret && GetLastError () == ERROR_PROC_NOT_FOUND) + uptime = GetTickCount () / 10; + else if (ret != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + debug_printf("NtQuerySystemInformation: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + return 0; + } + else + { + idle_time = spt.IdleTime.QuadPart / 100000ULL; + uptime = (spt.KernelTime.QuadPart + + spt.UserTime.QuadPart) / 100000ULL; + } + + return __small_sprintf (destbuf, "%U.%02u %U.%02u\n", + uptime / 100, long (uptime % 100), + idle_time / 100, long (idle_time % 100)); +} + +static +off_t +format_proc_stat (char *destbuf, size_t maxsize) +{ + unsigned long long user_time = 0ULL, kernel_time = 0ULL, idle_time = 0ULL; + unsigned long pages_in = 0UL, pages_out = 0UL, interrupt_count = 0UL, + context_switches = 0UL, swap_in = 0UL, swap_out = 0UL; + time_t boot_time = 0; + + if (wincap.is_winnt ()) + { + NTSTATUS ret; + SYSTEM_PROCESSOR_TIMES spt; + SYSTEM_PERFORMANCE_INFORMATION spi; + SYSTEM_TIME_OF_DAY_INFORMATION stodi; + ret = NtQuerySystemInformation (SystemProcessorTimes, + (PVOID) &spt, + sizeof spt, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQuerySystemInformation (SystemPerformanceInformation, + (PVOID) &spi, + sizeof spi, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQuerySystemInformation (SystemTimeOfDayInformation, + (PVOID) &stodi, + sizeof stodi, NULL); + if (ret != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + debug_printf("NtQuerySystemInformation: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + return 0; + } + kernel_time = (spt.KernelTime.QuadPart - spt.IdleTime.QuadPart) * HZ / 10000000ULL; + user_time = spt.UserTime.QuadPart * HZ / 10000000ULL; + idle_time = spt.IdleTime.QuadPart * HZ / 10000000ULL; + interrupt_count = spt.InterruptCount; + pages_in = spi.PagesRead; + pages_out = spi.PagefilePagesWritten + spi.MappedFilePagesWritten; + /* + * Note: there is no distinction made in this structure between pages + * read from the page file and pages read from mapped files, but there + * is such a distinction made when it comes to writing. Goodness knows + * why. The value of swap_in, then, will obviously be wrong but its our + * best guess. + */ + swap_in = spi.PagesRead; + swap_out = spi.PagefilePagesWritten; + context_switches = spi.ContextSwitches; + boot_time = to_time_t ((FILETIME *) &stodi.BootTime.QuadPart); + } + /* + * else + * { + * There are only two relevant performance counters on Windows 95/98/me, + * VMM/cPageIns and VMM/cPageOuts. The extra effort needed to read these + * counters is by no means worth it. + * } + */ + return __small_sprintf (destbuf, "cpu %U %U %U %U\n" + "page %u %u\n" + "swap %u %u\n" + "intr %u\n" + "ctxt %u\n" + "btime %u\n", + user_time, 0ULL, + kernel_time, idle_time, + pages_in, pages_out, + swap_in, swap_out, + interrupt_count, + context_switches, + boot_time); +} diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc new file mode 100644 index 00000000000..054c9e373cd --- /dev/null +++ b/winsup/cygwin/fhandler_process.cc @@ -0,0 +1,751 @@ +/* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem + + Copyright 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 <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <ntdef.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "pinfo.h" +#include "path.h" +#include "shared_info.h" +#include "dtable.h" +#include "cygheap.h" +#include "ntdll.h" +#include <sys/param.h> +#include <assert.h> +#include <sys/sysmacros.h> + +#define _COMPILING_NEWLIB +#include <dirent.h> + +static const int PROCESS_PPID = 2; +static const int PROCESS_EXENAME = 3; +static const int PROCESS_WINPID = 4; +static const int PROCESS_WINEXENAME = 5; +static const int PROCESS_STATUS = 6; +static const int PROCESS_UID = 7; +static const int PROCESS_GID = 8; +static const int PROCESS_PGID = 9; +static const int PROCESS_SID = 10; +static const int PROCESS_CTTY = 11; +static const int PROCESS_STAT = 12; +static const int PROCESS_STATM = 13; +static const int PROCESS_CMDLINE = 14; + +static const char * const process_listing[] = +{ + ".", + "..", + "ppid", + "exename", + "winpid", + "winexename", + "status", + "uid", + "gid", + "pgid", + "sid", + "ctty", + "stat", + "statm", + "cmdline", + NULL +}; + +static const int PROCESS_LINK_COUNT = + (sizeof (process_listing) / sizeof (const char *)) - 1; + +static off_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize); +static off_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize); +static off_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize); +static int get_process_state (DWORD dwProcessId); +static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize, + unsigned long *vmrss, unsigned long *vmtext, + unsigned long *vmdata, unsigned long *vmlib, + unsigned long *vmshare); + +/* Returns 0 if path doesn't exist, >0 if path is a directory, + * <0 if path is a file. + */ +int +fhandler_process::exists () +{ + const char *path = get_name (); + debug_printf ("exists (%s)", path); + path += proc_len + 1; + while (*path != 0 && !isdirsep (*path)) + path++; + if (*path == 0) + return 2; + + for (int i = 0; process_listing[i]; i++) + if (pathmatch (path + 1, process_listing[i])) + return -1; + return 0; +} + +fhandler_process::fhandler_process (): + fhandler_proc () +{ +} + +int +fhandler_process::fstat (struct __stat64 *buf, path_conv *pc) +{ + const char *path = get_name (); + int file_type = exists (); + (void) fhandler_base::fstat (buf, pc); + path += proc_len + 1; + pid = atoi (path); + pinfo p (pid); + if (!p) + { + set_errno (ENOENT); + return -1; + } + + buf->st_mode &= ~_IFMT & NO_W; + + switch (file_type) + { + case 0: + set_errno (ENOENT); + return -1; + case 1: + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + return 0; + case 2: + buf->st_ctime = buf->st_mtime = p->start_time; + buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec = 0; + time_as_timestruc_t (&buf->st_atim); + buf->st_uid = p->uid; + buf->st_gid = p->gid; + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + buf->st_nlink = PROCESS_LINK_COUNT; + return 0; + default: + case -1: + buf->st_uid = p->uid; + buf->st_gid = p->gid; + buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + return 0; + } +} + +struct dirent * +fhandler_process::readdir (DIR * dir) +{ + if (dir->__d_position >= PROCESS_LINK_COUNT) + { + set_errno (ENMFILE); + return NULL; + } + strcpy (dir->__d_dirent->d_name, process_listing[dir->__d_position++]); + syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir, + dir->__d_dirent->d_name); + return dir->__d_dirent; +} + +int +fhandler_process::open (path_conv *pc, int flags, mode_t mode) +{ + int process_file_no = -1; + + int res = fhandler_virtual::open (pc, flags, mode); + if (!res) + goto out; + + set_nohandle (true); + + const char *path; + path = get_name () + proc_len + 1; + pid = atoi (path); + while (*path != 0 && !isdirsep (*path)) + path++; + + if (*path == 0) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + flags |= O_DIROPEN; + goto success; + } + } + + process_file_no = -1; + for (int i = 0; process_listing[i]; i++) + { + if (path_prefix_p + (process_listing[i], path + 1, strlen (process_listing[i]))) + process_file_no = i; + } + if (process_file_no == -1) + { + if (flags & O_CREAT) + { + set_errno (EROFS); + res = 0; + goto out; + } + else + { + set_errno (ENOENT); + res = 0; + goto out; + } + } + if (flags & O_WRONLY) + { + set_errno (EROFS); + res = 0; + goto out; + } + + fileid = process_file_no; + if (!fill_filebuf ()) + { + res = 0; + goto out; + } + + if (flags & O_APPEND) + position = filesize; + else + position = 0; + +success: + res = 1; + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); +out: + syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode); + return res; +} + +bool +fhandler_process::fill_filebuf () +{ + pinfo p (pid); + + if (!p) + { + set_errno (ENOENT); + return false; + } + + switch (fileid) + { + case PROCESS_UID: + case PROCESS_GID: + case PROCESS_PGID: + case PROCESS_SID: + case PROCESS_CTTY: + case PROCESS_PPID: + { + filebuf = (char *) realloc (filebuf, bufalloc = 40); + int num; + switch (fileid) + { + case PROCESS_PPID: + num = p->ppid; + break; + case PROCESS_UID: + num = p->uid; + break; + case PROCESS_PGID: + num = p->pgid; + break; + case PROCESS_SID: + num = p->sid; + break; + case PROCESS_GID: + num = p->gid; + break; + case PROCESS_CTTY: + num = p->ctty; + break; + default: // what's this here for? + num = 0; + break; + } + __small_sprintf (filebuf, "%d\n", num); + filesize = strlen (filebuf); + break; + } + case PROCESS_CMDLINE: + { + if (filebuf) + free (filebuf); + filebuf = p->cmdline (filesize); + if (!filebuf || !*filebuf) + filebuf = strdup ("<defunct>"); + break; + } + case PROCESS_EXENAME: + { + filebuf = (char *) realloc (filebuf, bufalloc = MAX_PATH); + if (p->process_state & (PID_ZOMBIE | PID_EXITED)) + strcpy (filebuf, "<defunct>"); + else + { + mount_table->conv_to_posix_path (p->progname, filebuf, 1); + int len = strlen (filebuf); + if (len > 4) + { + char *s = filebuf + len - 4; + if (strcasecmp (s, ".exe") == 0) + *s = 0; + } + } + filesize = strlen (filebuf); + break; + } + case PROCESS_WINPID: + { + filebuf = (char *) realloc (filebuf, bufalloc = 40); + __small_sprintf (filebuf, "%d\n", p->dwProcessId); + filesize = strlen (filebuf); + break; + } + case PROCESS_WINEXENAME: + { + int len = strlen (p->progname); + filebuf = (char *) realloc (filebuf, bufalloc = (len + 2)); + strcpy (filebuf, p->progname); + filebuf[len] = '\n'; + filesize = len + 1; + break; + } + case PROCESS_STATUS: + { + filebuf = (char *) realloc (filebuf, bufalloc = 2048); + filesize = format_process_status (*p, filebuf, bufalloc); + break; + } + case PROCESS_STAT: + { + filebuf = (char *) realloc (filebuf, bufalloc = 2048); + filesize = format_process_stat (*p, filebuf, bufalloc); + break; + } + case PROCESS_STATM: + { + filebuf = (char *) realloc (filebuf, bufalloc = 2048); + filesize = format_process_statm (*p, filebuf, bufalloc); + break; + } + } + + return true; +} + +static +off_t +format_process_stat (_pinfo *p, char *destbuf, size_t maxsize) +{ + char cmd[MAX_PATH]; + int state = 'R'; + unsigned long fault_count = 0UL, + utime = 0UL, stime = 0UL, + start_time = 0UL, + vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL; + int priority = 0; + if (p->process_state & (PID_ZOMBIE | PID_EXITED)) + strcpy (cmd, "<defunct>"); + else + { + strcpy (cmd, p->progname); + char *last_slash = strrchr (cmd, '\\'); + if (last_slash != NULL) + strcpy (cmd, last_slash + 1); + int len = strlen (cmd); + if (len > 4) + { + char *s = cmd + len - 4; + if (strcasecmp (s, ".exe") == 0) + *s = 0; + } + } + /* + * Note: under Windows, a _process_ is always running - it's only _threads_ + * that get suspended. Therefore the default state is R (runnable). + */ + if (p->process_state & PID_ZOMBIE) + state = 'Z'; + else if (p->process_state & PID_STOPPED) + state = 'T'; + else if (wincap.is_winnt ()) + state = get_process_state (p->dwProcessId); + if (wincap.is_winnt ()) + { + NTSTATUS ret; + HANDLE hProcess; + VM_COUNTERS vmc; + KERNEL_USER_TIMES put; + PROCESS_BASIC_INFORMATION pbi; + QUOTA_LIMITS ql; + SYSTEM_TIME_OF_DAY_INFORMATION stodi; + SYSTEM_PROCESSOR_TIMES spt; + hProcess = OpenProcess (PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, + FALSE, p->dwProcessId); + if (hProcess != NULL) + { + ret = NtQueryInformationProcess (hProcess, + ProcessVmCounters, + (PVOID) &vmc, + sizeof vmc, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQueryInformationProcess (hProcess, + ProcessTimes, + (PVOID) &put, + sizeof put, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQueryInformationProcess (hProcess, + ProcessBasicInformation, + (PVOID) &pbi, + sizeof pbi, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQueryInformationProcess (hProcess, + ProcessQuotaLimits, + (PVOID) &ql, + sizeof ql, NULL); + CloseHandle (hProcess); + } + else + { + DWORD error = GetLastError (); + __seterrno_from_win_error (error); + debug_printf ("OpenProcess: ret = %d", + error); + return 0; + } + if (ret == STATUS_SUCCESS) + ret = NtQuerySystemInformation (SystemTimeOfDayInformation, + (PVOID) &stodi, + sizeof stodi, NULL); + if (ret == STATUS_SUCCESS) + ret = NtQuerySystemInformation (SystemProcessorTimes, + (PVOID) &spt, + sizeof spt, NULL); + if (ret != STATUS_SUCCESS) + { + __seterrno_from_win_error (RtlNtStatusToDosError (ret)); + debug_printf ("NtQueryInformationProcess: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + return 0; + } + fault_count = vmc.PageFaultCount; + utime = put.UserTime.QuadPart * HZ / 10000000ULL; + stime = put.KernelTime.QuadPart * HZ / 10000000ULL; + if (stodi.CurrentTime.QuadPart > put.CreateTime.QuadPart) + start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart - + stodi.CurrentTime.QuadPart + put.CreateTime.QuadPart) * HZ / 10000000ULL; + else + /* + * sometimes stodi.CurrentTime is a bit behind + * Note: some older versions of procps are broken and can't cope + * with process start times > time(NULL). + */ + start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart) * HZ / 10000000ULL; + priority = pbi.BasePriority; + unsigned page_size = getpagesize (); + vmsize = vmc.VirtualSize; + vmrss = vmc.WorkingSetSize / page_size; + vmmaxrss = ql.MaximumWorkingSetSize / page_size; + } + else + { + start_time = (GetTickCount () / 1000 - time (NULL) + p->start_time) * HZ; + } + return __small_sprintf (destbuf, "%d (%s) %c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %ld %ld " + "%lu %lu " + "%ld " + "%lu", + p->pid, cmd, + state, + p->ppid, p->pgid, p->sid, makedev (FH_TTYS, p->ctty), + -1, 0, fault_count, fault_count, 0, 0, utime, stime, + utime, stime, priority, 0, 0, 0, + start_time, vmsize, + vmrss, vmmaxrss + ); +} + +static +off_t +format_process_status (_pinfo *p, char *destbuf, size_t maxsize) +{ + char cmd[MAX_PATH]; + int state = 'R'; + const char *state_str = "unknown"; + unsigned long vmsize = 0UL, vmrss = 0UL, vmdata = 0UL, vmlib = 0UL, vmtext = 0UL, + vmshare = 0UL; + if (p->process_state & (PID_ZOMBIE | PID_EXITED)) + strcpy (cmd, "<defunct>"); + else + { + strcpy (cmd, p->progname); + char *last_slash = strrchr (cmd, '\\'); + if (last_slash != NULL) + strcpy (cmd, last_slash + 1); + int len = strlen (cmd); + if (len > 4) + { + char *s = cmd + len - 4; + if (strcasecmp (s, ".exe") == 0) + *s = 0; + } + } + /* + * Note: under Windows, a _process_ is always running - it's only _threads_ + * that get suspended. Therefore the default state is R (runnable). + */ + if (p->process_state & PID_ZOMBIE) + state = 'Z'; + else if (p->process_state & PID_STOPPED) + state = 'T'; + else if (wincap.is_winnt ()) + state = get_process_state (p->dwProcessId); + switch (state) + { + case 'O': + state_str = "running"; + break; + case 'D': + case 'S': + state_str = "sleeping"; + break; + case 'R': + state_str = "runnable"; + break; + case 'Z': + state_str = "zombie"; + break; + case 'T': + state_str = "stopped"; + break; + } + if (wincap.is_winnt ()) + { + if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata, &vmlib, &vmshare)) + return 0; + unsigned page_size = getpagesize (); + vmsize *= page_size; vmrss *= page_size; vmdata *= page_size; + vmtext *= page_size; vmlib *= page_size; + } + // The real uid value for *this* process is stored at cygheap->user.real_uid + // but we can't get at the real uid value for any other process, so + // just fake it as p->uid. Similar for p->gid. + return __small_sprintf (destbuf, "Name: %s\n" + "State: %c (%s)\n" + "Tgid: %d\n" + "Pid: %d\n" + "PPid: %d\n" + "Uid: %d %d %d %d\n" + "Gid: %d %d %d %d\n" + "VmSize: %8d kB\n" + "VmLck: %8d kB\n" + "VmRSS: %8d kB\n" + "VmData: %8d kB\n" + "VmStk: %8d kB\n" + "VmExe: %8d kB\n" + "VmLib: %8d kB\n" + "SigPnd: %016x\n" + "SigBlk: %016x\n" + "SigIgn: %016x\n", + cmd, + state, state_str, + p->pgid, + p->pid, + p->ppid, + p->uid, p->uid, p->uid, p->uid, + p->gid, p->gid, p->gid, p->gid, + vmsize >> 10, 0, vmrss >> 10, vmdata >> 10, 0, vmtext >> 10, vmlib >> 10, + 0, 0, p->getsigmask () + ); +} + +static +off_t +format_process_statm (_pinfo *p, char *destbuf, size_t maxsize) +{ + unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL, + vmlib = 0UL, vmshare = 0UL; + if (wincap.is_winnt ()) + { + if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata, + &vmlib, &vmshare)) + return 0; + } + return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld", + vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0 + ); +} + +static +int +get_process_state (DWORD dwProcessId) +{ + /* + * This isn't really heavy magic - just go through the processes' + * threads one by one and return a value accordingly + * Errors are silently ignored. + */ + NTSTATUS ret; + SYSTEM_PROCESSES *sp; + ULONG n = 0x1000; + PULONG p = new ULONG[n]; + int state =' '; + while (STATUS_INFO_LENGTH_MISMATCH == + (ret = NtQuerySystemInformation (SystemProcessesAndThreadsInformation, + (PVOID) p, + n * sizeof *p, NULL))) + delete [] p, p = new ULONG[n *= 2]; + if (ret != STATUS_SUCCESS) + { + debug_printf ("NtQuerySystemInformation: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + goto out; + } + state = 'Z'; + sp = (SYSTEM_PROCESSES *) p; + for (;;) + { + if (sp->ProcessId == dwProcessId) + { + SYSTEM_THREADS *st; + if (wincap.has_process_io_counters ()) + /* + * Windows 2000 and XP have an extra member in SYSTEM_PROCESSES + * which means the offset of the first SYSTEM_THREADS entry is + * different on these operating systems compared to NT 4. + */ + st = &sp->Threads[0]; + else + /* + * 136 is the offset of the first SYSTEM_THREADS entry on + * Windows NT 4. + */ + st = (SYSTEM_THREADS *) ((char *) sp + 136); + state = 'S'; + for (unsigned i = 0; i < sp->ThreadCount; i++) + { + if (st->State == StateRunning || + st->State == StateReady) + { + state = 'R'; + goto out; + } + st++; + } + break; + } + if (!sp->NextEntryDelta) + break; + sp = (SYSTEM_PROCESSES *) ((char *) sp + sp->NextEntryDelta); + } +out: + delete [] p; + return state; +} + +static +bool +get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss, + unsigned long *vmtext, unsigned long *vmdata, + unsigned long *vmlib, unsigned long *vmshare) +{ + bool res = true; + NTSTATUS ret; + HANDLE hProcess; + VM_COUNTERS vmc; + MEMORY_WORKING_SET_LIST *mwsl; + ULONG n = 0x1000, length; + PULONG p = new ULONG[n]; + unsigned page_size = getpagesize (); + hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, + FALSE, dwProcessId); + if (hProcess == NULL) + { + DWORD error = GetLastError (); + __seterrno_from_win_error (error); + debug_printf ("OpenProcess: ret = %d", + error); + return false; + } + while ((ret = NtQueryVirtualMemory (hProcess, 0, + MemoryWorkingSetList, + (PVOID) p, + n * sizeof *p, &length)), + (ret == STATUS_SUCCESS || ret == STATUS_INFO_LENGTH_MISMATCH) && + length >= n * sizeof *p) + delete [] p, p = new ULONG[n *= 2]; + if (ret != STATUS_SUCCESS) + { + debug_printf ("NtQueryVirtualMemory: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + res = false; + goto out; + } + mwsl = (MEMORY_WORKING_SET_LIST *) p; + for (unsigned long i = 0; i < mwsl->NumberOfPages; i++) + { + ++*vmrss; + unsigned flags = mwsl->WorkingSetList[i] & 0x0FFF; + if (flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE) == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE)) + ++*vmlib; + else if (flags & WSLE_PAGE_SHAREABLE) + ++*vmshare; + else if (flags & WSLE_PAGE_EXECUTE) + ++*vmtext; + else + ++*vmdata; + } + ret = NtQueryInformationProcess (hProcess, + ProcessVmCounters, + (PVOID) &vmc, + sizeof vmc, NULL); + if (ret != STATUS_SUCCESS) + { + debug_printf ("NtQueryInformationProcess: ret = %d, " + "Dos(ret) = %d", + ret, RtlNtStatusToDosError (ret)); + res = false; + goto out; + } + *vmsize = vmc.VirtualSize / page_size; +out: + delete [] p; + CloseHandle (hProcess); + return res; +} diff --git a/winsup/cygwin/fhandler_random.cc b/winsup/cygwin/fhandler_random.cc new file mode 100644 index 00000000000..e1faed4bb62 --- /dev/null +++ b/winsup/cygwin/fhandler_random.cc @@ -0,0 +1,171 @@ +/* fhandler_random.cc: code to access /dev/random and /dev/urandom + + Copyright 2000, 2001, 2002 Red Hat, Inc. + + Written by Corinna Vinschen (vinschen@cygnus.com) + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <errno.h> +#include <limits.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" + +#define RANDOM 8 +#define URANDOM 9 + +#define PSEUDO_MULTIPLIER (6364136223846793005LL) +#define PSEUDO_SHIFTVAL (21) + +fhandler_dev_random::fhandler_dev_random () + : fhandler_base (), crypt_prov ((HCRYPTPROV) NULL) +{ +} + +int +fhandler_dev_random::open (path_conv *, int flags, mode_t) +{ + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_nohandle (true); + set_open_status (); + return 1; +} + +BOOL +fhandler_dev_random::crypt_gen_random (void *ptr, size_t len) +{ + if (!crypt_prov + && !CryptAcquireContext (&crypt_prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET) + && !CryptAcquireContext (&crypt_prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET + | CRYPT_NEWKEYSET)) + { + debug_printf ("%E = CryptAquireContext()"); + return FALSE; + } + if (!CryptGenRandom (crypt_prov, len, (BYTE *)ptr)) + { + debug_printf ("%E = CryptGenRandom()"); + return FALSE; + } + return TRUE; +} + +int +fhandler_dev_random::pseudo_write (const void *ptr, size_t len) +{ + /* Use buffer to mess up the pseudo random number generator. */ + for (size_t i = 0; i < len; ++i) + pseudo = (pseudo + ((unsigned char *)ptr)[i]) * PSEUDO_MULTIPLIER + 1; + return len; +} + +int +fhandler_dev_random::write (const void *ptr, size_t len) +{ + if (!len) + return 0; + if (!ptr) + { + set_errno (EINVAL); + return -1; + } + + /* Limit len to a value <= 512 since we don't want to overact. + Copy to local buffer because CryptGenRandom violates const. */ + unsigned char buf[512]; + size_t limited_len = len <= 512 ? len : 512; + memcpy (buf, ptr, limited_len); + + /* Mess up system entropy source. Return error if device is /dev/random. */ + if (!crypt_gen_random (buf, limited_len) && dev == FH_RANDOM) + { + __seterrno (); + return -1; + } + /* Mess up the pseudo random number generator. */ + pseudo_write (buf, limited_len); + return len; +} + +int +fhandler_dev_random::pseudo_read (void *ptr, size_t len) +{ + /* Use pseudo random number generator as fallback entropy source. + This multiplier was obtained from Knuth, D.E., "The Art of + Computer Programming," Vol 2, Seminumerical Algorithms, Third + Edition, Addison-Wesley, 1998, p. 106 (line 26) & p. 108 */ + for (size_t i = 0; i < len; ++i) + { + pseudo = pseudo * PSEUDO_MULTIPLIER + 1; + ((unsigned char *)ptr)[i] = (pseudo >> PSEUDO_SHIFTVAL) & UCHAR_MAX; + } + return len; +} + +void __stdcall +fhandler_dev_random::read (void *ptr, size_t& len) +{ + if (!len) + return; + + if (!ptr) + { + set_errno (EINVAL); + (ssize_t) len = -1; + return; + } + + if (crypt_gen_random (ptr, len)) + return; + + /* If device is /dev/urandom, use pseudo number generator as fallback. + Don't do this for /dev/random since it's intended for uses that need + very high quality randomness. */ + if (dev == FH_URANDOM) + { + len = pseudo_read (ptr, len); + return; + } + + __seterrno (); + (ssize_t) len = -1; +} + +__off64_t +fhandler_dev_random::lseek (__off64_t, int) +{ + return 0; +} + +int +fhandler_dev_random::close (void) +{ + if (crypt_prov) + while (!CryptReleaseContext (crypt_prov, 0) + && GetLastError () == ERROR_BUSY) + Sleep (10); + return 0; +} + +int +fhandler_dev_random::dup (fhandler_base *child) +{ + fhandler_dev_random *fhr = (fhandler_dev_random *) child; + fhr->crypt_prov = (HCRYPTPROV)NULL; + return 0; +} + +void +fhandler_dev_random::dump () +{ + paranoid_printf ("here, fhandler_dev_random"); +} + diff --git a/winsup/cygwin/fhandler_raw.cc b/winsup/cygwin/fhandler_raw.cc new file mode 100644 index 00000000000..5e566a1aa35 --- /dev/null +++ b/winsup/cygwin/fhandler_raw.cc @@ -0,0 +1,568 @@ +/* fhandler_raw.cc. See fhandler.h for a description of the fhandler classes. + + Copyright 1999, 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. */ + +#include "winsup.h" +#include <sys/termios.h> +#include <errno.h> +#include <unistd.h> + +#include <cygwin/rdevio.h> +#include <sys/mtio.h> +#include <ntdef.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "ntdll.h" + +/* static wrapper functions to hide the effect of media changes and + bus resets which occurs after a new media is inserted. This is + also related to the used tape device. */ + +static BOOL write_file (HANDLE fh, const void *buf, DWORD to_write, + DWORD *written, int *err) +{ + BOOL ret; + + *err = 0; + if (!(ret = WriteFile (fh, buf, to_write, written, 0))) + { + if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED + || *err == ERROR_BUS_RESET) + { + *err = 0; + if (!(ret = WriteFile (fh, buf, to_write, written, 0))) + *err = GetLastError (); + } + } + syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)", + ret, *err, fh, buf, to_write, *written); + return ret; +} + +static BOOL read_file (HANDLE fh, void *buf, DWORD to_read, + DWORD *read, int *err) +{ + BOOL ret; + + *err = 0; + if (!(ret = ReadFile (fh, buf, to_read, read, 0))) + { + if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED + || *err == ERROR_BUS_RESET) + { + *err = 0; + if (!(ret = ReadFile (fh, buf, to_read, read, 0))) + *err = GetLastError (); + } + } + syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)", + ret, *err, fh, buf, to_read, *read); + return ret; +} + +/**********************************************************************/ +/* fhandler_dev_raw */ + +void +fhandler_dev_raw::clear (void) +{ + devbuf = NULL; + devbufsiz = 0; + devbufstart = 0; + devbufend = 0; + eom_detected = 0; + eof_detected = 0; + lastblk_to_read = 0; + varblkop = 0; +} + +int +fhandler_dev_raw::writebuf (void) +{ + DWORD written; + int ret = 0; + + if (is_writing && devbuf && devbufend) + { + DWORD to_write; + int ret = 0; + + memset (devbuf + devbufend, 0, devbufsiz - devbufend); + if (get_device () != FH_TAPE) + to_write = ((devbufend - 1) / 512 + 1) * 512; + else if (varblkop) + to_write = devbufend; + else + to_write = devbufsiz; + if (!write_file (get_handle (), devbuf, to_write, &written, &ret) + && is_eom (ret)) + eom_detected = 1; + if (written) + has_written = 1; + devbufstart = devbufend = 0; + } + is_writing = 0; + return ret; +} + +fhandler_dev_raw::fhandler_dev_raw () + : fhandler_base () +{ + clear (); + set_need_fork_fixup (); +} + +fhandler_dev_raw::~fhandler_dev_raw (void) +{ + if (devbufsiz > 1L) + delete [] devbuf; + clear (); +} + +int +fhandler_dev_raw::open (path_conv *real_path, int flags, mode_t) +{ + if (!wincap.has_raw_devices ()) + { + set_errno (ENOENT); + debug_printf ("%s is accessible under NT/W2K only",real_path->get_win32()); + return 0; + } + + /* Check for illegal flags. */ + if (flags & (O_APPEND | O_EXCL)) + { + set_errno (EINVAL); + return 0; + } + + /* Always open a raw device existing and binary. */ + flags &= ~(O_CREAT | O_TRUNC); + flags |= O_BINARY; + + DWORD access = GENERIC_READ | SYNCHRONIZE; + if (get_device () == FH_TAPE + || (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY + || (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDWR) + access |= GENERIC_WRITE; + + extern void str2buf2uni (UNICODE_STRING &, WCHAR *, const char *); + UNICODE_STRING dev; + WCHAR devname[MAX_PATH + 1]; + str2buf2uni (dev, devname, real_path->get_win32 ()); + OBJECT_ATTRIBUTES attr; + InitializeObjectAttributes (&attr, &dev, OBJ_CASE_INSENSITIVE, NULL, NULL); + + HANDLE h; + IO_STATUS_BLOCK io; + NTSTATUS status = NtOpenFile (&h, access, &attr, &io, wincap.shared (), + FILE_SYNCHRONOUS_IO_NONALERT); + if (!NT_SUCCESS (status)) + { + __seterrno_from_win_error (RtlNtStatusToDosError (status)); + return 0; + } + + set_io_handle (h); + set_flags ((flags & ~O_TEXT) | O_BINARY); + + if (devbufsiz > 1L) + devbuf = new char [devbufsiz]; + + return 1; +} + +int +fhandler_dev_raw::close (void) +{ + return fhandler_base::close (); +} + +void +fhandler_dev_raw::raw_read (void *ptr, size_t& ulen) +{ + DWORD bytes_read = 0; + DWORD read2; + DWORD bytes_to_read; + int ret; + size_t len = ulen; + char *tgt; + + /* In mode O_RDWR the buffer has to be written to device first */ + ret = writebuf (); + if (ret) + { + set_errno (is_eom (ret) ? ENOSPC : EACCES); + goto err; + } + + /* Checking a previous end of file */ + if (eof_detected && !lastblk_to_read) + { + eof_detected = 0; + ulen = 0; + return; + } + + /* Checking a previous end of media */ + if (eom_detected && !lastblk_to_read) + { + set_errno (ENOSPC); + goto err; + } + + if (devbuf) + { + while (len > 0) + { + if (devbufstart < devbufend) + { + bytes_to_read = min (len, devbufend - devbufstart); + debug_printf ("read %d bytes from buffer (rest %d)", + bytes_to_read, devbufstart - devbufend); + memcpy (ptr, devbuf + devbufstart, bytes_to_read); + len -= bytes_to_read; + ptr = (void *) ((char *) ptr + bytes_to_read); + bytes_read += bytes_to_read; + devbufstart += bytes_to_read; + + if (lastblk_to_read) + { + lastblk_to_read = 0; + break; + } + } + if (len > 0) + { + if (!varblkop && len >= devbufsiz) + { + if (get_device () == FH_TAPE) + bytes_to_read = (len / devbufsiz) * devbufsiz; + else + bytes_to_read = (len / 512) * 512; + tgt = (char *) ptr; + debug_printf ("read %d bytes direct from file",bytes_to_read); + } + else + { + bytes_to_read = devbufsiz; + tgt = devbuf; + if (varblkop) + debug_printf ("read variable bytes from file into buffer"); + else + debug_printf ("read %d bytes from file into buffer", + bytes_to_read); + } + if (!read_file (get_handle (), tgt, bytes_to_read, &read2, &ret)) + { + if (!is_eof (ret) && !is_eom (ret)) + { + debug_printf ("return -1, set errno to EACCES"); + set_errno (EACCES); + goto err; + } + + if (is_eof (ret)) + eof_detected = 1; + else + eom_detected = 1; + + if (!read2) + { + if (!bytes_read && is_eom (ret)) + { + debug_printf ("return -1, set errno to ENOSPC"); + set_errno (ENOSPC); + goto err; + } + break; + } + lastblk_to_read = 1; + } + if (! read2) + break; + if (tgt == devbuf) + { + devbufstart = 0; + devbufend = read2; + } + else + { + len -= bytes_to_read; + ptr = (void *) ((char *) ptr + bytes_to_read); + bytes_read += bytes_to_read; + } + } + } + } + else if (!read_file (get_handle (), ptr, len, &bytes_read, &ret)) + { + if (!is_eof (ret) && !is_eom (ret)) + { + debug_printf ("return -1, set errno to EACCES"); + set_errno (EACCES); + goto err; + } + if (bytes_read) + { + if (is_eof (ret)) + eof_detected = 1; + else + eom_detected = 1; + } + else if (is_eom (ret)) + { + debug_printf ("return -1, set errno to ENOSPC"); + set_errno (ENOSPC); + goto err; + } + } + + (ssize_t) ulen = bytes_read; + return; + +err: + (ssize_t) ulen = -1; + return; +} + +int +fhandler_dev_raw::raw_write (const void *ptr, size_t len) +{ + DWORD bytes_written = 0; + DWORD bytes_to_write; + DWORD written; + char *p = (char *) ptr; + char *tgt; + int ret; + + /* Checking a previous end of media on tape */ + if (eom_detected) + { + set_errno (ENOSPC); + return -1; + } + + if (!is_writing) + { + devbufend = devbufstart; + devbufstart = 0; + } + is_writing = 1; + + if (devbuf) + { + while (len > 0) + { + if (!varblkop && + (len < devbufsiz || devbufend > 0) && devbufend < devbufsiz) + { + bytes_to_write = min (len, devbufsiz - devbufend); + memcpy (devbuf + devbufend, p, bytes_to_write); + bytes_written += bytes_to_write; + devbufend += bytes_to_write; + p += bytes_to_write; + len -= bytes_to_write; + } + else + { + if (varblkop) + { + bytes_to_write = len; + tgt = p; + } + else if (devbufend == devbufsiz) + { + bytes_to_write = devbufsiz; + tgt = devbuf; + } + else + { + bytes_to_write = (len / devbufsiz) * devbufsiz; + tgt = p; + } + + ret = 0; + write_file (get_handle (), tgt, bytes_to_write, &written, &ret); + if (written) + has_written = 1; + + if (ret) + { + if (!is_eom (ret)) + { + __seterrno (); + return -1; + } + + eom_detected = 1; + + if (!written && !bytes_written) + { + set_errno (ENOSPC); + return -1; + } + + if (tgt == p) + bytes_written += written; + + break; // from while (len > 0) + } + + if (tgt == devbuf) + { + if (written != devbufsiz) + memmove (devbuf, devbuf + written, devbufsiz - written); + devbufend = devbufsiz - written; + } + else + { + len -= written; + p += written; + bytes_written += written; + } + } + } + } + else if (len > 0) + { + if (!write_file (get_handle (), ptr, len, &bytes_written, &ret)) + { + if (bytes_written) + has_written = 1; + if (!is_eom (ret)) + { + set_errno (EACCES); + return -1; + } + eom_detected = 1; + if (!bytes_written) + { + set_errno (ENOSPC); + return -1; + } + } + has_written = 1; + } + return bytes_written; +} + +int +fhandler_dev_raw::dup (fhandler_base *child) +{ + int ret = fhandler_base::dup (child); + + if (! ret) + { + fhandler_dev_raw *fhc = (fhandler_dev_raw *) child; + + fhc->devbufsiz = devbufsiz; + if (devbufsiz > 1L) + fhc->devbuf = new char [devbufsiz]; + fhc->devbufstart = 0; + fhc->devbufend = 0; + fhc->eom_detected = eom_detected; + fhc->eof_detected = eof_detected; + fhc->lastblk_to_read = 0; + fhc->varblkop = varblkop; + } + return ret; +} + +void +fhandler_dev_raw::fixup_after_fork (HANDLE) +{ + devbufstart = 0; + devbufend = 0; + lastblk_to_read = 0; +} + +void +fhandler_dev_raw::fixup_after_exec (HANDLE) +{ + if (devbufsiz > 1L) + devbuf = new char [devbufsiz]; + devbufstart = 0; + devbufend = 0; + lastblk_to_read = 0; +} + +int +fhandler_dev_raw::ioctl (unsigned int cmd, void *buf) +{ + int ret = NO_ERROR; + + if (cmd == RDIOCDOP) + { + struct rdop *op = (struct rdop *) buf; + + if (!op) + ret = ERROR_INVALID_PARAMETER; + else + switch (op->rd_op) + { + case RDSETBLK: + if (get_device () == FH_TAPE) + { + struct mtop mop; + + mop.mt_op = MTSETBLK; + mop.mt_count = op->rd_parm; + ret = ioctl (MTIOCTOP, &mop); + } + else if (op->rd_parm % 512) + ret = ERROR_INVALID_PARAMETER; + else if (devbuf && op->rd_parm < devbufend - devbufstart) + ret = ERROR_INVALID_PARAMETER; + else if (!devbuf || op->rd_parm != devbufsiz) + { + char *buf = new char [op->rd_parm]; + if (devbufsiz > 1L) + { + memcpy (buf, devbuf + devbufstart, devbufend - devbufstart); + devbufend -= devbufstart; + delete [] devbuf; + } + else + devbufend = 0; + + devbufstart = 0; + devbuf = buf; + devbufsiz = op->rd_parm; + } + break; + default: + break; + } + } + else if (cmd == RDIOCGET) + { + struct rdget *get = (struct rdget *) buf; + + if (!get) + ret = ERROR_INVALID_PARAMETER; + else + get->bufsiz = devbufsiz ? devbufsiz : 1L; + } + else + return fhandler_base::ioctl (cmd, buf); + + if (ret != NO_ERROR) + { + SetLastError (ret); + __seterrno (); + return -1; + } + return 0; +} diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc new file mode 100644 index 00000000000..2024416d8bf --- /dev/null +++ b/winsup/cygwin/fhandler_registry.cc @@ -0,0 +1,668 @@ +/* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem + + Copyright 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: Access permissions are ignored at the moment. */ + +#include "winsup.h" +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include <assert.h> + +#define _COMPILING_NEWLIB +#include <dirent.h> + +static const int registry_len = sizeof ("registry") - 1; +/* If this bit is set in __d_position then we are enumerating values, + * else sub-keys. keeping track of where we are is horribly messy + * the bottom 16 bits are the absolute position and the top 15 bits + * make up the value index if we are enuerating values. + */ +static const __off32_t REG_ENUM_VALUES_MASK = 0x8000000; +static const __off32_t REG_POSITION_MASK = 0xffff; + +/* List of root keys in /proc/registry. + * Possibly we should filter out those not relevant to the flavour of Windows + * Cygwin is running on. + */ +static const char *registry_listing[] = +{ + ".", + "..", + "HKEY_CLASSES_ROOT", + "HKEY_CURRENT_CONFIG", + "HKEY_CURRENT_USER", + "HKEY_LOCAL_MACHINE", + "HKEY_USERS", + "HKEY_DYN_DATA", // 95/98/Me + "HKEY_PERFOMANCE_DATA", // NT/2000/XP + NULL +}; + +static const HKEY registry_keys[] = +{ + (HKEY) INVALID_HANDLE_VALUE, + (HKEY) INVALID_HANDLE_VALUE, + HKEY_CLASSES_ROOT, + HKEY_CURRENT_CONFIG, + HKEY_CURRENT_USER, + HKEY_LOCAL_MACHINE, + HKEY_USERS, + HKEY_DYN_DATA, + HKEY_PERFORMANCE_DATA +}; + +static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY); + +/* These get added to each subdirectory in /proc/registry. + * If we wanted to implement writing, we could maybe add a '.writable' entry or + * suchlike. + */ +static const char *special_dot_files[] = +{ + ".", + "..", + NULL +}; + +static const int SPECIAL_DOT_FILE_COUNT = + (sizeof (special_dot_files) / sizeof (const char *)) - 1; + +/* Name given to default values */ +static const char *DEFAULT_VALUE_NAME = "@"; + +static HKEY open_key (const char *name, REGSAM access, bool isValue); + +/* Returns 0 if path doesn't exist, >0 if path is a directory, + * <0 if path is a file. + * + * We open the last key but one and then enum it's sub-keys and values to see if the + * final component is there. This gets round the problem of not having security access + * to the final key in the path. + */ +int +fhandler_registry::exists () +{ + int file_type = 0, index = 0, pathlen; + DWORD buf_size = MAX_PATH; + LONG error; + char buf[buf_size]; + const char *file; + HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; + + const char *path = get_name (); + debug_printf ("exists (%s)", path); + path += proc_len + registry_len + 2; + if (*path == 0) + { + file_type = 2; + goto out; + } + pathlen = strlen (path); + file = path + pathlen - 1; + if (isdirsep (*file) && pathlen > 1) + file--; + while (!isdirsep (*file)) + file--; + file++; + + if (file == path) + { + for (int i = 0; registry_listing[i]; i++) + if (path_prefix_p + (registry_listing[i], path, strlen (registry_listing[i]))) + { + file_type = 1; + goto out; + } + goto out; + } + + hKey = open_key (path, KEY_READ, false); + if (hKey != (HKEY) INVALID_HANDLE_VALUE) + file_type = 1; + else + { + hKey = open_key (path, KEY_READ, true); + if (hKey == (HKEY) INVALID_HANDLE_VALUE) + return 0; + + while (ERROR_SUCCESS == + (error = RegEnumKeyEx (hKey, index++, buf, &buf_size, NULL, NULL, + NULL, NULL)) + || (error == ERROR_MORE_DATA)) + { + if (pathmatch (buf, file)) + { + file_type = 1; + goto out; + } + buf_size = MAX_PATH; + } + if (error != ERROR_NO_MORE_ITEMS) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + goto out; + } + index = 0; + buf_size = MAX_PATH; + while (ERROR_SUCCESS == + (error = RegEnumValue (hKey, index++, buf, &buf_size, NULL, NULL, + NULL, NULL)) + || (error == ERROR_MORE_DATA)) + { + if (pathmatch (buf, file) || (buf[0] == '\0' && + pathmatch (file, DEFAULT_VALUE_NAME))) + { + file_type = -1; + goto out; + } + buf_size = MAX_PATH; + } + if (error != ERROR_NO_MORE_ITEMS) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + goto out; + } + } +out: + if (hKey != (HKEY) INVALID_HANDLE_VALUE) + RegCloseKey (hKey); + return file_type; +} + +fhandler_registry::fhandler_registry (): +fhandler_proc () +{ +} + +int +fhandler_registry::fstat (struct __stat64 *buf, path_conv *pc) +{ + this->fhandler_base::fstat (buf, pc); + buf->st_mode &= ~_IFMT & NO_W; + int file_type = exists (); + switch (file_type) + { + case 0: + set_errno (ENOENT); + return -1; + case 1: + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + break; + case 2: + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + buf->st_nlink = ROOT_KEY_COUNT; + break; + default: + case -1: + buf->st_mode |= S_IFREG; + buf->st_mode &= NO_X; + break; + } + if (file_type != 0 && file_type != 2) + { + HKEY hKey; + const char *path = get_name () + proc_len + registry_len + 2; + hKey = + open_key (path, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, + (file_type < 0) ? true : false); + + if (hKey != (HKEY) INVALID_HANDLE_VALUE) + { + FILETIME ftLastWriteTime; + DWORD subkey_count; + if (ERROR_SUCCESS == + RegQueryInfoKey (hKey, NULL, NULL, NULL, &subkey_count, NULL, + NULL, NULL, NULL, NULL, NULL, + &ftLastWriteTime)) + { + to_timestruc_t (&ftLastWriteTime, &buf->st_mtim); + buf->st_ctim = buf->st_mtim; + time_as_timestruc_t (&buf->st_atim); + if (file_type > 0) + buf->st_nlink = subkey_count; + else + { + int pathlen = strlen (path); + const char *value_name = path + pathlen - 1; + if (isdirsep (*value_name) && pathlen > 1) + value_name--; + while (!isdirsep (*value_name)) + value_name--; + value_name++; + DWORD dwSize; + if (ERROR_SUCCESS == + RegQueryValueEx (hKey, value_name, NULL, NULL, NULL, + &dwSize)) + buf->st_size = dwSize; + } + __uid32_t uid; + __gid32_t gid; + if (get_object_attribute + ((HANDLE) hKey, SE_REGISTRY_KEY, &buf->st_mode, &uid, + &gid) == 0) + { + buf->st_uid = uid; + buf->st_gid = gid; + buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + if (file_type > 0) + buf->st_mode |= S_IFDIR; + else + buf->st_mode &= NO_X; + } + } + RegCloseKey (hKey); + } + } + return 0; +} + +struct dirent * +fhandler_registry::readdir (DIR * dir) +{ + DWORD buf_size = MAX_PATH; + char buf[buf_size]; + HANDLE handle; + struct dirent *res = NULL; + const char *path = dir->__d_dirname + proc_len + 1 + registry_len; + LONG error; + + if (*path == 0) + { + if (dir->__d_position >= ROOT_KEY_COUNT) + goto out; + strcpy (dir->__d_dirent->d_name, registry_listing[dir->__d_position++]); + res = dir->__d_dirent; + goto out; + } + if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE + && dir->__d_position == 0) + { + handle = open_key (path + 1, KEY_READ, false); + dir->__d_u.__d_data.__handle = handle; + } + if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE) + goto out; + if (dir->__d_position < SPECIAL_DOT_FILE_COUNT) + { + strcpy (dir->__d_dirent->d_name, + special_dot_files[dir->__d_position++]); + res = dir->__d_dirent; + goto out; + } +retry: + if (dir->__d_position & REG_ENUM_VALUES_MASK) + /* For the moment, the type of key is ignored here. when write access is added, + * maybe add an extension for the type of each value? + */ + error = RegEnumValue ((HKEY) dir->__d_u.__d_data.__handle, + (dir->__d_position & ~REG_ENUM_VALUES_MASK) >> 16, + buf, &buf_size, NULL, NULL, NULL, NULL); + else + error = + RegEnumKeyEx ((HKEY) dir->__d_u.__d_data.__handle, dir->__d_position - + SPECIAL_DOT_FILE_COUNT, buf, &buf_size, NULL, NULL, NULL, + NULL); + if (error == ERROR_NO_MORE_ITEMS + && (dir->__d_position & REG_ENUM_VALUES_MASK) == 0) + { + /* If we're finished with sub-keys, start on values under this key. */ + dir->__d_position |= REG_ENUM_VALUES_MASK; + buf_size = MAX_PATH; + goto retry; + } + if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) + { + RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle); + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + seterrno_from_win_error (__FILE__, __LINE__, error); + goto out; + } + + /* We get here if `buf' contains valid data. */ + if (*buf == 0) + strcpy (dir->__d_dirent->d_name, DEFAULT_VALUE_NAME); + else + strcpy (dir->__d_dirent->d_name, buf); + + dir->__d_position++; + if (dir->__d_position & REG_ENUM_VALUES_MASK) + dir->__d_position += 0x10000; + res = dir->__d_dirent; +out: + syscall_printf ("%p = readdir (%p)", &dir->__d_dirent, dir); + return res; +} + +__off64_t +fhandler_registry::telldir (DIR * dir) +{ + return dir->__d_position & REG_POSITION_MASK; +} + +void +fhandler_registry::seekdir (DIR * dir, __off64_t loc) +{ + /* Unfortunately cannot simply set __d_position due to transition from sub-keys to + * values. + */ + rewinddir (dir); + while (loc > (dir->__d_position & REG_POSITION_MASK)) + if (!readdir (dir)) + break; +} + +void +fhandler_registry::rewinddir (DIR * dir) +{ + if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE) + { + (void) RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle); + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + } + dir->__d_position = 0; + return; +} + +int +fhandler_registry::closedir (DIR * dir) +{ + int res = 0; + if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE && + RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle) != ERROR_SUCCESS) + { + __seterrno (); + res = -1; + } + syscall_printf ("%d = closedir (%p)", res, dir); + return 0; +} + +int +fhandler_registry::open (path_conv * pc, int flags, mode_t mode) +{ + int pathlen; + const char *file; + HKEY handle; + + int res = fhandler_virtual::open (pc, flags, mode); + if (!res) + goto out; + + const char *path; + path = get_name () + proc_len + 1 + registry_len; + if (!*path) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + flags |= O_DIROPEN; + goto success; + } + } + path++; + pathlen = strlen (path); + file = path + pathlen - 1; + if (isdirsep (*file) && pathlen > 1) + file--; + while (!isdirsep (*file)) + file--; + file++; + + if (file == path) + { + for (int i = 0; registry_listing[i]; i++) + if (path_prefix_p + (registry_listing[i], path, strlen (registry_listing[i]))) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + flags |= O_DIROPEN; + goto success; + } + } + + if (flags & O_CREAT) + { + set_errno (EROFS); + res = 0; + goto out; + } + else + { + set_errno (ENOENT); + res = 0; + goto out; + } + } + + if (flags & O_WRONLY) + { + set_errno (EROFS); + res = 0; + goto out; + } + + handle = open_key (path, KEY_READ, true); + if (handle == (HKEY) INVALID_HANDLE_VALUE) + { + res = 0; + goto out; + } + + set_io_handle (handle); + + if (pathmatch (file, DEFAULT_VALUE_NAME)) + value_name = cstrdup (""); + else + value_name = cstrdup (file); + + if (!fill_filebuf ()) + { + RegCloseKey (handle); + res = 0; + goto out; + } + + if (flags & O_APPEND) + position = filesize; + else + position = 0; + +success: + res = 1; + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); +out: + syscall_printf ("%d = fhandler_registry::open (%p, %d)", res, flags, mode); + return res; +} + +int +fhandler_registry::close () +{ + int res = fhandler_virtual::close (); + if (res != 0) + return res; + HKEY handle = (HKEY) get_handle (); + if (handle != (HKEY) INVALID_HANDLE_VALUE) + { + if (RegCloseKey (handle) != ERROR_SUCCESS) + { + __seterrno (); + res = -1; + } + } + if (value_name) + cfree (value_name); + return res; +} + +bool +fhandler_registry::fill_filebuf () +{ + DWORD type, size; + LONG error; + HKEY handle = (HKEY) get_handle (); + if (handle != HKEY_PERFORMANCE_DATA) + { + error = RegQueryValueEx (handle, value_name, NULL, &type, NULL, &size); + if (error != ERROR_SUCCESS) + { + if (error != ERROR_FILE_NOT_FOUND) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + return false; + } + goto value_not_found; + } + bufalloc = size; + filebuf = (char *) malloc (bufalloc); + error = + RegQueryValueEx (handle, value_name, NULL, NULL, (BYTE *) filebuf, + &size); + if (error != ERROR_SUCCESS) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + return true; + } + filesize = size; + } + else + { + bufalloc = 0; + do + { + bufalloc += 1000; + filebuf = (char *) realloc (filebuf, bufalloc); + error = RegQueryValueEx (handle, value_name, NULL, &type, + (BYTE *) filebuf, &size); + if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) + { + if (error != ERROR_FILE_NOT_FOUND) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + return true; + } + goto value_not_found; + } + } + while (error == ERROR_MORE_DATA); + filesize = size; + } + return true; +value_not_found: + DWORD buf_size = MAX_PATH; + char buf[buf_size]; + int index = 0; + while (ERROR_SUCCESS == + (error = RegEnumKeyEx (handle, index++, buf, &buf_size, NULL, NULL, + NULL, NULL)) || (error == ERROR_MORE_DATA)) + { + if (pathmatch (buf, value_name)) + { + set_errno (EISDIR); + return false; + } + buf_size = MAX_PATH; + } + if (error != ERROR_NO_MORE_ITEMS) + { + seterrno_from_win_error (__FILE__, __LINE__, error); + return false; + } + set_errno (ENOENT); + return false; +} + +/* Auxillary member function to open registry keys. */ +static HKEY +open_key (const char *name, REGSAM access, bool isValue) +{ + HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; + HKEY hParentKey = (HKEY) INVALID_HANDLE_VALUE; + bool parentOpened = false; + char component[MAX_PATH]; + + while (*name) + { + const char *anchor = name; + while (*name && !isdirsep (*name)) + name++; + strncpy (component, anchor, name - anchor); + component[name - anchor] = '\0'; + if (*name) + name++; + if (*name == 0 && isValue == true) + goto out; + + if (hParentKey != (HKEY) INVALID_HANDLE_VALUE) + { + REGSAM effective_access = KEY_READ; + if ((strchr (name, '/') == NULL && isValue == true) || *name == 0) + effective_access = access; + LONG + error = + RegOpenKeyEx (hParentKey, component, 0, effective_access, &hKey); + if (error != ERROR_SUCCESS) + { + hKey = (HKEY) INVALID_HANDLE_VALUE; + seterrno_from_win_error (__FILE__, __LINE__, error); + return hKey; + } + if (parentOpened) + RegCloseKey (hParentKey); + hParentKey = hKey; + parentOpened = true; + } + else + { + for (int i = 0; registry_listing[i]; i++) + if (pathmatch (component, registry_listing[i])) + hKey = registry_keys[i]; + if (hKey == (HKEY) INVALID_HANDLE_VALUE) + return hKey; + hParentKey = hKey; + } + } +out: + return hKey; +} diff --git a/winsup/cygwin/fhandler_serial.cc b/winsup/cygwin/fhandler_serial.cc new file mode 100644 index 00000000000..b813c9121a7 --- /dev/null +++ b/winsup/cygwin/fhandler_serial.cc @@ -0,0 +1,1026 @@ +/* fhandler_serial.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 <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "sigproc.h" +#include "pinfo.h" +#include <sys/termios.h> +#include <ddk/ntddser.h> + +/**********************************************************************/ +/* fhandler_serial */ + +fhandler_serial::fhandler_serial () + : fhandler_base (), vmin_ (0), vtime_ (0), pgrp_ (myself->pgid) +{ + set_need_fork_fixup (); +} + +void +fhandler_serial::overlapped_setup () +{ + memset (&io_status, 0, sizeof (io_status)); + io_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (io_status.hEvent); + overlapped_armed = 0; +} + +void +fhandler_serial::raw_read (void *ptr, size_t& ulen) +{ + int tot; + DWORD n; + HANDLE w4[2]; + size_t minchars = vmin_ ?: ulen; + + w4[0] = io_status.hEvent; + w4[1] = signal_arrived; + + debug_printf ("ulen %d, vmin_ %d, vtime_ %d, hEvent %p", ulen, vmin_, vtime_, + io_status.hEvent); + if (!overlapped_armed) + { + (void) SetCommMask (get_handle (), EV_RXCHAR); + ResetEvent (io_status.hEvent); + } + + for (n = 0, tot = 0; ulen; ulen -= n, ptr = (char *) ptr + n) + { + COMSTAT st; + DWORD inq = 1; + + n = 0; + + if (!vtime_ && !vmin_) + inq = ulen; + else if (vtime_) + { + inq = ulen; // non-interruptible -- have to use kernel timeouts + // also note that this is not strictly correct. + // if vmin > ulen then things won't work right. + overlapped_armed = -1; + } + + if (!ClearCommError (get_handle (), &ev, &st)) + goto err; + else if (ev) + termios_printf ("error detected %x", ev); + else if (st.cbInQue) + inq = st.cbInQue; + else if (!overlapped_armed) + { + if ((size_t) tot >= minchars) + break; + else if (WaitCommEvent (get_handle (), &ev, &io_status)) + { + debug_printf ("WaitCommEvent succeeded: ev %x", ev); + if (!ev) + continue; + } + else if (GetLastError () != ERROR_IO_PENDING) + goto err; + else + { + overlapped_armed = 1; + switch (WaitForMultipleObjects (2, w4, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + if (!GetOverlappedResult (get_handle (), &io_status, &n, FALSE)) + goto err; + debug_printf ("n %d, ev %x", n, ev); + break; + case WAIT_OBJECT_0 + 1: + tot = -1; + PurgeComm (get_handle (), PURGE_RXABORT); + overlapped_armed = 0; + set_sig_errno (EINTR); + goto out; + default: + goto err; + } + } + } + + overlapped_armed = 0; + ResetEvent (io_status.hEvent); + if (inq > ulen) + inq = ulen; + debug_printf ("inq %d", inq); + if (ReadFile (get_handle (), ptr, min (inq, ulen), &n, &io_status)) + /* Got something */; + else if (GetLastError () != ERROR_IO_PENDING) + goto err; + else if (!GetOverlappedResult (get_handle (), &io_status, &n, TRUE)) + goto err; + + tot += n; + debug_printf ("vtime_ %d, vmin_ %d, n %d, tot %d", vtime_, vmin_, n, tot); + if (vtime_ || !vmin_ || !n) + break; + continue; + + err: + PurgeComm (get_handle (), PURGE_RXABORT); + debug_printf ("err %E"); + if (GetLastError () == ERROR_OPERATION_ABORTED) + n = 0; + else + { + tot = -1; + __seterrno (); + break; + } + } + +out: + ulen = tot; +} + +/* Cover function to WriteFile to provide Posix interface and semantics + (as much as possible). */ +int +fhandler_serial::raw_write (const void *ptr, size_t len) +{ + DWORD bytes_written; + OVERLAPPED write_status; + + memset (&write_status, 0, sizeof (write_status)); + write_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (write_status.hEvent); + + for (;;) + { + if (WriteFile (get_handle (), ptr, len, &bytes_written, &write_status)) + break; + + switch (GetLastError ()) + { + case ERROR_OPERATION_ABORTED: + continue; + case ERROR_IO_PENDING: + break; + default: + goto err; + } + + if (!GetOverlappedResult (get_handle (), &write_status, &bytes_written, TRUE)) + goto err; + + break; + } + + ForceCloseHandle (write_status.hEvent); + + return bytes_written; + +err: + __seterrno (); + ForceCloseHandle (write_status.hEvent); + return -1; +} + +void +fhandler_serial::dump (void) +{ + paranoid_printf ("here"); +} + +void +fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin) +{ + (void) open (NULL, flags, bin & (O_BINARY | O_TEXT)); +} + +int +fhandler_serial::open (path_conv *, int flags, mode_t mode) +{ + int res; + COMMTIMEOUTS to; + extern BOOL reset_com; + + syscall_printf ("fhandler_serial::open (%s, %p, %p)", + get_name (), flags, mode); + + if (!(res = this->fhandler_base::open (NULL, flags, mode))) + return 0; + + res = 1; + + (void) SetCommMask (get_handle (), EV_RXCHAR); + + set_r_no_interrupt (1); // Handled explicitly in read code + + overlapped_setup (); + + memset (&to, 0, sizeof (to)); + (void) SetCommTimeouts (get_handle (), &to); + + /* Reset serial port to known state of 9600-8-1-no flow control + on open for better behavior under Win 95. + + FIXME: This should only be done when explicitly opening the com + port. It should not be reset if an fd is inherited. + Using __progname in this way, to determine how far along in the + initialization we are, is really a terrible kludge and should + be fixed ASAP. + */ + extern char *__progname; + if (reset_com && __progname) + { + DCB state; + GetCommState (get_handle (), &state); + syscall_printf ("setting initial state on %s (reset_com %d)", + get_name (), reset_com); + state.BaudRate = CBR_9600; + state.ByteSize = 8; + state.StopBits = ONESTOPBIT; + state.Parity = NOPARITY; /* FIXME: correct default? */ + state.fBinary = TRUE; /* binary xfer */ + state.EofChar = 0; /* no end-of-data in binary mode */ + state.fNull = FALSE; /* don't discard nulls in binary mode */ + state.fParity = FALSE; /* ignore parity errors */ + state.fErrorChar = FALSE; + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + state.fOutX = FALSE; /* disable transmission flow control */ + state.fInX = FALSE; /* disable reception flow control */ + state.XonChar = 0x11; + state.XoffChar = 0x13; + state.fOutxDsrFlow = FALSE; /* disable DSR flow control */ + state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except + DTR */ + state.fOutxCtsFlow = FALSE; /* disable output flow control */ + state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */ + state.fDsrSensitivity = FALSE; /* don't assert DSR */ + state.fAbortOnError = TRUE; + if (!SetCommState (get_handle (), &state)) + system_printf ("couldn't set initial state for %s, %E", get_name ()); + } + + /* setting rts and dtr to known state so that ioctl() function with + request TIOCMGET could return correct value of RTS and DTR lines. + Important only for Win 9x systems */ + + if (!wincap.supports_reading_modem_output_lines ()) + { + if (EscapeCommFunction (get_handle (), SETDTR) == 0) + system_printf ("couldn't set initial state of DTR for %s, %E", get_name ()); + if (EscapeCommFunction (get_handle (), SETRTS) == 0) + system_printf ("couldn't set initial state of RTS for %s, %E", get_name ()); + + /* even though one of above functions fail I have to set rts and dtr + variables to initial value. */ + rts = TIOCM_RTS; + dtr = TIOCM_DTR; + } + + SetCommMask (get_handle (), EV_RXCHAR); + set_open_status (); + syscall_printf ("%p = fhandler_serial::open (%s, %p, %p)", + res, get_name (), flags, mode); + return res; +} + +int +fhandler_serial::close () +{ + (void) ForceCloseHandle (io_status.hEvent); + return fhandler_base::close (); +} + +/* tcsendbreak: POSIX 7.2.2.1 */ +/* Break for 250-500 milliseconds if duration == 0 */ +/* Otherwise, units for duration are undefined */ +int +fhandler_serial::tcsendbreak (int duration) +{ + unsigned int sleeptime = 300000; + + if (duration > 0) + sleeptime *= duration; + + if (SetCommBreak (get_handle ()) == 0) + return -1; + + /* FIXME: need to send zero bits during duration */ + usleep (sleeptime); + + if (ClearCommBreak (get_handle ()) == 0) + return -1; + + syscall_printf ("0 = fhandler_serial:tcsendbreak (%d)", duration); + + return 0; +} + +/* tcdrain: POSIX 7.2.2.1 */ +int +fhandler_serial::tcdrain (void) +{ + if (FlushFileBuffers (get_handle ()) == 0) + return -1; + + return 0; +} + +/* tcflow: POSIX 7.2.2.1 */ +int +fhandler_serial::tcflow (int action) +{ + DWORD win32action = 0; + DCB dcb; + char xchar; + + termios_printf ("action %d", action); + + switch (action) + { + case TCOOFF: + win32action = SETXOFF; + break; + case TCOON: + win32action = SETXON; + break; + case TCION: + case TCIOFF: + if (GetCommState (get_handle (), &dcb) == 0) + return -1; + if (action == TCION) + xchar = (dcb.XonChar ? dcb.XonChar : 0x11); + else + xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13); + if (TransmitCommChar (get_handle (), xchar) == 0) + return -1; + return 0; + break; + default: + return -1; + break; + } + + if (EscapeCommFunction (get_handle (), win32action) == 0) + return -1; + + return 0; +} + + +/* ioctl: */ +int +fhandler_serial::ioctl (unsigned int cmd, void *buffer) +{ + int res = 0; + +# define ibuffer ((int) buffer) +# define ipbuffer (*(int *) buffer) + + DWORD ev; + COMSTAT st; + if (!ClearCommError (get_handle (), &ev, &st)) + { + __seterrno (); + res = -1; + } + else + switch (cmd) + { + case TCFLSH: + res = tcflush (ibuffer); + break; + case TIOCMGET: + DWORD modem_lines; + if (!GetCommModemStatus (get_handle (), &modem_lines)) + { + __seterrno (); + res = -1; + } + else + { + ipbuffer = 0; + if (modem_lines & MS_CTS_ON) + ipbuffer |= TIOCM_CTS; + if (modem_lines & MS_DSR_ON) + ipbuffer |= TIOCM_DSR; + if (modem_lines & MS_RING_ON) + ipbuffer |= TIOCM_RI; + if (modem_lines & MS_RLSD_ON) + ipbuffer |= TIOCM_CD; + + DWORD cb; + DWORD mcr; + if (!DeviceIoControl (get_handle (), IOCTL_SERIAL_GET_DTRRTS, + NULL, 0, &mcr, 4, &cb, 0) || cb != 4) + ipbuffer |= rts | dtr; + else + { + if (mcr & 2) + ipbuffer |= TIOCM_RTS; + if (mcr & 1) + ipbuffer |= TIOCM_DTR; + } + } + break; + case TIOCMSET: + if (ipbuffer & TIOCM_RTS) + { + if (EscapeCommFunction (get_handle (), SETRTS)) + rts = TIOCM_RTS; + else + { + __seterrno (); + res = -1; + } + } + else + { + if (EscapeCommFunction (get_handle (), CLRRTS)) + rts = 0; + else + { + __seterrno (); + res = -1; + } + } + if (ipbuffer & TIOCM_DTR) + { + if (EscapeCommFunction (get_handle (), SETDTR)) + dtr = TIOCM_DTR; + else + { + __seterrno (); + res = -1; + } + } + else if (EscapeCommFunction (get_handle (), CLRDTR)) + dtr = 0; + else + { + __seterrno (); + res = -1; + } + break; + case TIOCINQ: + if (ev & CE_FRAME || ev & CE_IOE || ev & CE_OVERRUN || ev & CE_RXOVER + || ev & CE_RXPARITY) + { + set_errno (EINVAL); /* FIXME: Use correct errno */ + res = -1; + } + else + ipbuffer = st.cbInQue; + break; + default: + set_errno (ENOSYS); + res = -1; + break; + } + + termios_printf ("%d = ioctl (%p, %p)", res, cmd, buffer); +# undef ibuffer +# undef ipbuffer + return res; +} + +/* tcflush: POSIX 7.2.2.1 */ +int +fhandler_serial::tcflush (int queue) +{ + if (queue == TCOFLUSH || queue == TCIOFLUSH) + PurgeComm (get_handle (), PURGE_TXABORT | PURGE_TXCLEAR); + + if (queue == TCIFLUSH || queue == TCIOFLUSH) + /* Input flushing by polling until nothing turns up + (we stop after 1000 chars anyway) */ + for (int max = 1000; max > 0; max--) + { + COMSTAT st; + if (!PurgeComm (get_handle (), PURGE_RXABORT | PURGE_RXCLEAR)) + break; + low_priority_sleep (100); + if (!ClearCommError (get_handle (), &ev, &st) || !st.cbInQue) + break; + } + + return 0; +} + +/* tcsetattr: POSIX 7.2.1.1 */ +int +fhandler_serial::tcsetattr (int action, const struct termios *t) +{ + /* Possible actions: + TCSANOW: immediately change attributes. + TCSADRAIN: flush output, then change attributes. + TCSAFLUSH: flush output and discard input, then change attributes. + */ + + BOOL dropDTR = FALSE; + COMMTIMEOUTS to; + DCB ostate, state; + unsigned int ovtime = vtime_, ovmin = vmin_; + int tmpDtr, tmpRts; + tmpDtr = tmpRts = 0; + + termios_printf ("action %d", action); + if ((action == TCSADRAIN) || (action == TCSAFLUSH)) + { + FlushFileBuffers (get_handle ()); + termios_printf ("flushed file buffers"); + } + if (action == TCSAFLUSH) + PurgeComm (get_handle (), (PURGE_RXABORT | PURGE_RXCLEAR)); + + /* get default/last comm state */ + if (!GetCommState (get_handle (), &ostate)) + return -1; + + state = ostate; + + /* -------------- Set baud rate ------------------ */ + /* FIXME: WIN32 also has 14400, 56000, 128000, and 256000. + Unix also has 230400. */ + + switch (t->c_ospeed) + { + case B0: /* drop DTR */ + dropDTR = TRUE; + state.BaudRate = 0; + break; + case B110: + state.BaudRate = CBR_110; + break; + case B300: + state.BaudRate = CBR_300; + break; + case B600: + state.BaudRate = CBR_600; + break; + case B1200: + state.BaudRate = CBR_1200; + break; + case B2400: + state.BaudRate = CBR_2400; + break; + case B4800: + state.BaudRate = CBR_4800; + break; + case B9600: + state.BaudRate = CBR_9600; + break; + case B19200: + state.BaudRate = CBR_19200; + break; + case B38400: + state.BaudRate = CBR_38400; + break; + case B57600: + state.BaudRate = CBR_57600; + break; + case B115200: + state.BaudRate = CBR_115200; + break; + case B230400: + state.BaudRate = 230400 /* CBR_230400 - not defined */; + break; + default: + /* Unsupported baud rate! */ + termios_printf ("Invalid t->c_ospeed %d", t->c_ospeed); + set_errno (EINVAL); + return -1; + } + + /* -------------- Set byte size ------------------ */ + + switch (t->c_cflag & CSIZE) + { + case CS5: + state.ByteSize = 5; + break; + case CS6: + state.ByteSize = 6; + break; + case CS7: + state.ByteSize = 7; + break; + case CS8: + state.ByteSize = 8; + break; + default: + /* Unsupported byte size! */ + termios_printf ("Invalid t->c_cflag byte size %d", + t->c_cflag & CSIZE); + set_errno (EINVAL); + return -1; + } + + /* -------------- Set stop bits ------------------ */ + + if (t->c_cflag & CSTOPB) + state.StopBits = TWOSTOPBITS; + else + state.StopBits = ONESTOPBIT; + + /* -------------- Set parity ------------------ */ + + if (t->c_cflag & PARENB) + state.Parity = (t->c_cflag & PARODD) ? ODDPARITY : EVENPARITY; + else + state.Parity = NOPARITY; + + state.fBinary = TRUE; /* Binary transfer */ + state.EofChar = 0; /* No end-of-data in binary mode */ + state.fNull = FALSE; /* Don't discard nulls in binary mode */ + + /* -------------- Parity errors ------------------ */ + /* fParity combines the function of INPCK and NOT IGNPAR */ + + if ((t->c_iflag & INPCK) && !(t->c_iflag & IGNPAR)) + state.fParity = TRUE; /* detect parity errors */ + else + state.fParity = FALSE; /* ignore parity errors */ + + /* Only present in Win32, Unix has no equivalent */ + state.fErrorChar = FALSE; + state.ErrorChar = 0; + + /* -------------- Set software flow control ------------------ */ + /* Set fTXContinueOnXoff to FALSE. This prevents the triggering of a + premature XON when the remote device interprets a received character + as XON (same as IXANY on the remote side). Otherwise, a TRUE + value separates the TX and RX functions. */ + + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + + /* Transmission flow control */ + if (t->c_iflag & IXON) + state.fOutX = TRUE; /* enable */ + else + state.fOutX = FALSE; /* disable */ + + /* Reception flow control */ + if (t->c_iflag & IXOFF) + state.fInX = TRUE; /* enable */ + else + state.fInX = FALSE; /* disable */ + + /* XoffLim and XonLim are left at default values */ + + state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11); + state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13); + + /* -------------- Set hardware flow control ------------------ */ + + /* Disable DSR flow control */ + state.fOutxDsrFlow = FALSE; + + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* RTS/CTS flow control */ + if (t->c_cflag & CRTSCTS) + { /* enable */ + state.fOutxCtsFlow = TRUE; + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + } + else + { /* disable */ + state.fRtsControl = RTS_CONTROL_ENABLE; + state.fOutxCtsFlow = FALSE; + tmpRts = TIOCM_RTS; + } + + if (t->c_cflag & CRTSXOFF) + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + + /* -------------- DTR ------------------ */ + /* Assert DTR on device open */ + + state.fDtrControl = DTR_CONTROL_ENABLE; + + /* -------------- DSR ------------------ */ + /* Assert DSR at the device? */ + + if (t->c_cflag & CLOCAL) + state.fDsrSensitivity = FALSE; /* no */ + else + state.fDsrSensitivity = TRUE; /* yes */ + + /* -------------- Error handling ------------------ */ + /* Since read/write operations terminate upon error, we + will use ClearCommError() to resume. */ + + state.fAbortOnError = TRUE; + + if (memcmp (&ostate, &state, sizeof (state)) != 0) + SetCommState (get_handle (), &state); + + set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1); + set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1); + + if (dropDTR == TRUE) + { + EscapeCommFunction (get_handle (), CLRDTR); + tmpDtr = 0; + } + else + { + /* FIXME: Sometimes when CLRDTR is set, setting + state.fDtrControl = DTR_CONTROL_ENABLE will fail. This + is a problem since a program might want to change some + parameters while DTR is still down. */ + + EscapeCommFunction (get_handle (), SETDTR); + tmpDtr = TIOCM_DTR; + } + + rts = tmpRts; + dtr = tmpDtr; + + /* + The following documentation on was taken from "Linux Serial Programming + HOWTO". It explains how MIN (t->c_cc[VMIN] || vmin_) and TIME + (t->c_cc[VTIME] || vtime_) is to be used. + + In non-canonical input processing mode, input is not assembled into + lines and input processing (erase, kill, delete, etc.) does not + occur. Two parameters control the behavior of this mode: c_cc[VTIME] + sets the character timer, and c_cc[VMIN] sets the minimum number of + characters to receive before satisfying the read. + + If MIN > 0 and TIME = 0, MIN sets the number of characters to receive + before the read is satisfied. As TIME is zero, the timer is not used. + + If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will + be satisfied if a single character is read, or TIME is exceeded (t = + TIME *0.1 s). If TIME is exceeded, no character will be returned. + + If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The + read will be satisfied if MIN characters are received, or the time + between two characters exceeds TIME. The timer is restarted every time + a character is received and only becomes active after the first + character has been received. + + If MIN = 0 and TIME = 0, read will be satisfied immediately. The + number of characters currently available, or the number of characters + requested will be returned. According to Antonino (see contributions), + you could issue a fcntl(fd, F_SETFL, FNDELAY); before reading to get + the same result. + */ + + if (t->c_lflag & ICANON) + { + vmin_ = 0; + vtime_ = 0; + } + else + { + vtime_ = t->c_cc[VTIME] * 100; + vmin_ = t->c_cc[VMIN]; + } + + debug_printf ("vtime %d, vmin %d", vtime_, vmin_); + + if (ovmin == vmin_ && ovtime == vtime_) + return 0; + + memset (&to, 0, sizeof (to)); + + if ((vmin_ > 0) && (vtime_ == 0)) + { + /* Returns immediately with whatever is in buffer on a ReadFile(); + or blocks if nothing found. We will keep calling ReadFile(); until + vmin_ characters are read */ + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + to.ReadTotalTimeoutConstant = MAXDWORD - 1; + } + else if ((vmin_ == 0) && (vtime_ > 0)) + { + /* set timeoout constant appropriately and we will only try to + read one character in ReadFile() */ + to.ReadTotalTimeoutConstant = vtime_; + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + } + else if ((vmin_ > 0) && (vtime_ > 0)) + { + /* time applies to the interval time for this case */ + to.ReadIntervalTimeout = vtime_; + } + else if ((vmin_ == 0) && (vtime_ == 0)) + { + /* returns immediately with whatever is in buffer as per + Time-Outs docs in Win32 SDK API docs */ + to.ReadIntervalTimeout = MAXDWORD; + } + + debug_printf ("ReadTotalTimeoutConstant %d, ReadIntervalTimeout %d, ReadTotalTimeoutMultiplier %d", + to.ReadTotalTimeoutConstant, to.ReadIntervalTimeout, to.ReadTotalTimeoutMultiplier); + int res = SetCommTimeouts (get_handle (), &to); + if (!res) + { + system_printf ("SetCommTimeout failed, %E"); + __seterrno (); + return -1; + } + + return 0; +} + +/* tcgetattr: POSIX 7.2.1.1 */ +int +fhandler_serial::tcgetattr (struct termios *t) +{ + DCB state; + + /* Get current Win32 comm state */ + if (GetCommState (get_handle (), &state) == 0) + return -1; + + /* for safety */ + memset (t, 0, sizeof (*t)); + + /* -------------- Baud rate ------------------ */ + + switch (state.BaudRate) + { + case 0: + /* FIXME: need to drop DTR */ + t->c_cflag = t->c_ospeed = t->c_ispeed = B0; + break; + case CBR_110: + t->c_cflag = t->c_ospeed = t->c_ispeed = B110; + break; + case CBR_300: + t->c_cflag = t->c_ospeed = t->c_ispeed = B300; + break; + case CBR_600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B600; + break; + case CBR_1200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B1200; + break; + case CBR_2400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B2400; + break; + case CBR_4800: + t->c_cflag = t->c_ospeed = t->c_ispeed = B4800; + break; + case CBR_9600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B9600; + break; + case CBR_19200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B19200; + break; + case CBR_38400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B38400; + break; + case CBR_57600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B57600; + break; + case CBR_115200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B115200; + break; + case 230400: /* CBR_230400 - not defined */ + t->c_cflag = t->c_ospeed = t->c_ispeed = B230400; + break; + default: + /* Unsupported baud rate! */ + termios_printf ("Invalid baud rate %d", state.BaudRate); + set_errno (EINVAL); + return -1; + } + + /* -------------- Byte size ------------------ */ + + switch (state.ByteSize) + { + case 5: + t->c_cflag |= CS5; + break; + case 6: + t->c_cflag |= CS6; + break; + case 7: + t->c_cflag |= CS7; + break; + case 8: + t->c_cflag |= CS8; + break; + default: + /* Unsupported byte size! */ + termios_printf ("Invalid byte size %d", state.ByteSize); + set_errno (EINVAL); + return -1; + } + + /* -------------- Stop bits ------------------ */ + + if (state.StopBits == TWOSTOPBITS) + t->c_cflag |= CSTOPB; + + /* -------------- Parity ------------------ */ + + if (state.Parity == ODDPARITY) + t->c_cflag |= (PARENB | PARODD); + if (state.Parity == EVENPARITY) + t->c_cflag |= PARENB; + + /* -------------- Parity errors ------------------ */ + + /* fParity combines the function of INPCK and NOT IGNPAR */ + if (state.fParity == TRUE) + t->c_iflag |= INPCK; + else + t->c_iflag |= IGNPAR; /* not necessarily! */ + + /* -------------- Software flow control ------------------ */ + + /* transmission flow control */ + if (state.fOutX) + t->c_iflag |= IXON; + + /* reception flow control */ + if (state.fInX) + t->c_iflag |= IXOFF; + + t->c_cc[VSTART] = (state.XonChar ? state.XonChar : 0x11); + t->c_cc[VSTOP] = (state.XoffChar ? state.XoffChar : 0x13); + + /* -------------- Hardware flow control ------------------ */ + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* Input flow-control */ + if ((state.fRtsControl == RTS_CONTROL_HANDSHAKE) && + (state.fOutxCtsFlow == TRUE)) + t->c_cflag |= CRTSCTS; + if (state.fRtsControl == RTS_CONTROL_HANDSHAKE) + t->c_cflag |= CRTSXOFF; + + /* -------------- CLOCAL --------------- */ + /* DSR is only lead toggled only by CLOCAL. Check it to see if + CLOCAL was called. */ + /* FIXME: If tcsetattr() hasn't been called previously, this may + give a false CLOCAL. */ + + if (state.fDsrSensitivity == FALSE) + t->c_cflag |= CLOCAL; + + /* FIXME: need to handle IGNCR */ +#if 0 + if (!get_r_binary ()) + t->c_iflag |= IGNCR; +#endif + + if (!get_w_binary ()) + t->c_oflag |= ONLCR; + + t->c_cc[VTIME] = vtime_ / 100; + t->c_cc[VMIN] = vmin_; + + debug_printf ("vmin_ %d, vtime_ %d", vmin_, vtime_); + + return 0; +} + +void +fhandler_serial::fixup_after_fork (HANDLE parent) +{ + if (get_close_on_exec ()) + this->fhandler_base::fixup_after_fork (parent); + overlapped_setup (); + debug_printf ("io_status.hEvent %p", io_status.hEvent); +} + +void +fhandler_serial::fixup_after_exec (HANDLE) +{ + overlapped_setup (); + debug_printf ("io_status.hEvent %p", io_status.hEvent); + return; +} + +int +fhandler_serial::dup (fhandler_base *child) +{ + fhandler_serial *fhc = (fhandler_serial *) child; + overlapped_setup (); + fhc->vmin_ = vmin_; + fhc->vtime_ = vtime_; + return fhandler_base::dup (child); +} diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc new file mode 100644 index 00000000000..27fa7101278 --- /dev/null +++ b/winsup/cygwin/fhandler_socket.cc @@ -0,0 +1,1264 @@ +/* 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 <errno.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 "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" +#include "wsock_event.h" +#include <unistd.h> + +#define SECRET_EVENT_NAME "cygwin.local_socket.secret.%d.%08x-%08x-%08x-%08x" +#define ENTROPY_SOURCE_NAME "/dev/urandom" + +extern fhandler_socket *fdsock (int& fd, const char *name, SOCKET soc); +extern "C" { +int sscanf (const char *, const char *, ...); +} /* End of "C" section */ + +fhandler_dev_random* entropy_source; + +/* 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) + { + int fd = open (in->sa_data, O_RDONLY); + if (fd == -1) + return 0; + + int ret = 0; + char buf[128]; + memset (buf, 0, sizeof buf); + if (read (fd, buf, sizeof buf) != -1) + { + 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; + } + close (fd); + 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)); +} + +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 (NULL, 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) +{ + char buf [128]; + int* secret_ptr = (secret ? : connect_secret); + struct sockaddr_in sin; + int sin_len = sizeof (sin); + + if (::getsockname (get_socket (), (struct sockaddr*) &sin, &sin_len)) + { + debug_printf ("error getting local socket name (%d)", WSAGetLastError ()); + return NULL; + } + + __small_sprintf (buf, SECRET_EVENT_NAME, sin.sin_port, + secret_ptr [0], secret_ptr [1], + secret_ptr [2], secret_ptr [3]); + LPSECURITY_ATTRIBUTES sec = get_inheritance (true); + secret_event = CreateEvent (sec, FALSE, FALSE, buf); + if (!secret_event && GetLastError () == ERROR_ALREADY_EXISTS) + secret_event = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf); + + if (!secret_event) + /* nothing to do */; + else if (sec == &sec_all_nih || sec == &sec_none_nih) + ProtectHandle (secret_event); + else + ProtectHandleINH (secret_event); + + 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) + ForceCloseHandle (secret_event); + secret_event = NULL; +} + +int +fhandler_socket::check_peer_secret_event (struct sockaddr_in* peer, int* secret) +{ + char buf [128]; + HANDLE ev; + int* secret_ptr = (secret ? : connect_secret); + + __small_sprintf (buf, SECRET_EVENT_NAME, peer->sin_port, + secret_ptr [0], secret_ptr [1], + secret_ptr [2], secret_ptr [3]); + ev = CreateEvent (&sec_all_nih, FALSE, FALSE, buf); + if (!ev && GetLastError () == ERROR_ALREADY_EXISTS) + { + debug_printf ("event \"%s\" already exists", buf); + ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf); + } + + 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_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; + fhs->set_io_handle (get_io_handle ()); + if (get_addr_family () == AF_LOCAL) + fhs->set_sun_path (get_sun_path ()); + + fhs->fixup_before_fork_exec (GetCurrentProcessId ()); + if (winsock2_active) + { + fhs->fixup_after_fork (hMainProc); + return 0; + } + return fhandler_base::dup (child); +} + +int __stdcall +fhandler_socket::fstat (struct __stat64 *buf, path_conv *pc) +{ + int res = fhandler_base::fstat (buf, pc); + if (!res) + { + buf->st_mode &= ~_IFMT; + buf->st_mode |= _IFSOCK; + buf->st_ino = (ino_t) get_handle (); + } + 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; + int fd; + + 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); + + /* bind must fail if file system socket object already exists + so _open () is called with O_EXCL flag. */ + fd = ::open (un_addr->sun_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0); + if (fd < 0) + { + if (get_errno () == EEXIST) + set_errno (EADDRINUSE); + goto out; + } + + 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')); + len = strlen (buf) + 1; + + /* Note that the terminating nul is written. */ + if (::write (fd, buf, len) != len) + { + save_errno here; + ::close (fd); + unlink (un_addr->sun_path); + } + else + { + ::close (fd); + chmod (un_addr->sun_path, + (S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask); + 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]; + + 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 ()) + { + DWORD err = WSAGetLastError (); + if (err == WSAEWOULDBLOCK || err == WSAEALREADY) + { + WSASetLastError (WSAEINPROGRESS); + in_progress = TRUE; + } + 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; + } + } + + if (WSAGetLastError () == WSAEINPROGRESS) + 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; + WSAEVENT ev[2] = { WSA_INVALID_EVENT, signal_arrived }; + 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); + + if (!is_nonblocking ()) + { + ev[0] = WSACreateEvent (); + + if (ev[0] != WSA_INVALID_EVENT && + !WSAEventSelect (get_socket (), ev[0], FD_ACCEPT)) + { + WSANETWORKEVENTS sock_event; + int wait_result; + + wait_result = WSAWaitForMultipleEvents (2, ev, FALSE, WSA_INFINITE, + FALSE); + if (wait_result == WSA_WAIT_EVENT_0) + WSAEnumNetworkEvents (get_socket (), ev[0], &sock_event); + + /* Unset events for listening socket and + switch back to blocking mode */ + WSAEventSelect (get_socket (), ev[0], 0); + unsigned long nonblocking = 0; + ioctlsocket (get_socket (), FIONBIO, &nonblocking); + + switch (wait_result) + { + case WSA_WAIT_EVENT_0: + if (sock_event.lNetworkEvents & FD_ACCEPT) + { + if (sock_event.iErrorCode[FD_ACCEPT_BIT]) + { + WSASetLastError (sock_event.iErrorCode[FD_ACCEPT_BIT]); + set_winsock_errno (); + res = -1; + goto done; + } + } + /* else; : Should never happen since FD_ACCEPT is the only event + that has been selected */ + break; + case WSA_WAIT_EVENT_0 + 1: + debug_printf ("signal received during accept"); + set_errno (EINTR); + res = -1; + goto done; + case WSA_WAIT_FAILED: + default: /* Should never happen */ + WSASetLastError (WSAEFAULT); + set_winsock_errno (); + res = -1; + goto done; + } + } + } + + res = ::accept (get_socket (), peer, len); + + if ((SOCKET) res == (SOCKET) INVALID_SOCKET && + WSAGetLastError () == WSAEWOULDBLOCK) + in_progress = TRUE; + + if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) + { + if ((SOCKET) res != (SOCKET) 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 != (SOCKET) 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 != (SOCKET) INVALID_SOCKET) + closesocket (res); + set_errno (ECONNABORTED); + res = -1; + goto done; + } + } + + { + cygheap_fdnew res_fd; + if (res_fd < 0) + /* FIXME: what is correct errno? */; + else if ((SOCKET) res == (SOCKET) INVALID_SOCKET) + set_winsock_errno (); + else + { + fhandler_socket* res_fh = fdsock (res_fd, get_name (), res); + if (get_addr_family () == AF_LOCAL) + res_fh->set_sun_path (get_sun_path ()); + res_fh->set_addr_family (get_addr_family ()); + res_fh->set_socket_type (get_socket_type ()); + res = res_fd; + } + } + +done: + if (ev[0] != WSA_INVALID_EVENT) + WSACloseEvent (ev[0]); + + 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) + { + 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]; + + { + 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 = 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) + { + 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 SIGPIPE */ + if (res == -1 && get_errno () == ESHUTDOWN) + { + 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; /* seconds. 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 (!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_tape.cc b/winsup/cygwin/fhandler_tape.cc new file mode 100644 index 00000000000..7affc5524b9 --- /dev/null +++ b/winsup/cygwin/fhandler_tape.cc @@ -0,0 +1,839 @@ +/* fhandler_tape.cc. See fhandler.h for a description of the fhandler + classes. + + Copyright 1999, 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. */ + +#include "winsup.h" +#include <sys/termios.h> +#include <errno.h> +#include <unistd.h> +#include <sys/mtio.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" + +/**********************************************************************/ +/* fhandler_dev_tape */ + +void +fhandler_dev_tape::clear (void) +{ + lasterr = 0; + fhandler_dev_raw::clear (); +} + +int +fhandler_dev_tape::is_eom (int win_error) +{ + int ret = ((win_error == ERROR_END_OF_MEDIA) + || (win_error == ERROR_EOM_OVERFLOW) + || (win_error == ERROR_NO_DATA_DETECTED)); + if (ret) + debug_printf ("end of medium"); + return ret; +} + +int +fhandler_dev_tape::is_eof (int win_error) +{ + int ret = ((win_error == ERROR_FILEMARK_DETECTED) + || (win_error == ERROR_SETMARK_DETECTED)); + if (ret) + debug_printf ("end of file"); + return ret; +} + +fhandler_dev_tape::fhandler_dev_tape () + : fhandler_dev_raw () +{ + debug_printf ("unit: %d", dev.minor); +} + +int +fhandler_dev_tape::open (path_conv *real_path, int flags, mode_t) +{ + int ret; + + devbufsiz = 1L; + + ret = fhandler_dev_raw::open (real_path, flags); + if (ret) + { + struct mtget get; + struct mtop op; + struct mtpos pos; + + if (!ioctl (MTIOCGET, &get)) + /* Tape drive supports and is set to variable block size. */ + if (get.mt_dsreg == 0) + devbufsiz = get.mt_maxblksize; + else + devbufsiz = get.mt_dsreg; + varblkop = get.mt_dsreg == 0; + + if (devbufsiz > 1L) + devbuf = new char [devbufsiz]; + + /* The following rewind in position 0 solves a problem which appears + * in case of multi volume archives: The last ReadFile on first medium + * returns ERROR_NO_DATA_DETECTED. After media change, all subsequent + * ReadFile calls return ERROR_NO_DATA_DETECTED, too. + * The call to tape_set_pos seems to reset some internal flags. */ + if ((!ioctl (MTIOCPOS, &pos)) && (!pos.mt_blkno)) + { + op.mt_op = MTREW; + ioctl (MTIOCTOP, &op); + } + + if (flags & O_APPEND) + { + /* In append mode, seek to beginning of next filemark */ + op.mt_op = MTFSFM; + op.mt_count = 1; + ioctl (MTIOCTOP, &op); + } + } + + return ret; +} + +int +fhandler_dev_tape::close (void) +{ + struct mtop op; + int ret = 0; + + if (is_writing) + { + ret = writebuf (); + if ((has_written) && (! eom_detected)) + { + /* if last operation was writing, write a filemark */ + debug_printf ("writing filemark"); + op.mt_op = MTWEOF; + op.mt_count = 1; + ioctl (MTIOCTOP, &op); + } + } + + // To protected reads on signaling (e.g. Ctrl-C) + eof_detected = 1; + + if (is_rewind_device ()) + { + debug_printf ("rewinding"); + op.mt_op = MTREW; + ioctl (MTIOCTOP, &op); + } + + if (ret) + { + fhandler_dev_raw::close (); + return ret; + } + + return fhandler_dev_raw::close (); +} + +int +fhandler_dev_tape::fstat (struct __stat64 *buf, path_conv *pc) +{ + int ret; + + if (!(ret = fhandler_base::fstat (buf, pc))) + { + struct mtget get; + + if (!ioctl (MTIOCGET, &get)) + buf->st_blocks = get.mt_capacity / buf->st_blksize; + } + + return ret; +} + +__off64_t +fhandler_dev_tape::lseek (__off64_t offset, int whence) +{ + struct mtop op; + struct mtpos pos; + + debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence); + + writebuf (); + eom_detected = eof_detected = 0; + lastblk_to_read = 0; + devbufstart = devbufend = 0; + + if (ioctl (MTIOCPOS, &pos)) + { + return ILLEGAL_SEEK; + } + + switch (whence) + { + case SEEK_END: + op.mt_op = MTFSF; + op.mt_count = 1; + if (ioctl (MTIOCTOP, &op)) + return -1; + break; + case SEEK_SET: + if (whence == SEEK_SET && offset < 0) + { + set_errno (EINVAL); + return -1; + } + break; + case SEEK_CUR: + break; + default: + set_errno (EINVAL); + return -1; + } + + op.mt_op = MTFSR; + op.mt_count = offset / devbufsiz + - (whence == SEEK_SET ? pos.mt_blkno : 0); + + if (op.mt_count < 0) + { + op.mt_op = MTBSR; + op.mt_count = -op.mt_count; + } + + if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos)) + return -1; + + return (pos.mt_blkno * devbufsiz); +} + +int +fhandler_dev_tape::dup (fhandler_base *child) +{ + fhandler_dev_tape *fhc = (fhandler_dev_tape *) child; + + fhc->lasterr = lasterr; + return fhandler_dev_raw::dup (child); +} + +int +fhandler_dev_tape::ioctl (unsigned int cmd, void *buf) +{ + int ret = NO_ERROR; + unsigned long block; + + if (cmd == MTIOCTOP) + { + struct mtop *op = (struct mtop *) buf; + + if (! op) + ret = ERROR_INVALID_PARAMETER; + else + switch (op->mt_op) + { + case MTRESET: + break; + case MTFSF: + ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count); + break; + case MTBSF: + ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count); + break; + case MTFSR: + ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count); + break; + case MTBSR: + ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count); + break; + case MTWEOF: + if (tape_get_feature (TAPE_DRIVE_WRITE_FILEMARKS)) + ret = tape_write_marks (TAPE_FILEMARKS, op->mt_count); + else if (tape_get_feature (TAPE_DRIVE_WRITE_LONG_FMKS)) + ret = tape_write_marks (TAPE_LONG_FILEMARKS, op->mt_count); + else + ret = tape_write_marks (TAPE_SHORT_FILEMARKS, op->mt_count); + break; + case MTREW: + ret = tape_set_pos (TAPE_REWIND, 0); + break; + case MTOFFL: + ret = tape_prepare (TAPE_UNLOAD); + break; + case MTNOP: + break; + case MTRETEN: + if (! tape_get_feature (TAPE_DRIVE_END_OF_DATA)) + ret = ERROR_INVALID_PARAMETER; + else if (! (ret = tape_set_pos (TAPE_REWIND, 0, FALSE))) + ret = tape_prepare (TAPE_TENSION); + break; + case MTBSFM: + ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count, TRUE); + break; + case MTFSFM: + ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count, TRUE); + break; + case MTEOM: + if (tape_get_feature (TAPE_DRIVE_END_OF_DATA)) + ret = tape_set_pos (TAPE_SPACE_END_OF_DATA, 0); + else + ret = tape_set_pos (TAPE_SPACE_FILEMARKS, 32767); + break; + case MTERASE: + ret = tape_erase (TAPE_ERASE_SHORT); + break; + case MTRAS1: + case MTRAS2: + case MTRAS3: + ret = ERROR_INVALID_PARAMETER; + break; + case MTSETBLK: + { + long min, max; + + if (! tape_get_feature (TAPE_DRIVE_SET_BLOCK_SIZE)) + { + ret = ERROR_INVALID_PARAMETER; + break; + } + ret = tape_get_blocksize (&min, NULL, &max, NULL); + if (ret) + break; + if (devbuf && (size_t) op->mt_count == devbufsiz && !varblkop) + { + ret = 0; + break; + } + if ((op->mt_count == 0 + && !tape_get_feature (TAPE_DRIVE_VARIABLE_BLOCK)) + || (op->mt_count > 0 + && (op->mt_count < min || op->mt_count > max))) + { + ret = ERROR_INVALID_PARAMETER; + break; + } + if (devbuf && op->mt_count > 0 + && (size_t) op->mt_count < devbufend - devbufstart) + { + ret = ERROR_MORE_DATA; + break; + } + if (! (ret = tape_set_blocksize (op->mt_count))) + { + size_t size = 0; + if (op->mt_count == 0) + { + struct mtget get; + if ((ret = tape_status (&get)) != NO_ERROR) + break; + size = get.mt_maxblksize; + ret = NO_ERROR; + } + char *buf = NULL; + if (size > 1L && !(buf = new char [size])) + { + ret = ERROR_OUTOFMEMORY; + break; + } + if (devbufsiz > 1L && size > 1L) + { + memcpy (buf, devbuf + devbufstart, + devbufend - devbufstart); + devbufend -= devbufstart; + } + else + devbufend = 0; + if (devbufsiz > 1L) + delete [] devbuf; + devbufstart = 0; + devbuf = buf; + devbufsiz = size; + varblkop = op->mt_count == 0; + } + } + break; + case MTSETDENSITY: + ret = ERROR_INVALID_PARAMETER; + break; + case MTSEEK: + if (tape_get_feature (TAPE_DRIVE_ABSOLUTE_BLK)) + { + ret = tape_set_pos (TAPE_ABSOLUTE_BLOCK, op->mt_count); + break; + } + if (! (ret = tape_get_pos (&block))) + { + ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, + op->mt_count - block); + } + break; + case MTTELL: + if (! (ret = tape_get_pos (&block))) + op->mt_count = block; + break; + case MTSETDRVBUFFER: + ret = ERROR_INVALID_PARAMETER; + break; + case MTFSS: + ret = tape_set_pos (TAPE_SPACE_SETMARKS, op->mt_count); + break; + case MTBSS: + ret = tape_set_pos (TAPE_SPACE_SETMARKS, -op->mt_count); + break; + case MTWSM: + ret = tape_write_marks (TAPE_SETMARKS, op->mt_count); + break; + case MTLOCK: + ret = tape_prepare (TAPE_LOCK); + break; + case MTUNLOCK: + ret = tape_prepare (TAPE_UNLOCK); + break; + case MTLOAD: + ret = tape_prepare (TAPE_LOAD); + break; + case MTUNLOAD: + ret = tape_prepare (TAPE_UNLOAD); + break; + case MTCOMPRESSION: + ret = tape_compression (op->mt_count); + break; + case MTSETPART: + case MTMKPART: + default: + ret = ERROR_INVALID_PARAMETER; + break; + } + } + else if (cmd == MTIOCGET) + ret = tape_status ((struct mtget *) buf); + else if (cmd == MTIOCPOS) + { + ret = ERROR_INVALID_PARAMETER; + if (buf && (ret = tape_get_pos (&block))) + ((struct mtpos *) buf)->mt_blkno = block; + } + else + return fhandler_dev_raw::ioctl (cmd, buf); + + if (ret != NO_ERROR) + { + SetLastError (ret); + __seterrno (); + return -1; + } + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Private functions used by `ioctl' */ +/* ------------------------------------------------------------------ */ + +static int +tape_error (DWORD lasterr, const char *txt) +{ + if (lasterr) + debug_printf ("%s: error: %d", txt, lasterr); + + return lasterr; +} + +int +fhandler_dev_tape::tape_write_marks (int marktype, DWORD len) +{ + syscall_printf ("write_tapemark"); + while (((lasterr = WriteTapemark (get_handle (), + marktype, + len, + FALSE)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + return tape_error (lasterr, "tape_write_marks"); +} + +int +fhandler_dev_tape::tape_get_pos (unsigned long *ret) +{ + DWORD part, low, high; + + while (((lasterr = GetTapePosition (get_handle (), + TAPE_ABSOLUTE_POSITION, + &part, + &low, + &high)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + if (! tape_error (lasterr, "tape_get_pos") && ret) + *ret = low; + + return lasterr; +} + +static int _tape_set_pos (HANDLE hTape, int mode, long count) +{ + int err; + + while (((err = SetTapePosition (hTape, + mode, + 1, + count, + count < 0 ? -1 : 0, + FALSE)) == ERROR_MEDIA_CHANGED) + || (err == ERROR_BUS_RESET)) + ; + + return err; +} + +int +fhandler_dev_tape::tape_set_pos (int mode, long count, BOOLEAN sfm_func) +{ + unsigned long pos, tgtpos; + + switch (mode) + { + case TAPE_SPACE_RELATIVE_BLOCKS: + lasterr = tape_get_pos (&pos); + + if (lasterr) + return lasterr; + + tgtpos = pos + count; + + while (((lasterr = _tape_set_pos (get_handle (), + mode, + count)) == ERROR_FILEMARK_DETECTED) + || (lasterr == ERROR_SETMARK_DETECTED)) + { + lasterr = tape_get_pos (&pos); + if (lasterr) + return lasterr; + count = tgtpos - pos; + } + + if (lasterr == ERROR_BEGINNING_OF_MEDIA && ! tgtpos) + lasterr = NO_ERROR; + + break; + case TAPE_SPACE_FILEMARKS: + if (count < 0) + { + if (pos > 0) + { + if ((! _tape_set_pos (get_handle (), + TAPE_SPACE_RELATIVE_BLOCKS, + -1)) + || (sfm_func)) + ++count; + _tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, 1); + } + + while (! (lasterr = _tape_set_pos (get_handle (), mode, -1)) + && count++ < 0) + ; + + if (lasterr == ERROR_BEGINNING_OF_MEDIA) + { + if (! count) + lasterr = NO_ERROR; + } + else if (! sfm_func) + lasterr = _tape_set_pos (get_handle (), mode, 1); + } + else + { + if (sfm_func) + { + if (_tape_set_pos (get_handle (), + TAPE_SPACE_RELATIVE_BLOCKS, + 1) == ERROR_FILEMARK_DETECTED) + ++count; + _tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, -1); + } + + if (! (lasterr = _tape_set_pos (get_handle (), mode, count)) + && sfm_func) + lasterr = _tape_set_pos (get_handle (), mode, -1); + } + break; + case TAPE_SPACE_SETMARKS: + case TAPE_ABSOLUTE_BLOCK: + case TAPE_SPACE_END_OF_DATA: + case TAPE_REWIND: + lasterr = _tape_set_pos (get_handle (), mode, count); + break; + } + + return tape_error (lasterr, "tape_set_pos"); +} + +int +fhandler_dev_tape::tape_erase (int mode) +{ + DWORD varlen; + TAPE_GET_DRIVE_PARAMETERS dp; + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_DRIVE_INFORMATION, + (varlen = sizeof dp, &varlen), + &dp)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + switch (mode) + { + case TAPE_ERASE_SHORT: + if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_SHORT)) + mode = TAPE_ERASE_LONG; + break; + case TAPE_ERASE_LONG: + if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_LONG)) + mode = TAPE_ERASE_SHORT; + break; + } + + return tape_error (EraseTape (get_handle (), mode, FALSE), "tape_erase"); +} + +int +fhandler_dev_tape::tape_prepare (int action) +{ + while (((lasterr = PrepareTape (get_handle (), + action, + FALSE)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + return tape_error (lasterr, "tape_prepare"); +} + +BOOLEAN +fhandler_dev_tape::tape_get_feature (DWORD parm) +{ + DWORD varlen; + TAPE_GET_DRIVE_PARAMETERS dp; + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_DRIVE_INFORMATION, + (varlen = sizeof dp, &varlen), + &dp)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + if (lasterr) + return FALSE; + + return ((parm & TAPE_DRIVE_HIGH_FEATURES) + ? ((dp.FeaturesHigh & parm) != 0) + : ((dp.FeaturesLow & parm) != 0)); +} + +int +fhandler_dev_tape::tape_get_blocksize (long *min, long *def, long *max, long *cur) +{ + DWORD varlen; + TAPE_GET_DRIVE_PARAMETERS dp; + TAPE_GET_MEDIA_PARAMETERS mp; + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_DRIVE_INFORMATION, + (varlen = sizeof dp, &varlen), + &dp)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + if (lasterr) + return tape_error (lasterr, "tape_get_blocksize"); + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_MEDIA_INFORMATION, + (varlen = sizeof dp, &varlen), + &mp)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + if (lasterr) + return tape_error (lasterr, "tape_get_blocksize"); + + if (min) + *min = (long) dp.MinimumBlockSize; + if (def) + *def = (long) dp.DefaultBlockSize; + if (max) + *max = (long) dp.MaximumBlockSize; + if (cur) + *cur = (long) mp.BlockSize; + + return tape_error (lasterr, "tape_get_blocksize"); +} + +int +fhandler_dev_tape::tape_set_blocksize (long count) +{ + long min, max; + TAPE_SET_MEDIA_PARAMETERS mp; + + lasterr = tape_get_blocksize (&min, NULL, &max, NULL); + + if (lasterr) + return lasterr; + + if (count != 0 && (count < min || count > max)) + return tape_error (ERROR_INVALID_PARAMETER, "tape_set_blocksize"); + + mp.BlockSize = count; + + return tape_error (SetTapeParameters (get_handle (), + SET_TAPE_MEDIA_INFORMATION, + &mp), + "tape_set_blocksize"); +} + +static long long +get_ll (PLARGE_INTEGER i) +{ + long long l = 0; + + l = i->HighPart; + l <<= 32; + l |= i->LowPart; + return l; +} + +int +fhandler_dev_tape::tape_status (struct mtget *get) +{ + DWORD varlen; + TAPE_GET_DRIVE_PARAMETERS dp; + TAPE_GET_MEDIA_PARAMETERS mp; + int notape = 0; + + if (! get) + return ERROR_INVALID_PARAMETER; + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_DRIVE_INFORMATION, + (varlen = sizeof dp, &varlen), + &dp)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + /* Setting varlen to sizeof DP is by intention, actually! Never set + it to sizeof MP which seems to be more correct but results in a + ERROR_MORE_DATA error at least on W2K. */ + if ((lasterr) || (lasterr = GetTapeParameters (get_handle (), + GET_TAPE_MEDIA_INFORMATION, + (varlen = sizeof dp, &varlen), + &mp))) + notape = 1; + + memset (get, 0, sizeof *get); + + get->mt_type = MT_ISUNKNOWN; + + if (! notape && (dp.FeaturesLow & TAPE_DRIVE_TAPE_REMAINING)) + { + get->mt_remaining = get_ll (&mp.Remaining); + get->mt_resid = get->mt_remaining >> 10; + } + + if ((dp.FeaturesHigh & TAPE_DRIVE_SET_BLOCK_SIZE) && ! notape) + get->mt_dsreg = mp.BlockSize; + else + get->mt_dsreg = dp.DefaultBlockSize; + + if (notape) + get->mt_gstat |= GMT_DR_OPEN (-1); + + if (! notape) + { + if (dp.FeaturesLow & TAPE_DRIVE_GET_ABSOLUTE_BLK) + tape_get_pos ((unsigned long *) &get->mt_blkno); + + if (! get->mt_blkno) + get->mt_gstat |= GMT_BOT (-1); + + get->mt_gstat |= GMT_ONLINE (-1); + + if ((dp.FeaturesLow & TAPE_DRIVE_WRITE_PROTECT) && mp.WriteProtected) + get->mt_gstat |= GMT_WR_PROT (-1); + + if (dp.FeaturesLow & TAPE_DRIVE_TAPE_CAPACITY) + get->mt_capacity = get_ll (&mp.Capacity); + } + + if ((dp.FeaturesLow & TAPE_DRIVE_COMPRESSION) && dp.Compression) + get->mt_gstat |= GMT_HW_COMP (-1); + + if ((dp.FeaturesLow & TAPE_DRIVE_ECC) && dp.ECC) + get->mt_gstat |= GMT_HW_ECC (-1); + + if ((dp.FeaturesLow & TAPE_DRIVE_PADDING) && dp.DataPadding) + get->mt_gstat |= GMT_PADDING (-1); + + if ((dp.FeaturesLow & TAPE_DRIVE_REPORT_SMKS) && dp.ReportSetmarks) + get->mt_gstat |= GMT_IM_REP_EN (-1); + + get->mt_erreg = lasterr; + + get->mt_minblksize = dp.MinimumBlockSize; + get->mt_maxblksize = dp.MaximumBlockSize; + get->mt_defblksize = dp.DefaultBlockSize; + get->mt_featureslow = dp.FeaturesLow; + get->mt_featureshigh = dp.FeaturesHigh; + get->mt_eotwarningzonesize = dp.EOTWarningZoneSize; + + return 0; +} + +int +fhandler_dev_tape::tape_compression (long count) +{ + DWORD varlen; + TAPE_GET_DRIVE_PARAMETERS dpg; + TAPE_SET_DRIVE_PARAMETERS dps; + + while (((lasterr = GetTapeParameters (get_handle (), + GET_TAPE_DRIVE_INFORMATION, + (varlen = sizeof dpg, &varlen), + &dpg)) == ERROR_MEDIA_CHANGED) + || (lasterr == ERROR_BUS_RESET)) + ; + + if (lasterr) + return tape_error (lasterr, "tape_compression"); + + if (! (dpg.FeaturesLow & TAPE_DRIVE_COMPRESSION)) + return ERROR_INVALID_PARAMETER; + + if (count) + { + dps.ECC = dpg.ECC; + dps.Compression = count ? TRUE : FALSE; + dps.DataPadding = dpg.DataPadding; + dps.ReportSetmarks = dpg.ReportSetmarks; + dps.EOTWarningZoneSize = dpg.EOTWarningZoneSize; + lasterr = SetTapeParameters (get_handle (), + SET_TAPE_DRIVE_INFORMATION, + &dps); + + if (lasterr) + return tape_error (lasterr, "tape_compression"); + + dpg.Compression = dps.Compression; + } + + return 0; +} + diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc new file mode 100644 index 00000000000..dd55ee22cc6 --- /dev/null +++ b/winsup/cygwin/fhandler_tty.cc @@ -0,0 +1,1261 @@ +/* fhandler_tty.cc + + Copyright 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <limits.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "sigproc.h" +#include "pinfo.h" +#include "cygheap.h" +#include "shared_info.h" +#include "cygwin/cygserver.h" +#include "cygthread.h" + +/* Tty master stuff */ + +fhandler_tty_master NO_COPY *tty_master; + +static DWORD WINAPI process_input (void *); // Input queue thread +static DWORD WINAPI process_output (void *); // Output queue thread +static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread + +fhandler_tty_master::fhandler_tty_master () + : fhandler_pty_master (), console (NULL) +{ +} + +int +fhandler_tty_slave::get_unit () +{ + return dev == FH_TTY ? myself->ctty : dev.minor; +} + +void +fhandler_tty_master::set_winsize (bool sendSIGWINCH) +{ + winsize w; + console->ioctl (TIOCGWINSZ, &w); + get_ttyp ()->winsize = w; + if (sendSIGWINCH) + tc->kill_pgrp (SIGWINCH); +} + +int +fhandler_tty_master::init () +{ + termios_printf ("Creating master for tty%d", get_unit ()); + + slave = dev; + + if (init_console ()) + { + termios_printf ("can't create fhandler"); + return -1; + } + + termios ti; + memset (&ti, 0, sizeof (ti)); + console->tcsetattr (0, &ti); + + cygwin_shared->tty[get_unit ()]->common_init (this); + + set_winsize (false); + + inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE); + + cygthread *h; + h = new cygthread (process_input, cygself, "ttyin"); + h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); + h->zap_h (); + + h = new cygthread (process_ioctl, cygself, "ttyioctl"); + h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); + h->zap_h (); + + h = new cygthread (process_output, cygself, "ttyout"); + h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); + h->zap_h (); + + return 0; +} + +#ifdef DEBUGGING +static class mutex_stack +{ +public: + const char *fn; + int ln; + const char *tname; +} ostack[100]; + +static int osi; +#endif /*DEBUGGING*/ + +DWORD +fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln, + DWORD ms) +{ + if (strace.active) + strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms); + DWORD res = WaitForSingleObject (output_mutex, ms); + if (res == WAIT_OBJECT_0) + { +#ifndef DEBUGGING + if (strace.active) + strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res); +#else + ostack[osi].fn = fn; + ostack[osi].ln = ln; + ostack[osi].tname = cygthread::name (); + termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi); + osi++; +#endif + } + return res; +} + +void +fhandler_tty_common::__release_output_mutex (const char *fn, int ln) +{ + if (ReleaseMutex (output_mutex)) + { +#ifndef DEBUGGING + if (strace.active) + strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln); +#else + if (osi > 0) + osi--; + termios_printf ("released at %s:%d, osi %d", fn, ln, osi); + termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname); + ostack[osi].ln = -ln; +#endif + } +} + +/* Process tty input. */ + +void +fhandler_pty_master::doecho (const void *str, DWORD len) +{ + acquire_output_mutex (INFINITE); + if (!WriteFile (get_ttyp ()->to_master, str, len, &len, NULL)) + termios_printf ("Write to %p failed, %E", get_ttyp ()->to_master); +// WaitForSingleObject (output_done_event, INFINITE); + release_output_mutex (); +} + +int +fhandler_pty_master::accept_input () +{ + DWORD bytes_left; + int ret = 1; + + (void) WaitForSingleObject (input_mutex, INFINITE); + + bytes_left = eat_readahead (-1); + + if (!bytes_left) + { + termios_printf ("sending EOF to slave"); + get_ttyp ()->read_retval = 0; + } + else + { + char *p = rabuf; + DWORD rc; + DWORD written = 0; + + termios_printf ("about to write %d chars to slave", bytes_left); + rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL); + if (!rc) + { + debug_printf ("error writing to pipe %E"); + get_ttyp ()->read_retval = -1; + ret = -1; + } + else + { + get_ttyp ()->read_retval = 1; + p += written; + bytes_left -= written; + if (bytes_left > 0) + { + debug_printf ("to_slave pipe is full"); + puts_readahead (p, bytes_left); + ret = 0; + } + } + } + + SetEvent (input_available_event); + ReleaseMutex (input_mutex); + return ret; +} + +static DWORD WINAPI +process_input (void *) +{ + char rawbuf[INP_BUFFER_SIZE]; + + while (1) + { + size_t nraw = INP_BUFFER_SIZE; + tty_master->console->read ((void *) rawbuf, nraw); + (void) tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti); + } +} + +bool +fhandler_pty_master::hit_eof () +{ + if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ()) + { + /* We have the only remaining open handle to this pty, and + the slave pty has been opened at least once. We treat + this as EOF. */ + termios_printf ("all other handles closed"); + return 1; + } + return 0; +} + +/* Process tty output requests */ + +int +fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on) +{ + size_t rlen; + char outbuf[OUT_BUFFER_SIZE + 1]; + DWORD n; + int column = 0; + int rc = 0; + + if (len == 0) + goto out; + + if (need_nl) + { + /* We need to return a left over \n character, resulting from + \r\n conversion. Note that we already checked for FLUSHO and + output_stopped at the time that we read the character, so we + don't check again here. */ + buf[0] = '\n'; + need_nl = 0; + rc = 1; + goto out; + } + + + for (;;) + { + /* Set RLEN to the number of bytes to read from the pipe. */ + rlen = len; + if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR) + { + /* We are going to expand \n to \r\n, so don't read more than + half of the number of bytes requested. */ + rlen /= 2; + if (rlen == 0) + rlen = 1; + } + if (rlen > sizeof outbuf) + rlen = sizeof outbuf; + + HANDLE handle = get_io_handle (); + + n = 0; // get_readahead_into_buffer (outbuf, len); + if (!n) + { + /* Doing a busy wait like this is quite inefficient, but nothing + else seems to work completely. Windows should provide some sort + of overlapped I/O for pipes, or something, but it doesn't. */ + while (1) + { + if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL)) + goto err; + if (n > 0) + break; + if (hit_eof ()) + goto out; + if (n == 0 && is_nonblocking ()) + { + set_errno (EAGAIN); + rc = -1; + break; + } + + Sleep (10); + } + + if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE) + goto err; + } + + termios_printf ("bytes read %u", n); + get_ttyp ()->write_error = 0; + if (output_done_event != NULL) + SetEvent (output_done_event); + + if (get_ttyp ()->ti.c_lflag & FLUSHO) + continue; + + char *optr; + optr = buf; + if (pktmode_on) + *optr++ = TIOCPKT_DATA; + + if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output + { + memcpy (optr, outbuf, n); + optr += n; + } + else // raw output mode + { + char *iptr = outbuf; + + while (n--) + { + switch (*iptr) + { + case '\r': + if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0) + { + iptr++; + continue; + } + if (get_ttyp ()->ti.c_oflag & OCRNL) + *iptr = '\n'; + else + column = 0; + break; + case '\n': + if (get_ttyp ()->ti.c_oflag & ONLCR) + { + *optr++ = '\r'; + column = 0; + } + if (get_ttyp ()->ti.c_oflag & ONLRET) + column = 0; + break; + default: + column++; + break; + } + + /* Don't store data past the end of the user's buffer. This + can happen if the user requests a read of 1 byte when + doing \r\n expansion. */ + if (optr - buf >= (int) len) + { + if (*iptr != '\n' || n != 0) + system_printf ("internal error: %d unexpected characters", n); + need_nl = 1; + break; + } + + *optr++ = *iptr++; + } + } + rc = optr - buf; + break; + + err: + if (GetLastError () == ERROR_BROKEN_PIPE) + rc = 0; + else + { + __seterrno (); + rc = -1; + } + break; + } + +out: + termios_printf ("returning %d", rc); + return rc; +} + +static DWORD WINAPI +process_output (void *) +{ + char buf[OUT_BUFFER_SIZE * 2]; + + for (;;) + { + int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0); + if (n <= 0) + { + if (n < 0) + termios_printf ("ReadFile %E"); + ExitThread (0); + } + n = tty_master->console->write ((void *) buf, (size_t) n); + tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0; + } +} + + +/* Process tty ioctl requests */ + +static DWORD WINAPI +process_ioctl (void *) +{ + while (1) + { + WaitForSingleObject (tty_master->ioctl_request_event, INFINITE); + termios_printf ("ioctl() request"); + tty_master->get_ttyp ()->ioctl_retval = + tty_master->console->ioctl (tty_master->get_ttyp ()->cmd, + (void *) &tty_master->get_ttyp ()->arg); + SetEvent (tty_master->ioctl_done_event); + } +} + +/**********************************************************************/ +/* Tty slave stuff */ + +fhandler_tty_slave::fhandler_tty_slave () + : fhandler_tty_common () +{ + set_r_no_interrupt (1); +} + +/* FIXME: This function needs to close handles when it has + a failing condition. */ +int +fhandler_tty_slave::open (path_conv *, int flags, mode_t) +{ + tcinit (cygwin_shared->tty[get_unit ()]); + + attach_tty (get_unit ()); + tc->set_ctty (get_unit (), flags); + + set_flags ((flags & ~O_TEXT) | O_BINARY); + /* Create synchronisation events */ + char buf[40]; + + /* output_done_event may or may not exist. It will exist if the tty + was opened by fhandler_tty_master::init, normally called at + startup if use_tty is non-zero. It will not exist if this is a + pty opened by fhandler_pty_master::open. In the former case, tty + output is handled by a separate thread which controls output. */ + __small_sprintf (buf, OUTPUT_DONE_EVENT, get_unit ()); + output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + + if (!(output_mutex = get_ttyp ()->open_output_mutex ())) + { + termios_printf ("open output mutex failed, %E"); + __seterrno (); + return 0; + } + if (!(input_mutex = get_ttyp ()->open_input_mutex ())) + { + termios_printf ("open input mutex failed, %E"); + __seterrno (); + return 0; + } + __small_sprintf (buf, INPUT_AVAILABLE_EVENT, get_unit ()); + if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf))) + { + termios_printf ("open input event failed, %E"); + __seterrno (); + return 0; + } + + /* The ioctl events may or may not exist. See output_done_event, + above. */ + __small_sprintf (buf, IOCTL_REQUEST_EVENT, get_unit ()); + ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + __small_sprintf (buf, IOCTL_DONE_EVENT, get_unit ()); + ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); + + /* FIXME: Needs a method to eliminate tty races */ + { + acquire_output_mutex (500); + inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE); + get_ttyp ()->was_opened = TRUE; + release_output_mutex (); + } + + /* Duplicate tty handles. */ + + if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave) + { + termios_printf ("tty handles have been closed"); + set_errno (EACCES); + return 0; + } + + HANDLE from_master_local, to_master_local; + + if (!wincap.has_security () || + cygserver_running == CYGSERVER_UNAVAIL || + !cygserver_attach_tty (&from_master_local, &to_master_local)) + { + termios_printf ("cannot dup handles via server. using old method."); + + HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, + get_ttyp ()->master_pid); + termios_printf ("tty own handle %p",tty_owner); + if (tty_owner == NULL) + { + termios_printf ("can't open tty (%d) handle process %d", + get_unit (), get_ttyp ()->master_pid); + __seterrno (); + return 0; + } + + if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master, + hMainProc, &from_master_local, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate input, %E"); + __seterrno (); + return 0; + } + + if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master, + hMainProc, &to_master_local, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate output, %E"); + __seterrno (); + return 0; + } + CloseHandle (tty_owner); + } + + termios_printf ("duplicated from_master %p->%p from tty_owner", + get_ttyp ()->from_master, from_master_local); + termios_printf ("duplicated to_master %p->%p from tty_owner", + get_ttyp ()->to_master, to_master_local); + + set_io_handle (from_master_local); + set_output_handle (to_master_local); + + set_open_status (); + termios_printf ("tty%d opened", get_unit ()); + + return 1; +} + +int +fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr, + LPHANDLE to_master_ptr) +{ + if (!from_master_ptr || !to_master_ptr) + return 0; + + client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid, + (HANDLE) get_ttyp ()->from_master, + (HANDLE) get_ttyp ()->to_master); + + if (req.make_request () == -1 || req.error_code ()) + return 0; + + *from_master_ptr = req.from_master (); + *to_master_ptr = req.to_master (); + return 1; +} + +void +fhandler_tty_slave::init (HANDLE, DWORD a, mode_t) +{ + int mode = 0; + + a &= GENERIC_READ | GENERIC_WRITE; + if (a == GENERIC_READ) + mode = O_RDONLY; + if (a == GENERIC_WRITE) + mode = O_WRONLY; + if (a == (GENERIC_READ | GENERIC_WRITE)) + mode = O_RDWR; + + open (0, mode); +} + +int +fhandler_tty_slave::write (const void *ptr, size_t len) +{ + DWORD n, towrite = len; + + termios_printf ("tty%d, write(%x, %d)", get_unit (), ptr, len); + + acquire_output_mutex (INFINITE); + + while (len) + { + n = min (OUT_BUFFER_SIZE, len); + char *buf = (char *)ptr; + ptr = (char *) ptr + n; + len -= n; + + /* Previous write may have set write_error to != 0. Check it here. + This is less than optimal, but the alternative slows down tty + writes enormously. */ + if (get_ttyp ()->write_error) + { + set_errno (get_ttyp ()->write_error); + towrite = (DWORD) -1; + break; + } + + if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE) + { + DWORD err = GetLastError (); + termios_printf ("WriteFile failed, %E"); + switch (err) + { + case ERROR_NO_DATA: + err = ERROR_IO_DEVICE; + default: + __seterrno_from_win_error (err); + } + raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ + towrite = (DWORD) -1; + break; + } + + if (output_done_event != NULL) + { + DWORD rc; + DWORD x = n * 1000; + rc = WaitForSingleObject (output_done_event, x); + termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc); + } + } + release_output_mutex (); + return towrite; +} + +void __stdcall +fhandler_tty_slave::read (void *ptr, size_t& len) +{ + int totalread = 0; + int vmin = 0; + int vtime = 0; /* Initialized to prevent -Wuninitialized warning */ + size_t readlen; + DWORD bytes_in_pipe; + char buf[INP_BUFFER_SIZE]; + char peek_buf[INP_BUFFER_SIZE]; + DWORD time_to_wait; + DWORD rc; + HANDLE w4[2]; + + termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ()); + + if ((get_ttyp ()->ti.c_lflag & ICANON)) + time_to_wait = INFINITE; + else + { + vmin = get_ttyp ()->ti.c_cc[VMIN]; + if (vmin > INP_BUFFER_SIZE) + vmin = INP_BUFFER_SIZE; + vtime = get_ttyp ()->ti.c_cc[VTIME]; + if (vmin < 0) + vmin = 0; + if (vtime < 0) + vtime = 0; + if (!vmin && !vtime) + time_to_wait = 0; + else + time_to_wait = !vtime ? INFINITE : 100 * vtime; + } + + w4[0] = signal_arrived; + w4[1] = input_available_event; + + DWORD waiter = INFINITE; + while (len) + { + rc = WaitForMultipleObjects (2, w4, FALSE, waiter); + + if (rc == WAIT_TIMEOUT) + break; + + if (rc == WAIT_FAILED) + { + termios_printf ("wait for input event failed, %E"); + break; + } + + if (rc == WAIT_OBJECT_0) + { + /* if we've received signal after successfully reading some data, + just return all data successfully read */ + if (totalread > 0) + break; + set_sig_errno (EINTR); + (ssize_t) len = -1; + return; + } + + rc = WaitForSingleObject (input_mutex, 1000); + if (rc == WAIT_FAILED) + { + termios_printf ("wait for input mutex failed, %E"); + break; + } + else if (rc == WAIT_TIMEOUT) + { + termios_printf ("failed to acquire input mutex after input event arrived"); + break; + } + if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL)) + { + termios_printf ("PeekNamedPipe failed, %E"); + raise (SIGHUP); + bytes_in_pipe = 0; + } + + if (!vmin && !time_to_wait) + { + ReleaseMutex (input_mutex); + (ssize_t) len = bytes_in_pipe; + return; + } + + readlen = min (bytes_in_pipe, min (len, sizeof (buf))); + + if (vmin && readlen > (unsigned) vmin) + readlen = vmin; + + DWORD n = 0; + if (readlen) + { + termios_printf ("reading %d bytes (vtime %d)", readlen, vtime); + if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE) + { + termios_printf ("read failed, %E"); + raise (SIGHUP); + } + /* MSDN states that 5th prameter can be used to determine total + number of bytes in pipe, but for some reason this number doesn't + change after successful read. So we have to peek into the pipe + again to see if input is still available */ + if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL)) + { + termios_printf ("PeekNamedPipe failed, %E"); + raise (SIGHUP); + bytes_in_pipe = 0; + } + if (n) + { + len -= n; + totalread += n; + memcpy (ptr, buf, n); + ptr = (char *) ptr + n; + } + } + + if (!bytes_in_pipe) + ResetEvent (input_available_event); + + ReleaseMutex (input_mutex); + + if (get_ttyp ()->read_retval < 0) // read error + { + set_errno (-get_ttyp ()->read_retval); + totalread = -1; + break; + } + if (get_ttyp ()->read_retval == 0) //EOF + { + termios_printf ("saw EOF"); + break; + } + if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ()) + break; + if (vmin && totalread >= vmin) + break; + + /* vmin == 0 && vtime == 0: + * we've already read all input, if any, so return immediately + * vmin == 0 && vtime > 0: + * we've waited for input 10*vtime ms in WFSO(input_available_event), + * no matter whether any input arrived, we shouldn't wait any longer, + * so return immediately + * vmin > 0 && vtime == 0: + * here, totalread < vmin, so continue waiting until more data + * arrive + * vmin > 0 && vtime > 0: + * similar to the previous here, totalread < vmin, and timer + * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT, + * so "restart timer" and wait until more data arrive + */ + + if (vmin == 0) + break; + + if (n) + waiter = time_to_wait; + } + termios_printf ("%d=read(%x, %d)", totalread, ptr, len); + (ssize_t) len = totalread; + return; +} + +int +fhandler_tty_common::dup (fhandler_base *child) +{ + fhandler_tty_slave *fts = (fhandler_tty_slave *) child; + int errind; + + fts->tcinit (get_ttyp ()); + + attach_tty (get_unit ()); + tc->set_ctty (get_unit (), openflags); + + HANDLE nh; + + if (output_done_event == NULL) + fts->output_done_event = NULL; + else if (!DuplicateHandle (hMainProc, output_done_event, hMainProc, + &fts->output_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 1; + goto err; + } + if (ioctl_request_event == NULL) + fts->ioctl_request_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_request_event, hMainProc, + &fts->ioctl_request_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 2; + goto err; + } + if (ioctl_done_event == NULL) + fts->ioctl_done_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_done_event, hMainProc, + &fts->ioctl_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 3; + goto err; + } + if (!DuplicateHandle (hMainProc, input_available_event, hMainProc, + &fts->input_available_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 4; + goto err; + } + if (!DuplicateHandle (hMainProc, output_mutex, hMainProc, + &fts->output_mutex, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 5; + goto err; + } + if (!DuplicateHandle (hMainProc, input_mutex, hMainProc, + &fts->input_mutex, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 6; + goto err; + } + if (!DuplicateHandle (hMainProc, get_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 7; + goto err; + } + fts->set_io_handle (nh); + + if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 8; + goto err; + } + fts->set_output_handle (nh); + + if (inuse == NULL) + fts->inuse = NULL; + else if (!DuplicateHandle (hMainProc, inuse, hMainProc, + &fts->inuse, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 9; + goto err; + } + return 0; + +err: + __seterrno (); + termios_printf ("dup %d failed in DuplicateHandle, %E", errind); + return -1; +} + +int +fhandler_tty_slave::tcgetattr (struct termios *t) +{ + *t = get_ttyp ()->ti; + return 0; +} + +int +fhandler_tty_slave::tcsetattr (int, const struct termios *t) +{ + acquire_output_mutex (INFINITE); + get_ttyp ()->ti = *t; + release_output_mutex (); + return 0; +} + +int +fhandler_tty_slave::tcflush (int) +{ + return 0; +} + +int +fhandler_tty_slave::ioctl (unsigned int cmd, void *arg) +{ + termios_printf ("ioctl (%x)", cmd); + + if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid + && myself->ctty == get_unit () && (get_ttyp ()->ti.c_lflag & TOSTOP)) + { + /* background process */ + termios_printf ("bg ioctl pgid %d, tpgid %d, ctty %d", + myself->pgid, get_ttyp ()->getpgid (), myself->ctty); + raise (SIGTTOU); + } + + switch (cmd) + { + case TIOCGWINSZ: + case TIOCSWINSZ: + break; + case FIONBIO: + set_nonblocking (*(int *) arg); + goto out; + default: + set_errno (EINVAL); + return -1; + } + + acquire_output_mutex (INFINITE); + + get_ttyp ()->cmd = cmd; + get_ttyp ()->ioctl_retval = 0; + switch (cmd) + { + case TIOCGWINSZ: + get_ttyp ()->arg.winsize = get_ttyp ()->winsize; + if (ioctl_request_event) + SetEvent (ioctl_request_event); + *(struct winsize *) arg = get_ttyp ()->arg.winsize; + if (ioctl_done_event) + WaitForSingleObject (ioctl_done_event, INFINITE); + get_ttyp ()->winsize = get_ttyp ()->arg.winsize; + break; + case TIOCSWINSZ: + if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row + || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) + { + get_ttyp ()->arg.winsize = *(struct winsize *) arg; + if (ioctl_request_event) + { + get_ttyp ()->ioctl_retval = -1; + SetEvent (ioctl_request_event); + } + else + { + get_ttyp ()->winsize = *(struct winsize *) arg; + kill (-get_ttyp ()->getpgid (), SIGWINCH); + } + if (ioctl_done_event) + WaitForSingleObject (ioctl_done_event, INFINITE); + } + break; + } + + release_output_mutex (); + +out: + termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd); + return get_ttyp ()->ioctl_retval; +} + +/******************************************************* + fhandler_pty_master +*/ +fhandler_pty_master::fhandler_pty_master () + : fhandler_tty_common () +{ +} + +int +fhandler_pty_master::open (path_conv *, int flags, mode_t) +{ + int ntty = cygwin_shared->tty.allocate_tty (0); + if (ntty < 0) + return 0; + + slave = *ttys_dev; + slave.setunit (ntty); + cygwin_shared->tty[ntty]->common_init (this); + inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE); + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); + + termios_printf ("opened pty master tty%d", get_unit ()); + return 1; +} + +int +fhandler_tty_common::close () +{ + if (output_done_event && !CloseHandle (output_done_event)) + termios_printf ("CloseHandle (output_done_event), %E"); + if (ioctl_done_event && !CloseHandle (ioctl_done_event)) + termios_printf ("CloseHandle (ioctl_done_event), %E"); + if (ioctl_request_event && !CloseHandle (ioctl_request_event)) + termios_printf ("CloseHandle (ioctl_request_event), %E"); + if (inuse && !CloseHandle (inuse)) + termios_printf ("CloseHandle (inuse), %E"); + if (!ForceCloseHandle (input_mutex)) + termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex); + if (!ForceCloseHandle (output_mutex)) + termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex); + + /* Send EOF to slaves if master side is closed */ + if (!get_ttyp ()->master_alive ()) + { + termios_printf ("no more masters left. sending EOF" ); + SetEvent (input_available_event); + } + + if (!ForceCloseHandle (input_available_event)) + termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event); + if (!ForceCloseHandle1 (get_handle (), from_pty)) + termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ()); + if (!ForceCloseHandle1 (get_output_handle (), to_pty)) + termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ()); + + inuse = NULL; + termios_printf ("tty%d <%p,%p> closed", get_unit (), get_handle (), get_output_handle ()); + return 0; +} + +int +fhandler_pty_master::close () +{ +#if 0 + while (accept_input () > 0) + continue; +#endif + this->fhandler_tty_common::close (); + + if (!get_ttyp ()->master_alive ()) + { + termios_printf ("freeing tty%d (%d)", get_unit (), get_ttyp ()->ntty); +#if 0 + if (get_ttyp ()->to_slave) + ForceCloseHandle1 (get_ttyp ()->to_slave, to_slave); + if (get_ttyp ()->from_slave) + ForceCloseHandle1 (get_ttyp ()->from_slave, from_slave); +#endif + if (get_ttyp ()->from_master) + CloseHandle (get_ttyp ()->from_master); + if (get_ttyp ()->to_master) + CloseHandle (get_ttyp ()->to_master); + get_ttyp ()->init (); + } + + return 0; +} + +int +fhandler_pty_master::write (const void *ptr, size_t len) +{ + int i; + char *p = (char *) ptr; + termios ti = tc->ti; + + for (i = 0; i < (int) len; i++) + { + line_edit_status status = line_edit (p++, 1, ti); + if (status > line_edit_signalled) + { + if (status != line_edit_pipe_full) + i = -1; + break; + } + } + return i; +} + +void __stdcall +fhandler_pty_master::read (void *ptr, size_t& len) +{ + (ssize_t) len = process_slave_output ((char *) ptr, len, pktmode); + return; +} + +int +fhandler_pty_master::tcgetattr (struct termios *t) +{ + *t = cygwin_shared->tty[get_unit ()]->ti; + return 0; +} + +int +fhandler_pty_master::tcsetattr (int, const struct termios *t) +{ + cygwin_shared->tty[get_unit ()]->ti = *t; + return 0; +} + +int +fhandler_pty_master::tcflush (int) +{ + return 0; +} + +int +fhandler_pty_master::ioctl (unsigned int cmd, void *arg) +{ + switch (cmd) + { + case TIOCPKT: + pktmode = * (int *) arg; + break; + case TIOCGWINSZ: + *(struct winsize *) arg = get_ttyp ()->winsize; + break; + case TIOCSWINSZ: + if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row + || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) + { + get_ttyp ()->winsize = * (struct winsize *) arg; + kill (-get_ttyp ()->getpgid (), SIGWINCH); + } + break; + case FIONBIO: + set_nonblocking (*(int *) arg); + break; + default: + set_errno (EINVAL); + return -1; + } + return 0; +} + +char * +fhandler_pty_master::ptsname () +{ + static char buf[32]; + + __small_sprintf (buf, "/dev/tty%d", get_unit ()); + return buf; +} + +void +fhandler_tty_common::set_close_on_exec (int val) +{ +#ifndef DEBUGGING + this->fhandler_base::set_close_on_exec (val); +#else + /* FIXME: This is a duplication from fhandler_base::set_close_on_exec. + It is here because we need to specify the "from_pty" stuff here or + we'll get warnings from ForceCloseHandle when debugging. */ + set_inheritance (get_io_handle (), val); + set_close_on_exec_flag (val); +#endif + if (output_done_event) + set_inheritance (output_done_event, val); + if (ioctl_request_event) + set_inheritance (ioctl_request_event, val); + if (ioctl_done_event) + set_inheritance (ioctl_done_event, val); + if (inuse) + set_inheritance (inuse, val); + set_inheritance (output_mutex, val); + set_inheritance (input_mutex, val); + set_inheritance (input_available_event, val); + set_inheritance (output_handle, val); +} + +void +fhandler_tty_common::fixup_after_fork (HANDLE parent) +{ + this->fhandler_termios::fixup_after_fork (parent); + if (output_done_event) + fork_fixup (parent, output_done_event, "output_done_event"); + if (ioctl_request_event) + fork_fixup (parent, ioctl_request_event, "ioctl_request_event"); + if (ioctl_done_event) + fork_fixup (parent, ioctl_done_event, "ioctl_done_event"); + if (output_mutex) + fork_fixup (parent, output_mutex, "output_mutex"); + if (input_mutex) + fork_fixup (parent, input_mutex, "input_mutex"); + if (input_available_event) + fork_fixup (parent, input_available_event, "input_available_event"); + fork_fixup (parent, inuse, "inuse"); +} + +void +fhandler_pty_master::set_close_on_exec (int val) +{ + this->fhandler_tty_common::set_close_on_exec (val); + + /* FIXME: There is a console handle leak here. */ + if (get_ttyp ()->master_pid == GetCurrentProcessId ()) + { + get_ttyp ()->from_slave = get_handle (); + get_ttyp ()->to_slave = get_output_handle (); + termios_printf ("from_slave %p, to_slave %p", get_handle (), + get_output_handle ()); + } +} + +void +fhandler_tty_master::fixup_after_fork (HANDLE child) +{ + this->fhandler_pty_master::fixup_after_fork (child); + console->fixup_after_fork (child); +} + +void +fhandler_tty_master::fixup_after_exec (HANDLE) +{ + console->close (); + init_console (); +} + +int +fhandler_tty_master::init_console () +{ + console = (fhandler_console *) + cygheap->fdtab.build_fhandler (-1, *console_dev, "/dev/ttym"); + if (console == NULL) + return -1; + + console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY); + console->set_r_no_interrupt (1); + return 0; +} diff --git a/winsup/cygwin/fhandler_virtual.cc b/winsup/cygwin/fhandler_virtual.cc new file mode 100644 index 00000000000..4487275bf31 --- /dev/null +++ b/winsup/cygwin/fhandler_virtual.cc @@ -0,0 +1,235 @@ +/* fhandler_virtual.cc: base fhandler class for virtual filesystems + + Copyright 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. */ + +#include "winsup.h" +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "shared_info.h" +#include "cygheap.h" +#include <assert.h> + +#define _COMPILING_NEWLIB +#include <dirent.h> + +fhandler_virtual::fhandler_virtual (): + fhandler_base (), filebuf (NULL), bufalloc ((size_t) -1), + fileid (-1) +{ +} + +fhandler_virtual::~fhandler_virtual () +{ + if (filebuf) + free (filebuf); + filebuf = NULL; +} + +void +fhandler_virtual::fixup_after_exec (HANDLE) +{ + if (filebuf) + filebuf = NULL; +} + +DIR * +fhandler_virtual::opendir (path_conv& pc) +{ + DIR *dir; + DIR *res = NULL; + size_t len; + + if (exists () <= 0) + set_errno (ENOTDIR); + else if ((len = strlen (get_name ())) > MAX_PATH - 3) + set_errno (ENAMETOOLONG); + else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) + set_errno (ENOMEM); + else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL) + { + free (dir); + set_errno (ENOMEM); + } + else if ((dir->__d_dirent = + (struct dirent *) malloc (sizeof (struct dirent))) == NULL) + { + free (dir); + set_errno (ENOMEM); + } + else + { + strcpy (dir->__d_dirname, get_name ()); + dir->__d_dirent->d_version = __DIRENT_VERSION; + cygheap_fdnew fd; + fd = this; + fd->set_nohandle (true); + dir->__d_dirent->d_fd = fd; + dir->__d_u.__d_data.__fh = this; + dir->__d_cookie = __DIRENT_COOKIE; + dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE; + dir->__d_position = 0; + dir->__d_dirhash = get_namehash (); + + res = dir; + } + + syscall_printf ("%p = opendir (%s)", res, get_name ()); + return res; +} + +__off64_t fhandler_virtual::telldir (DIR * dir) +{ + return dir->__d_position; +} + +void +fhandler_virtual::seekdir (DIR * dir, __off64_t loc) +{ + dir->__d_position = loc; + return; +} + +void +fhandler_virtual::rewinddir (DIR * dir) +{ + dir->__d_position = 0; + return; +} + +int +fhandler_virtual::closedir (DIR * dir) +{ + return 0; +} + +__off64_t +fhandler_virtual::lseek (__off64_t offset, int whence) +{ + /* + * On Linux, when you lseek within a /proc file, + * the contents of the file are updated. + */ + if (!fill_filebuf ()) + return (__off64_t) -1; + switch (whence) + { + case SEEK_SET: + position = offset; + break; + case SEEK_CUR: + position += offset; + break; + case SEEK_END: + position = filesize + offset; + break; + default: + set_errno (EINVAL); + return (__off64_t) -1; + } + return position; +} + +int +fhandler_virtual::dup (fhandler_base * child) +{ + int ret = fhandler_base::dup (child); + + if (!ret) + { + fhandler_virtual *fhproc_child = (fhandler_virtual *) child; + fhproc_child->filebuf = (char *) malloc (filesize); + fhproc_child->bufalloc = fhproc_child->filesize = filesize; + fhproc_child->position = position; + memcpy (fhproc_child->filebuf, filebuf, filesize); + fhproc_child->set_flags (get_flags ()); + } + return ret; +} + +int +fhandler_virtual::close () +{ + if (filebuf) + free (filebuf); + filebuf = NULL; + bufalloc = (size_t) -1; + cygwin_shared->delqueue.process_queue (); + return 0; +} + +void +fhandler_virtual::read (void *ptr, size_t& len) +{ + if (len == 0) + return; + if (openflags & O_DIROPEN) + { + set_errno (EISDIR); + (ssize_t) len = -1; + return; + } + if (!filebuf) + { + (ssize_t) len = 0; + return; + } + if ((ssize_t) len > filesize - position) + (ssize_t) len = filesize - position; + if ((ssize_t) len < 0) + (ssize_t) len = 0; + else + memcpy (ptr, filebuf + position, len); + position += len; +} + +int +fhandler_virtual::write (const void *ptr, size_t len) +{ + set_errno (EACCES); + return -1; +} + +/* low-level open for all proc files */ +int +fhandler_virtual::open (path_conv *, int flags, mode_t mode) +{ + set_r_binary (1); + set_w_binary (1); + + set_has_acls (false); + set_isremote (false); + + /* what to do about symlinks? */ + set_symlink_p (false); + set_execable_p (not_executable); + set_socket_p (false); + + set_flags ((flags & ~O_TEXT) | O_BINARY); + + return 1; +} + +int +fhandler_virtual::exists () +{ + return 0; +} + +bool +fhandler_virtual::fill_filebuf () +{ + return true; +} diff --git a/winsup/cygwin/fhandler_windows.cc b/winsup/cygwin/fhandler_windows.cc new file mode 100644 index 00000000000..929aae5cec6 --- /dev/null +++ b/winsup/cygwin/fhandler_windows.cc @@ -0,0 +1,147 @@ +/* fhandler_windows.cc: code to access windows message queues. + + Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + Written by Sergey S. Okhapkin (sos@prospect.com.ru). + Feedback and testing by Andy Piper (andyp@parallax.co.uk). + +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 <errno.h> +#include <wingdi.h> +#include <winuser.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" + +/* +The following unix-style calls are supported: + + open ("/dev/windows", flags, mode=0) + - create a unix fd for message queue. + O_NONBLOCK flag controls the read() call behavior. + + read (fd, buf, len) + - return next message from queue. buf must point to MSG + structure, len must be >= sizeof (MSG). If read is set to + non-blocking and the queue is empty, read call returns -1 + immediately with errno set to EAGAIN, otherwise it blocks + untill the message will be received. + + write (fd, buf, len) + - send a message pointed by buf. len argument ignored. + + ioctl (fd, command, *param) + - control read()/write() behavior. + ioctl (fd, WINDOWS_POST, NULL): write() will PostMessage(); + ioctl (fd, WINDOWS_SEND, NULL): write() will SendMessage(); + ioctl (fd, WINDOWS_HWND, &hWnd): read() messages for + hWnd window. + + select () call marks read fd when any message posted to queue. +*/ + +fhandler_windows::fhandler_windows () + : fhandler_base (), hWnd_ (NULL), method_ (WINDOWS_POST) +{ +} + +int +fhandler_windows::open (path_conv *, int flags, mode_t) +{ + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_close_on_exec_flag (1); + set_open_status (); + return 1; +} + +int +fhandler_windows::write (const void *buf, size_t) +{ + MSG *ptr = (MSG *) buf; + + if (method_ == WINDOWS_POST) + { + if (!PostMessage (ptr->hwnd, ptr->message, ptr->wParam, ptr->lParam)) + { + __seterrno (); + return -1; + } + else + return sizeof (MSG); + } + else + return SendMessage (ptr->hwnd, ptr->message, ptr->wParam, ptr->lParam); +} + +void __stdcall +fhandler_windows::read (void *buf, size_t& len) +{ + MSG *ptr = (MSG *) buf; + + if (len < sizeof (MSG)) + { + set_errno (EINVAL); + (ssize_t) len = -1; + return; + } + + (ssize_t) len = GetMessage (ptr, hWnd_, 0, 0); + + if ((ssize_t) len == -1) + __seterrno (); + else + set_errno (0); + return; +} + +int +fhandler_windows::ioctl (unsigned int cmd, void *val) +{ + switch (cmd) + { + case WINDOWS_POST: + case WINDOWS_SEND: + method_ = cmd; + break; + case WINDOWS_HWND: + if (val == NULL) + { + set_errno (EINVAL); + return -1; + } + hWnd_ = * ((HWND *) val); + break; + default: + set_errno (EINVAL); + return -1; + } + return 0; +} + +void +fhandler_windows::set_close_on_exec (int val) +{ + if (get_handle ()) + this->fhandler_base::set_close_on_exec (val); + else + this->fhandler_base::set_close_on_exec_flag (val); + void *h = hWnd_; + if (h) + set_inheritance (h, val); +} + +void +fhandler_windows::fixup_after_fork (HANDLE parent) +{ + if (get_handle ()) + this->fhandler_base::fixup_after_fork (parent); + void *h = hWnd_; + if (h) + fork_fixup (parent, h, "hWnd_"); +} diff --git a/winsup/cygwin/fhandler_zero.cc b/winsup/cygwin/fhandler_zero.cc new file mode 100644 index 00000000000..64ad21fdc8f --- /dev/null +++ b/winsup/cygwin/fhandler_zero.cc @@ -0,0 +1,55 @@ +/* fhandler_dev_zero.cc: code to access /dev/zero + + Copyright 2000, 2001, 2002 Red Hat, Inc. + + Written by DJ Delorie (dj@cygnus.com) + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <errno.h> +#include "security.h" +#include "fhandler.h" + +fhandler_dev_zero::fhandler_dev_zero () + : fhandler_base () +{ +} + +int +fhandler_dev_zero::open (path_conv *, int flags, mode_t) +{ + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_nohandle (true); + set_open_status (); + return 1; +} + +int +fhandler_dev_zero::write (const void *, size_t len) +{ + return len; +} + +void __stdcall +fhandler_dev_zero::read (void *ptr, size_t& len) +{ + memset (ptr, 0, len); + return; +} + +__off64_t +fhandler_dev_zero::lseek (__off64_t, int) +{ + return 0; +} + +void +fhandler_dev_zero::dump () +{ + paranoid_printf ("here, fhandler_dev_zero"); +} diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc new file mode 100644 index 00000000000..67ff8238074 --- /dev/null +++ b/winsup/cygwin/mmap.cc @@ -0,0 +1,1046 @@ +/* mmap.cc + + 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 <unistd.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/mman.h> +#include <errno.h> +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygerrno.h" +#include "cygheap.h" +#include "pinfo.h" +#include "sys/cygwin.h" + +#define PAGE_CNT(bytes) howmany((bytes),getpagesize()) + +#define PGBITS (sizeof (DWORD)*8) +#define MAPSIZE(pages) howmany ((pages), PGBITS) + +#define MAP_SET(n) (map_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS))) +#define MAP_CLR(n) (map_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS))) +#define MAP_ISSET(n) (map_map_[(n)/PGBITS] & (1L << ((n) % PGBITS))) + +/* + * Simple class used to keep a record of all current + * mmap areas in a process. Needed so that + * they can be duplicated after a fork(). + */ + +class mmap_record +{ + private: + int fdesc_; + HANDLE mapping_handle_; + DWORD access_mode_; + __off64_t offset_; + DWORD size_to_map_; + caddr_t base_address_; + DWORD *map_map_; + device dev; + + public: + mmap_record (int fd, HANDLE h, DWORD ac, __off64_t o, DWORD s, caddr_t b) : + fdesc_ (fd), + mapping_handle_ (h), + access_mode_ (ac), + offset_ (o), + size_to_map_ (s), + base_address_ (b), + map_map_ (NULL) + { + dev.devn = 0; + if (fd >= 0 && !cygheap->fdtab.not_open (fd)) + dev = cygheap->fdtab[fd]->dev; + } + + /* Default Copy constructor/operator=/destructor are ok */ + + /* Simple accessors */ + int get_fd () const { return fdesc_; } + HANDLE get_handle () const { return mapping_handle_; } + device& get_device () { return dev; } + DWORD get_access () const { return access_mode_; } + DWORD get_offset () const { return offset_; } + DWORD get_size () const { return size_to_map_; } + caddr_t get_address () const { return base_address_; } + DWORD *get_map () const { return map_map_; } + + void alloc_map () + { + /* Allocate one bit per page */ + map_map_ = (DWORD *) calloc (MAPSIZE (PAGE_CNT (size_to_map_)), + sizeof (DWORD)); + if (wincap.virtual_protect_works_on_shared_pages ()) + { + DWORD old_prot; + if (!VirtualProtect (base_address_, size_to_map_, + PAGE_NOACCESS, &old_prot)) + syscall_printf ("-1 = alloc_map (): %E"); + } + } + void free_map () { if (map_map_) free (map_map_); } + + DWORD find_empty (DWORD pages); + __off64_t map_map (__off64_t off, DWORD len); + BOOL unmap_map (caddr_t addr, DWORD len); + void fixup_map (void); + int access (caddr_t address); + + fhandler_base *alloc_fh (); + void free_fh (fhandler_base *fh); +}; + +DWORD +mmap_record::find_empty (DWORD pages) +{ + DWORD mapped_pages = PAGE_CNT (size_to_map_); + DWORD start; + + if (pages > mapped_pages) + return (DWORD)-1; + for (start = 0; start <= mapped_pages - pages; ++start) + if (!MAP_ISSET (start)) + { + DWORD cnt; + for (cnt = 0; cnt < pages; ++cnt) + if (MAP_ISSET (start + cnt)) + break; + if (cnt >= pages) + return start; + } + return (DWORD)-1; +} + +__off64_t +mmap_record::map_map (__off64_t off, DWORD len) +{ + DWORD prot, old_prot; + switch (access_mode_) + { + case FILE_MAP_WRITE: + prot = PAGE_READWRITE; + break; + case FILE_MAP_READ: + prot = PAGE_READONLY; + break; + default: + prot = PAGE_WRITECOPY; + break; + } + + debug_printf ("map_map (fd=%d, off=%D, len=%d)", fdesc_, off, len); + len = PAGE_CNT (len); + if (fdesc_ == -1 && !off) + { + off = find_empty (len); + if (off != (DWORD)-1) + { + if (wincap.virtual_protect_works_on_shared_pages () + && !VirtualProtect (base_address_ + off * getpagesize (), + len * getpagesize (), prot, &old_prot)) + { + __seterrno (); + return (__off64_t)-1; + } + + while (len-- > 0) + MAP_SET (off + len); + return off * getpagesize (); + } + return 0L; + } + off -= offset_; + DWORD start = off / getpagesize (); + if (wincap.virtual_protect_works_on_shared_pages () + && !VirtualProtect (base_address_ + start * getpagesize (), + len * getpagesize (), prot, &old_prot)) + { + __seterrno (); + return (__off64_t)-1; + } + + for (; len-- > 0; ++start) + MAP_SET (start); + return off; +} + +BOOL +mmap_record::unmap_map (caddr_t addr, DWORD len) +{ + DWORD old_prot; + DWORD off = addr - base_address_; + off /= getpagesize (); + len = PAGE_CNT (len); + if (wincap.virtual_protect_works_on_shared_pages () + && !VirtualProtect (base_address_ + off * getpagesize (), + len * getpagesize (), PAGE_NOACCESS, &old_prot)) + syscall_printf ("-1 = unmap_map (): %E"); + + for (; len-- > 0; ++off) + MAP_CLR (off); + /* Return TRUE if all pages are free'd which may result in unmapping + the whole chunk. */ + for (len = MAPSIZE (PAGE_CNT (size_to_map_)); len > 0; ) + if (map_map_[--len]) + return FALSE; + return TRUE; +} + +void +mmap_record::fixup_map () +{ + if (!wincap.virtual_protect_works_on_shared_pages ()) + return; + + DWORD prot, old_prot; + switch (access_mode_) + { + case FILE_MAP_WRITE: + prot = PAGE_READWRITE; + break; + case FILE_MAP_READ: + prot = PAGE_READONLY; + break; + default: + prot = PAGE_WRITECOPY; + break; + } + + for (DWORD off = PAGE_CNT (size_to_map_); off > 0; --off) + VirtualProtect (base_address_ + off * getpagesize (), + getpagesize (), + MAP_ISSET (off - 1) ? prot : PAGE_NOACCESS, + &old_prot); +} + +int +mmap_record::access (caddr_t address) +{ + if (address < base_address_ || address >= base_address_ + size_to_map_) + return 0; + DWORD off = (address - base_address_) / getpagesize (); + return MAP_ISSET (off); +} + +static fhandler_disk_file fh_paging_file; + +fhandler_base * +mmap_record::alloc_fh () +{ + if (get_fd () == -1) + { + fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE); + return &fh_paging_file; + } + + /* The file descriptor could have been closed or, even + worse, could have been reused for another file before + the call to fork(). This requires creating a fhandler + of the correct type to be sure to call the method of the + correct class. */ + return cygheap->fdtab.build_fhandler (-1, get_device ()); +} + +void +mmap_record::free_fh (fhandler_base *fh) +{ + if (get_fd () != -1) + cfree (fh); +} + +class list { +public: + mmap_record *recs; + int nrecs, maxrecs; + int fd; + DWORD hash; + list (); + ~list (); + mmap_record *add_record (mmap_record r); + void erase (int i); + void erase (); + mmap_record *match (__off64_t off, DWORD len); + long match (caddr_t addr, DWORD len, long start); +}; + +list::list () +: nrecs (0), maxrecs (10), fd (0), hash (0) +{ + recs = (mmap_record *) malloc (10 * sizeof (mmap_record)); +} + +list::~list () +{ + for (mmap_record *rec = recs; nrecs-- > 0; ++rec) + rec->free_map (); + free (recs); +} + +mmap_record * +list::add_record (mmap_record r) +{ + if (nrecs == maxrecs) + { + maxrecs += 5; + recs = (mmap_record *) realloc (recs, maxrecs * sizeof (mmap_record)); + } + recs[nrecs] = r; + recs[nrecs].alloc_map (); + return recs + nrecs++; +} + +/* Used in mmap() */ +mmap_record * +list::match (__off64_t off, DWORD len) +{ + if (fd == -1 && !off) + { + len = PAGE_CNT (len); + for (int i = 0; i < nrecs; ++i) + if (recs[i].find_empty (len) != (DWORD)-1) + return recs + i; + } + else + { + for (int i = 0; i < nrecs; ++i) + if (off >= recs[i].get_offset () + && off + len <= recs[i].get_offset () + + (PAGE_CNT (recs[i].get_size ()) * getpagesize ())) + return recs + i; + } + return NULL; +} + +/* Used in munmap() */ +long +list::match (caddr_t addr, DWORD len, __off32_t start) +{ + for (int i = start + 1; i < nrecs; ++i) + if (addr >= recs[i].get_address () + && addr + len <= recs[i].get_address () + + (PAGE_CNT (recs[i].get_size ()) * getpagesize ())) + return i; + return -1; +} + +void +list::erase (int i) +{ + recs[i].free_map (); + for (; i < nrecs-1; i++) + recs[i] = recs[i+1]; + nrecs--; +} + +void +list::erase () +{ + erase (nrecs-1); +} + +class map { +public: + list **lists; + int nlists, maxlists; + map (); + ~map (); + list *get_list_by_fd (int fd); + list *add_list (list *l, int fd); + void erase (int i); +}; + +map::map () +{ + lists = (list **) malloc (10 * sizeof (list *)); + nlists = 0; + maxlists = 10; +} + +map::~map () +{ + free (lists); +} + +list * +map::get_list_by_fd (int fd) +{ + int i; + for (i=0; i<nlists; i++) +#if 0 /* The fd isn't sufficient since it could already be another file. */ + if (lists[i]->fd == fd +#else /* so we use the name hash value to identify the file unless + it's not an anonymous mapping. */ + if ((fd == -1 && lists[i]->fd == -1) + || (fd != -1 && lists[i]->hash == cygheap->fdtab[fd]->get_namehash ())) +#endif + return lists[i]; + return 0; +} + +list * +map::add_list (list *l, int fd) +{ + l->fd = fd; + if (fd != -1) + l->hash = cygheap->fdtab[fd]->get_namehash (); + if (nlists == maxlists) + { + maxlists += 5; + lists = (list **) realloc (lists, maxlists * sizeof (list *)); + } + lists[nlists++] = l; + return lists[nlists-1]; +} + +void +map::erase (int i) +{ + for (; i < nlists-1; i++) + lists[i] = lists[i+1]; + nlists--; +} + +/* + * Code to keep a record of all mmap'ed areas in a process. + * Needed to duplicate tham in a child of fork(). + * mmap_record classes are kept in an STL list in an STL map, keyed + * by file descriptor. This is *NOT* duplicated accross a fork(), it + * needs to be specially handled by the fork code. + */ + +static map *mmapped_areas; + +extern "C" +caddr_t +mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, __off64_t off) +{ + syscall_printf ("addr %x, len %d, prot %x, flags %x, fd %d, off %D", + addr, len, prot, flags, fd, off); + + static DWORD granularity; + if (!granularity) + { + SYSTEM_INFO si; + GetSystemInfo (&si); + granularity = si.dwAllocationGranularity; + } + + /* Error conditions according to SUSv2 */ + if (off % getpagesize () + || (!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE)) + || ((flags & MAP_SHARED) && (flags & MAP_PRIVATE)) + || ((flags & MAP_FIXED) && ((DWORD)addr % granularity)) + || !len) + { + set_errno (EINVAL); + syscall_printf ("-1 = mmap(): EINVAL"); + return MAP_FAILED; + } + + SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + + if (mmapped_areas == NULL) + { + /* First mmap call, create STL map */ + mmapped_areas = new map; + if (mmapped_areas == NULL) + { + set_errno (ENOMEM); + syscall_printf ("-1 = mmap(): ENOMEM"); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + } + + if (flags & MAP_ANONYMOUS) + fd = -1; + + /* Map always in multipliers of `granularity'-sized chunks. */ + __off64_t gran_off = off & ~(granularity - 1); + DWORD gran_len = howmany (off + len, granularity) * granularity - gran_off; + + fhandler_base *fh; + caddr_t base = addr; + HANDLE h; + + if (fd != -1) + { + /* Ensure that fd is open */ + cygheap_fdget cfd (fd); + if (cfd < 0) + { + syscall_printf ("-1 = mmap(): EBADF"); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + fh = cfd; + if (fh->get_device () == FH_FS) + { + DWORD high; + DWORD low = GetFileSize (fh->get_handle (), &high); + __off64_t fsiz = ((__off64_t)high << 32) + low; + fsiz -= gran_off; + if (gran_len > fsiz) + gran_len = fsiz; + } + else if (fh->get_device () == FH_ZERO) + /* mmap /dev/zero is like MAP_ANONYMOUS. */ + fd = -1; + } + if (fd == -1) + { + fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE); + fh = &fh_paging_file; + } + + list *l = mmapped_areas->get_list_by_fd (fd); + + /* First check if this mapping matches into the chunk of another + already performed mapping. Only valid for MAP_ANON in a special + case of MAP_PRIVATE. */ + if (l && fd == -1 && off == 0 && !(flags & MAP_FIXED)) + { + mmap_record *rec; + if ((rec = l->match (off, len)) != NULL) + { + if ((off = rec->map_map (off, len)) == (__off64_t)-1) + { + syscall_printf ("-1 = mmap()"); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK|WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + caddr_t ret = rec->get_address () + off; + syscall_printf ("%x = mmap() succeeded", ret); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return ret; + } + } + + DWORD access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; + /* copy-on-write doesn't work at all on 9x using anonymous maps. + Workaround: Anonymous mappings always use normal READ or WRITE + access and don't use named file mapping. + copy-on-write doesn't also work properly on 9x with real files. + While the changes are not propagated to the file, they are + visible to other processes sharing the same file mapping object. + Workaround: Don't use named file mapping. That should work since + sharing file mappings only works reliable using named + file mapping on 9x. + */ + if ((flags & MAP_PRIVATE) + && (wincap.has_working_copy_on_write () || fd != -1)) + access = FILE_MAP_COPY; + + h = fh->mmap (&base, gran_len, access, flags, gran_off); + + if (h == INVALID_HANDLE_VALUE) + { + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + + /* Now we should have a successfully mmapped area. + Need to save it so forked children can reproduce it. + */ + if (fd == -1) + gran_len = PAGE_CNT (gran_len) * getpagesize (); + mmap_record mmap_rec (fd, h, access, gran_off, gran_len, base); + + /* Get list of mmapped areas for this fd, create a new one if + one does not exist yet. + */ + if (l == 0) + { + /* Create a new one */ + l = new list; + if (l == 0) + { + fh->munmap (h, base, gran_len); + set_errno (ENOMEM); + syscall_printf ("-1 = mmap(): ENOMEM"); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + l = mmapped_areas->add_list (l, fd); + } + + /* Insert into the list */ + mmap_record *rec = l->add_record (mmap_rec); + if ((off = rec->map_map (off, len)) == (__off64_t)-1) + { + fh->munmap (h, base, gran_len); + l->erase (); + syscall_printf ("-1 = mmap()"); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return MAP_FAILED; + } + caddr_t ret = rec->get_address () + off; + syscall_printf ("%x = mmap() succeeded", ret); + ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); + return ret; +} + +extern "C" +caddr_t +mmap (caddr_t addr, size_t len, int prot, int flags, int fd, __off32_t off) +{ + return mmap64 (addr, len, prot, flags, fd, (__off64_t)off); +} + +/* munmap () removes an mmapped area. It insists that base area + requested is the same as that mmapped, error if not. */ + +extern "C" +int +munmap (caddr_t addr, size_t len) +{ + syscall_printf ("munmap (addr %x, len %d)", addr, len); + + /* Error conditions according to SUSv2 */ + if (((DWORD)addr % getpagesize ()) || !len) + { + set_errno (EINVAL); + syscall_printf ("-1 = munmap(): Invalid parameters"); + return -1; + } + + SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + /* Check if a mmap'ed area was ever created */ + if (mmapped_areas == NULL) + { + syscall_printf ("-1 = munmap(): mmapped_areas == NULL"); + set_errno (EINVAL); + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + return -1; + } + + /* Iterate through the map, looking for the mmapped area. + Error if not found. */ + + for (int it = 0; it < mmapped_areas->nlists; ++it) + { + list *l = mmapped_areas->lists[it]; + if (l) + { + long li = -1; + if ((li = l->match(addr, len, li)) >= 0) + { + mmap_record *rec = l->recs + li; + if (rec->unmap_map (addr, len)) + { + fhandler_base *fh = rec->alloc_fh (); + fh->munmap (rec->get_handle (), addr, len); + rec->free_fh (fh); + + /* Delete the entry. */ + l->erase (li); + } + syscall_printf ("0 = munmap(): %x", addr); + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + return 0; + } + } + } + + set_errno (EINVAL); + syscall_printf ("-1 = munmap(): EINVAL"); + + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap"); + return -1; +} + +/* Sync file with memory. Ignore flags for now. */ + +extern "C" +int +msync (caddr_t addr, size_t len, int flags) +{ + syscall_printf ("addr = %x, len = %d, flags = %x", + addr, len, flags); + + /* However, check flags for validity. */ + if ((flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE)) + || ((flags & MS_ASYNC) && (flags & MS_SYNC))) + { + syscall_printf ("-1 = msync(): Invalid flags"); + set_errno (EINVAL); + return -1; + } + + SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + /* Check if a mmap'ed area was ever created */ + if (mmapped_areas == NULL) + { + syscall_printf ("-1 = msync(): mmapped_areas == NULL"); + set_errno (EINVAL); + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + return -1; + } + + /* Iterate through the map, looking for the mmapped area. + Error if not found. */ + + for (int it = 0; it < mmapped_areas->nlists; ++it) + { + list *l = mmapped_areas->lists[it]; + if (l != 0) + { + for (int li = 0; li < l->nrecs; ++li) + { + mmap_record *rec = l->recs + li; + if (rec->access (addr)) + { + /* Check whole area given by len. */ + for (DWORD i = getpagesize (); i < len; ++i) + if (!rec->access (addr + i)) + goto invalid_address_range; + fhandler_base *fh = rec->alloc_fh (); + int ret = fh->msync (rec->get_handle (), addr, len, flags); + rec->free_fh (fh); + + if (ret) + syscall_printf ("%d = msync(): %E", ret); + else + syscall_printf ("0 = msync()"); + + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + return 0; + } + } + } + } + +invalid_address_range: + /* SUSv2: Return code if indicated memory was not mapped is ENOMEM. */ + set_errno (ENOMEM); + syscall_printf ("-1 = msync(): ENOMEM"); + + ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync"); + return -1; +} + +/* + * Base implementation: + * + * `mmap' returns ENODEV as documented in SUSv2. + * In contrast to the global function implementation, the member function + * `mmap' has to return the mapped base address in `addr' and the handle to + * the mapping object as return value. In case of failure, the fhandler + * mmap has to close that handle by itself and return INVALID_HANDLE_VALUE. + * + * `munmap' and `msync' get the handle to the mapping object as first parameter + * additionally. +*/ +HANDLE +fhandler_base::mmap (caddr_t *addr, size_t len, DWORD access, + int flags, __off64_t off) +{ + set_errno (ENODEV); + return INVALID_HANDLE_VALUE; +} + +int +fhandler_base::munmap (HANDLE h, caddr_t addr, size_t len) +{ + set_errno (ENODEV); + return -1; +} + +int +fhandler_base::msync (HANDLE h, caddr_t addr, size_t len, int flags) +{ + set_errno (ENODEV); + return -1; +} + +BOOL +fhandler_base::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address) +{ + set_errno (ENODEV); + return -1; +} + +/* Implementation for disk files. */ +HANDLE +fhandler_disk_file::mmap (caddr_t *addr, size_t len, DWORD access, + int flags, __off64_t off) +{ + DWORD protect; + + if (access & FILE_MAP_COPY) + protect = PAGE_WRITECOPY; + else if (access & FILE_MAP_WRITE) + protect = PAGE_READWRITE; + else + protect = PAGE_READONLY; + + HANDLE h; + + /* On 9x/ME try first to open the mapping by name when opening a + shared file object. This is needed since 9x/ME only shares + objects between processes by name. What a mess... */ + if (wincap.share_mmaps_only_by_name () + && get_handle () != INVALID_HANDLE_VALUE + && !(access & FILE_MAP_COPY)) + { + /* Grrr, the whole stuff is just needed to try to get a reliable + mapping of the same file. Even that uprising isn't bullet + proof but it does it's best... */ + char namebuf[MAX_PATH]; + cygwin_conv_to_full_posix_path (get_name (), namebuf); + for (int i = strlen (namebuf) - 1; i >= 0; --i) + namebuf[i] = cyg_tolower (namebuf [i]); + + debug_printf ("named sharing"); + if (!(h = OpenFileMapping (access, TRUE, namebuf))) + h = CreateFileMapping (get_handle (), &sec_none, protect, 0, 0, namebuf); + } + else + h = CreateFileMapping (get_handle (), &sec_none, protect, 0, + get_handle () == INVALID_HANDLE_VALUE ? len : 0, + NULL); + if (!h) + { + __seterrno (); + syscall_printf ("-1 = mmap(): CreateFileMapping failed with %E"); + return INVALID_HANDLE_VALUE; + } + + DWORD high = off >> 32, low = off & 0xffffffff; + void *base = MapViewOfFileEx (h, access, high, low, len, + (flags & MAP_FIXED) ? *addr : NULL); + debug_printf ("%x = MapViewOfFileEx (h:%x, access:%x, 0, off:%D, len:%d, addr:%x)", base, h, access, off, len, (flags & MAP_FIXED) ? *addr : NULL); + if (!base || ((flags & MAP_FIXED) && base != *addr)) + { + if (!base) + { + __seterrno (); + syscall_printf ("-1 = mmap(): MapViewOfFileEx failed with %E"); + } + else + { + set_errno (EINVAL); + syscall_printf ("-1 = mmap(): address shift with MAP_FIXED given"); + } + CloseHandle (h); + return INVALID_HANDLE_VALUE; + } + + *addr = (caddr_t) base; + return h; +} + +int +fhandler_disk_file::munmap (HANDLE h, caddr_t addr, size_t len) +{ + UnmapViewOfFile (addr); + CloseHandle (h); + return 0; +} + +int +fhandler_disk_file::msync (HANDLE h, caddr_t addr, size_t len, int flags) +{ + if (FlushViewOfFile (addr, len) == 0) + { + __seterrno (); + return -1; + } + return 0; +} + +BOOL +fhandler_disk_file::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset, + DWORD size, void *address) +{ + /* Re-create the MapViewOfFileEx call */ + void *base = MapViewOfFileEx (h, access, 0, offset, size, address); + if (base != address) + { + MEMORY_BASIC_INFORMATION m; + (void) VirtualQuery (address, &m, sizeof (m)); + system_printf ("requested %p != %p mem alloc base %p, state %p, size %d, %E", + address, base, m.AllocationBase, m.State, m.RegionSize); + } + return base == address; +} + +/* Set memory protection */ + +extern "C" +int +mprotect (caddr_t addr, size_t len, int prot) +{ + DWORD old_prot; + DWORD new_prot = 0; + + syscall_printf ("mprotect (addr %x, len %d, prot %x)", addr, len, prot); + + if (!wincap.virtual_protect_works_on_shared_pages () + && addr >= (caddr_t)0x80000000 && addr <= (caddr_t)0xBFFFFFFF) + { + syscall_printf ("0 = mprotect (9x: No VirtualProtect on shared memory)"); + return 0; + } + + if (prot == PROT_NONE) + new_prot = PAGE_NOACCESS; + else + { + switch (prot) + { + case PROT_READ | PROT_WRITE | PROT_EXEC: + new_prot = PAGE_EXECUTE_READWRITE; + break; + case PROT_READ | PROT_WRITE: + new_prot = PAGE_READWRITE; + break; + case PROT_READ | PROT_EXEC: + new_prot = PAGE_EXECUTE_READ; + break; + case PROT_READ: + new_prot = PAGE_READONLY; + break; + default: + syscall_printf ("-1 = mprotect (): invalid prot value"); + set_errno (EINVAL); + return -1; + } + } + + if (VirtualProtect (addr, len, new_prot, &old_prot) == 0) + { + __seterrno (); + syscall_printf ("-1 = mprotect (): %E"); + return -1; + } + + syscall_printf ("0 = mprotect ()"); + return 0; +} + +/* + * Call to re-create all the file mappings in a forked + * child. Called from the child in initialization. At this + * point we are passed a valid mmapped_areas map, and all the + * HANDLE's are valid for the child, but none of the + * mapped areas are in our address space. We need to iterate + * through the map, doing the MapViewOfFile calls. + */ + +int __stdcall +fixup_mmaps_after_fork (HANDLE parent) +{ + + debug_printf ("recreate_mmaps_after_fork, mmapped_areas %p", mmapped_areas); + + /* Check if a mmapped area was ever created */ + if (mmapped_areas == NULL) + return 0; + + /* Iterate through the map */ + for (int it = 0; it < mmapped_areas->nlists; ++it) + { + list *l = mmapped_areas->lists[it]; + if (l) + { + int li; + for (li = 0; li < l->nrecs; ++li) + { + mmap_record *rec = l->recs + li; + + debug_printf ("fd %d, h %x, access %x, offset %d, size %d, address %p", + rec->get_fd (), rec->get_handle (), rec->get_access (), + rec->get_offset (), rec->get_size (), rec->get_address ()); + + fhandler_base *fh = rec->alloc_fh (); + BOOL ret = fh->fixup_mmap_after_fork (rec->get_handle (), + rec->get_access (), + rec->get_offset (), + rec->get_size (), + rec->get_address ()); + rec->free_fh (fh); + + if (!ret) + return -1; + if (rec->get_access () == FILE_MAP_COPY) + { + for (char *address = rec->get_address (); + address < rec->get_address () + rec->get_size (); + address += getpagesize ()) + if (rec->access (address) + && !ReadProcessMemory (parent, address, address, + getpagesize (), NULL)) + { + DWORD old_prot; + + if (GetLastError () != ERROR_PARTIAL_COPY || + !wincap.virtual_protect_works_on_shared_pages ()) + { + system_printf ("ReadProcessMemory failed for " + "MAP_PRIVATE address %p, %E", + rec->get_address ()); + return -1; + } + if (!VirtualProtectEx (parent, + address, getpagesize (), + PAGE_READONLY, &old_prot)) + { + system_printf ("VirtualProtectEx failed for " + "MAP_PRIVATE address %p, %E", + rec->get_address ()); + return -1; + } + else + { + BOOL ret; + DWORD dummy_prot; + + ret = ReadProcessMemory (parent, address, address, + getpagesize (), NULL); + if (!VirtualProtectEx(parent, + address, getpagesize (), + old_prot, &dummy_prot)) + system_printf ("WARNING: VirtualProtectEx to " + "return to previous state " + "in parent failed for " + "MAP_PRIVATE address %p, %E", + rec->get_address ()); + if (!VirtualProtect (address, getpagesize (), + old_prot, &dummy_prot)) + system_printf ("WARNING: VirtualProtect to copy " + "protection to child failed for" + "MAP_PRIVATE address %p, %E", + rec->get_address ()); + if (!ret) + { + system_printf ("ReadProcessMemory (2nd try) " + "failed for " + "MAP_PRIVATE address %p, %E", + rec->get_address ()); + return -1; + } + } + } + } + rec->fixup_map (); + } + } + } + + debug_printf ("succeeded"); + return 0; +} diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc new file mode 100644 index 00000000000..8bad529748c --- /dev/null +++ b/winsup/cygwin/net.cc @@ -0,0 +1,2277 @@ +/* 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 <errno.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 "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" +#include "pinfo.h" +#include "registry.h" +#include "wsock_event.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); + 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) +{ +#ifdef _MT_SAFE +#define ntoa_buf _reent_winsup ()->_ntoa_buf +#else + static char *ntoa_buf = NULL; +#endif + + char *res = inet_ntoa (in); + + if (ntoa_buf) + { + free (ntoa_buf); + ntoa_buf = NULL; + } + if (res) + ntoa_buf = strdup (res); + return 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; +} + +static void +free_char_list (char **clist) +{ + if (clist) + { + for (char **cl = clist; *cl; ++cl) + free (*cl); + free (clist); + } +} + +static char ** +dup_char_list (char **src) +{ + char **dst; + int cnt = 0; + + for (char **cl = src; *cl; ++cl) + ++cnt; + if (!(dst = (char **) calloc (cnt + 1, sizeof *dst))) + return NULL; + while (cnt-- > 0) + if (!(dst[cnt] = strdup (src[cnt]))) + return NULL; + return dst; +} + +#define free_addr_list(addr_list) free_char_list (addr_list) + +static char ** +dup_addr_list (char **src, unsigned int size) +{ + char **dst; + int cnt = 0; + + for (char **cl = src; *cl; ++cl) + ++cnt; + if (!(dst = (char **) calloc (cnt + 1, sizeof *dst))) + return NULL; + while (cnt-- > 0) + { + if (!(dst[cnt] = (char *) malloc (size))) + return NULL; + memcpy (dst[cnt], src[cnt], size); + } + return dst; +} + +static void +free_protoent_ptr (struct protoent *&p) +{ + if (p) + { + debug_printf ("protoent: %s", p->p_name); + + if (p->p_name) + free (p->p_name); + free_char_list (p->p_aliases); + free ((void *) p); + p = NULL; + } +} + +static struct protoent * +dup_protoent_ptr (struct protoent *src) +{ + if (!src) + return NULL; + + struct protoent *dst = (struct protoent *) calloc (1, sizeof *dst); + + if (!dst) + return NULL; + + debug_printf ("protoent: %s", src->p_name); + + dst->p_proto = src->p_proto; + if (src->p_name && !(dst->p_name = strdup (src->p_name))) + goto out; + if (src->p_aliases && !(dst->p_aliases = dup_char_list (src->p_aliases))) + goto out; + + debug_printf ("protoent: copied %s", dst->p_name); + + return dst; + +out: + free_protoent_ptr (dst); + return NULL; +} + +#ifdef _MT_SAFE +#define protoent_buf _reent_winsup ()->_protoent_buf +#else +static struct protoent *protoent_buf = NULL; +#endif + +/* exported as getprotobyname: standards? */ +extern "C" struct protoent * +cygwin_getprotobyname (const char *p) +{ + if (check_null_str_errno (p)) + return NULL; + free_protoent_ptr (protoent_buf); + protoent_buf = dup_protoent_ptr (getprotobyname (p)); + if (!protoent_buf) + set_winsock_errno (); + + dump_protoent (protoent_buf); + return protoent_buf; +} + +/* exported as getprotobynumber: standards? */ +extern "C" struct protoent * +cygwin_getprotobynumber (int number) +{ + free_protoent_ptr (protoent_buf); + protoent_buf = dup_protoent_ptr (getprotobynumber (number)); + if (!protoent_buf) + set_winsock_errno (); + + dump_protoent (protoent_buf); + return protoent_buf; +} + +fhandler_socket * +fdsock (int &fd, const char *name, 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); + fhandler_socket *fh = + (fhandler_socket *) cygheap->fdtab.build_fhandler (fd, *socket_dev, name); + if (!fh) + return NULL; + fh->set_io_handle ((HANDLE) soc); + fh->set_flags (O_RDWR | O_BINARY); + fh->set_r_no_interrupt (winsock2_active); + debug_printf ("fd %d, name '%s', soc %p", fd, name, soc); + return fh; +} + +/* exported as socket: standards? */ +extern "C" int +cygwin_socket (int af, int type, int protocol) +{ + int res = -1; + SOCKET soc = 0; + fhandler_socket *fh = NULL; + + cygheap_fdnew fd; + + if (fd >= 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 char *name; + + if (af == AF_INET) + name = (type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp"); + else + name = (type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket"); + + fh = fdsock (fd, name, soc); + if (!fh) + { + closesocket (soc); + res = -1; + } + else + { + fh->set_addr_family (af); + fh->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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + + fhandler_socket *fh = get (fd); + + if (__check_invalid_read_ptr_errno (name, namelen) || !fh) + res = -1; + else + res = fh->connect (name, namelen); + + syscall_printf ("%d = connect (%d, %p, %d)", res, fd, name, namelen); + + return res; +} + +static void +free_servent_ptr (struct servent *&p) +{ + if (p) + { + debug_printf ("servent: %s", p->s_name); + + if (p->s_name) + free (p->s_name); + if (p->s_proto) + free (p->s_proto); + free_char_list (p->s_aliases); + free ((void *) p); + p = NULL; + } +} + +#pragma pack(push,2) +struct pservent +{ + char *s_name; + char **s_aliases; + short s_port; + char *s_proto; +}; + +#pragma pack(pop) +static struct servent * +dup_servent_ptr (struct servent *src) +{ + if (!src) + return NULL; + + struct servent *dst = (struct servent *) calloc (1, sizeof *dst); + + if (!dst) + return NULL; + + debug_printf ("servent: %s", src->s_name); + + dst->s_port = src->s_port; + if (src->s_name && !(dst->s_name = strdup (src->s_name))) + goto out; + if (src->s_aliases && !(dst->s_aliases = dup_char_list (src->s_aliases))) + goto out; + char *s_proto; + + if (IsBadReadPtr (src->s_proto, sizeof (src->s_proto)) + && !IsBadReadPtr (((pservent *) src)->s_proto, sizeof (src->s_proto))) + s_proto = ((pservent *) src)->s_proto; + else + s_proto = src->s_proto; + + if (s_proto && !(dst->s_proto = strdup (s_proto))) + goto out; + + debug_printf ("servent: copied %s", dst->s_name); + + return dst; + +out: + free_servent_ptr (dst); + return NULL; +} + +#ifdef _MT_SAFE +#define servent_buf _reent_winsup ()->_servent_buf +#else +static struct servent *servent_buf = NULL; +#endif + +/* exported as getservbyname: standards? */ +extern "C" struct servent * +cygwin_getservbyname (const char *name, const char *proto) +{ + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + if (check_null_str_errno (name) + || (proto != NULL && check_null_str_errno (proto))) + return NULL; + + free_servent_ptr (servent_buf); + servent_buf = dup_servent_ptr (getservbyname (name, proto)); + if (!servent_buf) + set_winsock_errno (); + + syscall_printf ("%x = getservbyname (%s, %s)", servent_buf, name, proto); + return servent_buf; +} + +/* exported as getservbyport: standards? */ +extern "C" struct servent * +cygwin_getservbyport (int port, const char *proto) +{ + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + if (proto != NULL && check_null_str_errno (proto)) + return NULL; + + free_servent_ptr (servent_buf); + servent_buf = dup_servent_ptr (getservbyport (port, proto)); + if (!servent_buf) + set_winsock_errno (); + + syscall_printf ("%x = getservbyport (%d, %s)", servent_buf, port, proto); + return servent_buf; +} + +extern "C" int +cygwin_gethostname (char *name, size_t len) +{ + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + 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; +} + +static void +free_hostent_ptr (struct hostent *&p) +{ + if (p) + { + debug_printf ("hostent: %s", p->h_name); + + if (p->h_name) + free ((void *) p->h_name); + free_char_list (p->h_aliases); + free_addr_list (p->h_addr_list); + free ((void *) p); + p = NULL; + } +} + +static struct hostent * +dup_hostent_ptr (struct hostent *src) +{ + if (!src) + return NULL; + + struct hostent *dst = (struct hostent *) calloc (1, sizeof *dst); + + if (!dst) + return NULL; + + debug_printf ("hostent: %s", src->h_name); + + dst->h_addrtype = src->h_addrtype; + dst->h_length = src->h_length; + if (src->h_name && !(dst->h_name = strdup (src->h_name))) + goto out; + if (src->h_aliases && !(dst->h_aliases = dup_char_list (src->h_aliases))) + goto out; + if (src->h_addr_list + && !(dst->h_addr_list = dup_addr_list (src->h_addr_list, src->h_length))) + goto out; + + debug_printf ("hostent: copied %s", dst->h_name); + + return dst; + +out: + free_hostent_ptr (dst); + return NULL; +} + +#ifdef _MT_SAFE +#define hostent_buf _reent_winsup ()->_hostent_buf +#else +static struct hostent *hostent_buf = NULL; +#endif + +/* 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 (0); + sigframe thisframe (mainthread); + 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; + } + + free_hostent_ptr (hostent_buf); + hostent_buf = dup_hostent_ptr (gethostbyname (name)); + if (!hostent_buf) + { + set_winsock_errno (); + set_host_errno (); + } + else + { + debug_printf ("h_name %s", hostent_buf->h_name); + h_errno = 0; + } + return hostent_buf; +} + +/* exported as gethostbyaddr: standards? */ +extern "C" struct hostent * +cygwin_gethostbyaddr (const char *addr, int len, int type) +{ + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + if (__check_invalid_read_ptr_errno (addr, len)) + return NULL; + + free_hostent_ptr (hostent_buf); + hostent_buf = dup_hostent_ptr (gethostbyaddr (addr, len, type)); + if (!hostent_buf) + { + set_winsock_errno (); + set_host_errno (); + } + else + { + debug_printf ("h_name %s", hostent_buf->h_name); + h_errno = 0; + } + return hostent_buf; +} + +/* exported as accept: standards? */ +extern "C" int +cygwin_accept (int fd, struct sockaddr *peer, int *len) +{ + int res; + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + + 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 + 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 (0); + sigframe thisframe (mainthread); + 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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + 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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + if (__check_null_invalid_struct_errno (domain, len)) + return -1; + + 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); + + /* FIXME: Are registry keys case sensitive? */ + if (r.error () || r.get_string ("Domain", domain, len, "") != ERROR_SUCCESS) + { + __seterrno (); + return -1; + } + + return 0; +} + +/* 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 (0); + sigframe thisframe (mainthread); + 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 (0); + sigframe thisframe (mainthread); + + 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; + + cygheap_fdnew res_fd; + + if (res_fd < 0) + goto done; + + if (fd2p) + { + cygheap_fdnew newfd (res_fd, false); + + if (*fd2p < 0) + goto done; + *fd2p = newfd; + } + + res = rcmd (ahost, inport, locuser, remuser, cmd, fd2p ? &fd2s : NULL); + if (res == (int) INVALID_SOCKET) + goto done; + else + { + fdsock (res_fd, "/dev/tcp", res); + res = res_fd; + } + + if (fd2p) + fdsock (*fd2p, "/dev/tcp", fd2s); + +done: + syscall_printf ("%d = rcmd (...)", res); + return res; +} + +/* exported as rresvport: standards? */ +extern "C" int +cygwin_rresvport (int *port) +{ + int res; + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + + if (check_null_invalid_struct_errno (port)) + return -1; + + cygheap_fdnew res_fd; + + if (res_fd < 0) + res = -1; + else + { + res = rresvport (port); + + if (res != (int) INVALID_SOCKET) + { + fdsock (res_fd, "/dev/tcp", res); + res = res_fd; + } + } + + 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 (0); + sigframe thisframe (mainthread); + + 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; + + cygheap_fdnew res_fd; + + if (res_fd < 0) + goto done; + if (fd2p) + { + cygheap_fdnew newfd (res_fd); + + if (newfd < 0) + goto done; + *fd2p = newfd; + } + res = rexec (ahost, inport, locuser, password, cmd, fd2p ? &fd2s : NULL); + if (res == (int) INVALID_SOCKET) + goto done; + else + { + fdsock (res_fd, "/dev/tcp", res); + res = res_fd; + } + if (fd2p) + fdsock (*fd2p, "/dev/tcp", fd2s); + +done: + 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; + cygheap_fdnew sb0; + fhandler_socket *fh; + + sig_dispatch_pending (0); + sigframe thisframe (mainthread); + 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; + } + + if (sb0 < 0) + goto done; + else + { + sb[0] = sb0; + cygheap_fdnew sb1 (sb0, false); + + if (sb1 < 0) + goto done; + + sb[1] = sb1; + } + + /* 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; + } + + res = 0; + + if (family == AF_LOCAL) + { + + fh = fdsock (sb[0], + type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket", + insock); + fh->set_sun_path (""); + fh->set_addr_family (AF_LOCAL); + fh->set_socket_type (type); + fh = fdsock (sb[1], + type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket", + outsock); + fh->set_sun_path (""); + fh->set_addr_family (AF_LOCAL); + fh->set_socket_type (type); + } + else + { + fh = fdsock (sb[0], type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp", + insock); + fh->set_addr_family (AF_INET); + fh->set_socket_type (type); + fh = fdsock (sb[1], type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp", + outsock); + fh->set_addr_family (AF_INET); + fh->set_socket_type (type); + } + +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 (0); + sigframe thisframe (mainthread); + + 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 (0); + sigframe thisframe (mainthread); + + 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/path.cc b/winsup/cygwin/path.cc new file mode 100644 index 00000000000..bb8870a9333 --- /dev/null +++ b/winsup/cygwin/path.cc @@ -0,0 +1,3588 @@ +/* path.cc: path 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. */ + +/* This module's job is to + - convert between POSIX and Win32 style filenames, + - support the `mount' functionality, + - support symlinks for files and directories + + Pathnames are handled as follows: + + - A \ or : in a path denotes a pure windows spec. + - Paths beginning with // (or \\) are not translated (i.e. looked + up in the mount table) and are assumed to be UNC path names. + + The goal in the above set of rules is to allow both POSIX and Win32 + flavors of pathnames without either interfering. The rules are + intended to be as close to a superset of both as possible. + + Note that you can have more than one path to a file. The mount + table is always prefered when translating Win32 paths to POSIX + paths. Win32 paths in mount table entries may be UNC paths or + standard Win32 paths starting with <drive-letter>: + + Text vs Binary issues are not considered here in path style + decisions, although the appropriate flags are retrieved and + stored in various structures. + + Removing mounted filesystem support would simplify things greatly, + but having it gives us a mechanism of treating disk that lives on a + UNIX machine as having UNIX semantics [it allows one to edit a text + file on that disk and not have cr's magically appear and perhaps + break apps running on UNIX boxes]. It also useful to be able to + layout a hierarchy without changing the underlying directories. + + The semantics of mounting file systems is not intended to precisely + follow normal UNIX systems. + + Each DOS drive is defined to have a current directory. Supporting + this would complicate things so for now things are defined so that + c: means c:\. FIXME: Is this still true? +*/ + +#include "winsup.h" +#include <stdio.h> +#include <stdlib.h> +#include <sys/mount.h> +#include <mntent.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <winioctl.h> +#include <wingdi.h> +#include <winuser.h> +#include <winnls.h> +#include <winnetwk.h> +#include <sys/cygwin.h> +#include <cygwin/version.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "sync.h" +#include "sigproc.h" +#include "pinfo.h" +#include "dtable.h" +#include "cygheap.h" +#include "shared_info.h" +#include "registry.h" +#include <assert.h> + +#ifdef _MT_SAFE +#define iteration _reent_winsup ()->_iteration +#define available_drives _reent_winsup ()->available_drives +#else +static int iteration; +static DWORD available_drives; +#endif + +static int normalize_win32_path (const char *src, char *dst); +static void slashify (const char *src, char *dst, int trailing_slash_p); +static void backslashify (const char *src, char *dst, int trailing_slash_p); + +struct symlink_info +{ + char contents[MAX_PATH + 4]; + char *ext_here; + int extn; + unsigned pflags; + DWORD fileattr; + int is_symlink; + bool ext_tacked_on; + int error; + bool case_clash; + _major_t major; + _minor_t minor; + _mode_t mode; + int check (char *path, const suffix_info *suffixes, unsigned opt); + bool parse_device (const char *); + BOOL case_check (char *path); +}; + +int pcheck_case = PCHECK_RELAXED; /* Determines the case check behaviour. */ + +static char shortcut_header[SHORTCUT_HDR_SIZE]; +static BOOL shortcut_initalized; + +static void +create_shortcut_header (void) +{ + if (!shortcut_initalized) + { + shortcut_header[0] = 'L'; + shortcut_header[4] = '\001'; + shortcut_header[5] = '\024'; + shortcut_header[6] = '\002'; + shortcut_header[12] = '\300'; + shortcut_header[19] = 'F'; + shortcut_header[20] = '\f'; + shortcut_header[60] = '\001'; + shortcut_initalized = TRUE; + } +} + +#define CYGWIN_REGNAME (cygheap->cygwin_regname ?: CYGWIN_INFO_CYGWIN_REGISTRY_NAME) + +/* Determine if path prefix matches current cygdrive */ +#define iscygdrive(path) \ + (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len)) + +#define iscygdrive_device(path) \ + (isalpha (path[mount_table->cygdrive_len]) && \ + (path[mount_table->cygdrive_len + 1] == '/' || \ + !path[mount_table->cygdrive_len + 1])) + +#define isproc(path) \ + (path_prefix_p (proc, (path), proc_len)) + +#define isvirtual_dev(devn) \ + (devn == FH_CYGDRIVE || devn == FH_PROC || devn == FH_REGISTRY || devn == FH_PROCESS) + +/* Return non-zero if PATH1 is a prefix of PATH2. + Both are assumed to be of the same path style and / vs \ usage. + Neither may be "". + LEN1 = strlen (PATH1). It's passed because often it's already known. + + Examples: + /foo/ is a prefix of /foo <-- may seem odd, but desired + /foo is a prefix of /foo/ + / is a prefix of /foo/bar + / is not a prefix of foo/bar + foo/ is a prefix foo/bar + /foo is not a prefix of /foobar +*/ + +int +path_prefix_p (const char *path1, const char *path2, int len1) +{ + /* Handle case where PATH1 has trailing '/' and when it doesn't. */ + if (len1 > 0 && isdirsep (path1[len1 - 1])) + len1--; + + if (len1 == 0) + return isdirsep (path2[0]) && !isdirsep (path2[1]); + + if (!pathnmatch (path1, path2, len1)) + return 0; + + return isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':'; +} + +/* Return non-zero if paths match in first len chars. + Check is dependent of the case sensitivity setting. */ +int +pathnmatch (const char *path1, const char *path2, int len) +{ + return pcheck_case == PCHECK_STRICT ? !strncmp (path1, path2, len) + : strncasematch (path1, path2, len); +} + +/* Return non-zero if paths match. Check is dependent of the case + sensitivity setting. */ +int +pathmatch (const char *path1, const char *path2) +{ + return pcheck_case == PCHECK_STRICT ? !strcmp (path1, path2) + : strcasematch (path1, path2); +} + +/* Normalize a POSIX path. + \'s are converted to /'s in the process. + All duplicate /'s, except for 2 leading /'s, are deleted. + The result is 0 for success, or an errno error value. */ + +#define isslash(c) ((c) == '/') + +static int +normalize_posix_path (const char *src, char *dst) +{ + const char *src_start = src; + char *dst_start = dst; + + syscall_printf ("src %s", src); + + if (isdrive (src) || strpbrk (src, "\\:")) + { + int err = normalize_win32_path (src, dst); + if (!err) + for (char *p = dst; (p = strchr (p, '\\')); p++) + *p = '/'; + return err; + } + + if (!isslash (src[0])) + { + if (!cygheap->cwd.get (dst)) + return get_errno (); + dst = strchr (dst, '\0'); + if (*src == '.') + { + if (dst == dst_start + 1 && *dst_start == '/') + --dst; + goto sawdot; + } + if (dst > dst_start && !isslash (dst[-1])) + *dst++ = '/'; + } + /* Two leading /'s? If so, preserve them. */ + else if (isslash (src[1])) + { + *dst++ = '/'; + *dst++ = '/'; + src += 2; + if (isslash (*src)) + { /* Starts with three or more slashes - reset. */ + dst = dst_start; + *dst++ = '/'; + src = src_start + 1; + } + else if (src[0] == '.' && isslash (src[1])) + { + *dst++ = '.'; + *dst++ = '/'; + src += 2; + } + } + else + *dst = '\0'; + + while (*src) + { + /* Strip runs of /'s. */ + if (!isslash (*src)) + *dst++ = *src++; + else + { + while (*++src) + { + if (isslash (*src)) + continue; + + if (*src != '.') + break; + + sawdot: + if (src[1] != '.') + { + if (!src[1]) + { + if (dst == dst_start) + *dst++ = '/'; + goto done; + } + if (!isslash (src[1])) + break; + } + else if (src[2] && !isslash (src[2])) + { + if (src[2] == '.') + return ENOENT; + break; + } + else + { + while (dst > dst_start && !isslash (*--dst)) + continue; + src++; + } + } + + *dst++ = '/'; + } + if ((dst - dst_start) >= MAX_PATH) + { + debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src); + return ENAMETOOLONG; + } + } + +done: + *dst = '\0'; + if (--dst > dst_start && isslash (*dst)) + *dst = '\0'; + + debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start); + return 0; +} + +inline void +path_conv::add_ext_from_sym (symlink_info &sym) +{ + if (sym.ext_here && *sym.ext_here) + { + known_suffix = path + sym.extn; + if (sym.ext_tacked_on) + strcpy (known_suffix, sym.ext_here); + } +} + +static void __stdcall mkrelpath (char *dst) __attribute__ ((regparm (2))); +static void __stdcall +mkrelpath (char *path) +{ + char cwd_win32[MAX_PATH]; + if (!cygheap->cwd.get (cwd_win32, 0)) + return; + + unsigned cwdlen = strlen (cwd_win32); + if (!path_prefix_p (cwd_win32, path, cwdlen)) + return; + + size_t n = strlen (path); + if (n < cwdlen) + return; + + char *tail = path; + if (n == cwdlen) + tail += cwdlen; + else + tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1; + + memmove (path, tail, strlen (tail) + 1); + if (!*path) + strcpy (path, "."); +} + +bool +fs_info::update (const char *win32_path) +{ + char tmp_buf [MAX_PATH]; + strncpy (tmp_buf, win32_path, MAX_PATH); + + if (!rootdir (tmp_buf)) + { + debug_printf ("Cannot get root component of path %s", win32_path); + name [0] = '\0'; + sym_opt = flags = serial = 0; + return false; + } + + if (strcmp (tmp_buf, root_dir) == 0) + return 1; + + strncpy (root_dir, tmp_buf, MAX_PATH); + drive_type = GetDriveType (root_dir); + if (drive_type == DRIVE_REMOTE || (drive_type == DRIVE_UNKNOWN && (root_dir[0] == '\\' && root_dir[1] == '\\'))) + is_remote_drive = 1; + else + is_remote_drive = 0; + + if (!GetVolumeInformation (root_dir, NULL, 0, &serial, NULL, &flags, + name, sizeof (name))) + { + debug_printf ("Cannot get volume information (%s), %E", root_dir); + name [0] = '\0'; + sym_opt = flags = serial = 0; + return false; + } + /* FIXME: Samba by default returns "NTFS" in file system name, but + * doesn't support Extended Attributes. If there's some fast way to + * distinguish between samba and real ntfs, it should be implemented + * here. + */ + sym_opt = (!is_remote_drive && strcmp (name, "NTFS") == 0) ? PC_CHECK_EA : 0; + + return true; +} + +char * +path_conv::return_and_clear_normalized_path () +{ + char *s = normalized_path; + normalized_path = NULL; + return s; +} + +void +path_conv::fillin (HANDLE h) +{ + BY_HANDLE_FILE_INFORMATION local; + if (!GetFileInformationByHandle (h, &local)) + { + fileattr = INVALID_FILE_ATTRIBUTES; + fs.serial = 0; + } + else + { + fileattr = local.dwFileAttributes; + fs.serial = local.dwVolumeSerialNumber; + } + fs.drive_type = DRIVE_UNKNOWN; +} + +/* Convert an arbitrary path SRC to a pure Win32 path, suitable for + passing to Win32 API routines. + + If an error occurs, `error' is set to the errno value. + Otherwise it is set to 0. + + follow_mode values: + SYMLINK_FOLLOW - convert to PATH symlink points to + SYMLINK_NOFOLLOW - convert to PATH of symlink itself + SYMLINK_IGNORE - do not check PATH for symlinks + SYMLINK_CONTENTS - just return symlink contents +*/ + +void +path_conv::check (const char *src, unsigned opt, + const suffix_info *suffixes) +{ + /* This array is used when expanding symlinks. It is MAX_PATH * 2 + in length so that we can hold the expanded symlink plus a + trailer. */ + char path_copy[MAX_PATH + 3]; + char tmp_buf[2 * MAX_PATH + 3]; + symlink_info sym; + bool need_directory = 0; + bool saw_symlinks = 0; + int is_relpath; + char *tail; + sigframe thisframe (mainthread); + +#if 0 + static path_conv last_path_conv; + static char last_src[MAX_PATH + 1]; + + if (*last_src && strcmp (last_src, src) == 0) + { + *this = last_path_conv; + return; + } +#endif + + int loop = 0; + path_flags = 0; + known_suffix = NULL; + fileattr = INVALID_FILE_ATTRIBUTES; + case_clash = false; + memset (&dev, 0, sizeof (dev)); + fs.root_dir[0] = '\0'; + fs.name[0] = '\0'; + fs.flags = fs.serial = 0; + fs.sym_opt = 0; + fs.drive_type = 0; + fs.is_remote_drive = 0; + normalized_path = NULL; + + if (!(opt & PC_NULLEMPTY)) + error = 0; + else if ((error = check_null_empty_str (src))) + return; + /* This loop handles symlink expansion. */ + for (;;) + { + MALLOC_CHECK; + assert (src); + + char *p = strrchr (src, '\0'); + /* Detect if the user was looking for a directory. We have to strip the + trailing slash initially and add it back on at the end due to Windows + brain damage. */ + if (--p > src) + { + if (isdirsep (*p)) + need_directory = 1; + else if (--p > src && p[1] == '.' && isdirsep (*p)) + need_directory = 1; + } + + is_relpath = !isabspath (src); + error = normalize_posix_path (src, path_copy); + if (error) + return; + + tail = strchr (path_copy, '\0'); // Point to end of copy + char *path_end = tail; + tail[1] = '\0'; + + /* Scan path_copy from right to left looking either for a symlink + or an actual existing file. If an existing file is found, just + return. If a symlink is found exit the for loop. + Also: be careful to preserve the errno returned from + symlink.check as the caller may need it. */ + /* FIXME: Do we have to worry about multiple \'s here? */ + int component = 0; // Number of translated components + sym.contents[0] = '\0'; + + for (;;) + { + const suffix_info *suff; + char pathbuf[MAX_PATH]; + char *full_path; + + /* Don't allow symlink.check to set anything in the path_conv + class if we're working on an inner component of the path */ + if (component) + { + suff = NULL; + sym.pflags = 0; + full_path = pathbuf; + } + else + { + suff = suffixes; + sym.pflags = path_flags; + full_path = this->path; + } + + /* Convert to native path spec sans symbolic link info. */ + error = mount_table->conv_to_win32_path (path_copy, full_path, dev, + &sym.pflags, 1); + + if (error) + return; + + if (dev.major == DEV_CYGDRIVE_MAJOR) + { + if (!component) + fileattr = FILE_ATTRIBUTE_DIRECTORY; + else + { + dev.devn = FH_BAD; + fileattr = GetFileAttributes (this->path); + } + goto out; + } + else if (isvirtual_dev (dev.devn)) + { + /* FIXME: Calling build_fhandler here is not the right way to handle this. */ + fhandler_virtual *fh = + (fhandler_virtual *) cygheap->fdtab.build_fhandler (-1, dev, (const char *) path_copy, NULL); + int file_type = fh->exists (); + switch (file_type) + { + case 1: + case 2: + fileattr = FILE_ATTRIBUTE_DIRECTORY; + break; + case -1: + fileattr = 0; + break; + default: + fileattr = INVALID_FILE_ATTRIBUTES; + break; + } + delete fh; + goto out; + } + /* devn should not be a device. If it is, then stop parsing now. */ + else if (dev.devn != FH_BAD) + { + fileattr = 0; + path_flags = sym.pflags; + if (component) + { + error = ENOTDIR; + return; + } + goto out; /* Found a device. Stop parsing. */ + } + + if (!fs.update (full_path)) + fs.root_dir[0] = '\0'; + + /* Eat trailing slashes */ + char *dostail = strchr (full_path, '\0'); + + /* If path is only a drivename, Windows interprets it as the + current working directory on this drive instead of the root + dir which is what we want. So we need the trailing backslash + in this case. */ + while (dostail > full_path + 3 && (*--dostail == '\\')) + *tail = '\0'; + + if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') + { + full_path[2] = '\\'; + full_path[3] = '\0'; + } + + if ((opt & PC_SYM_IGNORE) && pcheck_case == PCHECK_RELAXED) + { + fileattr = GetFileAttributes (this->path); + goto out; + } + + int len = sym.check (full_path, suff, opt | fs.sym_opt); + + if (sym.minor || sym.major) + { + dev.parse (sym.major, sym.minor); + if (!dev) + error = ENODEV; + else + { + dev.setfs (1); + dev.mode = sym.mode; + fileattr = sym.fileattr; + } + goto out; + } + + if (sym.case_clash) + { + if (pcheck_case == PCHECK_STRICT) + { + case_clash = TRUE; + error = ENOENT; + goto out; + } + /* If pcheck_case==PCHECK_ADJUST the case_clash is remembered + if the last component is concerned. This allows functions + which shall create files to avoid overriding already existing + files with another case. */ + if (!component) + case_clash = TRUE; + } + if (!(opt & PC_SYM_IGNORE)) + { + if (!component) + { + fileattr = sym.fileattr; + path_flags = sym.pflags; + } + + /* If symlink.check found an existing non-symlink file, then + it sets the appropriate flag. It also sets any suffix found + into `ext_here'. */ + if (!sym.is_symlink && sym.fileattr != INVALID_FILE_ATTRIBUTES) + { + error = sym.error; + if (component == 0) + add_ext_from_sym (sym); + if (pcheck_case == PCHECK_RELAXED) + goto out; // file found + /* Avoid further symlink evaluation. Only case checks are + done now. */ + opt |= PC_SYM_IGNORE; + } + /* Found a symlink if len > 0. If component == 0, then the + src path itself was a symlink. If !follow_mode then + we're done. Otherwise we have to insert the path found + into the full path that we are building and perform all of + these operations again on the newly derived path. */ + else if (len > 0) + { + saw_symlinks = 1; + if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW)) + { + set_symlink (); // last component of path is a symlink. + if (opt & PC_SYM_CONTENTS) + { + strcpy (path, sym.contents); + goto out; + } + add_ext_from_sym (sym); + if (pcheck_case == PCHECK_RELAXED) + goto out; + /* Avoid further symlink evaluation. Only case checks are + done now. */ + opt |= PC_SYM_IGNORE; + } + else + break; + } + /* No existing file found. */ + } + + /* Find the "tail" of the path, e.g. in '/for/bar/baz', + /baz is the tail. */ + char *newtail = strrchr (path_copy, '/'); + if (tail != path_end) + *tail = '/'; + + /* Exit loop if there is no tail or we are at the + beginning of a UNC path */ + if (!newtail || newtail == path_copy || (newtail == path_copy + 1 && newtail[-1] == '/')) + goto out; // all done + + tail = newtail; + + /* Haven't found an existing pathname component yet. + Pinch off the tail and try again. */ + *tail = '\0'; + component++; + } + + /* Arrive here if above loop detected a symlink. */ + if (++loop > MAX_LINK_DEPTH) + { + error = ELOOP; // Eep. + return; + } + + MALLOC_CHECK; + + /* The tail is pointing at a null pointer. Increment it and get the length. + If the tail was empty then this increment will end up pointing to the extra + \0 added to path_copy above. */ + int taillen = strlen (++tail); + int buflen = strlen (sym.contents); + if (buflen + taillen > MAX_PATH) + { + error = ENAMETOOLONG; + strcpy (path, "::ENAMETOOLONG::"); + return; + } + + /* Strip off current directory component since this is the part that refers + to the symbolic link. */ + if ((p = strrchr (path_copy, '/')) == NULL) + p = path_copy; + else if (p == path_copy) + p++; + *p = '\0'; + + char *headptr; + if (isabspath (sym.contents)) + headptr = tmp_buf; /* absolute path */ + else + { + /* Copy the first part of the path and point to the end. */ + strcpy (tmp_buf, path_copy); + headptr = strchr (tmp_buf, '\0'); + } + + /* See if we need to separate first part + symlink contents with a / */ + if (headptr > tmp_buf && headptr[-1] != '/') + *headptr++ = '/'; + + /* Copy the symlink contents to the end of tmp_buf. + Convert slashes. FIXME? */ + for (p = sym.contents; *p; p++) + *headptr++ = *p == '\\' ? '/' : *p; + + /* Copy any tail component */ + if (tail >= path_end) + *headptr = '\0'; + else + { + *headptr++ = '/'; + strcpy (headptr, tail); + } + + /* Now evaluate everything all over again. */ + src = tmp_buf; + } + + if (!(opt & PC_SYM_CONTENTS)) + add_ext_from_sym (sym); + +out: + if (opt & PC_POSIX) + { + if (tail[1] != '\0') + *tail = '/'; + normalized_path = cstrdup (path_copy); + } + /* Deal with Windows stupidity which considers filename\. to be valid + even when "filename" is not a directory. */ + if (!need_directory || error) + /* nothing to do */; + else if (fileattr & FILE_ATTRIBUTE_DIRECTORY) + path_flags &= ~PATH_SYMLINK; + else + { + debug_printf ("%s is a non-directory", path); + error = ENOTDIR; + return; + } + + if (dev.devn == FH_BAD) + { + if (!fs.update (path)) + { + fs.root_dir[0] = '\0'; + set_has_acls (false); + set_has_buggy_open (false); + } + else + { + set_isdisk (); + debug_printf ("root_dir(%s), this->path(%s), set_has_acls(%d)", + fs.root_dir, this->path, fs.flags & FS_PERSISTENT_ACLS); + if (!allow_smbntsec && fs.is_remote_drive) + set_has_acls (false); + else + set_has_acls (fs.flags & FS_PERSISTENT_ACLS); + /* Known file systems with buggy open calls. Further explanation + in fhandler.cc (fhandler_disk_file::open). */ + set_has_buggy_open (strcmp (fs.name, "SUNWNFS") == 0); + } + } +#if 0 + if (issocket ()) + devn = FH_SOCKET; +#endif + + if (!(opt & PC_FULL)) + { + if (is_relpath) + mkrelpath (this->path); + if (need_directory) + { + size_t n = strlen (this->path); + /* Do not add trailing \ to UNC device names like \\.\a: */ + if (this->path[n - 1] != '\\' && + (strncmp (this->path, "\\\\.\\", 4) != 0 || + !strncasematch (this->path + 4, "unc\\", 4))) + { + this->path[n] = '\\'; + this->path[n + 1] = '\0'; + } + } + } + + if (saw_symlinks) + set_has_symlinks (); + + if (!error && !isdir () && !(path_flags & PATH_ALL_EXEC)) + { + const char *p = strchr (path, '\0') - 4; + if (p >= path && + (strcasematch (".exe", p) || + strcasematch (".bat", p) || + strcasematch (".com", p))) + path_flags |= PATH_EXEC; + } + +#if 0 + if (!error) + { + last_path_conv = *this; + strcpy (last_src, src); + } +#endif +} + +static __inline int +digits (const char *name) +{ + char *p; + int n = strtol (name, &p, 10); + + return p > name && !*p ? n : -1; +} + + + +/* Return TRUE if src_path is a Win32 device name, filling out the device + name in win32_path */ + +static BOOL +win32_device_name (const char *src_path, char *win32_path, device& dev) +{ + dev.parse (src_path); + + if (dev.devn == FH_BAD) + return false; + + switch (dev.devn) + { + case FH_TAPE: + __small_sprintf (win32_path, dev.fmt, dev.minor % 128); + break; + case FH_RAWDRIVE: + __small_sprintf (win32_path, dev.fmt, dev.minor - 224 + 'A'); + break; + default: + __small_sprintf (win32_path, dev.fmt, dev.minor); + break; + } + return true; +} + +/* Normalize a Win32 path. + /'s are converted to \'s in the process. + All duplicate \'s, except for 2 leading \'s, are deleted. + + The result is 0 for success, or an errno error value. + FIXME: A lot of this should be mergeable with the POSIX critter. */ +static int +normalize_win32_path (const char *src, char *dst) +{ + const char *src_start = src; + char *dst_start = dst; + char *dst_root_start = dst; + bool beg_src_slash = isdirsep (src[0]); + + if (beg_src_slash && isdirsep (src[1])) + { + *dst++ = '\\'; + src++; + if (src[1] == '.' && isdirsep (src[2])) + { + *dst++ = '\\'; + *dst++ = '.'; + src += 2; + } + } + else if (strchr (src, ':') == NULL && *src != '/') + { + if (!cygheap->cwd.get (dst, 0)) + return get_errno (); + if (beg_src_slash) + { + if (dst[1] == ':') + dst[2] = '\0'; + else if (slash_unc_prefix_p (dst)) + { + char *p = strpbrk (dst + 2, "\\/"); + if (p && (p = strpbrk (p + 1, "\\/"))) + *p = '\0'; + } + } + if (strlen (dst) + 1 + strlen (src) >= MAX_PATH) + { + debug_printf ("ENAMETOOLONG = normalize_win32_path (%s)", src); + return ENAMETOOLONG; + } + dst += strlen (dst); + if (!beg_src_slash) + *dst++ = '\\'; + } + + while (*src) + { + /* Strip duplicate /'s. */ + if (isdirsep (src[0]) && isdirsep (src[1])) + src++; + /* Ignore "./". */ + else if (src[0] == '.' && isdirsep (src[1]) + && (src == src_start || isdirsep (src[-1]))) + src += 2; + + /* Backup if "..". */ + else if (src[0] == '.' && src[1] == '.' + /* dst must be greater than dst_start */ + && dst[-1] == '\\' + && (isdirsep (src[2]) || src[2] == 0)) + { + /* Back up over /, but not if it's the first one. */ + if (dst > dst_root_start + 1) + dst--; + /* Now back up to the next /. */ + while (dst > dst_root_start + 1 && dst[-1] != '\\' && dst[-2] != ':') + dst--; + src += 2; + if (isdirsep (*src)) + src++; + } + /* Otherwise, add char to result. */ + else + { + if (*src == '/') + *dst++ = '\\'; + else + *dst++ = *src; + ++src; + } + if ((dst - dst_start) >= MAX_PATH) + return ENAMETOOLONG; + } + *dst = 0; + debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start); + return 0; +} + +/* Various utilities. */ + +/* slashify: Convert all back slashes in src path to forward slashes + in dst path. Add a trailing slash to dst when trailing_slash_p arg + is set to 1. */ + +static void +slashify (const char *src, char *dst, int trailing_slash_p) +{ + const char *start = src; + + while (*src) + { + if (*src == '\\') + *dst++ = '/'; + else + *dst++ = *src; + ++src; + } + if (trailing_slash_p + && src > start + && !isdirsep (src[-1])) + *dst++ = '/'; + *dst++ = 0; +} + +/* backslashify: Convert all forward slashes in src path to back slashes + in dst path. Add a trailing slash to dst when trailing_slash_p arg + is set to 1. */ + +static void +backslashify (const char *src, char *dst, int trailing_slash_p) +{ + const char *start = src; + + while (*src) + { + if (*src == '/') + *dst++ = '\\'; + else + *dst++ = *src; + ++src; + } + if (trailing_slash_p + && src > start + && !isdirsep (src[-1])) + *dst++ = '\\'; + *dst++ = 0; +} + +/* nofinalslash: Remove trailing / and \ from SRC (except for the + first one). It is ok for src == dst. */ + +void __stdcall +nofinalslash (const char *src, char *dst) +{ + int len = strlen (src); + if (src != dst) + memcpy (dst, src, len + 1); + while (len > 1 && isdirsep (dst[--len])) + dst[len] = '\0'; +} + +/* slash_unc_prefix_p: Return non-zero if PATH begins with //UNC/SHARE */ + +int __stdcall +slash_unc_prefix_p (const char *path) +{ + char *p = NULL; + int ret = (isdirsep (path[0]) + && isdirsep (path[1]) + && isalpha (path[2]) + && path[3] != 0 + && !isdirsep (path[3]) + && ((p = strpbrk (path + 3, "\\/")) != NULL)); + if (!ret || p == NULL) + return ret; + return ret && isalnum (p[1]); +} + +/* conv_path_list: Convert a list of path names to/from Win32/POSIX. */ + +static void +conv_path_list (const char *src, char *dst, int to_posix_p) +{ + char *s; + char *d = dst; + char src_delim = to_posix_p ? ';' : ':'; + char dst_delim = to_posix_p ? ':' : ';'; + int (*conv_fn) (const char *, char *) = (to_posix_p + ? cygwin_conv_to_posix_path + : cygwin_conv_to_win32_path); + + char *srcbuf = (char *) alloca (strlen (src) + 1); + + for (;;) + { + s = strccpy (srcbuf, &src, src_delim); + int len = s - srcbuf; + if (len >= MAX_PATH) + srcbuf[MAX_PATH - 1] = '\0'; + (*conv_fn) (len ? srcbuf : ".", d); + if (!*src++) + break; + d = strchr (d, '\0'); + *d++ = dst_delim; + } +} + +/* init: Initialize the mount table. */ + +void +mount_info::init () +{ + nmounts = 0; + + /* Fetch the mount table and cygdrive-related information from + the registry. */ + from_registry (); +} + +static void +set_flags (unsigned *flags, unsigned val) +{ + *flags = val; + if (!(*flags & PATH_BINARY)) + { + *flags |= PATH_TEXT; + debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY)); + } + else + { + *flags |= PATH_BINARY; + debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY)); + } +} + +/* CGF FIXME device */ +static const device dev_proc = +{"/proc", FH_PROC, "/proc", 0, 0, 0, 0}; + +static const device dev_cygdrive = +{"/cygdrive", FH_CYGDRIVE, "/cygdrive", 0, 0, 0, 0}; + +static const device dev_fs = +{"", FH_FS, "", 0, 0, 0, 0}; + +/* conv_to_win32_path: Ensure src_path is a pure Win32 path and store + the result in win32_path. + + If win32_path != NULL, the relative path, if possible to keep, is + stored in win32_path. If the relative path isn't possible to keep, + the full path is stored. + + If full_win32_path != NULL, the full path is stored there. + + The result is zero for success, or an errno value. + + {,full_}win32_path must have sufficient space (i.e. MAX_PATH bytes). */ + +int +mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev, + unsigned *flags, bool no_normalize) +{ + while (sys_mount_table_counter < cygwin_shared->sys_mount_table_counter) + { + init (); + sys_mount_table_counter++; + } + int src_path_len = strlen (src_path); + MALLOC_CHECK; + unsigned dummy_flags; + int chroot_ok = !cygheap->root.exists (); + + dev.devn = FH_BAD; + + if (!flags) + flags = &dummy_flags; + + *flags = 0; + debug_printf ("conv_to_win32_path (%s)", src_path); + + if (src_path_len >= MAX_PATH) + { + debug_printf ("ENAMETOOLONG = conv_to_win32_path (%s)", src_path); + return ENAMETOOLONG; + } + + int i, rc; + mount_item *mi = NULL; /* initialized to avoid compiler warning */ + char pathbuf[MAX_PATH]; + + if (dst == NULL) + goto out; /* Sanity check. */ + + /* An MS-DOS spec has either a : or a \. If this is found, short + circuit most of the rest of this function. */ + if (strpbrk (src_path, ":\\") != NULL || slash_unc_prefix_p (src_path)) + { + debug_printf ("%s already win32", src_path); + rc = normalize_win32_path (src_path, dst); + if (rc) + { + debug_printf ("normalize_win32_path failed, rc %d", rc); + return rc; + } + + set_flags (flags, (unsigned) set_flags_from_win32_path (dst)); + goto out; + } + + /* Normalize the path, taking out ../../ stuff, we need to do this + so that we can move from one mounted directory to another with relative + stuff. + + eg mounting c:/foo /foo + d:/bar /bar + + cd /bar + ls ../foo + + should look in c:/foo, not d:/foo. + + We do this by first getting an absolute UNIX-style path and then + converting it to a DOS-style path, looking up the appropriate drive + in the mount table. */ + + if (no_normalize) + strcpy (pathbuf, src_path); + else + { + rc = normalize_posix_path (src_path, pathbuf); + + if (rc) + { + debug_printf ("%d = conv_to_win32_path (%s)", rc, src_path); + return rc; + } + } + + /* See if this is a cygwin "device" */ + if (win32_device_name (pathbuf, dst, dev)) + { + *flags = MOUNT_BINARY; /* FIXME: Is this a sensible default for devices? */ + rc = 0; + goto out_no_chroot_check; + } + + /* Check if the cygdrive prefix was specified. If so, just strip + off the prefix and transform it into an MS-DOS path. */ + MALLOC_CHECK; + if (isproc (pathbuf)) + { + dev = dev_proc; + dev.devn = fhandler_proc::get_proc_fhandler (pathbuf); + if (dev.devn == FH_BAD) + return ENOENT; + } + else if (iscygdrive (pathbuf)) + { + int n = mount_table->cygdrive_len - 1; + int unit; + + if (!pathbuf[n] || + (pathbuf[n] == '/' && pathbuf[n + 1] == '.' && !pathbuf[n + 2])) + { + unit = 0; + dst[0] = '\0'; + if (mount_table->cygdrive_len > 1) + dev = dev_cygdrive; + } + else if (cygdrive_win32_path (pathbuf, dst, unit)) + { + set_flags (flags, (unsigned) cygdrive_flags); + goto out; + } + else if (mount_table->cygdrive_len > 1) + return ENOENT; + } + + int chrooted_path_len; + chrooted_path_len = 0; + /* Check the mount table for prefix matches. */ + for (i = 0; i < nmounts; i++) + { + const char *path; + int len; + + mi = mount + posix_sorted[i]; + if (!cygheap->root.exists () + || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/')) + { + path = mi->posix_path; + len = mi->posix_pathlen; + } + else if (cygheap->root.posix_ok (mi->posix_path)) + { + path = cygheap->root.unchroot (mi->posix_path); + chrooted_path_len = len = strlen (path); + } + else + { + chrooted_path_len = 0; + continue; + } + + if (path_prefix_p (path, pathbuf, len)) + break; + } + + if (i >= nmounts) + { + backslashify (pathbuf, dst, 0); /* just convert */ + set_flags (flags, PATH_BINARY); + } + else + { + int n; + const char *native_path; + int posix_pathlen; + if (chroot_ok || chrooted_path_len || mi->posix_pathlen != 1 + || mi->posix_path[0] != '/') + { + n = mi->native_pathlen; + native_path = mi->native_path; + posix_pathlen = chrooted_path_len ?: mi->posix_pathlen; + chroot_ok = 1; + } + else + { + n = cygheap->root.native_length (); + native_path = cygheap->root.native_path (); + posix_pathlen = mi->posix_pathlen; + chroot_ok = 1; + } + memcpy (dst, native_path, n + 1); + const char *p = pathbuf + posix_pathlen; + if (*p == '/') + /* nothing */; + else if ((isdrive (dst) && !dst[2]) || *p) + dst[n++] = '\\'; + strcpy (dst + n, p); + backslashify (dst, dst, 0); + set_flags (flags, (unsigned) mi->flags); + } + + if (!isvirtual_dev (dev.devn)) + win32_device_name (src_path, dst, dev); + + out: + MALLOC_CHECK; + if (chroot_ok || cygheap->root.ischroot_native (dst)) + rc = 0; + else + { + debug_printf ("attempt to access outside of chroot '%s = %s'", + cygheap->root.posix_path (), cygheap->root.native_path ()); + rc = ENOENT; + } + + out_no_chroot_check: + debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc); + return rc; +} + +/* cygdrive_posix_path: Build POSIX path used as the + mount point for cygdrives created when there is no other way to + obtain a POSIX path from a Win32 one. */ + +void +mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p) +{ + int len = cygdrive_len; + + memcpy (dst, cygdrive, len + 1); + + /* Now finish the path off with the drive letter to be used. + The cygdrive prefix always ends with a trailing slash so + the drive letter is added after the path. */ + dst[len++] = cyg_tolower (src[0]); + if (!src[2] || (isdirsep (src[2]) && !src[3])) + dst[len++] = '\000'; + else + { + int n; + dst[len++] = '/'; + if (isdirsep (src[2])) + n = 3; + else + n = 2; + strcpy (dst + len, src + n); + } + slashify (dst, dst, trailing_slash_p); +} + +int +mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit) +{ + int res; + const char *p = src + cygdrive_len; + if (!isalpha (*p) || (!isdirsep (p[1]) && p[1])) + { + unit = -1; /* FIXME: should be zero, maybe? */ + dst[0] = '\0'; + res = 0; + } + else + { + dst[0] = cyg_tolower (*p); + dst[1] = ':'; + strcpy (dst + 2, p + 1); + backslashify (dst, dst, !dst[2]); + unit = dst[0]; + res = 1; + } + debug_printf ("src '%s', dst '%s'", src, dst); + return res; +} + +/* conv_to_posix_path: Ensure src_path is a POSIX path. + + The result is zero for success, or an errno value. + posix_path must have sufficient space (i.e. MAX_PATH bytes). + If keep_rel_p is non-zero, relative paths stay that way. */ + +int +mount_info::conv_to_posix_path (const char *src_path, char *posix_path, + int keep_rel_p) +{ + int src_path_len = strlen (src_path); + int relative_path_p = !isabspath (src_path); + int trailing_slash_p; + + if (src_path_len <= 1) + trailing_slash_p = 0; + else + { + const char *lastchar = src_path + src_path_len - 1; + trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':'; + } + + debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path, + keep_rel_p ? "keep-rel" : "no-keep-rel", + trailing_slash_p ? "add-slash" : "no-add-slash"); + MALLOC_CHECK; + + if (src_path_len >= MAX_PATH) + { + debug_printf ("ENAMETOOLONG"); + return ENAMETOOLONG; + } + + /* FIXME: For now, if the path is relative and it's supposed to stay + that way, skip mount table processing. */ + + if (keep_rel_p && relative_path_p) + { + slashify (src_path, posix_path, 0); + debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path); + return 0; + } + + char pathbuf[MAX_PATH]; + int rc = normalize_win32_path (src_path, pathbuf); + if (rc != 0) + { + debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path); + return rc; + } + + int pathbuflen = strlen (pathbuf); + for (int i = 0; i < nmounts; ++i) + { + mount_item &mi = mount[native_sorted[i]]; + if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen)) + continue; + + if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path)) + continue; + + /* SRC_PATH is in the mount table. */ + int nextchar; + const char *p = pathbuf + mi.native_pathlen; + + if (!*p || !p[1]) + nextchar = 0; + else if (*p == '/') + nextchar = -1; + else + nextchar = 1; + + int addslash = nextchar > 0 ? 1 : 0; + if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= MAX_PATH) + return ENAMETOOLONG; + strcpy (posix_path, mi.posix_path); + if (addslash) + strcat (posix_path, "/"); + if (nextchar) + slashify (p, + posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen), + trailing_slash_p); + + if (cygheap->root.exists ()) + { + const char *p = cygheap->root.unchroot (posix_path); + memmove (posix_path, p, strlen (p) + 1); + } + goto out; + } + + if (!cygheap->root.exists ()) + /* nothing */; + else if (cygheap->root.ischroot_native (pathbuf)) + { + const char *p = pathbuf + cygheap->root.native_length (); + if (*p) + slashify (p, posix_path, trailing_slash_p); + else + { + posix_path[0] = '/'; + posix_path[1] = '\0'; + } + } + else + return ENOENT; + + /* Not in the database. This should [theoretically] only happen if either + the path begins with //, or / isn't mounted, or the path has a drive + letter not covered by the mount table. If it's a relative path then the + caller must want an absolute path (otherwise we would have returned + above). So we always return an absolute path at this point. */ + if (isdrive (pathbuf)) + cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p); + else + { + /* The use of src_path and not pathbuf here is intentional. + We couldn't translate the path, so just ensure no \'s are present. */ + slashify (src_path, posix_path, trailing_slash_p); + } + +out: + debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path); + MALLOC_CHECK; + return 0; +} + +/* Return flags associated with a mount point given the win32 path. */ + +unsigned +mount_info::set_flags_from_win32_path (const char *p) +{ + for (int i = 0; i < nmounts; i++) + { + mount_item &mi = mount[native_sorted[i]]; + if (path_prefix_p (mi.native_path, p, mi.native_pathlen)) + return mi.flags; + } + return PATH_BINARY; +} + +/* read_mounts: Given a specific regkey, read mounts from under its + key. */ + +void +mount_info::read_mounts (reg_key& r) +{ + char posix_path[MAX_PATH]; + HKEY key = r.get_key (); + DWORD i, posix_path_size; + int res; + + /* Loop through subkeys */ + /* FIXME: we would like to not check MAX_MOUNTS but the heap in the + shared area is currently statically allocated so we can't have an + arbitrarily large number of mounts. */ + for (i = 0; ; i++) + { + char native_path[MAX_PATH]; + int mount_flags; + + posix_path_size = MAX_PATH; + /* FIXME: if maximum posix_path_size is 256, we're going to + run into problems if we ever try to store a mount point that's + over 256 but is under MAX_PATH. */ + res = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL, + NULL, NULL, NULL); + + if (res == ERROR_NO_MORE_ITEMS) + break; + else if (res != ERROR_SUCCESS) + { + debug_printf ("RegEnumKeyEx failed, error %d!", res); + break; + } + + /* Get a reg_key based on i. */ + reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL); + + /* Fetch info from the subkey. */ + subkey.get_string ("native", native_path, sizeof (native_path), ""); + mount_flags = subkey.get_int ("flags", 0); + + /* Add mount_item corresponding to registry mount point. */ + res = mount_table->add_item (native_path, posix_path, mount_flags, false); + if (res && get_errno () == EMFILE) + break; /* The number of entries exceeds MAX_MOUNTS */ + } +} + +/* from_registry: Build the entire mount table from the registry. Also, + read in cygdrive-related information from its registry location. */ + +void +mount_info::from_registry () +{ + /* Use current mount areas if either user or system mount areas + already exist. Otherwise, import old mounts. */ + + reg_key r; + + /* Retrieve cygdrive-related information. */ + read_cygdrive_info_from_registry (); + + nmounts = 0; + + /* First read mounts from user's table. */ + read_mounts (r); + + /* Then read mounts from system-wide mount table. */ + reg_key r1 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + read_mounts (r1); +} + +/* add_reg_mount: Add mount item to registry. Return zero on success, + non-zero on failure. */ +/* FIXME: Need a mutex to avoid collisions with other tasks. */ + +int +mount_info::add_reg_mount (const char * native_path, const char * posix_path, unsigned mountflags) +{ + int res = 0; + + /* Add the mount to the right registry location, depending on + whether MOUNT_SYSTEM is set in the mount flags. */ + if (!(mountflags & MOUNT_SYSTEM)) /* current_user mount */ + { + /* reg_key for user mounts in HKEY_CURRENT_USER. */ + reg_key reg_user; + + /* Start by deleting existing mount if one exists. */ + res = reg_user.kill (posix_path); + if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) + goto err; + + /* Create the new mount. */ + reg_key subkey = reg_key (reg_user.get_key (), + KEY_ALL_ACCESS, + posix_path, NULL); + res = subkey.set_string ("native", native_path); + if (res != ERROR_SUCCESS) + goto err; + res = subkey.set_int ("flags", mountflags); + } + else /* local_machine mount */ + { + /* reg_key for system mounts in HKEY_LOCAL_MACHINE. */ + reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + + /* Start by deleting existing mount if one exists. */ + res = reg_sys.kill (posix_path); + if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) + goto err; + + /* Create the new mount. */ + reg_key subkey = reg_key (reg_sys.get_key (), + KEY_ALL_ACCESS, + posix_path, NULL); + res = subkey.set_string ("native", native_path); + if (res != ERROR_SUCCESS) + goto err; + res = subkey.set_int ("flags", mountflags); + + sys_mount_table_counter++; + cygwin_shared->sys_mount_table_counter++; + } + + return 0; /* Success */ + err: + __seterrno_from_win_error (res); + return -1; +} + +/* del_reg_mount: delete mount item from registry indicated in flags. + Return zero on success, non-zero on failure.*/ +/* FIXME: Need a mutex to avoid collisions with other tasks. */ + +int +mount_info::del_reg_mount (const char * posix_path, unsigned flags) +{ + int res; + + if (!(flags & MOUNT_SYSTEM)) /* Delete from user registry */ + { + reg_key reg_user (KEY_ALL_ACCESS, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL); + res = reg_user.kill (posix_path); + } + else /* Delete from system registry */ + { + sys_mount_table_counter++; + cygwin_shared->sys_mount_table_counter++; + reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + res = reg_sys.kill (posix_path); + } + + if (res != ERROR_SUCCESS) + { + __seterrno_from_win_error (res); + return -1; + } + + return 0; /* Success */ +} + +/* read_cygdrive_info_from_registry: Read the default prefix and flags + to use when creating cygdrives from the special user registry + location used to store cygdrive information. */ + +void +mount_info::read_cygdrive_info_from_registry () +{ + /* reg_key for user path prefix in HKEY_CURRENT_USER. */ + reg_key r; + + if (r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, cygdrive, sizeof (cygdrive), "") != 0) + { + /* Didn't find the user path prefix so check the system path prefix. */ + + /* reg_key for system path prefix in HKEY_LOCAL_MACHINE. */ + reg_key r2 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + + if (r2.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, cygdrive, + sizeof (cygdrive), "")) + strcpy (cygdrive, CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX); + cygdrive_flags = r2.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE); + slashify (cygdrive, cygdrive, 1); + cygdrive_len = strlen (cygdrive); + } + else + { + /* Fetch user cygdrive_flags from registry; returns MOUNT_CYGDRIVE on + error. */ + cygdrive_flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE); + slashify (cygdrive, cygdrive, 1); + cygdrive_len = strlen (cygdrive); + } +} + +/* write_cygdrive_info_to_registry: Write the default prefix and flags + to use when creating cygdrives to the special user registry + location used to store cygdrive information. */ + +int +mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags) +{ + /* Determine whether to modify user or system cygdrive path prefix. */ + HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (flags & MOUNT_SYSTEM) + { + sys_mount_table_counter++; + cygwin_shared->sys_mount_table_counter++; + } + + /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in + HKEY_LOCAL_MACHINE. */ + reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + + /* Verify cygdrive prefix starts with a forward slash and if there's + another character, it's not a slash. */ + if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) || + (!isslash (cygdrive_prefix[0])) || + ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1])))) + { + set_errno (EINVAL); + return -1; + } + + char hold_cygdrive_prefix[strlen (cygdrive_prefix) + 1]; + /* Ensure that there is never a final slash */ + nofinalslash (cygdrive_prefix, hold_cygdrive_prefix); + + int res; + res = r.set_string (CYGWIN_INFO_CYGDRIVE_PREFIX, hold_cygdrive_prefix); + if (res != ERROR_SUCCESS) + { + __seterrno_from_win_error (res); + return -1; + } + r.set_int (CYGWIN_INFO_CYGDRIVE_FLAGS, flags); + + /* This also needs to go in the in-memory copy of "cygdrive", but only if + appropriate: + 1. setting user path prefix, or + 2. overwriting (a previous) system path prefix */ + if (!(flags & MOUNT_SYSTEM) || (mount_table->cygdrive_flags & MOUNT_SYSTEM)) + { + slashify (cygdrive_prefix, mount_table->cygdrive, 1); + mount_table->cygdrive_flags = flags; + mount_table->cygdrive_len = strlen (mount_table->cygdrive); + } + + return 0; +} + +int +mount_info::remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags) +{ + /* Determine whether to modify user or system cygdrive path prefix. */ + HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + if (flags & MOUNT_SYSTEM) + { + sys_mount_table_counter++; + cygwin_shared->sys_mount_table_counter++; + } + + /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in + HKEY_LOCAL_MACHINE. */ + reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + + /* Delete cygdrive prefix and flags. */ + int res = r.killvalue (CYGWIN_INFO_CYGDRIVE_PREFIX); + int res2 = r.killvalue (CYGWIN_INFO_CYGDRIVE_FLAGS); + + /* Reinitialize the cygdrive path prefix to reflect to removal from the + registry. */ + read_cygdrive_info_from_registry (); + + return (res != ERROR_SUCCESS) ? res : res2; +} + +int +mount_info::get_cygdrive_info (char *user, char *system, char* user_flags, + char* system_flags) +{ + /* Get the user path prefix from HKEY_CURRENT_USER. */ + reg_key r; + int res = r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, user, MAX_PATH, ""); + + /* Get the user flags, if appropriate */ + if (res == ERROR_SUCCESS) + { + int flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE); + strcpy (user_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode"); + } + + /* Get the system path prefix from HKEY_LOCAL_MACHINE. */ + reg_key r2 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", + CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME, + CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, + NULL); + int res2 = r2.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, system, MAX_PATH, ""); + + /* Get the system flags, if appropriate */ + if (res2 == ERROR_SUCCESS) + { + int flags = r2.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE); + strcpy (system_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode"); + } + + return (res != ERROR_SUCCESS) ? res : res2; +} + +static mount_item *mounts_for_sort; + +/* sort_by_posix_name: qsort callback to sort the mount entries. Sort + user mounts ahead of system mounts to the same POSIX path. */ +/* FIXME: should the user should be able to choose whether to + prefer user or system mounts??? */ +static int +sort_by_posix_name (const void *a, const void *b) +{ + mount_item *ap = mounts_for_sort + (*((int*) a)); + mount_item *bp = mounts_for_sort + (*((int*) b)); + + /* Base weighting on longest posix path first so that the most + obvious path will be chosen. */ + size_t alen = strlen (ap->posix_path); + size_t blen = strlen (bp->posix_path); + + int res = blen - alen; + + if (res) + return res; /* Path lengths differed */ + + /* The two paths were the same length, so just determine normal + lexical sorted order. */ + res = strcmp (ap->posix_path, bp->posix_path); + + if (res == 0) + { + /* need to select between user and system mount to same POSIX path */ + if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */ + return 1; + else + return -1; + } + + return res; +} + +/* sort_by_native_name: qsort callback to sort the mount entries. Sort + user mounts ahead of system mounts to the same POSIX path. */ +/* FIXME: should the user should be able to choose whether to + prefer user or system mounts??? */ +static int +sort_by_native_name (const void *a, const void *b) +{ + mount_item *ap = mounts_for_sort + (*((int*) a)); + mount_item *bp = mounts_for_sort + (*((int*) b)); + + /* Base weighting on longest win32 path first so that the most + obvious path will be chosen. */ + size_t alen = strlen (ap->native_path); + size_t blen = strlen (bp->native_path); + + int res = blen - alen; + + if (res) + return res; /* Path lengths differed */ + + /* The two paths were the same length, so just determine normal + lexical sorted order. */ + res = strcmp (ap->native_path, bp->native_path); + + if (res == 0) + { + /* need to select between user and system mount to same POSIX path */ + if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */ + return 1; + else + return -1; + } + + return res; +} + +void +mount_info::sort () +{ + for (int i = 0; i < nmounts; i++) + native_sorted[i] = posix_sorted[i] = i; + /* Sort them into reverse length order, otherwise we won't + be able to look for /foo in /. */ + mounts_for_sort = mount; /* ouch. */ + qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name); + qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name); +} + +/* Add an entry to the mount table. + Returns 0 on success, -1 on failure and errno is set. + + This is where all argument validation is done. It may not make sense to + do this when called internally, but it's cleaner to keep it all here. */ + +int +mount_info::add_item (const char *native, const char *posix, unsigned mountflags, int reg_p) +{ + /* Something's wrong if either path is NULL or empty, or if it's + not a UNC or absolute path. */ + + if ((native == NULL) || (*native == 0) || + (posix == NULL) || (*posix == 0) || + !isabspath (native) || !isabspath (posix) || + slash_unc_prefix_p (posix) || isdrive (posix)) + { + set_errno (EINVAL); + return -1; + } + + /* Make sure both paths do not end in /. */ + char nativetmp[MAX_PATH]; + char posixtmp[MAX_PATH]; + + backslashify (native, nativetmp, 0); + nofinalslash (nativetmp, nativetmp); + + slashify (posix, posixtmp, 0); + nofinalslash (posixtmp, posixtmp); + + debug_printf ("%s[%s], %s[%s], %p", + native, nativetmp, posix, posixtmp, mountflags); + + /* Duplicate /'s in path are an error. */ + for (char *p = posixtmp + 1; *p; ++p) + { + if (p[-1] == '/' && p[0] == '/') + { + set_errno (EINVAL); + return -1; + } + } + + /* Write over an existing mount item with the same POSIX path if + it exists and is from the same registry area. */ + int i; + for (i = 0; i < nmounts; i++) + { + if (strcasematch (mount[i].posix_path, posixtmp) && + (mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM)) + break; + } + + if (i == nmounts && nmounts == MAX_MOUNTS) + { + set_errno (EMFILE); + return -1; + } + + if (reg_p && add_reg_mount (nativetmp, posixtmp, mountflags)) + return -1; + + if (i == nmounts) + nmounts++; + mount[i].init (nativetmp, posixtmp, mountflags); + sort (); + + return 0; +} + +/* Delete a mount table entry where path is either a Win32 or POSIX + path. Since the mount table is really just a table of aliases, + deleting / is ok (although running without a slash mount is + strongly discouraged because some programs may run erratically + without one). If MOUNT_SYSTEM is set in flags, remove from system + registry, otherwise remove the user registry mount. +*/ + +int +mount_info::del_item (const char *path, unsigned flags, int reg_p) +{ + char pathtmp[MAX_PATH]; + int posix_path_p = false; + + /* Something's wrong if path is NULL or empty. */ + if (path == NULL || *path == 0 || !isabspath (path)) + { + set_errno (EINVAL); + return -1; + } + + if (slash_unc_prefix_p (path) || strpbrk (path, ":\\")) + backslashify (path, pathtmp, 0); + else + { + slashify (path, pathtmp, 0); + posix_path_p = TRUE; + } + nofinalslash (pathtmp, pathtmp); + + if (reg_p && posix_path_p && + del_reg_mount (pathtmp, flags) && + del_reg_mount (path, flags)) /* for old irregular entries */ + return -1; + + for (int i = 0; i < nmounts; i++) + { + int ent = native_sorted[i]; /* in the same order as getmntent() */ + if (((posix_path_p) + ? strcasematch (mount[ent].posix_path, pathtmp) + : strcasematch (mount[ent].native_path, pathtmp)) && + (mount[ent].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM)) + { + if (!posix_path_p && + reg_p && del_reg_mount (mount[ent].posix_path, flags)) + return -1; + + nmounts--; /* One less mount table entry */ + /* Fill in the hole if not at the end of the table */ + if (ent < nmounts) + memmove (mount + ent, mount + ent + 1, + sizeof (mount[ent]) * (nmounts - ent)); + sort (); /* Resort the table */ + return 0; + } + } + set_errno (EINVAL); + return -1; +} + +/************************* mount_item class ****************************/ + +static mntent * +fillout_mntent (const char *native_path, const char *posix_path, unsigned flags) +{ +#ifdef _MT_SAFE + struct mntent &ret=_reent_winsup ()->mntbuf; +#else + static NO_COPY struct mntent ret; +#endif + + /* Remove drivenum from list if we see a x: style path */ + if (strlen (native_path) == 2 && native_path[1] == ':') + { + int drivenum = cyg_tolower (native_path[0]) - 'a'; + if (drivenum >= 0 && drivenum <= 31) + available_drives &= ~(1 << drivenum); + } + + /* Pass back pointers to mount_table strings reserved for use by + getmntent rather than pointers to strings in the internal mount + table because the mount table might change, causing weird effects + from the getmntent user's point of view. */ + + strcpy (_reent_winsup ()->mnt_fsname, native_path); + ret.mnt_fsname = _reent_winsup ()->mnt_fsname; + strcpy (_reent_winsup ()->mnt_dir, posix_path); + ret.mnt_dir = _reent_winsup ()->mnt_dir; + + if (!(flags & MOUNT_SYSTEM)) /* user mount */ + strcpy (_reent_winsup ()->mnt_type, (char *) "user"); + else /* system mount */ + strcpy (_reent_winsup ()->mnt_type, (char *) "system"); + + ret.mnt_type = _reent_winsup ()->mnt_type; + + /* mnt_opts is a string that details mount params such as + binary or textmode, or exec. We don't print + `silent' here; it's a magic internal thing. */ + + if (!(flags & MOUNT_BINARY)) + strcpy (_reent_winsup ()->mnt_opts, (char *) "textmode"); + else + strcpy (_reent_winsup ()->mnt_opts, (char *) "binmode"); + + if (flags & MOUNT_CYGWIN_EXEC) + strcat (_reent_winsup ()->mnt_opts, (char *) ",cygexec"); + else if (flags & MOUNT_EXEC) + strcat (_reent_winsup ()->mnt_opts, (char *) ",exec"); + else if (flags & MOUNT_NOTEXEC) + strcat (_reent_winsup ()->mnt_opts, (char *) ",noexec"); + + if ((flags & MOUNT_CYGDRIVE)) /* cygdrive */ + strcat (_reent_winsup ()->mnt_opts, (char *) ",noumount"); + + ret.mnt_opts = _reent_winsup ()->mnt_opts; + + ret.mnt_freq = 1; + ret.mnt_passno = 1; + return &ret; +} + +struct mntent * +mount_item::getmntent () +{ + return fillout_mntent (native_path, posix_path, flags); +} + +static struct mntent * +cygdrive_getmntent () +{ + char native_path[4]; + char posix_path[MAX_PATH]; + DWORD mask = 1, drive = 'a'; + struct mntent *ret = NULL; + + while (available_drives) + { + for (/* nothing */; drive <= 'z'; mask <<= 1, drive++) + if (available_drives & mask) + break; + + __small_sprintf (native_path, "%c:\\", drive); + if (GetDriveType (native_path) == DRIVE_REMOVABLE || + GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES) + { + available_drives &= ~mask; + continue; + } + native_path[2] = '\0'; + __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive); + ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags); + break; + } + + return ret; +} + +struct mntent * +mount_info::getmntent (int x) +{ + if (x < 0 || x >= nmounts) + return cygdrive_getmntent (); + + return mount[native_sorted[x]].getmntent (); +} + +/* Fill in the fields of a mount table entry. */ + +void +mount_item::init (const char *native, const char *posix, unsigned mountflags) +{ + strcpy ((char *) native_path, native); + strcpy ((char *) posix_path, posix); + + native_pathlen = strlen (native_path); + posix_pathlen = strlen (posix_path); + + flags = mountflags; +} + +/********************** Mount System Calls **************************/ + +/* Mount table system calls. + Note that these are exported to the application. */ + +/* mount: Add a mount to the mount table in memory and to the registry + that will cause paths under win32_path to be translated to paths + under posix_path. */ + +extern "C" int +mount (const char *win32_path, const char *posix_path, unsigned flags) +{ + int res = -1; + + if (flags & MOUNT_CYGDRIVE) /* normal mount */ + { + /* When flags include MOUNT_CYGDRIVE, take this to mean that + we actually want to change the cygdrive prefix and flags + without actually mounting anything. */ + res = mount_table->write_cygdrive_info_to_registry (posix_path, flags); + win32_path = NULL; + } + else + res = mount_table->add_item (win32_path, posix_path, flags, TRUE); + + syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags); + return res; +} + +/* umount: The standard umount call only has a path parameter. Since + it is not possible for this call to specify whether to remove the + mount from the user or global mount registry table, assume the user + table. */ + +extern "C" int +umount (const char *path) +{ + return cygwin_umount (path, 0); +} + +/* cygwin_umount: This is like umount but takes an additional flags + parameter that specifies whether to umount from the user or system-wide + registry area. */ + +extern "C" int +cygwin_umount (const char *path, unsigned flags) +{ + int res = -1; + + if (flags & MOUNT_CYGDRIVE) + { + /* When flags include MOUNT_CYGDRIVE, take this to mean that we actually want + to remove the cygdrive prefix and flags without actually unmounting + anything. */ + res = mount_table->remove_cygdrive_info_from_registry (path, flags); + } + else + { + res = mount_table->del_item (path, flags, TRUE); + } + + syscall_printf ("%d = cygwin_umount (%s, %d)", res, path, flags); + return res; +} + +extern "C" FILE * +setmntent (const char *filep, const char *) +{ + iteration = 0; + available_drives = GetLogicalDrives (); + return (FILE *) filep; +} + +extern "C" struct mntent * +getmntent (FILE *) +{ + return mount_table->getmntent (iteration++); +} + +extern "C" int +endmntent (FILE *) +{ + return 1; +} + +/********************** Symbolic Link Support **************************/ + +/* Read symlink from Extended Attribute */ +int +get_symlink_ea (const char* frompath, char* buf, int buf_size) +{ + int res = NTReadEA (frompath, SYMLINK_EA_NAME, buf, buf_size); + if (res == 0) + debug_printf ("Cannot read symlink from EA"); + return (res - 1); +} + +/* Save symlink to Extended Attribute */ +BOOL +set_symlink_ea (const char* frompath, const char* topath) +{ + if (!NTWriteEA (frompath, SYMLINK_EA_NAME, topath, strlen (topath) + 1)) + { + debug_printf ("Cannot save symlink in EA"); + return false; + } + return TRUE; +} + +/* Create a symlink from FROMPATH to TOPATH. */ + +/* If TRUE create symlinks as Windows shortcuts, if false create symlinks + as normal files with magic number and system bit set. */ +int allow_winsymlinks = TRUE; + +extern "C" int +symlink (const char *topath, const char *frompath) +{ + return symlink_worker (topath, frompath, allow_winsymlinks, false); +} + +int +symlink_worker (const char *topath, const char *frompath, bool use_winsym, + bool isdevice) +{ + HANDLE h; + int res = -1; + path_conv win32_path, win32_topath; + char from[MAX_PATH + 5]; + char cwd[MAX_PATH + 1], *cp = NULL, c = 0; + char w32topath[MAX_PATH + 1]; + DWORD written; + SECURITY_ATTRIBUTES sa = sec_none_nih; + + /* POSIX says that empty 'frompath' is invalid input whlie empty + 'topath' is valid -- it's symlink resolver job to verify if + symlink contents point to existing filesystem object */ + if (check_null_empty_str_errno (topath) == EFAULT || + check_null_empty_str_errno (frompath)) + goto done; + + if (strlen (topath) >= MAX_PATH) + { + set_errno (ENAMETOOLONG); + goto done; + } + + win32_path.check (frompath, PC_SYM_NOFOLLOW); + if (use_winsym && !win32_path.exists ()) + { + strcpy (from, frompath); + strcat (from, ".lnk"); + win32_path.check (from, PC_SYM_NOFOLLOW); + } + + if (win32_path.error) + { + set_errno (win32_path.case_clash ? ECASECLASH : win32_path.error); + goto done; + } + + syscall_printf ("symlink (%s, %s)", topath, win32_path.get_win32 ()); + + if (win32_path.is_auto_device ()) + { + set_errno (EEXIST); + goto done; + } + + DWORD create_how; + if (!use_winsym) + create_how = CREATE_NEW; + else if (isdevice) + { + strcpy (w32topath, topath); + create_how = CREATE_ALWAYS; + (void) SetFileAttributes (win32_path, FILE_ATTRIBUTE_NORMAL); + } + else + { + if (!isabspath (topath)) + { + getcwd (cwd, MAX_PATH + 1); + if ((cp = strrchr (from, '/')) || (cp = strrchr (from, '\\'))) + { + c = *cp; + *cp = '\0'; + chdir (from); + } + backslashify (topath, w32topath, 0); + } + if (!cp || GetFileAttributes (w32topath) == INVALID_FILE_ATTRIBUTES) + { + win32_topath.check (topath, PC_SYM_NOFOLLOW); + if (!cp || win32_topath.error != ENOENT) + strcpy (w32topath, win32_topath); + } + if (cp) + { + *cp = c; + chdir (cwd); + } + create_how = CREATE_NEW; + } + + if (allow_ntsec && win32_path.has_acls ()) + set_security_attribute (S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO, + &sa, alloca (4096), 4096); + + h = CreateFile (win32_path, GENERIC_WRITE, 0, &sa, create_how, + FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + __seterrno (); + else + { + BOOL success; + + if (use_winsym) + { + create_shortcut_header (); + /* Don't change the datatypes of `len' and `win_len' since + their sizeof is used when writing. */ + unsigned short len = strlen (topath); + unsigned short win_len = strlen (w32topath); + success = WriteFile (h, shortcut_header, SHORTCUT_HDR_SIZE, + &written, NULL) + && written == SHORTCUT_HDR_SIZE + && WriteFile (h, &len, sizeof len, &written, NULL) + && written == sizeof len + && WriteFile (h, topath, len, &written, NULL) + && written == len + && WriteFile (h, &win_len, sizeof win_len, &written, NULL) + && written == sizeof win_len + && WriteFile (h, w32topath, win_len, &written, NULL) + && written == win_len; + } + else + { + /* This is the old technique creating a symlink. */ + char buf[sizeof (SYMLINK_COOKIE) + MAX_PATH + 10]; + + __small_sprintf (buf, "%s%s", SYMLINK_COOKIE, topath); + DWORD len = strlen (buf) + 1; + + /* Note that the terminating nul is written. */ + success = WriteFile (h, buf, len, &written, NULL) + || written != len; + + } + if (success) + { + CloseHandle (h); + if (!allow_ntsec && allow_ntea) + set_file_attribute (win32_path.has_acls (), + win32_path.get_win32 (), + S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); + + DWORD attr = use_winsym ? FILE_ATTRIBUTE_READONLY + : FILE_ATTRIBUTE_SYSTEM; +#ifdef HIDDEN_DOT_FILES + cp = strrchr (win32_path, '\\'); + if ((cp && cp[1] == '.') || *win32_path == '.') + attr |= FILE_ATTRIBUTE_HIDDEN; +#endif + SetFileAttributes (win32_path, attr); + + if (!isdevice && win32_path.fs_fast_ea ()) + set_symlink_ea (win32_path, topath); + res = 0; + } + else + { + __seterrno (); + CloseHandle (h); + DeleteFileA (win32_path.get_win32 ()); + } + } + +done: + syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, topath, + frompath, use_winsym, isdevice); + return res; +} + +static BOOL +cmp_shortcut_header (const char *file_header) +{ + create_shortcut_header (); + return memcmp (shortcut_header, file_header, SHORTCUT_HDR_SIZE); +} + +static int +check_shortcut (const char *path, DWORD fileattr, HANDLE h, + char *contents, int *error, unsigned *pflags) +{ + char file_header[SHORTCUT_HDR_SIZE]; + unsigned short len; + int res = 0; + DWORD got = 0; + + /* Valid Cygwin & U/WIN shortcuts are R/O. */ + if (!(fileattr & FILE_ATTRIBUTE_READONLY)) + goto file_not_symlink; + /* Read the files header information. This is used to check for a + Cygwin or U/WIN shortcut or later to check for executable files. */ + if (!ReadFile (h, file_header, SHORTCUT_HDR_SIZE, &got, 0)) + { + *error = EIO; + goto close_it; + } + /* Check header if the shortcut is really created by Cygwin or U/WIN. */ + if (got != SHORTCUT_HDR_SIZE || cmp_shortcut_header (file_header)) + goto file_not_symlink; + /* Next 2 byte are USHORT, containing length of description entry. */ + if (!ReadFile (h, &len, sizeof len, &got, 0)) + { + *error = EIO; + goto close_it; + } + if (got != sizeof len || len == 0 || len > MAX_PATH) + goto file_not_symlink; + /* Now read description entry. */ + if (!ReadFile (h, contents, len, &got, 0)) + { + *error = EIO; + goto close_it; + } + if (got != len) + goto file_not_symlink; + contents[len] = '\0'; + res = len; + if (res) /* It's a symlink. */ + *pflags = PATH_SYMLINK; + goto close_it; + +file_not_symlink: + /* Not a symlink, see if executable. */ + if (!(*pflags & PATH_ALL_EXEC) && has_exec_chars (file_header, got)) + *pflags |= PATH_EXEC; + +close_it: + CloseHandle (h); + return res; +} + + +static int +check_sysfile (const char *path, DWORD fileattr, HANDLE h, + char *contents, int *error, unsigned *pflags) +{ + char cookie_buf[sizeof (SYMLINK_COOKIE) - 1]; + DWORD got; + int res = 0; + + if (!ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0)) + { + debug_printf ("ReadFile1 failed"); + *error = EIO; + } + else if (got == sizeof (cookie_buf) + && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0) + { + /* It's a symlink. */ + *pflags = PATH_SYMLINK; + + res = ReadFile (h, contents, MAX_PATH + 1, &got, 0); + if (!res) + { + debug_printf ("ReadFile2 failed"); + *error = EIO; + } + else + { + /* Versions prior to b16 stored several trailing + NULs with the path (to fill the path out to 1024 + chars). Current versions only store one trailing + NUL. The length returned is the path without + *any* trailing NULs. We also have to handle (or + at least not die from) corrupted paths. */ + if (memchr (contents, 0, got) != NULL) + res = strlen (contents); + else + res = got; + } + } + else if (got == sizeof (cookie_buf) + && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0) + *pflags |= PATH_SOCKET; + else + { + /* Not a symlink, see if executable. */ + if (*pflags & PATH_ALL_EXEC) + /* Nothing to do */; + else if (has_exec_chars (cookie_buf, got)) + *pflags |= PATH_EXEC; + else + *pflags |= PATH_NOTEXEC; + } + syscall_printf ("%d = symlink.check_sysfile (%s, %s) (%p)", + res, path, contents, *pflags); + + CloseHandle (h); + return res; +} + +enum +{ + SCAN_BEG, + SCAN_LNK, + SCAN_HASLNK, + SCAN_JUSTCHECK, + SCAN_APPENDLNK, + SCAN_EXTRALNK, + SCAN_DONE, +}; + +class suffix_scan +{ + const suffix_info *suffixes, *suffixes_start; + int nextstate; + char *eopath; +public: + const char *path; + char *has (const char *, const suffix_info *); + int next (); + int lnk_match () {return nextstate >= SCAN_EXTRALNK;} +}; + +char * +suffix_scan::has (const char *in_path, const suffix_info *in_suffixes) +{ + nextstate = SCAN_BEG; + suffixes = suffixes_start = in_suffixes; + + char *ext_here = strrchr (in_path, '.'); + path = in_path; + eopath = strchr (path, '\0'); + + if (!ext_here) + goto noext; + + if (suffixes) + { + /* Check if the extension matches a known extension */ + for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++) + if (strcasematch (ext_here, ex->name)) + { + nextstate = SCAN_JUSTCHECK; + suffixes = NULL; /* Has an extension so don't scan for one. */ + goto done; + } + } + + /* Didn't match. Use last resort -- .lnk. */ + if (strcasematch (ext_here, ".lnk")) + { + nextstate = SCAN_HASLNK; + suffixes = NULL; + } + + noext: + ext_here = eopath; + + done: + return ext_here; +} + +int +suffix_scan::next () +{ + for (;;) + { + if (!suffixes) + switch (nextstate) + { + case SCAN_BEG: + suffixes = suffixes_start; + if (!suffixes) + { + nextstate = SCAN_LNK; + return 1; + } + if (!*suffixes->name) + suffixes++; + nextstate = SCAN_EXTRALNK; + /* fall through to suffix checking below */ + break; + case SCAN_HASLNK: + nextstate = SCAN_EXTRALNK; /* Skip SCAN_BEG */ + return 1; + case SCAN_LNK: + case SCAN_EXTRALNK: + strcpy (eopath, ".lnk"); + nextstate = SCAN_DONE; + return 1; + case SCAN_JUSTCHECK: + nextstate = SCAN_APPENDLNK; + return 1; + case SCAN_APPENDLNK: + strcat (eopath, ".lnk"); + nextstate = SCAN_DONE; + return 1; + default: + *eopath = '\0'; + return 0; + } + + while (suffixes && suffixes->name) + if (!suffixes->addon) + suffixes++; + else + { + strcpy (eopath, suffixes->name); + if (nextstate == SCAN_EXTRALNK) + strcat (eopath, ".lnk"); + suffixes++; + return 1; + } + suffixes = NULL; + } +} + +bool +symlink_info::parse_device (const char *contents) +{ + char *endptr; + _major_t mymajor; + _major_t myminor; + _mode_t mymode; + + mymajor = strtol (++contents, &endptr, 16); + if (endptr == contents) + return false; + + contents = endptr; + myminor = strtol (++contents, &endptr, 16); + if (endptr == contents) + return false; + + contents = endptr; + mymode = strtol (++contents, &endptr, 16); + if (endptr == contents) + return false; + + switch (mymode & S_IFMT) + { + case S_IFIFO: + mymajor = _major (FH_FIFO); + myminor = _minor (FH_FIFO); + break; + case S_IFBLK: + case S_IFCHR: + if (mymajor || myminor) + break; + default: + return false; + } + + major = mymajor; + minor = myminor; + mode = mymode; + return true; +} + +/* Check if PATH is a symlink. PATH must be a valid Win32 path name. + + If PATH is a symlink, put the value of the symlink--the file to + which it points--into BUF. The value stored in BUF is not + necessarily null terminated. BUFLEN is the length of BUF; only up + to BUFLEN characters will be stored in BUF. BUF may be NULL, in + which case nothing will be stored. + + Set *SYML if PATH is a symlink. + + Set *EXEC if PATH appears to be executable. This is an efficiency + hack because we sometimes have to open the file anyhow. *EXEC will + not be set for every executable file. + + Return -1 on error, 0 if PATH is not a symlink, or the length + stored into BUF if PATH is a symlink. */ + +int +symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) +{ + HANDLE h; + int res = 0; + suffix_scan suffix; + contents[0] = '\0'; + + is_symlink = TRUE; + ext_here = suffix.has (path, suffixes); + extn = ext_here - path; + major = 0; + minor = 0; + + pflags &= ~PATH_SYMLINK; + + case_clash = false; + + while (suffix.next ()) + { + error = 0; + fileattr = GetFileAttributes (suffix.path); + if (fileattr == INVALID_FILE_ATTRIBUTES) + { + /* The GetFileAttributes call can fail for reasons that don't + matter, so we just return 0. For example, getting the + attributes of \\HOST will typically fail. */ + debug_printf ("GetFileAttributes (%s) failed", suffix.path); + error = geterrno_from_win_error (GetLastError (), EACCES); + continue; + } + + + ext_tacked_on = !!*ext_here; + + if (pcheck_case != PCHECK_RELAXED && !case_check (path) + || (opt & PC_SYM_IGNORE)) + goto file_not_symlink; + + int sym_check; + + sym_check = 0; + + if (fileattr & FILE_ATTRIBUTE_DIRECTORY) + goto file_not_symlink; + + /* Windows shortcuts are treated as symlinks. */ + if (suffix.lnk_match ()) + sym_check = 1; + + /* This is the old Cygwin method creating symlinks: */ + /* A symlink will have the `system' file attribute. */ + /* Only files can be symlinks (which can be symlinks to directories). */ + if (fileattr & FILE_ATTRIBUTE_SYSTEM) + sym_check = 2; + + if (!sym_check) + goto file_not_symlink; + + if (sym_check > 0 && opt & PC_CHECK_EA && + (res = get_symlink_ea (suffix.path, contents, sizeof (contents))) > 0) + { + pflags = PATH_SYMLINK; + debug_printf ("Got symlink from EA: %s", contents); + break; + } + + /* Open the file. */ + + h = CreateFile (suffix.path, GENERIC_READ, FILE_SHARE_READ, + &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + res = -1; + if (h == INVALID_HANDLE_VALUE) + goto file_not_symlink; + + /* FIXME: if symlink isn't present in EA, but EAs are supported, + should we write it there? */ + switch (sym_check) + { + case 1: + res = check_shortcut (suffix.path, fileattr, h, contents, &error, &pflags); + if (!res) + /* check more below */; + else if (*contents == ':' && parse_device (contents)) + goto file_not_symlink; + else + break; + /* If searching for `foo' and then finding a `foo.lnk' which is + no shortcut, return the same as if file not found. */ + if (!suffix.lnk_match () || !ext_tacked_on) + goto file_not_symlink; + + fileattr = INVALID_FILE_ATTRIBUTES; + continue; /* in case we're going to tack *another* .lnk on this filename. */ + case 2: + res = check_sysfile (suffix.path, fileattr, h, contents, &error, &pflags); + if (!res) + goto file_not_symlink; + break; + } + break; + + file_not_symlink: + is_symlink = false; + syscall_printf ("%s", (major || minor) ? "is a device" : "not a symlink"); + res = 0; + break; + } + + syscall_printf ("%d = symlink.check (%s, %p) (%p)", + res, suffix.path, contents, pflags); + return res; +} + +/* Check the correct case of the last path component (given in DOS style). + Adjust the case in this->path if pcheck_case == PCHECK_ADJUST or return + false if pcheck_case == PCHECK_STRICT. + Dont't call if pcheck_case == PCHECK_RELAXED. +*/ + +BOOL +symlink_info::case_check (char *path) +{ + WIN32_FIND_DATA data; + HANDLE h; + char *c; + + /* Set a pointer to the beginning of the last component. */ + if (!(c = strrchr (path, '\\'))) + c = path; + else + ++c; + + if ((h = FindFirstFile (path, &data)) + != INVALID_HANDLE_VALUE) + { + FindClose (h); + + /* If that part of the component exists, check the case. */ + if (strcmp (c, data.cFileName)) + { + case_clash = TRUE; + + /* If check is set to STRICT, a wrong case results + in returning a ENOENT. */ + if (pcheck_case == PCHECK_STRICT) + return false; + + /* PCHECK_ADJUST adjusts the case in the incoming + path which points to the path in *this. */ + strcpy (c, data.cFileName); + } + } + return TRUE; +} + +/* readlink system call */ + +extern "C" int +readlink (const char *path, char *buf, int buflen) +{ + extern suffix_info stat_suffixes[]; + + if (buflen < 0) + { + set_errno (ENAMETOOLONG); + return -1; + } + + path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes); + + if (pathbuf.error) + { + set_errno (pathbuf.error); + syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen); + return -1; + } + + if (!pathbuf.exists ()) + { + set_errno (ENOENT); + return -1; + } + + if (!pathbuf.issymlink ()) + { + if (pathbuf.exists ()) + set_errno (EINVAL); + return -1; + } + + int len = min (buflen, (int) strlen (pathbuf.get_win32 ())); + memcpy (buf, pathbuf.get_win32 (), len); + + /* errno set by symlink.check if error */ + return len; +} + +/* Some programs rely on st_dev/st_ino being unique for each file. + Hash the path name and hope for the best. The hash arg is not + always initialized to zero since readdir needs to compute the + dirent ino_t based on a combination of the hash of the directory + done during the opendir call and the hash or the filename within + the directory. FIXME: Not bullet-proof. */ +/* Cygwin internal */ + +unsigned long __stdcall +hash_path_name (unsigned long hash, const char *name) +{ + if (!*name) + return hash; + + /* Perform some initial permutations on the pathname if this is + not "seeded" */ + if (!hash) + { + /* Simplistic handling of drives. If there is a drive specified, + make sure that the initial letter is upper case. If there is + no \ after the ':' assume access through the root directory + of that drive. + FIXME: Should really honor MS-Windows convention of using + the environment to track current directory on various drives. */ + if (name[1] == ':') + { + char *nn, *newname = (char *) alloca (strlen (name) + 2); + nn = newname; + *nn = isupper (*name) ? cyg_tolower (*name) : *name; + *++nn = ':'; + name += 2; + if (*name != '\\') + *++nn = '\\'; + strcpy (++nn, name); + name = newname; + goto hashit; + } + + /* Fill out the hashed path name with the current working directory if + this is not an absolute path and there is no pre-specified hash value. + Otherwise the inodes same will differ depending on whether a file is + referenced with an absolute value or relatively. */ + + if (!hash && !isabspath (name)) + { + hash = cygheap->cwd.get_hash (); + if (name[0] == '.' && name[1] == '\0') + return hash; + hash = (hash << 5) - hash + '\\'; + } + } + +hashit: + /* Build up hash. Ignore single trailing slash or \a\b\ != \a\b or + \a\b\. but allow a single \ if that's all there is. */ + do + { + int ch = cyg_tolower (*name); + hash = (hash << 5) - hash + ch; + } + while (*++name != '\0' && + !(*name == '\\' && (!name[1] || (name[1] == '.' && !name[2])))); + return hash; +} + +char * +getcwd (char *buf, size_t ulen) +{ + char* res = NULL; + if (ulen == 0 && buf) + set_errno (EINVAL); + else if (buf == NULL || !__check_null_invalid_struct_errno (buf, ulen)) + res = cygheap->cwd.get (buf, 1, 1, ulen); + return res; +} + +/* getwd: standards? */ +extern "C" char * +getwd (char *buf) +{ + return getcwd (buf, MAX_PATH); +} + +/* chdir: POSIX 5.2.1.1 */ +extern "C" int +chdir (const char *in_dir) +{ + if (check_null_empty_str_errno (in_dir)) + return -1; + + syscall_printf ("dir '%s'", in_dir); + + char *s; + char dir[strlen (in_dir) + 1]; + strcpy (dir, in_dir); + /* Incredibly. Windows allows you to specify a path with trailing + whitespace to SetCurrentDirectory. This doesn't work too well + with other parts of the API, though, apparently. So nuke trailing + white space. */ + for (s = strchr (dir, '\0'); --s >= dir && isspace ((unsigned int) (*s & 0xff)); ) + *s = '\0'; + + if (!*s) + { + set_errno (ENOENT); + return -1; + } + + /* Convert path. First argument ensures that we don't check for NULL/empty/invalid + again. */ + path_conv path (PC_NONULLEMPTY, dir, PC_FULL | PC_SYM_FOLLOW); + if (path.error) + { + set_errno (path.error); + syscall_printf ("-1 = chdir (%s)", dir); + return -1; + } + + + /* Look for trailing path component consisting entirely of dots. This + is needed only in case of chdir since Windows simply ignores count + of dots > 2 here instead of returning an error code. Counts of dots + <= 2 are already eliminated by normalize_posix_path. */ + const char *p = strrchr (dir, '/'); + if (!p) + p = dir; + else + p++; + + size_t len = strlen (p); + if (len > 2 && strspn (p, ".") == len) + { + set_errno (ENOENT); + return -1; + } + + const char *native_dir = path.get_win32 (); + + /* Check to see if path translates to something like C:. + If it does, append a \ to the native directory specification to + defeat the Windows 95 (i.e. MS-DOS) tendency of returning to + the last directory visited on the given drive. */ + if (isdrive (native_dir) && !native_dir[2]) + { + path.get_win32 ()[2] = '\\'; + path.get_win32 ()[3] = '\0'; + } + int res; + int devn = path.get_devn (); + if (!isvirtual_dev (devn)) + res = SetCurrentDirectory (native_dir) ? 0 : -1; + else if (!path.exists ()) + { + set_errno (ENOENT); + return -1; + } + else if (!path.isdir ()) + { + set_errno (ENOTDIR); + return -1; + } + else + { + native_dir = "c:\\"; + res = 0; + } + + /* If res != 0, we didn't change to a new directory. + Otherwise, set the current windows and posix directory cache from input. + If the specified directory is a MS-DOS style directory or if the directory + was symlinked, convert the MS-DOS path back to posix style. Otherwise just + store the given directory. This allows things like "find", which traverse + directory trees, to work correctly with Cygwin mounted directories. + FIXME: Is just storing the posixized windows directory the correct thing to + do when we detect a symlink? Should we instead rebuild the posix path from + the input by traversing links? This would be an expensive operation but + we'll see if Cygwin mailing list users whine about the current behavior. */ + if (res) + __seterrno (); + else if ((!path.has_symlinks () && strpbrk (dir, ":\\") == NULL + && pcheck_case == PCHECK_RELAXED) || isvirtual_dev (devn)) + cygheap->cwd.set (native_dir, dir); + else + cygheap->cwd.set (native_dir, NULL); + + /* Note that we're accessing cwd.posix without a lock here. I didn't think + it was worth locking just for strace. */ + syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%s'", res, + cygheap->cwd.posix, native_dir); + MALLOC_CHECK; + return res; +} + +extern "C" int +fchdir (int fd) +{ + int res; + sigframe thisframe (mainthread); + + cygheap_fdget cfd (fd); + if (cfd >= 0) + res = chdir (cfd->get_win32_name ()); + else + res = -1; + + syscall_printf ("%d = fchdir (%d)", res, fd); + return res; +} + +/******************** Exported Path Routines *********************/ + +/* Cover functions to the path conversion routines. + These are exported to the world as cygwin_foo by cygwin.din. */ + +extern "C" int +cygwin_conv_to_win32_path (const char *path, char *win32_path) +{ + path_conv p (path, PC_SYM_FOLLOW); + if (p.error) + { + win32_path[0] = '\0'; + set_errno (p.error); + return -1; + } + + strcpy (win32_path, p); + return 0; +} + +extern "C" int +cygwin_conv_to_full_win32_path (const char *path, char *win32_path) +{ + path_conv p (path, PC_SYM_FOLLOW | PC_FULL); + if (p.error) + { + win32_path[0] = '\0'; + set_errno (p.error); + return -1; + } + + strcpy (win32_path, p); + return 0; +} + +/* This is exported to the world as cygwin_foo by cygwin.din. */ + +extern "C" int +cygwin_conv_to_posix_path (const char *path, char *posix_path) +{ + if (check_null_empty_str_errno (path)) + return -1; + mount_table->conv_to_posix_path (path, posix_path, 1); + return 0; +} + +extern "C" int +cygwin_conv_to_full_posix_path (const char *path, char *posix_path) +{ + if (check_null_empty_str_errno (path)) + return -1; + mount_table->conv_to_posix_path (path, posix_path, 0); + return 0; +} + +/* The realpath function is supported on some UNIX systems. */ + +extern "C" char * +realpath (const char *path, char *resolved) +{ + int err; + + path_conv real_path (path, PC_SYM_FOLLOW | PC_FULL); + + if (real_path.error) + err = real_path.error; + else + { + err = mount_table->conv_to_posix_path (real_path.get_win32 (), resolved, 0); + if (err == 0) + return resolved; + } + + /* FIXME: on error, we are supposed to put the name of the path + component which could not be resolved into RESOLVED. */ + resolved[0] = '\0'; + + set_errno (err); + return NULL; +} + +/* Return non-zero if path is a POSIX path list. + This is exported to the world as cygwin_foo by cygwin.din. + +DOCTOOL-START +<sect1 id="add-func-cygwin-posix-path-list-p"> + <para>Rather than use a mode to say what the "proper" path list + format is, we allow any, and give apps the tools they need to + convert between the two. If a ';' is present in the path list it's + a Win32 path list. Otherwise, if the first path begins with + [letter]: (in which case it can be the only element since if it + wasn't a ';' would be present) it's a Win32 path list. Otherwise, + it's a POSIX path list.</para> +</sect1> +DOCTOOL-END + */ + +extern "C" int +cygwin_posix_path_list_p (const char *path) +{ + int posix_p = !(strchr (path, ';') || isdrive (path)); + return posix_p; +} + +/* These are used for apps that need to convert env vars like PATH back and + forth. The conversion is a two step process. First, an upper bound on the + size of the buffer needed is computed. Then the conversion is done. This + allows the caller to use alloca if it wants. */ + +static int +conv_path_list_buf_size (const char *path_list, int to_posix_p) +{ + int i, num_elms, max_mount_path_len, size; + const char *p; + + /* The theory is that an upper bound is + current_size + (num_elms * max_mount_path_len) */ + + char delim = to_posix_p ? ';' : ':'; + p = path_list; + for (num_elms = 1; (p = strchr (p, delim)) != NULL; ++num_elms) + ++p; + + /* 7: strlen ("//c") + slop, a conservative initial value */ + for (max_mount_path_len = 7, i = 0; i < mount_table->nmounts; ++i) + { + int mount_len = (to_posix_p + ? mount_table->mount[i].posix_pathlen + : mount_table->mount[i].native_pathlen); + if (max_mount_path_len < mount_len) + max_mount_path_len = mount_len; + } + + /* 100: slop */ + size = strlen (path_list) + (num_elms * max_mount_path_len) + 100; + return size; +} + +extern "C" int +cygwin_win32_to_posix_path_list_buf_size (const char *path_list) +{ + return conv_path_list_buf_size (path_list, 1); +} + +extern "C" int +cygwin_posix_to_win32_path_list_buf_size (const char *path_list) +{ + return conv_path_list_buf_size (path_list, 0); +} + +extern "C" int +cygwin_win32_to_posix_path_list (const char *win32, char *posix) +{ + conv_path_list (win32, posix, 1); + return 0; +} + +extern "C" int +cygwin_posix_to_win32_path_list (const char *posix, char *win32) +{ + conv_path_list (posix, win32, 0); + return 0; +} + +/* cygwin_split_path: Split a path into directory and file name parts. + Buffers DIR and FILE are assumed to be big enough. + + Examples (path -> `dir' / `file'): + / -> `/' / `' + "" -> `.' / `' + . -> `.' / `.' (FIXME: should this be `.' / `'?) + .. -> `.' / `..' (FIXME: should this be `..' / `'?) + foo -> `.' / `foo' + foo/bar -> `foo' / `bar' + foo/bar/ -> `foo' / `bar' + /foo -> `/' / `foo' + /foo/bar -> `/foo' / `bar' + c: -> `c:/' / `' + c:/ -> `c:/' / `' + c:foo -> `c:/' / `foo' + c:/foo -> `c:/' / `foo' + */ + +extern "C" void +cygwin_split_path (const char *path, char *dir, char *file) +{ + int dir_started_p = 0; + + /* Deal with drives. + Remember that c:foo <==> c:/foo. */ + if (isdrive (path)) + { + *dir++ = *path++; + *dir++ = *path++; + *dir++ = '/'; + if (!*path) + { + *dir = 0; + *file = 0; + return; + } + if (isdirsep (*path)) + ++path; + dir_started_p = 1; + } + + /* Determine if there are trailing slashes and "delete" them if present. + We pretend as if they don't exist. */ + const char *end = path + strlen (path); + /* path + 1: keep leading slash. */ + while (end > path + 1 && isdirsep (end[-1])) + --end; + + /* At this point, END points to one beyond the last character + (with trailing slashes "deleted"). */ + + /* Point LAST_SLASH at the last slash (duh...). */ + const char *last_slash; + for (last_slash = end - 1; last_slash >= path; --last_slash) + if (isdirsep (*last_slash)) + break; + + if (last_slash == path) + { + *dir++ = '/'; + *dir = 0; + } + else if (last_slash > path) + { + memcpy (dir, path, last_slash - path); + dir[last_slash - path] = 0; + } + else + { + if (dir_started_p) + ; /* nothing to do */ + else + *dir++ = '.'; + *dir = 0; + } + + memcpy (file, last_slash + 1, end - last_slash - 1); + file[end - last_slash - 1] = 0; +} + +/*****************************************************************************/ + +/* Return the hash value for the current win32 value. + This is used when constructing inodes. */ +DWORD +cwdstuff::get_hash () +{ + DWORD hashnow; + cwd_lock->acquire (); + hashnow = hash; + cwd_lock->release (); + return hashnow; +} + +/* Initialize cygcwd 'muto' for serializing access to cwd info. */ +void +cwdstuff::init () +{ + new_muto (cwd_lock); +} + +/* Get initial cwd. Should only be called once in a + process tree. */ +bool +cwdstuff::get_initial () +{ + cwd_lock->acquire (); + + if (win32) + return 1; + + int i; + DWORD len, dlen; + for (i = 0, dlen = MAX_PATH, len = 0; i < 3; dlen *= 2, i++) + { + win32 = (char *) crealloc (win32, dlen + 2); + if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen) + break; + } + + if (len == 0) + { + __seterrno (); + cwd_lock->release (); + debug_printf ("get_initial_cwd failed, %E"); + cwd_lock->release (); + return 0; + } + set (NULL); + return 1; /* Leaves cwd lock unreleased */ +} + +/* Fill out the elements of a cwdstuff struct. + It is assumed that the lock for the cwd is acquired if + win32_cwd == NULL. */ +void +cwdstuff::set (const char *win32_cwd, const char *posix_cwd) +{ + char pathbuf[MAX_PATH]; + + if (win32_cwd) + { + cwd_lock->acquire (); + win32 = (char *) crealloc (win32, strlen (win32_cwd) + 1); + strcpy (win32, win32_cwd); + } + + if (!posix_cwd) + mount_table->conv_to_posix_path (win32, pathbuf, 0); + else + (void) normalize_posix_path (posix_cwd, pathbuf); + + posix = (char *) crealloc (posix, strlen (pathbuf) + 1); + strcpy (posix, pathbuf); + + hash = hash_path_name (0, win32); + + if (win32_cwd) + cwd_lock->release (); + + return; +} + +/* Copy the value for either the posix or the win32 cwd into a buffer. */ +char * +cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen) +{ + MALLOC_CHECK; + + if (ulen) + /* nothing */; + else if (buf == NULL) + ulen = (unsigned) -1; + else + { + set_errno (EINVAL); + goto out; + } + + if (!get_initial ()) /* Get initial cwd and set cwd lock */ + return NULL; + + char *tocopy; + if (!need_posix) + tocopy = win32; + else + tocopy = posix; + + debug_printf ("posix %s", posix); + if (strlen (tocopy) >= ulen) + { + set_errno (ERANGE); + buf = NULL; + } + else + { + if (!buf) + buf = (char *) malloc (strlen (tocopy) + 1); + strcpy (buf, tocopy); + if (!buf[0]) /* Should only happen when chroot */ + strcpy (buf, "/"); + } + + cwd_lock->release (); + +out: + syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d", + buf, buf, ulen, need_posix, with_chroot, errno); + MALLOC_CHECK; + return buf; +} diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h new file mode 100644 index 00000000000..67a9ed79d1b --- /dev/null +++ b/winsup/cygwin/path.h @@ -0,0 +1,211 @@ +/* path.h: path data structures + + Copyright 1996, 1997, 1998, 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. */ + +struct suffix_info +{ + const char *name; + int addon; + suffix_info (const char *s, int addit = 0): name (s), addon (addit) {} +}; + +enum pathconv_arg +{ + PC_SYM_FOLLOW = 0x0001, + PC_SYM_NOFOLLOW = 0x0002, + PC_SYM_IGNORE = 0x0004, + PC_SYM_CONTENTS = 0x0008, + PC_FULL = 0x0010, + PC_NULLEMPTY = 0x0020, + PC_CHECK_EA = 0x0040, + PC_POSIX = 0x0080 +}; + +enum case_checking +{ + PCHECK_RELAXED = 0, + PCHECK_ADJUST = 1, + PCHECK_STRICT = 2 +}; + +#define PC_NONULLEMPTY -1 + +#include <sys/mount.h> + +enum path_types +{ + PATH_NOTHING = 0, + PATH_SYMLINK = MOUNT_SYMLINK, + PATH_BINARY = MOUNT_BINARY, + PATH_EXEC = MOUNT_EXEC, + PATH_NOTEXEC = MOUNT_NOTEXEC, + PATH_CYGWIN_EXEC = MOUNT_CYGWIN_EXEC, + PATH_ALL_EXEC = (PATH_CYGWIN_EXEC | PATH_EXEC), + PATH_TEXT = 0x02000000, + PATH_ISDISK = 0x04000000, + PATH_HAS_SYMLINKS = 0x10000000, + PATH_HASBUGGYOPEN = 0x20000000, + PATH_SOCKET = 0x40000000, + PATH_HASACLS = 0x80000000 +}; + +class symlink_info; +struct fs_info +{ + char name[MAX_PATH]; + char root_dir[MAX_PATH]; + DWORD flags; + DWORD serial; + DWORD sym_opt; /* additional options to pass to symlink_info resolver */ + DWORD is_remote_drive; + DWORD drive_type; + bool update (const char *); +}; +class path_conv +{ + char path[MAX_PATH]; + DWORD fileattr; + fs_info fs; + void add_ext_from_sym (symlink_info&); + public: + + unsigned path_flags; + char *known_suffix; + int error; + device dev; + BOOL case_clash; + char *normalized_path; + + int isdisk () const { return path_flags & PATH_ISDISK;} + int isremote () const {return fs.is_remote_drive;} + int has_acls () const {return path_flags & PATH_HASACLS;} + int has_symlinks () const {return path_flags & PATH_HAS_SYMLINKS;} + int hasgood_inode () const {return path_flags & PATH_HASACLS;} // Not strictly correct + int has_buggy_open () const {return path_flags & PATH_HASBUGGYOPEN;} + int binmode () const + { + if (path_flags & PATH_BINARY) + return O_BINARY; + if (path_flags & PATH_TEXT) + return O_TEXT; + return 0; + } + int issymlink () const {return path_flags & PATH_SYMLINK;} + int isdevice () const {return dev.devn && dev.devn != FH_FS && dev.devn != FH_FIFO;} + int isfifo () const {return dev == FH_FIFO;} + int isspecial () const {return dev.devn && dev.devn != FH_FS;} + int is_auto_device () const {return isdevice () && !is_fs_special ();} + int is_fs_special () const {return isspecial () && dev.isfs ();} + int issocket () const {return path_flags & PATH_SOCKET;} + int iscygexec () const {return path_flags & PATH_CYGWIN_EXEC;} + bool exists () const {return fileattr != INVALID_FILE_ATTRIBUTES;} + bool has_attribute (DWORD x) const {return exists () && (fileattr & x);} + int isdir () const {return has_attribute (FILE_ATTRIBUTE_DIRECTORY);} + executable_states exec_state () + { + extern int _check_for_executable; + if (path_flags & PATH_ALL_EXEC) + return is_executable; + if (path_flags & PATH_NOTEXEC) + return not_executable; + if (!_check_for_executable) + return dont_care_if_executable; + return dont_know_if_executable; + } + + void set_binary () {path_flags |= PATH_BINARY;} + void set_symlink () {path_flags |= PATH_SYMLINK;} + void set_has_symlinks () {path_flags |= PATH_HAS_SYMLINKS;} + void set_isdisk () {path_flags |= PATH_ISDISK; dev.devn = FH_FS;} + void set_exec (int x = 1) {path_flags |= x ? PATH_EXEC : PATH_NOTEXEC;} + void set_has_acls (int x = 1) {path_flags |= x ? PATH_HASACLS : PATH_NOTHING;} + void set_has_buggy_open (int x = 1) {path_flags |= x ? PATH_HASBUGGYOPEN : PATH_NOTHING;} + + void check (const char *src, unsigned opt = PC_SYM_FOLLOW, + const suffix_info *suffixes = NULL) __attribute__ ((regparm(3))); + + path_conv (int, const char *src, unsigned opt = PC_SYM_FOLLOW, + const suffix_info *suffixes = NULL) + { + check (src, opt, suffixes); + } + + path_conv (const char *src, unsigned opt = PC_SYM_FOLLOW, + const suffix_info *suffixes = NULL) + { + check (src, opt | PC_NULLEMPTY, suffixes); + } + + path_conv (): fileattr (INVALID_FILE_ATTRIBUTES), path_flags (0), + known_suffix (NULL), error (0), normalized_path (NULL) + {path[0] = '\0';} + + inline char *get_win32 () { return path; } + operator char *() {return path;} + operator const char *() {return path;} + operator DWORD &() {return fileattr;} + operator int () {return fileattr; } + char operator [](int i) const {return path[i];} + DWORD get_devn () {return dev.devn == FH_BAD ? (DWORD) FH_FS : dev.devn;} + short get_unitn () {return dev.minor;} + DWORD file_attributes () {return fileattr;} + DWORD drive_type () {return fs.drive_type;} + BOOL fs_fast_ea () {return fs.sym_opt & PC_CHECK_EA;} + void set_path (const char *p) {strcpy (path, p);} + char *return_and_clear_normalized_path (); + const char * root_dir () { return fs.root_dir; } + DWORD volser () { return fs.serial; } + const char *volname () {return fs.name; } + void fillin (HANDLE h); +}; + +/* Symlink marker */ +#define SYMLINK_COOKIE "!<symlink>" + +#define SYMLINK_EA_NAME ".CYGSYMLINK" + +/* Socket marker */ +#define SOCKET_COOKIE "!<socket >" + +/* The sizeof header written to a shortcut by Cygwin or U/WIN. */ +#define SHORTCUT_HDR_SIZE 76 + +/* Maximum depth of symlinks (after which ELOOP is issued). */ +#define MAX_LINK_DEPTH 10 +int __stdcall slash_unc_prefix_p (const char *path) __attribute__ ((regparm(1))); + +enum fe_types +{ + FE_NADA = 0, /* Nothing special */ + FE_NNF = 1, /* Return NULL if not found */ + FE_NATIVE = 2, /* Return native path in path_conv struct */ + FE_CWD = 4 /* Search CWD for program */ +}; +const char * __stdcall find_exec (const char *name, path_conv& buf, + const char *winenv = "PATH=", + unsigned opt = FE_NADA, + const char **known_suffix = NULL) + __attribute__ ((regparm(3))); + +/* Common macros for checking for invalid path names */ +#define isdrive(s) (isalpha (*(s)) && (s)[1] == ':') + +static inline bool +has_exec_chars (const char *buf, int len) +{ + return len >= 2 && + ((buf[0] == '#' && buf[1] == '!') || + (buf[0] == ':' && buf[1] == '\n') || + (buf[0] == 'M' && buf[1] == 'Z')); +} + +int pathmatch (const char *path1, const char *path2) __attribute__ ((regparm (2))); +int pathnmatch (const char *path1, const char *path2, int len) __attribute__ ((regparm (2))); + +int path_prefix_p (const char *path1, const char *path2, int len1) __attribute__ ((regparm (3))); diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc new file mode 100644 index 00000000000..c55d44b2a5d --- /dev/null +++ b/winsup/cygwin/pinfo.cc @@ -0,0 +1,718 @@ +/* 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 <errno.h> +#include <limits.h> +#include <stdarg.h> +#include "security.h" +#include "fhandler.h" +#include "path.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); + + if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + system_printf ("couldn't save current process handle %p, %E", hMainProc); + hexec_proc = NULL; + } +} + +/* 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; + + 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); + for (int i = 0; i < 10; i++) + { + int created; + char mapname[MAX_PATH]; + __small_sprintf (mapname, "cygpid.%x", 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 (FILE_MAP_READ | FILE_MAP_WRITE, FALSE, mapname); + created = 0; + } + else + { + h = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih, + PAGE_READWRITE, 0, mapsize, mapname); + created = h && GetLastError () != ERROR_ALREADY_EXISTS; + } + + if (!h) + { + if (createit) + __seterrno (); + procinfo = NULL; + return; + } + + procinfo = (_pinfo *) MapViewOfFileEx (h, FILE_MAP_READ | FILE_MAP_WRITE, + 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; +} + +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; + for (char **a = __argv; *a; a++) + n += strlen (*a) + 1; + 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 (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 (!pid || !this) + { + 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; + while ((isalive = alive ())) + 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)); + if (winpid) + /* nothing to do */; + else if (!pinfolist[nelem]) + return; + else + /* 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; + } + + 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/pinfo.h b/winsup/cygwin/pinfo.h new file mode 100644 index 00000000000..3599015cc60 --- /dev/null +++ b/winsup/cygwin/pinfo.h @@ -0,0 +1,244 @@ +/* pinfo.h: process table info + + 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. */ + +#ifndef _PINFO_H +#define _PINFO_H +/* Signal constants (have to define them here, unfortunately) */ + +enum +{ + __SIGFLUSH = -2, + __SIGSTRACE = -1, + __SIGCOMMUNE = 0, + __SIGOFFSET = 2 +}; + +#define PSIZE 63 + +#include <sys/resource.h> +#include "thread.h" + +struct commune_result +{ + char *s; + int n; + HANDLE handles[2]; +}; + +enum picom +{ + PICOM_CMDLINE = 1, + PICOM_FIFO = 2 +}; + +class _pinfo +{ +public: + /* Cygwin pid */ + pid_t pid; + + /* Various flags indicating the state of the process. See PID_ + constants below. */ + DWORD process_state; + + /* If hProcess is set, it's because it came from a + CreateProcess call. This means it's process relative + to the thing which created the process. That's ok because + we only use this handle from the parent. */ + HANDLE hProcess; + +#define PINFO_REDIR_SIZE ((char *) &myself.procinfo->hProcess - (char *) myself.procinfo) + + /* Handle associated with initial Windows pid which started it all. */ + HANDLE pid_handle; + + /* Handle to the logical parent of this pid. */ + HANDLE ppid_handle; + + /* Parent process id. */ + pid_t ppid; + + /* dwProcessId contains the processid used for sending signals. It + * will be reset in a child process when it is capable of receiving + * signals. + */ + DWORD dwProcessId; + + /* Used to spawn a child for fork(), among other things. */ + char progname[MAX_PATH]; + + /* User information. + The information is derived from the GetUserName system call, + with the name looked up in /etc/passwd and assigned a default value + if not found. This data resides in the shared data area (allowing + tasks to store whatever they want here) so it's for informational + purposes only. */ + __uid32_t uid; /* User ID */ + __gid32_t gid; /* Group ID */ + pid_t pgid; /* Process group ID */ + pid_t sid; /* Session ID */ + int ctty; /* Control tty */ + bool has_pgid_children;/* True if we've forked or spawned children with our GID. */ + + /* Resources used by process. */ + long start_time; + struct rusage rusage_self; + struct rusage rusage_children; + + /* Non-zero if process was stopped by a signal. */ + char stopsig; + + /* commune */ + pid_t hello_pid; + HANDLE tothem; + HANDLE fromthem; + + void exit (UINT n, bool norecord = 0) __attribute__ ((noreturn, regparm(2))); + + inline void set_has_pgid_children () + { + if (pgid == pid) + has_pgid_children = 1; + } + + inline void set_has_pgid_children (bool val) {has_pgid_children = val;} + + inline struct sigaction& getsig (int sig) + { + return thread2signal ? thread2signal->sigs[sig] : sigs[sig]; + } + + inline void copysigs (_pinfo *p) {memcpy (sigs, p->sigs, sizeof (sigs));} + + inline sigset_t& getsigmask () + { + return thread2signal ? *thread2signal->sigmask : sig_mask; + } + + inline void setsigmask (sigset_t mask) + { + if (thread2signal) + *(thread2signal->sigmask) = mask; + sig_mask = mask; + } + + inline LONG* getsigtodo (int sig) {return _sigtodo + __SIGOFFSET + sig;} + + inline HANDLE getthread2signal () + { + return thread2signal ? thread2signal->win32_obj_id : hMainThread; + } + + inline void setthread2signal (void *thr) {thread2signal = (pthread *) thr;} + void commune_recv (); + commune_result commune_send (DWORD, ...); + bool alive (); + char *cmdline (size_t &); + + friend void __stdcall set_myself (pid_t, HANDLE); + +private: + struct sigaction sigs[NSIG]; + sigset_t sig_mask; /* one set for everything to ignore. */ + LONG _sigtodo[NSIG + __SIGOFFSET]; + pthread *thread2signal; // NULL means thread any other means a pthread + CRITICAL_SECTION lock; +}; + +class pinfo +{ + HANDLE h; + _pinfo *procinfo; + bool destroy; +public: + void init (pid_t n, DWORD create = 0, HANDLE h = NULL) __attribute__ ((regparm(3))); + pinfo () {} + pinfo (_pinfo *x): procinfo (x) {} + pinfo (pid_t n) {init (n);} + pinfo (pid_t n, int create) {init (n, create);} + void release (); + ~pinfo () + { + if (destroy && procinfo) + release (); + } + + _pinfo *operator -> () const {return procinfo;} + int operator == (pinfo *x) const {return x->procinfo == procinfo;} + int operator == (pinfo &x) const {return x.procinfo == procinfo;} + int operator == (void *x) const {return procinfo == x;} + int operator == (int x) const {return (int) procinfo == (int) x;} + int operator == (char *x) const {return (char *) procinfo == x;} + _pinfo *operator * () const {return procinfo;} + operator _pinfo * () const {return procinfo;} + // operator bool () const {return (int) h;} +#ifndef _SIGPROC_H + int remember () {system_printf ("remember is not here"); return 0;} +#else + int remember () + { + int res = proc_subproc (PROC_ADDCHILD, (DWORD) this); + destroy = res ? false : true; + return res; + } +#endif + HANDLE shared_handle () {return h;} +}; + +#define ISSTATE(p, f) (!!((p)->process_state & f)) +#define NOTSTATE(p, f) (!((p)->process_state & f)) + +class winpids +{ + DWORD *pidlist; + DWORD npidlist; + pinfo *pinfolist; + DWORD (winpids::* enum_processes) (bool winpid); + DWORD enum_init (bool winpid); + DWORD enumNT (bool winpid); + DWORD enum9x (bool winpid); + void add (DWORD& nelem, bool, DWORD pid); + static CRITICAL_SECTION cs; +public: + DWORD npids; + inline void reset () { npids = 0; release (); } + void set (bool winpid); + winpids (int): enum_processes (&winpids::enum_init) { reset (); } + winpids (): pidlist (NULL), npidlist (0), pinfolist (NULL), + enum_processes (&winpids::enum_init), npids (0) { set (0); } + inline DWORD& winpid (int i) const {return pidlist[i];} + inline _pinfo *operator [] (int i) const {return (_pinfo *) pinfolist[i];} + ~winpids (); + void release (); + static void init (); +}; + +extern __inline pid_t +cygwin_pid (pid_t pid) +{ + return (pid_t) (wincap.has_negative_pids ()) ? -(int) pid : pid; +} + +void __stdcall pinfo_init (char **, int); +void __stdcall set_myself (pid_t pid, HANDLE h = NULL); +extern pinfo myself; + +#define _P_VFORK 0 +extern void __stdcall pinfo_fixup_after_fork (); +extern HANDLE hexec_proc; + +/* For mmaps across fork(). */ +int __stdcall fixup_mmaps_after_fork (HANDLE parent); +/* for shm areas across fork (). */ +int __stdcall fixup_shms_after_fork (); + +void __stdcall fill_rusage (struct rusage *, HANDLE); +void __stdcall add_rusage (struct rusage *, struct rusage *); +#endif /*_PINFO_H*/ diff --git a/winsup/cygwin/pipe.cc b/winsup/cygwin/pipe.cc new file mode 100644 index 00000000000..76a3d43a942 --- /dev/null +++ b/winsup/cygwin/pipe.cc @@ -0,0 +1,289 @@ +/* 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 <errno.h> +#include <sys/socket.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.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) +{ + this->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 = {this, in_ptr, &in_len}; + ResetEvent (read_state); + cygthread *th = new cygthread (read_pipe, &pi, "read_pipe"); + if (th->detach (read_state) && !in_len) + (ssize_t) in_len = -1; /* received a signal */ + } + (void) ReleaseMutex (guard); + return; +} + +int +fhandler_pipe::close () +{ + if (guard) + CloseHandle (guard); + if (writepipe_exists) + CloseHandle (writepipe_exists); + if (read_state && !cygheap->fdtab.in_vfork_cleanup ()) + CloseHandle (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); +} + +void +fhandler_pipe::fixup_after_fork (HANDLE parent) +{ + this->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 = this->fhandler_base::dup (child); + if (res) + return res; + + fhandler_pipe *ftp = (fhandler_pipe *) child; + + /* 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); + return -1; + } + + 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); + return -1; + } + + 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); + return -1; + } + + ftp->id = id; + ftp->orig_pid = orig_pid; + return 0; +} + +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 *) cygheap->fdtab.build_fhandler (-1, *piper_dev, "/dev/piper"); + fhs[1] = (fhandler_pipe *) cygheap->fdtab.build_fhandler (-1, *pipew_dev, "/dev/pipew"); + + 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 (); + + 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/select.cc b/winsup/cygwin/select.cc new file mode 100644 index 00000000000..442b8b89883 --- /dev/null +++ b/winsup/cygwin/select.cc @@ -0,0 +1,1505 @@ +/* select.cc + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + Written by Christopher Faylor of Cygnus Solutions + cgf@cygnus.com + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* + * The following line means that the BSD socket + * definitions for fd_set, FD_ISSET etc. are used in this + * file. + */ + +#define __INSIDE_CYGWIN_NET__ + +#include "winsup.h" +#include <errno.h> +#include <sys/socket.h> +#include <stdlib.h> +#include <sys/time.h> + +#include <wingdi.h> +#include <winuser.h> +#include <netdb.h> +#include <unistd.h> +#include <stdio.h> +#define USE_SYS_TYPES_FD_SET +#include <winsock.h> +#include "select.h" +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" +#include "perthread.h" +#include "tty.h" +#include "cygthread.h" + +/* + * All these defines below should be in sys/types.h + * but because of the includes above, they may not have + * been included. We create special UNIX_xxxx versions here. + */ + +#ifndef NBBY +#define NBBY 8 /* number of bits in a byte */ +#endif /* NBBY */ + +/* + * Select uses bit masks of file descriptors in longs. + * These macros manipulate such bit fields (the filesystem macros use chars). + * FD_SETSIZE may be defined by the user, but the default here + * should be >= NOFILE (param.h). + */ + +typedef long fd_mask; +#define UNIX_NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +#ifndef unix_howmany +#define unix_howmany(x,y) (((x)+((y)-1))/(y)) +#endif + +#define unix_fd_set fd_set + +#define NULL_fd_set ((fd_set *) NULL) +#define sizeof_fd_set(n) \ + ((unsigned) (NULL_fd_set->fds_bits + unix_howmany ((n), UNIX_NFDBITS))) +#define UNIX_FD_SET(n, p) \ + ((p)->fds_bits[(n)/UNIX_NFDBITS] |= (1L << ((n) % UNIX_NFDBITS))) +#define UNIX_FD_CLR(n, p) \ + ((p)->fds_bits[(n)/UNIX_NFDBITS] &= ~(1L << ((n) % UNIX_NFDBITS))) +#define UNIX_FD_ISSET(n, p) \ + ((p)->fds_bits[(n)/UNIX_NFDBITS] & (1L << ((n) % UNIX_NFDBITS))) +#define UNIX_FD_ZERO(p, n) \ + bzero ((caddr_t)(p), sizeof_fd_set ((n))) + +#define allocfd_set(n) ((fd_set *) memset (alloca (sizeof_fd_set (n)), 0, sizeof_fd_set (n))) +#define copyfd_set(to, from, n) memcpy (to, from, sizeof_fd_set (n)); + +#define set_handle_or_return_if_not_open(h, s) \ + h = (s)->fh->get_handle (); \ + if (cygheap->fdtab.not_open ((s)->fd)) \ + { \ + (s)->saw_error = true; \ + set_sig_errno (EBADF); \ + return -1; \ + } \ + +/* The main select code. + */ +extern "C" +int +cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *to) +{ + select_stuff sel; + fd_set *dummy_readfds = allocfd_set (maxfds); + fd_set *dummy_writefds = allocfd_set (maxfds); + fd_set *dummy_exceptfds = allocfd_set (maxfds); + sigframe thisframe (mainthread); + + select_printf ("%d, %p, %p, %p, %p", maxfds, readfds, writefds, exceptfds, to); + + if (!readfds) + readfds = dummy_readfds; + if (!writefds) + writefds = dummy_writefds; + if (!exceptfds) + exceptfds = dummy_exceptfds; + + for (int i = 0; i < maxfds; i++) + if (!sel.test_and_set (i, readfds, writefds, exceptfds)) + { + select_printf ("aborting due to test_and_set error"); + return -1; /* Invalid fd, maybe? */ + } + + /* Convert to milliseconds or INFINITE if to == NULL */ + DWORD ms = to ? (to->tv_sec * 1000) + (to->tv_usec / 1000) : INFINITE; + if (ms == 0 && to->tv_usec) + ms = 1; /* At least 1 ms granularity */ + + if (to) + select_printf ("to->tv_sec %d, to->tv_usec %d, ms %d", to->tv_sec, to->tv_usec, ms); + else + select_printf ("to NULL, ms %x", ms); + + select_printf ("sel.always_ready %d", sel.always_ready); + + int timeout = 0; + /* Allocate some fd_set structures using the number of fds as a guide. */ + fd_set *r = allocfd_set (maxfds); + fd_set *w = allocfd_set (maxfds); + fd_set *e = allocfd_set (maxfds); + + /* Degenerate case. No fds to wait for. Just wait. */ + if (sel.start.next == NULL) + { + if (WaitForSingleObject (signal_arrived, ms) == WAIT_OBJECT_0) + { + select_printf ("signal received"); + set_sig_errno (EINTR); + return -1; + } + timeout = 1; + } + else if (sel.always_ready || ms == 0) + /* Don't bother waiting. */; + else if ((timeout = sel.wait (r, w, e, ms) < 0)) + return -1; /* some kind of error */ + + sel.cleanup (); + copyfd_set (readfds, r, maxfds); + copyfd_set (writefds, w, maxfds); + copyfd_set (exceptfds, e, maxfds); + return timeout ? 0 : sel.poll (readfds, writefds, exceptfds); +} + +/* Call cleanup functions for all inspected fds. Gets rid of any + executing threads. */ +void +select_stuff::cleanup () +{ + select_record *s = &start; + + select_printf ("calling cleanup routines"); + while ((s = s->next)) + if (s->cleanup) + { + s->cleanup (s, this); + s->cleanup = NULL; + } +} + +/* Destroy all storage associated with select stuff. */ +select_stuff::~select_stuff () +{ + cleanup (); + select_record *s = &start; + select_record *snext = start.next; + + select_printf ("deleting select records"); + while ((s = snext)) + { + snext = s->next; + delete s; + } +} + +/* Add a record to the select chain */ +int +select_stuff::test_and_set (int i, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + select_record *s = NULL; + if (UNIX_FD_ISSET (i, readfds) && (s = cygheap->fdtab.select_read (i, s)) == NULL) + return 0; /* error */ + if (UNIX_FD_ISSET (i, writefds) && (s = cygheap->fdtab.select_write (i, s)) == NULL) + return 0; /* error */ + if (UNIX_FD_ISSET (i, exceptfds) && (s = cygheap->fdtab.select_except (i, s)) == NULL) + return 0; /* error */ + if (s == NULL) + return 1; /* nothing to do */ + + if (s->read_ready || s->write_ready || s->except_ready) + always_ready = true; + + if (s->windows_handle || s->windows_handle || s->windows_handle) + windows_used = true; + + s->next = start.next; + start.next = s; + return 1; +} + +/* The heart of select. Waits for an fd to do something interesting. */ +int +select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + DWORD ms) +{ + int wait_ret; + HANDLE w4[MAXIMUM_WAIT_OBJECTS]; + select_record *s = &start; + int m = 0; + int res = 0; + + w4[m++] = signal_arrived; /* Always wait for the arrival of a signal. */ + /* Loop through the select chain, starting up anything appropriate and + counting the number of active fds. */ + while ((s = s->next)) + { + if (m > MAXIMUM_WAIT_OBJECTS) + { + set_sig_errno (EINVAL); + return -1; + } + if (!s->startup (s, this)) + { + __seterrno (); + return -1; + } + if (s->h == NULL) + continue; + for (int i = 1; i < m; i++) + if (w4[i] == s->h) + goto next_while; + w4[m++] = s->h; + next_while: + continue; + } + + DWORD start_time = GetTickCount (); /* Record the current time for later use. */ + + debug_printf ("m %d, ms %u", m, ms); + for (;;) + { + if (!windows_used) + wait_ret = WaitForMultipleObjects (m, w4, FALSE, ms); + else + wait_ret = MsgWaitForMultipleObjects (m, w4, FALSE, ms, QS_ALLINPUT); + + switch (wait_ret) + { + case WAIT_OBJECT_0: + select_printf ("signal received"); + set_sig_errno (EINTR); + return -1; + case WAIT_FAILED: + select_printf ("WaitForMultipleObjects failed"); + __seterrno (); + return -1; + case WAIT_TIMEOUT: + select_printf ("timed out"); + res = 1; + goto out; + } + + select_printf ("woke up. wait_ret %d. verifying", wait_ret); + s = &start; + int gotone = FALSE; + /* Some types of object (e.g., consoles) wake up on "inappropriate" events + like mouse movements. The verify function will detect these situations. + If it returns false, then this wakeup was a false alarm and we should go + back to waiting. */ + while ((s = s->next)) + if (s->saw_error) + return -1; /* Somebody detected an error */ + else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) && + s->verify (s, readfds, writefds, exceptfds)) + gotone = true; + + select_printf ("gotone %d", gotone); + if (gotone) + goto out; + + if (ms == INFINITE) + { + select_printf ("looping"); + continue; + } + select_printf ("recalculating ms"); + + DWORD now = GetTickCount (); + if (now > (start_time + ms)) + { + select_printf ("timed out after verification"); + goto out; + } + ms -= (now - start_time); + start_time = now; + select_printf ("ms now %u", ms); + } + +out: + select_printf ("returning %d", res); + return res; +} + +static int +set_bits (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + int ready = 0; + select_printf ("me %p, testing fd %d (%s)", me, me->fd, me->fh->get_name ()); + if (me->read_selected && me->read_ready) + { + UNIX_FD_SET (me->fd, readfds); + ready++; + } + if (me->write_selected && me->write_ready) + { + UNIX_FD_SET (me->fd, writefds); + if (me->except_on_write && me->fh->get_device () == FH_SOCKET) + ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED); + ready++; + } + if ((me->except_selected || me->except_on_write) && me->except_ready) + { + if (me->except_on_write) /* Only on sockets */ + { + UNIX_FD_SET (me->fd, writefds); + if (me->fh->get_device () == FH_SOCKET) + ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED); + } + if (me->except_selected) + UNIX_FD_SET (me->fd, exceptfds); + ready++; + } + select_printf ("ready %d", ready); + return ready; +} + +/* Poll every fd in the select chain. Set appropriate fd in mask. */ +int +select_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds) +{ + int n = 0; + select_record *s = &start; + while ((s = s->next)) + n += (!s->peek || s->peek (s, true)) ? + set_bits (s, readfds, writefds, exceptfds) : 0; + select_printf ("returning %d", n); + return n; +} + +static int +verify_true (select_record *, fd_set *, fd_set *, fd_set *) +{ + return 1; +} + +static int +verify_ok (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + return set_bits (me, readfds, writefds, exceptfds); +} + +static int +no_startup (select_record *, select_stuff *) +{ + return 1; +} + +static int +no_verify (select_record *, fd_set *, fd_set *, fd_set *) +{ + return 0; +} + +static int +peek_pipe (select_record *s, bool from_select) +{ + int n = 0; + int gotone = 0; + fhandler_base *fh = s->fh; + + HANDLE h; + set_handle_or_return_if_not_open (h, s); + + /* pipes require a guard mutex to guard against the situation where multiple + readers are attempting to read from the same pipe. In this scenario, it + is possible for PeekNamedPipe to report available data to two readers but + only one will actually get the data. This will result in the other reader + entering fhandler_base::raw_read and blocking indefinitely in an interruptible + state. This causes things like "make -j2" to hang. So, for the non-select case + we use the pipe mutex, if it is available. */ + HANDLE guard_mutex = from_select ? NULL : fh->get_guard (); + + /* Don't perform complicated tests if we don't need to. */ + if (!s->read_selected && !s->except_selected) + goto out; + + if (s->read_selected) + { + if (s->read_ready) + { + select_printf ("already ready"); + gotone = 1; + goto out; + } + + switch (fh->get_device ()) + { + case FH_PTYM: + case FH_TTYM: + if (((fhandler_pty_master *) fh)->need_nl) + { + gotone = s->read_ready = true; + goto out; + } + break; + default: + if (fh->get_readahead_valid ()) + { + select_printf ("readahead"); + gotone = s->read_ready = true; + goto out; + } + } + + if (fh->bg_check (SIGTTIN) <= bg_eof) + { + gotone = s->read_ready = true; + goto out; + } + } + + if (fh->get_device () == FH_PIPEW) + /* nothing */; + else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL)) + { + select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ()); + n = -1; + } + else if (!n || !guard_mutex) + /* no guard mutex or nothing to read from the pipe. */; + else if (WaitForSingleObject (guard_mutex, 0) != WAIT_OBJECT_0) + { + select_printf ("%s, couldn't get mutex %p, %E", fh->get_name (), + guard_mutex); + n = 0; + } + else + { + /* Now that we have the mutex, make sure that no one else has snuck + in and grabbed the data that we originally saw. */ + if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL)) + { + select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ()); + n = -1; + } + if (n <= 0) + ReleaseMutex (guard_mutex); /* Oops. We lost the race. */ + } + + if (n < 0) + { + fh->set_eof (); /* Flag that other end of pipe is gone */ + select_printf ("%s, n %d", fh->get_name (), n); + if (s->except_selected) + gotone += s->except_ready = true; + if (s->read_selected) + gotone += s->read_ready = true; + } + if (n > 0 && s->read_selected) + { + select_printf ("%s, ready for read", fh->get_name ()); + gotone += s->read_ready = true; + } + if (!gotone && s->fh->hit_eof ()) + { + select_printf ("%s, saw EOF", fh->get_name ()); + if (s->except_selected) + gotone = s->except_ready = true; + if (s->read_selected) + gotone += s->read_ready = true; + select_printf ("saw eof on '%s'", fh->get_name ()); + } + +out: + return gotone || s->write_ready; +} + +static int start_thread_pipe (select_record *me, select_stuff *stuff); + +struct pipeinf + { + cygthread *thread; + BOOL stop_thread_pipe; + select_record *start; + }; + +static DWORD WINAPI +thread_pipe (void *arg) +{ + pipeinf *pi = (pipeinf *) arg; + BOOL gotone = FALSE; + + for (;;) + { + select_record *s = pi->start; + while ((s = s->next)) + if (s->startup == start_thread_pipe) + { + if (peek_pipe (s, true)) + gotone = true; + if (pi->stop_thread_pipe) + { + select_printf ("stopping"); + goto out; + } + } + /* Paranoid check */ + if (pi->stop_thread_pipe) + { + select_printf ("stopping from outer loop"); + break; + } + if (gotone) + break; + Sleep (10); + } +out: + return 0; +} + +static int +start_thread_pipe (select_record *me, select_stuff *stuff) +{ + if (stuff->device_specific_pipe) + { + me->h = *((pipeinf *) stuff->device_specific_pipe)->thread; + return 1; + } + pipeinf *pi = new pipeinf; + pi->start = &stuff->start; + pi->stop_thread_pipe = FALSE; + pi->thread = new cygthread (thread_pipe, (LPVOID) pi, "select_pipe"); + me->h = *pi->thread; + if (!me->h) + return 0; + stuff->device_specific_pipe = (void *) pi; + return 1; +} + +static void +pipe_cleanup (select_record *, select_stuff *stuff) +{ + pipeinf *pi = (pipeinf *) stuff->device_specific_pipe; + if (pi && pi->thread) + { + pi->stop_thread_pipe = true; + pi->thread->detach (); + delete pi; + stuff->device_specific_pipe = NULL; + } +} + +int +fhandler_pipe::ready_for_read (int fd, DWORD howlong) +{ + if (!howlong) + return this->fhandler_base::ready_for_read (fd, howlong); + + get_guard (); + return true; +} + +select_record * +fhandler_pipe::select_read (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_pipe; + s->peek = peek_pipe; + s->verify = verify_ok; + s->read_selected = true; + s->read_ready = false; + s->cleanup = pipe_cleanup; + return s; +} + +select_record * +fhandler_pipe::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->peek = peek_pipe; + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_pipe::select_except (select_record *s) +{ + if (!s) + s = new select_record; + s->startup = start_thread_pipe; + s->peek = peek_pipe; + s->verify = verify_ok; + s->cleanup = pipe_cleanup; + s->except_selected = true; + s->except_ready = false; + return s; +} + +static int +peek_console (select_record *me, bool) +{ + extern const char * get_nonascii_key (INPUT_RECORD& input_rec, char *); + fhandler_console *fh = (fhandler_console *) me->fh; + + if (!me->read_selected) + return me->write_ready; + + if (fh->get_readahead_valid ()) + { + select_printf ("readahead"); + return me->read_ready = true; + } + + if (me->read_ready) + { + select_printf ("already ready"); + return 1; + } + + INPUT_RECORD irec; + DWORD events_read; + HANDLE h; + char tmpbuf[17]; + set_handle_or_return_if_not_open (h, me); + + for (;;) + if (fh->bg_check (SIGTTIN) <= bg_eof) + return me->read_ready = true; + else if (!PeekConsoleInput (h, &irec, 1, &events_read) || !events_read) + break; + else + { + if (irec.EventType == KEY_EVENT) + { + if (irec.Event.KeyEvent.bKeyDown + && (irec.Event.KeyEvent.uChar.AsciiChar + || get_nonascii_key (irec, tmpbuf))) + return me->read_ready = true; + } + else + { + fh->send_winch_maybe (); + if (irec.EventType == MOUSE_EVENT + && fh->mouse_aware () + && (irec.Event.MouseEvent.dwEventFlags == 0 + || irec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)) + return me->read_ready = true; + } + + /* Read and discard the event */ + ReadConsoleInput (h, &irec, 1, &events_read); + } + + return me->write_ready; +} + +static int +verify_console (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_console (me, true); +} + + +select_record * +fhandler_console::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_console; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->h = get_handle (); + s->read_selected = true; + s->read_ready = false; + return s; +} + +select_record * +fhandler_console::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_console::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + set_cursor_maybe (); + } + + s->peek = peek_console; + s->except_selected = true; + s->except_ready = false; + return s; +} + +select_record * +fhandler_tty_common::select_read (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_read (s); +} + +select_record * +fhandler_tty_common::select_write (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_write (s); +} + +select_record * +fhandler_tty_common::select_except (select_record *s) +{ + return ((fhandler_pipe *) this)->fhandler_pipe::select_except (s); +} + +static int +verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds) +{ + if (WaitForSingleObject (me->h, 0) == WAIT_OBJECT_0) + me->read_ready = true; + return set_bits (me, readfds, writefds, exceptfds); +} + +select_record * +fhandler_tty_slave::select_read (select_record *s) +{ + if (!s) + s = new select_record; + s->h = input_available_event; + s->startup = no_startup; + s->peek = peek_pipe; + s->verify = verify_tty_slave; + s->read_selected = true; + s->read_ready = false; + s->cleanup = NULL; + return s; +} + +select_record * +fhandler_dev_null::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->read_selected = true; + s->read_ready = true; + return s; +} + +select_record * +fhandler_dev_null::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_dev_null::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = no_verify; + } + s->h = get_handle (); + s->except_selected = true; + s->except_ready = true; + return s; +} + +static int start_thread_serial (select_record *me, select_stuff *stuff); + +struct serialinf + { + cygthread *thread; + BOOL stop_thread_serial; + select_record *start; + }; + +static int +peek_serial (select_record *s, bool) +{ + COMSTAT st; + + fhandler_serial *fh = (fhandler_serial *) s->fh; + + if (fh->get_readahead_valid () || fh->overlapped_armed < 0) + return s->read_ready = true; + + select_printf ("fh->overlapped_armed %d", fh->overlapped_armed); + + HANDLE h; + set_handle_or_return_if_not_open (h, s); + int ready = 0; + + if (s->read_selected && s->read_ready || (s->write_selected && s->write_ready)) + { + select_printf ("already ready"); + ready = 1; + goto out; + } + + (void) SetCommMask (h, EV_RXCHAR); + + if (!fh->overlapped_armed) + { + COMSTAT st; + + ResetEvent (fh->io_status.hEvent); + + if (!ClearCommError (h, &fh->ev, &st)) + { + debug_printf ("ClearCommError"); + goto err; + } + else if (st.cbInQue) + return s->read_ready = true; + else if (WaitCommEvent (h, &fh->ev, &fh->io_status)) + return s->read_ready = true; + else if (GetLastError () == ERROR_IO_PENDING) + fh->overlapped_armed = 1; + else + { + debug_printf ("WaitCommEvent"); + goto err; + } + } + + HANDLE w4[2]; + DWORD to; + + w4[0] = fh->io_status.hEvent; + w4[1] = signal_arrived; + to = 10; + + switch (WaitForMultipleObjects (2, w4, FALSE, to)) + { + case WAIT_OBJECT_0: + if (!ClearCommError (h, &fh->ev, &st)) + { + debug_printf ("ClearCommError"); + goto err; + } + else if (!st.cbInQue) + Sleep (to); + else + { + return s->read_ready = true; + select_printf ("got something"); + } + break; + case WAIT_OBJECT_0 + 1: + select_printf ("interrupt"); + set_sig_errno (EINTR); + ready = -1; + break; + case WAIT_TIMEOUT: + break; + default: + debug_printf ("WaitForMultipleObjects"); + goto err; + } + +out: + return ready; + +err: + if (GetLastError () == ERROR_OPERATION_ABORTED) + { + select_printf ("operation aborted"); + return ready; + } + + __seterrno (); + s->saw_error = true; + select_printf ("error %E"); + return -1; +} + +static DWORD WINAPI +thread_serial (void *arg) +{ + serialinf *si = (serialinf *) arg; + BOOL gotone= FALSE; + + for (;;) + { + select_record *s = si->start; + while ((s = s->next)) + if (s->startup == start_thread_serial) + { + if (peek_serial (s, true)) + gotone = true; + } + if (si->stop_thread_serial) + { + select_printf ("stopping"); + break; + } + if (gotone) + break; + } + + select_printf ("exiting"); + return 0; +} + +static int +start_thread_serial (select_record *me, select_stuff *stuff) +{ + if (stuff->device_specific_serial) + { + me->h = *((serialinf *) stuff->device_specific_serial)->thread; + return 1; + } + serialinf *si = new serialinf; + si->start = &stuff->start; + si->stop_thread_serial = FALSE; + si->thread = new cygthread (thread_serial, (LPVOID) si, "select_serial"); + me->h = *si->thread; + stuff->device_specific_serial = (void *) si; + return 1; +} + +static void +serial_cleanup (select_record *, select_stuff *stuff) +{ + serialinf *si = (serialinf *) stuff->device_specific_serial; + if (si && si->thread) + { + si->stop_thread_serial = true; + si->thread->detach (); + delete si; + stuff->device_specific_serial = NULL; + } +} + +select_record * +fhandler_serial::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_serial; + s->verify = verify_ok; + s->cleanup = serial_cleanup; + } + s->peek = peek_serial; + s->read_selected = true; + s->read_ready = false; + return s; +} + +select_record * +fhandler_serial::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_serial; + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_serial::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = NULL; + s->peek = peek_serial; + s->except_selected = false; // Can't do this + s->except_ready = false; + return s; +} + +int +fhandler_base::ready_for_read (int fd, DWORD howlong) +{ + int avail = 0; + select_record me (this); + me.fd = fd; + while (!avail) + { + (void) select_read (&me); + avail = me.read_ready ?: me.peek (&me, false); + + if (fd >= 0 && cygheap->fdtab.not_open (fd)) + { + set_sig_errno (EBADF); + avail = 0; + break; + } + + if (howlong != INFINITE) + { + if (!avail) + set_sig_errno (EAGAIN); + break; + } + + if (WaitForSingleObject (signal_arrived, avail ? 0 : 10) == WAIT_OBJECT_0) + { + set_sig_errno (EINTR); + avail = 0; + break; + } + } + + if (get_guard () && !avail && me.read_ready) + ReleaseMutex (get_guard ()); + + select_printf ("read_ready %d, avail %d", me.read_ready, avail); + return avail; +} + +select_record * +fhandler_base::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = get_handle (); + s->read_selected = true; + s->read_ready = true; + return s; +} + +select_record * +fhandler_base::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + return s; +} + +select_record * +fhandler_base::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->h = NULL; + s->except_selected = true; + s->except_ready = false; + return s; +} + +struct socketinf + { + cygthread *thread; + winsock_fd_set readfds, writefds, exceptfds; + SOCKET exitsock; + struct sockaddr_in sin; + select_record *start; + }; + +static int +peek_socket (select_record *me, bool) +{ + winsock_fd_set ws_readfds, ws_writefds, ws_exceptfds; + struct timeval tv = {0, 0}; + WINSOCK_FD_ZERO (&ws_readfds); + WINSOCK_FD_ZERO (&ws_writefds); + WINSOCK_FD_ZERO (&ws_exceptfds); + + HANDLE h; + set_handle_or_return_if_not_open (h, me); + select_printf ("considering handle %p", h); + + if (me->read_selected && !me->read_ready) + { + select_printf ("adding read fd_set %s, fd %d", me->fh->get_name (), + me->fd); + WINSOCK_FD_SET (h, &ws_readfds); + } + if (me->write_selected && !me->write_ready) + { + select_printf ("adding write fd_set %s, fd %d", me->fh->get_name (), + me->fd); + WINSOCK_FD_SET (h, &ws_writefds); + } + if ((me->except_selected || me->except_on_write) && !me->except_ready) + { + select_printf ("adding except fd_set %s, fd %d", me->fh->get_name (), + me->fd); + WINSOCK_FD_SET (h, &ws_exceptfds); + } + int r; + if ((me->read_selected && !me->read_ready) + || (me->write_selected && !me->write_ready) + || ((me->except_selected || me->except_on_write) && !me->except_ready)) + { + r = WINSOCK_SELECT (0, &ws_readfds, &ws_writefds, &ws_exceptfds, &tv); + select_printf ("WINSOCK_SELECT returned %d", r); + if (r == -1) + { + select_printf ("error %d", WSAGetLastError ()); + set_winsock_errno (); + return 0; + } + if (WINSOCK_FD_ISSET (h, &ws_readfds) || (me->read_selected && me->read_ready)) + me->read_ready = true; + if (WINSOCK_FD_ISSET (h, &ws_writefds) || (me->write_selected && me->write_ready)) + me->write_ready = true; + if (WINSOCK_FD_ISSET (h, &ws_exceptfds) || ((me->except_selected || me->except_on_write) && me->except_ready)) + me->except_ready = true; + } + return me->read_ready || me->write_ready || me->except_ready; +} + +static int start_thread_socket (select_record *, select_stuff *); + +static DWORD WINAPI +thread_socket (void *arg) +{ + socketinf *si = (socketinf *) arg; + + select_printf ("stuff_start %p", &si->start); + int r = WINSOCK_SELECT (0, &si->readfds, &si->writefds, &si->exceptfds, NULL); + select_printf ("Win32 select returned %d", r); + if (r == -1) + select_printf ("error %d", WSAGetLastError ()); + select_record *s = si->start; + while ((s = s->next)) + if (s->startup == start_thread_socket) + { + HANDLE h = s->fh->get_handle (); + select_printf ("s %p, testing fd %d (%s)", s, s->fd, s->fh->get_name ()); + if (WINSOCK_FD_ISSET (h, &si->readfds)) + { + select_printf ("read_ready"); + s->read_ready = true; + } + if (WINSOCK_FD_ISSET (h, &si->writefds)) + { + select_printf ("write_ready"); + s->write_ready = true; + } + if (WINSOCK_FD_ISSET (h, &si->exceptfds)) + { + select_printf ("except_ready"); + s->except_ready = true; + } + } + + if (WINSOCK_FD_ISSET (si->exitsock, &si->readfds)) + select_printf ("saw exitsock read"); + + return 0; +} + +extern "C" unsigned long htonl (unsigned long); + +static int +start_thread_socket (select_record *me, select_stuff *stuff) +{ + socketinf *si; + + if ((si = (socketinf *) stuff->device_specific_socket)) + { + me->h = *si->thread; + return 1; + } + + si = new socketinf; + WINSOCK_FD_ZERO (&si->readfds); + WINSOCK_FD_ZERO (&si->writefds); + WINSOCK_FD_ZERO (&si->exceptfds); + select_record *s = &stuff->start; + while ((s = s->next)) + if (s->startup == start_thread_socket) + { + HANDLE h = s->fh->get_handle (); + select_printf ("Handle %p", h); + if (s->read_selected && !s->read_ready) + { + WINSOCK_FD_SET (h, &si->readfds); + select_printf ("Added to readfds"); + } + if (s->write_selected && !s->write_ready) + { + WINSOCK_FD_SET (h, &si->writefds); + select_printf ("Added to writefds"); + } + if ((s->except_selected || s->except_on_write) && !s->except_ready) + { + WINSOCK_FD_SET (h, &si->exceptfds); + select_printf ("Added to exceptfds"); + } + } + + if ((si->exitsock = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + set_winsock_errno (); + select_printf ("cannot create socket, %E"); + return -1; + } + /* Allow rapid reuse of the port. */ + int tmp = 1; + (void) setsockopt (si->exitsock, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp)); + + int sin_len = sizeof (si->sin); + memset (&si->sin, 0, sizeof (si->sin)); + si->sin.sin_family = AF_INET; + si->sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (bind (si->exitsock, (struct sockaddr *) &si->sin, sizeof (si->sin)) < 0) + { + select_printf ("cannot bind socket, %E"); + goto err; + } + + if (getsockname (si->exitsock, (struct sockaddr *) &si->sin, &sin_len) < 0) + { + select_printf ("getsockname error"); + goto err; + } + + if (listen (si->exitsock, 1)) + { + select_printf ("listen failed, %E"); + goto err; + } + + select_printf ("exitsock %p", si->exitsock); + WINSOCK_FD_SET ((HANDLE) si->exitsock, &si->readfds); + WINSOCK_FD_SET ((HANDLE) si->exitsock, &si->exceptfds); + stuff->device_specific_socket = (void *) si; + si->start = &stuff->start; + select_printf ("stuff_start %p", &stuff->start); + si->thread = new cygthread (thread_socket, (LPVOID) si, "select_socket"); + me->h = *si->thread; + return 1; + +err: + set_winsock_errno (); + closesocket (si->exitsock); + return -1; +} + +void +socket_cleanup (select_record *, select_stuff *stuff) +{ + socketinf *si = (socketinf *) stuff->device_specific_socket; + select_printf ("si %p si->thread %p", si, si ? si->thread : NULL); + if (si && si->thread) + { + select_printf ("connection to si->exitsock %p", si->exitsock); + SOCKET s = socket (AF_INET, SOCK_STREAM, 0); + + /* Set LINGER with 0 timeout for hard close */ + struct linger tmp = {1, 0}; /* On, 0 delay */ + (void) setsockopt (s, SOL_SOCKET, SO_LINGER, (char *)&tmp, sizeof (tmp)); + (void) setsockopt (si->exitsock, SOL_SOCKET, SO_LINGER, (char *)&tmp, sizeof (tmp)); + + /* Connecting to si->exitsock will cause any executing select to wake + up. When this happens then the exitsock condition will cause the + thread to terminate. */ + if (connect (s, (struct sockaddr *) &si->sin, sizeof (si->sin)) < 0) + { + set_winsock_errno (); + select_printf ("connect failed"); + /* FIXME: now what? */ + } + shutdown (s, SD_BOTH); + closesocket (s); + + /* Wait for thread to go away */ + si->thread->detach (); + shutdown (si->exitsock, SD_BOTH); + closesocket (si->exitsock); + stuff->device_specific_socket = NULL; + delete si; + } + select_printf ("returning"); +} + +select_record * +fhandler_socket::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + s->read_ready = saw_shutdown_read (); + s->read_selected = true; + return s; +} + +select_record * +fhandler_socket::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + s->write_ready = saw_shutdown_write () || is_unconnected (); + s->write_selected = true; + if (is_connect_pending ()) + { + s->except_ready = saw_shutdown_write () || saw_shutdown_read (); + s->except_on_write = true; + } + return s; +} + +select_record * +fhandler_socket::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = start_thread_socket; + s->verify = verify_true; + s->cleanup = socket_cleanup; + } + s->peek = peek_socket; + /* FIXME: Is this right? Should these be used as criteria for except? */ + s->except_ready = saw_shutdown_write () || saw_shutdown_read (); + s->except_selected = true; + return s; +} + +static int +peek_windows (select_record *me, bool) +{ + MSG m; + HANDLE h; + set_handle_or_return_if_not_open (h, me); + + if (me->read_selected && me->read_ready) + return 1; + + if (PeekMessage (&m, (HWND) h, 0, 0, PM_NOREMOVE)) + { + me->read_ready = true; + select_printf ("window %d(%p) ready", me->fd, me->fh->get_handle ()); + return 1; + } + + select_printf ("window %d(%p) not ready", me->fd, me->fh->get_handle ()); + return me->write_ready; +} + +static int +verify_windows (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_windows (me, true); +} + +select_record * +fhandler_windows::select_read (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + } + s->verify = verify_windows; + s->peek = peek_windows; + s->read_selected = true; + s->read_ready = false; + s->h = get_handle (); + s->windows_handle = true; + return s; +} + +select_record * +fhandler_windows::select_write (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_windows; + s->h = get_handle (); + s->write_selected = true; + s->write_ready = true; + s->windows_handle = true; + return s; +} + +select_record * +fhandler_windows::select_except (select_record *s) +{ + if (!s) + { + s = new select_record; + s->startup = no_startup; + s->verify = verify_ok; + } + s->peek = peek_windows; + s->h = get_handle (); + s->except_selected = true; + s->except_ready = true; + s->windows_handle = true; + return s; +} diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h new file mode 100644 index 00000000000..77e57a279fe --- /dev/null +++ b/winsup/cygwin/shared_info.h @@ -0,0 +1,191 @@ +/* shared_info.h: shared info for cygwin + + Copyright 2000, 2001 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 "tty.h" + +/* Mount table entry */ + +class mount_item +{ + public: + /* FIXME: Nasty static allocation. Need to have a heap in the shared + area [with the user being able to configure at runtime the max size]. */ + /* Win32-style mounted partition source ("C:\foo\bar"). + native_path[0] == 0 for unused entries. */ + char native_path[MAX_PATH]; + int native_pathlen; + + /* POSIX-style mount point ("/foo/bar") */ + char posix_path[MAX_PATH]; + int posix_pathlen; + + unsigned flags; + + void init (const char *dev, const char *path, unsigned flags); + + struct mntent *getmntent (); +}; + +/* Warning: Decreasing this value will cause cygwin.dll to ignore existing + higher numbered registry entries. Don't change this number willy-nilly. + What we need is to have a more dynamic allocation scheme, but the current + scheme should be satisfactory for a long while yet. */ +#define MAX_MOUNTS 30 + +#define MOUNT_VERSION 27 // increment when mount table changes and +#define MOUNT_VERSION_MAGIC CYGWIN_VERSION_MAGIC (MOUNT_MAGIC, MOUNT_VERSION) +#define CURR_MOUNT_MAGIC 0x6dd73a3fU +#define MOUNT_INFO_CB 16488 + +class reg_key; +struct device; + +/* NOTE: Do not make gratuitous changes to the names or organization of the + below class. The layout is checksummed to determine compatibility between + different cygwin versions. */ +class mount_info +{ + public: + DWORD version; + unsigned cb; + DWORD sys_mount_table_counter; + int nmounts; + mount_item mount[MAX_MOUNTS]; + + /* cygdrive_prefix is used as the root of the path automatically + prepended to a path when the path has no associated mount. + cygdrive_flags are the default flags for the cygdrives. */ + char cygdrive[MAX_PATH]; + size_t cygdrive_len; + unsigned cygdrive_flags; + private: + int posix_sorted[MAX_MOUNTS]; + int native_sorted[MAX_MOUNTS]; + + public: + /* Increment when setting up a reg_key if mounts area had to be + created so we know when we need to import old mount tables. */ + int had_to_create_mount_areas; + + void init (); + int add_item (const char *dev, const char *path, unsigned flags, int reg_p); + int del_item (const char *path, unsigned flags, int reg_p); + + void from_registry (); + int add_reg_mount (const char * native_path, const char * posix_path, + unsigned mountflags); + int del_reg_mount (const char * posix_path, unsigned mountflags); + + unsigned set_flags_from_win32_path (const char *path); + int conv_to_win32_path (const char *src_path, char *dst, device&, + unsigned *flags = NULL, bool no_normalize = 0); + int conv_to_posix_path (const char *src_path, char *posix_path, + int keep_rel_p); + struct mntent *getmntent (int x); + + int write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags); + int remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags); + int get_cygdrive_info (char *user, char *system, char* user_flags, + char* system_flags); + + private: + + void sort (); + void read_mounts (reg_key& r); + void mount_slash (); + void to_registry (); + + int cygdrive_win32_path (const char *src, char *dst, int& unit); + void cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p); + void read_cygdrive_info_from_registry (); +}; + +/******** Close-on-delete queue ********/ + +/* First pass at a file deletion queue structure. + + We can't keep this list in the per-process info, since + one process may open a file, and outlive a process which + wanted to unlink the file - and the data would go away. +*/ + +#define MAX_DELQUEUES_PENDING 100 + +class delqueue_list +{ + char name[MAX_DELQUEUES_PENDING][MAX_PATH]; + char inuse[MAX_DELQUEUES_PENDING]; + int empty; + +public: + void init (); + void queue_file (const char *dosname); + void process_queue (); +}; + +/******** Shared Info ********/ +/* Data accessible to all tasks */ + +#define SHARED_VERSION (unsigned)(cygwin_version.api_major << 8 | \ + cygwin_version.api_minor) +#define SHARED_VERSION_MAGIC CYGWIN_VERSION_MAGIC (SHARED_MAGIC, SHARED_VERSION) + +#define SHARED_INFO_CB 47112 + +#define CURR_SHARED_MAGIC 0x359218a2U + +/* NOTE: Do not make gratuitous changes to the names or organization of the + below class. The layout is checksummed to determine compatibility between + different cygwin versions. */ +class shared_info +{ + DWORD version; + DWORD cb; + public: + unsigned heap_chunk; + DWORD sys_mount_table_counter; + + tty_list tty; + delqueue_list delqueue; + void initialize (); + unsigned heap_chunk_size (); +}; + +extern shared_info *cygwin_shared; +extern mount_info *mount_table; +extern HANDLE cygwin_mount_h; + +enum shared_locations +{ + SH_CYGWIN_SHARED, + SH_MOUNT_TABLE, + SH_SHARED_CONSOLE, + SH_MYSELF, + SH_TOTAL_SIZE +}; +void __stdcall memory_init (); + +#define shared_align_past(p) \ + ((char *) (system_info.dwAllocationGranularity * \ + (((DWORD) ((p) + 1) + system_info.dwAllocationGranularity - 1) / \ + system_info.dwAllocationGranularity))) + +#define cygwin_shared_address ((void *) 0xa000000) + +#ifdef FHDEVN +struct console_state +{ + tty_min tty_min_state; + dev_console dev_state; +}; +#endif + +char *__stdcall shared_name (const char *, int); +void *__stdcall open_shared (const char *name, int n, HANDLE &shared_h, DWORD size, shared_locations); diff --git a/winsup/cygwin/smallprint.c b/winsup/cygwin/smallprint.c new file mode 100644 index 00000000000..ec31afa9cbf --- /dev/null +++ b/winsup/cygwin/smallprint.c @@ -0,0 +1,243 @@ +/* smallprint.c: small print routines for WIN32 + + Copyright 1996, 1998, 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. */ + +#include "winsup.h" +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +int __small_sprintf (char *dst, const char *fmt, ...); +int __small_vsprintf (char *dst, const char *fmt, va_list ap); + +static char * +rn (char *dst, int base, int dosign, long long val, int len, int pad) +{ + /* longest number is ULLONG_MAX, 18446744073709551615, 20 digits */ + unsigned uval; + char res[20]; + static const char str[16] = "0123456789ABCDEF"; + int l = 0; + + if (dosign && val < 0) + { + *dst++ = '-'; + uval = -val; + } + else if (dosign > 0 && val > 0) + { + *dst++ = '+'; + uval = val; + } + else + { + uval = val; + } + + do + { + res[l++] = str[uval % base]; + uval /= base; + } + while (uval); + + while (len -- > l) + *dst++ = pad; + + while (l > 0) + { + *dst++ = res[--l]; + } + + return dst; +} + +int +__small_vsprintf (char *dst, const char *fmt, va_list ap) +{ + char tmp[MAX_PATH + 1]; + char *orig = dst; + const char *s; + + while (*fmt) + { + int i, n = 0x7fff; + if (*fmt != '%') + *dst++ = *fmt++; + else + { + int len = 0; + char pad = ' '; + int addsign = -1; + + switch (*++fmt) + { + case '+': + addsign = 1; + fmt++; + break; + case '%': + *dst++ = *fmt++; + continue; + } + + for (;;) + { + char c = *fmt++; + switch (c) + { + case '0': + if (len == 0) + { + pad = '0'; + continue; + } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + len = len * 10 + (c - '0'); + continue; + case 'l': + continue; + case 'c': + { + int c = va_arg (ap, int); + if (c > ' ' && c <= 127) + *dst++ = c; + else + { + *dst++ = '0'; + *dst++ = 'x'; + dst = rn (dst, 16, 0, c, len, pad); + } + } + break; + case 'E': + strcpy (dst, "Win32 error "); + dst = rn (dst + sizeof ("Win32 error"), 10, 0, GetLastError (), len, pad); + break; + case 'd': + dst = rn (dst, 10, addsign, va_arg (ap, int), len, pad); + break; + case 'D': + dst = rn (dst, 10, addsign, va_arg (ap, long long), len, pad); + break; + case 'u': + dst = rn (dst, 10, 0, va_arg (ap, int), len, pad); + break; + case 'U': + dst = rn (dst, 10, 0, va_arg (ap, long long), len, pad); + break; + case 'o': + dst = rn (dst, 8, 0, va_arg (ap, unsigned), len, pad); + break; + case 'p': + *dst++ = '0'; + *dst++ = 'x'; + /* fall through */ + case 'x': + dst = rn (dst, 16, 0, va_arg (ap, int), len, pad); + break; + case 'X': + dst = rn (dst, 16, 0, va_arg (ap, long long), len, pad); + break; + case 'P': + if (!GetModuleFileName (NULL, tmp, MAX_PATH)) + s = "cygwin program"; + else + s = tmp; + goto fillin; + case '.': + n = strtol (fmt, (char **)&fmt, 10); + if (*fmt++ != 's') + goto endfor; + case 's': + s = va_arg (ap, char *); + if (s == NULL) + s = "(null)"; + fillin: + for (i = 0; *s && i < n; i++) + *dst++ = *s++; + break; + default: + *dst++ = '?'; + *dst++ = fmt[-1]; + } + endfor: + break; + } + } + } + *dst = 0; + return dst - orig; +} + +int +__small_sprintf (char *dst, const char *fmt, ...) +{ + int r; + va_list ap; + va_start (ap, fmt); + r = __small_vsprintf (dst, fmt, ap); + va_end (ap); + return r; +} + +void +small_printf (const char *fmt, ...) +{ + char buf[16384]; + va_list ap; + DWORD done; + int count; + +#if 0 /* Turn on to force console errors */ + extern SECURITY_ATTRIBUTES sec_none; + HANDLE h = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_WRITE, &sec_none, + OPEN_EXISTING, 0, 0); + if (h) + SetStdHandle (STD_ERROR_HANDLE, h); +#endif + + va_start (ap, fmt); + count = __small_vsprintf (buf, fmt, ap); + va_end (ap); + + WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, count, &done, NULL); + FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE)); +} + +#ifdef DEBUGGING +static HANDLE NO_COPY console_handle = NULL; +void +console_printf (const char *fmt, ...) +{ + char buf[16384]; + va_list ap; + DWORD done; + int count; + + if (!console_handle) + console_handle = CreateFileA ("CON", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, 0); + + if (console_handle == INVALID_HANDLE_VALUE) + console_handle = GetStdHandle (STD_ERROR_HANDLE); + + va_start (ap, fmt); + count = __small_vsprintf (buf, fmt, ap); + va_end (ap); + + WriteFile (console_handle, buf, count, &done, NULL); + FlushFileBuffers (console_handle); +} +#endif diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc new file mode 100644 index 00000000000..1c196384fbb --- /dev/null +++ b/winsup/cygwin/syscalls.cc @@ -0,0 +1,2662 @@ +/* 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 _close __FOO_close__ +#define _lseek __FOO_lseek__ +#define _open __FOO_open__ +#define _read __FOO_read__ +#define _write __FOO_write__ + +#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 <limits.h> +#include <winnls.h> +#include <wininet.h> +#include <lmcons.h> /* for UNLEN */ +#include <cygwin/version.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "sigproc.h" +#include "pinfo.h" +#include <unistd.h> +#include "shared_info.h" +#include "cygheap.h" +#define NEED_VFORK +#include <setjmp.h> +#include "perthread.h" +#include "pwdgrp.h" + +#undef _close +#undef _lseek +#undef _open +#undef _read +#undef _write + +SYSTEM_INFO system_info; + +static int __stdcall mknod_worker (const char *, mode_t, mode_t, _major_t, + _minor_t); + +/* 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) +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files"); + + fhandler_base *fh; + for (int i = 0; i < (int) cygheap->fdtab.size; i++) + if ((fh = cygheap->fdtab[i]) != NULL) + { + fh->close (); + cygheap->fdtab.release (i); + } + + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files"); + cygwin_shared->delqueue.process_queue (); +} + +BOOL __stdcall +check_pty_fds (void) +{ + int res = FALSE; + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK, "check_pty_fds"); + fhandler_base *fh; + for (int i = 0; i < (int) cygheap->fdtab.size; i++) + if ((fh = cygheap->fdtab[i]) != NULL && + (fh->get_device () == FH_TTYS || fh->get_device () == FH_PTYM)) + { + res = TRUE; + break; + } + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK, "check_pty_fds"); + return res; +} + +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; + sigframe thisframe (mainthread); + + 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"); + goto done; + } + + /* Check for shortcut as symlink condition. */ + if (win32_name.has_attribute (FILE_ATTRIBUTE_READONLY)) + { + int len = strlen (win32_name); + if (len > 4 && strcasematch ((char *) win32_name + len - 4, ".lnk")) + SetFileAttributes (win32_name, (DWORD) win32_name & ~FILE_ATTRIBUTE_READONLY); + } + + DWORD lasterr; + lasterr = 0; + for (int i = 0; i < 2; i++) + { + if (DeleteFile (win32_name)) + { + syscall_printf ("DeleteFile succeeded"); + goto ok; + } + + lasterr = GetLastError (); + if (i || lasterr != ERROR_ACCESS_DENIED || win32_name.issymlink ()) + break; /* Couldn't delete it. */ + + /* if access denied, chmod to be writable, in case it is not, + and try again */ + (void) chmod (win32_name, 0777); + } + + /* 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; + + /* Tried to delete file by normal DeleteFile and by resetting protection + and then deleting. That didn't work. + + There are two possible reasons for this: 1) The file may be opened and + Windows is not allowing it to be deleted, or 2) We may not have permissions + to delete the file. + + So, first assume that it may be 1) and try to remove the file using the + Windows FILE_FLAG_DELETE_ON_CLOSE semantics. This seems to work only + spottily on Windows 9x/Me but it does seem to work reliably on NT as + long as the file doesn't exist on a remote drive. */ + + bool delete_on_close_ok; + + delete_on_close_ok = !win32_name.isremote () + && wincap.has_delete_on_close (); + + /* Attempt to use "delete on close" semantics to handle removing + a file which may be open. */ + HANDLE h; + h = CreateFile (win32_name, GENERIC_READ, FILE_SHARE_READ, &sec_none_nih, + OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); + if (h == INVALID_HANDLE_VALUE) + { + if (GetLastError () == ERROR_FILE_NOT_FOUND) + goto ok; + } + else + { + CloseHandle (h); + syscall_printf ("CreateFile/CloseHandle succeeded"); + /* Everything is fine if the file has disappeared or if we know that the + FILE_FLAG_DELETE_ON_CLOSE will eventually work. */ + if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES + || delete_on_close_ok) + goto ok; /* The file is either gone already or will eventually be + deleted by the OS. */ + } + + /* 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. */ + cygwin_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 (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) +{ + 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 */ + } + + if (myself->pgid != myself->pid) + { + if (myself->ctty == TTY_CONSOLE + && !cygheap->fdtab.has_console_fds () + && !check_pty_fds ()) + FreeConsole (); + myself->ctty = -1; + myself->sid = getpid (); + myself->pgid = getpid (); + syscall_printf ("sid %d, pgid %d, ctty %d", myself->sid, myself->pgid, myself->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 (0); + sigframe thisframe (mainthread); + + 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 || !thisframe.call_signal_handler ()) + 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 (0); + const ssize_t tot = check_iovec_for_write (iov, iovcnt); + + sigframe thisframe (mainthread); + 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 > 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 (0); + sigframe thisframe (mainthread); + + 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) + { + path_conv pc; + if (!(fh = cygheap->fdtab.build_fhandler_from_name (fd, unix_path, + NULL, pc))) + res = -1; // errno already set + else if (fh->is_fs_special () && fh->device_access_denied (flags)) + { + fd.release (); + res = -1; + } + else if (!fh->open (&pc, flags, (mode & 07777) & ~cygheap->umask)) + { + fd.release (); + res = -1; + } + else 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" __off64_t +lseek64 (int fd, __off64_t pos, int dir) +{ + __off64_t res; + sigframe thisframe (mainthread); + + 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" __off32_t +lseek (int fd, __off32_t pos, int dir) +{ + return lseek64 (fd, (__off64_t) pos, dir); +} + +extern "C" __off32_t _lseek (int, __off32_t, int) + __attribute__ ((alias ("lseek"))); + +extern "C" int +close (int fd) +{ + int res; + sigframe thisframe (mainthread); + + 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; + sigframe thisframe (mainthread); + + 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; + sigframe thisframe (mainthread); + path_conv real_a (a, PC_SYM_FOLLOW | PC_FULL); + path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL); + + 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; + } + + /* Try to make hard link first on Windows NT */ + if (wincap.has_hard_links ()) + { + if (CreateHardLinkA (real_b, real_a, NULL)) + { + res = 0; + goto done; + } + + HANDLE hFileSource; + + WIN32_STREAM_ID StreamId; + DWORD dwBytesWritten; + LPVOID lpContext; + DWORD cbPathLen; + DWORD StreamSize; + WCHAR wbuf[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, 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; + + res = 0; + 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 ()) + { + res = 0; + goto done; + } + + DWORD attrib = 0; + if (win32_path.isdir ()) + attrib |= S_IFDIR; + res = get_file_attribute (win32_path.has_acls (), + win32_path.get_win32 (), + (int *) &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) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_FOLLOW, uid, gid); +} + +extern "C" int +chown (const char * name, __uid16_t uid, __gid16_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_FOLLOW, + uid16touid32 (uid), gid16togid32 (gid)); +} + +extern "C" int +lchown32 (const char * name, __uid32_t uid, __gid32_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid); +} + +extern "C" int +lchown (const char * name, __uid16_t uid, __gid16_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_NOFOLLOW, + uid16touid32 (uid), gid16togid32 (gid)); +} + +extern "C" int +fchown32 (int fd, __uid32_t uid, __gid32_t gid) +{ + sigframe thisframe (mainthread); + 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; + sigframe thisframe (mainthread); + + 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 (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) +{ + sigframe thisframe (mainthread); + 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 = 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; + sigframe thisframe (mainthread); + + cygheap_fdget cfd (fd); + if (cfd < 0) + res = -1; + else + { + path_conv pc (cfd->get_win32_name (), PC_SYM_NOFOLLOW); + memset (buf, 0, sizeof (struct __stat64)); + res = cfd->fstat (buf, &pc); + 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 +_fstat (int fd, struct __stat32 *buf) +{ + struct __stat64 buf64; + int ret = fstat64 (fd, &buf64); + if (!ret) + stat64_to_stat32 (&buf64, buf); + return ret; +} + +/* fsync: P96 6.6.1.1 */ +extern "C" int +fsync (int fd) +{ + sigframe thisframe (mainthread); + 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 */ +int __stdcall +stat_worker (const char *name, struct __stat64 *buf, int nofollow, + path_conv *pc) +{ + int res = -1; + path_conv real_path; + fhandler_base *fh = NULL; + + if (check_null_invalid_struct_errno (buf)) + goto done; + + if (!pc) + pc = &real_path; + + fh = cygheap->fdtab.build_fhandler_from_name (-1, name, NULL, *pc, + (nofollow ? PC_SYM_NOFOLLOW + : PC_SYM_FOLLOW) + | PC_FULL, stat_suffixes); + + if (pc->error) + { + debug_printf ("got %d error from build_fhandler_from_name", pc->error); + set_errno (pc->error); + } + else + { + debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow, + pc, (DWORD) real_path); + memset (buf, 0, sizeof (*buf)); + res = fh->fstat (buf, pc); + 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) +{ + sigframe thisframe (mainthread); + syscall_printf ("entering"); + return stat_worker (name, buf, 0); +} + +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; +} + +/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ +extern "C" int +lstat64 (const char *name, struct __stat64 *buf) +{ + sigframe thisframe (mainthread); + syscall_printf ("entering"); + return stat_worker (name, buf, 1); +} + +/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ +extern "C" int +cygwin_lstat (const char *name, struct __stat32 *buf) +{ + struct __stat64 buf64; + int ret = lstat64 (name, &buf64); + if (!ret) + stat64_to_stat32 (&buf64, buf); + return ret; +} + +extern int acl_access (const char *, int); + +extern "C" int +access (const char *fn, int flags) +{ + sigframe thisframe (mainthread); + // flags were incorrectly specified + if (flags & ~(F_OK|R_OK|W_OK|X_OK)) + { + set_errno (EINVAL); + return -1; + } + + if (allow_ntsec) + return acl_access (fn, flags); + + struct __stat64 st; + int r = stat_worker (fn, &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 +rename (const char *oldpath, const char *newpath) +{ + sigframe thisframe (mainthread); + 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[MAX_PATH + 5]; + if (real_old.issymlink () && !real_new.error && !real_new.case_clash) + { + int len_old = strlen (real_old.get_win32 ()); + if (strcasematch (real_old.get_win32 () + len_old - 4, ".lnk")) + { + 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.issymlink () && + real_new.known_suffix && strcasematch (real_new.known_suffix, ".lnk") && + (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; + + sigframe thisframe (mainthread); + int res; + const char* command[4]; + _sig_func_ptr oldint, oldquit; + sigset_t child_block, old_mask; + + if (cmdstring == (const char *) NULL) + return 1; + + oldint = signal (SIGINT, SIG_IGN); + oldquit = signal (SIGQUIT, SIG_IGN); + sigemptyset (&child_block); + sigaddset (&child_block, SIGCHLD); + (void) sigprocmask (SIG_BLOCK, &child_block, &old_mask); + + command[0] = "sh"; + command[1] = "-c"; + command[2] = cmdstring; + command[3] = (const char *) NULL; + + if ((res = spawnvp (_P_WAIT, "sh", command)) == -1) + { + // when exec fails, return value should be as if shell + // executed exit (127) + res = 127; + } + + signal (SIGINT, oldint); + signal (SIGQUIT, oldquit); + (void) sigprocmask (SIG_SETMASK, &old_mask, 0); + 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; +} + +static int +check_posix_perm (const char *fname, int v) +{ + extern int allow_ntea, allow_ntsec, allow_smbntsec; + + /* 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) +{ + cygheap_fdget cfd (fd); + if (cfd < 0 || !cfd->is_tty ()) + return 0; + return (char *) (cfd->ttyname ()); +} + +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) +{ + sigframe thisframe (mainthread); + 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, __off32_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) +{ + sigframe thisframe (mainthread); + 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, __off32_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) +{ + sigframe thisframe (mainthread); + if (!sfs) + { + set_errno (EFAULT); + return -1; + } + + path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL); + char *root = rootdir (full_path); + + syscall_printf ("statfs %s", root); + + DWORD spc, bps, freec, totalc; + + if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc)) + { + __seterrno (); + return -1; + } + + 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_bfree = sfs->f_bavail = 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) +{ + sigframe thisframe (mainthread); + 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) +{ + sigframe thisframe (mainthread); + 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); + 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) +{ + sigframe thisframe (mainthread); + if (pid == 0) + pid = getpid (); + + pinfo p (pid); + if (p == 0) + { + set_errno (ESRCH); + return -1; + } + return p->pgid; +} + +extern "C" int +setpgrp (void) +{ + sigframe thisframe (mainthread); + return setpgid (0, 0); +} + +extern "C" pid_t +getpgrp (void) +{ + sigframe thisframe (mainthread); + return getpgid (0); +} + +extern "C" char * +ptsname (int fd) +{ + sigframe thisframe (mainthread); + 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") + MAX_PATH]; + sprintf (buf, ":%x:%x:%x", major, minor, + type | (mode & (S_IRWXU | S_IRWXG | S_IRWXO))); + return symlink_worker (buf, path, true, true); +} + +/* mknod was the call to create directories before the introduction + of mkdir in 4.2BSD and SVR3. Use of mknod required superuser privs + so the mkdir command had to be setuid root. + Although mknod hasn't been implemented yet, some GNU tools (e.g. the + fileutils) assume its existence so we must provide a stub that always + fails. */ +extern "C" int +mknod (const char *path, mode_t mode, dev_t dev) +{ + if (check_null_empty_str_errno (path)) + return -1; + + if (strlen (path) >= 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 = dev >> 8 /* SIGH. _major (dev) */; + _minor_t minor = dev & 0xff /* SIGH _minor (dev) */; + switch (type) + { + case S_IFCHR: + case S_IFBLK: + break; + + case S_IFIFO: + major = _major (FH_FIFO); + minor = _minor (FH_FIFO) & 0xff; /* SIGH again */ + 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 +mkfifo (const char *_path, mode_t mode) +{ + return -1; +} + +/* seteuid: standards? */ +extern "C" int +seteuid32 (__uid32_t uid) +{ + debug_printf ("uid: %d myself->gid: %d", uid, myself->gid); + + if (!wincap.has_security () + || (uid == myself->uid && !cygheap->user.groups.ischanged)) + { + debug_printf ("Nothing happens"); + return 0; + } + + if (uid == ILLEGAL_UID) + { + set_errno (EINVAL); + return -1; + } + + sigframe thisframe (mainthread); + cygsid usersid; + user_groups &groups = cygheap->user.groups; + HANDLE ptok, sav_token; + BOOL sav_impersonated, sav_token_is_internal_token; + BOOL process_ok, explicitly_created_token = FALSE; + struct passwd * pw_new; + PSID origpsid, psid2 = NO_SID; + + pw_new = internal_getpwuid (uid); + if (!usersid.getfrompw (pw_new)) + { + set_errno (EINVAL); + return -1; + } + /* Save current information */ + sav_token = cygheap->user.token; + sav_impersonated = cygheap->user.impersonated; + + RevertToSelf (); + if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok)) + { + __seterrno (); + goto failed; + } + /* Verify if the process token is suitable. + Currently we do not try to differentiate between + internal tokens and others */ + process_ok = verify_token (ptok, usersid, groups); + debug_printf ("Process token %sverified", process_ok ? "" : "not "); + if (process_ok) + { + if (cygheap->user.issetuid ()) + cygheap->user.impersonated = FALSE; + else + { + CloseHandle (ptok); + goto success; /* No change */ + } + } + + if (!process_ok && cygheap->user.token != INVALID_HANDLE_VALUE) + { + /* Verify if the current tokem is suitable */ + BOOL token_ok = verify_token (cygheap->user.token, usersid, groups, + &sav_token_is_internal_token); + debug_printf ("Thread token %d %sverified", + cygheap->user.token, token_ok?"":"not "); + if (!token_ok) + cygheap->user.token = INVALID_HANDLE_VALUE; + else + { + /* Return if current token is valid */ + if (cygheap->user.impersonated) + { + CloseHandle (ptok); + if (!ImpersonateLoggedOnUser (cygheap->user.token)) + system_printf ("Impersonating in seteuid failed: %E"); + goto success; /* No change */ + } + } + } + + /* Set process def dacl to allow access to impersonated token */ + char dacl_buf[MAX_DACL_LEN (5)]; + if (usersid != (origpsid = cygheap->user.orig_sid ())) psid2 = usersid; + if (sec_acl ((PACL) dacl_buf, FALSE, origpsid, psid2)) + { + TOKEN_DEFAULT_DACL tdacl; + tdacl.DefaultDacl = (PACL) dacl_buf; + if (!SetTokenInformation (ptok, TokenDefaultDacl, + &tdacl, sizeof dacl_buf)) + debug_printf ("SetTokenInformation" + "(TokenDefaultDacl): %E"); + } + CloseHandle (ptok); + + if (!process_ok && cygheap->user.token == INVALID_HANDLE_VALUE) + { + /* If no impersonation token is available, try to + authenticate using NtCreateToken () or subauthentication. */ + cygheap->user.token = create_token (usersid, groups, pw_new); + if (cygheap->user.token != INVALID_HANDLE_VALUE) + explicitly_created_token = TRUE; + else + { + /* create_token failed. Try subauthentication. */ + debug_printf ("create token failed, try subauthentication."); + cygheap->user.token = subauth (pw_new); + if (cygheap->user.token == INVALID_HANDLE_VALUE) + goto failed; + } + } + + /* If using the token, set info and impersonate */ + if (!process_ok) + { + /* If the token was explicitly created, all information has + already been set correctly. */ + if (!explicitly_created_token) + { + /* Try setting owner to same value as user. */ + if (!SetTokenInformation (cygheap->user.token, TokenOwner, + &usersid, sizeof usersid)) + debug_printf ("SetTokenInformation(user.token, " + "TokenOwner): %E"); + /* Try setting primary group in token to current group */ + if (!SetTokenInformation (cygheap->user.token, + TokenPrimaryGroup, + &groups.pgsid, sizeof (cygsid))) + debug_printf ("SetTokenInformation(user.token, " + "TokenPrimaryGroup): %E"); + } + /* Now try to impersonate. */ + if (!ImpersonateLoggedOnUser (cygheap->user.token)) + { + debug_printf ("ImpersonateLoggedOnUser %E"); + __seterrno (); + goto failed; + } + cygheap->user.impersonated = TRUE; + } + + /* If sav_token was internally created and is replaced, destroy it. */ + if (sav_token != INVALID_HANDLE_VALUE && + sav_token != cygheap->user.token && + sav_token_is_internal_token) + CloseHandle (sav_token); + cygheap->user.set_name (pw_new->pw_name); + cygheap->user.set_sid (usersid); +success: + myself->uid = uid; + groups.ischanged = FALSE; + return 0; + + failed: + cygheap->user.token = sav_token; + cygheap->user.impersonated = sav_impersonated; + if (cygheap->user.issetuid () + && !ImpersonateLoggedOnUser (cygheap->user.token)) + system_printf ("Impersonating in seteuid failed: %E"); + 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)); +} + +/* setegid: from System V. */ +extern "C" int +setegid32 (__gid32_t gid) +{ + if (!wincap.has_security () || gid == myself->gid) + return 0; + + if (gid == ILLEGAL_GID) + { + set_errno (EINVAL); + return -1; + } + + sigframe thisframe (mainthread); + user_groups * groups = &cygheap->user.groups; + cygsid gsid; + HANDLE ptok; + + struct __group32 * gr = internal_getgrgid (gid); + if (!gr || gr->gr_gid != gid || !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; +} + +/* chroot: privileged Unix system call. */ +/* FIXME: Not privileged here. How should this be done? */ +extern "C" int +chroot (const char *newroot) +{ + sigframe thisframe (mainthread); + 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"); + return ret; +} + +extern "C" int +creat (const char *path, mode_t mode) +{ + sigframe thisframe (mainthread); + return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode); +} + +extern "C" void +__assertfail () +{ + exit (99); +} + +extern "C" int +getw (FILE *fp) +{ + sigframe thisframe (mainthread); + int w, ret; + ret = fread (&w, sizeof (int), 1, fp); + return ret != 1 ? EOF : w; +} + +extern "C" int +putw (int w, FILE *fp) +{ + sigframe thisframe (mainthread); + 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) +{ + sigframe thisframe (mainthread); + 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; +} + +extern "C" void +login (struct utmp *ut) +{ + sigframe thisframe (mainthread); + register int fd; + + pututline (ut); + if ((fd = open (_PATH_WTMP, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0) + { + (void) write (fd, (char *) ut, sizeof (struct utmp)); + (void) close (fd); + } +} + +/* It isn't possible to use unix-style I/O function in logout code because +cygwin's I/O subsystem may be inaccessible at logout () call time. +FIXME (cgf): huh? +*/ +extern "C" int +logout (char *line) +{ + sigframe thisframe (mainthread); + int res = 0; + HANDLE ut_fd; + static const char path_utmp[] = _PATH_UTMP; + + path_conv win32_path (path_utmp); + if (win32_path.error) + return 0; + + ut_fd = CreateFile (win32_path.get_win32 (), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sec_none_nih, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (ut_fd != INVALID_HANDLE_VALUE) + { + struct utmp *ut; + struct utmp ut_buf[100]; + /* FIXME: utmp file access is not 64 bit clean for now. */ + __off32_t pos = 0; /* Position in file */ + DWORD rd; + + while (!res && ReadFile (ut_fd, ut_buf, sizeof ut_buf, &rd, NULL) + && rd != 0) + { + struct utmp *ut_end = (struct utmp *) ((char *) ut_buf + rd); + + for (ut = ut_buf; ut < ut_end; ut++, pos += sizeof (*ut)) + if (ut->ut_name[0] + && strncmp (ut->ut_line, line, sizeof (ut->ut_line)) == 0) + /* Found the entry for LINE; mark it as logged out. */ + { + /* Zero out entries describing who's logged in. */ + bzero (ut->ut_name, sizeof (ut->ut_name)); + bzero (ut->ut_host, sizeof (ut->ut_host)); + time (&ut->ut_time); + + /* Now seek back to the position in utmp at which UT occured, + and write the new version of UT there. */ + if ((SetFilePointer (ut_fd, pos, 0, FILE_BEGIN) != 0xFFFFFFFF) + && (WriteFile (ut_fd, (char *) ut, sizeof (*ut), + &rd, NULL))) + { + res = 1; + break; + } + } + } + + CloseHandle (ut_fd); + } + + return res; +} + +static int utmp_fd = -2; +static char *utmp_file = (char *) _PATH_UTMP; + +static struct utmp utmp_data; + +extern "C" void +setutent () +{ + sigframe thisframe (mainthread); + if (utmp_fd == -2) + { + utmp_fd = open (utmp_file, O_RDWR); + } + lseek (utmp_fd, 0, SEEK_SET); +} + +extern "C" void +endutent () +{ + sigframe thisframe (mainthread); + if (utmp_fd != -2) + { + close (utmp_fd); + utmp_fd = -2; + } +} + +extern "C" void +utmpname (_CONST char *file) +{ + sigframe thisframe (mainthread); + if (check_null_empty_str (file)) + { + debug_printf ("Invalid file"); + return; + } + endutent (); + utmp_file = strdup (file); + debug_printf ("New UTMP file: %s", utmp_file); +} + +extern "C" struct utmp * +getutent () +{ + sigframe thisframe (mainthread); + if (utmp_fd == -2) + setutent (); + if (read (utmp_fd, &utmp_data, sizeof (utmp_data)) != sizeof (utmp_data)) + return NULL; + return &utmp_data; +} + +extern "C" struct utmp * +getutid (struct utmp *id) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct_errno (id)) + return NULL; + while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data)) + { + switch (id->ut_type) + { + case RUN_LVL: + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + if (id->ut_type == utmp_data.ut_type) + return &utmp_data; + break; + case INIT_PROCESS: + case LOGIN_PROCESS: + case USER_PROCESS: + case DEAD_PROCESS: + if (strncmp (id->ut_id, utmp_data.ut_id, UT_IDLEN) == 0) + return &utmp_data; + break; + default: + return NULL; + } + } + return NULL; +} + +extern "C" struct utmp * +getutline (struct utmp *line) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct_errno (line)) + return NULL; + while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data)) + { + if ((utmp_data.ut_type == LOGIN_PROCESS || + utmp_data.ut_type == USER_PROCESS) && + !strncmp (utmp_data.ut_line, line->ut_line, + sizeof (utmp_data.ut_line))) + return &utmp_data; + } + return NULL; +} + +extern "C" void +pututline (struct utmp *ut) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct (ut)) + return; + setutent (); + struct utmp *u; + if ((u = getutid (ut))) + lseek (utmp_fd, -sizeof(struct utmp), SEEK_CUR); + else + lseek (utmp_fd, 0, SEEK_END); + (void) write (utmp_fd, (char *) ut, sizeof (struct utmp)); +} diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc new file mode 100644 index 00000000000..aaabdd509c0 --- /dev/null +++ b/winsup/cygwin/tty.cc @@ -0,0 +1,464 @@ +/* tty.cc + + Copyright 1997, 1998, 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. */ + +#include "winsup.h" +#include <errno.h> +#include <unistd.h> +#include <utmp.h> +#include <wingdi.h> +#include <winuser.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "pinfo.h" +#include "cygwin/cygserver.h" +#include "shared_info.h" +#include "cygthread.h" + +extern fhandler_tty_master *tty_master; + +extern "C" +int +grantpt (int fd) +{ + return 0; +} + +extern "C" +int +unlockpt (int fd) +{ + return 0; +} + +extern "C" +int +ttyslot (void) +{ + if (NOTSTATE (myself, PID_USETTY)) + return -1; + return myself->ctty; +} + +void __stdcall +tty_init (void) +{ + if (!myself->ppid_handle && NOTSTATE (myself, PID_CYGPARENT)) + cygheap->fdtab.get_debugger_info (); + + if (NOTSTATE (myself, PID_USETTY)) + return; + if (myself->ctty == -1) + if (NOTSTATE (myself, PID_CYGPARENT)) + myself->ctty = attach_tty (myself->ctty); + else + return; + if (myself->ctty == -1) + termios_printf ("Can't attach to tty"); +} + +/* Create session's master tty */ + +void __stdcall +create_tty_master (int ttynum) +{ + device ttym = *ttym_dev; + ttym.setunit (ttynum); /* CGF FIXME device */ + tty_master = (fhandler_tty_master *) + cygheap->fdtab.build_fhandler (-1, ttym, "/dev/ttym", NULL); + if (tty_master->init ()) + api_fatal ("Can't create master tty"); + else + { + /* Log utmp entry */ + struct utmp our_utmp; + + bzero ((char *) &our_utmp, sizeof (utmp)); + (void) time (&our_utmp.ut_time); + strncpy (our_utmp.ut_name, getlogin (), sizeof (our_utmp.ut_name)); + cygwin_gethostname (our_utmp.ut_host, sizeof (our_utmp.ut_host)); + __small_sprintf (our_utmp.ut_line, "tty%d", ttynum); + our_utmp.ut_type = USER_PROCESS; + our_utmp.ut_pid = myself->pid; + myself->ctty = ttynum; + login (&our_utmp); + } +} + +void __stdcall +tty_terminate (void) +{ + if (NOTSTATE (myself, PID_USETTY)) + return; + cygwin_shared->tty.terminate (); +} + +int __stdcall +attach_tty (int num) +{ + if (num != -1) + { + return cygwin_shared->tty.connect_tty (num); + } + if (NOTSTATE (myself, PID_USETTY)) + return -1; + return cygwin_shared->tty.allocate_tty (1); +} + +void +tty_list::terminate (void) +{ + int ttynum = myself->ctty; + + /* Keep master running till there are connected clients */ + if (ttynum != -1 && ttys[ttynum].master_pid == GetCurrentProcessId ()) + { + tty *t = ttys + ttynum; + CloseHandle (t->from_master); + CloseHandle (t->to_master); + /* Wait for children which rely on tty handling in this process to + go away */ + for (int i = 0; ; i++) + { + if (!t->slave_alive ()) + break; + if (i >= 100) + { + small_printf ("waiting for children using tty%d to terminate\n", + ttynum); + i = 0; + } + + low_priority_sleep (200); + } + + termios_printf ("tty %d master about to finish", ttynum); + ForceCloseHandle1 (t->to_slave, to_pty); + ForceCloseHandle1 (t->from_slave, from_pty); + CloseHandle (tty_master->inuse); + t->init (); + + char buf[20]; + __small_sprintf (buf, "tty%d", ttynum); + logout (buf); + } +} + +int +tty_list::connect_tty (int ttynum) +{ + if (ttynum < 0 || ttynum >= NTTYS) + { + termios_printf ("ttynum (%d) out of range", ttynum); + return -1; + } + if (!ttys[ttynum].exists ()) + { + termios_printf ("tty %d was not allocated", ttynum); + return -1; + } + + return ttynum; +} + +void +tty_list::init (void) +{ + for (int i = 0; i < NTTYS; i++) + { + ttys[i].init (); + ttys[i].setntty (i); + } +} + +/* Search for tty class for our console. Allocate new tty if our process is + the only cygwin process in the current console. + Return tty number or -1 if error. + If flag == 0, just find a free tty. + */ +int +tty_list::allocate_tty (int with_console) +{ + HWND console; + + /* FIXME: This whole function needs a protective mutex. */ + + if (!with_console) + console = NULL; + else if (!(console = GetConsoleWindow ())) + { + char *oldtitle = new char [TITLESIZE]; + + if (!oldtitle) + { + termios_printf ("Can't *allocate console title buffer"); + return -1; + } + if (!GetConsoleTitle (oldtitle, TITLESIZE)) + { + termios_printf ("Can't read console title"); + return -1; + } + + if (WaitForSingleObject (title_mutex, INFINITE) == WAIT_FAILED) + termios_printf ("WFSO for title_mutext %p failed, %E", title_mutex); + + char buf[40]; + + __small_sprintf (buf, "cygwin.find.console.%d", myself->pid); + SetConsoleTitle (buf); + for (int times = 0; times < 25; times++) + { + Sleep (10); + if ((console = FindWindow (NULL, buf))) + break; + } + SetConsoleTitle (oldtitle); + Sleep (40); + ReleaseMutex (title_mutex); + if (console == NULL) + { + termios_printf ("Can't find console window"); + return -1; + } + } + /* Is a tty allocated for console? */ + + int freetty = -1; + for (int i = 0; i < NTTYS; i++) + { + if (!ttys[i].exists ()) + { + if (freetty < 0) /* Scanning? */ + freetty = i; /* Yes. */ + if (!with_console) /* Do we want to attach this to a console? */ + break; /* No. We've got one. */ + } + + if (with_console && ttys[i].gethwnd () == console) + { + termios_printf ("console %x already associated with tty%d", + console, i); + /* Is the master alive? */ + HANDLE hMaster; + hMaster = OpenProcess (PROCESS_DUP_HANDLE, FALSE, ttys[i].master_pid); + if (hMaster) + { + CloseHandle (hMaster); + return i; + } + /* Master is dead */ + freetty = i; + break; + } + } + + /* There is no tty allocated to console, allocate the first free found */ + if (freetty == -1) + { + system_printf ("No free ttys available"); + return -1; + } + tty *t = ttys + freetty; + t->init (); + t->setsid (-1); + t->setpgid (myself->pgid); + t->sethwnd (console); + + if (with_console) + { + termios_printf ("console %x associated with tty%d", console, freetty); + create_tty_master (freetty); + } + else + termios_printf ("tty%d allocated", freetty); + return freetty; +} + +BOOL +tty::slave_alive () +{ + return alive (TTY_SLAVE_ALIVE); +} + +BOOL +tty::master_alive () +{ + return alive (TTY_MASTER_ALIVE); +} + +BOOL +tty::alive (const char *fmt) +{ + HANDLE ev; + char buf[sizeof (TTY_MASTER_ALIVE) + 16]; + + __small_sprintf (buf, fmt, ntty); + if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf))) + CloseHandle (ev); + return ev != NULL; +} + +HANDLE +tty::create_inuse (const char *fmt) +{ + HANDLE h; + char buf[sizeof (TTY_MASTER_ALIVE) + 16]; + + __small_sprintf (buf, fmt, ntty); + h = CreateEvent (&sec_all, TRUE, FALSE, buf); + termios_printf ("%s = %p", buf, h); + if (!h) + termios_printf ("couldn't open inuse event, %E", buf); + return h; +} + +void +tty::init (void) +{ + output_stopped = 0; + setsid (0); + pgid = 0; + hwnd = NULL; + to_slave = NULL; + from_slave = NULL; + was_opened = 0; +} + +HANDLE +tty::get_event (const char *fmt, BOOL manual_reset) +{ + HANDLE hev; + char buf[40]; + + __small_sprintf (buf, fmt, ntty); + if (!(hev = CreateEvent (&sec_all, manual_reset, FALSE, buf))) + { + termios_printf ("couldn't create %s", buf); + set_errno (ENOENT); /* FIXME this can't be the right errno */ + return NULL; + } + + termios_printf ("created event %s", buf); + return hev; +} + +int +tty::make_pipes (fhandler_pty_master *ptym) +{ + /* Create communication pipes */ + + /* FIXME: should this be sec_none_nih? */ + if (CreatePipe (&from_master, &to_slave, &sec_all, 128 * 1024) == FALSE) + { + termios_printf ("can't create input pipe"); + set_errno (ENOENT); + return FALSE; + } + + // ProtectHandle1INH (to_slave, to_pty); + if (CreatePipe (&from_slave, &to_master, &sec_all, 128 * 1024) == FALSE) + { + termios_printf ("can't create output pipe"); + set_errno (ENOENT); + return FALSE; + } + // ProtectHandle1INH (from_slave, from_pty); + termios_printf ("tty%d from_slave %p, to_slave %p", ntty, from_slave, + to_slave); + + DWORD pipe_mode = PIPE_NOWAIT; + if (!SetNamedPipeHandleState (to_slave, &pipe_mode, NULL, NULL)) + termios_printf ("can't set to_slave to non-blocking mode"); + ptym->set_io_handle (from_slave); + ptym->set_output_handle (to_slave); + return TRUE; +} + +BOOL +tty::common_init (fhandler_pty_master *ptym) +{ + /* Set termios information. Force initialization. */ + ptym->tcinit (this, TRUE); + + if (!make_pipes (ptym)) + return FALSE; + ptym->need_nl = 0; + + /* Save our pid */ + + master_pid = GetCurrentProcessId (); + + /* Allow the others to open us (for handle duplication) */ + + /* FIXME: we shold NOT set the security wide open when the + daemon is running + */ + if (wincap.has_security ()) + { + if (cygserver_running == CYGSERVER_UNKNOWN) + cygserver_init (); + + if (cygserver_running != CYGSERVER_OK + && !SetKernelObjectSecurity (hMainProc, + DACL_SECURITY_INFORMATION, + get_null_sd ())) + system_printf ("Can't set process security, %E"); + } + + /* Create synchronisation events */ + + if (ptym->get_major () != DEV_TTYM_MAJOR) + { + ptym->output_done_event = ptym->ioctl_done_event = + ptym->ioctl_request_event = NULL; + } + else + { + if (!(ptym->output_done_event = get_event (OUTPUT_DONE_EVENT))) + return FALSE; + if (!(ptym->ioctl_done_event = get_event (IOCTL_DONE_EVENT))) + return FALSE; + if (!(ptym->ioctl_request_event = get_event (IOCTL_REQUEST_EVENT))) + return FALSE; + } + + if (!(ptym->input_available_event = get_event (INPUT_AVAILABLE_EVENT, TRUE))) + return FALSE; + + char buf[40]; + __small_sprintf (buf, OUTPUT_MUTEX, ntty); + if (!(ptym->output_mutex = CreateMutex (&sec_all, FALSE, buf))) + { + termios_printf ("can't create %s", buf); + set_errno (ENOENT); + return FALSE; + } + + __small_sprintf (buf, INPUT_MUTEX, ntty); + if (!(ptym->input_mutex = CreateMutex (&sec_all, FALSE, buf))) + { + termios_printf ("can't create %s", buf); + set_errno (ENOENT); + return FALSE; + } + + ProtectHandle1INH (ptym->output_mutex, output_mutex); + ProtectHandle1INH (ptym->input_mutex, input_mutex); + winsize.ws_col = 80; + winsize.ws_row = 25; + + termios_printf ("tty%d opened", ntty); + return TRUE; +} diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h new file mode 100644 index 00000000000..a2b54199fd7 --- /dev/null +++ b/winsup/cygwin/winsup.h @@ -0,0 +1,306 @@ +/* winsup.h: main Cygwin header file. + + 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. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define __INSIDE_CYGWIN__ + +#define strlen __builtin_strlen +#define strcmp __builtin_strcmp +#define strcpy __builtin_strcpy +#define memcpy __builtin_memcpy +#define memcmp __builtin_memcmp +#ifdef HAVE_BUILTIN_MEMSET +# define memset __builtin_memset +#endif + +#define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy"))) + +#ifdef __cplusplus + +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L +#define NEW_MACRO_VARARGS +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include <sys/types.h> +#include <sys/strace.h> + +extern const char case_folded_lower[]; +#define cyg_tolower(c) (case_folded_lower[(unsigned char)(c)]) +extern const char case_folded_upper[]; +#define cyg_toupper(c) (case_folded_upper[(unsigned char)(c)]) + +#ifndef MALLOC_DEBUG +#define cfree newlib_cfree_dont_use +#endif + +#define WIN32_LEAN_AND_MEAN 1 +#define _WINGDI_H +#define _WINUSER_H +#define _WINNLS_H +#define _WINVER_H +#define _WINNETWK_H +#define _WINSVC_H +#include <windows.h> +#include <wincrypt.h> +#include <lmcons.h> +#undef _WINGDI_H +#undef _WINUSER_H +#undef _WINNLS_H +#undef _WINVER_H +#undef _WINNETWK_H +#undef _WINSVC_H + +#include "wincap.h" + +/* The one function we use from winuser.h most of the time */ +extern "C" DWORD WINAPI GetLastError (void); + +enum codepage_type {ansi_cp, oem_cp}; +extern codepage_type current_codepage; + +UINT get_cp (); + +int __stdcall sys_wcstombs(char *, const WCHAR *, int) + __attribute__ ((regparm(3))); + +int __stdcall sys_mbstowcs(WCHAR *, const char *, int) + __attribute__ ((regparm(3))); + +/* Used to check if Cygwin DLL is dynamically loaded. */ +extern int dynamically_loaded; + +extern int cygserver_running; + +#define TITLESIZE 1024 + +/* status bit manipulation */ +#define __ISSETF(what, x, prefix) \ + ((what)->status & prefix##_##x) +#define __SETF(what, x, prefix) \ + ((what)->status |= prefix##_##x) +#define __CLEARF(what, x, prefix) \ + ((what)->status &= ~prefix##_##x) +#define __CONDSETF(n, what, x, prefix) \ + ((n) ? __SETF (what, x, prefix) : __CLEARF (what, x, prefix)) + +#include "debug.h" + +/* Events/mutexes */ +extern HANDLE title_mutex; + +/**************************** Convenience ******************************/ + +/* Used when treating / and \ as equivalent. */ +#define isdirsep(ch) \ + ({ \ + char __c = (ch); \ + ((__c) == '/' || (__c) == '\\'); \ + }) + +/* Convert a signal to a signal mask */ +#define SIGTOMASK(sig) (1<<((sig) - signal_shift_subtract)) +extern unsigned int signal_shift_subtract; + +#ifdef NEW_MACRO_VARARGS +# define api_fatal(...) __api_fatal ("%P: *** " __VA_ARGS__) +#else +# define api_fatal(fmt, args...) __api_fatal ("%P: *** " fmt,## args) +#endif + +#undef issep +#define issep(ch) (strchr (" \t\n\r", (ch)) != NULL) + +#define isabspath(p) \ + (isdirsep (*(p)) || (isalpha (*(p)) && (p)[1] == ':' && (!(p)[2] || isdirsep ((p)[2])))) + +/******************** Initialization/Termination **********************/ + +class per_process; +/* cygwin .dll initialization */ +void dll_crt0 (per_process *) __asm__ ("_dll_crt0__FP11per_process"); +extern "C" void __stdcall _dll_crt0 (); + +/* dynamically loaded dll initialization */ +extern "C" int dll_dllcrt0 (HMODULE, per_process *); + +/* dynamically loaded dll initialization for non-cygwin apps */ +extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *); + +/* exit the program */ +extern "C" void __stdcall do_exit (int) __attribute__ ((noreturn)); + +/* UID/GID */ +void uinfo_init (void); + +#define ILLEGAL_UID16 ((__uid16_t)-1) +#define ILLEGAL_UID ((__uid32_t)-1) +#define ILLEGAL_GID16 ((__gid16_t)-1) +#define ILLEGAL_GID ((__gid32_t)-1) +#define ILLEGAL_SEEK ((__off64_t)-1) + +#define uid16touid32(u16) ((u16)==ILLEGAL_UID16?ILLEGAL_UID:(__uid32_t)(u16)) +#define gid16togid32(g16) ((g16)==ILLEGAL_GID16?ILLEGAL_GID:(__gid32_t)(g16)) + +extern "C" __uid32_t getuid32 (void); +extern "C" __uid32_t geteuid32 (void); +extern "C" struct passwd *getpwuid32 (__uid32_t); + +/* various events */ +void events_init (void); +void events_terminate (void); + +void __stdcall close_all_files (void); +BOOL __stdcall check_pty_fds (void); + +/* Invisible window initialization/termination. */ +HWND __stdcall gethwnd (void); + +/* Globals that handle initialization of winsock in a child process. */ +extern HANDLE wsock32_handle; +extern HANDLE ws2_32_handle; + +/* Globals that handle initialization of netapi in a child process. */ +extern HANDLE netapi32_handle; + +/* debug_on_trap support. see exceptions.cc:try_to_debug() */ +extern "C" void error_start_init (const char*); +extern "C" int try_to_debug (bool waitloop = 1); + +void set_file_api_mode (codepage_type); + +extern int cygwin_finished_initializing; + +/**************************** Miscellaneous ******************************/ + +void __stdcall set_std_handle (int); +int __stdcall writable_directory (const char *file); +int __stdcall stat_dev (DWORD, int, unsigned long, struct __stat64 *); +extern BOOL allow_ntsec; + +unsigned long __stdcall hash_path_name (unsigned long hash, const char *name) __attribute__ ((regparm(2))); +void __stdcall nofinalslash (const char *src, char *dst) __attribute__ ((regparm(2))); +extern "C" char *__stdcall rootdir (char *full_path) __attribute__ ((regparm(1))); + +/* String manipulation */ +extern "C" char *__stdcall strccpy (char *s1, const char **s2, char c); +extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribute__ ((regparm(2))); +extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3))); +extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2))); + +/* Time related */ +void __stdcall totimeval (struct timeval *dst, FILETIME * src, int sub, int flag); +long __stdcall to_time_t (FILETIME * ptr); +void __stdcall to_timestruc_t (FILETIME * ptr, timestruc_t * out); +void __stdcall time_as_timestruc_t (timestruc_t * out); + +void __stdcall set_console_title (char *); +void early_stuff_init (); + +int __stdcall check_null_str (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_empty_str (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_empty_str_errno (const char *name) __attribute__ ((regparm(1))); +int __stdcall check_null_str_errno (const char *name) __attribute__ ((regparm(1))); +int __stdcall __check_null_invalid_struct (void *s, unsigned sz) __attribute__ ((regparm(2))); +int __stdcall __check_null_invalid_struct_errno (void *s, unsigned sz) __attribute__ ((regparm(2))); +int __stdcall __check_invalid_read_ptr_errno (const void *s, unsigned sz) __attribute__ ((regparm(2))); + +#define check_null_invalid_struct(s) \ + __check_null_invalid_struct ((s), sizeof (*(s))) +#define check_null_invalid_struct_errno(s) \ + __check_null_invalid_struct_errno ((s), sizeof (*(s))) + +struct iovec; +ssize_t check_iovec_for_read (const struct iovec *, int) __attribute__ ((regparm(2))); +ssize_t check_iovec_for_write (const struct iovec *, int) __attribute__ ((regparm(2))); + +#define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__) +void __set_winsock_errno (const char *fn, int ln) __attribute__ ((regparm(2))); + +extern bool wsock_started; + +/* Printf type functions */ +extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn)); +extern "C" int __small_sprintf (char *dst, const char *fmt, ...) /*__attribute__ ((regparm (2)))*/; +extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap) /*__attribute__ ((regparm (3)))*/; +extern void multiple_cygwin_problem (const char *, unsigned, unsigned); + +class path_conv; +int __stdcall stat_worker (const char *name, struct __stat64 *buf, int nofollow, + path_conv *pc = NULL) __attribute__ ((regparm (3))); + +int symlink_worker (const char *, const char *, bool, bool) + __attribute__ ((regparm (3))); + +int __stdcall low_priority_sleep (DWORD) __attribute__ ((regparm (1))); +#define SLEEP_0_STAY_LOW INFINITE + +/**************************** Exports ******************************/ + +extern "C" { +int cygwin_select (int , fd_set *, fd_set *, fd_set *, + struct timeval *to); +int cygwin_gethostname (char *__name, size_t __len); + +int kill_pgrp (pid_t, int); +int _kill (int, int); +int _raise (int sig); + +extern DWORD binmode; +extern char _data_start__, _data_end__, _bss_start__, _bss_end__; +extern void (*__CTOR_LIST__) (void); +extern void (*__DTOR_LIST__) (void); +extern SYSTEM_INFO system_info; +}; + +/*************************** Unsorted ******************************/ + +#define WM_ASYNCIO 0x8000 // WM_APP + +/* Note that MAX_PATH is defined in the windows headers */ +/* There is also PATH_MAX and MAXPATHLEN. + PATH_MAX is from Posix and does *not* include the trailing NUL. + MAXPATHLEN is from Unix. + + Thou shalt use MAX_PATH throughout. It avoids the NUL vs no-NUL + issue and is neither of the Unixy ones [so we can punt on which + one is the right one to use]. */ + +#define STD_RBITS (S_IRUSR | S_IRGRP | S_IROTH) +#define STD_WBITS (S_IWUSR) +#define STD_XBITS (S_IXUSR | S_IXGRP | S_IXOTH) +#define NO_W ~(S_IWUSR | S_IWGRP | S_IWOTH) +#define NO_R ~(S_IRUSR | S_IRGRP | S_IROTH) +#define NO_X ~(S_IXUSR | S_IXGRP | S_IXOTH) + +/* The title on program start. */ +extern char *old_title; +extern BOOL display_title; + +extern HANDLE hMainThread; +extern HANDLE hMainProc; + +extern bool cygwin_testing; +extern unsigned _cygwin_testing_magic; +extern HMODULE cygwin_hmodule; + +extern char almost_null[]; + +#define winsock2_active (wsadata.wVersion >= 512) +#define winsock_active (wsadata.wVersion < 512) +extern struct WSAData wsadata; + +#endif /* defined __cplusplus */ |