Mercurial > hg > xemacs-beta
diff src/ntproc.c @ 100:4be1180a9e89 r20-1b2
Import from CVS: tag r20-1b2
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:15:11 +0200 |
parents | |
children | 9b50b4588a93 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ntproc.c Mon Aug 13 09:15:11 2007 +0200 @@ -0,0 +1,1269 @@ +/* Process support for Windows NT port of XEMACS. + Copyright (C) 1992, 1995 Free Software Foundation, Inc. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + + Drew Bliss Oct 14, 1993 + Adapted from alarm.c by Tim Fleehart */ + +/* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <io.h> +#include <fcntl.h> +#include <signal.h> + +/* must include CRT headers *before* config.h */ +#include "config.h" +#undef signal +#undef wait +#undef spawnve +#undef select +#undef kill + +#include <windows.h> + +#include "lisp.h" +#include "nt.h" +#include "systime.h" +#include "syswait.h" +#include "process.h" + +/* Control whether spawnve quotes arguments as necessary to ensure + correct parsing by child process. Because not all uses of spawnve + are careful about constructing argv arrays, we make this behaviour + conditional (off by default). */ +Lisp_Object Vwin32_quote_process_args; + +/* Time to sleep before reading from a subprocess output pipe - this + avoids the inefficiency of frequently reading small amounts of data. + This is primarily necessary for handling DOS processes on Windows 95, + but is useful for Win32 processes on both Win95 and NT as well. */ +Lisp_Object Vwin32_pipe_read_delay; + +/* Control conversion of upper case file names to lower case. + nil means no, t means yes. */ +Lisp_Object Vwin32_downcase_file_names; + +/* Keep track of whether we have already started a DOS program. */ +BOOL dos_process_running; + +#ifndef SYS_SIGLIST_DECLARED +extern char *sys_siglist[]; +#endif + +#ifdef EMACSDEBUG +void _DebPrint (const char *fmt, ...) +{ + char buf[1024]; + va_list args; + + va_start (args, fmt); + vsprintf (buf, fmt, args); + va_end (args); + OutputDebugString (buf); +} +#endif + +typedef void (_CALLBACK_ *signal_handler)(int); + +/* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */ +static signal_handler sig_handlers[NSIG]; + +/* Fake signal implementation to record the SIGCHLD handler. */ +signal_handler +sys_signal (int sig, signal_handler handler) +{ + signal_handler old; + + if (sig != SIGCHLD) + { + errno = EINVAL; + return SIG_ERR; + } + old = sig_handlers[sig]; + sig_handlers[sig] = handler; + return old; +} + +/* Defined in <process.h> which conflicts with the local copy */ +#define _P_NOWAIT 1 + +/* Child process management list. */ +int child_proc_count = 0; +child_process child_procs[ MAX_CHILDREN ]; +child_process *dead_child = NULL; + +DWORD WINAPI reader_thread (void *arg); + +/* Find an unused process slot. */ +child_process * +new_child (void) +{ + child_process *cp; + DWORD id; + + for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--) + if (!CHILD_ACTIVE (cp)) + goto Initialise; + if (child_proc_count == MAX_CHILDREN) + return NULL; + cp = &child_procs[child_proc_count++]; + + Initialise: + memset (cp, 0, sizeof(*cp)); + cp->fd = -1; + cp->pid = -1; + cp->procinfo.hProcess = NULL; + cp->status = STATUS_READ_ERROR; + + /* use manual reset event so that select() will function properly */ + cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL); + if (cp->char_avail) + { + cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL); + if (cp->char_consumed) + { + cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id); + if (cp->thrd) + return cp; + } + } + delete_child (cp); + return NULL; +} + +void +delete_child (child_process *cp) +{ + int i; + + /* Should not be deleting a child that is still needed. */ + for (i = 0; i < MAXDESC; i++) + if (fd_info[i].cp == cp) + abort (); + + if (!CHILD_ACTIVE (cp)) + return; + + /* reap thread if necessary */ + if (cp->thrd) + { + DWORD rc; + + if (GetExitCodeThread (cp->thrd, &rc) && rc == STILL_ACTIVE) + { + /* let the thread exit cleanly if possible */ + cp->status = STATUS_READ_ERROR; + SetEvent (cp->char_consumed); + if (WaitForSingleObject (cp->thrd, 1000) != WAIT_OBJECT_0) + { + DebPrint (("delete_child.WaitForSingleObject (thread) failed " + "with %lu for fd %ld\n", GetLastError (), cp->fd)); + TerminateThread (cp->thrd, 0); + } + } + CloseHandle (cp->thrd); + cp->thrd = NULL; + } + if (cp->char_avail) + { + CloseHandle (cp->char_avail); + cp->char_avail = NULL; + } + if (cp->char_consumed) + { + CloseHandle (cp->char_consumed); + cp->char_consumed = NULL; + } + + /* update child_proc_count (highest numbered slot in use plus one) */ + if (cp == child_procs + child_proc_count - 1) + { + for (i = child_proc_count-1; i >= 0; i--) + if (CHILD_ACTIVE (&child_procs[i])) + { + child_proc_count = i + 1; + break; + } + } + if (i < 0) + child_proc_count = 0; +} + +/* Find a child by pid. */ +static child_process * +find_child_pid (DWORD pid) +{ + child_process *cp; + + for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--) + if (CHILD_ACTIVE (cp) && pid == cp->pid) + return cp; + return NULL; +} + + +/* Thread proc for child process and socket reader threads. Each thread + is normally blocked until woken by select() to check for input by + reading one char. When the read completes, char_avail is signalled + to wake up the select emulator and the thread blocks itself again. */ +DWORD WINAPI +reader_thread (void *arg) +{ + child_process *cp; + + /* Our identity */ + cp = (child_process *)arg; + + /* We have to wait for the go-ahead before we can start */ + if (cp == NULL || + WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0) + return 1; + + for (;;) + { + int rc; + + rc = _sys_read_ahead (cp->fd); + + /* The name char_avail is a misnomer - it really just means the + read-ahead has completed, whether successfully or not. */ + if (!SetEvent (cp->char_avail)) + { + DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n", + GetLastError (), cp->fd)); + return 1; + } + + if (rc == STATUS_READ_ERROR) + return 1; + + /* If the read died, the child has died so let the thread die */ + if (rc == STATUS_READ_FAILED) + break; + + /* Wait until our input is acknowledged before reading again */ + if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0) + { + DebPrint (("reader_thread.WaitForSingleObject failed with " + "%lu for fd %ld\n", GetLastError (), cp->fd)); + break; + } + } + return 0; +} + +static BOOL +create_child (char *exe, char *cmdline, char *env, + int * pPid, child_process *cp) +{ + STARTUPINFO start; + SECURITY_ATTRIBUTES sec_attrs; + SECURITY_DESCRIPTOR sec_desc; + + if (cp == NULL) abort (); + + memset (&start, 0, sizeof (start)); + start.cb = sizeof (start); + +#ifdef HAVE_NTGUI + start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + start.wShowWindow = SW_HIDE; + + start.hStdInput = GetStdHandle (STD_INPUT_HANDLE); + start.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + start.hStdError = GetStdHandle (STD_ERROR_HANDLE); +#endif /* HAVE_NTGUI */ + + /* Explicitly specify no security */ + if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION)) + goto EH_Fail; + if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE)) + goto EH_Fail; + sec_attrs.nLength = sizeof (sec_attrs); + sec_attrs.lpSecurityDescriptor = &sec_desc; + sec_attrs.bInheritHandle = FALSE; + + if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE, + CREATE_NEW_PROCESS_GROUP, + env, NULL, + &start, &cp->procinfo)) + goto EH_Fail; + + cp->pid = (int) cp->procinfo.dwProcessId; + + /* Hack for Windows 95, which assigns large (ie negative) pids */ + if (cp->pid < 0) + cp->pid = -cp->pid; + + /* pid must fit in a Lisp_Int */ + cp->pid = (cp->pid & VALMASK); + + + *pPid = cp->pid; + + return TRUE; + + EH_Fail: + DebPrint (("create_child.CreateProcess failed: %ld\n", GetLastError());); + return FALSE; +} + +/* create_child doesn't know what emacs' file handle will be for waiting + on output from the child, so we need to make this additional call + to register the handle with the process + This way the select emulator knows how to match file handles with + entries in child_procs. */ +void +register_child (int pid, int fd) +{ + child_process *cp; + + cp = find_child_pid (pid); + if (cp == NULL) + { + DebPrint (("register_child unable to find pid %lu\n", pid)); + return; + } + +#ifdef FULL_DEBUG + DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid)); +#endif + + cp->fd = fd; + + /* thread is initially blocked until select is called; set status so + that select will release thread */ + cp->status = STATUS_READ_ACKNOWLEDGED; + + /* attach child_process to fd_info */ + if (fd_info[fd].cp != NULL) + { + DebPrint (("register_child: fd_info[%d] apparently in use!\n", fd)); + abort (); + } + + fd_info[fd].cp = cp; +} + +/* When a process dies its pipe will break so the reader thread will + signal failure to the select emulator. + The select emulator then calls this routine to clean up. + Since the thread signaled failure we can assume it is exiting. */ +static void +reap_subprocess (child_process *cp) +{ + if (cp->procinfo.hProcess) + { + /* Reap the process */ + if (WaitForSingleObject (cp->procinfo.hProcess, INFINITE) != WAIT_OBJECT_0) + DebPrint (("reap_subprocess.WaitForSingleObject (process) failed " + "with %lu for fd %ld\n", GetLastError (), cp->fd)); + CloseHandle (cp->procinfo.hProcess); + cp->procinfo.hProcess = NULL; + CloseHandle (cp->procinfo.hThread); + cp->procinfo.hThread = NULL; + + /* If this was a DOS process, indicate that it is now safe to + start a new one. */ + if (cp->is_dos_process) + dos_process_running = FALSE; + } + + /* For asynchronous children, the child_proc resources will be freed + when the last pipe read descriptor is closed; for synchronous + children, we must explicitly free the resources now because + register_child has not been called. */ + if (cp->fd == -1) + delete_child (cp); +} + +/* Wait for any of our existing child processes to die + When it does, close its handle + Return the pid and fill in the status if non-NULL. */ + +int +sys_wait (int *status) +{ + DWORD active, retval; + int nh; + int pid; + child_process *cp, *cps[MAX_CHILDREN]; + HANDLE wait_hnd[MAX_CHILDREN]; + + nh = 0; + if (dead_child != NULL) + { + /* We want to wait for a specific child */ + wait_hnd[nh] = dead_child->procinfo.hProcess; + cps[nh] = dead_child; + if (!wait_hnd[nh]) abort (); + nh++; + } + else + { + for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--) + /* some child_procs might be sockets; ignore them */ + if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess) + { + wait_hnd[nh] = cp->procinfo.hProcess; + cps[nh] = cp; + if (!wait_hnd[nh]) abort (); + nh++; + } + } + + if (nh == 0) + { + /* Nothing to wait on, so fail */ + errno = ECHILD; + return -1; + } + + active = WaitForMultipleObjects (nh, wait_hnd, FALSE, INFINITE); + if (active == WAIT_FAILED) + { + errno = EBADF; + return -1; + } + else if (active == WAIT_TIMEOUT) + { + /* Should never happen */ + errno = EINVAL; + return -1; + } + else if (active >= WAIT_OBJECT_0 && + active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) + { + active -= WAIT_OBJECT_0; + } + else if (active >= WAIT_ABANDONED_0 && + active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS) + { + active -= WAIT_ABANDONED_0; + } + + if (!GetExitCodeProcess (wait_hnd[active], &retval)) + { + DebPrint (("Wait.GetExitCodeProcess failed with %lu\n", + GetLastError ())); + retval = 1; + } + if (retval == STILL_ACTIVE) + { + /* Should never happen */ + DebPrint (("Wait.WaitForMultipleObjects returned an active process\n")); + errno = EINVAL; + return -1; + } + + /* Massage the exit code from the process to match the format expected + by the WIFSTOPPED et al macros in syswait.h. Only WIFSIGNALED and + WIFEXITED are supported; WIFSTOPPED doesn't make sense under NT. */ + + if (retval == STATUS_CONTROL_C_EXIT) + retval = SIGINT; + else + retval <<= 8; + + cp = cps[active]; + pid = cp->pid; +#ifdef FULL_DEBUG + DebPrint (("Wait signaled with process pid %d\n", cp->pid)); +#endif + + if (status) + { + *status = retval; + } + else if (synch_process_alive) + { + synch_process_alive = 0; + + /* Report the status of the synchronous process. */ + if (WIFEXITED (retval)) + synch_process_retcode = WRETCODE (retval); + else if (WIFSIGNALED (retval)) + { + int code = WTERMSIG (retval); + char *signame = 0; + + if (code < NSIG) + { + /* Suppress warning if the table has const char *. */ + signame = (char *) sys_siglist[code]; + } + if (signame == 0) + signame = "unknown"; + + synch_process_death = signame; + } + + reap_subprocess (cp); + } + + return pid; +} + +int +win32_is_dos_binary (char * filename) +{ + IMAGE_DOS_HEADER dos_header; + DWORD signature; + int fd; + int is_dos_binary = FALSE; + + fd = open (filename, O_RDONLY | O_BINARY, 0); + if (fd >= 0) + { + char * p = strrchr (filename, '.'); + + /* We can only identify DOS .com programs from the extension. */ + if (p && stricmp (p, ".com") == 0) + is_dos_binary = TRUE; + else if (p && stricmp (p, ".bat") == 0) + { + /* A DOS shell script - it appears that CreateProcess is happy + to accept this (somewhat surprisingly); presumably it looks + at COMSPEC to determine what executable to actually invoke. + Therefore, we have to do the same here as well. */ + p = getenv ("COMSPEC"); + if (p) + is_dos_binary = win32_is_dos_binary (p); + } + else + { + /* Look for DOS .exe signature - if found, we must also check + that it isn't really a 16- or 32-bit Windows exe, since + both formats start with a DOS program stub. Note that + 16-bit Windows executables use the OS/2 1.x format. */ + if (read (fd, &dos_header, sizeof (dos_header)) == sizeof (dos_header) + && dos_header.e_magic == IMAGE_DOS_SIGNATURE + && lseek (fd, dos_header.e_lfanew, SEEK_SET) != -1) + { + if (read (fd, &signature, sizeof (signature)) != sizeof (signature) + || (signature != IMAGE_NT_SIGNATURE && + LOWORD (signature) != IMAGE_OS2_SIGNATURE)) + is_dos_binary = TRUE; + } + } + close (fd); + } + + return is_dos_binary; +} + +/* We pass our process ID to our children by setting up an environment + variable in their environment. */ +char ppid_env_var_buffer[64]; + +/* When a new child process is created we need to register it in our list, + so intercept spawn requests. */ +int +sys_spawnve (int mode, char *cmdname, char **argv, char **envp) +{ + Lisp_Object program, full; + char *cmdline, *env, *parg, **targ; + int arglen; + int pid; + child_process *cp; + int is_dos_binary; + + /* We don't care about the other modes */ + if (mode != _P_NOWAIT) + { + errno = EINVAL; + return -1; + } + + /* Handle executable names without an executable suffix. */ + program = make_string (cmdname, strlen (cmdname)); + if (NILP (Ffile_executable_p (program))) + { + struct gcpro gcpro1; + + full = Qnil; + GCPRO1 (program); + locate_file (Vexec_path, program, EXEC_SUFFIXES, &full, 1); + UNGCPRO; + if (NILP (full)) + { + errno = EINVAL; + return -1; + } + cmdname = XSTRING (full)->_data; + argv[0] = cmdname; + } + + /* make sure cmdname is in DOS format */ + strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]); + unixtodos_filename (cmdname); + argv[0] = cmdname; + + /* Check if program is a DOS executable, and if so whether we are + allowed to start it. */ + is_dos_binary = win32_is_dos_binary (cmdname); + if (is_dos_binary && dos_process_running) + { + errno = EAGAIN; + return -1; + } + + /* we have to do some conjuring here to put argv and envp into the + form CreateProcess wants... argv needs to be a space separated/null + terminated list of parameters, and envp is a null + separated/double-null terminated list of parameters. + + Additionally, zero-length args and args containing whitespace need + to be wrapped in double quotes. Args containing embedded double + quotes (as opposed to enclosing quotes, which we leave alone) are + usually illegal (most Win32 programs do not implement escaping of + double quotes - sad but true, at least for programs compiled with + MSVC), but we will escape quotes anyway for those programs that can + handle it. The Win32 gcc library from Cygnus doubles quotes to + escape them, so we will use that convention. + + Since I have no idea how large argv and envp are likely to be + we figure out list lengths on the fly and allocate them. */ + + /* do argv... */ + arglen = 0; + targ = argv; + while (*targ) + { + char * p = *targ; + int add_quotes = 0; + + if (*p == 0) + add_quotes = 1; + while (*p) + if (*p++ == '"') + { + /* allow for embedded quotes to be doubled - we won't + actually double quotes that aren't embedded though */ + arglen++; + add_quotes = 1; + } + else if (*p == ' ' || *p == '\t') + add_quotes = 1; + if (add_quotes) + arglen += 2; + arglen += strlen (*targ++) + 1; + } + cmdline = alloca (arglen); + targ = argv; + parg = cmdline; + while (*targ) + { + char * p = *targ; + int add_quotes = 0; + + if (*p == 0) + add_quotes = 1; + + if (!NILP (Vwin32_quote_process_args)) + { + /* This is conditional because it sometimes causes more + problems than it solves, since argv arrays are not always + carefully constructed. M-x grep, for instance, passes the + whole command line as one argument, so it becomes + impossible to pass a regexp which contains spaces. */ + for ( ; *p; p++) + if (*p == ' ' || *p == '\t' || *p == '"') + add_quotes = 1; + } + if (add_quotes) + { + char * first; + char * last; + + p = *targ; + first = p; + last = p + strlen (p) - 1; + *parg++ = '"'; + while (*p) + { + if (*p == '"' && p > first && p < last) + *parg++ = '"'; /* double up embedded quotes only */ + *parg++ = *p++; + } + *parg++ = '"'; + } + else + { + strcpy (parg, *targ); + parg += strlen (*targ); + } + *parg++ = ' '; + targ++; + } + *--parg = '\0'; + + /* and envp... */ + arglen = 1; + targ = envp; + while (*targ) + { + arglen += strlen (*targ++) + 1; + } + sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d", + GetCurrentProcessId ()); + arglen += strlen (ppid_env_var_buffer) + 1; + + env = alloca (arglen); + targ = envp; + parg = env; + while (*targ) + { + strcpy (parg, *targ); + parg += strlen (*targ++); + *parg++ = '\0'; + } + strcpy (parg, ppid_env_var_buffer); + parg += strlen (ppid_env_var_buffer); + *parg++ = '\0'; + *parg = '\0'; + + cp = new_child (); + if (cp == NULL) + { + errno = EAGAIN; + return -1; + } + + /* Now create the process. */ + if (!create_child (cmdname, cmdline, env, &pid, cp)) + { + delete_child (cp); + errno = ENOEXEC; + return -1; + } + + if (is_dos_binary) + { + cp->is_dos_process = TRUE; + dos_process_running = TRUE; + } + + return pid; +} + +/* Emulate the select call + Wait for available input on any of the given rfds, or timeout if + a timeout is given and no input is detected + wfds and efds are not supported and must be NULL. */ + +#if 0 +/* From ntterm.c */ +extern HANDLE keyboard_handle; +#endif +/* From process.c */ +extern int proc_buffered_char[]; + +int +sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, + EMACS_TIME *timeout) +{ + SELECT_TYPE orfds; + DWORD timeout_ms; + int i, nh, nr; + DWORD active; + child_process *cp; + HANDLE wait_hnd[MAXDESC]; + int fdindex[MAXDESC]; /* mapping from wait handles back to descriptors */ + + /* If the descriptor sets are NULL but timeout isn't, then just Sleep. */ + if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL) + { + Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); + return 0; + } + + /* Otherwise, we only handle rfds, so fail otherwise. */ + if (rfds == NULL || wfds != NULL || efds != NULL) + { + errno = EINVAL; + return -1; + } + + orfds = *rfds; + FD_ZERO (rfds); + nr = 0; + + /* Build a list of handles to wait on. */ + nh = 0; + for (i = 0; i < nfds; i++) + if (FD_ISSET (i, &orfds)) + { + if (i == 0) + { +#if 0 + if (keyboard_handle) + { + /* Handle stdin specially */ + wait_hnd[nh] = keyboard_handle; + fdindex[nh] = i; + nh++; + } +#endif + + /* Check for any emacs-generated input in the queue since + it won't be detected in the wait */ + if (detect_input_pending ()) + { + FD_SET (i, rfds); + return 1; + } + } + else + { + /* Child process and socket input */ + cp = fd_info[i].cp; + if (cp) + { + int current_status = cp->status; + + if (current_status == STATUS_READ_ACKNOWLEDGED) + { + /* Tell reader thread which file handle to use. */ + cp->fd = i; + /* Wake up the reader thread for this process */ + cp->status = STATUS_READ_READY; + if (!SetEvent (cp->char_consumed)) + DebPrint (("nt_select.SetEvent failed with " + "%lu for fd %ld\n", GetLastError (), i)); + } + +#ifdef CHECK_INTERLOCK + /* slightly crude cross-checking of interlock between threads */ + + current_status = cp->status; + if (WaitForSingleObject (cp->char_avail, 0) == WAIT_OBJECT_0) + { + /* char_avail has been signalled, so status (which may + have changed) should indicate read has completed + but has not been acknowledged. */ + current_status = cp->status; + if (current_status != STATUS_READ_SUCCEEDED && + current_status != STATUS_READ_FAILED) + DebPrint (("char_avail set, but read not completed: status %d\n", + current_status)); + } + else + { + /* char_avail has not been signalled, so status should + indicate that read is in progress; small possibility + that read has completed but event wasn't yet signalled + when we tested it (because a context switch occurred + or if running on separate CPUs). */ + if (current_status != STATUS_READ_READY && + current_status != STATUS_READ_IN_PROGRESS && + current_status != STATUS_READ_SUCCEEDED && + current_status != STATUS_READ_FAILED) + DebPrint (("char_avail reset, but read status is bad: %d\n", + current_status)); + } +#endif + wait_hnd[nh] = cp->char_avail; + fdindex[nh] = i; + if (!wait_hnd[nh]) abort (); + nh++; +#ifdef FULL_DEBUG + DebPrint (("select waiting on child %d fd %d\n", + cp-child_procs, i)); +#endif + } + else + { + /* Unable to find something to wait on for this fd, skip */ + DebPrint (("sys_select: fd %ld is invalid! ignoring\n", i)); + abort (); + } + } + } + + /* Nothing to look for, so we didn't find anything */ + if (nh == 0) + { + if (timeout) + Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); + return 0; + } + + /* + Wait for input + If a child process dies while this is waiting, its pipe will break + so the reader thread will signal an error condition, thus, the wait + will wake up + */ + timeout_ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFINITE; + + active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms); + + if (active == WAIT_FAILED) + { + DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n", + nh, timeout_ms, GetLastError ())); + /* don't return EBADF - this causes wait_reading_process_input to + abort; WAIT_FAILED is returned when single-stepping under + Windows 95 after switching thread focus in debugger, and + possibly at other times. */ + errno = EINTR; + return -1; + } + else if (active == WAIT_TIMEOUT) + { + return 0; + } + else if (active >= WAIT_OBJECT_0 && + active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) + { + active -= WAIT_OBJECT_0; + } + else if (active >= WAIT_ABANDONED_0 && + active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS) + { + active -= WAIT_ABANDONED_0; + } + + /* Loop over all handles after active (now officially documented as + being the first signalled handle in the array). We do this to + ensure fairness, so that all channels with data available will be + processed - otherwise higher numbered channels could be starved. */ + do + { + if (fdindex[active] == 0) + { + /* Keyboard input available */ + FD_SET (0, rfds); + nr++; + } + else + { + /* must be a socket or pipe */ + int current_status; + + cp = fd_info[ fdindex[active] ].cp; + + /* Read ahead should have completed, either succeeding or failing. */ + FD_SET (fdindex[active], rfds); + nr++; + current_status = cp->status; + if (current_status != STATUS_READ_SUCCEEDED) + { + if (current_status != STATUS_READ_FAILED) + DebPrint (("internal error: subprocess pipe signalled " + "at the wrong time (status %d)\n!", current_status)); + + /* The child_process entry for a socket or pipe will be + freed when the last descriptor using it is closed; for + pipes, we call the SIGCHLD handler. */ + if (fd_info[ fdindex[active] ].flags & FILE_PIPE) + { + /* The SIGCHLD handler will do a Wait so we know it won't + return until the process is dead + We force Wait to only wait for this process to avoid it + picking up other children that happen to be dead but that + we haven't noticed yet + SIG_DFL for SIGCHLD is ignore? */ + if (sig_handlers[SIGCHLD] != SIG_DFL && + sig_handlers[SIGCHLD] != SIG_IGN) + { +#ifdef FULL_DEBUG + DebPrint (("select calling SIGCHLD handler for pid %d\n", + cp->pid)); +#endif + dead_child = cp; + sig_handlers[SIGCHLD] (SIGCHLD); + dead_child = NULL; + } + + /* Clean up the child process entry in the table */ + reap_subprocess (cp); + } + } + } + + /* Test for input on remaining channels. */ + while (++active < nh) + if (WaitForSingleObject (wait_hnd[active], 0) == WAIT_OBJECT_0) + break; + } while (active < nh); + + return nr; +} + +/* Substitute for certain kill () operations */ +int +sys_kill (int pid, int sig) +{ + child_process *cp; + HANDLE proc_hand; + int need_to_free = 0; + int rc = 0; + + /* Only handle signals that will result in the process dying */ + if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP) + { + errno = EINVAL; + return -1; + } + + cp = find_child_pid (pid); + if (cp == NULL) + { + proc_hand = OpenProcess (PROCESS_TERMINATE, 0, pid); + if (proc_hand == NULL) + { + errno = EPERM; + return -1; + } + need_to_free = 1; + } + else + { + proc_hand = cp->procinfo.hProcess; + pid = cp->procinfo.dwProcessId; + } + + if (sig == SIGINT) + { + /* Ctrl-Break is NT equivalent of SIGINT. */ + if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid)) + { + DebPrint (("sys_kill.GenerateConsoleCtrlEvent return %d " + "for pid %lu\n", GetLastError (), pid)); + errno = EINVAL; + rc = -1; + } + } + else + { + /* Kill the process. On Win32 this doesn't kill child processes + so it doesn't work very well for shells which is why it's not + used in every case. Also, don't try to terminate DOS processes + (on Win95), because this will hang Emacs. */ + if (!(cp && cp->is_dos_process) + && !TerminateProcess (proc_hand, 0xff)) + { + DebPrint (("sys_kill.TerminateProcess returned %d " + "for pid %lu\n", GetLastError (), pid)); + errno = EINVAL; + rc = -1; + } + } + + if (need_to_free) + CloseHandle (proc_hand); + + return rc; +} + +#if 0 +extern int report_file_error (CONST char *, Lisp_Object); +#endif +/* The following two routines are used to manipulate stdin, stdout, and + stderr of our child processes. + + Assuming that in, out, and err are *not* inheritable, we make them + stdin, stdout, and stderr of the child as follows: + + - Save the parent's current standard handles. + - Set the std handles to inheritable duplicates of the ones being passed in. + (Note that _get_osfhandle() is an io.h procedure that retrieves the + NT file handle for a crt file descriptor.) + - Spawn the child, which inherits in, out, and err as stdin, + stdout, and stderr. (see Spawnve) + - Close the std handles passed to the child. + - Reset the parent's standard handles to the saved handles. + (see reset_standard_handles) + We assume that the caller closes in, out, and err after calling us. */ + +void +prepare_standard_handles (int in, int out, int err, HANDLE handles[3]) +{ + HANDLE parent; + HANDLE newstdin, newstdout, newstderr; + + parent = GetCurrentProcess (); + + handles[0] = GetStdHandle (STD_INPUT_HANDLE); + handles[1] = GetStdHandle (STD_OUTPUT_HANDLE); + handles[2] = GetStdHandle (STD_ERROR_HANDLE); + + /* make inheritable copies of the new handles */ + if (!DuplicateHandle (parent, + (HANDLE) _get_osfhandle (in), + parent, + &newstdin, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + report_file_error ("Duplicating input handle for child", Qnil); + + if (!DuplicateHandle (parent, + (HANDLE) _get_osfhandle (out), + parent, + &newstdout, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + report_file_error ("Duplicating output handle for child", Qnil); + + if (!DuplicateHandle (parent, + (HANDLE) _get_osfhandle (err), + parent, + &newstderr, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + report_file_error ("Duplicating error handle for child", Qnil); + + /* and store them as our std handles */ + if (!SetStdHandle (STD_INPUT_HANDLE, newstdin)) + report_file_error ("Changing stdin handle", Qnil); + + if (!SetStdHandle (STD_OUTPUT_HANDLE, newstdout)) + report_file_error ("Changing stdout handle", Qnil); + + if (!SetStdHandle (STD_ERROR_HANDLE, newstderr)) + report_file_error ("Changing stderr handle", Qnil); +} + +void +reset_standard_handles (int in, int out, int err, HANDLE handles[3]) +{ + /* close the duplicated handles passed to the child */ + CloseHandle (GetStdHandle (STD_INPUT_HANDLE)); + CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE)); + CloseHandle (GetStdHandle (STD_ERROR_HANDLE)); + + /* now restore parent's saved std handles */ + SetStdHandle (STD_INPUT_HANDLE, handles[0]); + SetStdHandle (STD_OUTPUT_HANDLE, handles[1]); + SetStdHandle (STD_ERROR_HANDLE, handles[2]); +} + +#ifdef HAVE_SOCKETS + +/* To avoid problems with winsock implementations that work over dial-up + connections causing or requiring a connection to exist while Emacs is + running, Emacs no longer automatically loads winsock on startup if it + is present. Instead, it will be loaded when open-network-stream is + first called. + + To allow full control over when winsock is loaded, we provide these + two functions to dynamically load and unload winsock. This allows + dial-up users to only be connected when they actually need to use + socket services. */ + +/* From nt.c */ +extern HANDLE winsock_lib; +extern BOOL term_winsock (void); +extern BOOL init_winsock (int load_now); + +extern Lisp_Object Vsystem_name; + +DEFUN ("win32-has-winsock", Fwin32_has_winsock, 0, 1, "", /* +Test for presence of the Windows socket library `winsock'. +Returns non-nil if winsock support is present, nil otherwise. + +If the optional argument LOAD-NOW is non-nil, the winsock library is +also loaded immediately if not already loaded. If winsock is loaded, +the winsock local hostname is returned (since this may be different from +the value of `system-name' and should supplant it), otherwise t is +returned to indicate winsock support is present. +*/ + (load_now)) +{ + int have_winsock; + + have_winsock = init_winsock (!NILP (load_now)); + if (have_winsock) + { + if (winsock_lib != NULL) + { + /* Return new value for system-name. The best way to do this + is to call init_system_name, saving and restoring the + original value to avoid side-effects. */ + Lisp_Object orig_hostname = Vsystem_name; + Lisp_Object hostname; + + init_system_name (); + hostname = Vsystem_name; + Vsystem_name = orig_hostname; + return hostname; + } + return Qt; + } + return Qnil; +} + +DEFUN ("win32-unload-winsock", Fwin32_unload_winsock, 0, 0, "", /* +Unload the Windows socket library `winsock' if loaded. +This is provided to allow dial-up socket connections to be disconnected +when no longer needed. Returns nil without unloading winsock if any +socket connections still exist. +*/ + ()) +{ + return term_winsock () ? Qt : Qnil; +} + +#endif /* HAVE_SOCKETS */ + + +syms_of_ntproc () +{ +#ifdef HAVE_SOCKETS + DEFSUBR (Fwin32_has_winsock); + DEFSUBR (Fwin32_unload_winsock); +#endif + + DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args /* +Non-nil enables quoting of process arguments to ensure correct parsing. +Because Windows does not directly pass argv arrays to child processes, +programs have to reconstruct the argv array by parsing the command +line string. For an argument to contain a space, it must be enclosed +in double quotes or it will be parsed as multiple arguments. + +However, the argument list to call-process is not always correctly +constructed (or arguments have already been quoted), so enabling this +option may cause unexpected behavior.*/ ); + Vwin32_quote_process_args = Qnil; + + DEFVAR_INT ("win32-pipe-read-delay", &Vwin32_pipe_read_delay /* +Forced delay before reading subprocess output. +This is done to improve the buffering of subprocess output, by +avoiding the inefficiency of frequently reading small amounts of data. + +If positive, the value is the number of milliseconds to sleep before +reading the subprocess output. If negative, the magnitude is the number +of time slices to wait (effectively boosting the priority of the child +process temporarily). A value of zero disables waiting entirely.*/ ); + Vwin32_pipe_read_delay = 50; + + DEFVAR_LISP ("win32-downcase-file-names", &Vwin32_downcase_file_names /* +Non-nil means convert all-upper case file names to lower case. +This applies when performing completions and file name expansion.*/ ); + Vwin32_downcase_file_names = Qnil; +} +/* end of ntproc.c */