diff options
-rw-r--r-- | winsup/cygwin/Makefile.in | 451 | ||||
-rw-r--r-- | winsup/cygwin/dcrt0.cc | 1180 | ||||
-rw-r--r-- | winsup/cygwin/sec_acl.cc | 1042 | ||||
-rw-r--r-- | winsup/cygwin/sigproc.cc | 1276 | ||||
-rw-r--r-- | winsup/cygwin/wincap.h | 121 |
5 files changed, 4070 insertions, 0 deletions
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in new file mode 100644 index 00000000000..5cbe318a1b2 --- /dev/null +++ b/winsup/cygwin/Makefile.in @@ -0,0 +1,451 @@ +# Makefile.in for Cygwin. +# Copyright 1995, 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 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@ +mandir:=@mandir@ +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@ + +override INSTALL:=@INSTALL@ +override INSTALL_PROGRAM:=@INSTALL_PROGRAM@ +override INSTALL_DATA:=@INSTALL_DATA@ + +# +# --enable options from configure +# +MT_SAFE:=@MT_SAFE@ +DEFS:=@DEFS@ + +CC:=@CC@ +# FIXME: Which is it, CC or CC_FOR_TARGET? +CC_FOR_TARGET:=$(CC) +CFLAGS=@CFLAGS@ +override CFLAGS+=-MMD ${$(*F)_CFLAGS} -fmerge-constants -ftracer +CXX=@CXX@ +CXXFLAGS=@CXXFLAGS@ + +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 + +@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 cygwin0.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 +TEST_DLL_NAME:=cygwin0.dll +TEST_LIB_NAME:=libcygwin0.a +DEF_FILE:=cygwin.def +DLL_ENTRY:=@DLL_ENTRY@ + +LIBGMON_A:=libgmon.a +CYGWIN_START:=crt0.o +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 $(w32api_lib)/libadvapi32.a + +MT_SAFE_OBJECTS:= +# Please maintain this list in sorted order, with maximum files per 80 col line +# +DLL_OFILES:=assert.o autoload.o bsdlib.o cxx.o cygheap.o cygthread.o cygtls.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_nodevice.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 flock.o fnmatch.o fork.o getopt.o glob.o grp.o heap.o \ + init.o ioctl.o ipc.o iruserok.o localtime.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 sigfe.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 \ + open acl aclcheck aclfrommode aclfrompbits \ + aclfromtext aclsort acltomode acltopbits \ + acltotext chown facl fchown fcntl fdopen fgetpos fopen \ + freopen fseeko fsetpos fstat ftello ftruncate \ + getegid geteuid getgid getgrent getgrgid getgrnam \ + getgroups getpwuid getpwuid_r getuid initgroups \ + lchown lseek lstat mknod mmap seekdir setegid seteuid \ + setgid setgroups setregid setreuid setuid stat \ + telldir tmpfile truncate + +NEW_FUNCTIONS:=regcomp posix_regcomp \ + regerror posix_regerror \ + regexec posix_regexec \ + regfree posix_regfree \ + open _open64 \ + acl _acl32 \ + aclcheck _aclcheck32 \ + aclfrommode _aclfrommode32 \ + aclfrompbits _aclfrompbits32 \ + aclfromtext _aclfromtext32 \ + aclsort _aclsort32 \ + acltomode _acltomode32 \ + acltopbits _acltopbits32 \ + acltotext _acltotext32 \ + chown _chown32 \ + facl _facl32 \ + fchown _fchown32 \ + fcntl _fcntl64 \ + fdopen _fdopen64 \ + fgetpos _fgetpos64 \ + fopen _fopen64 \ + freopen _freopen64 \ + fseeko _fseeko64 \ + fsetpos _fsetpos64 \ + fstat _fstat64 \ + ftello _ftello64 \ + ftruncate _ftruncate64 \ + getegid _getegid32 \ + geteuid _geteuid32 \ + getgid _getgid32 \ + getgrent _getgrent32 \ + getgrgid _getgrgid32 \ + getgrnam _getgrnam32 \ + getgroups _getgroups32 \ + getpwuid _getpwuid32 \ + getpwuid_r _getpwuid_r32 \ + getuid _getuid32 \ + initgroups _initgroups32 \ + lchown _lchown32 \ + lseek _lseek64 \ + lstat _lstat64 \ + mknod _mknod32 \ + mmap _mmap64 \ + seekdir _seekdir64 \ + setegid _setegid32 \ + seteuid _seteuid32 \ + setgid _setgid32 \ + setgroups _setgroups32 \ + setregid _setregid32 \ + setreuid _setreuid32 \ + setuid _setuid32 \ + stat _stat64 \ + telldir _telldir64 \ + tmpfile _tmpfile64 \ + truncate _truncate64 + +API_VER:=$(srcdir)/include/cygwin/version.h + +PWD:=${shell pwd} +LIB_NAME:=$(PWD)/libcygwin.a +LIBSERVER:=@LIBSERVER@ +SUBLIBS:=$(PWD)/libpthread.a $(PWD)/libm.a $(PWD)/libc.a +EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a +INSTOBJS:=automode.o binmode.o textmode.o +TARGET_LIBS:=$(LIB_NAME) $(CYGWIN_START) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS) + +ifneq "${filter -O%,$(CFLAGS)}" "" +cygheap_CFLAGS:=-fomit-frame-pointer +cygthread_CFLAGS:=-fomit-frame-pointer +cygtls_CFLAGS:=-fomit-frame-pointer +devices_CFLAGS:=-fomit-frame-pointer -Os +dir_CFLAGS:=-fomit-frame-pointer +fcntl_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_syslog_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 +malloc_CFLAGS:=-fomit-frame-pointer +malloc_wrapper_CFLAGS:=-fomit-frame-pointer +miscfuncs_CFLAGS:=-fomit-frame-pointer +regcomp_CFLAGS=-fomit-frame-pointer +regerror_CFLAGS=-fomit-frame-pointer +regexec_CFLAGS=-fomit-frame-pointer +regfree_CFLAGS=-fomit-frame-pointer +shared_CFLAGS:=-fomit-frame-pointer +smallprint_CFLAGS:=-fomit-frame-pointer +sysconf_CFLAGS:=-fomit-frame-pointer +uinfo_CFLAGS:=-fomit-frame-pointer +endif + +.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 .s + +all_host=@all_host@ +install_host=@install_host@ + +all: all_target $(all_host) + +all_target: $(TARGET_LIBS) + +all_host: $(TEST_LIB_NAME) + +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_PROGRAM) $(TEST_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 $(mandir)/man2/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.3'`; do \ + $(INSTALL_DATA) $$i $(mandir)/man3/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.5'`; do \ + $(INSTALL_DATA) $$i $(mandir)/man5/`basename $$i` ; \ + done; \ + for i in `find . -type f -name '*.7'`; do \ + $(INSTALL_DATA) $$i $(mandir)/man7/`basename $$i` ; \ + done + +install_target: + +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 +$(TEST_DLL_NAME): $(LDSCRIPT) $(DLL_OFILES) $(DLL_IMPORTS) $(LIBSERVER) $(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) $(LIBSERVER) $(LIBM) $(LIBC) \ + -lgcc $(DLL_IMPORTS) + @ln -f $@ new-$(DLL_NAME) + +# Rule to build libcygwin.a +$(LIB_NAME): rmsym newsym $(TEST_DLL_NAME) $(LIBCOS) + /bin/sh ${word 1,$^} ./cygdll.a "$(NM)" "$(AR)" $(OBSOLETE_FUNCTIONS) || exit 0 + /bin/sh ${word 2,$^} ./cygdll.a "$(AS)" "$(AR)" $(NEW_FUNCTIONS) || exit 0 + (echo create $(LIB_NAME); echo addmod $(LIBCOS); echo addlib cygdll.a; echo save) | $(AR) -M + $(RANLIB) $@ + +# 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) +$(TEST_LIB_NAME): $(LIB_NAME) + perl -p -e 'BEGIN{binmode(STDIN); binmode(STDOUT);}; s/cygwin1/cygwin0/g' < $? > $@ + +$(LIBSERVER): $(bupdir)/cygserver/Makefile + $(MAKE) -C $(bupdir)/cygserver libcygserver.a + +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,$^} USER_MAGIC 'class user_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 + +$(srcdir)/qevices.cc: cygwin-gperf devices.gperf devices.h + $^ > $@ + +$(srcdir)/devices.cc: gendevices devices.in devices.h + ${wordlist 1,2,$^} $@ + +$(PWD)/libpthread.a: speclib $(LIB_NAME) pthread.o thread.o + /bin/sh ${word 1, $^} $@ "${NM}" "$(AR)" ${wordlist 2, 99, $^} + +$(PWD)/libm.a: speclib $(LIB_NAME) $(LIBM) + /bin/sh ${word 1, $^} $@ "${NM}" "$(AR)" ${wordlist 2, 99, $^} + +$(PWD)/libc.a: speclib $(LIB_NAME) $(PWD)/libm.a libpthread.a + /bin/sh ${word 1, $^} -v $@ "${NM}" "$(AR)" ${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 $@ + +-lgcc: + : + +# + +Makefile: cygwin.din + +$(DEF_FILE): gendef cygwin.din $(srcdir)/tlsoffsets.h + $^ $@ sigfe.s + +$(srcdir)/tlsoffsets.h: gentls_offsets cygtls.h + $^ $@ $(COMPILE_CXX) + +sigfe.s: $(DEF_FILE) + @touch $@ + +sigfe.o: sigfe.s + $(CC) -c -o $@ $? + +winsup.h: config.h + +deps:=${wildcard *.d} +ifneq (,$(deps)) +include $(deps) +endif diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc new file mode 100644 index 00000000000..71ae1847c01 --- /dev/null +++ b/winsup/cygwin/dcrt0.cc @@ -0,0 +1,1180 @@ +/* dcrt0.cc -- essentially the main() for the Cygwin dll + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include "glob.h" +#include "exceptions.h" +#include <ctype.h> +#include <limits.h> +#include <wingdi.h> +#include <winuser.h> +#include "sigproc.h" +#include "pinfo.h" +#include "cygerrno.h" +#define NEED_VFORK +#include "perprocess.h" +#include "security.h" +#include "path.h" +#include "fhandler.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" +#include "sync.h" +#include "heap.h" + +#define MAX_AT_FILE_LEVEL 10 + +#define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0])) + +HANDLE NO_COPY hMainProc = (HANDLE) -1; +HANDLE NO_COPY hMainThread; + +per_thread_waitq NO_COPY waitq_storage; +per_thread_vfork NO_COPY vfork_storage; + +per_thread NO_COPY *threadstuff[] = {&waitq_storage, + &vfork_storage, + NULL}; + +bool display_title; +bool strip_title_path; +bool allow_glob = true; +codepage_type current_codepage = ansi_cp; + +int __argc_safe; +int _declspec(dllexport) __argc; +char _declspec(dllexport) **__argv; +vfork_save NO_COPY *main_vfork; + +static int NO_COPY envc; +char NO_COPY **envp; + +extern "C" void __sinit (_reent *); + +_threadinfo NO_COPY *_main_tls; + +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; + +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; + static struct _reent 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 && user_data->forkee) + return; // inherit constructed stuff from parent pid + + /* 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) (); +} + +/* + * 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 bool __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 = strechr (cmd, quote))) + strcpy (p, p + 1); + 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) + ci->stacksize = 0; + else + alloc_stack_hard_way (ci, b + sizeof (b) - 1); + + return; +} + +#ifdef DEBUGGING +void +break_here () +{ + debug_printf ("break here"); +} +#endif + +static void +initial_env (bool first) +{ + char buf[CYG_MAX_PATH + 1]; + if (!first) + /* nothing */; + else if (GetEnvironmentVariable ("CYGWIN_TESTING", buf, sizeof (buf) - 1)) + _cygwin_testing = 1; +#ifdef DEBUGGING + DWORD len; + static bool NO_COPY did_debugging_stuff; + if (did_debugging_stuff || (first && wincap.cant_debug_dll_entry ())) + return; + + did_debugging_stuff = true; + if (GetEnvironmentVariable ("CYGWIN_SLEEP", buf, sizeof (buf) - 1)) + { + DWORD ms = atoi (buf); + buf[0] = '\0'; + len = GetModuleFileName (NULL, buf, CYG_MAX_PATH); + console_printf ("Sleeping %d, pid %u %s\n", ms, GetCurrentProcessId (), buf); + while (ms--) + Sleep (1); + } + if (GetEnvironmentVariable ("CYGWIN_DEBUG", buf, sizeof (buf) - 1)) + { + char buf1[CYG_MAX_PATH + 1]; + len = GetModuleFileName (NULL, buf1, CYG_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 +} + +void __stdcall +dll_crt0_0 () +{ + wincap.init (); + initial_env (true); + + char zeros[sizeof (child_proc_info->zero)] = {0}; + + init_console_handler (); + init_global_security (); + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (), + GetCurrentProcess (), &hMainProc, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + hMainProc = GetCurrentProcess (); + + DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc, + &hMainThread, 0, false, DUPLICATE_SAME_ACCESS); + + (void) SetErrorMode (SEM_FAILCRITICALERRORS); + + STARTUPINFO si; + GetStartupInfo (&si); + child_proc_info = (child_info *) si.lpReserved2; + + int mypid = 0; + if (si.cbReserved2 < EXEC_MAGIC_SIZE || !child_proc_info + || memcmp (child_proc_info->zero, zeros, + sizeof (child_proc_info->zero)) != 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_user_h = child_proc_info->user_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; + } + } + + device::init (); + winpids::init (); + do_global_ctors (&__CTOR_LIST__, 1); + cygthread::init (); + + 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; + envp = spawn_info->moreinfo->envp; + cygheap->fdtab.fixup_after_exec (spawn_info->parent); + signal_fixup_after_exec (); + 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); + } + + _threadinfo::init (); + + /* Initialize events */ + events_init (); + + /* Init global well known SID objects */ + cygsid::init (); + cygheap->cwd.init (); +} + +/* 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 (char *) +{ + /* 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; + + initial_env (false); + check_sanity_and_sync (user_data); + malloc_init (); + + /* Initialize SIGSEGV handling, etc. */ + init_exceptions (&cygwin_except_entry); + + /* 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). */ + + user_data->resourcelocks->Init (); + user_data->threadinterface->Init (); + ProtectHandle (hMainProc); + ProtectHandle (hMainThread); + + /* Initialize pthread mainthread when not forked and it is safe to call new, + otherwise it is reinitalized in fixup_after_fork */ + if (!user_data->forkee) + { + __sinit (_impure_ptr); + pthread::init_mainthread (); + } + +#ifdef DEBUGGING + strace.microseconds (); +#endif + + /* 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 (); + + 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) + { + _tlsbase = (char *) fork_info->stackbottom; + _tlstop = (char *) 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 (); + + /* Initialize user info. */ + 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 *) malloc (CYG_MAX_PATH); + cygwin_conv_to_posix_path (__argv[0], new_argv0); + __argv[0] = (char *) realloc (new_argv0, strlen (new_argv0) + 1); + } + } + + __argc_safe = __argc; + 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; + + + set_errno (0); + + MALLOC_CHECK; + cygbench (__progname); + + /* 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 (); + if (user_data->main) + exit (user_data->main (__argc, __argv, *user_data->envptr)); +} + +struct _reent * +initialize_main_tls (char *padding) +{ + if (!_main_tls) + { + _main_tls = &_my_tls; + _main_tls->init_thread (padding, NULL); + } + return &_main_tls->local_clib; +} + +/* 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 () +{ + extern HANDLE sync_startup; + if (sync_startup) + { + (void) WaitForSingleObject (sync_startup, INFINITE); + CloseHandle (sync_startup); + } + + main_environ = user_data->envptr; + *main_environ = NULL; + + if (child_proc_info && child_proc_info->type == _PROC_FORK) + user_data->forkee = child_proc_info->cygpid; + + char padding[CYGTLS_PADSIZE]; + _impure_ptr = &reent_data; + _impure_ptr->_stdin = &_impure_ptr->__sf[0]; + _impure_ptr->_stdout = &_impure_ptr->__sf[1]; + _impure_ptr->_stderr = &_impure_ptr->__sf[2]; + _impure_ptr->_current_locale = "C"; + initialize_main_tls (padding); + dll_crt0_1 (padding); +} + +void +dll_crt0 (per_process *uptr) +{ + /* 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 (NULL); +} + +extern "C" void +__main (void) +{ + do_global_ctors (user_data->ctors, false); + atexit (do_global_dtors); +} + +exit_states NO_COPY exit_state; +extern CRITICAL_SECTION exit_lock; + +void __stdcall +do_exit (int status) +{ + syscall_printf ("do_exit (%d), exit_state %d", status, exit_state); + + vfork_save *vf = vfork_storage.val (); + if (vf != NULL && vf->pid < 0) + { + exit_state = ES_NOT_EXITING; + vf->restore_exit (status); + } + + EnterCriticalSection (&exit_lock); + muto::set_exiting_thread (); + if (exit_state < ES_EVENTS_TERMINATE) + { + exit_state = ES_EVENTS_TERMINATE; + events_terminate (); + } + + UINT n = (UINT) 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 (); + } + + minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n); + myself->exit (n); +} + +static muto *atexit_lock; + +extern "C" int +cygwin_atexit (void (*function)(void)) +{ + int res; + if (!atexit_lock) + new_muto (atexit_lock); + atexit_lock->acquire (); + res = atexit (function); + atexit_lock->release (); + return res; +} + +extern "C" void +cygwin_exit (int n) +{ + if (atexit_lock) + atexit_lock->acquire (); + 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); + int n = __small_sprintf (buf, "%P (%u): *** ", cygwin_pid (GetCurrentProcessId ())); + __small_vsprintf (buf + n, 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/sec_acl.cc b/winsup/cygwin/sec_acl.cc new file mode 100644 index 00000000000..472c20329e8 --- /dev/null +++ b/winsup/cygwin/sec_acl.cc @@ -0,0 +1,1042 @@ +/* sec_acl.cc: Sun compatible ACL functions. + + Copyright 2000, 2001, 2002, 2003 Red Hat, Inc. + + Written by Corinna Vinschen <corinna@vinschen.de> + +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 <grp.h> +#include <pwd.h> +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/acl.h> +#include <ctype.h> +#include <wingdi.h> +#include <winuser.h> +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "pinfo.h" +#include "cygheap.h" +#include "pwdgrp.h" + +extern "C" int aclsort32 (int nentries, int, __aclent32_t *aclbufp); +extern "C" int acl32 (const char *path, int cmd, int nentries, __aclent32_t *aclbufp); + +static int +searchace (__aclent32_t *aclp, int nentries, int type, __uid32_t id = ILLEGAL_UID) +{ + int i; + + for (i = 0; i < nentries; ++i) + if ((aclp[i].a_type == type && (id == ILLEGAL_UID || aclp[i].a_id == id)) + || !aclp[i].a_type) + return i; + return -1; +} + +static int +setacl (const char *file, int nentries, __aclent32_t *aclbufp) +{ + security_descriptor sd_ret; + + if (read_sd (file, sd_ret) <= 0) + { + debug_printf ("read_sd %E"); + return -1; + } + + BOOL dummy; + + /* Get owner SID. */ + PSID owner_sid; + if (!GetSecurityDescriptorOwner (sd_ret, &owner_sid, &dummy)) + { + __seterrno (); + return -1; + } + cygsid owner (owner_sid); + + /* Get group SID. */ + PSID group_sid; + if (!GetSecurityDescriptorGroup (sd_ret, &group_sid, &dummy)) + { + __seterrno (); + return -1; + } + cygsid group (group_sid); + + /* Initialize local security descriptor. */ + SECURITY_DESCRIPTOR sd; + if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION)) + { + __seterrno (); + return -1; + } + if (!SetSecurityDescriptorOwner (&sd, owner, FALSE)) + { + __seterrno (); + return -1; + } + if (!SetSecurityDescriptorGroup (&sd, group, FALSE)) + { + __seterrno (); + return -1; + } + + /* Fill access control list. */ + char acl_buf[3072]; + PACL acl = (PACL) acl_buf; + size_t acl_len = sizeof (ACL); + int ace_off = 0; + + cygsid sid; + struct passwd *pw; + struct __group32 *gr; + int pos; + + if (!InitializeAcl (acl, 3072, ACL_REVISION)) + { + __seterrno (); + return -1; + } + for (int i = 0; i < nentries; ++i) + { + DWORD allow; + /* Owner has more standard rights set. */ + if ((aclbufp[i].a_type & ~ACL_DEFAULT) == USER_OBJ) + allow = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA; + else + allow = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA; + if (aclbufp[i].a_perm & S_IROTH) + allow |= FILE_GENERIC_READ; + if (aclbufp[i].a_perm & S_IWOTH) + allow |= STANDARD_RIGHTS_WRITE | FILE_GENERIC_WRITE; + if (aclbufp[i].a_perm & S_IXOTH) + allow |= FILE_GENERIC_EXECUTE; + if ((aclbufp[i].a_perm & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)) + allow |= FILE_DELETE_CHILD; + /* Set inherit property. */ + DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT) + ? (SUB_CONTAINERS_AND_OBJECTS_INHERIT | INHERIT_ONLY) + : NO_INHERITANCE; + /* + * If a specific acl contains a corresponding default entry with + * identical permissions, only one Windows ACE with proper + * inheritance bits is created. + */ + if (!(aclbufp[i].a_type & ACL_DEFAULT) + && aclbufp[i].a_type & (USER|GROUP|OTHER_OBJ) + && (pos = searchace (aclbufp + i + 1, nentries - i - 1, + aclbufp[i].a_type | ACL_DEFAULT, + (aclbufp[i].a_type & (USER|GROUP)) + ? aclbufp[i].a_id : ILLEGAL_UID)) >= 0 + && aclbufp[i].a_perm == aclbufp[pos].a_perm) + { + inheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + /* This invalidates the corresponding default entry. */ + aclbufp[pos].a_type = USER|GROUP|ACL_DEFAULT; + } + switch (aclbufp[i].a_type) + { + case USER_OBJ: + if (!add_access_allowed_ace (acl, ace_off++, allow, + owner, acl_len, inheritance)) + return -1; + break; + case DEF_USER_OBJ: + if (!add_access_allowed_ace (acl, ace_off++, allow, + well_known_creator_owner_sid, acl_len, inheritance)) + return -1; + break; + case USER: + case DEF_USER: + if (!(pw = internal_getpwuid (aclbufp[i].a_id)) + || !sid.getfrompw (pw) + || !add_access_allowed_ace (acl, ace_off++, allow, + sid, acl_len, inheritance)) + return -1; + break; + case GROUP_OBJ: + if (!add_access_allowed_ace (acl, ace_off++, allow, + group, acl_len, inheritance)) + return -1; + break; + case DEF_GROUP_OBJ: + if (!add_access_allowed_ace (acl, ace_off++, allow, + well_known_creator_group_sid, acl_len, inheritance)) + return -1; + break; + case GROUP: + case DEF_GROUP: + if (!(gr = internal_getgrgid (aclbufp[i].a_id)) + || !sid.getfromgr (gr) + || !add_access_allowed_ace (acl, ace_off++, allow, + sid, acl_len, inheritance)) + return -1; + break; + case OTHER_OBJ: + case DEF_OTHER_OBJ: + if (!add_access_allowed_ace (acl, ace_off++, allow, + well_known_world_sid, + acl_len, inheritance)) + return -1; + break; + } + } + /* Set AclSize to computed value. */ + acl->AclSize = acl_len; + debug_printf ("ACL-Size: %d", acl_len); + /* Create DACL for local security descriptor. */ + if (!SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE)) + { + __seterrno (); + return -1; + } + /* Make self relative security descriptor in sd_ret. */ + DWORD sd_size = 0; + MakeSelfRelativeSD (&sd, sd_ret, &sd_size); + if (sd_size <= 0) + { + __seterrno (); + return -1; + } + if (!sd_ret.realloc (sd_size)) + { + set_errno (ENOMEM); + return -1; + } + if (!MakeSelfRelativeSD (&sd, sd_ret, &sd_size)) + { + __seterrno (); + return -1; + } + debug_printf ("Created SD-Size: %d", sd_ret.size ()); + return write_sd (file, sd_ret); +} + +/* Temporary access denied bits */ +#define DENY_R 040000 +#define DENY_W 020000 +#define DENY_X 010000 + +static void +getace (__aclent32_t &acl, int type, int id, DWORD win_ace_mask, + DWORD win_ace_type) +{ + acl.a_type = type; + acl.a_id = id; + + if ((win_ace_mask & FILE_READ_BITS) && !(acl.a_perm & (S_IROTH | DENY_R))) + if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) + acl.a_perm |= S_IROTH; + else if (win_ace_type == ACCESS_DENIED_ACE_TYPE) + acl.a_perm |= DENY_R; + + if ((win_ace_mask & FILE_WRITE_BITS) && !(acl.a_perm & (S_IWOTH | DENY_W))) + if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) + acl.a_perm |= S_IWOTH; + else if (win_ace_type == ACCESS_DENIED_ACE_TYPE) + acl.a_perm |= DENY_W; + + if ((win_ace_mask & FILE_EXEC_BITS) && !(acl.a_perm & (S_IXOTH | DENY_X))) + if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE) + acl.a_perm |= S_IXOTH; + else if (win_ace_type == ACCESS_DENIED_ACE_TYPE) + acl.a_perm |= DENY_X; +} + +static int +getacl (const char *file, DWORD attr, int nentries, __aclent32_t *aclbufp) +{ + security_descriptor sd; + + int ret; + if ((ret = read_sd (file, sd)) <= 0) + { + debug_printf ("read_sd %E"); + return ret; + } + + cygpsid owner_sid; + cygpsid group_sid; + BOOL dummy; + __uid32_t uid; + __gid32_t gid; + + if (!GetSecurityDescriptorOwner (sd, (PSID *) &owner_sid, &dummy)) + { + debug_printf ("GetSecurityDescriptorOwner %E"); + __seterrno (); + return -1; + } + uid = owner_sid.get_uid (); + + if (!GetSecurityDescriptorGroup (sd, (PSID *) &group_sid, &dummy)) + { + debug_printf ("GetSecurityDescriptorGroup %E"); + __seterrno (); + return -1; + } + gid = group_sid.get_gid (); + + __aclent32_t lacl[MAX_ACL_ENTRIES]; + memset (&lacl, 0, MAX_ACL_ENTRIES * sizeof (__aclent32_t)); + lacl[0].a_type = USER_OBJ; + lacl[0].a_id = uid; + lacl[1].a_type = GROUP_OBJ; + lacl[1].a_id = gid; + lacl[2].a_type = OTHER_OBJ; + lacl[2].a_id = ILLEGAL_GID; + lacl[3].a_type = CLASS_OBJ; + lacl[3].a_id = ILLEGAL_GID; + lacl[3].a_perm = S_IROTH | S_IWOTH | S_IXOTH; + + PACL acl; + BOOL acl_exists; + + if (!GetSecurityDescriptorDacl (sd, &acl_exists, &acl, &dummy)) + { + __seterrno (); + debug_printf ("GetSecurityDescriptorDacl %E"); + return -1; + } + + int pos, i, types_def = 0; + + if (!acl_exists || !acl) + for (pos = 0; pos < 3; ++pos) /* Don't change CLASS_OBJ entry */ + lacl[pos].a_perm = S_IROTH | S_IWOTH | S_IXOTH; + else + { + for (i = 0; i < acl->AceCount; ++i) + { + ACCESS_ALLOWED_ACE *ace; + + if (!GetAce (acl, i, (PVOID *) &ace)) + continue; + + cygpsid ace_sid ((PSID) &ace->SidStart); + int id; + int type = 0; + + if (ace_sid == well_known_world_sid) + { + type = OTHER_OBJ; + id = ILLEGAL_GID; + } + else if (ace_sid == group_sid) + { + type = GROUP_OBJ; + id = gid; + } + else if (ace_sid == owner_sid) + { + type = USER_OBJ; + id = uid; + } + else if (ace_sid == well_known_creator_group_sid) + { + type = GROUP_OBJ | ACL_DEFAULT; + id = ILLEGAL_GID; + } + else if (ace_sid == well_known_creator_owner_sid) + { + type = USER_OBJ | ACL_DEFAULT; + id = ILLEGAL_GID; + } + else + id = ace_sid.get_id (true, &type); + + if (!type) + continue; + if (!(ace->Header.AceFlags & INHERIT_ONLY || type & ACL_DEFAULT)) + { + if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0) + getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType); + } + if ((ace->Header.AceFlags & SUB_CONTAINERS_AND_OBJECTS_INHERIT) + && (attr & FILE_ATTRIBUTE_DIRECTORY)) + { + if (type == USER_OBJ) + type = USER; + else if (type == GROUP_OBJ) + type = GROUP; + type |= ACL_DEFAULT; + types_def |= type; + if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0) + getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType); + } + } + /* Include DEF_CLASS_OBJ if any default ace exists */ + if ((types_def & (USER|GROUP)) + && ((pos = searchace (lacl, MAX_ACL_ENTRIES, DEF_CLASS_OBJ)) >= 0)) + { + lacl[pos].a_type = DEF_CLASS_OBJ; + lacl[pos].a_id = ILLEGAL_GID; + lacl[pos].a_perm = S_IRWXU | S_IRWXG | S_IRWXO; + } + } + if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0) + pos = MAX_ACL_ENTRIES; + if (aclbufp) { + if (owner_sid == group_sid) + lacl[0].a_perm = lacl[1].a_perm; + if (pos > nentries) + { + set_errno (ENOSPC); + return -1; + } + memcpy (aclbufp, lacl, pos * sizeof (__aclent32_t)); + for (i = 0; i < pos; ++i) + aclbufp[i].a_perm &= ~(DENY_R | DENY_W | DENY_X); + aclsort32 (pos, 0, aclbufp); + } + syscall_printf ("%d = getacl (%s)", pos, file); + return pos; +} + +static int +acl_worker (const char *path, int cmd, int nentries, __aclent32_t *aclbufp, + int nofollow) +{ + extern suffix_info stat_suffixes[]; + path_conv real_path (path, (nofollow ? PC_SYM_NOFOLLOW : PC_SYM_FOLLOW) | PC_FULL, stat_suffixes); + if (real_path.error) + { + set_errno (real_path.error); + syscall_printf ("-1 = acl (%s)", path); + return -1; + } + if (!real_path.has_acls () || !allow_ntsec) + { + struct __stat64 st; + int ret = -1; + + switch (cmd) + { + case SETACL: + set_errno (ENOSYS); + break; + case GETACL: + if (!aclbufp) + set_errno(EFAULT); + else if (nentries < MIN_ACL_ENTRIES) + set_errno (ENOSPC); + else if ((nofollow && !lstat64 (path, &st)) + || (!nofollow && !stat64 (path, &st))) + { + aclbufp[0].a_type = USER_OBJ; + aclbufp[0].a_id = st.st_uid; + aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6; + aclbufp[1].a_type = GROUP_OBJ; + aclbufp[1].a_id = st.st_gid; + aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3; + aclbufp[2].a_type = OTHER_OBJ; + aclbufp[2].a_id = ILLEGAL_GID; + aclbufp[2].a_perm = st.st_mode & S_IRWXO; + aclbufp[3].a_type = CLASS_OBJ; + aclbufp[3].a_id = ILLEGAL_GID; + aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO; + ret = MIN_ACL_ENTRIES; + } + break; + case GETACLCNT: + ret = MIN_ACL_ENTRIES; + break; + } + syscall_printf ("%d = acl (%s)", ret, path); + return ret; + } + switch (cmd) + { + case SETACL: + if (!aclsort32 (nentries, 0, aclbufp)) + return setacl (real_path.get_win32 (), + nentries, aclbufp); + break; + case GETACL: + if (!aclbufp) + set_errno(EFAULT); + else + return getacl (real_path.get_win32 (), + real_path.file_attributes (), + nentries, aclbufp); + break; + case GETACLCNT: + return getacl (real_path.get_win32 (), + real_path.file_attributes (), + 0, NULL); + default: + set_errno (EINVAL); + break; + } + syscall_printf ("-1 = acl (%s)", path); + return -1; +} + +extern "C" int +acl32 (const char *path, int cmd, int nentries, __aclent32_t *aclbufp) +{ + return acl_worker (path, cmd, nentries, aclbufp, 0); +} + +extern "C" int +lacl32 (const char *path, int cmd, int nentries, __aclent32_t *aclbufp) +{ + return acl_worker (path, cmd, nentries, aclbufp, 1); +} + +extern "C" int +facl32 (int fd, int cmd, int nentries, __aclent32_t *aclbufp) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + { + syscall_printf ("-1 = facl (%d)", fd); + return -1; + } + const char *path = cfd->get_name (); + if (path == NULL) + { + syscall_printf ("-1 = facl (%d) (no name)", fd); + set_errno (ENOSYS); + return -1; + } + syscall_printf ("facl (%d): calling acl (%s)", fd, path); + return acl_worker (path, cmd, nentries, aclbufp, 0); +} + +extern "C" int +aclcheck32 (__aclent32_t *aclbufp, int nentries, int *which) +{ + bool has_user_obj = false; + bool has_group_obj = false; + bool has_other_obj = false; + bool has_class_obj = false; + bool has_ug_objs = false; + bool has_def_user_obj = false; + bool has_def_group_obj = false; + bool has_def_other_obj = false; + bool has_def_class_obj = false; + bool has_def_ug_objs = false; + int pos2; + + for (int pos = 0; pos < nentries; ++pos) + switch (aclbufp[pos].a_type) + { + case USER_OBJ: + if (has_user_obj) + { + if (which) + *which = pos; + return USER_ERROR; + } + has_user_obj = true; + break; + case GROUP_OBJ: + if (has_group_obj) + { + if (which) + *which = pos; + return GRP_ERROR; + } + has_group_obj = true; + break; + case OTHER_OBJ: + if (has_other_obj) + { + if (which) + *which = pos; + return OTHER_ERROR; + } + has_other_obj = true; + break; + case CLASS_OBJ: + if (has_class_obj) + { + if (which) + *which = pos; + return CLASS_ERROR; + } + has_class_obj = true; + break; + case USER: + case GROUP: + if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, + aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) + { + if (which) + *which = pos2; + return DUPLICATE_ERROR; + } + has_ug_objs = true; + break; + case DEF_USER_OBJ: + if (has_def_user_obj) + { + if (which) + *which = pos; + return USER_ERROR; + } + has_def_user_obj = true; + break; + case DEF_GROUP_OBJ: + if (has_def_group_obj) + { + if (which) + *which = pos; + return GRP_ERROR; + } + has_def_group_obj = true; + break; + case DEF_OTHER_OBJ: + if (has_def_other_obj) + { + if (which) + *which = pos; + return OTHER_ERROR; + } + has_def_other_obj = true; + break; + case DEF_CLASS_OBJ: + if (has_def_class_obj) + { + if (which) + *which = pos; + return CLASS_ERROR; + } + has_def_class_obj = true; + break; + case DEF_USER: + case DEF_GROUP: + if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1, + aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0) + { + if (which) + *which = pos2; + return DUPLICATE_ERROR; + } + has_def_ug_objs = true; + break; + default: + return ENTRY_ERROR; + } + if (!has_user_obj + || !has_group_obj + || !has_other_obj +#if 0 + /* These checks are not ok yet since CLASS_OBJ isn't fully implemented. */ + || (has_ug_objs && !has_class_obj) + || (has_def_ug_objs && !has_def_class_obj) +#endif + ) + { + if (which) + *which = -1; + return MISS_ERROR; + } + return 0; +} + +static int +acecmp (const void *a1, const void *a2) +{ +#define ace(i) ((const __aclent32_t *) a##i) + int ret = ace (1)->a_type - ace (2)->a_type; + if (!ret) + ret = ace (1)->a_id - ace (2)->a_id; + return ret; +#undef ace +} + +extern "C" int +aclsort32 (int nentries, int, __aclent32_t *aclbufp) +{ + if (aclcheck32 (aclbufp, nentries, NULL)) + return -1; + if (!aclbufp || nentries < 1) + { + set_errno (EINVAL); + return -1; + } + qsort ((void *) aclbufp, nentries, sizeof (__aclent32_t), acecmp); + return 0; +} + +extern "C" int +acltomode32 (__aclent32_t *aclbufp, int nentries, mode_t *modep) +{ + int pos; + + if (!aclbufp || nentries < 1 || !modep) + { + set_errno (EINVAL); + return -1; + } + *modep = 0; + if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + *modep |= (aclbufp[pos].a_perm & S_IRWXO) << 6; + if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + *modep |= (aclbufp[pos].a_perm & S_IRWXO) << 3; + int cpos; + if ((cpos = searchace (aclbufp, nentries, CLASS_OBJ)) >= 0 + && aclbufp[cpos].a_type == CLASS_OBJ) + *modep |= ((aclbufp[pos].a_perm & S_IRWXO) & aclbufp[cpos].a_perm) << 3; + if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + *modep |= aclbufp[pos].a_perm & S_IRWXO; + return 0; +} + +extern "C" int +aclfrommode32 (__aclent32_t *aclbufp, int nentries, mode_t *modep) +{ + int pos; + + if (!aclbufp || nentries < 1 || !modep) + { + set_errno (EINVAL); + return -1; + } + if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + aclbufp[pos].a_perm = (*modep & S_IRWXU) >> 6; + if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + aclbufp[pos].a_perm = (*modep & S_IRWXG) >> 3; + if ((pos = searchace (aclbufp, nentries, CLASS_OBJ)) >= 0 + && aclbufp[pos].a_type == CLASS_OBJ) + aclbufp[pos].a_perm = (*modep & S_IRWXG) >> 3; + if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0 + || !aclbufp[pos].a_type) + { + set_errno (EINVAL); + return -1; + } + aclbufp[pos].a_perm = (*modep & S_IRWXO); + return 0; +} + +extern "C" int +acltopbits32 (__aclent32_t *aclbufp, int nentries, mode_t *pbitsp) +{ + return acltomode32 (aclbufp, nentries, pbitsp); +} + +extern "C" int +aclfrompbits32 (__aclent32_t *aclbufp, int nentries, mode_t *pbitsp) +{ + return aclfrommode32 (aclbufp, nentries, pbitsp); +} + +static char * +permtostr (mode_t perm) +{ + static char pbuf[4]; + + pbuf[0] = (perm & S_IROTH) ? 'r' : '-'; + pbuf[1] = (perm & S_IWOTH) ? 'w' : '-'; + pbuf[2] = (perm & S_IXOTH) ? 'x' : '-'; + pbuf[3] = '\0'; + return pbuf; +} + +extern "C" char * +acltotext32 (__aclent32_t *aclbufp, int aclcnt) +{ + if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES + || aclcheck32 (aclbufp, aclcnt, NULL)) + { + set_errno (EINVAL); + return NULL; + } + char buf[32000]; + buf[0] = '\0'; + bool first = true; + + for (int pos = 0; pos < aclcnt; ++pos) + { + if (!first) + strcat (buf, ","); + first = false; + if (aclbufp[pos].a_type & ACL_DEFAULT) + strcat (buf, "default"); + switch (aclbufp[pos].a_type) + { + case USER_OBJ: + __small_sprintf (buf + strlen (buf), "user::%s", + permtostr (aclbufp[pos].a_perm)); + break; + case USER: + __small_sprintf (buf + strlen (buf), "user:%d:%s", + aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm)); + break; + case GROUP_OBJ: + __small_sprintf (buf + strlen (buf), "group::%s", + permtostr (aclbufp[pos].a_perm)); + break; + case GROUP: + __small_sprintf (buf + strlen (buf), "group:%d:%s", + aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm)); + break; + case CLASS_OBJ: + __small_sprintf (buf + strlen (buf), "mask::%s", + permtostr (aclbufp[pos].a_perm)); + break; + case OTHER_OBJ: + __small_sprintf (buf + strlen (buf), "other::%s", + permtostr (aclbufp[pos].a_perm)); + break; + default: + set_errno (EINVAL); + return NULL; + } + } + return strdup (buf); +} + +static mode_t +permfromstr (char *perm) +{ + mode_t mode = 0; + + if (strlen (perm) != 3) + return 01000; + if (perm[0] == 'r') + mode |= S_IROTH; + else if (perm[0] != '-') + return 01000; + if (perm[1] == 'w') + mode |= S_IWOTH; + else if (perm[1] != '-') + return 01000; + if (perm[2] == 'x') + mode |= S_IXOTH; + else if (perm[2] != '-') + return 01000; + return mode; +} + +extern "C" __aclent32_t * +aclfromtext32 (char *acltextp, int *) +{ + if (!acltextp) + { + set_errno (EINVAL); + return NULL; + } + char buf[strlen (acltextp) + 1]; + __aclent32_t lacl[MAX_ACL_ENTRIES]; + memset (lacl, 0, sizeof lacl); + int pos = 0; + strcpy (buf, acltextp); + char *lasts; + for (char *c = strtok_r (buf, ",", &lasts); + c; + c = strtok_r (NULL, ",", &lasts)) + { + if (!strncmp (c, "default", 7)) + { + lacl[pos].a_type |= ACL_DEFAULT; + c += 7; + } + if (!strncmp (c, "user:", 5)) + { + if (c[5] == ':') + lacl[pos].a_type |= USER_OBJ; + else + { + lacl[pos].a_type |= USER; + c += 5; + if (isalpha (*c)) + { + struct passwd *pw = internal_getpwnam (c); + if (!pw) + { + set_errno (EINVAL); + return NULL; + } + lacl[pos].a_id = pw->pw_uid; + c = strechr (c, ':'); + } + else if (isdigit (*c)) + lacl[pos].a_id = strtol (c, &c, 10); + if (*c != ':') + { + set_errno (EINVAL); + return NULL; + } + } + } + else if (!strncmp (c, "group:", 6)) + { + if (c[5] == ':') + lacl[pos].a_type |= GROUP_OBJ; + else + { + lacl[pos].a_type |= GROUP; + c += 5; + if (isalpha (*c)) + { + struct __group32 *gr = internal_getgrnam (c); + if (!gr) + { + set_errno (EINVAL); + return NULL; + } + lacl[pos].a_id = gr->gr_gid; + c = strechr (c, ':'); + } + else if (isdigit (*c)) + lacl[pos].a_id = strtol (c, &c, 10); + if (*c != ':') + { + set_errno (EINVAL); + return NULL; + } + } + } + else if (!strncmp (c, "mask:", 5)) + { + if (c[5] == ':') + lacl[pos].a_type |= CLASS_OBJ; + else + { + set_errno (EINVAL); + return NULL; + } + } + else if (!strncmp (c, "other:", 6)) + { + if (c[5] == ':') + lacl[pos].a_type |= OTHER_OBJ; + else + { + set_errno (EINVAL); + return NULL; + } + } + if ((lacl[pos].a_perm = permfromstr (c)) == 01000) + { + set_errno (EINVAL); + return NULL; + } + ++pos; + } + __aclent32_t *aclp = (__aclent32_t *) malloc (pos * sizeof (__aclent32_t)); + if (aclp) + memcpy (aclp, lacl, pos * sizeof (__aclent32_t)); + return aclp; +} + +/* __aclent16_t and __aclent32_t have same size and same member offsets */ +static __aclent32_t * +acl16to32 (__aclent16_t *aclbufp, int nentries) +{ + __aclent32_t *aclbufp32 = (__aclent32_t *) aclbufp; + if (aclbufp32) + for (int i = 0; i < nentries; i++) + aclbufp32[i].a_id &= USHRT_MAX; + return aclbufp32; +} + +extern "C" int +acl (const char *path, int cmd, int nentries, __aclent16_t *aclbufp) +{ + return acl32 (path, cmd, nentries, acl16to32 (aclbufp, nentries)); +} + +extern "C" int +facl (int fd, int cmd, int nentries, __aclent16_t *aclbufp) +{ + return facl32 (fd, cmd, nentries, acl16to32 (aclbufp, nentries)); +} + +extern "C" int +lacl (const char *path, int cmd, int nentries, __aclent16_t *aclbufp) +{ + return lacl32 (path, cmd, nentries, acl16to32 (aclbufp, nentries)); +} + +extern "C" int +aclcheck (__aclent16_t *aclbufp, int nentries, int *which) +{ + return aclcheck32 (acl16to32 (aclbufp, nentries), nentries, which); +} + +extern "C" int +aclsort (int nentries, int i, __aclent16_t *aclbufp) +{ + return aclsort32 (nentries, i, acl16to32 (aclbufp, nentries)); +} + + +extern "C" int +acltomode (__aclent16_t *aclbufp, int nentries, mode_t *modep) +{ + return acltomode32 (acl16to32 (aclbufp, nentries), nentries, modep); +} + +extern "C" int +aclfrommode (__aclent16_t *aclbufp, int nentries, mode_t *modep) +{ + return aclfrommode32 ((__aclent32_t *)aclbufp, nentries, modep); +} + +extern "C" int +acltopbits (__aclent16_t *aclbufp, int nentries, mode_t *pbitsp) +{ + return acltopbits32 (acl16to32 (aclbufp, nentries), nentries, pbitsp); +} + +extern "C" int +aclfrompbits (__aclent16_t *aclbufp, int nentries, mode_t *pbitsp) +{ + return aclfrompbits32 ((__aclent32_t *)aclbufp, nentries, pbitsp); +} + +extern "C" char * +acltotext (__aclent16_t *aclbufp, int aclcnt) +{ + return acltotext32 (acl16to32 (aclbufp, aclcnt), aclcnt); +} + +extern "C" __aclent16_t * +aclfromtext (char *acltextp, int * aclcnt) +{ + return (__aclent16_t *) aclfromtext32 (acltextp, aclcnt); +} diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc new file mode 100644 index 00000000000..b53aa5f58bf --- /dev/null +++ b/winsup/cygwin/sigproc.cc @@ -0,0 +1,1276 @@ +/* sigproc.cc: inter/intra signal and sub process handler + + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + + Written by Christopher Faylor + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <stdlib.h> +#include <time.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <sys/cygwin.h> +#include <assert.h> +#include <sys/signal.h> +#include "cygerrno.h" +#include "sync.h" +#include "pinfo.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "child_info_magic.h" +#include "shared_info.h" +#include "cygthread.h" +#include "cygtls.h" +#include "sigproc.h" +#include "perthread.h" +#include "exceptions.h" + +/* + * Convenience defines + */ +#define WSSC 60000 // Wait for signal completion +#define WPSP 40000 // Wait for proc_subproc mutex + +#define PSIZE 63 // Number of processes + +#define wake_wait_subproc() SetEvent (events[0]) + +#define no_signals_available() (!hwait_sig || (myself->sendsig == INVALID_HANDLE_VALUE) || exit_state) + +#define NZOMBIES 256 + +struct sigelem +{ + int sig; + int pid; + _threadinfo *tls; + class sigelem *next; + friend class pending_signals; + friend int __stdcall sig_dispatch_pending (); +}; + +class pending_signals +{ + sigelem sigs[NSIG + 1]; + sigelem start; + sigelem *end; + sigelem *prev; + sigelem *curr; + int empty; +public: + void reset () {curr = &start; prev = &start;} + void add (int sig, int pid, _threadinfo *tls); + void del (); + sigelem *next (); + friend int __stdcall sig_dispatch_pending (); +}; + +struct sigpacket +{ + int sig; + pid_t pid; + HANDLE wakeup; + sigset_t *mask; + _threadinfo *tls; +}; + +static pending_signals sigqueue; + +struct sigaction *global_sigs; + +void __stdcall +sigalloc () +{ + cygheap->sigs = global_sigs = + (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction)); +} + +void __stdcall +signal_fixup_after_exec () +{ + global_sigs = cygheap->sigs; + /* Set up child's signal handlers */ + for (int i = 0; i < NSIG; i++) + { + global_sigs[i].sa_mask = 0; + if (global_sigs[i].sa_handler != SIG_IGN) + global_sigs[i].sa_handler = SIG_DFL; + } +} + +/* + * Global variables + */ +const char *__sp_fn ; +int __sp_ln; + +char NO_COPY myself_nowait_dummy[1] = {'0'};// Flag to sig_send that signal goes to + // current process but no wait is required +HANDLE NO_COPY signal_arrived; // Event signaled when a signal has + // resulted in a user-specified + // function call +/* + * Common variables + */ + + +/* How long to wait for message/signals. Normally this is infinite. + * On termination, however, these are set to zero as a flag to exit. + */ + +#define Static static NO_COPY + +Static DWORD proc_loop_wait = 1000; // Wait for subprocesses to exit + +Static HANDLE sigcomplete_main; // Event signaled when a signal has + // finished processing for the main + // thread +HANDLE NO_COPY sigCONT; // Used to "STOP" a process +Static cygthread *hwait_sig; // Handle of wait_sig thread +Static cygthread *hwait_subproc; // Handle of sig_subproc thread + +Static HANDLE wait_sig_inited; // Control synchronization of + // message queue startup + +/* Used by WaitForMultipleObjects. These are handles to child processes. + */ +Static HANDLE events[PSIZE + 1]; // All my children's handles++ +#define hchildren (events + 1) // Where the children handles begin +Static char cpchildren[PSIZE * sizeof (pinfo)]; // All my children info +Static int nchildren; // Number of active children +Static char czombies[(NZOMBIES + 1) * sizeof (pinfo)]; // All my deceased children info +Static int nzombies; // Number of deceased children + +#define pchildren ((pinfo *) cpchildren) +#define zombies ((pinfo *) czombies) + +Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads +Static waitq waitq_main; // Storage for main thread + +muto NO_COPY *sync_proc_subproc = NULL; // Control access to subproc stuff + +DWORD NO_COPY sigtid = 0; // ID of the signal thread + +/* Functions + */ +static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1))); +static __inline__ bool get_proc_lock (DWORD, DWORD); +static void __stdcall remove_zombie (int); +static DWORD WINAPI wait_sig (VOID *arg); +static int __stdcall stopped_or_terminated (waitq *, _pinfo *); +static DWORD WINAPI wait_subproc (VOID *); + +/* Determine if the parent process is alive. + */ + +bool __stdcall +my_parent_is_alive () +{ + bool res; + if (!myself->ppid_handle) + { + debug_printf ("No myself->ppid_handle"); + res = false; + } + else + for (int i = 0; i < 2; i++) + switch (res = WaitForSingleObject (myself->ppid_handle, 0)) + { + case WAIT_OBJECT_0: + debug_printf ("parent dead."); + res = false; + goto out; + case WAIT_TIMEOUT: + debug_printf ("parent still alive"); + res = true; + goto out; + case WAIT_FAILED: + DWORD werr = GetLastError (); + if (werr == ERROR_INVALID_HANDLE && i == 0) + continue; + system_printf ("WFSO for myself->ppid_handle(%p) failed, error %d", + myself->ppid_handle, werr); + res = false; + goto out; + } +out: + return res; +} + +void __stdcall +wait_for_sigthread () +{ + sigproc_printf ("wait_sig_inited %p", wait_sig_inited); + HANDLE hsig_inited = wait_sig_inited; + (void) WaitForSingleObject (hsig_inited, INFINITE); + wait_sig_inited = NULL; + (void) ForceCloseHandle1 (hsig_inited, wait_sig_inited); +} + +/* Get the sync_proc_subproc muto to control access to + * children, zombie arrays. + * Attempt to handle case where process is exiting as we try to grab + * the mutex. + */ +static bool +get_proc_lock (DWORD what, DWORD val) +{ + Static int lastwhat = -1; + if (!sync_proc_subproc) + { + sigproc_printf ("sync_proc_subproc is NULL (1)"); + return false; + } + if (sync_proc_subproc->acquire (WPSP)) + { + lastwhat = what; + return true; + } + if (!sync_proc_subproc) + { + sigproc_printf ("sync_proc_subproc is NULL (2)"); + return false; + } + system_printf ("Couldn't aquire sync_proc_subproc for(%d,%d), %E, last %d", + what, val, lastwhat); + return true; +} + +static bool __stdcall +proc_can_be_signalled (_pinfo *p) +{ + if (p->sendsig == INVALID_HANDLE_VALUE) + { + set_errno (EPERM); + return false; + } + + if (p == myself_nowait || p == myself) + return hwait_sig; + + if (ISSTATE (p, PID_INITIALIZING) || + (((p)->process_state & (PID_ACTIVE | PID_IN_USE)) == + (PID_ACTIVE | PID_IN_USE))) + return true; + + set_errno (ESRCH); + return false; +} + +bool __stdcall +pid_exists (pid_t pid) +{ + pinfo p (pid); + return proc_exists (p); +} + +/* Test to determine if a process really exists and is processing signals. + */ +bool __stdcall +proc_exists (_pinfo *p) +{ + return p && !(p->process_state & (PID_EXITED | PID_ZOMBIE)); +} + +/* Return 1 if this is one of our children, zero otherwise. + FIXME: This really should be integrated with the rest of the proc_subproc + testing. Scanning these lists twice is inefficient. */ +int __stdcall +mychild (int pid) +{ + for (int i = 0; i < nchildren; i++) + if (pchildren[i]->pid == pid) + return 1; + for (int i = 0; i < nzombies; i++) + if (zombies[i]->pid == pid) + return 1; + return 0; +} + +/* Handle all subprocess requests + */ +#define vchild (*((pinfo *) val)) +int __stdcall +proc_subproc (DWORD what, DWORD val) +{ + int rc = 1; + int potential_match; + _pinfo *child; + int clearing; + waitq *w; + int thiszombie; + +#define wval ((waitq *) val) + + sigproc_printf ("args: %x, %d", what, val); + + if (!get_proc_lock (what, val)) // Serialize access to this function + { + system_printf ("couldn't get proc lock. what %d, val %d", what, val); + goto out1; + } + + switch (what) + { + /* Add a new subprocess to the children arrays. + * (usually called from the main thread) + */ + case PROC_ADDCHILD: + if (nchildren >= PSIZE - 1) + { + rc = 0; + break; + } + pchildren[nchildren] = vchild; + hchildren[nchildren] = vchild->hProcess; + if (!DuplicateHandle (hMainProc, vchild->hProcess, hMainProc, &vchild->pid_handle, + 0, 0, DUPLICATE_SAME_ACCESS)) + system_printf ("Couldn't duplicate child handle for pid %d, %E", vchild->pid); + ProtectHandle1 (vchild->pid_handle, pid_handle); + + if (!DuplicateHandle (hMainProc, hMainProc, vchild->hProcess, &vchild->ppid_handle, + SYNCHRONIZE | PROCESS_DUP_HANDLE, TRUE, 0)) + system_printf ("Couldn't duplicate my handle<%p> for pid %d, %E", hMainProc, vchild->pid); + vchild->ppid = myself->pid; + vchild->uid = myself->uid; + vchild->gid = myself->gid; + vchild->pgid = myself->pgid; + vchild->sid = myself->sid; + vchild->ctty = myself->ctty; + vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); + + sigproc_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p", + vchild->pid, nchildren, vchild->dwProcessId, + vchild->hProcess); + nchildren++; + + wake_wait_subproc (); + break; + + /* A child process had terminated. + Possibly this is just due to an exec(). Cygwin implements an exec() + as a "handoff" from one windows process to another. If child->hProcess + is different from what is recorded in hchildren, then this is an exec(). + Otherwise this is a normal child termination event. + (called from wait_subproc thread) */ + case PROC_CHILDTERMINATED: + if (hchildren[val] != pchildren[val]->hProcess) + { + sigproc_printf ("pid %d[%d], reparented old hProcess %p, new %p", + pchildren[val]->pid, val, hchildren[val], pchildren[val]->hProcess); + HANDLE h = hchildren[val]; + hchildren[val] = pchildren[val]->hProcess; /* Filled out by child */ + sync_proc_subproc->release (); // Release the lock ASAP + ForceCloseHandle1 (h, childhProc); + ProtectHandle1 (pchildren[val]->hProcess, childhProc); + rc = 0; + goto out; // This was an exec() + } + + sigproc_printf ("pid %d[%d] terminated, handle %p, nchildren %d, nzombies %d", + pchildren[val]->pid, val, hchildren[val], nchildren, nzombies); + + thiszombie = nzombies; + zombies[nzombies] = pchildren[val]; // Add to zombie array + zombies[nzombies++]->process_state = PID_ZOMBIE;// Walking dead + + sigproc_printf ("zombifying [%d], pid %d, handle %p, nchildren %d", + val, pchildren[val]->pid, hchildren[val], nchildren); + if ((int) val < --nchildren) + { + hchildren[val] = hchildren[nchildren]; + pchildren[val] = pchildren[nchildren]; + } + + /* See if we should care about the this terminated process. If we've + filled up our table or if we're ignoring SIGCHLD, then we immediately + remove the process and move on. Otherwise, this process becomes a zombie + which must be reaped by a wait() call. FIXME: This is a very inelegant + way to deal with this and could lead to process hangs. */ + if (nzombies >= NZOMBIES) + { + sigproc_printf ("zombie table overflow %d", thiszombie); + remove_zombie (thiszombie); + } + + /* Don't scan the wait queue yet. Caller will send SIGCHLD to this process. + This will cause an eventual scan of waiters. */ + break; + + /* Handle a wait4() operation. Allocates an event for the calling + * thread which is signaled when the appropriate pid exits or stops. + * (usually called from the main thread) + */ + case PROC_WAIT: + wval->ev = NULL; // Don't know event flag yet + + if (wval->pid <= 0) + child = NULL; // Not looking for a specific pid + else if (!mychild (wval->pid)) + goto out; // invalid pid. flag no such child + + wval->status = 0; // Don't know status yet + sigproc_printf ("wval->pid %d, wval->options %d", wval->pid, wval->options); + + /* If the first time for this thread, create a new event, otherwise + * reset the event. + */ + if ((wval->ev = wval->thread_ev) == NULL) + { + wval->ev = wval->thread_ev = CreateEvent (&sec_none_nih, TRUE, + FALSE, NULL); + ProtectHandle (wval->ev); + } + + ResetEvent (wval->ev); + w = waitq_head.next; + waitq_head.next = wval; /* Add at the beginning. */ + wval->next = w; /* Link in rest of the list. */ + clearing = 0; + goto scan_wait; + + /* Clear all waiting threads. Called from exceptions.cc prior to + the main thread's dispatch to a signal handler function. + (called from wait_sig thread) */ + case PROC_CLEARWAIT: + /* Clear all "wait"ing threads. */ + if (val) + sigproc_printf ("clear waiting threads"); + else + sigproc_printf ("looking for processes to reap"); + clearing = val; + + scan_wait: + /* Scan the linked list of wait()ing threads. If a wait's parameters + * match this pid, then activate it. + */ + for (w = &waitq_head; w->next != NULL; w = w->next) + { + if ((potential_match = checkstate (w)) > 0) + sigproc_printf ("released waiting thread"); + else if (!clearing && !(w->next->options & WNOHANG) && potential_match < 0) + sigproc_printf ("only found non-terminated children"); + else if (potential_match <= 0) // nothing matched + { + sigproc_printf ("waiting thread found no children"); + HANDLE oldw = w->next->ev; + w->next->pid = 0; + if (clearing) + w->next->status = -1; /* flag that a signal was received */ + else if (!potential_match || !(w->next->options & WNOHANG)) + w->next->ev = NULL; + if (!SetEvent (oldw)) + system_printf ("couldn't wake up wait event %p, %E", oldw); + w->next = w->next->next; + } + if (w->next == NULL) + break; + } + + if (!clearing) + sigproc_printf ("finished processing terminated/stopped child"); + else + { + waitq_head.next = NULL; + sigproc_printf ("finished clearing"); + } + + if (global_sigs[SIGCHLD].sa_handler == (void *) SIG_IGN) + while (nzombies) + remove_zombie (0); + break; + } + +out: + sync_proc_subproc->release (); // Release the lock +out1: + sigproc_printf ("returning %d", rc); + return rc; +} + +/* Terminate the wait_subproc thread. + * Called on process exit. + * Also called by spawn_guts to disassociate any subprocesses from this + * process. Subprocesses will then know to clean up after themselves and + * will not become zombies. + */ +void __stdcall +proc_terminate (void) +{ + sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies); + /* Signal processing is assumed to be blocked in this routine. */ + if (hwait_subproc) + { + proc_loop_wait = 0; // Tell wait_subproc thread to exit + sync_proc_subproc->acquire (WPSP); + wake_wait_subproc (); // Wake wait_subproc loop + hwait_subproc = NULL; + + (void) proc_subproc (PROC_CLEARWAIT, 1); + + /* Clean out zombie processes from the pid list. */ + int i; + for (i = 0; i < nzombies; i++) + { + if (zombies[i]->hProcess) + { + ForceCloseHandle1 (zombies[i]->hProcess, childhProc); + ForceCloseHandle1 (zombies[i]->pid_handle, pid_handle); + } + zombies[i]->ppid = 1; + zombies[i]->process_state = PID_EXITED; /* CGF FIXME - still needed? */ + zombies[i].release (); // FIXME: this breaks older gccs for some reason + } + + /* Disassociate my subprocesses */ + for (i = 0; i < nchildren; i++) + { + if (!pchildren[i]->hProcess) + sigproc_printf ("%d(%d) hProcess cleared already?", pchildren[i]->pid, + pchildren[i]->dwProcessId); + else + { + ForceCloseHandle1 (pchildren[i]->hProcess, childhProc); + sigproc_printf ("%d(%d) closed child handle", pchildren[i]->pid, + pchildren[i]->dwProcessId); + pchildren[i]->ppid = 1; + if (pchildren[i]->pgid == myself->pid) + pchildren[i]->process_state |= PID_ORPHANED; + } + pchildren[i].release (); + } + nchildren = nzombies = 0; + sync_proc_subproc = NULL; + } + sigproc_printf ("leaving"); +} + +/* Clear pending signal */ +void __stdcall +sig_clear (int target_sig) +{ + if (GetCurrentThreadId () != sigtid) + sig_send (myself, -target_sig); + else + { + sigqueue.reset (); + sigelem *q; + while ((q = sigqueue.next ())) + if (q->sig == target_sig) + { + sigqueue.del (); + break; + } + } + return; +} + +extern "C" int +sigpending (sigset_t *mask) +{ + sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING); + if (outset == SIG_BAD_MASK) + return -1; + *mask = outset; + return 0; +} + +/* Force the wait_sig thread to wake up and scan for pending signals */ +int __stdcall +sig_dispatch_pending () +{ + if (exit_state || GetCurrentThreadId () == sigtid || !sigqueue.start.next) + { +#ifdef DEBUGGING + sigproc_printf ("exit_state %d, GetCurrentThreadId () %p, sigtid %p, sigqueue.start.next %p", + exit_state, GetCurrentThreadId (), sigtid, sigqueue.start.next); +#endif + return 0; + } + +#ifdef DEBUGGING + sigproc_printf ("flushing"); +#endif + (void) sig_send (myself, __SIGFLUSH); + return call_signal_handler_now (); +} + +/* Message initialization. Called from dll_crt0_1 + * + * This routine starts the signal handling thread. The wait_sig_inited + * event is used to signal that the thread is ready to handle signals. + * We don't wait for this during initialization but instead detect it + * in sig_send to gain a little concurrency. + */ +void __stdcall +sigproc_init () +{ + wait_sig_inited = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (wait_sig_inited); + + /* sync_proc_subproc is used by proc_subproc. It serialises + * access to the children and zombie arrays. + */ + new_muto (sync_proc_subproc); + + /* local event signaled when main thread has been dispatched + to a signal handler function. */ + signal_arrived = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + ProtectHandle (signal_arrived); + + hwait_sig = new cygthread (wait_sig, cygself, "sig"); + hwait_sig->zap_h (); + + /* Initialize waitq structure for main thread. A waitq structure is + * allocated for each thread that executes a wait to allow multiple threads + * to perform waits. Pre-allocate a waitq structure for the main thread. + */ + waitq *w; + if ((w = (waitq *)waitq_storage.get ()) == NULL) + { + w = &waitq_main; + waitq_storage.set (w); + } + memset (w, 0, sizeof *w); // Just to be safe + + global_sigs[SIGSTOP].sa_flags = SA_RESTART | SA_NODEFER; + sigproc_printf ("process/signal handling enabled(%x)", myself->process_state); + return; +} + +/* Called on process termination to terminate signal and process threads. + */ +void __stdcall +sigproc_terminate (void) +{ + extern HANDLE hExeced; + hwait_sig = NULL; + + if (myself->sendsig == INVALID_HANDLE_VALUE) + sigproc_printf ("sigproc handling not active"); + else + { + sigproc_printf ("entering"); + // finished with anything it is doing + ForceCloseHandle (sigcomplete_main); + if (!hExeced) + { + HANDLE sendsig = myself->sendsig; + myself->sendsig = INVALID_HANDLE_VALUE; + CloseHandle (sendsig); + } + } + proc_terminate (); // Terminate process handling thread + + return; +} + +/* Send a signal to another process by raising its signal semaphore. + * If pinfo *p == NULL, send to the current process. + * If sending to this process, wait for notification that a signal has + * completed before returning. + */ +int __stdcall +sig_send (_pinfo *p, int sig, void *tls) +{ + int rc = 1; + bool its_me; + HANDLE sendsig; + sigpacket pack; + + bool wait_for_completion; + // FIXMENOW: Avoid using main thread's completion event! + if (!(its_me = (p == NULL || p == myself || p == myself_nowait))) + wait_for_completion = false; + else + { + if (no_signals_available ()) + { + sigproc_printf ("hwait_sig %p, myself->sendsig %p, exit_state %d", + hwait_sig, myself->sendsig, exit_state); + goto out; // Either exiting or not yet initializing + } + if (wait_sig_inited) + wait_for_sigthread (); + wait_for_completion = p != myself_nowait && _my_tls.isinitialized (); + p = myself; + } + + /* It is possible that the process is not yet ready to receive messages + * or that it has exited. Detect this. + */ + if (!proc_can_be_signalled (p)) /* Is the process accepting messages? */ + { + sigproc_printf ("invalid pid %d(%x), signal %d", + p->pid, p->process_state, sig); + goto out; + } + + if (its_me) + { + sendsig = myself->sendsig; + if (wait_for_completion) + pack.wakeup = sigcomplete_main; + } + else + { + HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId); + if (!hp) + { + sigproc_printf ("OpenProcess failed, %E"); + __seterrno (); + goto out; + } + for (int i = 0; !p->sendsig && i < 10000; i++) + low_priority_sleep (0); + if (!DuplicateHandle (hp, p->sendsig, hMainProc, &sendsig, false, 0, + DUPLICATE_SAME_ACCESS) || !sendsig) + { + sigproc_printf ("DuplicateHandle failed, %E"); + __seterrno (); + goto out; + } + CloseHandle (hp); + pack.wakeup = NULL; + } + + sigproc_printf ("sendsig %p, pid %d, signal %d, its_me %d", sendsig, p->pid, + sig, its_me); + + sigset_t pending; + if (!its_me) + pack.mask = NULL; + else if (sig == __SIGPENDING) + pack.mask = &pending; + else if (sig == __SIGFLUSH || sig > 0) + pack.mask = &myself->getsigmask (); + else + pack.mask = NULL; + + pack.sig = sig; + pack.pid = myself->pid; + pack.tls = (_threadinfo *) tls; + DWORD nb; + if (!WriteFile (sendsig, &pack, sizeof (pack), &nb, NULL) || nb != sizeof (pack)) + { + /* Couldn't send to the pipe. This probably means that the + process is exiting. */ + if (!its_me) + { + sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig); + __seterrno (); + ForceCloseHandle (sendsig); + } + else + { + if (no_signals_available ()) + sigproc_printf ("I'm going away now"); + else + system_printf ("error sending signal %d to pid %d, pipe handle %p, %E", + sig, p->pid, sendsig); + } + goto out; + } + + + /* No need to wait for signal completion unless this was a signal to + this process. + + If it was a signal to this process, wait for a dispatched signal. + Otherwise just wait for the wait_sig to signal that it has finished + processing the signal. */ + if (wait_for_completion) + { + sigproc_printf ("Waiting for pack.wakeup %p", pack.wakeup); + rc = WaitForSingleObject (pack.wakeup, WSSC); + } + else + { + rc = WAIT_OBJECT_0; + sigproc_printf ("Not waiting for sigcomplete. its_me %d signal %d", its_me, sig); + if (!its_me) + ForceCloseHandle (sendsig); + } + + if (rc == WAIT_OBJECT_0) + rc = 0; // Successful exit + else + { + if (!no_signals_available ()) + system_printf ("wait for sig_complete event failed, signal %d, rc %d, %E", + sig, rc); + set_errno (ENOSYS); + rc = -1; + } + + if (wait_for_completion) + call_signal_handler_now (); + +out: + if (sig != __SIGPENDING) + /* nothing */; + else if (!rc) + rc = (int) pending; + else + rc = SIG_BAD_MASK; + sigproc_printf ("returning %p from sending signal %d", rc, sig); + return rc; +} + +/* Initialize the wait_subproc thread. + * Called from fork() or spawn() to initialize the handling of subprocesses. + */ +void __stdcall +subproc_init (void) +{ + if (hwait_subproc) + return; + + /* A "wakeup" handle which can be toggled to make wait_subproc reexamine + * the hchildren array. + */ + events[0] = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + hwait_subproc = new cygthread (wait_subproc, NULL, "proc"); + hwait_subproc->zap_h (); + ProtectHandle (events[0]); + sigproc_printf ("started wait_subproc thread"); +} + +/* Initialize some of the memory block passed to child processes + by fork/spawn/exec. */ + +void __stdcall +init_child_info (DWORD chtype, child_info *ch, pid_t pid, HANDLE subproc_ready) +{ + memset (ch, 0, sizeof *ch); + ch->cb = chtype == PROC_FORK ? sizeof (child_info_fork) : sizeof (child_info); + ch->intro = PROC_MAGIC_GENERIC; + ch->magic = CHILD_INFO_MAGIC; + ch->type = chtype; + ch->cygpid = pid; + ch->subproc_ready = subproc_ready; + ch->pppid_handle = myself->ppid_handle; + ch->fhandler_union_cb = sizeof (fhandler_union); + ch->user_h = cygwin_user_h; +} + +/* Check the state of all of our children to see if any are stopped or + * terminated. + */ +static int __stdcall +checkstate (waitq *parent_w) +{ + int potential_match = 0; + + sigproc_printf ("nchildren %d, nzombies %d", nchildren, nzombies); + + /* Check already dead processes first to see if they match the criteria + * given in w->next. + */ + for (int i = 0; i < nzombies; i++) + switch (stopped_or_terminated (parent_w, zombies[i])) + { + case -1: + potential_match = -1; + break; + case 1: + remove_zombie (i); + potential_match = 1; + goto out; + } + + sigproc_printf ("checking alive children"); + + /* No dead terminated children matched. Check for stopped children. */ + for (int i = 0; i < nchildren; i++) + switch (stopped_or_terminated (parent_w, pchildren[i])) + { + case -1: + potential_match = -1; + break; + case 1: + potential_match = 1; + goto out; + } + +out: + sigproc_printf ("returning %d", potential_match); + return potential_match; +} + +/* Remove a zombie from zombies by swapping it with the last child in the list. + */ +static void __stdcall +remove_zombie (int ci) +{ + sigproc_printf ("removing %d, pid %d, nzombies %d", ci, zombies[ci]->pid, + nzombies); + + if (zombies[ci]) + { + ForceCloseHandle1 (zombies[ci]->hProcess, childhProc); + ForceCloseHandle1 (zombies[ci]->pid_handle, pid_handle); + zombies[ci].release (); + } + + if (ci < --nzombies) + zombies[ci] = zombies[nzombies]; + + return; +} + +/* Check status of child process vs. waitq member. + * + * parent_w is the pointer to the parent of the waitq member in question. + * child is the subprocess being considered. + * + * Returns + * 1 if stopped or terminated child matches parent_w->next criteria + * -1 if a non-stopped/terminated child matches parent_w->next criteria + * 0 if child does not match parent_w->next criteria + */ +static int __stdcall +stopped_or_terminated (waitq *parent_w, _pinfo *child) +{ + int potential_match; + waitq *w = parent_w->next; + + sigproc_printf ("considering pid %d", child->pid); + if (w->pid == -1) + potential_match = 1; + else if (w->pid == 0) + potential_match = child->pgid == myself->pgid; + else if (w->pid < 0) + potential_match = child->pgid == -w->pid; + else + potential_match = (w->pid == child->pid); + + if (!potential_match) + return 0; + + bool terminated; + + if ((terminated = child->process_state == PID_ZOMBIE) || + ((w->options & WUNTRACED) && child->stopsig)) + { + parent_w->next = w->next; /* successful wait. remove from wait queue */ + w->pid = child->pid; + + if (!terminated) + { + sigproc_printf ("stopped child"); + w->status = (child->stopsig << 8) | 0x7f; + child->stopsig = 0; + } + else /* Should only get here when child has been moved to the zombies array */ + { + DWORD status; + if (!GetExitCodeProcess (child->hProcess, &status)) + status = 0xffff; + if (status & EXIT_SIGNAL) + w->status = (status >> 8) & 0xff; /* exited due to signal */ + else + w->status = (status & 0xff) << 8; /* exited via "exit ()" */ + + add_rusage (&myself->rusage_children, &child->rusage_children); + add_rusage (&myself->rusage_children, &child->rusage_self); + + if (w->rusage) + { + add_rusage ((struct rusage *) w->rusage, &child->rusage_children); + add_rusage ((struct rusage *) w->rusage, &child->rusage_self); + } + } + + if (!SetEvent (w->ev)) /* wake up wait4 () immediately */ + system_printf ("couldn't wake up wait event %p, %E", w->ev); + return 1; + } + + return -potential_match; +} + +static void +talktome () +{ + winpids pids ((DWORD) PID_MAP_RW); + for (unsigned i = 0; i < pids.npids; i++) + if (pids[i]->hello_pid == myself->pid) + if (!IsBadWritePtr (pids[i], sizeof (_pinfo))) + pids[i]->commune_recv (); +} + +/* Process signals by waiting for a semaphore to become signaled. + Then scan an in-memory array representing queued signals. + Executes in a separate thread. + + Signals sent from this process are sent a completion signal so + that returns from kill/raise do not occur until the signal has + has been handled, as per POSIX. */ + +void +pending_signals::add (int sig, int pid, _threadinfo *tls) +{ + sigelem *se; + for (se = start.next; se; se = se->next) + if (se->sig == sig) + return; + while (sigs[empty].sig) + if (++empty == NSIG) + empty = 0; + se = sigs + empty; + se->sig = sig; + se->next = NULL; + se->tls = tls; + se->pid = pid; + if (end) + end->next = se; + end = se; + if (!start.next) + start.next = se; + empty++; +} + +void +pending_signals::del () +{ + sigelem *next = curr->next; + prev->next = next; + curr->sig = 0; +#ifdef DEBUGGING + curr->next = NULL; +#endif + if (end == curr) + end = prev; + empty = curr - sigs; + curr = next; +} + +sigelem * +pending_signals::next () +{ + sigelem *res; + prev = curr; + if (!curr || !(curr = curr->next)) + res = NULL; + else + res = curr; + return res; +} + +/* Process signals by waiting for signal data to arrive in a pipe. + Set a completion event if one was specified. */ +static DWORD WINAPI +wait_sig (VOID *self) +{ + HANDLE readsig; + char sa_buf[1024]; + + /* Initialization */ + (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); + + /* sigcomplete_main - event used to signal main thread on signal + completion */ + if (!CreatePipe (&readsig, &myself->sendsig, sec_user_nih (sa_buf), 0)) + api_fatal ("couldn't create signal pipe, %E"); + sigcomplete_main = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + sigproc_printf ("sigcomplete_main %p", sigcomplete_main); + sigCONT = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); + + /* Setting dwProcessId flags that this process is now capable of receiving + signals. Prior to this, dwProcessId was set to the windows pid of + of the original windows process which spawned us unless this was a + "toplevel" process. */ + myself->dwProcessId = GetCurrentProcessId (); + myself->process_state |= PID_ACTIVE; + myself->process_state &= ~PID_INITIALIZING; + + ProtectHandle (sigcomplete_main); + + /* If we've been execed, then there is still a stub left in the previous + windows process waiting to see if it's started a cygwin process or not. + Signalling subproc_ready indicates that we are a cygwin process. */ + if (child_proc_info && child_proc_info->type == PROC_EXEC) + { + debug_printf ("subproc_ready %p", child_proc_info->subproc_ready); + if (!SetEvent (child_proc_info->subproc_ready)) + system_printf ("SetEvent (subproc_ready) failed, %E"); + ForceCloseHandle1 (child_proc_info->subproc_ready, subproc_ready); + /* Initialize an "indirect" pid block so that if someone looks up this + process via its Windows PID it will be redirected to the appropriate + Cygwin PID shared memory block. */ + static pinfo NO_COPY myself_identity; + myself_identity.init (cygwin_pid (myself->dwProcessId), PID_EXECED); + } + + SetEvent (wait_sig_inited); + sigtid = GetCurrentThreadId (); + + exception_list el; + _my_tls.init_threadlist_exceptions (&el); + debug_printf ("entering ReadFile loop, readsig %p, myself->sendsig %p", + readsig, myself->sendsig); + + for (;;) + { + DWORD nb; + sigpacket pack; + if (!ReadFile (readsig, &pack, sizeof (pack), &nb, NULL)) + break; + if (myself->sendsig == INVALID_HANDLE_VALUE) + break; + + if (nb != sizeof (pack)) + { + system_printf ("short read from signal pipe: %d != %d", nb, + sizeof (pack)); + continue; + } + + if (!pack.sig) + { +#ifdef DEBUGGING + system_printf ("zero signal?"); +#endif + continue; + } + + sigset_t dummy_mask; + if (!pack.mask) + { + dummy_mask = myself->getsigmask (); + pack.mask = &dummy_mask; + } + + sigelem *q; + switch (pack.sig) + { + case __SIGCOMMUNE: + talktome (); + break; + case __SIGSTRACE: + strace.hello (); + break; + case __SIGPENDING: + *pack.mask = 0; + unsigned bit; + sigqueue.reset (); + while ((q = sigqueue.next ())) + if (myself->getsigmask () & (bit = SIGTOMASK (q->sig))) + *pack.mask |= bit; + break; + case __SIGFLUSH: + sigqueue.reset (); + while ((q = sigqueue.next ())) + if (sig_handle (q->sig, *pack.mask, q->pid, q->tls) > 0) + sigqueue.del (); + break; + default: + if (pack.sig < 0) + sig_clear (-pack.sig); + else + { + int sigres = sig_handle (pack.sig, *pack.mask, pack.pid, pack.tls); + if (sigres <= 0) + { +#ifdef DEBUGGING2 + if (!sigres) + system_printf ("Failed to arm signal %d from pid %d", pack.sig, pack.pid); +#endif + sigqueue.add (pack.sig, pack.pid, pack.tls);// FIXME: Shouldn't add this in !sh condition + } + if (pack.sig == SIGCHLD) + proc_subproc (PROC_CLEARWAIT, 0); + } + break; + } + if (pack.wakeup) + SetEvent (pack.wakeup); + } + + sigproc_printf ("done"); + ExitThread (0); +} + +/* Wait for subprocesses to terminate. Executes in a separate thread. */ +static DWORD WINAPI +wait_subproc (VOID *) +{ + sigproc_printf ("starting"); + int errloop = 0; + + for (;;) + { + DWORD rc = WaitForMultipleObjects (nchildren + 1, events, FALSE, + proc_loop_wait); + if (rc == WAIT_TIMEOUT) + if (!proc_loop_wait) + break; // Exiting + else + continue; + + if (rc == WAIT_FAILED) + { + if (!proc_loop_wait) + break; + + /* It's ok to get an ERROR_INVALID_HANDLE since another thread may have + closed a handle in the children[] array. So, we try looping a couple + of times to stabilize. FIXME - this is not foolproof. Probably, this + thread should be responsible for closing the children. */ + if (!errloop++) + proc_subproc (PROC_NOTHING, 0); // Just synchronize and continue + if (errloop < 10) + continue; + + system_printf ("wait failed. nchildren %d, wait %d, %E", + nchildren, proc_loop_wait); + + for (int i = 0; i <= nchildren; i++) + if ((rc = WaitForSingleObject (events[i], 0)) == WAIT_OBJECT_0 || + rc == WAIT_TIMEOUT) + continue; + else if (i == 0) + system_printf ("nchildren %d, event[%d] %p, %E", nchildren, i, events[i]); + else + { + system_printf ("nchildren %d, event[%d] %p, pchildren[%d] %p, events[0] %p, %E", + nchildren, i, events[i], i - 1, (_pinfo *) pchildren[i - 1], events[0]); + system_printf ("pid %d, dwProcessId %u, hProcess %p, progname '%s'", + pchildren[i - 1]->pid, pchildren[i - 1]->dwProcessId, + pchildren[i - 1]->hProcess, pchildren[i - 1]->progname); + } + break; + } + + errloop = 0; + rc -= WAIT_OBJECT_0; + if (rc-- != 0) + { + rc = proc_subproc (PROC_CHILDTERMINATED, rc); + if (!proc_loop_wait) // Don't bother if wait_subproc is + break; // exiting + + /* Send a SIGCHLD to myself. We do this here, rather than in proc_subproc + to avoid the proc_subproc lock since the signal thread will eventually + be calling proc_subproc and could unnecessarily block. */ + if (rc) + sig_send (myself_nowait, SIGCHLD); + } + sigproc_printf ("looping"); + } + + ForceCloseHandle (events[0]); + events[0] = NULL; + sigproc_printf ("done"); + ExitThread (0); +} diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h new file mode 100644 index 00000000000..66ab87e2759 --- /dev/null +++ b/winsup/cygwin/wincap.h @@ -0,0 +1,121 @@ +/* wincap.h: Header for OS capability class. + + Copyright 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 _WINCAP_H +#define _WINCAP_H + +struct wincaps +{ + DWORD lock_file_highword; + DWORD chunksize; + int shared; + unsigned is_winnt : 1; + unsigned is_server : 1; + unsigned access_denied_on_delete : 1; + unsigned has_delete_on_close : 1; + unsigned has_page_guard : 1; + unsigned has_security : 1; + unsigned has_security_descriptor_control : 1; + unsigned has_get_process_times : 1; + unsigned has_lseek_bug : 1; + unsigned has_lock_file_ex : 1; + unsigned has_signal_object_and_wait : 1; + unsigned has_eventlog : 1; + unsigned has_ip_helper_lib : 1; + unsigned has_set_handle_information : 1; + unsigned has_set_handle_information_on_console_handles: 1; + unsigned supports_smp : 1; + unsigned map_view_of_file_ex_sucks : 1; + unsigned altgr_is_ctrl_alt : 1; + unsigned has_physical_mem_access : 1; + unsigned has_working_copy_on_write : 1; + unsigned share_mmaps_only_by_name : 1; + unsigned virtual_protect_works_on_shared_pages : 1; + unsigned has_hard_links : 1; + unsigned can_open_directories : 1; + unsigned has_move_file_ex : 1; + unsigned has_negative_pids : 1; + unsigned has_unreliable_pipes : 1; + unsigned has_try_enter_critical_section : 1; + unsigned has_raw_devices : 1; + unsigned has_valid_processorlevel : 1; + unsigned has_64bit_file_access : 1; + unsigned has_process_io_counters : 1; + unsigned supports_reading_modem_output_lines : 1; + unsigned needs_memory_protection : 1; + unsigned pty_needs_alloc_console : 1; + unsigned has_terminal_services : 1; + unsigned has_switch_to_thread : 1; + unsigned cant_debug_dll_entry : 1; +}; + +class wincapc +{ + OSVERSIONINFOEX version; + char osnam[40]; + void *caps; + +public: + void init (); + + void set_chunksize (DWORD nchunksize); + + const char *osname () const { return osnam; } + +#define IMPLEMENT(cap) cap() const { return ((wincaps *) this->caps)->cap; } + + DWORD IMPLEMENT (lock_file_highword) + DWORD IMPLEMENT (chunksize) + int IMPLEMENT (shared) + bool IMPLEMENT (is_winnt) + bool IMPLEMENT (is_server) + bool IMPLEMENT (access_denied_on_delete) + bool IMPLEMENT (has_delete_on_close) + bool IMPLEMENT (has_page_guard) + bool IMPLEMENT (has_security) + bool IMPLEMENT (has_security_descriptor_control) + bool IMPLEMENT (has_get_process_times) + bool IMPLEMENT (has_lseek_bug) + bool IMPLEMENT (has_lock_file_ex) + bool IMPLEMENT (has_signal_object_and_wait) + bool IMPLEMENT (has_eventlog) + bool IMPLEMENT (has_ip_helper_lib) + bool IMPLEMENT (has_set_handle_information) + bool IMPLEMENT (has_set_handle_information_on_console_handles) + bool IMPLEMENT (supports_smp) + bool IMPLEMENT (map_view_of_file_ex_sucks) + bool IMPLEMENT (altgr_is_ctrl_alt) + bool IMPLEMENT (has_physical_mem_access) + bool IMPLEMENT (has_working_copy_on_write) + bool IMPLEMENT (share_mmaps_only_by_name) + bool IMPLEMENT (virtual_protect_works_on_shared_pages) + bool IMPLEMENT (has_hard_links) + bool IMPLEMENT (can_open_directories) + bool IMPLEMENT (has_move_file_ex) + bool IMPLEMENT (has_negative_pids) + bool IMPLEMENT (has_unreliable_pipes) + bool IMPLEMENT (has_try_enter_critical_section) + bool IMPLEMENT (has_raw_devices) + bool IMPLEMENT (has_valid_processorlevel) + bool IMPLEMENT (has_64bit_file_access) + bool IMPLEMENT (has_process_io_counters) + bool IMPLEMENT (supports_reading_modem_output_lines) + bool IMPLEMENT (needs_memory_protection) + bool IMPLEMENT (pty_needs_alloc_console) + bool IMPLEMENT (has_terminal_services) + bool IMPLEMENT (has_switch_to_thread) + bool IMPLEMENT (cant_debug_dll_entry) + +#undef IMPLEMENT +}; + +extern wincapc wincap; + +#endif /* _WINCAP_H */ |