From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- erts/etc/common/Makefile | 23 + erts/etc/common/Makefile.in | 564 ++++++++ erts/etc/common/dialyzer.c | 466 +++++++ erts/etc/common/erlc.c | 701 ++++++++++ erts/etc/common/erlexec.c | 2038 +++++++++++++++++++++++++++++ erts/etc/common/escript.c | 697 ++++++++++ erts/etc/common/heart.c | 1142 +++++++++++++++++ erts/etc/common/inet_gethost.c | 2757 ++++++++++++++++++++++++++++++++++++++++ erts/etc/common/typer.c | 416 ++++++ 9 files changed, 8804 insertions(+) create mode 100644 erts/etc/common/Makefile create mode 100644 erts/etc/common/Makefile.in create mode 100644 erts/etc/common/dialyzer.c create mode 100644 erts/etc/common/erlc.c create mode 100644 erts/etc/common/erlexec.c create mode 100644 erts/etc/common/escript.c create mode 100644 erts/etc/common/heart.c create mode 100644 erts/etc/common/inet_gethost.c create mode 100644 erts/etc/common/typer.c (limited to 'erts/etc/common') 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 +#endif + +#include + +#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 +/* FIXE ME config_win32.h? */ +#define HAVE_STRERROR 1 +#endif + +#include + +#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. + */ + 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 +#include +#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 ] [+P MAX_PROCS] [+R COMPAT_REL] " + "[+r] [+s SCHEDULER_OPTION] [+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] [+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 +#endif + +#include + +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 +#include +#include +#include +#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 +#include +#include + +#include + +#include +#include +#include + +#ifdef VXWORKS +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif +#if !defined(__WIN32__) && !defined(VXWORKS) +# include +# include +# include +# include +# include +# if defined(HEART_CORRECT_USING_TIMES) +# include +# include +# 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 +# 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 +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include + +#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 ] " + "[]\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 +#endif + +#include + +#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__ */ -- cgit v1.2.3