diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/sys/vxworks/sys.c | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/sys/vxworks/sys.c')
-rw-r--r-- | erts/emulator/sys/vxworks/sys.c | 2594 |
1 files changed, 2594 insertions, 0 deletions
diff --git a/erts/emulator/sys/vxworks/sys.c b/erts/emulator/sys/vxworks/sys.c new file mode 100644 index 0000000000..abddc7e107 --- /dev/null +++ b/erts/emulator/sys/vxworks/sys.c @@ -0,0 +1,2594 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * system-dependent functions + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <vxWorks.h> +#include <version.h> +#include <string.h> +#include <types.h> +#include <sigLib.h> +#include <ioLib.h> +#include <iosLib.h> +#include <envLib.h> +#include <fioLib.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <symLib.h> +#include <sysLib.h> +#include <sysSymTbl.h> +#include <loadLib.h> +#include <taskLib.h> +#include <taskVarLib.h> +#include <taskHookLib.h> +#include <tickLib.h> +#include <time.h> +#include <rngLib.h> +#include <semLib.h> +#include <selectLib.h> +#include <sockLib.h> +#include <a_out.h> +#include <wdLib.h> +#include <timers.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <stdarg.h> + + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif + +#include "sys.h" +#include "erl_alloc.h" + +/* 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 "elib_stat.h" + +#include "reclaim_private.h" /* Some more or less private reclaim facilities */ + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +EXTERN_FUNCTION(void, erl_start, (int, char**)); +EXTERN_FUNCTION(void, erl_exit, (int n, char*, _DOTS_)); +EXTERN_FUNCTION(void, erl_error, (char*, va_list)); +EXTERN_FUNCTION(int, driver_interrupt, (int, int)); +EXTERN_FUNCTION(void, increment_time, (int)); +EXTERN_FUNCTION(int, next_time, (_VOID_)); +EXTERN_FUNCTION(void, set_reclaim_free_function, (FreeFunction)); +EXTERN_FUNCTION(int, erl_mem_info_get, (MEM_PART_STATS *)); +EXTERN_FUNCTION(void, erl_crash_dump, (char* file, int line, char* fmt, ...)); + +#define ISREG(st) (((st).st_mode&S_IFMT) == S_IFREG) + +/* these are defined in usrLib.c */ +extern int spTaskPriority, spTaskOptions; + +/* forward declarations */ +static FUNCTION(FUNCPTR, lookup, (char*)); +static FUNCTION(int, read_fill, (int, char*, int)); +#if (CPU == SPARC) +static FUNCTION(RETSIGTYPE, fpe_sig_handler, (int)); /*where is this fun? */ +#elif (CPU == PPC603) +static FUNCTION(void, fix_registers, (void)); +#endif +static FUNCTION(void, close_pipes, (int*, int*, int)); +static FUNCTION(void, delete_hook, (void)); +static FUNCTION(void, initialize_allocation, (void)); + +FUNCTION(STATUS, uxPipeDrv, (void)); +FUNCTION(STATUS, pipe, (int*)); +FUNCTION(void, uxPipeShow, (int)); + +void erl_main(int argc, char **argv); +void argcall(char *args); + +/* Malloc-realted functions called from the VxWorks shell */ +EXTERN_FUNCTION(int, erl_set_memory_block, + (int, int, int, int, int, int, int, int, int, int)); +EXTERN_FUNCTION(int, erl_memory_show, + (int, int, int, int, int, int, int, int, int, int)); + +#define DEFAULT_PORT_STACK_SIZE 100000 +static int port_stack_size; + +static int erlang_id = 0; /* Inited at loading, set/reset at each run */ + +/* interval time reported to emulator */ +static int sys_itime; + +/* XXX - This is defined in .../config/all/configAll.h (NUM_FILES), + and not easily accessible at compile or run time - however, + in VxWorks 5.1 it is stored in the (undocumented?) maxFiles variable; + probably shouldn't depend on it, but we try to pick it up... */ +static int max_files = 50; /* default configAll.h */ + +int erts_vxworks_max_files; + +/* + * used by the break handler (set by signal handler on ctl-c) + */ +volatile int erts_break_requested; + +/********************* General functions ****************************/ + +Uint +erts_sys_misc_mem_sz(void) +{ + Uint res = erts_check_io_size(); + /* res += FIXME */ + return res; +} + +/* + * XXX This declaration should not be here. + */ +void erl_sys_schedule_loop(void); + +#ifdef SOFTDEBUG +static void do_trace(int line, char *file, char *format, ...) +{ + va_list va; + int tid = taskIdSelf(); + char buff[512]; + + va_start(va, format); + sprintf(buff,"Trace: Task: 0x%08x, %s:%d - ", + tid, file, line); + vsprintf(buff + strlen(buff), format, va); + va_end(va); + strcat(buff,"\r\n"); + write(2,buff,strlen(buff)); +} + +#define TRACE() do_trace(__LINE__, __FILE__,"") +#define TRACEF(Args...) do_trace(__LINE__,__FILE__, ## Args) +#endif + +void +erts_sys_pre_init(void) +{ + if (erlang_id != 0) { + /* NOTE: This particular case must *not* call erl_exit() */ + erts_fprintf(stderr, "Sorry, erlang is already running (as task %d)\n", + erlang_id); + exit(1); + } + + /* This must be done as early as possible... */ + if(!reclaim_init()) + fprintf(stderr, "Warning : reclaim facility should be initiated before " + "erlang is started!\n"); + erts_vxworks_max_files = max_files = reclaim_max_files(); + + /* Floating point exceptions */ +#if (CPU == SPARC) + sys_sigset(SIGFPE, fpe_sig_handler); +#elif (CPU == PPC603) + fix_registers(); +#endif + + /* register the private delete hook in reclaim */ + save_delete_hook((FUNCPTR)delete_hook, (caddr_t)0); + erlang_id = taskIdSelf(); +#ifdef DEBUG + printf("emulator task id = 0x%x\n", erlang_id); +#endif +} + +void erts_sys_alloc_init(void) +{ + initialize_allocation(); +} + +void +erl_sys_init(void) +{ + setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); + /* XXX Bug in VxWorks stdio loses fputch()'ed output after the + setvbuf() but before a *printf(), and possibly worse (malloc + errors, crash?) - so let's give it a *printf().... */ + fprintf(stdout, "%s",""); +} + +void +erl_sys_args(int* argc, char** argv) +{ + erts_init_check_io(); + max_files = erts_check_io_max_files(); + ASSERT(max_files <= erts_vxworks_max_files); +} + +/* + * 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) +{ + erts_check_io_interrupt(0); + erts_check_io(!runnable); +} + +void erts_do_break_handling(void) +{ + SET_BLOCKING(0); + /* call the break handling function, reset the flag */ + do_break(); + erts_break_requested = 0; + SET_NONBLOCKING(0); +} + +/* signal handling */ +RETSIGTYPE (*sys_sigset(sig, func))() + int sig; + RETSIGTYPE (*func)(); +{ + struct sigaction act, oact; + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = func; + sigaction(sig, &act, &oact); + return(oact.sa_handler); +} + +void sys_sigblock(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL); +} + +void sys_sigrelease(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); +} + +void +erts_sys_prepare_crash_dump(void) +{ + +} + +/* register signal handlers XXX - they don't work, need to find out why... */ +/* set up signal handlers for break and quit */ +static void request_break(void) +{ + /* just set a flag - checked for and handled + * in main thread (not signal handler). + * see check_io() + */ +#ifdef DEBUG + fprintf(stderr,"break!\n"); +#endif + erts_break_requested = 1; + erts_check_io_interrupt(1); /* Make sure we don't sleep in erts_poll_wait */ +} + +static void do_quit(void) +{ + halt_0(0); +} + +void erts_set_ignore_break(void) { +} + +void init_break_handler(void) +{ + sys_sigset(SIGINT, request_break); + sys_sigset(SIGQUIT, do_quit); +} + +void erts_replace_intr(void) { +} + +int sys_max_files(void) +{ + return(max_files); +} + +/******************* Routines for time measurement *********************/ + +int sys_init_time(void) +{ + erts_clock_rate = sysClkRateGet(); + /* + ** One could imagine that it would be better returning + ** a resolution more near the clock rate, like in: + ** return 1000 / erts_clock_rate; + ** but tests show that such isn't the case (rounding errors?) + ** Well, we go for the Unix variant of returning 1 + ** as a constant virtual clock rate. + */ + return SYS_CLOCK_RESOLUTION; +} + +int erts_clock_rate; +static volatile int ticks_inuse; +static volatile unsigned long ticks_collected; /* will wrap */ +static WDOG_ID watchdog_id; +static ULONG user_time; +static int this_task_id, sys_itime; +static SysHrTime hrtime_wrap; +static unsigned long last_tick_count; + +static void tolerant_time_clockint(int count) +{ + if (watchdog_id != NULL) { + if (taskIsReady(this_task_id)) + user_time += 1; + ++count; + if (!ticks_inuse) { + ticks_collected += count; + count = 0; + } + wdStart(watchdog_id, 1, (FUNCPTR)tolerant_time_clockint, count); + } +} + +int sys_init_hrtime(void) +{ + this_task_id = taskIdSelf(); /* OK, this only works for one single task + in the system... */ + user_time = 0; + + ticks_inuse = 0; + ticks_collected = 0; + hrtime_wrap = 0; + last_tick_count = 0; + + sys_itime = 1000 / erts_clock_rate; + watchdog_id = wdCreate(); + wdStart(watchdog_id, 1, (FUNCPTR) tolerant_time_clockint, 0); + return 0; +} + +SysHrTime sys_gethrtime(void) +{ + SysHrTime ticks; + + ++ticks_inuse; + ticks = (SysHrTime) (ticks_collected & 0x7FFFFFFF); + ticks_inuse = 0; + if (ticks < (SysHrTime) last_tick_count) { + hrtime_wrap += 1UL << 31; + } + last_tick_count = ticks; + return (ticks + hrtime_wrap) * ((SysHrTime) (1000000000UL / + erts_clock_rate)); +} + +void sys_gettimeofday(SysTimeval *tvp) +{ + struct timespec now; + + clock_gettime(CLOCK_REALTIME, &now); + tvp->tv_sec = now.tv_sec; + tvp->tv_usec = now.tv_nsec / 1000; +} + +clock_t sys_times(SysTimes *t) +{ + t->tms_stime = t->tms_cutime = t->tms_cstime = 0; + ++ticks_inuse; + t->tms_utime = user_time; + ticks_inuse = 0; + return tickGet(); /* The best we can do... */ +} + +/* This is called when *this task* is deleted */ +static void delete_hook(void) +{ + if (watchdog_id != NULL) { + wdDelete(watchdog_id); + watchdog_id = NULL; + } + erlang_id = 0; + this_task_id = 0; +} + +/************************** OS info *******************************/ + +/* Used by erlang:info/1. */ +/* (This code was formerly in drv.XXX/XXX_os_drv.c) */ + +#define MAX_VER_STR 9 /* Number of characters to + consider in version string */ + +static FUNCTION(int, get_number, (char** str_ptr)); + +char os_type[] = "vxworks"; + +static int +get_number(char **str_ptr) +{ + char* s = *str_ptr; /* Pointer to beginning of string. */ + char* dot; /* Pointer to dot in string or NULL. */ + + if (!isdigit(*s)) + return 0; + if ((dot = strchr(s, '.')) == NULL) { + *str_ptr = s+strlen(s); + return atoi(s); + } else { + *dot = '\0'; + *str_ptr = dot+1; + return atoi(s); + } +} + +/* namebuf; Where to return the name. */ +/* size; Size of name buffer. */ +void +os_flavor(char *namebuf, unsigned size) +{ + strcpy(namebuf, "-"); +} + +/* int* pMajor; Pointer to major version. */ +/* int* pMinor; Pointer to minor version. */ +/* int* pBuild; Pointer to build number. */ +void +os_version(int *pMajor, int *pMinor, int *pBuild) +{ + char os_ver[MAX_VER_STR+2]; + char* release; /* Pointer to the release string: + * X.Y or X.Y.Z. + */ + strncpy(os_ver, vxWorksVersion, MAX_VER_STR); + release = os_ver; + *pMajor = get_number(&release); + *pMinor = get_number(&release); + *pBuild = get_number(&release); +} + +void init_getenv_state(GETENV_STATE *state) +{ + *state = NULL; +} + +char *getenv_string(GETENV_STATE *state0) +{ + return NULL; +} + +void fini_getenv_state(GETENV_STATE *state) +{ + *state = NULL; +} + +/************************** Port I/O *******************************/ + + +/* I. Common stuff */ + +#define TMP_BUF_MAX (tmp_buf_size - 1024) +static byte *tmp_buf; +static Uint tmp_buf_size; + +/* II. The spawn/fd/vanilla drivers */ + +/* This data is shared by these drivers - initialized by spawn_init() */ +static struct driver_data { + int port_num, ofd, packet_bytes, report_exit; + int exitcode, exit_reported; /* For returning of exit codes. */ +} *driver_data; /* indexed by fd */ + +/* + * Locking only for exitcodes and exit_reported, one global sem for all + * spawn ports as this is rare. + */ +static SEM_ID driver_data_sem = NULL; +/* + * Also locking when looking up entries in the load table + */ +static SEM_ID entry_data_sem = NULL; + +/* We maintain a linked fifo queue of these structs in order */ +/* to manage unfinnished reads/and writes on differenet fd's */ + +typedef struct pend { + char *cpos; + int fd; + int remain; + struct pend *next; + char buf[1]; /* this is a trick to be able to malloc one chunk */ +} Pend; + +static struct fd_data { + int inport, outport; + char *buf, *cpos; + int sz, remain; /* for input on fd */ + Pend* pending; /* pending outputs */ + +} *fd_data; /* indexed by fd */ + + +/* Driver interfaces */ +static ErlDrvData spawn_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); +static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); +static ErlDrvData vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts); +static int spawn_init(void); +static void fd_stop(ErlDrvData); +static void stop(ErlDrvData); +static void ready_input(ErlDrvData fd, ErlDrvEvent ready_fd); +static void ready_output(ErlDrvData fd, ErlDrvEvent ready_fd); +static void output(ErlDrvData fd, char *buf, int len); +static void stop_select(ErlDrvEvent, void*); + +struct erl_drv_entry spawn_driver_entry = { + spawn_init, + spawn_start, + stop, + output, + ready_input, + ready_output, + "spawn", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, /* handle2 */ + NULL, /* process_exit */ + stop_select + +}; +struct erl_drv_entry fd_driver_entry = { + NULL, + fd_start, + fd_stop, + output, + ready_input, + ready_output, + "fd", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, /* handle2 */ + NULL, /* process_exit */ + stop_select +}; +struct erl_drv_entry vanilla_driver_entry = { + NULL, + vanilla_start, + stop, + output, + ready_input, + ready_output, + "vanilla", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, /* flush */ + NULL, /* call */ + NULL, /* event */ + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, /* ERL_DRV_FLAGs */ + NULL, /* handle2 */ + NULL, /* process_exit */ + stop_select +}; + +/* +** Set up enough of the driver_data structure to be able to report exit status. +** Some things may be initiated again, but that is no real problem. +*/ +static int pre_set_driver_data(int ifd, int ofd, + int read_write, int report_exit) { + if (read_write & DO_READ) { + driver_data[ifd].report_exit = report_exit; + driver_data[ifd].exitcode = 0; + driver_data[ifd].exit_reported = 0; + if (read_write & DO_WRITE) { + driver_data[ifd].ofd = ofd; + if (ifd != ofd) { + driver_data[ofd] = driver_data[ifd]; + driver_data[ofd].report_exit = 0; + } + } else { /* DO_READ only */ + driver_data[ifd].ofd = -1; + } + return(ifd); + } else { /* DO_WRITE only */ + driver_data[ofd].report_exit = 0; + driver_data[ofd].exitcode = 0; + driver_data[ofd].exit_reported = 0; + driver_data[ofd].ofd = ofd; + return(ofd); + } +} + +/* +** Set up the driver_data structure, it may have been initiated +** partly by the function above, but we dont care. +*/ +static int set_driver_data(int port_num, int ifd, int ofd, + int packet_bytes, int read_write, + int report_exit) +{ + 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; + if (read_write & DO_WRITE) { + driver_data[ifd].ofd = ofd; + if (ifd != ofd) { + driver_data[ofd] = driver_data[ifd]; + driver_data[ofd].report_exit = 0; + } + } else { /* DO_READ only */ + driver_data[ifd].ofd = -1; + } + (void) driver_select(port_num, ifd, 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 = 0; + driver_data[ofd].ofd = ofd; + return(ofd); + } +} + +static int need_new_sems = 1; + +static int spawn_init(void) +{ + char *stackenv; + int size; + driver_data = (struct driver_data *) + erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data)); + if (need_new_sems) { + driver_data_sem = semMCreate + (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); + entry_data_sem = semMCreate + (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); + } + if (driver_data_sem == NULL || entry_data_sem == NULL) { + erl_exit(1,"Could not allocate driver locking semaphore."); + } + need_new_sems = 0; + + (void)uxPipeDrv(); /* Install pipe driver */ + + if ((stackenv = getenv("ERLPORTSTACKSIZE")) != NULL && + (size = atoi(stackenv)) > 0) + port_stack_size = size; + else + port_stack_size = DEFAULT_PORT_STACK_SIZE; + return 0; +} + +/* Argv has to be built vith the save_xxx routines, not with whathever + sys_xxx2 has in mind... */ +#define argv_alloc save_malloc +#define argv_realloc save_realloc +#define argv_free save_free +/* Build argv, return argc or -1 on failure */ +static int build_argv(char *name, char ***argvp) +{ + int argvsize = 10, argc = 0; + char *args, *arglast = NULL, *argp; + char **argv; + +#ifdef DEBUG + fdprintf(2, "Building argv, %s =>\n", name); +#endif + if ((argv = (char **)argv_alloc(argvsize * sizeof(char *))) == NULL) + return(-1); + if ((args = argv_alloc(strlen(name) + 1)) == NULL) + return(-1); + strcpy(args, name); + argp = strtok_r(args, " \t", &arglast); + while (argp != NULL) { + if (argc + 1 >= argvsize) { + argvsize += 10; + argv = (char **)argv_realloc((char *)argv, argvsize*sizeof(char *)); + if (argv == NULL) { + argv_free(args); + return(-1); + } + } +#ifdef DEBUG + fdprintf(2, "%s\n", argp); +#endif + argv[argc++] = argp; + argp = strtok_r((char *)NULL, " \t", &arglast); + } + argv[argc] = NULL; + *argvp = argv; + return(argc); +} +#undef argv_alloc +#undef argv_realloc +#undef argv_free + + +/* Lookup and return global text symbol or NULL on failure + Symbol name is null-terminated and without the leading '_' */ +static FUNCPTR +lookup(char *sym) +{ + char buf[256]; + char *symname = buf; + int len; + FUNCPTR entry; + SYM_TYPE type; + + len = strlen(sym); + if (len > 254 && (symname = malloc(len+2)) == NULL) + return(NULL); +#if defined _ARCH_PPC || defined SIMSPARCSOLARIS + /* GCC for PPC and SIMSPARC doesn't add a leading _ to symbols */ + strcpy(symname, sym); +#else + sprintf(symname, "_%s", sym); +#endif + if (symFindByNameAndType(sysSymTbl, symname, (char **)&entry, + &type, N_EXT | N_TEXT, N_EXT | N_TEXT) != OK) + entry = NULL; + if (symname != buf) + free(symname); + return(entry); +} + +/* This function is spawned to build argc, argv, lookup the symbol to call, + connect and set up file descriptors, and make the actual call. + N.B. 'name' was allocated by the Erlang task (through plain_malloc) and + is freed by this port program task. + Note: 'name' may be a path containing '/'. */ + +static void call_proc(char *name, int ifd, int ofd, int read_write, + int redir_stderr, int driver_index, + int p6, int p7, int p8, int p9) +{ + int argc; + char **argv, *bname; + FUNCPTR entry; + int ret = -1; + + /* Must consume 'name' */ + argc = build_argv(name, &argv); + plain_free(name); + /* Find basename of path */ + if ((bname = strrchr(argv[0], '/')) != NULL) { + bname++; + } else { + bname = argv[0]; + } +#ifdef DEBUG + fdprintf(2, "Port program name: %s\n", bname); +#endif + semTake(entry_data_sem, WAIT_FOREVER); + + if (argc > 0) { + if ((entry = lookup(bname)) == NULL) { + int fd; + char *fn; + /* NOTE: We don't check the return value of loadModule, + since that was incompatibly changed from 5.0.2b to 5.1, + but rather do a repeated lookup(). */ + if ((fd = open(argv[0], O_RDONLY)) > 0) { + (void) loadModule(fd, GLOBAL_SYMBOLS); + close(fd); + entry = lookup(bname); + } + if (entry == NULL) { + /* filename == func failed, try func.o */ + if ((fn = malloc(strlen(argv[0]) + 3)) != NULL) { /* ".o\0" */ + strcpy(fn, argv[0]); + strcat(fn, ".o"); + if ((fd = open(fn, O_RDONLY)) > 0) { + (void) loadModule(fd, GLOBAL_SYMBOLS); + close(fd); + entry = lookup(bname); + } + free(fn); + } + } + } + } else { + entry = NULL; + } + semGive(entry_data_sem); + + if (read_write & DO_READ) { /* emulator read */ + save_fd(ofd); + ioTaskStdSet(0, 1, ofd); /* stdout for process */ + if(redir_stderr) + ioTaskStdSet(0, 2, ofd);/* stderr for process */ + } + if (read_write & DO_WRITE) { /* emulator write */ + save_fd(ifd); + ioTaskStdSet(0, 0, ifd); /* stdin for process */ + } + if (entry != NULL) { + ret = (*entry)(argc, argv, (char **)NULL); /* NULL for envp */ + } else { + fdprintf(2, "Could not exec \"%s\"\n", argv[0]); + ret = -1; + } + if (driver_data[driver_index].report_exit) { + semTake(driver_data_sem, WAIT_FOREVER); + driver_data[driver_index].exitcode = ret; + driver_data[driver_index].exit_reported = 1; + semGive(driver_data_sem); + } + /* We *don't* want to close the pipes here, but let the delete + hook take care of it - it might want to flush stdout and there'd + better be an open descriptor to flush to... */ + exit(ret); +} + +static void close_pipes(int ifd[2], int ofd[2], int read_write) +{ + if (read_write & DO_READ) { + (void) close(ifd[0]); + (void) close(ifd[1]); + } + if (read_write & DO_WRITE) { + (void) close(ofd[0]); + (void) close(ofd[1]); + } +} + +static void init_fd_data(int fd, int port_unused_argument) +{ + SET_NONBLOCKING(fd); + fd_data[fd].pending = NULL; + fd_data[fd].buf = fd_data[fd].cpos = NULL; + fd_data[fd].remain = fd_data[fd].sz = 0; +} + +static ErlDrvData spawn_start(ErlDrvPort port_num, char *name,SysDriverOpts* opts) +{ + int ifd[2], ofd[2], len, nl, id; + char taskname[11], *progname, *bname; + char *space_in_command; + int packet_bytes = opts->packet_bytes; + int read_write = opts->read_write; + int use_stdio = opts->use_stdio; + int redir_stderr = opts->redir_stderr; + int driver_index; + + if (!use_stdio){ + return (ErlDrvData) -3; + } + + /* Create pipes and set the Erlang task as owner of its + * read and write ends (through save_fd()). + */ + switch (read_write) { + case DO_READ: + if (pipe(ifd) < 0){ + return (ErlDrvData) -2; + } + if (ifd[0] >= max_files) { + close_pipes(ifd, ofd, read_write); + errno = ENFILE; + return (ErlDrvData) -2; + } + save_fd(ifd[0]); + break; + case DO_WRITE: + if (pipe(ofd) < 0) { + return (ErlDrvData) -2; + } + if (ofd[1] >= max_files) { + close_pipes(ifd, ofd, read_write); + errno = ENFILE; + return (ErlDrvData) -2; + } + save_fd(ofd[1]); + break; + case DO_READ|DO_WRITE: + if (pipe(ifd) < 0){ + return (ErlDrvData) -2; + } + if (ifd[0] >= max_files || pipe(ofd) < 0) { + close_pipes(ifd, ofd, DO_READ); + errno = ENFILE; + return (ErlDrvData) -2; + } + if (ofd[1] >= max_files) { + close_pipes(ifd, ofd, read_write); + errno = ENFILE; + return (ErlDrvData) -2; + } + save_fd(ifd[0]); + save_fd(ofd[1]); + break; + default: + return (ErlDrvData) -1; + } + + /* Allocate space for program name to be freed by the + * spawned task. We use plain_malloc so that the allocated + * space is not owned by the Erlang task. + */ + + if ((progname = plain_malloc(strlen(name) + 1)) == NULL) { + close_pipes(ifd, ofd, read_write); + errno = ENOMEM; + return (ErlDrvData) -2; + } + strcpy(progname, name); + + /* Check if name contains a space + * (e.g "port_test -o/home/gandalf/tornado/wind/target/erlang") + */ + if ((space_in_command = strrchr(progname, ' ')) != NULL) { + *space_in_command = '\0'; + } + + /* resulting in "port_test" */ + if ((bname = strrchr(progname, '/')) != NULL) + bname++; + else + bname = progname; + + /* resulting in "port_test" */ + len = strlen(bname); + nl = len > 10 ? 10 : len; + strncpy(taskname, bname, nl); + taskname[nl] = '\0'; + if (space_in_command != NULL) + *space_in_command = ' '; + driver_index = pre_set_driver_data(ifd[0], ofd[1], + read_write, opts->exit_status); + + /* resetting to "port_test -o/home/gandalf/tornado/wind/target/erlang" */ + if ((id = taskSpawn(taskname, spTaskPriority, spTaskOptions, + port_stack_size, (FUNCPTR)call_proc, (int)progname, + ofd[0], ifd[1], read_write, redir_stderr, driver_index, + 0,0,0,0)) + == ERROR) { + close_pipes(ifd, ofd, read_write); + plain_free(progname); /* only when spawn fails */ + errno = ENOMEM; + return (ErlDrvData) -2; + } +#ifdef DEBUG + fdprintf(2, "Spawned %s as %s[0x%x]\n", name, taskname, id); +#endif + if (read_write & DO_READ) + init_fd_data(ifd[0], port_num); + if (read_write & DO_WRITE) + init_fd_data(ofd[1], port_num); + return (ErlDrvData) (set_driver_data(port_num, ifd[0], ofd[1], + packet_bytes,read_write, + opts->exit_status)); +} + +static ErlDrvData fd_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts) +{ + if (((opts->read_write & DO_READ) && opts->ifd >= max_files) || + ((opts->read_write & DO_WRITE) && opts->ofd >= max_files)) { + return (ErlDrvData) -1; + } + + 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); + return (ErlDrvData) (set_driver_data(port_num, opts->ifd, opts->ofd, + opts->packet_bytes, opts->read_write, 0)); +} + +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); + fd_data[fd].buf = NULL; + fd_data[fd].sz = 0; + fd_data[fd].remain = 0; + fd_data[fd].cpos = NULL; +} + +static void nbio_stop_fd(int port_num, int fd) +{ + Pend *p, *p1; + + driver_select(port_num, fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); + clear_fd_data(fd); + p = fd_data[fd].pending; + SET_BLOCKING(fd); + while (p) { + p1 = p->next; + free(p); + p = p1; + } + fd_data[fd].pending = NULL; +} + +static void fd_stop(ErlDrvData drv_data) +{ + int ofd; + int fd = (int) drv_data; + + nbio_stop_fd(driver_data[fd].port_num, (int)fd); + ofd = driver_data[fd].ofd; + if (ofd != fd && ofd != -1) + nbio_stop_fd(driver_data[fd].port_num, (int)ofd); /* XXX fd = ofd? */ +} + +static ErlDrvData +vanilla_start(ErlDrvPort port_num, char *name, SysDriverOpts* opts) +{ + int flags, fd; + struct stat statbuf; + + DEBUGF(("vanilla_start, name: %s [r=%1i w=%1i]\n", name, + opts->read_write & DO_READ, + opts->read_write & DO_WRITE)); + + flags = (opts->read_write == DO_READ ? O_RDONLY : + opts->read_write == DO_WRITE ? O_WRONLY|O_CREAT|O_TRUNC : + O_RDWR|O_CREAT); + if ((fd = open(name, flags, 0666)) < 0){ + errno = ENFILE; + return (ErlDrvData) -2; + } + if (fd >= max_files) { + close(fd); + errno = ENFILE; + return (ErlDrvData) -2; + } + if (fstat(fd, &statbuf) < 0) { + close(fd); + errno = ENFILE; + return (ErlDrvData) -2; + } + + /* Return error for reading regular files (doesn't work) */ + if (ISREG(statbuf) && ((opts->read_write) & DO_READ)) { + close(fd); + return (ErlDrvData) -3; + } + init_fd_data(fd, port_num); + return (ErlDrvData) (set_driver_data(port_num, fd, fd, + opts->packet_bytes, opts->read_write, 0)); +} + +/* 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 stop(ErlDrvData drv_data) +{ + int port_num, ofd; + int fd = (int) drv_data; + + port_num = driver_data[fd].port_num; + nbio_stop_fd(port_num, fd); + driver_select(port_num, fd, ERL_DRV_USE, 0); /* close(fd) */ + + ofd = driver_data[fd].ofd; + if (ofd != fd && ofd != -1) { + nbio_stop_fd(port_num, ofd); + driver_select(port_num, ofd, ERL_DRV_USE, 0); /* close(fd) */ + } +} + +static int sched_write(int port_num,int fd, char *buf, int len, int pb) +{ + Pend *p, *p2, *p3; + int p_bytes = len; + + p = (Pend*) erts_alloc_fnf(ERTS_ALC_T_PEND_DATA, pb + len + sizeof(Pend)); + if (!p) { + driver_failure(port_num, -1); + return(-1); + } + + switch(pb) { + case 4: put_int32(len, p->buf); break; + case 2: put_int16(len, p->buf); break; + case 1: put_int8(len, p->buf); break; + case 0: break; /* Handles this case too */ + } + sys_memcpy(p->buf + pb, buf, len); + driver_select(port_num, fd, ERL_DRV_WRITE|ERL_DRV_USE, 1); + p->cpos = p->buf; + p->fd = fd; + p->next = NULL; + p->remain = len + pb; + p2 = fd_data[fd].pending; + if (p2 == NULL) + fd_data[fd].pending = p; + else { + p3 = p2->next; + while(p3) { + p_bytes += p2->remain; + p2 = p2->next; + p3 = p3->next; + } + p2->next = p; + } + if (p_bytes > (1 << 13)) /* More than 8 k pending */ + set_busy_port(port_num, 1); + return(0); +} + +/* Fd is the value returned as drv_data by the start func */ +static void output(ErlDrvData drv_data, char *buf, int len) +{ + int buf_done, port_num, wval, pb, ofd; + byte lb[4]; + struct iovec iv[2]; + int fd = (int) drv_data; + + pb = driver_data[fd].packet_bytes; + port_num = driver_data[fd].port_num; + + if ((ofd = driver_data[fd].ofd) == -1) { + return; + } + + if (fd_data[ofd].pending) { + sched_write(port_num, ofd, buf, len, pb); + return; + } + + if ((pb == 2 && len > 65535) || (pb == 1 && len > 255)) { + driver_failure_posix(port_num, EINVAL); + return; + } + if (pb == 0) { + wval = write(ofd, buf, len); + } else { + lb[0] = (len >> 24) & 255; /* MSB */ + lb[1] = (len >> 16) & 255; + lb[2] = (len >> 8) & 255; + lb[3] = len & 255; /* LSB */ + iv[0].iov_base = (char*) lb + (4 - pb); + iv[0].iov_len = pb; + iv[1].iov_base = buf; + iv[1].iov_len = len; + wval = writev(ofd, iv, 2); + } + if (wval == pb + len ) { + return; + } + if (wval < 0) { + if ((errno == EINTR) || (errno == ERRNO_BLOCK)) { + if (pb) { + sched_write(port_num, ofd, buf ,len, pb); + } else if (pb == 0) { + sched_write(port_num, ofd, buf ,len, 0); + } + return; + } + driver_failure_posix(driver_data[fd].port_num, EINVAL); + return; + } + if (wval < pb) { + sched_write(port_num, ofd, (lb +4 -pb) + wval, pb-wval, 0); + sched_write(port_num, ofd, buf ,len, 0); + return; + } + + /* we now know that wval < (pb + len) */ + buf_done = wval - pb; + sched_write(port_num, ofd, buf + buf_done, len - buf_done,0); +} + +static void stop_select(ErlDrvEvent fd, void* _) +{ + close((int)fd); +} + +static int ensure_header(int fd,char *buf,int packet_size, int sofar) +{ + int res = 0; + int remaining = packet_size - sofar; + + SET_BLOCKING(fd); + if (read_fill(fd, buf+sofar, remaining) != remaining) + return -1; + switch (packet_size) { + case 1: res = get_int8(buf); break; + case 2: res = get_int16(buf); break; + case 4: res = get_int32(buf); break; + } + SET_NONBLOCKING(fd); + return(res); +} + +static int port_inp_failure(int port_num, int ready_fd, int res) +{ + (void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0); + clear_fd_data(ready_fd); + if (res == 0) { + if (driver_data[ready_fd].report_exit) { + int tmpexit = 0; + int reported; + /* Lock the driver_data structure */ + semTake(driver_data_sem, WAIT_FOREVER); + if ((reported = driver_data[ready_fd].exit_reported)) + tmpexit = driver_data[ready_fd].exitcode; + semGive(driver_data_sem); + if (reported) { + erts_fprintf(stderr,"Exitcode %d reported\r\n", tmpexit); + driver_report_exit(port_num, tmpexit); + } + } + driver_failure_eof(port_num); + } else { + driver_failure(port_num, res); + } + return 0; +} + +/* 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 drv_data, ErlDrvEvent drv_event) +{ + int port_num, packet_bytes, res; + Uint h = 0; + char *buf; + int fd = (int) drv_data; + int ready_fd = (int) drv_event; + + port_num = driver_data[fd].port_num; + packet_bytes = driver_data[fd].packet_bytes; + + if (packet_bytes == 0) { + if ((res = read(ready_fd, tmp_buf, tmp_buf_size)) > 0) { + driver_output(port_num, (char*)tmp_buf, res); + return; + } + port_inp_failure(port_num, ready_fd, res); + return; + } + + if (fd_data[ready_fd].remain > 0) { /* We try to read the remainder */ + /* space is allocated in buf */ + res = read(ready_fd, fd_data[ready_fd].cpos, + fd_data[ready_fd].remain); + if (res < 0) { + if ((errno == EINTR) || (errno == ERRNO_BLOCK)) { + ; + } else { + 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[ready_fd].remain) { /* we're done */ + driver_output(port_num, fd_data[ready_fd].buf, + fd_data[ready_fd].sz); + clear_fd_data(ready_fd); + } else { /* if (res < fd_data[ready_fd].remain) */ + fd_data[ready_fd].cpos += res; + fd_data[ready_fd].remain -= res; + } + return; + } + + + if (fd_data[ready_fd].remain == 0) { /* clean fd */ + /* We make one read attempt and see what happens */ + res = read(ready_fd, tmp_buf, tmp_buf_size); + if (res < 0) { + if ((errno == EINTR) || (errno == ERRNO_BLOCK)) + return; + port_inp_failure(port_num, ready_fd, res); + return; + } + else if (res == 0) { /* eof */ + port_inp_failure(port_num, ready_fd, res); + return; + } + else if (res < packet_bytes) { /* Ugly case... get at least */ + if ((h = ensure_header(ready_fd, tmp_buf, packet_bytes, res))==-1) { + port_inp_failure(port_num, ready_fd, -1); + return; + } + buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) { + port_inp_failure(port_num, ready_fd, -1); + return; + } + fd_data[ready_fd].buf = buf; + fd_data[ready_fd].sz = h; + fd_data[ready_fd].remain = h; + fd_data[ready_fd].cpos = buf; + return; + } + else { /* if (res >= packet_bytes) */ + unsigned char* cpos = tmp_buf; + int bytes_left = res; + while (1) { /* driver_output as many as possible */ + if (bytes_left == 0) { + clear_fd_data(ready_fd); + return; + } + if (bytes_left < packet_bytes) { /* Yet an ugly case */ + if((h=ensure_header(ready_fd, cpos, + packet_bytes, bytes_left))==-1) { + port_inp_failure(port_num, ready_fd, -1); + return; + } + buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) + port_inp_failure(port_num, ready_fd, -1); + fd_data[ready_fd].buf = buf; + fd_data[ready_fd].sz = h; + fd_data[ready_fd].remain = h; + fd_data[ready_fd].cpos = buf; + return; + } + switch (packet_bytes) { + case 1: h = get_int8(cpos); cpos += 1; break; + case 2: h = get_int16(cpos); cpos += 2; break; + case 4: h = get_int32(cpos); cpos += 4; break; + } + bytes_left -= packet_bytes; + /* we've got the header, now check if we've got the data */ + 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 */ + buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h); + if (!buf) { + port_inp_failure(port_num, ready_fd, -1); + } + sys_memcpy(buf, cpos, bytes_left); + fd_data[ready_fd].buf = buf; + fd_data[ready_fd].sz = h; + fd_data[ready_fd].remain = h - bytes_left; + fd_data[ready_fd].cpos = buf + bytes_left; + return; + } + } + return; + } + } + fprintf(stderr, "remain %d \n", fd_data[ready_fd].remain); + port_inp_failure(port_num, ready_fd, -1); +} + + +/* 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 drv_data, ErlDrvEvent drv_event) +{ + Pend *p; + int wval; + + int fd = (int) drv_data; + int ready_fd = (int) drv_event; + + while(1) { + if ((p = fd_data[ready_fd].pending) == NULL) { + driver_select(driver_data[fd].port_num, ready_fd, + ERL_DRV_WRITE, 0); + return; + } + wval = write(p->fd, p->cpos, p->remain); + if (wval == p->remain) { + fd_data[ready_fd].pending = p->next; + erts_free(ERTS_ALC_T_PEND_DATA, p); + if (fd_data[ready_fd].pending == NULL) { + driver_select(driver_data[fd].port_num, ready_fd, + ERL_DRV_WRITE, 0); + set_busy_port(driver_data[fd].port_num, 0); + return; + } + else + continue; + } + else if (wval < 0) { + if (errno == ERRNO_BLOCK || errno == EINTR) + return; + else { + driver_select(driver_data[fd].port_num, ready_fd, + ERL_DRV_WRITE, 0); + driver_failure(driver_data[fd].port_num, -1); + return; + } + } + else if (wval < p->remain) { + p->cpos += wval; + p->remain -= wval; + return; + } + } +} + +/* 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){ + int p = taskIdSelf(); /* Hmm, may be negative??? requires some GB of + memory to make the TCB address convert to a + negative value. */ + sprintf(buffer,"%d", p); +} + +int +erts_sys_putenv(char *buffer, int sep_ix) +{ + return putenv(buffer); +} + +int +erts_sys_getenv(char *key, char *value, size_t *size) +{ + char *orig_value; + int res; + 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; +} + +void +sys_init_io(void) +{ + tmp_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_TMP_BUF, SYS_TMP_BUF_SIZE); + tmp_buf_size = SYS_TMP_BUF_SIZE; + fd_data = (struct fd_data *) + erts_alloc(ERTS_ALC_T_FD_TAB, max_files * sizeof(struct fd_data)); +} + + +/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */ + +static int read_fill(int fd, char *buf, int len) +{ + int i, got = 0; + do { + if ((i = read(fd, buf+got, len-got)) <= 0) { + return i; + } + got += i; + } while (got < len); + return (len); +} + + +/************************** Misc... *******************************/ + +extern const char pre_loaded_code[]; +extern char* const pre_loaded[]; + + +/* Float conversion */ + +int sys_chars_to_double(char *buf, double *fp) +{ + char *s = buf; + + /* The following check is incorporated from the Vee machine */ + +#define ISDIGIT(d) ((d) >= '0' && (d) <= '9') + + /* Robert says that something like this is what he really wanted: + * + * 7 == sscanf(Tbuf, "%[+-]%[0-9].%[0-9]%[eE]%[+-]%[0-9]%s", ....); + * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7) + * break; + */ + + /* Scan string to check syntax. */ + if (*s == '+' || *s == '-') + s++; + + if (!ISDIGIT(*s)) /* Leading digits. */ + return -1; + while (ISDIGIT(*s)) s++; + if (*s++ != '.') /* Decimal part. */ + return -1; + if (!ISDIGIT(*s)) + return -1; + while (ISDIGIT(*s)) s++; + if (*s == 'e' || *s == 'E') { + /* There is an exponent. */ + s++; + if (*s == '+' || *s == '-') + s++; + if (!ISDIGIT(*s)) + return -1; + while (ISDIGIT(*s)) s++; + } + if (*s) /* That should be it */ + return -1; + + if (sscanf(buf, "%lf", fp) != 1) + return -1; + return 0; +} + +/* + ** Convert a double to ascii format 0.dddde[+|-]ddd + ** return number of characters converted + */ + +int sys_double_to_chars(double fp, char *buf) +{ + (void) sprintf(buf, "%.20e", fp); + return strlen(buf); +} + + +/* Floating point exceptions */ + +#if (CPU == SPARC) +jmp_buf fpe_jmp; + +RETSIGTYPE fpe_sig_handler(int sig) +{ + longjmp(fpe_jmp, 1); +} + +#elif (CPU == PPC603) +static void fix_registers(void){ + FP_CONTEXT fpcontext; + fppSave(&fpcontext); + fpcontext.fpcsr &= ~(_PPC_FPSCR_INIT); + fppRestore(&fpcontext); +} +#endif + + +/* Return a pointer to a vector of names of preloaded modules */ + +Preload* sys_preloaded(void) +{ + return (Preload *) pre_loaded; +} + +/* Return a pointer to preloaded code for module "module" */ +unsigned char* sys_preload_begin(Preload *pp) +{ + return pp->code; +} + +/* Clean up if allocated */ +void sys_preload_end(Preload *pp) +{ + /* Nothing */ +} + +/* Read a key from console (?) */ + +int sys_get_key(int fd) +{ + int c; + unsigned char rbuf[64]; + + fflush(stdout); /* Flush query ??? */ + + if ((c = read(fd,rbuf,64)) <= 0) + return c; + return rbuf[0]; +} + + +/* A real printf that does the equivalent of fprintf(stdout, ...) */ + +/* ARGSUSED */ +static STATUS +stdio_write(char *buf, int nchars, int fp) +{ + if (fwrite(buf, sizeof(char), nchars, (FILE *)fp) == 0) + return(ERROR); + return(OK); +} + +int real_printf(const char *fmt, ...) +{ + va_list ap; + int err; + + va_start(ap, fmt); + err = fioFormatV(fmt, ap, stdio_write, (int)stdout); + va_end(ap); + return(err); +} + + +/* + * Little function to do argc, argv calls from (e.g.) VxWorks shell + * The arguments should be in the form of a single ""-enclosed string + * NOTE: This isn't really part of the emulator, just included here + * so we can use the handy functions and memory reclamation. + */ +void argcall(char *args) +{ + int argc; + char **argv; + FUNCPTR entry; + + if (args != NULL) { + if ((argc = build_argv(args, &argv)) > 0) { + if ((entry = lookup(argv[0])) != NULL) + (*entry)(argc, argv, (char **)NULL); /* NULL for envp */ + else + fprintf(stderr, "Couldn't find %s\n", argv[0]); + } else + fprintf(stderr, "Failed to build argv!\n"); + } else + fprintf(stderr, "No argument list!\n"); +} + + +/* That concludes the Erlang stuff - now we just need to implement an OS... + - Just kidding, but resource reclamation isn't the strength of VxWorks */ +#undef calloc +#undef free +#undef cfree +#undef malloc +#undef realloc +#undef open +#undef creat +#undef socket +#undef accept +#undef close +#undef fopen +#undef fdopen +#undef freopen +#undef fclose + +/********************* Using elib_malloc ****************************/ +/* This gives us yet another level of malloc wrappers. The purpouse */ +/* is to be able to select between different varieties of memory */ +/* allocation without recompiling. */ +/* Maybe the performance is somewhat degraded by this, but */ +/* on the other hand, performance may be much better if the most */ +/* suiting malloc is used (not to mention the much lower */ +/* fragmentation). */ +/* /Patrik N */ +/********************************************************************/ + +/* + * I don't want to include the whole elib header, especially + * as it uses char * for generic pointers. Let's fool ANSI C instead. + */ +extern void *elib_malloc(size_t); +extern void *elib_realloc(void *, size_t); +extern void elib_free(void *); +extern void elib_init(void *, int); +extern void elib_force_init(void *, int); +extern size_t elib_sizeof(void *); + +/* Flags */ +#define USING_ELIB_MALLOC 1 /* We are using the elib_malloc */ +#define WARN_MALLOC_MIX 2 /* Warn if plain malloc or save_malloc + is mixed with sys_free2 or + sys_realloc2 */ +#define REALLOC_MOVES 4 /* Always move on realloc + (less fragmentation) */ +#define USER_POOL 8 /* The user supplied the memory + pool, it was not save_alloced. */ +#define RECLAIM_USER_POOL 16 /* Use the reclaim mechanism in the + user pool. */ +#define NEW_USER_POOL 32 /* The user pool is newly suppllied, + any old pool should be discarded */ + + +#define ELIB_LOCK \ +if(alloc_flags & USING_ELIB_MALLOC) \ + semTake(elib_malloc_sem, WAIT_FOREVER) + +#define ELIB_UNLOCK \ +if(alloc_flags & USING_ELIB_MALLOC) \ + semGive(elib_malloc_sem) + +#define USER_RECLAIM() ((alloc_flags & USING_ELIB_MALLOC) && \ + (alloc_flags & USER_POOL) && \ + (alloc_flags & RECLAIM_USER_POOL)) + +/* + * Global state + * The use of function pointers for the malloc/realloc/free functions + * is actually only useful in the malloc case, we must know what kind of + * realloc/free we are going to use, so we could call elib_xxx directly. + * However, as the overhead is small and this construction makes it + * fairly easy to add another malloc algorithm, the function pointers + * are used in realloc/free to. + */ +static MallocFunction actual_alloc = &save_malloc; +static ReallocFunction actual_realloc = &save_realloc; +static FreeFunction actual_free = &save_free; +static int alloc_flags = 0; +static int alloc_pool_size = 0; +static void *alloc_pool_ptr = NULL; +static SEM_ID elib_malloc_sem = NULL; + +/* + * Descide if we should use the save_free instead of elib_free or, + * in the case of the free used in a delete hook, if we should + * use plain free instead of elib_free. + */ +static int use_save_free(void *ptr){ + register int diff = ((char *) ptr) - ((char *) alloc_pool_ptr); + /* + * Hmmm... should it be save_free even if diff is exactly 0? + * The answer is Yes if the whole area is save_alloced and No if not, + * so reclaim_free_hook is NOT run in the case of one save_alloced area. + */ + return (!(alloc_flags & USING_ELIB_MALLOC) || + (diff < 0 || diff >= alloc_pool_size)); +} + +/* + * A free function used by the task deletion hook for the save_xxx functions. + * Set with the set_reclaim_free_function function. + */ +static void reclaim_free_hook(void *ptr){ + if(use_save_free(ptr)){ + free(ptr); + } else { + ELIB_LOCK; + (*actual_free)(ptr); + ELIB_UNLOCK; + } +} + + +/* + * Initialize, sets the values of pointers based on + * either nothing (the default) or what's set previously by the + * erl_set_memory_block function. + */ +static void initialize_allocation(void){ + set_reclaim_free_function(NULL); + if(alloc_pool_size == 0){ + actual_alloc = (void *(*)(size_t))&save_malloc; + actual_realloc = (void *(*)(void *, size_t))&save_realloc; + actual_free = &save_free; + alloc_flags &= ~(USING_ELIB_MALLOC | USER_POOL | RECLAIM_USER_POOL); + } else { + if(elib_malloc_sem == NULL) + elib_malloc_sem = semMCreate + (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); + if(elib_malloc_sem == NULL) + erl_exit(1,"Could not create mutex semaphore for elib_malloc"); + if(!(alloc_flags & USER_POOL)){ + if((alloc_pool_ptr = save_malloc(alloc_pool_size)) == NULL) + erl_exit(1,"Erlang set to allocate a %d byte block initially;" + " not enough memory available.", alloc_pool_size); + elib_force_init(alloc_pool_ptr, alloc_pool_size); + } else if(alloc_flags & NEW_USER_POOL){ + elib_force_init(alloc_pool_ptr, alloc_pool_size); + } + actual_alloc=&elib_malloc; + actual_realloc=&elib_realloc; + actual_free=&elib_free; + alloc_flags |= USING_ELIB_MALLOC; + /* We MUST see to that the right free function is used + otherwise we'll get a very nasty crash! */ + if(USER_RECLAIM()) + set_reclaim_free_function(&reclaim_free_hook); + } + alloc_flags &= ~(NEW_USER_POOL); /* It's never new after initialization*/ +} + +/* This does not exist on other platforms, we just use it in sys.c + and the BSD resolver */ +void *sys_calloc2(Uint nelem, Uint elsize){ + void *ptr = erts_alloc_fnf(ERTS_ALC_T_UNDEF, nelem*elsize); + if(ptr != NULL) + memset(ptr,0,nelem*elsize); + return ptr; +} + +/* + * The malloc wrapper + */ +void * +erts_sys_alloc(ErtsAlcType_t type, void *extra, Uint size) +{ + register void *ret; + ELIB_LOCK; + if(USER_RECLAIM()) + ret = save_malloc2((size_t)size,actual_alloc); + else + ret = (*actual_alloc)((size_t)size); + ELIB_UNLOCK; + return ret; +} + +/* + * The realloc wrapper, may respond to the "realloc-always-moves" flag + * if the area is initially allocated with elib_malloc. + */ +void * +erts_sys_realloc(ErtsAlcType_t type, void *extra, void *ptr, Uint size) +{ + register void *ret; + if(use_save_free(ptr)){ + if((alloc_flags & WARN_MALLOC_MIX) && + (alloc_flags & USING_ELIB_MALLOC)) + erts_fprintf(stderr,"Warning, save_malloced data realloced " + "by sys_realloc2\n"); + return save_realloc(ptr, (size_t) size); + } else { + ELIB_LOCK; + if((alloc_flags & REALLOC_MOVES) && + (alloc_flags & USING_ELIB_MALLOC)){ + size_t osz = elib_sizeof(ptr); + if(USER_RECLAIM()) + ret = save_malloc2((size_t) size, actual_alloc); + else + ret = (*actual_alloc)((size_t) size); + if(ret != NULL){ + memcpy(ret,ptr,(((size_t)size) < osz) ? ((size_t)size) : osz); + if(USER_RECLAIM()) + save_free2(ptr,actual_free); + else + (*actual_free)(ptr); + } + } else { + if(USER_RECLAIM()) + ret = save_realloc2(ptr,(size_t)size,actual_realloc); + else + ret = (*actual_realloc)(ptr,(size_t)size); + } + ELIB_UNLOCK; + return ret; + } +} + +/* + * Wrapped free(). + */ +void +erts_sys_free(ErtsAlcType_t type, void *extra, void *ptr) +{ + if(use_save_free(ptr)){ + /* + * This might happen when linked in drivers use save_malloc etc + * directly. + */ + if((alloc_flags & WARN_MALLOC_MIX) && + (alloc_flags & USING_ELIB_MALLOC)) + erts_fprintf(stderr,"Warning, save_malloced data freed by " + "sys_free2\n"); + save_free(ptr); + } else { + ELIB_LOCK; + if(USER_RECLAIM()) + save_free2(ptr,actual_free); + else + (*actual_free)(ptr); + ELIB_UNLOCK; + } +} + +/* + * External interface to be called before erlang is started + * Parameters: + * isize: The size of the memory block where erlang should malloc(). + * iptr: (optional) A pointer to a user supplied memory block of + * size isize. + * warn_save: Instructs sys_free2 and sys_realloc2 to warn if + * memory allocation/reallocation/freeing is mixed between + * pure malloc/save_malloc/sys_alloc2 routines (only + * warns if elib is actually used in the sys_alloc2 routines). + * realloc_moves: Always allocate a fresh memory block on reallocation + * (less fragmentation). + * reclaim_in_supplied: Use memory reclaim mechanisms inside the user + * supplied area, this makes one area reusable between + * starts of erlang and might be nice for drivers etc. + */ + +int erl_set_memory_block(int isize, int iptr, int warn_save, + int realloc_moves, int reclaim_in_supplied, int p5, + int p6, int p7, int p8, int p9){ + if(erlang_id != 0){ + erts_fprintf(stderr,"Error, cannot set erlang memory block while an " + "erlang task is running!\n"); + return 1; + } + if(isize < 8 * 1024 *1024) + erts_fprintf(stderr, + "Warning, the memory pool of %dMb may be to small to " + "run erlang in!\n", isize / (1024 * 1024)); + alloc_pool_size = (size_t) isize; + alloc_pool_ptr = (void *) iptr; + alloc_flags = 0; + /* USING_ELIB_MALLOC gets set by the initialization routine */ + if((void *)iptr != NULL) + alloc_flags |= (USER_POOL | NEW_USER_POOL); + if(realloc_moves) + alloc_flags |= REALLOC_MOVES; + if(warn_save) + alloc_flags |= WARN_MALLOC_MIX; + if((void *)iptr != NULL && reclaim_in_supplied) + alloc_flags |= RECLAIM_USER_POOL; + return 0; +} + +/* External statistics interface */ +int erl_memory_show(int p0, int p1, int p2, int p3, int p4, int p5, + int p6, int p7, int p8, int p9){ + struct elib_stat statistics; + if(!(alloc_flags & USING_ELIB_MALLOC) && erlang_id != 0){ + erts_printf("Using plain save_alloc, use memShow instead.\n"); + return 1; + } + if(erlang_id == 0 && !((alloc_flags & USER_POOL) && + !(alloc_flags & NEW_USER_POOL))){ + erts_printf("Sorry, no allocation statistics until erlang " + "is started.\n"); + return 1; + } + erts_printf("Allocation settings:\n"); + erts_printf("Using elib_malloc with memory pool size of %lu bytes.\n", + (unsigned long) alloc_pool_size); + erts_printf("Realloc-always-moves is %s\n", + (alloc_flags & REALLOC_MOVES) ? "on" : "off"); + erts_printf("Warnings about mixed malloc/free's are %s\n", + (alloc_flags & WARN_MALLOC_MIX) ? "on" : "off"); + if(alloc_flags & USER_POOL){ + erts_printf("The memory block used by elib is user supplied " + "at 0x%08x.\n", (unsigned int) alloc_pool_ptr); + if(alloc_flags & RECLAIM_USER_POOL) + erts_printf("Allocated memory within the user supplied pool\n" + " will be automatically reclaimed at task exit.\n"); + } else { + erts_printf("The memory block used by elib is save_malloc'ed " + "at 0x%08x.\n", (unsigned int) alloc_pool_ptr); + } +#ifdef NO_FIX_ALLOC + erts_printf("Fix_alloc is disabled in this build\n"); +#endif + erts_printf("Statistics from elib_malloc:\n"); + ELIB_LOCK; + + elib_stat(&statistics); + ELIB_UNLOCK; + erts_printf("Type Size (bytes) Number of blocks\n"); + erts_printf("============= ============ ================\n"); + erts_printf("Total: %12lu %16lu\n", + (unsigned long) statistics.mem_total*4, + (unsigned long) statistics.mem_blocks); + erts_printf("Allocated: %12lu %16lu\n", + (unsigned long) statistics.mem_alloc*4, + (unsigned long) statistics.mem_blocks-statistics.free_blocks); + erts_printf("Free: %12lu %16lu\n", + (unsigned long) statistics.mem_free*4, + (unsigned long) statistics.free_blocks); + erts_printf("Largest free: %12lu -\n\n", + (unsigned long) statistics.max_free*4); + return 0; +} + + +/* +** More programmer friendly (as opposed to user friendly ;-) interface +** to the memory statistics. Resembles the VxWorks memPartInfoGet but +** does not take a partition id as parameter... +*/ +int erl_mem_info_get(MEM_PART_STATS *stats){ + struct elib_stat statistics; + if(!(alloc_flags & USING_ELIB_MALLOC)) + return -1; + ELIB_LOCK; + elib_stat(&statistics); + ELIB_UNLOCK; + stats->numBytesFree = statistics.mem_free*4; + stats->numBlocksFree = statistics.free_blocks; + stats->maxBlockSizeFree = statistics.max_free*4; + stats->numBytesAlloc = statistics.mem_alloc*4; + stats->numBlocksAlloc = statistics.mem_blocks-statistics.free_blocks; + return 0; +} + +/********************* Pipe driver **********************************/ +/* + * Purpose: Pipe driver with Unix (unnamed) pipe semantics. + * Author: Peter Hogfeldt ([email protected]) from an outline + * by Per Hedeland ([email protected]). + * + * Note: This driver must *not* use the reclaim facilities, hence it + * is placed here. (after the #undef's of open,malloc etc) + * + * This driver supports select() and non-blocking I/O via + * ioctl(fd, FIONBIO, val). + * + * 1997-03-21 Peter Hogfeldt + * Added non-blocking I/O. + * + */ + +/* + * SEMAPHORES + * + * Each end of a pipe has two semaphores: semExcl for serialising access to + * the pipe end, and semBlock for blocking I/O. + * + * reader->semBlock is available (full) if and only if the pipe is + * not empty, or the write end is closed. Otherwise + * it is unavailable (empty). It is initially + * unavailable. + * + * writer->semBlock is available (full) if and only if the pipe is + * not full, or if the reader end is closed. + * Otherwise it is unavailable. It is initially + * available. + */ + +#define UXPIPE_SIZE 4096 + +/* Forward declaration */ +typedef struct uxPipeDev UXPIPE_DEV; + +/* + * Pipe descriptor (one for each open pipe). + */ +typedef struct { + int drvNum; + UXPIPE_DEV *reader, *writer; + RING_ID ringId; +} UXPIPE; + +/* + * Device descriptor (one for each of the read and write + * ends of an open pipe). + */ +struct uxPipeDev { + UXPIPE *pipe; + int blocking; + SEL_WAKEUP_LIST wakeupList; + SEM_ID semExcl; + SEM_ID semBlock; +}; + +int uxPipeDrvNum = 0; /* driver number of pipe driver */ + +#define PIPE_NAME "/uxpipe" /* only used internally */ +#define PIPE_READ "/r" /* ditto */ +#define PIPE_WRITE "/w" /* ditto */ + +LOCAL char pipeRead[64], pipeWrite[64]; +LOCAL DEV_HDR devHdr; +LOCAL UXPIPE *newPipe; /* communicate btwn open()s in pipe() */ +LOCAL SEM_ID pipeSem; /* mutual exclusion in pipe() */ + +/* forward declarations */ +LOCAL int uxPipeOpen(DEV_HDR *pDv, char *name, int mode); +LOCAL int uxPipeClose(UXPIPE_DEV *pDev); +LOCAL int uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes); +LOCAL int uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes); +LOCAL STATUS uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg); + + +/*************************************************************************** + * + * uxPipeDrv - install Unix pipe driver + * + * This routine initializes the Unix pipe driver. It must be called + * before any other routine in this driver. + * + * RETURNS: + * OK, or ERROR if I/O system is unable to install driver. + */ + +STATUS +uxPipeDrv(void) +{ + if (uxPipeDrvNum > 0) + return (OK); /* driver already installed */ + if ((uxPipeDrvNum = iosDrvInstall((FUNCPTR) NULL, (FUNCPTR) NULL, + uxPipeOpen, uxPipeClose, uxPipeRead, + uxPipeWrite, uxPipeIoctl)) == ERROR) + return (ERROR); + if (iosDevAdd(&devHdr, PIPE_NAME, uxPipeDrvNum) == ERROR) + return (ERROR); + strcpy(pipeRead, PIPE_NAME); + strcat(pipeRead, PIPE_READ); + strcpy(pipeWrite, PIPE_NAME); + strcat(pipeWrite, PIPE_WRITE); + if ((pipeSem = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE)) == NULL) + return (ERROR); + return (OK); +} + +/*************************************************************************** + * + * uxPipeOpen - open a pipe + * + * RETURNS: Pointer to device descriptor, or ERROR if memory cannot be + * allocated (errno = ENOMEM), or invalid argument (errno = EINVAL). + */ + +/* + * DEV_HDR *pDv; pointer to device header (dummy) + * char *name; name of pipe to open ("/r" or "/w") + * int mode; access mode (O_RDONLY or O_WRONLY) + */ +LOCAL int +uxPipeOpen(DEV_HDR *pDv, char *name, int mode) +{ + UXPIPE_DEV *reader, *writer; + + if (mode == O_RDONLY && strcmp(name, PIPE_READ) == 0) { + /* reader open */ + if ((newPipe = (UXPIPE *) malloc(sizeof(UXPIPE))) != NULL) { + if ((newPipe->ringId = rngCreate(UXPIPE_SIZE)) != NULL) { + if ((reader = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) { + if ((reader->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { + if ((reader->semBlock = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) != NULL) { + reader->pipe = newPipe; + reader->blocking = 1; + selWakeupListInit(&reader->wakeupList); + newPipe->reader = reader; + newPipe->writer = NULL; + newPipe->drvNum = uxPipeDrvNum; + return ((int) reader); + } + semDelete(reader->semExcl); + } + free(reader); + } + rngDelete(newPipe->ringId); + } + free(newPipe); + newPipe = NULL; + errno = ENOMEM; + } + } else if (mode == O_WRONLY && strcmp(name, PIPE_WRITE) == 0) { + /* writer open */ + if (newPipe != NULL && + (writer = (UXPIPE_DEV *) malloc(sizeof(UXPIPE_DEV))) != NULL) { + if ((writer->semExcl = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { + if ((writer->semBlock = semBCreate(SEM_Q_FIFO, SEM_FULL)) != NULL) { + writer->blocking = 1; + writer->pipe = newPipe; + selWakeupListInit(&writer->wakeupList); + newPipe->writer = writer; + newPipe = NULL; + return ((int) writer); + } + semDelete(writer->semExcl); + } + free(writer); + } + if (newPipe != NULL) + free(newPipe); + newPipe = NULL; + errno = ENOMEM; + } else { + errno = EINVAL; + } + return (ERROR); +} + +/*************************************************************************** + * + * uxPipeClose - close read or write end of a pipe. + * + * RETURNS: + * OK, or ERROR if device descriptor does not refer to an open read or + write end of a pipe (errno = EBADF). + */ + +LOCAL int +uxPipeClose(UXPIPE_DEV *pDev) +{ + UXPIPE *pajp = pDev->pipe; + + taskLock(); + if (pDev == pajp->reader) { + /* Close this end */ + semDelete(pDev->semExcl); + semDelete(pDev->semBlock); + free(pDev); + pajp->reader = NULL; + /* Inform the other end */ + if (pajp->writer != NULL) { + selWakeupAll(&pajp->writer->wakeupList, SELWRITE); + semGive(pajp->writer->semBlock); + } + } else if (pDev == pajp->writer) { + /* Close this end */ + semDelete(pDev->semExcl); + semDelete(pDev->semBlock); + free(pDev); + pajp->writer = NULL; + /* Inform the other end */ + if (pajp->reader != NULL) { + selWakeupAll(&pajp->reader->wakeupList, SELREAD); + semGive(pajp->reader->semBlock); + } + } else { + errno = EBADF; + taskUnlock(); + return (ERROR); + } + if (pajp->reader == NULL && pajp->writer == NULL) { + rngDelete(pajp->ringId); + pajp->drvNum = 0; + free(pajp); + } + taskUnlock(); + return (OK); +} +/*************************************************************************** + * + * uxPipeRead - read from a pipe. + * + * Reads at most maxbytes bytes from the pipe. Blocks if blocking mode is + * set and the pipe is empty. + * + * RETURNS: + * number of bytes read, 0 on EOF, or ERROR if device descriptor does + * not refer to an open read end of a pipe (errno = EBADF), or if + * non-blocking mode is set and the pipe is empty (errno = EWOULDBLOCK). + */ + +LOCAL int +uxPipeRead(UXPIPE_DEV *pDev, char *buffer, int maxbytes) +{ + UXPIPE *pajp = pDev->pipe; + int nbytes = 0; + + if (pDev != pajp->reader) { + errno = EBADF; + return (ERROR); + } + if (maxbytes == 0) + return (0); + semTake(pDev->semExcl, WAIT_FOREVER); + /* + * Note that semBlock may be full, although there is nothing to read. + * This happens e.g. after the following sequence of operations: a + * reader task blocks, a writer task writes two times (the first + * write unblocks the reader task, the second write makes semBlock + * full). + */ + while (nbytes == 0) { + if (pDev->blocking) + semTake(pDev->semBlock, WAIT_FOREVER); + /* + * Reading and updating of the write end must not be interleaved + * with a write from another task - hence we lock this task. + */ + taskLock(); + nbytes = rngBufGet(pajp->ringId, buffer, maxbytes); + if (nbytes > 0) { + /* Give own semaphore if bytes remain or if write end is closed */ + if ((!rngIsEmpty(pajp->ringId) || pajp->writer == NULL) && + pDev->blocking) + semGive(pDev->semBlock); + /* Inform write end */ + if (pajp->writer != NULL) { + if (pajp->writer->blocking) + semGive(pajp->writer->semBlock); + selWakeupAll(&pajp->writer->wakeupList, SELWRITE); + } + } else if (pajp->writer == NULL) { + nbytes = 0; /* EOF */ + /* Give semaphore when write end is closed */ + if (pDev->blocking) + semGive(pDev->semBlock); + taskUnlock(); + semGive(pDev->semExcl); + return (nbytes); + } else if (!pDev->blocking) { + taskUnlock(); + semGive(pDev->semExcl); + errno = EWOULDBLOCK; + return (ERROR); + } + taskUnlock(); + } + semGive(pDev->semExcl); + return (nbytes); +} + +/*************************************************************************** + * + * uxPipeWrite - write to a pipe. + * + * Writes nbytes bytes to the pipe. Blocks if blocking mode is set, and if + * the pipe is full. + * + * RETURNS: + * number of bytes written, or ERROR if the device descriptor does not + * refer to an open write end of a pipe (errno = EBADF); or if the read end + * of the pipe is closed (errno = EPIPE); or if non-blocking mode is set + * and the pipe is full (errno = EWOULDBLOCK). + * + */ + +LOCAL int +uxPipeWrite(UXPIPE_DEV *pDev, char *buffer, int nbytes) +{ + + UXPIPE *pajp = pDev->pipe; + int sofar = 0, written; + + if (pDev != pajp->writer) { + errno = EBADF; + return (ERROR); + } + if (pajp->reader == NULL) { + errno = EPIPE; + return (ERROR); + } + if (nbytes == 0) + return (0); + semTake(pDev->semExcl, WAIT_FOREVER); + while (sofar < nbytes) { + if (pDev->blocking) + semTake(pDev->semBlock, WAIT_FOREVER); + if (pajp->reader == NULL) { + errno = EPIPE; + semGive(pDev->semBlock); + semGive(pDev->semExcl); + return (ERROR); + } + /* Writing and updating of the read end must not be interleaved + * with a read from another task - hence we lock this task. + */ + taskLock(); + written = rngBufPut(pajp->ringId, buffer + sofar, nbytes - sofar); + sofar += written; + /* Inform the read end if we really wrote something */ + if (written > 0 && pajp->reader != NULL) { + selWakeupAll(&pajp->reader->wakeupList, SELREAD); + if (pajp->reader->blocking) + semGive(pajp->reader->semBlock); + } + taskUnlock(); + if (!pDev->blocking) { + if (sofar == 0) { + errno = EWOULDBLOCK; + sofar = ERROR; + } + break; + } + } + /* Give own semaphore if space remains */ + if (!rngIsFull(pajp->ringId) && pDev->blocking) + semGive(pDev->semBlock); + semGive(pDev->semExcl); + return (sofar); +} + +/*************************************************************************** + * + * uxPipeIoctl - do device specific I/O control + * + * RETURNS: + * OK or ERROR. + */ + +LOCAL STATUS +uxPipeIoctl(FAST UXPIPE_DEV *pDev, FAST int function, int arg) + +{ + UXPIPE *pajp = pDev->pipe; + int status = OK; + + switch (function) { + case FIONBIO: + pDev->blocking = (*(int *)arg) ? 0 : 1; + break; + case FIOSELECT: + taskLock(); + selNodeAdd(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg); + if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELREAD && + pDev == pajp->reader && + (!rngIsEmpty(pajp->ringId) || pajp->writer == NULL)) + selWakeup((SEL_WAKEUP_NODE *) arg); + if (selWakeupType((SEL_WAKEUP_NODE *) arg) == SELWRITE && + pDev == pajp->writer && + (!rngIsFull(pajp->ringId) || pajp->reader == NULL)) + selWakeup((SEL_WAKEUP_NODE *) arg); + taskUnlock(); + break; + case FIOUNSELECT: + selNodeDelete(&pDev->wakeupList, (SEL_WAKEUP_NODE *) arg); + break; + default: + status = ERROR; + break; + } + return (status); +} + +/*************************************************************************** + * + * pipe - create an intertask channel + * + * Creates a pipe. fd[0] (fd[1]) is the read (write) file descriptor. + * + * RETURNS: + * OK or ERROR, if the pipe could not be created. + */ + +STATUS +pipe(int fd[2]) +{ + semTake(pipeSem, WAIT_FOREVER); + if ((fd[0] = open(pipeRead, O_RDONLY, 0)) != ERROR) { + if ((fd[1] = open(pipeWrite, O_WRONLY, 0)) != ERROR) { + semGive(pipeSem); + return (OK); + } + (void) close(fd[0]); + } + errno &= 0xFFFF; + if((errno & 0xFFFF) == EINTR) /* Why on earth EINTR??? */ + errno = ENFILE; /* It means we are out of file descriptors...*/ + semGive(pipeSem); + return (ERROR); +} + +/*************************************************************************** + * + * uxPipeShow - display pipe information + * + * RETURNS: + * N/A. + */ + +void +uxPipeShow(int fd) +{ + UXPIPE_DEV *pDev; + UXPIPE *pajp; + int drvValue; + + if ((drvValue = iosFdValue(fd)) == ERROR) { + erts_fprintf(stderr, "Error: file descriptor invalid\n"); + return; + } + pDev = (UXPIPE_DEV *)drvValue; + pajp = pDev->pipe; + if (pajp->drvNum != uxPipeDrvNum) { + erts_fprintf(stderr, "Error: Not a ux pipe device\n"); + return; + } + erts_fprintf(stderr, "Device : 0x%x\n", (int) pDev); + erts_fprintf(stderr, "Buffer size : %d\n", UXPIPE_SIZE); + erts_fprintf(stderr, "Bytes in buffer : %d\n\n", rngNBytes(pajp->ringId)); + erts_fprintf(stderr, "READ END\n\n"); + if (pajp->reader != NULL) { + erts_fprintf(stderr, "Mode : "); + erts_fprintf(stderr, "%s\n", + (pajp->reader->blocking) ? "blocking" : "non-blocking"); + } + erts_fprintf(stderr, "Status : "); + if (pajp->reader != NULL) { + erts_fprintf(stderr, "OPEN\n"); + erts_fprintf(stderr, "Wake-up list : %d\n\n", + selWakeupListLen(&pajp->reader->wakeupList)); + erts_fprintf(stderr, "Exclusion Semaphore\n"); + semShow(pajp->reader->semExcl, 1); + erts_fprintf(stderr, "Blocking Semaphore\n"); + semShow(pajp->reader->semBlock, 1); + } else + erts_fprintf(stderr, "CLOSED\n\n"); + erts_fprintf(stderr, "WRITE END\n\n"); + if (pajp->writer != NULL) { + erts_fprintf(stderr, "Mode : "); + erts_fprintf(stderr, "%s\n", + (pajp->writer->blocking) ? "blocking" : "non-blocking"); + } + erts_fprintf(stderr, "Status : "); + if (pajp->writer != NULL) { + erts_fprintf(stderr, "OPEN\n"); + erts_fprintf(stderr, "Wake-up list : %d\n\n", + selWakeupListLen(&pajp->writer->wakeupList)); + erts_fprintf(stderr, "Exclusion Semaphore\n"); + semShow(pajp->writer->semExcl, 1); + erts_fprintf(stderr, "Blocking Semaphore\n"); + semShow(pajp->writer->semBlock, 1); + } else + erts_fprintf(stderr, "CLOSED\n\n"); +} + +#ifdef DEBUG +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); + erl_crash_dump(file, line, "Assertion failed: %s\n", expr); + abort(); +} +void +erl_debug(char* fmt, ...) +{ + char sbuf[1024]; /* Temporary buffer. */ + va_list va; + + va_start(va, fmt); + vsprintf(sbuf, fmt, va); + va_end(va); + fprintf(stderr, "%s\n", sbuf); +} +#endif |