diff options
Diffstat (limited to 'erts/emulator/sys/ose/sys.c')
-rw-r--r-- | erts/emulator/sys/ose/sys.c | 1820 |
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; + +} |