aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc/common
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/etc/common
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/etc/common')
-rw-r--r--erts/etc/common/Makefile23
-rw-r--r--erts/etc/common/Makefile.in564
-rw-r--r--erts/etc/common/dialyzer.c466
-rw-r--r--erts/etc/common/erlc.c701
-rw-r--r--erts/etc/common/erlexec.c2038
-rw-r--r--erts/etc/common/escript.c697
-rw-r--r--erts/etc/common/heart.c1142
-rw-r--r--erts/etc/common/inet_gethost.c2757
-rw-r--r--erts/etc/common/typer.c416
9 files changed, 8804 insertions, 0 deletions
diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile
new file mode 100644
index 0000000000..73ab79d145
--- /dev/null
+++ b/erts/etc/common/Makefile
@@ -0,0 +1,23 @@
+#
+# %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%
+#
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+
+include $(ERL_TOP)/make/run_make.mk
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
new file mode 100644
index 0000000000..a9acab640e
--- /dev/null
+++ b/erts/etc/common/Makefile.in
@@ -0,0 +1,564 @@
+#
+# %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%
+#
+
+include $(ERL_TOP)/make/target.mk
+
+ERTS_LIB_TYPEMARKER=.$(TYPE)
+
+USING_MINGW=@MIXED_CYGWIN_MINGW@
+USING_VC=@MIXED_CYGWIN_VC@
+
+ifeq ($(TYPE),debug)
+PURIFY =
+TYPEMARKER = .debug
+TYPE_FLAGS = -DDEBUG @DEBUG_FLAGS@
+else
+ifeq ($(TYPE),purify)
+PURIFY = purify
+TYPEMARKER =
+ifeq ($(findstring ose,$(TARGET)),ose)
+TYPE_FLAGS = -g -XO -DPURIFY
+else
+TYPE_FLAGS = -g -O2 -DPURIFY
+endif
+else
+PURIFY =
+TYPEMARKER =
+ERTS_LIB_TYPEMARKER=
+TYPE_FLAGS =
+endif
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include ../../vsn.mk
+
+ERTS_INCL = -I$(ERL_TOP)/erts/include \
+ -I$(ERL_TOP)/erts/include/$(TARGET) \
+ -I$(ERL_TOP)/erts/include/internal \
+ -I$(ERL_TOP)/erts/include/internal/$(TARGET)
+
+CC = @CC@
+WFLAGS = @WFLAGS@
+CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) -I$(EMUDIR) \
+ $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\"
+LD = @LD@
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+
+ifeq ($(TARGET),win32)
+ifeq ($(TYPE),debug)
+CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \
+ -I$(EMUDIR) $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\")
+LDFLAGS += -g
+endif
+endif
+BINDIR = $(ERL_TOP)/bin/$(TARGET)
+OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
+EMUDIR = $(ERL_TOP)/erts/emulator/beam
+EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
+SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
+DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
+VXETC = ../vxworks
+UXETC = ../unix
+OSEETC = ../ose
+WINETC = ../win32
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+ERLEXEC = erl.exec
+else
+ifeq ($(findstring ose,$(TARGET)), ose)
+ERLEXEC =
+TAR = @TAR@
+else
+ifeq ($(TARGET), win32)
+ERLEXEC = erlexec.dll
+else
+ERLEXEC = erlexec
+endif
+endif
+endif
+
+# On windows we always need reentrant libraries.
+ifeq ($(TARGET),win32)
+ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+else
+ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+endif
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall
+INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf
+INSTALL_INCLUDES = $(VXETC)/reclaim.h
+INSTALL_TOP = $(VXETC)/README.VxWorks
+INSTALL_MISC =
+INSTALL_SRC = heart.c $(VXETC)/heart_config.h $(VXETC)/heart_config.c \
+ $(VXETC)/erl.exec.c $(VXETC)/rdate.c $(VXETC)/vxcall.c \
+ $(VXETC)/erl_io.c
+ERLEXECDIR = $(VXETC)
+INSTALL_LIBS = $(OBJDIR)/reclaim.o
+INSTALL_OBJS = $(OBJDIR)/heart.o
+TEXTFILES = $(BINDIR)/erl_script.sam
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+else
+ifeq ($(findstring ose,$(TARGET)), ose)
+INSTALL_TOP = $(OSEETC)/README.OSE
+INSTALL_ERL_OSE = monolith lm erl_utils drivers port_progs host
+INSTALL_SRC =
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+INSTALL_PROGS =
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+else
+ifeq ($(TARGET),win32)
+CFLAGS += -I$(EMUOSDIR) -I$(WINETC)
+RC=rc.sh
+MC=mc.sh
+ERLSRV_HEADERS= \
+ $(WINETC)/erlsrv/erlsrv_global.h \
+ $(WINETC)/erlsrv/erlsrv_registry.h \
+ $(WINETC)/erlsrv/erlsrv_util.h \
+ $(WINETC)/erlsrv/erlsrv_interactive.h \
+ $(WINETC)/erlsrv/erlsrv_service.h
+
+ifeq ($(USING_VC), yes)
+ERLRES_OBJ=erl.res
+ERLSRV_OBJECTS= \
+ $(OBJDIR)/erlsrv_registry.o \
+ $(OBJDIR)/erlsrv_service.o \
+ $(OBJDIR)/erlsrv_interactive.o \
+ $(OBJDIR)/erlsrv_main.o \
+ $(OBJDIR)/erlsrv_util.o \
+ $(OBJDIR)/erlsrv_logmess.res
+MC_OUTPUTS= \
+ $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.rc
+ MT_FLAG="-MT"
+else
+ERLRES_OBJ=erl_res.o
+ERLSRV_OBJECTS= \
+ $(OBJDIR)/erlsrv_registry.o \
+ $(OBJDIR)/erlsrv_service.o \
+ $(OBJDIR)/erlsrv_interactive.o \
+ $(OBJDIR)/erlsrv_main.o \
+ $(OBJDIR)/erlsrv_util.o \
+ $(OBJDIR)/erlsrv_logmess.o
+MC_OUTPUTS= \
+ $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.res
+ MT_FLAG="-MD"
+endif
+INET_GETHOST = $(BINDIR)/inet_gethost.exe
+INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer.exe $(BINDIR)/dialyzer.exe $(BINDIR)/erlc.exe $(BINDIR)/start_erl.exe $(BINDIR)/escript.exe
+INSTALL_SRC = $(WINETC)/start_erl.c $(WINETC)/Nmakefile.start_erl
+ERLEXECDIR=.
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+TEXTFILES = Install.ini
+INSTALL_TOP = Install.ini
+INSTALL_TOP_BIN = $(BINDIR)/Install.exe
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart.exe $(BINDIR)/erlsrv.exe \
+ $(BINDIR)/erl.exe $(BINDIR)/werl.exe \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+
+ENTRY_SRC=$(WINETC)/port_entry.c
+ENTRY_OBJ=$(OBJDIR)/port_entry.o
+ifeq ($(USING_VC), yes)
+WINDSOCK = ws2_32.lib mswsock.lib
+else
+WINDSOCK = -lws2_32
+endif
+PORT_ENTRY_POINT=erl_port_entry
+ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
+
+else
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+INET_GETHOST = $(BINDIR)/inet_gethost@EXEEXT@
+INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer@EXEEXT@ $(BINDIR)/dialyzer@EXEEXT@ \
+ $(BINDIR)/erlc@EXEEXT@ $(BINDIR)/escript@EXEEXT@ \
+ $(BINDIR)/run_erl $(BINDIR)/to_erl $(BINDIR)/dyn_erl
+INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src
+INSTALL_TOP = Install
+INSTALL_TOP_BIN =
+INSTALL_MISC = ../unix/format_man_pages ../unix/makewhatis
+INSTALL_SRC = ../unix/setuid_socket_wrap.c #delivered as an example
+ERLEXECDIR = .
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+TEXTFILES = Install erl.src
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart@EXEEXT@ \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+endif
+endif
+endif
+
+etc: erts_lib $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN)
+
+# erlexec needs the erts_internal library...
+erts_lib:
+ cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+
+docs:
+
+clean:
+ifneq ($(INSTALL_PROGS),)
+ rm -f $(INSTALL_PROGS)
+endif
+ifneq ($(ENTRY_OBJ),)
+ rm -f $(ENTRY_OBJ)
+endif
+ifneq ($(ERLSRV_OBJECTS),)
+ rm -f $(ERLSRV_OBJECTS)
+endif
+ifneq ($(MC_OUTPUTS),)
+ rm -f $(MC_OUTPUTS)
+endif
+ifneq ($(ERLRES_OBJ),)
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/$(ERLRES_OBJ)
+endif
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/win_erlexec.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/init_file.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/start_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dialyzer.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlexec.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl_io.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlc.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/escript.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart_config.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/inet_gethost.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/rdate.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/reclaim.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/werl.o
+ rm -f *~ core
+
+#
+# Objects & executables
+#
+#$(OBJDIR)/%.o: %.c
+# $(CC) $(CFLAGS) -o $@ -c $<
+#
+#$(OBJDIR)/%.o: ../unix/%.c
+# $(CC) $(CFLAGS) -o $@ -c $<
+#
+#$(BINDIR)/%: $(OBJDIR)/%.o
+# $(PURIFY) $(LD) $(LDFLAGS) -o $@ $< $(LIBS)
+
+$(OBJDIR)/inet_gethost.o: inet_gethost.c
+ $(CC) $(CFLAGS) -o $@ -c inet_gethost.c
+
+$(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ)
+ $(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS)
+
+$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS)
+
+$(OBJDIR)/run_erl.o: ../unix/run_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c
+
+$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+
+$(OBJDIR)/to_erl.o: ../unix/to_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c
+
+$(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
+
+$(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c
+
+$(OBJDIR)/safe_string.o: ../unix/safe_string.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c
+
+ifneq ($(TARGET),win32)
+$(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/$(ERLEXEC).o $(ERLEXEC_XLIBS)
+
+$(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c
+ $(CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c
+endif
+$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/erlc.o: erlc.c
+ $(CC) $(CFLAGS) -o $@ -c erlc.c
+
+$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/dialyzer.o: dialyzer.c
+ $(CC) $(CFLAGS) -o $@ -c dialyzer.c
+
+$(BINDIR)/typer@EXEEXT@: $(OBJDIR)/typer.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/typer.o: typer.c
+ $(CC) $(CFLAGS) -o $@ -c typer.c
+
+$(BINDIR)/escript@EXEEXT@: $(OBJDIR)/escript.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/escript.o: escript.c
+ $(CC) $(CFLAGS) -o $@ -c escript.c
+
+#------------------------------------------------------------------------
+# Windows specific targets
+# The windows platform is quite different from the others. erl/werl are small C programs
+# loading a DLL. INI files are used instead of environment variables and the Install
+# script is actually a program, also Install has an INI file which tells of emulator
+# versions etc.
+#----------------------------------------------------------------------
+
+ifeq ($(TARGET),win32)
+
+$(BINDIR)/$(ERLEXEC): $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) dbg
+ $(LD) -dll $(LDFLAGS) -o $@ $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERLEXEC_XLIBS)
+
+dbg:
+ echo DBG
+
+$(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+
+$(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+
+$(BINDIR)/start_erl@EXEEXT@: $(OBJDIR)/start_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o
+
+$(BINDIR)/Install@EXEEXT@: $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
+
+$(BINDIR)/erlsrv@EXEEXT@: $(ERLSRV_OBJECTS)
+ $(LD) $(LDFLAGS) $(MT_FLAG) -o $@ $(ERLSRV_OBJECTS)
+
+# The service expects to be compiled with $(MT_FLAG) flag.
+
+$(OBJDIR)/%.o: $(WINETC)/erlsrv/%.c $(ERLSRV_HEADERS)
+ $(CC) $(CFLAGS) $(MT_FLAG) -o $@ -c $<
+
+$(OBJDIR)/erlsrv_util.o: $(WINETC)/erlsrv/erlsrv_util.c $(ERLSRV_HEADERS) \
+$(OBJDIR)/erlsrv_logmess.h
+ $(CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $<
+
+ifeq ($(USING_VC), yes)
+$(OBJDIR)/erlsrv_logmess.res: $(OBJDIR)/erlsrv_logmess.rc
+ $(RC) -o $(OBJDIR)/erlsrv_logmess.res -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.rc
+else
+$(OBJDIR)/erlsrv_logmess.o: $(OBJDIR)/erlsrv_logmess.res
+ $(RC) -o $(OBJDIR)/erlsrv_logmess.o -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.res
+endif
+
+$(MC_OUTPUTS): $(WINETC)/erlsrv/erlsrv_logmess.mc
+ $(MC) -o $(OBJDIR) $(WINETC)/erlsrv/erlsrv_logmess.mc
+
+$(OBJDIR)/werl.o: $(WINETC)/erl.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -DWIN32_WERL -o $@ -c $(WINETC)/erl.c
+
+$(OBJDIR)/erl.o: $(WINETC)/erl.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(WINETC)/erl.c
+
+$(OBJDIR)/erlexec.o: $(ERLEXECDIR)/erlexec.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(ERLEXECDIR)/erlexec.c
+
+$(OBJDIR)/win_erlexec.o: $(WINETC)/win_erlexec.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(WINETC)/win_erlexec.c
+
+$(OBJDIR)/init_file.o: $(WINETC)/init_file.c $(WINETC)/init_file.h
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/init_file.c
+
+$(OBJDIR)/Install.o: $(WINETC)/Install.c $(WINETC)/init_file.h
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/Install.c
+
+$(OBJDIR)/$(ERLRES_OBJ): $(WINETC)/erl.rc $(WINETC)/erlang.ico $(WINETC)/erl_icon.ico $(WINETC)/hrl_icon.ico $(WINETC)/beam_icon.ico
+ $(RC) -o $@ -I$(WINETC) $(WINETC)/erl.rc
+
+$(OBJDIR)/start_erl.o: $(WINETC)/start_erl.c
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/start_erl.c
+
+$(ENTRY_OBJ): $(ENTRY_SRC)
+ $(CC) $(CFLAGS) -o $@ -c $(ENTRY_SRC)
+
+Install.ini: ../$(TARGET)/Install.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%I_VSN%;$(VSN);' \
+ -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
+ ../$(TARGET)/Install.src > Install.ini
+
+
+endif
+#---------------------------------------------------------
+# End of windows specific targets.
+#---------------------------------------------------------
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+$(BINDIR)/heart: $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
+
+$(OBJDIR)/heart_config.o: $(VXETC)/heart_config.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/heart_config.c
+
+$(OBJDIR)/reclaim.o: $(VXETC)/reclaim.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/reclaim.c
+
+$(OBJDIR)/heart.o: heart.c
+ $(CC) $(CFLAGS) -I$(VXETC) -o $@ -c heart.c
+
+$(BINDIR)/erl_script.sam: $(VXETC)/erl_script.sam.in ../../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' \
+ $(VXETC)/erl_script.sam.in > $(BINDIR)/erl_script.sam
+else
+
+$(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ)
+ $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \
+ $(ENTRY_OBJ) $(WINDSOCK)
+
+$(OBJDIR)/heart.o: heart.c
+ $(CC) $(CFLAGS) -o $@ -c heart.c
+
+endif
+
+
+# VxWorks specific executables and objects ...
+
+$(BINDIR)/erl_io: $(OBJDIR)/erl_io.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_io.o
+
+$(OBJDIR)/erl_io.o: $(VXETC)/erl_io.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/erl_io.c
+
+$(BINDIR)/rdate: $(OBJDIR)/rdate.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/rdate.o
+
+$(OBJDIR)/rdate.o: $(VXETC)/rdate.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/rdate.c
+
+$(BINDIR)/vxcall: $(OBJDIR)/vxcall.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/vxcall.o
+
+$(OBJDIR)/vxcall.o: $(VXETC)/vxcall.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/vxcall.c
+
+
+
+Install: ../unix/Install.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%I_VSN%;$(VSN);' \
+ -e 's;%EMULATOR%;$(EMULATOR);' \
+ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
+ -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
+ ../unix/Install.src > Install
+
+erl.src: ../unix/erl.src.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%EMULATOR%;$(EMULATOR);' \
+ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
+ -e 's;%VSN%;$(VSN);' \
+ ../unix/erl.src.src > erl.src
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: etc
+ifneq ($(INSTALL_OBJS),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/obj
+ $(INSTALL_DATA) $(INSTALL_OBJS) $(RELEASE_PATH)/erts-$(VSN)/obj
+endif
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/bin
+ifneq ($(TARGET), win32)
+ifneq ($(findstring vxworks,$(TARGET)), vxworks)
+ifneq ($(findstring ose,$(TARGET)), ose)
+ $(INSTALL_SCRIPT) erl.src $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+endif
+endif
+ifneq ($(INSTALL_PROGS),)
+ $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_TOP),)
+ $(INSTALL_SCRIPT) $(INSTALL_TOP) $(RELEASE_PATH)
+endif
+ifneq ($(INSTALL_TOP_BIN),)
+ $(INSTALL_PROGRAM) $(INSTALL_TOP_BIN) $(RELEASE_PATH)
+endif
+ifneq ($(INSTALL_MISC),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/misc
+ $(INSTALL_SCRIPT) $(INSTALL_MISC) $(RELEASE_PATH)/misc
+endif
+ifneq ($(INSTALL_ERL_OSE),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/build_erl_ose
+ cd $(OSEETC) && $(TAR) erl_ose_$(SYSTEM_VSN).tar $(INSTALL_ERL_OSE)
+ cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar $(RELEASE_PATH)/build_erl_ose
+endif
+ifneq ($(INSTALL_SRC),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/src
+ $(INSTALL_DATA) $(INSTALL_SRC) $(RELEASE_PATH)/erts-$(VSN)/src
+endif
+ifneq ($(INSTALL_EMBEDDED_DATA),)
+ $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_LIBS),)
+ $(INSTALL_DATA) $(INSTALL_LIBS) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_INCLUDES),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/include
+ $(INSTALL_DATA) $(INSTALL_INCLUDES) $(RELEASE_PATH)/erts-$(VSN)/include
+endif
+
+release_docs_spec:
+
+
+
+
+
diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c
new file mode 100644
index 0000000000..9c66be7f0f
--- /dev/null
+++ b/erts/etc/common/dialyzer.c
@@ -0,0 +1,466 @@
+/*
+ * %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%
+ */
+/*
+ * Purpose: Dialyzer front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char *env;
+ int need_shell = 0;
+
+ env = get_env("DIALYZER_EMULATOR");
+ emulator = env ? env : get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ if (argc > 1 && strcmp(argv[1], "--wx") == 0) {
+ PUSH2("-smp", "--wx"); /* wx currently requires SMP enabled */
+ argc--, argv++;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "-smp") == 0) {
+ PUSH("-smpauto");
+ argc--, argv++;
+ }
+
+ if (argc > 2 && strcmp(argv[1], "+S") == 0) {
+ PUSH3("-smp", "+S", argv[2]);
+ argc--, argv++;
+ argc--, argv++;
+ }
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-run", "dialyzer", "plain_cl");
+ PUSH("-extra");
+
+ /*
+ * Push everything except --shell.
+ */
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "--shell") == 0) {
+ need_shell = 1;
+ } else {
+ PUSH(argv[1]);
+ }
+ argc--, argv++;
+ }
+
+ if (!need_shell) {
+ UNSHIFT("-noinput");
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "dialyzer: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "dialyzer: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
new file mode 100644
index 0000000000..c958fed741
--- /dev/null
+++ b/erts/etc/common/erlc.c
@@ -0,0 +1,701 @@
+/*
+ * %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%
+ */
+/*
+ * Purpose: Common compiler front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+/* FIXE ME config_win32.h? */
+#define HAVE_STRERROR 1
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+static char* output_type = NULL; /* Type of output file. */
+#ifdef __WIN32__
+static int pause_after_execution = 0;
+#endif
+
+/*
+ * Local functions.
+ */
+
+static char* process_opt(int* pArgc, char*** pArgv, int offset);
+static void error(char* format, ...);
+static void usage(void);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+
+int
+main(int argc, char** argv)
+{
+ char cwd[MAXPATHLEN]; /* Current working directory. */
+ char** rpc_eargv; /* Pointer to the beginning of arguments
+ * if calling a running Erlang system
+ * via erl_rpc().
+ */
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char *env;
+
+ env = get_env("ERLC_EMULATOR");
+ emulator = env ? env : get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy adding of emulator options (like -pa)
+ * before '-s erlcompile compile_cmdline...'.
+ *
+ * Oh, by the way, we will push the compiler command in the
+ * base of the eargv vector, and move it up later.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ PUSH("-noinput");
+ PUSH2("-mode", "minimal");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-s", "erl_compile", "compile_cmdline");
+ rpc_eargv = eargv+eargc;
+
+ /*
+ * Push standard arguments to Erlang.
+ *
+ * The @cwd argument was once needed, but from on R13B02 is optional.
+ * For maximum compatibility between erlc and erl of different versions,
+ * still provide the @cwd argument, unless it is too long to be
+ * represented as an atom.
+ */
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ error("Failed to get current working directory: %s", strerror(errno));
+#ifdef __WIN32__
+ (void) GetShortPathName(cwd, cwd, sizeof(cwd));
+#endif
+ if (strlen(cwd) < 256) {
+ PUSH2("@cwd", cwd);
+ }
+
+ /*
+ * Parse all command line switches.
+ */
+
+ while (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '+')) {
+
+ /*
+ * Options starting with '+' are passed on to Erlang.
+ */
+
+ if (argv[1][0] == '+') {
+ PUSH2("@option", argv[1]+1);
+ } else {
+ /*
+ * Interpret options starting with '-'.
+ */
+
+ switch (argv[1][1]) {
+ case 'b':
+ output_type = process_opt(&argc, &argv, 0);
+ PUSH2("@output_type", output_type);
+ break;
+ case 'c': /* Allowed for compatibility with 'erl'. */
+ if (strcmp(argv[1], "-compile") != 0)
+ goto error;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'D':
+ {
+ char* def = process_opt(&argc, &argv, 0);
+ char* equals;
+
+ def = strsave(def); /* Do not clobber original. */
+ if ((equals = strchr(def, '=')) == NULL) {
+ PUSH2("@d", def);
+ } else {
+ *equals = '\0';
+ equals++;
+ PUSH3("@dv", def, equals);
+ }
+ }
+ break;
+ case 'h':
+ if (strcmp(argv[1], "-hybrid") == 0) {
+ UNSHIFT(argv[1]);
+ } else {
+ usage();
+ }
+ break;
+ case 'I':
+ PUSH2("@i", process_opt(&argc, &argv, 0));
+ break;
+ case 'o':
+ PUSH2("@outdir", process_opt(&argc, &argv, 0));
+ break;
+ case 'O':
+ PUSH("@optimize");
+ if (argv[1][2] == '\0')
+ PUSH("1");
+ else
+ PUSH(argv[1]+2);
+ break;
+ case 'p':
+ {
+ int c = argv[1][2];
+
+ if (c != 'a' && c != 'z') {
+ goto error;
+#ifdef __WIN32__
+ } else if (strcmp(argv[1], "-pause") == 0) {
+ pause_after_execution = 1;
+#endif
+ } else {
+ char option[4];
+
+ UNSHIFT(process_opt(&argc, &argv, 1));
+ option[0] = '-';
+ option[1] = 'p';
+ option[2] = c;
+ option[3] = '\0';
+ UNSHIFT(strsave(option));
+ }
+ }
+ break;
+ case 's':
+ if (strcmp(argv[1], "-smp") == 0) {
+ UNSHIFT(argv[1]);
+ } else {
+ goto error;
+ }
+ break;
+ case 'v': /* Verbose. */
+ PUSH2("@verbose", "true");
+ break;
+ case 'V':
+ /** XXX Version perhaps, but of what? **/
+ break;
+ case 'W': /* Enable warnings. */
+ if (strcmp(argv[1]+2, "all") == 0) {
+ PUSH2("@warn", "999");
+ } else if (isdigit((int)argv[1][2])) {
+ PUSH2("@warn", argv[1]+2);
+ } else {
+ PUSH2("@warn", "1");
+ }
+ break;
+ case 'E':
+ case 'S':
+ case 'P':
+ {
+ char* buf;
+
+ /*
+ * From the given upper-case letter, construct
+ * a quoted atom. This is a convenience for the
+ * Erlang compiler, to avoid fighting with the shell's
+ * quoting.
+ */
+
+ buf = emalloc(4);
+ buf[0] = '\'';
+ buf[1] = argv[1][1];
+ buf[2] = '\'';
+ buf[3] = '\0';
+
+ PUSH2("@option", buf);
+ }
+ break;
+
+ case '-':
+ goto no_more_options;
+
+ default:
+ error:
+ usage();
+ break;
+ }
+ }
+ argc--, argv++;
+ }
+
+ no_more_options:
+
+ if (argc <= 1) {
+ /*
+ * To avoid starting an Erlang system unless absolutely needed
+ * exit if no files were specified on the command line.
+ */
+ exit(0);
+ }
+
+ /*
+ * The rest of the command line must be filenames. Simply push them.
+ */
+
+ PUSH("@files");
+ while (argc > 1) {
+ PUSH(argv[1]);
+ argc--, argv++;
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static char*
+process_opt(int* pArgc, char*** pArgv, int offset)
+{
+ int argc = *pArgc;
+ char** argv = *pArgv;
+ int c = argv[1][1];
+
+ if (argv[1][2+offset] != '\0') {
+ /*
+ * The option was given as -x<value>.
+ */
+ return argv[1]+2+offset;
+ }
+
+ /*
+ * Look at the next argument.
+ */
+
+ argc--, argv++;
+ if (argc < 2 || argv[1][0] == '-')
+ error("No value given to -%c option", c);
+ *pArgc = argc;
+ *pArgv = argv;
+ return argv[1];
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "erlc: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ if (pause_after_execution) {
+ fprintf(stderr, "Press ENTER to continue . . .\n");
+ while (getchar() != '\n')
+ ;
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+usage(void)
+{
+ static struct {
+ char* name;
+ char* desc;
+ } options[] = {
+ {"-b type", "type of output file (e.g. jam or beam)"},
+ {"-d", "turn on debugging of erlc itself"},
+ {"-Dname", "define name"},
+ {"-Dname=value", "define name to have value"},
+ {"-hybrid", "compile using hybrid-heap emulator"},
+ {"-help", "shows this help text"},
+ {"-I path", "where to search for include files"},
+ {"-o name", "name output directory or file"},
+ {"-pa path", "add path to the front of Erlang's code path"},
+ {"-pz path", "add path to the end of Erlang's code path"},
+ {"-smp", "compile using SMP emulator"},
+ {"-v", "verbose compiler output"},
+ {"-W0", "disable warnings"},
+ {"-Wnumber", "set warning level to number"},
+ {"-Wall", "enable all warnings"},
+ {"-W", "enable warnings (default; same as -W1)"},
+ {"-E", "generate listing of expanded code (Erlang compiler)"},
+ {"-S", "generate assembly listing (Erlang compiler)"},
+ {"-P", "generate listing of preprocessed code (Erlang compiler)"},
+ {"+term", "pass the Erlang term unchanged to the compiler"},
+ };
+ int i;
+
+ fprintf(stderr, "Usage:\terlc [options] file.ext ...\n");
+ fprintf(stderr, "Options:\n");
+ for (i = 0; i < sizeof(options)/sizeof(options[0]); i++) {
+ fprintf(stderr, "%-14s %s\n", options[i].name, options[i].desc);
+ }
+ exit(1);
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "erlc: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
new file mode 100644
index 0000000000..4325418e7c
--- /dev/null
+++ b/erts/etc/common/erlexec.c
@@ -0,0 +1,2038 @@
+/*
+ * %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%
+ */
+
+/*
+ * This is a C version of the erl.exec Bourne shell script, including
+ * additions required for Windows NT.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_driver.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include "erl_misc_utils.h"
+
+#ifdef __WIN32__
+# include "erl_version.h"
+# include "init_file.h"
+#endif
+
+#define NO 0
+#define YES 1
+#define DEFAULT_PROGNAME "erl"
+
+#ifdef __WIN32__
+#define INI_FILENAME "erl.ini"
+#define INI_SECTION "erlang"
+#define DIRSEP "\\"
+#define PATHSEP ";"
+#define NULL_DEVICE "nul"
+#define BINARY_EXT ""
+#define DLL_EXT ".dll"
+#define EMULATOR_EXECUTABLE "beam.dll"
+#else
+#define PATHSEP ":"
+#define DIRSEP "/"
+#define NULL_DEVICE "/dev/null"
+#define BINARY_EXT ""
+#define EMULATOR_EXECUTABLE "beam"
+
+#endif
+#define QUOTE(s) s
+
+/* +M alloc_util allocators */
+static const char plusM_au_allocs[]= {
+ 'u', /* all alloc_util allocators */
+ 'B', /* binary_alloc */
+ 'D', /* std_alloc */
+ 'E', /* ets_alloc */
+ 'H', /* eheap_alloc */
+ 'L', /* ll_alloc */
+ 'R', /* driver_alloc */
+ 'S', /* sl_alloc */
+ 'T', /* temp_alloc */
+ '\0'
+};
+
+/* +M alloc_util allocator specific arguments */
+static char *plusM_au_alloc_switches[] = {
+ "as",
+ "asbcst",
+ "e",
+ "t",
+ "lmbcs",
+ "mbcgs",
+ "mbsd",
+ "mmbcs",
+ "mmmbc",
+ "mmsbc",
+ "msbclt",
+ "ramv",
+ "rmbcmt",
+ "rsbcmt",
+ "rsbcst",
+ "sbct",
+ "smbcs",
+ NULL
+};
+
+/* +M other arguments */
+static char *plusM_other_switches[] = {
+ "ea",
+ "ummc",
+ "uycs",
+ "im",
+ "is",
+ "it",
+ "Mamcbf",
+ "Mrmcbf",
+ "Mmcs",
+ "Mcci",
+ "Fe",
+ "Ye",
+ "Ym",
+ "Ytp",
+ "Ytt",
+ NULL
+};
+
+/* +s arguments with values */
+static char *pluss_val_switches[] = {
+ "bt",
+ "ct",
+ "ss",
+ NULL
+};
+
+/*
+ * Define sleep(seconds) in terms of Sleep() on Windows.
+ */
+
+#ifdef __WIN32__
+#define sleep(seconds) Sleep(seconds*1000)
+#endif
+
+#define SMP_SUFFIX ".smp"
+#define HYBRID_SUFFIX ".hybrid"
+
+#ifdef __WIN32__
+#define DEBUG_SUFFIX ".debug"
+#define EMU_TYPE_SUFFIX_LENGTH (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX)))
+#else
+/* The length of the longest memory architecture suffix. */
+#define EMU_TYPE_SUFFIX_LENGTH strlen(HYBRID_SUFFIX)
+#endif
+/*
+ * Define flags for different memory architectures.
+ */
+#define EMU_TYPE_SMP 0x0001
+#define EMU_TYPE_HYBRID 0x0002
+
+#ifdef __WIN32__
+#define EMU_TYPE_DEBUG 0x0004
+#endif
+
+void usage(const char *switchname);
+void start_epmd(char *epmd);
+void error(char* format, ...);
+
+/*
+ * Local functions.
+ */
+
+#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
+static void usage_notsup(const char *switchname);
+#endif
+static void usage_msg(const char *msg);
+static char **build_args_from_env(char *env_var);
+static char **build_args_from_string(char *env_var);
+static void initial_argv_massage(int *argc, char ***argv);
+static void get_parameters(int argc, char** argv);
+static void add_arg(char *new_arg);
+static void add_args(char *first_arg, ...);
+static void ensure_EargsSz(int sz);
+static void add_Eargs(char *new_arg);
+static void *emalloc(size_t size);
+static void *erealloc(void *p, size_t size);
+static void efree(void *p);
+static char* strsave(char* string);
+static int is_one_of_strings(char *str, char *strs[]);
+static char *write_str(char *to, char *from);
+static void get_home(void);
+static void add_epmd_port(void);
+#ifdef __WIN32__
+static void get_start_erl_data(char *);
+static char* get_value(HKEY key, char* value_name, BOOL mustExit);
+static char* possibly_quote(char* arg);
+
+/*
+ * Functions from win_erlexec.c
+ */
+int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached);
+int start_emulator(char* emu, char*start_prog, char** argv, int start_detached);
+#endif
+
+
+
+/*
+ * Variables.
+ */
+int nohup = 0;
+int keep_window = 0;
+
+static char **Eargsp = NULL; /* Emulator arguments (to appear first). */
+static int EargsSz = 0; /* Size of Eargsp */
+static int EargsCnt = 0; /* Number of emulator arguments. */
+static char **argsp = NULL; /* Common arguments. */
+static int argsCnt = 0; /* Number of common arguments */
+static int argsSz = 0; /* Size of argsp */
+static char tmpStr[10240]; /* Temporary string buffer. */
+static int verbose = 0; /* If non-zero, print some extra information. */
+static int start_detached = 0; /* If non-zero, the emulator should be
+ * started detached (in the background).
+ */
+static int emu_type = 0; /* If non-zero, start beam.ARCH or beam.ARCH.exe
+ * instead of beam or beam.exe, where ARCH is defined by flags. */
+static int emu_type_passed = 0; /* Types explicitly set */
+
+#ifdef __WIN32__
+static char *start_emulator_program = NULL; /* For detachec mode -
+ erl.exe/werl.exe */
+static char* key_val_name = ERLANG_VERSION; /* Used by the registry
+ * access functions.
+ */
+static char* boot_script = NULL; /* used by option -start_erl and -boot */
+static char* config_script = NULL; /* used by option -start_erl and -config */
+
+static HANDLE this_module_handle;
+static int run_werl;
+
+#endif
+
+/*
+ * Needed parameters to be fetched from the environment (Unix)
+ * or the ini file (Win32).
+ */
+
+static char* bindir; /* Location of executables. */
+static char* rootdir; /* Root location of Erlang installation. */
+static char* emu; /* Emulator to run. */
+static char* progname; /* Name of this program. */
+static char* home; /* Path of user's home directory. */
+
+static void
+set_env(char *key, char *value)
+{
+#ifdef __WIN32__
+ if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+#else
+ size_t size = strlen(key) + 1 + strlen(value) + 1;
+ char *str = emalloc(size);
+ sprintf(str, "%s=%s", key, value);
+ if (putenv(str) != 0)
+ error("putenv(\"%s\") failed!", str);
+#ifdef HAVE_COPYING_PUTENV
+ efree(str);
+#endif
+#endif
+}
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ efree(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ efree(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+/*
+ * Add the arcitecture suffix to the program name if needed,
+ * except on Windows, where we insert it just before ".DLL".
+ */
+static char*
+add_extra_suffixes(char *prog, int type)
+{
+ char *res;
+ char *p;
+ int len;
+#ifdef __WIN32__
+ char *dll_p;
+ int dll = 0;
+#endif
+
+ if (!type) {
+ return prog;
+ }
+
+ len = strlen(prog);
+
+ /* Worst-case allocation */
+ p = emalloc(len +
+ EMU_TYPE_SUFFIX_LENGTH +
+ + 1);
+ res = p;
+ p = write_str(p, prog);
+
+#ifdef __WIN32__
+ dll_p = res + len - 4;
+ if (dll_p >= res) {
+ if (dll_p[0] == '.' &&
+ (dll_p[1] == 'd' || dll_p[1] == 'D') &&
+ (dll_p[2] == 'l' || dll_p[2] == 'L') &&
+ (dll_p[3] == 'l' || dll_p[3] == 'L')) {
+ p = dll_p;
+ dll = 1;
+ }
+ }
+#endif
+
+#ifdef __WIN32__
+ if (type & EMU_TYPE_DEBUG) {
+ p = write_str(p, DEBUG_SUFFIX);
+ type &= ~(EMU_TYPE_DEBUG);
+ }
+#endif
+ if (type == EMU_TYPE_SMP) {
+ p = write_str(p, SMP_SUFFIX);
+ }
+ else if (type == EMU_TYPE_HYBRID) {
+ p = write_str(p, HYBRID_SUFFIX);
+ }
+#ifdef __WIN32__
+ if (dll) {
+ p = write_str(p, DLL_EXT);
+ }
+#endif
+
+ return res;
+}
+
+#ifdef __WIN32__
+__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ int haltAfterwards = 0; /* If true, put 's erlang halt' at the end
+ * of the arguments. */
+ int isdistributed = 0;
+ int no_epmd = 0;
+ int i;
+ char* s;
+ char *epmd_prog = NULL;
+ char *malloc_lib;
+ int process_args = 1;
+ int print_args_exit = 0;
+ int print_qouted_cmd_exit = 0;
+ erts_cpu_info_t *cpuinfo = NULL;
+
+#ifdef __WIN32__
+ this_module_handle = module;
+ run_werl = windowed;
+ /* if we started this erl just to get a detached emulator,
+ * the arguments are already prepared for beam, so we skip
+ * directly to start_emulator */
+ s = get_env("ERL_CONSOLE_MODE");
+ if (s != NULL && strcmp(s, "detached")==0) {
+ free_env_val(s);
+ s = get_env("ERL_EMULATOR_DLL");
+ if (s != NULL) {
+ argv[0] = strsave(s);
+ } else {
+ argv[0] = strsave(EMULATOR_EXECUTABLE);
+ }
+ ensure_EargsSz(argc + 1);
+ memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
+ Eargsp[argc] = NULL;
+ emu = argv[0];
+ start_emulator_program = strsave(argv[0]);
+ goto skip_arg_massage;
+ }
+ free_env_val(s);
+#else
+ int reset_cerl_detached = 0;
+
+ s = get_env("CERL_DETACHED_PROG");
+ if (s && strcmp(s, "") != 0) {
+ emu = s;
+ start_detached = 1;
+ reset_cerl_detached = 1;
+ ensure_EargsSz(argc + 1);
+ memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
+ Eargsp[argc] = emu;
+ Eargsp[argc] = NULL;
+ goto skip_arg_massage;
+ }
+ free_env_val(s);
+#endif
+
+ initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */
+
+ i = 1;
+#ifdef __WIN32__
+ /* Not used? /rickard */
+ if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) {
+ key_val_name = strsave(argv[i+1]);
+ i = 3;
+ }
+#endif
+
+ get_parameters(argc, argv);
+
+ /*
+ * Construct the path of the executable.
+ */
+ cpuinfo = erts_cpu_info_create();
+ /* '-smp auto' is default */
+#ifdef ERTS_HAVE_SMP_EMU
+ if (erts_get_cpu_configured(cpuinfo) > 1)
+ emu_type |= EMU_TYPE_SMP;
+#endif
+
+#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG)
+ emu_type_passed |= EMU_TYPE_DEBUG;
+ emu_type |= EMU_TYPE_DEBUG;
+#endif
+
+ /* We need to do this before the ordinary processing. */
+ malloc_lib = get_env("ERL_MALLOC_LIB");
+ while (i < argc) {
+ if (argv[i][0] == '+') {
+ if (argv[i][1] == 'M' && argv[i][2] == 'Y' && argv[i][3] == 'm') {
+ if (argv[i][4] == '\0') {
+ if (++i < argc)
+ malloc_lib = argv[i];
+ else
+ usage("+MYm");
+ }
+ else
+ malloc_lib = &argv[i][4];
+ }
+ }
+ else if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-smp") == 0) {
+ if (i + 1 >= argc)
+ goto smp;
+
+ if (strcmp(argv[i+1], "auto") == 0) {
+ i++;
+ smp_auto:
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ if (erts_get_cpu_configured(cpuinfo) > 1)
+ emu_type |= EMU_TYPE_SMP;
+ else
+#endif
+ emu_type &= ~EMU_TYPE_SMP;
+ }
+ else if (strcmp(argv[i+1], "enable") == 0) {
+ i++;
+ smp_enable:
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ emu_type |= EMU_TYPE_SMP;
+#else
+ usage_notsup("-smp enable");
+#endif
+ }
+ else if (strcmp(argv[i+1], "disable") == 0) {
+ i++;
+ smp_disable:
+ emu_type_passed |= EMU_TYPE_SMP;
+ emu_type &= ~EMU_TYPE_SMP;
+ }
+ else {
+ smp:
+
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ emu_type |= EMU_TYPE_SMP;
+#else
+ usage_notsup("-smp");
+#endif
+ }
+ } else if (strcmp(argv[i], "-smpenable") == 0) {
+ goto smp_enable;
+ } else if (strcmp(argv[i], "-smpauto") == 0) {
+ goto smp_auto;
+ } else if (strcmp(argv[i], "-smpdisable") == 0) {
+ goto smp_disable;
+#ifdef __WIN32__
+ } else if (strcmp(argv[i], "-debug") == 0) {
+ emu_type_passed |= EMU_TYPE_DEBUG;
+ emu_type |= EMU_TYPE_DEBUG;
+#endif
+ } else if (strcmp(argv[i], "-hybrid") == 0) {
+ emu_type_passed |= EMU_TYPE_HYBRID;
+#ifdef ERTS_HAVE_HYBRID_EMU
+ emu_type |= EMU_TYPE_HYBRID;
+#else
+ usage_notsup("-hybrid");
+#endif
+ } else if (strcmp(argv[i], "-extra") == 0) {
+ break;
+ }
+ }
+ i++;
+ }
+
+ erts_cpu_info_destroy(cpuinfo);
+ cpuinfo = NULL;
+
+ if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
+ /*
+ * We have a conflict. Only using explicitly passed arguments
+ * may solve it...
+ */
+ emu_type &= emu_type_passed;
+ if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
+ usage_msg("Hybrid heap emulator with SMP support selected. The "
+ "combination hybrid heap and SMP support is currently "
+ "not supported.");
+ }
+ }
+
+ if (malloc_lib) {
+ if (strcmp(malloc_lib, "libc") != 0)
+ usage("+MYm");
+ }
+ emu = add_extra_suffixes(emu, emu_type);
+ sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
+ emu = strsave(tmpStr);
+
+ add_Eargs(emu); /* Will be argv[0] -- necessary! */
+
+ /*
+ * Add the bindir to the path (unless it is there already).
+ */
+
+ s = get_env("PATH");
+ if (!s) {
+ sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir);
+ } else if (strstr(s, bindir) == NULL) {
+ sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir,
+ rootdir, s);
+ } else {
+ sprintf(tmpStr, "%s", s);
+ }
+ free_env_val(s);
+ set_env("PATH", tmpStr);
+
+ i = 1;
+
+#ifdef __WIN32__
+#define ADD_BOOT_CONFIG \
+ if (boot_script) \
+ add_args("-boot", boot_script, NULL); \
+ if (config_script) \
+ add_args("-config", config_script, NULL);
+#else
+#define ADD_BOOT_CONFIG
+#endif
+
+ get_home();
+ add_args("-home", home, NULL);
+
+ add_epmd_port();
+
+ add_arg("--");
+
+ while (i < argc) {
+ if (!process_args) { /* Copy arguments after '-extra' */
+ add_arg(argv[i]);
+ i++;
+ } else {
+ switch (argv[i][0]) {
+ case '-':
+ switch (argv[i][1]) {
+#ifdef __WIN32__
+ case 'b':
+ if (strcmp(argv[i], "-boot") == 0) {
+ if (boot_script)
+ error("Conflicting -start_erl and -boot options");
+ if (i+1 >= argc)
+ usage("-boot");
+ boot_script = strsave(argv[i+1]);
+ i++;
+ }
+ else {
+ add_arg(argv[i]);
+ }
+ break;
+#endif
+ case 'c':
+ if (strcmp(argv[i], "-compile") == 0) {
+ /*
+ * Note that the shell script erl.exec does an recursive call
+ * on itself here. We'll avoid doing that.
+ */
+ add_args("-noshell", "-noinput", "-s", "c", "lc_batch",
+ NULL);
+ add_Eargs("-B");
+ haltAfterwards = 0;
+ }
+#ifdef __WIN32__
+ else if (strcmp(argv[i], "-config") == 0){
+ if (config_script)
+ error("Conflicting -start_erl and -config options");
+ if (i+1 >= argc)
+ usage("-config");
+ config_script = strsave(argv[i+1]);
+ i++;
+ }
+#endif
+ else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ case 'd':
+ if (strcmp(argv[i], "-detached") != 0) {
+ add_arg(argv[i]);
+ } else {
+ start_detached = 1;
+ add_args("-noshell", "-noinput", NULL);
+ }
+ break;
+
+ case 'i':
+ if (strcmp(argv[i], "-instr") == 0) {
+ add_Eargs("-Mim");
+ add_Eargs("true");
+ }
+ else
+ add_arg(argv[i]);
+ break;
+
+ case 'e':
+ if (strcmp(argv[i], "-extra") == 0) {
+ process_args = 0;
+ ADD_BOOT_CONFIG;
+ add_arg(argv[i]);
+ } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */
+ verbose = 1;
+ } else if (strcmp(argv[i], "-emu_args_exit") == 0) {
+ print_args_exit = 1;
+ } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) {
+ print_qouted_cmd_exit = 1;
+ } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */
+ if (i+2 >= argc)
+ usage("-env");
+ set_env(argv[i+1], argv[i+2]);
+ i += 2;
+ } else if (strcmp(argv[i], "-epmd") == 0) {
+ if (i+1 >= argc)
+ usage("-epmd");
+ epmd_prog = argv[i+1];
+ ++i;
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+ case 'k':
+ if (strcmp(argv[i], "-keep_window") == 0) {
+ keep_window = 1;
+ } else
+ add_arg(argv[i]);
+ break;
+
+ case 'm':
+ /*
+ * Note that the shell script erl.exec does an recursive call
+ * on itself here. We'll avoid doing that.
+ */
+ if (strcmp(argv[i], "-make") == 0) {
+ add_args("-noshell", "-noinput", "-s", "make", "all", NULL);
+ add_Eargs("-B");
+ haltAfterwards = 1;
+ i = argc; /* Skip rest of command line */
+ } else if (strcmp(argv[i], "-man") == 0) {
+#if defined(__WIN32__)
+ error("-man not supported on Windows");
+#else
+ argv[i] = "man";
+ sprintf(tmpStr, "%s/man", rootdir);
+ set_env("MANPATH", tmpStr);
+ execvp("man", argv+i);
+ error("Could not execute the 'man' command.");
+#endif
+ } else
+ add_arg(argv[i]);
+ break;
+
+ case 'n':
+ if (strcmp(argv[i], "-name") == 0) { /* -name NAME */
+ if (i+1 >= argc)
+ usage("-name");
+
+ /*
+ * Note: Cannot use add_args() here, due to non-defined
+ * evaluation order.
+ */
+
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ isdistributed = 1;
+ i++;
+ } else if (strcmp(argv[i], "-noinput") == 0) {
+ add_args("-noshell", "-noinput", NULL);
+ } else if (strcmp(argv[i], "-nohup") == 0) {
+ add_arg("-nohup");
+ nohup = 1;
+ } else if (strcmp(argv[i], "-no_epmd") == 0) {
+ add_arg("-no_epmd");
+ no_epmd = 1;
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ case 's': /* -sname NAME */
+ if (strcmp(argv[i], "-sname") == 0) {
+ if (i+1 >= argc)
+ usage("-sname");
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ isdistributed = 1;
+ i++;
+ }
+#ifdef __WIN32__
+ else if (strcmp(argv[i], "-service_event") == 0) {
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ i++;
+ }
+ else if (strcmp(argv[i], "-start_erl") == 0) {
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ get_start_erl_data(argv[i+1]);
+ i++;
+ } else
+ get_start_erl_data((char *) NULL);
+ }
+#endif
+ else
+ add_arg(argv[i]);
+
+ break;
+
+ case 'v': /* -version */
+ if (strcmp(argv[i], "-version") == 0) {
+ add_Eargs("-V");
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ default:
+ add_arg(argv[i]);
+ break;
+ } /* switch(argv[i][1] */
+ break;
+
+ case '+':
+ switch (argv[i][1]) {
+ case '#':
+ case 'a':
+ case 'A':
+ case 'b':
+ case 'h':
+ case 'i':
+ case 'P':
+ case 'S':
+ case 'T':
+ case 'R':
+ case 'W':
+ case 'K':
+ if (argv[i][2] != '\0')
+ goto the_default;
+ if (i+1 >= argc)
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ break;
+ case 'B':
+ argv[i][0] = '-';
+ if (argv[i][2] != '\0') {
+ if ((argv[i][2] != 'i') &&
+ (argv[i][2] != 'c') &&
+ (argv[i][2] != 'd')) {
+ usage(argv[i]);
+ } else {
+ add_Eargs(argv[i]);
+ break;
+ }
+ }
+ if (i+1 < argc) {
+ if ((argv[i+1][0] != '-') &&
+ (argv[i+1][0] != '+')) {
+ if (argv[i+1][0] == 'i') {
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ break;
+ } else {
+ usage(argv[i]);
+ }
+ }
+ }
+ add_Eargs(argv[i]);
+ break;
+ case 'M': {
+ int x;
+ for (x = 0; plusM_au_allocs[x]; x++)
+ if (plusM_au_allocs[x] == argv[i][2])
+ break;
+ if ((plusM_au_allocs[x]
+ && is_one_of_strings(&argv[i][3],
+ plusM_au_alloc_switches))
+ || is_one_of_strings(&argv[i][2],
+ plusM_other_switches)) {
+ if (i+1 >= argc
+ || argv[i+1][0] == '-'
+ || argv[i+1][0] == '+')
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ }
+ else
+ goto the_default;
+ break;
+ }
+ case 's':
+ if (!is_one_of_strings(&argv[i][2],
+ pluss_val_switches))
+ goto the_default;
+ else {
+ if (i+1 >= argc
+ || argv[i+1][0] == '-'
+ || argv[i+1][0] == '+')
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ }
+ break;
+ default:
+ the_default:
+ argv[i][0] = '-'; /* Change +option to -option. */
+ add_Eargs(argv[i]);
+ }
+ break;
+
+ default:
+ add_arg(argv[i]);
+ } /* switch(argv[i][0] */
+ i++;
+ }
+ }
+
+ if (process_args) {
+ ADD_BOOT_CONFIG;
+ }
+#undef ADD_BOOT_CONFIG
+
+ /* Doesn't conflict with -extra, since -make skips all the rest of
+ the arguments. */
+ if (haltAfterwards) {
+ add_args("-s", "erlang", "halt", NULL);
+ }
+
+ if (isdistributed && !no_epmd)
+ start_epmd(epmd_prog);
+
+#if (! defined(__WIN32__)) && defined(DEBUG)
+ if (start_detached) {
+ /* Start the emulator within an xterm.
+ * Move up all arguments and insert
+ * "xterm -e " first.
+ * The path must be searched for this
+ * to work, i.e execvp() must be used.
+ */
+ ensure_EargsSz(EargsCnt+2);
+ for (i = EargsCnt; i > 0; i--)
+ Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */
+ EargsCnt += 2; /* Two args to insert */
+ Eargsp[0] = emu = "xterm";
+ Eargsp[1] = "-e";
+ }
+#endif
+
+ add_Eargs("--");
+ add_Eargs("-root");
+ add_Eargs(rootdir);
+ add_Eargs("-progname");
+ add_Eargs(progname);
+ add_Eargs("--");
+ ensure_EargsSz(EargsCnt + argsCnt + 1);
+ for (i = 0; i < argsCnt; i++)
+ Eargsp[EargsCnt++] = argsp[i];
+ Eargsp[EargsCnt] = NULL;
+
+ if (print_qouted_cmd_exit) {
+ printf("\"%s\" ", emu);
+ for (i = 1; i < EargsCnt; i++)
+ printf("\"%s\" ", Eargsp[i]);
+ printf("\n");
+ exit(0);
+ }
+
+ if (print_args_exit) {
+ for (i = 1; i < EargsCnt; i++)
+ printf("%s ", Eargsp[i]);
+ printf("\n");
+ exit(0);
+ }
+
+ if (verbose) {
+ printf("Executing: %s", emu);
+ for (i = 0; i < EargsCnt; i++)
+ printf(" %s", Eargsp[i]);
+ printf("\n\n");
+ }
+
+#ifdef __WIN32__
+
+ if (EargsSz != EargsCnt + 1)
+ Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) *
+ sizeof(char *));
+ efree((void *) argsp);
+
+ skip_arg_massage:
+ /*DebugBreak();*/
+
+ if (run_werl) {
+ if (start_detached) {
+ char *p;
+ /* transform werl to erl */
+ p = start_emulator_program+strlen(start_emulator_program);
+ while (--p >= start_emulator_program && *p != '/' && *p != '\\' &&
+ *p != 'W' && *p != 'w')
+ ;
+ if (p >= start_emulator_program && (*p == 'W' || *p == 'w') &&
+ (p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') &&
+ (p[3] == 'L' || p[3] == 'l')) {
+ memmove(p,p+1,strlen(p));
+ }
+ }
+ return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached);
+ } else {
+ return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
+ }
+
+#else
+
+ skip_arg_massage:
+ if (start_detached) {
+ int status = fork();
+ if (status != 0) /* Parent */
+ return 0;
+
+ if (reset_cerl_detached)
+ putenv("CERL_DETACHED_PROG=");
+
+ /* Detach from controlling terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined(TIOCNOTTY)
+ {
+ int fd = open("/dev/tty", O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
+ }
+ }
+#endif
+
+ status = fork();
+ if (status != 0) /* Parent */
+ return 0;
+
+ /*
+ * Grandchild.
+ */
+ close(0);
+ open("/dev/null", O_RDONLY);
+ close(1);
+ open("/dev/null", O_WRONLY);
+ close(2);
+ open("/dev/null", O_WRONLY);
+#ifdef DEBUG
+ execvp(emu, Eargsp); /* "xterm ..." needs to search the path */
+#endif
+ }
+#ifdef DEBUG
+ else
+#endif
+ {
+ execv(emu, Eargsp);
+ }
+ error("Error %d executing \'%s\'.", errno, emu);
+ return 1;
+#endif
+}
+
+
+static void
+usage_aux(void)
+{
+ fprintf(stderr,
+ "Usage: erl [-version] [-sname NAME | -name NAME] "
+ "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] "
+#ifdef __WIN32__
+ "[-start_erl [datafile]] "
+#endif
+ "[-smp "
+#ifdef ERTS_HAVE_SMP_EMU
+ "[enable|"
+#endif
+ "auto|disable"
+#ifdef ERTS_HAVE_SMP_EMU
+ "]"
+#endif
+ "] "
+#ifdef ERTS_HAVE_HYBRID_EMU
+ "[-hybrid] "
+#endif
+ "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
+ "[-args_file FILENAME] "
+ "[+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] [+h HEAP_SIZE] [+K BOOLEAN] "
+ "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] "
+ "[+r] [+s SCHEDULER_OPTION] [+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] [+W<i|w>] "
+ "[args ...]\n");
+ exit(1);
+}
+
+void
+usage(const char *switchname)
+{
+ fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname);
+ usage_aux();
+}
+
+#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
+static void
+usage_notsup(const char *switchname)
+{
+ fprintf(stderr, "Argument \'%s\' not supported.\n", switchname);
+ usage_aux();
+}
+#endif
+
+static void
+usage_msg(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ usage_aux();
+}
+
+static void
+usage_format(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ usage_aux();
+}
+
+void
+start_epmd(char *epmd)
+{
+ char epmd_cmd[MAXPATHLEN+100];
+#ifdef __WIN32__
+ char* arg1 = NULL;
+#endif
+ int result;
+
+ if (!epmd) {
+ epmd = epmd_cmd;
+#ifdef __WIN32__
+ sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir);
+ arg1 = "-daemon";
+#else
+ sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir);
+#endif
+ }
+#ifdef __WIN32__
+ if (arg1 != NULL) {
+ strcat(epmd, " ");
+ strcat(epmd, arg1);
+ }
+ {
+ STARTUPINFO start;
+ PROCESS_INFORMATION pi;
+ memset(&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ if (!CreateProcess(NULL, epmd, NULL, NULL, FALSE,
+ CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS,
+ NULL, NULL, &start, &pi))
+ result = -1;
+ else
+ result = 0;
+ }
+#else
+ result = system(epmd);
+#endif
+ if (result == -1) {
+ fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno);
+ exit(1);
+ }
+}
+
+static void
+add_arg(char *new_arg)
+{
+ if (argsCnt >= argsSz)
+ argsp = (char **) erealloc((void *) argsp,
+ sizeof(char *) * (argsSz += 20));
+ argsp[argsCnt++] = QUOTE(new_arg);
+}
+
+static void
+add_args(char *first_arg, ...)
+{
+ va_list ap;
+ char* arg;
+
+ add_arg(first_arg);
+ va_start(ap, first_arg);
+ while ((arg = va_arg(ap, char *)) != NULL) {
+ add_arg(arg);
+ }
+ va_end(ap);
+}
+
+static void
+ensure_EargsSz(int sz)
+{
+ if (EargsSz < sz)
+ Eargsp = (char **) erealloc((void *) Eargsp,
+ sizeof(char *) * (EargsSz = sz));
+}
+
+static void
+add_Eargs(char *new_arg)
+{
+ if (EargsCnt >= EargsSz)
+ Eargsp = (char **) erealloc((void *) Eargsp,
+ sizeof(char *) * (EargsSz += 20));
+ Eargsp[EargsCnt++] = QUOTE(new_arg);
+}
+
+#if !defined(__WIN32__)
+void error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "erlexec: %s\n", sbuf);
+ exit(1);
+}
+#endif
+
+static void *
+emalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+
+static void
+efree(void *p)
+{
+ free(p);
+}
+
+static int
+is_one_of_strings(char *str, char *strs[])
+{
+ int i, j;
+ for (i = 0; strs[i]; i++) {
+ for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++);
+ if (!str[j] && !strs[i][j])
+ return 1;
+ }
+ return 0;
+}
+
+static char *write_str(char *to, char *from)
+{
+ while (*from)
+ *(to++) = *(from++);
+ *to = '\0';
+ return to;
+}
+
+char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+
+#if defined(__WIN32__)
+
+static void get_start_erl_data(char *file)
+{
+ int fp;
+ char tmpbuffer[512];
+ char start_erl_data[512];
+ int bytesread;
+ char* env;
+ char* reldir;
+ char* otpstring;
+ char* tprogname;
+ if (boot_script)
+ error("Conflicting -start_erl and -boot options");
+ if (config_script)
+ error("Conflicting -start_erl and -config options");
+ env = get_env("RELDIR");
+ if (env)
+ reldir = strsave(env);
+ else {
+ sprintf(tmpbuffer, "%s/releases", rootdir);
+ reldir = strsave(tmpbuffer);
+ }
+ free_env_val(env);
+ if (file == NULL)
+ sprintf(start_erl_data, "%s/start_erl.data", reldir);
+ else
+ sprintf(start_erl_data, "%s", file);
+ fp = _open(start_erl_data, _O_RDONLY );
+ if( fp == -1 )
+ error( "open failed on %s",start_erl_data );
+ else {
+ if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 )
+ error( "Problem reading file %s", start_erl_data );
+ else {
+ tmpbuffer[bytesread]='\0';
+ if ((otpstring = strchr(tmpbuffer,' ')) != NULL) {
+ *otpstring = '\0';
+ otpstring++;
+
+/*
+ * otpstring is the otpversion
+ * tmpbuffer is the emuversion
+*/
+ }
+ }
+ }
+ tprogname = otpstring;
+ while (*tprogname) {
+ if (*tprogname <= ' ') {
+ *tprogname='\0';
+ break;
+ }
+ tprogname++;
+ }
+
+ bindir = emalloc(512);
+ sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer);
+ /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */
+ tprogname = progname;
+ progname = emalloc(strlen(tprogname) + 20);
+ sprintf(progname,"%s -start_erl",tprogname);
+
+ boot_script = emalloc(512);
+ config_script = emalloc(512);
+ sprintf(boot_script, "%s/%s/start", reldir, otpstring);
+ sprintf(config_script, "%s/%s/sys", reldir, otpstring);
+
+}
+
+
+static char *replace_filename(char *path, char *new_base)
+{
+ int plen = strlen(path);
+ char *res = malloc((plen+strlen(new_base)+1)*sizeof(char));
+ char *p;
+
+ strcpy(res,path);
+ for (p = res+plen-1 ;p >= res && *p != '\\'; --p)
+ ;
+ *(p+1) ='\0';
+ strcat(res,new_base);
+ return res;
+}
+
+static char *path_massage(char *long_path)
+{
+ char *p;
+
+ p = malloc(MAX_PATH+1);
+ strcpy(p, long_path);
+ GetShortPathName(p, p, MAX_PATH);
+ return p;
+}
+
+static char *do_lookup_in_section(InitSection *inis, char *name,
+ char *section, char *filename, int is_path)
+{
+ char *p = lookup_init_entry(inis, name);
+
+ if (p == NULL) {
+ error("Could not find key %s in section %s of file %s",
+ name,section,filename);
+ }
+
+ if (is_path) {
+ return path_massage(p);
+ } else {
+ return strsave(p);
+ }
+}
+
+
+static void get_parameters(int argc, char** argv)
+{
+ char *p;
+ char buffer[MAX_PATH];
+ char *ini_filename;
+ HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini
+ that resides in the same dir as erl.exe, not
+ an erl.ini in our directory */
+ InitFile *inif;
+ InitSection *inis;
+
+ if (module == NULL) {
+ error("Cannot GetModuleHandle()");
+ }
+
+ if (GetModuleFileName(module,buffer,MAX_PATH) == 0) {
+ error("Could not GetModuleFileName");
+ }
+
+ ini_filename = replace_filename(buffer,INI_FILENAME);
+
+ if ((inif = load_init_file(ini_filename)) == NULL) {
+ /* Assume that the path is absolute and that
+ it does not contain any symbolic link */
+
+ char buffer[MAX_PATH];
+
+ /* Determine bindir */
+ if (GetEnvironmentVariable("ERLEXEC_DIR", buffer, MAX_PATH) == 0) {
+ strcpy(buffer, ini_filename);
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p)
+ ;
+ *p ='\0';
+ }
+ bindir = path_massage(buffer);
+
+ /* Determine rootdir */
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p)
+ ;
+ p--;
+ for (;p >= buffer && *p != '\\'; --p)
+ ;
+ *p ='\0';
+ rootdir = path_massage(buffer);
+
+ /* Hardcoded progname */
+ progname = strsave(DEFAULT_PROGNAME);
+ } else {
+ if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) {
+ error("Could not find section %s in init file %s",
+ INI_SECTION, ini_filename);
+ }
+
+ bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1);
+ rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION,
+ ini_filename,1);
+ progname = do_lookup_in_section(inis, "Progname", INI_SECTION,
+ ini_filename,0);
+ free_init_file(inif);
+ }
+
+ emu = EMULATOR_EXECUTABLE;
+ start_emulator_program = strsave(argv[0]);
+
+ free(ini_filename);
+}
+
+static void
+get_home(void)
+{
+ int len;
+ char tmpstr[MAX_PATH+1];
+ char* homedrive;
+ char* homepath;
+
+ homedrive = get_env("HOMEDRIVE");
+ homepath = get_env("HOMEPATH");
+ if (!homedrive || !homepath) {
+ if (len = GetWindowsDirectory(tmpstr,MAX_PATH)) {
+ home = emalloc(len+1);
+ strcpy(home,tmpstr);
+ } else
+ error("HOMEDRIVE or HOMEPATH is not set and GetWindowsDir failed");
+ } else {
+ home = emalloc(strlen(homedrive)+strlen(homepath)+1);
+ strcpy(home, homedrive);
+ strcat(home, homepath);
+ }
+ free_env_val(homedrive);
+ free_env_val(homepath);
+}
+
+#else
+
+static void
+get_parameters(int argc, char** argv)
+{
+ progname = get_env("PROGNAME");
+ if (!progname) {
+ progname = strsave(DEFAULT_PROGNAME);
+ }
+
+ emu = get_env("EMU");
+ if (!emu) {
+ emu = strsave(EMULATOR_EXECUTABLE);
+ }
+
+ bindir = get_env("BINDIR");
+ if (!bindir) {
+ /* Determine bindir from absolute path to executable */
+ char *p;
+ char buffer[PATH_MAX];
+ strcpy(buffer, argv[0]);
+
+ for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p)
+ ;
+ *p ='\0';
+ bindir = strsave(buffer);
+ }
+
+ rootdir = get_env("ROOTDIR");
+ if (!rootdir) {
+ /* Determine rootdir from absolute path to bindir */
+ char *p;
+ char buffer[PATH_MAX];
+ strcpy(buffer, bindir);
+
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p)
+ ;
+ p--;
+ for (; p >= buffer && *p != '/'; --p)
+ ;
+ *p ='\0';
+ rootdir = strsave(buffer);
+ }
+
+ if (!progname || !emu || !rootdir || !bindir) {
+ error("PROGNAME, EMU, ROOTDIR and BINDIR must be set");
+ }
+}
+
+static void
+get_home(void)
+{
+ home = get_env("HOME");
+ if (home == NULL)
+ error("HOME must be set");
+}
+
+#endif
+
+static void add_epmd_port(void)
+{
+ char* port = get_env("ERL_EPMD_PORT");
+ if (port != NULL) {
+ add_args("-epmd_port", port, NULL);
+ }
+}
+
+static char **build_args_from_env(char *env_var)
+{
+ char *value = get_env(env_var);
+ char **res = build_args_from_string(value);
+ free_env_val(value);
+ return res;
+}
+
+static char **build_args_from_string(char *string)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int alloced = 0;
+ char **cur_s = NULL; /* Initialized to avoid warning. */
+ int s_alloced = 0;
+ int s_pos = 0;
+ char *p = string;
+ enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state;
+
+#define ENSURE() \
+ if (s_pos >= s_alloced) { \
+ if (!*cur_s) { \
+ *cur_s = emalloc(s_alloced = 20); \
+ } else { \
+ *cur_s = erealloc(*cur_s, s_alloced += 20); \
+ } \
+ }
+
+
+ if (!p)
+ return NULL;
+ argv = emalloc(sizeof(char *) * (alloced = 10));
+ state = Start;
+ for(;;) {
+ switch (state) {
+ case Start:
+ if (!*p)
+ goto done;
+ if (argc >= alloced - 1) { /* Make room for extra NULL */
+ argv = erealloc(argv, (alloced += 10) * sizeof(char *));
+ }
+ cur_s = argc + argv;
+ *cur_s = NULL;
+ s_pos = 0;
+ s_alloced = 0;
+ state = Build0;
+ break;
+ case Build0:
+ switch (*p) {
+ case ' ':
+ ++p;
+ break;
+ case '\0':
+ state = Start;
+ break;
+ default:
+ state = Build;
+ break;
+ }
+ break;
+ case Build:
+ switch (*p) {
+ case ' ':
+ case '\0':
+ ENSURE();
+ (*cur_s)[s_pos] = '\0';
+ ++argc;
+ state = Start;
+ break;
+ case '"':
+ ++p;
+ state = BuildDQuoted;
+ break;
+ case '\'':
+ ++p;
+ state = BuildSQuoted;
+ break;
+ case '\\':
+ ++p;
+ state = AcceptNext;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case BuildDQuoted:
+ switch (*p) {
+ case '"':
+ ++p;
+ /* fall through */
+ case '\0':
+ state = Build;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case BuildSQuoted:
+ switch (*p) {
+ case '\'':
+ ++p;
+ /* fall through */
+ case '\0':
+ state = Build;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case AcceptNext:
+ if (!*p) {
+ state = Build;
+ } else {
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ }
+ state = Build;
+ break;
+ }
+ }
+done:
+ argv[argc] = NULL; /* Sure to be large enough */
+ if (!argc) {
+ efree(argv);
+ return NULL;
+ }
+ return argv;
+#undef ENSURE
+}
+
+static char *
+errno_string(void)
+{
+ char *str = strerror(errno);
+ if (!str)
+ return "unknown error";
+ return str;
+}
+
+static char **
+read_args_file(char *filename)
+{
+ int c, aix = 0, quote = 0, cmnt = 0, asize = 0;
+ char **res, *astr = NULL;
+ FILE *file;
+
+#undef EAF_CMNT
+#undef EAF_QUOTE
+#undef SAVE_CHAR
+
+#define EAF_CMNT (1 << 8)
+#define EAF_QUOTE (1 << 9)
+#define SAVE_CHAR(C) \
+ do { \
+ if (!astr) \
+ astr = emalloc(sizeof(char)*(asize = 20)); \
+ if (aix == asize) \
+ astr = erealloc(astr, sizeof(char)*(asize += 20)); \
+ if (' ' != (char) (C)) \
+ astr[aix++] = (char) (C); \
+ else if (aix > 0 && astr[aix-1] != ' ') \
+ astr[aix++] = ' '; \
+ } while (0)
+
+ do {
+ errno = 0;
+ file = fopen(filename, "r");
+ } while (!file && errno == EINTR);
+ if (!file) {
+ usage_format("Failed to open arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
+ }
+
+ while (1) {
+ c = getc(file);
+ if (c == EOF) {
+ if (ferror(file)) {
+ if (errno == EINTR) {
+ clearerr(file);
+ continue;
+ }
+ usage_format("Failed to read arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
+ }
+ break;
+ }
+
+ switch (quote | cmnt | c) {
+ case '\\':
+ quote = EAF_QUOTE;
+ break;
+ case '#':
+ cmnt = EAF_CMNT;
+ break;
+ case EAF_CMNT|'\n':
+ cmnt = 0;
+ /* Fall through... */
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
+ if (!quote)
+ c = ' ';
+ /* Fall through... */
+ default:
+ if (!cmnt)
+ SAVE_CHAR(c);
+ quote = 0;
+ break;
+ }
+ }
+
+ SAVE_CHAR('\0');
+
+ fclose(file);
+
+ if (astr[0] == '\0')
+ res = NULL;
+ else
+ res = build_args_from_string(astr);
+
+ efree(astr);
+
+ return res;
+
+#undef EAF_CMNT
+#undef EAF_QUOTE
+#undef SAVE_CHAR
+}
+
+typedef struct {
+ char **argv;
+ int argc;
+ int size;
+} argv_buf;
+
+static void
+trim_argv_buf(argv_buf *abp)
+{
+ abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc));
+}
+
+static void
+save_arg(argv_buf *abp, char *arg)
+{
+ if (abp->size <= abp->argc) {
+ if (!abp->argv)
+ abp->argv = emalloc(sizeof(char *)*(abp->size = 100));
+ else
+ abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100));
+ }
+ abp->argv[abp->argc++] = arg;
+}
+
+#define DEF_ARGV_STACK_SIZE 10
+#define ARGV_STACK_SIZE_INCR 50
+
+typedef struct {
+ char **argv;
+ int ix;
+} argv_stack_element;
+
+typedef struct {
+ int top_ix;
+ int size;
+ argv_stack_element *base;
+ argv_stack_element def_buf[DEF_ARGV_STACK_SIZE];
+} argv_stack;
+
+#define ARGV_STACK_INIT(S) \
+do { \
+ (S)->top_ix = 0; \
+ (S)->size = DEF_ARGV_STACK_SIZE; \
+ (S)->base = &(S)->def_buf[0]; \
+} while (0)
+
+static void
+push_argv(argv_stack *stck, char **argv, int ix)
+{
+ if (stck->top_ix == stck->size) {
+ if (stck->base != &stck->def_buf[0]) {
+ stck->size += ARGV_STACK_SIZE_INCR;
+ stck->base = erealloc(stck->base,
+ sizeof(argv_stack_element)*stck->size);
+ }
+ else {
+ argv_stack_element *base;
+ base = emalloc(sizeof(argv_stack_element)
+ *(stck->size + ARGV_STACK_SIZE_INCR));
+ memcpy((void *) base,
+ (void *) stck->base,
+ sizeof(argv_stack_element)*stck->size);
+ stck->base = base;
+ stck->size += ARGV_STACK_SIZE_INCR;
+ }
+ }
+ stck->base[stck->top_ix].argv = argv;
+ stck->base[stck->top_ix++].ix = ix;
+}
+
+static void
+pop_argv(argv_stack *stck, char ***argvp, int *ixp)
+{
+ if (stck->top_ix == 0) {
+ *argvp = NULL;
+ *ixp = 0;
+ }
+ else {
+ *argvp = stck->base[--stck->top_ix].argv;
+ *ixp = stck->base[stck->top_ix].ix;
+ if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) {
+ efree(stck->base);
+ stck->base = &stck->def_buf[0];
+ stck->size = DEF_ARGV_STACK_SIZE;
+ }
+ }
+}
+
+static void
+get_file_args(char *filename, argv_buf *abp, argv_buf *xabp)
+{
+ argv_stack stck;
+ int i;
+ char **argv;
+
+ ARGV_STACK_INIT(&stck);
+
+ i = 0;
+ argv = read_args_file(filename);
+
+ while (argv) {
+
+ while (argv[i]) {
+ if (strcmp(argv[i], "-args_file") == 0) {
+ char **new_argv;
+ char *fname;
+ if (!argv[++i])
+ usage("-args_file");
+ fname = argv[i++];
+ new_argv = read_args_file(fname);
+ if (new_argv) {
+ if (argv[i])
+ push_argv(&stck, argv, i);
+ else
+ efree(argv);
+ i = 0;
+ argv = new_argv;
+ }
+ }
+ else {
+ if (strcmp(argv[i], "-extra") == 0) {
+ i++;
+ while (argv[i])
+ save_arg(xabp, argv[i++]);
+ break;
+ }
+ save_arg(abp, argv[i++]);
+ }
+ }
+
+ efree(argv);
+
+ pop_argv(&stck, &argv, &i);
+ }
+}
+
+static void
+initial_argv_massage(int *argc, char ***argv)
+{
+ argv_buf ab = {0}, xab = {0};
+ int ix, vix, ac;
+ char **av;
+ struct {
+ int argc;
+ char **argv;
+ } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL},
+ {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}};
+ /*
+ * The environment flag containing OTP release is intentionally
+ * undocumented and intended for OTP internal use only.
+ */
+
+ vix = 0;
+ av = build_args_from_env("ERL_AFLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ /* command line */
+ if (*argc > 1) {
+ avv[vix].argc = *argc - 1;
+ avv[vix++].argv = &(*argv)[1];
+ }
+
+ av = build_args_from_env("ERL_FLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ av = build_args_from_env("ERL_ZFLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ if (vix == (*argc > 1 ? 1 : 0)) {
+ /* Only command line argv; check if we can use argv as it is... */
+ ac = *argc;
+ av = *argv;
+ for (ix = 1; ix < ac; ix++) {
+ if (strcmp(av[ix], "-args_file") == 0) {
+ /* ... no; we need to expand arguments from
+ file into argument list */
+ goto build_new_argv;
+ }
+ if (strcmp(av[ix], "-extra") == 0) {
+ break;
+ }
+ }
+
+ /* ... yes; we can use argv as it is. */
+ return;
+ }
+
+ build_new_argv:
+
+ save_arg(&ab, (*argv)[0]);
+
+ vix = 0;
+ while (avv[vix].argv) {
+ ac = avv[vix].argc;
+ av = avv[vix].argv;
+
+ ix = 0;
+ while (ix < ac && av[ix]) {
+ if (strcmp(av[ix], "-args_file") == 0) {
+ if (++ix == ac)
+ usage("-args_file");
+ get_file_args(av[ix++], &ab, &xab);
+ }
+ else {
+ if (strcmp(av[ix], "-extra") == 0) {
+ ix++;
+ while (ix < ac && av[ix])
+ save_arg(&xab, av[ix++]);
+ break;
+ }
+ save_arg(&ab, av[ix++]);
+ }
+ }
+
+ vix++;
+ }
+
+ vix = 0;
+ while (avv[vix].argv) {
+ if (avv[vix].argc == INT_MAX) /* not command line */
+ efree(avv[vix].argv);
+ vix++;
+ }
+
+ if (xab.argc) {
+ save_arg(&ab, "-extra");
+ for (ix = 0; ix < xab.argc; ix++)
+ save_arg(&ab, xab.argv[ix]);
+ efree(xab.argv);
+ }
+
+ save_arg(&ab, NULL);
+ trim_argv_buf(&ab);
+ *argv = ab.argv;
+ *argc = ab.argc - 1;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ if (*s == ' ' || *s == '"') {
+ mustQuote = YES;
+ n++;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*s == '"') {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+
+#endif
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
new file mode 100644
index 0000000000..ab37d4af46
--- /dev/null
+++ b/erts/etc/common/escript.c
@@ -0,0 +1,697 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2007-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%
+ */
+/*
+ * Purpose: escript front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#define BOOL int
+#define FALSE 0
+#define TRUE 1
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define DIRSEPSTR "\\"
+# define PATHSEPSTR ";"
+# define PMAX MAX_PATH
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define DIRSEPSTR "/"
+# define PATHSEPSTR ":"
+# define PMAX PATH_MAX
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define UNSHIFT3(s, t, u) UNSHIFT(u); UNSHIFT(t); UNSHIFT(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+#define LINEBUFSZ 1024
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static void efree(void *p);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ efree(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ efree(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ efree(value);
+#endif
+}
+/*
+ * Find absolute path to this program
+ */
+
+static char *
+find_prog(char *origpath)
+{
+ char relpath[PMAX];
+ char abspath[PMAX];
+
+ strcpy(relpath, origpath);
+
+ if (strstr(relpath, DIRSEPSTR) == NULL) {
+ /* Just a base name */
+ char *envpath;
+
+ envpath = get_env("PATH");
+ if (envpath) {
+ /* Try to find the executable in the path */
+ char dir[PMAX];
+ char *beg = envpath;
+ char *end;
+ int sz;
+
+#ifdef __WIN32__
+ HANDLE dir_handle; /* Handle to directory. */
+ char wildcard[PMAX]; /* Wildcard to search for. */
+ WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */
+#else
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+#endif /* __WIN32__ */
+
+ BOOL look_for_sep = TRUE;
+
+ while (look_for_sep) {
+ end = strstr(beg, PATHSEPSTR);
+ if (end != NULL) {
+ sz = end - beg;
+ strncpy(dir, beg, sz);
+ dir[sz] = '\0';
+ } else {
+ sz = strlen(beg);
+ strcpy(dir, beg);
+ look_for_sep = FALSE;
+ }
+ beg = end + 1;
+
+#ifdef __WIN32__
+ strcpy(wildcard, dir);
+ strcat(wildcard, DIRSEPSTR);
+ strcat(wildcard, relpath); /* basename */
+ dir_handle = FindFirstFile(wildcard, &find_data);
+ if (dir_handle == INVALID_HANDLE_VALUE) {
+ /* Try next directory in path */
+ continue;
+ } else {
+ /* Wow we found the executable. */
+ strcpy(abspath, wildcard);
+ FindClose(dir_handle);
+ return strsave(abspath);
+ }
+#else
+ dp = opendir(dir);
+ if (dp != NULL) {
+ while (TRUE) {
+ dirp = readdir(dp);
+ if (dirp == NULL) {
+ closedir(dp);
+ /* Try next directory in path */
+ break;
+ }
+
+ if (strcmp(origpath, dirp->d_name) == 0) {
+ /* Wow we found the executable. */
+ strcpy(abspath, dir);
+ strcat(abspath, DIRSEPSTR);
+ strcat(abspath, dirp->d_name);
+ closedir(dp);
+ return strsave(abspath);
+ }
+ }
+ }
+#endif /* __WIN32__ */
+ }
+ }
+ }
+
+ {
+#ifdef __WIN32__
+ DWORD size;
+ char *absrest;
+ size = GetFullPathName(relpath, PMAX, abspath, &absrest);
+ if ((size == 0) || (size > PMAX)) {
+
+#else
+ if (!realpath(relpath, abspath)) {
+#endif /* __WIN32__ */
+ /* Cannot determine absolute path to escript. Try the relative. */
+ return strsave(relpath);
+ } else {
+ return strsave(abspath);
+ }
+ }
+}
+
+static void
+append_shebang_args(char* scriptname)
+{
+ /* Open script file */
+ FILE* fd = fopen (scriptname,"r");
+
+ if (fd != NULL) {
+ /* Read first line in script file */
+ static char linebuf[LINEBUFSZ];
+ char* ptr = fgets(linebuf, LINEBUFSZ, fd);
+
+ if (ptr != NULL && linebuf[0] == '#' && linebuf[1] == '!') {
+ /* Try to find args on second or third line */
+ ptr = fgets(linebuf, LINEBUFSZ, fd);
+ if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
+ /* Use second line */
+ } else {
+ /* Try third line */
+ ptr = fgets(linebuf, LINEBUFSZ, fd);
+ if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
+ /* Use third line */
+ } else {
+ /* Do not use any line */
+ ptr = NULL;
+ }
+ }
+
+ if (ptr != NULL) {
+ /* Use entire line but the leading chars */
+ char* beg = linebuf + 3;
+ char* end;
+ BOOL newline = FALSE;
+
+ /* Push all args */
+ while(beg && !newline) {
+ /* Skip leading spaces */
+ while (beg && beg[0] == ' ') {
+ beg++;
+ }
+
+ /* Find end of arg */
+ end = beg;
+ while (end && end[0] != ' ') {
+ if (end[0] == '\n') {
+ newline = TRUE;
+ end[0]= '\0';
+ break;
+ } else {
+ end++;
+ }
+ }
+
+ /* Empty arg */
+ if (beg == end) {
+ break;
+ }
+ end[0]= '\0';
+ PUSH(beg);
+ beg = end + 1;
+ }
+ }
+ }
+ fclose(fd);
+ } else {
+ error("Failed to open file: %s", scriptname);
+ }
+}
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char* env;
+ char* basename;
+ char* absname;
+ char scriptname[PMAX];
+ char** last_opt;
+ char** first_opt;
+
+ emulator = env = get_env("ESCRIPT_EMULATOR");
+ if (emulator == NULL) {
+ emulator = get_default_emulator(argv[0]);
+ }
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+1000;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH("-noshell");
+
+ /* Determine basename of the executable */
+ for (basename = argv[0]+strlen(argv[0]);
+ basename > argv[0] && !(IS_DIRSEP(basename[-1]));
+ --basename)
+ ;
+
+ first_opt = argv;
+ last_opt = argv;
+
+#ifdef __WIN32__
+ if (_stricmp(basename, "escript.exe") == 0) {
+#else
+ if (strcmp(basename, "escript") == 0) {
+#endif
+ /*
+ * Push all options (without the hyphen) before the script name.
+ */
+
+ while (argc > 1 && argv[1][0] == '-') {
+ PUSH(argv[1]+1);
+ argc--;
+ argv++;
+ last_opt = argv;
+ }
+
+ if (argc <= 1) {
+ error("Missing filename\n");
+ }
+ strcpy(scriptname, argv[1]);
+ argc--;
+ argv++;
+ } else {
+#ifdef __WIN32__
+ int len;
+#endif
+ absname = find_prog(argv[0]);
+ strcpy(scriptname, absname);
+ efree(absname);
+#ifdef __WIN32__
+ len = strlen(scriptname);
+ if (len >= 4 && _stricmp(scriptname+len-4, ".exe") == 0) {
+ scriptname[len-4] = '\0';
+ }
+#endif
+ strcat(scriptname, ".escript");
+ }
+
+ /*
+ * Read options from the %%! row in the script and add them as args
+ */
+
+ append_shebang_args(scriptname);
+
+ /*
+ * Push the script name and everything following it as extra arguments.
+ */
+
+ PUSH3("-run", "escript", "start");
+
+ /*
+ * Push all options (without the hyphen) before the script name.
+ */
+
+ while (first_opt != last_opt) {
+ PUSH(first_opt[1]+1);
+ first_opt++;
+ }
+
+ PUSH("-extra");
+ PUSH(scriptname);
+ while (argc > 1) {
+ PUSH(argv[1]);
+ argc--, argv++;
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = emalloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "escript: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "escript: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static void
+efree(void *p)
+{
+ free(p);
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = FALSE;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = TRUE;
+ continue;
+ case '"':
+ mustQuote = TRUE;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
new file mode 100644
index 0000000000..4f738947b7
--- /dev/null
+++ b/erts/etc/common/heart.c
@@ -0,0 +1,1142 @@
+/*
+ * %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%
+ */
+/**
+ *
+ * File: heart.c
+ * Purpose: Portprogram for supervision of the Erlang emulator.
+ *
+ * Synopsis: heart
+ *
+ * SPAWNING FROM ERLANG
+ *
+ * This program is started from Erlang as follows,
+ *
+ * Port = open_port({spawn, 'heart'}, [{packet, 2}]),
+ *
+ * ROLE OF THIS PORT PROGRAM
+ *
+ * This program is started by the Erlang emulator. It communicates
+ * with the emulator through file descriptor 0 (standard input).
+ *
+ * MESSAGE FORMAT
+ *
+ * All messages have the following format (a value in parentheses
+ * indicate field length in bytes),
+ *
+ * {Length(2), Operation(1)}
+ *
+ * START ACK
+ *
+ * When this program has started it sends an START ACK message to Erlang.
+ *
+ * HEART_BEATING
+ *
+ * This program expects a heart beat messages. If it does not receive a
+ * heart beat message from Erlang within heart_beat_timeout seconds, it
+ * reboots the system. The variable heart_beat_timeout is exported (so
+ * that it can be set from the shell in VxWorks, as is the variable
+ * heart_beat_report_delay). When using Solaris, the system is rebooted
+ * by executing the command stored in the environment variable
+ * HEART_COMMAND.
+ *
+ * BLOCKING DESCRIPTORS
+ *
+ * All file descriptors in this program are blocking. This can lead
+ * to deadlocks. The emulator reads and writes are blocking.
+ *
+ * STANDARD INPUT, OUTPUT AND ERROR
+ *
+ * This program communicates with Erlang through the standard
+ * input and output file descriptors (0 and 1). These descriptors
+ * (and the standard error descriptor 2) must NOT be closed
+ * explicitely by this program at termination (in UNIX it is
+ * taken care of by the operating system itself; in VxWorks
+ * it is taken care of by the spawn driver part of the Emulator).
+ *
+ * END OF FILE
+ *
+ * If a read from a file descriptor returns zero (0), it means
+ * that there is no process at the other end of the connection
+ * having the connection open for writing (end-of-file).
+ *
+ * HARDWARE WATCHDOG
+ *
+ * When used with VxWorks(with CPU40), the hardware
+ * watchdog is enabled, making sure that the system reboots
+ * even if the heart port program malfunctions or the system
+ * is completely overloaded.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef __WIN32__
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#include <process.h>
+#endif
+#ifdef VXWORKS
+#include "sys.h"
+#endif
+
+/*
+ * Implement time correction using times() call even on Linuxes
+ * that can simulate gethrtime with clock_gettime, no use implementing
+ * a phony gethrtime in this file as the time questions are so infrequent.
+ */
+#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
+# define HEART_CORRECT_USING_TIMES 1
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <stdarg.h>
+
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef VXWORKS
+# include <vxWorks.h>
+# include <ioLib.h>
+# include <selectLib.h>
+# include <netinet/in.h>
+# include <rebootLib.h>
+# include <sysLib.h>
+# include <taskLib.h>
+# include <wdLib.h>
+# include <taskHookLib.h>
+# include <selectLib.h>
+#endif
+#if !defined(__WIN32__) && !defined(VXWORKS)
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <sys/time.h>
+# include <unistd.h>
+# include <signal.h>
+# if defined(HEART_CORRECT_USING_TIMES)
+# include <sys/times.h>
+# include <limits.h>
+# endif
+#endif
+
+#define HEART_COMMAND_ENV "HEART_COMMAND"
+
+#define MSG_HDR_SIZE 2
+#define MSG_HDR_PLUS_OP_SIZE 3
+#define MSG_BODY_SIZE 2048
+#define MSG_TOTAL_SIZE 2050
+
+unsigned char cmd[MSG_BODY_SIZE];
+
+struct msg {
+ unsigned short len;
+ unsigned char op;
+ unsigned char fill[MSG_BODY_SIZE]; /* one too many */
+};
+
+/* operations */
+#define HEART_ACK 1
+#define HEART_BEAT 2
+#define SHUT_DOWN 3
+#define SET_CMD 4
+#define CLEAR_CMD 5
+#define GET_CMD 6
+#define HEART_CMD 7
+
+
+/* Maybe interesting to change */
+
+/* Times in seconds */
+#define HEART_BEAT_BOOT_DELAY 60 /* 1 minute */
+#define SELECT_TIMEOUT 5 /* Every 5 seconds we reset the
+ watchdog timer */
+
+/* heart_beat_timeout is the maximum gap in seconds between two
+ consecutive heart beat messages from Erlang, and HEART_BEAT_BOOT_DELAY
+ is the the extra delay that wd_keeper allows for, to give heart a
+ chance to reboot in the "normal" way before the hardware watchdog
+ enters the scene. heart_beat_report_delay is the time allowed for reporting
+ before rebooting under VxWorks. */
+
+int heart_beat_timeout = 60;
+int heart_beat_report_delay = 30;
+int heart_beat_boot_delay = HEART_BEAT_BOOT_DELAY;
+/* All current platforms have a process identifier that
+ fits in an unsigned long and where 0 is an impossible or invalid value */
+unsigned long heart_beat_kill_pid = 0;
+
+#define VW_WD_TIMEOUT (heart_beat_timeout+heart_beat_report_delay+heart_beat_boot_delay)
+#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay)
+
+/* reasons for reboot */
+#define R_TIMEOUT 1
+#define R_CLOSED 2
+#define R_ERROR 3
+#define R_SHUT_DOWN 4
+
+
+/* macros */
+
+#define NULLFDS ((fd_set *) NULL)
+#define NULLTV ((struct timeval *) NULL)
+
+/* prototypes */
+
+static int message_loop(int,int);
+static void do_terminate(int);
+static int notify_ack(int);
+static int heart_cmd_reply(int, char *);
+static int write_message(int, struct msg *);
+static int read_message(int, struct msg *);
+static int read_skip(int, char *, int, int);
+static int read_fill(int, char *, int);
+static void print_error(const char *,...);
+static void debugf(const char *,...);
+static void init_timestamp(void);
+static time_t timestamp(time_t *);
+
+#ifdef __WIN32__
+static BOOL enable_privilege(void);
+static BOOL do_shutdown(int);
+static void print_last_error(void);
+static HANDLE start_reader_thread(void);
+static DWORD WINAPI reader(LPVOID);
+static int test_win95(void);
+#define read _read
+#define write _write
+#endif
+
+/* static variables */
+
+static char program_name[256];
+static int erlin_fd = 0, erlout_fd = 1; /* std in and out */
+static int debug_on = 0;
+#ifdef __WIN32__
+static HANDLE hreader_thread;
+static HANDLE hevent_dataready;
+static struct msg m, *mp = &m;
+static int tlen; /* total message length */
+static FILE* conh;
+
+#endif
+
+static int
+is_env_set(char *key)
+{
+#ifdef __WIN32__
+ char buf[1];
+ DWORD sz = (DWORD) sizeof(buf);
+ SetLastError(0);
+ sz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) buf, sz);
+ return sz || GetLastError() != ERROR_ENVVAR_NOT_FOUND;
+#else
+ return getenv(key) != NULL;
+#endif
+}
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = malloc(size);
+ if (!value) {
+ print_error("Failed to allocate memory. Terminating...");
+ exit(1);
+ }
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+/*
+ * main
+ */
+static void get_arguments(int argc, char** argv) {
+ int i = 1;
+ int h;
+ int w;
+ unsigned long p;
+
+ while (i < argc) {
+ switch (argv[i][0]) {
+ case '-':
+ switch (argv[i][1]) {
+ case 'h':
+ if (strcmp(argv[i], "-ht") == 0)
+ if (sscanf(argv[i+1],"%i",&h) ==1)
+ if ((h > 10) && (h <= 65535)) {
+ heart_beat_timeout = h;
+ fprintf(stderr,"heart_beat_timeout = %d\n",h);
+ i++;
+ }
+ break;
+ case 'w':
+ if (strcmp(argv[i], "-wt") == 0)
+ if (sscanf(argv[i+1],"%i",&w) ==1)
+ if ((w > 10) && (w <= 65535)) {
+ heart_beat_boot_delay = w;
+ fprintf(stderr,"heart_beat_boot_delay = %d\n",w);
+ i++;
+ }
+ break;
+ case 'p':
+ if (strcmp(argv[i], "-pid") == 0)
+ if (sscanf(argv[i+1],"%lu",&p) ==1){
+ heart_beat_kill_pid = p;
+ fprintf(stderr,"heart_beat_kill_pid = %lu\n",p);
+ i++;
+ }
+ break;
+#ifdef __WIN32__
+ case 's':
+ if (strcmp(argv[i], "-shutdown") == 0){
+ do_shutdown(1);
+ exit(0);
+ }
+ break;
+#endif
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ i++;
+ }
+ debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p);
+}
+
+int
+main(int argc, char **argv)
+{
+ get_arguments(argc,argv);
+ if (is_env_set("HEART_DEBUG"))
+ debug_on=1;
+#ifdef __WIN32__
+ if (debug_on) {
+ if(!is_env_set("ERLSRV_SERVICE_NAME")) {
+ /* this redirects stderr to a separate console (for debugging purposes)*/
+ erlin_fd = _dup(0);
+ erlout_fd = _dup(1);
+ AllocConsole();
+ conh = freopen("CONOUT$","w",stderr);
+ if (conh != NULL)
+ fprintf(conh,"console alloced\n");
+ }
+ debugf("stderr\n");
+ }
+ _setmode(erlin_fd,_O_BINARY);
+ _setmode(erlout_fd,_O_BINARY);
+#endif
+ strcpy(program_name, argv[0]);
+ notify_ack(erlout_fd);
+ cmd[0] = '\0';
+ do_terminate(message_loop(erlin_fd,erlout_fd));
+ return 0;
+}
+
+/*
+ * message loop
+ */
+static int
+message_loop(erlin_fd, erlout_fd)
+ int erlin_fd, erlout_fd;
+{
+ int i;
+ time_t now, last_received;
+#ifdef __WIN32__
+ DWORD wresult;
+#else
+ fd_set read_fds;
+ int max_fd;
+ struct timeval timeout;
+ int tlen; /* total message length */
+ struct msg m, *mp = &m;
+#endif
+
+ init_timestamp();
+ timestamp(&now);
+ last_received = now;
+#ifdef __WIN32__
+ hevent_dataready = CreateEvent(NULL,FALSE,FALSE,NULL);
+ hreader_thread = start_reader_thread();
+#else
+ max_fd = erlin_fd;
+#endif
+
+ while (1) {
+#ifdef __WIN32__
+ wresult = WaitForSingleObject(hevent_dataready,SELECT_TIMEOUT*1000+ 2);
+ if (wresult == WAIT_FAILED) {
+ print_last_error();
+ return R_ERROR;
+ }
+
+ if (wresult == WAIT_TIMEOUT) {
+ debugf("wait timed out\n");
+ i = 0;
+ } else {
+ debugf("wait ok\n");
+ i = 1;
+ }
+#else
+ FD_ZERO(&read_fds); /* ZERO on each turn */
+ FD_SET(erlin_fd, &read_fds);
+ timeout.tv_sec = SELECT_TIMEOUT; /* On Linux timeout is modified
+ by select */
+ timeout.tv_usec = 0;
+ if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, &timeout)) < 0) {
+ print_error("error in select.");
+ return R_ERROR;
+ }
+#endif
+ /*
+ * Maybe heart beat time-out
+ * If we havn't got anything in 60 seconds we reboot, even if we may
+ * have got something in the last 5 seconds. We may end up here if
+ * the system clock is adjusted with more than 55 seconds, but we
+ * regard this as en error and reboot anyway.
+ */
+ timestamp(&now);
+ if (now > last_received + heart_beat_timeout) {
+ print_error("heart-beat time-out.");
+ return R_TIMEOUT;
+ }
+ /*
+ * Do not check fd-bits if select timeout
+ */
+ if (i == 0) {
+ continue;
+ }
+ /*
+ * Message from ERLANG
+ */
+#ifdef __WIN32__
+ if (wresult == WAIT_OBJECT_0) {
+ if (tlen < 0) {
+#else
+ if (FD_ISSET(erlin_fd, &read_fds)) {
+ if ((tlen = read_message(erlin_fd, mp)) < 0) {
+#endif
+ print_error("error in read_message.");
+ return R_ERROR;
+ }
+ if ((tlen > MSG_HDR_SIZE) && (tlen <= MSG_TOTAL_SIZE)) {
+ switch (mp->op) {
+ case HEART_BEAT:
+ timestamp(&last_received);
+#ifdef USE_WATCHDOG
+ /* reset the hardware watchdog timer */
+ wd_reset();
+#endif
+ break;
+ case SHUT_DOWN:
+ return R_SHUT_DOWN;
+ case SET_CMD:
+ /* override the HEART_COMMAND_ENV command */
+ memcpy(&cmd, &(mp->fill[0]),
+ tlen-MSG_HDR_PLUS_OP_SIZE);
+ cmd[tlen-MSG_HDR_PLUS_OP_SIZE] = '\0';
+ notify_ack(erlout_fd);
+ break;
+ case CLEAR_CMD:
+ /* use the HEART_COMMAND_ENV command */
+ cmd[0] = '\0';
+ notify_ack(erlout_fd);
+ break;
+ case GET_CMD:
+ /* send back command string */
+ {
+ char *env = NULL;
+ char *command
+ = (cmd[0]
+ ? (char *)cmd
+ : (env = get_env(HEART_COMMAND_ENV)));
+ /* Not set and not in env return "" */
+ if (!command) command = "";
+ heart_cmd_reply(erlout_fd, command);
+ free_env_val(env);
+ }
+ break;
+ default:
+ /* ignore all other messages */
+ break;
+ }
+ } else if (tlen == 0) {
+ /* Erlang has closed its end */
+ print_error("Erlang has closed.");
+ return R_CLOSED;
+ }
+ /* Junk erroneous messages */
+ }
+ }
+}
+
+#if defined(__WIN32__)
+static void
+kill_old_erlang(void){
+ HANDLE erlh;
+ DWORD exit_code;
+ if(heart_beat_kill_pid != 0){
+ if((erlh = OpenProcess(PROCESS_TERMINATE |
+ SYNCHRONIZE |
+ PROCESS_QUERY_INFORMATION ,
+ FALSE,
+ (DWORD) heart_beat_kill_pid)) == NULL){
+ return;
+ }
+ if(!TerminateProcess(erlh, 1)){
+ CloseHandle(erlh);
+ return;
+ }
+ if(WaitForSingleObject(erlh,5000) != WAIT_OBJECT_0){
+ print_error("Old process did not die, "
+ "WaitForSingleObject timed out.");
+ CloseHandle(erlh);
+ return;
+ }
+ if(!GetExitCodeProcess(erlh, &exit_code)){
+ print_error("Old process did not die, "
+ "GetExitCodeProcess failed.");
+ }
+ CloseHandle(erlh);
+ }
+}
+#elif !defined(VXWORKS)
+/* Unix eh? */
+static void
+kill_old_erlang(void){
+ pid_t pid;
+ int i;
+ int res;
+ if(heart_beat_kill_pid != 0){
+ pid = (pid_t) heart_beat_kill_pid;
+ res = kill(pid,SIGKILL);
+ for(i=0; i < 5 && res == 0; ++i){
+ sleep(1);
+ res = kill(pid,SIGKILL);
+ }
+ if(errno != ESRCH){
+ print_error("Unable to kill old process, "
+ "kill failed (tried multiple times).");
+ }
+ }
+}
+#endif /* Not on VxWorks */
+
+#ifdef __WIN32__
+void win_system(char *command)
+{
+ char *comspec;
+ char * cmdbuff;
+ char * extra = " /C ";
+ char *env;
+ STARTUPINFO start;
+ SECURITY_ATTRIBUTES attr;
+ PROCESS_INFORMATION info;
+
+ if (!debug_on || test_win95()) {
+ system(command);
+ return;
+ }
+ comspec = env = get_env("COMSPEC");
+ if (!comspec)
+ comspec = "CMD.EXE";
+ cmdbuff = malloc(strlen(command) + strlen(comspec) + strlen(extra) + 1);
+ if (!cmdbuff) {
+ print_error("Failed to allocate memory. Terminating...");
+ exit(1);
+ }
+ strcpy(cmdbuff, comspec);
+ strcat(cmdbuff, extra);
+ strcat(cmdbuff, command);
+ free_env_val(env);
+
+ debugf("running \"%s\"\r\n", cmdbuff);
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ start.wShowWindow = SW_HIDE;
+ start.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start.hStdOutput = GetStdHandle(STD_ERROR_HANDLE);
+ start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+
+ fflush(stderr);
+
+ if (!CreateProcess(NULL,
+ cmdbuff,
+ &attr,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &start,
+ &info)) {
+ debugf("Could not create process for the command %s.\r\n", cmdbuff);
+ }
+ WaitForSingleObject(info.hProcess,INFINITE);
+ free(cmdbuff);
+}
+#endif /* defined(__WIN32__) */
+
+/*
+ * do_terminate
+ */
+static void
+do_terminate(reason)
+ int reason;
+{
+ /*
+ When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish
+ (plus heart_beat_report_delay if under VxWorks), so we don't need
+ to call wd_reset().
+ */
+
+ switch (reason) {
+ case R_SHUT_DOWN:
+ break;
+ case R_TIMEOUT:
+ case R_ERROR:
+ case R_CLOSED:
+ default:
+#if defined(__WIN32__) /* Not VxWorks */
+ {
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(command);
+ print_error("Executed \"%s\". Terminating.",command);
+ }
+ free_env_val(command);
+ }
+ else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(&cmd[0]);
+ print_error("Executed \"%s\". Terminating.",cmd);
+ }
+ }
+
+#else
+ {
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ system(command);
+ print_error("Executed \"%s\". Terminating.",command);
+ }
+ free_env_val(command);
+ }
+ else {
+ kill_old_erlang();
+ system((char*)&cmd[0]);
+ print_error("Executed \"%s\". Terminating.",cmd);
+ }
+ }
+ break;
+#endif
+ } /* switch(reason) */
+}
+
+/*
+ * notify_ack
+ *
+ * Sends an HEART_ACK.
+ */
+static int
+notify_ack(fd)
+ int fd;
+{
+ struct msg m;
+
+ m.op = HEART_ACK;
+ m.len = htons(1);
+ return write_message(fd, &m);
+}
+
+
+/*
+ * send back current command
+ *
+ * Sends an HEART_CMD.
+ */
+static int
+heart_cmd_reply(int fd, char *s)
+{
+ struct msg m;
+ int len = strlen(s) + 1; /* Include \0 */
+
+ /* FIXME if s >= MSG_BODY_SIZE error */
+
+ m.op = HEART_CMD;
+ m.len = htons(len + 2); /* Include Op */
+ strcpy((char*)m.fill, s);
+
+ return write_message(fd, &m);
+}
+
+
+/*
+ * write_message
+ *
+ * Writes a message to a blocking file descriptor. Returns the total
+ * size of the message written (always > 0), or -1 if error.
+ *
+ * A message which is too short or too long, is not written. The return
+ * value is then MSG_HDR_SIZE (2), as if the message had been written.
+ * Is this really necessary? Can't we assume that the length is ok?
+ * FIXME.
+ */
+static int
+write_message(fd, mp)
+ int fd;
+ struct msg *mp;
+{
+ int len;
+ char* tmp;
+
+ tmp = (char*) &(mp->len);
+ len = (*tmp * 256) + *(tmp+1);
+ if ((len == 0) || (len > MSG_BODY_SIZE)) {
+ return MSG_HDR_SIZE;
+ } /* cc68k wants (char *) */
+ if (write(fd, (char *) mp, len + MSG_HDR_SIZE) != len + MSG_HDR_SIZE) {
+ return -1;
+ }
+ return len + MSG_HDR_SIZE;
+}
+
+/*
+ * read_message
+ *
+ * Reads a message from a blocking file descriptor. Returns the total
+ * size of the message read (> 0), 0 if eof, and < 0 if error.
+ *
+ * Note: The return value MSG_HDR_SIZE means a message of total size
+ * MSG_HDR_SIZE, i.e. without even an operation field.
+ *
+ * If the size of the message is larger than MSG_TOTAL_SIZE, the total
+ * number of bytes read is returned, but the buffer contains a truncated
+ * message.
+ */
+static int
+read_message(fd, mp)
+ int fd;
+ struct msg *mp;
+{
+ int rlen, i;
+ unsigned char* tmp;
+
+ if ((i = read_fill(fd, (char *) mp, MSG_HDR_SIZE)) != MSG_HDR_SIZE) {
+ /* < 0 is an error; = 0 is eof */
+ return i;
+ }
+
+ tmp = (unsigned char*) &(mp->len);
+ rlen = (*tmp * 256) + *(tmp+1);
+ if (rlen == 0) {
+ return MSG_HDR_SIZE;
+ }
+ if (rlen > MSG_BODY_SIZE) {
+ if ((i = read_skip(fd, (((char *) mp) + MSG_HDR_SIZE),
+ MSG_BODY_SIZE, rlen)) != rlen) {
+ return i;
+ } else {
+ return rlen + MSG_HDR_SIZE;
+ }
+ }
+ if ((i = read_fill(fd, ((char *) mp + MSG_HDR_SIZE), rlen)) != rlen) {
+ return i;
+ }
+ return rlen + MSG_HDR_SIZE;
+}
+
+/*
+ * read_fill
+ *
+ * Reads len bytes into buf from a blocking fd. Returns total number of
+ * bytes read (i.e. len) , 0 if eof, or < 0 if error. len must be > 0.
+ */
+static int
+read_fill(fd, buf, len)
+ int fd, len;
+ char *buf;
+{
+ int i, got = 0;
+
+ do {
+ if ((i = read(fd, buf + got, len - got)) <= 0) {
+ return i;
+ }
+ got += i;
+ } while (got < len);
+ return len;
+}
+
+/*
+ * read_skip
+ *
+ * Reads len bytes into buf from a blocking fd, but puts not more than
+ * maxlen bytes in buf. Returns total number of bytes read ( > 0),
+ * 0 if eof, or < 0 if error. len > maxlen > 0 must hold.
+ */
+static int
+read_skip(fd, buf, maxlen, len)
+ int fd, maxlen, len;
+ char *buf;
+{
+ int i, got = 0;
+ char c;
+
+ if ((i = read_fill(fd, buf, maxlen)) <= 0) {
+ return i;
+ }
+ do {
+ if ((i = read(fd, &c, 1)) <= 0) {
+ return i;
+ }
+ got += i;
+ } while (got < len - maxlen);
+ return len;
+}
+
+/*
+ * print_error
+ */
+static void
+print_error(const char *format,...)
+{
+ va_list args;
+ time_t now;
+ char *timestr;
+
+ va_start(args, format);
+ time(&now);
+ timestr = ctime(&now);
+ fprintf(stderr, "%s: %.*s: ", program_name, (int) strlen(timestr)-1, timestr);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\r\n");
+}
+
+static void
+debugf(const char *format,...)
+{
+ va_list args;
+
+ if (!debug_on) return;
+ va_start(args, format);
+ fprintf(stderr, "Heart: ");
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\r\n");
+}
+
+#ifdef __WIN32__
+void print_last_error() {
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ /* Display the string.*/
+ fprintf(stderr,"GetLastError:%s\n",lpMsgBuf);
+
+ /* Free the buffer. */
+ LocalFree( lpMsgBuf );
+}
+
+static int test_win95(void)
+{
+ OSVERSIONINFO osinfo;
+ osinfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
+ GetVersionEx(&osinfo);
+ if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ return 1;
+ else
+ return 0;
+}
+
+static BOOL enable_privilege() {
+ HANDLE ProcessHandle;
+ DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ HANDLE TokenHandle;
+ TOKEN_PRIVILEGES Tpriv;
+ LUID luid;
+ ProcessHandle = GetCurrentProcess();
+ OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
+ LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
+ Tpriv.PrivilegeCount = 1;
+ Tpriv.Privileges[0].Luid = luid;
+ Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
+}
+
+static BOOL do_shutdown(int really_shutdown) {
+ if (test_win95()) {
+ if (ExitWindowsEx(EWX_REBOOT,0)) {
+ return TRUE;
+ } else {
+ print_last_error();
+ return FALSE;
+ }
+ } else {
+ enable_privilege();
+ if (really_shutdown) {
+ if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE))
+ return TRUE;
+ } else if (InitiateSystemShutdown(NULL,
+ "shutdown by HEART\n"
+ "will be interrupted",
+ 30,TRUE,TRUE)) {
+ AbortSystemShutdown(NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+DWORD WINAPI reader(LPVOID lpvParam) {
+
+ while (1) {
+ debugf("reader is reading\n");
+ tlen = read_message(erlin_fd, mp);
+ debugf("reader setting event\n");
+ SetEvent(hevent_dataready);
+ if(tlen == 0)
+ break;
+ }
+ return 0;
+}
+
+HANDLE start_reader_thread(void) {
+ DWORD tid;
+ HANDLE thandle;
+ if ((thandle = (HANDLE)
+ _beginthreadex(NULL,0,reader,NULL,0,&tid)) == NULL) {
+ print_last_error();
+ exit(1);
+ }
+ return thandle;
+}
+#endif
+
+#if defined(__WIN32__)
+
+# define TICK_MASK 0x7FFFFFFFUL
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ static time_t extra = 0;
+ static unsigned last_ticks = 0;
+ unsigned this_ticks;
+ time_t r;
+
+ this_ticks = GetTickCount() & TICK_MASK;
+
+ if (this_ticks < last_ticks) {
+ extra += (time_t) ((TICK_MASK + 1) / 1000);
+ }
+
+ last_ticks = this_ticks;
+
+ r = ((time_t) (this_ticks / 1000)) + extra;
+
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(VXWORKS)
+
+static WDOG_ID watchdog_id;
+static volatile unsigned elapsed;
+static WIND_TCB *this_task;
+/* A simple variable is enough to lock the time update, as the
+ watchdog is run at interrupt level and never preempted. */
+static volatile int lock_time;
+
+static void my_delete_hook(WIND_TCB *tcb)
+{
+ if (tcb == this_task) {
+ wdDelete(watchdog_id);
+ watchdog_id = NULL;
+ taskDeleteHookDelete((FUNCPTR) &my_delete_hook);
+ }
+}
+
+static void my_wd_routine(int count)
+{
+ if (watchdog_id != NULL) {
+ ++count;
+ if (!lock_time) {
+ elapsed += count;
+ count = 0;
+ }
+ wdStart(watchdog_id, sysClkRateGet(),
+ (FUNCPTR) &my_wd_routine, count);
+ }
+}
+
+void init_timestamp(void)
+{
+ lock_time = 0;
+ elapsed = 0;
+ watchdog_id = wdCreate();
+ this_task = (WIND_TCB *) taskIdSelf();
+ taskDeleteHookAdd((FUNCPTR) &my_delete_hook);
+ wdStart(watchdog_id, sysClkRateGet(),
+ (FUNCPTR) &my_wd_routine, 0);
+}
+
+time_t timestamp(time_t *res)
+{
+ time_t r;
+ ++lock_time;
+ r = (time_t) elapsed;
+ --lock_time;
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(HAVE_GETHRTIME)
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ hrtime_t ht = gethrtime();
+ time_t r = (time_t) (ht / 1000000000);
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(HEART_CORRECT_USING_TIMES)
+
+# ifdef NO_SYSCONF
+# include <sys/param.h>
+# define TICKS_PER_SEC() HZ
+# else
+# define TICKS_PER_SEC() sysconf(_SC_CLK_TCK)
+# endif
+
+# define TICK_MASK 0x7FFFFFFFUL
+
+static unsigned tps;
+
+void init_timestamp(void)
+{
+ tps = TICKS_PER_SEC();
+}
+
+time_t timestamp(time_t *res)
+{
+ static time_t extra = 0;
+ static clock_t last_ticks = 0;
+ clock_t this_ticks;
+ struct tms dummy;
+ time_t r;
+
+ this_ticks = (times(&dummy) & TICK_MASK);
+
+ if (this_ticks < last_ticks) {
+ extra += (time_t) ((TICK_MASK + 1) / tps);
+ }
+
+ last_ticks = this_ticks;
+
+ r = ((time_t) (this_ticks / tps)) + extra;
+
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#else
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ return time(res);
+}
+
+#endif
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
new file mode 100644
index 0000000000..ff16ee02c4
--- /dev/null
+++ b/erts/etc/common/inet_gethost.c
@@ -0,0 +1,2757 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+/*
+ * Erlang port program to do the name service lookup for the erlang
+ * distribution and inet part of the kernel.
+ * A pool of subprocess is kept, to which a pair of pipes is connected.
+ * The main process schedules requests among the different subprocesses
+ * (created with fork()), to be able to handle as many requests as possible
+ * simultaneously. The controlling erlang machine may request a "cancel",
+ * in which case the process may be killed and restarted when the need arises.
+ * The single numeric parameter to this program is the maximum port pool size,
+ * which is the size of the bookkeeping array.
+ *
+ * Windows:
+ * There is instead of a pool of processes a pool of threads.
+ * Communication is not done through pipes but via message queues between
+ * the threads. The only "pipes" involved are the ones used for communicating
+ * with Erlang.
+ * Important note:
+ * For unknown reasons, the combination of a thread doing blocking I/O on
+ * a named pipe at the same time as another thread tries to resolve a hostname
+ * may (with certain software configurations) block the gethostbyname call (!)
+ * For that reason, standard input (and standard output) should be opened
+ * in asynchronous mode (FILE_FLAG_OVERLAPPED), which has to be done by Erlang.
+ * A special flag to open_port is used to work around this behaviour in winsock
+ * and the threads doing read and write handle asynchronous I/O.
+ * The ReadFile and WriteFile calls try to cope with both types of I/O, why
+ * the code is not really as Microsoft describes "the right way to do it" in
+ * their documentation. Important to note is that **there is no supported way
+ * to retrieve the information if the HANDLE was opened with
+ * FILE_FLAG_OVERLAPPED from the HANDLE itself**.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* These are not used even if they would exist which they should not */
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETHOSTBYNAME2
+#undef HAVE_GETNAMEINFO
+#undef HAVE_GETIPNODEBYADDR
+
+#else /* Unix */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#include <sys/times.h>
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* To simplify #ifdef code further down - select only one to be defined...
+** Use them in pairs - if one is broken do not trust its mate.
+**/
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO)
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETIPNODEBYADDR
+#undef HAVE_GETHOSTBYNAME2
+#elif defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_GETIPNODEBYADDR)
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETNAMEINFO
+#undef HAVE_GETHOSTBYNAME2
+#else
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETIPNODEBYADDR
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETNAMEINFO
+#endif
+
+#endif /* !WIN32 */
+
+#define PACKET_BYTES 4
+#ifdef WIN32
+#define READ_PACKET_BYTES(X,Y,Z) read_int32((X),(Y),(Z))
+#else
+#define READ_PACKET_BYTES(X,Y) read_int32((X),(Y))
+#endif
+#define PUT_PACKET_BYTES(X,Y) put_int32((X),(Y))
+/* The serial numbers of the requests */
+typedef int SerialType;
+
+#define INVALID_SERIAL -1
+
+/* The operations performed by this program */
+typedef unsigned char OpType;
+
+#define OP_GETHOSTBYNAME 1
+#define OP_GETHOSTBYADDR 2
+#define OP_CANCEL_REQUEST 3
+#define OP_CONTROL 4
+
+/* The protocol (IPV4/IPV6) */
+typedef unsigned char ProtoType;
+
+#define PROTO_IPV4 1
+#define PROTO_IPV6 2
+
+/* OP_CONTROL */
+typedef unsigned char CtlType;
+#define SETOPT_DEBUG_LEVEL 0
+
+/* The unit of an IP address (0 == error, 4 == IPV4, 16 == IPV6) */
+typedef unsigned char UnitType;
+
+#define UNIT_ERROR 0
+#define UNIT_IPV4 4
+#define UNIT_IPV6 16
+
+/* And the byte type */
+typedef unsigned char AddrByte; /* Must be compatible with character
+ datatype */
+
+/*
+ * Marshalled format of request:
+ *{
+ * Serial: 32 bit big endian
+ * Op:8 bit [1,2,3]
+ * If op == 1 {
+ * Proto:8 bit [1,2]
+ * Str: Null terminated array of characters
+ * } Else if op == 2 {
+ * Proto:8 bit [1,2]
+ * If proto == 1 {
+ * B0..B3: 4 bytes, most significant first
+ * } Else (proto == 2) {
+ * B0..B15: 16 bytes, most significant first
+ * }
+ * }
+ * (No more if op == 3)
+ *}
+ * The request arrives as a packet, with 4 packet size bytes.
+ */
+
+/* The main process unpackes the marshalled message and sends the data
+ * to a suitable port process or, in the case of a close request, kills the
+ * suitable port process. There is also a que of requests linked together,
+ * for when all subrocesses are busy.
+ */
+
+typedef struct QueItem {
+ struct QueItem *next;
+ int req_size;
+ AddrByte request[1];
+} QueItem; /* Variable size due to request's variable size */
+
+QueItem *que_first;
+QueItem *que_last;
+
+#ifdef WIN32
+typedef struct mesq {
+ HANDLE data_present;
+ CRITICAL_SECTION crit;
+ int shutdown;
+ QueItem *first;
+ QueItem *last;
+} MesQ;
+
+MesQ *to_erlang;
+MesQ *from_erlang;
+#endif
+
+/*
+ * Marshalled format of reply:
+ *{
+ * Serial: 32 bit big endian
+ * Unit: 8 bit, same as h_length or 0 for error
+ * if unit == 0 {
+ * Str: Null terminated character string explaining the error
+ * } else {
+ * Naddr: 32 bit big endian
+ * if unit = 4 {
+ * (B0..B3)0..(B0..B3)Naddr-1: Naddr*4 bytes most significant first
+ * } else if unit == 16 {
+ * (B0..B15)0..(B0..B15)Naddr-1: Naddr*16 bytes most significant first
+ * }
+ * Nnames: 32 bit big endian >= 1
+ * Name0: Null terminated string of characters
+ * Alias[0]..Alias[Nnames - 2]: Nnames - 1 Null terminated strings of chars
+ * }
+ *}
+ * Four packet size bytes prepended (big endian)
+ */
+/* Internal error codes */
+#define ERRCODE_NOTSUP 1
+#define ERRCODE_HOST_NOT_FOUND 2
+#define ERRCODE_TRY_AGAIN 3
+#define ERRCODE_NO_RECOVERY 4
+#define ERRCODE_NO_DATA 5
+#define ERRCODE_NETDB_INTERNAL 7
+
+/*
+ * Each worker process is represented in the parent by the following struct
+ */
+
+typedef unsigned WorkerState;
+
+#define WORKER_EMPTY 0 /* No process created */
+#define WORKER_FREE 1 /* Living waiting process */
+#define WORKER_BUSY 2 /* Living busy process */
+#define WORKER_STALLED 3 /* Living cancelled process */
+
+/* The timeout when killing a child process in seconds*/
+#define CHILDWAIT_TMO 1
+/* The domainname size_limit */
+#define DOMAINNAME_MAX 258 /* 255 + Opcode + Protocol + Null termination */
+
+typedef struct {
+ WorkerState state;
+#ifdef WIN32
+ DWORD pid; /* 0 if unused */
+ MesQ *writeto; /* Message queues */
+ MesQ *readfrom;
+#else
+ pid_t pid; /* -1 if unused */
+ int writeto, readfrom; /* Pipes */
+#endif
+ SerialType serial;
+ AddrByte domain[DOMAINNAME_MAX];
+ QueItem *que_first;
+ QueItem *que_last;
+ int que_size;
+} Worker;
+
+int num_busy_workers;
+int num_free_workers;
+int num_stalled_workers;
+int max_workers;
+int greedy_threshold;
+Worker *busy_workers; /* Workers doing any job that someone really is
+ interested in */
+Worker *free_workers; /* Really free workers */
+Worker *stalled_workers; /* May still deliver answers which we will
+ discard */
+#define BEE_GREEDY() (num_busy_workers >= greedy_threshold)
+
+static char *program_name;
+
+static int debug_level;
+#ifdef WIN32
+static HANDLE debug_console_allocated = INVALID_HANDLE_VALUE;
+#endif
+
+#ifdef NODEBUG
+#define DEBUGF(L,P) /* Nothing */
+#else
+#define DEBUGF(Level,Printf) do { if (debug_level >= (Level)) \
+ debugf Printf;} while(0)
+#endif
+#define ALLOC(Size) my_malloc(Size)
+#define REALLOC(Old, Size) my_realloc((Old), (Size))
+#define FREE(Ptr) free(Ptr)
+
+#ifdef WIN32
+#define WAKEUP_WINSOCK() do { \
+ char dummy_buff[100]; \
+ gethostname(dummy_buff,99); \
+} while (0)
+#endif
+
+/* The internal prototypes */
+static char *format_address(int siz, AddrByte *addr);
+static void debugf(char *format, ...);
+static void warning(char *format, ...);
+static void fatal(char *format, ...);
+static void *my_malloc(size_t size);
+static void *my_realloc(void *old, size_t size);
+static int get_int32(AddrByte *buff);
+static void put_int32(AddrByte *buff, int value);
+static int create_worker(Worker *pworker, int save_que);
+static int map_netdb_error(int netdb_code);
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static int map_netdb_error_ai(int netdb_code);
+#endif
+static char *errcode_to_string(int errcode);
+static size_t build_error_reply(SerialType serial, int errnum,
+ AddrByte **preply,
+ size_t *preply_size);
+#ifdef HAVE_GETADDRINFO
+static size_t build_reply_ai(SerialType serial, int, struct addrinfo *,
+ AddrByte **preply, size_t *preply_size);
+#endif
+static size_t build_reply(SerialType serial, struct hostent *he,
+ AddrByte **preply, size_t *preply_size);
+static int read_request(AddrByte **buff, size_t *buff_size);
+static OpType get_op(AddrByte *buff);
+static AddrByte *get_op_addr(AddrByte *buff);
+static SerialType get_serial(AddrByte *buff);
+static ProtoType get_proto(AddrByte *buff);
+static CtlType get_ctl(AddrByte *buff);
+static AddrByte *get_data(AddrByte *buff);
+static int get_debug_level(AddrByte *buff);
+static int relay_reply(Worker *pw);
+static int ignore_reply(Worker *pw);
+static void init_workers(int max);
+static void kill_worker(Worker *pw);
+static Worker *pick_worker(void);
+static void kill_last_picked_worker(void);
+static void stall_worker(SerialType serial);
+static int handle_io_busy(int ndx);
+static int handle_io_free(int ndx);
+static int handle_io_stalled(int ndx);
+static void check_que(void);
+static void main_loop(void);
+static void usage(char *unknown);
+static void domaincopy(AddrByte *out,AddrByte *in);
+static int domaineq(AddrByte *d1, AddrByte *d2);
+static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff);
+static Worker *pick_worker_greedy(AddrByte *domainbuff);
+static void restart_worker(Worker *w);
+static void start_que_request(Worker *w) ;
+#ifdef WIN32
+static int read_int32(HANDLE fd, int *res, HANDLE ev);
+static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev);
+static int write_exact(HANDLE fd, AddrByte *buff, DWORD len,HANDLE ev);
+DWORD WINAPI worker_loop(void *v);
+DWORD WINAPI reader(void *data);
+DWORD WINAPI writer(void *data);
+static int send_mes_to_worker(QueItem *m, Worker *pw);
+BOOL create_mesq(MesQ **q);
+BOOL enque_mesq(MesQ *q, QueItem *m);
+BOOL deque_mesq(MesQ *q, QueItem **m);
+BOOL close_mesq(MesQ *q);
+HANDLE event_mesq(MesQ *q);
+#else
+static size_t read_int32(int fd, int *res);
+static ssize_t read_exact(int fd, void *vbuff, size_t nbytes);
+static int write_exact(int fd, AddrByte *buff, int len);
+void reap_children(int ignored);
+static void init_signals(void);
+static void kill_all_workers(void);
+static void close_all_worker_fds(void);
+static int worker_loop(void);
+static int fillin_reply(Worker *pw);
+static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw);
+#endif
+
+#define ERL_DBG_LVL_ENV_VAR "ERL_INET_GETHOST_DEBUG"
+
+static int
+get_env_debug_level(void)
+{
+#ifdef __WIN32__
+ char value[21]; /* Enough for any 64-bit values */
+ DWORD sz = GetEnvironmentVariable((LPCTSTR) ERL_DBG_LVL_ENV_VAR,
+ (LPTSTR) value,
+ (DWORD) sizeof(value));
+ if (sz == 0 || sz > sizeof(value))
+ return 0;
+#else
+ char *value = getenv(ERL_DBG_LVL_ENV_VAR);
+ if (!value)
+ return 0;
+#endif
+ return atoi(value);
+}
+
+#ifdef WIN32
+static void do_allocate_console(void)
+{
+ AllocConsole();
+ debug_console_allocated = CreateFile ("CONOUT$", GENERIC_WRITE,
+ FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+}
+#ifdef HARDDEBUG
+DWORD WINAPI pseudo_worker_loop(void *v);
+static void poll_gethost(int row);
+#endif
+#endif
+
+/*
+ * Main
+ */
+int main(int argc, char **argv)
+{
+ int num_workers = 1;
+ char **ap = argv + 1;
+ int x;
+ int disable_greedy = 0;
+
+ program_name = *argv;
+ que_first = que_last = NULL;
+ debug_level = get_env_debug_level();
+ greedy_threshold = 0;
+
+ while (*ap) {
+ if (!strcmp(*ap, "-d")) {
+ ++debug_level;
+ } else if(!strcmp(*ap, "-g") && *(ap + 1)) {
+ ++ap;
+ x = atoi(*ap);
+ if (!x) {
+ usage(*ap);
+ } else {
+ greedy_threshold = x;
+ }
+ } else if(!strcmp(*ap, "-ng")) {
+ disable_greedy = 1;
+ } else {
+ x = atoi(*ap);
+ if (!x) {
+ usage(*ap);
+ } else {
+ num_workers = x;
+ }
+ }
+ ++ap;
+ }
+
+#ifdef WIN32
+ if (num_workers > 60 || greedy_threshold > 60) {
+ usage("More than 60 workers on windows impossible!");
+ num_workers = 60;
+ greedy_threshold = 0;
+ }
+#endif
+
+ if(!greedy_threshold) {
+ greedy_threshold = (3*num_workers)/4; /* 75% */
+ if (!greedy_threshold) {
+ greedy_threshold = num_workers;
+ }
+ }
+
+ if (disable_greedy) {
+ greedy_threshold = num_workers + 1;
+ }
+
+#ifdef WIN32
+ {
+ WORD wr;
+ WSADATA wsa_data;
+ int wsa_error;
+ wr = MAKEWORD(2,0);
+
+ wsa_error = WSAStartup(wr,&wsa_data);
+ if (wsa_error) {
+ fatal("Could not open usable winsock library.");
+ }
+ if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 0) {
+ fatal("Could not open recent enough winsock library.");
+ }
+
+ if (debug_level >= 1) {
+ do_allocate_console();
+
+ DEBUGF(1,("num_workers = %d, greedy_threshold = %d, "
+ "debug_level = %d.",
+ num_workers, greedy_threshold, debug_level));
+ }
+ }
+ WAKEUP_WINSOCK(); /* Why on earth is this needed? */
+
+#endif
+
+ init_workers(num_workers);
+
+ main_loop();
+#ifndef WIN32
+ kill_all_workers();
+#endif
+ return 0;
+}
+
+static void usage(char *unknown)
+{
+ fprintf(stderr,"%s: Unknown option \"%s\"\n"
+ "Usage: %s [-d [-d ...]] [-g <greedy threshold>] "
+ "[<number of workers>]\n",
+ program_name, unknown, program_name);
+}
+
+/*
+ * Main process main loop
+ */
+
+static int handle_io_busy(int ndx)
+{
+ /* Probably an answer */
+ int res;
+ res = relay_reply(&busy_workers[ndx]);
+ if (res < 0) {
+ /* Bad worker */
+ if (busy_workers[ndx].que_size) {
+ restart_worker(&busy_workers[ndx]);
+ start_que_request(&busy_workers[ndx]);
+ return 0;
+ } else {
+ kill_worker(&busy_workers[ndx]);
+ --num_busy_workers;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ }
+ return 1;
+ } else if (res == 0) {
+ /* Erlang has closed */
+ return -1;
+ } else {
+ if (busy_workers[ndx].que_size) {
+ start_que_request(&busy_workers[ndx]);
+ return 0;
+ }
+ /* The worker is no longer busy, it should be in the free list */
+ free_workers[num_free_workers] = busy_workers[ndx];
+ free_workers[num_free_workers].state = WORKER_FREE;
+ ++num_free_workers;
+ --num_busy_workers;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ return 1;
+ }
+}
+
+static int handle_io_free(int ndx)
+{
+ /* IO from a free worker means "kill me" */
+ DEBUGF(1,("Free worker[%ld] spontaneously died.",
+ (long) free_workers[ndx].pid));
+ kill_worker(&free_workers[ndx]);
+ --num_free_workers;
+ free_workers[ndx] = free_workers[num_free_workers];
+ return 1;
+}
+
+static int handle_io_stalled(int ndx)
+{
+ int res;
+ res = ignore_reply(&stalled_workers[ndx]);
+ if (res <= 0) {
+ /* Bad worker */
+ kill_worker(&stalled_workers[ndx]);
+ --num_stalled_workers;
+ stalled_workers[ndx] = stalled_workers[num_stalled_workers];
+ return 1;
+ } else {
+ DEBUGF(3,("Ignoring reply from stalled worker[%ld].",
+ (long) stalled_workers[ndx].pid));
+ free_workers[num_free_workers] = stalled_workers[ndx];
+ free_workers[num_free_workers].state = WORKER_FREE;
+ ++num_free_workers;
+ --num_stalled_workers;
+ stalled_workers[ndx] = stalled_workers[num_stalled_workers];
+ return 1;
+ }
+}
+
+static void check_que(void)
+{
+ /* Check if anything in the que can be handled */
+ Worker *cw;
+
+ while (que_first) {
+ QueItem *qi,*nxt;
+ qi = que_first;
+ nxt = qi->next; /* Need to save before it's getting put in another que
+ in threaded solution */
+ if ((cw = pick_worker()) == NULL) {
+ break;
+ }
+#ifdef WIN32
+ {
+ SerialType save_serial = get_serial(que_first->request);
+ if (send_mes_to_worker(que_first, cw) != 0) {
+ kill_last_picked_worker();
+ continue;
+ }
+ cw->serial = save_serial;
+ }
+#else
+ if (send_request_to_worker(que_first->request,
+ que_first->req_size, cw) != 0) {
+ /* Couldn't send request, kill the worker and retry */
+ kill_last_picked_worker();
+ continue;
+ }
+ cw->serial = get_serial(que_first->request);
+#endif
+ /* Went well, lets deque */
+ que_first = nxt;
+ if (que_first == NULL) {
+ que_last = NULL;
+ }
+ DEBUGF(3,("Did deque serial %d, Que is %sempty",
+ get_serial(qi->request), (que_first) ? "not " : ""));
+#ifndef WIN32
+ FREE(qi);
+#endif
+ }
+}
+
+static int clean_que_of(SerialType s)
+{
+ QueItem **qi;
+ int i;
+
+ for(qi=&que_first;*qi != NULL &&
+ s != get_serial((*qi)->request); qi = &((*qi)->next))
+ ;
+ if(*qi != NULL) {
+ QueItem *r = *qi;
+ *qi = (*qi)->next;
+ FREE(r);
+ if(que_last == r) {
+ /* Lost the "last" pointer, should be very uncommon
+ if the que is not empty, so we simply do a traversal
+ to reclaim it. */
+ if (que_first == NULL) {
+ que_last = NULL;
+ } else {
+ for (que_last=que_first;que_last->next != NULL;
+ que_last = que_last->next)
+ ;
+ }
+ }
+ DEBUGF(3,("Removing serial %d from global que on request, "
+ "que %sempty",s, (que_first) ? "not " : ""));
+ return 1;
+ }
+ for (i = 0; i < num_busy_workers; ++i) {
+ for(qi=&(busy_workers[i].que_first);*qi != NULL &&
+ s != get_serial((*qi)->request); qi = &((*qi)->next))
+ ;
+ if(*qi != NULL) {
+ QueItem *r = *qi;
+ *qi = (*qi)->next;
+ FREE(r);
+ if(busy_workers[i].que_last == r) {
+ /* Lost the "last" pointer, should be very uncommon
+ if the que is not empty, so we simply do a traversal
+ to reclaim it. */
+ if (busy_workers[i].que_first == NULL) {
+ busy_workers[i].que_last = NULL;
+ if (busy_workers[i].que_size != 1) {
+ fatal("Worker que size counter incorrect, internal datastructure error.");
+ }
+ } else {
+ for (busy_workers[i].que_last = busy_workers[i].que_first;
+ busy_workers[i].que_last->next != NULL;
+ busy_workers[i].que_last = busy_workers[i].que_last->next)
+ ;
+ }
+ }
+ --(busy_workers[i].que_size);
+ DEBUGF(3,("Removing serial %d from worker[%ld] specific que "
+ "on request, que %sempty",
+ s, (long) busy_workers[i].pid,
+ (busy_workers[i].que_first) ? "not " : ""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void main_loop(void)
+{
+ AddrByte *inbuff = NULL;
+ int insize;
+ int i,w;
+#ifdef WIN32
+ HANDLE handles[64];
+ DWORD num_handles;
+ DWORD index;
+ QueItem *qi;
+#else
+ size_t inbuff_size = 0;
+ fd_set fds;
+ int max_fd;
+#endif
+ int new_data;
+ int save_serial;
+ /* It's important that the free workers list is handled first */
+ Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
+ int *wsizes[3] = {&num_free_workers, &num_busy_workers,
+ &num_stalled_workers};
+ int (*handlers[3])(int) = {&handle_io_free, &handle_io_busy,
+ &handle_io_stalled};
+ Worker *cw;
+ AddrByte domainbuff[DOMAINNAME_MAX];
+
+#ifdef WIN32
+
+ {
+ DWORD dummy;
+ /* Create the reader and writer */
+ if ((!create_mesq(&to_erlang)) || (!create_mesq(&from_erlang))) {
+ fatal("Could not create message que! errno = %d.",GetLastError());
+ }
+ if (((HANDLE) _beginthreadex(NULL,0,writer,to_erlang,0,&dummy))
+ == NULL) {
+ fatal("Could not create writer thread! errno = %d.",GetLastError());
+ }
+ if (((HANDLE) _beginthreadex(NULL,0,reader,from_erlang,0,&dummy))
+ == NULL) {
+ fatal("Could not create reader thread! errno = %d.",GetLastError());
+ }
+ DEBUGF(4,("Created reader and writer threads."));
+#ifdef HARDDEBUG
+ poll_gethost(__LINE__);
+#endif
+ }
+#endif
+
+ for(;;) {
+#ifdef WIN32
+ num_handles = 0;
+ handles[num_handles++] = event_mesq(from_erlang);
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ handles[num_handles++] = event_mesq(workers[w][i].readfrom);
+ }
+ }
+
+ if ((index = WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE))
+ == WAIT_FAILED) {
+ fatal("Could not WaitForMultpleObjects! errno = %d.",GetLastError());
+ }
+ w = 0;
+ index -= WAIT_OBJECT_0;
+
+ DEBUGF(4,("Got data on index %d.",index));
+ if (index > 0) {
+ if (((int)index - 1) < *wsizes[0]) {
+ (*handlers[0])(index - 1);
+ } else if (((int)index - 1) < ((*wsizes[0]) + (*wsizes[1]))) {
+ (*handlers[1])(index - 1 - (*wsizes[0]));
+ } else {
+ (*handlers[2])(index - 1 - (*wsizes[0]) - (*wsizes[1]));
+ }
+ }
+ new_data = (index == 0);
+#else
+ max_fd = 0;
+ FD_ZERO(&fds);
+ FD_SET(0,&fds);
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ FD_SET(workers[w][i].readfrom,&fds);
+ if (workers[w][i].readfrom > max_fd) {
+ max_fd = workers[w][i].readfrom;
+ }
+ }
+ }
+ for (;;) {
+ if (select(max_fd + 1,&fds,NULL,NULL,NULL) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ fatal("Select failed (invalid internal structures?), "
+ "errno = %d.",errno);
+ }
+ }
+ break;
+ }
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ if (FD_ISSET(workers[w][i].readfrom, &fds)) {
+ int hres = (*handlers[w])(i);
+ if (hres < 0) {
+ return;
+ } else {
+ i -= hres; /* We'll retry this position, if hres == 1.
+ The position is usually
+ replaced with another worker,
+ a worker with
+ I/O usually changes state as we
+ use blocking file I/O */
+ }
+ }
+ }
+ }
+ new_data = FD_ISSET(0,&fds);
+
+#endif
+
+ check_que();
+
+ /* Now check for new requests... */
+ if (new_data) { /* Erlang... */
+ OpType op;
+#ifdef WIN32
+ if (!deque_mesq(from_erlang,&qi)) {
+ DEBUGF(1,("Erlang has closed."));
+ return;
+ }
+ insize = qi->req_size;
+ inbuff = qi->request;
+ DEBUGF(4,("Got data from erlang."));
+ DEBUGF(4,("OPeration == %d.",get_op(inbuff)));
+#else
+ insize = read_request(&inbuff, &inbuff_size);
+ if (insize == 0) { /* Other errors taken care of in
+ read_request */
+ DEBUGF(1,("Erlang has closed."));
+ return;
+ }
+#endif
+ op = get_op(inbuff);
+ if (op == OP_CANCEL_REQUEST) {
+ SerialType serial = get_serial(inbuff);
+ if (!clean_que_of(serial)) {
+ for (i = 0; i < num_busy_workers; ++i) {
+ if (busy_workers[i].serial == serial) {
+ if (busy_workers[i].que_size) {
+ restart_worker(&busy_workers[i]);
+ start_que_request(&busy_workers[i]);
+ } else {
+ stall_worker(i);
+ check_que();
+ }
+ break;
+ }
+ }
+ }
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue; /* New select */
+ } else if (op == OP_CONTROL) {
+ CtlType ctl;
+ SerialType serial = get_serial(inbuff);
+ if (serial != INVALID_SERIAL) {
+ fatal("Invalid serial: %d.", serial);
+ }
+ switch (ctl = get_ctl(inbuff)) {
+ case SETOPT_DEBUG_LEVEL:
+ {
+ int tmp_debug_level = get_debug_level(inbuff);
+#ifdef WIN32
+ if (debug_console_allocated == INVALID_HANDLE_VALUE &&
+ tmp_debug_level > 0) {
+ DWORD res;
+ do_allocate_console();
+ WriteFile(debug_console_allocated,
+ "Hej\n",4,&res,NULL);
+ }
+#endif
+ debug_level = tmp_debug_level;
+ DEBUGF(debug_level, ("debug_level = %d", debug_level));
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; i++) {
+ int res;
+#ifdef WIN32
+ QueItem *m;
+#endif
+ cw = &(workers[w][i]);
+#ifdef WIN32
+ m = ALLOC(sizeof(QueItem) - 1 + qi->req_size);
+ memcpy(m->request, qi->request,
+ (m->req_size = qi->req_size));
+ m->next = NULL;
+ if ((res = send_mes_to_worker(m, cw)) != 0) {
+ FREE(m);
+ }
+#else
+ res = send_request_to_worker(inbuff, insize, cw);
+#endif
+ if (res != 0) {
+ kill_worker(cw);
+ (*wsizes[w])--;
+ *cw = workers[w][*wsizes[w]];
+ }
+ }
+ }
+ }
+ break;
+ default:
+ warning("Unknown control requested from erlang (%d), "
+ "message discarded.", (int) ctl);
+ break;
+ }
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue; /* New select */
+ } else {
+ ProtoType proto;
+ if (op != OP_GETHOSTBYNAME && op != OP_GETHOSTBYADDR) {
+ warning("Unknown operation requested from erlang (%d), "
+ "message discarded.", op);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ if ((proto = get_proto(inbuff)) != PROTO_IPV4 &&
+ proto != PROTO_IPV6) {
+ warning("Unknown protocol requested from erlang (%d), "
+ "message discarded.", proto);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ if (get_domainname(inbuff,insize,domainbuff) < 0) {
+ warning("Malformed message sent from erlang, no domain, "
+ "message discarded.", op);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ }
+
+ if (BEE_GREEDY()) {
+ DEBUGF(4,("Beeing greedy!"));
+ if ((cw = pick_worker_greedy(domainbuff)) != NULL) {
+ /* Put it in the worker specific que if the
+ domainname matches... */
+#ifndef WIN32
+ QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
+ insize);
+ qi->req_size = insize;
+ memcpy(&(qi->request), inbuff, insize);
+ qi->next = NULL;
+#endif
+ if (!cw->que_first) {
+ cw->que_first = cw->que_last = qi;
+ } else {
+ cw->que_last->next = qi;
+ cw->que_last = qi;
+ }
+ ++(cw->que_size);
+ continue;
+ }
+ /* Otherwise busyness as usual */
+ }
+
+ save_serial = get_serial(inbuff);
+
+ while ((cw = pick_worker()) != NULL) {
+ int res;
+#ifdef WIN32
+ res = send_mes_to_worker(qi,cw);
+#else
+ res = send_request_to_worker(inbuff, insize, cw);
+#endif
+ if (res == 0) {
+ break;
+ } else {
+ kill_last_picked_worker();
+ }
+ }
+
+ if (cw == NULL) {
+ /* Insert into que */
+#ifndef WIN32
+ QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
+ insize);
+ qi->req_size = insize;
+ memcpy(&(qi->request), inbuff, insize);
+ qi->next = NULL;
+#endif
+ if (!que_first) {
+ que_first = que_last = qi;
+ } else {
+ que_last->next = qi;
+ que_last = qi;
+ }
+ } else {
+ cw->serial = save_serial;
+ domaincopy(cw->domain, domainbuff);
+ }
+ }
+ }
+}
+
+/*
+ * Main process worker administration
+ */
+
+static void init_workers(int max)
+{
+ max_workers = max;
+ num_busy_workers = 0;
+ num_free_workers = 0;
+ num_stalled_workers = 0;
+
+ busy_workers = ALLOC(sizeof(Worker) * max_workers);
+ free_workers = ALLOC(sizeof(Worker) * max_workers);
+ stalled_workers = ALLOC(sizeof(Worker) * max_workers);
+#ifndef WIN32
+ init_signals();
+#endif
+}
+
+#ifdef WIN32
+static void kill_worker(Worker *pw)
+{
+ /* Cannot really kill a thread in win32, have to just leave it to die */
+ close_mesq(pw->writeto);
+ close_mesq(pw->readfrom);
+ pw->state = WORKER_EMPTY;
+}
+#else
+static void kill_worker(Worker *pw)
+{
+ fd_set fds;
+ struct timeval tmo;
+ int selret;
+ static char buff[1024];
+
+ DEBUGF(3,("Killing worker[%ld] with fd %d, serial %d",
+ (long) pw->pid,
+ (int) pw->readfrom,
+ (int) pw->serial));
+ kill(pw->pid, SIGUSR1);
+ /* This is all just to check that the child died, not
+ really necessary */
+ for(;;) {
+ FD_ZERO(&fds);
+ FD_SET(pw->readfrom, &fds);
+ tmo.tv_usec=0;
+ tmo.tv_sec = CHILDWAIT_TMO;
+ selret = select(pw->readfrom+1, &fds, NULL, NULL, &tmo);
+ if (selret < 0) {
+ if (errno != EINTR) {
+ warning("Unable to select on dying child file descriptor, "
+ "errno = %d.",errno);
+ break;
+ }
+ } else if (selret == 0) {
+ warning("Timeout waiting for child process to die, "
+ "ignoring child (pid = %d).", pw->pid);
+ break;
+ } else {
+ int ret;
+ if ((ret = read(pw->readfrom, buff, 1024)) < 0) {
+ if (errno != EINTR) {
+ warning("Child file descriptor not closed properly, "
+ "errno = %d", errno);
+ break;
+ }
+ } else if (ret == 0) {
+ break;
+ }
+ /* continue */
+ }
+ }
+ /* Waiting is done by signal handler... */
+ close(pw->readfrom);
+ close(pw->writeto);
+ pw->state = WORKER_EMPTY;
+ /* Leave rest as is... */
+}
+
+static void kill_all_workers(void)
+/* Emergency function, will not check that the children died... */
+{
+ int i;
+ for (i = 0; i < num_busy_workers; ++i) {
+ kill(busy_workers[i].pid, SIGUSR1);
+ }
+ for (i = 0; i < num_free_workers; ++i) {
+ kill(free_workers[i].pid, SIGUSR1);
+ }
+ for (i = 0; i < num_stalled_workers; ++i) {
+ kill(stalled_workers[i].pid, SIGUSR1);
+ }
+}
+#endif /* !WIN32 */
+
+static Worker *pick_worker(void)
+{
+ Worker tmp;
+ if (num_free_workers > 0) {
+ --num_free_workers;
+ tmp = free_workers[num_free_workers];
+ } else if (num_stalled_workers > 0) {
+ /* "restart" the worker... */
+ --num_stalled_workers;
+ kill_worker(&(stalled_workers[num_stalled_workers]));
+ if (create_worker(&tmp,0) < 0) {
+ warning("Unable to create worker process, insufficient "
+ "resources");
+ return NULL;
+ }
+ } else {
+ if (num_busy_workers == max_workers) {
+ return NULL;
+ }
+ if (create_worker(&tmp,0) < 0) {
+ warning("Unable to create worker process, insufficient "
+ "resources");
+ return NULL;
+ }
+ }
+ /* tmp contains a worker now, make it busy and put it in the right
+ array */
+ tmp.state = WORKER_BUSY;
+ busy_workers[num_busy_workers] = tmp;
+ ++num_busy_workers;
+ return &(busy_workers[num_busy_workers-1]);
+}
+
+static Worker *pick_worker_greedy(AddrByte *domainbuff)
+{
+ int i;
+ int ql = 0;
+ int found = -1;
+ for (i=0; i < num_busy_workers; ++i) {
+ if (domaineq(busy_workers[i].domain, domainbuff)) {
+ if ((found < 0) || (busy_workers[i].que_size <
+ busy_workers[found].que_size)) {
+ found = i;
+ ql = busy_workers[i].que_size;
+ }
+ }
+ }
+ if (found >= 0) {
+ return &busy_workers[found];
+ }
+ return NULL;
+}
+
+static void restart_worker(Worker *w)
+{
+ kill_worker(w);
+ if (create_worker(w,1) < 0) {
+ fatal("Unable to create worker process, insufficient resources");
+ }
+}
+
+static void kill_last_picked_worker(void)
+{
+ kill_worker( &(busy_workers[num_busy_workers-1]));
+ --num_busy_workers;
+}
+
+/*
+ * Starts a request qued to a specific worker, check_que starts normally queued requests.
+ * We expect a que here...
+ */
+static void start_que_request(Worker *w)
+{
+ QueItem *qi;
+ SerialType save_serial;
+ if (!w->que_first || !w->que_size) {
+ fatal("Expected que'd requests but found none, "
+ "internal datastructure corrupted!");
+ }
+ qi = w->que_first;
+ w->que_first = w->que_first->next;
+ if (!w->que_first) {
+ w->que_last = NULL;
+ }
+ --(w->que_size);
+ save_serial = get_serial(qi->request);
+#ifdef WIN32
+ while (send_mes_to_worker(qi, w) != 0) {
+ restart_worker(w);
+ }
+#else
+ while (send_request_to_worker(qi->request,
+ qi->req_size, w) != 0) {
+ restart_worker(w);
+ }
+#endif
+ w->serial = save_serial;
+ DEBUGF(3,("Did deque serial %d from worker[%ld] specific que, "
+ "Que is %sempty",
+ get_serial(qi->request), (long) w->pid,
+ (w->que_first) ? "not " : ""));
+#ifndef WIN32
+ FREE(qi);
+#endif
+}
+
+#ifndef WIN32
+/* Signal utilities */
+static 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);
+}
+
+
+static void sys_sigblock(int sig)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
+}
+
+static void sys_sigrelease(int sig)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
+}
+
+/* Child signal handler */
+void reap_children(int ignored)
+{
+ int res;
+ sys_sigblock(SIGCHLD);
+ for (;;) {
+ while ((res = waitpid((pid_t)-1, NULL, WNOHANG)) > 0)
+ ;
+ if (!(res < 0 && errno == EAGAIN)) {
+ DEBUGF(4,("reap_children: res = %d, errno = %d.",res,errno));
+ break;
+ }
+ }
+ sys_sigrelease(SIGCHLD);
+}
+
+static void init_signals(void)
+{
+ sys_sigset(SIGCHLD,&reap_children); /* SIG_IGN would give same result
+ on most (?) platforms. */
+ sys_sigset(SIGPIPE, SIG_IGN);
+}
+#endif
+
+static void stall_worker(int ndx)
+{
+ --num_busy_workers;
+ stalled_workers[num_stalled_workers] = busy_workers[ndx];
+ stalled_workers[num_stalled_workers].state = WORKER_STALLED;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ DEBUGF(3, ("Stalled worker[%ld]",
+ (long) stalled_workers[num_stalled_workers].pid));
+ ++num_stalled_workers;
+}
+
+
+/*
+ * Main loop message passing
+ */
+#ifndef WIN32
+static int read_request(AddrByte **buff, size_t *buff_size)
+{
+ int siz;
+ int r;
+
+ if ((r = READ_PACKET_BYTES(0,&siz)) != PACKET_BYTES) {
+ if (r == 0) {
+ return 0;
+ } else {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ }
+
+ if (siz > *buff_size) {
+ if (buff_size == 0) {
+ *buff = ALLOC((*buff_size = siz));
+ } else {
+ *buff = REALLOC(*buff, (*buff_size = siz));
+ }
+ }
+ if (read_exact(0,*buff, siz) != siz) {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ if (siz < 5) {
+ fatal("Unexpected message on main input, message size %d less "
+ "than minimum.");
+ }
+ return siz;
+}
+
+#endif /* !WIN32 */
+
+static OpType get_op(AddrByte *buff)
+{
+ return (OpType) buff[4];
+}
+
+static AddrByte *get_op_addr(AddrByte *buff)
+{
+ return buff + 4;
+}
+
+static SerialType get_serial(AddrByte *buff)
+{
+ return get_int32(buff);
+}
+
+static ProtoType get_proto(AddrByte *buff)
+{
+ return (ProtoType) buff[5];
+}
+
+static CtlType get_ctl(AddrByte *buff)
+{
+ return (CtlType) buff[5];
+}
+
+static AddrByte *get_data(AddrByte *buff)
+{
+ return buff + 6;
+}
+
+static int get_debug_level(AddrByte *buff)
+{
+ return get_int32(buff + 6);
+}
+
+#ifdef WIN32
+static int send_mes_to_worker(QueItem *m, Worker *pw)
+{
+ if (!enque_mesq(pw->writeto, m)) {
+ warning("Unable to send to child process.");
+ return -1;
+ }
+ return 0;
+}
+#else
+static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw)
+{
+ AddrByte hdr[PACKET_BYTES];
+
+ PUT_PACKET_BYTES(hdr, rsize);
+ if (write_exact(pw->writeto, hdr, PACKET_BYTES) < 0) {
+ warning("Unable to write to child process.");
+ return -1;
+ }
+ if (write_exact(pw->writeto, (AddrByte *) pr, rsize) < 0) {
+ warning("Unable to write to child process.");
+ return -1;
+ }
+ return 0;
+}
+#endif /* !WIN32 */
+
+#ifdef WIN32
+static int relay_reply(Worker *pw)
+{
+ QueItem *m;
+ if (!deque_mesq(pw->readfrom,&m)) {
+ return 0;
+ }
+ if (!enque_mesq(to_erlang,m)) {
+ FREE(m);
+ return 0;
+ }
+ return 1;
+}
+
+static int ignore_reply(Worker *pw) {
+ QueItem *m;
+ if (!deque_mesq(pw->readfrom,&m)) {
+ return 0;
+ }
+ FREE(m);
+ return 1;
+}
+
+#else
+
+/* Static buffers used by the next three functions */
+static AddrByte *relay_buff = NULL;
+static int relay_buff_size = 0;
+
+static int fillin_reply(Worker *pw)
+{
+ int length;
+
+ if (READ_PACKET_BYTES(pw->readfrom, &length) != PACKET_BYTES) {
+ warning("Malformed reply (header) from worker process %d.",
+ pw->pid);
+ return -1;
+ }
+
+ if (relay_buff_size < (length + PACKET_BYTES)) {
+ if (!relay_buff_size) {
+ relay_buff =
+ ALLOC((relay_buff_size = (length + PACKET_BYTES)));
+ } else {
+ relay_buff =
+ REALLOC(relay_buff,
+ (relay_buff_size = (length + PACKET_BYTES)));
+ }
+ }
+ PUT_PACKET_BYTES(relay_buff, length);
+ if (read_exact(pw->readfrom, relay_buff + PACKET_BYTES, length) !=
+ length) {
+ warning("Malformed reply (data) from worker process %d.", pw->pid);
+ return -1;
+ }
+ return length;
+}
+
+static int relay_reply(Worker *pw)
+{
+ int length = fillin_reply(pw); /* Filled into the "global" buffer */
+ int res;
+
+ if (length < 0) {
+ return -1;
+ }
+ if ((res = write_exact(1, relay_buff, length + PACKET_BYTES)) < 0) {
+ fatal("Cannot write reply to erlang process, errno = %d.", errno);
+ } else if (res == 0) {
+ DEBUGF(1,("Erlang has closed write pipe."));
+ return 0;
+ }
+ return length;
+}
+
+static int ignore_reply(Worker *pw)
+{
+ return fillin_reply(pw);
+}
+
+#endif /* !WIN32 */
+
+/*
+ * Domain name "parsing" and worker specific queing
+ */
+static void domaincopy(AddrByte *out, AddrByte *in)
+{
+ AddrByte *ptr = out;
+ *ptr++ = *in++;
+ *ptr++ = *in++;
+ switch(*out) {
+ case OP_GETHOSTBYNAME:
+ while(*in != '\0' && *in != '.')
+ ++in;
+ strncpy((char*)ptr, (char*)in, DOMAINNAME_MAX-2);
+ ptr[DOMAINNAME_MAX-3] = '\0';
+ DEBUGF(4,("Saved domainname %s.", ptr));
+ return;
+ case OP_GETHOSTBYADDR:
+ memcpy(ptr,in, ((out[1] == PROTO_IPV4) ? UNIT_IPV4 : UNIT_IPV6) - 1);
+ DEBUGF(4, ("Saved domain address: %s.",
+ format_address(((out[1] == PROTO_IPV4) ?
+ UNIT_IPV4 : UNIT_IPV6) - 1,ptr)));
+ return;
+ default:
+ fatal("Trying to copy buffer not containing valid domain, [%d,%d].",
+ (int) out[0], (int) out[1]);
+ }
+}
+
+static int domaineq(AddrByte *d1, AddrByte *d2)
+{
+ if (d1[0] != d2[0] || d1[1] != d2[1]) {
+ return 0;
+ }
+ switch (d1[0]) {
+ case OP_GETHOSTBYNAME:
+ return !strcmp((char*)d1+2,(char*)d2+2);
+ case OP_GETHOSTBYADDR:
+ return !memcmp(d1+2,d2+2, ((d1[1] == PROTO_IPV4)
+ ? UNIT_IPV4 : UNIT_IPV6) - 1);
+ default:
+ fatal("Trying to compare buffers not containing valid domain, "
+ "[%d,%d].",
+ (int) d1[0], (int) d1[1]);
+ return -1; /* Lint... */
+ }
+}
+
+static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff)
+{
+ OpType op = get_op(inbuff);
+ ProtoType proto;
+ int i;
+ AddrByte *data;
+
+ data = get_data(inbuff);
+ switch (op) {
+ case OP_GETHOSTBYNAME:
+ data = get_data(inbuff);
+ for (i = (data - inbuff); i < insize && inbuff[i] != '\0'; ++i)
+ ;
+ if (i < insize) {
+ domaincopy(domainbuff, get_op_addr(inbuff));
+ return 0;
+ }
+ DEBUGF(3, ("Could not pick valid domainname in "
+ "gethostbyname operation"));
+ return -1;
+ case OP_GETHOSTBYADDR:
+ proto = get_proto(inbuff);
+ i = insize - (data - inbuff);
+ if ((proto == PROTO_IPV4 && i == UNIT_IPV4) ||
+ (proto == PROTO_IPV6 && i == UNIT_IPV6)) {
+ /* An address buffer */
+ domaincopy(domainbuff, get_op_addr(inbuff));
+ return 0;
+ }
+ DEBUGF(3, ("Could not pick valid domainname in gethostbyaddr "
+ "operation"));
+ return -1;
+ default:
+ DEBUGF(2, ("Could not pick valid domainname because of "
+ "invalid opcode %d.", (int) op));
+ return -1;
+ }
+}
+
+/*
+ * Worker subprocesses with utilities
+ */
+#ifdef WIN32
+static int create_worker(Worker *pworker, int save_que)
+{
+ MesQ **thread_data = ALLOC(2*sizeof(MesQ *));
+ DWORD tid;
+
+
+ if (!create_mesq(thread_data)) {
+ fatal("Could not create, pipes for subprocess, errno = %d",
+ GetLastError());
+ }
+ if (!create_mesq(thread_data + 1)) {
+ fatal("Could not create, pipes for subprocess, errno = %d",
+ GetLastError());
+ }
+ /* Save those before the thread starts */
+ pworker->writeto = thread_data[0];
+ pworker->readfrom = thread_data[1];
+
+ if (((HANDLE) _beginthreadex(NULL, 0, worker_loop, thread_data, 0, &tid))
+ == NULL) {
+ fatal("Could not create thread errno = %d",
+ GetLastError());
+ }
+ pworker->pid = tid;
+ pworker->state = WORKER_FREE;
+ pworker->serial = INVALID_SERIAL;
+ if (!save_que) {
+ pworker->que_first = pworker->que_last = NULL;
+ pworker->que_size = 0;
+ }
+ DEBUGF(3,("Created worker[%ld] with fd %d",
+ (long) pworker->pid, (int) pworker->readfrom));
+ return 0;
+}
+
+#else
+
+static int create_worker(Worker *pworker, int save_que)
+{
+ int p0[2], p1[2];
+ pid_t child;
+
+ if (pipe(p0)) {
+ warning("Could not create, pipes for subprocess, errno = %d",
+ errno);
+ return -1;
+ }
+
+ if (pipe(p1)) {
+ warning("Could not create, pipes for subprocess, errno = %d",
+ errno);
+ close(p0[0]);
+ close(p0[1]);
+ return -1;
+ }
+ if ((child = fork()) < 0) { /* failure */
+ warning("Could not fork(), errno = %d",
+ errno);
+ close(p0[0]);
+ close(p0[1]);
+ close(p1[0]);
+ close(p1[1]);
+ return -1;
+ } else if (child > 0) { /* parent */
+ close(p0[1]);
+ close(p1[0]);
+ pworker->writeto = p1[1];
+ pworker->readfrom = p0[0];
+ pworker->pid = child;
+ pworker->state = WORKER_FREE;
+ pworker->serial = INVALID_SERIAL;
+ if (!save_que) {
+ pworker->que_first = pworker->que_last = NULL;
+ pworker->que_size = 0;
+ }
+ DEBUGF(3,("Created worker[%ld] with fd %d",
+ (long) pworker->pid, (int) pworker->readfrom));
+ return 0;
+ } else { /* child */
+ close(p1[1]);
+ close(p0[0]);
+ close_all_worker_fds();
+ /* Make "fatal" not find any children */
+ num_busy_workers = num_free_workers = num_stalled_workers = 0;
+ if((dup2(p1[0],0) < 0) || (dup2(p0[1],1) < 0)) {
+ fatal("Worker could not dup2(), errno = %d",
+ errno);
+ return -1; /* lint... */
+ }
+ close(p1[0]);
+ close(p0[1]);
+ signal(SIGCHLD, SIG_IGN);
+ return worker_loop();
+ }
+}
+
+static void close_all_worker_fds(void)
+{
+ int w,i;
+ Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
+ int wsizes[3] = {num_free_workers, num_busy_workers,
+ num_stalled_workers};
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < wsizes[w]; ++i) {
+ if (workers[w][i].state != WORKER_EMPTY) {
+ close(workers[w][i].readfrom);
+ close(workers[w][i].writeto);
+ }
+ }
+ }
+}
+
+#endif /* !WIN32 */
+
+#ifdef WIN32
+DWORD WINAPI worker_loop(void *v)
+#else
+static int worker_loop(void)
+#endif
+{
+ AddrByte *req = NULL;
+ size_t req_size = 0;
+ int this_size;
+ AddrByte *reply = NULL;
+ size_t reply_size = 0;
+ size_t data_size;
+
+#ifdef WIN32
+ QueItem *m = NULL;
+ MesQ *readfrom = ((MesQ **) v)[0];
+ MesQ *writeto = ((MesQ **) v)[1];
+ /* XXX:PaN */
+ FREE(v);
+#endif
+
+ for(;;) {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo *ai = NULL;
+#endif
+ struct hostent *he = NULL;
+#ifdef HAVE_GETNAMEINFO
+ struct sockaddr *sa = NULL;
+ char name[NI_MAXHOST];
+#endif
+#if defined(HAVE_GETIPNODEBYNAME) || defined(HAVE_GETIPNODEBYADDR)
+ int free_he = 0;
+#endif
+ int error_num = 0;
+ SerialType serial;
+ OpType op;
+ ProtoType proto;
+ AddrByte *data;
+
+#ifdef WIN32
+ WaitForSingleObject(event_mesq(readfrom),INFINITE);
+ DEBUGF(4,("Worker got data on message que."));
+
+ if(!deque_mesq(readfrom,&m)) {
+ goto fail;
+ }
+ this_size = m->req_size;
+ req = m->request;
+#else
+ if (READ_PACKET_BYTES(0,&this_size) != PACKET_BYTES) {
+ DEBUGF(2,("Worker got error/EOF while reading size, exiting."));
+ exit(0);
+ }
+ if (this_size > req_size) {
+ if (req == NULL) {
+ req = ALLOC((req_size = this_size));
+ } else {
+ req = REALLOC(req, (req_size = this_size));
+ }
+ }
+ if (read_exact(0, req, (size_t) this_size) != this_size) {
+ DEBUGF(1,("Worker got EOF while reading data, exiting."));
+ exit(0);
+ }
+#endif
+ /* Decode the request... */
+ serial = get_serial(req);
+ if (OP_CONTROL == (op = get_op(req))) {
+ CtlType ctl;
+ if (serial != INVALID_SERIAL) {
+ DEBUGF(1, ("Worker got invalid serial: %d.", serial));
+ exit(0);
+ }
+ switch (ctl = get_ctl(req)) {
+ case SETOPT_DEBUG_LEVEL:
+ debug_level = get_debug_level(req);
+ DEBUGF(debug_level,
+ ("Worker debug_level = %d.", debug_level));
+ break;
+ }
+ continue;
+ }
+ proto = get_proto(req);
+ data = get_data(req);
+ DEBUGF(4,("Worker got request, op = %d, proto = %d, data = %s.",
+ op,proto,data));
+ /* Got a request, lets go... */
+ switch (op) {
+ case OP_GETHOSTBYNAME:
+ switch (proto) {
+
+#ifdef HAVE_IN6
+ case PROTO_IPV6: { /* switch (proto) { */
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = (AI_CANONNAME|AI_V4MAPPED|AI_ADDRCONFIG);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_INET6;
+ DEBUGF(5, ("Starting getaddrinfo(%s, ...)", data));
+ error_num = getaddrinfo((char *)data, NULL, &hints, &ai);
+ DEBUGF(5,("getaddrinfo returned %d", error_num));
+ if (error_num) {
+ error_num = map_netdb_error_ai(error_num);
+ }
+#elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
+ DEBUGF(5,("Starting getipnodebyname(%s)",data));
+ he = getipnodebyname(data, AF_INET6, AI_DEFAULT, &error_num);
+ if (he) {
+ free_he = 1;
+ error_num = 0;
+ DEBUGF(5,("getipnodebyname(,AF_INET6,,) OK"));
+ } else {
+ DEBUGF(5,("getipnodebyname(,AF_INET6,,) error %d", error_num));
+ error_num = map_netdb_error(error_num);
+ }
+#elif defined(HAVE_GETHOSTBYNAME2) /*#ifdef HAVE_GETADDRINFO */
+ DEBUGF(5,("Starting gethostbyname2(%s, AF_INET6)",data));
+ he = gethostbyname2((char*)data, AF_INET6);
+ if (he) {
+ error_num = 0;
+ DEBUGF(5,("gethostbyname2(, AF_INET6) OK"));
+ } else {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyname2(, AF_INET6) error %d", h_errno));
+ }
+#else
+ error_num = ERRCODE_NOTSUP;
+#endif /*#ifdef HAVE_GETADDRINFO */
+ } break;
+#endif /*ifdef HAVE_IN6 */
+
+ case PROTO_IPV4: { /* switch (proto) { */
+ DEBUGF(5,("Starting gethostbyname(%s)",data));
+ he = gethostbyname((char*)data);
+ if (he) {
+ error_num = 0;
+ DEBUGF(5,("gethostbyname OK"));
+ } else {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyname error %d", h_errno));
+ }
+ } break;
+
+ default: /* switch (proto) { */
+ /* Not supported... */
+ error_num = ERRCODE_NOTSUP;
+ break;
+ } /* switch (proto) { */
+
+ if (he) {
+ data_size = build_reply(serial, he, &reply, &reply_size);
+#ifdef HAVE_GETIPNODEBYNAME
+ if (free_he) {
+ freehostent(he);
+ }
+#endif
+#ifdef HAVE_GETADDRINFO
+ } else if (ai) {
+ data_size = build_reply_ai(serial, 16, ai,
+ &reply, &reply_size);
+ freeaddrinfo(ai);
+#endif
+ } else {
+ data_size = build_error_reply(serial, error_num,
+ &reply, &reply_size);
+ }
+ break; /* case OP_GETHOSTBYNAME: */
+
+ case OP_GETHOSTBYADDR: /* switch (op) { */
+ switch (proto) {
+#ifdef HAVE_IN6
+ case PROTO_IPV6: {
+#ifdef HAVE_GETNAMEINFO
+ struct sockaddr_in6 *sin;
+ socklen_t salen = sizeof(*sin);
+
+ sin = ALLOC(salen);
+#ifndef NO_SA_LEN
+ sin->sin6_len = salen;
+#endif
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = 0;
+ memcpy(&sin->sin6_addr, data, 16);
+ sa = (struct sockaddr *)sin;
+ DEBUGF(5,("Starting getnameinfo(,,%s,16,,,)",
+ format_address(16, data)));
+ error_num = getnameinfo(sa, salen, name, sizeof(name),
+ NULL, 0, NI_NAMEREQD);
+ DEBUGF(5,("getnameinfo returned %d", error_num));
+ if (error_num) {
+ error_num = map_netdb_error_ai(error_num);
+ sa = NULL;
+ }
+#elif defined(HAVE_GETIPNODEBYADDR) /*#ifdef HAVE_GETNAMEINFO*/
+ struct in6_addr ia;
+ memcpy(ia.s6_addr, data, 16);
+ DEBUGF(5,("Starting getipnodebyaddr(%s,16,AF_INET6,)",
+ format_address(16, data)));
+ he = getipnodebyaddr(&ia, 16, AF_INET6, &error_num);
+ free_he = 1;
+ if (! he) {
+ DEBUGF(5,("getipnodebyaddr error %d", error_num));
+ error_num = map_netdb_error(error_num);
+ } else {
+ DEBUGF(5,("getipnodebyaddr OK"));
+ }
+#else /*#ifdef HAVE_GETNAMEINFO*/
+ struct in6_addr ia;
+ memcpy(ia.s6_addr, data, 16);
+ DEBUGF(5,("Starting gethostbyaddr(%s,16,AF_INET6)",
+ format_address(16, data)));
+ he = gethostbyaddr((const char *) &ia, 16, AF_INET6);
+ if (! he) {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyaddr error %d", h_errno));
+ } else {
+ DEBUGF(5,("gethostbyaddr OK"));
+ }
+#endif /* #ifdef HAVE_GETNAMEINFO */
+ } break; /* case PROTO_IPV6: { */
+#endif /* #ifdef HAVE_IN6 */
+
+ case PROTO_IPV4: { /* switch(proto) { */
+ struct in_addr ia;
+ memcpy(&ia.s_addr, data, 4); /* Alignment required... */
+ DEBUGF(5,("Starting gethostbyaddr(%s,4,AF_INET)",
+ format_address(4, data)));
+ he = gethostbyaddr((const char *) &ia, 4, AF_INET);
+ if (! he) {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyaddr error %d", h_errno));
+ } else {
+ DEBUGF(5,("gethostbyaddr OK"));
+ }
+ } break;
+
+ default:
+ error_num = ERRCODE_NOTSUP;
+ } /* switch(proto) { */
+
+ if (he) {
+ data_size = build_reply(serial, he, &reply, &reply_size);
+#ifdef HAVE_GETIPNODEBYADDR
+ if (free_he) {
+ freehostent(he);
+ }
+#endif
+#ifdef HAVE_GETNAMEINFO
+ } else if (sa) {
+ struct addrinfo res;
+ memset(&res, 0, sizeof(res));
+ res.ai_canonname = name;
+ res.ai_addr = sa;
+ res.ai_next = NULL;
+ data_size = build_reply_ai(serial, 16, &res,
+ &reply, &reply_size);
+ free(sa);
+#endif
+ } else {
+ data_size = build_error_reply(serial, error_num,
+ &reply, &reply_size);
+ }
+ break; /* case OP_GETHOSTBYADR: */
+
+ default:
+ data_size = build_error_reply(serial, ERRCODE_NOTSUP,
+ &reply, &reply_size);
+ break;
+ } /* switch (op) { */
+
+#ifdef WIN32
+ m = REALLOC(m, sizeof(QueItem) - 1 + data_size - PACKET_BYTES);
+ m->next = NULL;
+ m->req_size = data_size - PACKET_BYTES;
+ memcpy(m->request,reply + PACKET_BYTES,data_size - PACKET_BYTES);
+ if (!enque_mesq(writeto,m)) {
+ goto fail;
+ }
+ m = NULL;
+#else
+ write(1, reply, data_size); /* No signals expected */
+#endif
+ } /* for (;;) */
+
+#ifdef WIN32
+ fail:
+ if (m != NULL) {
+ FREE(m);
+ }
+ close_mesq(readfrom);
+ close_mesq(writeto);
+ if (reply) {
+ FREE(reply);
+ }
+ return 1;
+#endif
+}
+
+static int map_netdb_error(int netdb_code)
+{
+ switch (netdb_code) {
+#ifdef HOST_NOT_FOUND
+ case HOST_NOT_FOUND:
+ return ERRCODE_HOST_NOT_FOUND;
+#endif
+#ifdef TRY_AGAIN
+ case TRY_AGAIN:
+ return ERRCODE_TRY_AGAIN;
+#endif
+#ifdef NO_RECOVERY
+ case NO_RECOVERY:
+ return ERRCODE_NO_RECOVERY;
+#endif
+#if defined(NO_DATA) || defined(NO_ADDRESS)
+#ifdef NO_DATA
+ case NO_DATA:
+#endif
+#ifdef NO_ADDRESS
+#if !defined(NO_DATA) || (NO_DATA != NO_ADDRESS)
+ case NO_ADDRESS:
+#endif
+#endif
+ return ERRCODE_NO_DATA;
+#endif
+ default:
+ return ERRCODE_NETDB_INTERNAL;
+ }
+}
+
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static int map_netdb_error_ai(int netdb_code)
+{
+ switch(netdb_code) {
+#ifdef EAI_ADDRFAMILY
+ case EAI_ADDRFAMILY:
+ return ERRCODE_NETDB_INTERNAL;
+#endif
+ case EAI_AGAIN:
+ return ERRCODE_TRY_AGAIN;
+ case EAI_BADFLAGS:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_FAIL:
+ return ERRCODE_HOST_NOT_FOUND;
+ case EAI_FAMILY:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_MEMORY:
+ return ERRCODE_NETDB_INTERNAL;
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+ return ERRCODE_HOST_NOT_FOUND;
+#endif
+ case EAI_NONAME:
+ return ERRCODE_HOST_NOT_FOUND;
+ case EAI_SERVICE:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_SOCKTYPE:
+ return ERRCODE_NETDB_INTERNAL;
+ default:
+ return ERRCODE_NETDB_INTERNAL;
+ }
+}
+#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
+
+
+static char *errcode_to_string(int errcode)
+{
+ switch (errcode) {
+ case ERRCODE_NOTSUP:
+ return "enotsup";
+ case ERRCODE_HOST_NOT_FOUND:
+ /*
+ * I would preffer
+ * return "host_not_found";
+ * but have to keep compatibility with the old
+ * inet_gethost's error codes...
+ */
+ return "notfound";
+ case ERRCODE_TRY_AGAIN:
+ return "try_again";
+ case ERRCODE_NO_RECOVERY:
+ return "no_recovery";
+ case ERRCODE_NO_DATA:
+ return "no_data";
+ default:
+ /*case ERRCODE_NETDB_INTERNAL:*/
+ return "netdb_internal";
+ }
+}
+
+static size_t build_error_reply(SerialType serial, int errnum,
+ AddrByte **preply,
+ size_t *preply_size)
+{
+ char *errstring = errcode_to_string(errnum);
+ int string_need = strlen(errstring) + 1; /* a '\0' too */
+ unsigned need;
+ AddrByte *ptr;
+
+ need = PACKET_BYTES + 4 /* Serial */ + 1 /* Unit */ + string_need;
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) 0; /* 4 or 16 */
+ strcpy((char*)ptr, errstring);
+ return need;
+}
+
+
+
+static size_t build_reply(SerialType serial, struct hostent *he,
+ AddrByte **preply, size_t *preply_size)
+{
+ unsigned need;
+ int strings_need;
+ int num_strings;
+ int num_addresses;
+ int i;
+ AddrByte *ptr;
+ int unit = he->h_length;
+
+ for (num_addresses = 0; he->h_addr_list[num_addresses] != NULL;
+ ++num_addresses)
+ ;
+ strings_need = strlen(he->h_name) + 1; /* 1 for null byte */
+ num_strings = 1;
+ if (he->h_aliases) {
+ for(i=0; he->h_aliases[i] != NULL; ++i) {
+ strings_need += strlen(he->h_aliases[i]) + 1;
+ ++num_strings;
+ }
+ }
+
+ need = PACKET_BYTES +
+ 4 /* Serial */ + 1 /* Unit */ + 4 /* Naddr */ +
+ (unit * num_addresses) /* Address bytes */ +
+ 4 /* Nnames */ + strings_need /* The name and alias strings */;
+
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) unit; /* 4 or 16 */
+ put_int32(ptr, num_addresses);
+ ptr += 4;
+ for (i = 0; i < num_addresses; ++i) {
+ memcpy(ptr, he->h_addr_list[i], unit);
+ ptr += unit;
+ }
+ put_int32(ptr, num_strings);
+ ptr += 4;
+ strcpy((char*)ptr, he->h_name);
+ ptr += 1 + strlen(he->h_name);
+ for (i = 0; i < (num_strings - 1); ++i) {
+ strcpy((char*)ptr, he->h_aliases[i]);
+ ptr += 1 + strlen(he->h_aliases[i]);
+ }
+ return need;
+}
+
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static size_t build_reply_ai(SerialType serial, int addrlen,
+ struct addrinfo *res0,
+ AddrByte **preply, size_t *preply_size)
+{
+ struct addrinfo *res;
+ int num_strings;
+ int num_addresses;
+ AddrByte *ptr;
+ int need;
+
+ num_addresses = 0;
+ num_strings = 0;
+ need = PACKET_BYTES +
+ 4 /* Serial */ + 1 /* addrlen */ +
+ 4 /* Naddr */ + 4 /* Nnames */;
+
+ for (res = res0; res != NULL; res = res->ai_next) {
+ if (res->ai_addr) {
+ num_addresses++;
+ need += addrlen;
+ }
+ if (res->ai_canonname) {
+ num_strings++;
+ need += strlen(res->ai_canonname) + 1;
+ }
+ }
+
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) addrlen; /* 4 or 16 */
+ put_int32(ptr, num_addresses);
+ ptr += 4;
+ for (res = res0; res != NULL && num_addresses; res = res->ai_next) {
+ if (res->ai_addr == NULL)
+ continue;
+ if (addrlen == 4)
+ memcpy(ptr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addrlen);
+#ifdef AF_INET6
+ else if (addrlen == 16)
+ memcpy(ptr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrlen);
+#endif
+ else
+ memcpy(ptr, res->ai_addr->sa_data, addrlen);
+ ptr += addrlen;
+ num_addresses--;
+ }
+ put_int32(ptr, num_strings);
+ ptr += 4;
+ for (res = res0; res != NULL && num_strings; res = res->ai_next) {
+ if (res->ai_canonname == NULL)
+ continue;
+ strcpy((char *)ptr, res->ai_canonname);
+ ptr += strlen(res->ai_canonname) + 1;
+ num_strings--;
+ }
+ return need;
+}
+
+#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
+
+
+
+/*
+ * Encode/decode/read/write
+ */
+
+static int get_int32(AddrByte *b)
+{
+ int res;
+ res = (unsigned) b[3];
+ res |= ((unsigned) b[2]) << 8;
+ res |= ((unsigned) b[1]) << 16;
+ res |= ((unsigned) b[0]) << 24;
+ return res;
+}
+
+static void put_int32(AddrByte *buff, int value)
+{
+ buff[0] = (((unsigned) value) >> 24) & 0xFF;
+ buff[1] = (((unsigned) value) >> 16) & 0xFF;
+ buff[2] = (((unsigned) value) >> 8) & 0xFF;
+ buff[3] = ((unsigned) value) & 0xFF;
+}
+#ifdef WIN32
+
+static int read_int32(HANDLE fd, int *res, HANDLE ev)
+{
+ AddrByte b[4];
+ int r;
+ if ((r = read_exact(fd,b,4,ev)) < 0) {
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ } else {
+ *res = (unsigned) b[3];
+ *res |= ((unsigned) b[2]) << 8;
+ *res |= ((unsigned) b[1]) << 16;
+ *res |= ((unsigned) b[0]) << 24;
+ }
+ return 4;
+}
+/*
+ * The standard input is expected to be opened with FILE_FLAG_OVERLAPPED
+ * but this code should handle both cases (although winsock might not).
+ */
+static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev)
+{
+ DWORD ret,got;
+ BOOL stat;
+ char *buff = vbuff;
+ OVERLAPPED ov;
+ DWORD err;
+
+
+ got = 0;
+ for(;;) {
+ memset(&ov,0,sizeof(ov));
+ ov.hEvent = ev;
+ ResetEvent(ov.hEvent);
+ stat = ReadFile(fd, buff, nbytes - got, &ret, &ov);
+ if (!stat) {
+ if ((err = GetLastError()) == ERROR_IO_PENDING) {
+ DEBUGF(4,("Overlapped read, waiting for completion..."));
+ WaitForSingleObject(ov.hEvent,INFINITE);
+ stat = GetOverlappedResult(fd,&ov,&ret,TRUE);
+ DEBUGF(4,("Overlapped read, completed with status %d,"
+ " result %d",stat,ret));
+ }
+ if (!stat) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ DEBUGF(1, ("End of file while reading from pipe."));
+ return 0;
+ } else {
+ DEBUGF(1, ("Error while reading from pipe,"
+ " errno = %d",
+ GetLastError()));
+ return -1;
+ }
+ }
+ } else {
+ DEBUGF(4,("Read completed syncronously, result %d",ret));
+ }
+ if (ret == 0) {
+ DEBUGF(1, ("End of file detected as zero read from pipe."));
+ return 0;
+ }
+ if (ret < nbytes - got) {
+ DEBUGF(4,("Not all data read from pipe, still %d bytes to read.",
+ nbytes - (got + ret)));
+ got += ret;
+ buff += ret;
+ } else {
+ return nbytes;
+ }
+ }
+}
+/*
+ * Now, we actually expect a HANDLE opened with FILE_FLAG_OVERLAPPED,
+ * but this code should handle both cases (although winsock
+ * does not always..)
+ */
+static int write_exact(HANDLE fd, AddrByte *buff, DWORD len, HANDLE ev)
+{
+ DWORD res,stat;
+ DWORD x = len;
+ OVERLAPPED ov;
+ DWORD err;
+
+
+ for(;;) {
+ memset(&ov,0,sizeof(ov));
+ ov.hEvent = ev;
+ ResetEvent(ov.hEvent);
+ stat = WriteFile(fd,buff,x,&res,&ov);
+ if (!stat) {
+ if ((err = GetLastError()) == ERROR_IO_PENDING) {
+ DEBUGF(4,("Overlapped write, waiting for competion..."));
+ WaitForSingleObject(ov.hEvent,INFINITE);
+ stat = GetOverlappedResult(fd,&ov,&res,TRUE);
+ DEBUGF(4,("Overlapped write, completed with status %d,"
+ " result %d",stat,res));
+ }
+ if (!stat) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ } else {
+ DEBUGF(4,("Write completed syncronously, result %d",res));
+ }
+
+ if (res < x) {
+ /* Microsoft states this can happen as HANDLE is a pipe... */
+ DEBUGF(4,("Not all data written to pipe, still %d bytes to write.",
+ x - res));
+ x -= res;
+ buff += res;
+ } else {
+ return len;
+ }
+ }
+}
+
+DWORD WINAPI reader(void *data) {
+ MesQ *mq = (MesQ *) data;
+ QueItem *m;
+ int siz;
+ int r;
+ HANDLE inp;
+ int x = 0;
+ HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ inp = GetStdHandle(STD_INPUT_HANDLE);
+ for (;;) {
+ if ((r = READ_PACKET_BYTES(inp,&siz,ev)) != 4) {
+ DEBUGF(1,("Erlang has closed (reading)"));
+ exit(0);
+ }
+ DEBUGF(4,("Read packet of size %d from erlang",siz));
+ m = ALLOC(sizeof(QueItem) - 1 + siz);
+ if (read_exact(inp, m->request, siz,ev) != siz) {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ if (siz < 5) {
+ fatal("Unexpected message on main input, message size %d less "
+ "than minimum.");
+ }
+ m->req_size = siz;
+ m->next = NULL;
+ if (!enque_mesq(mq, m)) {
+ fatal("Reader could not talk to main thread!");
+ }
+ }
+}
+
+DWORD WINAPI writer(void *data)
+{
+ MesQ *mq = (MesQ *) data;
+ QueItem *m;
+ HANDLE outp = GetStdHandle(STD_OUTPUT_HANDLE);
+ AddrByte hdr[PACKET_BYTES];
+ HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+
+ for (;;) {
+ WaitForSingleObject(event_mesq(mq),INFINITE);
+ if (!deque_mesq(mq, &m)) {
+ fatal("Writer could not talk to main thread!");
+ }
+ PUT_PACKET_BYTES(hdr, m->req_size);
+ if (write_exact(outp, hdr, 4, ev) != 4) {
+ DEBUGF(1,("Erlang has closed (writing)"));
+ exit(0);
+ }
+ if (write_exact(outp, m->request, m->req_size, ev) != m->req_size) {
+ DEBUGF(1,("Erlang has closed (writing)"));
+ exit(0);
+ }
+ FREE(m);
+ }
+}
+
+
+#else
+
+static size_t read_int32(int fd, int *res)
+{
+ AddrByte b[4];
+ int r;
+ if ((r = read_exact(fd,b,4)) < 0) {
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ } else {
+ *res = (unsigned) b[3];
+ *res |= ((unsigned) b[2]) << 8;
+ *res |= ((unsigned) b[1]) << 16;
+ *res |= ((unsigned) b[0]) << 24;
+ }
+ return 4;
+}
+
+static ssize_t read_exact(int fd, void *vbuff, size_t nbytes)
+{
+ ssize_t ret, got;
+ char *buff = vbuff;
+
+ got = 0;
+ for(;;) {
+ ret = read(fd, buff, nbytes - got);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ DEBUGF(1, ("Error while reading from pipe,"
+ " errno = %d",
+ errno));
+ return -1;
+ }
+ } else if (ret == 0) {
+ DEBUGF(1, ("End of file while reading from pipe."));
+ if (got == 0) {
+ return 0; /* "Normal" EOF */
+ } else {
+ return -1;
+ }
+ } else if (ret < nbytes - got) {
+ got += ret;
+ buff += ret;
+ } else {
+ return nbytes;
+ }
+ }
+}
+
+static int write_exact(int fd, AddrByte *buff, int len)
+{
+ int res;
+ int x = len;
+ for(;;) {
+ if((res = write(fd, buff, x)) == x) {
+ break;
+ }
+ if (res < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EPIPE) {
+ return 0;
+ }
+#ifdef ENXIO
+ else if (errno == ENXIO) {
+ return 0;
+ }
+#endif
+ else {
+ return -1;
+ }
+ } else {
+ /* Hmmm, blocking write but not all written, could this happen
+ if the other end was closed during the operation? Well,
+ it costs very little to handle anyway... */
+ x -= res;
+ buff += res;
+ }
+ }
+ return len;
+}
+
+#endif /* !WIN32 */
+
+/*
+ * Debug and memory allocation
+ */
+
+static char *format_address(int siz, AddrByte *addr)
+{
+ static char buff[50];
+ char tmp[10];
+ if (siz > 16) {
+ return "(unknown)";
+ }
+ *buff='\0';
+ if (siz <= 4) {
+ while(siz--) {
+ sprintf(tmp,"%d",(int) *addr++);
+ strcat(buff,tmp);
+ if(siz) {
+ strcat(buff,".");
+ }
+ }
+ return buff;
+ }
+ while(siz--) {
+ sprintf(tmp,"%02x",(int) *addr++);
+ strcat(buff,tmp);
+ if(siz) {
+ strcat(buff,":");
+ }
+ }
+ return buff;
+}
+
+static void debugf(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+#ifdef WIN32
+ sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
+#else
+ sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) getpid());
+#endif
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ if (debug_console_allocated != INVALID_HANDLE_VALUE) {
+ DWORD res;
+ WriteFile(debug_console_allocated,buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+}
+
+static void warning(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+ sprintf(buff,"%s[%d]: WARNING:",program_name, (int) getpid());
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ {
+ DWORD res;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+}
+
+static void fatal(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+ sprintf(buff,"%s[%d]: FATAL ERROR:",program_name, (int) getpid());
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ {
+ DWORD res;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+#ifndef WIN32
+ kill_all_workers();
+#endif
+ exit(1);
+}
+
+static void *my_malloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr) {
+ fatal("Cannot allocate %u bytes of memory.", (unsigned) size);
+ return NULL; /* lint... */
+ }
+ return ptr;
+}
+
+static void *my_realloc(void *old, size_t size)
+{
+ void *ptr = realloc(old, size);
+ if (!ptr) {
+ fatal("Cannot reallocate %u bytes of memory from %p.",
+ (unsigned) size, old);
+ return NULL; /* lint... */
+ }
+ return ptr;
+}
+
+#ifdef WIN32
+
+BOOL create_mesq(MesQ **q)
+{
+ MesQ *tmp = malloc(sizeof(MesQ));
+ tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL);
+ if (tmp->data_present == NULL) {
+ free(tmp);
+ return FALSE;
+ }
+ InitializeCriticalSection(&(tmp->crit)); /* Cannot fail */
+ tmp->shutdown = 0;
+ tmp->first = NULL;
+ tmp->last = NULL;
+ *q = tmp;
+ return TRUE;
+}
+
+BOOL enque_mesq(MesQ *q, QueItem *m)
+{
+ EnterCriticalSection(&(q->crit));
+ if (q->shutdown) {
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ if (q->last == NULL) {
+ q->first = q->last = m;
+ } else {
+ q->last->next = m;
+ q->last = m;
+ }
+ m->next = NULL;
+ if (!SetEvent(q->data_present)) {
+ fprintf(stderr,"Fatal: Unable to signal event in %s:%d, last error: %d\n",
+ __FILE__,__LINE__,GetLastError());
+ exit(1); /* Unable to continue at all */
+ }
+ LeaveCriticalSection(&(q->crit));
+ return TRUE;
+}
+
+BOOL deque_mesq(MesQ *q, QueItem **m)
+{
+ EnterCriticalSection(&(q->crit));
+ if (q->first == NULL) { /* Usually shutdown from other end */
+ ResetEvent(q->data_present);
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ *m = q->first;
+ q->first = q->first->next;
+ if (q->first == NULL) {
+ q->last = NULL;
+ ResetEvent(q->data_present);
+ }
+ (*m)->next = NULL;
+ LeaveCriticalSection(&(q->crit));
+ return TRUE;
+}
+
+BOOL close_mesq(MesQ *q)
+{
+ QueItem *tmp;
+ EnterCriticalSection(&(q->crit));
+ if (!q->shutdown) {
+ q->shutdown = TRUE;
+ if (!SetEvent(q->data_present)) {
+ fprintf(stderr,
+ "Fatal: Unable to signal event in %s:%d, last error: %d\n",
+ __FILE__,__LINE__,GetLastError());
+ exit(1); /* Unable to continue at all */
+ }
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ /* Noone else is supposed to use this object any more */
+ LeaveCriticalSection(&(q->crit));
+ DeleteCriticalSection(&(q->crit));
+ CloseHandle(q->data_present);
+ tmp = q->first;
+ while(tmp) {
+ q->first = q->first->next;
+ free(tmp);
+ tmp = q->first;
+ }
+ free(q);
+ return TRUE;
+}
+
+HANDLE event_mesq(MesQ *q)
+{
+ return q->data_present;
+}
+
+#ifdef HARDDEBUG
+DWORD WINAPI pseudo_worker_loop(void *v)
+{
+ HOSTENT *hep;
+
+ DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") starting"));
+ hep = gethostbyname("ftp.funet.fi");
+
+ DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") -> %d OK",(int) hep));
+ return 0;
+}
+
+static void poll_gethost(int row) {
+ HANDLE h;
+ DWORD tid;
+ h = (HANDLE) _beginthreadex(NULL, 0, pseudo_worker_loop, NULL, 0, &tid);
+ if (h == NULL) {
+ DEBUGF(1,("Failed to spawn pseudo worker (%d)...",row));
+ } else {
+ DEBUGF(1,("Waiting for pseudo worker (%d)", row));
+ WaitForSingleObject(h,INFINITE);
+ DEBUGF(1,("Done Waiting for pseudo worker (%d)", row));
+ }
+}
+#endif
+
+#endif /* WIN32 */
diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c
new file mode 100644
index 0000000000..c2567cb8b4
--- /dev/null
+++ b/erts/etc/common/typer.c
@@ -0,0 +1,416 @@
+/*
+ * %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%
+ */
+/*
+ * Purpose: Typer front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ int need_shell = 0;
+
+ emulator = get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ /*
+ * Push initial arguments.
+ */
+
+ if (argc > 1 && strcmp(argv[1], "-smp") == 0) {
+ PUSH("-smpauto");
+ argc--, argv++;
+ }
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-run", "typer", "start");
+ PUSH("-extra");
+
+ /*
+ * Push everything except --shell.
+ */
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "--shell") == 0) {
+ need_shell = 1;
+ } else {
+ PUSH(argv[1]);
+ }
+ argc--, argv++;
+ }
+
+ if (!need_shell) {
+ UNSHIFT("-noinput");
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we were executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "typer: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "typer: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */