/erts/emulator/sys/win32/sys.c
C | 3428 lines | 3075 code | 146 blank | 207 comment | 199 complexity | b34ffb891953f3c9b392fe7b2a669ab2 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-2-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2010. All Rights Reserved.
- *
- * The contents of this file are subject to the Erlang Public License,
- * Version 1.1, (the "License"); you may not use this file except in
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
- /*
- * system-dependent functions
- *
- */
- #ifdef HAVE_CONFIG_H
- # include "config.h"
- #endif
- #include "sys.h"
- #include "erl_alloc.h"
- #include "erl_sys_driver.h"
- #include "global.h"
- #include "erl_threads.h"
- #include "../../drivers/win32/win_con.h"
- #include "erl_cpu_topology.h"
- void erts_sys_init_float(void);
- void erl_start(int, char**);
- void erl_exit(int n, char*, ...);
- void erl_error(char*, va_list);
- void erl_crash_dump(char*, int, char*, ...);
- /*
- * Microsoft-specific function to map a WIN32 error code to a Posix errno.
- */
- extern void _dosmaperr(DWORD);
- #ifdef ERL_RUN_SHARED_LIB
- #ifdef __argc
- #undef __argc
- #endif
- #define __argc e_argc
- #ifdef __argv
- #undef __argv
- #endif
- #define __argv e_argv
- #endif
- static void init_console();
- static int get_and_remove_option(int* argc, char** argv, const char* option);
- static char *get_and_remove_option2(int *argc, char **argv,
- const char *option);
- static int init_async_io(struct async_io* aio, int use_threads);
- static void release_async_io(struct async_io* aio, ErlDrvPort);
- static void async_read_file(struct async_io* aio, LPVOID buf, DWORD numToRead);
- static int async_write_file(struct async_io* aio, LPVOID buf, DWORD numToWrite);
- static int get_overlapped_result(struct async_io* aio,
- LPDWORD pBytesRead, BOOL wait);
- static BOOL create_child_process(char *, HANDLE, HANDLE,
- HANDLE, LPHANDLE, BOOL,
- LPVOID, LPTSTR, unsigned,
- char **, int *);
- static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
- static int application_type(const char* originalName, char fullPath[MAX_PATH],
- BOOL search_in_path, BOOL handle_quotes,
- int *error_return);
- static int application_type_w(const char* originalName, WCHAR fullPath[MAX_PATH],
- BOOL search_in_path, BOOL handle_quotes,
- int *error_return);
- HANDLE erts_service_event;
- #ifdef ERTS_SMP
- static erts_smp_tsd_key_t win32_errstr_key;
- #endif
- static erts_smp_atomic_t pipe_creation_counter;
- static erts_smp_mtx_t sys_driver_data_lock;
- /* Results from application_type(_w) is one of */
- #define APPL_NONE 0
- #define APPL_DOS 1
- #define APPL_WIN3X 2
- #define APPL_WIN32 3
- static int driver_write(long, HANDLE, byte*, int);
- static void common_stop(int);
- static int create_file_thread(struct async_io* aio, int mode);
- #ifdef ERTS_SMP
- static void close_active_handle(ErlDrvPort, HANDLE handle);
- static DWORD WINAPI threaded_handle_closer(LPVOID param);
- #endif
- static DWORD WINAPI threaded_reader(LPVOID param);
- static DWORD WINAPI threaded_writer(LPVOID param);
- static DWORD WINAPI threaded_exiter(LPVOID param);
- #ifdef DEBUG
- static void debug_console(void);
- #endif
- BOOL WINAPI ctrl_handler(DWORD dwCtrlType);
- #define PORT_BUFSIZ 4096
- #define PORT_FREE (-1)
- #define PORT_EXITING (-2)
- #define DRV_BUF_ALLOC(SZ) \
- erts_alloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (SZ))
- #define DRV_BUF_REALLOC(P, SZ) \
- erts_realloc_fnf(ERTS_ALC_T_DRV_DATA_BUF, (P), (SZ))
- #define DRV_BUF_FREE(P) \
- erts_free(ERTS_ALC_T_DRV_DATA_BUF, (P))
- /********************* General functions ****************************/
- /*
- * Whether create_pipe() should use a named pipe or an anonymous.
- * (Named pipes are not supported on Windows 95.)
- */
- static int max_files = 1024;
- static BOOL use_named_pipes;
- static BOOL win_console = FALSE;
- static OSVERSIONINFO int_os_version; /* Version information for Win32. */
- /*#define USE_CANCELIOEX
- Disabled the use of CancelIoEx as its been seen to cause problem with some
- drivers. Not sure what to blame; faulty drivers or some form of invalid use.
- */
- #if defined(ERTS_SMP) && defined(USE_CANCELIOEX)
- static BOOL (WINAPI *fpCancelIoEx)(HANDLE,LPOVERLAPPED);
- #endif
- /* This is the system's main function (which may or may not be called "main")
- - do general system-dependent initialization
- - call erl_start() to parse arguments and do other init
- */
- static erts_smp_atomic_t sys_misc_mem_sz;
- HMODULE beam_module = NULL;
- void erl_sys_init();
- void erl_sys_args(int* argc, char** argv);
- int nohup;
- #ifndef __GNUC__
- void erts_sys_invalid_parameter_handler(const wchar_t * expression,
- const wchar_t * function,
- const wchar_t * file,
- unsigned int line,
- uintptr_t pReserved
- )
- {
- #ifdef DEBUG
- fprintf(stderr,
- "Debug: Invalid parameter\"%ls\" "
- "(detected in \"%ls\" [%ls:%d]) \n",
- (expression) ? expression : L"(unknown)",
- (function) ? function : L"(unknown)",
- (file) ? file : L"(unknown)",
- line);
- #endif
- return;
- }
- #endif
- void sys_primitive_init(HMODULE beam)
- {
- #ifndef __GNUC__
- /* Initialize this module handle (the beam.dll module handle) and
- take care of the standard library's aggressive invalid parameter
- handling... */
- _set_invalid_parameter_handler(&erts_sys_invalid_parameter_handler);
- #endif
- beam_module = (HMODULE) beam;
- }
- Uint
- erts_sys_misc_mem_sz(void)
- {
- Uint res = (Uint) erts_check_io_size();
- res += (Uint) erts_smp_atomic_read(&sys_misc_mem_sz);
- return res;
- }
- /*
- * Reset the terminal to the original settings on exit
- */
- void sys_tty_reset(int exit_code)
- {
- if (exit_code > 0)
- ConWaitForExit();
- else
- ConNormalExit();
- }
- void erl_sys_args(int* argc, char** argv)
- {
- char *event_name;
- nohup = get_and_remove_option(argc, argv, "-nohup");
- #ifdef DEBUG
- /*
- * Start a debug console if -console option given.
- */
- if (get_and_remove_option(argc, argv, "-console")) {
- debug_console();
- }
- #endif
- if (nohup && (event_name = get_and_remove_option2(argc, argv,
- "-service_event"))) {
- if ((erts_service_event =
- OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
- erts_fprintf(stderr,
- "Warning: could not open service event: %s\r\n",
- event_name);
- }
- } else {
- erts_service_event = NULL;
- }
- #ifdef DEBUG
- /*
- * Given the "-threads" option, always use threads instead of
- * named pipes.
- */
- if (get_and_remove_option(argc, argv, "-threads")) {
- use_named_pipes = FALSE;
- }
- #endif
- }
- void
- erts_sys_prepare_crash_dump(void)
- {
- /* Windows - free file descriptors are hopefully available */
- return;
- }
- static void
- init_console()
- {
- char* mode = erts_read_env("ERL_CONSOLE_MODE");
- if (!mode || strcmp(mode, "window") == 0) {
- win_console = TRUE;
- ConInit();
- /*nohup = 0;*/
- } else if (strncmp(mode, "tty:", 4) == 0) {
- if (mode[5] == 'c') {
- setvbuf(stdout, NULL, _IONBF, 0);
- }
- if (mode[6] == 'c') {
- setvbuf(stderr, NULL, _IONBF, 0);
- }
- }
- erts_free_read_env(mode);
- }
- int sys_max_files()
- {
- return max_files;
- }
- /*
- * Looks for the given option in the argv vector. If it is found,
- * it will be removed from the argv vector.
- *
- * If the return value indicates that the option was found and removed,
- * it is the responsibility of the caller to decrement the value of argc.
- *
- * Returns: 0 if the option wasn't found, 1 if it was found
- */
- static int
- get_and_remove_option(argc, argv, option)
- int* argc; /* Number of arguments. */
- char* argv[]; /* The argument vector. */
- const char* option; /* Option to search for and remove. */
- {
- int i;
- for (i = 1; i < *argc; i++) {
- if (strcmp(argv[i], option) == 0) {
- (*argc)--;
- while (i < *argc) {
- argv[i] = argv[i+1];
- i++;
- }
- argv[i] = NULL;
- return 1;
- }
- }
- return 0;
- }
- static char *get_and_remove_option2(int *argc, char **argv,
- const char *option)
- {
- char *ret;
- int i;
- for (i = 1; i < *argc; i++) {
- if (strcmp(argv[i], option) == 0) {
- if (i+1 < *argc) {
- ret = argv[i+1];
- (*argc) -= 2;
- while (i < *argc) {
- argv[i] = argv[i+2];
- i++;
- }
- argv[i] = NULL;
- return ret;
- }
- }
- }
- return NULL;
- }
-
- /************************** OS info *******************************/
- /* Used by erlang:info/1. */
- /* (This code was formerly in drv.XXX/XXX_os_drv.c) */
- char os_type[] = "win32";
- void
- os_flavor(namebuf, size)
- char* namebuf; /* Where to return the name. */
- unsigned size; /* Size of name buffer. */
- {
- switch (int_os_version.dwPlatformId) {
- case VER_PLATFORM_WIN32_WINDOWS:
- strcpy(namebuf, "windows");
- break;
- case VER_PLATFORM_WIN32_NT:
- strcpy(namebuf, "nt");
- break;
- default: /* Can't happen. */
- strcpy(namebuf, "unknown");
- break;
- }
- }
- void
- os_version(pMajor, pMinor, pBuild)
- int* pMajor; /* Pointer to major version. */
- int* pMinor; /* Pointer to minor version. */
- int* pBuild; /* Pointer to build number. */
- {
- *pMajor = int_os_version.dwMajorVersion;
- *pMinor = int_os_version.dwMinorVersion;
- *pBuild = int_os_version.dwBuildNumber;
- }
- /************************** Port I/O *******************************/
- /* I. Common stuff */
- /* II. The spawn/fd/vanilla drivers */
- /*
- * Definitions for driver flags.
- */
- #define DF_OVR_READY 1 /* Overlapped result is ready. */
- #define DF_EXIT_THREAD 2 /* The thread should exit. */
- #define DF_XLAT_CR 4 /* The thread should translate CRs. */
- #define DF_DROP_IF_INVH 8 /* Drop packages instead of crash if
- invalid handle (stderr) */
- #define OV_BUFFER_PTR(dp) ((LPVOID) ((dp)->ov.Internal))
- #define OV_NUM_TO_READ(dp) ((dp)->ov.InternalHigh)
- /*
- * This data is used to make overlapped I/O operations work on both
- * Windows NT (using true overlapped I/O) and Windows 95 (using threads).
- */
- typedef struct async_io {
- unsigned flags; /* Driver flags, definitions found above. */
- HANDLE thread; /* If -1, overlapped I/O is used (Windows NT).
- * Otherwise, it is the handle of the thread used
- * for simulating overlapped I/O (Windows 95 and
- * the console for Windows NT).
- */
- HANDLE fd; /* Handle for file or pipe. */
- #ifdef ERTS_SMP
- int async_io_active; /* if true, a close of the file will signal the event in ov */
- #endif
- OVERLAPPED ov; /* Control structure for overlapped reading.
- * When overlapped reading is simulated with
- * a thread, the fields are used as follows:
- * ov.Internal - Read buffer.
- * ov.InternalHigh - Number of bytes to read.
- * See macros above.
- */
- HANDLE ioAllowed; /* The thread will wait for this event
- * before starting a new read or write.
- */
- DWORD pendingError; /* Used to delay presentating an error to Erlang
- * until the check_io function is entered.
- */
- DWORD bytesTransferred; /* Bytes read or write in the last operation.
- * Valid only when DF_OVR_READY is set.
- */
- } AsyncIo;
- /*
- * Input thread for fd_driver (if fd_driver is running).
- */
- static AsyncIo* fd_driver_input = NULL;
- static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD);
- /*
- * This data is used by the spawn and vanilla drivers.
- * There will be one entry for each port, even if the input
- * and output HANDLES are different. Since handles are not
- * guaranteed to be small numbers in Win32, we cannot index
- * with them. I.e. the index for each entry is not equal to
- * none of the file handles.
- */
- typedef struct driver_data {
- int totalNeeded; /* Total number of bytes needed to fill
- * up the packet header or packet. */
- int bytesInBuffer; /* Number of bytes read so far in
- * the input buffer.
- */
- int inBufSize; /* Size of input buffer. */
- byte *inbuf; /* Buffer to use for overlapped read. */
- int outBufSize; /* Size of output buffer. */
- byte *outbuf; /* Buffer to use for overlapped write. */
- ErlDrvPort port_num; /* The port number. */
- int packet_bytes; /* 0: continous stream, 1, 2, or 4: the number
- * of bytes in the packet header.
- */
- HANDLE port_pid; /* PID of the port process. */
- AsyncIo in; /* Control block for overlapped reading. */
- AsyncIo out; /* Control block for overlapped writing. */
- int report_exit; /* Do report exit status for the port */
- } DriverData;
- static DriverData* driver_data; /* Pointer to array of driver data. */
- /* Driver interfaces */
- static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
- static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
- static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
- static int spawn_init(void);
- static int fd_init(void);
- static void fd_stop(ErlDrvData);
- static void stop(ErlDrvData);
- static void output(ErlDrvData, char*, int);
- static void ready_input(ErlDrvData, ErlDrvEvent);
- static void ready_output(ErlDrvData, ErlDrvEvent);
- static void stop_select(ErlDrvEvent, void*);
- struct erl_drv_entry spawn_driver_entry = {
- spawn_init,
- spawn_start,
- stop,
- output,
- ready_input,
- ready_output,
- "spawn",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL,
- NULL, /* process_exit */
- stop_select
- };
- #ifdef HARD_POLL_DEBUG
- extern void poll_debug_set_active_fd(ErtsSysFdType fd);
- extern void poll_debug_read_begin(ErtsSysFdType fd);
- extern void poll_debug_read_done(ErtsSysFdType fd, int bytes);
- extern void poll_debug_async_initialized(ErtsSysFdType fd);
- extern void poll_debug_async_immediate(ErtsSysFdType fd, int bytes);
- extern void poll_debug_write_begin(ErtsSysFdType fd);
- extern void poll_debug_write_done(ErtsSysFdType fd, int bytes);
- #endif
- extern int null_func(void);
- struct erl_drv_entry fd_driver_entry = {
- fd_init,
- fd_start,
- fd_stop,
- output,
- ready_input,
- ready_output,
- "fd",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL,
- NULL, /* process_exit */
- stop_select
- };
- struct erl_drv_entry vanilla_driver_entry = {
- null_func,
- vanilla_start,
- stop,
- output,
- ready_input,
- ready_output,
- "vanilla",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL,
- NULL, /* process_exit */
- stop_select
- };
- #if defined(USE_THREADS) && !defined(ERTS_SMP)
- static int async_drv_init(void);
- static ErlDrvData async_drv_start(ErlDrvPort, char*, SysDriverOpts*);
- static void async_drv_stop(ErlDrvData);
- static void async_drv_input(ErlDrvData, ErlDrvEvent);
- /* INTERNAL use only */
- void null_output(ErlDrvData drv_data, char* buf, int len)
- {
- }
- void null_ready_output(ErlDrvData drv_data, ErlDrvEvent event)
- {
- }
- struct erl_drv_entry async_driver_entry = {
- async_drv_init,
- async_drv_start,
- async_drv_stop,
- null_output,
- async_drv_input,
- null_ready_output,
- "async",
- NULL, /* finish */
- NULL, /* handle */
- NULL, /* control */
- NULL, /* timeout */
- NULL, /* outputv */
- NULL, /* ready_async */
- NULL, /* flush */
- NULL, /* call */
- NULL, /* event */
- ERL_DRV_EXTENDED_MARKER,
- ERL_DRV_EXTENDED_MAJOR_VERSION,
- ERL_DRV_EXTENDED_MINOR_VERSION,
- 0, /* ERL_DRV_FLAGs */
- NULL,
- NULL, /* process_exit */
- stop_select
- };
- #endif
- /*
- * Initialises a DriverData structure.
- *
- * Results: Returns a pointer to a DriverData structure, or NULL
- * if the initialsation failed.
- */
- static DriverData*
- new_driver_data(port_num, packet_bytes, wait_objs_required, use_threads)
- int port_num; /* The port number. */
- int packet_bytes; /* Number of bytes in header. */
- int wait_objs_required; /* The number objects this port is going
- /* wait for (typically 1 or 2). */
- int use_threads; /* TRUE if threads are intended to be used. */
- {
- DriverData* dp;
-
- erts_smp_mtx_lock(&sys_driver_data_lock);
- DEBUGF(("new_driver_data(port_num %d, pb %d)\n",
- port_num, packet_bytes));
- /*
- * We used to test first at all that there is enough room in the
- * array used by WaitForMultipleObjects(), but that is not necessary
- * any more, since driver_select() can't fail.
- */
- /*
- * Search for a free slot.
- */
- for (dp = driver_data; dp < driver_data+max_files; dp++) {
- if (dp->port_num == PORT_FREE) {
- dp->bytesInBuffer = 0;
- dp->totalNeeded = packet_bytes;
- dp->inBufSize = PORT_BUFSIZ;
- dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
- if (dp->inbuf == NULL) {
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- return NULL;
- }
- erts_smp_atomic_add(&sys_misc_mem_sz, dp->inBufSize);
- dp->outBufSize = 0;
- dp->outbuf = NULL;
- dp->port_num = port_num;
- dp->packet_bytes = packet_bytes;
- dp->port_pid = INVALID_HANDLE_VALUE;
- if (init_async_io(&dp->in, use_threads) == -1)
- break;
- if (init_async_io(&dp->out, use_threads) == -1)
- break;
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- return dp;
- }
- }
- /*
- * Error or no free driver data.
- */
- if (dp < driver_data+max_files) {
- release_async_io(&dp->in, dp->port_num);
- release_async_io(&dp->out, dp->port_num);
- }
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- return NULL;
- }
- static void
- release_driver_data(DriverData* dp)
- {
- erts_smp_mtx_lock(&sys_driver_data_lock);
- #ifdef ERTS_SMP
- #ifdef USE_CANCELIOEX
- if (fpCancelIoEx != NULL) {
- if (dp->in.thread == (HANDLE) -1 && dp->in.fd != INVALID_HANDLE_VALUE) {
- (*fpCancelIoEx)(dp->in.fd, NULL);
- }
- if (dp->out.thread == (HANDLE) -1 && dp->out.fd != INVALID_HANDLE_VALUE) {
- (*fpCancelIoEx)(dp->out.fd, NULL);
- }
- }
- else
- #endif
- {
- /* This is a workaround for the fact that CancelIo cant cancel
- requests issued by another thread and that we cant use
- CancelIoEx as that's only available in Vista etc.
- R14: Avoid scheduler deadlock by only wait for 10ms, and then spawn
- a thread that will keep waiting in in order to close handles. */
- HANDLE handles[2];
- int i = 0;
- int timeout = 10;
- if(dp->in.async_io_active && dp->in.fd != INVALID_HANDLE_VALUE) {
- CloseHandle(dp->in.fd);
- dp->in.fd = INVALID_HANDLE_VALUE;
- DEBUGF(("Waiting for the in event thingie"));
- if (WaitForSingleObject(dp->in.ov.hEvent,timeout) == WAIT_TIMEOUT) {
- close_active_handle(dp->port_num, dp->in.ov.hEvent);
- dp->in.ov.hEvent = NULL;
- timeout = 0;
- }
- DEBUGF(("...done\n"));
- }
- if(dp->out.async_io_active && dp->out.fd != INVALID_HANDLE_VALUE) {
- CloseHandle(dp->out.fd);
- dp->out.fd = INVALID_HANDLE_VALUE;
- DEBUGF(("Waiting for the out event thingie"));
- if (WaitForSingleObject(dp->out.ov.hEvent,timeout) == WAIT_TIMEOUT) {
- close_active_handle(dp->port_num, dp->out.ov.hEvent);
- dp->out.ov.hEvent = NULL;
- }
- DEBUGF(("...done\n"));
- }
- }
- #else
- if (dp->in.thread == (HANDLE) -1 && dp->in.fd != INVALID_HANDLE_VALUE) {
- CancelIo(dp->in.fd);
- }
- if (dp->out.thread == (HANDLE) -1 && dp->out.fd != INVALID_HANDLE_VALUE) {
- CancelIo(dp->out.fd);
- }
- #endif
- if (dp->inbuf != NULL) {
- ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->inBufSize);
- erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->inBufSize);
- DRV_BUF_FREE(dp->inbuf);
- dp->inBufSize = 0;
- dp->inbuf = NULL;
- }
- ASSERT(dp->inBufSize == 0);
- if (dp->outbuf != NULL) {
- ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->outBufSize);
- erts_smp_atomic_add(&sys_misc_mem_sz, -1*dp->outBufSize);
- DRV_BUF_FREE(dp->outbuf);
- dp->outBufSize = 0;
- dp->outbuf = NULL;
- }
- ASSERT(dp->outBufSize == 0);
- if (dp->port_pid != INVALID_HANDLE_VALUE) {
- CloseHandle(dp->port_pid);
- dp->port_pid = INVALID_HANDLE_VALUE;
- }
- release_async_io(&dp->in, dp->port_num);
- release_async_io(&dp->out, dp->port_num);
- /*
- * This must be last, because this function might be executed from
- * the exit thread.
- */
- dp->port_num = PORT_FREE;
- erts_smp_mtx_unlock(&sys_driver_data_lock);
- }
- #ifdef ERTS_SMP
- struct handles_to_be_closed {
- HANDLE handles[MAXIMUM_WAIT_OBJECTS];
- unsigned cnt;
- };
- static struct handles_to_be_closed* htbc_curr = NULL;
- CRITICAL_SECTION htbc_lock;
- static void close_active_handle(ErlDrvPort port_num, HANDLE handle)
- {
- struct handles_to_be_closed* htbc;
- int i;
- EnterCriticalSection(&htbc_lock);
- htbc = htbc_curr;
- if (htbc == NULL || htbc->cnt >= MAXIMUM_WAIT_OBJECTS) {
- DWORD tid;
- HANDLE thread;
- htbc = (struct handles_to_be_closed*) erts_alloc(ERTS_ALC_T_DRV_TAB,
- sizeof(*htbc));
- htbc->handles[0] = CreateAutoEvent(FALSE);
- htbc->cnt = 1;
- thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_handle_closer, htbc, 0, &tid);
- CloseHandle(thread);
- }
- htbc->handles[htbc->cnt++] = handle;
- driver_select(port_num, (ErlDrvEvent)handle, ERL_DRV_USE_NO_CALLBACK, 0);
- SetEvent(htbc->handles[0]);
- htbc_curr = htbc;
- LeaveCriticalSection(&htbc_lock);
- }
- static DWORD WINAPI
- threaded_handle_closer(LPVOID param)
- {
- struct handles_to_be_closed* htbc = (struct handles_to_be_closed*) param;
- unsigned ix;
- DWORD res;
- DEBUGF(("threaded_handle_closer %p started\r\n", htbc));
- EnterCriticalSection(&htbc_lock);
- for (;;) {
- {
- HANDLE* handles = htbc->handles;
- unsigned cnt = htbc->cnt;
- DWORD timeout = (htbc == htbc_curr) ? INFINITE : 10*1000;
- LeaveCriticalSection(&htbc_lock);
- DEBUGF(("threaded_handle_closer %p waiting for %d handles\r\n", htbc, cnt));
- res = WaitForMultipleObjects(cnt, handles, FALSE, timeout);
- }
- EnterCriticalSection(&htbc_lock);
- switch (res) {
- case WAIT_OBJECT_0:
- case WAIT_TIMEOUT:
- break; /* got some more handles to wait for maybe */
- default:
- ix = res - WAIT_OBJECT_0;
- if (ix > 0 && ix < htbc->cnt) {
- CloseHandle(htbc->handles[ix]);
- htbc->handles[ix] = htbc->handles[--htbc->cnt];
- }
- }
- if (htbc != htbc_curr) {
- if (htbc->cnt == 1) { /* no real handles left */
- break;
- }
- /* The thread with most free slots will be "current" */
- if (htbc->cnt < htbc_curr->cnt) {
- htbc_curr = htbc;
- DEBUGF(("threaded_handle_closer %p made current\r\n", htbc));
- }
- }
- }
- LeaveCriticalSection(&htbc_lock);
- CloseHandle(htbc->handles[0]);
- erts_free(ERTS_ALC_T_DRV_TAB, htbc);
- DEBUGF(("threaded_handle_closer %p terminating\r\n", htbc));
- return 0;
- }
- #endif /* ERTS_SMP */
- /*
- * Stores input and output file descriptors in the DriverData structure,
- * and calls driver_select().
- *
- * This function fortunately can't fail!
- */
- static ErlDrvData
- set_driver_data(dp, ifd, ofd, read_write, report_exit)
- DriverData* dp;
- HANDLE ifd;
- HANDLE ofd;
- int read_write;
- int report_exit;
- {
- int index = dp - driver_data;
- int result;
- dp->in.fd = ifd;
- dp->out.fd = ofd;
- dp->report_exit = report_exit;
- if (read_write & DO_READ) {
- result = driver_select(dp->port_num, (ErlDrvEvent)dp->in.ov.hEvent,
- ERL_DRV_READ|ERL_DRV_USE, 1);
- ASSERT(result != -1);
- async_read_file(&dp->in, dp->inbuf, dp->inBufSize);
- }
- if (read_write & DO_WRITE) {
- result = driver_select(dp->port_num, (ErlDrvEvent)dp->out.ov.hEvent,
- ERL_DRV_WRITE|ERL_DRV_USE, 1);
- ASSERT(result != -1);
- }
- return (ErlDrvData)index;
- }
- /*
- * Initialises an AsyncIo structure.
- */
- static int
- init_async_io(AsyncIo* aio, int use_threads)
- {
- aio->flags = 0;
- aio->thread = (HANDLE) -1;
- aio->fd = INVALID_HANDLE_VALUE;
- aio->ov.hEvent = NULL;
- aio->ov.Offset = 0L;
- aio->ov.OffsetHigh = 0L;
- aio->ioAllowed = NULL;
- aio->pendingError = 0;
- aio->bytesTransferred = 0;
- #ifdef ERTS_SMP
- aio->async_io_active = 0;
- #endif
- aio->ov.hEvent = CreateManualEvent(FALSE);
- if (aio->ov.hEvent == NULL)
- return -1;
- if (use_threads) {
- aio->ioAllowed = CreateAutoEvent(FALSE);
- if (aio->ioAllowed == NULL)
- return -1;
- }
- return 0;
- }
- /*
- * Releases everything allocated in an AsyncIo structure.
- */
- static void
- release_async_io(AsyncIo* aio, ErlDrvPort port_num)
- {
- aio->flags = 0;
- if (aio->thread != (HANDLE) -1)
- CloseHandle(aio->thread);
- aio->thread = (HANDLE) -1;
- if (aio->fd != INVALID_HANDLE_VALUE)
- CloseHandle(aio->fd);
- aio->fd = INVALID_HANDLE_VALUE;
- if (aio->ov.hEvent != NULL) {
- (void) driver_select(port_num,
- (ErlDrvEvent)aio->ov.hEvent,
- ERL_DRV_USE, 0);
- /* was CloseHandle(aio->ov.hEvent); */
- }
- aio->ov.hEvent = NULL;
- if (aio->ioAllowed != NULL)
- CloseHandle(aio->ioAllowed);
- aio->ioAllowed = NULL;
- }
- /* ----------------------------------------------------------------------
- * async_read_file --
- * Initiaties an asynchronous file read, or simulates that using
- * the thread associated with this driver data. To get the results,
- * call get_overlapped_result().
- *
- * Results:
- * None.
- * ----------------------------------------------------------------------
- */
- static void
- async_read_file(aio, buf, numToRead)
- AsyncIo* aio; /* Pointer to driver data. */
- LPVOID buf; /* Pointer to buffer to receive data. */
- DWORD numToRead; /* Number of bytes to read. */
- {
- aio->pendingError = NO_ERROR;
- #ifdef HARD_POLL_DEBUG
- poll_debug_async_initialized(aio->ov.hEvent);
- #endif
- if (aio->thread != (HANDLE) -1) {
- DEBUGF(("async_read_file: signaling thread 0x%x, event 0x%x\n",
- aio->thread, aio->ioAllowed));
- OV_BUFFER_PTR(aio) = buf;
- OV_NUM_TO_READ(aio) = numToRead;
- ResetEvent(aio->ov.hEvent);
- SetEvent(aio->ioAllowed);
- } else {
- #ifdef ERTS_SMP
- aio->async_io_active = 1; /* Will get 0 when the event actually happened */
- #endif
- if (ReadFile(aio->fd, buf, numToRead,
- &aio->bytesTransferred, &aio->ov)) {
- DEBUGF(("async_read_file: ReadFile() suceeded: %d bytes\n",
- aio->bytesTransferred));
- #ifdef HARD_POLL_DEBUG
- poll_debug_async_immediate(aio->ov.hEvent, aio->bytesTransferred);
- #endif
- aio->flags |= DF_OVR_READY;
- SetEvent(aio->ov.hEvent);
- } else {
- DWORD error = GetLastError();
- if (error != ERROR_IO_PENDING) {
- #ifdef HARD_POLL_DEBUG
- poll_debug_async_immediate(aio->ov.hEvent, 0);
- #endif
- aio->pendingError = error;
- SetEvent(aio->ov.hEvent);
- }
- DEBUGF(("async_read_file: ReadFile() -> %s\n", win32_errorstr(error)));
- }
- }
- }
- /* ----------------------------------------------------------------------
- * async_write_file --
- * Initiaties an asynchronous file write, or simulates that using
- * the output thread associated with this driver data.
- * To get the results, call get_overlapped_result().
- *
- * Results:
- * None.
- * ----------------------------------------------------------------------
- */
- static int
- async_write_file(aio, buf, numToWrite)
- AsyncIo* aio; /* Pointer to async control block. */
- LPVOID buf; /* Pointer to buffer with data to write. */
- DWORD numToWrite; /* Number of bytes to write. */
- {
- aio->pendingError = NO_ERROR;
- if (aio->thread != (HANDLE) -1) {
- DEBUGF(("async_write_file: signaling thread 0x%x, event 0x%x\n",
- aio->thread, aio->ioAllowed));
- OV_BUFFER_PTR(aio) = buf;
- OV_NUM_TO_READ(aio) = numToWrite;
- ResetEvent(aio->ov.hEvent);
- SetEvent(aio->ioAllowed);
- } else {
- #ifdef ERTS_SMP
- aio->async_io_active = 1; /* Will get 0 when the event actually happened */
- #endif
- if (WriteFile(aio->fd, buf, numToWrite,
- &aio->bytesTransferred, &aio->ov)) {
- DEBUGF(("async_write_file: WriteFile() suceeded: %d bytes\n",
- aio->bytesTransferred));
- #ifdef ERTS_SMP
- aio->async_io_active = 0; /* The event will not be signalled */
- #endif
- ResetEvent(aio->ov.hEvent);
- return TRUE;
- } else {
- DWORD error = GetLastError();
- if (error != ERROR_IO_PENDING) {
- aio->pendingError = error;
- SetEvent(aio->ov.hEvent);
- }
- DEBUGF(("async_write_file: WriteFile() -> %s\n", win32_errorstr(error)));
- }
- }
- return FALSE;
- }
- /* ----------------------------------------------------------------------
- * get_overlapped_result --
- *
- * Results:
- * Returns the error code for the overlapped result, or NO_ERROR
- * if no error.
- * ----------------------------------------------------------------------
- */
- static int
- get_overlapped_result(aio, pBytesRead, wait)
- AsyncIo* aio; /* Pointer to async control block. */
- LPDWORD pBytesRead; /* Where to place the number of bytes
- * transferred.
- */
- BOOL wait; /* If true, wait until result is ready. */
- {
- DWORD error = NO_ERROR; /* Error status from last function. */
- if (aio->thread != (HANDLE) -1) {
- /*
- * Simulate overlapped io with a thread.
- */
- DEBUGF(("get_overlapped_result: about to wait for event 0x%x\n",
- aio->ov.hEvent));
- error = WaitForSingleObject(aio->ov.hEvent, wait ? INFINITE : 0);
- switch (error) {
- case WAIT_OBJECT_0:
- error = aio->pendingError;
- aio->pendingError = NO_ERROR;
- *pBytesRead = aio->bytesTransferred;
- ResetEvent(aio->ov.hEvent);
- DEBUGF(("get_overlapped_result -> %s\n",
- win32_errorstr(error)));
- return error;
- case WAIT_TIMEOUT:
- DEBUGF(("get_overlapped_result -> %s\n",
- ERROR_IO_INCOMPLETE));
- return ERROR_IO_INCOMPLETE;
- case WAIT_FAILED: /* XXX: Shouldn't happen? */
- error = GetLastError();
- DEBUGF(("get_overlapped_result (WAIT_FAILED) -> %s\n",
- win32_errorstr(error)));
- return error;
- }
- } else if (aio->pendingError != NO_ERROR) { /* Pending error. */
- error = aio->pendingError;
- aio->pendingError = NO_ERROR;
- ResetEvent(aio->ov.hEvent);
- DEBUGF(("get_overlapped_result: pending error: %s\n",
- win32_errorstr(error)));
- return error;
- } else if (aio->flags & DF_OVR_READY) { /* Operation succeded. */
- aio->flags &= ~DF_OVR_READY;
- *pBytesRead = aio->bytesTransferred;
- ResetEvent(aio->ov.hEvent);
- DEBUGF(("get_overlapped_result: delayed success: %d bytes\n",
- aio->bytesTransferred));
- } else if (!GetOverlappedResult(aio->fd, &aio->ov, pBytesRead, wait)) {
- error = GetLastError();
- ResetEvent(aio->ov.hEvent);
- DEBUGF(("get_overlapped_result: error: %s\n", win32_errorstr(error)));
- return error;
- } else { /* Success. */
- DEBUGF(("get_overlapped_result: success\n"));
- ResetEvent(aio->ov.hEvent);
- }
- return NO_ERROR;
- }
-
- static int
- fd_init(void)
- {
- char kernel_dll_name[] = "kernel32";
- HMODULE module;
- module = GetModuleHandle(kernel_dll_name);
- fpSetHandleInformation = (module != NULL) ?
- (BOOL (WINAPI *)(HANDLE,DWORD,DWORD))
- GetProcAddress(module,"SetHandleInformation") :
- NULL;
- return 0;
- }
- static int
- spawn_init()
- {
- int i;
- #if defined(ERTS_SMP) && defined(USE_CANCELIOEX)
- HMODULE module = GetModuleHandle("kernel32");
- fpCancelIoEx = (BOOL (WINAPI *)(HANDLE,LPOVERLAPPED))
- ((module != NULL) ? GetProcAddress(module,"CancelIoEx") : NULL);
- DEBUGF(("fpCancelIoEx = %p\r\n", fpCancelIoEx));
- #endif
- driver_data = (struct driver_data *)
- erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
- erts_smp_atomic_add(&sys_misc_mem_sz, max_files*sizeof(struct driver_data));
- for (i = 0; i < max_files; i++)
- driver_data[i].port_num = PORT_FREE;
- return 0;
- }
- static ErlDrvData
- spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
- {
- HANDLE hToChild = INVALID_HANDLE_VALUE; /* Write handle to child. */
- HANDLE hFromChild = INVALID_HANDLE_VALUE; /* Read handle from child. */
- HANDLE hChildStdin = INVALID_HANDLE_VALUE; /* Child's stdin. */
- HANDLE hChildStdout = INVALID_HANDLE_VALUE; /* Child's stout. */
- HANDLE hChildStderr = INVALID_HANDLE_VALUE; /* Child's sterr. */
- int close_child_stderr = 0;
- DriverData* dp; /* Pointer to driver data. */
- ErlDrvData retval = ERL_DRV_ERROR_GENERAL; /* Return value. */
- int ok;
- int neededSelects = 0;
- SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
- char* envir = opts->envir;
- int errno_return = -1;
-
- if (opts->read_write & DO_READ)
- neededSelects++;
- if (opts->read_write & DO_WRITE)
- neededSelects++;
- if ((dp = new_driver_data(port_num, opts->packet_bytes, neededSelects,
- !use_named_pipes)) == NULL)
- return ERL_DRV_ERROR_GENERAL;
- /*
- * Create two pipes to communicate with the port program.
- */
- if (opts->read_write & DO_READ) {
- if (!create_pipe(&hFromChild, &hChildStdout, FALSE,
- opts->overlapped_io))
- goto error;
- } else {
- hChildStdout = CreateFile("nul", GENERIC_WRITE, 0,
- &sa, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- DEBUGF(("Created nul file for hChildStdout = %d\n",hChildStdout));
- }
- if (opts->read_write & DO_WRITE) {
- if (!create_pipe(&hChildStdin, &hToChild, TRUE, opts->overlapped_io)) {
- CloseHandle(hFromChild);
- hFromChild = INVALID_HANDLE_VALUE;
- CloseHandle(hChildStdout);
- goto error;
- }
- } else {
- hChildStdin = CreateFile("nul", GENERIC_READ, 0,
- &sa, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- DEBUGF(("Created nul file for hChildStdin = %d\n",hChildStdin));
- }
- /*
- * Make sure that standard error is valid handle, because a Command Prompt
- * window not work properly otherwise. We leave standard error alone if
- * it is okay and no redirection was specified.
- */
- hChildStderr = GetStdHandle(STD_ERROR_HANDLE);
- if (opts->redir_stderr) {
- hChildStderr = hChildStdout;
- } else if (hChildStderr == INVALID_HANDLE_VALUE || hChildStderr == 0) {
- hChildStderr = CreateFile("nul", GENERIC_WRITE, 0, &sa, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- close_child_stderr = 1;
- }
- if (fpSetHandleInformation != NULL) {
- (*fpSetHandleInformation)(hChildStderr, HANDLE_FLAG_INHERIT, 1);
- }
- /*
- * Spawn the port program.
- */
- DEBUGF(("Spawning \"%s\"\n", name));
- envir = win_build_environment(envir); /* Still an ansi environment, could be
- converted to unicode for spawn_executable, but
- that is not done (yet) */
- ok = create_child_process(name,
- hChildStdin,
- hChildStdout,
- hChildStderr,
- &dp->port_pid,
- opts->hide_window,
- (LPVOID) envir,
- (LPTSTR) opts->wd,
- opts->spawn_type,
- opts->argv,
- &errno_return);
- CloseHandle(hChildStdin);
- CloseHandle(hChildStdout);
- if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
- hChildStderr != 0) {
- CloseHandle(hChildStderr);
- }
- if (envir != NULL) {
- erts_free(ERTS_ALC_T_ENVIRONMENT, envir);
- }
- if (!ok) {
- dp->port_pid = INVALID_HANDLE_VALUE;
- if (errno_return >= 0) {
- retval = ERL_DRV_ERROR_ERRNO;
- }
- } else {
- if (!use_named_pipes) {
- if ((opts->read_write & DO_READ) &&
- !create_file_thread(&dp->in, DO_READ))
- goto error;
- if ((opts->read_write & DO_WRITE) &&
- !create_file_thread(&dp->out, DO_WRITE)) {
- dp->in.flags = DF_EXIT_THREAD;
- SetEvent(dp->in.ioAllowed);
- WaitForSingleObject(dp->in.thread, INFINITE);
- dp->in.thread = (HANDLE) -1;
- goto error;
- }
- }
- #ifdef HARD_POLL_DEBUG
- if (strncmp(name,"inet_gethost",12) == 0) {
- erts_printf("Debugging \"%s\"\n", name);
- poll_debug_set_active_fd(dp->in.ov.hEvent);
- }
- #endif
- retval = set_driver_data(dp, hFromChild, hToChild, opts->read_write,
- opts->exit_status);
- }
-
- if (retval != ERL_DRV_ERROR_GENERAL && retval != ERL_DRV_ERROR_ERRNO)
- return retval;
-
- error:
- if (hFromChild != INVALID_HANDLE_VALUE)
- CloseHandle(hFromChild);
- if (hToChild != INVALID_HANDLE_VALUE)
- CloseHandle(hToChild);
- release_driver_data(dp);
- if (retval == ERL_DRV_ERROR_ERRNO) {
- errno = errno_return;
- }
- return retval;
- }
- static int
- create_file_thread(AsyncIo* aio, int mode)
- {
- DWORD tid; /* Id for thread. */
- aio->thread = (HANDLE)
- _beginthreadex(NULL, 0,
- (mode & DO_WRITE) ? threaded_writer : threaded_reader,
- aio, 0, &tid);
- return aio->thread != (HANDLE) -1;
- }
- /*
- * A helper function used by create_child_process().
- * Parses a command line with arguments and returns the length of the
- * first part containing the program name.
- * Example: input = "\"Program Files\"\\erl arg1 arg2"
- * gives 19 as result.
- * The length returned is equivalent with length(argv[0]) if the
- * comman line should have been prepared by _setargv for the main function
- */
- int parse_command(char* cmd){
- #define NORMAL 2
- #define STRING 1
- #define STOP 0
- int i =0;
- int state = NORMAL;
- while (cmd[i]) {
- switch (cmd[i]) {
- case '"':
- if (state == NORMAL)
- state = STRING;
- else
- state = NORMAL;
- break;
- case '\\':
- if ((state == STRING) && (cmd[i+1]=='"'))
- i++;
- break;
- case ' ':
- if (state == NORMAL)
- state = STOP;
- break;
- default:
- break;
- }
- if (state == STOP) {
- return i;
- }
- i++;
- }
- return i;
- }
- static BOOL need_quotes(WCHAR *str)
- {
- int in_quote = 0;
- int backslashed = 0;
- int naked_space = 0;
- while (*str != L'\0') {
- switch (*str) {
- case L'\\' :
- backslashed = !backslashed;
- break;
- case L'"':
- if (backslashed) {
- backslashed=0;
- } else {
- in_quote = !in_quote;
- }
- break;
- case L' ':
- backslashed = 0;
- if (!(backslashed || in_quote)) {
- naked_space++;
- }
- break;
- default:
- backslashed = 0;
- }
- ++str;
- }
- return (naked_space > 0);
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * create_child_process --
- *
- * Create a child process that has pipes as its
- * standard input, output, and error. The child process runs
- * synchronously under Win32s and asynchronously under Windows NT
- * and Windows 95, and runs with the same environment variables
- * as the creating process.
- *
- * The complete Windows search path is searched to find the specified
- * executable. If an executable by the given name is not found,
- * automatically tries appending ".com", ".exe", and ".bat" to the
- * executable name.
- *
- * Results:
- * The return value is FALSE if there was a problem creating the child process.
- * Otherwise, the return value is 0 and *phPid is
- * filled with the process id of the child process.
- *
- * Side effects:
- * A process is created.
- *
- *----------------------------------------------------------------------
- */
- static BOOL
- create_child_process
- (
- char *origcmd, /* Command line for child process (including
- * name of executable). Or whole executable if st is
- * ERTS_SPAWN_EXECUTABLE
- */
- HANDLE hStdin, /* The standard input handle for child. */
- HANDLE hStdout, /* The standard output handle for child. */
- HANDLE hStderr, /* The standard error handle for child. */
- LPHANDLE phPid, /* Pointer to variable to received PID. */
- BOOL hide, /* Hide the window unconditionally. */
- LPVOID env, /* Environment for the child */
- LPTSTR wd, /* Working dir for the child */
- unsigned st, /* Flags for spawn, tells us how to interpret origcmd */
- char **argv, /* Argument vector if given. */
- int *errno_return /* Place to put an errno in in case of failure */
- )
- {
- PROCESS_INFORMATION piProcInfo = {0};
- BOOL ok = FALSE;
- int applType;
- /* Not to be changed for different types of executables */
- int staticCreateFlags = GetPriorityClass(GetCurrentProcess());
- int createFlags = DETACHED_PROCESS;
- char *newcmdline = NULL;
- int cmdlength;
- char* thecommand;
- LPTSTR appname = NULL;
- HANDLE hProcess = GetCurrentProcess();
-
- *errno_return = -1;
- if (st != ERTS_SPAWN_EXECUTABLE) {
- STARTUPINFO siStartInfo = {0};
- char execPath[MAX_PATH];
- siStartInfo.cb = sizeof(STARTUPINFO);
- siStartInfo.dwFlags = STARTF_USESTDHANDLES;
- siStartInfo.hStdInput = hStdin;
- siStartInfo.hStdOutput = hStdout;
- siStartInfo.hStdError = hStderr;
- /*
- * Parse out the program name from the command line (it can be quoted and
- * contain spaces).
- */
- newcmdline = erts_alloc(ERTS_ALC_T_TMP, 2048);
- cmdlength = parse_command(origcmd);
- thecommand = (char *) erts_alloc(ERTS_ALC_T_TMP, cmdlength+1);
- strncpy(thecommand, origcmd, cmdlength);
- thecommand[cmdlength] = '\0';
- DEBUGF(("spawn command: %s\n", thecommand));
-
- applType = application_type(thecommand, execPath, TRUE,
- TRUE, errno_return);
- DEBUGF(("application_type returned for (%s) is %d\n", thecommand, applType));
- erts_free(ERTS_ALC_T_TMP, (void *) thecommand);
- if (applType == APPL_NONE) {
- erts_free(ERTS_ALC_T_TMP,newcmdline);
- return FALSE;
- }
- newcmdline[0] = '\0';
- if (applType == APPL_DOS) {
- /*
- * Under NT, 16-bit DOS applications will not run unless they
- * can be attached to a console. Run the 16-bit program as
- * a normal process inside of a hidden console application,
- * and then run that hidden console as a detached process.
- */
-
- siStartInfo.wShowWindow = SW_HIDE;
- siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = CREATE_NEW_CONSOLE;
- strcat(newcmdline, "cmd.exe /c ");
- } else if (hide) {
- DEBUGF(("hiding window\n"));
- siStartInfo.wShowWindow = SW_HIDE;
- siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = 0;
- }
- strcat(newcmdline, execPath);
- strcat(newcmdline, origcmd+cmdlength);
- DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags));
- ok = CreateProcessA(appname,
- newcmdline,
- NULL,
- NULL,
- TRUE,
- createFlags | staticCreateFlags,
- env,
- wd,
- &siStartInfo,
- &piProcInfo);
- } else { /* ERTS_SPAWN_EXECUTABLE, filename and args are in unicode ({utf16,little}) */
- int run_cmd = 0;
- STARTUPINFOW siStartInfo = {0};
- WCHAR execPath[MAX_PATH];
- siStartInfo.cb = sizeof(STARTUPINFOW);
- siStartInfo.dwFlags = STARTF_USESTDHANDLES;
- siStartInfo.hStdInput = hStdin;
- siStartInfo.hStdOutput = hStdout;
- siStartInfo.hStdError = hStderr;
- applType = application_type_w(origcmd, (char *) execPath, FALSE, FALSE,
- errno_return);
- if (applType == APPL_NONE) {
- return FALSE;
- }
- if (applType == APPL_DOS) {
- /*
- * See comment above
- */
-
- siStartInfo.wShowWindow = SW_HIDE;
- siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = CREATE_NEW_CONSOLE;
- run_cmd = 1;
- } else if (hide) {
- DEBUGF(("hiding window\n"));
- siStartInfo.wShowWindow = SW_HIDE;
- siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = 0;
- }
- if (run_cmd) {
- WCHAR cmdPath[MAX_PATH];
- int cmdType;
- cmdType = application_type_w((char *) L"cmd.exe", (char *) cmdPath, TRUE, FALSE, errno_return);
- if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
- return FALSE;
- }
- appname = (char *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(WCHAR));
- wcscpy((WCHAR *) appname,cmdPath);
- } else {
- appname = (char *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(WCHAR));
- wcscpy((WCHAR *) appname, execPath);
- }
- if (argv == NULL) {
- BOOL orig_need_q = need_quotes(execPath);
- WCHAR *ptr;
- int ocl = wcslen(execPath);
- if (run_cmd) {
- newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
- (ocl + ((orig_need_q) ? 3 : 1)
- + 11)*sizeof(WCHAR));
- memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(WCHAR));
- ptr = (WCHAR *) (newcmdline + (11*sizeof(WCHAR)));
- } else {
- newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
- (ocl + ((orig_need_q) ? 3 : 1))*sizeof(WCHAR));
- ptr = (WCHAR *) newcmdline;
- }
- if (orig_need_q) {
- *ptr++ = L'"';
- }
- memcpy(ptr,execPath,ocl*sizeof(WCHAR));
- ptr += ocl;
- if (orig_need_q) {
- *ptr++ = L'"';
- }
- *ptr = L'\0';
- } else {
- int sum = 1; /* '\0' */
- WCHAR **ar = (WCHAR **) argv;
- WCHAR *n;
- char *save_arg0 = NULL;
- if (argv[0] == erts_default_arg0 || run_cmd) {
- save_arg0 = argv[0];
- argv[0] = (char *) execPath;
- }
- if (run_cmd) {
- sum += 11; /* cmd.exe /c */
- }
- while (*ar != NULL) {
- sum += wcslen(*ar);
- if (need_quotes(*ar)) {
- sum += 2; /* quotes */
- }
- sum++; /* space */
- ++ar;
- }
- ar = (WCHAR **) argv;
- newcmdline = erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(WCHAR));
- n = (WCHAR *) newcmdline;
- if (run_cmd) {
- memcpy(n,L"cmd.exe /c ",11*sizeof(WCHAR));
- n += 11;
- }
- while (*ar != NULL) {
- int q = need_quotes(*ar);
- sum = wcslen(*ar);
- if (q) {
- *n++ = L'"';
- }
- memcpy(n,*ar,sum*sizeof(WCHAR));
- n += sum;
- if (q) {
- *n++ = L'"';
- }
- *n++ = L' ';
- ++ar;
- }
- *(n-1) = L'\0';
- if (save_arg0 != NULL) {
- argv[0] = save_arg0;
- }
- }
-
- DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags));
- ok = CreateProcessW((WCHAR *) appname,
- (WCHAR *) newcmdline,
- NULL,
- NULL,
- TRUE,
- createFlags | staticCreateFlags,
- env,
- (WCHAR *) wd,
- &siStartInfo,
- &piProcInfo);
- } /* end SPAWN_EXECUTABLE */
- if (newcmdline != NULL) {
- erts_free(ERTS_ALC_T_TMP,newcmdline);
- }
- if (appname != NULL) {
- erts_free(ERTS_ALC_T_TMP,appname);
- }
- if (!ok) {
- DEBUGF(("CreateProcess failed: %s\n", last_error()));
- if (*errno_return < 0) {
- *errno_return = EACCES;
- }
- return FALSE;
- }
- CloseHandle(piProcInfo.hThread); /* Necessary to avoid resource leak. */
- *phPid = piProcInfo.hProcess;
-
- if (applType == APPL_DOS) {
- WaitForSingleObject(hProcess, 50);
- }
-
- /*
- * When an application spawns a process repeatedly, a new thread
- * instance will be created for each process but the previous
- * instances may not be cleaned up. This results in a significant
- * virtual memory loss each time the process is spawned. If there
- * is a WaitForInputIdle() call between CreateProcess() and
- * CloseHandle(), the problem does not occur. PSS ID Number: Q124121
- */
-
- WaitForInputIdle(piProcInfo.hProcess, 5000);
-
- return ok;
- }
- /*
- * Note, inheritRead == FALSE means "inhetitWrite", i e one of the
- * pipe ends is always expected to be inherited. The pipe end that should
- * be inherited is opened without overlapped io flags, as the child program
- * would expect stdout not to demand overlapped I/O.
- */
- static int create_pipe(HANDLE *phRead, HANDLE *phWrite, BOOL inheritRead, BOOL overlapped_io)
- {
- SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
- char pipe_name[128]; /* Name of pipe. */
- Uint calls;
- /*
- * If we should't use named pipes, create anonmous pipes.
- */
- if (!use_named_pipes) {
- int success;
- HANDLE non_inherited; /* Non-inherited copy of handle. */
- if (!CreatePipe(phRead, phWrite, &sa, 0)) {
- DEBUGF(("Error creating anonyomous pipe: %s\n", last_error()));
- return FALSE;
- }
- if (inheritRead) {
- success = DuplicateHandle(GetCurrentProcess(), *phWrite,
- GetCurrentProcess(), &non_inherited, 0,
- FALSE, DUPLICATE_SAME_ACCESS);
- CloseHandle(*phWrite);
- *phWrite = non_inherited;
- } else {
- success = DuplicateHandle(GetCurrentProcess(), *phRead,
- GetCurrentProcess(), &non_inherited, 0,
- FALSE, DUPLICATE_SAME_ACCESS);
- CloseHandle(*phRead);
- *phRead = non_inherited;
- }
- return success;
- }
- /*
- * Otherwise, create named pipes.
- */
- calls = (Uint) erts_smp_atomic_inctest(&pipe_creation_counter);
- sprintf(pipe_name, "\\\\.\\pipe\\erlang44_%d_%d",
- getpid(), calls);
- DEBUGF(("Creating pipe %s\n", pipe_name));
- sa.bInheritHandle = inheritRead;
- if ((*phRead = CreateNamedPipe(pipe_name,
- PIPE_ACCESS_INBOUND |
- ((inheritRead && !overlapped_io) ? 0 : FILE_FLAG_OVERLAPPED),
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
- 1,
- 0,
- 0,
- 2000,
- &sa)) == NULL) {
- DEBUGF(("Error creating pipe: %s\n", last_error()));
- return FALSE;
- }
-
- sa.bInheritHandle = !inheritRead;
- if ((*phWrite = CreateFile(pipe_name,
- GENERIC_WRITE,
- 0, /* No sharing */
- &sa,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL |
- ((inheritRead || overlapped_io) ? FILE_FLAG_OVERLAPPED : 0),
- NULL)) == INVALID_HANDLE_VALUE) {
- CloseHandle(*phRead);
- DEBUGF(("Error opening other end of pipe: %s\n", last_error()));
- return FALSE;
- }
- return TRUE;
- }
- static int application_type
- (
- const char *originalName, /* Name of the application to find. */
- char fullPath[MAX_PATH], /* Filled with complete path to
- * application. */
- BOOL search_in_path, /* If we should search the system wide path */
- BOOL handle_quotes, /* If we should handle quotes around executable */
- int *error_return /* A place to put an error code */
- )
- {
- int applType, i;
- HANDLE hFile;
- char *ext, *rest;
- char buf[2];
- DWORD read;
- IMAGE_DOS_HEADER header;
- static char extensions[][5] = {"", ".com", ".exe", ".bat"};
- int is_quoted;
- int len;
- /* Look for the program as an external program. First try the name
- * as it is, then try adding .com, .exe, and .bat, in that ord…
Large files files are truncated, but you can click here to view the full file