aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/ose/sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/ose/sys.c')
-rw-r--r--erts/emulator/sys/ose/sys.c1820
1 files changed, 1820 insertions, 0 deletions
diff --git a/erts/emulator/sys/ose/sys.c b/erts/emulator/sys/ose/sys.c
new file mode 100644
index 0000000000..c475aafe38
--- /dev/null
+++ b/erts/emulator/sys/ose/sys.c
@@ -0,0 +1,1820 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2013. 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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef ISC32
+#define _POSIX_SOURCE
+#define _XOPEN_SOURCE
+#endif
+
+#include <sys/time.h> /* ose*/
+#include <time.h>
+/* ose
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/utsname.h>
+*/
+
+#include <sys/uio.h>
+#include <termios.h>
+#include <ctype.h>
+
+#ifdef ISC32
+#include <sys/bsdtypes.h>
+#endif
+
+#include <termios.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#define ERTS_WANT_BREAK_HANDLING
+#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
+#include "sys.h"
+#include "erl_thr_progress.h"
+
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+#ifdef USE_THREADS
+#include "erl_threads.h"
+#endif
+
+#include "erl_mseg.h"
+
+/*ose*/
+#include "unistd.h"
+#include "efs.h"
+#include "erl_printf.h"
+#if 0
+#define TRACE do { \
+ erts_fprintf(stderr, " %s / %d / pid 0x%x\n", __FUNCTION__, __LINE__, current_process()); \
+ } while(0)
+#else
+#define TRACE do {} while(0)
+#endif
+/*ose*/
+
+extern char **environ;
+static erts_smp_rwmtx_t environ_rwmtx;
+
+#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
+ * vector sock_sendv().
+ */
+/*
+ * Don't need global.h, but bif_table.h (included by bif.h),
+ * won't compile otherwise
+ */
+#include "global.h"
+#include "bif.h"
+
+#include "erl_sys_driver.h"
+#include "erl_check_io.h"
+#include "erl_cpu_topology.h"
+
+#ifndef DISABLE_VFORK
+#define DISABLE_VFORK 0
+#endif
+
+/* The priority for reader/writer processes */
+#define FD_PROC_PRI 20
+
+/*
+ * [OTP-3906]
+ * Solaris signal management gets confused when threads are used and a
+ * lot of child processes dies. The confusion results in that SIGCHLD
+ * signals aren't delivered to the emulator which in turn results in
+ * a lot of defunct processes in the system.
+ *
+ * The problem seems to appear when a signal is frequently
+ * blocked/unblocked at the same time as the signal is frequently
+ * propagated. The child waiter thread is a workaround for this problem.
+ * The SIGCHLD signal is always blocked (in all threads), and the child
+ * waiter thread fetches the signal by a call to sigwait(). See
+ * child_waiter().
+ */
+
+typedef struct ErtsSysReportExit_ ErtsSysReportExit;
+struct ErtsSysReportExit_ {
+ ErtsSysReportExit *next;
+ Eterm port;
+ int pid;
+ int ifd;
+ int ofd;
+ ErlDrvEvent in_sig_descr;
+ ErlDrvEvent out_sig_descr;
+};
+
+/* This data is shared by these drivers - initialized by spawn_init() */
+static struct driver_data {
+ ErlDrvPort port_num;
+ int ofd, packet_bytes;
+ ErtsSysReportExit *report_exit;
+ int pid;
+ int alive;
+ int status;
+ ErlDrvEvent in_sig_descr;
+ ErlDrvEvent out_sig_descr;
+ PROCESS in_proc;
+ PROCESS out_proc;
+ ErlDrvPDL pdl;
+} *driver_data; /* indexed by fd */
+
+static ErtsSysReportExit *report_exit_list;
+
+extern int driver_interrupt(int, int);
+extern void do_break(void);
+
+extern void erl_sys_args(int*, char**);
+
+/* The following two defs should probably be moved somewhere else */
+
+extern void erts_sys_init_float(void);
+
+extern void erl_crash_dump(char* file, int line, char* fmt, ...);
+
+#define DIR_SEPARATOR_CHAR '/'
+
+#if defined(DEBUG)
+#define ERL_BUILD_TYPE_MARKER ".debug"
+#else /* opt */
+#define ERL_BUILD_TYPE_MARKER
+#endif
+
+#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER
+
+#ifdef DEBUG
+static int debug_log = 0;
+#endif
+
+#ifdef ERTS_SMP
+static erts_smp_atomic32_t have_prepared_crash_dump;
+#define ERTS_PREPARED_CRASH_DUMP \
+ ((int) erts_smp_atomic32_xchg_nob(&have_prepared_crash_dump, 1))
+#else
+static volatile int have_prepared_crash_dump;
+#define ERTS_PREPARED_CRASH_DUMP \
+ (have_prepared_crash_dump++)
+#endif
+
+static erts_smp_atomic_t sys_misc_mem_sz;
+
+#if defined(ERTS_SMP)
+erts_mtx_t chld_stat_mtx;
+#endif
+
+#if defined(ERTS_SMP) /* ------------------------------------------------- */
+#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
+#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
+
+#else /* ------------------------------------------------------------------- */
+#define CHLD_STAT_LOCK
+#define CHLD_STAT_UNLOCK
+static volatile int children_died;
+#endif
+
+
+static struct fd_data {
+ char pbuf[4]; /* hold partial packet bytes */
+ int psz; /* size of pbuf */
+ char *buf;
+ char *cpos;
+ int sz;
+ int remain; /* for input on fd */
+} *fd_data; /* indexed by fd */
+
+/********************* General functions ****************************/
+
+/* This is used by both the drivers and general I/O, must be set early */
+static int max_files = -1;
+
+/*
+ * a few variables used by the break handler
+ */
+#ifdef ERTS_SMP
+erts_smp_atomic32_t erts_break_requested;
+#define ERTS_SET_BREAK_REQUESTED \
+ erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 1)
+#define ERTS_UNSET_BREAK_REQUESTED \
+ erts_smp_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 0)
+#else
+volatile int erts_break_requested = 0;
+#define ERTS_SET_BREAK_REQUESTED (erts_break_requested = 1)
+#define ERTS_UNSET_BREAK_REQUESTED (erts_break_requested = 0)
+#endif
+/* set early so the break handler has access to initial mode */
+static struct termios initial_tty_mode;
+static int replace_intr = 0;
+/* assume yes initially, ttsl_init will clear it */
+int using_oldshell = 1;
+
+static void
+init_check_io(void)
+{
+ erts_init_check_io();
+ max_files = erts_check_io_max_files();
+}
+
+#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
+#define ERTS_CHK_IO_AS_INTR() erts_check_io_async_sig_interrupt()
+#else
+#define ERTS_CHK_IO_AS_INTR() erts_check_io_interrupt(1)
+#endif
+#define ERTS_CHK_IO_INTR erts_check_io_interrupt
+#define ERTS_CHK_IO_INTR_TMD erts_check_io_interrupt_timed
+#define ERTS_CHK_IO erts_check_io
+#define ERTS_CHK_IO_SZ erts_check_io_size
+
+
+void
+erts_sys_schedule_interrupt(int set)
+{
+ ERTS_CHK_IO_INTR(set);
+}
+
+#ifdef ERTS_SMP
+void
+erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec)
+{
+ ERTS_CHK_IO_INTR_TMD(set, msec);
+}
+#endif
+
+Uint
+erts_sys_misc_mem_sz(void)
+{
+ Uint res = ERTS_CHK_IO_SZ();
+ res += erts_smp_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 (using_oldshell && !replace_intr) {
+ SET_BLOCKING(0);
+ }
+ else if (isatty(0)) {
+ tcsetattr(0,TCSANOW,&initial_tty_mode);
+ }
+}
+
+#ifdef USE_THREADS
+
+typedef struct {
+ int sched_bind_data;
+} erts_thr_create_data_t;
+
+/*
+ * thr_create_prepare() is called in parent thread before thread creation.
+ * Returned value is passed as argument to thr_create_cleanup().
+ */
+static void *
+thr_create_prepare(void)
+{
+ erts_thr_create_data_t *tcdp;
+
+ tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t));
+
+ tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare();
+
+ return (void *) tcdp;
+}
+
+
+/* thr_create_cleanup() is called in parent thread after thread creation. */
+static void
+thr_create_cleanup(void *vtcdp)
+{
+ erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
+
+ erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data);
+
+ erts_free(ERTS_ALC_T_TMP, tcdp);
+}
+
+static void
+thr_create_prepare_child(void *vtcdp)
+{
+ erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_thread_setup();
+#endif
+
+ erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data);
+}
+
+#endif /* #ifdef USE_THREADS */
+
+void
+erts_sys_pre_init(void)
+{
+ erts_printf_add_cr_to_stdout = 1;
+ erts_printf_add_cr_to_stderr = 1;
+#ifdef USE_THREADS
+ {
+ erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
+
+ eid.thread_create_child_func = thr_create_prepare_child;
+ /* Before creation in parent */
+ eid.thread_create_prepare_func = thr_create_prepare;
+ /* After creation in parent */
+ eid.thread_create_parent_func = thr_create_cleanup,
+
+ erts_thr_init(&eid);
+
+ report_exit_list = NULL;
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_init();
+#endif
+
+#if defined(ERTS_SMP)
+ erts_mtx_init(&chld_stat_mtx, "child_status");
+#endif
+ }
+#ifdef ERTS_SMP
+ erts_smp_atomic32_init_nob(&erts_break_requested, 0);
+ erts_smp_atomic32_init_nob(&have_prepared_crash_dump, 0);
+#else
+ erts_break_requested = 0;
+ have_prepared_crash_dump = 0;
+#endif
+#if !defined(ERTS_SMP)
+ children_died = 0;
+#endif
+#endif /* USE_THREADS */
+ erts_smp_atomic_init_nob(&sys_misc_mem_sz, 0);
+}
+
+void
+erl_sys_init(void)
+{
+
+#ifdef USE_SETLINEBUF
+ setlinebuf(stdout);
+#else
+ setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
+#endif
+
+ erts_sys_init_float();
+
+ /* we save this so the break handler can set and reset it properly */
+ /* also so that we can reset on exit (break handler or not) */
+ if (isatty(0)) {
+ tcgetattr(0,&initial_tty_mode);
+ }
+ tzset(); /* Required at least for NetBSD with localtime_r() */
+}
+
+static ERTS_INLINE int
+prepare_crash_dump(int secs)
+{
+#define NUFBUF (3)
+ int i, max;
+ char env[21]; /* enough to hold any 64-bit integer */
+ size_t envsz;
+ /*DeclareTmpHeapNoproc(heap,NUFBUF);*/
+ /*Eterm *hp = heap;*/
+ /*Eterm list = NIL;*/
+ int has_heart = 0;
+
+ UseTmpHeapNoproc(NUFBUF);
+
+ if (ERTS_PREPARED_CRASH_DUMP)
+ return 0; /* We have already been called */
+
+
+ /* Positive secs means an alarm must be set
+ * 0 or negative means no alarm
+ *
+ * Set alarm before we try to write to a port
+ * we don't want to hang on a port write with
+ * no alarm.
+ *
+ */
+
+#if 0 /*ose TBD!!!*/
+ if (secs >= 0) {
+ alarm((unsigned int)secs);
+ }
+#endif
+
+ /* Make sure we unregister at epmd (unknown fd) and get at least
+ one free filedescriptor (for erl_crash.dump) */
+
+ max = max_files;
+ if (max < 1024)
+ max = 1024;
+ for (i = 3; i < max; i++) {
+ close(i);
+ }
+
+ envsz = sizeof(env);
+ i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
+ if (i >= 0) {
+ int nice_val;
+ nice_val = i != 0 ? 0 : atoi(env);
+ if (nice_val > 39) {
+ nice_val = 39;
+ }
+ set_pri(nice_val);
+ }
+
+ UnUseTmpHeapNoproc(NUFBUF);
+#undef NUFBUF
+ return has_heart;
+}
+
+int erts_sys_prepare_crash_dump(int secs)
+{
+ return prepare_crash_dump(secs);
+}
+
+static ERTS_INLINE void
+break_requested(void)
+{
+ /*
+ * just set a flag - checked for and handled by
+ * scheduler threads erts_check_io() (not signal handler).
+ */
+#ifdef DEBUG
+ fprintf(stderr,"break!\n");
+#endif
+ if (ERTS_BREAK_REQUESTED)
+ erl_exit(ERTS_INTR_EXIT, "");
+
+ ERTS_SET_BREAK_REQUESTED;
+ ERTS_CHK_IO_AS_INTR(); /* Make sure we don't sleep in poll */
+}
+
+/* Disable break */
+void erts_set_ignore_break(void) {
+
+}
+
+/* Don't use ctrl-c for break handler but let it be
+ used by the shell instead (see user_drv.erl) */
+void erts_replace_intr(void) {
+ struct termios mode;
+
+ if (isatty(0)) {
+ tcgetattr(0, &mode);
+
+ /* here's an example of how to replace ctrl-c with ctrl-u */
+ /* mode.c_cc[VKILL] = 0;
+ mode.c_cc[VINTR] = CKILL; */
+
+ mode.c_cc[VINTR] = 0; /* disable ctrl-c */
+ tcsetattr(0, TCSANOW, &mode);
+ replace_intr = 1;
+ }
+}
+
+void init_break_handler(void)
+{
+
+}
+
+int sys_max_files(void)
+{
+ return(max_files);
+}
+
+
+/************************** OS info *******************************/
+
+/* Used by erlang:info/1. */
+/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
+
+char os_type[] = "ose";
+
+void
+os_flavor(char* namebuf, /* Where to return the name. */
+ unsigned size) /* Size of name buffer. */
+{
+#if 0
+ struct utsname uts; /* Information about the system. */
+ char* s;
+
+ (void) uname(&uts);
+ for (s = uts.sysname; *s; s++) {
+ if (isupper((int) *s)) {
+ *s = tolower((int) *s);
+ }
+ }
+ strcpy(namebuf, uts.sysname);
+#else
+ strncpy(namebuf, "release", size);
+#endif
+}
+
+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 = 5;
+ *pMinor = 7;
+ *pBuild = 0;
+}
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ *state = NULL;
+}
+
+char **environ; /*ose - needs replacement*/
+
+char *getenv_string(GETENV_STATE *state0)
+{
+ char **state = (char **) *state0;
+ char *cp;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+
+ if (state == NULL)
+ state = environ;
+
+ cp = *state++;
+ *state0 = (GETENV_STATE) state;
+
+ return cp;
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ *state = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+
+/************************** Port I/O *******************************/
+
+
+
+/* I. Common stuff */
+
+/*
+ * Decreasing the size of it below 16384 is not allowed.
+ */
+#define SYSDRIVERASYNCSIG 1000
+#define SYSDRIVERCONFSIG 1001
+
+typedef struct SysDriverAsyncSignal_ {
+ SIGSELECT sig_no;
+ int type;
+ byte *buff;
+ ssize_t res;
+ int errno_copy;
+} SysDriverAsyncSignal;
+
+typedef struct SysDriverConfSignal_ {
+ SIGSELECT sig_no;
+ int fd;
+ PROCESS parent;
+} SysDriverConfSignal;
+
+union SIGNAL {
+ SIGSELECT sig_no;
+ SysDriverAsyncSignal sys_async;
+ SysDriverConfSignal conf_async;
+};
+
+/* II. The spawn/fd drivers */
+
+#define ERTS_SYS_READ_BUF_SZ (64*1024)
+
+/* Driver interfaces */
+static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvSSizeT fd_control(ErlDrvData, unsigned int, char *, ErlDrvSizeT,
+ char **, ErlDrvSizeT);
+static int spawn_init(void);
+static void fd_stop(ErlDrvData);
+static void erl_stop(ErlDrvData);
+static void ready_input(ErlDrvData, ErlDrvEvent);
+static void ready_output(ErlDrvData, ErlDrvEvent);
+static void output(ErlDrvData, char*, ErlDrvSizeT);
+static void outputv(ErlDrvData, ErlIOVec*);
+static void stop_select(ErlDrvEvent, void*);
+static int resolve_signal(OseSignal* sig, int *mode) {
+ return sig->sig_no == SYSDRIVERASYNCSIG ? sig->sys_async.type : -1;
+}
+
+OS_PROCESS(fd_writer_process);
+OS_PROCESS(fd_reader_process);
+
+struct erl_drv_entry spawn_driver_entry = {
+ spawn_init,
+ spawn_start,
+ erl_stop,
+ output,
+ ready_input,
+ ready_output,
+ "spawn",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL, NULL,
+ stop_select,
+ resolve_signal
+};
+struct erl_drv_entry fd_driver_entry = {
+ NULL,
+ fd_start,
+ fd_stop,
+ output,
+ ready_input,
+ ready_output,
+ "fd",
+ NULL,
+ NULL,
+ fd_control,
+ 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, /* handle2 */
+ NULL, /* process_exit */
+ stop_select,
+ resolve_signal
+};
+
+static int set_driver_data(ErlDrvPort port_num,
+ int ifd,
+ int ofd,
+ int packet_bytes,
+ int read_write,
+ int exit_status,
+ int pid)
+{
+ Port *prt;
+ ErtsSysReportExit *report_exit;
+ OseSignal *sig;
+
+ /*erts_fprintf(stderr, " %s / pid %x / ofd %d / ifd %d\n", __FUNCTION__, current_process(), ofd, ifd);*/
+
+
+ if (!exit_status)
+ report_exit = NULL;
+ else {
+ report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT,
+ sizeof(ErtsSysReportExit));
+ report_exit->next = report_exit_list;
+ report_exit->port = erts_drvport2id(port_num);
+ report_exit->pid = pid;
+ report_exit->ifd = read_write & DO_READ ? ifd : -1;
+ report_exit->ofd = read_write & DO_WRITE ? ofd : -1;
+
+ if (read_write & DO_READ)
+ report_exit->in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ifd);
+ if (read_write & DO_WRITE)
+ report_exit->out_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG, ofd);
+
+ report_exit_list = report_exit;
+ }
+
+ prt = erts_drvport2port(port_num);
+ if (prt != ERTS_INVALID_ERL_DRV_PORT)
+ prt->os_pid = pid;
+
+ if (read_write & DO_READ) {
+ driver_data[ifd].packet_bytes = packet_bytes;
+ driver_data[ifd].port_num = port_num;
+ driver_data[ifd].report_exit = report_exit;
+ driver_data[ifd].pid = pid;
+ driver_data[ifd].alive = 1;
+ driver_data[ifd].status = 0;
+ driver_data[ifd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ifd);
+
+ driver_data[ifd].in_proc = create_process(OS_PRI_PROC,"beam_fd_reader",
+ fd_reader_process, 0x800,
+ FD_PROC_PRI, 0, 0, NULL, 0, 0);
+ efs_clone(driver_data[ifd].in_proc);
+ sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG);
+ sig->conf_async.fd = ifd;
+ sig->conf_async.parent = current_process();
+ send(&sig, driver_data[ifd].in_proc);
+ start(driver_data[ifd].in_proc);
+
+ if (read_write & DO_WRITE) {
+ driver_data[ifd].ofd = ofd;
+ driver_data[ifd].out_sig_descr =
+ erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,ofd);
+ driver_data[ifd].pdl = driver_pdl_create(port_num);
+ driver_data[ifd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer",
+ fd_writer_process, 0x800,
+ FD_PROC_PRI, 0, 0, NULL, 0, 0);
+ sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG);
+ sig->conf_async.fd = ofd;
+ sig->conf_async.parent = current_process();
+ send(&sig, driver_data[ifd].out_proc);
+ // efs_clone(driver_data[ifd].out_proc);
+ start(driver_data[ifd].out_proc);
+ if (ifd != ofd)
+ driver_data[ofd] = driver_data[ifd]; /* structure copy */
+ } else { /* DO_READ only */
+ driver_data[ifd].ofd = -1;
+ }
+ (void) driver_select(port_num, driver_data[ifd].in_sig_descr, (ERL_DRV_READ | ERL_DRV_USE), 1);
+ return(ifd);
+ } else { /* DO_WRITE only */
+ driver_data[ofd].packet_bytes = packet_bytes;
+ driver_data[ofd].port_num = port_num;
+ driver_data[ofd].report_exit = report_exit;
+ driver_data[ofd].ofd = ofd;
+ driver_data[ofd].pid = pid;
+ driver_data[ofd].alive = 1;
+ driver_data[ofd].status = 0;
+ driver_data[ofd].in_sig_descr = erl_drv_ose_event_alloc(SYSDRIVERASYNCSIG,
+ ofd);
+ driver_data[ofd].out_sig_descr = driver_data[ofd].in_sig_descr;
+ driver_data[ofd].out_proc = create_process(OS_PRI_PROC, "beam_fd_writer",
+ fd_writer_process, 0x800,
+ FD_PROC_PRI, 0, 0, NULL, 0, 0);
+ sig = alloc(sizeof(SysDriverConfSignal), SYSDRIVERCONFSIG);
+ sig->conf_async.fd = ofd;
+ sig->conf_async.parent = current_process();
+ send(&sig, driver_data[ofd].out_proc);
+ start(driver_data[ofd].out_proc);
+ //efs_clone(driver_data[ifd].out_proc);
+ driver_data[ofd].pdl = driver_pdl_create(port_num);
+ return(ofd);
+ }
+}
+
+static int spawn_init()
+{
+ int i;
+ TRACE;
+
+ driver_data = (struct driver_data *)
+ erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, max_files * sizeof(struct driver_data));
+
+ for (i = 0; i < max_files; i++)
+ driver_data[i].pid = -1;
+
+ return 1;
+}
+
+static void init_fd_data(int fd, ErlDrvPort port_num)
+{
+ TRACE;
+
+ fd_data[fd].buf = NULL;
+ fd_data[fd].cpos = NULL;
+ fd_data[fd].remain = 0;
+ fd_data[fd].sz = 0;
+ fd_data[fd].psz = 0;
+}
+
+static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+
+ long res = 0;
+
+ TRACE;
+ /* Have to implement for OSE */
+ return (ErlDrvData)res;
+}
+
+OS_PROCESS(fd_reader_process) {
+ OseSignal *sig;
+ PROCESS parent;
+ int fd;
+ byte *read_buf;
+
+ SIGSELECT sigsel[] = {1,SYSDRIVERCONFSIG};
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_init();
+#endif
+
+ TRACE;
+
+ sig = receive(sigsel);
+
+ TRACE;
+
+ fd = sig->conf_async.fd;
+
+ parent = sig->conf_async.parent;
+ free_buf(&sig);
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ {
+ char buf[31];
+ erts_snprintf(&buf[0], 31, "fd_reader %beu", fd);
+ erts_lc_set_thread_name(&buf[0]);
+ }
+#endif
+
+ sigsel[1] = SYSDRIVERASYNCSIG;
+
+ read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ while (1) {
+ int errno_copy = errno;
+ ssize_t res;
+ res = read(fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ sig = alloc(sizeof(SysDriverAsyncSignal), SYSDRIVERASYNCSIG);
+ sig->sys_async.buff = read_buf;
+ sig->sys_async.res = res;
+ if (res <= 0 && errno == EBADF) {
+ fprintf(stderr,"Could not read from input fd (fd %d/ errno %d/ res %d)\n",
+ fd, errno, res);
+ break;
+ }
+ if (errno != errno_copy)
+ sig->sys_async.errno_copy = errno;
+ else
+ sig->sys_async.errno_copy = -1;
+ sig->sys_async.type = fd;
+ send(&sig,parent);
+ /* Wait for acc from async_read */
+ sig = receive(sigsel);
+ free_buf(&sig);
+ }
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, read_buf);
+}
+
+OS_PROCESS(fd_writer_process) {
+ OseSignal *sig;
+ PROCESS parent;
+ int fd;
+ SIGSELECT sigsel[] = { 1, SYSDRIVERCONFSIG, SYSDRIVERASYNCSIG };
+
+ TRACE;
+ /* Only wait for config event with the fd which we are printing to */
+ sig = receive(sigsel);
+
+ TRACE;
+
+ fd = sig->conf_async.fd;
+ parent = sig->conf_async.parent;
+ free_buf(&sig);
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ {
+ char buf[31];
+ erts_snprintf(&buf[0], 31, "fd_writer %beu", fd);
+ erts_lc_set_thread_name(&buf[0]);
+ }
+#endif
+
+ sigsel[0] = 2;
+ /* Why do I need these?!? */
+ if (fd == 1) {
+ FILE* ffd = stdout;
+ } else if (fd == 2) {
+ FILE* ffd = stderr;
+ }
+
+ while (1) {
+ int errno_copy = errno;
+ int res;
+ SysIOVec *iov0;
+ SysIOVec *iov;
+ int iovlen;
+ int iovcnt;
+ int n = 0, i;
+ size_t p;
+ /* fprintf(stderr,"0x%x: fd_writer, receive\n", current_process()); */
+ sig = receive(sigsel);
+ /* size = sig->sys_async.res;*/
+ if (sig->sig_no == SYSDRIVERCONFSIG)
+ return;
+ driver_pdl_lock(driver_data[fd].pdl);
+
+ iov0 = driver_peekq(driver_data[fd].port_num, &iovlen);
+
+ /* Calculate iovcnt */
+ for (p = 0, iovcnt = 0; iovcnt < iovlen;
+ p += iov0[iovcnt++].iov_len)
+ ;
+ iov = driver_alloc(sizeof(SysIOVec) * iovcnt);
+ memcpy(iov, iov0, iovcnt * sizeof(SysIOVec));
+ driver_pdl_unlock(driver_data[fd].pdl);
+ /* Let go of lock until we deque from original vector */
+
+ if (iovlen > 0) {
+ for (i = 0; i < iovcnt; i++) {
+ res = write(fd, iov[i].iov_base, iov[i].iov_len > 256 ? 256 : iov[i].iov_len);
+ if (res < 0)
+ break;
+ n += res;
+ }
+ if (res > 0)
+ res = n;
+ } else if (iovlen == 0) {
+ res = 0;
+ } else { /* Port has terminated */
+ res = -1;
+ }
+ driver_free(iov);
+
+ sig->sys_async.buff = NULL;
+ sig->sys_async.res = res;
+ if (errno != errno_copy)
+ sig->sys_async.errno_copy = errno;
+ else
+ sig->sys_async.errno_copy = -1;
+ sig->sys_async.type = fd;
+ send(&sig, parent);
+ }
+}
+
+#define FD_DEF_HEIGHT 24
+#define FD_DEF_WIDTH 80
+/* Control op */
+#define FD_CTRL_OP_GET_WINSIZE 100
+
+static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(fd,TIOCGWINSZ,&ws) == 0) {
+ *width = (Uint32) ws.ws_col;
+ *height = (Uint32) ws.ws_row;
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ int fd = (int)(long)drv_data;
+ char resbuff[2*sizeof(Uint32)];
+ switch (command) {
+ case FD_CTRL_OP_GET_WINSIZE:
+ {
+ Uint32 w,h;
+ if (fd_get_window_size(fd,&w,&h))
+ return 0;
+ memcpy(resbuff,&w,sizeof(Uint32));
+ memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
+ }
+ break;
+ default:
+ return 0;
+ }
+ if (rlen < 2*sizeof(Uint32)) {
+ *rbuf = driver_alloc(2*sizeof(Uint32));
+ }
+ memcpy(*rbuf,resbuff,2*sizeof(Uint32));
+ return 2*sizeof(Uint32);
+}
+
+static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+ ErlDrvData res;
+
+ TRACE;
+
+ CHLD_STAT_LOCK;
+ if (opts->read_write & DO_READ) {
+ init_fd_data(opts->ifd, port_num);
+ }
+ if (opts->read_write & DO_WRITE) {
+ init_fd_data(opts->ofd, port_num);
+ }
+ res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd,
+ opts->packet_bytes,
+ opts->read_write, 0, -1);
+ CHLD_STAT_UNLOCK;
+ return res;
+}
+
+static void clear_fd_data(int fd)
+{
+ TRACE;
+
+ if (fd_data[fd].sz > 0) {
+ erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf);
+ ASSERT(erts_smp_atomic_read_nob(&sys_misc_mem_sz) >= fd_data[fd].sz);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, -1*fd_data[fd].sz);
+ }
+ fd_data[fd].buf = NULL;
+ fd_data[fd].sz = 0;
+ fd_data[fd].remain = 0;
+ fd_data[fd].cpos = NULL;
+ fd_data[fd].psz = 0;
+}
+
+static void nbio_stop_fd(ErlDrvPort prt, ErlDrvEvent ev)
+{
+ int fd;
+ TRACE;
+ driver_select(prt,ev,DO_READ|DO_WRITE,0);
+ erl_drv_ose_event_fetch(ev, NULL, &fd);
+ clear_fd_data(fd);
+ SET_BLOCKING(fd);
+}
+
+static void fd_stop(ErlDrvData fd) /* Does not close the fds */
+{
+ int ofd;
+
+ TRACE;
+
+ nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].in_sig_descr);
+ ofd = driver_data[(int)(long)fd].ofd;
+ if (ofd != (int)(long)fd && ofd != -1)
+ nbio_stop_fd(driver_data[(int)(long)fd].port_num, driver_data[(int)(long)fd].out_sig_descr);
+}
+
+/* Note that driver_data[fd].ifd == fd if the port was opened for reading, */
+/* otherwise (i.e. write only) driver_data[fd].ofd = fd. */
+
+static void erl_stop(ErlDrvData fd)
+{
+ ErlDrvPort prt;
+ int ofd;
+
+ TRACE;
+
+ prt = driver_data[(int)(long)fd].port_num;
+ nbio_stop_fd(prt, driver_data[(int)(long)fd].in_sig_descr);
+
+ ofd = driver_data[(int)(long)fd].ofd;
+ if (ofd != (int)(long)fd && (int)(long)ofd != -1)
+ nbio_stop_fd(prt, driver_data[(int)(long)fd].out_sig_descr);
+ else
+ ofd = -1;
+
+ CHLD_STAT_LOCK;
+
+ /* Mark as unused. */
+ driver_data[(int)(long)fd].pid = -1;
+
+ CHLD_STAT_UNLOCK;
+
+ /* SMP note: Close has to be last thing done (open file descriptors work
+ as locks on driver_data[] entries) */
+ driver_select(prt, driver_data[(int)(long)fd].in_sig_descr, ERL_DRV_USE, 0); /* close(fd); */
+ if (ofd >= 0) {
+ driver_select(prt, driver_data[(int)(long)fd].out_sig_descr, ERL_DRV_USE, 0); /* close(ofd); */
+ }
+}
+
+static void outputv(ErlDrvData e, ErlIOVec* ev)
+{
+ int fd = (int)(long)e;
+ ErlDrvPort ix = driver_data[fd].port_num;
+ int pb = driver_data[fd].packet_bytes;
+ ErlDrvSizeT sz;
+ char lb[4];
+ char* lbp;
+ ErlDrvSizeT len = ev->size;
+
+ TRACE;
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ /* if (pb >= 0 && (len & (((ErlDrvSizeT)1 << (pb*8))) - 1) != len) {*/
+ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ /* Handles 0 <= pb <= 4 only */
+ put_int32((Uint32) len, lb);
+ lbp = lb + (4-pb);
+
+ ev->iov[0].iov_base = lbp;
+ ev->iov[0].iov_len = pb;
+ ev->size += pb;
+ driver_pdl_lock(driver_data[fd].pdl);
+ if ((sz = driver_sizeq(ix)) > 0) {
+ /* fprintf(stderr,"0x%x: outputv, enq\n", current_process()); */
+ driver_enqv(ix, ev, 0);
+ if (sz + ev->size >= (1 << 13))
+ set_busy_port(ix, 1);
+ driver_pdl_unlock(driver_data[fd].pdl);
+ }
+ else {
+ OseSignal *sig;
+ /* fprintf(stderr,"0x%x: outputv, enq+sel\n", current_process()); */
+ driver_enqv(ix, ev, 0); /* n is the skip value */
+ driver_pdl_unlock(driver_data[fd].pdl);
+ driver_select(ix, driver_data[fd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG);
+ sig->sys_async.type = fd;
+ sig->sys_async.res = pb+len;
+ send(&sig,driver_data[fd].out_proc);
+ }
+ /* return 0;*/
+}
+
+
+static void output(ErlDrvData e, char* buf, ErlDrvSizeT len)
+{
+ int fd = (int)(long)e;
+ ErlDrvPort ix = driver_data[fd].port_num;
+ int pb = driver_data[fd].packet_bytes;
+ int ofd = driver_data[fd].ofd;
+ ErlDrvSizeT sz;
+ char lb[4];
+ char* lbp;
+#if 0
+ struct iovec iv[2];
+#endif
+
+ TRACE;
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ put_int32(len, lb);
+ lbp = lb + (4-pb);
+
+ driver_pdl_lock(driver_data[fd].pdl);
+ if ((sz = driver_sizeq(ix)) > 0) {
+ /* fprintf(stderr,"0x%x: output, enq\n", current_process()); */
+ driver_enq(ix, lbp, pb);
+ driver_enq(ix, buf, len);
+ driver_pdl_unlock(driver_data[fd].pdl);
+ if (sz + len + pb >= (1 << 13))
+ set_busy_port(ix, 1);
+ }
+ else {
+ OseSignal *sig;
+ /* fprintf(stderr,"0x%x: output, enq+select\n", current_process()); */
+#if 0
+ iv[0].iov_base = lbp;
+ iv[0].iov_len = pb; /* should work for pb=0 */
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+#endif
+ driver_enq(ix, lbp, pb);
+ driver_enq(ix, buf, len);
+ driver_pdl_unlock(driver_data[fd].pdl);
+ driver_select(ix, driver_data[ofd].out_sig_descr, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ sig = alloc(sizeof(SysDriverAsyncSignal),SYSDRIVERASYNCSIG);
+ sig->sys_async.type = fd;
+ sig->sys_async.res = pb+len;
+ send(&sig,driver_data[fd].out_proc);
+ }
+ return; /* 0; */
+}
+
+static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res)
+ /* Result: 0 (eof) or -1 (error) */
+{
+ int err = errno;
+ int fd;
+
+ ASSERT(res <= 0);
+ (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
+ erl_drv_ose_event_fetch(ready_fd,NULL,&fd);
+ clear_fd_data(fd);
+ if (res == 0) {
+ if (driver_data[fd].report_exit) {
+ CHLD_STAT_LOCK;
+
+ if (driver_data[fd].alive) {
+ /*
+ * We have eof and want to report exit status, but the process
+ * hasn't exited yet. When it does report_exit_status() will
+ * driver_select() this fd which will make sure that we get
+ * back here with driver_data[ready_fd].alive == 0 and
+ * driver_data[ready_fd].status set.
+ */
+ CHLD_STAT_UNLOCK;
+ return 0;
+ }
+ else {
+ int status = driver_data[fd].status;
+ CHLD_STAT_UNLOCK;
+
+#if 0 /*ose we should find something for these statuses*/
+ /* We need not be prepared for stopped/continued processes. */
+ if (WIFSIGNALED(status))
+ status = 128 + WTERMSIG(status);
+ else
+ status = WEXITSTATUS(status);
+#endif
+ driver_report_exit(driver_data[fd].port_num, status);
+ }
+ }
+ driver_failure_eof(port_num);
+ } else {
+ driver_failure_posix(port_num, err);
+ }
+ return 0;
+}
+
+static int async_read(ErlDrvEvent fd, byte *buff, int size) {
+ OseSignal *sigptr = erl_drv_ose_get_input_signal(fd);
+ int res = sigptr->sys_async.res;
+ if (res > 0)
+ memcpy(buff,sigptr->sys_async.buff,sigptr->sys_async.res);
+ errno = sigptr->sys_async.errno_copy;
+ send(&sigptr,sender(&sigptr));
+ ASSERT(erl_drv_ose_get_input_signal(fd) == NULL);
+ return res;
+}
+
+/* fd is the drv_data that is returned from the */
+/* initial start routine */
+/* ready_fd is the descriptor that is ready to read */
+
+static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
+{
+ int fd = (int)(long)e;
+ ErlDrvPort port_num;
+ int packet_bytes;
+ int res;
+ Uint h;
+
+ TRACE;
+
+ port_num = driver_data[fd].port_num;
+ packet_bytes = driver_data[fd].packet_bytes;
+
+ if (packet_bytes == 0) {
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == 0)
+ port_inp_failure(port_num, ready_fd, res);
+ else
+ driver_output(port_num, (char*) read_buf, res);
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+ else if (fd_data[fd].remain > 0) { /* We try to read the remainder */
+ /* space is allocated in buf */
+ res = async_read(ready_fd, (byte*)fd_data[fd].cpos,
+ fd_data[fd].remain);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == 0) {
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == fd_data[fd].remain) { /* we're done */
+ driver_output(port_num, fd_data[fd].buf,
+ fd_data[fd].sz);
+ clear_fd_data(fd);
+ }
+ else { /* if (res < fd_data[ready_fd].remain) */
+ fd_data[fd].cpos += res;
+ fd_data[fd].remain -= res;
+ }
+ }
+ else if (fd_data[fd].remain == 0) { /* clean fd */
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ /* We make one read attempt and see what happens */
+ res = async_read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == 0) { /* eof */
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res < packet_bytes - fd_data[fd].psz) {
+ memcpy(fd_data[fd].pbuf+fd_data[fd].psz,
+ read_buf, res);
+ fd_data[fd].psz += res;
+ }
+ else { /* if (res >= packet_bytes) */
+ unsigned char* cpos = read_buf;
+ int bytes_left = res;
+
+ while (1) {
+ int psz = fd_data[fd].psz;
+ char* pbp = fd_data[fd].pbuf + psz;
+
+ while(bytes_left && (psz < packet_bytes)) {
+ *pbp++ = *cpos++;
+ bytes_left--;
+ psz++;
+ }
+
+ if (psz < packet_bytes) {
+ fd_data[fd].psz = psz;
+ break;
+ }
+ fd_data[fd].psz = 0;
+
+ switch (packet_bytes) {
+ case 1: h = get_int8(fd_data[fd].pbuf); break;
+ case 2: h = get_int16(fd_data[fd].pbuf); break;
+ case 4: h = get_int32(fd_data[fd].pbuf); break;
+ default: ASSERT(0); return; /* -1; */
+ }
+
+ if (h <= (bytes_left)) {
+ driver_output(port_num, (char*) cpos, h);
+ cpos += h;
+ bytes_left -= h;
+ continue;
+ }
+ else { /* The last message we got was split */
+ char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
+ if (!buf) {
+ errno = ENOMEM;
+ port_inp_failure(port_num, ready_fd, -1);
+ }
+ else {
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, h);
+ sys_memcpy(buf, cpos, bytes_left);
+ fd_data[fd].buf = buf;
+ fd_data[fd].sz = h;
+ fd_data[fd].remain = h - bytes_left;
+ fd_data[fd].cpos = buf + bytes_left;
+ }
+ break;
+ }
+ }
+ }
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+}
+
+
+/* fd is the drv_data that is returned from the */
+/* initial start routine */
+/* ready_fd is the descriptor that is ready to read */
+
+static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd)
+{
+ int fd = (int)(long)e;
+ ErlDrvPort ix = driver_data[fd].port_num;
+ OseSignal *sigptr = erl_drv_ose_get_output_signal(ready_fd);
+ ssize_t n;
+ struct iovec* iv;
+ int vsize;
+
+ while (sigptr != NULL) {
+
+ driver_pdl_lock(driver_data[fd].pdl);
+ if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) {
+ /* fprintf(stderr,"0x%x: ready_output, unselect\n", current_process()); */
+ driver_pdl_unlock(driver_data[fd].pdl);
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ set_busy_port(ix, 0);
+ free_buf(&sigptr);
+ if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL)
+ return; /* 0; */
+ continue;
+ }
+ driver_pdl_unlock(driver_data[fd].pdl);
+ n = sigptr->sys_async.res;
+ if (n < 0) {
+ if (errno == ERRNO_BLOCK || errno == EINTR) {
+ /* fprintf(stderr,"0x%x: ready_output, send to %x\n", current_process(),driver_data[fd].out_proc);*/
+ send(&sigptr,driver_data[fd].out_proc);
+ if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL)
+ return; /* 0; */
+ continue;
+ } else {
+ int res = sigptr->sys_async.errno_copy;
+ /* fprintf(stderr,"0x%x: ready_output, error\n", current_process()); */
+ free_buf(&sigptr);
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ driver_failure_posix(ix, res);
+ if ((sigptr = erl_drv_ose_get_output_signal(ready_fd)) == NULL)
+ return; /* -1; */
+ continue;
+ }
+ } else {
+ int remain;
+ driver_pdl_lock(driver_data[fd].pdl);
+ if ((remain = driver_deq(driver_data[fd].port_num, n)) == -1)
+ abort();
+ /* fprintf(stderr, "0x%x: ready_output, %d to %x, remain %d\n", current_process(),
+ n, driver_data[fd].out_proc, remain); */
+ driver_pdl_unlock(driver_data[fd].pdl);
+ if (remain != 0)
+ send(&sigptr, driver_data[fd].out_proc);
+ else
+ continue;
+ }
+ sigptr = erl_drv_ose_get_output_signal(ready_fd);
+ }
+ return; /* 0; */
+}
+
+static void stop_select(ErlDrvEvent fd, void* _)
+{
+ close((int)fd);
+}
+
+
+void erts_do_break_handling(void)
+{
+ struct termios temp_mode;
+ int saved = 0;
+
+ /*
+ * Most functions that do_break() calls are intentionally not thread safe;
+ * therefore, make sure that all threads but this one are blocked before
+ * proceeding!
+ */
+ erts_smp_thr_progress_block();
+
+ /* during break we revert to initial settings */
+ /* this is done differently for oldshell */
+ if (using_oldshell && !replace_intr) {
+ SET_BLOCKING(1);
+ }
+ else if (isatty(0)) {
+ tcgetattr(0,&temp_mode);
+ tcsetattr(0,TCSANOW,&initial_tty_mode);
+ saved = 1;
+ }
+
+ /* call the break handling function, reset the flag */
+ do_break();
+
+ fflush(stdout);
+
+ /* after break we go back to saved settings */
+ if (using_oldshell && !replace_intr) {
+ SET_NONBLOCKING(1);
+ }
+ else if (saved) {
+ tcsetattr(0,TCSANOW,&temp_mode);
+ }
+
+ erts_smp_thr_progress_unblock();
+}
+
+static pid_t
+getpid(void)
+{
+ return get_bid(current_process());
+}
+
+int getpagesize(void)
+{
+ return 1024;
+}
+
+
+/* Fills in the systems representation of the jam/beam process identifier.
+** The Pid is put in STRING representation in the supplied buffer,
+** no interpretatione 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, size_t buffer_size){
+ pid_t p = getpid();
+ /* Assume the pid is scalar and can rest in an unsigned long... */
+ erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
+}
+
+int
+erts_sys_putenv_raw(char *key, char *value) {
+ return erts_sys_putenv(key, value);
+}
+int
+erts_sys_putenv(char *key, char *value)
+{
+ int res;
+ char *env;
+ Uint need = strlen(key) + strlen(value) + 2;
+
+#ifdef HAVE_COPYING_PUTENV
+ env = erts_alloc(ERTS_ALC_T_TMP, need);
+#else
+ env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, need);
+#endif
+ strcpy(env,key);
+ strcat(env,"=");
+ strcat(env,value);
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = putenv(env);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+#ifdef HAVE_COPYING_PUTENV
+ erts_free(ERTS_ALC_T_TMP, env);
+#endif
+ return res;
+}
+
+int
+erts_sys_getenv__(char *key, char *value, size_t *size)
+{
+ int res;
+ char *orig_value = getenv(key);
+ if (!orig_value)
+ res = -1;
+ else {
+ size_t len = sys_strlen(orig_value);
+ if (len >= *size) {
+ *size = len + 1;
+ res = 1;
+ }
+ else {
+ *size = len;
+ sys_memcpy((void *) value, (void *) orig_value, len+1);
+ res = 0;
+ }
+ }
+ return res;
+}
+
+int
+erts_sys_getenv_raw(char *key, char *value, size_t *size) {
+ return erts_sys_getenv(key, value, size);
+}
+
+/*
+ * erts_sys_getenv
+ * returns:
+ * -1, if environment key is not set with a value
+ * 0, if environment key is set and value fits into buffer res
+ * 1, if environment key is set but does not fit into buffer res
+ * res is set with the needed buffer res value
+ */
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ int res;
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ res = erts_sys_getenv__(key, value, size);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return res;
+}
+
+void
+sys_init_io(void)
+{
+ fd_data = (struct fd_data *)
+ erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data));
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz,
+ max_files * sizeof(struct fd_data));
+}
+
+extern const char pre_loaded_code[];
+extern Preload pre_loaded[];
+
+void erts_sys_alloc_init(void)
+{
+}
+
+void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
+{
+ void *res = malloc((size_t) sz);
+#if HAVE_ERTS_MSEG
+ if (!res) {
+ erts_mseg_clear_cache();
+ return malloc((size_t) sz);
+ }
+#endif
+ return res;
+}
+
+void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
+{
+ void *res = realloc(p, (size_t) sz);
+#if HAVE_ERTS_MSEG
+ if (!res) {
+ erts_mseg_clear_cache();
+ return realloc(p, (size_t) sz);
+ }
+#endif
+ return res;
+}
+
+void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
+{
+ free(p);
+}
+
+/* Return a pointer to a vector of names of preloaded modules */
+
+Preload*
+sys_preloaded(void)
+{
+ return pre_loaded;
+}
+
+/* Return a pointer to preloaded code for module "module" */
+unsigned char*
+sys_preload_begin(Preload* p)
+{
+ return p->code;
+}
+
+/* Clean up if allocated */
+void sys_preload_end(Preload* p)
+{
+ /* Nothing */
+}
+
+/* Read a key from console (?) */
+
+int sys_get_key(fd)
+int fd;
+{
+ int c;
+ unsigned char rbuf[64];
+
+ TRACE;
+
+ fflush(stdout); /* Flush query ??? */
+
+ if ((c = read(fd,rbuf,64)) <= 0) {
+ return c;
+ }
+
+ return rbuf[0];
+}
+
+
+#ifdef DEBUG
+
+extern int erts_initialized;
+void
+erl_assert_error(char* expr, char* file, int line)
+{
+ fflush(stdout);
+ fprintf(stderr, "Assertion failed: %s in %s, line %d\n",
+ expr, file, line);
+ fflush(stderr);
+ ramlog_printf("%d: Assertion failed: %s in %s, line %d\n",
+ current_process(), expr, file, line);
+ abort();
+}
+
+void
+erl_debug(char* fmt, ...)
+{
+ char sbuf[1024]; /* Temporary buffer. */
+ va_list va;
+
+ if (debug_log) {
+ va_start(va, fmt);
+ vsprintf(sbuf, fmt, va);
+ va_end(va);
+ fprintf(stderr, "%s", sbuf);
+ }
+}
+
+#endif /* DEBUG */
+
+static ERTS_INLINE void
+report_exit_status(ErtsSysReportExit *rep, int status)
+{
+ Port *pp;
+#ifdef ERTS_SMP
+ CHLD_STAT_UNLOCK;
+ pp = erts_thr_id2port_sflgs(rep->port,
+ ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+ CHLD_STAT_LOCK;
+#else
+ pp = erts_id2port_sflgs(rep->port,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+#endif
+ if (pp) {
+ if (rep->ifd >= 0) {
+ driver_data[rep->ifd].alive = 0;
+ driver_data[rep->ifd].status = status;
+ (void) driver_select(ERTS_Port2ErlDrvPort(pp),
+ rep->in_sig_descr,
+ (ERL_DRV_READ|ERL_DRV_USE),
+ 1);
+ }
+ if (rep->ofd >= 0) {
+ driver_data[rep->ofd].alive = 0;
+ driver_data[rep->ofd].status = status;
+ (void) driver_select(ERTS_Port2ErlDrvPort(pp),
+ rep->out_sig_descr,
+ (ERL_DRV_WRITE|ERL_DRV_USE),
+ 1);
+ }
+#ifdef ERTS_SMP
+ erts_thr_port_release(pp);
+#else
+ erts_port_release(pp);
+#endif
+ }
+ erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
+}
+
+#define ERTS_REPORT_EXIT_STATUS report_exit_status
+
+/*
+ * 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)
+{
+ ASSERT(get_fsem(current_process()) == 0);
+#ifdef ERTS_SMP
+ ERTS_CHK_IO(!runnable);
+#else
+ ERTS_CHK_IO( 1 );
+#endif
+ ASSERT(get_fsem(current_process()) == 0);
+ ERTS_SMP_LC_ASSERT(!erts_thr_progress_is_blocking());
+}
+
+
+#ifdef ERTS_SMP
+
+void
+erts_sys_main_thread(void)
+{
+ erts_thread_disable_fpe();
+
+ /* Become signal receiver thread... */
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_set_thread_name("signal_receiver");
+#endif
+
+ while (1) {
+ static const SIGSELECT sigsel[] = {0};
+ OseSignal *msg = receive(sigsel);
+
+ fprintf(stderr,"Main thread got message %d from 0x%x!!\r\n",
+ msg->sig_no, sender(&msg));
+ free_buf(&msg);
+ }
+}
+
+#endif /* ERTS_SMP */
+
+void
+erl_sys_args(int* argc, char** argv)
+{
+ int i, j;
+
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+
+ init_check_io();
+
+ /* Handled arguments have been marked with NULL. Slide arguments
+ not handled towards the beginning of argv. */
+ for (i = 0, j = 0; i < *argc; i++) {
+ if (argv[i])
+ argv[j++] = argv[i];
+ }
+ *argc = j;
+
+}