aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/win32/sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/win32/sys.c')
-rw-r--r--erts/emulator/sys/win32/sys.c3093
1 files changed, 3093 insertions, 0 deletions
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
new file mode 100644
index 0000000000..3194493ac8
--- /dev/null
+++ b/erts/emulator/sys/win32/sys.c
@@ -0,0 +1,3093 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. 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"
+
+
+void erts_sys_init_float(void);
+
+void erl_start(int, char**);
+void erl_exit(int n, char*, _DOTS_);
+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 FUNCTION(BOOL, CreateChildProcess, (char *, HANDLE, HANDLE,
+ HANDLE, LPHANDLE, BOOL,
+ LPVOID, LPTSTR, unsigned,
+ char **, int *));
+static int create_pipe(LPHANDLE, LPHANDLE, BOOL, BOOL);
+static int ApplicationType(const char* originalName, char 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 ApplicationType is one of */
+#define APPL_NONE 0
+#define APPL_DOS 1
+#define APPL_WIN3X 2
+#define APPL_WIN32 3
+
+static FUNCTION(int, driver_write, (long, HANDLE, byte*, int));
+static void common_stop(int);
+static int create_file_thread(struct async_io* aio, int mode);
+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. */
+
+
+/* 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;
+}
+
+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
+ /* This is a workaround for the fact that CancelIo cant cancel
+ requests issued by another thread and that we still cant use
+ CancelIoEx as that's only availabele in Vista etc. */
+ 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"));
+ WaitForSingleObject(dp->in.ov.hEvent,INFINITE);
+ 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"));
+ WaitForSingleObject(dp->out.ov.hEvent,INFINITE);
+ DEBUGF(("...done\n"));
+ }
+#else
+ if (dp->out.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);
+}
+
+/*
+ * 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;
+
+ 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);
+ ok = CreateChildProcess(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 CreateChildProcess().
+ * 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;
+}
+
+BOOL need_quotes(char *str)
+{
+ int in_quote = 0;
+ int backslashed = 0;
+ int naked_space = 0;
+ while (*str != '\0') {
+ switch (*str) {
+ case '\\' :
+ backslashed = !backslashed;
+ break;
+ case '"':
+ if (backslashed) {
+ backslashed=0;
+ } else {
+ in_quote = !in_quote;
+ }
+ break;
+ case ' ':
+ backslashed = 0;
+ if (!(backslashed || in_quote)) {
+ naked_space++;
+ }
+ break;
+ default:
+ backslashed = 0;
+ }
+ ++str;
+ }
+ return (naked_space > 0);
+}
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateChildProcess --
+ *
+ * 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
+CreateChildProcess
+(
+ 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};
+ STARTUPINFO siStartInfo = {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;
+ char execPath[MAX_PATH];
+ int cmdlength;
+ char* thecommand;
+ LPTSTR appname = NULL;
+ HANDLE hProcess = GetCurrentProcess();
+
+ *errno_return = -1;
+
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ 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).
+ */
+ 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 = ApplicationType(thecommand, execPath, TRUE,
+ TRUE, errno_return);
+ DEBUGF(("ApplicationType 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);
+ } else { /* ERTS_SPAWN_EXECUTABLE */
+ int run_cmd = 0;
+ applType = ApplicationType(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) {
+ char cmdPath[MAX_PATH];
+ int cmdType;
+ cmdType = ApplicationType("cmd.exe", cmdPath, TRUE, FALSE, errno_return);
+ if (cmdType == APPL_NONE || cmdType == APPL_DOS) {
+ return FALSE;
+ }
+ appname = (char *) erts_alloc(ERTS_ALC_T_TMP, strlen(cmdPath)+1);
+ strcpy(appname,cmdPath);
+ } else {
+ appname = (char *) erts_alloc(ERTS_ALC_T_TMP, strlen(execPath)+1);
+ strcpy(appname,execPath);
+ }
+ if (argv == NULL) {
+ BOOL orig_need_q = need_quotes(execPath);
+ char *ptr;
+ int ocl = strlen(execPath);
+ if (run_cmd) {
+ newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
+ ocl + ((orig_need_q) ? 3 : 1)
+ + 11);
+ memcpy(newcmdline,"cmd.exe /c ",11);
+ ptr = newcmdline + 11;
+ } else {
+ newcmdline = (char *) erts_alloc(ERTS_ALC_T_TMP,
+ ocl + ((orig_need_q) ? 3 : 1));
+ ptr = newcmdline;
+ }
+ if (orig_need_q) {
+ *ptr++ = '"';
+ }
+ memcpy(ptr,execPath,ocl);
+ ptr += ocl;
+ if (orig_need_q) {
+ *ptr++ = '"';
+ }
+ *ptr = '\0';
+ } else {
+ int sum = 1; /* '\0' */
+ char **ar = argv;
+ char *n;
+ char *save_arg0 = NULL;
+ if (argv[0] == erts_default_arg0 || run_cmd) {
+ save_arg0 = argv[0];
+ argv[0] = execPath;
+ }
+ if (run_cmd) {
+ sum += 11; /* cmd.exe /c */
+ }
+ while (*ar != NULL) {
+ sum += strlen(*ar);
+ if (need_quotes(*ar)) {
+ sum += 2; /* quotes */
+ }
+ sum++; /* space */
+ ++ar;
+ }
+ ar = argv;
+ newcmdline = erts_alloc(ERTS_ALC_T_TMP, sum);
+ n = newcmdline;
+ if (run_cmd) {
+ memcpy(n,"cmd.exe /c ",11);
+ n += 11;
+ }
+ while (*ar != NULL) {
+ int q = need_quotes(*ar);
+ sum = strlen(*ar);
+ if (q) {
+ *n++ = '"';
+ }
+ memcpy(n,*ar,sum);
+ n += sum;
+ if (q) {
+ *n++ = '"';
+ }
+ *n++ = ' ';
+ ++ar;
+ }
+ ASSERT(n > newcmdline);
+ *(n-1) = '\0';
+ if (save_arg0 != NULL) {
+ argv[0] = save_arg0;
+ }
+ }
+
+ }
+ DEBUGF(("Creating child process: %s, createFlags = %d\n", newcmdline, createFlags));
+ ok = CreateProcess(appname,
+ newcmdline,
+ NULL,
+ NULL,
+ TRUE,
+ createFlags | staticCreateFlags,
+ env,
+ wd,
+ &siStartInfo,
+ &piProcInfo);
+
+ 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 ApplicationType
+(
+ 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 order, to
+ * the name, looking for an executable.
+ * NOTE! that we does not support execution of .com programs on Windows NT
+ *
+ *
+ * Using the raw SearchPath() procedure doesn't do quite what is
+ * necessary. If the name of the executable already contains a '.'
+ * character, it will not try appending the specified extension when
+ * searching (in other words, SearchPath will not find the program
+ * "a.b.exe" if the arguments specified "a.b" and ".exe").
+ * So, first look for the file as it is named. Then manually append
+ * the extensions, looking for a match. (')
+ */
+
+ len = strlen(originalName);
+ is_quoted = handle_quotes && len > 0 && originalName[0] == '"' &&
+ originalName[len-1] == '"';
+
+ applType = APPL_NONE;
+ *error_return = ENOENT;
+ for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
+ if(is_quoted) {
+ lstrcpyn(fullPath, originalName+1, MAX_PATH - 7);
+ len = strlen(fullPath);
+ if(len > 0) {
+ fullPath[len-1] = '\0';
+ }
+ } else {
+ lstrcpyn(fullPath, originalName, MAX_PATH - 5);
+ }
+ lstrcat(fullPath, extensions[i]);
+ SearchPath((search_in_path) ? NULL : ".", fullPath, NULL, MAX_PATH, fullPath, &rest);
+
+ /*
+ * Ignore matches on directories or data files, return if identified
+ * a known type.
+ */
+
+ if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
+ continue;
+ }
+
+ ext = strrchr(fullPath, '.');
+ if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) {
+ *error_return = EACCES;
+ applType = APPL_DOS;
+ break;
+ }
+
+ hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ continue;
+ }
+
+ *error_return = EACCES; /* If considered an error,
+ it's an access error */
+ header.e_magic = 0;
+ ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
+ if (header.e_magic != IMAGE_DOS_SIGNATURE) {
+ /*
+ * Doesn't have the magic number for relocatable executables. If
+ * filename ends with .com, assume it's a DOS application anyhow.
+ * Note that we didn't make this assumption at first, because some
+ * supposed .com files are really 32-bit executables with all the
+ * magic numbers and everything.
+ */
+
+ CloseHandle(hFile);
+ if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) {
+ applType = APPL_DOS;
+ break;
+ }
+ continue;
+ }
+ if (header.e_lfarlc != sizeof(header)) {
+ /*
+ * All Windows 3.X and Win32 and some DOS programs have this value
+ * set here. If it doesn't, assume that since it already had the
+ * other magic number it was a DOS application.
+ */
+
+ CloseHandle(hFile);
+ applType = APPL_DOS;
+ break;
+ }
+
+ /*
+ * The DWORD at header.e_lfanew points to yet another magic number.
+ */
+
+ buf[0] = '\0';
+ SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
+ ReadFile(hFile, (void *) buf, 2, &read, NULL);
+ CloseHandle(hFile);
+
+ if ((buf[0] == 'L') && (buf[1] == 'E')) {
+ applType = APPL_DOS;
+ } else if ((buf[0] == 'N') && (buf[1] == 'E')) {
+ applType = APPL_WIN3X;
+ } else if ((buf[0] == 'P') && (buf[1] == 'E')) {
+ applType = APPL_WIN32;
+ } else {
+ continue;
+ }
+ break;
+ }
+
+ if (applType == APPL_NONE) {
+ return APPL_NONE;
+ }
+
+ if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
+ /*
+ * Replace long path name of executable with short path name for
+ * 16-bit applications. Otherwise the application may not be able
+ * to correctly parse its own command line to separate off the
+ * application name from the arguments.
+ */
+
+ GetShortPathName(fullPath, fullPath, MAX_PATH);
+ }
+ if (is_quoted) {
+ /* restore quotes on quoted program name */
+ len = strlen(fullPath);
+ memmove(fullPath+1,fullPath,len);
+ fullPath[0]='"';
+ fullPath[len+1]='"';
+ fullPath[len+2]='\0';
+ }
+ return applType;
+}
+
+/*
+ * Thread function used to emulate overlapped reading.
+ */
+
+DWORD WINAPI
+threaded_reader(LPVOID param)
+{
+ AsyncIo* aio = (AsyncIo *) param;
+ HANDLE thread = GetCurrentThread();
+ char* buf;
+ DWORD numToRead;
+
+ for (;;) {
+ WaitForSingleObject(aio->ioAllowed, INFINITE);
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ buf = OV_BUFFER_PTR(aio);
+ numToRead = OV_NUM_TO_READ(aio);
+ aio->pendingError = 0;
+ if (!ReadFile(aio->fd, buf, numToRead, &aio->bytesTransferred, NULL))
+ aio->pendingError = GetLastError();
+ else if (aio->flags & DF_XLAT_CR) {
+ char *s;
+ int n;
+
+ n = aio->bytesTransferred;
+ for (s = buf; s < buf+n; s++) {
+ if (*s == '\r') {
+ if (s < buf + n - 1 && s[1] == '\n') {
+ memmove(s, s+1, (buf+n - s - 1));
+ --n;
+ } else {
+ *s = '\n';
+ }
+ }
+ }
+ aio->bytesTransferred = n;
+ }
+ SetEvent(aio->ov.hEvent);
+ if ((aio->flags & DF_XLAT_CR) == 0 && aio->bytesTransferred == 0) {
+ break;
+ }
+ if (aio->pendingError != NO_ERROR) {
+ break;
+ }
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Thread function used to emulate overlapped writing
+ */
+
+DWORD WINAPI
+threaded_writer(LPVOID param)
+{
+ AsyncIo* aio = (AsyncIo *) param;
+ HANDLE thread = GetCurrentThread();
+ char* buf;
+ DWORD numToWrite;
+ int ok;
+
+ for (;;) {
+ WaitForSingleObject(aio->ioAllowed, INFINITE);
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ buf = OV_BUFFER_PTR(aio);
+ numToWrite = OV_NUM_TO_READ(aio);
+ aio->pendingError = 0;
+ ok = WriteFile(aio->fd, buf, numToWrite, &aio->bytesTransferred, NULL);
+ if (!ok) {
+ aio->pendingError = GetLastError();
+ if (aio->pendingError == ERROR_INVALID_HANDLE &&
+ aio->flags & DF_DROP_IF_INVH) {
+ /* This is standard error and we'we got an
+ invalid standard error FD (non-inheritable) from parent.
+ Just drop the message and be happy. */
+ aio->pendingError = 0;
+ aio->bytesTransferred = numToWrite;
+ } else if (aio->pendingError == ERROR_NOT_ENOUGH_MEMORY) {
+ /* This could be a console, which limits utput to 64kbytes,
+ which might translate to less on a unicode system.
+ Try 16k chunks and see if it works before giving up. */
+ int done = 0;
+ DWORD transferred;
+ aio->pendingError = 0;
+ aio->bytesTransferred = 0;
+ ok = 1;
+ while (ok && (numToWrite - done) > 0x4000) {
+ ok = WriteFile(aio->fd, buf + done, 0x4000, &transferred, NULL);
+ aio->bytesTransferred += transferred;
+ done += 0x4000;
+ }
+ if (ok && (numToWrite - done) > 0) {
+ ok = WriteFile(aio->fd, buf + done, (numToWrite - done),
+ &transferred, NULL);
+ aio->bytesTransferred += transferred;
+ }
+ if (!ok) {
+ aio->pendingError = GetLastError();
+ }
+ }
+ }
+ SetEvent(aio->ov.hEvent);
+ if (aio->pendingError != NO_ERROR || aio->bytesTransferred == 0)
+ break;
+ if (aio->flags & DF_EXIT_THREAD)
+ break;
+ }
+ CloseHandle(aio->fd);
+ aio->fd = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static HANDLE
+translate_fd(int fd)
+{
+ DWORD access;
+ HANDLE handle;
+
+ switch (fd) {
+ case 0:
+ access = GENERIC_READ;
+ handle = GetStdHandle(STD_INPUT_HANDLE);
+ break;
+ case 1:
+ access = GENERIC_WRITE;
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ break;
+ case 2:
+ access = GENERIC_WRITE;
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ break;
+ default:
+ return (HANDLE) fd;
+ }
+ DEBUGF(("translate_fd(%d) -> std(%d)\n", fd, handle));
+
+ if (handle == INVALID_HANDLE_VALUE || handle == 0) {
+ handle = CreateFile("nul", access, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+ DEBUGF(("translate_fd(%d) -> %d\n", fd, handle));
+ return handle;
+}
+
+static ErlDrvData
+fd_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ DriverData* dp;
+ int is_std_error = (opts->ofd == 2);
+
+ opts->ifd = (int) translate_fd(opts->ifd);
+ opts->ofd = (int) translate_fd(opts->ofd);
+ if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, TRUE)) == NULL)
+ return ERL_DRV_ERROR_GENERAL;
+
+ if (!create_file_thread(&dp->in, DO_READ)) {
+ dp->port_num = PORT_FREE;
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ if (!create_file_thread(&dp->out, DO_WRITE)) {
+ dp->port_num = PORT_FREE;
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ fd_driver_input = &(dp->in);
+ dp->in.flags = DF_XLAT_CR;
+ if (is_std_error) {
+ dp->out.flags |= DF_DROP_IF_INVH; /* Just drop messages if stderror
+ is an invalid handle */
+ }
+ return set_driver_data(dp, opts->ifd, opts->ofd, opts->read_write, 0);
+}
+
+static void fd_stop(ErlDrvData d)
+{
+ int fd = (int)d;
+ /*
+ * I don't know a clean way to terminate the threads
+ * (TerminateThread() doesn't release the stack),
+ * so will we'll let the threads live. Normally, the fd
+ * driver is only used to support the -oldshell option,
+ * so this shouldn't be a problem in practice.
+ *
+ * Since we will not attempt to terminate the threads,
+ * better not close the input or output files either.
+ */
+
+ driver_data[fd].in.thread = (HANDLE) -1;
+ driver_data[fd].out.thread = (HANDLE) -1;
+ driver_data[fd].in.fd = INVALID_HANDLE_VALUE;
+ driver_data[fd].out.fd = INVALID_HANDLE_VALUE;
+
+ /*return */ common_stop(fd);
+}
+
+static ErlDrvData
+vanilla_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ HANDLE ofd,ifd;
+ DriverData* dp;
+ DWORD access; /* Access mode: GENERIC_READ, GENERIC_WRITE. */
+ DWORD crFlags;
+ HANDLE this_process = GetCurrentProcess();
+
+ access = 0;
+ if (opts->read_write == DO_READ)
+ access |= GENERIC_READ;
+ if (opts->read_write == DO_WRITE)
+ access |= GENERIC_WRITE;
+
+ if (opts->read_write == DO_READ)
+ crFlags = OPEN_EXISTING;
+ else if (opts->read_write == DO_WRITE)
+ crFlags = CREATE_ALWAYS;
+ else
+ crFlags = OPEN_ALWAYS;
+
+ if ((dp = new_driver_data(port_num, opts->packet_bytes, 2, FALSE)) == NULL)
+ return ERL_DRV_ERROR_GENERAL;
+ ofd = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (!DuplicateHandle(this_process, (HANDLE) ofd,
+ this_process, &ifd, 0,
+ FALSE, DUPLICATE_SAME_ACCESS)) {
+ CloseHandle(ofd);
+ ofd = INVALID_HANDLE_VALUE;
+ }
+ if (ofd == INVALID_HANDLE_VALUE)
+ return ERL_DRV_ERROR_GENERAL;
+ return set_driver_data(dp, ifd, ofd, opts->read_write,0);
+}
+
+static void
+stop(ErlDrvData index)
+{
+ common_stop((int)index);
+}
+
+static void common_stop(int index)
+{
+ DriverData* dp = driver_data+index;
+
+ DEBUGF(("common_stop(%d)\n", index));
+
+ if (dp->in.ov.hEvent != NULL) {
+ (void) driver_select(dp->port_num,
+ (ErlDrvEvent)dp->in.ov.hEvent,
+ ERL_DRV_READ, 0);
+ }
+ if (dp->out.ov.hEvent != NULL) {
+ (void) driver_select(dp->port_num,
+ (ErlDrvEvent)dp->out.ov.hEvent,
+ ERL_DRV_WRITE, 0);
+ }
+
+ if (dp->out.thread == (HANDLE) -1 && dp->in.thread == (HANDLE) -1) {
+ release_driver_data(dp);
+ } else {
+ /*
+ * If there are read or write threads, start a thread which will
+ * wait for them to finish.
+ */
+ HANDLE thread;
+ DWORD tid;
+ dp->port_num = PORT_EXITING;
+ thread = (HANDLE *) _beginthreadex(NULL, 0, threaded_exiter, dp, 0, &tid);
+ CloseHandle(thread);
+ }
+}
+
+DWORD WINAPI
+threaded_exiter(LPVOID param)
+{
+ DriverData* dp = (DriverData *) param;
+ HANDLE handles[2];
+ int i;
+
+ /*
+ * Ask the threads to terminated.
+ *
+ * Note that we can't reliable test the state of the ioAllowed event,
+ * because it is an auto reset event. Therefore, always set the
+ * exit flag and signal the event.
+ */
+
+ i = 0;
+ if (dp->out.thread != (HANDLE) -1) {
+ dp->out.flags = DF_EXIT_THREAD;
+ SetEvent(dp->out.ioAllowed);
+ handles[i++] = dp->out.thread;
+ }
+ if (dp->in.thread != (HANDLE) -1) {
+ dp->in.flags = DF_EXIT_THREAD;
+ SetEvent(dp->in.ioAllowed);
+ handles[i++] = dp->in.thread;
+ }
+
+ /*
+ * If we were lucky, the following happened above:
+ * 1) The output thread terminated (and closed the pipe).
+ * 2) As a consequence of that, the port program received
+ * EOF on its standard input.
+ * 3) Hopefully, because of (2), the port program terminated.
+ * 4) Because of (3), the input thread terminated.
+ *
+ * But this might need some time; therefore, we must wait for
+ * both threads to terminate.
+ */
+
+ if (i > 0) {
+ switch (WaitForMultipleObjects(i, handles, TRUE, 5000)) {
+ case WAIT_TIMEOUT:
+ DEBUGF(("Timeout waiting for %d threads failed\n", i));
+ break;
+ case WAIT_FAILED:
+ DEBUGF(("Wait for %d threads failed: %s\n",
+ i, win32_errorstr(GetLastError())));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Wait for threads to terminate didn't help. Now use some force.
+ * TerminateThread() is *not* a good idea, because it doesn't clean
+ * up the thread's stack.
+ *
+ * Instead we well terminate the port program and wait for the
+ * threads to terminate themselves when they receive end of file.
+ */
+
+ if (dp->out.thread != (HANDLE) -1) {
+ int error;
+
+ if (WaitForSingleObject(dp->out.thread, 0) == WAIT_OBJECT_0) {
+ CloseHandle(dp->out.thread);
+ dp->out.thread = (HANDLE) -1;
+ } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
+ DEBUGF(("Killing port process 0x%x (output thread)\n", dp->port_pid));
+ TerminateProcess(dp->port_pid, 0);
+ if (!CloseHandle(dp->port_pid))
+ DEBUGF(("Failed to close output handle!!!\n"));
+ dp->port_pid = INVALID_HANDLE_VALUE;
+ DEBUGF(("Waiting for output thread 0x%x to finish\n", dp->out.thread));
+ error = WaitForSingleObject(dp->out.thread, INFINITE);
+ }
+ }
+
+ if (dp->in.thread != (HANDLE) -1) {
+ if (WaitForSingleObject(dp->in.thread, 0) == WAIT_OBJECT_0) {
+ CloseHandle(dp->in.thread);
+ dp->in.thread = (HANDLE) -1;
+ } else if (dp->port_pid != INVALID_HANDLE_VALUE) {
+ DEBUGF(("Killing port process 0x%x (input thread)\n", dp->port_pid));
+ TerminateProcess(dp->port_pid, 0);
+ if (!CloseHandle(dp->port_pid))
+ DEBUGF(("Failed to close input handle!!!\n"));
+ dp->port_pid = INVALID_HANDLE_VALUE;
+
+ DEBUGF(("Waiting for input thread 0x%x to finish\n", dp->in.thread));
+ switch (WaitForSingleObject(dp->in.thread, INFINITE)) {
+ case WAIT_OBJECT_0:
+ CloseHandle(dp->in.thread);
+ dp->in.thread = (HANDLE) -1;
+ break;
+ default:
+ DEBUGF(("Wait for input thread to finish failed: %s\n",
+ win32_errorstr(GetLastError())));
+ break;
+ }
+ }
+ }
+
+ release_driver_data(dp);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------
+ * output --
+ * Outputs data from Erlang to the port program.
+ *
+ * Results:
+ * Returns the actual number of bytes written (including the
+ * packet header) or -1 if an error occurred.
+ * ----------------------------------------------------------------------
+ */
+
+static void
+output(ErlDrvData drv_data, char* buf, int len)
+/* long drv_data; /* The slot to use in the driver data table.
+ * For Windows NT, this is *NOT* a file handle.
+ * The handle is found in the driver data.
+ */
+/* char *buf; /* Pointer to data to write to the port program. */
+/* int len; /* Number of bytes to write. */
+{
+ DriverData* dp;
+ int pb; /* The header size for this port. */
+ int port_num; /* The actual port number (for diagnostics). */
+ char* current;
+
+ dp = driver_data + (int)drv_data;
+ if ((port_num = dp->port_num) == -1)
+ return ; /*-1;*/
+
+ pb = dp->packet_bytes;
+
+ if ((pb+len) == 0)
+ return ; /* 0; */
+
+ /*
+ * Check that the message can be sent with given header length.
+ */
+
+ if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) {
+ driver_failure_posix(port_num, EINVAL);
+ return ; /* -1; */
+ }
+
+ /*
+ * Allocate memory for both the message and the header.
+ */
+
+ ASSERT(dp->outbuf == NULL);
+ ASSERT(dp->outBufSize == 0);
+
+ ASSERT(!dp->outbuf);
+ dp->outbuf = DRV_BUF_ALLOC(pb+len);
+ if (!dp->outbuf) {
+ driver_failure_posix(port_num, ENOMEM);
+ return ; /* -1; */
+ }
+
+ dp->outBufSize = pb+len;
+ erts_smp_atomic_add(&sys_misc_mem_sz, dp->outBufSize);
+
+ /*
+ * Store header bytes (if any).
+ */
+
+ current = dp->outbuf;
+ switch (pb) {
+ case 4:
+ *current++ = (len >> 24) & 255;
+ *current++ = (len >> 16) & 255;
+ case 2:
+ *current++ = (len >> 8) & 255;
+ case 1:
+ *current++ = len & 255;
+ }
+
+ /*
+ * Start the write.
+ */
+
+ if (len)
+ memcpy(current, buf, len);
+
+ if (!async_write_file(&dp->out, dp->outbuf, pb+len)) {
+ set_busy_port(port_num, 1);
+ } else {
+ dp->out.ov.Offset += pb+len; /* For vanilla driver. */
+ /* XXX OffsetHigh should be changed too. */
+ 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;
+ }
+ /*return 0;*/
+}
+
+
+/* ----------------------------------------------------------------------
+ * ready_input --
+ * This function is called (indirectly) from check_io() when an
+ * event object has been signaled, indicating that there is
+ * something to read on the corresponding file handle.
+ *
+ * If the port is working in the continous stream mode (packet_bytes == 0),
+ * whatever data read will be sent straight to Erlang.
+ *
+ * Results:
+ * Always 0.
+ * ----------------------------------------------------------------------
+ */
+
+static void
+ready_input(ErlDrvData drv_data, ErlDrvEvent ready_event)
+/* long drv_data; /* Driver data. */
+/* HANDLE ready_event; /* The handle for the ready event. */
+{
+ int error = 0; /* The error code (assume initially no errors). */
+ DWORD bytesRead; /* Number of bytes read. */
+ DriverData* dp;
+ int pb;
+
+ dp = driver_data+(int)drv_data;
+ pb = dp->packet_bytes;
+#ifdef ERTS_SMP
+ if(dp->in.thread == (HANDLE) -1) {
+ dp->in.async_io_active = 0;
+ }
+#endif
+ DEBUGF(("ready_input: dp %p, event 0x%x\n", dp, ready_event));
+
+ /*
+ * Evaluate the result of the overlapped read.
+ */
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_read_begin(dp->in.ov.hEvent);
+#endif
+
+ error = get_overlapped_result(&dp->in, &bytesRead, TRUE);
+
+#ifdef HARD_POLL_DEBUG
+ poll_debug_read_done(dp->in.ov.hEvent,bytesRead);
+#endif
+
+ if (error == NO_ERROR) {
+ if (pb == 0) { /* Continous stream. */
+#ifdef DEBUG
+ DEBUGF(("ready_input: %d: ", bytesRead));
+ erl_bin_write(dp->inbuf, 16, bytesRead);
+ DEBUGF(("\n"));
+#endif
+ driver_output(dp->port_num, dp->inbuf, bytesRead);
+ } else { /* Packet mode */
+ dp->bytesInBuffer += bytesRead;
+
+ /*
+ * Loop until we've exhausted the data in the buffer.
+ */
+
+ for (;;) {
+
+ /*
+ * Check for completion of a header read.
+ */
+
+ if (dp->bytesInBuffer >= dp->totalNeeded &&
+ dp->totalNeeded == pb) {
+
+ /*
+ * We have successfully read the packet header
+ * (and perhaps even the packet). Get the packet size
+ * from the header and update dp->totalNeeded to include
+ * the packet size.
+ */
+
+ int packet_size = 0;
+ unsigned char *header = (unsigned char *) dp->inbuf;
+
+ switch (pb) {
+ case 4:
+ packet_size = (packet_size << 8) | *header++;
+ packet_size = (packet_size << 8) | *header++;
+ case 2:
+ packet_size = (packet_size << 8) | *header++;
+ case 1:
+ packet_size = (packet_size << 8) | *header++;
+ }
+
+ dp->totalNeeded += packet_size;
+
+ /*
+ * Make sure that the receive buffer is big enough.
+ */
+
+ if (dp->inBufSize < dp->totalNeeded) {
+ char* new_buf;
+
+ new_buf = DRV_BUF_REALLOC(dp->inbuf, dp->totalNeeded);
+ if (new_buf == NULL) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ break; /* Break out of loop into error handler. */
+ }
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= dp->inBufSize);
+ erts_smp_atomic_add(&sys_misc_mem_sz,
+ dp->totalNeeded - dp->inBufSize);
+ dp->inBufSize = dp->totalNeeded;
+ dp->inbuf = new_buf;
+ }
+ }
+
+ /*
+ * Check for completion of a packet read.
+ */
+
+ if (dp->bytesInBuffer < dp->totalNeeded) {
+ /*
+ * Not enough bytes in the buffer. Break out of
+ * the loop and initiate a new read.
+ */
+
+ break;
+ } else {
+
+ /*
+ * We have successfully read a complete packet, which
+ * can be passed to Erlang.
+ */
+
+ driver_output(dp->port_num, dp->inbuf+pb, dp->totalNeeded-pb);
+
+ /*
+ * Update the number of bytes remaining in the buffer,
+ * and move the data remaining (if any) to the beginning
+ * of the buffer.
+ */
+
+ dp->bytesInBuffer -= dp->totalNeeded;
+ if (dp->bytesInBuffer > 0) {
+ memmove(dp->inbuf, dp->inbuf+dp->totalNeeded,
+ dp->bytesInBuffer);
+ }
+
+ /*
+ * Indicate that we need the size of a header, and
+ * go through the loop once more (to either process
+ * remaining bytes or initiate reading more).
+ */
+
+ dp->totalNeeded = pb;
+ }
+ }
+ }
+ }
+
+ /*
+ * Start a new overlapped read, or report the error.
+ */
+
+ if (error == NO_ERROR) {
+ async_read_file(&dp->in, dp->inbuf+dp->bytesInBuffer,
+ dp->inBufSize - dp->bytesInBuffer);
+ } else {
+ DEBUGF(("ready_input(): error: %s\n", win32_errorstr(error)));
+ if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
+ /* Maybe check exit status */
+ if (dp->report_exit) {
+ DWORD exitcode;
+ if (GetExitCodeProcess(dp->port_pid, &exitcode) &&
+ exitcode != STILL_ACTIVE) {
+ driver_report_exit(dp->port_num, exitcode);
+ }
+ }
+ driver_failure_eof(dp->port_num);
+ } else { /* Report real errors. */
+ int error = GetLastError();
+ (void) driver_select(dp->port_num, ready_event, ERL_DRV_READ, 0);
+ _dosmaperr(error);
+ driver_failure_posix(dp->port_num, errno);
+ }
+ }
+
+ /*return 0;*/
+}
+
+static void
+ready_output(ErlDrvData drv_data, ErlDrvEvent ready_event)
+{
+ DWORD bytesWritten;
+ DriverData* dp = driver_data + (int)drv_data;
+ int error;
+
+#ifdef ERTS_SMP
+ if(dp->out.thread == (HANDLE) -1) {
+ dp->out.async_io_active = 0;
+ }
+#endif
+ DEBUGF(("ready_output(%d, 0x%x)\n", drv_data, ready_event));
+ set_busy_port(dp->port_num, 0);
+ if (!(dp->outbuf)) {
+ /* Happens because event sometimes get signalled during a succesful
+ write... */
+ return;
+ }
+ 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;
+#ifdef HARD_POLL_DEBUG
+ poll_debug_write_begin(dp->out.ov.hEvent);
+#endif
+ error = get_overlapped_result(&dp->out, &bytesWritten, TRUE);
+#ifdef HARD_POLL_DEBUG
+ poll_debug_write_done(dp->out.ov.hEvent,bytesWritten);
+#endif
+
+ if (error == NO_ERROR) {
+ dp->out.ov.Offset += bytesWritten; /* For vanilla driver. */
+ return ; /* 0; */
+ }
+
+ (void) driver_select(dp->port_num, ready_event, ERL_DRV_WRITE, 0);
+ _dosmaperr(error);
+ driver_failure_posix(dp->port_num, errno);
+ /* return 0; */
+}
+
+static void stop_select(ErlDrvEvent e, void* _)
+{
+ CloseHandle((HANDLE)e);
+}
+
+/* Fills in the systems representation of the beam process identifier.
+** The Pid is put in STRING representation in the supplied buffer,
+** no interpretation of this should be done by the rest of the
+** emulator. The buffer should be at least 21 bytes long.
+*/
+void sys_get_pid(char *buffer){
+ DWORD p = GetCurrentProcessId();
+ /* The pid is scalar and is an unsigned long. */
+ sprintf(buffer,"%lu",(unsigned long) p);
+}
+
+void
+sys_init_io(void)
+{
+
+ /* Now heres an icky one... This is called before drivers are, so we
+ can change our view of the number of open files possible.
+ We estimate the number to twice the amount of ports.
+ We really dont know on windows, do we? */
+ max_files = 2*erts_max_ports;
+
+#ifdef USE_THREADS
+#ifdef ERTS_SMP
+ if (init_async(-1) < 0)
+ erl_exit(1, "Failed to initialize async-threads\n");
+#else
+ {
+ /* This is special stuff, starting a driver from the
+ * system routines, but is a nice way of handling stuff
+ * the erlang way
+ */
+ SysDriverOpts dopts;
+ int ret;
+
+ sys_memset((void*)&dopts, 0, sizeof(SysDriverOpts));
+ add_driver_entry(&async_driver_entry);
+ ret = erts_open_driver(NULL, NIL, "async", &dopts, NULL);
+ DEBUGF(("open_driver = %d\n", ret));
+ if (ret < 0)
+ erl_exit(1, "Failed to open async driver\n");
+ erts_port[ret].status |= ERTS_PORT_SFLG_IMMORTAL;
+ }
+#endif
+#endif
+}
+
+#ifdef ERTS_SMP
+void
+erts_sys_main_thread(void)
+{
+ HANDLE dummy;
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_set_thread_name("parent_thread");
+#endif
+ dummy = CreateEvent(NULL, FALSE, FALSE, NULL);
+ for(;;) {
+ WaitForSingleObject(dummy, INFINITE);
+ }
+}
+#endif
+
+void erts_sys_alloc_init(void)
+{
+ elib_ensure_initialized();
+}
+
+void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
+{
+ return malloc((size_t) sz);
+}
+
+void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
+{
+ return realloc(p, (size_t) sz);
+}
+
+void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
+{
+ free(p);
+}
+
+static Preload* preloaded = NULL;
+static unsigned* res_name = NULL;
+static int num_preloaded = 0;
+
+/* Return a pointer to a vector of names of preloaded modules */
+
+Preload* sys_preloaded(void)
+{
+ HRSRC hRes;
+ unsigned char* data;
+
+#define GETWORD(p) (0[p] | 1[p] << 8)
+#define GETDWORD(p) (GETWORD(p) | GETWORD(p+2) << 16)
+
+
+ if (preloaded == NULL) {
+ int i;
+ ASSERT(beam_module != NULL);
+ hRes = FindResource(beam_module, 0, "ERLANG_DICT");
+ /* We might have a resource compiler laying out the 0 resource with
+ "0" as a textual name instead... */
+ if (hRes == NULL) {
+ hRes = FindResource(beam_module, "0", "ERLANG_DICT");
+ }
+ if (hRes == NULL) {
+ DWORD n = GetLastError();
+ fprintf(stderr, "No ERLANG_DICT resource\n");
+ exit(1);
+ }
+ data = (unsigned char *) LoadResource(beam_module, hRes);
+
+ num_preloaded = GETWORD(data);
+ if (num_preloaded == 0) {
+ fprintf(stderr, "No preloaded modules\n");
+ exit(1);
+ }
+
+ data += 2;
+ preloaded = erts_alloc(ERTS_ALC_T_PRELOADED,
+ (num_preloaded+1)*sizeof(Preload));
+ res_name = erts_alloc(ERTS_ALC_T_PRELOADED,
+ (num_preloaded+1)*sizeof(unsigned));
+ erts_smp_atomic_add(&sys_misc_mem_sz,
+ (num_preloaded+1)*sizeof(Preload)
+ + (num_preloaded+1)*sizeof(unsigned));
+ for (i = 0; i < num_preloaded; i++) {
+ int n;
+
+ preloaded[i].size = GETDWORD(data);
+ data += 4;
+ res_name[i] = GETWORD(data);
+ data += 2;
+ n = GETWORD(data);
+ data += 2;
+ preloaded[i].name = erts_alloc(ERTS_ALC_T_PRELOADED, n+1);
+ erts_smp_atomic_add(&sys_misc_mem_sz, n+1);
+ sys_memcpy(preloaded[i].name, data, n);
+ preloaded[i].name[n] = '\0';
+ data += n;
+ DEBUGF(("name: %s; size: %d; resource: %p\n",
+ preloaded[i].name, preloaded[i].size, res_name[i]));
+ }
+ preloaded[i].name = NULL;
+ }
+
+#undef GETWORD
+#undef GETDWORD
+ return preloaded;
+}
+
+/* Return a pointer to preloaded code for module "module" */
+unsigned char* sys_preload_begin(Preload* pp)
+{
+ HRSRC hRes;
+ unsigned resource;
+
+ ASSERT(beam_module != NULL);
+
+ resource = res_name[pp-preloaded];
+ DEBUGF(("Loading name: %s; size: %d; resource: %p\n",
+ pp->name, pp->size, resource));
+ hRes = FindResource(beam_module, (char *) resource, "ERLANG_CODE");
+ return pp->code = LoadResource(beam_module, hRes);
+}
+
+/* Clean up if allocated */
+void sys_preload_end(Preload* pp)
+{
+}
+
+/* Read a key from console */
+
+int
+sys_get_key(int fd)
+{
+ ASSERT(fd == 0);
+
+ if (win_console) {
+ return ConGetKey();
+ }
+
+ /*
+ * Black magic follows. (Code stolen from get_overlapped_result())
+ */
+
+ if (fd_driver_input != NULL && fd_driver_input->thread != (HANDLE)-1) {
+ DWORD error;
+ int key;
+
+ error = WaitForSingleObject(fd_driver_input->ov.hEvent, INFINITE);
+ if (error == WAIT_OBJECT_0) {
+ if (fd_driver_input->bytesTransferred > 0) {
+ int n;
+ int i;
+ char* buf = OV_BUFFER_PTR(fd_driver_input);
+
+ fd_driver_input->bytesTransferred--;
+ n = fd_driver_input->bytesTransferred;
+ key = buf[0];
+ for (i = n; i > 0; i--) {
+ buf[i-1] = buf[i];
+ }
+ return key;
+ }
+ }
+ }
+ return '*'; /* Error! */
+}
+
+/*
+ * Returns a human-readable description of the last error.
+ * The returned pointer will be valid only as long as last-error()
+ * isn't called again.
+ */
+
+char* win32_errorstr(int error)
+{
+#ifdef SMP
+ LPTSTR lpBufPtr = erts_smp_tsd_get(win32_errstr_key);
+#else
+ static LPTSTR lpBufPtr = NULL;
+#endif
+ if (lpBufPtr) {
+ LocalFree(lpBufPtr);
+ }
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpBufPtr,
+ 0,
+ NULL);
+ SetLastError(error);
+#ifdef ERTS_SMP
+ erts_smp_tsd_set(win32_errstr_key,lpBufPtr);
+#endif
+ return lpBufPtr;
+}
+
+char* last_error(void)
+{
+ return win32_errorstr(GetLastError());
+}
+
+static void* sys_func_memzero(void* s, size_t n)
+{
+ return sys_memzero(s, n);
+}
+
+#ifdef DEBUG
+static HANDLE hDebugWrite = INVALID_HANDLE_VALUE;
+
+void erl_debug(char *fmt,...)
+{
+ char sbuf[1024]; /* Temporary buffer. */
+ DWORD written; /* Actual number of chars written. */
+ va_list va;
+
+ if (hDebugWrite != INVALID_HANDLE_VALUE) {
+ va_start(va, fmt);
+ vsprintf(sbuf, fmt, va);
+ WriteFile(hDebugWrite, sbuf, strlen(sbuf), &written, NULL);
+ va_end(va);
+ }
+}
+
+static void debug_console(void)
+{
+ HANDLE hRead; /* Handle to read end of pipe. */
+ SECURITY_ATTRIBUTES sa;
+ PROCESS_INFORMATION procInfo;
+ STARTUPINFO startInfo;
+ BOOL ok;
+
+ /*
+ * Create a pipe for communicating with the sub process.
+ */
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+ if (!CreatePipe(&hRead, &hDebugWrite, &sa, 0)) {
+ fprintf(stderr, "Failed to create pipe: %d\n",
+ GetLastError());
+ exit(1);
+ }
+
+ startInfo.cb = sizeof(STARTUPINFO);
+ startInfo.lpTitle = "Erlang Debug Log";
+ startInfo.lpReserved = NULL;
+ startInfo.lpReserved2 = NULL;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpDesktop = NULL;
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.hStdInput = hRead;
+
+ /* The following handles are not intended to be used. */
+ startInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ ok = CreateProcess(NULL,
+ "erl_log.exe", /* Application */
+ NULL, /* Process security attributes. */
+ NULL, /* Thread security attributes. */
+ TRUE, /* Handle inheritance flag. */
+ CREATE_NEW_CONSOLE, /* Flags. */
+ NULL, /* Environment. */
+ NULL, /* Current directory. */
+ &startInfo,/* Startup info. */
+ &procInfo /* Process information. */
+ );
+
+ CloseHandle(hRead);
+
+ if (ok) {
+ /*
+ * Since we don't use these, close them at once to avoid a resource
+ * leak.
+ */
+ CloseHandle(procInfo.hProcess);
+ CloseHandle(procInfo.hThread);
+ } else {
+ fprintf(stderr, "Create process failed: %s\n", last_error());
+ exit(1);
+ }
+}
+
+void
+erl_bin_write(buf, sz, max)
+ unsigned char* buf;
+ int sz;
+ int max;
+{
+ int i, imax;
+ char comma[5] = ",";
+
+ if (hDebugWrite == INVALID_HANDLE_VALUE)
+ return;
+
+ if (!sz)
+ return;
+ if (sz > max)
+ imax = max;
+ else
+ imax = sz;
+
+ for (i=0; i<imax; i++) {
+ if (i == imax-1) {
+ if (sz > max)
+ strcpy(comma, ",...");
+ else
+ comma[0] = 0;
+ }
+ if (isdigit(buf[i]))
+ erl_debug("%u%s", (int)(buf[i]), comma);
+ else {
+ if (isalpha(buf[i])) {
+ erl_debug("%c%s", buf[i], comma);
+ }
+ else
+ erl_debug("%u%s", (int)(buf[i]), comma);
+ }
+ }
+}
+
+void
+erl_assert_error(char* expr, char* file, int line)
+{
+ char message[1024];
+
+ sprintf(message, "File %hs, line %d: %hs", file, line, expr);
+ MessageBox(GetActiveWindow(), message, "Assertion failed",
+ MB_OK | MB_ICONERROR);
+#if 0
+ erl_crash_dump(file, line, "Assertion failed: %hs\n", expr);
+#endif
+ DebugBreak();
+}
+
+#endif /* DEBUG */
+
+static void
+check_supported_os_version(void)
+{
+#if defined(_WIN32_WINNT)
+ {
+ DWORD major = (_WIN32_WINNT >> 8) & 0xff;
+ DWORD minor = _WIN32_WINNT & 0xff;
+
+ if (int_os_version.dwPlatformId != VER_PLATFORM_WIN32_NT
+ || int_os_version.dwMajorVersion < major
+ || (int_os_version.dwMajorVersion == major
+ && int_os_version.dwMinorVersion < minor))
+ erl_exit(-1,
+ "Windows version not supported "
+ "(min required: winnt %d.%d)\n",
+ major, minor);
+ }
+#else
+ erl_exit(-1,
+ "Windows version not supported "
+ "(min required: win %d.%d)\n",
+ nt_major, nt_minor);
+#endif
+}
+
+#ifdef USE_THREADS
+static void *ethr_internal_alloc(size_t size)
+{
+ return erts_alloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, (Uint) size);
+}
+static void *ethr_internal_realloc(void *ptr, size_t size)
+{
+ return erts_realloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, ptr, (Uint) size);
+}
+static void ethr_internal_free(void *ptr)
+{
+ erts_free(ERTS_ALC_T_ETHR_INTERNAL, ptr);
+}
+#endif
+
+void
+erts_sys_pre_init(void)
+{
+ int_os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&int_os_version);
+ check_supported_os_version();
+#ifdef USE_THREADS
+ {
+ erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
+ eid.alloc = ethr_internal_alloc;
+ eid.realloc = ethr_internal_realloc;
+ eid.free = ethr_internal_free;
+ erts_thr_init(&eid);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_init();
+#endif
+ }
+#endif
+ erts_smp_atomic_init(&sys_misc_mem_sz, 0);
+ erts_sys_env_init();
+}
+
+/*
+ * the last two only used for standalone erlang
+ * they should are used by sae_main in beam dll to
+ * enable standalone execution via erl_api-routines
+ */
+
+void noinherit_std_handle(DWORD type)
+{
+ HANDLE h = GetStdHandle(type);
+ if (h != 0 && h != INVALID_HANDLE_VALUE) {
+ SetHandleInformation(h,HANDLE_FLAG_INHERIT,0);
+ }
+}
+
+
+void erl_sys_init(void)
+{
+ HANDLE handle;
+
+ noinherit_std_handle(STD_OUTPUT_HANDLE);
+ noinherit_std_handle(STD_INPUT_HANDLE);
+ noinherit_std_handle(STD_ERROR_HANDLE);
+
+
+ erts_smp_mtx_init(&sys_driver_data_lock, "sys_driver_data_lock");
+
+#ifdef ERTS_SMP
+ erts_smp_tsd_key_create(&win32_errstr_key);
+#endif
+ erts_smp_atomic_init(&pipe_creation_counter,0);
+ /*
+ * Test if we have named pipes or not.
+ */
+
+ switch (int_os_version.dwPlatformId) {
+ case VER_PLATFORM_WIN32_WINDOWS:
+ DEBUGF(("Running on Windows 95"));
+ use_named_pipes = FALSE;
+ break;
+ case VER_PLATFORM_WIN32_NT:
+ DEBUGF(("Running on Windows NT"));
+#ifdef DISABLE_NAMED_PIPES
+ use_named_pipes = FALSE;
+#else
+ use_named_pipes = TRUE;
+#endif
+ break;
+ default: /* Unsupported platform. */
+ exit(1);
+ }
+ DEBUGF((" %d.%d, build %d, %s\n",
+ int_os_version.dwMajorVersion, int_os_version.dwMinorVersion,
+ int_os_version.dwBuildNumber, int_os_version.szCSDVersion));
+
+ ASSERT(beam_module != NULL);
+ init_console();
+
+ /*
+ * The following makes sure that the current directory for the current drive
+ * is remembered (in the environment).
+ */
+
+ chdir(".");
+
+ /*
+ * Make sure that the standard error handle is valid.
+ */
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ if (handle == INVALID_HANDLE_VALUE || handle == 0) {
+ SetStdHandle(STD_ERROR_HANDLE, GetStdHandle(STD_OUTPUT_HANDLE));
+ }
+ erts_sys_init_float();
+ erts_init_check_io();
+
+ /* Suppress windows error message popups */
+ SetErrorMode(SetErrorMode(0) |
+ SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+}
+
+#ifdef ERTS_SMP
+void
+erts_sys_schedule_interrupt(int set)
+{
+ erts_check_io_interrupt(set);
+}
+
+void
+erts_sys_schedule_interrupt_timed(int set, long msec)
+{
+ erts_check_io_interrupt_timed(set, msec);
+}
+#endif
+
+/*
+ * Called from schedule() when it runs out of runnable processes,
+ * or when Erlang code has performed INPUT_REDUCTIONS reduction
+ * steps. runnable == 0 iff there are no runnable Erlang processes.
+ */
+void
+erl_sys_schedule(int runnable)
+{
+#ifdef ERTS_SMP
+ erts_check_io(!runnable);
+ ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
+#else
+ erts_check_io_interrupt(0);
+ if (runnable) {
+ erts_check_io(0); /* Poll for I/O */
+ check_async_ready(); /* Check async completions */
+ } else {
+ erts_check_io(check_async_ready() ? 0 : 1);
+ }
+#endif
+}
+
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+/*
+ * Async operation support.
+ */
+
+static ErlDrvEvent async_drv_event;
+
+void
+sys_async_ready(int fd)
+{
+ SetEvent((HANDLE)async_drv_event);
+}
+
+static int
+async_drv_init(void)
+{
+ async_drv_event = (ErlDrvEvent) NULL;
+ return 0;
+}
+
+static ErlDrvData
+async_drv_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+ if (async_drv_event != (ErlDrvEvent) NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ if ((async_drv_event = (ErlDrvEvent)CreateAutoEvent(FALSE)) == (ErlDrvEvent) NULL) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ driver_select(port_num, async_drv_event, ERL_DRV_READ|ERL_DRV_USE, 1);
+ if (init_async(async_drv_event) < 0) {
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ return (ErlDrvData)port_num;
+}
+
+static void
+async_drv_stop(ErlDrvData port_num)
+{
+ exit_async();
+ driver_select((ErlDrvPort)port_num, async_drv_event, ERL_DRV_READ|ERL_DRV_USE, 0);
+ /*CloseHandle((HANDLE)async_drv_event);*/
+ async_drv_event = (ErlDrvEvent) NULL;
+}
+
+
+static void
+async_drv_input(ErlDrvData port_num, ErlDrvEvent e)
+{
+ check_async_ready();
+
+ /*
+ * Our event is auto-resetting.
+ */
+}
+
+#endif
+