/erts/emulator/sys/win32/sys.c
C | 3291 lines | 2901 code | 167 blank | 223 comment | 209 complexity | 42bf62a5a9ccf009101fd19c86970b87 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2017. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions 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"
- #include <malloc.h>
- void erts_sys_init_float(void);
- void erl_start(int, char**);
- void erts_exit(int n, char*, ...);
- void erl_error(char*, va_list);
- /*
- * 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
- typedef struct driver_data DriverData;
- 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(DriverData *dp, 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(wchar_t *, HANDLE, HANDLE,
- HANDLE, LPHANDLE, LPDWORD, BOOL,
- LPVOID, wchar_t*, unsigned,
- wchar_t **, int *);
- static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
- static int application_type(const wchar_t* originalName, wchar_t fullPath[MAX_PATH],
- BOOL search_in_path, BOOL handle_quotes,
- int *error_return);
- static void *build_env_block(const erts_osenv_t *env);
- HANDLE erts_service_event;
- static erts_tsd_key_t win32_errstr_key;
- static erts_atomic_t pipe_creation_counter;
- /* 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 int create_file_thread(struct async_io* aio, int mode);
- static void close_active_handle(DriverData *, HANDLE handle);
- static DWORD WINAPI threaded_handle_closer(LPVOID param);
- 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 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(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_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;
- }
- UWord
- erts_sys_get_page_size(void)
- {
- SYSTEM_INFO info;
- GetSystemInfo(&info);
- return (UWord)info.dwPageSize;
- }
- Uint
- erts_sys_misc_mem_sz(void)
- {
- Uint res = (Uint) erts_check_io_size();
- res += (Uint) erts_atomic_read_mb(&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 == ERTS_ERROR_EXIT)
- ConWaitForExit();
- else
- ConNormalExit();
- }
- void erl_sys_args(int* argc, char** argv)
- {
- char *event_name;
- erts_sys_env_init();
- 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
- }
- /*
- * Function returns 1 if we can read from all values in between
- * start and stop.
- */
- int
- erts_sys_is_area_readable(char *start, char *stop) {
- volatile char tmp;
- __try
- {
- while(start < stop) {
- tmp = *start;
- start++;
- }
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- return 0;
- }
- return 1;
- }
- int erts_sys_prepare_crash_dump(int secs)
- {
- Port *heart_port;
- Eterm heap[3];
- Eterm *hp = heap;
- Eterm list = NIL;
- heart_port = erts_get_heart_port();
- if (heart_port) {
- list = CONS(hp, make_small(8), list); hp += 2;
- /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
- erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
- heart_port->common.id, list, NULL);
- return 1;
- }
- /* Windows - free file descriptors are hopefully available */
- /* Alarm not used on windows */
- return 0;
- }
- int erts_set_signal(Eterm signal, Eterm type) {
- return 0;
- }
- static void
- init_console(void)
- {
- 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(void)
- {
- 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(int* argc, char* argv[], const char *option)
- {
- 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(char *namebuf, unsigned size)
- {
- 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 DF_THREAD_FLUSHED 16 /* The thread should exit. */
- #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. */
- int async_io_active; /* if true, a close of the file will signal the event in ov */
- 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.
- */
- HANDLE flushEvent; /* Used to signal that a flush should be done. */
- HANDLE flushReplyEvent; /* Used to signal that a flush has been done. */
- 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.
- */
- DriverData *dp; /* Pointer to driver data struct which
- this struct is part of */
- } 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.
- */
- 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 handle. */
- int packet_bytes; /* 0: continuous 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 */
- erts_atomic32_t refc; /* References to this struct */
- };
- /* 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*, ErlDrvSizeT);
- 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
- };
- static ERTS_INLINE void
- refer_driver_data(DriverData *dp)
- {
- #ifdef DEBUG
- erts_aint32_t refc = erts_atomic32_inc_read_nob(&dp->refc);
- ASSERT(refc > 1);
- #else
- erts_atomic32_inc_nob(&dp->refc);
- #endif
- }
- static ERTS_INLINE void
- unrefer_driver_data(DriverData *dp)
- {
- erts_aint32_t refc = erts_atomic32_dec_read_mb(&dp->refc);
- ASSERT(refc >= 0);
- if (refc == 0)
- driver_free(dp);
- }
- /*
- * Initialises a DriverData structure.
- *
- * Results: Returns a pointer to a DriverData structure, or NULL
- * if the initialsation failed.
- */
- static DriverData*
- new_driver_data(ErlDrvPort port_num, int packet_bytes, int wait_objs_required, int use_threads)
- {
- DriverData* dp;
- DEBUGF(("new_driver_data(%p, pb %d)\n", port_num, packet_bytes));
- dp = driver_alloc(sizeof(DriverData));
- if (!dp)
- return NULL;
- /*
- * 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.
- */
- erts_atomic32_init_nob(&dp->refc, 1);
- dp->bytesInBuffer = 0;
- dp->totalNeeded = packet_bytes;
- dp->inBufSize = PORT_BUFSIZ;
- dp->inbuf = DRV_BUF_ALLOC(dp->inBufSize);
- if (dp->inbuf == NULL)
- goto buf_alloc_error;
- erts_atomic_add_nob(&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, &dp->in, use_threads) == -1)
- goto async_io_error1;
- if (init_async_io(dp, &dp->out, use_threads) == -1)
- goto async_io_error2;
- return dp;
- async_io_error2:
- release_async_io(&dp->in, dp->port_num);
- async_io_error1:
- release_async_io(&dp->out, dp->port_num);
- buf_alloc_error:
- driver_free(dp);
- return NULL;
- }
- static void
- release_driver_data(DriverData* dp)
- {
- #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, 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, dp->out.ov.hEvent);
- dp->out.ov.hEvent = NULL;
- }
- DEBUGF(("...done\n"));
- }
- }
- if (dp->inbuf != NULL) {
- ASSERT(erts_atomic_read_nob(&sys_misc_mem_sz) >= dp->inBufSize);
- erts_atomic_add_nob(&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_atomic_read_nob(&sys_misc_mem_sz) >= dp->outBufSize);
- erts_atomic_add_nob(&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.
- */
- unrefer_driver_data(dp);
- }
- struct handles_to_be_closed {
- HANDLE handles[MAXIMUM_WAIT_OBJECTS];
- DriverData *drv_data[MAXIMUM_WAIT_OBJECTS];
- unsigned cnt;
- };
- static struct handles_to_be_closed* htbc_curr = NULL;
- CRITICAL_SECTION htbc_lock;
- static void close_active_handle(DriverData *dp, 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->drv_data[0] = NULL;
- htbc->cnt = 1;
- thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_handle_closer, htbc, 0, &tid);
- CloseHandle(thread);
- }
- i = htbc->cnt++;
- htbc->handles[i] = handle;
- htbc->drv_data[i] = dp;
- if (dp)
- refer_driver_data(dp); /* Need to keep driver data until we have
- closed the event; outstanding operation
- might write into it.. */
- 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) {
- int move_ix;
- CloseHandle(htbc->handles[ix]);
- if (htbc->drv_data[ix])
- unrefer_driver_data(htbc->drv_data[ix]);
- move_ix = --htbc->cnt;
- htbc->handles[ix] = htbc->handles[move_ix];
- htbc->drv_data[ix] = htbc->drv_data[move_ix];
- }
- }
- 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]);
- ASSERT(!htbc->drv_data[0]);
- erts_free(ERTS_ALC_T_DRV_TAB, htbc);
- DEBUGF(("threaded_handle_closer %p terminating\r\n", htbc));
- return 0;
- }
- /*
- * 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(DriverData* dp, HANDLE ifd, HANDLE ofd, int read_write, int report_exit)
- {
- 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) dp;
- }
- static ErlDrvData
- reuse_driver_data(DriverData *dp, HANDLE ifd, HANDLE ofd, int read_write, ErlDrvPort port_num)
- {
- int result;
- dp->port_num = port_num;
- dp->in.fd = ifd;
- dp->out.fd = ofd;
- dp->report_exit = 0;
- 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);
- }
- 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) dp;
- }
- /*
- * Initialises an AsyncIo structure.
- */
- static int
- init_async_io(DriverData *dp, AsyncIo* aio, int use_threads)
- {
- aio->dp = dp;
- 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->flushEvent = NULL;
- aio->flushReplyEvent = NULL;
- aio->pendingError = 0;
- aio->bytesTransferred = 0;
- aio->async_io_active = 0;
- aio->ov.hEvent = CreateManualEvent(FALSE);
- if (aio->ov.hEvent == NULL)
- return -1;
- if (use_threads) {
- OV_BUFFER_PTR(aio) = NULL;
- OV_NUM_TO_READ(aio) = 0;
- aio->ioAllowed = CreateAutoEvent(FALSE);
- if (aio->ioAllowed == NULL)
- return -1;
- aio->flushEvent = CreateAutoEvent(FALSE);
- if (aio->flushEvent == NULL)
- return -1;
- aio->flushReplyEvent = CreateAutoEvent(FALSE);
- if (aio->flushReplyEvent == 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)
- CloseHandle(aio->ov.hEvent);
- aio->ov.hEvent = NULL;
- if (aio->ioAllowed != NULL)
- CloseHandle(aio->ioAllowed);
- aio->ioAllowed = NULL;
- if (aio->flushEvent != NULL)
- CloseHandle(aio->flushEvent);
- aio->flushEvent = NULL;
- if (aio->flushReplyEvent != NULL)
- CloseHandle(aio->flushReplyEvent);
- aio->flushReplyEvent = 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(AsyncIo* aio, LPVOID buf, DWORD numToRead)
- {
- 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 {
- aio->async_io_active = 1; /* Will get 0 when the event actually happened */
- 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(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 {
- aio->async_io_active = 1; /* Will get 0 when the event actually happened */
- if (WriteFile(aio->fd, buf, numToWrite,
- &aio->bytesTransferred, &aio->ov)) {
- DEBUGF(("async_write_file: WriteFile() suceeded: %d bytes\n",
- aio->bytesTransferred));
- aio->async_io_active = 0; /* The event will not be signalled */
- 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(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(void)
- {
- int i;
- #if 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
- return 0;
- }
- static ErlDrvData
- spawn_start(ErlDrvPort port_num, char* utf8_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. */
- DWORD pid;
- 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};
- int errno_return = -1;
- wchar_t *name;
- int len;
- 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.
- */
- if ((len = MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, NULL, 0)) > 0) {
- name = erts_alloc(ERTS_ALC_T_TMP, len*sizeof(wchar_t));
- MultiByteToWideChar(CP_UTF8, 0, utf8_name, -1, name, len);
- } else { /* Not valid utf-8, just convert byte to wchar */
- int i;
- len = strlen(utf8_name);
- name = erts_alloc(ERTS_ALC_T_TMP, (1+len)*sizeof(wchar_t));
- for(i=0; i<len; i++) {
- name[i] = (wchar_t) utf8_name[i];
- }
- name[i] = L'\0';
- }
- DEBUGF(("Spawning \"%S\"\n", name));
- {
- void *environment_block = build_env_block(&opts->envir);
- ok = create_child_process(name,
- hChildStdin,
- hChildStdout,
- hChildStderr,
- &dp->port_pid,
- &pid,
- opts->hide_window,
- environment_block,
- (wchar_t *) opts->wd,
- opts->spawn_type,
- (wchar_t **) opts->argv,
- &errno_return);
- CloseHandle(hChildStdin);
- CloseHandle(hChildStdout);
- if (close_child_stderr && hChildStderr != INVALID_HANDLE_VALUE &&
- hChildStderr != 0) {
- CloseHandle(hChildStderr);
- }
- erts_free(ERTS_ALC_T_TMP, environment_block);
- erts_free(ERTS_ALC_T_TMP, name);
- }
- 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) {
- /* We assume that this cannot generate a negative number */
- erl_drv_set_os_pid(port_num, pid);
- }
- }
-
- 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;
- }
- struct __build_env_state {
- WCHAR *next_variable;
- };
- static void build_env_foreach(void *_state, const erts_osenv_data_t *key,
- const erts_osenv_data_t *value)
- {
- struct __build_env_state *state = (struct __build_env_state*)(_state);
- sys_memcpy(state->next_variable, key->data, key->length);
- state->next_variable += (int)key->length / sizeof(WCHAR);
- *state->next_variable++ = L'=';
- sys_memcpy(state->next_variable, value->data, value->length);
- state->next_variable += (int)value->length / sizeof(WCHAR);
- *state->next_variable++ = L'\0';
- }
- /* Builds an environment block suitable for CreateProcessW. */
- static void *build_env_block(const erts_osenv_t *env) {
- struct __build_env_state build_state;
- WCHAR *env_block;
- env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
- (env->variable_count * sizeof(L"=\0") + sizeof(L'\0')));
- build_state.next_variable = env_block;
- erts_osenv_foreach_native(env, &build_state, build_env_foreach);
- (*build_state.next_variable) = L'\0';
- return env_block;
- }
- static int
- create_file_thread(AsyncIo* aio, int mode)
- {
- DWORD tid; /* Id for thread. */
- refer_driver_data(aio->dp);
- aio->thread = (HANDLE)
- _beginthreadex(NULL, 0,
- (mode & DO_WRITE) ? threaded_writer : threaded_reader,
- aio, 0, &tid);
- if (aio->thread != (HANDLE) -1)
- return 1;
- unrefer_driver_data(aio->dp);
- return 0;
- }
- /*
- * 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
- * command line should have been prepared by _setargv for the main function
- */
- int parse_command(wchar_t* cmd){
- #define NORMAL 2
- #define STRING 1
- #define STOP 0
- int i =0;
- int state = NORMAL;
- while (cmd[i]) {
- switch (cmd[i]) {
- case L'"':
- if (state == NORMAL)
- state = STRING;
- else
- state = NORMAL;
- break;
- case L'\\':
- if ((state == STRING) && (cmd[i+1]==L'"'))
- i++;
- break;
- case L' ':
- if (state == NORMAL)
- state = STOP;
- break;
- default:
- break;
- }
- if (state == STOP) {
- return i;
- }
- i++;
- }
- return i;
- }
- /*
- * Translating of command line arguments to correct format. In the examples
- * below the '' are not part of the actual string.
- * 'io:format("hello").' -> 'io:format(\"hello\").'
- * 'io:format("is anybody in there?").' -> '"io:format(\"is anybody in there?\")."'
- * 'Just nod if you can hear me.' -> '"Just nod if you can hear me."'
- * 'Is there ""anyone at home?' -> '"Is there \"\"anyone at home?"'
- * 'Relax."' -> 'Relax.\"'
- *
- * If new == NULL we just calculate the length.
- *
- * The reason for having to quote all of the is because CreateProcessW removes
- * one level of escaping since it takes a single long command line rather
- * than the argument chunks that unix uses.
- */
- static int escape_and_quote(wchar_t *str, wchar_t *new, BOOL *quoted) {
- int i, j = 0;
- if (new == NULL)
- *quoted = FALSE;
- else if (*quoted)
- new[j++] = L'"';
- for ( i = 0; str[i] != L'\0'; i++,j++) {
- if (str[i] == L' ' && new == NULL && *quoted == FALSE) {
- *quoted = TRUE;
- j++;
- }
- /* check if we have to escape quotes */
- if (str[i] == L'"') {
- if (new) new[j] = L'\\';
- j++;
- }
- if (new) new[j] = str[i];
- }
- if (*quoted) {
- if (new) new[j] = L'"';
- j++;
- }
- return j;
- }
- /*
- *----------------------------------------------------------------------
- *
- * 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
- (
- wchar_t *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 Process handle. */
- LPDWORD pdwID, /* Pointer to variable to received Process ID */
- BOOL hide, /* Hide the window unconditionally. */
- LPVOID env, /* Environment for the child */
- wchar_t *wd, /* Working dir for the child */
- unsigned st, /* Flags for spawn, tells us how to interpret origcmd */
- wchar_t **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;
- wchar_t *newcmdline = NULL;
- int cmdlength;
- wchar_t* thecommand;
- wchar_t* appname = NULL;
- HANDLE hProcess = GetCurrentProcess();
- STARTUPINFOW siStartInfo = {0};
- wchar_t execPath[MAX_PATH];
- *errno_return = -1;
- siStartInfo.cb = sizeof(STARTUPINFOW);
- siStartInfo.dwFlags = STARTF_USESTDHANDLES;
- siStartInfo.hStdInput = hStdin;
- siStartInfo.hStdOutput = hStdout;
- siStartInfo.hStdError = hStderr;
- if (st != ERTS_SPAWN_EXECUTABLE) {
- /*
- * Parse out the program name from the command line (it can be quoted and
- * contain spaces).
- */
- cmdlength = parse_command(origcmd);
- newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (MAX_PATH+wcslen(origcmd)-cmdlength)*sizeof(wchar_t));
- thecommand = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (cmdlength+1)*sizeof(wchar_t));
- wcsncpy(thecommand, origcmd, cmdlength);
- thecommand[cmdlength] = L'\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] = L'\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;
- wcscat(newcmdline, L"cmd.exe /c ");
- } else if (hide) {
- DEBUGF(("hiding window\n"));
- siStartInfo.wShowWindow = SW_HIDE;
- siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = 0;
- }
- wcscat(newcmdline, execPath);
- wcscat(newcmdline, origcmd+cmdlength);
- DEBUGF(("Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
- ok = CreateProcessW(appname,
- newcmdline,
- NULL,
- NULL,
- TRUE,
- createFlags | staticCreateFlags |
- CREATE_UNICODE_ENVIRONMENT,
- env,
- wd,
- &siStartInfo,
- &piProcInfo);
- } else { /* ERTS_SPAWN_EXECUTABLE, filename and args are in unicode ({utf16,little}) */
- int run_cmd = 0;
- applType = application_type(origcmd, 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_t cmdPath[MAX_PATH];
- int cmdType;
- cmdType = application_type(L"cmd.exe", cmdPath, TRUE, FALSE, errno_return);
- if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
- return FALSE;
- }
- appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(cmdPath)+1)*sizeof(wchar_t));
- wcscpy(appname,cmdPath);
- } else {
- appname = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, (wcslen(execPath)+1)*sizeof(wchar_t));
- wcscpy(appname, execPath);
- }
- if (argv == NULL) {
- BOOL orig_need_q;
- wchar_t *ptr;
- int ocl = escape_and_quote(execPath, NULL, &orig_need_q);
- if (run_cmd) {
- newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
- (ocl + 1 + 11)*sizeof(wchar_t));
- memcpy(newcmdline,L"cmd.exe /c ",11*sizeof(wchar_t));
- ptr = newcmdline + 11;
- } else {
- newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP,
- (ocl + 1)*sizeof(wchar_t));
- ptr = (wchar_t *) newcmdline;
- }
- ptr += escape_and_quote(execPath, ptr, &orig_need_q);
- ptr[0] = L'\0';
- } else {
- int sum = 0;
- BOOL *qte = NULL;
- wchar_t **ar = argv;
- wchar_t *n;
- wchar_t *save_arg0 = NULL;
- if (argv[0] == (wchar_t *) erts_default_arg0 || run_cmd) {
- save_arg0 = argv[0];
- argv[0] = execPath;
- }
- if (run_cmd) {
- sum += 11; /* cmd.exe /c */
- }
- while (*ar != NULL) ar++;
- qte = erts_alloc(ERTS_ALC_T_TMP, (ar - argv)*sizeof(BOOL));
- ar = argv;
- while (*ar != NULL) {
- sum += escape_and_quote(*ar,NULL,qte+(ar - argv));
- sum++; /* space */
- ++ar;
- }
- ar = argv;
- newcmdline = (wchar_t *) erts_alloc(ERTS_ALC_T_TMP, sum*sizeof(wchar_t));
- n = newcmdline;
- if (run_cmd) {
- memcpy(n,L"cmd.exe /c ",11*sizeof(wchar_t));
- n += 11;
- }
- while (*ar != NULL) {
- n += escape_and_quote(*ar,n,qte+(ar - argv));
- *n++ = L' ';
- ++ar;
- }
- *(n-1) = L'\0'; /* overwrite last space with '\0' */
- if (save_arg0 != NULL) {
- argv[0] = save_arg0;
- }
- erts_free(ERTS_ALC_T_TMP, qte);
- }
-
- DEBUGF((stderr,"Creating child process: %S, createFlags = %d\n", newcmdline, createFlags));
- ok = CreateProcessW((wchar_t *) appname,
- (wchar_t *) newcmdline,
- NULL,
- NULL,
- TRUE,
- createFlags | staticCreateFlags |
- CREATE_UNICODE_ENVIRONMENT,
- env,
- 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;
- *pdwID = piProcInfo.dwProcessId;
- if (applType == APPL_DOS) {
- WaitForSingleObject(hProcess, 50);
- }
-
- 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[256]; /* 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;
- …
Large files files are truncated, but you can click here to view the full file