aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/unix
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/unix')
-rw-r--r--erts/emulator/sys/unix/driver_int.h41
-rw-r--r--erts/emulator/sys/unix/erl9_start.c130
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c122
-rw-r--r--erts/emulator/sys/unix/erl_main.c31
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h339
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys_ddll.c280
-rw-r--r--erts/emulator/sys/unix/sys.c3346
-rw-r--r--erts/emulator/sys/unix/sys_float.c815
-rw-r--r--erts/emulator/sys/unix/sys_time.c134
9 files changed, 5238 insertions, 0 deletions
diff --git a/erts/emulator/sys/unix/driver_int.h b/erts/emulator/sys/unix/driver_int.h
new file mode 100644
index 0000000000..a7ee8087ab
--- /dev/null
+++ b/erts/emulator/sys/unix/driver_int.h
@@ -0,0 +1,41 @@
+/*
+ * %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 dependant driver declarations
+ */
+
+#ifndef __DRIVER_INT_H__
+#define __DRIVER_INT_H__
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/types.h>
+#include <sys/uio.h>
+
+typedef struct iovec SysIOVec;
+
+#else
+
+typedef struct {
+ char* iov_base;
+ int iov_len;
+} SysIOVec;
+
+#endif
+
+#endif
diff --git a/erts/emulator/sys/unix/erl9_start.c b/erts/emulator/sys/unix/erl9_start.c
new file mode 100644
index 0000000000..578062d7e2
--- /dev/null
+++ b/erts/emulator/sys/unix/erl9_start.c
@@ -0,0 +1,130 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-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%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * XXX This is a temporary dummy to make sys.c happy until we'll rewrite it.
+ */
+unsigned preloaded_size_ring0 = 1;
+unsigned char preloaded_ring0[1] = {0};
+
+Preload pre_loaded[] = {
+ {"ring0", 1, preloaded_ring0},
+ {0, 0, 0}
+};
+
+int
+main(int argc, char** argv)
+{
+ char sbuf[1024];
+ struct {
+ void* p;
+ int sz;
+ } bins[2];
+ int bin_num = 0;
+ FILE* fp;
+ char* progname = argv[0];
+ char* eq;
+
+ argv++, argc--;
+
+ if (argc > 0 && argv[0][0] == '-') {
+ argv++, argc--;
+ }
+ if (argc < 1) {
+ abort();
+ }
+ if ((fp = fopen(argv[0], "r")) == NULL) {
+ abort();
+ }
+
+ /* Needs to be called before any memory allocation */
+ erts_short_init();
+
+ while (fgets(sbuf, sizeof sbuf, fp)) {
+ if (sbuf[0] == '#') {
+ continue; /* Comment */
+ } else if (sbuf[0] == 'e' && strncmp("exec", sbuf, 4) == 0) {
+ continue; /* Comment ;-) */
+ } else if ((eq = strchr(sbuf, '=')) != NULL) {
+ char* val;
+ char* p = strchr(sbuf, '\n');
+ if (p) {
+ *p = '\0';
+ }
+ *eq = '\0';
+ val = erts_read_env(sbuf);
+ if (val == NULL) {
+ *eq = '=';
+ erts_sys_putenv(sbuf, eq - &sbuf[0]);
+ }
+ erts_free_read_env(val);
+ } else if (sbuf[0] == ':' && '0' <= sbuf[1] && sbuf[1] <= '9') {
+ int load_size = atoi(sbuf+1);
+ void* bin;
+
+ bin = malloc(load_size);
+ if (fread(bin, 1, load_size, fp) != load_size) {
+ abort();
+ }
+ bins[bin_num].p = bin;
+ bins[bin_num].sz = load_size;
+ bin_num++;
+ } else if (strcmp(sbuf, "--end--\n") == 0) {
+ int rval;
+ Eterm mod = NIL;
+ char *val;
+
+ fclose(fp);
+
+ if (bin_num != 2) {
+ abort();
+ }
+
+ val = erts_read_env("ERLBREAKHANDLER");
+ if (val) {
+ init_break_handler();
+ }
+ erts_free_read_env(val);
+
+ if ((rval = erts_load_module(NULL, 0, NIL, &mod, bins[0].p, bins[0].sz)) < 0) {
+ fprintf(stderr, "%s: Load of initial module failed: %d\n",
+ progname, rval);
+ abort();
+ }
+ erts_first_process(mod, bins[1].p, bins[1].sz, argc, argv);
+ free(bins[0].p);
+ free(bins[1].p);
+ process_main();
+ abort();
+ } else {
+ fprintf(stderr, "%s: bad line: %s\n", progname, sbuf);
+ abort();
+ }
+ }
+ abort();
+}
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
new file mode 100644
index 0000000000..7c6e4a2f37
--- /dev/null
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -0,0 +1,122 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-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%
+ */
+
+/*
+ * After a vfork() (or fork()) the child exec()s to this program which
+ * sets up the child and exec()s to the user program (see spawn_start()
+ * in sys.c and ticket OTP-4389).
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define NEED_CHILD_SETUP_DEFINES
+#include "sys.h"
+#include "erl_misc_utils.h"
+
+#ifdef SIG_SIGSET /* Old SysV */
+void sys_sigrelease(int sig)
+{
+ sigrelse(sig);
+}
+#else /* !SIG_SIGSET */
+#ifdef SIG_SIGNAL /* Old BSD */
+sys_sigrelease(int sig)
+{
+ sigsetmask(sigblock(0) & ~sigmask(sig));
+}
+#else /* !SIG_SIGNAL */ /* The True Way - POSIX!:-) */
+void sys_sigrelease(int sig)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
+}
+#endif /* !SIG_SIGNAL */
+#endif /* !SIG_SIGSET */
+
+int
+main(int argc, char *argv[])
+{
+ int i, from, to;
+ int erts_spawn_executable = 0;
+
+ /* OBSERVE!
+ * Keep child setup after fork() (implemented in sys.c) up to date
+ * if changes are made here.
+ */
+
+ if (argc != CS_ARGV_NO_OF_ARGS) {
+ if (argc < CS_ARGV_NO_OF_ARGS) {
+ return 1;
+ } else {
+ erts_spawn_executable = 1;
+ }
+ }
+
+ if (strcmp("false", argv[CS_ARGV_UNBIND_IX]) != 0)
+ if (erts_unbind_from_cpu_str(argv[CS_ARGV_UNBIND_IX]) != 0)
+ return 1;
+
+ for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++) {
+ if (argv[CS_ARGV_DUP2_OP_IX(i)][0] == '-'
+ && argv[CS_ARGV_DUP2_OP_IX(i)][1] == '\0')
+ break;
+ if (sscanf(argv[CS_ARGV_DUP2_OP_IX(i)], "%d:%d", &from, &to) != 2)
+ return 1;
+ if (dup2(from, to) < 0)
+ return 1;
+ }
+
+ if (sscanf(argv[CS_ARGV_FD_CR_IX], "%d:%d", &from, &to) != 2)
+ return 1;
+ for (i = from; i <= to; i++)
+ (void) close(i);
+
+ if (!(argv[CS_ARGV_WD_IX][0] == '.' && argv[CS_ARGV_WD_IX][1] == '\0')
+ && chdir(argv[CS_ARGV_WD_IX]) < 0)
+ return 1;
+
+#if defined(USE_SETPGRP_NOARGS) /* SysV */
+ (void) setpgrp();
+#elif defined(USE_SETPGRP) /* BSD */
+ (void) setpgrp(0, getpid());
+#else /* POSIX */
+ (void) setsid();
+#endif
+
+ sys_sigrelease(SIGCHLD);
+ sys_sigrelease(SIGINT);
+ sys_sigrelease(SIGUSR1);
+
+ if (erts_spawn_executable) {
+ if (argv[CS_ARGV_NO_OF_ARGS + 1] == NULL) {
+ execl(argv[CS_ARGV_NO_OF_ARGS],argv[CS_ARGV_NO_OF_ARGS],
+ (char *) NULL);
+ } else {
+ execv(argv[CS_ARGV_NO_OF_ARGS],&(argv[CS_ARGV_NO_OF_ARGS + 1]));
+ }
+ } else {
+ execl("/bin/sh", "sh", "-c", argv[CS_ARGV_CMD_IX], (char *) NULL);
+ }
+ return 1;
+}
diff --git a/erts/emulator/sys/unix/erl_main.c b/erts/emulator/sys/unix/erl_main.c
new file mode 100644
index 0000000000..b26f93f77e
--- /dev/null
+++ b/erts/emulator/sys/unix/erl_main.c
@@ -0,0 +1,31 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-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%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+
+int
+main(int argc, char **argv)
+{
+ erl_start(argc, argv);
+ return 0;
+}
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
new file mode 100644
index 0000000000..2d5ef882f6
--- /dev/null
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -0,0 +1,339 @@
+/*
+ * %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%
+ *
+ * This file handles differences between different Unix systems.
+ * This should be the only place with conditional compilation
+ * depending on the type of OS.
+ */
+
+#ifndef _ERL_UNIX_SYS_H
+#define _ERL_UNIX_SYS_H
+
+#include <stdio.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef QNX
+#include <memory.h>
+#endif
+
+#if defined(__sun__) && defined(__SVR4) && !defined(__EXTENSIONS__)
+# define __EXTENSIONS__
+# include <sys/types.h>
+# undef __EXTENSIONS__
+#else
+# include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include "erl_errno.h"
+#include <signal.h>
+
+
+#if HAVE_SYS_SOCKETIO_H
+# include <sys/socketio.h>
+#endif
+#if HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_NET_ERRNO_H
+#include <net/errno.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef HAVE_MMAP
+# define HAVE_MMAP 0
+#endif
+
+#if HAVE_MMAP
+# include <sys/mman.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <sys/times.h>
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+#ifdef QNX
+#include <process.h>
+#include <sys/qnx_glob.h>
+#endif
+
+#include <pwd.h>
+
+#ifndef HZ
+#define HZ 60
+#endif
+
+#ifdef NETDB_H_NEEDS_IN_H
+#include <netinet/in.h>
+#endif
+#include <netdb.h>
+
+/*
+ * Make sure that MAXPATHLEN is defined.
+ */
+#ifdef GETHRTIME_WITH_CLOCK_GETTIME
+#undef HAVE_GETHRTIME
+#define HAVE_GETHRTIME 1
+#endif
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# else
+# define MAXPATHLEN 2048
+# endif
+#endif
+
+/* File descriptors are numbers anc consecutively allocated on Unix */
+#define ERTS_SYS_CONTINOUS_FD_NUMBERS
+
+#define HAVE_ERTS_CHECK_IO_DEBUG
+int erts_check_io_debug(void);
+
+
+#ifndef ENABLE_CHILD_WAITER_THREAD
+# undef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
+# define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
+# ifdef ERTS_SMP
+# define ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN
+void erts_check_children(void);
+# endif
+#endif
+
+typedef void *GETENV_STATE;
+
+/*
+** For the erl_timer_sup module.
+*/
+
+typedef struct timeval SysTimeval;
+
+#define sys_gettimeofday(Arg) ((void) gettimeofday((Arg), NULL))
+
+typedef struct tms SysTimes;
+
+extern int erts_ticks_per_sec;
+
+#define SYS_CLK_TCK (erts_ticks_per_sec)
+
+#define sys_times(Arg) times(Arg)
+
+#define ERTS_WRAP_SYS_TIMES 1
+extern int erts_ticks_per_sec_wrap;
+#define SYS_CLK_TCK_WRAP (erts_ticks_per_sec_wrap)
+extern clock_t sys_times_wrap(void);
+
+#ifdef HAVE_GETHRTIME
+#ifdef GETHRTIME_WITH_CLOCK_GETTIME
+typedef long long SysHrTime;
+
+extern SysHrTime sys_gethrtime(void);
+#define sys_init_hrtime() /* Nothing */
+
+#else /* Real gethrtime (Solaris) */
+
+typedef hrtime_t SysHrTime;
+
+#define sys_gethrtime() gethrtime()
+#define sys_init_hrtime() /* Nothing */
+
+#endif /* GETHRTIME_WITH_CLOCK_GETTIME */
+#endif /* HAVE_GETHRTIME */
+
+#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME))
+typedef long long SysCpuTime;
+typedef struct timespec SysTimespec;
+
+#if defined(HAVE_GETHRVTIME)
+#define sys_gethrvtime() gethrvtime()
+#define sys_get_proc_cputime(t,tp) (t) = sys_gethrvtime(), \
+ (tp).tv_sec = (time_t)((t)/1000000000LL), \
+ (tp).tv_nsec = (long)((t)%1000000000LL)
+int sys_start_hrvtime(void);
+int sys_stop_hrvtime(void);
+
+#elif defined(HAVE_CLOCK_GETTIME)
+#define sys_clock_gettime(cid,tp) clock_gettime((cid),&(tp))
+#define sys_get_proc_cputime(t,tp) sys_clock_gettime(CLOCK_PROCESS_CPUTIME_ID,(tp))
+
+#endif
+#endif
+
+/* No use in having other resolutions than 1 Ms. */
+#define SYS_CLOCK_RESOLUTION 1
+
+/* These are defined in sys.c */
+#if defined(SIG_SIGSET) /* Old SysV */
+RETSIGTYPE (*sys_sigset())();
+#elif defined(SIG_SIGNAL) /* Old BSD */
+RETSIGTYPE (*sys_sigset())();
+#else
+RETSIGTYPE (*sys_sigset(int, RETSIGTYPE (*func)(int)))(int);
+#endif
+extern void sys_sigrelease(int);
+extern void sys_sigblock(int);
+extern void sys_stop_cat(void);
+
+/*
+ * Handling of floating point exceptions.
+ */
+
+#ifdef USE_ISINF_ISNAN /* simulate finite() */
+# define finite(f) (!isinf(f) && !isnan(f))
+# define HAVE_FINITE
+#endif
+
+#ifdef NO_FPE_SIGNALS
+
+#define erts_get_current_fp_exception() NULL
+#ifdef ERTS_SMP
+#define erts_thread_init_fp_exception() do{}while(0)
+#endif
+# define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
+# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {}
+# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action)
+# define __ERTS_SAVE_FP_EXCEPTION(fpexnp)
+# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp)
+
+#define erts_sys_block_fpe() 0
+#define erts_sys_unblock_fpe(x) do{}while(0)
+
+#else /* !NO_FPE_SIGNALS */
+
+extern volatile unsigned long *erts_get_current_fp_exception(void);
+#ifdef ERTS_SMP
+extern void erts_thread_init_fp_exception(void);
+#endif
+# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
+# define erts_fwait(fpexnp,f) \
+ __asm__ __volatile__("fwait" : "=m"(*(fpexnp)) : "m"(f))
+# elif (defined(__powerpc__) || defined(__ppc__)) && defined(__GNUC__)
+# define erts_fwait(fpexnp,f) \
+ __asm__ __volatile__("" : "=m"(*(fpexnp)) : "fm"(f))
+# elif defined(__sparc__) && defined(__linux__) && defined(__GNUC__)
+# define erts_fwait(fpexnp,f) \
+ __asm__ __volatile__("" : "=m"(*(fpexnp)) : "em"(f))
+# else
+# define erts_fwait(fpexnp,f) \
+ __asm__ __volatile__("" : "=m"(*(fpexnp)) : "g"(f))
+# endif
+# if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
+ extern void erts_restore_fpu(void);
+# else
+# define erts_restore_fpu() /*empty*/
+# endif
+# if (!defined(__GNUC__) || \
+ (__GNUC__ < 2) || \
+ (__GNUC__ == 2 && __GNUC_MINOR < 96)) && \
+ !defined(__builtin_expect)
+# define __builtin_expect(x, expected_value) (x)
+# endif
+static __inline__ int erts_check_fpe(volatile unsigned long *fp_exception, double f)
+{
+ erts_fwait(fp_exception, f);
+ if (__builtin_expect(*fp_exception == 0, 1))
+ return 0;
+ *fp_exception = 0;
+ erts_restore_fpu();
+ return 1;
+}
+# undef erts_fwait
+# undef erts_restore_fpu
+extern void erts_fp_check_init_error(volatile unsigned long *fp_exception);
+static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception)
+{
+ if (__builtin_expect(*fp_exception == 0, 1))
+ return;
+ erts_fp_check_init_error(fp_exception);
+}
+# define __ERTS_FP_ERROR(fpexnp, f, Action) do { if (erts_check_fpe((fpexnp),(f))) { Action; } } while (0)
+# define __ERTS_SAVE_FP_EXCEPTION(fpexnp) unsigned long old_erl_fp_exception = *(fpexnp)
+# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp) \
+ do { *(fpexnp) = old_erl_fp_exception; } while (0)
+ /* This is for library calls where we don't trust the external
+ code to always throw floating-point exceptions on errors. */
+static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f)
+{
+ return erts_check_fpe(fp_exception, f) || !finite(f);
+}
+# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \
+ do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0)
+
+int erts_sys_block_fpe(void);
+void erts_sys_unblock_fpe(int);
+
+#endif /* !NO_FPE_SIGNALS */
+
+#define ERTS_FP_CHECK_INIT(p) __ERTS_FP_CHECK_INIT(&(p)->fp_exception)
+#define ERTS_FP_ERROR(p, f, A) __ERTS_FP_ERROR(&(p)->fp_exception, f, A)
+#define ERTS_FP_ERROR_THOROUGH(p, f, A) __ERTS_FP_ERROR_THOROUGH(&(p)->fp_exception, f, A)
+
+
+#ifdef NEED_CHILD_SETUP_DEFINES
+/* The child setup argv[] */
+#define CS_ARGV_PROGNAME_IX 0 /* Program name */
+#define CS_ARGV_UNBIND_IX 1 /* Unbind from cpu */
+#define CS_ARGV_WD_IX 2 /* Working directory */
+#define CS_ARGV_CMD_IX 3 /* Command */
+#define CS_ARGV_FD_CR_IX 4 /* Fd close range */
+#define CS_ARGV_DUP2_OP_IX(N) ((N) + 5) /* dup2 operations */
+
+#define CS_ARGV_NO_OF_DUP2_OPS 3 /* Number of dup2 ops */
+#define CS_ARGV_NO_OF_ARGS 8 /* Number of arguments */
+#endif /* #ifdef NEED_CHILD_SETUP_DEFINES */
+
+/* Threads */
+#ifdef USE_THREADS
+extern int init_async(int);
+extern int exit_async(void);
+#endif
+
+#define ERTS_EXIT_AFTER_DUMP _exit
+
+#ifdef ERTS_TIMER_THREAD
+struct erts_iwait; /* opaque for clients */
+extern struct erts_iwait *erts_iwait_init(void);
+extern void erts_iwait_wait(struct erts_iwait *iwait, struct timeval *delay);
+extern void erts_iwait_interrupt(struct erts_iwait *iwait);
+#endif /* ERTS_TIMER_THREAD */
+
+#endif /* #ifndef _ERL_UNIX_SYS_H */
diff --git a/erts/emulator/sys/unix/erl_unix_sys_ddll.c b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
new file mode 100644
index 0000000000..336d9586c4
--- /dev/null
+++ b/erts/emulator/sys/unix/erl_unix_sys_ddll.c
@@ -0,0 +1,280 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2006-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%
+ */
+
+/*
+ * Interface functions to the dynamic linker using dl* functions.
+ * (As far as I know it works on SunOS 4, 5, Linux and FreeBSD. /Seb)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+
+/* some systems do not have RTLD_NOW defined, and require the "mode"
+ * argument to dload() always be 1.
+ */
+#ifndef RTLD_NOW
+# define RTLD_NOW 1
+#endif
+
+#define MAX_NAME_LEN 255 /* XXX should we get the system path size? */
+#define EXT_LEN 3
+#define FILE_EXT ".so" /* extension appended to the filename */
+
+static char **errcodes = NULL;
+static int num_errcodes = 0;
+static int num_errcodes_allocated = 0;
+
+#define my_strdup(WHAT) my_strdup_in(ERTS_ALC_T_DDLL_ERRCODES, WHAT);
+
+static char *my_strdup_in(ErtsAlcType_t type, char *what)
+{
+ char *res = erts_alloc(type, strlen(what) + 1);
+ strcpy(res, what);
+ return res;
+}
+
+
+static int find_errcode(char *string, ErtsSysDdllError* err)
+{
+ int i;
+
+ if (err != NULL) {
+ erts_sys_ddll_free_error(err); /* in case we ignored an earlier error */
+ err->str = my_strdup_in(ERTS_ALC_T_DDLL_TMP_BUF, string);
+ return 0;
+ }
+ for(i=0;i<num_errcodes;++i) {
+ if (!strcmp(string, errcodes[i])) {
+ return i;
+ }
+ }
+ if (num_errcodes_allocated == num_errcodes) {
+ errcodes = (num_errcodes_allocated == 0)
+ ? erts_alloc(ERTS_ALC_T_DDLL_ERRCODES,
+ (num_errcodes_allocated = 10) * sizeof(char *))
+ : erts_realloc(ERTS_ALC_T_DDLL_ERRCODES, errcodes,
+ (num_errcodes_allocated += 10) * sizeof(char *));
+ }
+ errcodes[num_errcodes++] = my_strdup(string);
+ return (num_errcodes - 1);
+}
+
+void erl_sys_ddll_init(void) {
+#if defined(HAVE_DLOPEN) && defined(ERTS_NEED_DLOPEN_BEFORE_DLERROR)
+ /*
+ * dlopen() needs to be called before we make the first call to
+ * dlerror(); otherwise, dlerror() might dump core. At least
+ * some versions of linuxthread suffer from this bug.
+ */
+ void *handle = dlopen("/nonexistinglib", RTLD_NOW);
+ if (handle)
+ dlclose(handle);
+#endif
+ return;
+}
+
+/*
+ * Open a shared object
+ */
+int erts_sys_ddll_open2(char *full_name, void **handle, ErtsSysDdllError* err)
+{
+#if defined(HAVE_DLOPEN)
+ char* dlname;
+ int len = sys_strlen(full_name);
+ int ret;
+
+ dlname = erts_alloc(ERTS_ALC_T_TMP, len + EXT_LEN + 1);
+ sys_strcpy(dlname, full_name);
+ sys_strcpy(dlname+len, FILE_EXT);
+
+ ret = erts_sys_ddll_open_noext(dlname, handle, err);
+
+ erts_free(ERTS_ALC_T_TMP, (void *) dlname);
+ return ret;
+#else
+ return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
+#endif
+}
+
+int erts_sys_ddll_open_noext(char *dlname, void **handle, ErtsSysDdllError* err)
+{
+ int ret = ERL_DE_NO_ERROR;
+ char *str;
+ dlerror();
+ if ((*handle = dlopen(dlname, RTLD_NOW)) == NULL) {
+ str = dlerror();
+
+ if (err == NULL) {
+ /*
+ * Remove prefix filename to avoid exploading number of
+ * error codes on extreme usage.
+ */
+ if (strstr(str,dlname) == str) {
+ char *save_str = str;
+ str += strlen(dlname);
+ while (*str == ':' || *str == ' ') {
+ ++str;
+ }
+ if (*str == '\0') { /* Better with filename than nothing... */
+ str = save_str;
+ }
+ }
+ }
+ ret = ERL_DE_DYNAMIC_ERROR_OFFSET - find_errcode(str, err);
+ }
+ return ret;
+}
+
+/*
+ * Find a symbol in the shared object
+ */
+int erts_sys_ddll_sym2(void *handle, char *func_name, void **function,
+ ErtsSysDdllError* err)
+{
+#if defined(HAVE_DLOPEN)
+ void *sym;
+ char *e;
+ int ret;
+ dlerror();
+ sym = dlsym(handle, func_name);
+ if ((e = dlerror()) != NULL) {
+ ret = ERL_DE_DYNAMIC_ERROR_OFFSET - find_errcode(e, err);
+ } else {
+ *function = sym;
+ ret = ERL_DE_NO_ERROR;
+ }
+ return ret;
+#else
+ return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
+#endif
+}
+
+/* XXX:PaN These two will be changed with new driver interface! */
+
+/*
+ * Load the driver init function, might appear under different names depending on object arch...
+ */
+
+int erts_sys_ddll_load_driver_init(void *handle, void **function)
+{
+ void *fn;
+ int res;
+ if ((res = erts_sys_ddll_sym2(handle, "driver_init", &fn, NULL)) != ERL_DE_NO_ERROR) {
+ res = erts_sys_ddll_sym2(handle, "_driver_init", &fn, NULL);
+ }
+ if (res == ERL_DE_NO_ERROR) {
+ *function = fn;
+ }
+ return res;
+}
+
+int erts_sys_ddll_load_nif_init(void *handle, void **function, ErtsSysDdllError* err)
+{
+ void *fn;
+ int res;
+ if ((res = erts_sys_ddll_sym2(handle, "nif_init", &fn, err)) != ERL_DE_NO_ERROR) {
+ res = erts_sys_ddll_sym2(handle, "_nif_init", &fn, err);
+ }
+ if (res == ERL_DE_NO_ERROR) {
+ *function = fn;
+ }
+ return res;
+}
+
+/*
+ * Call the driver_init function, whatever it's really called, simple on unix...
+*/
+void *erts_sys_ddll_call_init(void *function) {
+ void *(*initfn)(void) = function;
+ return (*initfn)();
+}
+void *erts_sys_ddll_call_nif_init(void *function) {
+ return erts_sys_ddll_call_init(function);
+}
+
+
+
+/*
+ * Close a chared object
+ */
+int erts_sys_ddll_close2(void *handle, ErtsSysDdllError* err)
+{
+#if defined(HAVE_DLOPEN)
+ int ret;
+ char *s;
+ dlerror();
+ if (dlclose(handle) == 0) {
+ ret = ERL_DE_NO_ERROR;
+ } else {
+ if ((s = dlerror()) == NULL) {
+ find_errcode("unspecified error", err);
+ ret = ERL_DE_ERROR_UNSPECIFIED;
+ } else {
+ ret = ERL_DE_DYNAMIC_ERROR_OFFSET - find_errcode(s, err);
+ }
+ }
+ return ret;
+#else
+ return ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY;
+#endif
+}
+
+
+/*
+ * Return string that describes the (current) error
+ */
+char *erts_sys_ddll_error(int code)
+{
+ int actual_code;
+
+ if (code > ERL_DE_DYNAMIC_ERROR_OFFSET) {
+ return "Unspecified error";
+ }
+ actual_code = -1*(code - ERL_DE_DYNAMIC_ERROR_OFFSET);
+#if defined(HAVE_DLOPEN)
+ {
+ char *msg;
+
+ if (actual_code >= num_errcodes) {
+ msg = "Unknown dlload error";
+ } else {
+ msg = errcodes[actual_code];
+ }
+ return msg;
+ }
+#endif
+ return "no error";
+}
+
+void erts_sys_ddll_free_error(ErtsSysDdllError* err)
+{
+ if (err->str != NULL) {
+ erts_free(ERTS_ALC_T_DDLL_TMP_BUF, err->str);
+ }
+}
+
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
new file mode 100644
index 0000000000..183525b222
--- /dev/null
+++ b/erts/emulator/sys/unix/sys.c
@@ -0,0 +1,3346 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef ISC32
+#define _POSIX_SOURCE
+#define _XOPEN_SOURCE
+#endif
+
+#include <sys/times.h> /* ! */
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#ifdef ISC32
+#include <sys/bsdtypes.h>
+#endif
+
+#include <termios.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#define NEED_CHILD_SETUP_DEFINES
+#define ERTS_WANT_BREAK_HANDLING
+#define ERTS_WANT_GOT_SIGUSR1
+#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
+#include "sys.h"
+
+#ifdef USE_THREADS
+#include "erl_threads.h"
+#endif
+
+#include "erl_mseg.h"
+
+extern char **environ;
+static erts_smp_rwmtx_t environ_rwmtx;
+
+#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
+ * vector sock_sendv().
+ */
+
+/*
+ * Don't need global.h, but bif_table.h (included by bif.h),
+ * won't compile otherwise
+ */
+#include "global.h"
+#include "bif.h"
+
+#include "erl_sys_driver.h"
+#include "erl_check_io.h"
+
+#ifndef DISABLE_VFORK
+#define DISABLE_VFORK 0
+#endif
+
+#ifdef USE_THREADS
+# ifdef ENABLE_CHILD_WAITER_THREAD
+# define CHLDWTHR ENABLE_CHILD_WAITER_THREAD
+# else
+# define CHLDWTHR 0
+# endif
+#else
+# define CHLDWTHR 0
+#endif
+/*
+ * [OTP-3906]
+ * Solaris signal management gets confused when threads are used and a
+ * lot of child processes dies. The confusion results in that SIGCHLD
+ * signals aren't delivered to the emulator which in turn results in
+ * a lot of defunct processes in the system.
+ *
+ * The problem seems to appear when a signal is frequently
+ * blocked/unblocked at the same time as the signal is frequently
+ * propagated. The child waiter thread is a workaround for this problem.
+ * The SIGCHLD signal is always blocked (in all threads), and the child
+ * waiter thread fetches the signal by a call to sigwait(). See
+ * child_waiter().
+ */
+
+typedef struct ErtsSysReportExit_ ErtsSysReportExit;
+struct ErtsSysReportExit_ {
+ ErtsSysReportExit *next;
+ Eterm port;
+ int pid;
+ int ifd;
+ int ofd;
+#if CHLDWTHR && !defined(ERTS_SMP)
+ int status;
+#endif
+};
+
+static ErtsSysReportExit *report_exit_list;
+#if CHLDWTHR && !defined(ERTS_SMP)
+static ErtsSysReportExit *report_exit_transit_list;
+#endif
+
+extern int check_async_ready(void);
+extern int driver_interrupt(int, int);
+/*EXTERN_FUNCTION(void, increment_time, (int));*/
+/*EXTERN_FUNCTION(int, next_time, (_VOID_));*/
+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"
+#elif defined(PURIFY)
+#define ERL_BUILD_TYPE_MARKER ".purify"
+#elif defined(QUANTIFY)
+#define ERL_BUILD_TYPE_MARKER ".quantify"
+#elif defined(PURECOV)
+#define ERL_BUILD_TYPE_MARKER ".purecov"
+#elif defined(VALGRIND)
+#define ERL_BUILD_TYPE_MARKER ".valgrind"
+#else /* opt */
+#define ERL_BUILD_TYPE_MARKER
+#endif
+
+#define CHILD_SETUP_PROG_NAME "child_setup" ERL_BUILD_TYPE_MARKER
+#if !DISABLE_VFORK
+static char *child_setup_prog;
+#endif
+
+#ifdef DEBUG
+static int debug_log = 0;
+#endif
+
+#ifdef ERTS_SMP
+erts_smp_atomic_t erts_got_sigusr1;
+#define ERTS_SET_GOT_SIGUSR1 \
+ erts_smp_atomic_set(&erts_got_sigusr1, 1)
+#define ERTS_UNSET_GOT_SIGUSR1 \
+ erts_smp_atomic_set(&erts_got_sigusr1, 0)
+static erts_smp_atomic_t have_prepared_crash_dump;
+#define ERTS_PREPARED_CRASH_DUMP \
+ ((int) erts_smp_atomic_xchg(&have_prepared_crash_dump, 1))
+#else
+volatile int erts_got_sigusr1;
+#define ERTS_SET_GOT_SIGUSR1 (erts_got_sigusr1 = 1)
+#define ERTS_UNSET_GOT_SIGUSR1 (erts_got_sigusr1 = 0)
+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)
+static void smp_sig_notify(char c);
+static int sig_notify_fds[2] = {-1, -1};
+#elif defined(USE_THREADS)
+static int async_fd[2];
+#endif
+
+#if CHLDWTHR || defined(ERTS_SMP)
+erts_mtx_t chld_stat_mtx;
+#endif
+#if CHLDWTHR
+static erts_tid_t child_waiter_tid;
+/* chld_stat_mtx is used to protect against concurrent accesses
+ of the driver_data fields pid, alive, and status. */
+erts_cnd_t chld_stat_cnd;
+static long children_alive;
+#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
+#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
+#define CHLD_STAT_WAIT erts_cnd_wait(&chld_stat_cnd, &chld_stat_mtx)
+#define CHLD_STAT_SIGNAL erts_cnd_signal(&chld_stat_cnd)
+#elif defined(ERTS_SMP) /* ------------------------------------------------- */
+#define CHLD_STAT_LOCK erts_mtx_lock(&chld_stat_mtx)
+#define CHLD_STAT_UNLOCK erts_mtx_unlock(&chld_stat_mtx)
+
+#else /* ------------------------------------------------------------------- */
+#define CHLD_STAT_LOCK
+#define CHLD_STAT_UNLOCK
+static volatile int children_died;
+#endif
+
+
+static struct fd_data {
+ char pbuf[4]; /* hold partial packet bytes */
+ int psz; /* size of pbuf */
+ char *buf;
+ char *cpos;
+ int sz;
+ int remain; /* for input on fd */
+} *fd_data; /* indexed by fd */
+
+/* static FUNCTION(int, write_fill, (int, char*, int)); unused? */
+static FUNCTION(void, note_child_death, (int, int));
+
+#if CHLDWTHR
+static FUNCTION(void *, child_waiter, (void *));
+#endif
+
+/********************* 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_atomic_t erts_break_requested;
+#define ERTS_SET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 1)
+#define ERTS_UNSET_BREAK_REQUESTED \
+ erts_smp_atomic_set(&erts_break_requested, (long) 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;
+
+#ifdef ERTS_ENABLE_KERNEL_POLL
+
+int erts_use_kernel_poll = 0;
+
+struct {
+ int (*select)(ErlDrvPort, ErlDrvEvent, int, int);
+ int (*event)(ErlDrvPort, ErlDrvEvent, ErlDrvEventData);
+ void (*check_io_interrupt)(int);
+ void (*check_io_interrupt_tmd)(int, long);
+ void (*check_io)(int);
+ Uint (*size)(void);
+ Eterm (*info)(void *);
+ int (*check_io_debug)(void);
+} io_func = {0};
+
+
+int
+driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)
+{
+ return (*io_func.select)(port, event, mode, on);
+}
+
+int
+driver_event(ErlDrvPort port, ErlDrvEvent event, ErlDrvEventData event_data)
+{
+ return (*io_func.event)(port, event, event_data);
+}
+
+Eterm erts_check_io_info(void *p)
+{
+ return (*io_func.info)(p);
+}
+
+int
+erts_check_io_debug(void)
+{
+ return (*io_func.check_io_debug)();
+}
+
+
+static void
+init_check_io(void)
+{
+ if (erts_use_kernel_poll) {
+ io_func.select = driver_select_kp;
+ io_func.event = driver_event_kp;
+ io_func.check_io_interrupt = erts_check_io_interrupt_kp;
+ io_func.check_io_interrupt_tmd = erts_check_io_interrupt_timed_kp;
+ io_func.check_io = erts_check_io_kp;
+ io_func.size = erts_check_io_size_kp;
+ io_func.info = erts_check_io_info_kp;
+ io_func.check_io_debug = erts_check_io_debug_kp;
+ erts_init_check_io_kp();
+ max_files = erts_check_io_max_files_kp();
+ }
+ else {
+ io_func.select = driver_select_nkp;
+ io_func.event = driver_event_nkp;
+ io_func.check_io_interrupt = erts_check_io_interrupt_nkp;
+ io_func.check_io_interrupt_tmd = erts_check_io_interrupt_timed_nkp;
+ io_func.check_io = erts_check_io_nkp;
+ io_func.size = erts_check_io_size_nkp;
+ io_func.info = erts_check_io_info_nkp;
+ io_func.check_io_debug = erts_check_io_debug_nkp;
+ erts_init_check_io_nkp();
+ max_files = erts_check_io_max_files_nkp();
+ }
+}
+
+#define ERTS_CHK_IO_INTR (*io_func.check_io_interrupt)
+#define ERTS_CHK_IO_INTR_TMD (*io_func.check_io_interrupt_tmd)
+#define ERTS_CHK_IO (*io_func.check_io)
+#define ERTS_CHK_IO_SZ (*io_func.size)
+
+#else /* !ERTS_ENABLE_KERNEL_POLL */
+
+static void
+init_check_io(void)
+{
+ erts_init_check_io();
+ max_files = erts_check_io_max_files();
+}
+
+#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
+
+#endif
+
+#ifdef ERTS_SMP
+void
+erts_sys_schedule_interrupt(int set)
+{
+ ERTS_CHK_IO_INTR(set);
+}
+
+void
+erts_sys_schedule_interrupt_timed(int set, long 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(&sys_misc_mem_sz);
+ return res;
+}
+
+/*
+ * reset the terminal to the original settings on exit
+ */
+void sys_tty_reset(void)
+{
+ if (using_oldshell && !replace_intr) {
+ SET_BLOCKING(0);
+ }
+ else if (isatty(0)) {
+ tcsetattr(0,TCSANOW,&initial_tty_mode);
+ }
+}
+
+#ifdef __tile__
+/* Direct malloc to spread memory around the caches of multiple tiles. */
+#include <malloc.h>
+MALLOC_USE_HASH(1);
+#endif
+
+#ifdef USE_THREADS
+static void *ethr_internal_alloc(size_t size)
+{
+ return erts_alloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, (Uint) size);
+}
+static void *ethr_internal_realloc(void *ptr, size_t size)
+{
+ return erts_realloc_fnf(ERTS_ALC_T_ETHR_INTERNAL, ptr, (Uint) size);
+}
+static void ethr_internal_free(void *ptr)
+{
+ erts_free(ERTS_ALC_T_ETHR_INTERNAL, ptr);
+}
+
+#ifdef ERTS_THR_HAVE_SIG_FUNCS
+/*
+ * Child thread inherits parents signal mask at creation. In order to
+ * guarantee that the main thread will receive all SIGINT, SIGCHLD, and
+ * SIGUSR1 signals sent to the process, we block these signals in the
+ * parent thread when creating a new thread.
+ */
+
+static sigset_t thr_create_sigmask;
+
+#endif /* #ifdef ERTS_THR_HAVE_SIG_FUNCS */
+
+typedef struct {
+#ifdef ERTS_THR_HAVE_SIG_FUNCS
+ sigset_t saved_sigmask;
+#endif
+ int unbind_child;
+} 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;
+ ErtsSchedulerData *esdp;
+
+ tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t));
+
+#ifdef ERTS_THR_HAVE_SIG_FUNCS
+ erts_thr_sigmask(SIG_BLOCK, &thr_create_sigmask, &tcdp->saved_sigmask);
+#endif
+ esdp = erts_get_scheduler_data();
+ tcdp->unbind_child = esdp && erts_is_scheduler_bound(esdp);
+
+ 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;
+
+#ifdef ERTS_THR_HAVE_SIG_FUNCS
+ /* Restore signalmask... */
+ erts_thr_sigmask(SIG_SETMASK, &tcdp->saved_sigmask, NULL);
+#endif
+
+ 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;
+
+#ifndef NO_FPE_SIGNALS
+ /*
+ * We do not want fp exeptions in other threads than the
+ * scheduler threads. We enable fpe explicitly in the scheduler
+ * threads after this.
+ */
+ erts_thread_disable_fpe();
+#endif
+
+ if (tcdp->unbind_child) {
+ erts_smp_rwmtx_rlock(&erts_cpu_bind_rwmtx);
+ erts_unbind_from_cpu(erts_cpuinfo);
+ erts_smp_rwmtx_runlock(&erts_cpu_bind_rwmtx);
+ }
+
+}
+
+#endif /* #ifdef USE_THREADS */
+
+void
+erts_sys_pre_init(void)
+{
+ erts_printf_add_cr_to_stdout = 1;
+ erts_printf_add_cr_to_stderr = 1;
+#ifdef USE_THREADS
+ {
+ erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
+ eid.alloc = ethr_internal_alloc;
+ eid.realloc = ethr_internal_realloc;
+ eid.free = ethr_internal_free;
+
+ 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,
+
+#ifdef ERTS_THR_HAVE_SIG_FUNCS
+ sigemptyset(&thr_create_sigmask);
+ sigaddset(&thr_create_sigmask, SIGINT); /* block interrupt */
+ sigaddset(&thr_create_sigmask, SIGCHLD); /* block child signals */
+ sigaddset(&thr_create_sigmask, SIGUSR1); /* block user defined signal */
+#endif
+
+ erts_thr_init(&eid);
+
+ report_exit_list = NULL;
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ erts_lcnt_init();
+#endif
+
+#if CHLDWTHR || defined(ERTS_SMP)
+ erts_mtx_init(&chld_stat_mtx, "child_status");
+#endif
+#if CHLDWTHR
+#ifndef ERTS_SMP
+ report_exit_transit_list = NULL;
+#endif
+ erts_cnd_init(&chld_stat_cnd);
+ children_alive = 0;
+#endif
+ }
+#ifdef ERTS_SMP
+ erts_smp_atomic_init(&erts_break_requested, 0);
+ erts_smp_atomic_init(&erts_got_sigusr1, 0);
+ erts_smp_atomic_init(&have_prepared_crash_dump, 0);
+#else
+ erts_break_requested = 0;
+ erts_got_sigusr1 = 0;
+ have_prepared_crash_dump = 0;
+#endif
+#if !CHLDWTHR && !defined(ERTS_SMP)
+ children_died = 0;
+#endif
+#endif /* USE_THREADS */
+ erts_smp_atomic_init(&sys_misc_mem_sz, 0);
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+void
+erl_sys_init(void)
+{
+#if !DISABLE_VFORK
+ int res;
+ char bindir[MAXPATHLEN];
+ size_t bindirsz = sizeof(bindir);
+ Uint csp_path_sz;
+
+ res = erts_sys_getenv("BINDIR", bindir, &bindirsz);
+ if (res != 0) {
+ if (res < 0)
+ erl_exit(-1,
+ "Environment variable BINDIR is not set\n");
+ if (res > 0)
+ erl_exit(-1,
+ "Value of environment variable BINDIR is too large\n");
+ }
+ if (bindir[0] != DIR_SEPARATOR_CHAR)
+ erl_exit(-1,
+ "Environment variable BINDIR does not contain an"
+ " absolute path\n");
+ csp_path_sz = (strlen(bindir)
+ + 1 /* DIR_SEPARATOR_CHAR */
+ + sizeof(CHILD_SETUP_PROG_NAME)
+ + 1);
+ child_setup_prog = erts_alloc(ERTS_ALC_T_CS_PROG_PATH, csp_path_sz);
+ erts_smp_atomic_add(&sys_misc_mem_sz, csp_path_sz);
+ sprintf(child_setup_prog,
+ "%s%c%s",
+ bindir,
+ DIR_SEPARATOR_CHAR,
+ CHILD_SETUP_PROG_NAME);
+#endif
+
+#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() */
+}
+
+/* signal handling */
+
+#ifdef SIG_SIGSET /* Old SysV */
+RETSIGTYPE (*sys_sigset(sig, func))()
+int sig;
+RETSIGTYPE (*func)();
+{
+ return(sigset(sig, func));
+}
+void sys_sigblock(int sig)
+{
+ sighold(sig);
+}
+void sys_sigrelease(int sig)
+{
+ sigrelse(sig);
+}
+#else /* !SIG_SIGSET */
+#ifdef SIG_SIGNAL /* Old BSD */
+RETSIGTYPE (*sys_sigset(sig, func))(int, int)
+int sig;
+RETSIGTYPE (*func)();
+{
+ return(signal(sig, func));
+}
+sys_sigblock(int sig)
+{
+ sigblock(sig);
+}
+sys_sigrelease(int sig)
+{
+ sigsetmask(sigblock(0) & ~sigmask(sig));
+}
+#else /* !SIG_SIGNAL */ /* The True Way - POSIX!:-) */
+RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int)
+{
+ struct sigaction act, oact;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = func;
+ sigaction(sig, &act, &oact);
+ return(oact.sa_handler);
+}
+
+#ifdef USE_THREADS
+#undef sigprocmask
+#define sigprocmask erts_thr_sigmask
+#endif
+
+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);
+}
+#endif /* !SIG_SIGNAL */
+#endif /* !SIG_SIGSET */
+
+#if (0) /* not used? -- gordon */
+static void (*break_func)();
+static RETSIGTYPE break_handler(int sig)
+{
+#ifdef QNX
+ /* Turn off SIGCHLD during break processing */
+ sys_sigblock(SIGCHLD);
+#endif
+ (*break_func)();
+#ifdef QNX
+ sys_sigrelease(SIGCHLD);
+#endif
+}
+#endif /* 0 */
+
+static ERTS_INLINE void
+prepare_crash_dump(void)
+{
+ int i, max;
+ char env[21]; /* enough to hold any 64-bit integer */
+ size_t envsz;
+
+ if (ERTS_PREPARED_CRASH_DUMP)
+ return; /* We have already been called */
+
+ /* 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++) {
+#if defined(ERTS_SMP)
+ /* We don't want to close the signal notification pipe... */
+ if (i == sig_notify_fds[0] || i == sig_notify_fds[1])
+ continue;
+#elif defined(USE_THREADS)
+ /* We don't want to close the async notification pipe... */
+ if (i == async_fd[0] || i == async_fd[1])
+ continue;
+#endif
+ 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;
+ }
+ nice(nice_val);
+ }
+
+ envsz = sizeof(env);
+ i = erts_sys_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz);
+ if (i >= 0) {
+ unsigned sec;
+ sec = (unsigned) i != 0 ? 0 : atoi(env);
+ alarm(sec);
+ }
+
+}
+
+void
+erts_sys_prepare_crash_dump(void)
+{
+ prepare_crash_dump();
+}
+
+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_INTR(1); /* Make sure we don't sleep in poll */
+}
+
+/* set up signal handlers for break and quit */
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE request_break(void)
+#else
+static RETSIGTYPE request_break(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('I');
+#else
+ break_requested();
+#endif
+}
+
+static ERTS_INLINE void
+sigusr1_exit(void)
+{
+ /* We do this at interrupt level, since the main reason for
+ wanting to generate a crash dump in this way is that the emulator
+ is hung somewhere, so it won't be able to poll any flag we set here.
+ */
+ ERTS_SET_GOT_SIGUSR1;
+ prepare_crash_dump();
+ erl_exit(1, "Received SIGUSR1\n");
+}
+
+#ifdef ETHR_UNUSABLE_SIGUSRX
+#warning "Unusable SIGUSR1 & SIGUSR2. Disabling use of these signals"
+#endif
+
+#ifndef ETHR_UNUSABLE_SIGUSRX
+
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE user_signal1(void)
+#else
+static RETSIGTYPE user_signal1(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('1');
+#else
+ sigusr1_exit();
+#endif
+}
+
+#ifdef QUANTIFY
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE user_signal2(void)
+#else
+static RETSIGTYPE user_signal2(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('2');
+#else
+ quantify_save_data();
+#endif
+}
+#endif
+
+#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
+
+static void
+quit_requested(void)
+{
+ erl_exit(ERTS_INTR_EXIT, "");
+}
+
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE do_quit(void)
+#else
+static RETSIGTYPE do_quit(int signum)
+#endif
+{
+#ifdef ERTS_SMP
+ smp_sig_notify('Q');
+#else
+ quit_requested();
+#endif
+}
+
+/* Disable break */
+void erts_set_ignore_break(void) {
+ sys_sigset(SIGINT, SIG_IGN);
+ sys_sigset(SIGQUIT, SIG_IGN);
+ sys_sigset(SIGTSTP, SIG_IGN);
+}
+
+/* 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)
+{
+ sys_sigset(SIGINT, request_break);
+#ifndef ETHR_UNUSABLE_SIGUSRX
+ sys_sigset(SIGUSR1, user_signal1);
+#ifdef QUANTIFY
+ sys_sigset(SIGUSR2, user_signal2);
+#endif
+#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
+ sys_sigset(SIGQUIT, do_quit);
+}
+
+int sys_max_files(void)
+{
+ return(max_files);
+}
+
+static void block_signals(void)
+{
+#if !CHLDWTHR
+ sys_sigblock(SIGCHLD);
+#endif
+#ifndef ERTS_SMP
+ sys_sigblock(SIGINT);
+#ifndef ETHR_UNUSABLE_SIGUSRX
+ sys_sigblock(SIGUSR1);
+#endif
+#endif
+}
+
+static void unblock_signals(void)
+{
+ /* Update erl_child_setup.c if changed */
+#if !CHLDWTHR
+ sys_sigrelease(SIGCHLD);
+#endif
+#ifndef ERTS_SMP
+ sys_sigrelease(SIGINT);
+#ifndef ETHR_UNUSABLE_SIGUSRX
+ sys_sigrelease(SIGUSR1);
+#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
+#endif
+}
+/************************** Time stuff **************************/
+#ifdef HAVE_GETHRTIME
+#ifdef GETHRTIME_WITH_CLOCK_GETTIME
+
+SysHrTime sys_gethrtime(void)
+{
+ struct timespec ts;
+ long long result;
+ if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) {
+ erl_exit(1,"Fatal, could not get clock_monotonic value!, "
+ "errno = %d\n", errno);
+ }
+ result = ((long long) ts.tv_sec) * 1000000000LL +
+ ((long long) ts.tv_nsec);
+ return (SysHrTime) result;
+}
+#endif
+#endif
+
+/************************** OS info *******************************/
+
+/* Used by erlang:info/1. */
+/* (This code was formerly in drv.XXX/XXX_os_drv.c) */
+
+char os_type[] = "unix";
+
+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((int) *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);
+ }
+}
+
+void
+os_flavor(char* namebuf, /* Where to return the name. */
+ unsigned size) /* Size of name buffer. */
+{
+ static int called = 0;
+ static struct utsname uts; /* Information about the system. */
+
+ if (!called) {
+ char* s;
+
+ (void) uname(&uts);
+ called = 1;
+ for (s = uts.sysname; *s; s++) {
+ if (isupper((int) *s)) {
+ *s = tolower((int) *s);
+ }
+ }
+ }
+ strcpy(namebuf, uts.sysname);
+}
+
+void
+os_version(pMajor, pMinor, pBuild)
+int* pMajor; /* Pointer to major version. */
+int* pMinor; /* Pointer to minor version. */
+int* pBuild; /* Pointer to build number. */
+{
+ struct utsname uts; /* Information about the system. */
+ char* release; /* Pointer to the release string:
+ * X.Y or X.Y.Z.
+ */
+
+ (void) uname(&uts);
+ release = uts.release;
+ *pMajor = get_number(&release);
+ *pMinor = get_number(&release);
+ *pBuild = get_number(&release);
+}
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ *state = NULL;
+}
+
+char *getenv_string(GETENV_STATE *state0)
+{
+ char **state = (char **) *state0;
+ char *cp;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+
+ if (state == NULL)
+ state = environ;
+
+ cp = *state++;
+ *state0 = (GETENV_STATE) state;
+
+ return cp;
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ *state = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+
+/************************** Port I/O *******************************/
+
+
+
+/* I. Common stuff */
+
+/*
+ * Decreasing the size of it below 16384 is not allowed.
+ */
+
+/* II. The spawn/fd/vanilla drivers */
+
+#define ERTS_SYS_READ_BUF_SZ (64*1024)
+
+/* This data is shared by these drivers - initialized by spawn_init() */
+static struct driver_data {
+ int port_num, ofd, packet_bytes;
+ ErtsSysReportExit *report_exit;
+ int pid;
+ int alive;
+ int status;
+} *driver_data; /* indexed by fd */
+
+/* Driver interfaces */
+static ErlDrvData spawn_start(ErlDrvPort, char*, SysDriverOpts*);
+static ErlDrvData fd_start(ErlDrvPort, char*, SysDriverOpts*);
+static int fd_control(ErlDrvData, unsigned int, char *, int, char **, int);
+static ErlDrvData vanilla_start(ErlDrvPort, char*, SysDriverOpts*);
+static int spawn_init(void);
+static void fd_stop(ErlDrvData);
+static void stop(ErlDrvData);
+static void ready_input(ErlDrvData, ErlDrvEvent);
+static void ready_output(ErlDrvData, ErlDrvEvent);
+static void output(ErlDrvData, char*, int);
+static void outputv(ErlDrvData, ErlIOVec*);
+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,
+ 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,
+ 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,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 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
+};
+
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+static int async_drv_init(void);
+static ErlDrvData async_drv_start(ErlDrvPort, char*, SysDriverOpts*);
+static void async_drv_stop(ErlDrvData);
+static void async_drv_input(ErlDrvData, ErlDrvEvent);
+
+/* INTERNAL use only */
+
+struct erl_drv_entry async_driver_entry = {
+ async_drv_init,
+ async_drv_start,
+ async_drv_stop,
+ NULL,
+ async_drv_input,
+ NULL,
+ "async",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#endif
+
+/* Handle SIGCHLD signals. */
+#if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
+static RETSIGTYPE onchld(void)
+#else
+static RETSIGTYPE onchld(int signum)
+#endif
+{
+#if CHLDWTHR
+ ASSERT(0); /* We should *never* catch a SIGCHLD signal */
+#elif defined(ERTS_SMP)
+ smp_sig_notify('C');
+#else
+ children_died = 1;
+ ERTS_CHK_IO_INTR(1); /* Make sure we don't sleep in poll */
+#endif
+}
+
+static int set_driver_data(int port_num,
+ int ifd,
+ int ofd,
+ int packet_bytes,
+ int read_write,
+ int exit_status,
+ int pid)
+{
+ ErtsSysReportExit *report_exit;
+
+ if (!exit_status)
+ report_exit = NULL;
+ else {
+ report_exit = erts_alloc(ERTS_ALC_T_PRT_REP_EXIT,
+ sizeof(ErtsSysReportExit));
+ report_exit->next = report_exit_list;
+ report_exit->port = erts_port[port_num].id;
+ report_exit->pid = pid;
+ report_exit->ifd = read_write & DO_READ ? ifd : -1;
+ report_exit->ofd = read_write & DO_WRITE ? ofd : -1;
+#if CHLDWTHR && !defined(ERTS_SMP)
+ report_exit->status = 0;
+#endif
+ report_exit_list = 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;
+ driver_data[ifd].pid = pid;
+ driver_data[ifd].alive = 1;
+ driver_data[ifd].status = 0;
+ if (read_write & DO_WRITE) {
+ driver_data[ifd].ofd = ofd;
+ if (ifd != ofd)
+ driver_data[ofd] = driver_data[ifd]; /* structure copy */
+ } else { /* DO_READ only */
+ driver_data[ifd].ofd = -1;
+ }
+ (void) driver_select(port_num, 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 = report_exit;
+ driver_data[ofd].ofd = ofd;
+ driver_data[ofd].pid = pid;
+ driver_data[ofd].alive = 1;
+ driver_data[ofd].status = 0;
+ return(ofd);
+ }
+}
+
+static int spawn_init()
+{
+ int i;
+#if CHLDWTHR
+ erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER;
+ thr_opts.detached = 0;
+ thr_opts.suggested_stack_size = 0; /* Smallest possible */
+#endif
+
+ sys_sigset(SIGPIPE, SIG_IGN); /* Ignore - we'll handle the write failure */
+ driver_data = (struct driver_data *)
+ erts_alloc(ERTS_ALC_T_DRV_TAB, max_files * sizeof(struct driver_data));
+ erts_smp_atomic_add(&sys_misc_mem_sz,
+ max_files * sizeof(struct driver_data));
+
+ for (i = 0; i < max_files; i++)
+ driver_data[i].pid = -1;
+
+#if CHLDWTHR
+ sys_sigblock(SIGCHLD);
+#endif
+
+ sys_sigset(SIGCHLD, onchld); /* Reap children */
+
+#if CHLDWTHR
+ erts_thr_create(&child_waiter_tid, child_waiter, NULL, &thr_opts);
+#endif
+
+ return 1;
+}
+
+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 prt)
+{
+ fd_data[fd].buf = NULL;
+ fd_data[fd].cpos = NULL;
+ fd_data[fd].remain = 0;
+ fd_data[fd].sz = 0;
+ fd_data[fd].psz = 0;
+}
+
+static char **build_unix_environment(char *block)
+{
+ int i;
+ int j;
+ int len;
+ char *cp;
+ char **cpp;
+ char** old_env;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+
+ cp = block;
+ len = 0;
+ while (*cp != '\0') {
+ cp += strlen(cp) + 1;
+ len++;
+ }
+ old_env = environ;
+ while (*old_env++ != NULL) {
+ len++;
+ }
+
+ cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT,
+ sizeof(char *) * (len+1));
+ if (cpp == NULL) {
+ return NULL;
+ }
+
+ cp = block;
+ len = 0;
+ while (*cp != '\0') {
+ cpp[len] = cp;
+ cp += strlen(cp) + 1;
+ len++;
+ }
+
+ i = len;
+ for (old_env = environ; *old_env; old_env++) {
+ char* old = *old_env;
+
+ for (j = 0; j < len; j++) {
+ char *s, *t;
+
+ s = cpp[j];
+ t = old;
+ while (*s == *t && *s != '=') {
+ s++, t++;
+ }
+ if (*s == '=' && *t == '=') {
+ break;
+ }
+ }
+
+ if (j == len) { /* New version not found */
+ cpp[len++] = old;
+ }
+ }
+
+ for (j = 0; j < i; j++) {
+ if (cpp[j][strlen(cpp[j])-1] == '=') {
+ cpp[j] = cpp[--len];
+ }
+ }
+
+ cpp[len] = NULL;
+ return cpp;
+}
+
+/*
+ [arndt] In most Unix systems, including Solaris 2.5, 'fork' allocates memory
+ in swap space for the child of a 'fork', whereas 'vfork' does not do this.
+ The natural call to use here is therefore 'vfork'. Due to a bug in
+ 'vfork' in Solaris 2.5 (apparently fixed in 2.6), using 'vfork'
+ can be dangerous in what seems to be these circumstances:
+ If the child code under a vfork sets the signal action to SIG_DFL
+ (or SIG_IGN)
+ for any signal which was previously set to a signal handler, the
+ state of the parent is clobbered, so that the later arrival of
+ such a signal yields a sigsegv in the parent. If the signal was
+ not set to a signal handler, but ignored, all seems to work.
+ If you change the forking code below, beware of this.
+ */
+
+static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
+{
+#define CMD_LINE_PREFIX_STR "exec "
+#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1)
+
+ int ifd[2], ofd[2], len, pid, i;
+ char **volatile new_environ; /* volatile since a vfork() then cannot
+ cause 'new_environ' to be clobbered
+ in the parent process. */
+ int saved_errno;
+ long res;
+ char *cmd_line;
+#ifndef QNX
+ int unbind;
+#endif
+#if !DISABLE_VFORK
+ int no_vfork;
+ size_t no_vfork_sz = sizeof(no_vfork);
+
+ no_vfork = (erts_sys_getenv("ERL_NO_VFORK",
+ (char *) &no_vfork,
+ &no_vfork_sz) >= 0);
+#endif
+
+ switch (opts->read_write) {
+ case DO_READ:
+ if (pipe(ifd) < 0)
+ return ERL_DRV_ERROR_ERRNO;
+ if (ifd[0] >= max_files) {
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = EMFILE;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ ofd[1] = -1; /* keep purify happy */
+ break;
+ case DO_WRITE:
+ if (pipe(ofd) < 0) return ERL_DRV_ERROR_ERRNO;
+ if (ofd[1] >= max_files) {
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = EMFILE;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ ifd[0] = -1; /* keep purify happy */
+ break;
+ case DO_READ|DO_WRITE:
+ if (pipe(ifd) < 0) return ERL_DRV_ERROR_ERRNO;
+ errno = EMFILE; /* default for next two conditions */
+ if (ifd[0] >= max_files || pipe(ofd) < 0) {
+ close_pipes(ifd, ofd, DO_READ);
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ if (ofd[1] >= max_files) {
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = EMFILE;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ break;
+ default:
+ ASSERT(0);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+ /* started with spawn_executable, not with spawn */
+ len = strlen(name);
+ cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP, len + 1);
+ if (!cmd_line) {
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ memcpy((void *) cmd_line,(void *) name, len);
+ cmd_line[len] = '\0';
+ if (access(cmd_line,X_OK) != 0) {
+ int save_errno = errno;
+ erts_free(ERTS_ALC_T_TMP, cmd_line);
+ errno = save_errno;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ } else {
+ /* make the string suitable for giving to "sh" */
+ len = strlen(name);
+ cmd_line = (char *) erts_alloc_fnf(ERTS_ALC_T_TMP,
+ CMD_LINE_PREFIX_STR_SZ + len + 1);
+ if (!cmd_line) {
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+ memcpy((void *) cmd_line,
+ (void *) CMD_LINE_PREFIX_STR,
+ CMD_LINE_PREFIX_STR_SZ);
+ memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len);
+ cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0';
+ }
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+
+ if (opts->envir == NULL) {
+ new_environ = environ;
+ } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) {
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ errno = ENOMEM;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+
+#ifndef QNX
+ /* Block child from SIGINT and SIGUSR1. Must be before fork()
+ to be safe. */
+ block_signals();
+
+ CHLD_STAT_LOCK;
+
+ unbind = erts_is_scheduler_bound(NULL);
+ if (unbind)
+ erts_smp_rwmtx_rlock(&erts_cpu_bind_rwmtx);
+
+#if !DISABLE_VFORK
+ /* See fork/vfork discussion before this function. */
+ if (no_vfork) {
+#endif
+
+ DEBUGF(("Using fork\n"));
+ pid = fork();
+
+ if (pid == 0) {
+ /* The child! Setup child... */
+
+ if (unbind && erts_unbind_from_cpu(erts_cpuinfo) != 0)
+ goto child_error;
+
+ /* OBSERVE!
+ * Keep child setup after vfork() (implemented below and in
+ * erl_child_setup.c) up to date if changes are made here.
+ */
+
+ if (opts->use_stdio) {
+ if (opts->read_write & DO_READ) {
+ /* stdout for process */
+ if (dup2(ifd[1], 1) < 0)
+ goto child_error;
+ if(opts->redir_stderr)
+ /* stderr for process */
+ if (dup2(ifd[1], 2) < 0)
+ goto child_error;
+ }
+ if (opts->read_write & DO_WRITE)
+ /* stdin for process */
+ if (dup2(ofd[0], 0) < 0)
+ goto child_error;
+ }
+ else { /* XXX will fail if ofd[0] == 4 (unlikely..) */
+ if (opts->read_write & DO_READ)
+ if (dup2(ifd[1], 4) < 0)
+ goto child_error;
+ if (opts->read_write & DO_WRITE)
+ if (dup2(ofd[0], 3) < 0)
+ goto child_error;
+ }
+
+ for (i = opts->use_stdio ? 3 : 5; i < max_files; i++)
+ (void) close(i);
+
+ if (opts->wd && chdir(opts->wd) < 0)
+ goto child_error;
+
+#if defined(USE_SETPGRP_NOARGS) /* SysV */
+ (void) setpgrp();
+#elif defined(USE_SETPGRP) /* BSD */
+ (void) setpgrp(0, getpid());
+#else /* POSIX */
+ (void) setsid();
+#endif
+
+ unblock_signals();
+
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+ if (opts->argv == NULL) {
+ execle(cmd_line,cmd_line,(char *) NULL, new_environ);
+ } else {
+ if (opts->argv[0] == erts_default_arg0) {
+ opts->argv[0] = cmd_line;
+ }
+ execve(cmd_line, opts->argv, new_environ);
+ if (opts->argv[0] == cmd_line) {
+ opts->argv[0] = erts_default_arg0;
+ }
+ }
+ } else {
+ execle("/bin/sh", "sh", "-c", cmd_line, (char *) NULL, new_environ);
+ }
+ child_error:
+ _exit(1);
+ }
+#if !DISABLE_VFORK
+ }
+ else { /* Use vfork() */
+ char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)*
+ sizeof(char *));
+ char fd_close_range[44]; /* 44 bytes are enough to */
+ char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][44]; /* hold any "%d:%d" string */
+ /* on a 64-bit machine. */
+
+ /* Setup argv[] for the child setup program (implemented in
+ erl_child_setup.c) */
+ i = 0;
+ if (opts->use_stdio) {
+ if (opts->read_write & DO_READ){
+ /* stdout for process */
+ sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 1);
+ if(opts->redir_stderr)
+ /* stderr for process */
+ sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 2);
+ }
+ if (opts->read_write & DO_WRITE)
+ /* stdin for process */
+ sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 0);
+ } else { /* XXX will fail if ofd[0] == 4 (unlikely..) */
+ if (opts->read_write & DO_READ)
+ sprintf(&dup2_op[i++][0], "%d:%d", ifd[1], 4);
+ if (opts->read_write & DO_WRITE)
+ sprintf(&dup2_op[i++][0], "%d:%d", ofd[0], 3);
+ }
+ for (; i < CS_ARGV_NO_OF_DUP2_OPS; i++)
+ strcpy(&dup2_op[i][0], "-");
+ sprintf(fd_close_range, "%d:%d", opts->use_stdio ? 3 : 5, max_files-1);
+
+ cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog;
+ cs_argv[CS_ARGV_WD_IX] = opts->wd ? opts->wd : ".";
+ cs_argv[CS_ARGV_UNBIND_IX]
+ = (unbind ? erts_get_unbind_from_cpu_str(erts_cpuinfo) : "false");
+ cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range;
+ for (i = 0; i < CS_ARGV_NO_OF_DUP2_OPS; i++)
+ cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &dup2_op[i][0];
+
+ if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
+ int num = 0;
+ int j = 0;
+ if (opts->argv != NULL) {
+ for(; opts->argv[num] != NULL; ++num)
+ ;
+ }
+ cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *));
+ cs_argv[CS_ARGV_CMD_IX] = "-";
+ cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line;
+ if (opts->argv != NULL) {
+ for (;opts->argv[j] != NULL; ++j) {
+ if (opts->argv[j] == erts_default_arg0) {
+ cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line;
+ } else {
+ cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts->argv[j];
+ }
+ }
+ }
+ cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL;
+ } else {
+ cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */
+ cs_argv[CS_ARGV_NO_OF_ARGS] = NULL;
+ }
+ DEBUGF(("Using vfork\n"));
+ pid = vfork();
+
+ if (pid == 0) {
+ /* The child! */
+
+ /* Observe!
+ * OTP-4389: The child setup program (implemented in
+ * erl_child_setup.c) will perform the necessary setup of the
+ * child before it execs to the user program. This because
+ * vfork() only allow an *immediate* execve() or _exit() in the
+ * child.
+ */
+ execve(child_setup_prog, cs_argv, new_environ);
+ _exit(1);
+ }
+ erts_free(ERTS_ALC_T_TMP,cs_argv);
+ }
+#endif
+
+ if (unbind)
+ erts_smp_rwmtx_runlock(&erts_cpu_bind_rwmtx);
+
+ if (pid == -1) {
+ saved_errno = errno;
+ CHLD_STAT_UNLOCK;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ unblock_signals();
+ close_pipes(ifd, ofd, opts->read_write);
+ errno = saved_errno;
+ return ERL_DRV_ERROR_ERRNO;
+ }
+#else /* QNX */
+ if (opts->use_stdio) {
+ if (opts->read_write & DO_READ)
+ qnx_spawn_options.iov[1] = ifd[1]; /* stdout for process */
+ if (opts->read_write & DO_WRITE)
+ qnx_spawn_options.iov[0] = ofd[0]; /* stdin for process */
+ }
+ else {
+ if (opts->read_write & DO_READ)
+ qnx_spawn_options.iov[4] = ifd[1];
+ if (opts->read_write & DO_WRITE)
+ qnx_spawn_options.iov[3] = ofd[0];
+ }
+ /* Close fds on exec */
+ for (i = 3; i < max_files; i++)
+ fcntl(i, F_SETFD, 1);
+
+ qnx_spawn_options.flags = _SPAWN_SETSID;
+ if ((pid = spawnl(P_NOWAIT, "/bin/sh", "/bin/sh", "-c", cmd_line,
+ (char *) 0)) < 0) {
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+ reset_qnx_spawn();
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ close_pipes(ifd, ofd, opts->read_write);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ reset_qnx_spawn();
+#endif /* QNX */
+
+ erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
+
+ if (new_environ != environ)
+ erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
+
+ if (opts->read_write & DO_READ)
+ (void) close(ifd[1]);
+ if (opts->read_write & DO_WRITE)
+ (void) close(ofd[0]);
+
+ if (opts->read_write & DO_READ) {
+ SET_NONBLOCKING(ifd[0]);
+ init_fd_data(ifd[0], port_num);
+ }
+ if (opts->read_write & DO_WRITE) {
+ SET_NONBLOCKING(ofd[1]);
+ init_fd_data(ofd[1], port_num);
+ }
+
+ res = set_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
+ opts->read_write, opts->exit_status, pid);
+ /* Don't unblock SIGCHLD until now, since the call above must
+ first complete putting away the info about our new subprocess. */
+ unblock_signals();
+
+#if CHLDWTHR
+ ASSERT(children_alive >= 0);
+
+ if (!(children_alive++))
+ CHLD_STAT_SIGNAL; /* Wake up child waiter thread if no children
+ was alive before we fork()ed ... */
+#endif
+ /* Don't unlock chld_stat_mtx until now of the same reason as above */
+ CHLD_STAT_UNLOCK;
+
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+
+ return (ErlDrvData)res;
+#undef CMD_LINE_PREFIX_STR
+#undef CMD_LINE_PREFIX_STR_SZ
+}
+
+#ifdef QNX
+static reset_qnx_spawn()
+{
+ int i;
+
+ /* Reset qnx_spawn_options */
+ qnx_spawn_options.flags = 0;
+ qnx_spawn_options.iov[0] = 0xff;
+ qnx_spawn_options.iov[1] = 0xff;
+ qnx_spawn_options.iov[2] = 0xff;
+ qnx_spawn_options.iov[3] = 0xff;
+}
+#endif
+
+#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 int fd_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, int len,
+ char **rbuf, int rlen)
+{
+ int fd = (int)(long)drv_data;
+ char resbuff[2*sizeof(Uint32)];
+ switch (command) {
+ case FD_CTRL_OP_GET_WINSIZE:
+ {
+ Uint32 w,h;
+ if (fd_get_window_size(fd,&w,&h))
+ return 0;
+ memcpy(resbuff,&w,sizeof(Uint32));
+ memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
+ }
+ break;
+ default:
+ return 0;
+ }
+ if (rlen < 2*sizeof(Uint32)) {
+ *rbuf = driver_alloc(2*sizeof(Uint32));
+ }
+ memcpy(*rbuf,resbuff,2*sizeof(Uint32));
+ return 2*sizeof(Uint32);
+}
+
+static ErlDrvData fd_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+ ErlDrvData res;
+
+ if (((opts->read_write & DO_READ) && opts->ifd >= max_files) ||
+ ((opts->read_write & DO_WRITE) && opts->ofd >= max_files))
+ return ERL_DRV_ERROR_GENERAL;
+
+ /*
+ * Historical:
+ *
+ * "Note about nonblocking I/O.
+ *
+ * At least on Solaris, setting the write end of a TTY to nonblocking,
+ * will set the input end to nonblocking as well (and vice-versa).
+ * If erl is run in a pipeline like this: cat | erl
+ * the input end of the TTY will be the standard input of cat.
+ * And cat is not prepared to handle nonblocking I/O."
+ *
+ * Actually, the reason for this is not that the tty itself gets set
+ * in non-blocking mode, but that the "input end" (cat's stdin) and
+ * the "output end" (erlang's stdout) are typically the "same" file
+ * descriptor, dup()'ed from a single fd by one of this process'
+ * ancestors.
+ *
+ * The workaround for this problem used to be a rather bad kludge,
+ * interposing an extra process ("internal cat") between erlang's
+ * stdout and the original stdout, allowing erlang to set its stdout
+ * in non-blocking mode without affecting the stdin of the preceding
+ * process in the pipeline - and being a kludge, it caused all kinds
+ * of weird problems.
+ *
+ * So, this is the current logic:
+ *
+ * The only reason to set non-blocking mode on the output fd at all is
+ * if it's something that can cause a write() to block, of course,
+ * i.e. primarily if it points to a tty, socket, pipe, or fifo.
+ *
+ * If we don't set non-blocking mode when we "should" have, and output
+ * becomes blocked, the entire runtime system will be suspended - this
+ * is normally bad of course, and can happen fairly "easily" - e.g. user
+ * hits ^S on tty - but doesn't necessarily happen.
+ *
+ * If we do set non-blocking mode when we "shouldn't" have, the runtime
+ * system will end up seeing EOF on the input fd (due to the preceding
+ * process dying), which typically will cause the entire runtime system
+ * to terminate immediately (due to whatever erlang process is seeing
+ * the EOF taking it as a signal to halt the system). This is *very* bad.
+ *
+ * I.e. we should take a conservative approach, and only set non-
+ * blocking mode when we a) need to, and b) are reasonably certain
+ * that it won't be a problem. And as in the example above, the problem
+ * occurs when input fd and output fd point to different "things".
+ *
+ * However, determining that they are not just the same "type" of
+ * "thing", but actually the same instance of that type of thing, is
+ * unreasonably complex in many/most cases.
+ *
+ * Also, with pipes, sockets, and fifos it's far from obvious that the
+ * user *wants* non-blocking output: If you're running erlang inside
+ * some complex pipeline, you're probably not running a real-time system
+ * that must never stop, but rather *want* it to suspend if the output
+ * channel is "full".
+ *
+ * So, the bottom line: We will only set the output fd non-blocking if
+ * it points to a tty, and either a) the input fd also points to a tty,
+ * or b) we can make sure that setting the output fd non-blocking
+ * doesn't interfere with someone else's input, via a somewhat milder
+ * kludge than the above.
+ *
+ * Also keep in mind that while this code is almost exclusively run as
+ * a result of an erlang open_port({fd,0,1}, ...), that isn't the only
+ * case - it can be called with any old pre-existing file descriptors,
+ * the relations between which (if they're even two) we can only guess
+ * at - still, we try our best...
+ */
+
+ 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);
+
+ /* If we don't have a read end, all bets are off - no non-blocking. */
+ if (opts->read_write & DO_READ) {
+
+ if (isatty(opts->ofd)) { /* output fd is a tty:-) */
+
+ if (isatty(opts->ifd)) { /* input fd is also a tty */
+
+ /* To really do this "right", we should also check that
+ input and output fd point to the *same* tty - but
+ this seems like overkill; ttyname() isn't for free,
+ and this is a very common case - and it's hard to
+ imagine a scenario where setting non-blocking mode
+ here would cause problems - go ahead and do it. */
+
+ SET_NONBLOCKING(opts->ofd);
+
+ } else { /* output fd is a tty, input fd isn't */
+
+ /* This is a "problem case", but also common (see the
+ example above) - i.e. it makes sense to try a bit
+ harder before giving up on non-blocking mode: Try to
+ re-open the tty that the output fd points to, and if
+ successful replace the original one with the "new" fd
+ obtained this way, and set *that* one in non-blocking
+ mode. (Yes, this is a kludge.)
+
+ However, re-opening the tty may fail in a couple of
+ (unusual) cases:
+
+ 1) The name of the tty (or an equivalent one, i.e.
+ same major/minor number) can't be found, because
+ it actually lives somewhere other than /dev (or
+ wherever ttyname() looks for it), and isn't
+ equivalent to any of those that do live in the
+ "standard" place - this should be *very* unusual.
+
+ 2) Permissions on the tty don't allow us to open it -
+ it's perfectly possible to have an fd open to an
+ object whose permissions wouldn't allow us to open
+ it. This is not as unusual as it sounds, one case
+ is if the user has su'ed to someone else (not
+ root) - we have a read/write fd open to the tty
+ (because it has been inherited all the way down
+ here), but we have neither read nor write
+ permission for the tty.
+
+ In these cases, we finally give up, and don't set the
+ output fd in non-blocking mode. */
+
+ char *tty;
+ int nfd;
+
+ if ((tty = ttyname(opts->ofd)) != NULL &&
+ (nfd = open(tty, O_WRONLY)) != -1) {
+ dup2(nfd, opts->ofd);
+ close(nfd);
+ SET_NONBLOCKING(opts->ofd);
+ }
+ }
+ }
+ }
+ }
+ CHLD_STAT_LOCK;
+ res = (ErlDrvData)(long)set_driver_data(port_num, opts->ifd, opts->ofd,
+ opts->packet_bytes,
+ opts->read_write, 0, -1);
+ CHLD_STAT_UNLOCK;
+ return res;
+}
+
+static void clear_fd_data(int fd)
+{
+ if (fd_data[fd].sz > 0) {
+ erts_free(ERTS_ALC_T_FD_ENTRY_BUF, (void *) fd_data[fd].buf);
+ ASSERT(erts_smp_atomic_read(&sys_misc_mem_sz) >= fd_data[fd].sz);
+ erts_smp_atomic_add(&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(int prt, int fd)
+{
+ driver_select(prt,fd,DO_READ|DO_WRITE,0);
+ clear_fd_data(fd);
+ SET_BLOCKING(fd);
+}
+
+static void fd_stop(ErlDrvData fd) /* Does not close the fds */
+{
+ int ofd;
+
+ nbio_stop_fd(driver_data[(int)(long)fd].port_num, (int)(long)fd);
+ ofd = driver_data[(int)(long)fd].ofd;
+ if (ofd != (int)(long)fd && ofd != -1)
+ nbio_stop_fd(driver_data[(int)(long)fd].port_num, (int)(long)ofd);
+}
+
+static ErlDrvData vanilla_start(ErlDrvPort port_num, char* name,
+ SysDriverOpts* opts)
+{
+ int flags, fd;
+ ErlDrvData res;
+
+ 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)
+ return ERL_DRV_ERROR_GENERAL;
+ if (fd >= max_files) {
+ close(fd);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ SET_NONBLOCKING(fd);
+ init_fd_data(fd, port_num);
+
+ CHLD_STAT_LOCK;
+ res = (ErlDrvData)(long)set_driver_data(port_num, fd, fd,
+ opts->packet_bytes,
+ opts->read_write, 0, -1);
+ CHLD_STAT_UNLOCK;
+ return res;
+}
+
+/* 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 fd)
+{
+ int prt, ofd;
+
+ prt = driver_data[(int)(long)fd].port_num;
+ nbio_stop_fd(prt, (int)(long)fd);
+
+ ofd = driver_data[(int)(long)fd].ofd;
+ if (ofd != (int)(long)fd && (int)(long)ofd != -1)
+ nbio_stop_fd(prt, ofd);
+ else
+ ofd = -1;
+
+ CHLD_STAT_LOCK;
+
+ /* Mark as unused. Maybe resetting the 'port_num' slot is better? */
+ driver_data[(int)(long)fd].pid = -1;
+
+ CHLD_STAT_UNLOCK;
+
+ /* SMP note: Close has to be last thing done (open file descriptors work
+ as locks on driver_data[] entries) */
+ driver_select(prt, (int)(long)fd, ERL_DRV_USE, 0); /* close(fd); */
+ if (ofd >= 0) {
+ driver_select(prt, (int)(long)ofd, ERL_DRV_USE, 0); /* close(ofd); */
+ }
+}
+
+static void outputv(ErlDrvData e, ErlIOVec* ev)
+{
+ int fd = (int)(long)e;
+ int ix = driver_data[fd].port_num;
+ int pb = driver_data[fd].packet_bytes;
+ int ofd = driver_data[fd].ofd;
+ int n;
+ int sz;
+ char lb[4];
+ char* lbp;
+ int len = ev->size;
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ put_int32(len, lb);
+ lbp = lb + (4-pb);
+
+ ev->iov[0].iov_base = lbp;
+ ev->iov[0].iov_len = pb;
+ ev->size += pb;
+ if ((sz = driver_sizeq(ix)) > 0) {
+ driver_enqv(ix, ev, 0);
+ if (sz + ev->size >= (1 << 13))
+ set_busy_port(ix, 1);
+ }
+ else {
+ int vsize = ev->vsize > MAX_VSIZE ? MAX_VSIZE : ev->vsize;
+
+ n = writev(ofd, (const void *) (ev->iov), vsize);
+ if (n == ev->size)
+ return; /* 0;*/
+ if (n < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
+ driver_failure_posix(ix, errno);
+ return; /* -1;*/
+ }
+ n = 0;
+ }
+ driver_enqv(ix, ev, n); /* n is the skip value */
+ driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ }
+ /* return 0;*/
+}
+
+
+static void output(ErlDrvData e, char* buf, int len)
+{
+ int fd = (int)(long)e;
+ int ix = driver_data[fd].port_num;
+ int pb = driver_data[fd].packet_bytes;
+ int ofd = driver_data[fd].ofd;
+ int n;
+ int sz;
+ char lb[4];
+ char* lbp;
+ struct iovec iv[2];
+
+ /* (len > ((unsigned long)-1 >> (4-pb)*8)) */
+ if (((pb == 2) && (len > 0xffff)) || (pb == 1 && len > 0xff)) {
+ driver_failure_posix(ix, EINVAL);
+ return; /* -1; */
+ }
+ put_int32(len, lb);
+ lbp = lb + (4-pb);
+
+ if ((sz = driver_sizeq(ix)) > 0) {
+ driver_enq(ix, lbp, pb);
+ driver_enq(ix, buf, len);
+ if (sz + len + pb >= (1 << 13))
+ set_busy_port(ix, 1);
+ }
+ else {
+ iv[0].iov_base = lbp;
+ iv[0].iov_len = pb; /* should work for pb=0 */
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+ n = writev(ofd, iv, 2);
+ if (n == pb+len)
+ return; /* 0; */
+ if (n < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK)) {
+ driver_failure_posix(ix, errno);
+ return; /* -1; */
+ }
+ n = 0;
+ }
+ if (n < pb) {
+ driver_enq(ix, lbp+n, pb-n);
+ driver_enq(ix, buf, len);
+ }
+ else {
+ n -= pb;
+ driver_enq(ix, buf+n, len-n);
+ }
+ driver_select(ix, ofd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
+ }
+ return; /* 0; */
+}
+
+static int port_inp_failure(int port_num, int ready_fd, int res)
+ /* Result: 0 (eof) or -1 (error) */
+{
+ int err = errno;
+
+ ASSERT(res <= 0);
+ (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) {
+ CHLD_STAT_LOCK;
+
+ if (driver_data[ready_fd].alive) {
+ /*
+ * We have eof and want to report exit status, but the process
+ * hasn't exited yet. When it does report_exit_status() will
+ * driver_select() this fd which will make sure that we get
+ * back here with driver_data[ready_fd].alive == 0 and
+ * driver_data[ready_fd].status set.
+ */
+ CHLD_STAT_UNLOCK;
+ return 0;
+ }
+ else {
+ int status = driver_data[ready_fd].status;
+ CHLD_STAT_UNLOCK;
+
+ /* We need not be prepared for stopped/continued processes. */
+ if (WIFSIGNALED(status))
+ status = 128 + WTERMSIG(status);
+ else
+ status = WEXITSTATUS(status);
+
+ driver_report_exit(driver_data[ready_fd].port_num, status);
+ }
+ }
+ driver_failure_eof(port_num);
+ } else {
+ driver_failure_posix(port_num, err);
+ }
+ 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 e, ErlDrvEvent ready_fd)
+{
+ int fd = (int)(long)e;
+ int port_num;
+ int packet_bytes;
+ int res;
+ Uint h;
+
+ port_num = driver_data[fd].port_num;
+ packet_bytes = driver_data[fd].packet_bytes;
+
+ if (packet_bytes == 0) {
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == 0)
+ port_inp_failure(port_num, ready_fd, res);
+ else
+ driver_output(port_num, (char*) read_buf, res);
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+ else if (fd_data[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))
+ 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;
+ }
+ }
+ else if (fd_data[ready_fd].remain == 0) { /* clean fd */
+ byte *read_buf = (byte *) erts_alloc(ERTS_ALC_T_SYS_READ_BUF,
+ ERTS_SYS_READ_BUF_SZ);
+ /* We make one read attempt and see what happens */
+ res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
+ if (res < 0) {
+ if ((errno != EINTR) && (errno != ERRNO_BLOCK))
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res == 0) { /* eof */
+ port_inp_failure(port_num, ready_fd, res);
+ }
+ else if (res < packet_bytes - fd_data[ready_fd].psz) {
+ memcpy(fd_data[ready_fd].pbuf+fd_data[ready_fd].psz,
+ read_buf, res);
+ fd_data[ready_fd].psz += res;
+ }
+ else { /* if (res >= packet_bytes) */
+ unsigned char* cpos = read_buf;
+ int bytes_left = res;
+
+ while (1) {
+ int psz = fd_data[ready_fd].psz;
+ char* pbp = fd_data[ready_fd].pbuf + psz;
+
+ while(bytes_left && (psz < packet_bytes)) {
+ *pbp++ = *cpos++;
+ bytes_left--;
+ psz++;
+ }
+
+ if (psz < packet_bytes) {
+ fd_data[ready_fd].psz = psz;
+ break;
+ }
+ fd_data[ready_fd].psz = 0;
+
+ switch (packet_bytes) {
+ case 1: h = get_int8(fd_data[ready_fd].pbuf); break;
+ case 2: h = get_int16(fd_data[ready_fd].pbuf); break;
+ case 4: h = get_int32(fd_data[ready_fd].pbuf); break;
+ default: ASSERT(0); return; /* -1; */
+ }
+
+ if (h <= (bytes_left)) {
+ driver_output(port_num, (char*) cpos, h);
+ cpos += h;
+ bytes_left -= h;
+ continue;
+ }
+ else { /* The last message we got was split */
+ char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
+ if (!buf) {
+ errno = ENOMEM;
+ port_inp_failure(port_num, ready_fd, -1);
+ }
+ else {
+ erts_smp_atomic_add(&sys_misc_mem_sz, h);
+ 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;
+ }
+ break;
+ }
+ }
+ }
+ erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
+ }
+}
+
+
+/* fd is the drv_data that is returned from the */
+/* initial start routine */
+/* ready_fd is the descriptor that is ready to read */
+
+static void ready_output(ErlDrvData e, ErlDrvEvent ready_fd)
+{
+ int fd = (int)(long)e;
+ int ix = driver_data[fd].port_num;
+ int n;
+ struct iovec* iv;
+ int vsize;
+
+
+ if ((iv = (struct iovec*) driver_peekq(ix, &vsize)) == NULL) {
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ return; /* 0; */
+ }
+ vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize;
+ if ((n = writev(ready_fd, iv, vsize)) > 0) {
+ if (driver_deq(ix, n) == 0)
+ set_busy_port(ix, 0);
+ }
+ else if (n < 0) {
+ if (errno == ERRNO_BLOCK || errno == EINTR)
+ return; /* 0; */
+ else {
+ int res = errno;
+ driver_select(ix, ready_fd, ERL_DRV_WRITE, 0);
+ driver_failure_posix(ix, res);
+ return; /* -1; */
+ }
+ }
+ return; /* 0; */
+}
+
+static void stop_select(ErlDrvEvent fd, void* _)
+{
+ close((int)fd);
+}
+
+/*
+** Async opertation support
+*/
+#if defined(USE_THREADS) && !defined(ERTS_SMP)
+static void
+sys_async_ready_failed(int fd, int r, int err)
+{
+ char buf[120];
+ sprintf(buf, "sys_async_ready(): Fatal error: fd=%d, r=%d, errno=%d\n",
+ fd, r, err);
+ (void) write(2, buf, strlen(buf));
+ abort();
+}
+
+/* called from threads !! */
+void sys_async_ready(int fd)
+{
+ int r;
+ while (1) {
+ r = write(fd, "0", 1); /* signal main thread fd MUST be async_fd[1] */
+ if (r == 1) {
+ DEBUGF(("sys_async_ready(): r = 1\r\n"));
+ break;
+ }
+ if (r < 0 && errno == EINTR) {
+ DEBUGF(("sys_async_ready(): r = %d\r\n", r));
+ continue;
+ }
+ sys_async_ready_failed(fd, r, errno);
+ }
+}
+
+static int async_drv_init(void)
+{
+ async_fd[0] = -1;
+ async_fd[1] = -1;
+ return 0;
+}
+
+static ErlDrvData async_drv_start(ErlDrvPort port_num,
+ char* name, SysDriverOpts* opts)
+{
+ if (async_fd[0] != -1)
+ return ERL_DRV_ERROR_GENERAL;
+ if (pipe(async_fd) < 0)
+ return ERL_DRV_ERROR_GENERAL;
+
+ DEBUGF(("async_drv_start: %d\r\n", port_num));
+
+ SET_NONBLOCKING(async_fd[0]);
+ driver_select(port_num, async_fd[0], ERL_DRV_READ, 1);
+
+ if (init_async(async_fd[1]) < 0)
+ return ERL_DRV_ERROR_GENERAL;
+ return (ErlDrvData)port_num;
+}
+
+static void async_drv_stop(ErlDrvData e)
+{
+ int port_num = (int)(long)e;
+
+ DEBUGF(("async_drv_stop: %d\r\n", port_num));
+
+ exit_async();
+
+ driver_select(port_num, async_fd[0], ERL_DRV_READ, 0);
+
+ close(async_fd[0]);
+ close(async_fd[1]);
+ async_fd[0] = async_fd[1] = -1;
+}
+
+
+static void async_drv_input(ErlDrvData e, ErlDrvEvent fd)
+{
+ char *buf[32];
+ DEBUGF(("async_drv_input\r\n"));
+ while (read((int) fd, (void *) buf, 32) > 0); /* fd MUST be async_fd[0] */
+ check_async_ready(); /* invoke all async_ready */
+}
+#endif
+
+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_block_system(0);
+ /*
+ * NOTE: since we allow gc we are not allowed to lock
+ * (any) process main locks while blocking system...
+ */
+
+ /* 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();
+
+ ERTS_UNSET_BREAK_REQUESTED;
+
+ 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_release_system();
+}
+
+/* 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){
+ pid_t p = getpid();
+ /* Assume the pid is scalar and can rest in an unsigned long... */
+ sprintf(buffer,"%lu",(unsigned long) p);
+}
+
+int
+erts_sys_putenv(char *buffer, int sep_ix)
+{
+ int res;
+ char *env;
+#ifdef HAVE_COPYING_PUTENV
+ env = buffer;
+#else
+ Uint sz = strlen(buffer)+1;
+ env = erts_alloc(ERTS_ALC_T_PUTENV_STR, sz);
+ erts_smp_atomic_add(&sys_misc_mem_sz, sz);
+ strcpy(env,buffer);
+#endif
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = putenv(env);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ char *orig_value;
+ int res;
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ 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;
+ }
+ }
+ 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(&sys_misc_mem_sz,
+ max_files * sizeof(struct fd_data));
+
+#ifdef USE_THREADS
+#ifdef ERTS_SMP
+ if (init_async(-1) < 0)
+ erl_exit(1, "Failed to initialize async-threads\n");
+#else
+ {
+ /* This is speical stuff, starting a driver from the
+ * system routines, but is a nice way of handling stuff
+ * the erlang way
+ */
+ SysDriverOpts dopts;
+ int ret;
+
+ sys_memset((void*)&dopts, 0, sizeof(SysDriverOpts));
+ add_driver_entry(&async_driver_entry);
+ ret = erts_open_driver(NULL, NIL, "async", &dopts, NULL);
+ DEBUGF(("open_driver = %d\n", ret));
+ if (ret < 0)
+ erl_exit(1, "Failed to open async driver\n");
+ erts_port[ret].status |= ERTS_PORT_SFLG_IMMORTAL;
+ }
+#endif
+#endif
+
+}
+
+#if (0) /* unused? */
+static int write_fill(fd, buf, len)
+int fd, len;
+char *buf;
+{
+ int i, done = 0;
+
+ do {
+ if ((i = write(fd, buf+done, len-done)) < 0) {
+ if (errno != EINTR)
+ return (i);
+ i = 0;
+ }
+ done += i;
+ } while (done < len);
+ return (len);
+}
+#endif
+
+extern const char pre_loaded_code[];
+extern Preload pre_loaded[];
+
+void erts_sys_alloc_init(void)
+{
+ elib_ensure_initialized();
+}
+
+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(char* expr, char* file, int line)
+{
+ fflush(stdout);
+ fprintf(stderr, "Assertion failed: %s in %s, line %d\n",
+ expr, file, line);
+ fflush(stderr);
+#if !defined(ERTS_SMP) && 0
+ /* Writing a crashdump from a failed assertion when smp support
+ * is enabled almost a guaranteed deadlocking, don't even bother.
+ *
+ * It could maybe be useful (but I'm not convinced) to write the
+ * crashdump if smp support is disabled...
+ */
+ if (erts_initialized)
+ erl_crash_dump(file, line, "Assertion failed: %s\n", expr);
+#endif
+ abort();
+}
+
+void
+erl_debug(char* fmt, ...)
+{
+ char sbuf[1024]; /* Temporary buffer. */
+ va_list va;
+
+ if (debug_log) {
+ va_start(va, fmt);
+ vsprintf(sbuf, fmt, va);
+ va_end(va);
+ fprintf(stderr, "%s", sbuf);
+ }
+}
+
+#endif /* DEBUG */
+
+static ERTS_INLINE void
+report_exit_status(ErtsSysReportExit *rep, int status)
+{
+ Port *pp;
+#ifdef ERTS_SMP
+ CHLD_STAT_UNLOCK;
+#endif
+ pp = erts_id2port_sflgs(rep->port,
+ NULL,
+ 0,
+ ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
+#ifdef ERTS_SMP
+ CHLD_STAT_LOCK;
+#endif
+ if (pp) {
+ if (rep->ifd >= 0) {
+ driver_data[rep->ifd].alive = 0;
+ driver_data[rep->ifd].status = status;
+ (void) driver_select((ErlDrvPort) internal_port_index(pp->id),
+ rep->ifd,
+ (ERL_DRV_READ|ERL_DRV_USE),
+ 1);
+ }
+ if (rep->ofd >= 0) {
+ driver_data[rep->ofd].alive = 0;
+ driver_data[rep->ofd].status = status;
+ (void) driver_select((ErlDrvPort) internal_port_index(pp->id),
+ rep->ofd,
+ (ERL_DRV_WRITE|ERL_DRV_USE),
+ 1);
+ }
+ erts_port_release(pp);
+ }
+ erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
+}
+
+#if !CHLDWTHR /* ---------------------------------------------------------- */
+
+#define ERTS_REPORT_EXIT_STATUS report_exit_status
+
+static int check_children(void)
+{
+ int res = 0;
+ int pid;
+ int status;
+
+#ifndef ERTS_SMP
+ if (children_died)
+#endif
+ {
+ sys_sigblock(SIGCHLD);
+ CHLD_STAT_LOCK;
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ note_child_death(pid, status);
+#ifndef ERTS_SMP
+ children_died = 0;
+#endif
+ CHLD_STAT_UNLOCK;
+ sys_sigrelease(SIGCHLD);
+ res = 1;
+ }
+ return res;
+}
+
+#ifdef ERTS_SMP
+
+void
+erts_check_children(void)
+{
+ (void) check_children();
+}
+
+#endif
+
+#elif CHLDWTHR && defined(ERTS_SMP) /* ------------------------------------- */
+
+#define ERTS_REPORT_EXIT_STATUS report_exit_status
+
+#define check_children() (0)
+
+
+#else /* CHLDWTHR && !defined(ERTS_SMP) ------------------------------------ */
+
+#define ERTS_REPORT_EXIT_STATUS initiate_report_exit_status
+
+static ERTS_INLINE void
+initiate_report_exit_status(ErtsSysReportExit *rep, int status)
+{
+ rep->next = report_exit_transit_list;
+ rep->status = status;
+ report_exit_transit_list = rep;
+ /*
+ * We need the scheduler thread to call check_children().
+ * If the scheduler thread is sleeping in a poll with a
+ * timeout, we need to wake the scheduler thread. We use the
+ * functionality of the async driver to do this, instead of
+ * implementing yet another driver doing the same thing. A
+ * little bit ugly, but it works...
+ */
+ sys_async_ready(async_fd[1]);
+}
+
+static int check_children(void)
+{
+ int res;
+ ErtsSysReportExit *rep;
+ CHLD_STAT_LOCK;
+ rep = report_exit_transit_list;
+ res = rep != NULL;
+ while (rep) {
+ ErtsSysReportExit *curr_rep = rep;
+ rep = rep->next;
+ report_exit_status(curr_rep, curr_rep->status);
+ }
+ report_exit_transit_list = NULL;
+ CHLD_STAT_UNLOCK;
+ return res;
+}
+
+#endif /* ------------------------------------------------------------------ */
+
+static void note_child_death(int pid, int status)
+{
+ ErtsSysReportExit **repp = &report_exit_list;
+ ErtsSysReportExit *rep = report_exit_list;
+
+ while (rep) {
+ if (pid == rep->pid) {
+ *repp = rep->next;
+ ERTS_REPORT_EXIT_STATUS(rep, status);
+ break;
+ }
+ repp = &rep->next;
+ rep = rep->next;
+ }
+}
+
+#if CHLDWTHR
+
+static void *
+child_waiter(void *unused)
+{
+ int pid;
+ int status;
+
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_set_thread_name("child waiter");
+#endif
+
+ while(1) {
+#ifdef DEBUG
+ int waitpid_errno;
+#endif
+ pid = waitpid(-1, &status, 0);
+#ifdef DEBUG
+ waitpid_errno = errno;
+#endif
+ CHLD_STAT_LOCK;
+ if (pid < 0) {
+ ASSERT(waitpid_errno == ECHILD);
+ }
+ else {
+ children_alive--;
+ ASSERT(children_alive >= 0);
+ note_child_death(pid, status);
+ }
+ while (!children_alive)
+ CHLD_STAT_WAIT; /* Wait for children to wait on... :) */
+ CHLD_STAT_UNLOCK;
+ }
+
+ return NULL;
+}
+
+#endif
+
+/*
+ * Called from schedule() when it runs out of runnable processes,
+ * or when Erlang code has performed INPUT_REDUCTIONS reduction
+ * steps. runnable == 0 iff there are no runnable Erlang processes.
+ */
+void
+erl_sys_schedule(int runnable)
+{
+#ifdef ERTS_SMP
+ ERTS_CHK_IO(!runnable);
+ ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
+#else
+ ERTS_CHK_IO_INTR(0);
+ if (runnable) {
+ ERTS_CHK_IO(0); /* Poll for I/O */
+ check_async_ready(); /* Check async completions */
+ } else {
+ int wait_for_io = !check_async_ready();
+ if (wait_for_io)
+ wait_for_io = !check_children();
+ ERTS_CHK_IO(wait_for_io);
+ }
+ (void) check_children();
+#endif
+}
+
+
+#ifdef ERTS_SMP
+
+static erts_smp_tid_t sig_dispatcher_tid;
+
+static void
+smp_sig_notify(char c)
+{
+ int res;
+ do {
+ /* write() is async-signal safe (according to posix) */
+ res = write(sig_notify_fds[1], &c, 1);
+ } while (res < 0 && errno == EINTR);
+ if (res != 1) {
+ char msg[] =
+ "smp_sig_notify(): Failed to notify signal-dispatcher thread "
+ "about received signal";
+ (void) write(2, msg, sizeof(msg));
+ abort();
+ }
+}
+
+static void *
+signal_dispatcher_thread_func(void *unused)
+{
+ int initialized = 0;
+#if !CHLDWTHR
+ int notify_check_children = 0;
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ erts_lc_set_thread_name("signal_dispatcher");
+#endif
+ while (1) {
+ char buf[32];
+ int res, i;
+ /* Block on read() waiting for a signal notification to arrive... */
+ res = read(sig_notify_fds[0], (void *) &buf[0], 32);
+ if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ erl_exit(ERTS_ABORT_EXIT,
+ "signal-dispatcher thread got unexpected error: %s (%d)\n",
+ erl_errno_id(errno),
+ errno);
+ }
+ for (i = 0; i < res; i++) {
+ /*
+ * NOTE 1: The signal dispatcher thread should not do work
+ * that takes a substantial amount of time (except
+ * perhaps in test and debug builds). It needs to
+ * be responsive, i.e, it should only dispatch work
+ * to other threads.
+ *
+ * NOTE 2: The signal dispatcher thread is not a blockable
+ * thread (i.e., it hasn't called
+ * erts_register_blockable_thread()). This is
+ * intentional. We want to be able to interrupt
+ * writing of a crash dump by hitting C-c twice.
+ * Since it isn't a blockable thread it is important
+ * that it doesn't change the state of any data that
+ * a blocking thread expects to have exclusive access
+ * to (unless the signal dispatcher itself explicitly
+ * is blocking all blockable threads).
+ */
+ switch (buf[i]) {
+ case 0: /* Emulator initialized */
+ initialized = 1;
+#if !CHLDWTHR
+ if (!notify_check_children)
+#endif
+ break;
+#if !CHLDWTHR
+ case 'C': /* SIGCHLD */
+ if (initialized)
+ erts_smp_notify_check_children_needed();
+ else
+ notify_check_children = 1;
+ break;
+#endif
+ case 'I': /* SIGINT */
+ break_requested();
+ break;
+ case 'Q': /* SIGQUIT */
+ quit_requested();
+ break;
+ case '1': /* SIGUSR1 */
+ sigusr1_exit();
+ break;
+#ifdef QUANTIFY
+ case '2': /* SIGUSR2 */
+ quantify_save_data(); /* Might take a substantial amount of
+ time, but this is a test/debug
+ build */
+ break;
+#endif
+ default:
+ erl_exit(ERTS_ABORT_EXIT,
+ "signal-dispatcher thread received unknown "
+ "signal notification: '%c'\n",
+ buf[i]);
+ }
+ }
+ ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
+ }
+ return NULL;
+}
+
+static void
+init_smp_sig_notify(void)
+{
+ erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER;
+ thr_opts.detached = 1;
+
+ if (pipe(sig_notify_fds) < 0) {
+ erl_exit(ERTS_ABORT_EXIT,
+ "Failed to create signal-dispatcher pipe: %s (%d)\n",
+ erl_errno_id(errno),
+ errno);
+ }
+
+ /* Start signal handler thread */
+ erts_smp_thr_create(&sig_dispatcher_tid,
+ signal_dispatcher_thread_func,
+ NULL,
+ &thr_opts);
+}
+
+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
+
+ smp_sig_notify(0); /* Notify initialized */
+ while (1) {
+ /* Wait for a signal to arrive... */
+#ifdef DEBUG
+ int res =
+#else
+ (void)
+#endif
+ select(0, NULL, NULL, NULL, NULL);
+ ASSERT(res < 0);
+ ASSERT(errno == EINTR);
+ }
+}
+
+#endif /* ERTS_SMP */
+
+#ifdef ERTS_ENABLE_KERNEL_POLL /* get_value() is currently only used when
+ kernel-poll is enabled */
+
+/* Get arg marks argument as handled by
+ putting NULL in argv */
+static char *
+get_value(char* rest, char** argv, int* ip)
+{
+ char *param = argv[*ip]+1;
+ argv[*ip] = NULL;
+ if (*rest == '\0') {
+ char *next = argv[*ip + 1];
+ if (next[0] == '-'
+ && next[1] == '-'
+ && next[2] == '\0') {
+ erts_fprintf(stderr, "bad \"%s\" value: \n", param);
+ erts_usage();
+ }
+ (*ip)++;
+ argv[*ip] = NULL;
+ return next;
+ }
+ return rest;
+}
+
+#endif /* ERTS_ENABLE_KERNEL_POLL */
+
+void
+erl_sys_args(int* argc, char** argv)
+{
+ int i, j;
+
+ i = 1;
+
+ ASSERT(argc && argv);
+
+ while (i < *argc) {
+ if(argv[i][0] == '-') {
+ switch (argv[i][1]) {
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ case 'K': {
+ char *arg = get_value(argv[i] + 2, argv, &i);
+ if (strcmp("true", arg) == 0) {
+ erts_use_kernel_poll = 1;
+ }
+ else if (strcmp("false", arg) == 0) {
+ erts_use_kernel_poll = 0;
+ }
+ else {
+ erts_fprintf(stderr, "bad \"K\" value: %s\n", arg);
+ erts_usage();
+ }
+ break;
+ }
+#endif
+ case '-':
+ goto done_parsing;
+ default:
+ break;
+ }
+ }
+ i++;
+ }
+
+ done_parsing:
+
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ if (erts_use_kernel_poll) {
+ char no_kp[10];
+ size_t no_kp_sz = sizeof(no_kp);
+ int res = erts_sys_getenv("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
+ if (res > 0
+ || (res == 0
+ && sys_strcmp("false", no_kp) != 0
+ && sys_strcmp("FALSE", no_kp) != 0)) {
+ erts_use_kernel_poll = 0;
+ }
+ }
+#endif
+
+ init_check_io();
+
+#ifdef ERTS_SMP
+ init_smp_sig_notify();
+#endif
+
+ /* 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;
+}
+
+
+#ifdef ERTS_TIMER_THREAD
+
+/*
+ * Interruptible-wait facility: low-level synchronisation state
+ * and methods that are implementation dependent.
+ *
+ * Constraint: Every implementation must define 'struct erts_iwait'
+ * with a field 'erts_smp_atomic_t state;'.
+ */
+
+/* values for struct erts_iwait's state field */
+#define IWAIT_WAITING 0
+#define IWAIT_AWAKE 1
+#define IWAIT_INTERRUPT 2
+
+#if 0 /* XXX: needs feature test in erts/configure.in */
+
+/*
+ * This is an implementation of the interruptible wait facility on
+ * top of Linux-specific futexes.
+ */
+#include <asm/unistd.h>
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+static int sys_futex(void *futex, int op, int val, const struct timespec *timeout)
+{
+ return syscall(__NR_futex, futex, op, val, timeout);
+}
+
+struct erts_iwait {
+ erts_smp_atomic_t state; /* &state.counter is our futex */
+};
+
+static void iwait_lowlevel_init(struct erts_iwait *iwait) { /* empty */ }
+
+static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay)
+{
+ struct timespec timeout;
+ int res;
+
+ timeout.tv_sec = delay->tv_sec;
+ timeout.tv_nsec = delay->tv_usec * 1000;
+ res = sys_futex((void*)&iwait->state.counter, FUTEX_WAIT, IWAIT_WAITING, &timeout);
+ if (res < 0 && errno != ETIMEDOUT && errno != EWOULDBLOCK && errno != EINTR)
+ perror("FUTEX_WAIT");
+}
+
+static void iwait_lowlevel_interrupt(struct erts_iwait *iwait)
+{
+ int res = sys_futex((void*)&iwait->state.counter, FUTEX_WAKE, 1, NULL);
+ if (res < 0)
+ perror("FUTEX_WAKE");
+}
+
+#else /* using poll() or select() */
+
+/*
+ * This is an implementation of the interruptible wait facility on
+ * top of pipe(), poll() or select(), read(), and write().
+ */
+struct erts_iwait {
+ erts_smp_atomic_t state;
+ int read_fd; /* wait polls and reads this fd */
+ int write_fd; /* interrupt writes this fd */
+};
+
+static void iwait_lowlevel_init(struct erts_iwait *iwait)
+{
+ int fds[2];
+
+ if (pipe(fds) < 0) {
+ perror("pipe()");
+ exit(1);
+ }
+ iwait->read_fd = fds[0];
+ iwait->write_fd = fds[1];
+}
+
+#if defined(ERTS_USE_POLL)
+
+#include <sys/poll.h>
+#define PERROR_POLL "poll()"
+
+static int iwait_lowlevel_poll(int read_fd, struct timeval *delay)
+{
+ struct pollfd pollfd;
+ int timeout;
+
+ pollfd.fd = read_fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+ timeout = delay->tv_sec * 1000 + delay->tv_usec / 1000;
+ return poll(&pollfd, 1, timeout);
+}
+
+#else /* !ERTS_USE_POLL */
+
+#include <sys/select.h>
+#define PERROR_POLL "select()"
+
+static int iwait_lowlevel_poll(int read_fd, struct timeval *delay)
+{
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(read_fd, &readfds);
+ return select(read_fd + 1, &readfds, NULL, NULL, delay);
+}
+
+#endif /* !ERTS_USE_POLL */
+
+static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay)
+{
+ int res;
+ char buf[64];
+
+ res = iwait_lowlevel_poll(iwait->read_fd, delay);
+ if (res > 0)
+ (void)read(iwait->read_fd, buf, sizeof buf);
+ else if (res < 0 && errno != EINTR)
+ perror(PERROR_POLL);
+}
+
+static void iwait_lowlevel_interrupt(struct erts_iwait *iwait)
+{
+ int res = write(iwait->write_fd, "!", 1);
+ if (res < 0)
+ perror("write()");
+}
+
+#endif /* using poll() or select() */
+
+#if 0 /* not using poll() or select() */
+/*
+ * This is an implementation of the interruptible wait facility on
+ * top of pthread_cond_timedwait(). This has two problems:
+ * 1. pthread_cond_timedwait() requires an absolute time point,
+ * so the relative delay must be converted to absolute time.
+ * Worse, this breaks if the machine's time is adjusted while
+ * we're preparing to wait.
+ * 2. Each cond operation requires additional mutex lock/unlock operations.
+ *
+ * Problem 2 is probably not too bad on Linux (they'll just become
+ * relatively cheap futex operations), but problem 1 is the real killer.
+ * Only use this implementation if no better alternatives are available!
+ */
+struct erts_iwait {
+ erts_smp_atomic_t state;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+};
+
+static void iwait_lowlevel_init(struct erts_iwait *iwait)
+{
+ iwait->cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER;
+ iwait->mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
+}
+
+static void iwait_lowlevel_wait(struct erts_iwait *iwait, struct timeval *delay)
+{
+ struct timeval tmp;
+ struct timespec timeout;
+
+ /* Due to pthread_cond_timedwait()'s use of absolute
+ time, this must be the real gettimeofday(), _not_
+ the "smoothed" one beam/erl_time_sup.c implements. */
+ gettimeofday(&tmp, NULL);
+
+ tmp.tv_sec += delay->tv_sec;
+ tmp.tv_usec += delay->tv_usec;
+ if (tmp.tv_usec >= 1000*1000) {
+ tmp.tv_usec -= 1000*1000;
+ tmp.tv_sec += 1;
+ }
+ timeout.tv_sec = tmp.tv_sec;
+ timeout.tv_nsec = tmp.tv_usec * 1000;
+ pthread_mutex_lock(&iwait->mutex);
+ pthread_cond_timedwait(&iwait->cond, &iwait->mutex, &timeout);
+ pthread_mutex_unlock(&iwait->mutex);
+}
+
+static void iwait_lowlevel_interrupt(struct erts_iwait *iwait)
+{
+ pthread_mutex_lock(&iwait->mutex);
+ pthread_cond_signal(&iwait->cond);
+ pthread_mutex_unlock(&iwait->mutex);
+}
+
+#endif /* not using POLL */
+
+/*
+ * Interruptible-wait facility. This is just a wrapper around the
+ * low-level synchronisation code, where we maintain our logical
+ * state in order to suppress some state transitions.
+ */
+
+struct erts_iwait *erts_iwait_init(void)
+{
+ struct erts_iwait *iwait = malloc(sizeof *iwait);
+ if (!iwait) {
+ perror("malloc");
+ exit(1);
+ }
+ iwait_lowlevel_init(iwait);
+ erts_smp_atomic_init(&iwait->state, IWAIT_AWAKE);
+ return iwait;
+}
+
+void erts_iwait_wait(struct erts_iwait *iwait, struct timeval *delay)
+{
+ if (erts_smp_atomic_xchg(&iwait->state, IWAIT_WAITING) != IWAIT_INTERRUPT)
+ iwait_lowlevel_wait(iwait, delay);
+ erts_smp_atomic_set(&iwait->state, IWAIT_AWAKE);
+}
+
+void erts_iwait_interrupt(struct erts_iwait *iwait)
+{
+ if (erts_smp_atomic_xchg(&iwait->state, IWAIT_INTERRUPT) == IWAIT_WAITING)
+ iwait_lowlevel_interrupt(iwait);
+}
+
+#endif /* ERTS_TIMER_THREAD */
diff --git a/erts/emulator/sys/unix/sys_float.c b/erts/emulator/sys/unix/sys_float.c
new file mode 100644
index 0000000000..15da6ab45c
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_float.c
@@ -0,0 +1,815 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2001-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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "global.h"
+#include "erl_process.h"
+
+
+#ifdef NO_FPE_SIGNALS
+
+void
+erts_sys_init_float(void)
+{
+# ifdef SIGFPE
+ sys_sigset(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */
+# endif
+}
+
+static ERTS_INLINE void set_current_fp_exception(unsigned long pc)
+{
+ /* nothing to do */
+}
+
+#else /* !NO_FPE_SIGNALS */
+
+#ifdef ERTS_SMP
+static erts_tsd_key_t fpe_key;
+
+/* once-only initialisation early in the main thread (via erts_sys_init_float()) */
+static void erts_init_fp_exception(void)
+{
+ /* XXX: the wrappers prevent using a pthread destructor to
+ deallocate the key's value; so when/where do we do that? */
+ erts_tsd_key_create(&fpe_key);
+}
+
+void erts_thread_init_fp_exception(void)
+{
+ unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
+ *fpe = 0L;
+ erts_tsd_set(fpe_key, fpe);
+}
+
+static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void)
+{
+ return (volatile unsigned long*)erts_tsd_get(fpe_key);
+}
+#else /* !SMP */
+#define erts_init_fp_exception() /*empty*/
+static volatile unsigned long fp_exception;
+#define erts_thread_get_fp_exception() (&fp_exception)
+#endif /* SMP */
+
+volatile unsigned long *erts_get_current_fp_exception(void)
+{
+ Process *c_p;
+
+ c_p = erts_get_current_process();
+ if (c_p)
+ return &c_p->fp_exception;
+ return erts_thread_get_fp_exception();
+}
+
+static void set_current_fp_exception(unsigned long pc)
+{
+ volatile unsigned long *fpexnp = erts_get_current_fp_exception();
+ ASSERT(fpexnp != NULL);
+ *fpexnp = pc;
+}
+
+void erts_fp_check_init_error(volatile unsigned long *fpexnp)
+{
+ char buf[64];
+ snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n",
+ __builtin_return_address(0), (void*)*fpexnp);
+ write(2, buf, strlen(buf));
+ *fpexnp = 0;
+#if defined(__i386__) || defined(__x86_64__)
+ erts_restore_fpu();
+#endif
+}
+
+/* Is there no standard identifier for Darwin/MacOSX ? */
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+#if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
+
+static void unmask_x87(void)
+{
+ unsigned short cw;
+
+ __asm__ __volatile__("fstcw %0" : "=m"(cw));
+ cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */
+ __asm__ __volatile__("fldcw %0" : : "m"(cw));
+}
+
+/* mask x87 FPE, return true if the previous state was unmasked */
+static int mask_x87(void)
+{
+ unsigned short cw;
+ int unmasked;
+
+ __asm__ __volatile__("fstcw %0" : "=m"(cw));
+ unmasked = (cw & (0x01|0x04|0x08)) == 0;
+ /* or just set cw = 0x37f */
+ cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */
+ __asm__ __volatile__("fldcw %0" : : "m"(cw));
+ return unmasked;
+}
+
+static void unmask_sse2(void)
+{
+ unsigned int mxcsr;
+
+ __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
+ mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
+ __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
+}
+
+/* mask SSE2 FPE, return true if the previous state was unmasked */
+static int mask_sse2(void)
+{
+ unsigned int mxcsr;
+ int unmasked;
+
+ __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
+ unmasked = (mxcsr & 0x0680) == 0;
+ /* or just set mxcsr = 0x1f80 */
+ mxcsr &= ~0x003F; /* clear exn flags */
+ mxcsr |= 0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */
+ __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
+ return unmasked;
+}
+
+#if defined(__x86_64__)
+
+static inline int cpu_has_sse2(void) { return 1; }
+
+#else /* !__x86_64__ */
+
+/*
+ * Check if an x86-32 processor has SSE2.
+ */
+static unsigned int xor_eflags(unsigned int mask)
+{
+ unsigned int eax, edx;
+
+ eax = mask; /* eax = mask */
+ __asm__("pushfl\n\t"
+ "popl %0\n\t" /* edx = original EFLAGS */
+ "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */
+ "pushl %1\n\t"
+ "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */
+ "pushfl\n\t"
+ "popl %1\n\t" /* eax = new EFLAGS */
+ "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */
+ "pushl %0\n\t"
+ "popfl" /* restore original EFLAGS */
+ : "=d"(edx), "=a"(eax)
+ : "1"(eax));
+ return eax;
+}
+
+static __inline__ unsigned int cpuid_eax(unsigned int op)
+{
+ unsigned int eax, save_ebx;
+
+ /* In PIC mode i386 reserves EBX. So we must save
+ and restore it ourselves to not upset gcc. */
+ __asm__(
+ "movl %%ebx, %1\n\t"
+ "cpuid\n\t"
+ "movl %1, %%ebx"
+ : "=a"(eax), "=m"(save_ebx)
+ : "0"(op)
+ : "cx", "dx");
+ return eax;
+}
+
+static __inline__ unsigned int cpuid_edx(unsigned int op)
+{
+ unsigned int eax, edx, save_ebx;
+
+ /* In PIC mode i386 reserves EBX. So we must save
+ and restore it ourselves to not upset gcc. */
+ __asm__(
+ "movl %%ebx, %2\n\t"
+ "cpuid\n\t"
+ "movl %2, %%ebx"
+ : "=a"(eax), "=d"(edx), "=m"(save_ebx)
+ : "0"(op)
+ : "cx");
+ return edx;
+}
+
+/* The AC bit, bit #18, is a new bit introduced in the EFLAGS
+ * register on the Intel486 processor to generate alignment
+ * faults. This bit cannot be set on the Intel386 processor.
+ */
+static __inline__ int is_386(void)
+{
+ return ((xor_eflags(1<<18) >> 18) & 1) == 0;
+}
+
+/* Newer x86 processors have a CPUID instruction, as indicated by
+ * the ID bit (#21) in EFLAGS being modifiable.
+ */
+static __inline__ int has_CPUID(void)
+{
+ return (xor_eflags(1<<21) >> 21) & 1;
+}
+
+static int cpu_has_sse2(void)
+{
+ unsigned int maxlev, features;
+ static int has_sse2 = -1;
+
+ if (has_sse2 >= 0)
+ return has_sse2;
+ has_sse2 = 0;
+
+ if (is_386())
+ return 0;
+ if (!has_CPUID())
+ return 0;
+ maxlev = cpuid_eax(0);
+ /* Intel A-step Pentium had a preliminary version of CPUID.
+ It also didn't have SSE2. */
+ if ((maxlev & 0xFFFFFF00) == 0x0500)
+ return 0;
+ /* If max level is zero then CPUID cannot report any features. */
+ if (maxlev == 0)
+ return 0;
+ features = cpuid_edx(1);
+ has_sse2 = (features & (1 << 26)) != 0;
+
+ return has_sse2;
+}
+#endif /* !__x86_64__ */
+
+static void unmask_fpe(void)
+{
+ __asm__ __volatile__("fnclex");
+ unmask_x87();
+ if (cpu_has_sse2())
+ unmask_sse2();
+}
+
+static void unmask_fpe_conditional(int unmasked)
+{
+ if (unmasked)
+ unmask_fpe();
+}
+
+/* mask x86 FPE, return true if the previous state was unmasked */
+static int mask_fpe(void)
+{
+ int unmasked;
+
+ unmasked = mask_x87();
+ if (cpu_has_sse2())
+ unmasked |= mask_sse2();
+ return unmasked;
+}
+
+void erts_restore_fpu(void)
+{
+ __asm__ __volatile__("fninit");
+ unmask_x87();
+ if (cpu_has_sse2())
+ unmask_sse2();
+}
+
+#elif defined(__sparc__) && defined(__linux__)
+
+#if defined(__arch64__)
+#define LDX "ldx"
+#define STX "stx"
+#else
+#define LDX "ld"
+#define STX "st"
+#endif
+
+static void unmask_fpe(void)
+{
+ unsigned long fsr;
+
+ __asm__(STX " %%fsr, %0" : "=m"(fsr));
+ fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
+ fsr |= (0x1AUL << 23); /* enable NV, OF, DZ exceptions */
+ __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
+}
+
+static void unmask_fpe_conditional(int unmasked)
+{
+ if (unmasked)
+ unmask_fpe();
+}
+
+/* mask SPARC FPE, return true if the previous state was unmasked */
+static int mask_fpe(void)
+{
+ unsigned long fsr;
+ int unmasked;
+
+ __asm__(STX " %%fsr, %0" : "=m"(fsr));
+ unmasked = ((fsr >> 23) & 0x1A) == 0x1A;
+ fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
+ __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
+ return unmasked;
+}
+
+#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))
+
+#if defined(__linux__)
+#include <sys/prctl.h>
+
+static void set_fpexc_precise(void)
+{
+ if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) {
+ perror("PR_SET_FPEXC");
+ exit(1);
+ }
+}
+
+#elif defined(__DARWIN__)
+
+#include <mach/mach.h>
+#include <pthread.h>
+
+/*
+ * FE0 FE1 MSR bits
+ * 0 0 floating-point exceptions disabled
+ * 0 1 floating-point imprecise nonrecoverable
+ * 1 0 floating-point imprecise recoverable
+ * 1 1 floating-point precise mode
+ *
+ * Apparently:
+ * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0,
+ * and resets FE0 and FE1 to 0 after each SIGFPE.
+ * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1,
+ * and does not reset FE0 or FE1 after a SIGFPE.
+ */
+#define FE0_MASK (1<<11)
+#define FE1_MASK (1<<8)
+
+/* a thread cannot get or set its own MSR bits */
+static void *fpu_fpe_enable(void *arg)
+{
+ thread_t t = *(thread_t*)arg;
+ struct ppc_thread_state state;
+ unsigned int state_size = PPC_THREAD_STATE_COUNT;
+
+ if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) {
+ perror("thread_get_state");
+ exit(1);
+ }
+ if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) {
+#if 1
+ /* This would also have to be performed in the SIGFPE handler
+ to work around the MSR reset older Darwin releases do. */
+ state.srr1 |= (FE1_MASK|FE0_MASK);
+ thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size);
+#else
+ fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1);
+ exit(1);
+#endif
+ }
+ return NULL; /* Ok, we appear to be on Darwin 6.0 or later */
+}
+
+static void set_fpexc_precise(void)
+{
+ thread_t self = mach_thread_self();
+ pthread_t enabler;
+
+ if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) {
+ perror("pthread_create");
+ } else if (pthread_join(enabler, NULL)) {
+ perror("pthread_join");
+ }
+}
+
+#endif
+
+static void set_fpscr(unsigned int fpscr)
+{
+ union {
+ double d;
+ unsigned int fpscr[2];
+ } u;
+
+ u.fpscr[0] = 0xFFF80000;
+ u.fpscr[1] = fpscr;
+ __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d));
+}
+
+static unsigned int get_fpscr(void)
+{
+ union {
+ double d;
+ unsigned int fpscr[2];
+ } u;
+
+ __asm__("mffs %0" : "=f"(u.d));
+ return u.fpscr[1];
+}
+
+static void unmask_fpe(void)
+{
+ set_fpexc_precise();
+ set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */
+}
+
+static void unmask_fpe_conditional(int unmasked)
+{
+ if (unmasked)
+ unmask_fpe();
+}
+
+/* mask PowerPC FPE, return true if the previous state was unmasked */
+static int mask_fpe(void)
+{
+ int unmasked;
+
+ unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10);
+ set_fpscr(0x00);
+ return unmasked;
+}
+
+#else
+
+static void unmask_fpe(void)
+{
+ fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
+}
+
+static void unmask_fpe_conditional(int unmasked)
+{
+ if (unmasked)
+ unmask_fpe();
+}
+
+/* mask IEEE FPE, return true if previous state was unmasked */
+static int mask_fpe(void)
+{
+ const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
+ fp_except old_mask;
+
+ old_mask = fpsetmask(0);
+ return (old_mask & unmasked_mask) == unmasked_mask;
+}
+
+#endif
+
+#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || (defined(__OpenBSD__) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
+
+#if defined(__linux__) && defined(__i386__)
+#if !defined(X86_FXSR_MAGIC)
+#define X86_FXSR_MAGIC 0x0000
+#endif
+#elif defined(__FreeBSD__) && defined(__x86_64__)
+#include <sys/types.h>
+#include <machine/fpu.h>
+#elif defined(__FreeBSD__) && defined(__i386__)
+#include <sys/types.h>
+#include <machine/npx.h>
+#elif defined(__DARWIN__)
+#include <machine/signal.h>
+#elif defined(__OpenBSD__) && defined(__x86_64__)
+#include <sys/types.h>
+#include <machine/fpu.h>
+#endif
+#if !(defined(__OpenBSD__) && defined(__x86_64__))
+#include <ucontext.h>
+#endif
+#include <string.h>
+
+#if defined(__linux__) && defined(__x86_64__)
+#define mc_pc(mc) ((mc)->gregs[REG_RIP])
+#elif defined(__linux__) && defined(__i386__)
+#define mc_pc(mc) ((mc)->gregs[REG_EIP])
+#elif defined(__DARWIN__) && defined(__i386__)
+#ifdef DARWIN_MODERN_MCONTEXT
+#define mc_pc(mc) ((mc)->__ss.__eip)
+#else
+#define mc_pc(mc) ((mc)->ss.eip)
+#endif
+#elif defined(__DARWIN__) && defined(__x86_64__)
+#ifdef DARWIN_MODERN_MCONTEXT
+#define mc_pc(mc) ((mc)->__ss.__rip)
+#else
+#define mc_pc(mc) ((mc)->ss.rip)
+#endif
+#elif defined(__FreeBSD__) && defined(__x86_64__)
+#define mc_pc(mc) ((mc)->mc_rip)
+#elif defined(__FreeBSD__) && defined(__i386__)
+#define mc_pc(mc) ((mc)->mc_eip)
+#elif defined(__OpenBSD__) && defined(__x86_64__)
+#define mc_pc(mc) ((mc)->sc_rip)
+#elif defined(__sun__) && defined(__x86_64__)
+#define mc_pc(mc) ((mc)->gregs[REG_RIP])
+#endif
+
+static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
+{
+ ucontext_t *uc = puc;
+ unsigned long pc;
+
+#if defined(__linux__)
+#if defined(__x86_64__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ fpregset_t fpstate = mc->fpregs;
+ pc = mc_pc(mc);
+ /* A failed SSE2 instruction will restart. To avoid
+ looping we mask SSE2 exceptions now and unmask them
+ again later in erts_check_fpe()/erts_restore_fpu().
+ On RISCs we update PC to skip the failed instruction,
+ but the ever increasing complexity of the x86 instruction
+ set encoding makes that a poor solution here. */
+ fpstate->mxcsr = 0x1F80;
+ fpstate->swd &= ~0xFF;
+#elif defined(__i386__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ fpregset_t fpstate = mc->fpregs;
+ pc = mc_pc(mc);
+ if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
+ ((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
+ fpstate->sw &= ~0xFF;
+#elif defined(__sparc__) && defined(__arch64__)
+ /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
+ struct sigcontext *sc = (struct sigcontext*)puc;
+ pc = sc->sigc_regs.tpc;
+ sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
+ sc->sigc_regs.tnpc += 4;
+#elif defined(__sparc__)
+ /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
+ struct sigcontext *sc = (struct sigcontext*)puc;
+ pc = sc->si_regs.pc;
+ sc->si_regs.pc = sc->si_regs.npc;
+ sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
+#elif defined(__powerpc__)
+#if defined(__powerpc64__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ unsigned long *regs = &mc->gp_regs[0];
+#else
+ mcontext_t *mc = uc->uc_mcontext.uc_regs;
+ unsigned long *regs = &mc->gregs[0];
+#endif
+ pc = regs[PT_NIP];
+ regs[PT_NIP] += 4;
+ regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */
+#endif
+#elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
+#ifdef DARWIN_MODERN_MCONTEXT
+ mcontext_t mc = uc->uc_mcontext;
+ pc = mc_pc(mc);
+ mc->__fs.__fpu_mxcsr = 0x1F80;
+ *(unsigned short *)&mc->__fs.__fpu_fsw &= ~0xFF;
+#else
+ mcontext_t mc = uc->uc_mcontext;
+ pc = mc_pc(mc);
+ mc->fs.fpu_mxcsr = 0x1F80;
+ *(unsigned short *)&mc->fs.fpu_fsw &= ~0xFF;
+#endif /* DARWIN_MODERN_MCONTEXT */
+#elif defined(__DARWIN__) && defined(__ppc__)
+ mcontext_t mc = uc->uc_mcontext;
+ pc = mc->ss.srr0;
+ mc->ss.srr0 += 4;
+ mc->fs.fpscr = 0x80|0x40|0x10;
+#elif defined(__FreeBSD__) && defined(__x86_64__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate;
+ struct envxmm *envxmm = &savefpu->sv_env;
+ pc = mc_pc(mc);
+ envxmm->en_mxcsr = 0x1F80;
+ envxmm->en_sw &= ~0xFF;
+#elif defined(__FreeBSD__) && defined(__i386__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate;
+ pc = mc_pc(mc);
+ if (mc->mc_fpformat == _MC_FPFMT_XMM) {
+ struct envxmm *envxmm = &savefpu->sv_xmm.sv_env;
+ envxmm->en_mxcsr = 0x1F80;
+ envxmm->en_sw &= ~0xFF;
+ } else {
+ struct env87 *env87 = &savefpu->sv_87.sv_env;
+ env87->en_sw &= ~0xFF;
+ }
+#elif defined(__OpenBSD__) && defined(__x86_64__)
+ struct fxsave64 *fxsave = uc->sc_fpstate;
+ pc = mc_pc(uc);
+ fxsave->fx_mxcsr = 0x1F80;
+ fxsave->fx_fsw &= ~0xFF;
+#elif defined(__sun__) && defined(__x86_64__)
+ mcontext_t *mc = &uc->uc_mcontext;
+ struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state;
+ pc = mc_pc(mc);
+ fpstate->mxcsr = 0x1F80;
+ fpstate->sw &= ~0xFF;
+#endif
+#if 0
+ {
+ char buf[64];
+ snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
+ write(2, buf, strlen(buf));
+ }
+#endif
+ set_current_fp_exception(pc);
+}
+
+static void erts_thread_catch_fp_exceptions(void)
+{
+ struct sigaction act;
+ memset(&act, 0, sizeof act);
+ act.sa_sigaction = fpe_sig_action;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGFPE, &act, NULL);
+ unmask_fpe();
+}
+
+#else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
+
+static void fpe_sig_handler(int sig)
+{
+ set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */
+}
+
+static void erts_thread_catch_fp_exceptions(void)
+{
+ sys_sigset(SIGFPE, fpe_sig_handler);
+ unmask_fpe();
+}
+
+#endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
+
+/* once-only initialisation early in the main thread */
+void erts_sys_init_float(void)
+{
+ erts_init_fp_exception();
+ erts_thread_catch_fp_exceptions();
+ erts_printf_block_fpe = erts_sys_block_fpe;
+ erts_printf_unblock_fpe = erts_sys_unblock_fpe;
+}
+
+#endif /* NO_FPE_SIGNALS */
+
+void erts_thread_init_float(void)
+{
+#ifdef ERTS_SMP
+ /* This allows Erlang schedulers to leave Erlang-process context
+ and still have working FP exceptions. XXX: is this needed? */
+ erts_thread_init_fp_exception();
+#endif
+
+#ifndef NO_FPE_SIGNALS
+ /* NOTE:
+ * erts_thread_disable_fpe() is called in all threads at
+ * creation. We at least need to call unmask_fpe()
+ */
+#if defined(__DARWIN__) || defined(__FreeBSD__)
+ /* Darwin (7.9.0) does not appear to propagate FP exception settings
+ to a new thread from its parent. So if we want FP exceptions, we
+ must manually re-enable them in each new thread.
+ FreeBSD 6.1 appears to suffer from a similar issue. */
+ erts_thread_catch_fp_exceptions();
+#else
+ unmask_fpe();
+#endif
+
+#endif
+}
+
+void erts_thread_disable_fpe(void)
+{
+#if !defined(NO_FPE_SIGNALS)
+ (void)mask_fpe();
+#endif
+}
+
+#if !defined(NO_FPE_SIGNALS)
+int erts_sys_block_fpe(void)
+{
+ return mask_fpe();
+}
+
+void erts_sys_unblock_fpe(int unmasked)
+{
+ unmask_fpe_conditional(unmasked);
+}
+#endif
+
+/* The following check is incorporated from the Vee machine */
+
+#define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
+
+/*
+ ** Convert a double to ascii format 0.dddde[+|-]ddd
+ ** return number of characters converted
+ **
+ ** These two functions should maybe use localeconv() to pick up
+ ** the current radix character, but since it is uncertain how
+ ** expensive such a system call is, and since no-one has heard
+ ** of other radix characters than '.' and ',' an ad-hoc
+ ** low execution time solution is used instead.
+ */
+
+int
+sys_double_to_chars(double fp, char *buf)
+{
+ char *s = buf;
+
+ (void) sprintf(buf, "%.20e", fp);
+ /* Search upto decimal point */
+ if (*s == '+' || *s == '-') s++;
+ while (ISDIGIT(*s)) s++;
+ if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
+ /* Scan to end of string */
+ while (*s) s++;
+ return s-buf; /* i.e strlen(buf) */
+}
+
+/* Float conversion */
+
+int
+sys_chars_to_double(char* buf, double* fp)
+{
+#ifndef NO_FPE_SIGNALS
+ volatile unsigned long *fpexnp = erts_get_current_fp_exception();
+#endif
+ char *s = buf, *t, *dp;
+
+ /* Robert says that something like this is what he really wanted:
+ * (The [.,] radix test is NOT what Robert wanted - it was added later)
+ *
+ * 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 != '.' && *s != ',') /* Decimal part. */
+ return -1;
+ dp = s++; /* Remember decimal point pos just in case */
+ 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;
+
+#ifdef NO_FPE_SIGNALS
+ errno = 0;
+#endif
+ __ERTS_FP_CHECK_INIT(fpexnp);
+ *fp = strtod(buf, &t);
+ __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
+ if (t != s) { /* Whole string not scanned */
+ /* Try again with other radix char */
+ *dp = (*dp == '.') ? ',' : '.';
+ errno = 0;
+ __ERTS_FP_CHECK_INIT(fpexnp);
+ *fp = strtod(buf, &t);
+ __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
+ }
+
+#ifdef NO_FPE_SIGNALS
+ if (errno == ERANGE && (*fp == 0.0 || *fp == HUGE_VAL || *fp == -HUGE_VAL)) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int
+matherr(struct exception *exc)
+{
+#if !defined(NO_FPE_SIGNALS)
+ set_current_fp_exception((unsigned long)__builtin_return_address(0));
+#endif
+ return 1;
+}
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
new file mode 100644
index 0000000000..fcce54a2c4
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -0,0 +1,134 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2005-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%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* These need to be undef:ed to not break activation of
+ * micro level process accounting on /proc/self
+ */
+#ifdef _LARGEFILE_SOURCE
+# undef _LARGEFILE_SOURCE
+#endif
+#ifdef _FILE_OFFSET_BITS
+# undef _FILE_OFFSET_BITS
+#endif
+
+#include "sys.h"
+#include "global.h"
+
+#ifdef NO_SYSCONF
+# define TICKS_PER_SEC() HZ
+#else
+#define TICKS_PER_SEC() sysconf(_SC_CLK_TCK)
+#endif
+
+#ifdef HAVE_GETHRVTIME_PROCFS_IOCTL
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/signal.h>
+# include <sys/fault.h>
+# include <sys/syscall.h>
+# include <sys/procfs.h>
+# include <fcntl.h>
+#endif
+
+/******************* Routines for time measurement *********************/
+
+int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */
+int erts_ticks_per_sec_wrap = 0; /* Will be SYS_CLK_TCK_WRAP */
+static int ticks_bsr = 0; /* Shift wrapped tick value this much to the right */
+
+/*
+ * init timers, chose a tick length, and return it.
+ * Unix is priviliged when it comes to time, as erl_time_sup.c
+ * does almost everything. Other platforms have to
+ * emulate Unix in this sense.
+ */
+int sys_init_time(void)
+{
+ /*
+ * This (erts_ticks_per_sec) is only for times() (CLK_TCK),
+ * the resolution is always one millisecond..
+ */
+ if ((erts_ticks_per_sec = TICKS_PER_SEC()) < 0)
+ erl_exit(1, "Can't get clock ticks/sec\n");
+ if (erts_ticks_per_sec >= 1000) {
+ /* Workaround for beta linux kernels, need to be done in runtime
+ to make erlang run on both 2.4 and 2.5 kernels. In the future,
+ the kernel ticks might as
+ well be used as a high res timer instead, but that's for when the
+ majority uses kernels with HZ == 1024 */
+ ticks_bsr = 3;
+ } else {
+ ticks_bsr = 0;
+ }
+ erts_ticks_per_sec_wrap = (erts_ticks_per_sec >> ticks_bsr);
+ return SYS_CLOCK_RESOLUTION;
+}
+
+clock_t sys_times_wrap(void)
+{
+ SysTimes dummy;
+ clock_t result = (sys_times(&dummy) >> ticks_bsr);
+ return result;
+}
+
+
+
+
+#ifdef HAVE_GETHRVTIME_PROCFS_IOCTL
+
+int sys_start_hrvtime(void)
+{
+ long msacct = PR_MSACCT;
+ int fd;
+
+ if ( (fd = open("/proc/self", O_WRONLY)) == -1) {
+ return -1;
+ }
+ if (ioctl(fd, PIOCSET, &msacct) < 0) {
+ close(fd);
+ return -2;
+ }
+ close(fd);
+ return 0;
+}
+
+int sys_stop_hrvtime(void)
+{
+ long msacct = PR_MSACCT;
+ int fd;
+
+ if ( (fd = open("/proc/self", O_WRONLY)) == -1) {
+ return -1;
+ }
+ if (ioctl(fd, PIOCRESET, &msacct) < 0) {
+ close(fd);
+ return -2;
+ }
+ close(fd);
+ return 0;
+}
+
+#endif /* HAVE_GETHRVTIME_PROCFS_IOCTL */
+
+