/* * %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 #include "sys/time.h" #include "time.h" #include "sys/uio.h" #include "termios.h" #include "ctype.h" #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 #include "sys.h" #include "erl_thr_progress.h" #ifdef USE_THREADS #include "erl_threads.h" #endif #include "erl_mseg.h" #include "unistd.h" #include "efs.h" #include "erl_printf.h" #include "aio.h" #include "pm.h" #include "fcntl.h" /* Set the define to 1 to get some logging */ #if 0 #include "ramlog.h" #define LOG(output) ramlog_printf output #else #define LOG(output) #endif extern char **environ; static erts_smp_rwmtx_t environ_rwmtx; static PROCESS sig_proxy_pid = 0; #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" /* The priority for reader/writer processes */ #define FD_PROC_PRI get_pri(current_process()) typedef struct ErtsSysReportExit_ ErtsSysReportExit; struct ErtsSysReportExit_ { ErtsSysReportExit *next; Eterm port; int pid; int ifd; int ofd; ErlDrvEvent attach_event; ErlDrvEvent input_event; ErlDrvEvent output_event; }; /* This data is shared by these drivers - initialized by spawn_init() */ static struct driver_data { ErlDrvPort port_num; int ofd; int ifd; int packet_bytes; ErtsSysReportExit *report_exit; int pid; int alive; int status; ErlDrvEvent input_event; ErlDrvEvent output_event; struct aiocb aiocb; FmHandle handle; char *install_handle; } *driver_data; /* indexed by fd */ struct async { SIGSELECT signo; ErlDrvTermData port; ErlDrvTermData proc; PROCESS spid; PROCESS target; Uint32 ref; }; static ErtsSysReportExit *report_exit_list; static ERTS_INLINE void report_exit_status(ErtsSysReportExit *rep, int status); 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 #define SET_AIO(REQ,FD,SIZE,BUFF) \ memset(&(REQ),0,sizeof(REQ)); \ (REQ).aio_fildes = FD; \ (REQ).aio_offset = FM_POSITION_CURRENT; \ (REQ).aio_nbytes = SIZE; \ (REQ).aio_buf = BUFF; \ (REQ).aio_sigevent.sigev_notify = SIGEV_NONE /* the first sizeof(struct aiocb *) bytes of the write buffer * will contain the pointer to the aiocb struct, this needs * to be freed between asynchronous writes. * A write of 0 bytes is ignored. */ #define WRITE_AIO(FD,SIZE,BUFF) do { \ if (SIZE > 0) { \ struct aiocb *write_req = driver_alloc(sizeof(struct aiocb)); \ char *write_buff = driver_alloc((sizeof(char)*SIZE)+1+ \ (sizeof(struct aiocb *))); \ *(struct aiocb **)write_buff = (struct aiocb *)write_req; \ write_buff += sizeof(struct aiocb *); \ memcpy(write_buff,BUFF,SIZE+1); \ SET_AIO(*write_req,FD,SIZE,write_buff); \ if (aio_write(write_req)) \ ramlog_printf("%s:%d: write failed with %d\n", \ __FILE__,__LINE__,errno); \ } \ } while(0) /* free the write_buffer and write_req * created in the WRITE_AIO() request macro */ #define FREE_AIO(ptr) do { \ struct aiocb *aiocb_ptr; \ char *buffer_ptr; \ aiocb_ptr = *(struct aiocb **)((ptr)-sizeof(struct aiocb *)); \ buffer_ptr = (((char*)ptr)-sizeof(struct aiocb *)); \ driver_free(aiocb_ptr); \ driver_free(buffer_ptr); \ } while(0) #define DISPATCH_AIO(sig) do { \ if (aio_dispatch(sig)) \ ramlog_printf("%s:%d: dispatch failed with %d\n", \ __FILE__,__LINE__,errno); \ } while(0) #define AIO_PIPE_SIZE 1024 /* debug print macros */ #define DEBUG_RES 0 #ifdef DEBUG_RES #define DEBUG_CHECK_RES(actual, expected) \ do { \ if (actual != expected ) { \ ramlog_printf("Result check failed" \ " got: 0x%08x expected:0x%08x\nat: %s:%d\n", \ actual, expected, __FILE__, __LINE__); \ abort(); /* This might perhaps be too harsh? */ \ } \ } while(0) #else #define DEBUG_CHECK_RES #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 PROCESS get_signal_proxy_pid(void); 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 */ /* The two functions below are stolen from win_con.c They have to use malloc/free/realloc directly becasue we want to do able to do erts_printf very early on. */ #define VPRINTF_BUF_INC_SIZE 128 static erts_dsprintf_buf_t * grow_vprintf_buf(erts_dsprintf_buf_t *dsbufp, size_t need) { char *buf; size_t size; ASSERT(dsbufp); if (!dsbufp->str) { size = (((need + VPRINTF_BUF_INC_SIZE - 1) / VPRINTF_BUF_INC_SIZE) * VPRINTF_BUF_INC_SIZE); buf = (char *) malloc(size * sizeof(char)); } else { size_t free_size = dsbufp->size - dsbufp->str_len; if (need <= free_size) return dsbufp; size = need - free_size + VPRINTF_BUF_INC_SIZE; size = (((size + VPRINTF_BUF_INC_SIZE - 1) / VPRINTF_BUF_INC_SIZE) * VPRINTF_BUF_INC_SIZE); size += dsbufp->size; buf = (char *) realloc((void *) dsbufp->str, size * sizeof(char)); } if (!buf) return NULL; if (buf != dsbufp->str) dsbufp->str = buf; dsbufp->size = size; return dsbufp; } static int erts_sys_ramlog_printf(char *format, va_list arg_list) { int res,i; erts_dsprintf_buf_t dsbuf = ERTS_DSPRINTF_BUF_INITER(grow_vprintf_buf); res = erts_vdsprintf(&dsbuf, format, arg_list); if (res >= 0) { for (i = 0; i < dsbuf.str_len; i+= 50) /* We print 50 characters at a time because otherwise the ramlog looks broken */ ramlog_printf("%.*s",dsbuf.str_len-50 < 0?dsbuf.str_len:50,dsbuf.str+i); } if (dsbuf.str) free((void *) dsbuf.str); return res; } 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_printf_stdout_func = erts_sys_ramlog_printf; 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 */ union SIGNAL { SIGSELECT sig_no; struct FmReadPtr fm_read_reply; struct FmWritePtr fm_write_reply; struct async async; }; /* II. The spawn/fd drivers */ /* * Decreasing the size of it below 16384 is not allowed. */ #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 stop_select(ErlDrvEvent, void*); static PROCESS get_signal_proxy_pid(void) { union SIGNAL *sig; SIGSELECT any_sig[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH}; if (!sig_proxy_pid) { sig = alloc(sizeof(union SIGNAL), ERTS_SIGNAL_OSE_DRV_ATTACH); hunt("ose_signal_driver_proxy", 0, NULL, &sig); sig = receive(any_sig); sig_proxy_pid = sender(&sig); free_buf(&sig); } ASSERT(sig_proxy_pid); return sig_proxy_pid; } static ErlDrvOseEventId resolve_signal(union SIGNAL* sig) { switch(sig->sig_no) { case FM_READ_PTR_REPLY: return (ErlDrvOseEventId)sig->fm_read_reply.handle; case FM_WRITE_PTR_REPLY: return (ErlDrvOseEventId)sig->fm_write_reply.handle; case ERTS_SIGNAL_OSE_DRV_ATTACH: return (ErlDrvOseEventId)sig->async.target; default: break; } return (ErlDrvOseEventId)-1; } struct erl_drv_entry spawn_driver_entry = { spawn_init, spawn_start, NULL, /* 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 }; struct erl_drv_entry fd_driver_entry = { NULL, fd_start, fd_stop, output, ready_input, ready_output, "fd", NULL, NULL, fd_control, NULL, NULL, 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 }; static void set_spawn_fd(int local_fd, int remote_fd, PROCESS remote_pid) { PROCESS vm_pid; FmHandle handle; char env_val[55]; char env_name[10]; EfsStatus efs_res; /* get pid of pipevm and handle of chosen fd */ efs_res = efs_examine_fd(local_fd, FLIB_FD_VMPID, &vm_pid, 0); DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); /* setup the file descriptor to buffer per line */ efs_res = efs_config_fd(local_fd, FLIB_FD_BUFMODE, FM_BUFF_LINE, FLIB_FD_BUFSIZE, 80, 0); DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); /* duplicate handle and set spawn pid owner */ efs_res = efs_dup_to(local_fd, remote_pid, &handle); DEBUG_CHECK_RES(efs_res, EFS_SUCCESS); sprintf(env_name, "FD%d", remote_fd); /* Syntax of the environment variable: * "FD#" ",,,," */ sprintf(env_val, "0x%lx,0x%lx,%lu,%lu,0x%x", vm_pid, handle, FM_BUFF_LINE, 80, O_APPEND); set_env(remote_pid, env_name, env_val); } static ErlDrvData set_driver_data(ErlDrvPort port_num, int ifd, int ofd, int packet_bytes, int read_write, int exit_status, PROCESS pid) { Port *prt; ErtsSysReportExit *report_exit; prt = erts_drvport2port(port_num); if (prt != ERTS_INVALID_ERL_DRV_PORT) { prt->os_pid = pid; } /* READ */ if (read_write & DO_READ) { EfsStatus res = efs_examine_fd(ifd, FLIB_FD_HANDLE, &driver_data[ifd].handle, 0); if (res != EFS_SUCCESS) ramlog_printf("%s:%d: efs_examine_fd(%d) failed with %d\n", __FILE__,__LINE__,ifd,errno); driver_data[ifd].ifd = ifd; driver_data[ifd].packet_bytes = packet_bytes; driver_data[ifd].port_num = port_num; driver_data[ifd].pid = pid; /* async read struct */ memset(&driver_data[ifd].aiocb, 0, sizeof(struct aiocb)); driver_data[ifd].aiocb.aio_buf = driver_alloc(AIO_PIPE_SIZE); driver_data[ifd].aiocb.aio_fildes = ifd; driver_data[ifd].aiocb.aio_nbytes = (packet_bytes?packet_bytes:AIO_PIPE_SIZE); driver_data[ifd].alive = 1; driver_data[ifd].status = 0; driver_data[ifd].input_event = erl_drv_ose_event_alloc(FM_READ_PTR_REPLY, driver_data[ifd].handle, resolve_signal, &driver_data[ifd].ifd); /* READ & WRITE */ if (read_write & DO_WRITE) { driver_data[ifd].ofd = ofd; efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); driver_data[ifd].output_event = erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, driver_data[ofd].handle, resolve_signal, &driver_data[ofd].ofd); driver_data[ofd].pid = pid; if (ifd != ofd) { driver_data[ofd] = driver_data[ifd]; driver_data[ofd].aiocb.aio_buf = NULL; } } else { /* READ ONLY */ driver_data[ifd].ofd = -1; } /* enable input event */ (void) driver_select(port_num, driver_data[ifd].input_event, (ERL_DRV_READ | ERL_DRV_USE), 1); if (aio_read(&driver_data[ifd].aiocb)) ramlog_printf("%s:%d: aio_read(%d) failed with %d\n", __FILE__,__LINE__,ifd,errno); } else { /* WRITE ONLY */ efs_examine_fd(ofd, FLIB_FD_HANDLE, &driver_data[ofd].handle, 0); driver_data[ofd].packet_bytes = packet_bytes; driver_data[ofd].port_num = port_num; driver_data[ofd].ofd = ofd; driver_data[ofd].pid = pid; driver_data[ofd].alive = 1; driver_data[ofd].status = 0; driver_data[ofd].output_event = erl_drv_ose_event_alloc(FM_WRITE_PTR_REPLY, driver_data[ofd].handle, resolve_signal, &driver_data[ofd].ofd); driver_data[ofd].input_event = driver_data[ofd].output_event; } /* this is used for spawned load modules, and is needed * to properly uninstall them */ if (exit_status) { struct PmProgramInfo *info; int install_handle_size; union SIGNAL *sig; PmStatus pm_status; 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; report_exit_list = report_exit; report_exit->attach_event = erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH, pid, resolve_signal, &driver_data[ifd].ifd); /* setup ifd and ofd report exit */ driver_data[ifd].report_exit = report_exit; driver_data[ofd].report_exit = report_exit; pm_status = ose_pm_program_info(pid, &info); DEBUG_CHECK_RES(pm_status, PM_SUCCESS); install_handle_size = strlen(info->install_handle)+1; driver_data[ifd].install_handle = driver_alloc(install_handle_size); strcpy(driver_data[ifd].install_handle, info->install_handle); free_buf((union SIGNAL **)&info); sig = alloc(sizeof(struct async), ERTS_SIGNAL_OSE_DRV_ATTACH); sig->async.target = pid; send(&sig, get_signal_proxy_pid()); /* this event will trigger when we receive an attach signal * from the recently dead load module */ (void)driver_select(port_num,report_exit->attach_event, DO_READ, 1); } else { report_exit = NULL; } /* the return value is the pointer to the driver_data struct we created * in this function, it will be used in the drivers input * and output functions */ return (ErlDrvData)((!(read_write & DO_READ) && read_write & DO_WRITE) ? &driver_data[ofd] : &driver_data[ifd]); } 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_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) { 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; } /* FIXME write a decent text on pipes on ose */ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts) { int ifd[2]; int ofd[2]; static uint32_t ticker = 1; PmStatus pm_status; OSDOMAIN domain = PM_NEW_DOMAIN; PROCESS progpid, mainbid, mainpid; char *handle = NULL; struct PmProgramInfo *info; char *args = NULL; char *tmp_handle; ErlDrvData res = (ErlDrvData)-1; int handle_size; char *ptr; args = driver_alloc(strlen(name)+1); strcpy(args, name); /* We need to handle name in three parts * - install handle (must be unique) * - install binary (needed for ose_pm_install_load_module()) * - full path (as argument to the spawned applications env.var */ /* full path including arguments */ args = driver_alloc(strlen(name)+1); strcpy(args, name); /* handle path */ tmp_handle = strrchr(name, '/'); if (tmp_handle == NULL) { tmp_handle = name; } else { tmp_handle++; } /* handle args */ ptr = strchr(tmp_handle, ' '); if (ptr != NULL) { *ptr = '\0'; handle_size = ptr - tmp_handle; } else { handle_size = strlen(name)+1; } /* make room for ticker */ handle_size += (ticker<10)?3:((ticker<100)?4:5); handle = driver_alloc(handle_size); do { snprintf(handle, handle_size, "%s_%d", tmp_handle, ticker); pm_status = ose_pm_install_load_module(0, "ELF", name, handle, 0, 0, NULL); ticker++; } while (pm_status == PM_EINSTALL_HANDLE_ALREADY_INSTALLED); if (pm_status != PM_SUCCESS) { errno = ENOSYS; /* FIXME add comment */ return ERL_DRV_ERROR_ERRNO; } /* Create Program */ pm_status = ose_pm_create_program(&domain, handle, 0, 0, NULL, &progpid, &mainbid); DEBUG_CHECK_RES(pm_status, PM_SUCCESS); /* Get the mainpid from the newly created program */ pm_status = ose_pm_program_info(progpid, &info); DEBUG_CHECK_RES(pm_status, PM_SUCCESS); mainpid = info->main_process; free_buf ((union SIGNAL **)&info); /* pipevm needs to be started * pipe will return 0 if success, -1 if not, * errno will be set */ if (pipe(ifd) != 0 || pipe(ofd) != 0) { DEBUG_CHECK_RES(0, -1); ASSERT(0); } /* setup driver data */ res = set_driver_data(port_num, ofd[0], ifd[1], opts->packet_bytes, opts->read_write, 1 /* opts->exit_status */, progpid); /* init the fd_data array for read/write */ init_fd_data(ofd[0], port_num); init_fd_data(ifd[1], port_num); /* setup additional configurations * for the spawned applications environment */ if (args != NULL) { set_env(progpid, "ARGV", args); } set_env(mainbid, "EFS_RESOLVE_TMO", 0); set_spawn_fd(ifd[0], 0, mainpid); set_spawn_fd(ofd[1], 1, mainpid); set_spawn_fd(ofd[1], 2, mainpid); /* start the spawned program */ pm_status = ose_pm_start_program(mainbid); DEBUG_CHECK_RES(pm_status, PM_SUCCESS); /* close unused fd's */ close(ifd[0]); close(ofd[1]); if (handle) { driver_free(handle); } return (ErlDrvData)res; } #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) { struct driver_data *data = (struct driver_data *)drv_data; char resbuff[2*sizeof(Uint32)]; switch (command) { case FD_CTRL_OP_GET_WINSIZE: { Uint32 w,h; if (fd_get_window_size(data->ifd,&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; 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 = 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) { 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; driver_select(prt,ev,DO_READ|DO_WRITE,0); erl_drv_ose_event_fetch(ev, NULL, NULL, (void **)&fd); clear_fd_data(*fd); SET_BLOCKING(*fd); } static void fd_stop(ErlDrvData drv_data) /* Does not close the fds */ { struct driver_data *data = (struct driver_data *)drv_data; if (data->ofd != -1) { if (data->ifd != data->ofd) { /* read and write */ nbio_stop_fd(data->port_num, data->input_event); nbio_stop_fd(data->port_num, data->output_event); } else { /* write only */ nbio_stop_fd(data->port_num, data->output_event); } } else { /* read only */ nbio_stop_fd(data->port_num, data->input_event); } } static void erl_stop(ErlDrvData drv_data) { struct driver_data *data = (struct driver_data *)drv_data; CHLD_STAT_LOCK; data->pid = -1; CHLD_STAT_UNLOCK; if (data->ofd != -1) { if (data->ifd != data->ofd) { /* read and write */ nbio_stop_fd(data->port_num, data->input_event); nbio_stop_fd(data->port_num, data->output_event); } else { /* write only */ nbio_stop_fd(data->port_num, data->output_event); } } else { /* read only */ nbio_stop_fd(data->port_num, data->input_event); } close(data->ifd); close(data->ofd); } /* The parameter e is a pointer to the driver_data structure * related to the fd to be used as output */ static void output(ErlDrvData drv_data, char* buf, ErlDrvSizeT len) { ErlDrvSizeT sz; char lb[4]; char* lbp; struct driver_data *data = (struct driver_data *)drv_data; if (((data->packet_bytes == 2) && (len > 0xffff)) || (data->packet_bytes == 1 && len > 0xff)) { driver_failure_posix(data->port_num, EINVAL); return; /* -1; */ } put_int32(len, lb); lbp = lb + (4-(data->packet_bytes)); if ((sz = driver_sizeq(data->port_num)) > 0) { if (data->packet_bytes != 0) { driver_enq(data->port_num, lbp, data->packet_bytes); } driver_enq(data->port_num, buf, len); if (sz + len + data->packet_bytes >= (1 << 13)) set_busy_port(data->port_num, 1); } else { char *pbbuf; if (data->packet_bytes != 0) { pbbuf = malloc(len + data->packet_bytes); int i; for (i = 0; i < data->packet_bytes; i++) { *pbbuf++ = *lbp++; } strncpy(pbbuf, buf, len); pbbuf -= data->packet_bytes; } driver_select(data->port_num, data->output_event, ERL_DRV_WRITE|ERL_DRV_USE, 1); WRITE_AIO(data->ofd, (data->packet_bytes ? len+data->packet_bytes : len), (data->packet_bytes ? pbbuf : buf)); if (data->packet_bytes != 0) free(pbbuf); } return; /* 0; */ } /* This function is being run when we in recieve * either a read of 0 bytes, or the attach signal from a dying * spawned load module */ static int port_inp_failure(ErlDrvPort port_num, ErlDrvEvent ready_fd, int res) /* Result: 0 (eof) or -1 (error) */ { int *fd; SIGSELECT sig_no; ASSERT(res <= 0); erl_drv_ose_event_fetch(ready_fd,&sig_no, NULL, (void **)&fd); /* As we need to handle two signals, we do this in two steps */ if (driver_data[*fd].alive) { report_exit_status(driver_data[*fd].report_exit, 0); /* status? */ } else { driver_select(port_num,ready_fd,DO_READ|DO_WRITE,0); clear_fd_data(*fd); driver_report_exit(driver_data[*fd].port_num, driver_data[*fd].status); /* As we do not really know if the spawn has crashed or exited nicely * we do not check the result status of the following call.. FIXME * can we handle this in a better way? */ ose_pm_uninstall_load_module(driver_data[*fd].install_handle); driver_free(driver_data[*fd].install_handle); driver_free((void *)driver_data[*fd].aiocb.aio_buf); close(*fd); } return 0; } /* The parameter e is a pointer to the driver_data structure * related to the fd to be used as output. * ready_fd is the event that triggered this call to ready_input */ static void ready_input(ErlDrvData drv_data, ErlDrvEvent ready_fd) { int res; Uint h; char *buf; union SIGNAL *sig; struct driver_data *data = (struct driver_data *)drv_data; sig = erl_drv_ose_get_signal(ready_fd); ASSERT(sig); while (sig) { /* If we've recieved an attach signal, we need to handle * it in port_inp_failure */ if (sig->sig_no == ERTS_SIGNAL_OSE_DRV_ATTACH) { port_inp_failure(data->port_num, ready_fd, 0); } else { res = sig->fm_read_reply.actual; if (res == 0) { port_inp_failure(data->port_num, ready_fd, res); break; } if (data->packet_bytes == 0) { if (res < 0) { if ((errno != EINTR) && (errno != ERRNO_BLOCK)) { port_inp_failure(data->port_num, ready_fd, res); } } else if (res == 0) { /* read of 0 bytes, eof, otherside of pipe is assumed dead */ port_inp_failure(data->port_num, ready_fd, res); break; } else { buf = driver_alloc(res); memcpy(buf, (void *)data->aiocb.aio_buf, res); driver_select(data->port_num, data->output_event, ERL_DRV_WRITE|ERL_DRV_USE, 1); driver_output(data->port_num, (char*) buf, res); driver_free(buf); } /* clear the previous read */ memset(data->aiocb.aio_buf, 0, res); /* issue a new read */ DISPATCH_AIO(sig); aio_read(&data->aiocb); } else if (data->packet_bytes && fd_data[data->ifd].remain > 0) { /* we've read a partial package, or a header */ if (res == fd_data[data->ifd].remain) { /* we are done! */ char *buf = data->aiocb.aio_buf; int i; /* do we have anything buffered? */ if (fd_data[data->ifd].buf != NULL) { memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, buf, res); buf = fd_data[data->ifd].buf; } fd_data[data->ifd].sz += res; driver_output(data->port_num, buf, (fd_data[data->ifd].sz>0?fd_data[data->ifd].sz:res)); clear_fd_data(data->ifd); /* clear the previous read */ memset(data->aiocb.aio_buf, 0, res); /* issue a new read */ DISPATCH_AIO(sig); data->aiocb.aio_nbytes = data->packet_bytes; if (data->aiocb.aio_buf == NULL) { port_inp_failure(data->port_num, ready_fd, -1); } aio_read(&data->aiocb); } else if(res < fd_data[data->ifd].remain) { /* received part of a package */ if (fd_data[data->ifd].sz == 0) { fd_data[data->ifd].sz += res; memcpy(fd_data[data->ifd].buf, data->aiocb.aio_buf, res); fd_data[data->ifd].remain -= res; } else { memcpy(fd_data[data->ifd].buf + fd_data[data->ifd].sz, data->aiocb.aio_buf, res); fd_data[data->ifd].sz += res; fd_data[data->ifd].remain -= res; } /* clear the previous read */ memset(data->aiocb.aio_buf, 0, res); /* issue a new read */ DISPATCH_AIO(sig); data->aiocb.aio_nbytes = fd_data[data->ifd].remain; if (data->aiocb.aio_buf == NULL) { port_inp_failure(data->port_num, ready_fd, -1); } aio_read(&data->aiocb); } } else if (data->packet_bytes && fd_data[data->ifd].remain == 0) { /* we've recieved a header */ /* analyze the header FIXME */ switch (data->packet_bytes) { case 1: h = get_int8(data->aiocb.aio_buf); break; case 2: h = get_int16(data->aiocb.aio_buf); break; case 4: h = get_int32(data->aiocb.aio_buf); break; } fd_data[data->ifd].buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h + data->packet_bytes); fd_data[data->ifd].remain = ((h + data->packet_bytes) - res); /* clear the previous read */ memset(data->aiocb.aio_buf, 0, data->packet_bytes); /* issue a new read */ DISPATCH_AIO(sig); data->aiocb.aio_nbytes = h; if (data->aiocb.aio_buf == NULL) { port_inp_failure(data->port_num, ready_fd, -1); } aio_read(&data->aiocb); } } sig = erl_drv_ose_get_signal(ready_fd); } } /* The parameter e is a pointer to the driver_data structure * related to the fd to be used as output. * ready_fd is the event that triggered this call to ready_input */ static void ready_output(ErlDrvData drv_data, ErlDrvEvent ready_fd) { SysIOVec *iov; int vlen; int res; union SIGNAL *sig; struct driver_data *data = (struct driver_data *)drv_data; sig = erl_drv_ose_get_signal(ready_fd); ASSERT(sig); while (sig != NULL) { if (sig->fm_write_reply.actual <= 0) { int status; status = efs_status_to_errno(sig->fm_write_reply.status); driver_select(data->port_num, ready_fd, ERL_DRV_WRITE, 0); DISPATCH_AIO(sig); FREE_AIO(sig->fm_write_reply.buffer); driver_failure_posix(data->port_num, status); } else { /* written bytes > 0 */ iov = driver_peekq(data->port_num, &vlen); if (vlen > 0) { DISPATCH_AIO(sig); FREE_AIO(sig->fm_write_reply.buffer); res = driver_deq(data->port_num, iov[0].iov_len); if (res > 0) { iov = driver_peekq(data->port_num, &vlen); WRITE_AIO(data->ofd, iov[0].iov_len, iov[0].iov_base); } } else if (vlen == 0) { DISPATCH_AIO(sig); FREE_AIO(sig->fm_write_reply.buffer); } } sig = erl_drv_ose_get_signal(ready_fd); } } static void stop_select(ErlDrvEvent ready_fd, void* _) { int *fd; erl_drv_ose_event_fetch(ready_fd, NULL, NULL, (void **)&fd); erl_drv_ose_event_free(ready_fd); close(*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; erts_smp_rwmtx_rwlock(&environ_rwmtx); res = set_env(get_bid(current_process()), key, value); erts_smp_rwmtx_rwunlock(&environ_rwmtx); return res; } int erts_sys_unsetenv(char *key) { int res; erts_smp_rwmtx_rwlock(&environ_rwmtx); res = set_env(get_bid(current_process()),key,NULL); erts_smp_rwmtx_rwunlock(&environ_rwmtx); return res; } int erts_sys_getenv__(char *key, char *value, size_t *size) { int res; char *orig_value = get_env(get_bid(current_process()), 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; } free_buf((union SIGNAL **)&orig_value); } 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]; 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(const char* expr, const char* func, const char* file, int line) { fflush(stdout); fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", file, func, line, expr); fflush(stderr); ramlog_printf("%s:%d:%s() Assertion failed: %s\n", file, func, line, expr); 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) { if (rep->ifd >= 0) { driver_data[rep->ifd].alive = 0; driver_data[rep->ifd].status = status; } if (rep->ofd >= 0) { driver_data[rep->ofd].alive = 0; driver_data[rep->ofd].status = status; } 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 ASSERT(erts_get_scheduler_data()->no == 1); 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}; union SIGNAL *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; }