diff options
author | Erlang/OTP <otp@erlang.org> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <otp@erlang.org> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/etc | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/etc')
101 files changed, 27824 insertions, 0 deletions
diff --git a/erts/etc/Makefile b/erts/etc/Makefile new file mode 100644 index 0000000000..2b32b8ae50 --- /dev/null +++ b/erts/etc/Makefile @@ -0,0 +1,27 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-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 + + +SUB_DIRECTORIES = common +ifeq ($(TARGET),win32) +SUB_DIRECTORIES += win32 +endif + +include $(ERL_TOP)/make/otp_subdir.mk diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile new file mode 100644 index 0000000000..73ab79d145 --- /dev/null +++ b/erts/etc/common/Makefile @@ -0,0 +1,23 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +# Invoke with GNU make or clearmake -C gnu. +# + +include $(ERL_TOP)/make/run_make.mk diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in new file mode 100644 index 0000000000..a9acab640e --- /dev/null +++ b/erts/etc/common/Makefile.in @@ -0,0 +1,564 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +include $(ERL_TOP)/make/target.mk + +ERTS_LIB_TYPEMARKER=.$(TYPE) + +USING_MINGW=@MIXED_CYGWIN_MINGW@ +USING_VC=@MIXED_CYGWIN_VC@ + +ifeq ($(TYPE),debug) +PURIFY = +TYPEMARKER = .debug +TYPE_FLAGS = -DDEBUG @DEBUG_FLAGS@ +else +ifeq ($(TYPE),purify) +PURIFY = purify +TYPEMARKER = +ifeq ($(findstring ose,$(TARGET)),ose) +TYPE_FLAGS = -g -XO -DPURIFY +else +TYPE_FLAGS = -g -O2 -DPURIFY +endif +else +PURIFY = +TYPEMARKER = +ERTS_LIB_TYPEMARKER= +TYPE_FLAGS = +endif +endif + +include $(ERL_TOP)/make/$(TARGET)/otp.mk +include ../../vsn.mk + +ERTS_INCL = -I$(ERL_TOP)/erts/include \ + -I$(ERL_TOP)/erts/include/$(TARGET) \ + -I$(ERL_TOP)/erts/include/internal \ + -I$(ERL_TOP)/erts/include/internal/$(TARGET) + +CC = @CC@ +WFLAGS = @WFLAGS@ +CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) -I$(EMUDIR) \ + $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\" +LD = @LD@ +LIBS = @LIBS@ +LDFLAGS = @LDFLAGS@ + +ifeq ($(TARGET),win32) +ifeq ($(TYPE),debug) +CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \ + -I$(EMUDIR) $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\") +LDFLAGS += -g +endif +endif +BINDIR = $(ERL_TOP)/bin/$(TARGET) +OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET) +EMUDIR = $(ERL_TOP)/erts/emulator/beam +EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@ +SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@ +DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@ +VXETC = ../vxworks +UXETC = ../unix +OSEETC = ../ose +WINETC = ../win32 + +ifeq ($(findstring vxworks,$(TARGET)), vxworks) +ERLEXEC = erl.exec +else +ifeq ($(findstring ose,$(TARGET)), ose) +ERLEXEC = +TAR = @TAR@ +else +ifeq ($(TARGET), win32) +ERLEXEC = erlexec.dll +else +ERLEXEC = erlexec +endif +endif +endif + +# On windows we always need reentrant libraries. +ifeq ($(TARGET),win32) +ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ +else +ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ +endif + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- + +ifeq ($(findstring vxworks,$(TARGET)), vxworks) +INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall +INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf +INSTALL_INCLUDES = $(VXETC)/reclaim.h +INSTALL_TOP = $(VXETC)/README.VxWorks +INSTALL_MISC = +INSTALL_SRC = heart.c $(VXETC)/heart_config.h $(VXETC)/heart_config.c \ + $(VXETC)/erl.exec.c $(VXETC)/rdate.c $(VXETC)/vxcall.c \ + $(VXETC)/erl_io.c +ERLEXECDIR = $(VXETC) +INSTALL_LIBS = $(OBJDIR)/reclaim.o +INSTALL_OBJS = $(OBJDIR)/heart.o +TEXTFILES = $(BINDIR)/erl_script.sam +ERLSRV_OBJECTS= +MC_OUTPUTS= +ENTRY_LDFLAGS= +ENTRY_OBJ= +INSTALL_PROGS = \ + $(INET_GETHOST) \ + $(BINDIR)/heart \ + $(BINDIR)/$(ERLEXEC) \ + $(INSTALL_EMBEDDED_PROGS) +else +ifeq ($(findstring ose,$(TARGET)), ose) +INSTALL_TOP = $(OSEETC)/README.OSE +INSTALL_ERL_OSE = monolith lm erl_utils drivers port_progs host +INSTALL_SRC = +INSTALL_LIBS = +INSTALL_OBJS = +INSTALL_INCLUDES = +INSTALL_PROGS = +ERLSRV_OBJECTS= +MC_OUTPUTS= +ENTRY_LDFLAGS= +ENTRY_OBJ= +else +ifeq ($(TARGET),win32) +CFLAGS += -I$(EMUOSDIR) -I$(WINETC) +RC=rc.sh +MC=mc.sh +ERLSRV_HEADERS= \ + $(WINETC)/erlsrv/erlsrv_global.h \ + $(WINETC)/erlsrv/erlsrv_registry.h \ + $(WINETC)/erlsrv/erlsrv_util.h \ + $(WINETC)/erlsrv/erlsrv_interactive.h \ + $(WINETC)/erlsrv/erlsrv_service.h + +ifeq ($(USING_VC), yes) +ERLRES_OBJ=erl.res +ERLSRV_OBJECTS= \ + $(OBJDIR)/erlsrv_registry.o \ + $(OBJDIR)/erlsrv_service.o \ + $(OBJDIR)/erlsrv_interactive.o \ + $(OBJDIR)/erlsrv_main.o \ + $(OBJDIR)/erlsrv_util.o \ + $(OBJDIR)/erlsrv_logmess.res +MC_OUTPUTS= \ + $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.rc + MT_FLAG="-MT" +else +ERLRES_OBJ=erl_res.o +ERLSRV_OBJECTS= \ + $(OBJDIR)/erlsrv_registry.o \ + $(OBJDIR)/erlsrv_service.o \ + $(OBJDIR)/erlsrv_interactive.o \ + $(OBJDIR)/erlsrv_main.o \ + $(OBJDIR)/erlsrv_util.o \ + $(OBJDIR)/erlsrv_logmess.o +MC_OUTPUTS= \ + $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.res + MT_FLAG="-MD" +endif +INET_GETHOST = $(BINDIR)/inet_gethost.exe +INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer.exe $(BINDIR)/dialyzer.exe $(BINDIR)/erlc.exe $(BINDIR)/start_erl.exe $(BINDIR)/escript.exe +INSTALL_SRC = $(WINETC)/start_erl.c $(WINETC)/Nmakefile.start_erl +ERLEXECDIR=. +INSTALL_LIBS = +INSTALL_OBJS = +INSTALL_INCLUDES = +TEXTFILES = Install.ini +INSTALL_TOP = Install.ini +INSTALL_TOP_BIN = $(BINDIR)/Install.exe +INSTALL_PROGS = \ + $(INET_GETHOST) \ + $(BINDIR)/heart.exe $(BINDIR)/erlsrv.exe \ + $(BINDIR)/erl.exe $(BINDIR)/werl.exe \ + $(BINDIR)/$(ERLEXEC) \ + $(INSTALL_EMBEDDED_PROGS) + +ENTRY_SRC=$(WINETC)/port_entry.c +ENTRY_OBJ=$(OBJDIR)/port_entry.o +ifeq ($(USING_VC), yes) +WINDSOCK = ws2_32.lib mswsock.lib +else +WINDSOCK = -lws2_32 +endif +PORT_ENTRY_POINT=erl_port_entry +ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT) + +else +ENTRY_LDFLAGS= +ENTRY_OBJ= +ERLSRV_OBJECTS= +MC_OUTPUTS= +INET_GETHOST = $(BINDIR)/inet_gethost@EXEEXT@ +INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer@EXEEXT@ $(BINDIR)/dialyzer@EXEEXT@ \ + $(BINDIR)/erlc@EXEEXT@ $(BINDIR)/escript@EXEEXT@ \ + $(BINDIR)/run_erl $(BINDIR)/to_erl $(BINDIR)/dyn_erl +INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src +INSTALL_TOP = Install +INSTALL_TOP_BIN = +INSTALL_MISC = ../unix/format_man_pages ../unix/makewhatis +INSTALL_SRC = ../unix/setuid_socket_wrap.c #delivered as an example +ERLEXECDIR = . +INSTALL_LIBS = +INSTALL_OBJS = +INSTALL_INCLUDES = +TEXTFILES = Install erl.src +INSTALL_PROGS = \ + $(INET_GETHOST) \ + $(BINDIR)/heart@EXEEXT@ \ + $(BINDIR)/$(ERLEXEC) \ + $(INSTALL_EMBEDDED_PROGS) +endif +endif +endif + +etc: erts_lib $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN) + +# erlexec needs the erts_internal library... +erts_lib: + cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE) + +docs: + +clean: +ifneq ($(INSTALL_PROGS),) + rm -f $(INSTALL_PROGS) +endif +ifneq ($(ENTRY_OBJ),) + rm -f $(ENTRY_OBJ) +endif +ifneq ($(ERLSRV_OBJECTS),) + rm -f $(ERLSRV_OBJECTS) +endif +ifneq ($(MC_OUTPUTS),) + rm -f $(MC_OUTPUTS) +endif +ifneq ($(ERLRES_OBJ),) + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/$(ERLRES_OBJ) +endif + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/win_erlexec.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/init_file.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/start_erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dialyzer.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlexec.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl_io.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlc.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/escript.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart_config.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/inet_gethost.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/rdate.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/reclaim.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl.o + rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/werl.o + rm -f *~ core + +# +# Objects & executables +# +#$(OBJDIR)/%.o: %.c +# $(CC) $(CFLAGS) -o $@ -c $< +# +#$(OBJDIR)/%.o: ../unix/%.c +# $(CC) $(CFLAGS) -o $@ -c $< +# +#$(BINDIR)/%: $(OBJDIR)/%.o +# $(PURIFY) $(LD) $(LDFLAGS) -o $@ $< $(LIBS) + +$(OBJDIR)/inet_gethost.o: inet_gethost.c + $(CC) $(CFLAGS) -o $@ -c inet_gethost.c + +$(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) + $(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS) + +$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS) + +$(OBJDIR)/run_erl.o: ../unix/run_erl.c + $(CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c + +$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o + +$(OBJDIR)/to_erl.o: ../unix/to_erl.c + $(CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c + +$(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o + +$(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c + $(CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c + +$(OBJDIR)/safe_string.o: ../unix/safe_string.c + $(CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c + +ifneq ($(TARGET),win32) +$(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/$(ERLEXEC).o $(ERLEXEC_XLIBS) + +$(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c + $(CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c +endif +$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) + +$(OBJDIR)/erlc.o: erlc.c + $(CC) $(CFLAGS) -o $@ -c erlc.c + +$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) + +$(OBJDIR)/dialyzer.o: dialyzer.c + $(CC) $(CFLAGS) -o $@ -c dialyzer.c + +$(BINDIR)/typer@EXEEXT@: $(OBJDIR)/typer.o + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS) + +$(OBJDIR)/typer.o: typer.c + $(CC) $(CFLAGS) -o $@ -c typer.c + +$(BINDIR)/escript@EXEEXT@: $(OBJDIR)/escript.o + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS) + +$(OBJDIR)/escript.o: escript.c + $(CC) $(CFLAGS) -o $@ -c escript.c + +#------------------------------------------------------------------------ +# Windows specific targets +# The windows platform is quite different from the others. erl/werl are small C programs +# loading a DLL. INI files are used instead of environment variables and the Install +# script is actually a program, also Install has an INI file which tells of emulator +# versions etc. +#---------------------------------------------------------------------- + +ifeq ($(TARGET),win32) + +$(BINDIR)/$(ERLEXEC): $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) dbg + $(LD) -dll $(LDFLAGS) -o $@ $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERLEXEC_XLIBS) + +dbg: + echo DBG + +$(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) + +$(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) + +$(BINDIR)/start_erl@EXEEXT@: $(OBJDIR)/start_erl.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o + +$(BINDIR)/Install@EXEEXT@: $(OBJDIR)/Install.o $(OBJDIR)/init_file.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/Install.o $(OBJDIR)/init_file.o + +$(BINDIR)/erlsrv@EXEEXT@: $(ERLSRV_OBJECTS) + $(LD) $(LDFLAGS) $(MT_FLAG) -o $@ $(ERLSRV_OBJECTS) + +# The service expects to be compiled with $(MT_FLAG) flag. + +$(OBJDIR)/%.o: $(WINETC)/erlsrv/%.c $(ERLSRV_HEADERS) + $(CC) $(CFLAGS) $(MT_FLAG) -o $@ -c $< + +$(OBJDIR)/erlsrv_util.o: $(WINETC)/erlsrv/erlsrv_util.c $(ERLSRV_HEADERS) \ +$(OBJDIR)/erlsrv_logmess.h + $(CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $< + +ifeq ($(USING_VC), yes) +$(OBJDIR)/erlsrv_logmess.res: $(OBJDIR)/erlsrv_logmess.rc + $(RC) -o $(OBJDIR)/erlsrv_logmess.res -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.rc +else +$(OBJDIR)/erlsrv_logmess.o: $(OBJDIR)/erlsrv_logmess.res + $(RC) -o $(OBJDIR)/erlsrv_logmess.o -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.res +endif + +$(MC_OUTPUTS): $(WINETC)/erlsrv/erlsrv_logmess.mc + $(MC) -o $(OBJDIR) $(WINETC)/erlsrv/erlsrv_logmess.mc + +$(OBJDIR)/werl.o: $(WINETC)/erl.c + $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ + -DWIN32_WERL -o $@ -c $(WINETC)/erl.c + +$(OBJDIR)/erl.o: $(WINETC)/erl.c + $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ + -o $@ -c $(WINETC)/erl.c + +$(OBJDIR)/erlexec.o: $(ERLEXECDIR)/erlexec.c + $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ + -o $@ -c $(ERLEXECDIR)/erlexec.c + +$(OBJDIR)/win_erlexec.o: $(WINETC)/win_erlexec.c + $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \ + -o $@ -c $(WINETC)/win_erlexec.c + +$(OBJDIR)/init_file.o: $(WINETC)/init_file.c $(WINETC)/init_file.h + $(CC) $(CFLAGS) -o $@ -c $(WINETC)/init_file.c + +$(OBJDIR)/Install.o: $(WINETC)/Install.c $(WINETC)/init_file.h + $(CC) $(CFLAGS) -o $@ -c $(WINETC)/Install.c + +$(OBJDIR)/$(ERLRES_OBJ): $(WINETC)/erl.rc $(WINETC)/erlang.ico $(WINETC)/erl_icon.ico $(WINETC)/hrl_icon.ico $(WINETC)/beam_icon.ico + $(RC) -o $@ -I$(WINETC) $(WINETC)/erl.rc + +$(OBJDIR)/start_erl.o: $(WINETC)/start_erl.c + $(CC) $(CFLAGS) -o $@ -c $(WINETC)/start_erl.c + +$(ENTRY_OBJ): $(ENTRY_SRC) + $(CC) $(CFLAGS) -o $@ -c $(ENTRY_SRC) + +Install.ini: ../$(TARGET)/Install.src ../../vsn.mk $(TARGET)/Makefile + sed -e 's;%I_VSN%;$(VSN);' \ + -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \ + ../$(TARGET)/Install.src > Install.ini + + +endif +#--------------------------------------------------------- +# End of windows specific targets. +#--------------------------------------------------------- + +ifeq ($(findstring vxworks,$(TARGET)), vxworks) +$(BINDIR)/heart: $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o + +$(OBJDIR)/heart_config.o: $(VXETC)/heart_config.c + $(CC) $(CFLAGS) -o $@ -c $(VXETC)/heart_config.c + +$(OBJDIR)/reclaim.o: $(VXETC)/reclaim.c + $(CC) $(CFLAGS) -o $@ -c $(VXETC)/reclaim.c + +$(OBJDIR)/heart.o: heart.c + $(CC) $(CFLAGS) -I$(VXETC) -o $@ -c heart.c + +$(BINDIR)/erl_script.sam: $(VXETC)/erl_script.sam.in ../../vsn.mk + sed -e 's;%VSN%;$(VSN);' \ + $(VXETC)/erl_script.sam.in > $(BINDIR)/erl_script.sam +else + +$(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ) + $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \ + $(ENTRY_OBJ) $(WINDSOCK) + +$(OBJDIR)/heart.o: heart.c + $(CC) $(CFLAGS) -o $@ -c heart.c + +endif + + +# VxWorks specific executables and objects ... + +$(BINDIR)/erl_io: $(OBJDIR)/erl_io.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_io.o + +$(OBJDIR)/erl_io.o: $(VXETC)/erl_io.c + $(CC) $(CFLAGS) -o $@ -c $(VXETC)/erl_io.c + +$(BINDIR)/rdate: $(OBJDIR)/rdate.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/rdate.o + +$(OBJDIR)/rdate.o: $(VXETC)/rdate.c + $(CC) $(CFLAGS) -o $@ -c $(VXETC)/rdate.c + +$(BINDIR)/vxcall: $(OBJDIR)/vxcall.o + $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/vxcall.o + +$(OBJDIR)/vxcall.o: $(VXETC)/vxcall.c + $(CC) $(CFLAGS) -o $@ -c $(VXETC)/vxcall.c + + + +Install: ../unix/Install.src ../../vsn.mk $(TARGET)/Makefile + sed -e 's;%I_VSN%;$(VSN);' \ + -e 's;%EMULATOR%;$(EMULATOR);' \ + -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \ + -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \ + ../unix/Install.src > Install + +erl.src: ../unix/erl.src.src ../../vsn.mk $(TARGET)/Makefile + sed -e 's;%EMULATOR%;$(EMULATOR);' \ + -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \ + -e 's;%VSN%;$(VSN);' \ + ../unix/erl.src.src > erl.src + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: etc +ifneq ($(INSTALL_OBJS),) + $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/obj + $(INSTALL_DATA) $(INSTALL_OBJS) $(RELEASE_PATH)/erts-$(VSN)/obj +endif + $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/bin +ifneq ($(TARGET), win32) +ifneq ($(findstring vxworks,$(TARGET)), vxworks) +ifneq ($(findstring ose,$(TARGET)), ose) + $(INSTALL_SCRIPT) erl.src $(RELEASE_PATH)/erts-$(VSN)/bin +endif +endif +endif +ifneq ($(INSTALL_PROGS),) + $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin +endif +ifneq ($(INSTALL_TOP),) + $(INSTALL_SCRIPT) $(INSTALL_TOP) $(RELEASE_PATH) +endif +ifneq ($(INSTALL_TOP_BIN),) + $(INSTALL_PROGRAM) $(INSTALL_TOP_BIN) $(RELEASE_PATH) +endif +ifneq ($(INSTALL_MISC),) + $(INSTALL_DIR) $(RELEASE_PATH)/misc + $(INSTALL_SCRIPT) $(INSTALL_MISC) $(RELEASE_PATH)/misc +endif +ifneq ($(INSTALL_ERL_OSE),) + $(INSTALL_DIR) $(RELEASE_PATH)/build_erl_ose + cd $(OSEETC) && $(TAR) erl_ose_$(SYSTEM_VSN).tar $(INSTALL_ERL_OSE) + cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar $(RELEASE_PATH)/build_erl_ose +endif +ifneq ($(INSTALL_SRC),) + $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/src + $(INSTALL_DATA) $(INSTALL_SRC) $(RELEASE_PATH)/erts-$(VSN)/src +endif +ifneq ($(INSTALL_EMBEDDED_DATA),) + $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) $(RELEASE_PATH)/erts-$(VSN)/bin +endif +ifneq ($(INSTALL_LIBS),) + $(INSTALL_DATA) $(INSTALL_LIBS) $(RELEASE_PATH)/erts-$(VSN)/bin +endif +ifneq ($(INSTALL_INCLUDES),) + $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/include + $(INSTALL_DATA) $(INSTALL_INCLUDES) $(RELEASE_PATH)/erts-$(VSN)/include +endif + +release_docs_spec: + + + + + diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c new file mode 100644 index 0000000000..9c66be7f0f --- /dev/null +++ b/erts/etc/common/dialyzer.c @@ -0,0 +1,466 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: Dialyzer front-end. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#ifdef __WIN32__ +#include <winbase.h> +#endif + +#include <ctype.h> + +#define NO 0 +#define YES 1 + +#define ASIZE(a) (sizeof(a)/sizeof(a[0])) + +static int debug = 0; /* Bit flags for debug printouts. */ + +static char** eargv_base; /* Base of vector. */ +static char** eargv; /* First argument for erl. */ + +static int eargc; /* Number of arguments in eargv. */ + +#ifdef __WIN32__ +# define QUOTE(s) possibly_quote(s) +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +# define ERL_NAME "erl.exe" +#else +# define QUOTE(s) s +# define IS_DIRSEP(c) ((c) == '/') +# define ERL_NAME "erl" +#endif + +#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s) +#define PUSH(s) eargv[eargc++] = QUOTE(s) +#define PUSH2(s, t) PUSH(s); PUSH(t) +#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u) + +/* + * Local functions. + */ + +static void error(char* format, ...); +static char* emalloc(size_t size); +static char* strsave(char* string); +static void push_words(char* src); +static int run_erlang(char* name, char** argv); +static char* get_default_emulator(char* progname); +#ifdef __WIN32__ +static char* possibly_quote(char* arg); +#endif + +/* + * Supply a strerror() function if libc doesn't. + */ +#ifndef HAVE_STRERROR + +extern int sys_nerr; + +#ifndef SYS_ERRLIST_DECLARED +extern const char * const sys_errlist[]; +#endif /* !SYS_ERRLIST_DECLARED */ + +char *strerror(int errnum) +{ + static char *emsg[1024]; + + if (errnum != 0) { + if (errnum > 0 && errnum < sys_nerr) + sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]); + else + sprintf((char *) &emsg[0], "errnum = %d ", errnum); + } + else { + emsg[0] = '\0'; + } + return (char *) &emsg[0]; +} +#endif /* !HAVE_STRERROR */ + +static char * +get_env(char *key) +{ +#ifdef __WIN32__ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + free(value); + value = emalloc(size); + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + free(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + free(value); +#endif +} + +int +main(int argc, char** argv) +{ + int eargv_size; + int eargc_base; /* How many arguments in the base of eargv. */ + char* emulator; + char *env; + int need_shell = 0; + + env = get_env("DIALYZER_EMULATOR"); + emulator = env ? env : get_default_emulator(argv[0]); + + /* + * Allocate the argv vector to be used for arguments to Erlang. + * Arrange for starting to pushing information in the middle of + * the array, to allow easy addition of commands in the beginning. + */ + + eargv_size = argc*4+100; + eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); + eargv = eargv_base; + eargc = 0; + push_words(emulator); + eargc_base = eargc; + eargv = eargv + eargv_size/2; + eargc = 0; + + free_env_val(env); + + /* + * Push initial arguments. + */ + + if (argc > 1 && strcmp(argv[1], "--wx") == 0) { + PUSH2("-smp", "--wx"); /* wx currently requires SMP enabled */ + argc--, argv++; + } + + if (argc > 1 && strcmp(argv[1], "-smp") == 0) { + PUSH("-smpauto"); + argc--, argv++; + } + + if (argc > 2 && strcmp(argv[1], "+S") == 0) { + PUSH3("-smp", "+S", argv[2]); + argc--, argv++; + argc--, argv++; + } + + PUSH("+B"); + PUSH2("-boot", "start_clean"); + PUSH3("-run", "dialyzer", "plain_cl"); + PUSH("-extra"); + + /* + * Push everything except --shell. + */ + + while (argc > 1) { + if (strcmp(argv[1], "--shell") == 0) { + need_shell = 1; + } else { + PUSH(argv[1]); + } + argc--, argv++; + } + + if (!need_shell) { + UNSHIFT("-noinput"); + } + + /* + * Move up the commands for invoking the emulator and adjust eargv + * accordingly. + */ + + while (--eargc_base >= 0) { + UNSHIFT(eargv_base[eargc_base]); + } + + /* + * Invoke Erlang with the collected options. + */ + + PUSH(NULL); + return run_erlang(eargv[0], eargv); +} + +static void +push_words(char* src) +{ + char sbuf[1024]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} +#ifdef __WIN32__ +char *make_commandline(char **argv) +{ + static char *buff = NULL; + static int siz = 0; + int num = 0; + char **arg, *p; + + if (*argv == NULL) { + return ""; + } + for (arg = argv; *arg != NULL; ++arg) { + num += strlen(*arg)+1; + } + if (!siz) { + siz = num; + buff = malloc(siz*sizeof(char)); + } else if (siz < num) { + siz = num; + buff = realloc(buff,siz*sizeof(char)); + } + p = buff; + for (arg = argv; *arg != NULL; ++arg) { + strcpy(p,*arg); + p+=strlen(*arg); + *p++=' '; + } + *(--p) = '\0'; + + if (debug) { + printf("Processed commandline:%s\n",buff); + } + return buff; +} + +int my_spawnvp(char **argv) +{ + STARTUPINFO siStartInfo; + PROCESS_INFORMATION piProcInfo; + DWORD ec; + + memset(&siStartInfo,0,sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags |= STARTF_USESHOWWINDOW; + + + if (!CreateProcess(NULL, + make_commandline(argv), + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &siStartInfo, + &piProcInfo)) { + return -1; + } + CloseHandle(piProcInfo.hThread); + + WaitForSingleObject(piProcInfo.hProcess,INFINITE); + if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) { + return 0; + } + return (int) ec; +} +#endif /* __WIN32__ */ + + +static int +run_erlang(char* progname, char** argv) +{ +#ifdef __WIN32__ + int status; +#endif + + if (debug) { + int i = 0; + while (argv[i] != NULL) + printf(" %s", argv[i++]); + printf("\n"); + } + +#ifdef __WIN32__ + /* + * Alas, we must wait here for the program to finish. + * Otherwise, the shell from which we was executed will think + * we are finished and print a prompt and read keyboard input. + */ + + status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/; + if (status == -1) { + fprintf(stderr, "dialyzer: Error executing '%s': %d", progname, + GetLastError()); + } + return status; +#else + execvp(progname, argv); + error("Error %d executing \'%s\'.", errno, progname); + return 2; +#endif +} + +static void +error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "dialyzer: %s\n", sbuf); + exit(1); +} + +static char* +emalloc(size_t size) +{ + char *p = malloc(size); + if (p == NULL) + error("Insufficient memory"); + return p; +} + +static char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + +static char* +get_default_emulator(char* progname) +{ + char sbuf[MAXPATHLEN]; + char* s; + + strcpy(sbuf, progname); + for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { + if (IS_DIRSEP(*s)) { + strcpy(s+1, ERL_NAME); +#ifdef __WIN32__ + if (_access(sbuf, 0) != -1) { + return strsave(sbuf); + } +#else + if (access(sbuf, 1) != -1) { + return strsave(sbuf); + } +#endif + break; + } + } + return ERL_NAME; +} + +#ifdef __WIN32__ +static char* +possibly_quote(char* arg) +{ + int mustQuote = NO; + int n = 0; + char* s; + char* narg; + + if (arg == NULL) { + return arg; + } + + /* + * Scan the string to find out if it needs quoting and return + * the original argument if not. + */ + + for (s = arg; *s; s++, n++) { + switch(*s) { + case ' ': + mustQuote = YES; + continue; + case '"': + mustQuote = YES; + n++; + continue; + case '\\': + if(s[1] == '"') + n++; + continue; + default: + continue; + } + } + if (!mustQuote) { + return arg; + } + + /* + * Insert the quotes and put a backslash in front of every quote + * inside the string. + */ + + s = narg = emalloc(n+2+1); + for (*s++ = '"'; *arg; arg++, s++) { + if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) { + *s++ = '\\'; + } + *s = *arg; + } + if (s[-1] == '\\') { + *s++ ='\\'; + } + *s++ = '"'; + *s = '\0'; + return narg; +} +#endif /* __WIN32__ */ diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c new file mode 100644 index 0000000000..c958fed741 --- /dev/null +++ b/erts/etc/common/erlc.c @@ -0,0 +1,701 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: Common compiler front-end. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#ifdef __WIN32__ +#include <winbase.h> +/* FIXE ME config_win32.h? */ +#define HAVE_STRERROR 1 +#endif + +#include <ctype.h> + +#define NO 0 +#define YES 1 + +#define ASIZE(a) (sizeof(a)/sizeof(a[0])) + +static int debug = 0; /* Bit flags for debug printouts. */ + +static char** eargv_base; /* Base of vector. */ +static char** eargv; /* First argument for erl. */ + +static int eargc; /* Number of arguments in eargv. */ + +#ifdef __WIN32__ +# define QUOTE(s) possibly_quote(s) +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +# define ERL_NAME "erl.exe" +#else +# define QUOTE(s) s +# define IS_DIRSEP(c) ((c) == '/') +# define ERL_NAME "erl" +#endif + +#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s) +#define PUSH(s) eargv[eargc++] = QUOTE(s) +#define PUSH2(s, t) PUSH(s); PUSH(t) +#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u) + +static char* output_type = NULL; /* Type of output file. */ +#ifdef __WIN32__ +static int pause_after_execution = 0; +#endif + +/* + * Local functions. + */ + +static char* process_opt(int* pArgc, char*** pArgv, int offset); +static void error(char* format, ...); +static void usage(void); +static char* emalloc(size_t size); +static char* strsave(char* string); +static void push_words(char* src); +static int run_erlang(char* name, char** argv); +static char* get_default_emulator(char* progname); +#ifdef __WIN32__ +static char* possibly_quote(char* arg); +#endif + +/* + * Supply a strerror() function if libc doesn't. + */ +#ifndef HAVE_STRERROR + +extern int sys_nerr; + +#ifndef SYS_ERRLIST_DECLARED +extern const char * const sys_errlist[]; +#endif /* !SYS_ERRLIST_DECLARED */ + +char *strerror(int errnum) +{ + static char *emsg[1024]; + + if (errnum != 0) { + if (errnum > 0 && errnum < sys_nerr) + sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]); + else + sprintf((char *) &emsg[0], "errnum = %d ", errnum); + } + else { + emsg[0] = '\0'; + } + return (char *) &emsg[0]; +} +#endif /* !HAVE_STRERROR */ + +static char * +get_env(char *key) +{ +#ifdef __WIN32__ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + free(value); + value = emalloc(size); + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + free(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + free(value); +#endif +} + + +int +main(int argc, char** argv) +{ + char cwd[MAXPATHLEN]; /* Current working directory. */ + char** rpc_eargv; /* Pointer to the beginning of arguments + * if calling a running Erlang system + * via erl_rpc(). + */ + int eargv_size; + int eargc_base; /* How many arguments in the base of eargv. */ + char* emulator; + char *env; + + env = get_env("ERLC_EMULATOR"); + emulator = env ? env : get_default_emulator(argv[0]); + + /* + * Allocate the argv vector to be used for arguments to Erlang. + * Arrange for starting to pushing information in the middle of + * the array, to allow easy adding of emulator options (like -pa) + * before '-s erlcompile compile_cmdline...'. + * + * Oh, by the way, we will push the compiler command in the + * base of the eargv vector, and move it up later. + */ + + eargv_size = argc*4+100; + eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); + eargv = eargv_base; + eargc = 0; + push_words(emulator); + eargc_base = eargc; + eargv = eargv + eargv_size/2; + eargc = 0; + + free_env_val(env); + + /* + * Push initial arguments. + */ + + PUSH("-noinput"); + PUSH2("-mode", "minimal"); + PUSH2("-boot", "start_clean"); + PUSH3("-s", "erl_compile", "compile_cmdline"); + rpc_eargv = eargv+eargc; + + /* + * Push standard arguments to Erlang. + * + * The @cwd argument was once needed, but from on R13B02 is optional. + * For maximum compatibility between erlc and erl of different versions, + * still provide the @cwd argument, unless it is too long to be + * represented as an atom. + */ + if (getcwd(cwd, sizeof(cwd)) == NULL) + error("Failed to get current working directory: %s", strerror(errno)); +#ifdef __WIN32__ + (void) GetShortPathName(cwd, cwd, sizeof(cwd)); +#endif + if (strlen(cwd) < 256) { + PUSH2("@cwd", cwd); + } + + /* + * Parse all command line switches. + */ + + while (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '+')) { + + /* + * Options starting with '+' are passed on to Erlang. + */ + + if (argv[1][0] == '+') { + PUSH2("@option", argv[1]+1); + } else { + /* + * Interpret options starting with '-'. + */ + + switch (argv[1][1]) { + case 'b': + output_type = process_opt(&argc, &argv, 0); + PUSH2("@output_type", output_type); + break; + case 'c': /* Allowed for compatibility with 'erl'. */ + if (strcmp(argv[1], "-compile") != 0) + goto error; + break; + case 'd': + debug = 1; + break; + case 'D': + { + char* def = process_opt(&argc, &argv, 0); + char* equals; + + def = strsave(def); /* Do not clobber original. */ + if ((equals = strchr(def, '=')) == NULL) { + PUSH2("@d", def); + } else { + *equals = '\0'; + equals++; + PUSH3("@dv", def, equals); + } + } + break; + case 'h': + if (strcmp(argv[1], "-hybrid") == 0) { + UNSHIFT(argv[1]); + } else { + usage(); + } + break; + case 'I': + PUSH2("@i", process_opt(&argc, &argv, 0)); + break; + case 'o': + PUSH2("@outdir", process_opt(&argc, &argv, 0)); + break; + case 'O': + PUSH("@optimize"); + if (argv[1][2] == '\0') + PUSH("1"); + else + PUSH(argv[1]+2); + break; + case 'p': + { + int c = argv[1][2]; + + if (c != 'a' && c != 'z') { + goto error; +#ifdef __WIN32__ + } else if (strcmp(argv[1], "-pause") == 0) { + pause_after_execution = 1; +#endif + } else { + char option[4]; + + UNSHIFT(process_opt(&argc, &argv, 1)); + option[0] = '-'; + option[1] = 'p'; + option[2] = c; + option[3] = '\0'; + UNSHIFT(strsave(option)); + } + } + break; + case 's': + if (strcmp(argv[1], "-smp") == 0) { + UNSHIFT(argv[1]); + } else { + goto error; + } + break; + case 'v': /* Verbose. */ + PUSH2("@verbose", "true"); + break; + case 'V': + /** XXX Version perhaps, but of what? **/ + break; + case 'W': /* Enable warnings. */ + if (strcmp(argv[1]+2, "all") == 0) { + PUSH2("@warn", "999"); + } else if (isdigit((int)argv[1][2])) { + PUSH2("@warn", argv[1]+2); + } else { + PUSH2("@warn", "1"); + } + break; + case 'E': + case 'S': + case 'P': + { + char* buf; + + /* + * From the given upper-case letter, construct + * a quoted atom. This is a convenience for the + * Erlang compiler, to avoid fighting with the shell's + * quoting. + */ + + buf = emalloc(4); + buf[0] = '\''; + buf[1] = argv[1][1]; + buf[2] = '\''; + buf[3] = '\0'; + + PUSH2("@option", buf); + } + break; + + case '-': + goto no_more_options; + + default: + error: + usage(); + break; + } + } + argc--, argv++; + } + + no_more_options: + + if (argc <= 1) { + /* + * To avoid starting an Erlang system unless absolutely needed + * exit if no files were specified on the command line. + */ + exit(0); + } + + /* + * The rest of the command line must be filenames. Simply push them. + */ + + PUSH("@files"); + while (argc > 1) { + PUSH(argv[1]); + argc--, argv++; + } + + /* + * Move up the commands for invoking the emulator and adjust eargv + * accordingly. + */ + + while (--eargc_base >= 0) { + UNSHIFT(eargv_base[eargc_base]); + } + + /* + * Invoke Erlang with the collected options. + */ + + PUSH(NULL); + return run_erlang(eargv[0], eargv); +} + +static char* +process_opt(int* pArgc, char*** pArgv, int offset) +{ + int argc = *pArgc; + char** argv = *pArgv; + int c = argv[1][1]; + + if (argv[1][2+offset] != '\0') { + /* + * The option was given as -x<value>. + */ + return argv[1]+2+offset; + } + + /* + * Look at the next argument. + */ + + argc--, argv++; + if (argc < 2 || argv[1][0] == '-') + error("No value given to -%c option", c); + *pArgc = argc; + *pArgv = argv; + return argv[1]; +} + +static void +push_words(char* src) +{ + char sbuf[1024]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} +#ifdef __WIN32__ +char *make_commandline(char **argv) +{ + static char *buff = NULL; + static int siz = 0; + int num = 0; + char **arg, *p; + + if (*argv == NULL) { + return ""; + } + for (arg = argv; *arg != NULL; ++arg) { + num += strlen(*arg)+1; + } + if (!siz) { + siz = num; + buff = malloc(siz*sizeof(char)); + } else if (siz < num) { + siz = num; + buff = realloc(buff,siz*sizeof(char)); + } + p = buff; + for (arg = argv; *arg != NULL; ++arg) { + strcpy(p,*arg); + p+=strlen(*arg); + *p++=' '; + } + *(--p) = '\0'; + + if (debug) { + printf("Processed commandline:%s\n",buff); + } + return buff; +} + +int my_spawnvp(char **argv) +{ + STARTUPINFO siStartInfo; + PROCESS_INFORMATION piProcInfo; + DWORD ec; + + memset(&siStartInfo,0,sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags |= STARTF_USESHOWWINDOW; + + + if (!CreateProcess(NULL, + make_commandline(argv), + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &siStartInfo, + &piProcInfo)) { + return -1; + } + CloseHandle(piProcInfo.hThread); + + WaitForSingleObject(piProcInfo.hProcess,INFINITE); + if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) { + return 0; + } + return (int) ec; +} +#endif /* __WIN32__ */ + + +static int +run_erlang(char* progname, char** argv) +{ +#ifdef __WIN32__ + int status; +#endif + + if (debug) { + int i = 0; + while (argv[i] != NULL) + printf(" %s", argv[i++]); + printf("\n"); + } + +#ifdef __WIN32__ + /* + * Alas, we must wait here for the program to finish. + * Otherwise, the shell from which we was executed will think + * we are finished and print a prompt and read keyboard input. + */ + + status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/; + if (status == -1) { + fprintf(stderr, "erlc: Error executing '%s': %d", progname, + GetLastError()); + } + if (pause_after_execution) { + fprintf(stderr, "Press ENTER to continue . . .\n"); + while (getchar() != '\n') + ; + } + return status; +#else + execvp(progname, argv); + error("Error %d executing \'%s\'.", errno, progname); + return 2; +#endif +} + +static void +usage(void) +{ + static struct { + char* name; + char* desc; + } options[] = { + {"-b type", "type of output file (e.g. jam or beam)"}, + {"-d", "turn on debugging of erlc itself"}, + {"-Dname", "define name"}, + {"-Dname=value", "define name to have value"}, + {"-hybrid", "compile using hybrid-heap emulator"}, + {"-help", "shows this help text"}, + {"-I path", "where to search for include files"}, + {"-o name", "name output directory or file"}, + {"-pa path", "add path to the front of Erlang's code path"}, + {"-pz path", "add path to the end of Erlang's code path"}, + {"-smp", "compile using SMP emulator"}, + {"-v", "verbose compiler output"}, + {"-W0", "disable warnings"}, + {"-Wnumber", "set warning level to number"}, + {"-Wall", "enable all warnings"}, + {"-W", "enable warnings (default; same as -W1)"}, + {"-E", "generate listing of expanded code (Erlang compiler)"}, + {"-S", "generate assembly listing (Erlang compiler)"}, + {"-P", "generate listing of preprocessed code (Erlang compiler)"}, + {"+term", "pass the Erlang term unchanged to the compiler"}, + }; + int i; + + fprintf(stderr, "Usage:\terlc [options] file.ext ...\n"); + fprintf(stderr, "Options:\n"); + for (i = 0; i < sizeof(options)/sizeof(options[0]); i++) { + fprintf(stderr, "%-14s %s\n", options[i].name, options[i].desc); + } + exit(1); +} + +static void +error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "erlc: %s\n", sbuf); + exit(1); +} + +static char* +emalloc(size_t size) +{ + char *p = malloc(size); + if (p == NULL) + error("Insufficient memory"); + return p; +} + +static char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + +static char* +get_default_emulator(char* progname) +{ + char sbuf[MAXPATHLEN]; + char* s; + + strcpy(sbuf, progname); + for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { + if (IS_DIRSEP(*s)) { + strcpy(s+1, ERL_NAME); +#ifdef __WIN32__ + if (_access(sbuf, 0) != -1) { + return strsave(sbuf); + } +#else + if (access(sbuf, 1) != -1) { + return strsave(sbuf); + } +#endif + break; + } + } + return ERL_NAME; +} + +#ifdef __WIN32__ +static char* +possibly_quote(char* arg) +{ + int mustQuote = NO; + int n = 0; + char* s; + char* narg; + + if (arg == NULL) { + return arg; + } + + /* + * Scan the string to find out if it needs quoting and return + * the original argument if not. + */ + + for (s = arg; *s; s++, n++) { + switch(*s) { + case ' ': + mustQuote = YES; + continue; + case '"': + mustQuote = YES; + n++; + continue; + case '\\': + if(s[1] == '"') + n++; + continue; + default: + continue; + } + } + if (!mustQuote) { + return arg; + } + + /* + * Insert the quotes and put a backslash in front of every quote + * inside the string. + */ + + s = narg = emalloc(n+2+1); + for (*s++ = '"'; *arg; arg++, s++) { + if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) { + *s++ = '\\'; + } + *s = *arg; + } + if (s[-1] == '\\') { + *s++ ='\\'; + } + *s++ = '"'; + *s = '\0'; + return narg; +} +#endif /* __WIN32__ */ diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c new file mode 100644 index 0000000000..4325418e7c --- /dev/null +++ b/erts/etc/common/erlexec.c @@ -0,0 +1,2038 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * This is a C version of the erl.exec Bourne shell script, including + * additions required for Windows NT. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_driver.h" +#include <stdlib.h> +#include <stdarg.h> +#include "erl_misc_utils.h" + +#ifdef __WIN32__ +# include "erl_version.h" +# include "init_file.h" +#endif + +#define NO 0 +#define YES 1 +#define DEFAULT_PROGNAME "erl" + +#ifdef __WIN32__ +#define INI_FILENAME "erl.ini" +#define INI_SECTION "erlang" +#define DIRSEP "\\" +#define PATHSEP ";" +#define NULL_DEVICE "nul" +#define BINARY_EXT "" +#define DLL_EXT ".dll" +#define EMULATOR_EXECUTABLE "beam.dll" +#else +#define PATHSEP ":" +#define DIRSEP "/" +#define NULL_DEVICE "/dev/null" +#define BINARY_EXT "" +#define EMULATOR_EXECUTABLE "beam" + +#endif +#define QUOTE(s) s + +/* +M alloc_util allocators */ +static const char plusM_au_allocs[]= { + 'u', /* all alloc_util allocators */ + 'B', /* binary_alloc */ + 'D', /* std_alloc */ + 'E', /* ets_alloc */ + 'H', /* eheap_alloc */ + 'L', /* ll_alloc */ + 'R', /* driver_alloc */ + 'S', /* sl_alloc */ + 'T', /* temp_alloc */ + '\0' +}; + +/* +M alloc_util allocator specific arguments */ +static char *plusM_au_alloc_switches[] = { + "as", + "asbcst", + "e", + "t", + "lmbcs", + "mbcgs", + "mbsd", + "mmbcs", + "mmmbc", + "mmsbc", + "msbclt", + "ramv", + "rmbcmt", + "rsbcmt", + "rsbcst", + "sbct", + "smbcs", + NULL +}; + +/* +M other arguments */ +static char *plusM_other_switches[] = { + "ea", + "ummc", + "uycs", + "im", + "is", + "it", + "Mamcbf", + "Mrmcbf", + "Mmcs", + "Mcci", + "Fe", + "Ye", + "Ym", + "Ytp", + "Ytt", + NULL +}; + +/* +s arguments with values */ +static char *pluss_val_switches[] = { + "bt", + "ct", + "ss", + NULL +}; + +/* + * Define sleep(seconds) in terms of Sleep() on Windows. + */ + +#ifdef __WIN32__ +#define sleep(seconds) Sleep(seconds*1000) +#endif + +#define SMP_SUFFIX ".smp" +#define HYBRID_SUFFIX ".hybrid" + +#ifdef __WIN32__ +#define DEBUG_SUFFIX ".debug" +#define EMU_TYPE_SUFFIX_LENGTH (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX))) +#else +/* The length of the longest memory architecture suffix. */ +#define EMU_TYPE_SUFFIX_LENGTH strlen(HYBRID_SUFFIX) +#endif +/* + * Define flags for different memory architectures. + */ +#define EMU_TYPE_SMP 0x0001 +#define EMU_TYPE_HYBRID 0x0002 + +#ifdef __WIN32__ +#define EMU_TYPE_DEBUG 0x0004 +#endif + +void usage(const char *switchname); +void start_epmd(char *epmd); +void error(char* format, ...); + +/* + * Local functions. + */ + +#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +static void usage_notsup(const char *switchname); +#endif +static void usage_msg(const char *msg); +static char **build_args_from_env(char *env_var); +static char **build_args_from_string(char *env_var); +static void initial_argv_massage(int *argc, char ***argv); +static void get_parameters(int argc, char** argv); +static void add_arg(char *new_arg); +static void add_args(char *first_arg, ...); +static void ensure_EargsSz(int sz); +static void add_Eargs(char *new_arg); +static void *emalloc(size_t size); +static void *erealloc(void *p, size_t size); +static void efree(void *p); +static char* strsave(char* string); +static int is_one_of_strings(char *str, char *strs[]); +static char *write_str(char *to, char *from); +static void get_home(void); +static void add_epmd_port(void); +#ifdef __WIN32__ +static void get_start_erl_data(char *); +static char* get_value(HKEY key, char* value_name, BOOL mustExit); +static char* possibly_quote(char* arg); + +/* + * Functions from win_erlexec.c + */ +int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached); +int start_emulator(char* emu, char*start_prog, char** argv, int start_detached); +#endif + + + +/* + * Variables. + */ +int nohup = 0; +int keep_window = 0; + +static char **Eargsp = NULL; /* Emulator arguments (to appear first). */ +static int EargsSz = 0; /* Size of Eargsp */ +static int EargsCnt = 0; /* Number of emulator arguments. */ +static char **argsp = NULL; /* Common arguments. */ +static int argsCnt = 0; /* Number of common arguments */ +static int argsSz = 0; /* Size of argsp */ +static char tmpStr[10240]; /* Temporary string buffer. */ +static int verbose = 0; /* If non-zero, print some extra information. */ +static int start_detached = 0; /* If non-zero, the emulator should be + * started detached (in the background). + */ +static int emu_type = 0; /* If non-zero, start beam.ARCH or beam.ARCH.exe + * instead of beam or beam.exe, where ARCH is defined by flags. */ +static int emu_type_passed = 0; /* Types explicitly set */ + +#ifdef __WIN32__ +static char *start_emulator_program = NULL; /* For detachec mode - + erl.exe/werl.exe */ +static char* key_val_name = ERLANG_VERSION; /* Used by the registry + * access functions. + */ +static char* boot_script = NULL; /* used by option -start_erl and -boot */ +static char* config_script = NULL; /* used by option -start_erl and -config */ + +static HANDLE this_module_handle; +static int run_werl; + +#endif + +/* + * Needed parameters to be fetched from the environment (Unix) + * or the ini file (Win32). + */ + +static char* bindir; /* Location of executables. */ +static char* rootdir; /* Root location of Erlang installation. */ +static char* emu; /* Emulator to run. */ +static char* progname; /* Name of this program. */ +static char* home; /* Path of user's home directory. */ + +static void +set_env(char *key, char *value) +{ +#ifdef __WIN32__ + if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value)) + error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); +#else + size_t size = strlen(key) + 1 + strlen(value) + 1; + char *str = emalloc(size); + sprintf(str, "%s=%s", key, value); + if (putenv(str) != 0) + error("putenv(\"%s\") failed!", str); +#ifdef HAVE_COPYING_PUTENV + efree(str); +#endif +#endif +} + +static char * +get_env(char *key) +{ +#ifdef __WIN32__ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + efree(value); + value = emalloc(size); + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + efree(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + free(value); +#endif +} + +/* + * Add the arcitecture suffix to the program name if needed, + * except on Windows, where we insert it just before ".DLL". + */ +static char* +add_extra_suffixes(char *prog, int type) +{ + char *res; + char *p; + int len; +#ifdef __WIN32__ + char *dll_p; + int dll = 0; +#endif + + if (!type) { + return prog; + } + + len = strlen(prog); + + /* Worst-case allocation */ + p = emalloc(len + + EMU_TYPE_SUFFIX_LENGTH + + + 1); + res = p; + p = write_str(p, prog); + +#ifdef __WIN32__ + dll_p = res + len - 4; + if (dll_p >= res) { + if (dll_p[0] == '.' && + (dll_p[1] == 'd' || dll_p[1] == 'D') && + (dll_p[2] == 'l' || dll_p[2] == 'L') && + (dll_p[3] == 'l' || dll_p[3] == 'L')) { + p = dll_p; + dll = 1; + } + } +#endif + +#ifdef __WIN32__ + if (type & EMU_TYPE_DEBUG) { + p = write_str(p, DEBUG_SUFFIX); + type &= ~(EMU_TYPE_DEBUG); + } +#endif + if (type == EMU_TYPE_SMP) { + p = write_str(p, SMP_SUFFIX); + } + else if (type == EMU_TYPE_HYBRID) { + p = write_str(p, HYBRID_SUFFIX); + } +#ifdef __WIN32__ + if (dll) { + p = write_str(p, DLL_EXT); + } +#endif + + return res; +} + +#ifdef __WIN32__ +__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed) +#else +int main(int argc, char **argv) +#endif +{ + int haltAfterwards = 0; /* If true, put 's erlang halt' at the end + * of the arguments. */ + int isdistributed = 0; + int no_epmd = 0; + int i; + char* s; + char *epmd_prog = NULL; + char *malloc_lib; + int process_args = 1; + int print_args_exit = 0; + int print_qouted_cmd_exit = 0; + erts_cpu_info_t *cpuinfo = NULL; + +#ifdef __WIN32__ + this_module_handle = module; + run_werl = windowed; + /* if we started this erl just to get a detached emulator, + * the arguments are already prepared for beam, so we skip + * directly to start_emulator */ + s = get_env("ERL_CONSOLE_MODE"); + if (s != NULL && strcmp(s, "detached")==0) { + free_env_val(s); + s = get_env("ERL_EMULATOR_DLL"); + if (s != NULL) { + argv[0] = strsave(s); + } else { + argv[0] = strsave(EMULATOR_EXECUTABLE); + } + ensure_EargsSz(argc + 1); + memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *)); + Eargsp[argc] = NULL; + emu = argv[0]; + start_emulator_program = strsave(argv[0]); + goto skip_arg_massage; + } + free_env_val(s); +#else + int reset_cerl_detached = 0; + + s = get_env("CERL_DETACHED_PROG"); + if (s && strcmp(s, "") != 0) { + emu = s; + start_detached = 1; + reset_cerl_detached = 1; + ensure_EargsSz(argc + 1); + memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *)); + Eargsp[argc] = emu; + Eargsp[argc] = NULL; + goto skip_arg_massage; + } + free_env_val(s); +#endif + + initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */ + + i = 1; +#ifdef __WIN32__ + /* Not used? /rickard */ + if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) { + key_val_name = strsave(argv[i+1]); + i = 3; + } +#endif + + get_parameters(argc, argv); + + /* + * Construct the path of the executable. + */ + cpuinfo = erts_cpu_info_create(); + /* '-smp auto' is default */ +#ifdef ERTS_HAVE_SMP_EMU + if (erts_get_cpu_configured(cpuinfo) > 1) + emu_type |= EMU_TYPE_SMP; +#endif + +#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG) + emu_type_passed |= EMU_TYPE_DEBUG; + emu_type |= EMU_TYPE_DEBUG; +#endif + + /* We need to do this before the ordinary processing. */ + malloc_lib = get_env("ERL_MALLOC_LIB"); + while (i < argc) { + if (argv[i][0] == '+') { + if (argv[i][1] == 'M' && argv[i][2] == 'Y' && argv[i][3] == 'm') { + if (argv[i][4] == '\0') { + if (++i < argc) + malloc_lib = argv[i]; + else + usage("+MYm"); + } + else + malloc_lib = &argv[i][4]; + } + } + else if (argv[i][0] == '-') { + if (strcmp(argv[i], "-smp") == 0) { + if (i + 1 >= argc) + goto smp; + + if (strcmp(argv[i+1], "auto") == 0) { + i++; + smp_auto: + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + if (erts_get_cpu_configured(cpuinfo) > 1) + emu_type |= EMU_TYPE_SMP; + else +#endif + emu_type &= ~EMU_TYPE_SMP; + } + else if (strcmp(argv[i+1], "enable") == 0) { + i++; + smp_enable: + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + emu_type |= EMU_TYPE_SMP; +#else + usage_notsup("-smp enable"); +#endif + } + else if (strcmp(argv[i+1], "disable") == 0) { + i++; + smp_disable: + emu_type_passed |= EMU_TYPE_SMP; + emu_type &= ~EMU_TYPE_SMP; + } + else { + smp: + + emu_type_passed |= EMU_TYPE_SMP; +#ifdef ERTS_HAVE_SMP_EMU + emu_type |= EMU_TYPE_SMP; +#else + usage_notsup("-smp"); +#endif + } + } else if (strcmp(argv[i], "-smpenable") == 0) { + goto smp_enable; + } else if (strcmp(argv[i], "-smpauto") == 0) { + goto smp_auto; + } else if (strcmp(argv[i], "-smpdisable") == 0) { + goto smp_disable; +#ifdef __WIN32__ + } else if (strcmp(argv[i], "-debug") == 0) { + emu_type_passed |= EMU_TYPE_DEBUG; + emu_type |= EMU_TYPE_DEBUG; +#endif + } else if (strcmp(argv[i], "-hybrid") == 0) { + emu_type_passed |= EMU_TYPE_HYBRID; +#ifdef ERTS_HAVE_HYBRID_EMU + emu_type |= EMU_TYPE_HYBRID; +#else + usage_notsup("-hybrid"); +#endif + } else if (strcmp(argv[i], "-extra") == 0) { + break; + } + } + i++; + } + + erts_cpu_info_destroy(cpuinfo); + cpuinfo = NULL; + + if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { + /* + * We have a conflict. Only using explicitly passed arguments + * may solve it... + */ + emu_type &= emu_type_passed; + if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) { + usage_msg("Hybrid heap emulator with SMP support selected. The " + "combination hybrid heap and SMP support is currently " + "not supported."); + } + } + + if (malloc_lib) { + if (strcmp(malloc_lib, "libc") != 0) + usage("+MYm"); + } + emu = add_extra_suffixes(emu, emu_type); + sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); + emu = strsave(tmpStr); + + add_Eargs(emu); /* Will be argv[0] -- necessary! */ + + /* + * Add the bindir to the path (unless it is there already). + */ + + s = get_env("PATH"); + if (!s) { + sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir); + } else if (strstr(s, bindir) == NULL) { + sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, + rootdir, s); + } else { + sprintf(tmpStr, "%s", s); + } + free_env_val(s); + set_env("PATH", tmpStr); + + i = 1; + +#ifdef __WIN32__ +#define ADD_BOOT_CONFIG \ + if (boot_script) \ + add_args("-boot", boot_script, NULL); \ + if (config_script) \ + add_args("-config", config_script, NULL); +#else +#define ADD_BOOT_CONFIG +#endif + + get_home(); + add_args("-home", home, NULL); + + add_epmd_port(); + + add_arg("--"); + + while (i < argc) { + if (!process_args) { /* Copy arguments after '-extra' */ + add_arg(argv[i]); + i++; + } else { + switch (argv[i][0]) { + case '-': + switch (argv[i][1]) { +#ifdef __WIN32__ + case 'b': + if (strcmp(argv[i], "-boot") == 0) { + if (boot_script) + error("Conflicting -start_erl and -boot options"); + if (i+1 >= argc) + usage("-boot"); + boot_script = strsave(argv[i+1]); + i++; + } + else { + add_arg(argv[i]); + } + break; +#endif + case 'c': + if (strcmp(argv[i], "-compile") == 0) { + /* + * Note that the shell script erl.exec does an recursive call + * on itself here. We'll avoid doing that. + */ + add_args("-noshell", "-noinput", "-s", "c", "lc_batch", + NULL); + add_Eargs("-B"); + haltAfterwards = 0; + } +#ifdef __WIN32__ + else if (strcmp(argv[i], "-config") == 0){ + if (config_script) + error("Conflicting -start_erl and -config options"); + if (i+1 >= argc) + usage("-config"); + config_script = strsave(argv[i+1]); + i++; + } +#endif + else { + add_arg(argv[i]); + } + break; + + case 'd': + if (strcmp(argv[i], "-detached") != 0) { + add_arg(argv[i]); + } else { + start_detached = 1; + add_args("-noshell", "-noinput", NULL); + } + break; + + case 'i': + if (strcmp(argv[i], "-instr") == 0) { + add_Eargs("-Mim"); + add_Eargs("true"); + } + else + add_arg(argv[i]); + break; + + case 'e': + if (strcmp(argv[i], "-extra") == 0) { + process_args = 0; + ADD_BOOT_CONFIG; + add_arg(argv[i]); + } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */ + verbose = 1; + } else if (strcmp(argv[i], "-emu_args_exit") == 0) { + print_args_exit = 1; + } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) { + print_qouted_cmd_exit = 1; + } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */ + if (i+2 >= argc) + usage("-env"); + set_env(argv[i+1], argv[i+2]); + i += 2; + } else if (strcmp(argv[i], "-epmd") == 0) { + if (i+1 >= argc) + usage("-epmd"); + epmd_prog = argv[i+1]; + ++i; + } else { + add_arg(argv[i]); + } + break; + case 'k': + if (strcmp(argv[i], "-keep_window") == 0) { + keep_window = 1; + } else + add_arg(argv[i]); + break; + + case 'm': + /* + * Note that the shell script erl.exec does an recursive call + * on itself here. We'll avoid doing that. + */ + if (strcmp(argv[i], "-make") == 0) { + add_args("-noshell", "-noinput", "-s", "make", "all", NULL); + add_Eargs("-B"); + haltAfterwards = 1; + i = argc; /* Skip rest of command line */ + } else if (strcmp(argv[i], "-man") == 0) { +#if defined(__WIN32__) + error("-man not supported on Windows"); +#else + argv[i] = "man"; + sprintf(tmpStr, "%s/man", rootdir); + set_env("MANPATH", tmpStr); + execvp("man", argv+i); + error("Could not execute the 'man' command."); +#endif + } else + add_arg(argv[i]); + break; + + case 'n': + if (strcmp(argv[i], "-name") == 0) { /* -name NAME */ + if (i+1 >= argc) + usage("-name"); + + /* + * Note: Cannot use add_args() here, due to non-defined + * evaluation order. + */ + + add_arg(argv[i]); + add_arg(argv[i+1]); + isdistributed = 1; + i++; + } else if (strcmp(argv[i], "-noinput") == 0) { + add_args("-noshell", "-noinput", NULL); + } else if (strcmp(argv[i], "-nohup") == 0) { + add_arg("-nohup"); + nohup = 1; + } else if (strcmp(argv[i], "-no_epmd") == 0) { + add_arg("-no_epmd"); + no_epmd = 1; + } else { + add_arg(argv[i]); + } + break; + + case 's': /* -sname NAME */ + if (strcmp(argv[i], "-sname") == 0) { + if (i+1 >= argc) + usage("-sname"); + add_arg(argv[i]); + add_arg(argv[i+1]); + isdistributed = 1; + i++; + } +#ifdef __WIN32__ + else if (strcmp(argv[i], "-service_event") == 0) { + add_arg(argv[i]); + add_arg(argv[i+1]); + i++; + } + else if (strcmp(argv[i], "-start_erl") == 0) { + if (i+1 < argc && argv[i+1][0] != '-') { + get_start_erl_data(argv[i+1]); + i++; + } else + get_start_erl_data((char *) NULL); + } +#endif + else + add_arg(argv[i]); + + break; + + case 'v': /* -version */ + if (strcmp(argv[i], "-version") == 0) { + add_Eargs("-V"); + } else { + add_arg(argv[i]); + } + break; + + default: + add_arg(argv[i]); + break; + } /* switch(argv[i][1] */ + break; + + case '+': + switch (argv[i][1]) { + case '#': + case 'a': + case 'A': + case 'b': + case 'h': + case 'i': + case 'P': + case 'S': + case 'T': + case 'R': + case 'W': + case 'K': + if (argv[i][2] != '\0') + goto the_default; + if (i+1 >= argc) + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + break; + case 'B': + argv[i][0] = '-'; + if (argv[i][2] != '\0') { + if ((argv[i][2] != 'i') && + (argv[i][2] != 'c') && + (argv[i][2] != 'd')) { + usage(argv[i]); + } else { + add_Eargs(argv[i]); + break; + } + } + if (i+1 < argc) { + if ((argv[i+1][0] != '-') && + (argv[i+1][0] != '+')) { + if (argv[i+1][0] == 'i') { + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + break; + } else { + usage(argv[i]); + } + } + } + add_Eargs(argv[i]); + break; + case 'M': { + int x; + for (x = 0; plusM_au_allocs[x]; x++) + if (plusM_au_allocs[x] == argv[i][2]) + break; + if ((plusM_au_allocs[x] + && is_one_of_strings(&argv[i][3], + plusM_au_alloc_switches)) + || is_one_of_strings(&argv[i][2], + plusM_other_switches)) { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + } + else + goto the_default; + break; + } + case 's': + if (!is_one_of_strings(&argv[i][2], + pluss_val_switches)) + goto the_default; + else { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + } + break; + default: + the_default: + argv[i][0] = '-'; /* Change +option to -option. */ + add_Eargs(argv[i]); + } + break; + + default: + add_arg(argv[i]); + } /* switch(argv[i][0] */ + i++; + } + } + + if (process_args) { + ADD_BOOT_CONFIG; + } +#undef ADD_BOOT_CONFIG + + /* Doesn't conflict with -extra, since -make skips all the rest of + the arguments. */ + if (haltAfterwards) { + add_args("-s", "erlang", "halt", NULL); + } + + if (isdistributed && !no_epmd) + start_epmd(epmd_prog); + +#if (! defined(__WIN32__)) && defined(DEBUG) + if (start_detached) { + /* Start the emulator within an xterm. + * Move up all arguments and insert + * "xterm -e " first. + * The path must be searched for this + * to work, i.e execvp() must be used. + */ + ensure_EargsSz(EargsCnt+2); + for (i = EargsCnt; i > 0; i--) + Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */ + EargsCnt += 2; /* Two args to insert */ + Eargsp[0] = emu = "xterm"; + Eargsp[1] = "-e"; + } +#endif + + add_Eargs("--"); + add_Eargs("-root"); + add_Eargs(rootdir); + add_Eargs("-progname"); + add_Eargs(progname); + add_Eargs("--"); + ensure_EargsSz(EargsCnt + argsCnt + 1); + for (i = 0; i < argsCnt; i++) + Eargsp[EargsCnt++] = argsp[i]; + Eargsp[EargsCnt] = NULL; + + if (print_qouted_cmd_exit) { + printf("\"%s\" ", emu); + for (i = 1; i < EargsCnt; i++) + printf("\"%s\" ", Eargsp[i]); + printf("\n"); + exit(0); + } + + if (print_args_exit) { + for (i = 1; i < EargsCnt; i++) + printf("%s ", Eargsp[i]); + printf("\n"); + exit(0); + } + + if (verbose) { + printf("Executing: %s", emu); + for (i = 0; i < EargsCnt; i++) + printf(" %s", Eargsp[i]); + printf("\n\n"); + } + +#ifdef __WIN32__ + + if (EargsSz != EargsCnt + 1) + Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) * + sizeof(char *)); + efree((void *) argsp); + + skip_arg_massage: + /*DebugBreak();*/ + + if (run_werl) { + if (start_detached) { + char *p; + /* transform werl to erl */ + p = start_emulator_program+strlen(start_emulator_program); + while (--p >= start_emulator_program && *p != '/' && *p != '\\' && + *p != 'W' && *p != 'w') + ; + if (p >= start_emulator_program && (*p == 'W' || *p == 'w') && + (p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') && + (p[3] == 'L' || p[3] == 'l')) { + memmove(p,p+1,strlen(p)); + } + } + return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached); + } else { + return start_emulator(emu, start_emulator_program, Eargsp, start_detached); + } + +#else + + skip_arg_massage: + if (start_detached) { + int status = fork(); + if (status != 0) /* Parent */ + return 0; + + if (reset_cerl_detached) + putenv("CERL_DETACHED_PROG="); + + /* Detach from controlling terminal */ +#ifdef HAVE_SETSID + setsid(); +#elif defined(TIOCNOTTY) + { + int fd = open("/dev/tty", O_RDWR); + if (fd >= 0) { + ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } + } +#endif + + status = fork(); + if (status != 0) /* Parent */ + return 0; + + /* + * Grandchild. + */ + close(0); + open("/dev/null", O_RDONLY); + close(1); + open("/dev/null", O_WRONLY); + close(2); + open("/dev/null", O_WRONLY); +#ifdef DEBUG + execvp(emu, Eargsp); /* "xterm ..." needs to search the path */ +#endif + } +#ifdef DEBUG + else +#endif + { + execv(emu, Eargsp); + } + error("Error %d executing \'%s\'.", errno, emu); + return 1; +#endif +} + + +static void +usage_aux(void) +{ + fprintf(stderr, + "Usage: erl [-version] [-sname NAME | -name NAME] " + "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] " +#ifdef __WIN32__ + "[-start_erl [datafile]] " +#endif + "[-smp " +#ifdef ERTS_HAVE_SMP_EMU + "[enable|" +#endif + "auto|disable" +#ifdef ERTS_HAVE_SMP_EMU + "]" +#endif + "] " +#ifdef ERTS_HAVE_HYBRID_EMU + "[-hybrid] " +#endif + "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] " + "[-args_file FILENAME] " + "[+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] [+h HEAP_SIZE] [+K BOOLEAN] " + "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] " + "[+r] [+s SCHEDULER_OPTION] [+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] [+W<i|w>] " + "[args ...]\n"); + exit(1); +} + +void +usage(const char *switchname) +{ + fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname); + usage_aux(); +} + +#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU) +static void +usage_notsup(const char *switchname) +{ + fprintf(stderr, "Argument \'%s\' not supported.\n", switchname); + usage_aux(); +} +#endif + +static void +usage_msg(const char *msg) +{ + fprintf(stderr, "%s\n", msg); + usage_aux(); +} + +static void +usage_format(char *format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + usage_aux(); +} + +void +start_epmd(char *epmd) +{ + char epmd_cmd[MAXPATHLEN+100]; +#ifdef __WIN32__ + char* arg1 = NULL; +#endif + int result; + + if (!epmd) { + epmd = epmd_cmd; +#ifdef __WIN32__ + sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir); + arg1 = "-daemon"; +#else + sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir); +#endif + } +#ifdef __WIN32__ + if (arg1 != NULL) { + strcat(epmd, " "); + strcat(epmd, arg1); + } + { + STARTUPINFO start; + PROCESS_INFORMATION pi; + memset(&start, 0, sizeof (start)); + start.cb = sizeof (start); + if (!CreateProcess(NULL, epmd, NULL, NULL, FALSE, + CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS, + NULL, NULL, &start, &pi)) + result = -1; + else + result = 0; + } +#else + result = system(epmd); +#endif + if (result == -1) { + fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno); + exit(1); + } +} + +static void +add_arg(char *new_arg) +{ + if (argsCnt >= argsSz) + argsp = (char **) erealloc((void *) argsp, + sizeof(char *) * (argsSz += 20)); + argsp[argsCnt++] = QUOTE(new_arg); +} + +static void +add_args(char *first_arg, ...) +{ + va_list ap; + char* arg; + + add_arg(first_arg); + va_start(ap, first_arg); + while ((arg = va_arg(ap, char *)) != NULL) { + add_arg(arg); + } + va_end(ap); +} + +static void +ensure_EargsSz(int sz) +{ + if (EargsSz < sz) + Eargsp = (char **) erealloc((void *) Eargsp, + sizeof(char *) * (EargsSz = sz)); +} + +static void +add_Eargs(char *new_arg) +{ + if (EargsCnt >= EargsSz) + Eargsp = (char **) erealloc((void *) Eargsp, + sizeof(char *) * (EargsSz += 20)); + Eargsp[EargsCnt++] = QUOTE(new_arg); +} + +#if !defined(__WIN32__) +void error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "erlexec: %s\n", sbuf); + exit(1); +} +#endif + +static void * +emalloc(size_t size) +{ + void *p = malloc(size); + if (p == NULL) + error("Insufficient memory"); + return p; +} + +static void * +erealloc(void *p, size_t size) +{ + void *res = realloc(p, size); + if (res == NULL) + error("Insufficient memory"); + return res; +} + +static void +efree(void *p) +{ + free(p); +} + +static int +is_one_of_strings(char *str, char *strs[]) +{ + int i, j; + for (i = 0; strs[i]; i++) { + for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++); + if (!str[j] && !strs[i][j]) + return 1; + } + return 0; +} + +static char *write_str(char *to, char *from) +{ + while (*from) + *(to++) = *(from++); + *to = '\0'; + return to; +} + +char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + + +#if defined(__WIN32__) + +static void get_start_erl_data(char *file) +{ + int fp; + char tmpbuffer[512]; + char start_erl_data[512]; + int bytesread; + char* env; + char* reldir; + char* otpstring; + char* tprogname; + if (boot_script) + error("Conflicting -start_erl and -boot options"); + if (config_script) + error("Conflicting -start_erl and -config options"); + env = get_env("RELDIR"); + if (env) + reldir = strsave(env); + else { + sprintf(tmpbuffer, "%s/releases", rootdir); + reldir = strsave(tmpbuffer); + } + free_env_val(env); + if (file == NULL) + sprintf(start_erl_data, "%s/start_erl.data", reldir); + else + sprintf(start_erl_data, "%s", file); + fp = _open(start_erl_data, _O_RDONLY ); + if( fp == -1 ) + error( "open failed on %s",start_erl_data ); + else { + if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 ) + error( "Problem reading file %s", start_erl_data ); + else { + tmpbuffer[bytesread]='\0'; + if ((otpstring = strchr(tmpbuffer,' ')) != NULL) { + *otpstring = '\0'; + otpstring++; + +/* + * otpstring is the otpversion + * tmpbuffer is the emuversion +*/ + } + } + } + tprogname = otpstring; + while (*tprogname) { + if (*tprogname <= ' ') { + *tprogname='\0'; + break; + } + tprogname++; + } + + bindir = emalloc(512); + sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer); + /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */ + tprogname = progname; + progname = emalloc(strlen(tprogname) + 20); + sprintf(progname,"%s -start_erl",tprogname); + + boot_script = emalloc(512); + config_script = emalloc(512); + sprintf(boot_script, "%s/%s/start", reldir, otpstring); + sprintf(config_script, "%s/%s/sys", reldir, otpstring); + +} + + +static char *replace_filename(char *path, char *new_base) +{ + int plen = strlen(path); + char *res = malloc((plen+strlen(new_base)+1)*sizeof(char)); + char *p; + + strcpy(res,path); + for (p = res+plen-1 ;p >= res && *p != '\\'; --p) + ; + *(p+1) ='\0'; + strcat(res,new_base); + return res; +} + +static char *path_massage(char *long_path) +{ + char *p; + + p = malloc(MAX_PATH+1); + strcpy(p, long_path); + GetShortPathName(p, p, MAX_PATH); + return p; +} + +static char *do_lookup_in_section(InitSection *inis, char *name, + char *section, char *filename, int is_path) +{ + char *p = lookup_init_entry(inis, name); + + if (p == NULL) { + error("Could not find key %s in section %s of file %s", + name,section,filename); + } + + if (is_path) { + return path_massage(p); + } else { + return strsave(p); + } +} + + +static void get_parameters(int argc, char** argv) +{ + char *p; + char buffer[MAX_PATH]; + char *ini_filename; + HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini + that resides in the same dir as erl.exe, not + an erl.ini in our directory */ + InitFile *inif; + InitSection *inis; + + if (module == NULL) { + error("Cannot GetModuleHandle()"); + } + + if (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + error("Could not GetModuleFileName"); + } + + ini_filename = replace_filename(buffer,INI_FILENAME); + + if ((inif = load_init_file(ini_filename)) == NULL) { + /* Assume that the path is absolute and that + it does not contain any symbolic link */ + + char buffer[MAX_PATH]; + + /* Determine bindir */ + if (GetEnvironmentVariable("ERLEXEC_DIR", buffer, MAX_PATH) == 0) { + strcpy(buffer, ini_filename); + for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + ; + *p ='\0'; + } + bindir = path_massage(buffer); + + /* Determine rootdir */ + for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p) + ; + p--; + for (;p >= buffer && *p != '\\'; --p) + ; + *p ='\0'; + rootdir = path_massage(buffer); + + /* Hardcoded progname */ + progname = strsave(DEFAULT_PROGNAME); + } else { + if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) { + error("Could not find section %s in init file %s", + INI_SECTION, ini_filename); + } + + bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1); + rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION, + ini_filename,1); + progname = do_lookup_in_section(inis, "Progname", INI_SECTION, + ini_filename,0); + free_init_file(inif); + } + + emu = EMULATOR_EXECUTABLE; + start_emulator_program = strsave(argv[0]); + + free(ini_filename); +} + +static void +get_home(void) +{ + int len; + char tmpstr[MAX_PATH+1]; + char* homedrive; + char* homepath; + + homedrive = get_env("HOMEDRIVE"); + homepath = get_env("HOMEPATH"); + if (!homedrive || !homepath) { + if (len = GetWindowsDirectory(tmpstr,MAX_PATH)) { + home = emalloc(len+1); + strcpy(home,tmpstr); + } else + error("HOMEDRIVE or HOMEPATH is not set and GetWindowsDir failed"); + } else { + home = emalloc(strlen(homedrive)+strlen(homepath)+1); + strcpy(home, homedrive); + strcat(home, homepath); + } + free_env_val(homedrive); + free_env_val(homepath); +} + +#else + +static void +get_parameters(int argc, char** argv) +{ + progname = get_env("PROGNAME"); + if (!progname) { + progname = strsave(DEFAULT_PROGNAME); + } + + emu = get_env("EMU"); + if (!emu) { + emu = strsave(EMULATOR_EXECUTABLE); + } + + bindir = get_env("BINDIR"); + if (!bindir) { + /* Determine bindir from absolute path to executable */ + char *p; + char buffer[PATH_MAX]; + strcpy(buffer, argv[0]); + + for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p) + ; + *p ='\0'; + bindir = strsave(buffer); + } + + rootdir = get_env("ROOTDIR"); + if (!rootdir) { + /* Determine rootdir from absolute path to bindir */ + char *p; + char buffer[PATH_MAX]; + strcpy(buffer, bindir); + + for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p) + ; + p--; + for (; p >= buffer && *p != '/'; --p) + ; + *p ='\0'; + rootdir = strsave(buffer); + } + + if (!progname || !emu || !rootdir || !bindir) { + error("PROGNAME, EMU, ROOTDIR and BINDIR must be set"); + } +} + +static void +get_home(void) +{ + home = get_env("HOME"); + if (home == NULL) + error("HOME must be set"); +} + +#endif + +static void add_epmd_port(void) +{ + char* port = get_env("ERL_EPMD_PORT"); + if (port != NULL) { + add_args("-epmd_port", port, NULL); + } +} + +static char **build_args_from_env(char *env_var) +{ + char *value = get_env(env_var); + char **res = build_args_from_string(value); + free_env_val(value); + return res; +} + +static char **build_args_from_string(char *string) +{ + int argc = 0; + char **argv = NULL; + int alloced = 0; + char **cur_s = NULL; /* Initialized to avoid warning. */ + int s_alloced = 0; + int s_pos = 0; + char *p = string; + enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state; + +#define ENSURE() \ + if (s_pos >= s_alloced) { \ + if (!*cur_s) { \ + *cur_s = emalloc(s_alloced = 20); \ + } else { \ + *cur_s = erealloc(*cur_s, s_alloced += 20); \ + } \ + } + + + if (!p) + return NULL; + argv = emalloc(sizeof(char *) * (alloced = 10)); + state = Start; + for(;;) { + switch (state) { + case Start: + if (!*p) + goto done; + if (argc >= alloced - 1) { /* Make room for extra NULL */ + argv = erealloc(argv, (alloced += 10) * sizeof(char *)); + } + cur_s = argc + argv; + *cur_s = NULL; + s_pos = 0; + s_alloced = 0; + state = Build0; + break; + case Build0: + switch (*p) { + case ' ': + ++p; + break; + case '\0': + state = Start; + break; + default: + state = Build; + break; + } + break; + case Build: + switch (*p) { + case ' ': + case '\0': + ENSURE(); + (*cur_s)[s_pos] = '\0'; + ++argc; + state = Start; + break; + case '"': + ++p; + state = BuildDQuoted; + break; + case '\'': + ++p; + state = BuildSQuoted; + break; + case '\\': + ++p; + state = AcceptNext; + break; + default: + ENSURE(); + (*cur_s)[s_pos++] = *p++; + break; + } + break; + case BuildDQuoted: + switch (*p) { + case '"': + ++p; + /* fall through */ + case '\0': + state = Build; + break; + default: + ENSURE(); + (*cur_s)[s_pos++] = *p++; + break; + } + break; + case BuildSQuoted: + switch (*p) { + case '\'': + ++p; + /* fall through */ + case '\0': + state = Build; + break; + default: + ENSURE(); + (*cur_s)[s_pos++] = *p++; + break; + } + break; + case AcceptNext: + if (!*p) { + state = Build; + } else { + ENSURE(); + (*cur_s)[s_pos++] = *p++; + } + state = Build; + break; + } + } +done: + argv[argc] = NULL; /* Sure to be large enough */ + if (!argc) { + efree(argv); + return NULL; + } + return argv; +#undef ENSURE +} + +static char * +errno_string(void) +{ + char *str = strerror(errno); + if (!str) + return "unknown error"; + return str; +} + +static char ** +read_args_file(char *filename) +{ + int c, aix = 0, quote = 0, cmnt = 0, asize = 0; + char **res, *astr = NULL; + FILE *file; + +#undef EAF_CMNT +#undef EAF_QUOTE +#undef SAVE_CHAR + +#define EAF_CMNT (1 << 8) +#define EAF_QUOTE (1 << 9) +#define SAVE_CHAR(C) \ + do { \ + if (!astr) \ + astr = emalloc(sizeof(char)*(asize = 20)); \ + if (aix == asize) \ + astr = erealloc(astr, sizeof(char)*(asize += 20)); \ + if (' ' != (char) (C)) \ + astr[aix++] = (char) (C); \ + else if (aix > 0 && astr[aix-1] != ' ') \ + astr[aix++] = ' '; \ + } while (0) + + do { + errno = 0; + file = fopen(filename, "r"); + } while (!file && errno == EINTR); + if (!file) { + usage_format("Failed to open arguments file \"%s\": %s\n", + filename, + errno_string()); + } + + while (1) { + c = getc(file); + if (c == EOF) { + if (ferror(file)) { + if (errno == EINTR) { + clearerr(file); + continue; + } + usage_format("Failed to read arguments file \"%s\": %s\n", + filename, + errno_string()); + } + break; + } + + switch (quote | cmnt | c) { + case '\\': + quote = EAF_QUOTE; + break; + case '#': + cmnt = EAF_CMNT; + break; + case EAF_CMNT|'\n': + cmnt = 0; + /* Fall through... */ + case '\n': + case '\f': + case '\r': + case '\t': + case '\v': + if (!quote) + c = ' '; + /* Fall through... */ + default: + if (!cmnt) + SAVE_CHAR(c); + quote = 0; + break; + } + } + + SAVE_CHAR('\0'); + + fclose(file); + + if (astr[0] == '\0') + res = NULL; + else + res = build_args_from_string(astr); + + efree(astr); + + return res; + +#undef EAF_CMNT +#undef EAF_QUOTE +#undef SAVE_CHAR +} + +typedef struct { + char **argv; + int argc; + int size; +} argv_buf; + +static void +trim_argv_buf(argv_buf *abp) +{ + abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc)); +} + +static void +save_arg(argv_buf *abp, char *arg) +{ + if (abp->size <= abp->argc) { + if (!abp->argv) + abp->argv = emalloc(sizeof(char *)*(abp->size = 100)); + else + abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100)); + } + abp->argv[abp->argc++] = arg; +} + +#define DEF_ARGV_STACK_SIZE 10 +#define ARGV_STACK_SIZE_INCR 50 + +typedef struct { + char **argv; + int ix; +} argv_stack_element; + +typedef struct { + int top_ix; + int size; + argv_stack_element *base; + argv_stack_element def_buf[DEF_ARGV_STACK_SIZE]; +} argv_stack; + +#define ARGV_STACK_INIT(S) \ +do { \ + (S)->top_ix = 0; \ + (S)->size = DEF_ARGV_STACK_SIZE; \ + (S)->base = &(S)->def_buf[0]; \ +} while (0) + +static void +push_argv(argv_stack *stck, char **argv, int ix) +{ + if (stck->top_ix == stck->size) { + if (stck->base != &stck->def_buf[0]) { + stck->size += ARGV_STACK_SIZE_INCR; + stck->base = erealloc(stck->base, + sizeof(argv_stack_element)*stck->size); + } + else { + argv_stack_element *base; + base = emalloc(sizeof(argv_stack_element) + *(stck->size + ARGV_STACK_SIZE_INCR)); + memcpy((void *) base, + (void *) stck->base, + sizeof(argv_stack_element)*stck->size); + stck->base = base; + stck->size += ARGV_STACK_SIZE_INCR; + } + } + stck->base[stck->top_ix].argv = argv; + stck->base[stck->top_ix++].ix = ix; +} + +static void +pop_argv(argv_stack *stck, char ***argvp, int *ixp) +{ + if (stck->top_ix == 0) { + *argvp = NULL; + *ixp = 0; + } + else { + *argvp = stck->base[--stck->top_ix].argv; + *ixp = stck->base[stck->top_ix].ix; + if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) { + efree(stck->base); + stck->base = &stck->def_buf[0]; + stck->size = DEF_ARGV_STACK_SIZE; + } + } +} + +static void +get_file_args(char *filename, argv_buf *abp, argv_buf *xabp) +{ + argv_stack stck; + int i; + char **argv; + + ARGV_STACK_INIT(&stck); + + i = 0; + argv = read_args_file(filename); + + while (argv) { + + while (argv[i]) { + if (strcmp(argv[i], "-args_file") == 0) { + char **new_argv; + char *fname; + if (!argv[++i]) + usage("-args_file"); + fname = argv[i++]; + new_argv = read_args_file(fname); + if (new_argv) { + if (argv[i]) + push_argv(&stck, argv, i); + else + efree(argv); + i = 0; + argv = new_argv; + } + } + else { + if (strcmp(argv[i], "-extra") == 0) { + i++; + while (argv[i]) + save_arg(xabp, argv[i++]); + break; + } + save_arg(abp, argv[i++]); + } + } + + efree(argv); + + pop_argv(&stck, &argv, &i); + } +} + +static void +initial_argv_massage(int *argc, char ***argv) +{ + argv_buf ab = {0}, xab = {0}; + int ix, vix, ac; + char **av; + struct { + int argc; + char **argv; + } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, + {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}}; + /* + * The environment flag containing OTP release is intentionally + * undocumented and intended for OTP internal use only. + */ + + vix = 0; + av = build_args_from_env("ERL_AFLAGS"); + if (av) + avv[vix++].argv = av; + + /* command line */ + if (*argc > 1) { + avv[vix].argc = *argc - 1; + avv[vix++].argv = &(*argv)[1]; + } + + av = build_args_from_env("ERL_FLAGS"); + if (av) + avv[vix++].argv = av; + + av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); + if (av) + avv[vix++].argv = av; + + av = build_args_from_env("ERL_ZFLAGS"); + if (av) + avv[vix++].argv = av; + + if (vix == (*argc > 1 ? 1 : 0)) { + /* Only command line argv; check if we can use argv as it is... */ + ac = *argc; + av = *argv; + for (ix = 1; ix < ac; ix++) { + if (strcmp(av[ix], "-args_file") == 0) { + /* ... no; we need to expand arguments from + file into argument list */ + goto build_new_argv; + } + if (strcmp(av[ix], "-extra") == 0) { + break; + } + } + + /* ... yes; we can use argv as it is. */ + return; + } + + build_new_argv: + + save_arg(&ab, (*argv)[0]); + + vix = 0; + while (avv[vix].argv) { + ac = avv[vix].argc; + av = avv[vix].argv; + + ix = 0; + while (ix < ac && av[ix]) { + if (strcmp(av[ix], "-args_file") == 0) { + if (++ix == ac) + usage("-args_file"); + get_file_args(av[ix++], &ab, &xab); + } + else { + if (strcmp(av[ix], "-extra") == 0) { + ix++; + while (ix < ac && av[ix]) + save_arg(&xab, av[ix++]); + break; + } + save_arg(&ab, av[ix++]); + } + } + + vix++; + } + + vix = 0; + while (avv[vix].argv) { + if (avv[vix].argc == INT_MAX) /* not command line */ + efree(avv[vix].argv); + vix++; + } + + if (xab.argc) { + save_arg(&ab, "-extra"); + for (ix = 0; ix < xab.argc; ix++) + save_arg(&ab, xab.argv[ix]); + efree(xab.argv); + } + + save_arg(&ab, NULL); + trim_argv_buf(&ab); + *argv = ab.argv; + *argc = ab.argc - 1; +} + +#ifdef __WIN32__ +static char* +possibly_quote(char* arg) +{ + int mustQuote = NO; + int n = 0; + char* s; + char* narg; + + /* + * Scan the string to find out if it needs quoting and return + * the original argument if not. + */ + + for (s = arg; *s; s++, n++) { + if (*s == ' ' || *s == '"') { + mustQuote = YES; + n++; + } + } + if (!mustQuote) { + return arg; + } + + /* + * Insert the quotes and put a backslash in front of every quote + * inside the string. + */ + + s = narg = emalloc(n+2+1); + for (*s++ = '"'; *arg; arg++, s++) { + if (*s == '"') { + *s++ = '\\'; + } + *s = *arg; + } + *s++ = '"'; + *s = '\0'; + return narg; +} + +#endif diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c new file mode 100644 index 0000000000..ab37d4af46 --- /dev/null +++ b/erts/etc/common/escript.c @@ -0,0 +1,697 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2007-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: escript front-end. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#ifdef __WIN32__ +#include <winbase.h> +#endif + +#include <ctype.h> + +static int debug = 0; /* Bit flags for debug printouts. */ + +static char** eargv_base; /* Base of vector. */ +static char** eargv; /* First argument for erl. */ + +static int eargc; /* Number of arguments in eargv. */ + +#define BOOL int +#define FALSE 0 +#define TRUE 1 + +#ifdef __WIN32__ +# define QUOTE(s) possibly_quote(s) +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +# define DIRSEPSTR "\\" +# define PATHSEPSTR ";" +# define PMAX MAX_PATH +# define ERL_NAME "erl.exe" +#else +# define QUOTE(s) s +# define IS_DIRSEP(c) ((c) == '/') +# define DIRSEPSTR "/" +# define PATHSEPSTR ":" +# define PMAX PATH_MAX +# define ERL_NAME "erl" +#endif + +#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s) +#define UNSHIFT3(s, t, u) UNSHIFT(u); UNSHIFT(t); UNSHIFT(s) +#define PUSH(s) eargv[eargc++] = QUOTE(s) +#define PUSH2(s, t) PUSH(s); PUSH(t) +#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u) +#define LINEBUFSZ 1024 + +/* + * Local functions. + */ + +static void error(char* format, ...); +static char* emalloc(size_t size); +static void efree(void *p); +static char* strsave(char* string); +static void push_words(char* src); +static int run_erlang(char* name, char** argv); +static char* get_default_emulator(char* progname); +#ifdef __WIN32__ +static char* possibly_quote(char* arg); +#endif + +/* + * Supply a strerror() function if libc doesn't. + */ +#ifndef HAVE_STRERROR + +extern int sys_nerr; + +#ifndef SYS_ERRLIST_DECLARED +extern const char * const sys_errlist[]; +#endif /* !SYS_ERRLIST_DECLARED */ + +char *strerror(int errnum) +{ + static char *emsg[1024]; + + if (errnum != 0) { + if (errnum > 0 && errnum < sys_nerr) + sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]); + else + sprintf((char *) &emsg[0], "errnum = %d ", errnum); + } + else { + emsg[0] = '\0'; + } + return (char *) &emsg[0]; +} +#endif /* !HAVE_STRERROR */ + +static char * +get_env(char *key) +{ +#ifdef __WIN32__ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + efree(value); + value = emalloc(size); + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + efree(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + efree(value); +#endif +} +/* + * Find absolute path to this program + */ + +static char * +find_prog(char *origpath) +{ + char relpath[PMAX]; + char abspath[PMAX]; + + strcpy(relpath, origpath); + + if (strstr(relpath, DIRSEPSTR) == NULL) { + /* Just a base name */ + char *envpath; + + envpath = get_env("PATH"); + if (envpath) { + /* Try to find the executable in the path */ + char dir[PMAX]; + char *beg = envpath; + char *end; + int sz; + +#ifdef __WIN32__ + HANDLE dir_handle; /* Handle to directory. */ + char wildcard[PMAX]; /* Wildcard to search for. */ + WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */ +#else + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ +#endif /* __WIN32__ */ + + BOOL look_for_sep = TRUE; + + while (look_for_sep) { + end = strstr(beg, PATHSEPSTR); + if (end != NULL) { + sz = end - beg; + strncpy(dir, beg, sz); + dir[sz] = '\0'; + } else { + sz = strlen(beg); + strcpy(dir, beg); + look_for_sep = FALSE; + } + beg = end + 1; + +#ifdef __WIN32__ + strcpy(wildcard, dir); + strcat(wildcard, DIRSEPSTR); + strcat(wildcard, relpath); /* basename */ + dir_handle = FindFirstFile(wildcard, &find_data); + if (dir_handle == INVALID_HANDLE_VALUE) { + /* Try next directory in path */ + continue; + } else { + /* Wow we found the executable. */ + strcpy(abspath, wildcard); + FindClose(dir_handle); + return strsave(abspath); + } +#else + dp = opendir(dir); + if (dp != NULL) { + while (TRUE) { + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + /* Try next directory in path */ + break; + } + + if (strcmp(origpath, dirp->d_name) == 0) { + /* Wow we found the executable. */ + strcpy(abspath, dir); + strcat(abspath, DIRSEPSTR); + strcat(abspath, dirp->d_name); + closedir(dp); + return strsave(abspath); + } + } + } +#endif /* __WIN32__ */ + } + } + } + + { +#ifdef __WIN32__ + DWORD size; + char *absrest; + size = GetFullPathName(relpath, PMAX, abspath, &absrest); + if ((size == 0) || (size > PMAX)) { + +#else + if (!realpath(relpath, abspath)) { +#endif /* __WIN32__ */ + /* Cannot determine absolute path to escript. Try the relative. */ + return strsave(relpath); + } else { + return strsave(abspath); + } + } +} + +static void +append_shebang_args(char* scriptname) +{ + /* Open script file */ + FILE* fd = fopen (scriptname,"r"); + + if (fd != NULL) { + /* Read first line in script file */ + static char linebuf[LINEBUFSZ]; + char* ptr = fgets(linebuf, LINEBUFSZ, fd); + + if (ptr != NULL && linebuf[0] == '#' && linebuf[1] == '!') { + /* Try to find args on second or third line */ + ptr = fgets(linebuf, LINEBUFSZ, fd); + if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') { + /* Use second line */ + } else { + /* Try third line */ + ptr = fgets(linebuf, LINEBUFSZ, fd); + if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') { + /* Use third line */ + } else { + /* Do not use any line */ + ptr = NULL; + } + } + + if (ptr != NULL) { + /* Use entire line but the leading chars */ + char* beg = linebuf + 3; + char* end; + BOOL newline = FALSE; + + /* Push all args */ + while(beg && !newline) { + /* Skip leading spaces */ + while (beg && beg[0] == ' ') { + beg++; + } + + /* Find end of arg */ + end = beg; + while (end && end[0] != ' ') { + if (end[0] == '\n') { + newline = TRUE; + end[0]= '\0'; + break; + } else { + end++; + } + } + + /* Empty arg */ + if (beg == end) { + break; + } + end[0]= '\0'; + PUSH(beg); + beg = end + 1; + } + } + } + fclose(fd); + } else { + error("Failed to open file: %s", scriptname); + } +} + +int +main(int argc, char** argv) +{ + int eargv_size; + int eargc_base; /* How many arguments in the base of eargv. */ + char* emulator; + char* env; + char* basename; + char* absname; + char scriptname[PMAX]; + char** last_opt; + char** first_opt; + + emulator = env = get_env("ESCRIPT_EMULATOR"); + if (emulator == NULL) { + emulator = get_default_emulator(argv[0]); + } + + /* + * Allocate the argv vector to be used for arguments to Erlang. + * Arrange for starting to pushing information in the middle of + * the array, to allow easy addition of commands in the beginning. + */ + + eargv_size = argc*4+1000; + eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); + eargv = eargv_base; + eargc = 0; + push_words(emulator); + eargc_base = eargc; + eargv = eargv + eargv_size/2; + eargc = 0; + + free_env_val(env); + + /* + * Push initial arguments. + */ + + PUSH("+B"); + PUSH2("-boot", "start_clean"); + PUSH("-noshell"); + + /* Determine basename of the executable */ + for (basename = argv[0]+strlen(argv[0]); + basename > argv[0] && !(IS_DIRSEP(basename[-1])); + --basename) + ; + + first_opt = argv; + last_opt = argv; + +#ifdef __WIN32__ + if (_stricmp(basename, "escript.exe") == 0) { +#else + if (strcmp(basename, "escript") == 0) { +#endif + /* + * Push all options (without the hyphen) before the script name. + */ + + while (argc > 1 && argv[1][0] == '-') { + PUSH(argv[1]+1); + argc--; + argv++; + last_opt = argv; + } + + if (argc <= 1) { + error("Missing filename\n"); + } + strcpy(scriptname, argv[1]); + argc--; + argv++; + } else { +#ifdef __WIN32__ + int len; +#endif + absname = find_prog(argv[0]); + strcpy(scriptname, absname); + efree(absname); +#ifdef __WIN32__ + len = strlen(scriptname); + if (len >= 4 && _stricmp(scriptname+len-4, ".exe") == 0) { + scriptname[len-4] = '\0'; + } +#endif + strcat(scriptname, ".escript"); + } + + /* + * Read options from the %%! row in the script and add them as args + */ + + append_shebang_args(scriptname); + + /* + * Push the script name and everything following it as extra arguments. + */ + + PUSH3("-run", "escript", "start"); + + /* + * Push all options (without the hyphen) before the script name. + */ + + while (first_opt != last_opt) { + PUSH(first_opt[1]+1); + first_opt++; + } + + PUSH("-extra"); + PUSH(scriptname); + while (argc > 1) { + PUSH(argv[1]); + argc--, argv++; + } + + /* + * Move up the commands for invoking the emulator and adjust eargv + * accordingly. + */ + + while (--eargc_base >= 0) { + UNSHIFT(eargv_base[eargc_base]); + } + + /* + * Invoke Erlang with the collected options. + */ + + PUSH(NULL); + return run_erlang(eargv[0], eargv); +} + +static void +push_words(char* src) +{ + char sbuf[1024]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} +#ifdef __WIN32__ +char *make_commandline(char **argv) +{ + static char *buff = NULL; + static int siz = 0; + int num = 0; + char **arg, *p; + + if (*argv == NULL) { + return ""; + } + for (arg = argv; *arg != NULL; ++arg) { + num += strlen(*arg)+1; + } + if (!siz) { + siz = num; + buff = emalloc(siz*sizeof(char)); + } else if (siz < num) { + siz = num; + buff = realloc(buff,siz*sizeof(char)); + } + p = buff; + for (arg = argv; *arg != NULL; ++arg) { + strcpy(p,*arg); + p+=strlen(*arg); + *p++=' '; + } + *(--p) = '\0'; + + if (debug) { + printf("Processed commandline:%s\n",buff); + } + return buff; +} + +int my_spawnvp(char **argv) +{ + STARTUPINFO siStartInfo; + PROCESS_INFORMATION piProcInfo; + DWORD ec; + + memset(&siStartInfo,0,sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + if (!CreateProcess(NULL, + make_commandline(argv), + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &siStartInfo, + &piProcInfo)) { + return -1; + } + CloseHandle(piProcInfo.hThread); + + WaitForSingleObject(piProcInfo.hProcess,INFINITE); + if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) { + return 0; + } + return (int) ec; +} +#endif /* __WIN32__ */ + + +static int +run_erlang(char* progname, char** argv) +{ +#ifdef __WIN32__ + int status; +#endif + + if (debug) { + int i = 0; + while (argv[i] != NULL) + printf(" %s", argv[i++]); + printf("\n"); + } + +#ifdef __WIN32__ + /* + * Alas, we must wait here for the program to finish. + * Otherwise, the shell from which we was executed will think + * we are finished and print a prompt and read keyboard input. + */ + + status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/; + if (status == -1) { + fprintf(stderr, "escript: Error executing '%s': %d", progname, + GetLastError()); + } + return status; +#else + execvp(progname, argv); + error("Error %d executing \'%s\'.", errno, progname); + return 2; +#endif +} + +static void +error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "escript: %s\n", sbuf); + exit(1); +} + +static char* +emalloc(size_t size) +{ + char *p = malloc(size); + if (p == NULL) + error("Insufficient memory"); + return p; +} + +static void +efree(void *p) +{ + free(p); +} + +static char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + +static char* +get_default_emulator(char* progname) +{ + char sbuf[MAXPATHLEN]; + char* s; + + strcpy(sbuf, progname); + for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { + if (IS_DIRSEP(*s)) { + strcpy(s+1, ERL_NAME); +#ifdef __WIN32__ + if (_access(sbuf, 0) != -1) { + return strsave(sbuf); + } +#else + if (access(sbuf, 1) != -1) { + return strsave(sbuf); + } +#endif + break; + } + } + return ERL_NAME; +} + +#ifdef __WIN32__ +static char* +possibly_quote(char* arg) +{ + int mustQuote = FALSE; + int n = 0; + char* s; + char* narg; + + if (arg == NULL) { + return arg; + } + + /* + * Scan the string to find out if it needs quoting and return + * the original argument if not. + */ + + for (s = arg; *s; s++, n++) { + switch(*s) { + case ' ': + mustQuote = TRUE; + continue; + case '"': + mustQuote = TRUE; + n++; + continue; + case '\\': + if(s[1] == '"') + n++; + continue; + default: + continue; + } + } + if (!mustQuote) { + return arg; + } + + /* + * Insert the quotes and put a backslash in front of every quote + * inside the string. + */ + + s = narg = emalloc(n+2+1); + for (*s++ = '"'; *arg; arg++, s++) { + if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) { + *s++ = '\\'; + } + *s = *arg; + } + if (s[-1] == '\\') { + *s++ ='\\'; + } + *s++ = '"'; + *s = '\0'; + return narg; +} +#endif /* __WIN32__ */ diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c new file mode 100644 index 0000000000..4f738947b7 --- /dev/null +++ b/erts/etc/common/heart.c @@ -0,0 +1,1142 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/** + * + * File: heart.c + * Purpose: Portprogram for supervision of the Erlang emulator. + * + * Synopsis: heart + * + * SPAWNING FROM ERLANG + * + * This program is started from Erlang as follows, + * + * Port = open_port({spawn, 'heart'}, [{packet, 2}]), + * + * ROLE OF THIS PORT PROGRAM + * + * This program is started by the Erlang emulator. It communicates + * with the emulator through file descriptor 0 (standard input). + * + * MESSAGE FORMAT + * + * All messages have the following format (a value in parentheses + * indicate field length in bytes), + * + * {Length(2), Operation(1)} + * + * START ACK + * + * When this program has started it sends an START ACK message to Erlang. + * + * HEART_BEATING + * + * This program expects a heart beat messages. If it does not receive a + * heart beat message from Erlang within heart_beat_timeout seconds, it + * reboots the system. The variable heart_beat_timeout is exported (so + * that it can be set from the shell in VxWorks, as is the variable + * heart_beat_report_delay). When using Solaris, the system is rebooted + * by executing the command stored in the environment variable + * HEART_COMMAND. + * + * BLOCKING DESCRIPTORS + * + * All file descriptors in this program are blocking. This can lead + * to deadlocks. The emulator reads and writes are blocking. + * + * STANDARD INPUT, OUTPUT AND ERROR + * + * This program communicates with Erlang through the standard + * input and output file descriptors (0 and 1). These descriptors + * (and the standard error descriptor 2) must NOT be closed + * explicitely by this program at termination (in UNIX it is + * taken care of by the operating system itself; in VxWorks + * it is taken care of by the spawn driver part of the Emulator). + * + * END OF FILE + * + * If a read from a file descriptor returns zero (0), it means + * that there is no process at the other end of the connection + * having the connection open for writing (end-of-file). + * + * HARDWARE WATCHDOG + * + * When used with VxWorks(with CPU40), the hardware + * watchdog is enabled, making sure that the system reboots + * even if the heart port program malfunctions or the system + * is completely overloaded. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef __WIN32__ +#include <windows.h> +#include <io.h> +#include <fcntl.h> +#include <process.h> +#endif +#ifdef VXWORKS +#include "sys.h" +#endif + +/* + * Implement time correction using times() call even on Linuxes + * that can simulate gethrtime with clock_gettime, no use implementing + * a phony gethrtime in this file as the time questions are so infrequent. + */ +#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME) +# define HEART_CORRECT_USING_TIMES 1 +#endif + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> + +#include <stdarg.h> + +#include <string.h> +#include <time.h> +#include <errno.h> + +#ifdef VXWORKS +# include <vxWorks.h> +# include <ioLib.h> +# include <selectLib.h> +# include <netinet/in.h> +# include <rebootLib.h> +# include <sysLib.h> +# include <taskLib.h> +# include <wdLib.h> +# include <taskHookLib.h> +# include <selectLib.h> +#endif +#if !defined(__WIN32__) && !defined(VXWORKS) +# include <sys/types.h> +# include <netinet/in.h> +# include <sys/time.h> +# include <unistd.h> +# include <signal.h> +# if defined(HEART_CORRECT_USING_TIMES) +# include <sys/times.h> +# include <limits.h> +# endif +#endif + +#define HEART_COMMAND_ENV "HEART_COMMAND" + +#define MSG_HDR_SIZE 2 +#define MSG_HDR_PLUS_OP_SIZE 3 +#define MSG_BODY_SIZE 2048 +#define MSG_TOTAL_SIZE 2050 + +unsigned char cmd[MSG_BODY_SIZE]; + +struct msg { + unsigned short len; + unsigned char op; + unsigned char fill[MSG_BODY_SIZE]; /* one too many */ +}; + +/* operations */ +#define HEART_ACK 1 +#define HEART_BEAT 2 +#define SHUT_DOWN 3 +#define SET_CMD 4 +#define CLEAR_CMD 5 +#define GET_CMD 6 +#define HEART_CMD 7 + + +/* Maybe interesting to change */ + +/* Times in seconds */ +#define HEART_BEAT_BOOT_DELAY 60 /* 1 minute */ +#define SELECT_TIMEOUT 5 /* Every 5 seconds we reset the + watchdog timer */ + +/* heart_beat_timeout is the maximum gap in seconds between two + consecutive heart beat messages from Erlang, and HEART_BEAT_BOOT_DELAY + is the the extra delay that wd_keeper allows for, to give heart a + chance to reboot in the "normal" way before the hardware watchdog + enters the scene. heart_beat_report_delay is the time allowed for reporting + before rebooting under VxWorks. */ + +int heart_beat_timeout = 60; +int heart_beat_report_delay = 30; +int heart_beat_boot_delay = HEART_BEAT_BOOT_DELAY; +/* All current platforms have a process identifier that + fits in an unsigned long and where 0 is an impossible or invalid value */ +unsigned long heart_beat_kill_pid = 0; + +#define VW_WD_TIMEOUT (heart_beat_timeout+heart_beat_report_delay+heart_beat_boot_delay) +#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay) + +/* reasons for reboot */ +#define R_TIMEOUT 1 +#define R_CLOSED 2 +#define R_ERROR 3 +#define R_SHUT_DOWN 4 + + +/* macros */ + +#define NULLFDS ((fd_set *) NULL) +#define NULLTV ((struct timeval *) NULL) + +/* prototypes */ + +static int message_loop(int,int); +static void do_terminate(int); +static int notify_ack(int); +static int heart_cmd_reply(int, char *); +static int write_message(int, struct msg *); +static int read_message(int, struct msg *); +static int read_skip(int, char *, int, int); +static int read_fill(int, char *, int); +static void print_error(const char *,...); +static void debugf(const char *,...); +static void init_timestamp(void); +static time_t timestamp(time_t *); + +#ifdef __WIN32__ +static BOOL enable_privilege(void); +static BOOL do_shutdown(int); +static void print_last_error(void); +static HANDLE start_reader_thread(void); +static DWORD WINAPI reader(LPVOID); +static int test_win95(void); +#define read _read +#define write _write +#endif + +/* static variables */ + +static char program_name[256]; +static int erlin_fd = 0, erlout_fd = 1; /* std in and out */ +static int debug_on = 0; +#ifdef __WIN32__ +static HANDLE hreader_thread; +static HANDLE hevent_dataready; +static struct msg m, *mp = &m; +static int tlen; /* total message length */ +static FILE* conh; + +#endif + +static int +is_env_set(char *key) +{ +#ifdef __WIN32__ + char buf[1]; + DWORD sz = (DWORD) sizeof(buf); + SetLastError(0); + sz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) buf, sz); + return sz || GetLastError() != ERROR_ENVVAR_NOT_FOUND; +#else + return getenv(key) != NULL; +#endif +} + +static char * +get_env(char *key) +{ +#ifdef __WIN32__ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + free(value); + value = malloc(size); + if (!value) { + print_error("Failed to allocate memory. Terminating..."); + exit(1); + } + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + free(value); + return NULL; + } + if (nsz <= size) + return value; + size = nsz; + } +#else + return getenv(key); +#endif +} + +static void +free_env_val(char *value) +{ +#ifdef __WIN32__ + if (value) + free(value); +#endif +} + +/* + * main + */ +static void get_arguments(int argc, char** argv) { + int i = 1; + int h; + int w; + unsigned long p; + + while (i < argc) { + switch (argv[i][0]) { + case '-': + switch (argv[i][1]) { + case 'h': + if (strcmp(argv[i], "-ht") == 0) + if (sscanf(argv[i+1],"%i",&h) ==1) + if ((h > 10) && (h <= 65535)) { + heart_beat_timeout = h; + fprintf(stderr,"heart_beat_timeout = %d\n",h); + i++; + } + break; + case 'w': + if (strcmp(argv[i], "-wt") == 0) + if (sscanf(argv[i+1],"%i",&w) ==1) + if ((w > 10) && (w <= 65535)) { + heart_beat_boot_delay = w; + fprintf(stderr,"heart_beat_boot_delay = %d\n",w); + i++; + } + break; + case 'p': + if (strcmp(argv[i], "-pid") == 0) + if (sscanf(argv[i+1],"%lu",&p) ==1){ + heart_beat_kill_pid = p; + fprintf(stderr,"heart_beat_kill_pid = %lu\n",p); + i++; + } + break; +#ifdef __WIN32__ + case 's': + if (strcmp(argv[i], "-shutdown") == 0){ + do_shutdown(1); + exit(0); + } + break; +#endif + default: + ; + } + break; + default: + ; + } + i++; + } + debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p); +} + +int +main(int argc, char **argv) +{ + get_arguments(argc,argv); + if (is_env_set("HEART_DEBUG")) + debug_on=1; +#ifdef __WIN32__ + if (debug_on) { + if(!is_env_set("ERLSRV_SERVICE_NAME")) { + /* this redirects stderr to a separate console (for debugging purposes)*/ + erlin_fd = _dup(0); + erlout_fd = _dup(1); + AllocConsole(); + conh = freopen("CONOUT$","w",stderr); + if (conh != NULL) + fprintf(conh,"console alloced\n"); + } + debugf("stderr\n"); + } + _setmode(erlin_fd,_O_BINARY); + _setmode(erlout_fd,_O_BINARY); +#endif + strcpy(program_name, argv[0]); + notify_ack(erlout_fd); + cmd[0] = '\0'; + do_terminate(message_loop(erlin_fd,erlout_fd)); + return 0; +} + +/* + * message loop + */ +static int +message_loop(erlin_fd, erlout_fd) + int erlin_fd, erlout_fd; +{ + int i; + time_t now, last_received; +#ifdef __WIN32__ + DWORD wresult; +#else + fd_set read_fds; + int max_fd; + struct timeval timeout; + int tlen; /* total message length */ + struct msg m, *mp = &m; +#endif + + init_timestamp(); + timestamp(&now); + last_received = now; +#ifdef __WIN32__ + hevent_dataready = CreateEvent(NULL,FALSE,FALSE,NULL); + hreader_thread = start_reader_thread(); +#else + max_fd = erlin_fd; +#endif + + while (1) { +#ifdef __WIN32__ + wresult = WaitForSingleObject(hevent_dataready,SELECT_TIMEOUT*1000+ 2); + if (wresult == WAIT_FAILED) { + print_last_error(); + return R_ERROR; + } + + if (wresult == WAIT_TIMEOUT) { + debugf("wait timed out\n"); + i = 0; + } else { + debugf("wait ok\n"); + i = 1; + } +#else + FD_ZERO(&read_fds); /* ZERO on each turn */ + FD_SET(erlin_fd, &read_fds); + timeout.tv_sec = SELECT_TIMEOUT; /* On Linux timeout is modified + by select */ + timeout.tv_usec = 0; + if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, &timeout)) < 0) { + print_error("error in select."); + return R_ERROR; + } +#endif + /* + * Maybe heart beat time-out + * If we havn't got anything in 60 seconds we reboot, even if we may + * have got something in the last 5 seconds. We may end up here if + * the system clock is adjusted with more than 55 seconds, but we + * regard this as en error and reboot anyway. + */ + timestamp(&now); + if (now > last_received + heart_beat_timeout) { + print_error("heart-beat time-out."); + return R_TIMEOUT; + } + /* + * Do not check fd-bits if select timeout + */ + if (i == 0) { + continue; + } + /* + * Message from ERLANG + */ +#ifdef __WIN32__ + if (wresult == WAIT_OBJECT_0) { + if (tlen < 0) { +#else + if (FD_ISSET(erlin_fd, &read_fds)) { + if ((tlen = read_message(erlin_fd, mp)) < 0) { +#endif + print_error("error in read_message."); + return R_ERROR; + } + if ((tlen > MSG_HDR_SIZE) && (tlen <= MSG_TOTAL_SIZE)) { + switch (mp->op) { + case HEART_BEAT: + timestamp(&last_received); +#ifdef USE_WATCHDOG + /* reset the hardware watchdog timer */ + wd_reset(); +#endif + break; + case SHUT_DOWN: + return R_SHUT_DOWN; + case SET_CMD: + /* override the HEART_COMMAND_ENV command */ + memcpy(&cmd, &(mp->fill[0]), + tlen-MSG_HDR_PLUS_OP_SIZE); + cmd[tlen-MSG_HDR_PLUS_OP_SIZE] = '\0'; + notify_ack(erlout_fd); + break; + case CLEAR_CMD: + /* use the HEART_COMMAND_ENV command */ + cmd[0] = '\0'; + notify_ack(erlout_fd); + break; + case GET_CMD: + /* send back command string */ + { + char *env = NULL; + char *command + = (cmd[0] + ? (char *)cmd + : (env = get_env(HEART_COMMAND_ENV))); + /* Not set and not in env return "" */ + if (!command) command = ""; + heart_cmd_reply(erlout_fd, command); + free_env_val(env); + } + break; + default: + /* ignore all other messages */ + break; + } + } else if (tlen == 0) { + /* Erlang has closed its end */ + print_error("Erlang has closed."); + return R_CLOSED; + } + /* Junk erroneous messages */ + } + } +} + +#if defined(__WIN32__) +static void +kill_old_erlang(void){ + HANDLE erlh; + DWORD exit_code; + if(heart_beat_kill_pid != 0){ + if((erlh = OpenProcess(PROCESS_TERMINATE | + SYNCHRONIZE | + PROCESS_QUERY_INFORMATION , + FALSE, + (DWORD) heart_beat_kill_pid)) == NULL){ + return; + } + if(!TerminateProcess(erlh, 1)){ + CloseHandle(erlh); + return; + } + if(WaitForSingleObject(erlh,5000) != WAIT_OBJECT_0){ + print_error("Old process did not die, " + "WaitForSingleObject timed out."); + CloseHandle(erlh); + return; + } + if(!GetExitCodeProcess(erlh, &exit_code)){ + print_error("Old process did not die, " + "GetExitCodeProcess failed."); + } + CloseHandle(erlh); + } +} +#elif !defined(VXWORKS) +/* Unix eh? */ +static void +kill_old_erlang(void){ + pid_t pid; + int i; + int res; + if(heart_beat_kill_pid != 0){ + pid = (pid_t) heart_beat_kill_pid; + res = kill(pid,SIGKILL); + for(i=0; i < 5 && res == 0; ++i){ + sleep(1); + res = kill(pid,SIGKILL); + } + if(errno != ESRCH){ + print_error("Unable to kill old process, " + "kill failed (tried multiple times)."); + } + } +} +#endif /* Not on VxWorks */ + +#ifdef __WIN32__ +void win_system(char *command) +{ + char *comspec; + char * cmdbuff; + char * extra = " /C "; + char *env; + STARTUPINFO start; + SECURITY_ATTRIBUTES attr; + PROCESS_INFORMATION info; + + if (!debug_on || test_win95()) { + system(command); + return; + } + comspec = env = get_env("COMSPEC"); + if (!comspec) + comspec = "CMD.EXE"; + cmdbuff = malloc(strlen(command) + strlen(comspec) + strlen(extra) + 1); + if (!cmdbuff) { + print_error("Failed to allocate memory. Terminating..."); + exit(1); + } + strcpy(cmdbuff, comspec); + strcat(cmdbuff, extra); + strcat(cmdbuff, command); + free_env_val(env); + + debugf("running \"%s\"\r\n", cmdbuff); + + memset (&start, 0, sizeof (start)); + start.cb = sizeof (start); + start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + start.wShowWindow = SW_HIDE; + start.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start.hStdOutput = GetStdHandle(STD_ERROR_HANDLE); + start.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = TRUE; + + fflush(stderr); + + if (!CreateProcess(NULL, + cmdbuff, + &attr, + NULL, + TRUE, + 0, + NULL, + NULL, + &start, + &info)) { + debugf("Could not create process for the command %s.\r\n", cmdbuff); + } + WaitForSingleObject(info.hProcess,INFINITE); + free(cmdbuff); +} +#endif /* defined(__WIN32__) */ + +/* + * do_terminate + */ +static void +do_terminate(reason) + int reason; +{ + /* + When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish + (plus heart_beat_report_delay if under VxWorks), so we don't need + to call wd_reset(). + */ + + switch (reason) { + case R_SHUT_DOWN: + break; + case R_TIMEOUT: + case R_ERROR: + case R_CLOSED: + default: +#if defined(__WIN32__) /* Not VxWorks */ + { + if(!cmd[0]) { + char *command = get_env(HEART_COMMAND_ENV); + if(!command) + print_error("Would reboot. Terminating."); + else { + kill_old_erlang(); + /* High prio combined with system() works badly indeed... */ + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); + win_system(command); + print_error("Executed \"%s\". Terminating.",command); + } + free_env_val(command); + } + else { + kill_old_erlang(); + /* High prio combined with system() works badly indeed... */ + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); + win_system(&cmd[0]); + print_error("Executed \"%s\". Terminating.",cmd); + } + } + +#else + { + if(!cmd[0]) { + char *command = get_env(HEART_COMMAND_ENV); + if(!command) + print_error("Would reboot. Terminating."); + else { + kill_old_erlang(); + system(command); + print_error("Executed \"%s\". Terminating.",command); + } + free_env_val(command); + } + else { + kill_old_erlang(); + system((char*)&cmd[0]); + print_error("Executed \"%s\". Terminating.",cmd); + } + } + break; +#endif + } /* switch(reason) */ +} + +/* + * notify_ack + * + * Sends an HEART_ACK. + */ +static int +notify_ack(fd) + int fd; +{ + struct msg m; + + m.op = HEART_ACK; + m.len = htons(1); + return write_message(fd, &m); +} + + +/* + * send back current command + * + * Sends an HEART_CMD. + */ +static int +heart_cmd_reply(int fd, char *s) +{ + struct msg m; + int len = strlen(s) + 1; /* Include \0 */ + + /* FIXME if s >= MSG_BODY_SIZE error */ + + m.op = HEART_CMD; + m.len = htons(len + 2); /* Include Op */ + strcpy((char*)m.fill, s); + + return write_message(fd, &m); +} + + +/* + * write_message + * + * Writes a message to a blocking file descriptor. Returns the total + * size of the message written (always > 0), or -1 if error. + * + * A message which is too short or too long, is not written. The return + * value is then MSG_HDR_SIZE (2), as if the message had been written. + * Is this really necessary? Can't we assume that the length is ok? + * FIXME. + */ +static int +write_message(fd, mp) + int fd; + struct msg *mp; +{ + int len; + char* tmp; + + tmp = (char*) &(mp->len); + len = (*tmp * 256) + *(tmp+1); + if ((len == 0) || (len > MSG_BODY_SIZE)) { + return MSG_HDR_SIZE; + } /* cc68k wants (char *) */ + if (write(fd, (char *) mp, len + MSG_HDR_SIZE) != len + MSG_HDR_SIZE) { + return -1; + } + return len + MSG_HDR_SIZE; +} + +/* + * read_message + * + * Reads a message from a blocking file descriptor. Returns the total + * size of the message read (> 0), 0 if eof, and < 0 if error. + * + * Note: The return value MSG_HDR_SIZE means a message of total size + * MSG_HDR_SIZE, i.e. without even an operation field. + * + * If the size of the message is larger than MSG_TOTAL_SIZE, the total + * number of bytes read is returned, but the buffer contains a truncated + * message. + */ +static int +read_message(fd, mp) + int fd; + struct msg *mp; +{ + int rlen, i; + unsigned char* tmp; + + if ((i = read_fill(fd, (char *) mp, MSG_HDR_SIZE)) != MSG_HDR_SIZE) { + /* < 0 is an error; = 0 is eof */ + return i; + } + + tmp = (unsigned char*) &(mp->len); + rlen = (*tmp * 256) + *(tmp+1); + if (rlen == 0) { + return MSG_HDR_SIZE; + } + if (rlen > MSG_BODY_SIZE) { + if ((i = read_skip(fd, (((char *) mp) + MSG_HDR_SIZE), + MSG_BODY_SIZE, rlen)) != rlen) { + return i; + } else { + return rlen + MSG_HDR_SIZE; + } + } + if ((i = read_fill(fd, ((char *) mp + MSG_HDR_SIZE), rlen)) != rlen) { + return i; + } + return rlen + MSG_HDR_SIZE; +} + +/* + * read_fill + * + * Reads len bytes into buf from a blocking fd. Returns total number of + * bytes read (i.e. len) , 0 if eof, or < 0 if error. len must be > 0. + */ +static int +read_fill(fd, buf, len) + int fd, len; + char *buf; +{ + int i, got = 0; + + do { + if ((i = read(fd, buf + got, len - got)) <= 0) { + return i; + } + got += i; + } while (got < len); + return len; +} + +/* + * read_skip + * + * Reads len bytes into buf from a blocking fd, but puts not more than + * maxlen bytes in buf. Returns total number of bytes read ( > 0), + * 0 if eof, or < 0 if error. len > maxlen > 0 must hold. + */ +static int +read_skip(fd, buf, maxlen, len) + int fd, maxlen, len; + char *buf; +{ + int i, got = 0; + char c; + + if ((i = read_fill(fd, buf, maxlen)) <= 0) { + return i; + } + do { + if ((i = read(fd, &c, 1)) <= 0) { + return i; + } + got += i; + } while (got < len - maxlen); + return len; +} + +/* + * print_error + */ +static void +print_error(const char *format,...) +{ + va_list args; + time_t now; + char *timestr; + + va_start(args, format); + time(&now); + timestr = ctime(&now); + fprintf(stderr, "%s: %.*s: ", program_name, (int) strlen(timestr)-1, timestr); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\r\n"); +} + +static void +debugf(const char *format,...) +{ + va_list args; + + if (!debug_on) return; + va_start(args, format); + fprintf(stderr, "Heart: "); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\r\n"); +} + +#ifdef __WIN32__ +void print_last_error() { + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + /* Display the string.*/ + fprintf(stderr,"GetLastError:%s\n",lpMsgBuf); + + /* Free the buffer. */ + LocalFree( lpMsgBuf ); +} + +static int test_win95(void) +{ + OSVERSIONINFO osinfo; + osinfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + GetVersionEx(&osinfo); + if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + return 1; + else + return 0; +} + +static BOOL enable_privilege() { + HANDLE ProcessHandle; + DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES; + HANDLE TokenHandle; + TOKEN_PRIVILEGES Tpriv; + LUID luid; + ProcessHandle = GetCurrentProcess(); + OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle); + LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid); + Tpriv.PrivilegeCount = 1; + Tpriv.Privileges[0].Luid = luid; + Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0); +} + +static BOOL do_shutdown(int really_shutdown) { + if (test_win95()) { + if (ExitWindowsEx(EWX_REBOOT,0)) { + return TRUE; + } else { + print_last_error(); + return FALSE; + } + } else { + enable_privilege(); + if (really_shutdown) { + if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE)) + return TRUE; + } else if (InitiateSystemShutdown(NULL, + "shutdown by HEART\n" + "will be interrupted", + 30,TRUE,TRUE)) { + AbortSystemShutdown(NULL); + return TRUE; + } + return FALSE; + } +} + +DWORD WINAPI reader(LPVOID lpvParam) { + + while (1) { + debugf("reader is reading\n"); + tlen = read_message(erlin_fd, mp); + debugf("reader setting event\n"); + SetEvent(hevent_dataready); + if(tlen == 0) + break; + } + return 0; +} + +HANDLE start_reader_thread(void) { + DWORD tid; + HANDLE thandle; + if ((thandle = (HANDLE) + _beginthreadex(NULL,0,reader,NULL,0,&tid)) == NULL) { + print_last_error(); + exit(1); + } + return thandle; +} +#endif + +#if defined(__WIN32__) + +# define TICK_MASK 0x7FFFFFFFUL + +void init_timestamp(void) +{ +} + +time_t timestamp(time_t *res) +{ + static time_t extra = 0; + static unsigned last_ticks = 0; + unsigned this_ticks; + time_t r; + + this_ticks = GetTickCount() & TICK_MASK; + + if (this_ticks < last_ticks) { + extra += (time_t) ((TICK_MASK + 1) / 1000); + } + + last_ticks = this_ticks; + + r = ((time_t) (this_ticks / 1000)) + extra; + + if (res != NULL) + *res = r; + return r; +} + +#elif defined(VXWORKS) + +static WDOG_ID watchdog_id; +static volatile unsigned elapsed; +static WIND_TCB *this_task; +/* A simple variable is enough to lock the time update, as the + watchdog is run at interrupt level and never preempted. */ +static volatile int lock_time; + +static void my_delete_hook(WIND_TCB *tcb) +{ + if (tcb == this_task) { + wdDelete(watchdog_id); + watchdog_id = NULL; + taskDeleteHookDelete((FUNCPTR) &my_delete_hook); + } +} + +static void my_wd_routine(int count) +{ + if (watchdog_id != NULL) { + ++count; + if (!lock_time) { + elapsed += count; + count = 0; + } + wdStart(watchdog_id, sysClkRateGet(), + (FUNCPTR) &my_wd_routine, count); + } +} + +void init_timestamp(void) +{ + lock_time = 0; + elapsed = 0; + watchdog_id = wdCreate(); + this_task = (WIND_TCB *) taskIdSelf(); + taskDeleteHookAdd((FUNCPTR) &my_delete_hook); + wdStart(watchdog_id, sysClkRateGet(), + (FUNCPTR) &my_wd_routine, 0); +} + +time_t timestamp(time_t *res) +{ + time_t r; + ++lock_time; + r = (time_t) elapsed; + --lock_time; + if (res != NULL) + *res = r; + return r; +} + +#elif defined(HAVE_GETHRTIME) + +void init_timestamp(void) +{ +} + +time_t timestamp(time_t *res) +{ + hrtime_t ht = gethrtime(); + time_t r = (time_t) (ht / 1000000000); + if (res != NULL) + *res = r; + return r; +} + +#elif defined(HEART_CORRECT_USING_TIMES) + +# ifdef NO_SYSCONF +# include <sys/param.h> +# define TICKS_PER_SEC() HZ +# else +# define TICKS_PER_SEC() sysconf(_SC_CLK_TCK) +# endif + +# define TICK_MASK 0x7FFFFFFFUL + +static unsigned tps; + +void init_timestamp(void) +{ + tps = TICKS_PER_SEC(); +} + +time_t timestamp(time_t *res) +{ + static time_t extra = 0; + static clock_t last_ticks = 0; + clock_t this_ticks; + struct tms dummy; + time_t r; + + this_ticks = (times(&dummy) & TICK_MASK); + + if (this_ticks < last_ticks) { + extra += (time_t) ((TICK_MASK + 1) / tps); + } + + last_ticks = this_ticks; + + r = ((time_t) (this_ticks / tps)) + extra; + + if (res != NULL) + *res = r; + return r; +} + +#else + +void init_timestamp(void) +{ +} + +time_t timestamp(time_t *res) +{ + return time(res); +} + +#endif diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c new file mode 100644 index 0000000000..ff16ee02c4 --- /dev/null +++ b/erts/etc/common/inet_gethost.c @@ -0,0 +1,2757 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Erlang port program to do the name service lookup for the erlang + * distribution and inet part of the kernel. + * A pool of subprocess is kept, to which a pair of pipes is connected. + * The main process schedules requests among the different subprocesses + * (created with fork()), to be able to handle as many requests as possible + * simultaneously. The controlling erlang machine may request a "cancel", + * in which case the process may be killed and restarted when the need arises. + * The single numeric parameter to this program is the maximum port pool size, + * which is the size of the bookkeeping array. + * + * Windows: + * There is instead of a pool of processes a pool of threads. + * Communication is not done through pipes but via message queues between + * the threads. The only "pipes" involved are the ones used for communicating + * with Erlang. + * Important note: + * For unknown reasons, the combination of a thread doing blocking I/O on + * a named pipe at the same time as another thread tries to resolve a hostname + * may (with certain software configurations) block the gethostbyname call (!) + * For that reason, standard input (and standard output) should be opened + * in asynchronous mode (FILE_FLAG_OVERLAPPED), which has to be done by Erlang. + * A special flag to open_port is used to work around this behaviour in winsock + * and the threads doing read and write handle asynchronous I/O. + * The ReadFile and WriteFile calls try to cope with both types of I/O, why + * the code is not really as Microsoft describes "the right way to do it" in + * their documentation. Important to note is that **there is no supported way + * to retrieve the information if the HANDLE was opened with + * FILE_FLAG_OVERLAPPED from the HANDLE itself**. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <winsock2.h> +#include <windows.h> +#include <process.h> +#include <stdio.h> +#include <stdlib.h> + +/* These are not used even if they would exist which they should not */ +#undef HAVE_GETADDRINFO +#undef HAVE_GETIPNODEBYNAME +#undef HAVE_GETHOSTBYNAME2 +#undef HAVE_GETNAMEINFO +#undef HAVE_GETIPNODEBYADDR + +#else /* Unix */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <signal.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#include <sys/times.h> + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +/* To simplify #ifdef code further down - select only one to be defined... +** Use them in pairs - if one is broken do not trust its mate. +**/ +#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO) +#undef HAVE_GETIPNODEBYNAME +#undef HAVE_GETIPNODEBYADDR +#undef HAVE_GETHOSTBYNAME2 +#elif defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_GETIPNODEBYADDR) +#undef HAVE_GETADDRINFO +#undef HAVE_GETNAMEINFO +#undef HAVE_GETHOSTBYNAME2 +#else +#undef HAVE_GETIPNODEBYNAME +#undef HAVE_GETIPNODEBYADDR +#undef HAVE_GETADDRINFO +#undef HAVE_GETNAMEINFO +#endif + +#endif /* !WIN32 */ + +#define PACKET_BYTES 4 +#ifdef WIN32 +#define READ_PACKET_BYTES(X,Y,Z) read_int32((X),(Y),(Z)) +#else +#define READ_PACKET_BYTES(X,Y) read_int32((X),(Y)) +#endif +#define PUT_PACKET_BYTES(X,Y) put_int32((X),(Y)) +/* The serial numbers of the requests */ +typedef int SerialType; + +#define INVALID_SERIAL -1 + +/* The operations performed by this program */ +typedef unsigned char OpType; + +#define OP_GETHOSTBYNAME 1 +#define OP_GETHOSTBYADDR 2 +#define OP_CANCEL_REQUEST 3 +#define OP_CONTROL 4 + +/* The protocol (IPV4/IPV6) */ +typedef unsigned char ProtoType; + +#define PROTO_IPV4 1 +#define PROTO_IPV6 2 + +/* OP_CONTROL */ +typedef unsigned char CtlType; +#define SETOPT_DEBUG_LEVEL 0 + +/* The unit of an IP address (0 == error, 4 == IPV4, 16 == IPV6) */ +typedef unsigned char UnitType; + +#define UNIT_ERROR 0 +#define UNIT_IPV4 4 +#define UNIT_IPV6 16 + +/* And the byte type */ +typedef unsigned char AddrByte; /* Must be compatible with character + datatype */ + +/* + * Marshalled format of request: + *{ + * Serial: 32 bit big endian + * Op:8 bit [1,2,3] + * If op == 1 { + * Proto:8 bit [1,2] + * Str: Null terminated array of characters + * } Else if op == 2 { + * Proto:8 bit [1,2] + * If proto == 1 { + * B0..B3: 4 bytes, most significant first + * } Else (proto == 2) { + * B0..B15: 16 bytes, most significant first + * } + * } + * (No more if op == 3) + *} + * The request arrives as a packet, with 4 packet size bytes. + */ + +/* The main process unpackes the marshalled message and sends the data + * to a suitable port process or, in the case of a close request, kills the + * suitable port process. There is also a que of requests linked together, + * for when all subrocesses are busy. + */ + +typedef struct QueItem { + struct QueItem *next; + int req_size; + AddrByte request[1]; +} QueItem; /* Variable size due to request's variable size */ + +QueItem *que_first; +QueItem *que_last; + +#ifdef WIN32 +typedef struct mesq { + HANDLE data_present; + CRITICAL_SECTION crit; + int shutdown; + QueItem *first; + QueItem *last; +} MesQ; + +MesQ *to_erlang; +MesQ *from_erlang; +#endif + +/* + * Marshalled format of reply: + *{ + * Serial: 32 bit big endian + * Unit: 8 bit, same as h_length or 0 for error + * if unit == 0 { + * Str: Null terminated character string explaining the error + * } else { + * Naddr: 32 bit big endian + * if unit = 4 { + * (B0..B3)0..(B0..B3)Naddr-1: Naddr*4 bytes most significant first + * } else if unit == 16 { + * (B0..B15)0..(B0..B15)Naddr-1: Naddr*16 bytes most significant first + * } + * Nnames: 32 bit big endian >= 1 + * Name0: Null terminated string of characters + * Alias[0]..Alias[Nnames - 2]: Nnames - 1 Null terminated strings of chars + * } + *} + * Four packet size bytes prepended (big endian) + */ +/* Internal error codes */ +#define ERRCODE_NOTSUP 1 +#define ERRCODE_HOST_NOT_FOUND 2 +#define ERRCODE_TRY_AGAIN 3 +#define ERRCODE_NO_RECOVERY 4 +#define ERRCODE_NO_DATA 5 +#define ERRCODE_NETDB_INTERNAL 7 + +/* + * Each worker process is represented in the parent by the following struct + */ + +typedef unsigned WorkerState; + +#define WORKER_EMPTY 0 /* No process created */ +#define WORKER_FREE 1 /* Living waiting process */ +#define WORKER_BUSY 2 /* Living busy process */ +#define WORKER_STALLED 3 /* Living cancelled process */ + +/* The timeout when killing a child process in seconds*/ +#define CHILDWAIT_TMO 1 +/* The domainname size_limit */ +#define DOMAINNAME_MAX 258 /* 255 + Opcode + Protocol + Null termination */ + +typedef struct { + WorkerState state; +#ifdef WIN32 + DWORD pid; /* 0 if unused */ + MesQ *writeto; /* Message queues */ + MesQ *readfrom; +#else + pid_t pid; /* -1 if unused */ + int writeto, readfrom; /* Pipes */ +#endif + SerialType serial; + AddrByte domain[DOMAINNAME_MAX]; + QueItem *que_first; + QueItem *que_last; + int que_size; +} Worker; + +int num_busy_workers; +int num_free_workers; +int num_stalled_workers; +int max_workers; +int greedy_threshold; +Worker *busy_workers; /* Workers doing any job that someone really is + interested in */ +Worker *free_workers; /* Really free workers */ +Worker *stalled_workers; /* May still deliver answers which we will + discard */ +#define BEE_GREEDY() (num_busy_workers >= greedy_threshold) + +static char *program_name; + +static int debug_level; +#ifdef WIN32 +static HANDLE debug_console_allocated = INVALID_HANDLE_VALUE; +#endif + +#ifdef NODEBUG +#define DEBUGF(L,P) /* Nothing */ +#else +#define DEBUGF(Level,Printf) do { if (debug_level >= (Level)) \ + debugf Printf;} while(0) +#endif +#define ALLOC(Size) my_malloc(Size) +#define REALLOC(Old, Size) my_realloc((Old), (Size)) +#define FREE(Ptr) free(Ptr) + +#ifdef WIN32 +#define WAKEUP_WINSOCK() do { \ + char dummy_buff[100]; \ + gethostname(dummy_buff,99); \ +} while (0) +#endif + +/* The internal prototypes */ +static char *format_address(int siz, AddrByte *addr); +static void debugf(char *format, ...); +static void warning(char *format, ...); +static void fatal(char *format, ...); +static void *my_malloc(size_t size); +static void *my_realloc(void *old, size_t size); +static int get_int32(AddrByte *buff); +static void put_int32(AddrByte *buff, int value); +static int create_worker(Worker *pworker, int save_que); +static int map_netdb_error(int netdb_code); +#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) +static int map_netdb_error_ai(int netdb_code); +#endif +static char *errcode_to_string(int errcode); +static size_t build_error_reply(SerialType serial, int errnum, + AddrByte **preply, + size_t *preply_size); +#ifdef HAVE_GETADDRINFO +static size_t build_reply_ai(SerialType serial, int, struct addrinfo *, + AddrByte **preply, size_t *preply_size); +#endif +static size_t build_reply(SerialType serial, struct hostent *he, + AddrByte **preply, size_t *preply_size); +static int read_request(AddrByte **buff, size_t *buff_size); +static OpType get_op(AddrByte *buff); +static AddrByte *get_op_addr(AddrByte *buff); +static SerialType get_serial(AddrByte *buff); +static ProtoType get_proto(AddrByte *buff); +static CtlType get_ctl(AddrByte *buff); +static AddrByte *get_data(AddrByte *buff); +static int get_debug_level(AddrByte *buff); +static int relay_reply(Worker *pw); +static int ignore_reply(Worker *pw); +static void init_workers(int max); +static void kill_worker(Worker *pw); +static Worker *pick_worker(void); +static void kill_last_picked_worker(void); +static void stall_worker(SerialType serial); +static int handle_io_busy(int ndx); +static int handle_io_free(int ndx); +static int handle_io_stalled(int ndx); +static void check_que(void); +static void main_loop(void); +static void usage(char *unknown); +static void domaincopy(AddrByte *out,AddrByte *in); +static int domaineq(AddrByte *d1, AddrByte *d2); +static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff); +static Worker *pick_worker_greedy(AddrByte *domainbuff); +static void restart_worker(Worker *w); +static void start_que_request(Worker *w) ; +#ifdef WIN32 +static int read_int32(HANDLE fd, int *res, HANDLE ev); +static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev); +static int write_exact(HANDLE fd, AddrByte *buff, DWORD len,HANDLE ev); +DWORD WINAPI worker_loop(void *v); +DWORD WINAPI reader(void *data); +DWORD WINAPI writer(void *data); +static int send_mes_to_worker(QueItem *m, Worker *pw); +BOOL create_mesq(MesQ **q); +BOOL enque_mesq(MesQ *q, QueItem *m); +BOOL deque_mesq(MesQ *q, QueItem **m); +BOOL close_mesq(MesQ *q); +HANDLE event_mesq(MesQ *q); +#else +static size_t read_int32(int fd, int *res); +static ssize_t read_exact(int fd, void *vbuff, size_t nbytes); +static int write_exact(int fd, AddrByte *buff, int len); +void reap_children(int ignored); +static void init_signals(void); +static void kill_all_workers(void); +static void close_all_worker_fds(void); +static int worker_loop(void); +static int fillin_reply(Worker *pw); +static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw); +#endif + +#define ERL_DBG_LVL_ENV_VAR "ERL_INET_GETHOST_DEBUG" + +static int +get_env_debug_level(void) +{ +#ifdef __WIN32__ + char value[21]; /* Enough for any 64-bit values */ + DWORD sz = GetEnvironmentVariable((LPCTSTR) ERL_DBG_LVL_ENV_VAR, + (LPTSTR) value, + (DWORD) sizeof(value)); + if (sz == 0 || sz > sizeof(value)) + return 0; +#else + char *value = getenv(ERL_DBG_LVL_ENV_VAR); + if (!value) + return 0; +#endif + return atoi(value); +} + +#ifdef WIN32 +static void do_allocate_console(void) +{ + AllocConsole(); + debug_console_allocated = CreateFile ("CONOUT$", GENERIC_WRITE, + FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); +} +#ifdef HARDDEBUG +DWORD WINAPI pseudo_worker_loop(void *v); +static void poll_gethost(int row); +#endif +#endif + +/* + * Main + */ +int main(int argc, char **argv) +{ + int num_workers = 1; + char **ap = argv + 1; + int x; + int disable_greedy = 0; + + program_name = *argv; + que_first = que_last = NULL; + debug_level = get_env_debug_level(); + greedy_threshold = 0; + + while (*ap) { + if (!strcmp(*ap, "-d")) { + ++debug_level; + } else if(!strcmp(*ap, "-g") && *(ap + 1)) { + ++ap; + x = atoi(*ap); + if (!x) { + usage(*ap); + } else { + greedy_threshold = x; + } + } else if(!strcmp(*ap, "-ng")) { + disable_greedy = 1; + } else { + x = atoi(*ap); + if (!x) { + usage(*ap); + } else { + num_workers = x; + } + } + ++ap; + } + +#ifdef WIN32 + if (num_workers > 60 || greedy_threshold > 60) { + usage("More than 60 workers on windows impossible!"); + num_workers = 60; + greedy_threshold = 0; + } +#endif + + if(!greedy_threshold) { + greedy_threshold = (3*num_workers)/4; /* 75% */ + if (!greedy_threshold) { + greedy_threshold = num_workers; + } + } + + if (disable_greedy) { + greedy_threshold = num_workers + 1; + } + +#ifdef WIN32 + { + WORD wr; + WSADATA wsa_data; + int wsa_error; + wr = MAKEWORD(2,0); + + wsa_error = WSAStartup(wr,&wsa_data); + if (wsa_error) { + fatal("Could not open usable winsock library."); + } + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 0) { + fatal("Could not open recent enough winsock library."); + } + + if (debug_level >= 1) { + do_allocate_console(); + + DEBUGF(1,("num_workers = %d, greedy_threshold = %d, " + "debug_level = %d.", + num_workers, greedy_threshold, debug_level)); + } + } + WAKEUP_WINSOCK(); /* Why on earth is this needed? */ + +#endif + + init_workers(num_workers); + + main_loop(); +#ifndef WIN32 + kill_all_workers(); +#endif + return 0; +} + +static void usage(char *unknown) +{ + fprintf(stderr,"%s: Unknown option \"%s\"\n" + "Usage: %s [-d [-d ...]] [-g <greedy threshold>] " + "[<number of workers>]\n", + program_name, unknown, program_name); +} + +/* + * Main process main loop + */ + +static int handle_io_busy(int ndx) +{ + /* Probably an answer */ + int res; + res = relay_reply(&busy_workers[ndx]); + if (res < 0) { + /* Bad worker */ + if (busy_workers[ndx].que_size) { + restart_worker(&busy_workers[ndx]); + start_que_request(&busy_workers[ndx]); + return 0; + } else { + kill_worker(&busy_workers[ndx]); + --num_busy_workers; + busy_workers[ndx] = busy_workers[num_busy_workers]; + } + return 1; + } else if (res == 0) { + /* Erlang has closed */ + return -1; + } else { + if (busy_workers[ndx].que_size) { + start_que_request(&busy_workers[ndx]); + return 0; + } + /* The worker is no longer busy, it should be in the free list */ + free_workers[num_free_workers] = busy_workers[ndx]; + free_workers[num_free_workers].state = WORKER_FREE; + ++num_free_workers; + --num_busy_workers; + busy_workers[ndx] = busy_workers[num_busy_workers]; + return 1; + } +} + +static int handle_io_free(int ndx) +{ + /* IO from a free worker means "kill me" */ + DEBUGF(1,("Free worker[%ld] spontaneously died.", + (long) free_workers[ndx].pid)); + kill_worker(&free_workers[ndx]); + --num_free_workers; + free_workers[ndx] = free_workers[num_free_workers]; + return 1; +} + +static int handle_io_stalled(int ndx) +{ + int res; + res = ignore_reply(&stalled_workers[ndx]); + if (res <= 0) { + /* Bad worker */ + kill_worker(&stalled_workers[ndx]); + --num_stalled_workers; + stalled_workers[ndx] = stalled_workers[num_stalled_workers]; + return 1; + } else { + DEBUGF(3,("Ignoring reply from stalled worker[%ld].", + (long) stalled_workers[ndx].pid)); + free_workers[num_free_workers] = stalled_workers[ndx]; + free_workers[num_free_workers].state = WORKER_FREE; + ++num_free_workers; + --num_stalled_workers; + stalled_workers[ndx] = stalled_workers[num_stalled_workers]; + return 1; + } +} + +static void check_que(void) +{ + /* Check if anything in the que can be handled */ + Worker *cw; + + while (que_first) { + QueItem *qi,*nxt; + qi = que_first; + nxt = qi->next; /* Need to save before it's getting put in another que + in threaded solution */ + if ((cw = pick_worker()) == NULL) { + break; + } +#ifdef WIN32 + { + SerialType save_serial = get_serial(que_first->request); + if (send_mes_to_worker(que_first, cw) != 0) { + kill_last_picked_worker(); + continue; + } + cw->serial = save_serial; + } +#else + if (send_request_to_worker(que_first->request, + que_first->req_size, cw) != 0) { + /* Couldn't send request, kill the worker and retry */ + kill_last_picked_worker(); + continue; + } + cw->serial = get_serial(que_first->request); +#endif + /* Went well, lets deque */ + que_first = nxt; + if (que_first == NULL) { + que_last = NULL; + } + DEBUGF(3,("Did deque serial %d, Que is %sempty", + get_serial(qi->request), (que_first) ? "not " : "")); +#ifndef WIN32 + FREE(qi); +#endif + } +} + +static int clean_que_of(SerialType s) +{ + QueItem **qi; + int i; + + for(qi=&que_first;*qi != NULL && + s != get_serial((*qi)->request); qi = &((*qi)->next)) + ; + if(*qi != NULL) { + QueItem *r = *qi; + *qi = (*qi)->next; + FREE(r); + if(que_last == r) { + /* Lost the "last" pointer, should be very uncommon + if the que is not empty, so we simply do a traversal + to reclaim it. */ + if (que_first == NULL) { + que_last = NULL; + } else { + for (que_last=que_first;que_last->next != NULL; + que_last = que_last->next) + ; + } + } + DEBUGF(3,("Removing serial %d from global que on request, " + "que %sempty",s, (que_first) ? "not " : "")); + return 1; + } + for (i = 0; i < num_busy_workers; ++i) { + for(qi=&(busy_workers[i].que_first);*qi != NULL && + s != get_serial((*qi)->request); qi = &((*qi)->next)) + ; + if(*qi != NULL) { + QueItem *r = *qi; + *qi = (*qi)->next; + FREE(r); + if(busy_workers[i].que_last == r) { + /* Lost the "last" pointer, should be very uncommon + if the que is not empty, so we simply do a traversal + to reclaim it. */ + if (busy_workers[i].que_first == NULL) { + busy_workers[i].que_last = NULL; + if (busy_workers[i].que_size != 1) { + fatal("Worker que size counter incorrect, internal datastructure error."); + } + } else { + for (busy_workers[i].que_last = busy_workers[i].que_first; + busy_workers[i].que_last->next != NULL; + busy_workers[i].que_last = busy_workers[i].que_last->next) + ; + } + } + --(busy_workers[i].que_size); + DEBUGF(3,("Removing serial %d from worker[%ld] specific que " + "on request, que %sempty", + s, (long) busy_workers[i].pid, + (busy_workers[i].que_first) ? "not " : "")); + return 1; + } + } + return 0; +} + +static void main_loop(void) +{ + AddrByte *inbuff = NULL; + int insize; + int i,w; +#ifdef WIN32 + HANDLE handles[64]; + DWORD num_handles; + DWORD index; + QueItem *qi; +#else + size_t inbuff_size = 0; + fd_set fds; + int max_fd; +#endif + int new_data; + int save_serial; + /* It's important that the free workers list is handled first */ + Worker *workers[3] = {free_workers, busy_workers, stalled_workers}; + int *wsizes[3] = {&num_free_workers, &num_busy_workers, + &num_stalled_workers}; + int (*handlers[3])(int) = {&handle_io_free, &handle_io_busy, + &handle_io_stalled}; + Worker *cw; + AddrByte domainbuff[DOMAINNAME_MAX]; + +#ifdef WIN32 + + { + DWORD dummy; + /* Create the reader and writer */ + if ((!create_mesq(&to_erlang)) || (!create_mesq(&from_erlang))) { + fatal("Could not create message que! errno = %d.",GetLastError()); + } + if (((HANDLE) _beginthreadex(NULL,0,writer,to_erlang,0,&dummy)) + == NULL) { + fatal("Could not create writer thread! errno = %d.",GetLastError()); + } + if (((HANDLE) _beginthreadex(NULL,0,reader,from_erlang,0,&dummy)) + == NULL) { + fatal("Could not create reader thread! errno = %d.",GetLastError()); + } + DEBUGF(4,("Created reader and writer threads.")); +#ifdef HARDDEBUG + poll_gethost(__LINE__); +#endif + } +#endif + + for(;;) { +#ifdef WIN32 + num_handles = 0; + handles[num_handles++] = event_mesq(from_erlang); + for (w = 0; w < 3; ++w) { + for (i = 0; i < *wsizes[w]; ++i) { + handles[num_handles++] = event_mesq(workers[w][i].readfrom); + } + } + + if ((index = WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE)) + == WAIT_FAILED) { + fatal("Could not WaitForMultpleObjects! errno = %d.",GetLastError()); + } + w = 0; + index -= WAIT_OBJECT_0; + + DEBUGF(4,("Got data on index %d.",index)); + if (index > 0) { + if (((int)index - 1) < *wsizes[0]) { + (*handlers[0])(index - 1); + } else if (((int)index - 1) < ((*wsizes[0]) + (*wsizes[1]))) { + (*handlers[1])(index - 1 - (*wsizes[0])); + } else { + (*handlers[2])(index - 1 - (*wsizes[0]) - (*wsizes[1])); + } + } + new_data = (index == 0); +#else + max_fd = 0; + FD_ZERO(&fds); + FD_SET(0,&fds); + for (w = 0; w < 3; ++w) { + for (i = 0; i < *wsizes[w]; ++i) { + FD_SET(workers[w][i].readfrom,&fds); + if (workers[w][i].readfrom > max_fd) { + max_fd = workers[w][i].readfrom; + } + } + } + for (;;) { + if (select(max_fd + 1,&fds,NULL,NULL,NULL) < 0) { + if (errno == EINTR) { + continue; + } else { + fatal("Select failed (invalid internal structures?), " + "errno = %d.",errno); + } + } + break; + } + for (w = 0; w < 3; ++w) { + for (i = 0; i < *wsizes[w]; ++i) { + if (FD_ISSET(workers[w][i].readfrom, &fds)) { + int hres = (*handlers[w])(i); + if (hres < 0) { + return; + } else { + i -= hres; /* We'll retry this position, if hres == 1. + The position is usually + replaced with another worker, + a worker with + I/O usually changes state as we + use blocking file I/O */ + } + } + } + } + new_data = FD_ISSET(0,&fds); + +#endif + + check_que(); + + /* Now check for new requests... */ + if (new_data) { /* Erlang... */ + OpType op; +#ifdef WIN32 + if (!deque_mesq(from_erlang,&qi)) { + DEBUGF(1,("Erlang has closed.")); + return; + } + insize = qi->req_size; + inbuff = qi->request; + DEBUGF(4,("Got data from erlang.")); + DEBUGF(4,("OPeration == %d.",get_op(inbuff))); +#else + insize = read_request(&inbuff, &inbuff_size); + if (insize == 0) { /* Other errors taken care of in + read_request */ + DEBUGF(1,("Erlang has closed.")); + return; + } +#endif + op = get_op(inbuff); + if (op == OP_CANCEL_REQUEST) { + SerialType serial = get_serial(inbuff); + if (!clean_que_of(serial)) { + for (i = 0; i < num_busy_workers; ++i) { + if (busy_workers[i].serial == serial) { + if (busy_workers[i].que_size) { + restart_worker(&busy_workers[i]); + start_que_request(&busy_workers[i]); + } else { + stall_worker(i); + check_que(); + } + break; + } + } + } +#ifdef WIN32 + FREE(qi); +#endif + continue; /* New select */ + } else if (op == OP_CONTROL) { + CtlType ctl; + SerialType serial = get_serial(inbuff); + if (serial != INVALID_SERIAL) { + fatal("Invalid serial: %d.", serial); + } + switch (ctl = get_ctl(inbuff)) { + case SETOPT_DEBUG_LEVEL: + { + int tmp_debug_level = get_debug_level(inbuff); +#ifdef WIN32 + if (debug_console_allocated == INVALID_HANDLE_VALUE && + tmp_debug_level > 0) { + DWORD res; + do_allocate_console(); + WriteFile(debug_console_allocated, + "Hej\n",4,&res,NULL); + } +#endif + debug_level = tmp_debug_level; + DEBUGF(debug_level, ("debug_level = %d", debug_level)); + for (w = 0; w < 3; ++w) { + for (i = 0; i < *wsizes[w]; i++) { + int res; +#ifdef WIN32 + QueItem *m; +#endif + cw = &(workers[w][i]); +#ifdef WIN32 + m = ALLOC(sizeof(QueItem) - 1 + qi->req_size); + memcpy(m->request, qi->request, + (m->req_size = qi->req_size)); + m->next = NULL; + if ((res = send_mes_to_worker(m, cw)) != 0) { + FREE(m); + } +#else + res = send_request_to_worker(inbuff, insize, cw); +#endif + if (res != 0) { + kill_worker(cw); + (*wsizes[w])--; + *cw = workers[w][*wsizes[w]]; + } + } + } + } + break; + default: + warning("Unknown control requested from erlang (%d), " + "message discarded.", (int) ctl); + break; + } +#ifdef WIN32 + FREE(qi); +#endif + continue; /* New select */ + } else { + ProtoType proto; + if (op != OP_GETHOSTBYNAME && op != OP_GETHOSTBYADDR) { + warning("Unknown operation requested from erlang (%d), " + "message discarded.", op); +#ifdef WIN32 + FREE(qi); +#endif + continue; + } + if ((proto = get_proto(inbuff)) != PROTO_IPV4 && + proto != PROTO_IPV6) { + warning("Unknown protocol requested from erlang (%d), " + "message discarded.", proto); +#ifdef WIN32 + FREE(qi); +#endif + continue; + } + if (get_domainname(inbuff,insize,domainbuff) < 0) { + warning("Malformed message sent from erlang, no domain, " + "message discarded.", op); +#ifdef WIN32 + FREE(qi); +#endif + continue; + } + } + + if (BEE_GREEDY()) { + DEBUGF(4,("Beeing greedy!")); + if ((cw = pick_worker_greedy(domainbuff)) != NULL) { + /* Put it in the worker specific que if the + domainname matches... */ +#ifndef WIN32 + QueItem *qi = ALLOC(sizeof(QueItem) - 1 + + insize); + qi->req_size = insize; + memcpy(&(qi->request), inbuff, insize); + qi->next = NULL; +#endif + if (!cw->que_first) { + cw->que_first = cw->que_last = qi; + } else { + cw->que_last->next = qi; + cw->que_last = qi; + } + ++(cw->que_size); + continue; + } + /* Otherwise busyness as usual */ + } + + save_serial = get_serial(inbuff); + + while ((cw = pick_worker()) != NULL) { + int res; +#ifdef WIN32 + res = send_mes_to_worker(qi,cw); +#else + res = send_request_to_worker(inbuff, insize, cw); +#endif + if (res == 0) { + break; + } else { + kill_last_picked_worker(); + } + } + + if (cw == NULL) { + /* Insert into que */ +#ifndef WIN32 + QueItem *qi = ALLOC(sizeof(QueItem) - 1 + + insize); + qi->req_size = insize; + memcpy(&(qi->request), inbuff, insize); + qi->next = NULL; +#endif + if (!que_first) { + que_first = que_last = qi; + } else { + que_last->next = qi; + que_last = qi; + } + } else { + cw->serial = save_serial; + domaincopy(cw->domain, domainbuff); + } + } + } +} + +/* + * Main process worker administration + */ + +static void init_workers(int max) +{ + max_workers = max; + num_busy_workers = 0; + num_free_workers = 0; + num_stalled_workers = 0; + + busy_workers = ALLOC(sizeof(Worker) * max_workers); + free_workers = ALLOC(sizeof(Worker) * max_workers); + stalled_workers = ALLOC(sizeof(Worker) * max_workers); +#ifndef WIN32 + init_signals(); +#endif +} + +#ifdef WIN32 +static void kill_worker(Worker *pw) +{ + /* Cannot really kill a thread in win32, have to just leave it to die */ + close_mesq(pw->writeto); + close_mesq(pw->readfrom); + pw->state = WORKER_EMPTY; +} +#else +static void kill_worker(Worker *pw) +{ + fd_set fds; + struct timeval tmo; + int selret; + static char buff[1024]; + + DEBUGF(3,("Killing worker[%ld] with fd %d, serial %d", + (long) pw->pid, + (int) pw->readfrom, + (int) pw->serial)); + kill(pw->pid, SIGUSR1); + /* This is all just to check that the child died, not + really necessary */ + for(;;) { + FD_ZERO(&fds); + FD_SET(pw->readfrom, &fds); + tmo.tv_usec=0; + tmo.tv_sec = CHILDWAIT_TMO; + selret = select(pw->readfrom+1, &fds, NULL, NULL, &tmo); + if (selret < 0) { + if (errno != EINTR) { + warning("Unable to select on dying child file descriptor, " + "errno = %d.",errno); + break; + } + } else if (selret == 0) { + warning("Timeout waiting for child process to die, " + "ignoring child (pid = %d).", pw->pid); + break; + } else { + int ret; + if ((ret = read(pw->readfrom, buff, 1024)) < 0) { + if (errno != EINTR) { + warning("Child file descriptor not closed properly, " + "errno = %d", errno); + break; + } + } else if (ret == 0) { + break; + } + /* continue */ + } + } + /* Waiting is done by signal handler... */ + close(pw->readfrom); + close(pw->writeto); + pw->state = WORKER_EMPTY; + /* Leave rest as is... */ +} + +static void kill_all_workers(void) +/* Emergency function, will not check that the children died... */ +{ + int i; + for (i = 0; i < num_busy_workers; ++i) { + kill(busy_workers[i].pid, SIGUSR1); + } + for (i = 0; i < num_free_workers; ++i) { + kill(free_workers[i].pid, SIGUSR1); + } + for (i = 0; i < num_stalled_workers; ++i) { + kill(stalled_workers[i].pid, SIGUSR1); + } +} +#endif /* !WIN32 */ + +static Worker *pick_worker(void) +{ + Worker tmp; + if (num_free_workers > 0) { + --num_free_workers; + tmp = free_workers[num_free_workers]; + } else if (num_stalled_workers > 0) { + /* "restart" the worker... */ + --num_stalled_workers; + kill_worker(&(stalled_workers[num_stalled_workers])); + if (create_worker(&tmp,0) < 0) { + warning("Unable to create worker process, insufficient " + "resources"); + return NULL; + } + } else { + if (num_busy_workers == max_workers) { + return NULL; + } + if (create_worker(&tmp,0) < 0) { + warning("Unable to create worker process, insufficient " + "resources"); + return NULL; + } + } + /* tmp contains a worker now, make it busy and put it in the right + array */ + tmp.state = WORKER_BUSY; + busy_workers[num_busy_workers] = tmp; + ++num_busy_workers; + return &(busy_workers[num_busy_workers-1]); +} + +static Worker *pick_worker_greedy(AddrByte *domainbuff) +{ + int i; + int ql = 0; + int found = -1; + for (i=0; i < num_busy_workers; ++i) { + if (domaineq(busy_workers[i].domain, domainbuff)) { + if ((found < 0) || (busy_workers[i].que_size < + busy_workers[found].que_size)) { + found = i; + ql = busy_workers[i].que_size; + } + } + } + if (found >= 0) { + return &busy_workers[found]; + } + return NULL; +} + +static void restart_worker(Worker *w) +{ + kill_worker(w); + if (create_worker(w,1) < 0) { + fatal("Unable to create worker process, insufficient resources"); + } +} + +static void kill_last_picked_worker(void) +{ + kill_worker( &(busy_workers[num_busy_workers-1])); + --num_busy_workers; +} + +/* + * Starts a request qued to a specific worker, check_que starts normally queued requests. + * We expect a que here... + */ +static void start_que_request(Worker *w) +{ + QueItem *qi; + SerialType save_serial; + if (!w->que_first || !w->que_size) { + fatal("Expected que'd requests but found none, " + "internal datastructure corrupted!"); + } + qi = w->que_first; + w->que_first = w->que_first->next; + if (!w->que_first) { + w->que_last = NULL; + } + --(w->que_size); + save_serial = get_serial(qi->request); +#ifdef WIN32 + while (send_mes_to_worker(qi, w) != 0) { + restart_worker(w); + } +#else + while (send_request_to_worker(qi->request, + qi->req_size, w) != 0) { + restart_worker(w); + } +#endif + w->serial = save_serial; + DEBUGF(3,("Did deque serial %d from worker[%ld] specific que, " + "Que is %sempty", + get_serial(qi->request), (long) w->pid, + (w->que_first) ? "not " : "")); +#ifndef WIN32 + FREE(qi); +#endif +} + +#ifndef WIN32 +/* Signal utilities */ +static RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int) +{ + struct sigaction act, oact; + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = func; + sigaction(sig, &act, &oact); + return(oact.sa_handler); +} + + +static void sys_sigblock(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL); +} + +static void sys_sigrelease(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, sig); + sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL); +} + +/* Child signal handler */ +void reap_children(int ignored) +{ + int res; + sys_sigblock(SIGCHLD); + for (;;) { + while ((res = waitpid((pid_t)-1, NULL, WNOHANG)) > 0) + ; + if (!(res < 0 && errno == EAGAIN)) { + DEBUGF(4,("reap_children: res = %d, errno = %d.",res,errno)); + break; + } + } + sys_sigrelease(SIGCHLD); +} + +static void init_signals(void) +{ + sys_sigset(SIGCHLD,&reap_children); /* SIG_IGN would give same result + on most (?) platforms. */ + sys_sigset(SIGPIPE, SIG_IGN); +} +#endif + +static void stall_worker(int ndx) +{ + --num_busy_workers; + stalled_workers[num_stalled_workers] = busy_workers[ndx]; + stalled_workers[num_stalled_workers].state = WORKER_STALLED; + busy_workers[ndx] = busy_workers[num_busy_workers]; + DEBUGF(3, ("Stalled worker[%ld]", + (long) stalled_workers[num_stalled_workers].pid)); + ++num_stalled_workers; +} + + +/* + * Main loop message passing + */ +#ifndef WIN32 +static int read_request(AddrByte **buff, size_t *buff_size) +{ + int siz; + int r; + + if ((r = READ_PACKET_BYTES(0,&siz)) != PACKET_BYTES) { + if (r == 0) { + return 0; + } else { + fatal("Unexpected end of file on main input, errno = %d",errno); + } + } + + if (siz > *buff_size) { + if (buff_size == 0) { + *buff = ALLOC((*buff_size = siz)); + } else { + *buff = REALLOC(*buff, (*buff_size = siz)); + } + } + if (read_exact(0,*buff, siz) != siz) { + fatal("Unexpected end of file on main input, errno = %d",errno); + } + if (siz < 5) { + fatal("Unexpected message on main input, message size %d less " + "than minimum."); + } + return siz; +} + +#endif /* !WIN32 */ + +static OpType get_op(AddrByte *buff) +{ + return (OpType) buff[4]; +} + +static AddrByte *get_op_addr(AddrByte *buff) +{ + return buff + 4; +} + +static SerialType get_serial(AddrByte *buff) +{ + return get_int32(buff); +} + +static ProtoType get_proto(AddrByte *buff) +{ + return (ProtoType) buff[5]; +} + +static CtlType get_ctl(AddrByte *buff) +{ + return (CtlType) buff[5]; +} + +static AddrByte *get_data(AddrByte *buff) +{ + return buff + 6; +} + +static int get_debug_level(AddrByte *buff) +{ + return get_int32(buff + 6); +} + +#ifdef WIN32 +static int send_mes_to_worker(QueItem *m, Worker *pw) +{ + if (!enque_mesq(pw->writeto, m)) { + warning("Unable to send to child process."); + return -1; + } + return 0; +} +#else +static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw) +{ + AddrByte hdr[PACKET_BYTES]; + + PUT_PACKET_BYTES(hdr, rsize); + if (write_exact(pw->writeto, hdr, PACKET_BYTES) < 0) { + warning("Unable to write to child process."); + return -1; + } + if (write_exact(pw->writeto, (AddrByte *) pr, rsize) < 0) { + warning("Unable to write to child process."); + return -1; + } + return 0; +} +#endif /* !WIN32 */ + +#ifdef WIN32 +static int relay_reply(Worker *pw) +{ + QueItem *m; + if (!deque_mesq(pw->readfrom,&m)) { + return 0; + } + if (!enque_mesq(to_erlang,m)) { + FREE(m); + return 0; + } + return 1; +} + +static int ignore_reply(Worker *pw) { + QueItem *m; + if (!deque_mesq(pw->readfrom,&m)) { + return 0; + } + FREE(m); + return 1; +} + +#else + +/* Static buffers used by the next three functions */ +static AddrByte *relay_buff = NULL; +static int relay_buff_size = 0; + +static int fillin_reply(Worker *pw) +{ + int length; + + if (READ_PACKET_BYTES(pw->readfrom, &length) != PACKET_BYTES) { + warning("Malformed reply (header) from worker process %d.", + pw->pid); + return -1; + } + + if (relay_buff_size < (length + PACKET_BYTES)) { + if (!relay_buff_size) { + relay_buff = + ALLOC((relay_buff_size = (length + PACKET_BYTES))); + } else { + relay_buff = + REALLOC(relay_buff, + (relay_buff_size = (length + PACKET_BYTES))); + } + } + PUT_PACKET_BYTES(relay_buff, length); + if (read_exact(pw->readfrom, relay_buff + PACKET_BYTES, length) != + length) { + warning("Malformed reply (data) from worker process %d.", pw->pid); + return -1; + } + return length; +} + +static int relay_reply(Worker *pw) +{ + int length = fillin_reply(pw); /* Filled into the "global" buffer */ + int res; + + if (length < 0) { + return -1; + } + if ((res = write_exact(1, relay_buff, length + PACKET_BYTES)) < 0) { + fatal("Cannot write reply to erlang process, errno = %d.", errno); + } else if (res == 0) { + DEBUGF(1,("Erlang has closed write pipe.")); + return 0; + } + return length; +} + +static int ignore_reply(Worker *pw) +{ + return fillin_reply(pw); +} + +#endif /* !WIN32 */ + +/* + * Domain name "parsing" and worker specific queing + */ +static void domaincopy(AddrByte *out, AddrByte *in) +{ + AddrByte *ptr = out; + *ptr++ = *in++; + *ptr++ = *in++; + switch(*out) { + case OP_GETHOSTBYNAME: + while(*in != '\0' && *in != '.') + ++in; + strncpy((char*)ptr, (char*)in, DOMAINNAME_MAX-2); + ptr[DOMAINNAME_MAX-3] = '\0'; + DEBUGF(4,("Saved domainname %s.", ptr)); + return; + case OP_GETHOSTBYADDR: + memcpy(ptr,in, ((out[1] == PROTO_IPV4) ? UNIT_IPV4 : UNIT_IPV6) - 1); + DEBUGF(4, ("Saved domain address: %s.", + format_address(((out[1] == PROTO_IPV4) ? + UNIT_IPV4 : UNIT_IPV6) - 1,ptr))); + return; + default: + fatal("Trying to copy buffer not containing valid domain, [%d,%d].", + (int) out[0], (int) out[1]); + } +} + +static int domaineq(AddrByte *d1, AddrByte *d2) +{ + if (d1[0] != d2[0] || d1[1] != d2[1]) { + return 0; + } + switch (d1[0]) { + case OP_GETHOSTBYNAME: + return !strcmp((char*)d1+2,(char*)d2+2); + case OP_GETHOSTBYADDR: + return !memcmp(d1+2,d2+2, ((d1[1] == PROTO_IPV4) + ? UNIT_IPV4 : UNIT_IPV6) - 1); + default: + fatal("Trying to compare buffers not containing valid domain, " + "[%d,%d].", + (int) d1[0], (int) d1[1]); + return -1; /* Lint... */ + } +} + +static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff) +{ + OpType op = get_op(inbuff); + ProtoType proto; + int i; + AddrByte *data; + + data = get_data(inbuff); + switch (op) { + case OP_GETHOSTBYNAME: + data = get_data(inbuff); + for (i = (data - inbuff); i < insize && inbuff[i] != '\0'; ++i) + ; + if (i < insize) { + domaincopy(domainbuff, get_op_addr(inbuff)); + return 0; + } + DEBUGF(3, ("Could not pick valid domainname in " + "gethostbyname operation")); + return -1; + case OP_GETHOSTBYADDR: + proto = get_proto(inbuff); + i = insize - (data - inbuff); + if ((proto == PROTO_IPV4 && i == UNIT_IPV4) || + (proto == PROTO_IPV6 && i == UNIT_IPV6)) { + /* An address buffer */ + domaincopy(domainbuff, get_op_addr(inbuff)); + return 0; + } + DEBUGF(3, ("Could not pick valid domainname in gethostbyaddr " + "operation")); + return -1; + default: + DEBUGF(2, ("Could not pick valid domainname because of " + "invalid opcode %d.", (int) op)); + return -1; + } +} + +/* + * Worker subprocesses with utilities + */ +#ifdef WIN32 +static int create_worker(Worker *pworker, int save_que) +{ + MesQ **thread_data = ALLOC(2*sizeof(MesQ *)); + DWORD tid; + + + if (!create_mesq(thread_data)) { + fatal("Could not create, pipes for subprocess, errno = %d", + GetLastError()); + } + if (!create_mesq(thread_data + 1)) { + fatal("Could not create, pipes for subprocess, errno = %d", + GetLastError()); + } + /* Save those before the thread starts */ + pworker->writeto = thread_data[0]; + pworker->readfrom = thread_data[1]; + + if (((HANDLE) _beginthreadex(NULL, 0, worker_loop, thread_data, 0, &tid)) + == NULL) { + fatal("Could not create thread errno = %d", + GetLastError()); + } + pworker->pid = tid; + pworker->state = WORKER_FREE; + pworker->serial = INVALID_SERIAL; + if (!save_que) { + pworker->que_first = pworker->que_last = NULL; + pworker->que_size = 0; + } + DEBUGF(3,("Created worker[%ld] with fd %d", + (long) pworker->pid, (int) pworker->readfrom)); + return 0; +} + +#else + +static int create_worker(Worker *pworker, int save_que) +{ + int p0[2], p1[2]; + pid_t child; + + if (pipe(p0)) { + warning("Could not create, pipes for subprocess, errno = %d", + errno); + return -1; + } + + if (pipe(p1)) { + warning("Could not create, pipes for subprocess, errno = %d", + errno); + close(p0[0]); + close(p0[1]); + return -1; + } + if ((child = fork()) < 0) { /* failure */ + warning("Could not fork(), errno = %d", + errno); + close(p0[0]); + close(p0[1]); + close(p1[0]); + close(p1[1]); + return -1; + } else if (child > 0) { /* parent */ + close(p0[1]); + close(p1[0]); + pworker->writeto = p1[1]; + pworker->readfrom = p0[0]; + pworker->pid = child; + pworker->state = WORKER_FREE; + pworker->serial = INVALID_SERIAL; + if (!save_que) { + pworker->que_first = pworker->que_last = NULL; + pworker->que_size = 0; + } + DEBUGF(3,("Created worker[%ld] with fd %d", + (long) pworker->pid, (int) pworker->readfrom)); + return 0; + } else { /* child */ + close(p1[1]); + close(p0[0]); + close_all_worker_fds(); + /* Make "fatal" not find any children */ + num_busy_workers = num_free_workers = num_stalled_workers = 0; + if((dup2(p1[0],0) < 0) || (dup2(p0[1],1) < 0)) { + fatal("Worker could not dup2(), errno = %d", + errno); + return -1; /* lint... */ + } + close(p1[0]); + close(p0[1]); + signal(SIGCHLD, SIG_IGN); + return worker_loop(); + } +} + +static void close_all_worker_fds(void) +{ + int w,i; + Worker *workers[3] = {free_workers, busy_workers, stalled_workers}; + int wsizes[3] = {num_free_workers, num_busy_workers, + num_stalled_workers}; + for (w = 0; w < 3; ++w) { + for (i = 0; i < wsizes[w]; ++i) { + if (workers[w][i].state != WORKER_EMPTY) { + close(workers[w][i].readfrom); + close(workers[w][i].writeto); + } + } + } +} + +#endif /* !WIN32 */ + +#ifdef WIN32 +DWORD WINAPI worker_loop(void *v) +#else +static int worker_loop(void) +#endif +{ + AddrByte *req = NULL; + size_t req_size = 0; + int this_size; + AddrByte *reply = NULL; + size_t reply_size = 0; + size_t data_size; + +#ifdef WIN32 + QueItem *m = NULL; + MesQ *readfrom = ((MesQ **) v)[0]; + MesQ *writeto = ((MesQ **) v)[1]; + /* XXX:PaN */ + FREE(v); +#endif + + for(;;) { +#ifdef HAVE_GETADDRINFO + struct addrinfo *ai = NULL; +#endif + struct hostent *he = NULL; +#ifdef HAVE_GETNAMEINFO + struct sockaddr *sa = NULL; + char name[NI_MAXHOST]; +#endif +#if defined(HAVE_GETIPNODEBYNAME) || defined(HAVE_GETIPNODEBYADDR) + int free_he = 0; +#endif + int error_num = 0; + SerialType serial; + OpType op; + ProtoType proto; + AddrByte *data; + +#ifdef WIN32 + WaitForSingleObject(event_mesq(readfrom),INFINITE); + DEBUGF(4,("Worker got data on message que.")); + + if(!deque_mesq(readfrom,&m)) { + goto fail; + } + this_size = m->req_size; + req = m->request; +#else + if (READ_PACKET_BYTES(0,&this_size) != PACKET_BYTES) { + DEBUGF(2,("Worker got error/EOF while reading size, exiting.")); + exit(0); + } + if (this_size > req_size) { + if (req == NULL) { + req = ALLOC((req_size = this_size)); + } else { + req = REALLOC(req, (req_size = this_size)); + } + } + if (read_exact(0, req, (size_t) this_size) != this_size) { + DEBUGF(1,("Worker got EOF while reading data, exiting.")); + exit(0); + } +#endif + /* Decode the request... */ + serial = get_serial(req); + if (OP_CONTROL == (op = get_op(req))) { + CtlType ctl; + if (serial != INVALID_SERIAL) { + DEBUGF(1, ("Worker got invalid serial: %d.", serial)); + exit(0); + } + switch (ctl = get_ctl(req)) { + case SETOPT_DEBUG_LEVEL: + debug_level = get_debug_level(req); + DEBUGF(debug_level, + ("Worker debug_level = %d.", debug_level)); + break; + } + continue; + } + proto = get_proto(req); + data = get_data(req); + DEBUGF(4,("Worker got request, op = %d, proto = %d, data = %s.", + op,proto,data)); + /* Got a request, lets go... */ + switch (op) { + case OP_GETHOSTBYNAME: + switch (proto) { + +#ifdef HAVE_IN6 + case PROTO_IPV6: { /* switch (proto) { */ +#ifdef HAVE_GETADDRINFO + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = (AI_CANONNAME|AI_V4MAPPED|AI_ADDRCONFIG); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_INET6; + DEBUGF(5, ("Starting getaddrinfo(%s, ...)", data)); + error_num = getaddrinfo((char *)data, NULL, &hints, &ai); + DEBUGF(5,("getaddrinfo returned %d", error_num)); + if (error_num) { + error_num = map_netdb_error_ai(error_num); + } +#elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */ + DEBUGF(5,("Starting getipnodebyname(%s)",data)); + he = getipnodebyname(data, AF_INET6, AI_DEFAULT, &error_num); + if (he) { + free_he = 1; + error_num = 0; + DEBUGF(5,("getipnodebyname(,AF_INET6,,) OK")); + } else { + DEBUGF(5,("getipnodebyname(,AF_INET6,,) error %d", error_num)); + error_num = map_netdb_error(error_num); + } +#elif defined(HAVE_GETHOSTBYNAME2) /*#ifdef HAVE_GETADDRINFO */ + DEBUGF(5,("Starting gethostbyname2(%s, AF_INET6)",data)); + he = gethostbyname2((char*)data, AF_INET6); + if (he) { + error_num = 0; + DEBUGF(5,("gethostbyname2(, AF_INET6) OK")); + } else { + error_num = map_netdb_error(h_errno); + DEBUGF(5,("gethostbyname2(, AF_INET6) error %d", h_errno)); + } +#else + error_num = ERRCODE_NOTSUP; +#endif /*#ifdef HAVE_GETADDRINFO */ + } break; +#endif /*ifdef HAVE_IN6 */ + + case PROTO_IPV4: { /* switch (proto) { */ + DEBUGF(5,("Starting gethostbyname(%s)",data)); + he = gethostbyname((char*)data); + if (he) { + error_num = 0; + DEBUGF(5,("gethostbyname OK")); + } else { + error_num = map_netdb_error(h_errno); + DEBUGF(5,("gethostbyname error %d", h_errno)); + } + } break; + + default: /* switch (proto) { */ + /* Not supported... */ + error_num = ERRCODE_NOTSUP; + break; + } /* switch (proto) { */ + + if (he) { + data_size = build_reply(serial, he, &reply, &reply_size); +#ifdef HAVE_GETIPNODEBYNAME + if (free_he) { + freehostent(he); + } +#endif +#ifdef HAVE_GETADDRINFO + } else if (ai) { + data_size = build_reply_ai(serial, 16, ai, + &reply, &reply_size); + freeaddrinfo(ai); +#endif + } else { + data_size = build_error_reply(serial, error_num, + &reply, &reply_size); + } + break; /* case OP_GETHOSTBYNAME: */ + + case OP_GETHOSTBYADDR: /* switch (op) { */ + switch (proto) { +#ifdef HAVE_IN6 + case PROTO_IPV6: { +#ifdef HAVE_GETNAMEINFO + struct sockaddr_in6 *sin; + socklen_t salen = sizeof(*sin); + + sin = ALLOC(salen); +#ifndef NO_SA_LEN + sin->sin6_len = salen; +#endif + sin->sin6_family = AF_INET6; + sin->sin6_port = 0; + memcpy(&sin->sin6_addr, data, 16); + sa = (struct sockaddr *)sin; + DEBUGF(5,("Starting getnameinfo(,,%s,16,,,)", + format_address(16, data))); + error_num = getnameinfo(sa, salen, name, sizeof(name), + NULL, 0, NI_NAMEREQD); + DEBUGF(5,("getnameinfo returned %d", error_num)); + if (error_num) { + error_num = map_netdb_error_ai(error_num); + sa = NULL; + } +#elif defined(HAVE_GETIPNODEBYADDR) /*#ifdef HAVE_GETNAMEINFO*/ + struct in6_addr ia; + memcpy(ia.s6_addr, data, 16); + DEBUGF(5,("Starting getipnodebyaddr(%s,16,AF_INET6,)", + format_address(16, data))); + he = getipnodebyaddr(&ia, 16, AF_INET6, &error_num); + free_he = 1; + if (! he) { + DEBUGF(5,("getipnodebyaddr error %d", error_num)); + error_num = map_netdb_error(error_num); + } else { + DEBUGF(5,("getipnodebyaddr OK")); + } +#else /*#ifdef HAVE_GETNAMEINFO*/ + struct in6_addr ia; + memcpy(ia.s6_addr, data, 16); + DEBUGF(5,("Starting gethostbyaddr(%s,16,AF_INET6)", + format_address(16, data))); + he = gethostbyaddr((const char *) &ia, 16, AF_INET6); + if (! he) { + error_num = map_netdb_error(h_errno); + DEBUGF(5,("gethostbyaddr error %d", h_errno)); + } else { + DEBUGF(5,("gethostbyaddr OK")); + } +#endif /* #ifdef HAVE_GETNAMEINFO */ + } break; /* case PROTO_IPV6: { */ +#endif /* #ifdef HAVE_IN6 */ + + case PROTO_IPV4: { /* switch(proto) { */ + struct in_addr ia; + memcpy(&ia.s_addr, data, 4); /* Alignment required... */ + DEBUGF(5,("Starting gethostbyaddr(%s,4,AF_INET)", + format_address(4, data))); + he = gethostbyaddr((const char *) &ia, 4, AF_INET); + if (! he) { + error_num = map_netdb_error(h_errno); + DEBUGF(5,("gethostbyaddr error %d", h_errno)); + } else { + DEBUGF(5,("gethostbyaddr OK")); + } + } break; + + default: + error_num = ERRCODE_NOTSUP; + } /* switch(proto) { */ + + if (he) { + data_size = build_reply(serial, he, &reply, &reply_size); +#ifdef HAVE_GETIPNODEBYADDR + if (free_he) { + freehostent(he); + } +#endif +#ifdef HAVE_GETNAMEINFO + } else if (sa) { + struct addrinfo res; + memset(&res, 0, sizeof(res)); + res.ai_canonname = name; + res.ai_addr = sa; + res.ai_next = NULL; + data_size = build_reply_ai(serial, 16, &res, + &reply, &reply_size); + free(sa); +#endif + } else { + data_size = build_error_reply(serial, error_num, + &reply, &reply_size); + } + break; /* case OP_GETHOSTBYADR: */ + + default: + data_size = build_error_reply(serial, ERRCODE_NOTSUP, + &reply, &reply_size); + break; + } /* switch (op) { */ + +#ifdef WIN32 + m = REALLOC(m, sizeof(QueItem) - 1 + data_size - PACKET_BYTES); + m->next = NULL; + m->req_size = data_size - PACKET_BYTES; + memcpy(m->request,reply + PACKET_BYTES,data_size - PACKET_BYTES); + if (!enque_mesq(writeto,m)) { + goto fail; + } + m = NULL; +#else + write(1, reply, data_size); /* No signals expected */ +#endif + } /* for (;;) */ + +#ifdef WIN32 + fail: + if (m != NULL) { + FREE(m); + } + close_mesq(readfrom); + close_mesq(writeto); + if (reply) { + FREE(reply); + } + return 1; +#endif +} + +static int map_netdb_error(int netdb_code) +{ + switch (netdb_code) { +#ifdef HOST_NOT_FOUND + case HOST_NOT_FOUND: + return ERRCODE_HOST_NOT_FOUND; +#endif +#ifdef TRY_AGAIN + case TRY_AGAIN: + return ERRCODE_TRY_AGAIN; +#endif +#ifdef NO_RECOVERY + case NO_RECOVERY: + return ERRCODE_NO_RECOVERY; +#endif +#if defined(NO_DATA) || defined(NO_ADDRESS) +#ifdef NO_DATA + case NO_DATA: +#endif +#ifdef NO_ADDRESS +#if !defined(NO_DATA) || (NO_DATA != NO_ADDRESS) + case NO_ADDRESS: +#endif +#endif + return ERRCODE_NO_DATA; +#endif + default: + return ERRCODE_NETDB_INTERNAL; + } +} + +#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) +static int map_netdb_error_ai(int netdb_code) +{ + switch(netdb_code) { +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + return ERRCODE_NETDB_INTERNAL; +#endif + case EAI_AGAIN: + return ERRCODE_TRY_AGAIN; + case EAI_BADFLAGS: + return ERRCODE_NETDB_INTERNAL; + case EAI_FAIL: + return ERRCODE_HOST_NOT_FOUND; + case EAI_FAMILY: + return ERRCODE_NETDB_INTERNAL; + case EAI_MEMORY: + return ERRCODE_NETDB_INTERNAL; +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: + return ERRCODE_HOST_NOT_FOUND; +#endif + case EAI_NONAME: + return ERRCODE_HOST_NOT_FOUND; + case EAI_SERVICE: + return ERRCODE_NETDB_INTERNAL; + case EAI_SOCKTYPE: + return ERRCODE_NETDB_INTERNAL; + default: + return ERRCODE_NETDB_INTERNAL; + } +} +#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */ + + +static char *errcode_to_string(int errcode) +{ + switch (errcode) { + case ERRCODE_NOTSUP: + return "enotsup"; + case ERRCODE_HOST_NOT_FOUND: + /* + * I would preffer + * return "host_not_found"; + * but have to keep compatibility with the old + * inet_gethost's error codes... + */ + return "notfound"; + case ERRCODE_TRY_AGAIN: + return "try_again"; + case ERRCODE_NO_RECOVERY: + return "no_recovery"; + case ERRCODE_NO_DATA: + return "no_data"; + default: + /*case ERRCODE_NETDB_INTERNAL:*/ + return "netdb_internal"; + } +} + +static size_t build_error_reply(SerialType serial, int errnum, + AddrByte **preply, + size_t *preply_size) +{ + char *errstring = errcode_to_string(errnum); + int string_need = strlen(errstring) + 1; /* a '\0' too */ + unsigned need; + AddrByte *ptr; + + need = PACKET_BYTES + 4 /* Serial */ + 1 /* Unit */ + string_need; + if (*preply_size < need) { + if (*preply_size == 0) { + *preply = ALLOC((*preply_size = need)); + } else { + *preply = REALLOC(*preply, + (*preply_size = need)); + } + } + ptr = *preply; + PUT_PACKET_BYTES(ptr,need - PACKET_BYTES); + ptr += PACKET_BYTES; + put_int32(ptr,serial); + ptr +=4; + *ptr++ = (AddrByte) 0; /* 4 or 16 */ + strcpy((char*)ptr, errstring); + return need; +} + + + +static size_t build_reply(SerialType serial, struct hostent *he, + AddrByte **preply, size_t *preply_size) +{ + unsigned need; + int strings_need; + int num_strings; + int num_addresses; + int i; + AddrByte *ptr; + int unit = he->h_length; + + for (num_addresses = 0; he->h_addr_list[num_addresses] != NULL; + ++num_addresses) + ; + strings_need = strlen(he->h_name) + 1; /* 1 for null byte */ + num_strings = 1; + if (he->h_aliases) { + for(i=0; he->h_aliases[i] != NULL; ++i) { + strings_need += strlen(he->h_aliases[i]) + 1; + ++num_strings; + } + } + + need = PACKET_BYTES + + 4 /* Serial */ + 1 /* Unit */ + 4 /* Naddr */ + + (unit * num_addresses) /* Address bytes */ + + 4 /* Nnames */ + strings_need /* The name and alias strings */; + + if (*preply_size < need) { + if (*preply_size == 0) { + *preply = ALLOC((*preply_size = need)); + } else { + *preply = REALLOC(*preply, + (*preply_size = need)); + } + } + ptr = *preply; + PUT_PACKET_BYTES(ptr,need - PACKET_BYTES); + ptr += PACKET_BYTES; + put_int32(ptr,serial); + ptr +=4; + *ptr++ = (AddrByte) unit; /* 4 or 16 */ + put_int32(ptr, num_addresses); + ptr += 4; + for (i = 0; i < num_addresses; ++i) { + memcpy(ptr, he->h_addr_list[i], unit); + ptr += unit; + } + put_int32(ptr, num_strings); + ptr += 4; + strcpy((char*)ptr, he->h_name); + ptr += 1 + strlen(he->h_name); + for (i = 0; i < (num_strings - 1); ++i) { + strcpy((char*)ptr, he->h_aliases[i]); + ptr += 1 + strlen(he->h_aliases[i]); + } + return need; +} + +#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) +static size_t build_reply_ai(SerialType serial, int addrlen, + struct addrinfo *res0, + AddrByte **preply, size_t *preply_size) +{ + struct addrinfo *res; + int num_strings; + int num_addresses; + AddrByte *ptr; + int need; + + num_addresses = 0; + num_strings = 0; + need = PACKET_BYTES + + 4 /* Serial */ + 1 /* addrlen */ + + 4 /* Naddr */ + 4 /* Nnames */; + + for (res = res0; res != NULL; res = res->ai_next) { + if (res->ai_addr) { + num_addresses++; + need += addrlen; + } + if (res->ai_canonname) { + num_strings++; + need += strlen(res->ai_canonname) + 1; + } + } + + if (*preply_size < need) { + if (*preply_size == 0) { + *preply = ALLOC((*preply_size = need)); + } else { + *preply = REALLOC(*preply, + (*preply_size = need)); + } + } + + ptr = *preply; + PUT_PACKET_BYTES(ptr,need - PACKET_BYTES); + ptr += PACKET_BYTES; + put_int32(ptr,serial); + ptr +=4; + *ptr++ = (AddrByte) addrlen; /* 4 or 16 */ + put_int32(ptr, num_addresses); + ptr += 4; + for (res = res0; res != NULL && num_addresses; res = res->ai_next) { + if (res->ai_addr == NULL) + continue; + if (addrlen == 4) + memcpy(ptr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addrlen); +#ifdef AF_INET6 + else if (addrlen == 16) + memcpy(ptr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrlen); +#endif + else + memcpy(ptr, res->ai_addr->sa_data, addrlen); + ptr += addrlen; + num_addresses--; + } + put_int32(ptr, num_strings); + ptr += 4; + for (res = res0; res != NULL && num_strings; res = res->ai_next) { + if (res->ai_canonname == NULL) + continue; + strcpy((char *)ptr, res->ai_canonname); + ptr += strlen(res->ai_canonname) + 1; + num_strings--; + } + return need; +} + +#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */ + + + +/* + * Encode/decode/read/write + */ + +static int get_int32(AddrByte *b) +{ + int res; + res = (unsigned) b[3]; + res |= ((unsigned) b[2]) << 8; + res |= ((unsigned) b[1]) << 16; + res |= ((unsigned) b[0]) << 24; + return res; +} + +static void put_int32(AddrByte *buff, int value) +{ + buff[0] = (((unsigned) value) >> 24) & 0xFF; + buff[1] = (((unsigned) value) >> 16) & 0xFF; + buff[2] = (((unsigned) value) >> 8) & 0xFF; + buff[3] = ((unsigned) value) & 0xFF; +} +#ifdef WIN32 + +static int read_int32(HANDLE fd, int *res, HANDLE ev) +{ + AddrByte b[4]; + int r; + if ((r = read_exact(fd,b,4,ev)) < 0) { + return -1; + } else if (r == 0) { + return 0; + } else { + *res = (unsigned) b[3]; + *res |= ((unsigned) b[2]) << 8; + *res |= ((unsigned) b[1]) << 16; + *res |= ((unsigned) b[0]) << 24; + } + return 4; +} +/* + * The standard input is expected to be opened with FILE_FLAG_OVERLAPPED + * but this code should handle both cases (although winsock might not). + */ +static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev) +{ + DWORD ret,got; + BOOL stat; + char *buff = vbuff; + OVERLAPPED ov; + DWORD err; + + + got = 0; + for(;;) { + memset(&ov,0,sizeof(ov)); + ov.hEvent = ev; + ResetEvent(ov.hEvent); + stat = ReadFile(fd, buff, nbytes - got, &ret, &ov); + if (!stat) { + if ((err = GetLastError()) == ERROR_IO_PENDING) { + DEBUGF(4,("Overlapped read, waiting for completion...")); + WaitForSingleObject(ov.hEvent,INFINITE); + stat = GetOverlappedResult(fd,&ov,&ret,TRUE); + DEBUGF(4,("Overlapped read, completed with status %d," + " result %d",stat,ret)); + } + if (!stat) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + DEBUGF(1, ("End of file while reading from pipe.")); + return 0; + } else { + DEBUGF(1, ("Error while reading from pipe," + " errno = %d", + GetLastError())); + return -1; + } + } + } else { + DEBUGF(4,("Read completed syncronously, result %d",ret)); + } + if (ret == 0) { + DEBUGF(1, ("End of file detected as zero read from pipe.")); + return 0; + } + if (ret < nbytes - got) { + DEBUGF(4,("Not all data read from pipe, still %d bytes to read.", + nbytes - (got + ret))); + got += ret; + buff += ret; + } else { + return nbytes; + } + } +} +/* + * Now, we actually expect a HANDLE opened with FILE_FLAG_OVERLAPPED, + * but this code should handle both cases (although winsock + * does not always..) + */ +static int write_exact(HANDLE fd, AddrByte *buff, DWORD len, HANDLE ev) +{ + DWORD res,stat; + DWORD x = len; + OVERLAPPED ov; + DWORD err; + + + for(;;) { + memset(&ov,0,sizeof(ov)); + ov.hEvent = ev; + ResetEvent(ov.hEvent); + stat = WriteFile(fd,buff,x,&res,&ov); + if (!stat) { + if ((err = GetLastError()) == ERROR_IO_PENDING) { + DEBUGF(4,("Overlapped write, waiting for competion...")); + WaitForSingleObject(ov.hEvent,INFINITE); + stat = GetOverlappedResult(fd,&ov,&res,TRUE); + DEBUGF(4,("Overlapped write, completed with status %d," + " result %d",stat,res)); + } + if (!stat) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + return 0; + } else { + return -1; + } + } + } else { + DEBUGF(4,("Write completed syncronously, result %d",res)); + } + + if (res < x) { + /* Microsoft states this can happen as HANDLE is a pipe... */ + DEBUGF(4,("Not all data written to pipe, still %d bytes to write.", + x - res)); + x -= res; + buff += res; + } else { + return len; + } + } +} + +DWORD WINAPI reader(void *data) { + MesQ *mq = (MesQ *) data; + QueItem *m; + int siz; + int r; + HANDLE inp; + int x = 0; + HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL); + + inp = GetStdHandle(STD_INPUT_HANDLE); + for (;;) { + if ((r = READ_PACKET_BYTES(inp,&siz,ev)) != 4) { + DEBUGF(1,("Erlang has closed (reading)")); + exit(0); + } + DEBUGF(4,("Read packet of size %d from erlang",siz)); + m = ALLOC(sizeof(QueItem) - 1 + siz); + if (read_exact(inp, m->request, siz,ev) != siz) { + fatal("Unexpected end of file on main input, errno = %d",errno); + } + if (siz < 5) { + fatal("Unexpected message on main input, message size %d less " + "than minimum."); + } + m->req_size = siz; + m->next = NULL; + if (!enque_mesq(mq, m)) { + fatal("Reader could not talk to main thread!"); + } + } +} + +DWORD WINAPI writer(void *data) +{ + MesQ *mq = (MesQ *) data; + QueItem *m; + HANDLE outp = GetStdHandle(STD_OUTPUT_HANDLE); + AddrByte hdr[PACKET_BYTES]; + HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL); + + + for (;;) { + WaitForSingleObject(event_mesq(mq),INFINITE); + if (!deque_mesq(mq, &m)) { + fatal("Writer could not talk to main thread!"); + } + PUT_PACKET_BYTES(hdr, m->req_size); + if (write_exact(outp, hdr, 4, ev) != 4) { + DEBUGF(1,("Erlang has closed (writing)")); + exit(0); + } + if (write_exact(outp, m->request, m->req_size, ev) != m->req_size) { + DEBUGF(1,("Erlang has closed (writing)")); + exit(0); + } + FREE(m); + } +} + + +#else + +static size_t read_int32(int fd, int *res) +{ + AddrByte b[4]; + int r; + if ((r = read_exact(fd,b,4)) < 0) { + return -1; + } else if (r == 0) { + return 0; + } else { + *res = (unsigned) b[3]; + *res |= ((unsigned) b[2]) << 8; + *res |= ((unsigned) b[1]) << 16; + *res |= ((unsigned) b[0]) << 24; + } + return 4; +} + +static ssize_t read_exact(int fd, void *vbuff, size_t nbytes) +{ + ssize_t ret, got; + char *buff = vbuff; + + got = 0; + for(;;) { + ret = read(fd, buff, nbytes - got); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else { + DEBUGF(1, ("Error while reading from pipe," + " errno = %d", + errno)); + return -1; + } + } else if (ret == 0) { + DEBUGF(1, ("End of file while reading from pipe.")); + if (got == 0) { + return 0; /* "Normal" EOF */ + } else { + return -1; + } + } else if (ret < nbytes - got) { + got += ret; + buff += ret; + } else { + return nbytes; + } + } +} + +static int write_exact(int fd, AddrByte *buff, int len) +{ + int res; + int x = len; + for(;;) { + if((res = write(fd, buff, x)) == x) { + break; + } + if (res < 0) { + if (errno == EINTR) { + continue; + } else if (errno == EPIPE) { + return 0; + } +#ifdef ENXIO + else if (errno == ENXIO) { + return 0; + } +#endif + else { + return -1; + } + } else { + /* Hmmm, blocking write but not all written, could this happen + if the other end was closed during the operation? Well, + it costs very little to handle anyway... */ + x -= res; + buff += res; + } + } + return len; +} + +#endif /* !WIN32 */ + +/* + * Debug and memory allocation + */ + +static char *format_address(int siz, AddrByte *addr) +{ + static char buff[50]; + char tmp[10]; + if (siz > 16) { + return "(unknown)"; + } + *buff='\0'; + if (siz <= 4) { + while(siz--) { + sprintf(tmp,"%d",(int) *addr++); + strcat(buff,tmp); + if(siz) { + strcat(buff,"."); + } + } + return buff; + } + while(siz--) { + sprintf(tmp,"%02x",(int) *addr++); + strcat(buff,tmp); + if(siz) { + strcat(buff,":"); + } + } + return buff; +} + +static void debugf(char *format, ...) +{ + char buff[2048]; + char *ptr; + va_list ap; + + va_start(ap,format); +#ifdef WIN32 + sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId()); +#else + sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) getpid()); +#endif + ptr = buff + strlen(buff); + vsprintf(ptr,format,ap); + strcat(ptr,"\r\n"); +#ifdef WIN32 + if (debug_console_allocated != INVALID_HANDLE_VALUE) { + DWORD res; + WriteFile(debug_console_allocated,buff,strlen(buff),&res,NULL); + } +#else + write(2,buff,strlen(buff)); +#endif + va_end(ap); +} + +static void warning(char *format, ...) +{ + char buff[2048]; + char *ptr; + va_list ap; + + va_start(ap,format); + sprintf(buff,"%s[%d]: WARNING:",program_name, (int) getpid()); + ptr = buff + strlen(buff); + vsprintf(ptr,format,ap); + strcat(ptr,"\r\n"); +#ifdef WIN32 + { + DWORD res; + WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL); + } +#else + write(2,buff,strlen(buff)); +#endif + va_end(ap); +} + +static void fatal(char *format, ...) +{ + char buff[2048]; + char *ptr; + va_list ap; + + va_start(ap,format); + sprintf(buff,"%s[%d]: FATAL ERROR:",program_name, (int) getpid()); + ptr = buff + strlen(buff); + vsprintf(ptr,format,ap); + strcat(ptr,"\r\n"); +#ifdef WIN32 + { + DWORD res; + WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL); + } +#else + write(2,buff,strlen(buff)); +#endif + va_end(ap); +#ifndef WIN32 + kill_all_workers(); +#endif + exit(1); +} + +static void *my_malloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) { + fatal("Cannot allocate %u bytes of memory.", (unsigned) size); + return NULL; /* lint... */ + } + return ptr; +} + +static void *my_realloc(void *old, size_t size) +{ + void *ptr = realloc(old, size); + if (!ptr) { + fatal("Cannot reallocate %u bytes of memory from %p.", + (unsigned) size, old); + return NULL; /* lint... */ + } + return ptr; +} + +#ifdef WIN32 + +BOOL create_mesq(MesQ **q) +{ + MesQ *tmp = malloc(sizeof(MesQ)); + tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL); + if (tmp->data_present == NULL) { + free(tmp); + return FALSE; + } + InitializeCriticalSection(&(tmp->crit)); /* Cannot fail */ + tmp->shutdown = 0; + tmp->first = NULL; + tmp->last = NULL; + *q = tmp; + return TRUE; +} + +BOOL enque_mesq(MesQ *q, QueItem *m) +{ + EnterCriticalSection(&(q->crit)); + if (q->shutdown) { + LeaveCriticalSection(&(q->crit)); + return FALSE; + } + if (q->last == NULL) { + q->first = q->last = m; + } else { + q->last->next = m; + q->last = m; + } + m->next = NULL; + if (!SetEvent(q->data_present)) { + fprintf(stderr,"Fatal: Unable to signal event in %s:%d, last error: %d\n", + __FILE__,__LINE__,GetLastError()); + exit(1); /* Unable to continue at all */ + } + LeaveCriticalSection(&(q->crit)); + return TRUE; +} + +BOOL deque_mesq(MesQ *q, QueItem **m) +{ + EnterCriticalSection(&(q->crit)); + if (q->first == NULL) { /* Usually shutdown from other end */ + ResetEvent(q->data_present); + LeaveCriticalSection(&(q->crit)); + return FALSE; + } + *m = q->first; + q->first = q->first->next; + if (q->first == NULL) { + q->last = NULL; + ResetEvent(q->data_present); + } + (*m)->next = NULL; + LeaveCriticalSection(&(q->crit)); + return TRUE; +} + +BOOL close_mesq(MesQ *q) +{ + QueItem *tmp; + EnterCriticalSection(&(q->crit)); + if (!q->shutdown) { + q->shutdown = TRUE; + if (!SetEvent(q->data_present)) { + fprintf(stderr, + "Fatal: Unable to signal event in %s:%d, last error: %d\n", + __FILE__,__LINE__,GetLastError()); + exit(1); /* Unable to continue at all */ + } + LeaveCriticalSection(&(q->crit)); + return FALSE; + } + /* Noone else is supposed to use this object any more */ + LeaveCriticalSection(&(q->crit)); + DeleteCriticalSection(&(q->crit)); + CloseHandle(q->data_present); + tmp = q->first; + while(tmp) { + q->first = q->first->next; + free(tmp); + tmp = q->first; + } + free(q); + return TRUE; +} + +HANDLE event_mesq(MesQ *q) +{ + return q->data_present; +} + +#ifdef HARDDEBUG +DWORD WINAPI pseudo_worker_loop(void *v) +{ + HOSTENT *hep; + + DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") starting")); + hep = gethostbyname("ftp.funet.fi"); + + DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") -> %d OK",(int) hep)); + return 0; +} + +static void poll_gethost(int row) { + HANDLE h; + DWORD tid; + h = (HANDLE) _beginthreadex(NULL, 0, pseudo_worker_loop, NULL, 0, &tid); + if (h == NULL) { + DEBUGF(1,("Failed to spawn pseudo worker (%d)...",row)); + } else { + DEBUGF(1,("Waiting for pseudo worker (%d)", row)); + WaitForSingleObject(h,INFINITE); + DEBUGF(1,("Done Waiting for pseudo worker (%d)", row)); + } +} +#endif + +#endif /* WIN32 */ diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c new file mode 100644 index 0000000000..c2567cb8b4 --- /dev/null +++ b/erts/etc/common/typer.c @@ -0,0 +1,416 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2006-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: Typer front-end. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#ifdef __WIN32__ +#include <winbase.h> +#endif + +#include <ctype.h> + +#define NO 0 +#define YES 1 + +#define ASIZE(a) (sizeof(a)/sizeof(a[0])) + +static int debug = 0; /* Bit flags for debug printouts. */ + +static char** eargv_base; /* Base of vector. */ +static char** eargv; /* First argument for erl. */ + +static int eargc; /* Number of arguments in eargv. */ + +#ifdef __WIN32__ +# define QUOTE(s) possibly_quote(s) +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +# define ERL_NAME "erl.exe" +#else +# define QUOTE(s) s +# define IS_DIRSEP(c) ((c) == '/') +# define ERL_NAME "erl" +#endif + +#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s) +#define PUSH(s) eargv[eargc++] = QUOTE(s) +#define PUSH2(s, t) PUSH(s); PUSH(t) +#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u) + +/* + * Local functions. + */ + +static void error(char* format, ...); +static char* emalloc(size_t size); +static char* strsave(char* string); +static void push_words(char* src); +static int run_erlang(char* name, char** argv); +static char* get_default_emulator(char* progname); +#ifdef __WIN32__ +static char* possibly_quote(char* arg); +#endif + +/* + * Supply a strerror() function if libc doesn't. + */ +#ifndef HAVE_STRERROR + +extern int sys_nerr; + +#ifndef SYS_ERRLIST_DECLARED +extern const char * const sys_errlist[]; +#endif /* !SYS_ERRLIST_DECLARED */ + +char *strerror(int errnum) +{ + static char *emsg[1024]; + + if (errnum != 0) { + if (errnum > 0 && errnum < sys_nerr) + sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]); + else + sprintf((char *) &emsg[0], "errnum = %d ", errnum); + } + else { + emsg[0] = '\0'; + } + return (char *) &emsg[0]; +} +#endif /* !HAVE_STRERROR */ + +int +main(int argc, char** argv) +{ + int eargv_size; + int eargc_base; /* How many arguments in the base of eargv. */ + char* emulator; + int need_shell = 0; + + emulator = get_default_emulator(argv[0]); + + /* + * Allocate the argv vector to be used for arguments to Erlang. + * Arrange for starting to pushing information in the middle of + * the array, to allow easy addition of commands in the beginning. + */ + + eargv_size = argc*4+100; + eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); + eargv = eargv_base; + eargc = 0; + push_words(emulator); + eargc_base = eargc; + eargv = eargv + eargv_size/2; + eargc = 0; + + /* + * Push initial arguments. + */ + + if (argc > 1 && strcmp(argv[1], "-smp") == 0) { + PUSH("-smpauto"); + argc--, argv++; + } + + PUSH("+B"); + PUSH2("-boot", "start_clean"); + PUSH3("-run", "typer", "start"); + PUSH("-extra"); + + /* + * Push everything except --shell. + */ + + while (argc > 1) { + if (strcmp(argv[1], "--shell") == 0) { + need_shell = 1; + } else { + PUSH(argv[1]); + } + argc--, argv++; + } + + if (!need_shell) { + UNSHIFT("-noinput"); + } + + /* + * Move up the commands for invoking the emulator and adjust eargv + * accordingly. + */ + + while (--eargc_base >= 0) { + UNSHIFT(eargv_base[eargc_base]); + } + + /* + * Invoke Erlang with the collected options. + */ + + PUSH(NULL); + return run_erlang(eargv[0], eargv); +} + +static void +push_words(char* src) +{ + char sbuf[1024]; + char* dst; + + dst = sbuf; + while ((*dst++ = *src++) != '\0') { + if (isspace((int)*src)) { + *dst = '\0'; + PUSH(strsave(sbuf)); + dst = sbuf; + do { + src++; + } while (isspace((int)*src)); + } + } + if (sbuf[0]) + PUSH(strsave(sbuf)); +} +#ifdef __WIN32__ +char *make_commandline(char **argv) +{ + static char *buff = NULL; + static int siz = 0; + int num = 0; + char **arg, *p; + + if (*argv == NULL) { + return ""; + } + for (arg = argv; *arg != NULL; ++arg) { + num += strlen(*arg)+1; + } + if (!siz) { + siz = num; + buff = malloc(siz*sizeof(char)); + } else if (siz < num) { + siz = num; + buff = realloc(buff,siz*sizeof(char)); + } + p = buff; + for (arg = argv; *arg != NULL; ++arg) { + strcpy(p,*arg); + p+=strlen(*arg); + *p++=' '; + } + *(--p) = '\0'; + + if (debug) { + printf("Processed commandline:%s\n",buff); + } + return buff; +} + +int my_spawnvp(char **argv) +{ + STARTUPINFO siStartInfo; + PROCESS_INFORMATION piProcInfo; + DWORD ec; + + memset(&siStartInfo,0,sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES; + siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags |= STARTF_USESHOWWINDOW; + + + if (!CreateProcess(NULL, + make_commandline(argv), + NULL, + NULL, + TRUE, + 0, + NULL, + NULL, + &siStartInfo, + &piProcInfo)) { + return -1; + } + CloseHandle(piProcInfo.hThread); + + WaitForSingleObject(piProcInfo.hProcess,INFINITE); + if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) { + return 0; + } + return (int) ec; +} +#endif /* __WIN32__ */ + + +static int +run_erlang(char* progname, char** argv) +{ +#ifdef __WIN32__ + int status; +#endif + + if (debug) { + int i = 0; + while (argv[i] != NULL) + printf(" %s", argv[i++]); + printf("\n"); + } + +#ifdef __WIN32__ + /* + * Alas, we must wait here for the program to finish. + * Otherwise, the shell from which we were executed will think + * we are finished and print a prompt and read keyboard input. + */ + + status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/; + if (status == -1) { + fprintf(stderr, "typer: Error executing '%s': %d", progname, + GetLastError()); + } + return status; +#else + execvp(progname, argv); + error("Error %d executing \'%s\'.", errno, progname); + return 2; +#endif +} + +static void +error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "typer: %s\n", sbuf); + exit(1); +} + +static char* +emalloc(size_t size) +{ + char *p = malloc(size); + if (p == NULL) + error("Insufficient memory"); + return p; +} + +static char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + +static char* +get_default_emulator(char* progname) +{ + char sbuf[MAXPATHLEN]; + char* s; + + strcpy(sbuf, progname); + for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { + if (IS_DIRSEP(*s)) { + strcpy(s+1, ERL_NAME); +#ifdef __WIN32__ + if (_access(sbuf, 0) != -1) { + return strsave(sbuf); + } +#else + if (access(sbuf, 1) != -1) { + return strsave(sbuf); + } +#endif + break; + } + } + return ERL_NAME; +} + +#ifdef __WIN32__ +static char* +possibly_quote(char* arg) +{ + int mustQuote = NO; + int n = 0; + char* s; + char* narg; + + if (arg == NULL) { + return arg; + } + + /* + * Scan the string to find out if it needs quoting and return + * the original argument if not. + */ + + for (s = arg; *s; s++, n++) { + switch(*s) { + case ' ': + mustQuote = YES; + continue; + case '"': + mustQuote = YES; + n++; + continue; + case '\\': + if(s[1] == '"') + n++; + continue; + default: + continue; + } + } + if (!mustQuote) { + return arg; + } + + /* + * Insert the quotes and put a backslash in front of every quote + * inside the string. + */ + + s = narg = emalloc(n+2+1); + for (*s++ = '"'; *arg; arg++, s++) { + if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) { + *s++ = '\\'; + } + *s = *arg; + } + if (s[-1] == '\\') { + *s++ ='\\'; + } + *s++ = '"'; + *s = '\0'; + return narg; +} +#endif /* __WIN32__ */ diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src new file mode 100644 index 0000000000..410a77d91c --- /dev/null +++ b/erts/etc/unix/Install.src @@ -0,0 +1,175 @@ +#!/bin/sh +# +# %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% +# +# Patch $ERL_ROOT/emulator/obj/Makefile.dist & make +# +# +start_option=query +unset cross +while [ $# -ne 0 ]; do + case $1 in + -minimal) start_option=minimal ;; + -sasl) start_option=sasl ;; + -cross) cross=yes ;; + *) ERL_ROOT=$1 ;; + esac + shift +done + +if [ -z "$cross" ] +then + TARGET_ERL_ROOT="$ERL_ROOT" +else + TARGET_ERL_ROOT="$ERL_ROOT" + ERL_ROOT=`pwd` +fi + +if [ -z "$ERL_ROOT" -o ! -d "$ERL_ROOT" ] +then + echo "Install: need ERL_ROOT directory as argument" + exit 1 +fi + +case ":$ERL_ROOT" in + :/*) + ;; + *) + echo "Install: need an absolute path to ERL_ROOT" + exit 1 + ;; +esac + +if [ ! -d "$ERL_ROOT/erts-%I_VSN%/bin" ] +then + echo "Install: The directory $ERL_ROOT/erts-%I_VSN%/bin does not exist" + echo " Bad location or erts module not un-tared" + exit 1 +fi + +if [ ! -d $ERL_ROOT/bin ] +then + mkdir $ERL_ROOT/bin +fi + +# +# Fetch target system. +# +SYS=`(uname -s) 2>/dev/null` || SYS=unknown +REL=`(uname -r) 2>/dev/null` || REL=unknown +case $SYS:$REL in + SunOS:5.*) + TARGET=sunos5 ;; + Linux:*) + TARGET=linux ;; + *) + TARGET="" ;; +esac + +cd $ERL_ROOT/erts-%I_VSN%/bin + +sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" erl.src > erl +chmod 755 erl + +# +# Create start file for embedded system use, +# +(cd $ERL_ROOT/erts-%I_VSN%/bin; + sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" start.src > start; + chmod 755 start) + +cd $ERL_ROOT/bin + +cp -p $ERL_ROOT/erts-%I_VSN%/bin/erl . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/erlc . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/dialyzer . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/typer . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/escript . + +# +# Set a soft link to epmd +# This should not be done for an embedded system! +# + +# Remove old links first. +if [ -h epmd ]; then + /bin/rm -f epmd +fi + +ln -s $TARGET_ERL_ROOT/erts-%I_VSN%/bin/epmd epmd + +cp -p $ERL_ROOT/erts-%I_VSN%/bin/run_erl . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/to_erl . +cp -p $ERL_ROOT/erts-%I_VSN%/bin/start . +sed -e "s;%EMU%;%EMULATOR%%EMULATOR_NUMBER%;" $ERL_ROOT/erts-%I_VSN%/bin/start_erl.src > start_erl +chmod 755 start_erl +echo "" + +echo %I_VSN% %I_SYSTEM_VSN% > $ERL_ROOT/releases/start_erl.data +sed -e "s;%ERL_ROOT%;$TARGET_ERL_ROOT;" $ERL_ROOT/releases/RELEASES.src > $ERL_ROOT/releases/RELEASES + +if [ "$start_option" = "query" ] +then + echo "Do you want to use a minimal system startup" + echo "instead of the SASL startup? (y/n) [n]: " | tr -d '\012' + read reply + case $reply in + [Yy]*) + start_option=minimal ;; + *) + start_option=sasl ;; + esac +fi + +case $start_option in + minimal) + Name=start_clean ;; + sasl) + Name=start_sasl ;; + *) + Name=start_sasl ;; +esac + +cp -p ../releases/%I_SYSTEM_VSN%/start_*.boot . +cp -p $Name.boot start.boot +cp -p ../releases/%I_SYSTEM_VSN%/$Name.script start.script + +# +# We always run ranlib unless Solaris/SunOS 5 +# but ignore failures. +# +if [ "X$TARGET" != "Xsunos5" -a -d $ERL_ROOT/usr/lib ]; then + cd $ERL_ROOT/usr/lib + for library in lib*.a + do + (ranlib $library) > /dev/null 2>&1 + done +fi + + +# +# Fixing the man pages +# + +if [ -d $ERL_ROOT/man ] +then + cd $ERL_ROOT + ./misc/format_man_pages $ERL_ROOT +fi + + diff --git a/erts/etc/unix/README b/erts/etc/unix/README new file mode 100644 index 0000000000..45b4aec2da --- /dev/null +++ b/erts/etc/unix/README @@ -0,0 +1,111 @@ + + %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% + +-------------------------------------------------------------------------------- +%sunos4 This is Erlang version %VERSION% for SunOS 4. +%solaris2 This is Erlang version %VERSION% for Solaris 2 (SunOS 5). +%isc32 This is Erlang version %VERSION% for Interactive UNIX. +%aix This is Erlang version %VERSION% for AIX. +%hpux This is Erlang version %VERSION% for HP-UX. +%osf This is Erlang version %VERSION% for OSF/1 (currently unsupported). +%linux This is Erlang version %VERSION% for Linux. +%qnx This is Erlang version %VERSION% for QNX. +%freebsd This is Erlang version %VERSION% for FreeBSD. + + +Installation +------------ + +Please refer to the "System Administrator's Guide" for a description +of how to install the Erlang system. Ultra-short summary for the +impatient: Run the 'Install' script in this directory and answer the +questions; defaults (if any) are given in square brackets [] at the +end of each question. + +Note that the Install script will terminate if it detects problems - +you will have to correct them and re-run the script. If everything +goes well, the last printout should be: + +Erlang installation sucessfully completed + +If it isn't, something went wrong - check the printouts to find out +what it was. + +%hpux Note: On HP-UX, it isn't possible to have per-manpage-tree 'whatis' +%hpux files. Thus, 'erl -man -k <subject>' will not work, and it isn't +%hpux recommended to integrate the Erlang man pages into /usr/lib/whatis +%hpux since (as mentioned in the "System Administrator's Guide") there are +%hpux some potential conflicts in naming with standard Unix man pages. +%hpux +%isc32 Note: The release currently includes several files with names longer +%isc32 than 14 characters - this means that you will have problems unpacking +%isc32 it in a standard Interactive S51K (or S52K) filesystem (which you've +%isc32 probably already noticed...). Furthermore, the Erlang filer makes no +%isc32 attempts to deal "intelligently" with such restrictions. The bottom +%isc32 line is that you have to install the Erlang system in an S5L (or +%isc32 possibly NFS) filesystem, unless you have found a way to make the +%isc32 Interactive system silently truncate filenames longer than 14 +%isc32 characters when using S5?K (if so, please tell us about it!). +%isc32 + +Overview of the files/directories in the system +----------------------------------------------- + +README - this file. + +RELNOTES - release notes. + +Install - the main installation script. + +bin - the directory where all code that is to be executed + directly by UNIX is placed during the installation. + +lib - a number of "bundles" included in the release - each + bundle lives in a subdirectory. Most of them are written + entirely in Erlang, but in some cases C programs are also + used (these are copied to the bin directory during + installation). The code server will automatically add the + appropriate directory for each bundle to the search path. + Some of the more noteworthy bundles: + std - this is the standard library, with modules such as + file, io, lists, etc. + compiler - the Erlang compiler (of course) + debugger - the Erlang debugger (ditto) + pxw - the "Primitive X Window interface", which perhaps + isn't so primitive anymore... + For further information on these and the other bundles, + please refer to the man pages. + +doc - The printed documentation in compressed PostScript format, + and some code examples. + +man - Manual pages, best accessed with 'erl -man' - there are + some conflicts with standard Unix manpages if you put + this directory in $MANPATH. + +emulator - The object code for the emulator itself is in the 'obj' + subdirectory, along with a simple Makefile and a couple + of source files that advanced users *may* be interested in + changing - care should be taken, of course, since any + changes may make the system non-functional. Refer to the + "System Adminstrator's Guide" and "Additional Features" + documents for some more information on this. + +misc - Some pieces that don't belong to any particular part of the + system - e.g. the new erl_interface package, and an Erlang + mode for emacs. diff --git a/erts/etc/unix/RELNOTES b/erts/etc/unix/RELNOTES new file mode 100644 index 0000000000..d1a110fce3 --- /dev/null +++ b/erts/etc/unix/RELNOTES @@ -0,0 +1,327 @@ + + %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% + +============================================================================= + +Release Notes Erlang 4.3.1, Stockholm, October 1995 + +This is a maintenance release correcting some minor problems in the 4.3 +release. The changes are mostly related to problems when using Erlang in +an distributed environment. For features and incompatibilities look in the +following description of the 4.3 release. + +-- If you already have the 4.3 release and run in an distributed environment + you should change all nodes to 4.3.1 since some changes could (at least + potentially) create problems/incompatibilities. (You ought to change + anyway due to bugs fixed...). + +============================================================================= + +Release Notes Erlang 4.3, Stockholm, June 1995 + +This is a list of the changes, fixes, and enhancements which have occurred +between the Erlang 4.2 release of March 1994, and the Erlang 4.3 release of +June 1995. There is also information on problems reported for 4.2 that still +remain in release 4.3. For a more detailed description of new or changed +functions, please refer to the respective manual pages, which are referred +to as man-page(3) or man-page(1). + +Erlang release 4.3 is basically the same system as release 4.2. +A large number of improvements and enhancements have, however, +occurred. The 4.3 system is largely source code compatible with +the 4.2 system, but there is a number of incompatibilities. + +*** Major Improvements +------------------------------------------------------------------ + +-- The system is considerably faster and smaller. + A fully networked system now requires less than a Megabyte + of memory to start. + +-- The system has built-in hashlists which makes it possible + to store,search and manipulate very large quantities of data, + see ets(3). + +-- Bignums, integers can now be arbitrarily large (almost) + +-- A fully integrated debugger/interpreter that can be used + to debug distributed applications, see int(3), and + the user manual. + +-- Distributed Erlang can now be run in environments where DNS + (The Domain Name system) is not configured, see dist_erl(1). + +-- A new trace/3 BIF which can be used for a variety of + purposes, see erlang(3). + + +*** Minor improvements and new modules. +--------------------------------------------------------------------- + +-- A new BIF to monitor other nodes, monitor_node/2, + see erlang(3). + +-- Floating point exceptions and bad parameters to math functions + are now handled correctly, (possibly not implemented on all + architectures) + +-- epmd can be used to access DNS, see epmd(3). + +-- Erlang now contains MACROS, include files, structures, etc. + These are currently not documented, and are used at the + user's own risk, as the syntax might change. + +-- The configuration of the Erlang system has been simplified. + Not many users are aware of this possibility at all, however. + The only parameter left for configuration is now the size of + TMP_BUF, so no upper limits remain for the number of functions, + modules, etc. + +-- Parallel make, see make(3). + +-- generic.erl, is recommended for writing servers, + see generic(3). + +-- timer.erl a new module to be used for various types of timing + related activities. + +-- The new formatter ~p has been introduced into the formatting + routines. io:format("String ~p", [Term]). will print the + term Term with any lists of integers that seem to be strings + as strings. This means that the majority of printouts will + now have a more attractive appearance. However, it also means + that some lists of integers that are real "lists of integers" + will be displayed as strings. Example: + + 1> [65,66]. + "AB" + +-- Deep lists can now be sent to ports. The lists must be well formed + but can contain integers and binaries. + +-- There is a new interface to udp/ip, see udp(3). + +-- slave.erl is a new and nicer way to start up slave nodes in a + UNIX environment. + +-- ddd.erl is a distributed fully replicated data dictionary. + +-- queue.erl FIFO queues as an abstract datatype. + +-- There are enhancements in the socket interface, see socket(3). + +-- rpc.erl is a new module that now contains some of the functions + that used to be in net.erl, which has now been removed, + see rpc(3). + +-- lists.erl contains some new functionality, see lists(3). + +-- BIF erlang:now() returns the current time. + This BIF is guaranteed to produce continuously increasing values. + +-- The new module auth.erl is for handling authentication, see auth(3). + +-- The file $HOME/.erlang.cookie is now automatically and + silently created if it does not exist. This means that new and/or + naive users can ignore the issues of cookies entirely. + +-- user.erl has been slightly rewritten so that Erlang + programs can now be part of a UNIX pipe, see erl(3), io(3). + +-- The new library directory tools now contain various + "tools" + + +*** Command line flags changes. +------------------------------------------------------------------- + +-- The -s and -h flags take values measured in H_GRAIN and S_GRAIN + H_GRAIN == S_GRAIN == 64. (Default is 1, which means that the default + heap and stack size is 64 words.) + +-- The maximum size of the atom_table is now configurable from + the command line. + +-- erl -sname Name starts a new node with a short name. (s == short), + see erl(1). + +-- The breakhandler can now be turned off with the aid of the flag +B. + +-- init.erl has been rewritten. A -load flag is now accepted, + see init(3). + +-- The -cookie flag is no longer necessary if the system is to + read the $HOME/.erlang.cookie file. This is the default. + +-- The flag -env Variable Value extends the UNIX environment + for the Erlang system, see erl(3). + + +*** Reported Fixed Bugs and Malfunctions +------------------------------------------------------------------- + +-- Do not assume that the first two directory entries "." and ".." + make file:list_dir() work better in some environments. + +-- Faster/better garbage collection. + +-- Stack sizes are automatically shrunk. + +-- Distributed Erlang now handles the case when for example the + Ethernet plug is unplugged. Erlang continuously polls all + remote nodes to ensure that the remote nodes are really alive. + +-- A bug has been corrected in the terminal driver. The system + could fail after a large series of printouts without any + newlines "\n" at all. + +-- Formating of floats: a '9' would sometimes become a ':'. + +-- Formating with the use of '*' and negativ size fields now work + as expected. + +-- The format of the 'EXIT' values is now ALWAYS the same + regardless of where the 'EXIT' was produced. + +-- Bugs in exit/2 when the first argument is a port + and second argument is a tuple, have been fixed. + +-- A bug in the random generator has been fixed, see random(3)) + +-- Object code can now be 'trace' compiled by passing the + flag 'trace' to the compiler. This is necessary for + the trace/3 BIF to work on function calls. + +-- error_logger has been improved and is more flexible, see error_logger(3). + +-- The compiler is not so verbose any more. + +-- A bug in the loading of code has been fixed. It is now possible to load + code into the "hole" created by erlang:delete_module/1. + +-- The file system now accepts very large messages. In 4.2 there + was a limit of 64K, which meant that some VERY large modules + could not be compiled. + +-- Support for real interrupts/signals in linked-in drivers have been added. + +-- open_port does not make new atoms all the time. + +-- statistics(io) does now return two counters, + one for all input and one for all output. + +-- There have been minor bug fixes in the erl_interface c-library. + + +*** New TOOLS for Software Development/Tuning/Debugging +--------------------------------------------------------------- + +-- int, is a fully integrated debugger/interpreter, see int(3). + +-- eprof, is a (tty-based) tool for real-time profiling, see eprof(3). + +-- dbg, is a (tty-based) interface to the the trace/3 BIF, see dbg(3). + +-- pman, is a (pxw-based) interface to the trace/3 BIF. + +-- emseq, is a (tty-based) message sequence chart tool. (Not documented) + +-- perfmon, is a (pxw-based) performance monitor. (Not documented) + +-- exref, is a (tty-based) cross-reference tool. + + +*** New Targets Not Generally Available for 4.2 +------------------------------------------------------------------ + +FreeBSD running on PCs +LINUX running on PCs +QNX + + +*** Incompatibilities with 4.2. +-------------------------------------------------------------------- + +-- The BIF node_link/1 has been replace by monitor_node/2 + See erlang(3). + +-- The 4.3 system is not object code compatible with 4.2. + This means that all source code has to be recompiled. It + is not possible to load 4.2 object code. It is also not + possible to run distribution between 4.3 and erlier versions + due to the new alive check. + +-- The external term format has been changed. This will only affect + programs where the BIF term_to_binary/1 has been used for writing + output on files. The directory misc/external contains a program + ext42_to_ext43.erl that can be used for converting files and + binaries from 4.2 format to 4.3 format. This will affect very + few programs. + +-- The names of the Erlang specific i/o modules are now prefixed by + "erl_", for example erl_scan and erl_parse. + +-- The calls to tokenize/parse have been changed, partially to make their + naming more systematic and also to handle the new line number + information. Their return values have also been made more regular with + all functions returning 'ok', {ok, Value} or {ok, Value, EndLine} where + appropriate when successful, and {error, ErrorInfo} or + {error, ErrorInfo, EndLine} if there is an error. + +-- There is a standardised error information format, ErrorInfo above, which + is returned for all input functions. It has the format: + {ErrorLine, Module, ErrorDescriptor} + where a string describing the error can be obtained by calling + apply(Module, format_error, [ErrorDescriptor]). + The handling of line number is application specific. + +-- The function io:read/1/2 now returns {ok, Term} or {error, ErrorInfo} + for consistency. + +-- The Erlang tokeniser/parser has been converted to return line number + information in the token and parse formats. These formats can now + be handled by the pretty printer (erl_pp) but it does not make use of them. + +-- The function file:open/2 now returns {ok, Pid} or {error, What}. This is + consistent with the return values from the rest of the i/o system. + +-- RTFMP! (Read The Friendly Man Pages) + +-- Module net.erl has been removed. The functionality of net.erl + now resides in the rpc, auth and net_kernel modules. + +-- The old debug BIFs (including the module debugger.erl) have + been removed. The BIF trace/3 replaces it. + +-- The BIF not_alive/0 has been removed. + + +*** Documentation: +-------------- + +All manual pages have been updated, some of them substantially. + + +*** Known problems: +--------------- + +The $HOME/.erlang file should be run before the shell is started. + +The Postscript documentation in the doc directory assumes A4 paper. + +list_to_pid/1 on remote pids may behave in an unexpected manner. diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src new file mode 100644 index 0000000000..f81ef6b0fe --- /dev/null +++ b/erts/etc/unix/cerl.src @@ -0,0 +1,285 @@ +#!/bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2003-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 script to start Erlang/OTP for debugging. PATH is set to +# include this script so if slave nodes are started they will use this +# script as well. +# +# usage: cerl [ OPTIONS ] [ ARGS ] +# +# The OPTIONS are +# +# -rootdir $MYROOTDIR +# Run an installed emulator built from this source +# -debug Run debug compiled emulator +# -gdb Run the debug compiled emulator in emacs and gdb. +# You have to start beam in gdb using "run". +# -break F Run the debug compiled emulator in emacs and gdb and set break. +# The session is started, i.e. "run" is already don for you. +# -xxgdb FIXME currently disabled +# -purify Run emulator compiled for purify +# -quantify Run emulator compiled for quantify +# -purecov Run emulator compiled for purecov +# -gcov Run emulator compiled for gcov +# -valgrind Run emulator compiled for valgrind +# -lcnt Run emulator compiled for lock counting +# -nox Unset the DISPLAY variable to disable us of X Windows +# +# FIXME For GDB you can also set the break point using "-break FUNCTION". +# FIXME For GDB you can also point out your own .gdbini...... + +# These are marked for export +export ROOTDIR +export PROGNAME +export EMU +export BINDIR +export PATH + +cargs= +xargs= +cxargs_add() { + while [ $# -gt 0 ]; do + cargs="$cargs $1" + xargs="$xargs $1" + shift + done +} + +core= + +GDB= +GDBBP= +TYPE= +EMU_TYPE= +debug= +run_valgrind=no + +# Default rootdir +ROOTDIR=%SRC_ROOTDIR% +BINDIR="$ROOTDIR/bin/`$ROOTDIR/erts/autoconf/config.guess`" +#BINDIR="$ROOTDIR/bin/%TARGET%" +PROGNAME=$ROOTDIR/bin/cerl +EMU=beam + +PRELOADED=$ROOTDIR/erts/preloaded/ebin + + +while [ $# -gt 0 ]; do + case "$1" in + +*) + # A system parameter! + cxargs_add $1 + shift + # If next argument does not begin with a hyphen or a plus, + # it is used as the value of the system parameter. + if [ $# -gt 0 ]; then + case $1 in + -*|+*) + ;; + *) + cxargs_add $1 + shift;; + esac + fi;; + "-instr") + cxargs_add $1 + shift + ;; + "-target") + shift + BINDIR="$ROOTDIR/bin/$1" + shift + ;; + "-rootdir") + shift + cargs="$cargs -rootdir $1" + ROOTDIR="$1" + BINDIR=$ROOTDIR/erts-%VSN%/bin + shift + ;; + "-display") + shift + DISPLAY="$1" + export DISPLAY + shift + ;; + "-nox") + shift + unset DISPLAY + ;; + "-smp") + shift + cargs="$cargs -smp" + EMU_TYPE=.smp + ;; + "-lcnt") + shift + cargs="$cargs -lcnt" + TYPE=.lcnt + ;; + "-frag") + shift + cargs="$cargs -frag" + EMU_TYPE=.frag + ;; + "-smp_frag") + shift + cargs="$cargs -smp_frag" + EMU_TYPE=.smp_frag + ;; + "-gprof") + shift + cargs="$cargs -gprof" + TYPE=.gprof + ;; + "-hybrid") + shift + cargs="$cargs -hybrid" + EMU_TYPE=.hybrid + ;; + "-debug") + shift + cargs="$cargs -debug" + TYPE=.debug + ;; + "-gdb") + shift + GDB=gdb + ;; + "-break") + shift + GDB=gdb + GDBBP="$GDBBP (insert-string \"break $1\") (comint-send-input)" + shift + ;; + "-core") + shift + GDB=gdb + core="$1" + shift + ;; +# "-xxgdb") +# shift +# GDB=xxgdb +# ;; + "-shared") + shift + cargs="$cargs -shared" + TYPE=.shared + ;; + "-purify") + shift + cargs="$cargs -purify" + TYPE=.purify + ;; + "-quantify") + shift + cargs="$cargs -quantify" + TYPE=.quantify + ;; + "-purecov") + shift + cargs="$cargs -purecov" + TYPE=.purecov + ;; + "-gcov") + shift + cargs="$cargs -gcov" + TYPE=.gcov + ;; + "-valgrind") + shift + cargs="$cargs -valgrind" + TYPE=.valgrind + run_valgrind=yes + ;; + *) + break + ;; + esac +done + + +PATH=$BINDIR:$ROOTDIR/bin:$PATH +EXEC=$BINDIR/erlexec + +PROGNAME="$PROGNAME $cargs" +EMU=$EMU$TYPE$EMU_TYPE +if [ $run_valgrind != yes ]; then + xargs="$xargs -pz $PRELOADED --" +fi +if [ "x$GDB" = "x" ]; then + if [ $run_valgrind = yes ]; then + emu_xargs=`echo $xargs | sed "s|+|-|g"` + if [ "x$VALGRIND_LOG_DIR" = "x" ]; then + valgrind_log= + else + valgrind_log="--log-file=$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log" + fi + if [ "x$VALGRIND_LOG_XML" = "x" ]; then + valgrind_xml= + else + export VALGRIND_LOG_XML + valgrind_xml="--xml=yes" + fi + if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then + valgrind_misc_flags= + else + valgrind_misc_flags="$VALGRIND_MISC_FLAGS" + fi + beam_args=`$EXEC -emu_args_exit ${1+"$@"}` + # Ahhhh... Need to quote $PROGNAME... + early_beam_args=`echo $beam_args | sed "s|^\(.*-progname\).*$|\1|g"` + late_beam_args=`echo $beam_args | sed "s|^$pre_beam_args.*\(-- -home.*\)$|\1|g"` + + exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED + else + exec $EXEC $xargs ${1+"$@"} + fi +else + if [ "x$EMACS" = "x" ]; then + EMACS=emacs + fi + + case "x$core" in + x) + # Get emu args to use from erlexec... + beam_args=`$EXEC -emu_args_exit ${1+"$@"}` + gdbcmd="(insert-string \"set args $beam_args\") \ + (comint-send-input)" + ;; + x/*) + gdbcmd="(insert-string \"core ${core}\") (comint-send-input)" + GDBBP= + ;; + *) + dir=`pwd` + gdbcmd="(insert-string \"core ${dir}/${core}\") \ + (comint-send-input)" + GDBBP= + ;; + esac + + gdbcmd="$gdbcmd $GDBBP \ + (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \ + (comint-send-input)" + # Fire up gdb in emacs... + exec $EMACS --eval "(progn (gdb \"gdb $EMU\") $gdbcmd)" +fi diff --git a/erts/etc/unix/dyn_erl.c b/erts/etc/unix/dyn_erl.c new file mode 100644 index 0000000000..984935417e --- /dev/null +++ b/erts/etc/unix/dyn_erl.c @@ -0,0 +1,400 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 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 Bourne shell script + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include <stdlib.h> +#include <stdarg.h> + +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#define PATHSEP ":" +#define DIRSEP "/" +#define DIRSEPCHAR '/' + +static void +error(char* format, ...) +{ + char sbuf[1024]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + fprintf(stderr, "erl: %s\n", sbuf); + exit(1); +} + +/* + * Variables. + */ + +/* + * Manage memory + */ + +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 char* +strsave(char* string) +{ + char* p = emalloc(strlen(string)+1); + strcpy(p, string); + return p; +} + +/* + * Manage environment variables + */ + +static char * +get_env(char *key) +{ + return getenv(key); +} + +static void +set_env(char *key, char *value) +{ + 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 +} + +// /* A realpath look alike */ +// static char * +// follow_symlinks(const char *path, char *resolved_path) +// { +// char tmp[PATH_MAX]; +// int len; +// +// strcpy(resolved_path, path); +// +// for (;;) { +// len = readlink(resolved_path, tmp, PATH_MAX); +// +// if (len == -1) { +// if (errno == EINVAL) { +// /* Not a symbolic link. use the original name */ +// break; +// } else { +// return NULL; +// } +// } else { +// tmp[len] = '\0'; +// strcpy(resolved_path, tmp); +// } +// } +// +// return resolved_path; +// } + +/* + * Find absolute path to this program + */ + +static char * +find_prog(char *origpath) +{ + char relpath[PATH_MAX]; + char abspath[PATH_MAX]; + + strcpy(relpath, origpath); + + if (strstr(relpath, DIRSEP) == NULL) { + /* Just a base name */ + char *envpath; + + envpath = get_env("PATH"); + if (envpath) { + /* Try to find the executable in the path */ + char dir[PATH_MAX]; + char *beg = envpath; + char *end; + int sz; + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ + BOOL look_for_sep = TRUE; + + while (look_for_sep) { + end = strstr(beg, PATHSEP); + 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; + + 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(relpath, dir); + strcat(relpath, DIRSEP); + strcat(relpath, dirp->d_name); + closedir(dp); + look_for_sep = FALSE; + break; + } + } + } + } + } + } + + if (!realpath(relpath, abspath)) { + error("Cannot determine real path to erl"); + } + + return strdup(abspath); +} + +/* + * Find bindir + */ + +static void +copy_latest_vsn(char *latest_vsn, char *next_vsn) +{ + char *lp; + char *np; + BOOL greater; + + /* Find vsn */ + for (lp = latest_vsn+strlen(latest_vsn)-1 ;lp > latest_vsn && *lp != DIRSEPCHAR; --lp) + ; + + /* lp =+ length("erts-"); */ + for (np = next_vsn+strlen(next_vsn)-1 ;np > next_vsn && *np != DIRSEPCHAR; --np) + ; + + /* np =+ length("erts-"); */ + while (TRUE) { + if (*lp != *np) { + if (*np > *lp) { + greater = TRUE; + } else { + greater = FALSE; + } + + /* Find next dot or eos */ + while (*lp != '\0' && *np != '\0') { + lp++; + np++; + if (*np == '.' && *lp == '.') { + break; + } + if (*np == '\0' && *lp == '\0') { + break; + } + if (*lp == '.' || *lp == '\0') { + greater = TRUE; + } + if (*np == '.' || *np == '\0') { + greater = FALSE; + } + } + if (greater) { + strcpy(latest_vsn, next_vsn); + } + return; + } + ++lp; + ++np; + } +} + +static char * +find_erts_vsn(char *erl_top) +{ + /* List install dir and look for latest erts-vsn */ + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ + char latest_vsn[PATH_MAX]; /* Latest erts-vsn directory name. */ + + dp = opendir(erl_top); + if (dp == NULL) { + return NULL; + } + + latest_vsn[0] = '\0'; + for (;;) { + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + break; + } + if (strncmp("erts-", dirp->d_name, 5) == 0) { + copy_latest_vsn(latest_vsn, dirp->d_name); + } + } + + if (latest_vsn[0] == '\0') { + return NULL; + } else { + char *p = malloc((strlen(erl_top)+1+strlen(latest_vsn)+4+1)*sizeof(char)); + strcpy(p,erl_top); + strcat(p,DIRSEP); + strcat(p,latest_vsn); + strcat(p,DIRSEP); + strcat(p,"bin"); + return p; + } +} + +static char * +find_bindir(char *erlpath) +{ + /* Assume that the path to erl is absolute and + * that it is not a symbolic link*/ + + char *p; + char *p2; + char buffer[PATH_MAX]; + + strcpy(buffer, erlpath); + + /* Chop of base name*/ + for (p = buffer+strlen(buffer)-1 ;p >= buffer && *p != DIRSEPCHAR; --p) + ; + *p = '\0'; + p--; + + /* Check if dir path is like ...\buffer\erts-vsn\bin */ + for (;p >= buffer && *p != DIRSEPCHAR; --p) + ; + p--; + for (p2 = p;p2 >= buffer && *p2 != DIRSEPCHAR; --p2) + ; + p2++; + if (strncmp(p2, "erts-", 5) == 0) { + p = strsave(buffer); + return p; + } + + /* Assume that dir path is like ...\buffer\bin */ + *++p ='\0'; /* chop off bin dir */ + + p = find_erts_vsn(buffer); + if (p == NULL) { + return strsave(buffer); + } else { + return p; + } +} + +/* + * main + */ + +int +main(int argc, char **argv) +{ + char *p; + char *abspath; + char *bindir; /* Location of executables. */ + char rootdir[PATH_MAX]; /* Root location of Erlang installation. */ + char progname[PATH_MAX]; /* Name of this program. */ + char erlexec[PATH_MAX]; /* Path to erlexec */ + + /* Determine progname */ + abspath = find_prog(argv[0]); + strcpy(progname, abspath); + for (p = progname+strlen(progname)-1;p >= progname && *p != '/'; --p) + ; + + /* Determine bindir */ + bindir = find_bindir(abspath); + + /* Determine rootdir */ + strcpy(rootdir, bindir); + for (p = rootdir+strlen(rootdir)-1;p >= rootdir && *p != '/'; --p) + ; + p--; + for (;p >= rootdir && *p != '/'; --p) + ; + *p ='\0'; + + /* Update environment */ + set_env("EMU", "beam"); + set_env("PROGNAME", progname); + set_env("BINDIR", bindir); + set_env("ROOTDIR", rootdir); + + /* Invoke erlexec */ + strcpy(erlexec, bindir); + strcat(erlexec, DIRSEP); + strcat(erlexec, "erlexec"); + + efree(abspath); + efree(bindir); + + execvp(erlexec, argv); + error("Error %d executing \'%s\'.", errno, erlexec); + return 2; +} diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src new file mode 100644 index 0000000000..50603f12f4 --- /dev/null +++ b/erts/etc/unix/erl.src.src @@ -0,0 +1,28 @@ +#!/bin/sh +# +# %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% +# +ROOTDIR=%FINAL_ROOTDIR% +BINDIR=$ROOTDIR/erts-%VSN%/bin +EMU=%EMULATOR%%EMULATOR_NUMBER% +PROGNAME=`echo $0 | sed 's/.*\///'` +export EMU +export ROOTDIR +export BINDIR +export PROGNAME +exec $BINDIR/erlexec ${1+"$@"} diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands new file mode 100644 index 0000000000..6a01e0b7e0 --- /dev/null +++ b/erts/etc/unix/etp-commands @@ -0,0 +1,2054 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2005-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +############################################################################ +# Help commands +# + +define etp-help + help etp-help +end + +document etp-help +%--------------------------------------------------------------------------- +% etp-help +% +% Same as "help etp-help" +% +% Emulator Toolbox for Pathologists +% - GDB command toolbox for analyzing core dumps from the +% Erlang emulator (BEAM). +% +% Should work for 32-bit erts-5.2/R9B, ... +% +% The commands are prefixed with: +% etp: Acronym for erts-term-print +% etpf: Acronym for erts-term-print-flat +% +% User commands (these have help themselves): +% +% Most useful: +% etp, etpf +% +% Useful for doing step-by-step traversal of lists and tuples after +% calling the toplevel command etpf: +% etpf-cons, etpf-boxed, +% +% Special commands for not really terms: +% etp-mfa, etp-cp, +% etp-msgq, etpf-msgq, +% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump +% etp-offheapdump, etpf-offheapdump, +% etp-print-procs, etp-search-heaps, etp-search-alloc, +% etp-ets-tables, etp-ets-tabledump +% +% Complex commands that use the Erlang support module. +% etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end +% +% Erlang support module handling commands: +% etp-run +% +% Parameter handling commands: +% etp-show, etp-set-max-depth, etp-set-max-string-length +% +% Other commands you may find in this toolbox are suffixed -1, -2, ... +% and are internal; not for the console user. +% +% The Erlang support module requires `erl' and `erlc' in the path. +% The compiled "erl_commands.beam" file is stored in the current +% working directory, so it is thereby in the search path of `erl'. +% +% These are just helpful commands when analyzing core dumps, but +% you will not get away without knowing the gory details of the +% tag bits. Do not forget about the e.g p, p/x, x and x/4x commands. +% +% Execution speed of user defined gdb commands is not lightning fast. +% It may well take half a minute to dump a complex term with the default +% max depth values on our old Sparc Ultra-10's. +% +% To use the Erlang support module, the environment variable ROOTDIR +% must be set to the toplevel installation directory of Erlang/OTP, +% so the etp-commands file becomes: +% $ROOTDIR/erts/etc/unix/etp-commands +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Toplevel commands +# + +define etp +# Args: Eterm +# +# Reentrant +# + etp-1 ((Eterm)($arg0)) 0 + printf ".\n" +end + +document etp +%--------------------------------------------------------------------------- +% etp Eterm +% +% Takes a toplevel Erlang term and prints the whole deep term +% very much as in Erlang itself. Up to a max depth. See etp-show. +%--------------------------------------------------------------------------- +end + +define etp-1 +# Args: Eterm, int depth +# +# Reentrant +# + if (($arg0) & 0x3) == 1 + # Cons pointer + if $etp_flat + printf "<etpf-cons %#x>", ($arg0) + else + etp-list-1 ($arg0) ($arg1) + end + else + if (($arg0) & 0x3) == 2 + if $etp_flat + printf "<etpf-boxed %#x>", ($arg0) + else + etp-boxed-1 ($arg0) ($arg1) + end + else + if (($arg0) & 0x3) == 3 + etp-immediate-1 ($arg0) + else + # (($arg0) & 0x3) == 0 + if (($arg0) == 0x0) + printf "<the non-value>" + else + if (($arg0) == 0x4) + printf "<the non-value debug>" + else + etp-cp-1 ($arg0) + end + end + end + end + end +end + +define etpf +# Args: Eterm +# +# Non-reentrant + set $etp_flat = 1 + etp-1 ((Eterm)($arg0)) + set $etp_flat = 0 + printf ".\n" +end + +document etpf +%--------------------------------------------------------------------------- +% etpf Eterm +% +% Takes a toplevel Erlang term and prints it is. If it is a deep term +% print which command to use to traverse down one level. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Commands for nested terms. Some are recursive. +# + +define etp-list-1 +# Args: Eterm cons_cell, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + if $etp_chart + etp-chart-entry-1 ($arg0) ($arg1) 2 + end + etp-list-printable-1 ($arg0) ($arg1) + if !$etp_list_printable + # Print normal list + printf "[" + etp-list-2 ($arg0) (($arg1)+1) + end + end +end + +define etp-list-printable-1 +# Args: Eterm list, int depth +# +# Non-reentrant +# +# Returns: $etp_list_printable +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Loop to check if it is a printable string + set $etp_list_p = ($arg0) + set $etp_list_printable = ($etp_list_p != $etp_nil) + set $etp_list_i = 0 + while ($etp_list_p != $etp_nil) && \ + ($etp_list_i < $etp_max_string_length) && \ + $etp_list_printable + if ($etp_list_p & 0x3) == 0x1 + # Cons pointer + set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] + if ($etp_list_n & 0xF) == 0xF + etp-ct-printable-1 ($etp_list_n>>4) + if $etp_ct_printable + # Printable + set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] + set $etp_list_i++ + else + set $etp_list_printable = 0 + end + else + set $etp_list_printable = 0 + end + else + set $etp_list_printable = 0 + end + end + # + if $etp_list_printable + # Print printable string + printf "\"" + set $etp_list_p = ($arg0) + set $etp_list_i = 0 + while $etp_list_p != $etp_nil + set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0] + etp-char-1 ($etp_list_n>>4) '"' + set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1] + set $etp_list_i++ + if $etp_list_p == $etp_nil + printf "\"" + else + if $etp_list_i >= $etp_max_string_length + set $etp_list_p = $etp_nil + printf "\"++[...]" + else + if $etp_chart + etp-chart-entry-1 ($arg0) (($arg1)+$etp_list_i) 2 + end + end + end + end + end + end +end + +define etp-list-2 +# Args: Eterm cons_cell, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + if ($arg1) >= $etp_max_depth + printf "...]" + else + etp-1 (((Eterm*)(($arg0)&~0x3))[0]) (($arg1)+1) + if ((Eterm*)(($arg0) & ~0x3))[1] == $etp_nil + # Tail is [] + printf "]" + else + if $etp_chart + etp-chart-entry-1 ($arg0) ($arg1) 2 + end + if (((Eterm*)(($arg0)&~0x3))[1]&0x3) == 0x1 + # Tail is cons cell + printf "," + etp-list-2 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) + else + # Tail is other term + printf "|" + etp-1 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1) + printf "]" + end + end + end + end +end + +define etpf-cons +# Args: Eterm +# +# Reentrant capable +# + if ((Eterm)($arg0) & 0x3) != 0x1 + printf "#NotCons<%#x>", ($arg0) + else + # Cons pointer + set $etp_flat = 1 + printf "[" + etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[0]) + printf "|" + etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[1]) + printf "]\n" + set $etp_flat = 0 + end +end + +document etpf-cons +%--------------------------------------------------------------------------- +% etpf-cons Eterm +% +% Takes a Cons ptr and prints the Car and Cdr cells with etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-boxed-1 +# Args: Eterm, int depth +# +# Reentrant +# + if (($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", ($arg0) + else + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 + if $etp_chart + etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 + end + printf "#BoxedError<%#x>", ($arg0) + else + if $etp_chart + etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ + ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) + end + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3f) == 0x0 + printf "{" + etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \ + 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}' + else + etp-boxed-immediate-1 ($arg0) + end + end + end +end + +define etp-boxed-immediate-1 +# Args: Eterm, int depth +# +# Non-reentrant +# + if (($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", ($arg0) + else + if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 + printf "#BoxedError<%#x>", ($arg0) + else + set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) + set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF + if $etp_boxed_immediate_h == 0xC + etp-extpid-1 ($arg0) + else + if $etp_boxed_immediate_h == 0xD + etp-extport-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x2) || \ + ($etp_boxed_immediate_h == 0x3) + etp-bignum-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x6) + etp-float-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0x4) + etp-ref-1 ($arg0) + else + if ($etp_boxed_immediate_h == 0xE) + etp-extref-1 ($arg0) + else + # Hexdump the rest + if ($etp_boxed_immediate_h == 0x5) + printf "#Fun<" + else + if ($etp_boxed_immediate_h == 0x8) + printf "#RefcBinary<" + else + if ($etp_boxed_immediate_h == 0x9) + printf "#HeapBinary<" + else + if ($etp_boxed_immediate_h == 0xA) + printf "#SubBinary<" + else + printf "#Header%X<", $etp_boxed_immediate_h + end + end + end + end + set $etp_boxed_immediate_arity = $etp_boxed_immediate_p[0]>>6 + while $etp_boxed_immediate_arity > 0 + set $etp_boxed_immediate_p++ + if $etp_boxed_immediate_arity > 1 + printf "%#x,", *$etp_boxed_immediate_p + else + printf "%#x", *$etp_boxed_immediate_p + if ($etp_boxed_immediate_h == 0xA) + set $etp_boxed_immediate_p++ + printf ":%#x", *$etp_boxed_immediate_p + end + printf ">" + end + set $etp_boxed_immediate_arity-- + end + # End of hexdump + end + end + end + end + end + end + end + end +end + +define etpf-boxed +# Args: Eterm +# +# Non-reentrant +# + set $etp_flat = 1 + etp-boxed-1 ((Eterm)($arg0)) 0 + set $etp_flat = 0 + printf ".\n" +end + +document etpf-boxed +%--------------------------------------------------------------------------- +% etpf-boxed Eterm +% +% Take a Boxed ptr and print the contents in one level using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-array-1 +# Args: Eterm* p, int depth, int width, int pos, int size, int end_char +# +# Reentrant +# + if ($arg3) < ($arg4) + if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth) + etp-1 (($arg0)[($arg3)]) (($arg1)+1) + if (($arg3) + 1) != ($arg4) + printf "," + end + etp-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) ($arg4) ($arg5) + else + printf "...%c", ($arg5) + end + else + printf "%c", ($arg5) + end +end + + + +#define etpa-1 +## Args: Eterm, int depth, int index, int arity +## +## Reentrant +## +# if ($arg1) >= $etp_max_depth+$etp_max_string_length +# printf "%% Max depth for term %d\n", $etp_chart_id +# else +# if ($arg2) < ($arg3) +# etp-1 (((Eterm*)(($arg0)&~0x3))[$arg2]) (($arg1)+1) +# etpa-1 ($arg0) (($arg1)+1) (($arg2)+1) ($arg3) +# end +# end +#end + +############################################################################ +# Commands for non-nested terms. Recursion leaves. Some call other leaves. +# + +define etp-immediate-1 +# Args: Eterm +# +# Reentrant capable +# + if (($arg0) & 0x3) != 0x3 + printf "#NotImmediate<%#x>", ($arg0) + else + if (($arg0) & 0xF) == 0x3 + etp-pid-1 ($arg0) + else + if (($arg0) & 0xF) == 0x7 + etp-port-1 ($arg0) + else + if (($arg0) & 0xF) == 0xf + # Fixnum + printf "%ld", (long)((Sint)($arg0)>>4) + else + # Immediate2 - 0xB + if (($arg0) & 0x3f) == 0x0b + etp-atom-1 ($arg0) + else + if (($arg0) & 0x3f) == 0x1b + printf "#Catch<%d>", ($arg0)>>6 + else + if (($arg0) == $etp_nil) + printf "[]" + else + printf "#UnknownImmediate<%#x>", ($arg0) + end + end + end + end + end + end + end +end + + + +define etp-atom-1 +# Args: Eterm atom +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3f) != 0xb + printf "#NotAtom<%#x>", ($arg0) + else + set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + set $etp_atom_1_quote = 1 + # Check if atom has to be quoted + if ($etp_atom_1_i > 0) + etp-ct-atom-1 (*$etp_atom_1_p) + if $etp_ct_atom + # Atom start character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + set $etp_atom_1_quote = 0 + else + set $etp_atom_1_i = 0 + end + end + while $etp_atom_1_i > 0 + etp-ct-name-1 (*$etp_atom_1_p) + if $etp_ct_name + # Name character + set $etp_atom_1_p++ + set $etp_atom_1_i-- + else + set $etp_atom_1_quote = 1 + set $etp_atom_1_i = 0 + end + end + # Print the atom + if $etp_atom_1_quote + printf "'" + end + set $etp_atom_1_i = ($etp_atom_1_ap)->len + set $etp_atom_1_p = ($etp_atom_1_ap)->name + while $etp_atom_1_i > 0 + etp-char-1 (*$etp_atom_1_p) '\'' + set $etp_atom_1_p++ + set $etp_atom_1_i-- + end + if $etp_atom_1_quote + printf "'" + end + end +end + + + +define etp-char-1 +# Args: int char, int quote_char +# +# Non-reentrant +# + if (($arg0) < 0) || (0377 < ($arg0)) + printf "#NotChar<%#x>", ($arg0) + else + if ($arg0) == ($arg1) + printf "\\%c", ($arg0) + else + etp-ct-printable-1 ($arg0) + if $etp_ct_printable + if $etp_ct_printable < 0 + printf "%c", ($arg0) + else + printf "\\%c", $etp_ct_printable + end + else + printf "\\%03o", ($arg0) + end + end + end +end + +define etp-ct-printable-1 +# Args: int +# +# Determines if integer is a printable character +# +# Non-reentrant +# Returns: $etp_ct_printable +# escape alias char, or -1 if no escape alias + if ($arg0) == 010 + set $etp_ct_printable = 'b' + else + if ($arg0) == 011 + set $etp_ct_printable = 't' + else + if ($arg0) == 012 + set $etp_ct_printable = 'n' + else + if ($arg0) == 013 + set $etp_ct_printable = 'v' + else + if ($arg0) == 014 + set $etp_ct_printable = 'f' + else + if ($arg0) == 033 + set $etp_ct_printable = 'e' + else + if ((040 <= ($arg0)) && (($arg0) <= 0176)) || \ + ((0240 <= ($arg0)) && (($arg0) <= 0377)) + # Other printable character + set $etp_ct_printable = -1 + else + set $etp_ct_printable = 0 + end + end + end + end + end + end + end +end + +define etp-ct-atom-1 +# Args: int +# +# Determines if integer is a atom first character +# +# Non-reentrant +# Returns: $etp_ct_atom + if ((0141 <= ($arg0)) && (($arg0) <= 0172)) || \ + ((0337 <= ($arg0)) && (($arg0) != 0367) && (($arg0) <= 0377)) + # Atom start character + set $etp_ct_atom = 1 + else + set $etp_ct_atom = 0 + end +end + +define etp-ct-variable-1 +# Args: int +# +# Determines if integer is a variable first character +# +# Non-reentrant +# Returns: $etp_ct_variable + if ((056 == ($arg0)) || \ + (0101 <= ($arg0)) && (($arg0) <= 0132)) || \ + (0137 == ($arg0)) || \ + ((0300 <= ($arg0)) && (($arg0) != 0327) && (($arg0) <= 0336)) + # Variable start character + set $etp_ct_variable = 1 + else + set $etp_ct_variable = 0 + end +end + +define etp-ct-name-1 +# Args: int +# +# Determines if integer is a name character, +# i.e non-first atom or variable character. +# +# Non-reentrant +# Returns: $etp_ct_variable + if (($arg0) == 0100 || \ + (060 <= ($arg0)) && (($arg0) <= 071)) + set $etp_ct_name = 1 + else + etp-ct-atom-1 ($arg0) + if $etp_ct_atom + set $etp_ct_name = 1 + else + etp-ct-variable-1 ($arg0) + set $etp_ct_name = $etp_ct_variable + end + end +end + + + +define etp-pid-1 +# Args: Eterm pid +# +# Non-reentrant +# + set $etp_pid_1 = (Eterm)($arg0) + if ($etp_pid_1 & 0xF) == 0x3 + # Internal pid + printf "<0.%u.%u>", (unsigned) ($etp_pid_1>>4)&0x7fff, \ + (unsigned) ($etp_pid_1>>19)&0x1fff + else + printf "#NotPid<%#x>", ($arg0) + end +end + +define etp-extpid-1 +# Args: Eterm extpid +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extpid_1_p->header & 0x3f) != 0x30 + printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header + else + ## External pid + set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff + set $etp_extpid_1_serial = ($etp_extpid_1_p->data.ui[0]>>15)&0x1fff + set $etp_extpid_1_np = $etp_extpid_1_p->node + set $etp_extpid_1_creation = $etp_extpid_1_np->creation + set $etp_extpid_1_dep = $etp_extpid_1_np->dist_entry + set $etp_extpid_1_node = $etp_extpid_1_np->sysname + if ($etp_extpid_1_node & 0x3f) != 0xb + # Should be an atom + printf "#ExternalPidError<%#x>", ($arg0) + else + if $etp_extpid_1_dep == erts_this_dist_entry + printf "<0:" + else + printf "<%u:", $etp_extpid_1_node>>6 + end + etp-atom-1 ($etp_extpid_1_node) + printf "/%u.%u.%u>", $etp_extpid_1_creation, \ + $etp_extpid_1_number, $etp_extpid_1_serial + end + end + end +end + + + +define etp-port-1 +# Args: Eterm port +# +# Non-reentrant +# + set $etp_port_1 = (Eterm)($arg0) + if ($etp_port_1 & 0xF) == 0x7 + # Internal port + printf "#Port<0.%u>", (unsigned) ($etp_port_1>>4)&0x3ffff + else + printf "#NotPort<%#x>", ($arg0) + end +end + +define etp-extport-1 +# Args: Eterm extport +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extport_1_p->header & 0x3F) != 0x34 + printf "#NotExternalPort<%#x>", $etp_extport_1->header + else + ## External port + set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff + set $etp_extport_1_np = $etp_extport_1_p->node + set $etp_extport_1_creation = $etp_extport_1_np->creation + set $etp_extport_1_dep = $etp_extport_1_np->dist_entry + set $etp_extport_1_node = $etp_extport_1_np->sysname + if ($etp_extport_1_node & 0x3f) != 0xb + # Should be an atom + printf "#ExternalPortError<%#x>", ($arg0) + else + if $etp_extport_1_dep == erts_this_dist_entry + printf "#Port<0:" + else + printf "#Port<%u:", $etp_extport_1_node>>6 + end + etp-atom-1 ($etp_extport_1_node) + printf "/%u.%u>", $etp_extport_1_creation, $etp_extport_1_number + end + end + end +end + + + +define etp-bignum-1 +# Args: Eterm bignum +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) + if ($etp_bignum_1_p[0] & 0x3b) != 0x08 + printf "#NotBignum<%#x>", $etp_bignum_1_p[0] + else + set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) + if $etp_bignum_1_i < 1 + printf "#BignumError<%#x>", (Eterm)($arg0) + else + if $etp_bignum_1_p[0] & 0x04 + printf "-" + end + set $etp_bignum_1_p = (ErtsDigit *)($etp_bignum_1_p + 1) + printf "16#" + if $etp_arch64 + while $etp_bignum_1_i > 0 + set $etp_bignum_1_i-- + printf "%016lx", $etp_bignum_1_p[$etp_bignum_1_i] + end + else + while $etp_bignum_1_i > 0 + set $etp_bignum_1_i-- + printf "%08x", $etp_bignum_1_p[$etp_bignum_1_i] + end + end + end + end + end +end + + + +define etp-float-1 +# Args: Eterm float +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) + if ($etp_float_1_p[0] & 0x3f) != 0x18 + printf "#NotFloat<%#x>", $etp_float_1_p[0] + else + printf "%f", *(double*)($etp_float_1_p+1) + end + end +end + + + +define etp-ref-1 +# Args: Eterm ref +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_ref_1_p = (RefThing *)((Eterm)($arg0) & ~0x3) + if ($etp_ref_1_p->header & 0x3b) != 0x10 + printf "#NotRef<%#x>", $etp_ref_1_p->header + else + set $etp_ref_1_nump = (Uint32 *) 0 + set $etp_ref_1_error = 0 + if ($etp_ref_1_p->header >> 6) == 0 + set $etp_ref_1_error = 1 + else + if $etp_arch64 + set $etp_ref_1_i = (int) $etp_ref_1_p->data.ui32[0] + if (($etp_ref_1_i + 1) > (2 * ($etp_ref_1_p->header >> 6))) + set $etp_ref_1_error = 1 + else + set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[1] + end + else + set $etp_ref_1_i = (int) ($etp_ref_1_p->header >> 6) + set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[0] + end + end + if $etp_ref_1_error + printf "#InternalRefError<%#x>", ($arg0) + else + printf "#Ref<0" + set $etp_ref_1_i-- + while $etp_ref_1_i >= 0 + printf ".%u", (unsigned) $etp_ref_1_nump[$etp_ref_1_i] + set $etp_ref_1_i-- + end + printf ">" + end + end + end +end + + + +define etp-extref-1 +# Args: Eterm extref +# +# Non-reentrant +# + if ((Eterm)($arg0) & 0x3) != 0x2 + printf "#NotBoxed<%#x>", (Eterm)($arg0) + else + set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) + if ($etp_extref_1_p->header & 0x3F) != 0x38 + printf "#NotExternalRef<%#x>", $etp_extref_1->header + else + ## External ref + set $etp_extref_1_nump = (Uint32 *) 0 + set $etp_extref_1_error = 0 + set $etp_extref_1_i = (int) ($etp_extref_1_p->header >> 6) + set $etp_extref_1_np = $etp_extref_1_p->node + set $etp_extref_1_creation = $etp_extref_1_np->creation + set $etp_extref_1_dep = $etp_extref_1_np->dist_entry + set $etp_extref_1_node = $etp_extref_1_np->sysname + if ($etp_extref_1_node & 0x3f) != 0xb || $etp_extref_1_i < 3 + # Node should be an atom + set $etp_extref_1_error = 1 + else + ## $etp_extref_1_i now equals data (Uint) words + set $etp_extref_1_i -= 2 + if $etp_arch64 + if ((((int) $etp_extref_1_p->data.ui32[0]) + 1) \ + > (2 * $etp_extref_1_i)) + set $etp_extref_1_error = 1 + else + set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[1] + set $etp_extref_1_i = (int) $etp_extref_1_p->data.ui32[0] + end + else + set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[0] + end + ## $etp_extref_1_i now equals no of ref num (Uint32) words + if !$etp_extref_1_error + if $etp_extref_1_dep == erts_this_dist_entry + printf "#Ref<0:" + else + printf "#Ref<%u:", $etp_extref_1_node>>6 + end + etp-atom-1 ($etp_extref_1_node) + printf "/%u", $etp_extref_1_creation + end + end + if $etp_extref_1_error + printf "#ExternalRefError<%#x>", ($arg0) + else + set $etp_extref_1_i-- + while $etp_extref_1_i >= 0 + printf ".%u", (unsigned) $etp_extref_1_nump[$etp_extref_1_i] + set $etp_extref_1_i-- + end + printf ">" + end + end + end +end + + + +define etp-mfa-1 +# Args: Eterm*, int offset +# +# Reentrant +# + printf "<" + etp-atom-1 (((Eterm*)($arg0))[0]) + printf ":" + etp-atom-1 (((Eterm*)($arg0))[1]) + printf "/%d", ((Eterm*)($arg0))[2] + if ($arg1) > 0 + printf "+%#x>", ($arg1) + else + printf ">" + end +end + +define etp-mfa +# Args: Eterm* +# +# Reentrant capable +# + etp-mfa-1 ($arg0) 0 + printf ".\n" +end + +document etp-mfa +%--------------------------------------------------------------------------- +% etp-mfa Eterm* +% +% Take an Eterm* to an MFA function name entry and print it. +% These can be found e.g in the process structure; +% process_tab[i]->current and process_tab[i]->initial. +%--------------------------------------------------------------------------- +end + + + +define etp-cp-1 +# Args: Eterm cp +# +# Non-reentrant +# + set $etp_cp = (Eterm)($arg0) + set $etp_cp_low = modules + set $etp_cp_high = $etp_cp_low + num_loaded_modules + set $etp_cp_mid = mid_module + set $etp_cp_p = 0 + # + while $etp_cp_low < $etp_cp_high + if $etp_cp < $etp_cp_mid->start + set $etp_cp_high = $etp_cp_mid + else + if $etp_cp > $etp_cp_mid->end + set $etp_cp_low = $etp_cp_mid + 1 + else + set $etp_cp_p = $etp_cp_low = $etp_cp_high = $etp_cp_mid + end + end + set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 + end + if $etp_cp_p + set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8) + set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0] + set $etp_cp_p = 0 + while $etp_cp_low < $etp_cp_high + set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2 + if $etp_cp < $etp_cp_mid[0] + set $etp_cp_high = $etp_cp_mid + else + if $etp_cp < $etp_cp_mid[1] + set $etp_cp_p = $etp_cp_mid[0]+2 + set $etp_cp_low = $etp_cp_high = $etp_cp_mid + else + set $etp_cp_low = $etp_cp_mid + 1 + end + end + end + end + if $etp_cp_p + printf "#Cp" + etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2))) + else + if $etp_cp == beam_apply+1 + printf "#Cp<terminate process normally>" + else + if *(Eterm*)($etp_cp) == beam_return_trace[0] + if ($etp_cp) == beam_exception_trace + printf "#Cp<exception trace>" + else + printf "#Cp<return trace>" + end + else + if *(Eterm*)($etp_cp) == beam_return_to_trace[0] + printf "#Cp<return to trace>" + else + printf "#Cp<%#x>", $etp_cp + end + end + end + end +end + +define etp-cp +# Args: Eterm cp +# +# Reentrant capable +# + etp-cp-1 ($arg0) + printf ".\n" +end + +document etp-cp +%--------------------------------------------------------------------------- +% etp-cp Eterm +% +% Take a code continuation pointer and print +% module, function, arity and offset. +% +% Code continuation pointers can be found in the process structure e.g +% process_tab[i]->cp and process_tab[i]->i, the second is the +% program counter, which is the same thing as a continuation pointer. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Commands for special term bunches. +# + +define etp-msgq +# Args: ErlMessageQueue* +# +# Non-reentrant +# + set $etp_msgq = ($arg0) + set $etp_msgq_p = $etp_msgq->first + set $etp_msgq_i = $etp_msgq->len + set $etp_msgq_prev = $etp_msgq->last + printf "%% Message queue (%d):", $etp_msgq_i + if ($etp_msgq_i > 0) && $etp_msgq_p + printf "\n[" + else + printf "\n" + end + while ($etp_msgq_i > 0) && $etp_msgq_p + set $etp_msgq_i-- + set $etp_msgq_next = $etp_msgq_p->next + # Msg + etp-1 ($etp_msgq_p->m[0]) 0 + if ($etp_msgq_i > 0) && $etp_msgq_next + printf ", %% " + else + printf "]. %% " + end + # Seq_trace token + etp-1 ($etp_msgq_p->m[1]) 0 + if $etp_msgq_p == $etp_msgq->save + printf ", <=\n" + else + printf "\n" + end + if ($etp_msgq_i > 0) && $etp_msgq_next + printf " " + end + # + set $etp_msgq_prev = $etp_msgq_p + set $etp_msgq_p = $etp_msgq_next + end + if $etp_msgq_i != 0 + printf "#MsgQShort<%d>\n", $etp_msgq_i + end + if $etp_msgq_p != 0 + printf "#MsgQLong<%#lx%p>\n", (unsigned long)$etp_msgq_p + end + if $etp_msgq_prev != $etp_msgq->last + printf "#MsgQEndError<%#lx%p>\n", (unsigned long)$etp_msgq_prev + end +end + +document etp-msgq +%--------------------------------------------------------------------------- +% etp-msgq ErlMessageQueue* +% +% Take an ErlMessageQueue* and print the contents of the message queue. +% Sequential trace tokens are included in comments and +% the current match position in the queue is marked '<='. +% +% A process's message queue is process_tab[i]->msg. +%--------------------------------------------------------------------------- +end + + + +define etpf-msgq +# Args: Process* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-msgq ($arg0) + set $etp_flat = 0 +end + +document etpf-msgq +%--------------------------------------------------------------------------- +% etpf-msgq ErlMessageQueue* +% +% Same as 'etp-msgq' but print the messages using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-stacktrace +# Args: Process* +# +# Non-reentrant +# + set $etp_stacktrace_p = ($arg0)->stop + set $etp_stacktrace_end = ($arg0)->hend + printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p + etp ($arg0)->cp + while $etp_stacktrace_p < $etp_stacktrace_end + if ($etp_stacktrace_p[0] & 0x3) == 0x0 + # Continuation pointer + etp $etp_stacktrace_p[0] + end + set $etp_stacktrace_p++ + end +end + +document etp-stacktrace +%--------------------------------------------------------------------------- +% etp-stacktrace Process* +% +% Take an Process* and print a stactrace for the process. +% The stacktrace consists just of the pushed code continuation +% pointers on the stack, the most recently pushed first. +%--------------------------------------------------------------------------- +end + +define etp-stackdump +# Args: Process* +# +# Non-reentrant +# + set $etp_stackdump_p = ($arg0)->stop + set $etp_stackdump_end = ($arg0)->hend + printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p + etp ($arg0)->cp + while $etp_stackdump_p < $etp_stackdump_end + etp $etp_stackdump_p[0] + set $etp_stackdump_p++ + end +end + +document etp-stackdump +%--------------------------------------------------------------------------- +% etp-stackdump Process* +% +% Take an Process* and print a stackdump for the process. +% The stackdump consists of all pushed values on the stack. +% All code continuation pointers are preceeded with a line +% of dashes to make the stack frames more visible. +%--------------------------------------------------------------------------- +end + +define etpf-stackdump +# Args: Process* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-stackdump ($arg0) + set $etp_flat = 0 +end + +document etpf-stackdump +%--------------------------------------------------------------------------- +% etpf-stackdump Process* +% +% Same as etp-stackdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-dictdump +# Args: ProcDict* +# +# Non-reentrant +# + set $etp_dictdump = ($arg0) + if $etp_dictdump + set $etp_dictdump_n = \ + $etp_dictdump->homeSize + $etp_dictdump->splitPosition + set $etp_dictdump_i = 0 + set $etp_dictdump_written = 0 + if $etp_dictdump_n > $etp_dictdump->size + set $etp_dictdump_n = $etp_dictdump->size + end + set $etp_dictdump_cnt = $etp_dictdump->numElements + printf "%% Dictionary (%d):\n[", $etp_dictdump_cnt + while $etp_dictdump_i < $etp_dictdump_n && \ + $etp_dictdump_cnt > 0 + set $etp_dictdump_p = $etp_dictdump->data[$etp_dictdump_i] + if $etp_dictdump_p != $etp_nil + if ((Eterm)$etp_dictdump_p & 0x3) == 0x2 + # Boxed + if $etp_dictdump_written + printf ",\n " + else + set $etp_dictdump_written = 1 + end + etp-1 $etp_dictdump_p 0 + set $etp_dictdump_cnt-- + else + while ((Eterm)$etp_dictdump_p & 0x3) == 0x1 && \ + $etp_dictdump_cnt > 0 + # Cons ptr + if $etp_dictdump_written + printf ",\n " + else + set $etp_dictdump_written = 1 + end + etp-1 (((Eterm*)((Eterm)$etp_dictdump_p&~0x3))[0]) 0 + set $etp_dictdump_cnt-- + set $etp_dictdump_p = ((Eterm*)((Eterm)$etp_dictdump_p & ~0x3))[1] + end + if $etp_dictdump_p != $etp_nil + printf "#DictSlotError<%d>:", $etp_dictdump_i + set $etp_dictdump_flat = $etp_flat + set $etp_flat = 1 + etp-1 ((Eterm)$etp_dictdump_p) 0 + set $etp_flat = $etp_dictdump_flat + end + end + end + set $etp_dictdump_i++ + end + if $etp_dictdump_cnt != 0 + printf "#DictCntError<%d>, ", $etp_dictdump_cnt + end + else + printf "%% Dictionary (0):\n[" + end + printf "].\n" +end + +document etp-dictdump +%--------------------------------------------------------------------------- +% etp-dictdump ErlProcDict* +% +% Take an ErlProcDict* and print all entries in the process dictionary. +%--------------------------------------------------------------------------- +end + +define etpf-dictdump +# Args: ErlProcDict* +# +# Non-reentrant +# + set $etp_flat = 1 + etp-dictdump ($arg0) + set $etp_flat = 0 +end + +document etpf-dictdump +%--------------------------------------------------------------------------- +% etpf-dictdump ErlProcDict* +% +% Same as etp-dictdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + + + +define etp-offheapdump +# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) +# +# Non-reentrant +# + set $etp_offheapdump_p = ($arg0) + set $etp_offheapdump_i = 0 + set $etp_offheapdump_ + printf "%% Offheap dump:\n[" + while ($etp_offheapdump_p != 0) && ($etp_offheapdump_i < $etp_max_depth) + if ((Eterm)$etp_offheapdump_p & 0x3) == 0x0 + if $etp_offheapdump_i > 0 + printf ",\n " + end + etp-1 ((Eterm)$etp_offheapdump_p|0x2) 0 + set $etp_offheapdump_p = $etp_offheapdump_p->next + set $etp_offheapdump_i++ + else + printf "#TaggedPtr<%#x>", $etp_offheapdump_p + set $etp_offheapdump_p = 0 + end + end + printf "].\n" +end + +document etp-offheapdump +%--------------------------------------------------------------------------- +% etp-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) +% +% Take an pointer to a linked list and print the terms in the list +% up to the max depth. +%--------------------------------------------------------------------------- +end + +define etpf-offheapdump +# Args: ( ExternalThing* | ProcBin* | ErlFunThing* ) +# +# Non-reentrant +# + set $etp_flat = 1 + etp-offheapdump ($arg0) + set $etp_flat = 0 +end + +document etpf-offheapdump +%--------------------------------------------------------------------------- +% etpf-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* ) +% +% Same as etp-offheapdump but print the values using etpf (flat). +%--------------------------------------------------------------------------- +end + +define etp-print-procs +# Args: Eterm +# +# Non-reentrant +# + etp-print-procs-1 +end + +define etp-print-procs-1 +# Args: Eterm* +# +# Non-reentrant +# + set $etp_print_procs_q = erts_max_processes / 10 + set $etp_print_procs_r = erts_max_processes % 10 + set $etp_print_procs_t = 10 + set $etp_print_procs_m = $etp_print_procs_q + if $etp_print_procs_r > 0 + set $etp_print_procs_m++ + set $etp_print_procs_r-- + end + set $etp_print_procs_i = 0 + set $etp_print_procs_found = 0 + while $etp_print_procs_i < erts_max_processes + if process_tab[$etp_print_procs_i] + printf "%d: ", $etp_print_procs_i + etp-1 process_tab[$etp_print_procs_i]->id + printf " " + etp-1 ((Eterm)(process_tab[$etp_print_procs_i]->i)) + printf " heap=%d/%d(%d)", process_tab[$etp_print_procs_i]->htop - process_tab[$etp_print_procs_i]->heap, \ + process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->heap, \ + process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->stop + printf " old=%d/%d ", process_tab[$etp_print_procs_i]->old_htop - process_tab[$etp_print_procs_i]->old_heap, \ + process_tab[$etp_print_procs_i]->old_hend - process_tab[$etp_print_procs_i]->old_heap + printf " mbuf_sz=%d ", process_tab[$etp_print_procs_i]->mbuf_sz + printf " min=%d ", process_tab[$etp_print_procs_i]->min_heap_size + printf " flags=%x ", process_tab[$etp_print_procs_i]->flags + printf " msgs=%d ", process_tab[$etp_print_procs_i]->msg.len + printf "\n" + end + set $etp_print_procs_i++ + if $etp_print_procs_i > $etp_print_procs_m + printf "%% %d%%...\n", $etp_print_procs_t + set $etp_print_procs_t += 10 + set $etp_print_procs_m += $etp_print_procs_q + if $etp_print_procs_r > 0 + set $etp_print_procs_m++ + set $etp_print_procs_r-- + end + end + end + printf "%% 100%%.\n" +end + +document etp-print-procs +%--------------------------------------------------------------------------- +% etp-print-procs Eterm +% +% Print some information about ALL processes. +%--------------------------------------------------------------------------- +end + + +define etp-search-heaps +# Args: Eterm +# +# Non-reentrant +# + printf "%% Search all (<%u) process heaps for ", erts_max_processes + set $etp_flat = 1 + etp-1 ($arg0) 0 + set $etp_flat = 0 + printf ":...\n" + etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) +end + +define etp-search-heaps-1 +# Args: Eterm* +# +# Non-reentrant +# + set $etp_search_heaps_q = erts_max_processes / 10 + set $etp_search_heaps_r = erts_max_processes % 10 + set $etp_search_heaps_t = 10 + set $etp_search_heaps_m = $etp_search_heaps_q + if $etp_search_heaps_r > 0 + set $etp_search_heaps_m++ + set $etp_search_heaps_r-- + end + set $etp_search_heaps_i = 0 + set $etp_search_heaps_found = 0 + while $etp_search_heaps_i < erts_max_processes + if process_tab[$etp_search_heaps_i] + if (process_tab[$etp_search_heaps_i]->heap <= ($arg0)) && \ + (($arg0) < process_tab[$etp_search_heaps_i]->hend) + printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \ + ($arg0)-process_tab[$etp_search_heaps_i]->heap + end + if (process_tab[$etp_search_heaps_i]->old_heap <= ($arg0)) && \ + (($arg0) <= process_tab[$etp_search_heaps_i]->old_hend) + printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \ + ($arg0)-process_tab[$etp_search_heaps_i]->old_heap + end + set $etp_search_heaps_cnt = 0 + set $etp_search_heaps_p = process_tab[$etp_search_heaps_i]->mbuf + while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth) + set $etp_search_heaps_cnt++ + if (&($etp_search_heaps_p->mem) <= ($arg0)) && \ + (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) + printf "process_tab[%d]->mbuf(%d)+%d\n", \ + $etp_search_heaps_i, $etp_search_heaps_cnt, \ + ($arg0)-&($etp_search_heaps_p->mem) + end + set $etp_search_heaps_p = $etp_search_heaps_p->next + end + if $etp_search_heaps_p + printf "process_tab[%d] %% Too many HeapFragments\n", \ + $etp_search_heaps_i + end + end + set $etp_search_heaps_i++ + if $etp_search_heaps_i > $etp_search_heaps_m + printf "%% %d%%...\n", $etp_search_heaps_t + set $etp_search_heaps_t += 10 + set $etp_search_heaps_m += $etp_search_heaps_q + if $etp_search_heaps_r > 0 + set $etp_search_heaps_m++ + set $etp_search_heaps_r-- + end + end + end + printf "%% 100%%.\n" +end + +document etp-search-heaps +%--------------------------------------------------------------------------- +% etp-search-heaps Eterm +% +% Search all process heaps in process_tab[], including the heap fragments +% (process_tab[]->mbuf) for the specified Eterm. +%--------------------------------------------------------------------------- +end + + + +define etp-search-alloc +# Args: Eterm +# +# Non-reentrant +# + printf "%% Search allocated memory blocks for " + set $etp_flat = 1 + etp-1 ($arg0) 0 + set $etp_flat = 0 + printf ":...\n" + set $etp_search_alloc_n = sizeof(erts_allctrs) / sizeof(*erts_allctrs) + set $etp_search_alloc_i = 0 + while $etp_search_alloc_i < $etp_search_alloc_n + if erts_allctrs[$etp_search_alloc_i].alloc + set $etp_search_alloc_f = (erts_allctrs+$etp_search_alloc_i) + while ($etp_search_alloc_f->alloc == debug_alloc) || \ + ($etp_search_alloc_f->alloc == stat_alloc) || \ + ($etp_search_alloc_f->alloc == map_stat_alloc) + set $etp_search_alloc_f = \ + (ErtsAllocatorFunctions_t*)$etp_search_alloc_f->extra + end + if ($etp_search_alloc_f->alloc != erts_sys_alloc) && \ + ($etp_search_alloc_f->alloc != erts_fix_alloc) + if ($etp_search_alloc_f->alloc == erts_alcu_alloc) || \ + ($etp_search_alloc_f->alloc == erts_alcu_alloc_ts) + # alcu alloc + set $etp_search_alloc_e = (Allctr_t*)$etp_search_alloc_f->extra + # mbc_list + set $etp_search_alloc_p = $etp_search_alloc_e->mbc_list.first + set $etp_search_alloc_cnt = 0 + while $etp_search_alloc_p && \ + ($etp_search_alloc_cnt < $etp_max_depth) + set $etp_search_alloc_cnt++ + if $etp_search_alloc_p <= ($arg0) && \ + ($arg0) < (char*)$etp_search_alloc_p + \ + ($etp_search_alloc_p->chdr & (Uint)~7) + printf "erts_allctrs[%d] %% %salloc: mbc_list: %d\n", \ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ + $etp_search_alloc_cnt + end + if $etp_search_alloc_p == $etp_search_alloc_e->mbc_list.last + if $etp_search_alloc_p->next + printf \ + "erts_allctrs[%d] %% %salloc: mbc_list.last error %p\n",\ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ + $etp_search_alloc_p + end + set $etp_search_alloc_p = 0 + else + set $etp_search_alloc_p = $etp_search_alloc_p->next + end + end + if $etp_search_alloc_p + printf "erts_allctrs[%d] %% %salloc: too large mbc_list %p\n", \ + $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, + $ept_search_alloc_p + end + # sbc_list + set $etp_search_alloc_p = $etp_search_alloc_e->sbc_list.first + set $etp_search_alloc_cnt = 0 + while $etp_search_alloc_p && \ + ($etp_search_alloc_cnt < $etp_max_depth) + set $etp_search_alloc_cnt++ + if $etp_search_alloc_p <= ($arg0) && \ + ($arg0) < (char*)$etp_search_alloc_p + \ + ($etp_search_alloc_p->chdr & (Uint)~7) + printf "erts_allctrs[%d] %% %salloc: sbc_list: %d\n", \ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \ + $etp_search_alloc_cnt + end + if $etp_search_alloc_p == $etp_search_alloc_e->sbc_list.last + if $etp_search_alloc_p->next + printf \ + "erts_allctrs[%d] %% %salloc: sbc_list.last error %p",\ + $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\ + $etp_search_alloc_p + end + set $etp_search_alloc_p = 0 + else + set $etp_search_alloc_p = $etp_search_alloc_p->next + end + end + if $etp_search_alloc_p + printf "erts_allctrs[%d] %% %salloc: too large sbc_list %p\n", \ + $ept_search_alloc_i, $etp_search_alloc_e->name_prefix, + $ept_search_alloc_p + end + else + printf "erts_allctrs[%d] %% %s: unknown allocator\n", \ + $etp_search_alloc_i, erts_alc_a2ad[$etp_search_alloc_i] + end + end + end + set $etp_search_alloc_i++ + end +end + +document etp-search-alloc +%--------------------------------------------------------------------------- +% etp-search-heaps Eterm +% +% Search all internal allocator memory blocks for for the specified Eterm. +%--------------------------------------------------------------------------- +end + + + +define etp-overlapped-heaps +# Args: +# +# Non-reentrant +# + printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" + set $etp_overlapped_heaps_q = erts_max_processes / 10 + set $etp_overlapped_heaps_r = erts_max_processes % 10 + set $etp_overlapped_heaps_t = 10 + set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q + if $etp_overlapped_heaps_r > 0 + set $etp_overlapped_heaps_m++ + set $etp_overlapped_heaps_r-- + end + set $etp_overlapped_heaps_i = 0 + set $etp_overlapped_heaps_found = 0 + dump binary value etp-commands.bin 'o' + append binary value etp-commands.bin 'v' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'r' + append binary value etp-commands.bin 'l' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'd' + append binary value etp-commands.bin '-' + append binary value etp-commands.bin 'h' + append binary value etp-commands.bin 'e' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'p' + append binary value etp-commands.bin 's' + append binary value etp-commands.bin '\0' + while $etp_overlapped_heaps_i < erts_max_processes + if process_tab[$etp_overlapped_heaps_i] + append binary value etp-commands.bin \ + (Eterm)$etp_overlapped_heaps_i + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->heap + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->hend + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->old_heap + append binary value etp-commands.bin \ + (Eterm)process_tab[$etp_overlapped_heaps_i]->old_hend + set $etp_overlapped_heaps_p = process_tab[$etp_overlapped_heaps_i]->mbuf + set $etp_overlapped_heaps_cnt = 0 + while $etp_overlapped_heaps_p && \ + ($etp_overlapped_heaps_cnt < $etp_max_depth) + set $etp_overlapped_heaps_cnt++ + append binary value etp-commands.bin \ + (Eterm)$etp_overlapped_heaps_p + append binary value etp-commands.bin \ +(Eterm)(&($etp_overlapped_heaps_p->mem)+$etp_overlapped_heaps_p->size) + set $etp_overlapped_heaps_p = $etp_overlapped_heaps_p->next + end + if $etp_overlapped_heaps_p + printf "process_tab[%d] %% Too many HeapFragments\n", \ + $etp_overlapped_heaps_i + end + append binary value etp-commands.bin (Eterm)0x0 + append binary value etp-commands.bin (Eterm)0x0 + end + set $etp_overlapped_heaps_i++ + if $etp_overlapped_heaps_i > $etp_overlapped_heaps_m + printf "%% %d%%...\n", $etp_overlapped_heaps_t + set $etp_overlapped_heaps_t += 10 + set $etp_overlapped_heaps_m += $etp_overlapped_heaps_q + if $etp_overlapped_heaps_r > 0 + set $etp_overlapped_heaps_m++ + set $etp_overlapped_heaps_r-- + end + end + end + etp-run +end + +document etp-overlapped-heaps +%--------------------------------------------------------------------------- +% etp-overlapped-heaps +% +% Dump all process heap addresses in process_tab[], including +% the heap fragments in binary format on the file etp-commands.bin. +% Then call etp_commands:file/1 to analyze if any heaps overlap. +% +% Requires 'erl' in the path and 'etp_commands.beam' in 'erl's search path. +%--------------------------------------------------------------------------- +end + + + +define etp-chart +# Args: Process* +# +# Non-reentrant + etp-chart-start ($arg0) + set ($arg0) = ($arg0) + etp-msgq (($arg0)->msg) + etp-stackdump ($arg0) + etp-dictdump (($arg0)->dictionary) + etp-dictdump (($arg0)->debug_dictionary) + printf "%% Dumping other process data...\n" + etp ($arg0)->seq_trace_token + etp ($arg0)->fvalue + printf "%% Dumping done.\n" + etp-chart-print +end + +document etp-chart +%--------------------------------------------------------------------------- +% etp-chart Process* +% +% Dump all process data to the file "etp-commands.bin" and then use +% the Erlang support module to print a memory chart of all terms. +%--------------------------------------------------------------------------- +end + + + +define etp-chart-start +# Args: Process* +# +# Non-reentrant + set $etp_chart = 1 + set $etp_chart_id = 0 + set $etp_chart_start_p = ($arg0) + dump binary value etp-commands.bin 'c' + append binary value etp-commands.bin 'h' + append binary value etp-commands.bin 'a' + append binary value etp-commands.bin 'r' + append binary value etp-commands.bin 't' + append binary value etp-commands.bin '\0' + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->heap) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->high_water) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->hend) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_heap) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_hend) + set $etp_chart_start_cnt = 0 + set $etp_chart_start_p = $etp_chart_start_p->mbuf + while $etp_chart_start_p && ($etp_chart_start_cnt < $etp_max_depth) + set $etp_chart_start_cnt++ + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->mem) + append binary value etp-commands.bin (Eterm)($etp_chart_start_p->size) + set $etp_chart_start_p = $etp_chart_start_p->next + end + append binary value etp-commands.bin (Eterm)(0) + append binary value etp-commands.bin (Eterm)(0) + if $etp_chart_start_p + printf "%% Too many HeapFragments\n" + end +end + +document etp-chart-start +%--------------------------------------------------------------------------- +% etp-chart-start Process* +% +% Dump a chart head to the file "etp-commands.bin". +%--------------------------------------------------------------------------- +end + + + +define etp-chart-entry-1 +# Args: Eterm, int depth, int words +# +# Reentrant capable + if ($arg1) == 0 + set $etp_chart_id++ + printf "#%d:", $etp_chart_id + end + append binary value etp-commands.bin ($arg0)&~0x3 + append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) + append binary value etp-commands.bin (Eterm)$etp_chart_id + append binary value etp-commands.bin (Eterm)($arg1) +# printf "<dumped %#x %lu %lu %lu>", ($arg0)&~0x3, \ +# (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) +end + + + +define etp-chart-print + set $etp_chart = 0 + etp-run +end + +document etp-chart-print +%--------------------------------------------------------------------------- +% etp-chart-print Process* +% +% Print a memory chart of the dumped data in "etp-commands.bin", and stop +% chart recording. +%--------------------------------------------------------------------------- +end + +############################################################################ +# ETS table debug +# + +define etp-ets-tables +# Args: +# +# Non-reentrant + printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs + while $etp_ets_tables_i < db_max_tabs + if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0 + printf "%% %d:", $etp_ets_tables_i + etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0 + printf " " + etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0 + printf "\n" + end + set $etp_ets_tables_i++ + end + set $etp_ets_tables_i = 0 +end + +document etp-ets-tables +%--------------------------------------------------------------------------- +% etp-ets-tables +% +% Dump all ETS table names and their indexies. +%--------------------------------------------------------------------------- +end + +define etp-ets-tabledump +# Args: int tableindex +# +# Non-reentrant + printf "%% Dumping ETS table %d:", ($arg0) + set $etp_ets_tabledump_n = 0 + set $etp_ets_tabledump_t = meta_main_tab[($arg0)].u.tb + set $etp_ets_tabledump_i = 0 + etp-1 ($etp_ets_tabledump_t->common.the_name) 0 + printf " status=%#x\n", $etp_ets_tabledump_t->common.status + if $etp_ets_tabledump_t->common.status & 0x130 + # Hash table + set $etp_ets_tabledump_h = $etp_ets_tabledump_t->hash + printf "%% nitems=%d\n", $etp_ets_tabledump_t->common.nitems + while $etp_ets_tabledump_i < $etp_ets_tabledump_h->nactive + set $etp_ets_tabledump_l = $etp_ets_tabledump_h->seg \ + [$etp_ets_tabledump_i>>8][$etp_ets_tabledump_i&0xFF] + if $etp_ets_tabledump_l + printf "%% Slot %d:\n", $etp_ets_tabledump_i + while $etp_ets_tabledump_l + if $etp_ets_tabledump_n + printf "," + else + printf "[" + end + set $etp_ets_tabledump_n++ + etp-1 ((Eterm)($etp_ets_tabledump_l->dbterm.tpl)|0x2) 0 + if $etp_ets_tabledump_l->hvalue == ((unsigned long)-1) + printf "% *\n" + else + printf "\n" + end + set $etp_ets_tabledump_l = $etp_ets_tabledump_l->next + if $etp_ets_tabledump_n >= $etp_max_depth + set $etp_ets_tabledump_l = 0 + end + end + end + set $etp_ets_tabledump_i++ + end + if $etp_ets_tabledump_n + printf "].\n" + end + else + printf "%% Not a hash table\n" + end +end + +document etp-ets-tabledump +%--------------------------------------------------------------------------- +% etp-ets-tabledump Slot +% +% Dump an ETS table with a specified slot index. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Erlang support module handling +# + +define etp-run + shell make -f "${ROOTDIR:?}/erts/etc/unix/etp_commands.mk" \ + ROOTDIR="${ROOTDIR:?}" ETP_DATA="etp-commands.bin" +end + +document etp-run +%--------------------------------------------------------------------------- +% etp-run +% +% Make and run the Erlang support module on the input file +% "erl-commands.bin". The environment variable ROOTDIR must +% be set to find $ROOTDIR/erts/etc/unix/etp_commands.mk. +% +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Toolbox parameter handling +# + +define etp-set-max-depth + if ($arg0) > 0 + set $etp_max_depth = ($arg0) + else + echo %%%Error: max-depth <= 0 %%%\n + end +end + +document etp-set-max-depth +%--------------------------------------------------------------------------- +% etp-set-max-depth Depth +% +% Set the max term depth to use for etp. The term dept limit +% works in both depth and width, so if you set the max depth to 10, +% an 11 element flat tuple will be truncated. +%--------------------------------------------------------------------------- +end + +define etp-set-max-string-length + if ($arg0) > 0 + set $etp_max_string_length = ($arg0) + else + echo %%%Error: max-string-length <= 0 %%%\n + end +end + +document etp-set-max-string-length +%--------------------------------------------------------------------------- +% etp-set-max-strint-length Length +% +% Set the max string length to use for ept when printing lists +% that can be shown as printable strings. Printable strings +% that are longer will be truncated, and not even checked if +% they really are printable all the way to the end. +%--------------------------------------------------------------------------- +end + +define etp-show + printf "etp-set-max-depth %d\n", $etp_max_depth + printf "etp-set-max-string-length %d\n", $etp_max_string_length +end + +document etp-show +%--------------------------------------------------------------------------- +% etp-show +% +% Show the commands needed to set all etp parameters +% to their current value. +%--------------------------------------------------------------------------- +end + +############################################################################ +# Init +# + +define etp-init + set $etp_arch64 = (sizeof(void *) == 8) + if $etp_arch64 + set $etp_nil = 0xfffffffffffffffb + else + set $etp_nil = 0xfffffffb + end + set $etp_flat = 0 + set $etp_chart_id = 0 + set $etp_chart = 0 + + set $etp_max_depth = 20 + set $etp_max_string_length = 100 + + set $etp_ets_tables_i = 0 +end + +document etp-init +%--------------------------------------------------------------------------- +% Use etp-help for a command overview and general help. +% +% To use the Erlang support module, the environment variable ROOTDIR +% must be set to the toplevel installation directory of Erlang/OTP, +% so the etp-commands file becomes: +% $ROOTDIR/erts/etc/unix/etp-commands +% Also, erl and erlc must be in the path. +%--------------------------------------------------------------------------- +end + + +etp-init +help etp-init +etp-show diff --git a/erts/etc/unix/etp_commands.erl b/erts/etc/unix/etp_commands.erl new file mode 100644 index 0000000000..66cb76edbc --- /dev/null +++ b/erts/etc/unix/etp_commands.erl @@ -0,0 +1,173 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(etp_commands). + +-export([file/1]). + +file([Fname]) -> + Result = (catch file_1(Fname)), + io:format("% ~p~n", [Result]), + init:stop(). + +file_1(Fname) -> + io:format("% Reading ~p...~n", [Fname]), + {ok,Fd} = file:open(Fname, [read,binary]), + case read_op(Fd, 128) of + "chart" -> + io:format("% Reading heap chart data...~n"), + chart_scan(Fd); + "overlapped-heaps" -> + io:format("% Reading overlapped-heaps data...~n"), + overlapped_scan(Fd) + end. + +read_op(_Fd, 0) -> + []; +read_op(Fd, N) -> + case file:read(Fd, 1) of + {ok,<<0>>} -> []; + {ok,<<C>>} -> [C|read_op(Fd, N-1)] + end. + + + +overlapped_scan(Fd) -> + overlapped_scan_1(Fd, []). + +overlapped_scan_1(Fd, R) -> + case file:read(Fd, 4*5) of + eof -> + io:format("% Analyzing overlaps...~n"), + overlapped_analyze(lists:sort(R)); + {ok,<<Id:32/native,Heap:32/native,Hend:32/native, + 0:32/native,0:32/native>>} + when Heap < Hend -> + overlapped_scan_to_0(Fd, [{{Heap,Hend},{Id,heap}}|R], Id, 1); + {ok,<<Id:32/native,Heap:32/native,Hend:32/native, + OldHeap:32/native,OldHend:32/native>>} + when Heap < Hend, OldHeap < OldHend-> + overlapped_scan_to_0(Fd, [{{Heap,Hend},{Id,heap}}, + {{OldHeap,OldHend},{Id,old_heap}}|R], + Id, 1) + end. + +overlapped_scan_to_0(Fd, R, Id, Cnt) -> + case file:read(Fd, 4*2) of + {ok,<<0:32/native,0:32/native>>} -> + overlapped_scan_1(Fd, R); + {ok,<<Heap:32/native,Hend:32/native>>} + when Heap < Hend -> + overlapped_scan_to_0(Fd, + [{{Heap,Hend},{Id,{heap_fragment,Cnt}}}|R], + Id, Cnt+1); + eof -> + io:format("% Premature end of dump: ~p~n", [Id,Cnt|R]) + end. + +overlapped_analyze([]) -> + io:format("% Oops! was that file empty?~n"); +overlapped_analyze([{{_,Hend1},_}|[{{Heap2,_},_}|_]=R]) + when Hend1 =< Heap2 -> + overlapped_analyze(R); +overlapped_analyze([{Addrs1,Tag1}|[{Addrs2,Tag2}|_]=R]) -> + io:format("% ~p overlaps ~p (~p,~p)~n", [Tag1,Tag2,Addrs1,Addrs2]), + overlapped_analyze(R); +overlapped_analyze([_]) -> + io:format("% End of overlaps~n"). + + +chart_scan(Fd) -> + {ok,<<Heap:32/native,HighWater:32/native,Hend:32/native, + OldHeap:32/native,OldHend:32/native>>} = file:read(Fd, 4*5), + chart_scan_1(Fd, + [{Heap,Heap,heap,0}, + {HighWater,HighWater,high_water,0}, + {Hend,Hend,hend,0}, + {OldHeap,OldHeap,old_heap,0}, + {OldHend,OldHend,old_hend,0}|chart_scan_hdr(Fd)]). + +chart_scan_hdr(Fd) -> + chart_scan_hdr_2(0, chart_scan_hdr_1(Fd)). + +chart_scan_hdr_1(Fd) -> + case file:read(Fd, 4*2) of + eof -> []; + {ok,<<0:32/native,0:32/native>>} -> []; + {ok,<<Start:32/native,Size:32/native>>} -> + [{Start,Size}|chart_scan_hdr_1(Fd)] + end. + +chart_scan_hdr_2(_N, []) -> []; +chart_scan_hdr_2(N, [{Start,End}|T]) when Start =< End -> + [{Start,Start,{heap_frag,N},0},{End,End,{heap_frag_end,N},0} + |chart_scan_hdr_2(N+1, T)]. + +chart_scan_1(Fd, R) -> + case file:read(Fd, 4*4) of + eof -> + io:format("% Analyzing heap chart...~n"), + chart_analyze(lists:sort(R)); + {ok, + <<Addr:32/native,Size:32/native,Id:32/native,Depth:32/native>>} -> + chart_scan_1(Fd, [{Addr,Addr+Size,Id,Depth}|R]) + end. + +%-define(raw_chart_dump, 1). +-ifdef(raw_chart_dump). + +chart_analyze([]) -> + io:format("% End of chart~n"); +chart_analyze([{S,E,Id,D}|R]) -> + io:format("% ~.16x-~.16x: ~w[~w]~n", + [S,"0x",E,"0x",Id,D]), + chart_analyze(R). + +-else. + +chart_analyze([]) -> + io:format("% ***Oops, was chart empty?***~n"); +chart_analyze([{S,_,Id,D}=X|R]) -> + io:format("% ~.16x: ~w[~w", [S,"0x",Id,D]), + chart_analyze_1(R, X). + +chart_analyze_1([{S,E,Id,D}=X|R], {S,E,Id,_}) -> + io:format(",~w", [D]), + chart_analyze_1(R, X); +chart_analyze_1([{S,E,Id,D}=X|R], {S,E,_,_}) -> + io:format("],~w[~w", [Id,D]), + chart_analyze_1(R, X); +chart_analyze_1(R, X) -> + io:format("]~n"), + chart_analyze_2(R, X). + +chart_analyze_2([], {_,E,_,_}) -> + io:format("% ~.16x: End of chart~n", [E,"0x"]); +chart_analyze_2([{S,_,_,_}|_]=R, {_,E,_,_}) -> + if E == S -> + chart_analyze(R); + E < S -> + io:format("% ~.16x:~n", [E,"0x"]), + chart_analyze(R); + true -> + io:format("% ~.16x: ***Overlap***~n", [E,"0x"]), + chart_analyze(R) + end. + +-endif. diff --git a/erts/etc/unix/etp_commands.mk b/erts/etc/unix/etp_commands.mk new file mode 100644 index 0000000000..1d9a269b68 --- /dev/null +++ b/erts/etc/unix/etp_commands.mk @@ -0,0 +1,27 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2005-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +MAKE_AND_EXECUTE_ETP_COMMANDS : $(ETP_DATA) etp_commands.beam + erl -noshell -run etp_commands file "$(ETP_DATA)" + +.PHONY : MAKE_AND_EXECUTE_ETP_COMMANDS + +etp_commands.beam : $(ROOTDIR)/erts/etc/unix/etp_commands.erl $(ROOTDIR)/erts/etc/unix/etp_commands.mk + erlc $(ROOTDIR)/erts/etc/unix/etp_commands.erl + diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages new file mode 100644 index 0000000000..2c4f6eee4f --- /dev/null +++ b/erts/etc/unix/format_man_pages @@ -0,0 +1,149 @@ +#!/bin/sh +# +# +# %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% +# +# Format man_pages +# + +ERL_ROOT=$1 + +echo "Formatting manual pages (this may take a while...)" + +if [ -z "$ERL_ROOT" -o ! -d "$ERL_ROOT" ] +then + echo "Install: need ERL_ROOT directory as argument" + exit 1 +fi + +if [ `echo $ERL_ROOT | awk '{ print substr($1,1,1) }'` != "/" ] +then + echo "Install: need an absolute path to ERL_ROOT" + exit 1 +fi + +# +# Fetch target system. +# +SYS=`(uname -s) 2>/dev/null` || SYS=unknown +REL=`(uname -r) 2>/dev/null` || REL=unknown +case $SYS:$REL in + SunOS:5.*) + TARGET=sunos5 ;; + Linux:*) + TARGET=linux ;; + Darwin:9.*) + TARGET=darwin ;; + OpenBSD:3.*) + TARGET=openbsd ;; + *) + TARGET="" ;; +esac + +# +# Create the 'cat' directories (probably not needed) +# + +cd $ERL_ROOT + +if [ ! -d man/cat1 ] +then + mkdir man/cat1 +fi + +if [ ! -d man/cat3 ] +then + mkdir man/cat3 +fi + +if [ ! -d man/cat4 ] +then + mkdir man/cat4 +fi + +if [ ! -d man/cat6 ] +then + mkdir man/cat6 +fi + +# +# Cleanup old formatting +# + +cd $ERL_ROOT/man + +rm -f whatis windex + +# Remove old cat files +rm -f cat*/*.[0-9]* *.txt + +# +# Create new formatted pages +# + +case :"$TARGET" in +:linux|:darwin) + # Do not build whatis database, since makewhatis can only run by root + # echo "whatis database not created, since makewhatis can only be run by root." + ## We would have run + ## /usr/sbin/makewhatis -v $ERL_ROOT/man -c $ERL_ROOT/man > /dev/null 2>&1 + + if [ ! -x /usr/bin/groff ]; then + echo "Cannot find groff - no formating of manual pages" + exit + fi + + echo "Creating cat files ..." + + # Create cat files + for dir in man* + do + cd $dir + for file in *.[0-9]* + do + if [ -f $file ]; then + name=`echo $file | sed 's/\.[^.]*$//'` + sec=`echo $file | sed 's/.*\.//'` + /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \ + > $ERL_ROOT/man/cat$sec/$file + fi + done + cd .. + done + ;; +:*) + if [ -f "/vmunix" ]; then + CATMAN=/usr/etc/catman + elif [ "$TARGET" = "openbsd" ]; then + CATMAN=/usr/sbin/catman + else + CATMAN=/usr/bin/catman + fi + + if [ "$TARGET" = "sunos5" ] + then + # Special processing of footer + rm -f /tmp/erltmac_an + sed 's/Last change://g' /usr/share/lib/tmac/an > /tmp/erltmac_an + $CATMAN -M $ERL_ROOT/man -T /tmp/erltmac_an > /dev/null 2>&1 + rm -f /tmp/erltmac_an + fi + + $CATMAN -M $ERL_ROOT/man > /dev/null 2>&1 + ;; +esac diff --git a/erts/etc/unix/makewhatis b/erts/etc/unix/makewhatis new file mode 100644 index 0000000000..047c6efdfa --- /dev/null +++ b/erts/etc/unix/makewhatis @@ -0,0 +1,327 @@ +#!/bin/sh +# makewhatis: create the whatis database +# Created: Sun Jun 14 10:49:37 1992 +# Revised: Sat Jan 8 14:12:37 1994 by faith@cs.unc.edu +# Revised: Sat Mar 23 17:56:18 1996 by micheal@actrix.gen.nz +# Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu) +# May be freely distributed and modified as long as copyright is retained. +# +# Wed Dec 23 13:27:50 1992: Rik Faith (faith@cs.unc.edu) applied changes +# based on Mitchum DSouza (mitchum.dsouza@mrc-apu.cam.ac.uk) cat patches. +# Also, cleaned up code and make it work with NET-2 doc pages. +# +# makewhatis-1.4: aeb 940802, 941007, 950417 +# Fixed so that the -c option works correctly for the cat pages +# on my machine. Fix for -u by Nan Zou (nan@ksu.ksu.edu). +# Many minor changes. +# The -s option is undocumented, and may well disappear again. +# +# Sat Mar 23 1996: Michael Hamilton (michael@actrix.gen.nz). +# I changed the script to invoke gawk only once for each directory tree. +# This speeds things up considerably (from 30 minutes down to 1.5 minutes +# on my 486DX66). +# 960401 - aeb: slight adaptation to work correctly with cat pages. +# 960510 - added fixes by brennan@raven.ca.boeing.com, author of mawk. +# 971012 - replaced "test -z" - it doesnt work on SunOS 4.1.3_U1. +# 980710 - be more careful with TMPFILE +# +# Note for Slackware users: "makewhatis -v -w -c" will work. + +# %ExternalCopyright% +PATH=/usr/bin:/bin + +DEFMANPATH=/usr/man +DEFCATPATH=/usr/man/preformat:/usr/man + +# Find a place for our temporary files. If security is not a concern, use +# TMPFILE=/tmp/whatis$$; TMPFILEDIR=none +# Of course makewhatis should only have the required permissions +# (for reading and writing directories like /usr/man). +# We try here to be careful (and avoid preconstructed symlinks) +# in case makewhatis is run as root, by creating a subdirectory of /tmp. +# If that fails we use $HOME. +# The code below uses test -O which doesnt work on all systems. +TMPFILE=$HOME/whatis$$ +TMPFILEDIR=/tmp/whatis$$ +if [ ! -d $TMPFILEDIR ]; then + mkdir $TMPFILEDIR + chmod 0700 $TMPFILEDIR + if [ -O $TMPFILEDIR ]; then + TMPFILE=$TMPFILEDIR/w + fi +fi + +topath=manpath + +defmanpath=$DEFMANPATH +defcatpath= + +sections="1 2 3 4 5 6 7 8 9 n l" + +for name in $* +do +if [ -n "$setsections" ]; then + setsections= + sections=$name + continue +fi +case $name in + -c) topath=catpath + defmanpath= + defcatpath=$DEFCATPATH + continue;; + -s) setsections=1 + continue;; + -u) findarg="-ctime 0" + update=1 + continue;; + -v) verbose=1 + continue;; + -w) manpath=`man --path` + continue;; + -*) echo "Usage: makewhatis [-u] [-v] [-w] [manpath] [-c [catpath]]" + echo " This will build the whatis database for the man pages" + echo " found in manpath and the cat pages found in catpath." + echo " -u: update database with new pages" + echo " -v: verbose" + echo " -w: use manpath obtained from \`man --path\`" + echo " [manpath]: man directories (default: $DEFMANPATH)" + echo " [catpath]: cat directories (default: the first existing" + echo " directory in $DEFCATPATH)" + exit;; + *) if [ -d $name ] + then + eval $topath="\$$topath":$name + else + echo "No such directory $name" + exit + fi;; +esac +done + +manpath=`echo ${manpath-$defmanpath} | tr : ' '` +if [ x"$catpath" = x ]; then + for d in `echo $defcatpath | tr : ' '` + do + if [ -d $d ]; then catpath=$d; break; fi + done +fi +catpath=`echo ${catpath} | tr : ' '` + +# first truncate all the whatis files that will be created new, +# then only update - we might visit the same directory twice +if [ x$update = x ]; then + for pages in man cat + do + eval path="\$$pages"path + for mandir in $path + do + cp /dev/null $mandir/whatis + done + done +fi + +for pages in man cat +do + export pages + eval path="\$$pages"path + for mandir in $path + do + if [ x$verbose != x ]; then + echo "about to enter $mandir" > /dev/tty + fi + if [ -s ${mandir}/whatis -a $pages = man ]; then + if [ x$verbose != x ]; then + echo skipping $mandir - we did it already > /dev/tty + fi + else + here=`pwd` + cd $mandir + for i in $sections + do + if [ -d ${pages}$i ] + then + cd ${pages}$i + section=$i + export section verbose + find . -name '*' $findarg -print | /usr/bin/gawk ' + + function readline() { + if (use_zcat) { + result = (pipe_cmd | getline); + if (result < 0) { + print "Pipe error: " pipe_cmd " " ERRNO > "/dev/stderr"; + } + } else { + result = (getline < filename); + if (result < 0) { + print "Read file error: " filename " " ERRNO > "/dev/stderr"; + } + } + return result; + } + + function closeline() { + if (use_zcat) { + return close(pipe_cmd); + } else { + return close(filename); + } + } + + function do_one() { + after = 0; insh = 0; thisjoin = 1; charct = 0; + + if (verbose) { + print "adding " filename > "/dev/tty" + } + + use_zcat = (filename ~ /\.Z$/ || filename ~ /\.z$/ || + filename ~ /\.gz$/); + match(filename, "/[^/]+$"); + progname = substr(filename, RSTART + 1, RLENGTH - 1); + if (match(progname, "\\." section "[A-Za-z]+")) { + actual_section = substr(progname, RSTART + 1, RLENGTH - 1); + } else { + actual_section = section; + } + sub(/\..*/, "", progname); + if (use_zcat) { + pipe_cmd = "zcat " filename; + } + + while (readline() > 0) { + gsub(/.\b/, ""); + if (($1 ~ /^\.[Ss][Hh]/ && $2 ~ /[Nn][Aa][Mm][Ee]/) || + (pages == "cat" && $1 ~ /^NAME/)) { + if (!insh) + insh = 1; + else { + printf "\n"; + closeline(); + return; + } + } else if (insh) { + if ($1 ~ /^\.[Ss][HhYS]/ || + (pages == "cat" && + ($1 ~ /^S[yYeE]/ || $1 ~ /^DESCRIPTION/ || + $1 ~ /^COMMAND/ || $1 ~ /^OVERVIEW/ || + $1 ~ /^STRUCTURES/ || $1 ~ /^INTRODUCTION/))) { + # end insh for Synopsis, Syntax, but also for + # DESCRIPTION (e.g., XFree86.1x), + # COMMAND (e.g., xspread.1) + # OVERVIEW (e.g., TclCommandWriting.3) + # STRUCTURES (e.g., XEvent.3x) + # INTRODUCTION (e.g., TclX.n) + printf "\n"; + closeline(); + return; + } else { # derived from Tom Christiansen perl script + if (!after && $0 ~ progname"-") { # Fix old cat pages + sub(progname"-", progname" - "); + } + gsub(/ /, " "); # Translate tabs to spaces + gsub(/ +/, " "); # Collapse spaces + gsub(/ *, */, ", "); # Fix comma spacings + sub(/^ /, ""); # Kill initial spaces + sub(/ $/, ""); # Kill trailing spaces + sub(/__+/, "_"); # Collapse underscores + if ($0 ~ /[^ ]-$/) { + sub(/-$/, ""); # Handle Hyphenations + nextjoin = 1; + } else + nextjoin = 0; + sub(/^.[IB] /, ""); # Kill bold and italics + sub(/^.Nm /, ""); # Kill bold + sub(/^.Tn /, ""); # Kill normal + sub(/^.Li /, ""); # Kill .Li + sub(/^.Dq /, ""); # Kill .Dq + sub(/^.Nd */, "- "); # Convert .Nd to dash + gsub(/\\f[PRIB0123]/, ""); # Kill font changes + gsub(/\\s[-+0-9]*/, ""); # Kill size changes + gsub(/\\&/, ""); # Kill \& + gsub(/\\\((ru|ul)/, "_"); # Translate + gsub(/\\\((mi|hy|em)/, "-"); # Translate + gsub(/\\\*\(../, ""); # Kill troff strings + sub(/^\.\\\".*/, ""); # Kill comments + gsub(/\\/, ""); # Kill all backslashes + if ($1 ~ /^\.../ || $1 == "") { + if (after && !needmore) { + printf "\n"; + thisjoin = 1; + charct = 0; + after = 0; + } + } else { + if ($0 ~ /^- /) { + sub("- ", " - "); + } else if (!thisjoin && $0 !~ /^- /) { + printf " "; + charct += 1; + } + thisjoin = nextjoin; + if ($0 !~ / - / && $0 !~ / -$/ && $0 !~ /^- /) { + printf "%s", $0; + charct += length(); + needmore = 0; + } else { + after = 1 + if ($0 ~ / - /) { + where = match( $0 , / - /); + } else if ($0 ~ / -$/) { + where = match( $0, / -$/); + } else { + where = 1; + } + if ((width = 20-charct) < 0) width=0 + printf "%-*s", width, sprintf( "%s (%s)", + substr( $0, 1, where-1 ), actual_section ); + printf "%s", substr( $0, where ) + if ($0 ~ /- *$/) { + needmore = 1; + } else { + needmore = 0; + } + } + } + } + } + } + closeline(); + } + + { # Main action - process each filename read in. + filename = $0; + do_one(); + } + ' pages=$pages section=$section verbose=$verbose + cd .. + fi + done > $TMPFILE + + cd $here + + # kludge for Slackware's /usr/man/preformat + if [ $mandir = /usr/man/preformat ] + then + mandir1=/usr/man + else + mandir1=$mandir + fi + + if [ -f ${mandir1}/whatis ] + then + cat ${mandir1}/whatis >> $TMPFILE + fi + sed '/^$/d' < $TMPFILE | sort | uniq > ${mandir1}/whatis + + chmod 644 ${mandir1}/whatis + rm $TMPFILE + fi + done +done + +# remove the dir if we created it +if [ $TMPFILE = $TMPFILEDIR/w ]; then + rmdir $TMPFILEDIR +fi diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c new file mode 100644 index 0000000000..4bb148df98 --- /dev/null +++ b/erts/etc/unix/run_erl.c @@ -0,0 +1,1298 @@ +/* + * %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% + */ +/* + * Module: run_erl.c + * + * This module implements a reader/writer process that opens two specified + * FIFOs, one for reading and one for writing; reads from the read FIFO + * and writes to stdout and the write FIFO. + * + ________ _________ + | |--<-- pipe.r (fifo1) --<--| | + | to_erl | | run_erl | (parent) + |________|-->-- pipe.w (fifo2) -->--|_________| + ^ master pty + | + | slave pty + ____V____ + | | + | "erl" | (child) + |_________| +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_WORKING_POSIX_OPENPT +#define _XOPEN_SOURCE 600 +#endif +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <dirent.h> +#include <termios.h> +#include <time.h> +#ifndef NO_SYSLOG +# include <syslog.h> +#endif +#ifdef HAVE_PTY_H +# include <pty.h> +#endif +#ifdef HAVE_UTMP_H +# include <utmp.h> +#endif +#ifdef HAVE_UTIL_H +# include <util.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif + +#include "run_erl.h" +#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */ + +#ifdef O_NONBLOCK +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# ifndef EAGAIN +# define EAGAIN -3898734 +# endif +#endif + +#define noDEBUG + +#define DEFAULT_LOG_GENERATIONS 5 +#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */ +#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */ +#define DEFAULT_LOG_MAXSIZE 100000 +#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */ +#define LOG_STUBNAME "erlang.log." +#define LOG_PERM 0664 +#define DEFAULT_LOG_ACTIVITY_MINUTES 5 +#define DEFAULT_LOG_ALIVE_MINUTES 15 +#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y" +#define ALIVE_BUFFSIZ 256 + +#define PERM 0600 +#define STATUSFILENAME "/run_erl.log" +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +#ifndef O_SYNC +#define O_SYNC 0 +#define USE_FSYNC 1 +#endif + +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +#define FILENAME_BUFSIZ FILENAME_MAX + +/* prototypes */ +static void usage(char *); +static int create_fifo(char *name, int perm); +static int open_pty_master(char **name); +static int open_pty_slave(char *name); +static void pass_on(pid_t); +static void exec_shell(char **); +static void status(const char *format,...); +static void error_logf(int priority, int line, const char *format,...); +static void catch_sigchild(int); +static int next_log(int log_num); +static int prev_log(int log_num); +static int find_next_log_num(void); +static int open_log(int log_num, int flags); +static void write_to_log(int* lfd, int* log_num, char* buf, int len); +static void daemon_init(void); +static char *simple_basename(char *path); +static void init_outbuf(void); +static int outbuf_size(void); +static void clear_outbuf(void); +static char* outbuf_first(void); +static void outbuf_delete(int bytes); +static void outbuf_append(const char* bytes, int n); +static int write_all(int fd, const char* buf, int len); +static int extract_ctrl_seq(char* buf, int len); +static void set_window_size(unsigned col, unsigned row); + + +#ifdef DEBUG +static void show_terminal_settings(struct termios *t); +#endif + +/* static data */ +static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ]; +static char statusfile[FILENAME_BUFSIZ]; +static char log_dir[FILENAME_BUFSIZ]; +static char pipename[FILENAME_BUFSIZ]; +static FILE *stdstatus = NULL; +static int log_generations = DEFAULT_LOG_GENERATIONS; +static int log_maxsize = DEFAULT_LOG_MAXSIZE; +static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES; +static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES; +static int log_alive_in_gmt = 0; +static char log_alive_format[ALIVE_BUFFSIZ+1]; +static int run_daemon = 0; +static char *program_name; +static int mfd; /* master pty fd */ +static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ + +/* + * Output buffer. + * + * outbuf_base <= outbuf_out <= outbuf_in <= outbuf_base+outbuf_total + */ +static char* outbuf_base; +static int outbuf_total; +static char* outbuf_out; +static char* outbuf_in; + +#if defined(NO_SYSCONF) || !defined(_SC_OPEN_MAX) +# if defined(OPEN_MAX) +# define HIGHEST_FILENO() OPEN_MAX +# else +# define HIGHEST_FILENO() 64 /* arbitrary value */ +# endif +#else +# define HIGHEST_FILENO() sysconf(_SC_OPEN_MAX) +#endif + + +#ifdef NO_SYSLOG +# define OPEN_SYSLOG() ((void) 0) +#else +# define OPEN_SYSLOG() openlog(simple_basename(program_name), \ + LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER) +#endif + +#define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n") +#define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1) +#define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2) + +#ifdef HAVE_STRERROR +# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno) +#else +# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno +#endif +#define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format)) +#define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1) + + +int main(int argc, char **argv) +{ + int childpid; + int sfd; + int fd; + char *p, *ptyslave=NULL; + int i = 1; + int off_argv; + + program_name = argv[0]; + + if(argc<4) { + usage(argv[0]); + exit(1); + } + + init_outbuf(); + + if (!strcmp(argv[1],"-daemon")) { + daemon_init(); + ++i; + } + + off_argv = i; + strn_cpy(pipename, sizeof(pipename), argv[i++]); + strn_cpy(log_dir, sizeof(log_dir), argv[i]); + strn_cpy(statusfile, sizeof(statusfile), log_dir); + strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME); + +#ifdef DEBUG + status("%s: pid is : %d\n", argv[0], getpid()); +#endif + + /* Get values for LOG file handling from the environment */ + if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) { + log_alive_minutes = atoi(p); + if (!log_alive_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 " + "(current value is %s)",p); + } + log_activity_minutes = log_alive_minutes / 3; + if (!log_activity_minutes) { + ++log_activity_minutes; + } + } + if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) { + log_activity_minutes = atoi(p); + if (!log_activity_minutes) { + ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 " + "(current value is %s)",p); + } + } + if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) { + if (strlen(p) > ALIVE_BUFFSIZ) { + ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of " + "%d characters", ALIVE_BUFFSIZ); + } + strn_cpy(log_alive_format, sizeof(log_alive_format), p); + } else { + strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT); + } + if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) { + ++log_alive_in_gmt; + } + if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) { + log_generations = atoi(p); + if (log_generations < LOG_MIN_GENERATIONS) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS); + if (log_generations > LOG_MAX_GENERATIONS) + ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS); + } + + if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) { + log_maxsize = atoi(p); + if (log_maxsize < LOG_MIN_MAXSIZE) + ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE); + } + + /* + * Create FIFOs and open them + */ + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a unique pipe name in the specified */ + /* directory */ + int highest_pipe_num = 0; + DIR *dirp; + struct dirent *direntp; + + dirp = opendir(pipename); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename); + exit(1); + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, sizeof(pipename), "%s.%d", + PIPE_STUBNAME, highest_pipe_num+1); + } /* if */ + + /* write FIFO - is read FIFO for `to_erl' program */ + strn_cpy(fifo1, sizeof(fifo1), pipename); + strn_cat(fifo1, sizeof(fifo1), ".r"); + if (create_fifo(fifo1, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1); + exit(1); + } + + /* read FIFO - is write FIFO for `to_erl' program */ + strn_cpy(fifo2, sizeof(fifo2), pipename); + strn_cat(fifo2, sizeof(fifo2), ".w"); + + /* Check that nobody is running run_erl already */ + if ((fd = open (fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as client succeeded -- run_erl is already running! */ + fprintf(stderr, "Erlang already running on pipe %s.\n", pipename); + close(fd); + exit(1); + } + if (create_fifo(fifo2, PERM) < 0) { + ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2); + exit(1); + } + + /* + * Open master pseudo-terminal + */ + + if ((mfd = open_pty_master(&ptyslave)) < 0) { + ERRNO_ERR0(LOG_ERR,"Could not open pty master"); + exit(1); + } + + /* + * Now create a child process + */ + + if ((childpid = fork()) < 0) { + ERRNO_ERR0(LOG_ERR,"Cannot fork"); + exit(1); + } + if (childpid == 0) { + /* Child */ + close(mfd); + /* disassociate from control terminal */ +#ifdef USE_SETPGRP_NOARGS /* SysV */ + setpgrp(); +#elif defined(USE_SETPGRP) /* BSD */ + setpgrp(0,getpid()); +#else /* POSIX */ + setsid(); +#endif + /* Open the slave pty */ + if ((sfd = open_pty_slave(ptyslave)) < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open pty slave '%s'", ptyslave); + exit(1); + } + /* But sfd may be one of the stdio fd's now, and we should be unmodern and not use dup2... */ + /* easiest to dup it up... */ + while (sfd < 3) { + sfd = dup(sfd); + } + +#ifndef NO_SYSLOG + /* Before fiddling with file descriptors we make sure syslog is turned off + or "closed". In the single case where we might want it again, + we will open it again instead. Would not want syslog to + go to some other fd... */ + if (run_daemon) { + closelog(); + } +#endif + + /* Close stdio */ + close(0); + close(1); + close(2); + + if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) { + status("Cannot dup\n"); + } + close(sfd); + exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */ + /* the command name, so we have to */ + /* adjust. */ + } else { + /* Parent */ + /* Ignore the SIGPIPE signal, write() will return errno=EPIPE */ + struct sigaction sig_act; + sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = 0; + sig_act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sig_act, (struct sigaction *)NULL); + + sigemptyset(&sig_act.sa_mask); + sig_act.sa_flags = SA_NOCLDSTOP; + sig_act.sa_handler = catch_sigchild; + sigaction(SIGCHLD, &sig_act, (struct sigaction *)NULL); + + /* + * read and write: enter the workloop + */ + + pass_on(childpid); + } + return 0; +} /* main() */ + +/* pass_on() + * Is the work loop of the logger. Selects on the pipe to the to_erl + * program erlang. If input arrives from to_erl it is passed on to + * erlang. + */ +static void pass_on(pid_t childpid) +{ + int len; + fd_set readfds; + fd_set writefds; + fd_set* writefds_ptr; + struct timeval timeout; + time_t last_activity; + char buf[BUFSIZ]; + char log_alive_buffer[ALIVE_BUFFSIZ+1]; + int lognum; + int rfd, wfd=0, lfd=0; + int maxfd; + int ready; + int got_some = 0; /* from to_erl */ + + /* Open the to_erl pipe for reading. + * We can't open the writing side because nobody is reading and + * we'd either hang or get an error. + */ + if ((rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); + exit(1); + } + +#ifdef DEBUG + status("run_erl: %s opened for reading\n", fifo2); +#endif + + /* Open the log file */ + + lognum = find_next_log_num(); + lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC); + + /* Enter the work loop */ + + while (1) { + int exit_status; + maxfd = MAX(rfd, mfd); + maxfd = MAX(wfd, maxfd); + FD_ZERO(&readfds); + FD_SET(rfd, &readfds); + FD_SET(mfd, &readfds); + FD_ZERO(&writefds); + if (outbuf_size() == 0) { + writefds_ptr = NULL; + } else { + FD_SET(wfd, &writefds); + writefds_ptr = &writefds; + } + time(&last_activity); + timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */ + timeout.tv_usec = 0; + ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout); + if (ready < 0) { + if (errno == EINTR) { + if (waitpid(childpid, &exit_status, WNOHANG) == childpid) { + /* + * The Erlang emulator has terminated. Give us some more + * time to write out any pending data before we terminate too. + */ + alarm(5); + } + FD_ZERO(&readfds); + FD_ZERO(&writefds); + } else { + /* Some error occured */ + ERRNO_ERR0(LOG_ERR,"Error in select."); + exit(1); + } + } else { + time_t now; + + if (waitpid(childpid, &exit_status, WNOHANG) == childpid) { + alarm(5); + FD_ZERO(&readfds); + FD_ZERO(&writefds); + } + + /* Check how long time we've been inactive */ + time(&now); + if(!ready || now - last_activity > log_activity_minutes*60) { + /* Either a time out: 15 minutes without action, */ + /* or something is coming in right now, but it's a long time */ + /* since last time, so let's write a time stamp this message */ + struct tm *tmptr; + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n===== %s%s\n", + ready?"":"ALIVE ", log_alive_buffer); + write_to_log(&lfd, &lognum, buf, strlen(buf)); + } + } + + /* + * Write any pending output first. + */ + if (FD_ISSET(wfd, &writefds)) { + int written; + char* buf = outbuf_first(); + + len = outbuf_size(); + written = write(wfd, buf, len); + if (written < 0 && errno == EAGAIN) { + /* + * Nothing was written - this is really strange because + * select() told us we could write. Ignore. + */ + } else if (written < 0) { + /* + * A write error. Assume that to_erl has terminated. + */ + clear_outbuf(); + close(wfd); + wfd = 0; + } else { + /* Delete the written part (or all) from the buffer. */ + outbuf_delete(written); + } + } + + /* + * Read master pty and write to FIFO. + */ + if (FD_ISSET(mfd, &readfds)) { +#ifdef DEBUG + status("Pty master read; "); +#endif + if ((len = read(mfd, buf, BUFSIZ)) <= 0) { + close(rfd); + if(wfd) close(wfd); + close(mfd); + unlink(fifo1); + unlink(fifo2); + if (len < 0) { + if(errno == EIO) + ERROR0(LOG_ERR,"Erlang closed the connection."); + else + ERRNO_ERR0(LOG_ERR,"Error in reading from terminal"); + exit(1); + } + exit(0); + } + + write_to_log(&lfd, &lognum, buf, len); + + /* + * Save in the output queue. + */ + + if (wfd) { + outbuf_append(buf, len); + } + } + + /* + * Read from FIFO, write to master pty + */ + if (FD_ISSET(rfd, &readfds)) { +#ifdef DEBUG + status("FIFO read; "); +#endif + if ((len = read(rfd, buf, BUFSIZ)) < 0) { + close(rfd); + if(wfd) close(wfd); + close(mfd); + unlink(fifo1); + unlink(fifo2); + ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); + exit(1); + } + + if(!len) { + /* to_erl closed its end of the pipe */ + close(rfd); + rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); + if (rfd < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); + exit(1); + } + got_some = 0; /* reset for next session */ + } + else { + if(!wfd) { + /* Try to open the write pipe to to_erl. Now that we got some data + * from to_erl, to_erl should already be reading this pipe - open + * should succeed. But in case of error, we just ignore it. + */ + if ((wfd = open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { + status("Client expected on FIFO %s, but can't open (len=%d)\n", + fifo1, len); + close(rfd); + rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); + if (rfd < 0) { + ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); + exit(1); + } + wfd = 0; + } + else { +#ifdef DEBUG + status("run_erl: %s opened for writing\n", fifo1); +#endif + } + } + + if (!got_some && wfd && buf[0] == '\022') { + char wbuf[30]; + int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n", + RUN_ERL_HI_VER, RUN_ERL_LO_VER); + outbuf_append(wbuf,wlen); + } + got_some = 1; + + + /* Write the message */ +#ifdef DEBUG + status("Pty master write; "); +#endif + len = extract_ctrl_seq(buf, len); + + if(len==1 && buf[0] == '\003') { + kill(childpid,SIGINT); + } + else if (len>0 && write_all(mfd, buf, len) != len) { + ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); + close(rfd); + if(wfd) close(wfd); + close(mfd); + exit(1); + } + } +#ifdef DEBUG + status("OK\n"); +#endif + } + } +} /* pass_on() */ + +static void catch_sigchild(int sig) +{ +} + +/* + * next_log: + * Returns the index number that follows the given index number. + * (Wrapping after log_generations) + */ +static int next_log(int log_num) { + return log_num>=log_generations?1:log_num+1; +} + +/* + * prev_log: + * Returns the index number that precedes the given index number. + * (Wrapping after log_generations) + */ +static int prev_log(int log_num) { + return log_num<=1?log_generations:log_num-1; +} + +/* + * find_next_log_num() + * Searches through the log directory to check which logs that already + * exist. It finds the "hole" in the sequence, and returns the index + * number for the last log in the log sequence. If there is no hole, index + * 1 is returned. + */ +static int find_next_log_num(void) { + int i, next_gen, log_gen; + DIR *dirp; + struct dirent *direntp; + int log_exists[LOG_MAX_GENERATIONS+1]; + int stub_len = strlen(LOG_STUBNAME); + + /* Initialize exiting log table */ + + for(i=log_generations; i>=0; i--) + log_exists[i] = 0; + dirp = opendir(log_dir); + if(!dirp) { + ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir); + exit(1); + } + + /* Check the directory for existing logs */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) { + int num = atoi(direntp->d_name+stub_len); + if(num < 1 || num > log_generations) + continue; + log_exists[num] = 1; + } + } + closedir(dirp); + + /* Find out the next available log file number */ + + next_gen = 0; + for(i=log_generations; i>=0; i--) { + if(log_exists[i]) + if(next_gen) + break; + else + ; + else + next_gen = i; + } + + /* Find out the current log file number */ + + if(next_gen) + log_gen = prev_log(next_gen); + else + log_gen = 1; + + return log_gen; +} /* find_next_log_num() */ + +/* open_log() + * Opens a log file (with given index) for writing. Writing may be + * at the end or a trucnating write, according to flags. + * A LOGGING STARTED and time stamp message is inserted into the log file + */ +static int open_log(int log_num, int flags) +{ + char buf[FILENAME_MAX]; + time_t now; + struct tm *tmptr; + char log_buffer[ALIVE_BUFFSIZ+1]; + int lfd; + + /* Remove the next log (to keep a "hole" in the log sequence) */ + sn_printf(buf, sizeof(buf), "%s/%s%d", + log_dir, LOG_STUBNAME, next_log(log_num)); + unlink(buf); + + /* Create or continue on the current log file */ + sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num); + if((lfd = open(buf, flags, LOG_PERM))<0){ + ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf); + exit(1); + } + + /* Write a LOGGING STARTED and time stamp into the log file */ + time(&now); + if (log_alive_in_gmt) { + tmptr = gmtime(&now); + } else { + tmptr = localtime(&now); + } + if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format, + tmptr)) { + strn_cpy(log_buffer, sizeof(log_buffer), + "(could not format time in 256 positions " + "with current format string.)"); + } + log_buffer[ALIVE_BUFFSIZ] = '\0'; + + sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n", + log_buffer); + if (write_all(lfd, buf, strlen(buf)) < 0) + status("Error in writing to log.\n"); + +#if USE_FSYNC + fsync(lfd); +#endif + + return lfd; +} + +/* write_to_log() + * Writes a message to a log file. If the current log file is full, + * a new log file is opened. + */ +static void write_to_log(int* lfd, int* log_num, char* buf, int len) +{ + int size; + + /* Decide if new logfile needed, and open if so */ + + size = lseek(*lfd,0,SEEK_END); + if(size+len > log_maxsize) { + close(*lfd); + *log_num = next_log(*log_num); + *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC); + } + + /* Write to log file */ + + if (write_all(*lfd, buf, len) < 0) { + status("Error in writing to log.\n"); + } + +#if USE_FSYNC + fsync(*lfd); +#endif +} + +/* create_fifo() + * Creates a new fifo with the given name and permission. + */ +static int create_fifo(char *name, int perm) +{ + if ((mkfifo(name, perm) < 0) && (errno != EEXIST)) + return -1; + return 0; +} + + +/* open_pty_master() + * Find a master device, open and return fd and slave device name. + */ + +static int open_pty_master(char **ptyslave) +{ + int mfd; + +/* Use the posix_openpt if working, as this guarantees creation of the + slave device properly. */ +#ifdef HAVE_WORKING_POSIX_OPENPT + if ((mfd = posix_openpt(O_RDWR)) >= 0) { + if ((*ptyslave = ptsname(mfd)) != NULL && + grantpt(mfd) == 0 && + unlockpt(mfd) == 0) { + + return mfd; + } + close(mfd); + } + /* fallback to openpty if it exist */ +#endif + +#ifdef HAVE_OPENPTY +# ifdef PATH_MAX +# define SLAVE_SIZE PATH_MAX +# else +# define SLAVE_SIZE 1024 +# endif + { + static char slave[SLAVE_SIZE]; + int sfd; +# undef SLAVE_SIZE + + if (openpty(&mfd, &sfd, slave, NULL, NULL) == 0) { + close(sfd); + *ptyslave = slave; + return mfd; + } + } + +#elif !defined(HAVE_WORKING_POSIX_OPENPT) + /* + * The traditional way to find ptys. We only try it if neither + * posix_openpt or openpty() are available. + */ + char *major, *minor; + + static char majorchars[] = "pqrstuvwxyzabcdePQRSTUVWXYZABCDE"; + static char minorchars[] = "0123456789abcdefghijklmnopqrstuv" + "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+"; + + /* In the old time the names where /dex/ptyXY where */ + /* X is in "pqrs" and Y in "0123456789abcdef" but FreeBSD */ + /* and some Linux version has extended this. */ + + /* This code could probebly be improved alot. For example look at */ + /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/pty.c.html */ + /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/upty.h.html */ + + { + /* New style devpts or devfs /dev/pty/{m,s}{0,1....} */ + + static char ptyname[] = "/dev/pty/mX"; + + for (minor = minorchars; *minor; minor++) { + ptyname[10] = *minor; + if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + ptyname[9] = 's'; + *ptyslave = ptyname; + return mfd; + } + } + } + + { + /* Unix98 style /dev/ptym/ptyXY and /dev/pty/ttyXY */ + + static char ptyname[] = "/dev/ptym/ptyXY"; + static char ttyname[] = "/dev/pty/ttyXY"; + + for (major = majorchars; *major; major++) { + ptyname[13] = *major; + for (minor = minorchars; *minor; minor++) { + ptyname[14] = *minor; + if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + ttyname[12] = *major; + ttyname[13] = *minor; + *ptyslave = ttyname; + return mfd; + } + } + } + } + + { + /* Old style /dev/ptyXY */ + + static char ptyname[] = "/dev/ptyXY"; + + for (major = majorchars; *major; major++) { + ptyname[8] = *major; + for (minor = minorchars; *minor; minor++) { + ptyname[9] = *minor; + if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) { + ptyname[5] = 't'; + *ptyslave = ptyname; + return mfd; + } + } + } + } +#endif /* !HAVE_OPENPTY */ + return -1; +} + +static int open_pty_slave(char *name) +{ + int sfd; +#ifdef DEBUG + struct termios tty_rmode; +#endif + + if ((sfd = open(name, O_RDWR, 0)) < 0) { + return -1; + } + +#ifdef DEBUG + if (tcgetattr(sfd, &tty_rmode) , 0) { + fprintf(stderr, "Cannot get terminals current mode\n"); + exit(-1); + } + show_terminal_settings(&tty_rmode); +#endif + + return sfd; +} + +/* exec_shell() + * Executes the named command (in argv format) in a /bin/sh. IO redirection + * should already have been taken care of, and this process should be the + * child of a fork. + */ +static void exec_shell(char **argv) +{ + char *sh, **vp; + int i; + + sh = "/bin/sh"; + if ((argv[0] = strrchr(sh, '/')) != NULL) + argv[0]++; + else + argv[0] = sh; + argv[1] = "-c"; + status("Args before exec of shell:\n"); + for (vp = argv, i = 0; *vp; vp++, i++) + status("argv[%d] = %s\n", i, *vp); + if (stdstatus) { + fclose(stdstatus); + } + execv(sh, argv); + if (run_daemon) { + OPEN_SYSLOG(); + } + ERRNO_ERR0(LOG_ERR,"Could not execv"); +} + +/* status() + * Prints the arguments to a status file + * Works like printf (see vfrpintf) + */ +static void status(const char *format,...) +{ + va_list args; + time_t now; + + if (stdstatus == NULL) + stdstatus = fopen(statusfile, "w"); + if (stdstatus == NULL) + return; + now = time(NULL); + fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now)); + va_start(args, format); + vfprintf(stdstatus, format, args); + va_end(args); + fflush(stdstatus); +} + +static void daemon_init(void) + /* As R Stevens wants it, to a certain extent anyway... */ +{ + pid_t pid; + int i, maxfd = HIGHEST_FILENO(); + + if ((pid = fork()) != 0) + exit(0); +#if defined(USE_SETPGRP_NOARGS) + setpgrp(); +#elif defined(USE_SETPGRP) + setpgrp(0,getpid()); +#else + setsid(); /* Seems to be the case on all current platforms */ +#endif + signal(SIGHUP, SIG_IGN); + if ((pid = fork()) != 0) + exit(0); + + /* Should change working directory to "/" and change umask now, but that + would be backward incompatible */ + + for (i = 0; i < maxfd; ++i ) { + close(i); + } + + OPEN_SYSLOG(); + run_daemon = 1; +} + +/* error_logf() + * Prints the arguments to stderr or syslog + * Works like printf (see vfprintf) + */ +static void error_logf(int priority, int line, const char *format, ...) +{ + va_list args; + va_start(args, format); + +#ifndef NO_SYSLOG + if (run_daemon) { + vsyslog(priority,format,args); + } + else +#endif + { + time_t now = time(NULL); + fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now)); + vfprintf(stderr, format, args); + } + va_end(args); +} + +static void usage(char *pname) +{ + fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname); + fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n"); + fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n"); + fprintf(stderr, "size of the log file when to switch to the next log file\n"); +} + +/* Instead of making sure basename exists, we do our own */ +static char *simple_basename(char *path) +{ + char *ptr; + for (ptr = path; *ptr != '\0'; ++ptr) { + if (*ptr == '/') { + path = ptr + 1; + } + } + return path; +} + +static void init_outbuf(void) +{ + outbuf_total = 1; + outbuf_base = malloc(BUFSIZ); + clear_outbuf(); +} + +static void clear_outbuf(void) +{ + outbuf_in = outbuf_out = outbuf_base; +} + +static int outbuf_size(void) +{ + return outbuf_in - outbuf_out; +} + +static char* outbuf_first(void) +{ + return outbuf_out; +} + +static void outbuf_delete(int bytes) +{ + outbuf_out += bytes; + if (outbuf_out >= outbuf_in) { + outbuf_in = outbuf_out = outbuf_base; + } +} + +static void outbuf_append(const char* buf, int n) +{ + if (outbuf_base+outbuf_total < outbuf_in+n) { + /* + * The new data does not fit at the end of the buffer. + * Slide down the data to the beginning of the buffer. + */ + if (outbuf_out > outbuf_base) { + int size = outbuf_in - outbuf_out; + char* p; + + outbuf_in -= outbuf_out - outbuf_base; + p = outbuf_base; + while (size-- > 0) { + *p++ = *outbuf_out++; + } + outbuf_out = outbuf_base; + } + + /* + * Allocate a larger buffer if we still cannot fit the data. + */ + if (outbuf_base+outbuf_total < outbuf_in+n) { + int size = outbuf_in - outbuf_out; + outbuf_total = size+n; + outbuf_base = realloc(outbuf_base, outbuf_total); + outbuf_out = outbuf_base; + outbuf_in = outbuf_base + size; + } + } + + /* + * Copy data to the end of the buffer. + */ + memcpy(outbuf_in, buf, n); + outbuf_in += n; +} + +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +static int write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + for (;;) { + written = write(fd,buf,left); + if (written == left) { + return len; + } + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } +} + +/* Extract any control sequences that are ment only for run_erl + * and should not be forwarded to the pty. + */ +static int extract_ctrl_seq(char* buf, int len) +{ + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + char* bufend = buf + len; + char* start = buf; + char* command; + char* end; + + for (;;) { + start = find_str(start, bufend-start, prefix); + if (!start) break; + + command = start + strlen(prefix); + end = find_str(command, bufend-command, suffix); + if (end) { + unsigned col, row; + if (sscanf(command,"version=%u", &protocol_ver)==1) { + /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/ + } + else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) { + set_window_size(col,row); + } + else { + ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n", + (int)(end-command), command); + } + + /* Remove ctrl sequence from buf */ + end += strlen(suffix); + memmove(start, end, bufend-end); + bufend -= end - start; + } + else { + ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n", + (int)(bufend-start), start); + break; + } + } + return bufend - buf; +} + +static void set_window_size(unsigned col, unsigned row) +{ +#ifdef TIOCSWINSZ + struct winsize ws; + ws.ws_col = col; + ws.ws_row = row; + if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) { + ERRNO_ERR0(LOG_ERR,"Failed to set window size"); + } +#endif +} + + +#ifdef DEBUG + +#define S(x) ((x) > 0 ? 1 : 0) + +static void show_terminal_settings(struct termios *t) +{ + printf("c_iflag:\n"); + printf("Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); + printf("Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); + printf("Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); + printf("Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); + printf("Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); + printf("Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); + printf("Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); + printf("Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); + printf("Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); + printf("ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); + printf("Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); + printf("\n"); + printf("c_oflag:\n"); + printf("Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); + printf("\n"); + printf("c_cflag:\n"); + printf("Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); + printf("\n"); + printf("c_local:\n"); + printf("Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); + printf("\n"); + printf("c_cc:\n"); + printf("c_cc[VEOF] %d\n", t->c_cc[VEOF]); +} + +#endif /* DEBUG */ + + diff --git a/erts/etc/unix/run_erl.h b/erts/etc/unix/run_erl.h new file mode 100644 index 0000000000..843cda680c --- /dev/null +++ b/erts/etc/unix/run_erl.h @@ -0,0 +1,30 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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% + */ + +/* + * The protocol version number used between to_erl and run_erl. + */ +#define RUN_ERL_HI_VER 1 /* My preferred protocol version */ +#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */ + +/* Version history: + * 0: Older, without version handshake + * 1: R12B-3, version handshake + window size ctrl + */ + diff --git a/erts/etc/unix/safe_string.c b/erts/etc/unix/safe_string.c new file mode 100644 index 0000000000..a77d9c5456 --- /dev/null +++ b/erts/etc/unix/safe_string.c @@ -0,0 +1,123 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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% + */ +/* + * Module: safe_string.c + * + * This is a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "safe_string.h" +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + + +static void string_overflow_handler(const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr,format,args); + va_end(args); + exit(1); +} + +int vsn_printf(char* dst, size_t size, const char* format, va_list args) +{ + int ret = vsnprintf(dst, size, format, args); + if (ret >= size || ret < 0) { + string_overflow_handler("Buffer truncated '%s'\n",dst); + } + return ret; +} + +int sn_printf(char* dst, size_t size, const char* format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = vsn_printf(dst,size,format,args); + va_end(args); + return ret; +} + +int strn_cpy(char* dst, size_t size, const char* src) +{ + return sn_printf(dst,size,"%s",src); +} + +int strn_cat(char* dst, size_t size, const char* src) +{ + return strn_catf(dst,size,"%s",src); +} + +int strn_catf(char* dst, size_t size, const char* format, ...) +{ + int ret; + va_list args; +#ifdef _GNU_SOURCE + int len = strnlen(dst,size); +#else + int len = strlen(dst); +#endif + + if (len >= size) { + string_overflow_handler("Buffer already overflowed '%.*s'\n", + size, dst); + } + va_start(args, format); + ret = vsn_printf(dst+len, size-len, format, args); + va_end(args); + return len+ret; +} + +char* find_str(const char* haystack, int hsize, const char* needle) +{ + int i = 0; + int nsize = strlen(needle); + hsize -= nsize - 1; + for (i=0; i<hsize; i++) { + if (haystack[i]==needle[0] && strncmp(haystack+i,needle,nsize)==0) { + return (char*)(haystack+i); + } + } + return NULL; +} + +#ifndef HAVE_MEMMOVE +void* memmove(void *dest, const void *src, size_t n) +{ + int i; + if (src > dest) { + for (i=0; i<n; i++) ((char*)dest)[i] = ((char*)src)[i]; + } + else { + for (i=(int)(n-1); i>=0; i--) ((char*)dest)[i] = ((char*)src)[i]; + } + return dest; +} +#endif /* HAVE_MEMMOVE */ + diff --git a/erts/etc/unix/safe_string.h b/erts/etc/unix/safe_string.h new file mode 100644 index 0000000000..c70e528814 --- /dev/null +++ b/erts/etc/unix/safe_string.h @@ -0,0 +1,65 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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% + */ +/* + * Module: safe_string.h + * + * This is an interface to a bunch of generic string operation + * that are safe regarding buffer overflow. + * + * All string functions terminate the process with an error message + * on buffer overflow. + */ + +#include <stdio.h> +#include <stdarg.h> + +/* Like vsnprintf() + */ +int vsn_printf(char* dst, size_t size, const char* format, va_list args); + +/* Like snprintf() + */ +int sn_printf(char* dst, size_t size, const char* format, ...); + +/* Like strncpy() + * Returns length of copied string. + */ +int strn_cpy(char* dst, size_t size, const char* src); + +/* Almost like strncat() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_cat(char* dst, size_t size, const char* src); + +/* Combination of strncat() and snprintf() + * size is sizeof entire dst buffer. + * Returns length of resulting string. + */ +int strn_catf(char* dst, size_t size, const char* format, ...); + +/* Simular to strstr() but search size bytes of haystack + * without regard to '\0' characters. + */ +char* find_str(const char* haystack, int size, const char* needle); + +#ifndef HAVE_MEMMOVE +void* memmove(void *dest, const void *src, size_t n); +#endif + diff --git a/erts/etc/unix/setuid_socket_wrap.c b/erts/etc/unix/setuid_socket_wrap.c new file mode 100644 index 0000000000..3f0657770c --- /dev/null +++ b/erts/etc/unix/setuid_socket_wrap.c @@ -0,0 +1,259 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-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% + */ +/* + * setuid_socket_wrap.c + * + * ./a.out [-s [tag,][addr]:[port]]* [-d [tag,][addr]:[port]]* + * [-r [tag,]proto]* -- program args + * + * Where: -s = stream socket, -d datagram socket and -r means raw socket. + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef EXEC_PROGRAM +# define EXEC_PROGRAM "/bin/echo" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +struct sock_list { + struct sock_list *next; + int fd; + int type; + int protocol; + struct sockaddr_in addr; + char *arg; +}; + +int parse_addr(addr, str) + struct sockaddr_in *addr; + char *str; +{ + int port = 0; + char *cp; + struct hostent *hp; + struct servent *se; + + if ((cp = strrchr(str, (int)':')) != NULL) + *cp++ = '\0'; + if (cp) { + if (!isdigit((int)cp[0])) { + if ((se = getservbyname(cp, "tcp")) != NULL) { + port = ntohs(se->s_port); + } else { + fprintf(stderr, "unknown port %s\n", cp); + return -1; + } + } else { + port = atoi(cp); + } + } + if (port < 0 || port > 0xffff) { + fprintf(stderr, "bad port number %d\n", port); + return -1; + } + + bzero(addr, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + if (*str == '\000') { + addr->sin_addr.s_addr = INADDR_ANY; + } else { + if ((addr->sin_addr.s_addr = inet_addr(str)) == INADDR_NONE) { + if ((hp = gethostbyname(str)) == NULL) { + fprintf(stderr, "\"%s\" unknown host or address!\n", str); + return -1; + } else { + bcopy(hp->h_addr_list[0], &addr->sin_addr.s_addr,hp->h_length); + } + } + } + return 0; +} + +struct sock_list *new_entry(type, argstr) + int type; + char *argstr; +{ + struct sock_list *sle; + char *cp; + + sle = (struct sock_list *)malloc(sizeof(struct sock_list)); + if (!sle) + return NULL; + sle->next = NULL; + sle->fd = -1; + + if ((cp = strchr(argstr, (int)',')) != NULL) { + *cp++ = '\0'; + sle->arg = argstr; + argstr = cp; + } else { + sle->arg = "-fd"; + } + sle->type = type; + switch (type) { + case SOCK_RAW: { + struct protoent *pe; + pe = getprotobyname(argstr); + if (!pe) { + fprintf(stderr, "Unknown protocol: %s\n", argstr); + free(sle); + return NULL; + } + sle->protocol = pe->p_proto; + break; + } + case SOCK_STREAM: + case SOCK_DGRAM: + sle->protocol = 0; + if (parse_addr(&sle->addr, argstr) < 0) { + free(sle); + return NULL; + } + break; + } + return sle; +} + +int open_socket(sle) + struct sock_list *sle; +{ + sle->fd = socket(AF_INET, sle->type, sle->protocol); + if (sle->fd < 0) { + perror("socket"); + return -1; + } + if (sle->type != SOCK_RAW) { +#if 0 + printf("binding fd %d to %s:%d\n", sle->fd, + inet_ntoa(sle->addr.sin_addr), ntohs(sle->addr.sin_port)); +#endif + if (bind(sle->fd, (struct sockaddr *)&sle->addr, sizeof(sle->addr))<0){ + perror("bind"); + close(sle->fd); + return -1; + } + } + return sle->fd; +} + +int main(argc, argv) + int argc; + char *argv[]; +{ + struct sock_list *sl = NULL, *sltmp = NULL; + int count = 0; + int c; + + while ((c = getopt(argc, argv, "s:d:r:")) != EOF) + switch (c) { + case 's': + sltmp = new_entry(SOCK_STREAM, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + case 'd': + sltmp = new_entry(SOCK_DGRAM, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + case 'r': + sltmp = new_entry(SOCK_RAW, optarg); + if (!sltmp) { + exit(1); + } + sltmp->next = sl; + sl = sltmp; + count++; + break; + default: + exit(1); + } + argc -= optind; + argv += optind; + + for(sltmp = sl; sltmp != NULL; sltmp = sltmp->next) + if (open_socket(sltmp) < 0) { + fprintf(stderr, "failed to create socket!\n"); + exit(1); + } + + setuid(getuid()); + + { + int i; + char **newargv; + char *run_prog = EXEC_PROGRAM; + char *run_prog_name; + + newargv = (char **)malloc((1 + 2*count + argc + 1) * sizeof(char*)); + + if ((run_prog_name = strrchr(run_prog, (int)'/')) == NULL) + run_prog_name = run_prog; + else + run_prog_name++; + + i = 0; + newargv[i++] = run_prog_name; + + for (; argc; argc--, argv++, i++) + newargv[i] = *argv; + for(sltmp = sl; sltmp != NULL; ) { + char *fd_str = (char *)malloc(8); + if (!fd_str) exit(1); + sprintf(fd_str, "%d", sltmp->fd); + if (sltmp->arg && *(sltmp->arg)) + newargv[i++] = sltmp->arg; + newargv[i++] = fd_str; + sl = sltmp; + sltmp = sltmp->next; + free(sl); + } + newargv[i] = (char *)NULL; + execv(run_prog, newargv); + perror("exec"); + exit(1); + } + exit(0); +} diff --git a/erts/etc/unix/start.src b/erts/etc/unix/start.src new file mode 100644 index 0000000000..8479be0987 --- /dev/null +++ b/erts/etc/unix/start.src @@ -0,0 +1,36 @@ +#!/bin/sh +# +# %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 program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%FINAL_ROOTDIR% + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA" diff --git a/erts/etc/unix/start_erl.src b/erts/etc/unix/start_erl.src new file mode 100644 index 0000000000..ea8022c449 --- /dev/null +++ b/erts/etc/unix/start_erl.src @@ -0,0 +1,47 @@ +#!/bin/sh +# +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# This program is called by run_erl. It starts +# the erlang emulator and sets -boot and -config parameters. +# It should only be used at an embedded target system. +# +# Usage: start_erl RootDir RelDir DataFile [ErlFlags ...] +# +ROOTDIR=$1 +shift +RELDIR=$1 +shift +DataFile=$1 +shift + +ERTS_VSN=`awk '{print $1}' $DataFile` +VSN=`awk '{print $2}' $DataFile` + +BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin +EMU=beam +PROGNAME=`echo $0 | sed 's/.*\///'` +export EMU +export ROOTDIR +export BINDIR +export PROGNAME +export RELDIR + +exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys ${1+"$@"} + diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c new file mode 100644 index 0000000000..588d127445 --- /dev/null +++ b/erts/etc/unix/to_erl.c @@ -0,0 +1,610 @@ +/* + * %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% + */ +/* + * Module: to_erl.c + * + * This module implements a process that opens two specified FIFOs, one + * for reading and one for writing; reads from its stdin, and writes what + * it has read to the write FIF0; reads from the read FIFO, and writes to + * its stdout. + * + ________ _________ + | |--<-- pipe.r (fifo1) --<--| | + | to_erl | | run_erl | (parent) + |________|-->-- pipe.w (fifo2) -->--|_________| + ^ master pty + | + | slave pty + ____V____ + | | + | "erl" | (child) + |_________| + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <dirent.h> +#include <signal.h> +#include <errno.h> +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif + +#include "run_erl.h" +#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */ + +#if defined(O_NONBLOCK) +# define DONT_BLOCK_PLEASE O_NONBLOCK +#else +# define DONT_BLOCK_PLEASE O_NDELAY +# if !defined(EAGAIN) +# define EAGAIN -3898734 +# endif +#endif + +#ifdef HAVE_STRERROR +# define STRERROR(x) strerror(x) +#else +# define STRERROR(x) "" +#endif + +#define noDEBUG + +#define PIPE_DIR "/tmp/" +#define PIPE_STUBNAME "erlang.pipe" +#define PIPE_STUBLEN strlen(PIPE_STUBNAME) + +#ifdef DEBUG +#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); } +#else +#define STATUS(s) +#endif + +#ifndef FILENAME_MAX +#define FILENAME_MAX 250 +#endif + +static struct termios tty_smode, tty_rmode; +static int tty_eof = 0; +static int recv_sig = 0; +static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */ + +static int write_all(int fd, const char* buf, int len); +static int window_size_seq(char* buf, size_t bufsz); +static int version_handshake(char* buf, int len, int wfd); +#ifdef DEBUG +static void show_terminal_settings(struct termios *); +#endif + +static void handle_ctrlc(int sig) +{ + /* Reinstall the handler, and signal break flag */ + signal(SIGINT,handle_ctrlc); + recv_sig = SIGINT; +} + +static void handle_sigwinch(int sig) +{ + recv_sig = SIGWINCH; +} + +static void usage(char *pname) +{ + fprintf(stderr, "Usage: %s [-h|-F] [pipe_name|pipe_dir/]\n", pname); + fprintf(stderr, "\t-h\tThis help text.\n"); + fprintf(stderr, "\t-F\tForce connection even though pipe is locked by other to_erl process.\n"); +} + +int main(int argc, char **argv) +{ + char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; + int i, len, wfd, rfd, result = 0; + fd_set readfds; + char buf[BUFSIZ]; + char pipename[FILENAME_MAX]; + int pipeIx = 1; + int force_lock = 0; + int got_some = 0; + + if (argc >= 2 && argv[1][0]=='-') { + switch (argv[1][1]) { + case 'h': + usage(argv[0]); + exit(1); + case 'F': + force_lock = 1; + break; + default: + fprintf(stderr,"Invalid option '%s'\n",argv[1]); + exit(1); + } + pipeIx = 2; + } + +#ifdef DEBUG + fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid()); +#endif + + strn_cpy(pipename, sizeof(pipename), + (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); + + if(*pipename && pipename[strlen(pipename)-1] == '/') { + /* The user wishes us to find a pipe name in the specified */ + /* directory */ + int highest_pipe_num = 0; + DIR *dirp; + struct dirent *direntp; + + dirp = opendir(pipename); + if(!dirp) { + fprintf(stderr, "Can't access pipe directory %s.\n", pipename); + exit(1); + } + + /* Check the directory for existing pipes */ + + while((direntp=readdir(dirp)) != NULL) { + if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { + int num = atoi(direntp->d_name+PIPE_STUBLEN+1); + if(num > highest_pipe_num) + highest_pipe_num = num; + } + } + closedir(dirp); + strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), + PIPE_STUBNAME, highest_pipe_num); + } /* if */ + + /* read FIFO */ + sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); + /* write FIFO */ + sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); + + /* Check that nobody is running to_erl on this pipe already */ + if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { + /* Open as server succeeded -- to_erl is already running! */ + close(wfd); + fprintf(stderr, "Another to_erl process already attached to pipe " + "%s.\n", pipename); + if (force_lock) { + fprintf(stderr, "But we proceed anyway by force (-F).\n"); + } + else { + exit(1); + } + } + + if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); +#endif + fprintf(stderr, "No running Erlang on pipe %s.\n", pipename); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); +#endif + + if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { +#ifdef DEBUG + fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); +#endif + fprintf(stderr, "No running Erlang on pipe %s.\n", pipename); + close(rfd); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); +#endif + + fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); + + /* Set break handler to our handler */ + signal(SIGINT,handle_ctrlc); + + /* + * Save the current state of the terminal, and set raw mode. + */ + if (tcgetattr(0, &tty_rmode) , 0) { + fprintf(stderr, "Cannot get terminals current mode\n"); + exit(-1); + } + tty_smode = tty_rmode; + tty_eof = '\004'; /* Ctrl+D to exit */ +#ifdef DEBUG + show_terminal_settings(&tty_rmode); +#endif + tty_smode.c_iflag = + 1*BRKINT |/*Signal interrupt on break.*/ + 1*IGNPAR |/*Ignore characters with parity errors.*/ + 1*ISTRIP |/*Strip character.*/ + 0; + +#if 0 +0*IGNBRK |/*Ignore break condition.*/ +0*PARMRK |/*Mark parity errors.*/ +0*INPCK |/*Enable input parity check.*/ +0*INLCR |/*Map NL to CR on input.*/ +0*IGNCR |/*Ignore CR.*/ +0*ICRNL |/*Map CR to NL on input.*/ +0*IUCLC |/*Map upper-case to lower-case on input.*/ +0*IXON |/*Enable start/stop output control.*/ +0*IXANY |/*Enable any character to restart output.*/ +0*IXOFF |/*Enable start/stop input control.*/ +0*IMAXBEL|/*Echo BEL on input line too long.*/ +#endif + + tty_smode.c_oflag = + 1*OPOST |/*Post-process output.*/ + 1*ONLCR |/*Map NL to CR-NL on output.*/ +#ifdef XTABS + 1*XTABS |/*Expand tabs to spaces. (Linux)*/ +#endif +#ifdef OXTABS + 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ +#endif +#ifdef NL0 + 1*NL0 |/*Select newline delays*/ +#endif +#ifdef CR0 + 1*CR0 |/*Select carriage-return delays*/ +#endif +#ifdef TAB0 + 1*TAB0 |/*Select horizontal tab delays*/ +#endif +#ifdef BS0 + 1*BS0 |/*Select backspace delays*/ +#endif +#ifdef VT0 + 1*VT0 |/*Select vertical tab delays*/ +#endif +#ifdef FF0 + 1*FF0 |/*Select form feed delays*/ +#endif + 0; + +#if 0 +0*OLCUC |/*Map lower case to upper on output.*/ +0*OCRNL |/*Map CR to NL on output.*/ +0*ONOCR |/*No CR output at column 0.*/ +0*ONLRET |/*NL performs CR function.*/ +0*OFILL |/*Use fill characters for delay.*/ +0*OFDEL |/*Fill is DEL, else NULL.*/ +0*NL1 | +0*CR1 | +0*CR2 | +0*CR3 | +0*TAB1 | +0*TAB2 | +0*TAB3 |/*Expand tabs to spaces.*/ +0*BS1 | +0*VT1 | +0*FF1 | +#endif + + /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ + /* advisable if this is a *real* terminal, such as the console. In fact */ + /* this may hang the entire machine, deep, deep down (signalling break */ + /* or toggling the abort switch doesn't help) */ + + tty_smode.c_lflag = + 0; + +#if 0 +0*ISIG |/*Enable signals.*/ +0*ICANON |/*Canonical input (erase and kill processing).*/ +0*XCASE |/*Canonical upper/lower presentation.*/ +0*ECHO |/*Enable echo.*/ +0*ECHOE |/*Echo erase character as BS-SP-BS.*/ +0*ECHOK |/*Echo NL after kill character.*/ +0*ECHONL |/*Echo NL.*/ +0*NOFLSH |/*Disable flush after interrupt or quit.*/ +0*TOSTOP |/*Send SIGTTOU for background output.*/ +0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ +0*ECHOPRT|/*Echo erase character as character erased.*/ +0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ +0*FLUSHO |/*Output is being flushed.*/ +0*PENDIN |/*Retype pending input at next read or input character.*/ +0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ +#endif + + tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ + tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ + tty_smode.c_cc[VINTR] =3; + + tcsetattr(0, TCSANOW, &tty_smode); + +#ifdef DEBUG + show_terminal_settings(&tty_smode); +#endif + /* + * "Write a ^R to the FIFO which causes the other end to redisplay + * the input line." + * This does not seem to work as was intended in old comment above. + * However, this control character is now (R12B-3) used by run_erl + * to trigger the version handshaking between to_erl and run_erl + * at the start of every new to_erl-session. + */ + write(wfd, "\022", 1); + + /* + * read and write + */ + while (1) { + FD_ZERO(&readfds); + FD_SET(0, &readfds); + FD_SET(rfd, &readfds); + if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (recv_sig) { + FD_ZERO(&readfds); + } + else { + fprintf(stderr, "Error in select.\n"); + result = -1; + break; + } + } + len = 0; + + /* + * Read from terminal and write to FIFO + */ + if (recv_sig) { + switch (recv_sig) { + case SIGINT: + fprintf(stderr, "[Break]\n\r"); + buf[0] = '\003'; + len = 1; + break; + case SIGWINCH: + len = window_size_seq(buf,sizeof(buf)); + break; + default: + fprintf(stderr,"Unexpected signal: %u\n",recv_sig); + } + recv_sig = 0; + } + else if (FD_ISSET(0, &readfds)) { + len = read(0, buf, sizeof(buf)); + if (len <= 0) { + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from stdin.\n"); + result = -1; + } else { + fprintf(stderr, "[EOF]\n\r"); + } + break; + } + /* check if there is an eof character in input */ + for (i = 0; i < len && buf[i] != tty_eof; i++); + if (buf[i] == tty_eof) { + fprintf(stderr, "[Quit]\n\r"); + break; + } + } + + if (len) { +#ifdef DEBUG + write(1, buf, len); +#endif + if (write_all(wfd, buf, len) != len) { + fprintf(stderr, "Error in writing to FIFO.\n"); + close(rfd); + close(wfd); + result = -1; + break; + } + STATUS("\" OK\r\n"); + } + + /* + * Read from FIFO, write to terminal. + */ + if (FD_ISSET(rfd, &readfds)) { + STATUS("FIFO read: "); + len = read(rfd, buf, BUFSIZ); + if (len < 0 && errno == EAGAIN) { + /* + * No data this time, but the writing end of the FIFO is still open. + * Do nothing. + */ + ; + } else if (len <= 0) { + /* + * Either an error or end of file. In either case, break out + * of the loop. + */ + close(rfd); + close(wfd); + if (len < 0) { + fprintf(stderr, "Error in reading from FIFO.\n"); + result = -1; + } else + fprintf(stderr, "[End]\n\r"); + break; + } else { + if (!got_some) { + if ((len=version_handshake(buf,len,wfd)) < 0) { + close(rfd); + close(wfd); + result = -1; + break; + } + if (protocol_ver >= 1) { + /* Tell run_erl size of terminal window */ + signal(SIGWINCH, handle_sigwinch); + raise(SIGWINCH); + } + got_some = 1; + } + + /* + * We successfully read at least one character. Write what we got. + */ + STATUS("Terminal write: \""); + if (write_all(1, buf, len) != len) { + fprintf(stderr, "Error in writing to terminal.\n"); + close(rfd); + close(wfd); + result = -1; + break; + } + STATUS("\" OK\r\n"); + } + } + } + + /* + * Reset terminal characterstics + * XXX + */ + tcsetattr(0, TCSANOW, &tty_rmode); + return 0; +} + +/* Call write() until entire buffer has been written or error. + * Return len or -1. + */ +static int write_all(int fd, const char* buf, int len) +{ + int left = len; + int written; + while (left) { + written = write(fd,buf,left); + if (written < 0) { + return -1; + } + left -= written; + buf += written; + } + return len; +} + +static int window_size_seq(char* buf, size_t bufsz) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + static const char prefix[] = "\033_"; + static const char suffix[] = "\033\\"; + /* This Esc sequence is called "Application Program Command" + and seems suitable to use for our own customized stuff. */ + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { + int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", + prefix, ws.ws_col, ws.ws_row, suffix); + return len; + } +#endif /* TIOCGWINSZ */ + return 0; +} + +/* to_erl run_erl + * | | + * |---------- '\022' -------->| (session start) + * | | + * |<---- "[run_erl v1-0]" ----| (version interval) + * | | + * |--- Esc_"version=1"Esc\ -->| (common version) + * | | + */ +static int version_handshake(char* buf, int len, int wfd) +{ + unsigned re_high=0, re_low; + char *end = find_str(buf,len,"]\n"); + + if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { + char wbuf[30]; + int wlen; + + if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { + fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", + RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); + return -1; + } + /* Choose highest common version */ + protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; + + wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", + protocol_ver); + if (write_all(wfd, wbuf, wlen) < 0) { + fprintf(stderr,"Failed to send version handshake\n"); + return -1; + } + end += 2; + len -= (end-buf); + memmove(buf,end,len); + + } + else { /* we assume old run_erl without version handshake */ + protocol_ver = 0; + } + + if (re_high != RUN_ERL_HI_VER) { + fprintf(stderr,"run_erl has different version, " + "using common protocol level %u\n", protocol_ver); + } + + return len; +} + + +#ifdef DEBUG +#define S(x) ((x) > 0 ? 1 : 0) + +static void show_terminal_settings(struct termios *t) +{ + fprintf(stderr,"c_iflag:\n"); + fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT)); + fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL)); + fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK)); + fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR)); + fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR)); + fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR)); + fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK)); + fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP)); + fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF)); + fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON)); + fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_oflag:\n"); + fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cflag:\n"); + fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_local:\n"); + fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO)); + fprintf(stderr,"\n"); + fprintf(stderr,"c_cc:\n"); + fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]); +} +#endif diff --git a/erts/etc/vxworks/README.VxWorks b/erts/etc/vxworks/README.VxWorks new file mode 100644 index 0000000000..299e35b513 --- /dev/null +++ b/erts/etc/vxworks/README.VxWorks @@ -0,0 +1,350 @@ + + %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% + +----------------------------------------------------------------------- +README, Erlang/OTP R11B for VxWorks on PPC860 and PPC603 +----------------------------------------------------------------------- +20060515 -- Patrik Nyblom, support@erlang.ericsson.se + +R11B is a libraries only release for VxWorks. Only the libraries of +erl_interface (ei+erl_inteface) and ic are expected to be used. Still +the whole erlang system is distributed, although no support will be +given for anything else but the libraries. The information in this +file still applies to the full erlang distribution and parts of it are +therefore somewhat irrelevant to commercial users. + + +Included OTP applications +------------------------- + +appmon +asn1 +compiler +cosEvent +cosNotification +cosTime +cosTransaction +debugger +erl_interface +erts +eva [1] +ic +inets [2] +jinterface +kernel +mesh +mnemosyne +mnesia [1] +mnesia_session +orber +os_mon +pman +runtime_tools +sasl +snmp +stdlib +tools +tv + +[1] Only ram_copies work, The VxWorks filesystems are not + reliable enough for disk_copies to be fully supported. +[2] CGI scripts do not work on VxWorks. + +Omitted applications +-------------------- + +crypto +emacs +etk +gs +odbc +parsetools +toolbar +ssl +megaco +webtools + +As `crypto' and `ssl' provides cryptographic functionality to `inets' +and `snmp', the latter applications will not handle cryptography on +VxWorks. + +Graphical interfaces +-------------------- + +For applications using graphical interfaces, only the backend part works. + +Compilers +--------- + +All compilers are expected to be run on a cross host. The VxWorks +systems memory capabilities are too restricting to allow native +compiling. The expected host system is a Sun Solaris machine, although +Erlang compilation may be done on most platforms. + +Supported boards and configuration (only libraries supported) +---------------------------------- +The following boards and configurations are supported: + +* Force PowerCore 603 with Force pcore603 BSP and VxWorks 3.5.1 (no + SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory. + +* Force Powercore 750 with Force pcore750 BSP and VxWorks 3.5.1 (no + SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory. + +* PSS Core PPC860 processors, only erl_interface (too small main memory). + +Most PowerPC boards with FPU are expected to work, but will need to be +tested by OTP to be fully supported. + +The PPC603 build has been compiled with Wind River's `-mlongcall' +flag (SPR25893) to support arbitrary function calls across more +than 32 MB of memory. + +The PPC860 (PowerQuicc) has no FPU and requires a separate build. + +For Erlang to run, the Wind kernel has to be configured with a minimum +of these variables defined in config.h (or by the Tornado +configuration tool): + + INCLUDE_ANSI_ALL + INCLUDE_ENV_VARS + INCLUDE_EXC_HANDLING + INCLUDE_EXC_TASK + INCLUDE_FLOATING_POINT + INCLUDE_FORMATTED_IO + INCLUDE_IO_SYSTEM + INCLUDE_LOADER + INCLUDE_NETWORK + INCLUDE_NET_INIT + INCLUDE_NET_SHOW + INCLUDE_NET_SYM_TBL or INCLUDE_STANDALONE_SYM_TBL + INCLUDE_PIPES + INCLUDE_POSIX_FTRUNC + INCLUDE_RLOGIN or INCLUDE_TELNET (for pty's only) + INCLUDE_SELECT + INCLUDE_SEM_BINARY + INCLUDE_SEM_COUNTING + INCLUDE_SEM_MUTEX + INCLUDE_SHELL (*) + INCLUDE_SHOW_ROUTINES + INCLUDE_SIGNALS + INCLUDE_STARTUP_SCRIPT (*) + INCLUDE_STDIO + INCLUDE_SYM_TBL + INCLUDE_TASK_HOOKS + INCLUDE_TASK_VARS + INCLUDE_TTY_DEV + INCLUDE_UNLOADER + INCLUDE_NFS or INCLUDE_RAMDRV or INCLUDE_DOSFS (i.e. a file system, + possibly read-only) (**) + +(*) Needed for the example startup script, not actually needed in production if + erlang is set up by a c routine called from usrConfig.c. +(**) INCLUDE_NFS usually requires the NFS_USER_ID and NFS_GROUP_ID variables + to be set in config.h + +As an erlang system may open a lot of files, it is recommended to raise the +default NUM_FILES variable to something like 256 in config.h like this: + #ifdef NUM_FILES + #undef NUM_FILES + #endif + #define NUM_FILES 256 + +The SENS stack *has* to be of version 1.1 or higher, 1.0 is *not* +supported and will not work reliably. Upgrades as well as the patch +for SPR23938 can be found at www.wrs.com (i.e. WindSurf). Also, the +following constants in $WIND_BASE/target/h/netBufLib.h has to be +raised to a value of at least four times the default: + + NUM_NET_MBLKS + NUM_64 + NUM_128 + NUM_256 + NUM_512 + NUM_1024 + NUM_2048 + + NUM_SYS_64 + NUM_SYS_128 + NUM_SYS_256 + NUM_SYS_512 + +Use the show routines mbufShow and netStackSysPoolShow to verify that +these pools are not exhausted. + +Installation +------------ + +To install Erlang on a VxWorks card, the following knowledge is +expected: + +* VxWorks installation and configuration. + +* Network (TCP/IP) configuration. + +* Erlang basic operation and configuration. + +There is no specific install script for erlang on the VxWorks +platform. There is however an example VxWorks startup file named +erts-5.0.1/bin/erl_script.sam under the root of an unpacked +release. There may of course be other things to do in the start +script, like using the rdate program in the erlang distribution to get +a correct date and set the TIMEZONE variable. + +Please consult the "Embedded System" documentation for further +information on installation. + +Known Bugs and problems +----------------------- + +We have found the VxWorks/NFS client file system to be unreliable. +Important file operations like rename, ftruncate, cd and unlink +doesn't always work as expected. Therefore more complex file using +parts of OTP, like DETS and disk based mnesia tables cannot be used +reliably with NFS. Lowering the NFS cache size (global variable +nfsCacheSize) to 512 gives a more reliable NFS client, but to disk +base the mnesia tables over NFS is still not a good idea, especially +as disk based mnesia tables are not supported on VxWorks. Another +problem with VxWorks file systems is that the error codes they produce +are not consistent. We have worked around this problem by mapping the +codes to POSIX ones, by doing this we make the VxWorks Erlang platform +behave similarly to the UNIX and Windows implementations. + +The rename and ftruncate operations over NFS are emulated using +copying of files. This is mainly for our own test suites and it is not +recommended to use file:rename and/or file:ftruncate on NFS file +systems in production. + +Floating point operations is somewhat faulty. For instance, testing +floating point numbers for equality should be done with care. This is +actually not a bug, IEEE specifies no equality among floating point +numbers. + +Memory handling +--------------- + +Please read the erl_set_memory_block(3) manual page in the ERTS +documentation for information concerning memory handling in the erlang +emulator. Also please observe that reclaim.o has to be loaded and +reclaim_init() called before any other erlang routines are loaded and +started. If one wish to use the resource reclamation routines in other +programs, refer to the header file in `erts-5.0.1/include/reclaim.h'. +Including that file in your C source makes malloc/realloc/free and +open/fopen/socket/close etc be redefined to routines that makes the +memory and files be free'd/closed when the task exits. Still, +reclaim_init() *has* to be called before any program that uses this is +started. + +Using heart +----------- + +The default behavior of the heart object file that is part of the +distribution is that it reboots the target when the Erlang process +hasn't given it a heart beat in 60 seconds. The normal heart beat rate +is one beat per five seconds. This makes an effective "software +watchdog" but there is really no substitute for the real thing --- a +hardware watchdog. If you want to add a hardware watchdog to the +system please contact us for directions. If you want to disable the +reboot you may set the environment variable HEART_DONT_REBOOT (see the +example erlang start script, erl). Please note that if you DO want the +card to reboot you mustn't define HEART_DONT_REBOOT at all. E.g. to +disable heart reboot you may include the following line in the start +script (as is indeed the case with the example start script). + + putenv "HEART_DONT_REBOOT=1" + +A few words on writing port program and dynamically loaded drivers for VxWorks +------------------------------------------------------------------------------ + +VxWorks has one name-space for all symbols. This makes it harder to +write C programs whose global symbols doesn't interfere with each +other. It is a good rule to avoid all globally visible symbols that +are not absolutely necessary. Due to these facts we use the following +naming rules that are crucial to follow. (there are more issues +involved but the issues described here is a good beginning). + +Port programs must have a function with the same name as the object +file. E.g. if you have an object file named `port_test.o' it must +contain a globally visible function named `port_test'. This is the +function that will be called when you output data from Erlang to the +port. (The object file, in this example, should be named +`port_test.o', but `port_test' will also do). + +Also, in an embedded system, it is recommended to load the port +program into the system before the port program is used. This is to +avoid the real time degradation dynamical linking in runtime would +introduce. Use VxWorks command ld < "port_prg" to accomplish this. + +Dynamically linked drivers must have a function with the same name as +the object file with the added suffix `_init'. We recommend the use of +the macro DRIVER_INIT in `driver.h'. E.g. if you have an object file +named `echo_drv.eld' it must contain a globally visible function +`echo_drv_init'. (The object file, in this example, should be named +`echo_drv.eld' (`eld' is short for Erlang Loadable Driver), but +`echo_drv.o' and `echo_drv' will both also do). It is also very +important to initialize all unused function pointer in the +`driver_entry' struct to NULL (see example below). + +Example of dynamically linked driver +------------------------------------ + +#include <stdio.h> +#include "driver.h" + +static int erlang_port; +static long echo_start(); +static int echo_stop(), echo_read(); + +static struct driver_entry echo_driver_entry = { + null_func, + echo_start, + echo_stop, + echo_read, + null_func, + null_func, + "echo_drv", + null_func +}; + +int DRIVER_INIT(echo_drv)(void *handle) +{ + erlang_port = -1; + + echo_driver_entry.handle = handle; + return (int) &echo_driver_entry; +} + +static long echo_start(long port,char *buf) +{ + if (erlang_port != -1) { + return -1; + } + + erlang_port = port; + return port; +} + +static int echo_read(long port, char *buf, int count) +{ + return driver_output(erlang_port, buf, count); +} + +static int echo_stop() +{ + return erlang_port = -1; +} diff --git a/erts/etc/vxworks/erl.exec.c b/erts/etc/vxworks/erl.exec.c new file mode 100644 index 0000000000..6b45ebaa39 --- /dev/null +++ b/erts/etc/vxworks/erl.exec.c @@ -0,0 +1,129 @@ +/* + * %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% + */ +/* + A simpified version of the 'erl.exec' "startup script". + Called (e.g. from VxWorks shell) with all arguments in a + single string, e.g.: erl "-name thisnode -s mymod myfunc". + These arguments are handled as in 'erl.exec': + -name + -sname + -noshell + -noinput + anything else is just passed on to the emulator. Note that there + is no automatic start of epmd, that -oldshell is implicit, and + that you need to set current directory appropriately if you want + auto-load of port programs +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DEFAULT_HOMEDIR /* used if environment HOME isn't set */ +#define DEFAULT_HOMEDIR "/" +#endif + +#define ARGLEN 2048 /* Total length of args passed to erl_main */ +#define ARGMAX 64 /* Max no of "extra" args */ + +static char *erl_cmd = "erl_main -n "; + +static toomuch() +{ + fprintf(stderr, "erl: Too many arguments\n"); + return(-1); +} + +static toolittle(arg) +char *arg; +{ + fprintf(stderr, "erl.exec: Missing argument for %s\n", arg); + return(-1); +} + +erl_exec(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) +int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10; +{ + char *shell = "-oldshell ", *noshell = "", + *home, *rootdir, *bindir, *progname; + char cmd[ARGLEN], eargs[ARGLEN], iargs[ARGLEN]; + char *args[ARGMAX], *arglast = NULL, *argp; + int nargs = 0, len, i; + + if ((rootdir = getenv("ROOTDIR")) == NULL || + (bindir = getenv("BINDIR")) == NULL || + (progname = getenv("PROGNAME")) == NULL) { + fprintf(stderr, "erl.exec: ROOTDIR, BINDIR, and PROGNAME must be set."); + return -1; + } + eargs[0] = '\0'; + iargs[0] = '\0'; + if ((home = getenv("HOME")) == NULL) + home = DEFAULT_HOMEDIR; + argp = strtok_r((char *)arg1, " \t", &arglast); + while (argp != NULL) { + if (strcmp(argp, "-name") == 0) { + if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL) + return(toolittle("-name")); + strcat(iargs, "-name "); + strcat(iargs, argp); + strcat(iargs, " "); + } else if (strcmp(argp, "-sname") == 0) { + if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL) + return(toolittle("-sname")); + strcat(iargs, "-sname "); + strcat(iargs, argp); + strcat(iargs, " "); + } else if (strcmp(argp, "-noshell") == 0) { + strcat(iargs, "-noshell -noinp_shell "); + } else if (strcmp(argp, "-noinput") == 0) { + strcat(iargs, "-noshell -noinput "); + } else { + if (nargs > ARGMAX - 1) + return(toomuch()); + args[nargs++] = argp; + } + argp = strtok_r((char *)NULL, " \t", &arglast); + } + strcpy(cmd, erl_cmd); + strcat(cmd, eargs); + strcat(cmd, " -- -root "); + strcat(cmd, rootdir); + strcat(cmd, " -progname "); + strcat(cmd, progname); + strcat(cmd, " -- "); + strcat(cmd, "-home "); + strcat(cmd, home); + strcat(cmd, " "); + strcat(cmd, iargs); + + len = strlen(cmd); + for (i = 0; i < nargs; i++) { + if (len + strlen(args[i]) + 2 >= ARGLEN) + return(toomuch()); + cmd[len++] = ' '; + strcpy(&cmd[len], args[i]); + len += strlen(args[i]); + } + argcall(cmd); +} + diff --git a/erts/etc/vxworks/erl_io.c b/erts/etc/vxworks/erl_io.c new file mode 100644 index 0000000000..0032b77079 --- /dev/null +++ b/erts/etc/vxworks/erl_io.c @@ -0,0 +1,108 @@ +/* + * %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% + */ +/* Some stuff to let the Erlang and VxWorks shells coexist peacefully. + Basically, run Erlang as a spawned task with input redirected to + the slave side of a pseudo-tty, and connect explicitly to the master + side of the pseudo-tty to send input to Erlang when desired. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <ioLib.h> +#include <taskLib.h> +#include <ptyDrv.h> + +extern int spTaskPriority, spTaskOptions; + +#define TBUFSIZ 512 + +#define DEFAULT_STACK_SIZE 100000 + +static int slavefd = -1, masterfd = -1; +static run_erl(); + +/* Frontend to the Erlang startup function - callable from VxWorks shell + or script. 'arg' is actually a string passed to the real startup. */ +start_erl(arg) +int arg; +{ + int stacksize; + char *stackenv; + + /* create and open the pty - we want the master side to be open + all the time, since closing it probably generates EOF on the + slave side */ + (void)ptyDevCreate("/pty/erlang.", TBUFSIZ, TBUFSIZ); + if (slavefd != -1) + (void)close(slavefd); + slavefd = open("/pty/erlang.S", O_RDONLY, 0); + if (masterfd != -1) + (void)close(masterfd); + masterfd = open("/pty/erlang.M", O_WRONLY, 0); + + /* flush any old leftover garbage */ + (void) ioctl(masterfd, FIOFLUSH, 0); + if ((stackenv = getenv("ERLSTACKSIZE")) == NULL) + stacksize = DEFAULT_STACK_SIZE; + else + stacksize = atoi(stackenv); + /* spawn Erlang, via stub below */ + return(taskSpawn("erlang", spTaskPriority, spTaskOptions, stacksize, + run_erl, arg, 0,0,0,0,0,0,0,0,0)); +} + +/* Little stub that runs in the spawned task - we need this to redirect + stdin reliably (redirections aren't "inherited" in VxWorks) */ +static +run_erl(arg) +int arg; +{ + ioTaskStdSet(0, 0, slavefd); /* redirect stdin to slave side of pty */ + + /* We don't want to redirect stdout/err since no one will be reading + from the master side (to_erl - and the open()s above - could be + made bidirectional, but still the master side would only be read + when to_erl was running), and output can eventually fill the pty + buffer and cause the Erlang system to block. Not redirecting + stdout/err will have the effect that output from Erlang, e.g. the + shell prompt, will appear on console/rlogin/whatever even when + to_erl isn't running, which may be confusing - can't win 'em all... */ + + erl_exec(arg, 0,0,0,0,0,0,0,0); /* call the real startup */ +} + +/* Function callable from VxWorks shell to talk to Erlang - stop talking + and return to VxWorks shell through ^D (EOF) */ +to_erl() +{ + char buf[TBUFSIZ]; + int cc; + + if (masterfd == -1) { /* sanity check */ + fprintf(stderr, "Must start_erl first!\n"); + return(-1); + } + while ((cc = read(0, buf, TBUFSIZ)) > 0) /* just pass everything through */ + if (write(masterfd, buf, cc) != cc) { + fprintf(stderr, "Write to Erlang failed!\n"); + return(-1); + } + return(cc); +} diff --git a/erts/etc/vxworks/erl_script.sam.in b/erts/etc/vxworks/erl_script.sam.in new file mode 100644 index 0000000000..81c2b0128d --- /dev/null +++ b/erts/etc/vxworks/erl_script.sam.in @@ -0,0 +1,100 @@ +# +# %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% +# +# +# erl_script.sam +# Sample VxWorks script to start Erlang +# +# Note! This is not a complete or ready to use VxWorks startup script, +# rather an example of what You should add to Your existing startupscript +# to execute the erlang emulator at boot. +# +# When writing Your own script to start erlang, the paths to +# the binaries have to be changed to reflect your system. +# +# The ROOTDIR variable should not point to a ftp or rcp filesystem unless +# the erlang machine is run in embedded mode. Loading of modules +# is far to slow if the erlang binaries are not placed on a real filesystem +# like NFS or any type of local filesystem. +# + +# +# Load modules +# + +# +# First load and initiate the reclaim facility +# +ld </home/tornado/erlvxworks/erts-%VSN%/bin/reclaim.o +reclaim_init() + +# +# Now load the runtime system +# +ld </home/tornado/erlvxworks/erts-%VSN%/bin/jam +ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl.exec +ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl_io +ld </home/tornado/erlvxworks/erts-%VSN%/bin/vxcall +ld </home/tornado/erlvxworks/erts-%VSN%/bin/heart +ld </home/tornado/erlvxworks/erts-%VSN%/bin/epmd + +# +# Stack sizes +# +putenv "ERLSTACKSIZE=100000" +putenv "ERLPORTSTACKSIZE=100000" + +# +# Activate Round robin scheduling +# +kernelTimeSlice 1 + +# +# Distribution +# The VxWorks machines host name +sethostname "sb001", 5 +# Erlangs internal resolver +putenv "ERLRESCONF=/home/tornado/erlvxworks/erts-%VSN%/bin/resolv.conf" + +# +# Start epmd (for distribution) +# +start_epmd "-daemon" + +# +# Misc environment variables +# +putenv "ROOTDIR=/home/tornado/erlvxworks" +putenv "BINDIR=/home/tornado/erlvxworks/erts-%VSN%/bin" +putenv "PROGNAME=erl" +putenv "HOME=/home/tornado/erlvxworks" + +# +# Set heart no reboot mode (to make heart reboot - +# don't define HEART_DONT_REBOOT at all) +# +putenv "HEART_DONT_REBOOT=1" + +# To get fullsweep garbage collection on systems with +# very limited memory, set ERL_FULLSWEEP_AFTER to "0": +# putenv "ERL_FULLSWEEP_AFTER=0" + +# +# Start Erlang/OTP +# +start_erl "-oldshell -heart -sname vxnode -setcookie switch -boot start_sasl" diff --git a/erts/etc/vxworks/heart_config.c b/erts/etc/vxworks/heart_config.c new file mode 100644 index 0000000000..7e60e61fbb --- /dev/null +++ b/erts/etc/vxworks/heart_config.c @@ -0,0 +1,60 @@ +/* + * %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% + */ +/* + * A basic heart configure module for VxWorks. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <vxWorks.h> +#include <stdio.h> +#include <stdlib.h> +#include <rebootLib.h> +#include <sysLib.h> + +/* wd_init is executed to initialize a watchdog (if one is used). */ +int wd_init(timeout, prio) + int timeout, prio; +{ + +} + +/* wd_reset should be called every 5th second from heart */ +void wd_reset() +{ + +} + +/* This routine is called when erlang has closed */ +void heart_reboot() +{ + if (getenv("HEART_DONT_REBOOT") != NULL) { + fprintf(stderr, "heart_config: HEART_DONT_REBOOT set, no reboot ...\n"); + } else { + fprintf(stderr, "heart_config: rebooting ...\n"); + taskDelay(sysClkRateGet() * 5); + reboot(BOOT_CLEAR); + } +} + + + + diff --git a/erts/etc/vxworks/heart_config.h b/erts/etc/vxworks/heart_config.h new file mode 100644 index 0000000000..5ffaaa8c3f --- /dev/null +++ b/erts/etc/vxworks/heart_config.h @@ -0,0 +1,35 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * This is heart's watchdog interface for VxWorks. + */ + +#ifndef _HW_WATCHDOG_H +#define _HW_WATCHDOG_H + +extern void wd_init(int timeout, int prio); /* wd_init initializes the + watchdog, if one is used. */ +extern void wd_reset(void); /* wd_reset is used by heart to kick + the watchdog, if one is used. */ +extern void heart_reboot(void); /* reboot is called if heart discovers + that the Erlang task has stopped sending + heart beats. It can log system status + and should reboot VxWorks. */ + +#endif diff --git a/erts/etc/vxworks/rdate.c b/erts/etc/vxworks/rdate.c new file mode 100644 index 0000000000..3e8cc644d0 --- /dev/null +++ b/erts/etc/vxworks/rdate.c @@ -0,0 +1,87 @@ +/* + * %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% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <timers.h> +#ifdef NETDB +#include <netdb.h> +#endif +#include <sys/socket.h> +#include <netinet/in.h> + +/* + rdate("host") - Set the time from "host". +*/ + +/* No getservbyname() available... */ +#define TIME_PORT 37 + +rdate(host) +char *host; +{ + u_long haddr; +#ifdef NETDB + struct hostent *hp; +#endif + struct sockaddr_in saddr; + int sock; + u_long net_time; + struct timespec t; + + if ((haddr = inet_addr(host)) == ERROR) { +#ifdef NETDB + if ((hp = gethostbyname(host)) == NULL) { +#else + if ((haddr = hostGetByName(host)) == ERROR) { +#endif + printf("Host not found.\n"); + return(-1); + } +#ifdef NETDB + memcpy(&haddr, hp->h_addr, sizeof(haddr)); +#endif + } + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + memcpy(&saddr.sin_addr, &haddr, sizeof(haddr)); + saddr.sin_port = htons(TIME_PORT); + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("socket"); + return(-1); + } + if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + perror("connect"); + close(sock); + return(-1); + } + if (read(sock, &net_time, 4) != 4) { + perror("read"); + close(sock); + return(-1); + } + t.tv_sec = ntohl(net_time); + t.tv_sec -= 2208988800; /* seconds 1900-01-01 -- 1970-01-01 */ + t.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &t); + close(sock); + return(0); +} diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c new file mode 100644 index 0000000000..d8676b3750 --- /dev/null +++ b/erts/etc/vxworks/reclaim.c @@ -0,0 +1,551 @@ +/* + * %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% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <version.h> +#include <string.h> +#include <types.h> +#include <sigLib.h> +#include <ioLib.h> +#include <iosLib.h> +#include <fioLib.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <symLib.h> +#include <sysLib.h> +#include <sysSymTbl.h> +#include <loadLib.h> +#include <taskLib.h> +#include <taskVarLib.h> +#include <taskHookLib.h> +#include <tickLib.h> +#include <time.h> +#include <rngLib.h> +#include <semLib.h> +#include <selectLib.h> +#include <sockLib.h> +#include <a_out.h> +#include <wdLib.h> +#include <timers.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <stdarg.h> + +#include <stdio.h> +#include <math.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + + + +#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */ +#include "reclaim.h" +#include "reclaim_private.h" + +#undef open +#undef creat +#undef socket +#undef accept +#undef close +#undef fopen +#undef fdopen +#undef freopen +#undef fclose +/* XXX Should do opendir/closedir too... */ +#undef malloc +#undef calloc +#undef realloc +#undef free +#undef cfree + +#ifdef _ARCH_PPC +#define MAX_FILES_SYM_NAME "maxFiles" +#else +#define MAX_FILES_SYM_NAME "_maxFiles" +#endif + + +/* + * Use another free() function upon task deletion? + * Note! When changing free function, the new free function will have to + * be able to cope with ALL previously used malloced areas, that is + * it has to be able to find out how things are malloced + * to free them in the right way! + */ +static FreeFunction reclaim_free_function = NULL; + +/* delete hook handling (see below) */ +static int hook_added = 0; /* Initated at first reclaim_init, an extra + non MT-safe check that we only get + initialized once */ + +/* Forward... */ +static void save_reclaim(WIND_TCB *tcbp); + +struct mall_data { + struct mall_data *next; + char *self; +}; + +struct task_data { + FUNCPTR version; /* To recognize when we have reloaded */ + int max_files; /* It may change... */ + struct fd_set open_fds; + struct mall_data *mall_data; + FUNCPTR delete_hook; + caddr_t hook_data; + FILE *open_fps[1]; /* Will be max_files long */ +} *task_data = NULL; + +static int max_files = 50; /* default configAll.h */ + +int reclaim_max_files(void) +{ + return max_files; +} + +#ifdef DEBUG +#define check_hook() \ +((task_data != NULL || \ + fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \ + (unsigned int)taskIdSelf(),\ + __LINE__)) && \ +(task_data != NULL || \ + (taskVarAdd(0, (int *)&task_data) == OK && \ + (task_data = (struct task_data *)\ + calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \ + (task_data->version = (FUNCPTR)save_reclaim) != NULL && \ + (task_data->max_files = max_files) != 0 && \ + fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf())))) +#else +#define check_hook() \ +(task_data != NULL || \ + (taskVarAdd(0, (int *)&task_data) == OK && \ + (task_data = (struct task_data *)\ + calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \ + (task_data->version = (FUNCPTR)save_reclaim) != NULL && \ + (task_data->max_files = max_files) != 0)) +#endif + +/* + * Global initialization of the reclaim data structures, mainly + * the max_files variable. This HAS to be called by some task before + * the first task that utilizes this exit's, preferrably before any + * task makes the first use of this library. + */ +STATUS reclaim_init(void) +{ + int *mp; + SYM_TYPE type; + struct task_data *tdp; + int i; + + if (!hook_added) { + /* race condition */ + ++hook_added; + /* Try to find the maxFiles value */ + if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp, + &type, + N_EXT | N_BSS, N_EXT | N_BSS) == OK || + symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp, + &type, + N_EXT | N_DATA, N_EXT | N_DATA) == OK) { + +#ifdef DEBUG + fdprintf(2, "Found maxFiles=%d\n", *mp); +#endif + if (*mp <= FD_SETSIZE) + max_files = *mp; + else + max_files = FD_SETSIZE; + } + if (task_data != NULL && task_data->max_files != max_files) { + /* fix our own iff we have one */ + if ((tdp = (struct task_data *) + realloc(task_data, sizeof(struct task_data) + + max_files*sizeof(FILE *))) != NULL) { + task_data = tdp; + for (i = task_data->max_files; i < max_files; i++) + task_data->open_fps[i] = NULL; + task_data->max_files = max_files; + } + } + /* Make sure taskVariables are deleted AFTER our hook is run. */ + taskVarInit(); + if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) { + fprintf(stderr, + "Panic: taskDeleteHook cannot be added for reclaim.\n"); + return ERROR; + } + return OK; + } else + return ERROR; +} + +/* N.B.!! We do *not* execute in the context of the dying task here, + but rather that of tExcTask - we do get a pointer to the task's + TCB though - this pointer is in fact also the task's ID. */ +static void save_reclaim(WIND_TCB *tcbp) +{ + int i, var, oldfd; + struct task_data *tdp; + struct mall_data *mdp, *mdnextp; + + if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR && + var != 0) { + tdp = (struct task_data *)var; + if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */ +#ifdef DEBUG + fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp); +#endif + /* Ugh! VxWorks doesn't even flush stdout/err - we need to + get at those (which are task-private of course, i.e. we + can't just do fflush(stdout) here) - we could be really + pedantic and try to redefine stdout/err (which "are" + function calls) too, snarfing the values when they are + used - but besides the overhead this is problematic since + they are actually #defines already... We'll peek in the + TCB instead (no documentation of course). And of course, + we must also meddle with the *file descriptor* indirections, + or we'll just flush out on tExcTask's descriptors... */ + for (i = 1; i <= 2; i++) { + if (tcbp->taskStdFp[i] != NULL) { +#ifdef DEBUG + fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr"); +#endif + oldfd = ioTaskStdGet(0, i); + ioTaskStdSet(0, i, tcbp->taskStd[i]); + fflush(tcbp->taskStdFp[i]); + ioTaskStdSet(0, i, oldfd); + } + } + for (i = 3; i < tdp->max_files; i++) { + if (FD_ISSET(i, &tdp->open_fds)) { +#ifdef DEBUG + fdprintf(2, "close(%d) ", i); +#endif + (void) close(i); + } + if (tdp->open_fps[i] != NULL) { +#ifdef DEBUG + fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]); +#endif + (void) fclose(tdp->open_fps[i]); + } + } + i = 0; + mdp = tdp->mall_data; + while (mdp != NULL) { + mdnextp = mdp->next; + if(reclaim_free_function != NULL) + (*reclaim_free_function)(mdp->self); + else + free(mdp->self); + i++; + mdp = mdnextp; + } +#ifdef DEBUG + fdprintf(2, "\nFreeing memory: total %d mallocs\n", i); +#endif + + if (tdp->delete_hook != NULL) { +#ifdef DEBUG + fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook); +#endif + (*tdp->delete_hook)(tdp->hook_data); +#ifdef DEBUG + fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook); +#endif + } +#ifdef DEBUG + fdprintf(2, "Freeing own mem at 0x%08x\n", tdp); +#endif + (void) free((char *)tdp); +#ifdef DEBUG + fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp, + taskIdSelf()); + checkStack(0); +#endif + } + } +#ifdef DEBUG + else + fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var); +#endif +} + +/* + * This sets another free function to be used by the task deletion hook. + * The free function HAS to be able to free ANY type of dynamically allocated + * memory that can be in the task data list of allocated memory, that is + * also memory that's allocated before the free function was set. + * A "user-supplied" free function is GLOBAL to the system!!! + * A race condition is present, a task delete hook may be running when this + * function is called, that may not be especially funny... + */ +void set_reclaim_free_function(FreeFunction f){ + reclaim_free_function = f; +} + +void save_delete_hook(FUNCPTR func, caddr_t parm) +{ + if (check_hook()) { + task_data->delete_hook = func; + task_data->hook_data = parm; + } +} + +/* + * plain_malloc is only used by spawn_start; plain_free by call_proc; + * save_fd is used by both. + */ +void *plain_malloc(size_t size){ + return(malloc(size)); +} + +void *plain_realloc(void *ptr, size_t size){ + return(realloc(ptr, size)); +} + +void plain_free(void *ptr){ + free(ptr); +} + +void save_fd(int fd){ + if (fd >= 0 && check_hook() && fd < task_data->max_files) + FD_SET(fd, &task_data->open_fds); +} + +int save_open(char *path, int flags, /*mode_t mode*/ ...){ + int fd; + mode_t mode = 0; + if(flags & O_CREAT){ + va_list pvar; + va_start(pvar,flags); +#ifdef __GNUC__ +#warning save_open() gives three known alignment warnings. +#endif + mode = va_arg(pvar, mode_t); + va_end(pvar); + } + if ((fd = open(path, flags, mode)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_creat(char *path, int mode){ + int fd; + if ((fd = creat(path, mode)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_socket(int domain, int type, int protocol){ + int fd; + if ((fd = socket(domain, type, protocol)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_accept(int s, struct sockaddr *addr, int *addrlen){ + int fd; + if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook()) + FD_SET(fd, &task_data->open_fds); + return(fd); +} + +int save_close(int fd){ + if (fd >= 0 && fd <= FD_SETSIZE && check_hook()) + FD_CLR(fd, &task_data->open_fds); + return(close(fd)); +} + +/* The dealing with FILE *'s below isn't strictly correct, we assume + that one never has several pointing to the same fd - in the unlikely + event that one does, all but the last one opened is forgotten */ +FILE *save_fopen(const char *filename, char *type){ + FILE *fp; + + if ((fp = fopen(filename, type)) != NULL && + check_hook() && fileno(fp) < task_data->max_files) + task_data->open_fps[fileno(fp)] = fp; + return(fp); +} + +FILE *save_fdopen(int fd, char *type){ + FILE *fp; + + if ((fp = fdopen(fd, type)) != NULL && + check_hook() && fileno(fp) < task_data->max_files) { + task_data->open_fps[fileno(fp)] = fp; + FD_CLR(fd, &task_data->open_fds); + } + return(fp); +} + +FILE *save_freopen(char *filename, char *type, FILE *stream){ + FILE *fp; + + if (check_hook()) { + if(fileno(stream) < task_data->max_files && + task_data->open_fps[fileno(stream)] == stream) + task_data->open_fps[fileno(stream)] = NULL; + if ((fp = freopen(filename, type, stream)) != NULL && + fileno(fp) < task_data->max_files) + task_data->open_fps[fileno(fp)] = fp; + } else + fp = freopen(filename, type, stream); + return(fp); +} + +int save_fclose(FILE *stream){ + if (check_hook() && fileno(stream) < task_data->max_files && + task_data->open_fps[fileno(stream)] == stream) + task_data->open_fps[fileno(stream)] = NULL; + return(fclose(stream)); +} + +/* We link all malloc'ed segments by adding a couple of pointers + at the *end* - that way we can return the address malloc gave + (need to make sure we align those pointers) */ + +/* + #define MALL_MARGIN 32 + #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN) + #define save_mall_size1(size) \ + (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*))) + + #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN) + #define save_mall_enq1(ptr, mdp) \ + (((struct mall_data *)(mdp))->self = (ptr), \ + ((struct mall_data *)(mdp))->next = task_data->mall_data, \ + task_data->mall_data = (struct mall_data *)(mdp)) +*/ +#define save_mall_size(size) \ +(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*))) +#define save_mall_enq(ptr, mdp) \ +(((struct mall_data *)(mdp))->self = (ptr), \ + ((struct mall_data *)(mdp))->next = task_data->mall_data, \ + task_data->mall_data = (struct mall_data *)(mdp)) + + +#define save_mall_deq(ptr) { \ + struct mall_data *mdp = task_data->mall_data, \ + **prevnext = &task_data->mall_data; \ + while (mdp != NULL && mdp->self != (ptr)) { \ + prevnext = &mdp->next; \ + mdp = mdp->next; \ + } \ + if (mdp != NULL) *prevnext = mdp->next; \ +} + +void *save_malloc2(size_t size, MallocFunction mf){ + unsigned msize = save_mall_size(size); + char *ptr; + + if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + return((void *) ptr); +} + +void *save_malloc(size_t size){ + return save_malloc2(size, &malloc); +} + +void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){ + unsigned msize = save_mall_size(nelem * elsize); + char *ptr; + + if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + return((void *) ptr); +} + +void *save_calloc(size_t nelem, size_t elsize){ + return save_calloc2(nelem,elsize,&calloc); +} + +void *save_realloc2(void *optr, size_t size, ReallocFunction rf){ + unsigned msize = save_mall_size(size); + char *ptr; + + /* First we must dequeue the old save block, after that + we try to realloc, if that succeeds we enqueue the new + block, if it fails we have to enqueue the old one anew + so we must deduce the size of that old block first. */ + + struct mall_data *mdp0 = task_data->mall_data, + **prevnext0 = &task_data->mall_data; + while (mdp0 != NULL && mdp0->self != (((char *) optr))) { + prevnext0 = &mdp0->next; + mdp0 = mdp0->next; + } + /* mdp0 == NULL (can) mean that the block that is realloced + have been malloced with an (for example) ordinary malloc + (that is not a save_malloc). This is handled like: no dequeing + is done of that block, the new block is enqueued */ + if (mdp0 != NULL) + save_mall_deq(((char *) optr)); + + if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL && + check_hook()) + save_mall_enq((void *) ptr, (void *) (ptr + msize)); + else if (mdp0 != NULL) + /* re-enqueue the old block that has just been dequeued */ + save_mall_enq(((char *) optr), mdp0); + + return((void *) ptr); +} + +void *save_realloc(void *optr, size_t size){ + return save_realloc2(optr,size,&realloc); +} + +void save_free2(void *ptr, FreeFunction ff) +{ + if (check_hook()) + save_mall_deq(((char *) ptr)); + (*ff)(ptr); +} + +void save_free(void *ptr){ + save_free2(ptr,&free); +} + +void save_cfree2(void *ptr, CfreeFunction cf) +{ + if (check_hook()) + save_mall_deq(((char *)ptr)); + (*cf)(ptr); +} + +void save_cfree(void *ptr){ + save_cfree2(ptr,&cfree); +} + diff --git a/erts/etc/vxworks/reclaim.h b/erts/etc/vxworks/reclaim.h new file mode 100644 index 0000000000..ca9aa8f6be --- /dev/null +++ b/erts/etc/vxworks/reclaim.h @@ -0,0 +1,150 @@ +/* + * %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% + */ +#ifndef _RECLAIM_H +#define _RECLAIM_H + + +/* The Erlang release for VxWorks includes a simple mechanism for + "resource reclamation" at task exit - it allows replacement of the + functions that open/close "files" and malloc/free memory with versions + that keep track, to be able to "reclaim" file descriptors and memory + when a task exits (regardless of *how* it exits). + + The interface to this mechanism is made available via this file, + with the following caveats: + + - The interface may change (or perhaps even be removed, though that + isn't likely until VxWorks itself provides similar functionality) + in future releases - i.e. you must always use the version of this + file that comes with the Erlang release you are using. + + - Disaster is guaranteed if you use the mechanism incorrectly (see + below for the correct way), e.g. allocate memory with the "tracking" + version of malloc() and free it with the "standard" version of free(). + + - The mechanism (of course) incurs some performance penalty - thus + for a simple program you may be better off with careful programming, + making sure that you do whatever close()/free()/etc calls that are + appropriate at all exit points (though if you need to guard against + taskDelete() etc, things get messy...). + + To use the mechanism, simply program your application normally, i.e. + use open()/close()/malloc()/free() etc as usual, but #include this + file before any usage of the relevant functions. NOTE: To avoid the + "disaster" mentioned above, you *must* #include it in *all* (or none) + of the files that manipulate a particular file descriptor, allocated + memory area, etc. + + Before any task that uses this utility is loaded (which includes the + erlang emulator), the reclaim.o object file has to be loaded and + the function reclaim_init() has to be called. reclaim_init should be called + only _ONCE_ in a systems lifetime and has only a primitive guard + against multiple calls (i.e. a global variable is checked). Therefore + the initialization should occur either in the start script of the system + or (even better) in the usrInit() part of system initialization. The + object file itself should be loaded only once, so linking it with the + kernel is a good idea, linking with each application is an extremely bad + dito. Make really sure that it's loaded _before_ any application that + uses it if You want to load it in the startup script. + + If You dont want to have #define's for the posix/stdio names + of the file/memory operations (i.e. no #define malloc save_malloc etc), + #define RECLAIM_NO_ALIAS in Your source before reclaim.h is included. +*/ + +#include <vxWorks.h> /* STATUS, size_t */ +#include <sockLib.h> /* struct sockaddr */ +#include <memLib.h> +#include <stdio.h> /* FILE */ + +#if defined(__STDC__) +#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \ +extern RetType FunName ParamList +#define _RECLAIM_VOID_PTR void * +#define _RECLAIM_VOID_PARAM void +#define _RECLAIM_VOID_RETURN void +#elif defined(__cplusplus) +#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \ +extern "C" RetType FunName ParamList +#define _RECLAIM_VOID_PTR void * +#define _RECLAIM_VOID_PARAM +#define _RECLAIM_VOID_RETURN void +#else +#define _RECLAIM_DECL_FUN(RetType, FunName, Ignore) extern RetType FunName() +#define DECLARE_FUNCTION_TYPE(RetType, Type, PList) typedef RetType (* Type)() +#define _RECLAIM_VOID_PTR char * +#define _RECLAIM_VOID_PARAM +#define _RECLAIM_VOID_RETURN +#endif /* __STDC__ / __cplusplus */ + +/* Initialize the facility, on a per system basis. */ +_RECLAIM_DECL_FUN(STATUS, reclaim_init, (_RECLAIM_VOID_PARAM)); + +/* File descriptor operations */ +_RECLAIM_DECL_FUN(int,save_open,(char *, int, ...)); +_RECLAIM_DECL_FUN(int,save_creat,(char *, int)); +_RECLAIM_DECL_FUN(int,save_socket,(int, int, int)); +_RECLAIM_DECL_FUN(int,save_accept,(int, struct sockaddr *, int *)); +_RECLAIM_DECL_FUN(int,save_close,(int)); +/* Interface to add an fd to what's reclaimed even though it's not open with + one of the above functions */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_RETURN, save_fd, (int fd)); +#ifndef RECLAIM_NO_ALIAS +#define open save_open +#define creat save_creat +#define socket save_socket +#define accept save_accept +#define close save_close +#endif +/* Stdio file operations */ +_RECLAIM_DECL_FUN(FILE *, save_fopen, (const char *, char *)); +_RECLAIM_DECL_FUN(FILE *, save_fdopen, (int, char *)); +_RECLAIM_DECL_FUN(FILE *, save_freopen, (char *, char *, FILE *)); +_RECLAIM_DECL_FUN(int, save_fclose, (FILE *)); +/* XXX Should do opendir/closedir too... */ +#ifndef RECLAIM_NO_ALIAS +#define fopen save_fopen +#define fdopen save_fdopen +#define freopen save_freopen +#define fclose save_fclose +#endif +/* Memory allocation */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_malloc, (size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_calloc, (size_t, size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_realloc, + (_RECLAIM_VOID_PTR, size_t)); +_RECLAIM_DECL_FUN(void, save_free, (_RECLAIM_VOID_PTR)); +_RECLAIM_DECL_FUN(void, save_cfree, (_RECLAIM_VOID_PTR)); +#ifndef RECLAIM_NO_ALIAS +#define malloc save_malloc +#define calloc save_calloc +#define realloc save_realloc +#define free save_free +#define cfree save_cfree +#endif +/* Generic interfaces to malloc etc... */ +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_malloc, (size_t)); +_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_realloc, + (_RECLAIM_VOID_PTR, size_t)); +_RECLAIM_DECL_FUN(void, plain_free, (_RECLAIM_VOID_PTR)); +#endif /* _RECLAIM_H */ + + + + diff --git a/erts/etc/vxworks/reclaim_private.h b/erts/etc/vxworks/reclaim_private.h new file mode 100644 index 0000000000..4ed935bee2 --- /dev/null +++ b/erts/etc/vxworks/reclaim_private.h @@ -0,0 +1,44 @@ +/* + * %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% + */ +#ifndef _RECLAIM_PRIVATE_H +#define _RECLAIM_PRIVATE_H +/* + * Private header for the reclaim facility, also included in the emulator. + */ + +#include "reclaim.h" + +/* Typedefs for ANSI memory allocation function pointers */ +typedef void *(*MallocFunction)(size_t); +typedef void *(*ReallocFunction)(void *, size_t); +typedef void *(*CallocFunction)(size_t, size_t); +typedef void (*FreeFunction)(void *); +typedef STATUS (*CfreeFunction)(char *); + +/* Functions for internal use and use by the emulator */ +extern int reclaim_max_files(void); +extern void set_reclaim_free_function(FreeFunction f); +extern void save_delete_hook(FUNCPTR func, caddr_t parm); +extern void *save_malloc2(size_t size, MallocFunction mf); +extern void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf); +extern void *save_realloc2(void *optr, size_t size, ReallocFunction rf); +extern void save_free2(void *ptr, FreeFunction ff); +extern void save_cfree2(void *ptr, CfreeFunction ff); + +#endif /* _RECLAIM_PRIVATE_H */ diff --git a/erts/etc/vxworks/resolv.conf b/erts/etc/vxworks/resolv.conf new file mode 100644 index 0000000000..85c89d64c4 --- /dev/null +++ b/erts/etc/vxworks/resolv.conf @@ -0,0 +1,6 @@ +domain du.uab.ericsson.se +nameserver 134.138.176.16 +nameserver 136.225.254.224 +nameserver 134.138.128.25 +search du.uab.ericsson.se uab.ericsson.se ericsson.se +lookup bind file diff --git a/erts/etc/vxworks/vxcall.c b/erts/etc/vxworks/vxcall.c new file mode 100644 index 0000000000..3362d05fc5 --- /dev/null +++ b/erts/etc/vxworks/vxcall.c @@ -0,0 +1,145 @@ +/* + * %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% + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <vxWorks.h> +#include <symLib.h> +#include <sysSymTbl.h> +#include <a_out.h> + +extern char *malloc(); +static STATUS lookup(); + +/* + Little utility to convert from Unix' argv,argv calling conventions to + VxWorks' arg0,arg1,arg2,... + Will do limited argument parsing - no parenthesis around nor commas + between the args, which may be "-enclosed strings (without \ escapes), + '-enclosed characters (also no \ escapes), integers, or symbols. +*/ + +int vxcall(argc, argv) +int argc; +char **argv; +{ + int vxarg[10]; /* Max 10 args can be passed */ + FUNCPTR entry; + SYM_TYPE type; + int i, l; + +#ifdef DEBUG + fdprintf(2, "vxcall:"); + for (i = 1; i < argc; i++) + fdprintf(2, " %s", argv[i]); + fdprintf(2, "\n"); +#endif + if (lookup(argv[1], N_EXT | N_TEXT, (char **)&entry) != OK) + return(ERROR); + /* Do limited "C" parsing of the args */ + for (i = 0; i < 10; i++) { + if (i < argc - 2) { + switch (argv[i+2][0]) { + case '"': + l = strlen(argv[i+2]) - 1; + if (argv[i+2][l] != '"') + return(ERROR); + /* just strip the quotes - should do \escapes within... */ + vxarg[i] = (int)&argv[i+2][1]; + argv[i+2][l] = '\0'; + break; + case '\'': + if (argv[i+2][2] != '\'') + return(ERROR); + vxarg[i] = argv[i+2][1]; /* should do \escapes... */ + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + vxarg[i] = atoi(argv[i+2]); /* should do octal, hex, float.. */ + break; + default: + if (lookup(argv[i+2], 0, (char **)&vxarg[i]) != OK) + return(ERROR); + } + } else + vxarg[i] = 0; + } +#ifdef DEBUG + fdprintf(2, "calling 0x%x(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n", + entry, vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4], + vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]); +#endif + return((*entry)(vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4], + vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9])); +} + +/* Entry point for unix:cmd in post-4.1 erlang - uses "sh -c 'cmd...'" */ +int sh(argc, argv) +int argc; +char **argv; +{ + int ll = strlen(argv[argc-1]) - 1; + +#ifdef DEBUG + int i; + fdprintf(2, "sh:"); + for (i = 1; i < argc; i++) + fdprintf(2, " %s", argv[i]); + fdprintf(2, "\n"); +#endif + if (strcmp(argv[1], "-c") != 0 || + argv[2][0] != '\'' || argv[argc-1][ll] != '\'') + return(ERROR); + argv[argc-1][ll] = '\0'; /* delete trailing ' */ + argv[2]++; /* skip leading ' (*after* the above!) */ + return(vxcall(argc-1, argv+1)); +} + +/* Lookup symbol; get address for text symbols, value (assuming int) + otherwise; return OK or ERROR on failure + Symbol name is null-terminated and without the leading '_' */ +STATUS +lookup(sym, stype, value) +char *sym, **value; +int stype; +{ + char buf[256]; + char *symname = buf; + int len, ret; + SYM_TYPE type; + + len = strlen(sym); + if (len > 254 && (symname = malloc(len+2)) == NULL) + return(ERROR); +#if defined _ARCH_PPC || defined SIMSPARCSOLARIS + /* GCC for PPC or SIMSPARC doesn't add a leading _ to symbols */ + strcpy(symname, sym); +#else + sprintf(symname, "_%s", sym); +#endif + ret = (stype != 0) ? + symFindByNameAndType(sysSymTbl, symname, value, &type, stype, stype) : + symFindByName(sysSymTbl, symname, value, &type); + if (symname != buf) + free(symname); + if (ret == OK && (type & N_TEXT) == 0) /* get value */ + *value = (char *)*((int *)*value); + return(ret); +} diff --git a/erts/etc/vxworks/wd_example.c b/erts/etc/vxworks/wd_example.c new file mode 100644 index 0000000000..0e3a6a1cb2 --- /dev/null +++ b/erts/etc/vxworks/wd_example.c @@ -0,0 +1,141 @@ +/* + * %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% + */ +/* + * File: frc5te_wd.c + * Purpose: Watchdog NMI handling for FORCE 5TE + * + * Description: + * The watchdog handler routines are system specific. A program that + * wants to utilize a hardware watchdog should call wd_init and test + * the return value. If wd_init returns true (!0); there is a hardware + * watchdog, and that watchdog has been activated. If no watchdog exists, + * wd_init returns false (0). + * + * To keep the watchdog happy, call wd_reset at least every X seconds, + * where X is the number of seconds specified in the call to wd_init. + * + * The watchdog can be disarmed by setting the variable wd_disarmed to 1, + * and armed again by setting the same variable to 0. Watchdog status + * information can be retrieved using the function wd_status. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <frc5e.h> +#include <logLib.h> +#include <taskLib.h> +#include <sysLib.h> +#include <stdio.h> +#include "hw_watchdog.h" + +/* prototypes */ +extern sysNMIConnect(); +#ifdef __STDC__ +void wd_keeper(int); +void wd_nmi_int(UINT8); +void wd_status(void); +#else +void wd_keeper(); +void wd_nmi_int(); +void wd_status(); +#endif + +#define WD_NMI_MIN_DELAY 0.830 /* Min time before watchdog NMI (in seconds) */ +#define WD_RESET_FREQUENCY (WD_NMI_MIN_DELAY / 2) /* how often the timer is reset */ + +#define WD_KEEPER_STACK_SIZE 10000 + +/* global variables */ +extern int spTaskOptions; +static volatile int wd_count_startval; /* start value set by wd_init */ +static volatile int wd_count; /* counter for wd_keeper */ +volatile int wd_disarmed = 0; /* debug feature */ + +/* wd_init is executed to initialize the watchdog. It spawns the task */ +/* wd_keeper and returns true (non-zero) if a hardware watchdog exists, */ +/* or returns false (zero) otherwise. */ +int wd_init(timeout, prio) + int timeout, prio; +{ + taskSpawn("wd_keeper", prio, spTaskOptions, WD_KEEPER_STACK_SIZE, + (FUNCPTR)wd_keeper, timeout,0,0,0,0,0,0,0,0,0); + return !0; /* watchdog exists */ +} + + +/* wd_reset is called as an alive-signal from the supervisor process. */ +/* If there is no call to this function within a certain time, the */ +/* watchdog will reboot the system. */ +void wd_reset() +{ + wd_count = wd_count_startval; +} + + +/* wd_keeper runs as a separate task and resets the watchdog timer */ +/* before an NMI is generated. This task uses the counter wd_count to */ +/* decide if it should exit or keep resetting the timer. */ +/* Note! This task must run with higher priority than the application! */ +void wd_keeper(timeout) + int timeout; +{ + int wd_delay = sysClkRateGet() * WD_RESET_FREQUENCY; + wd_count_startval = (int)(timeout / WD_RESET_FREQUENCY); + wd_count = wd_count_startval; + + /* Connect and enable level 15 interrupts */ + sysNMIConnect((VOIDFUNCPTR) wd_nmi_int, WD_NMI, WD_NMI); + *(char *)FRC5CE_GEN_PURPOSE2_REG |= FRC5CE_NMI_ENABLE; + + while ((wd_count > 0) || wd_disarmed) { + *(char *)FRC5CE_VME_A32MAP_REG |= FRC5CE_WATCHDOG_ENABLE; + taskDelay(wd_delay); + if (!wd_disarmed) wd_count--; + else wd_count = wd_count_startval; + } + logMsg("Watchdog keeper exits. No alive signal from application in %d seconds.\n",wd_count_startval * WD_RESET_FREQUENCY,0,0,0,0,0); +} + + +/* wd_nmi_int is the function connected to the watchdog interrupt. */ +/* It will report the failure to reset the watchdog timer. */ +void wd_nmi_int(type) + UINT8 type; +{ + switch(type) { + case WD_NMI: + logMsg("Watchdog interrupt! System will reboot.\n",0,0,0,0,0,0); + break; + default: + logMsg("Bad type (%d) in call to watchdog interrupt handler.\n",type,0,0,0,0,0); + break; + } +} + + +/* wd_status displays the current value of the counter. */ +void wd_status() +{ + fprintf(stderr, "Watchdog is %sarmed.\n", wd_disarmed ? "dis" : ""); + fprintf(stderr, "Counter value: %d\n", wd_count); + fprintf(stderr, "Start value is: %d (%d seconds)\n", + wd_count_startval, (int)(wd_count_startval * WD_RESET_FREQUENCY)); +} diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c new file mode 100644 index 0000000000..4a559cd8a2 --- /dev/null +++ b/erts/etc/win32/Install.c @@ -0,0 +1,229 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2003-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% + */ +/* + * Some code just simply does not deserve functions :-) + * Dead simple installation program to set up init files etc after erlang is + * copied to its destination. Also to be used after a patch is applied. + */ +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include "init_file.h" + +int main(int argc, char **argv) +{ + int silent = 0; + int start_sasl = 0; + char *root = NULL; + int i; + char buffer[MAX_PATH]; + char erts_dir[MAX_PATH]; + char release_dir[MAX_PATH]; + char bin_dir[MAX_PATH]; + char *tmp; + char my_ini_filename[MAX_PATH]; + InitFile *my_ini_file; + InitSection *my_ini_section; + char version_string[MAX_PATH]; + InitFile *ini_file; + InitSection *ini_section; + HANDLE module = GetModuleHandle(NULL); + char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe", + "dialyzer.exe", "typer.exe", + "escript.exe", NULL }; + char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL }; + char fromname[MAX_PATH]; + char toname[MAX_PATH]; + + + for (i = 1; i < argc; ++i) { + switch(argv[i][0]) { + case '-' : + switch(argv[i][1]) { + case 's' : + silent = 1; + break; + default: + fprintf(stderr, "Unknown command switch %s\n", + argv[i]); + exit(1); + } + break; + default: + if (root != NULL) { + fprintf(stderr, "Only one root directory can be specified, " + "parameter %s is illegal\n", + argv[i]); + exit(1); + } + root = argv[i]; + break; + } + } + if (root == NULL) { + if (module = NULL) { + fprintf(stderr, "Cannot GetModuleHandle()\n"); + exit(1); + } + + if (GetModuleFileName(module,buffer,MAX_PATH) == 0) { + fprintf(stderr,"Could not GetModuleFileName()\n"); + exit(1); + } + i = strlen(buffer) - 1; + while ( i >= 0 && buffer[i] != '\\') { + --i; + } + if (i < 0) { + fprintf(stderr,"GetModuleFileName returned broken path\n"); + exit(1); + } + buffer[i] = '\0'; + root = buffer; + } + + if (!silent) { + char answer[100]; + char *eol; + start_sasl = 1; + printf("Do you want a minimal startup instead of sasl [No]: "); + fflush(stdout); + if (fgets(answer,100,stdin) == NULL) { + fprintf(stderr, "Could not read answer from user.\n"); + exit(1); + } + eol = strchr(answer,'\n'); + if (eol == NULL) { + while (getchar() != '\n') + ; + } else { + *eol = '\0'; + } + if ((eol = strchr(answer, '\r')) != NULL) { + *eol = '\0'; + } + if (_stricmp(answer,"yes") == 0 || _stricmp(answer,"y") == 0) { + start_sasl = 0; + } + } + sprintf(my_ini_filename,"%s\\Install.ini",root); + my_ini_file = load_init_file(my_ini_filename); + if (my_ini_file == NULL) { + fprintf(stderr,"Cannot open init file %s\n",my_ini_filename); + exit(1); + } + + if ((my_ini_section = lookup_init_section(my_ini_file,"Install")) + == NULL) { + fprintf(stderr,"No [Install] section in init file %s\n", + my_ini_filename); + exit(1); + } + + if ((tmp = lookup_init_entry(my_ini_section, "VSN")) == NULL) { + fprintf(stderr,"No key VSN in init file %s\n", + my_ini_filename); + exit(1); + } + + strcpy(version_string,tmp); + + sprintf(erts_dir,"%s\\erts-%s\\bin",root,tmp); + if ((tmp = lookup_init_entry(my_ini_section, "SYSTEM_VSN")) == NULL) { + fprintf(stderr,"No key SYSTEM_VSN in init file %s\n", + my_ini_filename); + exit(1); + } + sprintf(release_dir,"%s\\releases\\%s",root,tmp); + + sprintf(bin_dir,"%s\\bin",root); + CreateDirectory(bin_dir,NULL); + + free_init_file(my_ini_file); + + for (i = 0; binaries[i] != NULL; ++i) { + sprintf(fromname,"%s\\%s",erts_dir,binaries[i]); + sprintf(toname,"%s\\%s",bin_dir,binaries[i]); + if (GetFileAttributes(fromname) == 0xFFFFFFFF) { + fprintf(stderr,"Could not find file %s\n", + fromname); + exit(1); + } + if (!CopyFile(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %s to %s\n", + fromname,toname); + fprintf(stderr,"Continuing installation anyway...\n"); + } + } + + for (i = 0; scripts[i] != NULL; ++i) { + sprintf(fromname,"%s\\%s",release_dir,scripts[i]); + sprintf(toname,"%s\\%s",bin_dir,scripts[i]); + if (GetFileAttributes(fromname) == 0xFFFFFFFF) { + fprintf(stderr,"Could not find file %s\n", + fromname); + exit(1); + } + if (!CopyFile(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %s to %s\n", + fromname,toname); + fprintf(stderr,"Cannot continue installation, bailing out.\n"); + exit(1); + } + } + if (start_sasl) { + sprintf(fromname,"%s\\start_sasl.boot",bin_dir); + } else { + sprintf(fromname,"%s\\start_clean.boot",bin_dir); + } + sprintf(toname,"%s\\start.boot",bin_dir); + if (!CopyFile(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %s to %s\n", + fromname,toname); + fprintf(stderr,"Cannot continue installation, bailing out.\n"); + exit(1); + } + ini_file = create_init_file(); + ini_section = create_init_section("erlang"); + add_init_section(ini_file,ini_section); + add_init_entry(ini_section,"Bindir",erts_dir); + add_init_entry(ini_section,"Progname","erl"); + add_init_entry(ini_section,"Rootdir",root); + sprintf(fromname,"%s\\erl.ini",erts_dir); + sprintf(toname,"%s\\erl.ini",bin_dir); + if (store_init_file(ini_file,fromname) != 0) { + fprintf(stderr,"Could not create file %s\n", + fromname); + fprintf(stderr,"Cannot continue installation, bailing out.\n"); + exit(1); + } + if (!CopyFile(fromname,toname,FALSE)) { + fprintf(stderr,"Could not copy file %s to %s\n", + fromname,toname); + fprintf(stderr,"Cannot continue installation, bailing out.\n"); + exit(1); + } + if (!silent) { + printf("Erlang %s installed successfully\n", version_string); + } + return 0; +} + + + diff --git a/erts/etc/win32/Install.src b/erts/etc/win32/Install.src new file mode 100644 index 0000000000..4aaa171ce0 --- /dev/null +++ b/erts/etc/win32/Install.src @@ -0,0 +1,4 @@ +[Install] +VSN=%I_VSN% +SYSTEM_VSN=%I_SYSTEM_VSN% + diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile new file mode 100644 index 0000000000..400e5c5bba --- /dev/null +++ b/erts/etc/win32/Makefile @@ -0,0 +1,72 @@ +# +# %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 +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +include ../../vsn.mk + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELEASE_PATH= ../../release/$(TARGET) +RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN) +ROOTSYSDIR = $(RELEASE_PATH) + +BINDIR = $(ERL_TOP)/bin/$(TARGET) +RUNTIME = $(ERL_TOP)/erts/emulator/beam +SYS = $(ERL_TOP)/erts/emulator/sys/win32 +OBJ = $(ERL_TOP)/erts/obj/$(TARGET) +ROOTDIR = $(ERL_TOP)/erts + +INSTALL_PROGS = \ + $(BINDIR)/inet_gethost.exe \ + $(BINDIR)/erl.exe \ + $(BINDIR)/werl.exe \ + $(BINDIR)/heart.exe \ + $(BINDIR)/erlc.exe \ + $(BINDIR)/erlsrv.exe \ + $(BINDIR)/start_erl.exe + +INSTALL_SRC = ./start_erl.c ./Nmakefile.start_erl + +INSTALL_LIBS = $(BINDIR)/erl_dll.lib + +INSTALL_ICONS = ./beam_icon.ico ./erl_icon.ico ./hrl_icon.ico + +opt debug all clean depend: + @echo Nothing to do for "'"$@"'" on $(TARGET) + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: + $(INSTALL_DIR) $(RELSYSDIR)/bin + $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) $(ROOTSYSDIR)/usr/include + $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib + $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib/icons + $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELSYSDIR)/bin + $(INSTALL_DATA) $(INSTALL_SRC) $(RELSYSDIR)/src + $(INSTALL_DATA) $(INSTALL_ICONS) $(ROOTSYSDIR)/usr/lib/icons + +release_docs release_docs_spec docs: + diff --git a/erts/etc/win32/Nmakefile.start_erl b/erts/etc/win32/Nmakefile.start_erl new file mode 100644 index 0000000000..5bf9fd78d5 --- /dev/null +++ b/erts/etc/win32/Nmakefile.start_erl @@ -0,0 +1,33 @@ +# +# %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% +# +# Example nmakefile to build start_erl.exe from start_erl.c +# Microsoft Visual C++ is expected to be installed +# and the PATH, INCLUDE and LIB environment valiables set up correctly. +# Invoke with nmake -f NMakefile.start_erl. + +CC=cl +CFLAGS=-W3 +LDFLAGS=user32.lib advapi32.lib + +start_erl.exe: start_erl.c + $(CC) $(CFLAGS) $? -Festart_erl.exe $(LDFLAGS) + +clean: + -del start_erl.obj start_erl.exe + diff --git a/erts/etc/win32/beam.rc b/erts/etc/win32/beam.rc new file mode 100644 index 0000000000..cd7db67d4d --- /dev/null +++ b/erts/etc/win32/beam.rc @@ -0,0 +1,102 @@ +// +// %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% +// +#include <windows.h> +#include "resource.h" + +1 ICON DISCARDABLE "erlang.ico" +2 ICON DISCARDABLE "erl_icon.ico" +3 ICON DISCARDABLE "hrl_icon.ico" +4 ICON DISCARDABLE "beam_icon.ico" +1 BITMAP MOVEABLE PURE "toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// +1 MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open Logfile...", IDMENU_STARTLOG + MENUITEM "&Close Logfile", IDMENU_STOPLOG + MENUITEM SEPARATOR + MENUITEM "&Exit\tAlt+F4", IDMENU_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Copy\tCtrl+C", IDMENU_COPY + MENUITEM "&Paste\tCtrl+V", IDMENU_PASTE + MENUITEM SEPARATOR + MENUITEM "Select A&ll", IDMENU_SELALL + END + POPUP "&Options" + BEGIN + MENUITEM "&Select Font...", IDMENU_FONT + MENUITEM "Select &Background...", IDMENU_SELECTBKG + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", IDMENU_TOOLBAR + END + POPUP "&Help" + BEGIN + MENUITEM "&About", IDMENU_ABOUT + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// +AboutBox DIALOG DISCARDABLE 0, 0, 217, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Erlang Shell" +FONT 8, "MS Sans Serif" +BEGIN + ICON 1,-1,11,17,18,20 + LTEXT "Erlang Shell Version 1.0",ID_VERSIONSTRING,40,10,119,8, + SS_NOPREFIX + LTEXT "Copyright � Ericsson Telecom AB",-1,40,25, + 119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP +END + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerators +// + +1 ACCELERATORS +{ + VK_CANCEL, ID_BREAK, VIRTKEY, CONTROL + "^C", IDMENU_COPY + VK_INSERT, IDMENU_COPY, VIRTKEY, CONTROL + "^V", IDMENU_PASTE + VK_INSERT, IDMENU_PASTE, VIRTKEY, SHIFT + VK_F1, IDMENU_ABOUT, VIRTKEY +} + + + + + + + + + diff --git a/erts/etc/win32/beam_icon.ico b/erts/etc/win32/beam_icon.ico Binary files differnew file mode 100644 index 0000000000..fb22afda62 --- /dev/null +++ b/erts/etc/win32/beam_icon.ico diff --git a/erts/etc/win32/cygwin_tools/erl b/erts/etc/win32/cygwin_tools/erl new file mode 100755 index 0000000000..576825c4be --- /dev/null +++ b/erts/etc/win32/cygwin_tools/erl @@ -0,0 +1,48 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Note! This shellscript expects to be run in a cygwin environment, +# it converts erlc command lines to native windows erlc commands, which +# basically means running the command cygpath on whatever is a path... + +CMD="" +for x in "$@"; do + case "$x" in + -I/*|-o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`; + #echo "Foooo:$z" + MPATH=`cygpath -m $y`; + CMD="$CMD -$z\"$MPATH\"";; + /*) + #echo "absolute:"$x; + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; +# +{*);; + *) +# y=`echo $x | sed 's,",\\\\\\\\\\\",g'`; + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac +done +#echo "$@" +#eval echo erlc.exe $CMD +ERL_TOP=`cygpath -m $ERL_TOP` +export ERL_TOP +eval erl.exe $CMD diff --git a/erts/etc/win32/cygwin_tools/erlc b/erts/etc/win32/cygwin_tools/erlc new file mode 100755 index 0000000000..a18ec27bf4 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/erlc @@ -0,0 +1,61 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Note! This shellscript expects to be run in a cygwin environment, +# it converts erlc command lines to native windows erlc commands, which +# basically means running the command cygpath on whatever is a path... + +CMD="" +ECHO_ONLY=false +for x in "$@"; do + case "$x" in + --echo_only) + ECHO_ONLY=true;; + -I/*|-o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -$z$MPATH";; + -pa/*) + y=`echo $x | sed 's,^-pa\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -pa $MPATH";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; +# Needed for +'{preproc_flags,whatever}' + +{preproc_flags,*}) + y=`echo $x | sed 's,^+{preproc_flags\,"\(.*\)"},\1,g'`; + z=`eval $0 --echo_only $y`; + case "$z" in # Dont "doubledoublequote" + \"*\") + CMD="$CMD +'{preproc_flags,$z}'";; + *) + CMD="$CMD +'{preproc_flags,\"$z\"}'";; + esac;; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac +done +if [ $ECHO_ONLY = true ]; then + echo $CMD +else + eval erlc.exe $CMD +fi diff --git a/erts/etc/win32/cygwin_tools/javac.sh b/erts/etc/win32/cygwin_tools/javac.sh new file mode 100755 index 0000000000..f9ee24593f --- /dev/null +++ b/erts/etc/win32/cygwin_tools/javac.sh @@ -0,0 +1,53 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Note! This shellscript expects to be run in a cygwin environment, +# it converts erlc command lines to native windows erlc commands, which +# basically means running the command cygpath on whatever is a path... + +CMD="" +CLASSPATH=`cygpath -m -p $CLASSPATH` +export CLASSPATH +#echo "CLASSPATH=$CLASSPATH" +SAVE="$@" +while test -n "$1" ; do + x="$1" + case "$x" in + -I/*|-o/*|-d/*) + y=`echo $x | sed 's,^-[Iod]\(/.*\),\1,g'`; + z=`echo $x | sed 's,^-\([Iod]\)\(/.*\),\1,g'`; + #echo "Foooo:$z" + MPATH=`cygpath -m $y`; + CMD="$CMD -$z\"$MPATH\"";; + -d|-I|-o) + shift; + MPATH=`cygpath -m $1`; + CMD="$CMD $x $MPATH";; + /*) + #echo "absolute:"$x; + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done +#echo javac.exe $CMD +eval javac.exe $CMD diff --git a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh new file mode 100755 index 0000000000..20fe143890 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh @@ -0,0 +1,44 @@ +#! /bin/bash +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2003-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% +# +# Create a local init-file for erlang in the build environment. +if [ -z "$1" ]; then + echo "error: $0: No rootdir given" + exit 1 +else + RDIR=$1 +fi +if [ -z "$2" ]; then + echo "error: $0: No bindir given" + exit 1 +else + BDIR=$2 +fi + +DRDIR=`(cygpath -d $RDIR 2>/dev/null || cygpath -w $RDIR) | sed 's,\\\,\\\\\\\\,g'` +DBDIR=`(cygpath -d $BDIR 2>/dev/null || cygpath -w $BDIR) | sed 's,\\\,\\\\\\\\,g'` + + +cat > $RDIR/bin/erl.ini <<EOF +[erlang] +Bindir=$DBDIR +Progname=erl +Rootdir=$DRDIR +EOF + diff --git a/erts/etc/win32/cygwin_tools/make_local_ini.sh b/erts/etc/win32/cygwin_tools/make_local_ini.sh new file mode 100755 index 0000000000..8e29573dc4 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/make_local_ini.sh @@ -0,0 +1,41 @@ +#! /bin/bash +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2003-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% +# +# Create a local init-file for erlang in the build environment. +if [ -z "$1" ]; then + if [ -z $ERL_TOP ]; then + echo "error: $0: No rootdir available" + exit 1 + else + RDIR=$ERL_TOP + fi +else + RDIR=$1 +fi + +DDIR=`(cygpath -d $RDIR 2>/dev/null || cygpath -w $RDIR) | sed 's,\\\,\\\\\\\\,g'` + + +cat > $RDIR/bin/erl.ini <<EOF +[erlang] +Bindir=$DDIR\\\\bin\\\\win32 +Progname=erl +Rootdir=$DDIR +EOF + diff --git a/erts/etc/win32/cygwin_tools/mingw/ar.sh b/erts/etc/win32/cygwin_tools/mingw/ar.sh new file mode 100755 index 0000000000..5b8f58e5de --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/ar.sh @@ -0,0 +1,55 @@ +#! /bin/sh +# +# %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% +# +CMD="" +if [ -z "$MINGW_EXE_PATH" ]; then + echo "You have to set MINGW_EXE_PATH to run ar.sh" >&2 + exit 1 +fi +while test -n "$1" ; do + x="$1" + case "$x" in + -o|-out:) + shift + case "$1" in + /*) + MPATH=`cygpath -m $1`;; + *) + MPATH=$1;; + esac + CMD="rcv \"$MPATH\" $CMD";; + -out:*) + y=`echo $x | sed 's,^-out:\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="rcv \"$MPATH\" $CMD";; + -o*) + y=`echo $x | sed 's,^-o\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="rcv \"$MPATH\" $CMD";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done + +eval $MINGW_EXE_PATH/ar.exe $CMD diff --git a/erts/etc/win32/cygwin_tools/mingw/cc.sh b/erts/etc/win32/cygwin_tools/mingw/cc.sh new file mode 100755 index 0000000000..ae284893fa --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/cc.sh @@ -0,0 +1,293 @@ +#! /bin/sh +# +# %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% +# +# Icky cl wrapper that does it's best to behave like a Unixish cc. +# Made to work for Erlang builds and to make configure happy, not really +# general I suspect. +# set -x +# Save the command line for debug outputs +SAVE="$@" + +# Constants +COMMON_CFLAGS="-mwindows -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -DWIN32_MINGW" + +# Variables +# The stdout and stderr for the compiler +MSG_FILE=/tmp/gcc.exe.$$.1 +ERR_FILE=/tmp/gcc.exe.$$.2 + +# "Booleans" determined during "command line parsing" +# If the stdlib option is explicitly passed to this program +MD_FORCED=false +# If we're preprocession (only) i.e. -E +PREPROCESSING=false +# If this is supposed to be a debug build +DEBUG_BUILD=false +# If this is supposed to be an optimized build (there can only be one...) +OPTIMIZED_BUILD=false +# If we're linking or only compiling +LINKING=true + +# This data is accumulated during command line "parsing" +# The stdlibrary option, default multithreaded dynamic +# NOTE! The standard library options are actually ignored by the linker later +# on, I've retained parsing of them from the VC++ version as they may be +# needed in the future... +MD=-MD +# Flags for debug compilation +DEBUG_FLAGS="" +# Flags for optimization +OPTIMIZE_FLAGS="" +# The specified output filename (if any), may be either object or exe. +OUTFILE="" +# Unspe3cified command line options for the compiler +CMD="" +# All the c source files, in unix style +SOURCES="" +# All the options to pass to the linker, kept in Unix style +LINKCMD="" +LINKSOURCES="" +DEPENDING=false + +if [ -z "$MINGW_EXE_PATH" ]; then + echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2 + exit 1 +fi + + +# Loop through the parameters and set the above variables accordingly +# Also convert some cygwin filenames to "mixed style" dito (understood by the +# compiler very well), except for anything passed to the linker, that script +# handles those and the sources, which are also kept unixish for now + +while test -n "$1" ; do + x="$1" + case "$x" in + -Wall) + ;; + -c) + LINKING=false;; + -E) + PREPROCESSING=true; + LINKING=false;; # Obviously... + -MM|-M) + DEPENDING=true; + LINKING=false;; # Obviously... + -O*) + # Optimization hardcoded + OPTIMIZE_FLAGS="$x"; + DEBUG_FLAGS="-ggdb"; + DEBUG_BUILD=false; + if [ $MD_FORCED = false ]; then + MD=-MD; + fi + OPTIMIZED_BUILD=true;; + -g|-ggdb) + if [ $OPTIMIZED_BUILD = false ];then + # Hardcoded windows debug flags + DEBUG_FLAGS="-ggdb"; + if [ $MD_FORCED = false ]; then + MD=-MDd; + fi + DEBUG_BUILD=true; + fi;; + # Allow forcing of stdlib + -mt|-MT) + MD="-MT"; + MD_FORCED=true;; + -md|-MD) + MD="-MD"; + MD_FORCED=true;; + -ml|-ML) + MD="-ML"; + MD_FORCED=true;; + -mdd|-MDD|-MDd) + MD="-MDd"; + MD_FORCED=true;; + -mtd|-MTD|-MTd) + MD="-MTd"; + MD_FORCED=true;; + -mld|-MLD|-MLd) + MD="-MLd"; + MD_FORCED=true;; + -o) + shift; + OUTFILE="$1";; + -o*) + y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`; + OUTFILE="$y";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -$z\"$MPATH\"";; + -I*) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD $y";; + -D*) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD $y";; + -l*) + y=`echo $x | sed 's,^-l\(.*\),\1,g'`; + LINKCMD="$LINKCMD $x";; + /*.c) + SOURCES="$SOURCES $x";; + *.c) + SOURCES="$SOURCES $x";; + /*.o) + LINKCMD="$LINKCMD $x";; + *.o) + LINKCMD="$LINKCMD $x";; + *) + # Try to quote uninterpreted options + y=`echo $x | sed 's,",\\\",g'`; + LINKCMD="$LINKCMD $y";; + esac + shift +done + +#Return code from compiler, linker.sh and finally this script... +RES=0 + +# Accumulated object names +ACCUM_OBJECTS="" + +# A temporary object file location +TMPOBJDIR=/tmp/tmpobj$$ +rm -rf $TMPOBJDIR +mkdir $TMPOBJDIR + + +for x in $SOURCES; do + # Compile each source + if [ $LINKING = false ]; then + # We should have an output defined, which is a directory + # or an object file + case $OUTFILE in + /*.o) + # Simple output, SOURCES should be one single + n=`echo $SOURCES | wc -w`; + if [ $n -gt 1 ]; then + echo "cc.sh:Error, multiple sources, one object output."; + exit 1; + else + output_filename=`cygpath -m $OUTFILE`; + fi;; + *.o) + # Relative path needs no translation + n=`echo $SOURCES | wc -w` + if [ $n -gt 1 ]; then + echo "cc.sh:Error, multiple sources, one object output." + exit 1 + else + output_filename=$OUTFILE + fi;; + /*) + # Absolute directory + o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'` + output_filename=`cygpath -m $OUTFILE` + output_filename="$output_filename/${o}";; + *) + # Relative_directory or empty string (.//x.o is valid) + o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'` + output_filename="./${OUTFILE}/${o}";; + esac + else + # We are linking, which means we build objects in a temporary + # directory and link from there. We should retain the basename + # of each source to make examining the exe easier... + o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'` + output_filename=$TMPOBJDIR/$o + ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename" + fi + # Now we know enough, lets try a compilation... + MPATH=`cygpath -m $x` + if [ $DEPENDING = true ]; then + output_flag="-MM" + elif [ $PREPROCESSING = true ]; then + output_flag="-E" + else + output_flag="-c -o `cygpath -m ${output_filename}`" + fi + params="$COMMON_CFLAGS $DEBUG_FLAGS $OPTIMIZE_FLAGS \ + $CMD ${output_flag} $MPATH" + if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then + echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG + echo $MINGW_EXE_PATH/gcc $params >>$CC_SH_DEBUG_LOG + fi + eval $MINGW_EXE_PATH/gcc $params >$MSG_FILE 2>$ERR_FILE + RES=$? + if [ $PREPROCESSING = false -a $DEPENDING = false ]; then + cat $ERR_FILE >&2 + cat $MSG_FILE + else + cat $ERR_FILE >&2 + if [ $DEPENDING = true ]; then + cat $MSG_FILE | sed 's|\([a-z]\):/|/cygdrive/\1/|g' + else + cat $MSG_FILE + fi + fi + rm -f $ERR_FILE $MSG_FILE + if [ $RES != 0 ]; then + rm -rf $TMPOBJDIR + exit $RES + fi +done + +#If we got here, we succeeded in compiling (if there were anything to compile) +#The output filename should name an executable if we're linking +if [ $LINKING = true ]; then + case $OUTFILE in + "") + # Use the first source name to name the executable + first_source="" + for x in $SOURCES; do first_source=$x; break; done; + if [ -n "$first_source" ]; then + e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`; + out_spec="-o $e"; + else + out_spec=""; + fi;; + *) + out_spec="-o $OUTFILE";; + esac + # Descide which standard library to link against + case $MD in + -ML) + stdlib="-lLIBC";; + -MLd) + stdlib="-lLIBCD";; + -MD) + stdlib="-lMSVCRT";; + -MDd) + stdlib="-lMSVCRTD";; + -MT) + stdlib="-lLIBCMT";; + -MTd) + stdlib="-lLIBMTD";; + esac + # And finally call the next script to do the linking... + params="$out_spec $LINKCMD $stdlib" + eval ld.sh $ACCUM_OBJECTS $params + RES=$? +fi +rm -rf $TMPOBJDIR + +exit $RES diff --git a/erts/etc/win32/cygwin_tools/mingw/coffix.c b/erts/etc/win32/cygwin_tools/mingw/coffix.c new file mode 100644 index 0000000000..5dff030a69 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/coffix.c @@ -0,0 +1,161 @@ +/* + * %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% + */ +/* +** This mini tool fixes an incompatibility between +** Microsoft's tools, who dont like the virtual size being put in +** the physical address field, but rely on the raw size field for +** sizing the ".bss" section. +** This fixes some of the problems with linking gcc compiled objects +** together with MSVC dito. +** +** Courtesy DJ Delorie for describing the COFF file format on +** http://www.delorie.com/djgpp/doc/coff/ +** The coff structures are fetched from Microsofts headers though. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <windows.h> +#include <winnt.h> /* Structure definitions for PE (COFF) */ + +static int dump_edit(char *filename, int edit); +static int v_printf(char *format, ...); + + +char *progname; +int verbouse = 0; + +int main(int argc, char **argv) +{ + int findex = 1; + int edit = 0; + int ret; + + progname = argv[0]; + if (argc == 1) { + fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname); + return 1; + } + for (findex = 1; + findex < argc && (*argv[findex] == '-' || *argv[findex] == '/'); + ++findex) + switch (argv[findex][1]) { + case 'e': + case 'E': + edit = 1; + break; + case 'v': + case 'V': + verbouse = 1; + default: + fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]); + break; + } + if (findex == argc) { + fprintf(stderr,"%s: No filenames given.\n", progname); + return 1; + } + for(; findex < argc; ++findex) + if ((ret = dump_edit(argv[findex],edit)) != 0) + return ret; + return 0; +} + +int dump_edit(char *filename, int edit) +{ + FILE *f = fopen(filename, (edit) ? "r+b" : "rb"); + IMAGE_FILE_HEADER filhdr; + IMAGE_SECTION_HEADER scnhdr; + int i; + + if (f == NULL) { + fprintf(stderr, "%s: cannot open %s.\n", progname, filename); + return 1; + } + + if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) { + fprintf(stderr,"%s: Could not read COFF header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + v_printf("File: %s\n", filename); + v_printf("Magic number: 0x%08x\n", filhdr.Machine); + v_printf("Number of sections: %d\n",filhdr.NumberOfSections); + + if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) { + fprintf(stderr,"%s: Could not read COFF optional header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + + for (i = 0; i < filhdr.NumberOfSections; ++i) { + if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) { + fprintf(stderr,"%s: Could not read section header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + v_printf("Section %s:\n", scnhdr.Name); + v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress); + v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData); + if (scnhdr.Misc.PhysicalAddress != 0 && + scnhdr.SizeOfRawData == 0) { + printf("Section header %s in file %s will confuse MSC linker, " + "virtual size is 0x%08x and raw size is 0\n", + scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress, + scnhdr.SizeOfRawData); + if (edit) { + scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress; + scnhdr.Misc.PhysicalAddress = 0; + if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 || + fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) { + fprintf(stderr,"%s: could not edit file %s.\n", + progname, filename); + fclose(f); + return 1; + } + printf("Edited object, virtual size is now 0, and " + "raw size is 0x%08x.\n", scnhdr.SizeOfRawData); + } else { + printf("Specify option '-e' to correct the problem.\n"); + } + } + } + fclose(f); + return 0; +} + + +static int v_printf(char *format, ...) +{ + va_list ap; + int ret = 0; + if (verbouse) { + va_start(ap, format); + ret = vfprintf(stdout, format, ap); + va_end(ap); + } + return ret; +} + diff --git a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh new file mode 100755 index 0000000000..f3865c8cae --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh @@ -0,0 +1,90 @@ +#! /bin/sh +# +# %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% +# +TOOLDIR=$ERL_TOP/erts/etc/win32/cygwin_tools/mingw +COFFIX=$TOOLDIR/coffix +WTOOLDIR=`(cygpath -d $TOOLDIR 2>/dev/null || cygpath -w $TOOLDIR)` + +# Do primitive 'make' +newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print` +if [ -z $newer_exe ]; then + echo recompiling $COFFIX.exe + cl.exe -Fe${WTOOLDIR}\\coffix.exe ${WTOOLDIR}\\coffix.c + rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb +fi + +# Try to find out the output filename and remove it from command line +CMD="" +OUTFILE="" +INFILE="" +SKIP_COFFIX=false +while test -n "$1" ; do + x="$1" + case "$x" in + -o/*) + OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;; + -o) + shift + OUTFILE=$1;; + -MM) + SKIP_COFFIX=true + CMD="$CMD \"$x\"";; + *.c) + INFILE="$INFILE $x"; + CMD="$CMD \"$x\"";; + *) + CMD="$CMD \"$x\"";; + esac + shift +done +if [ -z "$INFILE" ]; then + echo 'emu_cc.sh: please give an input filename for the compiler' >&2 + exit 1 +fi +if [ -z "$OUTFILE" ]; then + OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'` +fi + +if [ $SKIP_COFFIX = false ]; then + n=`echo $INFILE | wc -w`; + if [ $n -gt 1 ]; then + echo "emu_cc.sh:Error, multiple sources, one object output."; + exit 1; + fi + TEMPFILE=/tmp/tmp_emu_cc$$.o + if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then + echo "gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1 + fi + eval gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD + RES=$? + if [ $RES = 0 ]; then + $COFFIX.exe -e `(cygpath -d $TEMPFILE 2>/dev/null || cygpath -w $TEMPFILE)` + RES=$? + if [ $RES = 0 ]; then + cp $TEMPFILE $OUTFILE + else + echo "emu_cc.sh: fatal: coffix failed!" >&2 + fi + fi + rm -f $TEMPFILE + exit $RES +else + eval gcc -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD 2>/dev/null + exit $? +fi diff --git a/erts/etc/win32/cygwin_tools/mingw/ld.sh b/erts/etc/win32/cygwin_tools/mingw/ld.sh new file mode 100755 index 0000000000..145bd2fad9 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/ld.sh @@ -0,0 +1,147 @@ +#! /bin/sh +# set -x +# +# %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% +# +# Save the command line for debug outputs +SAVE="$@" +kernel_libs="-lkernel32 -ladvapi32" +gdi_libs="-lgdi32 -luser32 -lcomctl32 -lcomdlg32 -lshell32" +DEFAULT_LIBRARIES="$kernel_libs $gdi_libs" + +CMD="" +STDLIB=-lmsvcrt +DEBUG_BUILD=false +STDLIB_FORCED=false +BUILD_DLL=false +OUTPUT_FILENAME="" + +if [ -z "$MINGW_EXE_PATH" ]; then + echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2 + exit 1 +fi + +while test -n "$1" ; do + x="$1" + case "$x" in + -dll| -DLL | -shared) + BUILD_DLL=true;; + -L/*|-L.*) + y=`echo $x | sed 's,^-L\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -L \"$MPATH\"";; + -lMSVCRT|-lmsvcrt) + STDLIB_FORCED=true; + STDLIB=-lmsvcrt;; + -lMSVCRTD|-lmsvcrtd) + STDLIB_FORCED=true; + STDLIB=-lmsvcrtd;; + -lLIBCMT|-llibcmt) + STDLIB_FORCED=true; + STDLIB=-llibcmt;; + -lLIBCMTD|-llibcmtd) + STDLIB_FORCED=true; + STDLIB=-llibcmtd;; + -lsocket) + DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES -lws2_32";; + -l*) + y=`echo $x | sed 's,^-l\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -l\"${MPATH}\"";; + -g) + DEBUG_BUILD=true;; + -pdb:none|-incremental:no) + ;; + -implib:*) + y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -Xlinker --out-implib -Xlinker \"${MPATH}\"";; + -entry:*) + y=`echo $x | sed 's,^-entry:\(.*\),\1,g'`; + CMD="$CMD -Xlinker --entry -Xlinker _$y";; + -def:*) + ;; + ## Ignore -def: for now as ld.sh core dumps... + # y=`echo $x | sed 's,^-def:\(.*\),\1,g'`; + # MPATH=`cygpath -m $y`; + # CMD="$CMD -Xlinker --output-def -Xlinker \"${MPATH}\"";; + -o) + shift + MPATH=`cygpath -m $1`; + OUTPUT_FILENAME="$MPATH";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + OUTPUT_FILENAME="$MPATH";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done +if [ $DEBUG_BUILD = true ]; then + linktype="-g" + if [ $STDLIB_FORCED = false ]; then + STDLIB=-lmsvcrt #d? + fi +else + linktype= +fi + +if [ $BUILD_DLL = true ];then + case "$OUTPUT_FILENAME" in + *.exe|*.EXE) + echo "Warning, output set to .exe when building DLL" >&2 + CMD="-shared -o \"$OUTPUT_FILENAME\" $CMD";; + *.dll|*.DLL) + CMD="-shared -o \"$OUTPUT_FILENAME\" $CMD";; + "") + CMD="-shared -o \"a.dll\" $CMD";; + *) + CMD="-shared -o \"${OUTPUT_FILENAME}.dll\" $CMD";; + esac +else + case "$OUTPUT_FILENAME" in + *.exe|*.EXE) + CMD="-o \"$OUTPUT_FILENAME\" $CMD";; + *.dll|*.DLL) + echo "Warning, output set to .dll when building EXE" >&2 + CMD="-o \"$OUTPUT_FILENAME\" $CMD";; + "") + CMD="-o \"a.exe\" $CMD";; + *) + CMD="-o \"${OUTPUT_FILENAME}.exe\" $CMD";; + esac +fi + +p=$$ +CMD="$linktype $CMD $STDLIB $DEFAULT_LIBRARIES" +if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then + echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG + echo $MINGW_EXE_PATH/gcc.exe $CMD >>$LD_SH_DEBUG_LOG +fi +eval $MINGW_EXE_PATH/gcc "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2 +RES=$? +#tail +2 /tmp/link.exe.${p}.2 >&2 +cat /tmp/link.exe.${p}.2 >&2 +cat /tmp/link.exe.${p}.1 +rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1 +exit $RES diff --git a/erts/etc/win32/cygwin_tools/mingw/mc.sh b/erts/etc/win32/cygwin_tools/mingw/mc.sh new file mode 100755 index 0000000000..873149172a --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/mc.sh @@ -0,0 +1,89 @@ +#! /bin/sh +# set -x +# +# %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% +# +# Save the command line for debug outputs +SAVE="$@" +CMD="" +OUTPUT_DIRNAME="" + +# Find the correct mc.exe. This could be done by the configure script, +# But as we seldom use the resource compiler, it might as well be done here... +if [ -z "$WINE_EXE_PATH" ]; then + echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2 + exit 1 +fi + +MCC="$WINE_EXE_PATH/wmc.exe -i" +RCC="$WINE_EXE_PATH/wrc.exe -i" + +while test -n "$1" ; do + x="$1" + case "$x" in + -o) + shift + OUTPUT_DIRNAME="$1";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + OUTPUT_DIRNAME="$y";; + -I) + shift + MPATH=`cygpath -m $1`; + CMD="$CMD -I\"$MPATH\"";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -I\"$MPATH\"";; + *) + MPATH=`cygpath -m -a $x`; + INFILE=$MPATH;; + #CMD="$CMD \"$MPATH\"";; + esac + shift +done +p=$$ +if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then + echo mc.sh "$SAVE" >>$MC_SH_DEBUG_LOG + echo $MCC $INFILE $CMD >>$MC_SH_DEBUG_LOG +fi +if [ -n "$OUTPUT_DIRNAME" ]; then + cd $OUTPUT_DIRNAME + RES=$? + if [ "$RES" != "0" ]; then + echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2 + exit $RES + fi +fi +eval $MCC "$INFILE" "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2 +RES=$? +cat /tmp/mc.exe.${p}.2 >&2 +cat /tmp/mc.exe.${p}.1 +rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1 +if [ $RES -eq 0 ]; then + XINFILE=`echo $INFILE | sed 's,.*/\([^/]*\),\1,' | sed 's,.mc$,.rc,'` + if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then + echo $RCC $XINFILE $CMD >>$MC_SH_DEBUG_LOG + fi + eval $RCC $XINFILE "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2 + RES=$? + cat /tmp/rc.exe.${p}.2 >&2 + cat /tmp/rc.exe.${p}.1 + rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1 +fi +exit $RES diff --git a/erts/etc/win32/cygwin_tools/mingw/rc.sh b/erts/etc/win32/cygwin_tools/mingw/rc.sh new file mode 100755 index 0000000000..37296f9e9f --- /dev/null +++ b/erts/etc/win32/cygwin_tools/mingw/rc.sh @@ -0,0 +1,94 @@ +#! /bin/sh +# set -x +# +# %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% +# +# Save the command line for debug outputs +SAVE="$@" +CMD="" +OUTPUT_FILENAME="" + +if [ -z "$MINGW_EXE_PATH" ]; then + echo "You have to set MINGW_EXE_PATH to run rc.sh" >&2 + exit 1 +fi + + +# # Find the correct rc.exe. This could be done by the configure script, +# # But as we seldom use the resource compiler, it might as well be done here... +# RCC="" +# save_ifs=$IFS +# IFS=: +# for p in $PATH; do +# if [ -f $p/windres.exe ]; then +# if [ -n "`$p/windres.exe --version 2>&1 | grep -i "GNU windres"`" ]; then +# RCC=$p/windres.exe +# fi +# fi +# done +# IFS=$save_ifs + +RCC=$MINGW_EXE_PATH/windres.exe + +if [ -z "$RCC" ]; then + echo 'windres.exe not found!' >&2 + exit 1 +fi + +while test -n "$1" ; do + x="$1" + case "$x" in + -o) + shift + MPATH=`cygpath -m $1`; + OUTPUT_FILENAME="$MPATH";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + OUTPUT_FILENAME="$MPATH";; + -I) + shift + MPATH=`cygpath -m $1`; + CMD="$CMD -I\"$MPATH\"";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -I\"$MPATH\"";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done +p=$$ +if [ -n "$OUTPUT_FILENAME" ]; then + CMD="-o $OUTPUT_FILENAME $CMD" +fi +if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then + echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG + echo windres.exe $CMD >>$RC_SH_DEBUG_LOG +fi +eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2 +RES=$? +cat /tmp/rc.exe.${p}.2 >&2 +cat /tmp/rc.exe.${p}.1 +rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1 +exit $RES diff --git a/erts/etc/win32/cygwin_tools/vc/ar.sh b/erts/etc/win32/cygwin_tools/vc/ar.sh new file mode 100755 index 0000000000..24d275b01a --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/ar.sh @@ -0,0 +1,47 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +CMD="" +while test -n "$1" ; do + x="$1" + case "$x" in + -out:) + shift + case "$1" in + /*) + MPATH=`cygpath -m $1`;; + *) + MPATH=$1;; + esac + CMD="$CMD -out:\"$MPATH\"";; + -out:/*) + y=`echo $x | sed 's,^-out:\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -out:\"$MPATH\"";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done + +eval lib.exe $CMD diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh new file mode 100755 index 0000000000..4939465d08 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/cc.sh @@ -0,0 +1,321 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Icky cl wrapper that does it's best to behave like a Unixish cc. +# Made to work for Erlang builds and to make configure happy, not really +# general I suspect. +# set -x +# Save the command line for debug outputs +SAVE="$@" + +# Constants +COMMON_CFLAGS="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE" + +# Variables +# The stdout and stderr for the compiler +MSG_FILE=/tmp/cl.exe.$$.1 +ERR_FILE=/tmp/cl.exe.$$.2 + +# "Booleans" determined during "command line parsing" +# If the stdlib option is explicitly passed to this program +MD_FORCED=false +# If we're preprocession (only) i.e. -E +PREPROCESSING=false +# If we're generating dependencies (implies preprocesing) +DEPENDENCIES=false +# If this is supposed to be a debug build +DEBUG_BUILD=false +# If this is supposed to be an optimized build (there can only be one...) +OPTIMIZED_BUILD=false +# If we're linking or only compiling +LINKING=true + +# This data is accumulated during command line "parsing" +# The stdlibrary option, default multithreaded dynamic +MD=-MD +# Flags for debug compilation +DEBUG_FLAGS="" +# Flags for optimization +OPTIMIZE_FLAGS="" +# The specified output filename (if any), may be either object or exe. +OUTFILE="" +# Unspecified command line options for the compiler +CMD="" +# All the c source files, in unix style +SOURCES="" +# All the options to pass to the linker, kept in Unix style +LINKCMD="" + + +# Loop through the parameters and set the above variables accordingly +# Also convert some cygwin filenames to "mixed style" dito (understood by the +# compiler very well), except for anything passed to the linker, that script +# handles those and the sources, which are also kept unixish for now + +while test -n "$1" ; do + x="$1" + case "$x" in + -Wall) + ;; + -c) + LINKING=false;; + #CMD="$CMD -c";; + -MM) + PREPROCESSING=true; + LINKING=false; + DEPENDENCIES=true;; + -E) + PREPROCESSING=true; + LINKING=false;; # Obviously... + #CMD="$CMD -E";; + -Owx) + # Optimization hardcoded of wxErlang, needs to disable debugging too + OPTIMIZE_FLAGS="-Ob2ity -Gs -Zi"; + DEBUG_FLAGS=""; + DEBUG_BUILD=false; + if [ $MD_FORCED = false ]; then + MD=-MD; + fi + OPTIMIZED_BUILD=true;; + -O*) + # Optimization hardcoded, needs to disable debugging too + OPTIMIZE_FLAGS="-Ox -Zi"; + DEBUG_FLAGS=""; + DEBUG_BUILD=false; + if [ $MD_FORCED = false ]; then + MD=-MD; + fi + OPTIMIZED_BUILD=true;; + -g|-ggdb) + if [ $OPTIMIZED_BUILD = false ];then + # Hardcoded windows debug flags + DEBUG_FLAGS="-Z7"; + if [ $MD_FORCED = false ]; then + MD=-MDd; + fi + LINKCMD="$LINKCMD -g"; + DEBUG_BUILD=true; + fi;; + # Allow forcing of stdlib + -mt|-MT) + MD="-MT"; + MD_FORCED=true;; + -md|-MD) + MD="-MD"; + MD_FORCED=true;; + -ml|-ML) + MD="-ML"; + MD_FORCED=true;; + -mdd|-MDD|-MDd) + MD="-MDd"; + MD_FORCED=true;; + -mtd|-MTD|-MTd) + MD="-MTd"; + MD_FORCED=true;; + -mld|-MLD|-MLd) + MD="-MLd"; + MD_FORCED=true;; + -o) + shift; + OUTFILE="$1";; + -o*) + y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`; + OUTFILE="$y";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -$z\"$MPATH\"";; + -I*) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD $y";; + -D*) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD $y";; + -EH*) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD $y";; + -l*) + y=`echo $x | sed 's,^-l\(.*\),\1,g'`; + LINKCMD="$LINKCMD $x";; + /*.c) + SOURCES="$SOURCES $x";; + *.c) + SOURCES="$SOURCES $x";; + /*.cc) + SOURCES="$SOURCES $x";; + *.cc) + SOURCES="$SOURCES $x";; + /*.cpp) + SOURCES="$SOURCES $x";; + *.cpp) + SOURCES="$SOURCES $x";; + /*.o) + LINKCMD="$LINKCMD $x";; + *.o) + LINKCMD="$LINKCMD $x";; + *) + # Try to quote uninterpreted options + y=`echo $x | sed 's,",\\\",g'`; + LINKCMD="$LINKCMD $y";; + esac + shift +done + +#Return code from compiler, linker.sh and finally this script... +RES=0 + +# Accumulated object names +ACCUM_OBJECTS="" + +# A temporary object file location +TMPOBJDIR=/tmp/tmpobj$$ +mkdir $TMPOBJDIR + +# Compile +for x in $SOURCES; do + start_time=`date '+%s'` + # Compile each source + if [ $LINKING = false ]; then + # We should have an output defined, which is a directory + # or an object file + case $OUTFILE in + /*.o) + # Simple output, SOURCES should be one single + n=`echo $SOURCES | wc -w`; + if [ $n -gt 1 ]; then + echo "cc.sh:Error, multiple sources, one object output."; + exit 1; + else + output_filename=`cygpath -m $OUTFILE`; + fi;; + *.o) + # Relative path needs no translation + n=`echo $SOURCES | wc -w` + if [ $n -gt 1 ]; then + echo "cc.sh:Error, multiple sources, one object output." + exit 1 + else + output_filename=$OUTFILE + fi;; + /*) + # Absolute directory + o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'` + output_filename=`cygpath -m $OUTFILE` + output_filename="$output_filename/${o}";; + *) + # Relative_directory or empty string (.//x.o is valid) + o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'` + output_filename="./${OUTFILE}/${o}";; + esac + else + # We are linking, which means we build objects in a temporary + # directory and link from there. We should retain the basename + # of each source to make examining the exe easier... + o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'` + output_filename=$TMPOBJDIR/$o + ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename" + fi + # Now we know enough, lets try a compilation... + MPATH=`cygpath -m $x` + if [ $PREPROCESSING = true ]; then + output_flag="-E" + else + output_flag="-c -Fo`cygpath -m ${output_filename}`" + fi + params="$COMMON_CFLAGS $MD $DEBUG_FLAGS $OPTIMIZE_FLAGS \ + $CMD ${output_flag} $MPATH" + if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then + echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG + echo cl.exe $params >>$CC_SH_DEBUG_LOG + fi + eval cl.exe $params >$MSG_FILE 2>$ERR_FILE + RES=$? + if test $PREPROCESSING = false; then + cat $ERR_FILE >&2 + tail -n +2 $MSG_FILE + else + tail -n +2 $ERR_FILE >&2 + if test $DEPENDENCIES = true; then + if test `grep -v $x $MSG_FILE | grep -c '#line'` != "0"; then + o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'` + echo -n $o':' + # Some versions of cygpath does not read paths linewise + # but uses space as separator, why pathnames containing + # spaces need to be removed. To avoid different + # behaviours in different versions of cygwin, we would need to + # write our own cygpath replacement, but this will have to do + # for now... + cat $MSG_FILE | grep '#line' | grep -v $x | awk -F\" '{printf("%s\n",$2)}' | sort -u | grep -v " " | cygpath -f - -m -s | cygpath -f - | awk '{printf("\\\n %s ",$0)}' + echo + echo + after_sed=`date '+%s'` + echo Made dependencises for $x':' `expr $after_sed '-' $start_time` 's' >&2 + fi + else + cat $MSG_FILE + fi + fi + rm -f $ERR_FILE $MSG_FILE + if [ $RES != 0 ]; then + rm -rf $TMPOBJDIR + exit $RES + fi +done + +# If we got here, we succeeded in compiling (if there were anything to compile) +# The output filename should name an executable if we're linking +if [ $LINKING = true ]; then + case $OUTFILE in + "") + # Use the first source name to name the executable + first_source="" + for x in $SOURCES; do first_source=$x; break; done; + if [ -n "$first_source" ]; then + e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`; + out_spec="-o $e"; + else + out_spec=""; + fi;; + *) + out_spec="-o $OUTFILE";; + esac + # Descide which standard library to link against + case $MD in + -ML) + stdlib="-lLIBC";; + -MLd) + stdlib="-lLIBCD";; + -MD) + stdlib="-lMSVCRT";; + -MDd) + stdlib="-lMSVCRTD";; + -MT) + stdlib="-lLIBCMT";; + -MTd) + stdlib="-lLIBMTD";; + esac + # And finally call the next script to do the linking... + params="$out_spec $LINKCMD $stdlib" + eval ld.sh $ACCUM_OBJECTS $params + RES=$? +fi +rm -rf $TMPOBJDIR + +exit $RES diff --git a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c new file mode 100644 index 0000000000..18ecc31c17 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c @@ -0,0 +1,864 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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 <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <sys/cygwin.h> + + + +#ifdef CCP_POSIX_TO_WIN_A +#define NEW_CYGPATH_INTERFACE +#endif + +#ifdef NEW_CYGPATH_INTERFACE +#define GET_WIN32_SIZE(Posix) \ +cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), NULL, 0) +#define CONVERT_TO_WIN32(Posix,Win32,Size) \ +cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), \ + (Win32), (Size)) +#else +#define GET_WIN32_SIZE(Posix) PATH_MAX +#define CONVERT_TO_WIN32(Posix,Win32,Size) \ +((cygwin32_conv_to_full_win32_path((Posix),(Win32)) >= 0) ? 0 : -1) +#endif + +/*#define HARDDEBUG 1*/ + +#ifdef HARDDEBUG +#define DEBUGF(X) printf X +#else +#define DEBUGF(X) /* noop */ +#endif +char *tmpobjdir = ""; + +char *add_to(char *src,char *add) { + int len = strlen(src)+strlen(add)+1; + char *n; + + if (strlen(src) == 0) { + n = malloc(len); + strcpy(n,add); + return n; + } + n = realloc(src,len); + strcat(n,add); + return n; +} + +void maybe_cleanup(void) +{ + DIR *dir; + struct dirent *dent; + if (*tmpobjdir == '\0') { + return; + } + if (!(dir = opendir(tmpobjdir))) { + return; + } + while((dent = readdir(dir)) != NULL) { + char *fullname = add_to("",tmpobjdir); + fullname = add_to(fullname,"/"); + fullname = add_to(fullname,dent->d_name); + unlink(fullname); + free(fullname); + } + closedir(dir); + rmdir(tmpobjdir); +} + + + + +void error(char *str) +{ + fprintf(stderr,"%s\n",str); + maybe_cleanup(); + exit(1); +} + + +char **add_to_src(char **srcarr, char *add) +{ + int num; + if (srcarr == NULL) { + srcarr = malloc(sizeof(char *)*2); + srcarr[0]=malloc(strlen(add)+1); + strcpy(srcarr[0],add); + srcarr[1] = NULL; + } else { + for(num = 0; srcarr[num] != NULL; ++num) + ; + num +=1; + srcarr = realloc(srcarr,sizeof(char *)*(num+1)); + srcarr[num-1] = malloc(strlen(add)+1); + strcpy(srcarr[num-1],add); + srcarr[num] = NULL; + } + return srcarr; +} + + + +char *object_name(char *source) { + char *tmp = add_to("",source); + int j = strlen(tmp)-2; + if (j < 0) { + j = 0; + } + while(j>0 && tmp[j] != '.') { + --j; + } + if (tmp[j] == '.') { + ++j; + } + tmp[j++] = 'o'; + tmp[j] = '\0'; + return tmp; +} + +char *exe_name(char *source) { + char *tmp = add_to("",source); + int j = strlen(tmp)-2; + if (j < 0) { + j = 0; + } + while(j>0 && tmp[j] != '.') { + --j; + } + if (tmp[j] == '.') { + ++j; + } + tmp[j] = '\0'; + return add_to(tmp,"exe"); +} + +char *dyn_get_short(char *longp) +{ + int size; + char *shortp; + size = GetShortPathName(longp,NULL,0); + if (size <= 0) { + return NULL; + } + shortp = malloc(size); + if (GetShortPathName(longp,shortp,size) != size - 1) { + free(shortp); + return NULL; + } + return shortp; +} + +char *do_cyp(char *posix) +{ + ssize_t size; + char *win32; + size = GET_WIN32_SIZE(posix); + char *ret = NULL; + if (size < 0) { + fprintf(stderr,"Could not cygpath %s, errno = %d\n", + posix,errno); + } else { + win32 = (char *) malloc (size); + if (CONVERT_TO_WIN32(posix, + win32, size)) { + fprintf(stderr,"Could not cygpath %s, errno = %d\n", + posix,errno); + } else { + char *w32_short = dyn_get_short(win32); + DEBUGF(("win32 = %s, w32_short = %s\n",win32, (w32_short == NULL) ? "NULL" : w32_short)); + if (w32_short == NULL) { + char *rest = malloc(size); + char *first = malloc(size); + int x = 0; + int y = strlen(win32) - 1; + strcpy(first,win32); + while (w32_short == NULL) { + while ( y > 0 && first[y] != '\\') { + rest[x++] = first[y--]; + } + if (y > 0) { + rest[x++] = first[y]; + first[y--] = '\0'; + } else { + break; + } + w32_short = dyn_get_short(first); + DEBUGF(("first = %s, w32_short = %s\n",first, (w32_short == NULL) ? "NULL" : w32_short)); + } + if (w32_short != NULL) { + y = strlen(w32_short); + w32_short = realloc(w32_short,y+1+x); + /* spool back */ + while ( x > 0) { + w32_short[y++] = rest[--x]; + } + w32_short[y] = '\0'; + } else { + w32_short = malloc(strlen(win32)+1); + strcpy(w32_short,win32); /* last resort */ + } + free(first); + free(rest); + } + ret = w32_short; + while (*ret) { + if (*ret == '\\') { + *ret = '/'; + } + ++ret; + } + ret = w32_short; + } + free(win32); + } + return ret; +} + + + +char *save = ""; + +void save_args(int argc, char **argv) +{ + int i; + for(i = 0; i < argc; ++i) { + save = add_to(save,argv[i]); + save = add_to(save," "); + } +} + +char *progname="cc_wrap"; + +int my_create_pipe(HANDLE *read_p, HANDLE *write_p) +{ + char name_buff[1000]; + SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; + static int counter = 0; + + ++counter; + + sprintf(name_buff,"\\\\.\\pipe\\%s_%d_%d",progname,getpid(),counter); + sa.bInheritHandle = FALSE; + if ((*read_p = CreateNamedPipe(name_buff, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + 0, + 0, + 2000, + &sa)) == INVALID_HANDLE_VALUE || + *read_p == NULL) { + return 0; + } + sa.bInheritHandle = TRUE; + if ((*write_p = CreateFile(name_buff, + GENERIC_WRITE, + 0, /* No sharing */ + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE || + *write_p == NULL) { + CloseHandle(*read_p); + return 0; + } + return 1; +} + +void forwardenv(void) +{ + char *(envs[]) = {"LIB","INCLUDE","LIBPATH", "LD_SH_DEBUG_LOG", NULL}; + char **p = envs; + while (*p != NULL) { + char *val = getenv(*p); + if (val != NULL) { + SetEnvironmentVariable(*p,val); + } + ++p; + } +} + +HANDLE do_run(char *commandline, HANDLE *out, HANDLE *err) +{ + STARTUPINFO start; + HANDLE write_pipe_stdout = NULL, read_pipe_stdout = NULL; + HANDLE write_pipe_stderr = NULL, read_pipe_stderr = NULL; + SECURITY_ATTRIBUTES pipe_security; + SECURITY_ATTRIBUTES attr; + PROCESS_INFORMATION info; + + memset(&start,0,sizeof(start)); + memset(&pipe_security,0,sizeof(pipe_security)); + memset(&attr,0,sizeof(attr)); + memset(&info,0,sizeof(info)); + + pipe_security.nLength = sizeof(pipe_security); + pipe_security.lpSecurityDescriptor = NULL; + pipe_security.bInheritHandle = TRUE; + + if(!my_create_pipe(&read_pipe_stdout,&write_pipe_stdout)){ + error("Could not create stdout pipes!"); + } + if(!my_create_pipe(&read_pipe_stderr,&write_pipe_stderr)){ + error("Could not create stderr pipes!"); + } + start.cb = sizeof (start); + start.dwFlags = STARTF_USESHOWWINDOW; + start.wShowWindow = SW_HIDE; + start.hStdOutput = write_pipe_stdout; + start.hStdError = write_pipe_stderr; + start.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start.dwFlags |= STARTF_USESTDHANDLES; + + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = TRUE; + forwardenv(); /* Cygwin and windows environment variables... sigh... */ + if(!CreateProcess(NULL, + commandline, + &attr, + NULL, + TRUE, + CREATE_DEFAULT_ERROR_MODE, + NULL, + NULL, + &start, + &info)){ + fprintf(stderr,"Could not run %s, last error: %d\n",commandline,GetLastError()); + error("Could not create process"); + } + *out = read_pipe_stdout; + *err = read_pipe_stderr; + CloseHandle(write_pipe_stdout); + CloseHandle(write_pipe_stderr); + return info.hProcess; +} +#define HANDLE_STDOUT 0 +#define HANDLE_STDERR 1 +#define HANDLE_PROC 2 + +#ifdef HARDDEBUG +char *prefix = ""; +#endif + +int handle_overlapped(HANDLE fd, OVERLAPPED *ovp, char *buffer, + int bufflen, int get_old, FILE *whereto, int *skip) +{ + DWORD res,read,err; + char *ptr; + + DEBUGF(("In handle_overlapped(%d,0x%08x,0x%08x,%d,%d), prefix = %s\n", + fd,ovp,buffer,bufflen,get_old,prefix)); + /* h�mta resultat av gamla f�rst */ + if (get_old) { + res = GetOverlappedResult(fd,ovp,&read,TRUE); + DEBUGF(("read = %d, res = %d, GetLastError() = %d\n",read,res,GetLastError())); + if (!res) { + return 0; + } + buffer[read] = '\0'; + ptr = buffer; + while(*skip && *ptr != '\0') { + if (*ptr == '\n') { + --(*skip); + } + ++ptr; + } + if(*ptr != '\0') { + fprintf(whereto,"%s",ptr); + } + } + + ResetEvent(ovp->hEvent); + + for(;;) { + res = ReadFile(fd,buffer,bufflen-1,&read,ovp); + + if (!res) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + DEBUGF(("Error I/O Pending\n")); + return 1; + } + DEBUGF(("ReadFileFailed for %s, %d\n",prefix,err)); + return 0; + } + buffer[read] = '\0'; + ptr = buffer; + while(*skip && *ptr != '\0') { + if (*ptr == '\n') { + --(*skip); + } + ++ptr; + } + if(*ptr != '\0') { + fprintf(whereto,"%s",ptr); + } + } +} + + +int run(char *commandline,int skipout,int skiperr) +{ + HANDLE harr[3]; + HANDLE real_stdout,real_stderr; + OVERLAPPED ov_out,ov_err; + char outbuff[1024],errbuff[1024]; + DWORD ret,exitcode; + HANDLE wait[3]; + int map[3]; + DWORD nwait = 3; + int i,j; + unsigned living_handles = 0x7; + + harr[HANDLE_STDOUT] = CreateEvent(NULL, + TRUE, + FALSE, /*not signalled */ + NULL); + harr[HANDLE_STDERR] = CreateEvent(NULL, + TRUE, + FALSE,/*not signalled */ + NULL); + + memset(&ov_out,0,sizeof(ov_out)); + memset(&ov_err,0,sizeof(ov_err)); + + ov_out.hEvent = harr[HANDLE_STDOUT]; + ov_err.hEvent = harr[HANDLE_STDERR]; + + harr[HANDLE_PROC] = do_run(commandline,&real_stdout,&real_stderr); + +#ifdef HARDDEBUG + prefix = "STDOUT"; +#endif + handle_overlapped(real_stdout,&ov_out,outbuff,1024,0,stdout,&skipout); +#ifdef HARDDEBUG + prefix = "STDERR"; +#endif + handle_overlapped(real_stderr,&ov_err,errbuff,1024,0,stderr,&skiperr); + + for(;;) { + nwait = 0; + for(i=0;i<3;++i) { + if ((living_handles & (1U << i))) { + map[nwait] = i; + wait[nwait++] = harr[i]; + } + } + + ret = WaitForMultipleObjects(nwait, + wait, + FALSE, + INFINITE); + DEBUGF(("Wait returned %d\n",ret)); + + if (ret == WAIT_FAILED) { + error("Wait failed"); + } + + ret -= WAIT_OBJECT_0; + + switch (map[ret]) { + case HANDLE_PROC: + + DEBUGF(("Process died!\n")); + GetExitCodeProcess(harr[HANDLE_PROC],&exitcode); + if ((living_handles &= (~(1U<<HANDLE_PROC))) == 0) { + goto done; + } + --nwait; + break; + case HANDLE_STDOUT: +#ifdef HARDDEBUG + prefix = "STDOUT"; +#endif + if (!handle_overlapped(real_stdout,&ov_out, outbuff,1024,1,stdout,&skipout)) { + if ((living_handles &= (~(1U<<HANDLE_STDOUT))) == 0) { + goto done; + } + } + break; + case HANDLE_STDERR: +#ifdef HARDDEBUG + prefix = "STDERR"; +#endif + if (!handle_overlapped(real_stderr,&ov_err, errbuff,1024,1,stderr,&skiperr)){ + if ((living_handles &= (~(1U<<HANDLE_STDERR))) == 0) { + goto done; + } + } + break; + default: + error("Unexpected wait result"); + } + } + done: + CloseHandle(harr[HANDLE_PROC]); + CloseHandle(harr[HANDLE_STDOUT]); + CloseHandle(harr[HANDLE_STDERR]); + CloseHandle(real_stdout); + CloseHandle(real_stderr); + return (int) exitcode; +} + +int main(int argc, char **argv) +{ + int i; + int x; + char *s; + char *mpath; + char *debuglog; + FILE *debugfile; + + char *common_cflags="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE"; + char *md = "-MD"; + char *debug_flags = ""; + char *optimize_flags = ""; + char *outfile = ""; + char *cmd = ""; + char **sources = NULL; + char *accum_objects = ""; + char *linkcmd = ""; + + int md_forced = 0; + int preprocessing = 0; + int debug_build = 0; + int optimized_build = 0; + int linking = 1; + int retval; + + save_args(argc,argv); + + for(i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + char *opt = argv[i]+1; + switch(*opt) { + case 'W': + if(strcmp(opt,"Wall")) { + goto filename; + } + break; + case 'c': + if(strlen(opt) > 1) { + goto filename; + } + linking = 0; + break; + case 'E': + if(strlen(opt) > 1) { + if (opt[1] == 'H') { + cmd = add_to(cmd," "); + cmd = add_to(cmd,opt); + } else { + goto filename; + } + } + preprocessing = 1; + linking = 0; + break; + case 'O': + /* ignore what opt is requested, set hard */ + optimize_flags = "-Ox -Zi"; + debug_flags = ""; + debug_build = 0; + if (!md_forced) { + md = "-MD"; + } + optimized_build = 1; + break; + case 'g': + if (strcmp(opt,"g") && strcmp(opt,"ggdb")) { + goto filename; + } + if (!optimized_build) { + debug_flags = "-Z7"; + if (!md_forced) { + md = "-MDd"; + } + linkcmd = add_to(linkcmd," -g"); + debug_build = 1; + } + break; + case 'm': + case 'M': + if(!strcmp(opt,"mt") || !strcmp(opt,"MT")) { + md = "-MT"; + } else if (!strcmp(opt,"md") || !strcmp(opt,"MD")) { + md = "-MD"; + } else if (!strcmp(opt,"ml") || !strcmp(opt,"ML")) { + md = "-ML"; + } else if (!strcmp(opt,"mdd") || !strcmp(opt,"MDd") || + !strcmp(opt,"MDD")) { + md = "-MDd"; + } else if (!strcmp(opt,"mtd") || !strcmp(opt,"MTd") || + !strcmp(opt,"MTD")) { + md = "-MTd"; + } else if (!strcmp(opt,"mld") || !strcmp(opt,"MLd") || + !strcmp(opt,"MLD")) { + md = "-MLd"; + } else { + goto filename; + } + md_forced = 1; + break; + case 'o': + if (!strcmp(opt,"o")) { + ++i; + if (i >= argc) { + error("-o without filename"); + } + outfile = argv[i]; + } else { + outfile = opt+1; + } + break; + case 'I': + if(opt[1] == '/') { + mpath = do_cyp(opt+1); + cmd = add_to(cmd," -I\""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + } else { + cmd = add_to(cmd," "); + cmd = add_to(cmd,opt); + } + break; + case 'D': + cmd = add_to(cmd," -"); + cmd = add_to(cmd,opt); + case 'l': + linkcmd = add_to(linkcmd," -"); + linkcmd = add_to(linkcmd,opt); + break; + default: + goto filename; + } + continue; + } + filename: + s = argv[i]; + x = strlen(s); + if (x > 1 && s[x-1] == 'c' && s[x-2] == '.') { + /* C source */ + sources = add_to_src(sources,s); + } else if (x > 3 && !strcmp(s + (x - 4),".cpp")) { + /* C++ */ + sources = add_to_src(sources,s); + } else if (x > 1 && s[x-1] == 'o' && s[x-2] == '.') { + linkcmd = add_to(linkcmd," "); + linkcmd = add_to(linkcmd,s); + } else { + /* Pass rest to linker */ + linkcmd = add_to(linkcmd," "); + linkcmd = add_to(linkcmd,s); + } + } + if ((debuglog = getenv("CC_SH_DEBUG_LOG")) != NULL) { + debugfile = fopen(debuglog,"wb+"); + if (debugfile) { + fprintf(debugfile,"----------------\n"); + } + } else { + debugfile = NULL; + } + + tmpobjdir = add_to("","/tmp/tmpobj"); + { + char pidstr[100]; + pid_t pid = getpid(); + sprintf(pidstr,"%d",pid); + tmpobjdir = add_to(tmpobjdir,pidstr); + } + mkdir(tmpobjdir,0777); + if (sources != NULL) { + char *output_filename; + char *output_flag; + char *params; + for (i=0;sources[i] != NULL; ++i) { + if (!linking) { + int x = strlen(outfile); + if (x > 1 && outfile[x-1] == 'o' && outfile[x-2] == '.') { + if (*outfile != '/') { + /* non absolute object */ + if (i > 0) { + error("Single object multiple sources"); + } + output_filename = add_to("",outfile); + } else { + if (i > 0) { + error("Single object multiple sources"); + } + output_filename = do_cyp(outfile); + } + } else { + char *tmp = object_name(sources[i]); + + /*fprintf(stderr,"sources[i] = %s\ntmp = %s\n", + sources[i],tmp);*/ + + if (!x || outfile[0] != '/') { + /* non absolute directory */ + output_filename = add_to("",outfile); + } else { + output_filename = do_cyp(outfile); + } + /*fprintf(stderr,"output_filename = %s\n",output_filename);*/ + if (*output_filename != '\0') { + output_filename = add_to(output_filename,"/"); + } + output_filename = add_to(output_filename,tmp); + free(tmp); + } + } else { + char *tmp = object_name(sources[i]); + output_filename = add_to("",tmpobjdir); + output_filename = add_to(output_filename,"/"); + output_filename = add_to(output_filename,tmp); + accum_objects = add_to(accum_objects," "); + accum_objects = add_to(accum_objects,output_filename); + /* reform to dos path */ + free(output_filename); + output_filename = do_cyp(tmpobjdir); + output_filename = add_to(output_filename,"/"); + output_filename = add_to(output_filename,tmp); + } + mpath = do_cyp(sources[i]); + if (preprocessing) { + output_flag = add_to("","-E"); + } else { + output_flag = add_to("","-c -Fo"); + output_flag = add_to(output_flag,output_filename); + } + params = add_to("","cl.exe "); + params = add_to(params,common_cflags); + params = add_to(params," "); + params = add_to(params,md); + params = add_to(params," "); + params = add_to(params,debug_flags); + params = add_to(params," "); + params = add_to(params,optimize_flags); + params = add_to(params," "); + params = add_to(params,cmd); + params = add_to(params," "); + params = add_to(params,output_flag); + params = add_to(params," "); + params = add_to(params,mpath); + free(output_filename); + free(output_flag); + free(mpath); + + if (debugfile) { + fprintf(debugfile,"%s\n",save); + fprintf(debugfile,"%s\n",params); + } + if (preprocessing) { + retval = run(params,0,1); + } else { + retval = run(params,1,0); + } + if (retval != 0) { + maybe_cleanup(); + return retval; + } + free(params); + } + } + if (linking) { + char *out_spec; + char *stdlib; + char *params; + if (strlen(outfile) == 0) { + if (sources != NULL && sources[0] != NULL) { + char *tmp = exe_name(sources[0]); + out_spec = add_to("","-o "); + out_spec = add_to(out_spec,tmp); + free(tmp); + } else { + out_spec = add_to("",""); + } + } else { + out_spec = add_to("","-o "); + out_spec = add_to(out_spec,outfile); + } + if (!strcmp(md,"-ML")) { + stdlib="-lLIBC"; + } else if (!strcmp(md,"-MLd")) { + stdlib="-lLIBCD"; + } else if (!strcmp(md,"-MD")) { + stdlib="-lMSVCRT"; + } else if (!strcmp(md,"-MDd")) { + stdlib="-lMSVCRTD"; + } else if (!strcmp(md,"-MT")) { + stdlib="-lLIBCMT"; + } else if (!strcmp(md,"-MTd")) { + stdlib="-lLIBMTD"; + } else { + stdlib = ""; + } +#if 0 + params = add_to("","bash ld.sh "); +#else + params = add_to("","ld_wrap.exe "); +#endif + params = add_to(params,accum_objects); + params = add_to(params," "); + params = add_to(params,out_spec); + params = add_to(params," "); + params = add_to(params,linkcmd); + params = add_to(params," "); + params = add_to(params,stdlib); + free(out_spec); + free(accum_objects); + if (debugfile) { + fprintf(debugfile,"%s\n",params); + } + if (retval = run(params,0,0) != 0) { + maybe_cleanup(); + return retval; + } + free(params); + } + maybe_cleanup(); + return 0; +} + + + + + + + + + + + + + + + + + + diff --git a/erts/etc/win32/cygwin_tools/vc/coffix.c b/erts/etc/win32/cygwin_tools/vc/coffix.c new file mode 100644 index 0000000000..dee0132a61 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/coffix.c @@ -0,0 +1,161 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-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 mini tool fixes an incompatibility between +** Microsoft's tools, who dont like the virtual size being put in +** the physical address field, but rely on the raw size field for +** sizing the ".bss" section. +** This fixes some of the problems with linking gcc compiled objects +** together with MSVC dito. +** +** Courtesy DJ Delorie for describing the COFF file format on +** http://www.delorie.com/djgpp/doc/coff/ +** The coff structures are fetched from Microsofts headers though. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <windows.h> +#include <winnt.h> /* Structure definitions for PE (COFF) */ + +static int dump_edit(char *filename, int edit); +static int v_printf(char *format, ...); + + +char *progname; +int verbouse = 0; + +int main(int argc, char **argv) +{ + int findex = 1; + int edit = 0; + int ret; + + progname = argv[0]; + if (argc == 1) { + fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname); + return 1; + } + for (findex = 1; + findex < argc && (*argv[findex] == '-' || *argv[findex] == '/'); + ++findex) + switch (argv[findex][1]) { + case 'e': + case 'E': + edit = 1; + break; + case 'v': + case 'V': + verbouse = 1; + default: + fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]); + break; + } + if (findex == argc) { + fprintf(stderr,"%s: No filenames given.\n", progname); + return 1; + } + for(; findex < argc; ++findex) + if ((ret = dump_edit(argv[findex],edit)) != 0) + return ret; + return 0; +} + +int dump_edit(char *filename, int edit) +{ + FILE *f = fopen(filename, (edit) ? "r+b" : "rb"); + IMAGE_FILE_HEADER filhdr; + IMAGE_SECTION_HEADER scnhdr; + int i; + + if (f == NULL) { + fprintf(stderr, "%s: cannot open %s.\n", progname, filename); + return 1; + } + + if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) { + fprintf(stderr,"%s: Could not read COFF header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + v_printf("File: %s\n", filename); + v_printf("Magic number: 0x%08x\n", filhdr.Machine); + v_printf("Number of sections: %d\n",filhdr.NumberOfSections); + + if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) { + fprintf(stderr,"%s: Could not read COFF optional header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + + for (i = 0; i < filhdr.NumberOfSections; ++i) { + if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) { + fprintf(stderr,"%s: Could not read section header from %s," + " is this a PE (COFF) file?\n", progname, filename); + fclose(f); + return 1; + } + v_printf("Section %s:\n", scnhdr.Name); + v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress); + v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData); + if (scnhdr.Misc.PhysicalAddress != 0 && + scnhdr.SizeOfRawData == 0) { + printf("Section header %s in file %s will confuse MSC linker, " + "virtual size is 0x%08x and raw size is 0\n", + scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress, + scnhdr.SizeOfRawData); + if (edit) { + scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress; + scnhdr.Misc.PhysicalAddress = 0; + if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 || + fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) { + fprintf(stderr,"%s: could not edit file %s.\n", + progname, filename); + fclose(f); + return 1; + } + printf("Edited object, virtual size is now 0, and " + "raw size is 0x%08x.\n", scnhdr.SizeOfRawData); + } else { + printf("Specify option '-e' to correct the problem.\n"); + } + } + } + fclose(f); + return 0; +} + + +static int v_printf(char *format, ...) +{ + va_list ap; + int ret = 0; + if (verbouse) { + va_start(ap, format); + ret = vfprintf(stdout, format, ap); + va_end(ap); + } + return ret; +} + diff --git a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh new file mode 100755 index 0000000000..c74c35111b --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh @@ -0,0 +1,90 @@ +#! /bin/sh +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +TOOLDIR=$ERL_TOP/erts/etc/win32/cygwin_tools/vc +COFFIX=$TOOLDIR/coffix +WTOOLDIR=`(cygpath -d $TOOLDIR 2>/dev/null || cygpath -w $TOOLDIR)` + +# Do primitive 'make' +newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print` +if [ -z $newer_exe ]; then + echo recompiling $COFFIX.exe + cl.exe -Fe${WTOOLDIR}\\coffix.exe ${WTOOLDIR}\\coffix.c + rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb +fi + +# Try to find out the output filename and remove it from command line +CMD="" +OUTFILE="" +INFILE="" +SKIP_COFFIX=false +while test -n "$1" ; do + x="$1" + case "$x" in + -o/*) + OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;; + -o) + shift + OUTFILE=$1;; + -MM) + SKIP_COFFIX=true + CMD="$CMD \"$x\"";; + *.c) + INFILE="$INFILE $x"; + CMD="$CMD \"$x\"";; + *) + CMD="$CMD \"$x\"";; + esac + shift +done +if [ -z "$INFILE" ]; then + echo 'emu_cc.sh: please give an input filename for the compiler' >&2 + exit 1 +fi +if [ -z "$OUTFILE" ]; then + OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'` +fi + +if [ $SKIP_COFFIX = false ]; then + n=`echo $INFILE | wc -w`; + if [ $n -gt 1 ]; then + echo "emu_cc.sh:Error, multiple sources, one object output."; + exit 1; + fi + TEMPFILE=/tmp/tmp_emu_cc$$.o + if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then + echo "gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1 + fi + eval gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD + RES=$? + if [ $RES = 0 ]; then + $COFFIX.exe -e `(cygpath -d $TEMPFILE 2>/dev/null || cygpath -w $TEMPFILE)` + RES=$? + if [ $RES = 0 ]; then + cp $TEMPFILE $OUTFILE + else + echo "emu_cc.sh: fatal: coffix failed!" >&2 + fi + fi + rm -f $TEMPFILE + exit $RES +else + eval gcc -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer -fno-tree-copyrename $CMD 2>/dev/null + exit $? +fi diff --git a/erts/etc/win32/cygwin_tools/vc/ld.sh b/erts/etc/win32/cygwin_tools/vc/ld.sh new file mode 100755 index 0000000000..ac39bf871c --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/ld.sh @@ -0,0 +1,192 @@ +#! /bin/sh +# set -x +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Save the command line for debug outputs +SAVE="$@" +kernel_libs="kernel32.lib advapi32.lib" +gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib" +DEFAULT_LIBRARIES="$kernel_libs $gdi_libs" + +CMD="" +STDLIB=MSVCRT.LIB +DEBUG_BUILD=false +STDLIB_FORCED=false +BUILD_DLL=false +OUTPUT_FILENAME="" + +while test -n "$1" ; do + x="$1" + case "$x" in + -dll| -DLL) + BUILD_DLL=true;; + -L/*|-L.*) + y=`echo $x | sed 's,^-L\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -libpath:\"$MPATH\"";; + -lMSVCRT|-lmsvcrt) + STDLIB_FORCED=true; + STDLIB=MSVCRT.LIB;; + -lMSVCRTD|-lmsvcrtd) + STDLIB_FORCED=true; + STDLIB=MSVCRTD.LIB;; + -lLIBCMT|-llibcmt) + STDLIB_FORCED=true; + STDLIB=LIBCMT.LIB;; + -lLIBCMTD|-llibcmtd) + STDLIB_FORCED=true; + STDLIB=LIBCMTD.LIB;; + -lsocket) + DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES WS2_32.LIB";; + -l*) + y=`echo $x | sed 's,^-l\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD \"${MPATH}.lib\"";; + -g) + DEBUG_BUILD=true;; + -pdb:none|-incremental:no) + ;; + -implib:*) + y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -implib:\"${MPATH}\"";; + -def:*) + y=`echo $x | sed 's,^-def:\(.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -def:\"${MPATH}\"";; + -o) + shift + MPATH=`cygpath -m $1`; + OUTPUT_FILENAME="$MPATH";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + OUTPUT_FILENAME="$MPATH";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done +if [ $DEBUG_BUILD = true ]; then + linktype="-debug -pdb:none" + if [ $STDLIB_FORCED = false ]; then + STDLIB=MSVCRTD.LIB + fi +fi +# Generate a PDB +linkadd_pdb="" +case "$OUTPUT_FILENAME" in + *.exe|*.EXE) + fn=`echo "$OUTPUT_FILENAME" | sed 's,[eE][xX][eE]$,,g'`; + linkadd_pdb="-pdb:\"${fn}pdb\"";; + *.dll|*.DLL) + fn=`echo "$OUTPUT_FILENAME" | sed 's,[dD][lL][lL]$,,g'`; + linkadd_pdb="-pdb:\"${fn}pdb\"";; + "") + linkadd_pdb="-pdb:\"a.pdb\"";; + *) + linkadd_pdb="-pdb:\"${OUTPUT_FILENAME}.pdb\"";; +esac + + linktype="-debug $linkadd_pdb" + +CHMOD_FILE="" + +if [ $BUILD_DLL = true ];then + case "$OUTPUT_FILENAME" in + *.exe|*.EXE) + echo "Warning, output set to .exe when building DLL" >&2 + CHMOD_FILE="$OUTPUT_FILENAME"; + CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}\;2"; + MANIFEST="${OUTPUT_FILENAME}.manifest";; + *.dll|*.DLL) + CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}\;2"; + MANIFEST="${OUTPUT_FILENAME}.manifest";; + "") + CMD="-dll -out:\"a.dll\" $CMD"; + OUTPUTRES="a.dll\;2"; + MANIFEST="a.dll.manifest";; + *) + CMD="-dll -out:\"${OUTPUT_FILENAME}.dll\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}.dll\;2"; + MANIFEST="${OUTPUT_FILENAME}.dll.manifest";; + esac +else + case "$OUTPUT_FILENAME" in + *.exe|*.EXE) + CHMOD_FILE="$OUTPUT_FILENAME"; + CMD="-out:\"$OUTPUT_FILENAME\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}\;1" + MANIFEST="${OUTPUT_FILENAME}.manifest";; + *.dll|*.DLL) + echo "Warning, output set to .dll when building EXE" >&2 + CMD="-out:\"$OUTPUT_FILENAME\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}\;1"; + MANIFEST="${OUTPUT_FILENAME}.manifest";; + "") + CHMOD_FILE="a.exe"; + CMD="-out:\"a.exe\" $CMD"; + OUTPUTRES="a.exe\;1"; + MANIFEST="a.exe.manifest";; + *) + CMD="-out:\"${OUTPUT_FILENAME}.exe\" $CMD"; + OUTPUTRES="${OUTPUT_FILENAME}.exe\;1"; + MANIFEST="${OUTPUT_FILENAME}.exe.manifest";; + esac +fi + +p=$$ +CMD="$linktype -nologo -incremental:no $CMD $STDLIB $DEFAULT_LIBRARIES" +if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then + echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG + echo link.exe $CMD >>$LD_SH_DEBUG_LOG +fi +eval link.exe "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2 +RES=$? +CMANIFEST=`cygpath $MANIFEST` +if [ "$RES" = "0" -a -f "$CMANIFEST" ]; then + eval mt.exe -nologo -manifest "$MANIFEST" -outputresource:"$OUTPUTRES" >>/tmp/link.exe.${p}.1 2>>/tmp/link.exe.${p}.2 + RES=$? + if [ "$RES" != "0" ]; then + REMOVE=`echo "$OUTPUTRES" | sed 's,\\\;[12]$,,g'` + CREMOVE=`cygpath $REMOVE` + rm -f "$CREMOVE" + fi + rm -f "$CMANIFEST" +fi + +# This works around some strange behaviour +# in cygwin 1.7 Beta on Windows 7 with samba drive. +# Configure will think the compiler failed if test -x fails, +# which it might do as we might not be the owner of the +# file. +if [ '!' -z "$CHMOD_FILE" -a -s "$CHMOD_FILE" -a '!' -x "$CHMOD_FILE" ]; then + chmod +x $CHMOD_FILE +fi + +tail -n +2 /tmp/link.exe.${p}.2 >&2 +cat /tmp/link.exe.${p}.1 +rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1 +exit $RES diff --git a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c new file mode 100644 index 0000000000..7fb3c145ee --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c @@ -0,0 +1,796 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2008-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 <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <sys/cygwin.h> + + + +#ifdef CCP_POSIX_TO_WIN_A +#define NEW_CYGPATH_INTERFACE +#endif + +#ifdef NEW_CYGPATH_INTERFACE +#define GET_WIN32_SIZE(Posix) \ +cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), NULL, 0) +#define CONVERT_TO_WIN32(Posix,Win32,Size) \ +cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), \ + (Win32), (Size)) +#else +#define GET_WIN32_SIZE(Posix) PATH_MAX +#define CONVERT_TO_WIN32(Posix,Win32,Size) \ +((cygwin32_conv_to_full_win32_path((Posix),(Win32)) >= 0) ? 0 : -1) +#endif + +/*#define HARDDEBUG 1*/ + +#ifdef HARDDEBUG +#define DEBUGF(X) printf X +#else +#define DEBUGF(X) /* noop */ +#endif +char *tmpobjdir = ""; + +char *add_to(char *src,char *add) { + int len = strlen(src)+strlen(add)+1; + char *n; + + if (strlen(src) == 0) { + n = malloc(len); + strcpy(n,add); + return n; + } + n = realloc(src,len); + strcat(n,add); + return n; +} + +void error(char *str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + + +char *dyn_get_short(char *longp) +{ + int size; + char *shortp; + size = GetShortPathName(longp,NULL,0); + if (size <= 0) { + return NULL; + } + shortp = malloc(size); + if (GetShortPathName(longp,shortp,size) != size - 1) { + free(shortp); + return NULL; + } + return shortp; +} + +char *do_cyp(char *posix) +{ + ssize_t size; + char *win32; + size = GET_WIN32_SIZE(posix); + char *ret = NULL; + if (size < 0) { + fprintf(stderr,"Could not cygpath %s, errno = %d\n", + posix,errno); + } else { + win32 = (char *) malloc (size); + if (CONVERT_TO_WIN32(posix, + win32, size)) { + fprintf(stderr,"Could not cygpath %s, errno = %d\n", + posix,errno); + } else { + char *w32_short = dyn_get_short(win32); + DEBUGF(("win32 = %s, w32_short = %s\n",win32, (w32_short == NULL) ? "NULL" : w32_short)); + if (w32_short == NULL) { + char *rest = malloc(size); + char *first = malloc(size); + int x = 0; + int y = strlen(win32) - 1; + strcpy(first,win32); + while (w32_short == NULL) { + while ( y > 0 && first[y] != '\\') { + rest[x++] = first[y--]; + } + if (y > 0) { + rest[x++] = first[y]; + first[y--] = '\0'; + } else { + break; + } + w32_short = dyn_get_short(first); + DEBUGF(("first = %s, w32_short = %s\n",first, (w32_short == NULL) ? "NULL" : w32_short)); + } + if (w32_short != NULL) { + y = strlen(w32_short); + w32_short = realloc(w32_short,y+1+x); + /* spool back */ + while ( x > 0) { + w32_short[y++] = rest[--x]; + } + w32_short[y] = '\0'; + } else { + w32_short = malloc(strlen(win32)+1); + strcpy(w32_short,win32); /* last resort */ + } + free(first); + free(rest); + } + ret = w32_short; + while (*ret) { + if (*ret == '\\') { + *ret = '/'; + } + ++ret; + } + ret = w32_short; + } + free(win32); + } + return ret; +} + + + +char *save = ""; + +void save_args(int argc, char **argv) +{ + int i; + for(i = 0; i < argc; ++i) { + save = add_to(save,argv[i]); + save = add_to(save," "); + } +} + +char *progname="ld_wrap"; + +int my_create_pipe(HANDLE *read_p, HANDLE *write_p) +{ + char name_buff[1000]; + SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; + static int counter = 0; + + ++counter; + + sprintf(name_buff,"\\\\.\\pipe\\%s_%d_%d",progname,getpid(),counter); + sa.bInheritHandle = FALSE; + if ((*read_p = CreateNamedPipe(name_buff, + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + 0, + 0, + 2000, + &sa)) == INVALID_HANDLE_VALUE || + *read_p == NULL) { + return 0; + } + sa.bInheritHandle = TRUE; + if ((*write_p = CreateFile(name_buff, + GENERIC_WRITE, + 0, /* No sharing */ + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE || + *write_p == NULL) { + CloseHandle(*read_p); + return 0; + } + return 1; +} + +void forwardenv(void) +{ + char *(envs[]) = {"LIB","INCLUDE","LIBPATH", NULL}; + char **p = envs; + while (*p != NULL) { + char *val = getenv(*p); + if (val != NULL) { + SetEnvironmentVariable(*p,val); + } + ++p; + } +} + +HANDLE do_run(char *commandline, HANDLE *out, HANDLE *err) +{ + STARTUPINFO start; + HANDLE write_pipe_stdout = NULL, read_pipe_stdout = NULL; + HANDLE write_pipe_stderr = NULL, read_pipe_stderr = NULL; + SECURITY_ATTRIBUTES pipe_security; + SECURITY_ATTRIBUTES attr; + PROCESS_INFORMATION info; + + + memset(&start,0,sizeof(start)); + memset(&pipe_security,0,sizeof(pipe_security)); + memset(&attr,0,sizeof(attr)); + memset(&info,0,sizeof(info)); + + + pipe_security.nLength = sizeof(pipe_security); + pipe_security.lpSecurityDescriptor = NULL; + pipe_security.bInheritHandle = TRUE; + + if(!my_create_pipe(&read_pipe_stdout,&write_pipe_stdout)){ + error("Could not create stdout pipes!"); + } + if(!my_create_pipe(&read_pipe_stderr,&write_pipe_stderr)){ + error("Could not create stderr pipes!"); + } + start.cb = sizeof (start); + start.dwFlags = STARTF_USESHOWWINDOW; + start.wShowWindow = SW_HIDE; + start.hStdOutput = write_pipe_stdout; + start.hStdError = write_pipe_stderr; + start.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start.dwFlags |= STARTF_USESTDHANDLES; + + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = TRUE; + forwardenv(); /* Cygwin and windows environment variables... sigh... */ + if(!CreateProcess(NULL, + commandline, + &attr, + NULL, + TRUE, + CREATE_DEFAULT_ERROR_MODE, + NULL, + NULL, + &start, + &info)){ + error("Could not create process"); + } + *out = read_pipe_stdout; + *err = read_pipe_stderr; + CloseHandle(write_pipe_stdout); + CloseHandle(write_pipe_stderr); + return info.hProcess; +} +#define HANDLE_STDOUT 0 +#define HANDLE_STDERR 1 +#define HANDLE_PROC 2 + +#ifdef HARDDEBUG +char *prefix = ""; +#endif + +int handle_overlapped(HANDLE fd, OVERLAPPED *ovp, char *buffer, + int bufflen, int get_old, FILE *whereto, int *skip) +{ + DWORD res,read,err; + char *ptr; + + DEBUGF(("In handle_overlapped(%d,0x%08x,0x%08x,%d,%d), prefix = %s\n", + fd,ovp,buffer,bufflen,get_old,prefix)); + /* h�mta resultat av gamla f�rst */ + if (get_old) { + res = GetOverlappedResult(fd,ovp,&read,TRUE); + DEBUGF(("read = %d, res = %d, GetLastError() = %d\n",read,res,GetLastError())); + if (!res) { + return 0; + } + buffer[read] = '\0'; + ptr = buffer; + while(*skip && *ptr != '\0') { + if (*ptr == '\n') { + --(*skip); + } + ++ptr; + } + if(*ptr != '\0') { + fprintf(whereto,"%s",ptr); + } + } + + ResetEvent(ovp->hEvent); + + for(;;) { + res = ReadFile(fd,buffer,bufflen-1,&read,ovp); + + if (!res) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + DEBUGF(("Error I/O Pending\n")); + return 1; + } + DEBUGF(("ReadFileFailed for %s, %d\n",prefix,err)); + return 0; + } + buffer[read] = '\0'; + ptr = buffer; + while(*skip && *ptr != '\0') { + if (*ptr == '\n') { + --(*skip); + } + ++ptr; + } + if(*ptr != '\0') { + fprintf(whereto,"%s",ptr); + } + } +} + + +int run(char *commandline,int skipout,int skiperr) +{ + HANDLE harr[3]; + HANDLE real_stdout,real_stderr; + OVERLAPPED ov_out,ov_err; + char outbuff[1024],errbuff[1024]; + DWORD ret,exitcode; + HANDLE wait[3]; + int map[3]; + DWORD nwait = 3; + int i,j; + unsigned living_handles = 0x7; + + harr[HANDLE_STDOUT] = CreateEvent(NULL, + TRUE, + FALSE, /*not signalled */ + NULL); + harr[HANDLE_STDERR] = CreateEvent(NULL, + TRUE, + FALSE,/*not signalled */ + NULL); + + memset(&ov_out,0,sizeof(ov_out)); + memset(&ov_err,0,sizeof(ov_err)); + + ov_out.hEvent = harr[HANDLE_STDOUT]; + ov_err.hEvent = harr[HANDLE_STDERR]; + + harr[HANDLE_PROC] = do_run(commandline,&real_stdout,&real_stderr); + +#ifdef HARDDEBUG + prefix = "STDOUT"; +#endif + handle_overlapped(real_stdout,&ov_out,outbuff,1024,0,stdout,&skipout); +#ifdef HARDDEBUG + prefix = "STDERR"; +#endif + handle_overlapped(real_stderr,&ov_err,errbuff,1024,0,stderr,&skiperr); + + for(;;) { + nwait = 0; + for(i=0;i<3;++i) { + if ((living_handles & (1U << i))) { + map[nwait] = i; + wait[nwait++] = harr[i]; + } + } + + ret = WaitForMultipleObjects(nwait, + wait, + FALSE, + INFINITE); + DEBUGF(("Wait returned %d\n",ret)); + + if (ret == WAIT_FAILED) { + error("Wait failed"); + } + + ret -= WAIT_OBJECT_0; + + switch (map[ret]) { + case HANDLE_PROC: + + DEBUGF(("Process died!\n")); + GetExitCodeProcess(harr[HANDLE_PROC],&exitcode); + if ((living_handles &= (~(1U<<HANDLE_PROC))) == 0) { + goto done; + } + --nwait; + break; + case HANDLE_STDOUT: +#ifdef HARDDEBUG + prefix = "STDOUT"; +#endif + if (!handle_overlapped(real_stdout,&ov_out, outbuff,1024,1,stdout,&skipout)) { + if ((living_handles &= (~(1U<<HANDLE_STDOUT))) == 0) { + goto done; + } + } + break; + case HANDLE_STDERR: +#ifdef HARDDEBUG + prefix = "STDERR"; +#endif + if (!handle_overlapped(real_stderr,&ov_err, errbuff,1024,1,stderr,&skiperr)){ + if ((living_handles &= (~(1U<<HANDLE_STDERR))) == 0) { + goto done; + } + } + break; + default: + error("Unexpected wait result"); + } + } + done: + CloseHandle(harr[HANDLE_PROC]); + CloseHandle(harr[HANDLE_STDOUT]); + CloseHandle(harr[HANDLE_STDERR]); + CloseHandle(real_stdout); + CloseHandle(real_stderr); + return (int) exitcode; +} + +int main(int argc, char **argv) +{ + int i; + int x; + char *s; + char *mpath; + char *debuglog; + char *remove; + FILE *debugfile; + FILE *tmpfile; + int filefound; + int retval = 0; + + char *kernel_libs="kernel32.lib advapi32.lib"; + char *gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib"; + char *default_libraries = ""; + char *cmd = ""; + char *stdlib = "MSVCRT.LIB"; + int debug_build = 0; + int stdlib_forced = 0; + int build_dll = 0; + char *output_filename = ""; + char *linkadd_pdb = ""; + char *linktype = ""; + char *manifest = ""; + char *outputres = ""; + + save_args(argc,argv); + //fprintf(stderr,"ld_wrap!\n"); + + default_libraries = add_to(default_libraries,kernel_libs); + default_libraries = add_to(default_libraries," "); + default_libraries = add_to(default_libraries,gdi_libs); + + for(i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + char *opt = argv[i]+1; + switch(*opt) { + case 'D': + if(strcmp(opt,"DLL")) { + goto filename; + } + build_dll = 1; + break; + case 'd': + if(!strncmp(opt,"def:",4)) { + mpath = do_cyp(opt+4); + cmd = add_to(cmd," -def:\""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + } else if(strcmp(opt,"dll")) { + goto filename; + } else { + build_dll = 1; + } + break; + case 'L': + mpath = do_cyp(opt+1); + cmd = add_to(cmd," -libpath:\""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + break; + case 'l': + if(!strcmp(opt,"lMSVCRT") || !strcmp(opt,"lmsvcrt")) { + stdlib = "MSVCRT.LIB"; + stdlib_forced = 1; + } else if(!strcmp(opt,"lMSVCRTD") || !strcmp(opt,"lmsvcrtd")) { + stdlib = "MSVCRTD.LIB"; + stdlib_forced = 1; + } else if(!strcmp(opt,"lLIBCMT") || !strcmp(opt,"llibcmt")) { + stdlib = "LIBCMT.LIB"; + stdlib_forced = 1; + } else if(!strcmp(opt,"lLIBCMTD") || !strcmp(opt,"llibcmtd")) { + stdlib = "LIBCMTD.LIB"; + stdlib_forced = 1; + } else if(!strcmp(opt,"lsocket")) { + default_libraries = add_to(default_libraries," "); + default_libraries = add_to(default_libraries,"WS2_32.LIB"); + } else { + mpath = do_cyp(opt+1); + cmd = add_to(cmd," \""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + } + break; + case 'g': + debug_build = 1; + break; + case 'p': + if (strcmp(opt,"pdb:none")) { + goto filename; + } + break; + case 'i': + if (!strncmp(opt,"implib:",7)) { + mpath = do_cyp(opt+7); + cmd = add_to(cmd," -implib:\""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + } else if (strcmp(opt,"incremental:no")) { + goto filename; + } + break; + case 'o': + if (!strcmp(opt,"o")) { + ++i; + if (i >= argc) { + error("-o without filename"); + } + output_filename = do_cyp(argv[i]); + } else { + output_filename = do_cyp(opt+1); + } + break; + default: + goto filename; + } + continue; + } + filename: + s = argv[i]; + if (*s == '/') { + mpath = do_cyp(s); + cmd = add_to(cmd," \""); + cmd = add_to(cmd,mpath); + cmd = add_to(cmd,"\""); + free(mpath); + } else { + cmd = add_to(cmd," \""); + cmd = add_to(cmd,s); + cmd = add_to(cmd,"\""); + } + } + if ((debuglog = getenv("LD_SH_DEBUG_LOG")) != NULL) { + debugfile = fopen(debuglog,"wb+"); + if (debugfile) { + fprintf(debugfile,"----------------\n"); + } + } else { + debugfile = NULL; + } + + if (debug_build) { + if (!stdlib_forced) { + stdlib = "MSVCRTD.LIB"; + } + } + + s = add_to("",output_filename); + x = strlen(s); + + if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") || + !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) { + *(s+x-3) = '\0'; + linkadd_pdb = add_to(linkadd_pdb,"-pdb:\""); + linkadd_pdb = add_to(linkadd_pdb,s); + linkadd_pdb = add_to(linkadd_pdb,"pdb\""); + } else if (!x) { + linkadd_pdb = add_to(linkadd_pdb,"-pdb:\"a.pdb\""); + } else { + linkadd_pdb = add_to(linkadd_pdb,"-pdb:\""); + linkadd_pdb = add_to(linkadd_pdb,s); + linkadd_pdb = add_to(linkadd_pdb,".pdb\""); + } + free(s); + + + linktype = add_to(linktype,"-debug "); + linktype = add_to(linktype,linkadd_pdb); + + free(linkadd_pdb); + + s = add_to("",output_filename); + x = strlen(s); + + if (build_dll) { + if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") || + !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) { + + if (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE")) { + fprintf(stderr,"Warning, output set to .exe when building DLL"); + } + mpath = cmd; + cmd = add_to("","-dll -out:\""); + cmd = add_to(cmd,s); + cmd = add_to(cmd,"\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,output_filename); + outputres = add_to(outputres,";2"); + manifest = add_to(manifest,output_filename); + manifest = add_to(manifest,".manifest"); + } else if (x == 0) { + mpath = cmd; + cmd = add_to("","-dll -out:\"a.dll\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,"a.dll;2"); + manifest = add_to(manifest,"a.dll.manifest"); + } else { + mpath = cmd; + cmd = add_to("","-dll -out:\""); + cmd = add_to(cmd,s); + cmd = add_to(cmd,".dll\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,output_filename); + outputres = add_to(outputres,".dll;2"); + manifest = add_to(manifest,output_filename); + manifest = add_to(manifest,".dll.manifest"); + } + } else { + if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") || + !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) { + + if (!strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL")) { + fprintf(stderr,"Warning, output set to .exe when building DLL"); + } + mpath = cmd; + cmd = add_to("","-out:\""); + cmd = add_to(cmd,s); + cmd = add_to(cmd,"\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,output_filename); + outputres = add_to(outputres,";1"); + manifest = add_to(manifest,output_filename); + manifest = add_to(manifest,".manifest"); + } else if (x == 0) { + mpath = cmd; + cmd = add_to("","-out:\"a.exe\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,"a.exe;1"); + manifest = add_to(manifest,"a.exe.manifest"); + } else { + mpath = cmd; + cmd = add_to("","-out:\""); + cmd = add_to(cmd,s); + cmd = add_to(cmd,".exe\" "); + cmd = add_to(cmd,mpath); + if (*mpath) { + free(mpath); + } + + outputres = add_to(outputres,output_filename); + outputres = add_to(outputres,".exe;1"); + manifest = add_to(manifest,output_filename); + manifest = add_to(manifest,".exe.manifest"); + } + } + + s = cmd; + cmd = add_to("","link.exe "); + cmd = add_to(cmd,linktype); + cmd = add_to(cmd," -nologo -incremental:no "); + cmd = add_to(cmd,s); + cmd = add_to(cmd," "); + cmd = add_to(cmd,stdlib); + cmd = add_to(cmd," "); + cmd = add_to(cmd,default_libraries); + + if (*s) { + free(s); + } + + + if (debugfile) { + fprintf(debugfile,"%s\n",save); + fprintf(debugfile,"%s\n",cmd); + } + + retval = run(cmd,0,0); + + + mpath = do_cyp(manifest); + filefound = 0; + tmpfile = fopen(mpath,"rb"); + if (tmpfile != NULL) { + filefound = 1; + fclose(tmpfile); + } + if (retval == 0 && filefound) { + s = add_to("","mt.exe -nologo -manifest \""); + s = add_to(s,manifest); + s = add_to(s,"\" -outputresource:\""); + s = add_to(s,outputres); + s = add_to(s,"\""); + if (debugfile) { + fprintf(debugfile,"%s\n",s); + } + retval = run(s,0,0); + if (*s) { + free(s); + } + if (retval) { + /* cleanup needed */ + remove = add_to("",outputres); + x = strlen(remove); + remove[x-2] = '\0'; + if (debugfile) { + fprintf(debugfile,"remove %s\n",remove); + } + DeleteFile(remove); + free(remove); + } + if (debugfile) { + fprintf(debugfile,"remove %s\n",manifest); + } + DeleteFile(manifest); + } + return retval; +} + + + + + + + + + + + + + + + + + + diff --git a/erts/etc/win32/cygwin_tools/vc/mc.sh b/erts/etc/win32/cygwin_tools/vc/mc.sh new file mode 100755 index 0000000000..813b59947b --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/mc.sh @@ -0,0 +1,87 @@ +#! /bin/sh +# set -x +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Save the command line for debug outputs +SAVE="$@" +CMD="" +OUTPUT_DIRNAME="" + +# Find the correct mc.exe. This could be done by the configure script, +# But as we seldom use the resource compiler, it might as well be done here... +MCC="" +save_ifs=$IFS +IFS=: +for p in $PATH; do + if [ -f $p/mc.exe ]; then + if [ -n "`$p/mc.exe -? 2>&1 >/dev/null </dev/null \ + | grep -i \"message compiler\"`" ]; then + MCC=$p/mc.exe + fi + fi +done +IFS=$save_ifs + +if [ -z "$MCC" ]; then + echo 'mc.exe not found!' >&2 + exit 1 +fi + +while test -n "$1" ; do + x="$1" + case "$x" in + -o) + shift + OUTPUT_DIRNAME="$1";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + OUTPUT_DIRNAME="$y";; + -I) + shift + MPATH=`cygpath -m $1`; + CMD="$CMD -I\"$MPATH\"";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -I\"$MPATH\"";; + *) + MPATH=`cygpath -m -a $x`; + CMD="$CMD \"$MPATH\"";; + esac + shift +done +p=$$ +if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then + echo rc.sh "$SAVE" >>$MC_SH_DEBUG_LOG + echo rc.exe $CMD >>$MC_SH_DEBUG_LOG +fi +if [ -n "$OUTPUT_DIRNAME" ]; then + cd $OUTPUT_DIRNAME + RES=$? + if [ "$RES" != "0" ]; then + echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2 + exit $RES + fi +fi +eval $MCC "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2 +RES=$? +tail +2 /tmp/mc.exe.${p}.2 >&2 +cat /tmp/mc.exe.${p}.1 +rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1 +exit $RES diff --git a/erts/etc/win32/cygwin_tools/vc/rc.sh b/erts/etc/win32/cygwin_tools/vc/rc.sh new file mode 100755 index 0000000000..748de48890 --- /dev/null +++ b/erts/etc/win32/cygwin_tools/vc/rc.sh @@ -0,0 +1,86 @@ +#! /bin/sh +# set -x +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2002-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# Save the command line for debug outputs +SAVE="$@" +CMD="" +OUTPUT_FILENAME="" + +# Find the correct rc.exe. This could be done by the configure script, +# But as we seldom use the resource compiler, it might as well be done here... +RCC="" +save_ifs=$IFS +IFS=: +for p in $PATH; do + if [ -f $p/rc.exe ]; then + if [ -n "`$p/rc.exe -? 2>&1 | grep -i "resource compiler"`" ]; then + RCC=$p/rc.exe + fi + fi +done +IFS=$save_ifs + +if [ -z "$RCC" ]; then + echo 'rc.exe not found!' >&2 + exit 1 +fi + +while test -n "$1" ; do + x="$1" + case "$x" in + -o) + shift + MPATH=`cygpath -m $1`; + OUTPUT_FILENAME="$MPATH";; + -o/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + OUTPUT_FILENAME="$MPATH";; + -I) + shift + MPATH=`cygpath -m $1`; + CMD="$CMD -I\"$MPATH\"";; + -I/*) + y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`; + MPATH=`cygpath -m $y`; + CMD="$CMD -I\"$MPATH\"";; + /*) + MPATH=`cygpath -m $x`; + CMD="$CMD \"$MPATH\"";; + *) + y=`echo $x | sed 's,",\\\",g'`; + CMD="$CMD \"$y\"";; + esac + shift +done +p=$$ +if [ -n "$OUTPUT_FILENAME" ]; then + CMD="-Fo$OUTPUT_FILENAME $CMD" +fi +if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then + echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG + echo rc.exe $CMD >>$RC_SH_DEBUG_LOG +fi +eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2 +RES=$? +tail +2 /tmp/rc.exe.${p}.2 >&2 +cat /tmp/rc.exe.${p}.1 +rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1 +exit $RES diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c new file mode 100644 index 0000000000..d341153966 --- /dev/null +++ b/erts/etc/win32/erl.c @@ -0,0 +1,283 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2003-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% + */ +#pragma comment(linker,"/manifestdependency:\"type='win32' "\ + "name='Microsoft.Windows.Common-Controls' "\ + "version='6.0.0.0' processorArchitecture='*' "\ + "publicKeyToken='6595b64144ccf1df' language='*'\"") +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include "init_file.h" + +typedef int ErlexecFunction(int, char **, HANDLE, int); + +#define INI_FILENAME "erl.ini" +#define INI_SECTION "erlang" +#define ERLEXEC_BASENAME "erlexec.dll" + +static void get_parameters(void); +static void error(char* format, ...); + +static char *erlexec_name; +static char *erlexec_dir; + +#ifdef WIN32_WERL +#define WERL 1 +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + PSTR szCmdLine, int iCmdShow) +{ + int argc = __argc; + char **argv = __argv; +#else +#define WERL 0 +int main(int argc, char **argv) +{ +#endif + HANDLE erlexec_handle; /* Instance */ + ErlexecFunction *win_erlexec; + char *path = malloc(100); + char *npath; + int pathlen; + + get_parameters(); + + if ((pathlen = GetEnvironmentVariable("PATH",path,100)) == 0) { + error("No PATH variable (!)"); + } else if (pathlen > 100) { + path = realloc(path,pathlen); + GetEnvironmentVariable("PATH",path,pathlen); + } + npath = malloc(strlen(path) + strlen(erlexec_dir) + 2); + sprintf(npath,"%s;%s",erlexec_dir,path); + SetEnvironmentVariable("PATH",npath); + + if ((erlexec_handle = LoadLibrary(erlexec_name)) == NULL) { + error("Could not load module %s.",erlexec_name); + } + + if ((win_erlexec = (ErlexecFunction *) + GetProcAddress(erlexec_handle,"win_erlexec")) == NULL) { + error("Could not find entry point \"win_erlexec\" in %s.", erlexec_name); + } + + return (*win_erlexec)(argc,argv,erlexec_handle,WERL); + +} + + +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 *do_lookup_in_section(InitSection *inis, char *name, + char *section, char *filename) +{ + 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); + } + return _strdup(p); +} + +static void copy_latest_vsn(char *latest_vsn, char *next_vsn) +{ + /* Copy */ + char *lp; + char *np; + /* Find vsn */ + for (lp = next_vsn+strlen(next_vsn)-1 ;lp >= next_vsn && *lp != '\\'; --lp) + ; + /* lp =+ length("erts-"); */ + for (np = next_vsn+strlen(next_vsn)-1 ;np >= next_vsn && *np != '\\'; --np) + ; + /* np =+ length("erts-"); */ + + for (; lp && np; ++lp, ++np) { + if (*lp == *np) { + continue; + } + if (*np == '.' || *np == '\0' || *np <= *lp) { + /* */ + return; + } + if (*lp == '.' || *lp == '\0') { + strcpy(latest_vsn, next_vsn); + return; + } + } + return; +} + +static char *find_erlexec_dir2(char *install_dir) +{ + /* List install dir and look for latest erts-vsn */ + + HANDLE dir_handle; /* Handle to directory. */ + char wildcard[MAX_PATH]; /* Wildcard to search for. */ + WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */ + char latest_vsn[MAX_PATH]; + + /* Setup wildcard */ + int length = strlen(install_dir); + char *p; + + if (length+3 >= MAX_PATH) { + error("Cannot find erlexec.exe"); + } + + strcpy(wildcard, install_dir); + p = wildcard+length-1; + if (*p != '/' && *p != '\\') + *++p = '\\'; + strcpy(++p, "erts-*"); + + /* Find first dir */ + dir_handle = FindFirstFile(wildcard, &find_data); + if (dir_handle == INVALID_HANDLE_VALUE) { + /* No erts-vsn found*/ + return NULL; + } + strcpy(latest_vsn, find_data.cFileName); + + /* Find the rest */ + while(FindNextFile(dir_handle, &find_data)) { + copy_latest_vsn(latest_vsn, find_data.cFileName); + } + + FindClose(dir_handle); + + p = malloc((strlen(install_dir)+1+strlen(latest_vsn)+4+1)*sizeof(char)); + + strcpy(p,install_dir); + strcat(p,"\\"); + strcat(p,latest_vsn); + strcat(p,"\\bin"); + return p; +} + +static char *find_erlexec_dir(char *erlpath) +{ + /* Assume that the path to erl is absolute and + * that it is not a symbolic link*/ + + char *dir =_strdup(erlpath); + char *p; + char *p2; + + /* Chop of base name*/ + for (p = dir+strlen(dir)-1 ;p >= dir && *p != '\\'; --p) + ; + *p ='\0'; + p--; + + /* Check if dir path is like ...\install_dir\erts-vsn\bin */ + for (;p >= dir && *p != '\\'; --p) + ; + p--; + for (p2 = p;p2 >= dir && *p2 != '\\'; --p2) + ; + p2++; + if (strncmp(p2, "erts-", strlen("erts-")) == 0) { + p = _strdup(dir); + free(dir); + return p; + } + + /* Assume that dir path is like ...\install_dir\bin */ + *++p ='\0'; /* chop off bin dir */ + + p = find_erlexec_dir2(dir); + free(dir); + if (p == NULL) { + error("Cannot find erlexec.exe"); + } else { + return p; + } +} + +static void get_parameters(void) +{ + char buffer[MAX_PATH]; + char *ini_filename; + HANDLE module = GetModuleHandle(NULL); + InitFile *inif; + InitSection *inis; + char *bindir; + + 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) { + erlexec_dir = find_erlexec_dir(ini_filename); + SetEnvironmentVariable("ERLEXEC_DIR", erlexec_dir); + } else { + + if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) { + error("Could not find section %s in init file %s", + INI_SECTION, ini_filename); + } + + erlexec_dir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename); + free_init_file(inif); + } + + erlexec_name = malloc(strlen(erlexec_dir) + strlen(ERLEXEC_BASENAME) + 2); + strcpy(erlexec_name,erlexec_dir); + strcat(erlexec_name, "\\" ERLEXEC_BASENAME); + + free(ini_filename); +} + + +static void error(char* format, ...) +{ + char sbuf[2048]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + +#ifndef WIN32_WERL + fprintf(stderr, "%s\n", sbuf); +#else + MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR); +#endif + exit(1); +} + diff --git a/erts/etc/win32/erl.rc b/erts/etc/win32/erl.rc new file mode 100644 index 0000000000..88213d48f2 --- /dev/null +++ b/erts/etc/win32/erl.rc @@ -0,0 +1,31 @@ +// +// %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% +// +#include <windows.h> +#include "resource.h" + +1 ICON DISCARDABLE "erlang.ico" + + + + + + + + + diff --git a/erts/etc/win32/erl_icon.ico b/erts/etc/win32/erl_icon.ico Binary files differnew file mode 100644 index 0000000000..3e228317cd --- /dev/null +++ b/erts/etc/win32/erl_icon.ico diff --git a/erts/etc/win32/erl_log.c b/erts/etc/win32/erl_log.c new file mode 100644 index 0000000000..85cc49e0e3 --- /dev/null +++ b/erts/etc/win32/erl_log.c @@ -0,0 +1,73 @@ +/* + * %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% + */ +/* ----------------------------------------------------------------- + * erl_log: + * + * Provides a simple debug log for the Erlang emulator. + * It simples echoes its standard intput to the console. + * + * Author: Bjorn Gustavsson + * Created: 1996-12-06 + * ----------------------------------------------------------------- + */ + +#include <windows.h> +#include <stdio.h> + +static void print_last_error(char* message); + +main() +{ + HANDLE in; + HANDLE out; + char sbuf[256]; + DWORD written; + DWORD numChars; + + in = GetStdHandle(STD_INPUT_HANDLE); + out = CreateFile("CONOUT$", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (out == INVALID_HANDLE_VALUE) { + print_last_error("CreateFile"); + exit(1); + } + + while (ReadFile(in, sbuf, sizeof(sbuf), &numChars, NULL) && numChars) { + WriteFile(out, sbuf, numChars, &written, NULL); + } + return 0; +} + +static void print_last_error(char* message) +{ + LPTSTR* lpBufPtr; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpBufPtr, + 0, + NULL); + if (message == NULL) + printf("%s", lpBufPtr); + else + printf("%s: %s\n", message, lpBufPtr); +} diff --git a/erts/etc/win32/erlang.ico b/erts/etc/win32/erlang.ico Binary files differnew file mode 100644 index 0000000000..cee8b58af9 --- /dev/null +++ b/erts/etc/win32/erlang.ico diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h new file mode 100644 index 0000000000..d3922dc1e3 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_global.h @@ -0,0 +1,37 @@ +/* + * %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% + */ +#ifndef _ERLSRV_GLOBAL_H +#define _ERLSRV_GLOBAL_H + +#define APP_NAME "ErlSrv" + +#define ERLANG_MACHINE "erl.exe" + +#define SERVICE_ENV "ERLSRV_SERVICE_NAME" +#define EXECUTABLE_ENV "ERLSRV_EXECUTABLE" +#define DEBUG_ENV "ERLSRV_DEBUG" + +#ifdef _DEBUG +#define HARDDEBUG 1 +#define DEBUG 1 +#else +#define NDEBUG 1 +#endif + +#endif /* _ERLSRV_GLOBAL_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c new file mode 100644 index 0000000000..13e029b364 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c @@ -0,0 +1,1163 @@ +/* + * %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% + */ +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "erlsrv_global.h" +#include "erlsrv_registry.h" +#include "erlsrv_interactive.h" +#include "erlsrv_util.h" /* service_name */ + +#define DBG fprintf(stderr,"argv[0]:%s line %d\n",argv[0],__LINE__) + +/* Really HAS to correcpond to the enum in erlsrv_registry.h */ +static char *arg_tab[] = { + "stopaction", "st", + "onfail", "on", + "machine", "m", + "env", "e", + "workdir", "w", + "priority", "p", + "sname", "sn", + "name", "n", + "args", "ar", + "debugtype", "d", + "internalservicename","i", + "comment","c", + NULL, NULL +}; + +static char *generate_real_service_name(char *display_name){ + SYSTEMTIME systime; + FILETIME ftime; + char *buff = malloc(strlen(display_name)+ + (8*2)+1); + char *tmp = _strdup(display_name); + int i; + /* 2 Hex chars for each byte in a DWORD */ + GetSystemTime(&systime); + SystemTimeToFileTime(&systime,&ftime); + /* Remove trailing version info to avoid user confusion */ + for(i = (strlen(tmp)-1);i > 0; --i) + if(tmp[i] == '_'){ + tmp[i] = '\0'; + break; + } + sprintf(buff,"%s%08x%08x",tmp,ftime.dwHighDateTime, + ftime.dwLowDateTime); + free(tmp); + return buff; +} + +static int lookup_arg(char *arg){ + int i; + if(*arg != '-' && *arg != '/') + return -1; + for(i=0; arg_tab[i] != NULL; i += 2){ + if(!_strnicmp(arg_tab[i],arg+1,strlen(arg+1)) && + !_strnicmp(arg_tab[i+1],arg+1,strlen(arg_tab[i+1]))) + return (i / 2); + } + return -1; +} + + + +char *edit_env(char *edit, char *oldenv){ + char **arg; + char *value; + char *name = strdup(edit); + int i; + char *tmp; + arg = env_to_arg(oldenv); + value = strchr(name,'='); + if(value){ + *(value++) = '\0'; + if(*value == '\0') + value = NULL; + } + for(i=0;arg[i] != NULL; ++i){ + tmp = strchr(arg[i],'='); + if(((int) strlen(name)) == (tmp - arg[i]) && + !_strnicmp(name,arg[i], tmp - arg[i])) + break; + } + if(arg[i] != NULL){ + free(arg[i]); + if(value){ + arg[i] = strdup(edit); + } else { + do { + arg[i] = arg[i+1]; + ++i; + } while(arg[i] != NULL); + } + } else if(value){ /* add to arg, which is always allocated + to hold one extra environment variable*/ + arg[i] = strdup(edit); + arg[i+1] = NULL; + } + free(name); + return arg_to_env(arg); +} + +int last_error = 0; + +void print_last_error(void){ + char *mes; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + (last_error) ? last_error : GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &mes, + 0, + NULL ); + fprintf(stderr,"Error: %s",mes); + LocalFree(mes); +} + +static BOOL install_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + char filename[MAX_PATH + 3]; + DWORD fnsiz=MAX_PATH; + char dependant[] = { 'L','a','n','m','a','n', + 'W','o','r','k','s','t', + 'a','t','i','o','n','\0','\0'}; + + if(!(fnsiz = GetModuleFileName(NULL, filename, fnsiz))) + return FALSE; + if(strchr(filename,' ')){ + memmove(filename+1,filename,fnsiz); + filename[0] ='\"'; /* " */ + filename[fnsiz+1] = '\"'; /* " */ + filename[fnsiz+2] = '\0'; + } + if((scm = OpenSCManager(NULL, + NULL, + SC_MANAGER_CONNECT | + SC_MANAGER_CREATE_SERVICE)) + == NULL){ + last_error = GetLastError(); + return FALSE; + } + service = CreateService(scm, + real_service_name, + service_name, + SERVICE_ALL_ACCESS & + ~(SERVICE_PAUSE_CONTINUE), + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + filename, + NULL, + NULL, + dependant, + NULL, + NULL); + if(service == NULL){ + CloseServiceHandle(scm); + last_error = GetLastError(); + return FALSE; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return TRUE; +} + +static BOOL remove_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + if((scm = OpenSCManager(NULL, + NULL, + GENERIC_WRITE)) + == NULL) + return FALSE; + service = OpenService(scm, + real_service_name, + SERVICE_ALL_ACCESS); + if(service == NULL){ + CloseServiceHandle(scm); + return FALSE; + } + if(!DeleteService(service)){ + last_error = GetLastError(); + return FALSE; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return TRUE; +} + +static BOOL open_service_control(SC_HANDLE *scm, SC_HANDLE *service){ + if((*scm = OpenSCManager(NULL, + NULL, + SC_MANAGER_ALL_ACCESS)) + == NULL) + return FALSE; + *service = OpenService(*scm, + real_service_name, + SERVICE_ALL_ACCESS); + if(service == NULL){ + CloseServiceHandle(*scm); + return FALSE; + } + return TRUE; +} + +static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){ + if((*scm = OpenSCManager(NULL, + NULL, + /*GENERIC_WRITE | GENERIC_EXECUTE*/ + SC_MANAGER_ALL_ACCESS)) + == NULL){ + last_error = GetLastError(); + return FALSE; + } + *service = OpenService(*scm, + real_service_name, + /*GENERIC_WRITE*/ + SERVICE_ALL_ACCESS); + if(service == NULL){ + last_error = GetLastError(); + CloseServiceHandle(*scm); + return FALSE; + } + return TRUE; +} + +static BOOL set_service_comment(char *comment) { + SC_HANDLE scm; + SC_HANDLE service; + SERVICE_DESCRIPTION sd; + BOOL ret = TRUE; + sd.lpDescription = comment; + if (!open_service_config(&scm,&service)) { + return FALSE; + } + if (!ChangeServiceConfig2(service,SERVICE_CONFIG_DESCRIPTION,&sd)) { + last_error = GetLastError(); + ret = FALSE; + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return ret; +} + +static BOOL wait_service_trans(DWORD initial, DWORD passes, DWORD goal, + int timeout) +{ + SC_HANDLE scm; + SC_HANDLE service; + int moved = 0; + BOOL ret; + int i; + SERVICE_STATUS stat; + + if(! open_service_config(&scm,&service)) + return FALSE; + for(i = 0; i < timeout; ++i){ + if(!QueryServiceStatus(service,&stat)){ + last_error = GetLastError(); + ret = FALSE; + goto out; + } + if(stat.dwCurrentState == initial){ + if(moved){ + ret = FALSE; + + /* + * The exitcode is usually strange when we tried to stop and failed, + * to report a timeout is more appropriate. + */ + if(goal == SERVICE_STOPPED) + last_error = ERROR_SERVICE_REQUEST_TIMEOUT; + else + last_error = stat.dwWin32ExitCode; + goto out; + } + } else if(stat.dwCurrentState == passes){ + moved = 1; + } else if(stat.dwCurrentState == goal){ + ret = TRUE; + goto out; + } + Sleep(1000); + } + ret = FALSE; + last_error = ERROR_SERVICE_REQUEST_TIMEOUT; +out: + CloseServiceHandle(scm); + CloseServiceHandle(service); + return ret; +} + +static BOOL stop_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + BOOL ret; + SERVICE_STATUS ss; + + if(!open_service_control(&scm,&service)){ +#ifdef HARDDEBUG + fprintf(stderr,"Failed to open service.\n"); +#endif + return FALSE; + } + ret = ControlService(service,SERVICE_CONTROL_STOP,&ss); + if(!ret){ + last_error = GetLastError(); + } + CloseServiceHandle(service); + CloseServiceHandle(scm); +#ifdef HARDDEBUG + if(!ret) + { + fprintf(stderr,"Failed to control service.\n"); + print_last_error(); + } +#endif + return ret; +} + +static BOOL start_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + BOOL ret; + + if(!open_service_control(&scm,&service)) + return FALSE; + + ret = StartService(service,0,NULL); + if(!ret){ + last_error = GetLastError(); + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return ret; +} + +static BOOL disable_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + BOOL ret; + + if(!open_service_config(&scm,&service)) + return FALSE; + + ret = ChangeServiceConfig(service, + SERVICE_NO_CHANGE, + SERVICE_DISABLED, + SERVICE_NO_CHANGE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + if(!ret){ + last_error = GetLastError(); + } + + CloseServiceHandle(service); + CloseServiceHandle(scm); + return ret; +} + +static BOOL enable_service(void){ + SC_HANDLE scm; + SC_HANDLE service; + BOOL ret; + +if(!open_service_config(&scm,&service)) + return FALSE; + + ret = ChangeServiceConfig(service, + SERVICE_NO_CHANGE, + SERVICE_AUTO_START, + SERVICE_NO_CHANGE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + if(!ret){ + last_error = GetLastError(); + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return ret; +} + +static BOOL set_interactive(BOOL interactive){ + SC_HANDLE scm; + SC_HANDLE service; + BOOL ret; + + if(!open_service_config(&scm,&service)) + return FALSE; + + ret = ChangeServiceConfig(service, + SERVICE_WIN32_OWN_PROCESS | ((interactive) ? + SERVICE_INTERACTIVE_PROCESS : 0), + SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + if(!ret){ + last_error = GetLastError(); + } + CloseServiceHandle(service); + CloseServiceHandle(scm); + return ret; +} + + +RegEntry *old_entries = NULL; + +BOOL fetch_current(RegEntry *new){ + int i; + + if(!(old_entries = get_keys(service_name))) + return FALSE; + for(i=0;i<num_reg_entries;++i) + new[i] = old_entries[i]; + return TRUE; +} + +void cleanup_old(){ + if(old_entries != NULL) + free_keys(old_entries); +} + +BOOL fill_in_defaults(RegEntry *new){ + char filename[MAX_PATH]; + char *ptr; + + + if(!GetModuleFileName(NULL, filename, MAX_PATH)) + return FALSE; + for(ptr = filename + strlen(filename) - 1; + ptr > filename && *ptr != '\\'; + --ptr) + ; + if(*ptr == '\\') + ++ptr; + *ptr = '\0'; + + ptr = malloc(strlen(filename)+strlen(ERLANG_MACHINE)+1); + strcpy(ptr,filename); + strcat(ptr,ERLANG_MACHINE); + + new[StopAction].data.bytes = ""; + new[OnFail].data.value = ON_FAIL_IGNORE; + new[Machine].data.bytes = ptr; + new[Machine].data.expand.unexpanded = ptr; + new[Env].data.bytes = "\0"; + new[WorkDir].data.bytes = new[WorkDir].data.expand.unexpanded = + ""; + new[Priority].data.value = NORMAL_PRIORITY_CLASS; + new[SName].data.bytes = service_name; + new[Name].data.bytes = ""; + new[Args].data.bytes = new[Args].data.expand.unexpanded = ""; + new[DebugType].data.value = DEBUG_TYPE_NO_DEBUG; + new[InternalServiceName].data.bytes = real_service_name; + new[Comment].data.bytes = ""; + return TRUE; +} + +int do_usage(char *arg0){ + printf("Usage:\n"); + printf("%s {set | add} <servicename>\n" + "\t[-st[opaction] [<erlang shell command>]]\n" + "\t[-on[fail] [{reboot | restart | restart_always}]]\n" + "\t[-m[achine] [<erl-command>]]\n" + "\t[-e[nv] [<variable>[=<value>]]]\n" + "\t[-w[orkdir] [<directory>]]\n" + "\t[-p[riority] [{low|high|realtime}]]\n" + "\t[{-sn[ame] | -n[ame]} [<nodename>]]\n" + "\t[-d[ebugtype] [{new|reuse|console}]]\n" + "\t[-ar[gs] [<limited erl arguments>]]\n\n" + "%s {start | stop | disable | enable} <servicename>\n\n" + "%s remove <servicename>\n\n" + "%s rename <servicename> <servicename>\n\n" + "%s list [<servicename>]\n\n" + "%s help\n\n", + arg0,arg0,arg0,arg0,arg0,arg0); + printf("Manipulates Erlang system services on Windows NT.\n\n"); + printf("When no parameter to an option is specified, the option\n" + "is reset to it's default value. To set an empty argument\n" + "list, give option -args as last option on command line " + "with\n" + "no arguments.\n\n"); + printf("Se Erlang documentation for full description.\n"); + return 0; +} + +int do_manage(int argc,char **argv){ + char *action = argv[1]; + RegEntry *current = empty_reg_tab(); + + if(argc < 3){ + fprintf(stderr,"%s: No servicename given!\n",argv[0]); + do_usage(argv[0]); + return 1; + } + service_name = argv[2]; + if(!fetch_current(current)){ + fprintf(stderr,"%s: The service %s is not an erlsrv controlled service.\n", + argv[0],service_name); + return 1; + } + real_service_name = _strdup(current[InternalServiceName].data.bytes); + free_keys(current); + + if(!_stricmp(action,"start")){ + if(!start_service()){ + fprintf(stderr,"%s: Failed to start service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } else { + if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING, + SERVICE_RUNNING, 60)){ + fprintf(stderr,"%s: Failed to start service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } + printf("%s: Service %s started.\n", + argv[0],service_name); + return 0; + } + } + if(!_stricmp(action,"stop")){ + if(!stop_service()){ + fprintf(stderr,"%s: Failed to stop service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } else { + if(!wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING, + SERVICE_STOPPED, 60)){ + fprintf(stderr,"%s: Failed to stop service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } + printf("%s: Service %s stopped.\n", + argv[0],service_name); + return 0; + } + } + if(!_stricmp(action,"disable")){ +#if 0 + if(stop_service()){ + printf("%s: Service %s stopped.\n", + argv[0],service_name); + } +#endif + if(!disable_service()){ + fprintf(stderr,"%s: Failed to disable service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } else { + printf("%s: Service %s disabled.\n", + argv[0],service_name); + return 0; + } + } + if(!_stricmp(action,"enable")){ + if(!enable_service()){ + fprintf(stderr,"%s: Failed to enable service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } else { + printf("%s: Service %s enabled.\n", + argv[0],service_name); + return 0; + } + } + fprintf(stderr,"%s: Unrecignized argument %s.\n", + argv[0],action); + return 1; +} + +int do_add_or_set(int argc, char **argv){ + RegEntry *new_entries; + RegEntry *default_entries; + int add = 0; + int i; + int current; + int set_comment = 0; + new_entries = empty_reg_tab(); + default_entries = empty_reg_tab(); + if(argc < 3){ + fprintf(stderr,"%s: No servicename given!\n",argv[0]); + do_usage(argv[0]); + return 1; + } + service_name = argv[2]; + if(!_stricmp(argv[1],"add")){ + if(fetch_current(default_entries)){ + fprintf(stderr,"%s: A service with the name %s already " + "exists.\n", + argv[0],service_name); + return 1; + } + real_service_name = generate_real_service_name(service_name); + if(!fill_in_defaults(new_entries)){ + fprintf(stderr,"%s: Internal error.\n", argv[0]); + return 1; + } + add = 1; + } else { + if(!fetch_current(new_entries)){ + fprintf(stderr,"%s: No service with the name %s exists.\n", + argv[0], service_name); + return 1; + } + real_service_name = new_entries[InternalServiceName].data.bytes; + } + + if(!fill_in_defaults(default_entries)){ + fprintf(stderr,"%s: Internal error.\n", argv[0]); + return 1; + } + + /* make sure env is malloced... */ + new_entries[Env].data.bytes = envdup(new_entries[Env].data.bytes); + + for(i = 3; i < argc; ++i){ + switch((current = lookup_arg(argv[i]))){ + case Comment: + set_comment = 1; + case Machine: + case WorkDir: + case Args: + if(i+1 >= argc){ + new_entries[current].data.bytes = + default_entries[current].data.bytes; + new_entries[current].data.expand.unexpanded = + default_entries[current].data.expand.unexpanded; + } else { + new_entries[current].data.expand.unexpanded = + new_entries[current].data.bytes = argv[i+1]; + ++i; + } + break; + case SName: + new_entries[Name].data.bytes = ""; + case StopAction: + case Name: + if(i+1 >= argc || + *argv[i+1] == '-' || *argv[i+1] == '/'){ + new_entries[current].data.bytes = + default_entries[current].data.bytes; + } else { + new_entries[current].data.bytes = argv[i+1]; + ++i; + } + break; + case OnFail: + if(i+1 >= argc || + *argv[i+1] == '-' || *argv[i+1] == '/'){ + new_entries[current].data.value = + default_entries[current].data.value; + } else { + if(!_stricmp(argv[i+1],"reboot")) + new_entries[current].data.value = ON_FAIL_REBOOT; + else if(!_stricmp(argv[i+1],"restart")) + new_entries[current].data.value = ON_FAIL_RESTART; + else if(!_stricmp(argv[i+1],"restart_always")) + new_entries[current].data.value = ON_FAIL_RESTART_ALWAYS; + else { + fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + argv[0],argv[i+1]); + return 1; + } + ++i; + } + break; + case DebugType: + if(i+1 >= argc || + *argv[i+1] == '-' || *argv[i+1] == '/'){ + new_entries[current].data.value = + default_entries[current].data.value; + } else { + if(!_stricmp(argv[i+1],"new")) + new_entries[current].data.value = DEBUG_TYPE_NEW; + else if(!_stricmp(argv[i+1],"reuse")) + new_entries[current].data.value = DEBUG_TYPE_REUSE; + else if(!_stricmp(argv[i+1],"console")) + new_entries[current].data.value = DEBUG_TYPE_CONSOLE; + else { + fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + argv[0],argv[i+1]); + return 1; + } + ++i; + } + break; + case Priority: + if(i+1 >= argc || + *argv[i+1] == '-' || *argv[i+1] == '/'){ + new_entries[current].data.value = + default_entries[current].data.value; + } else { + if(!_stricmp(argv[i+1],"high")) + new_entries[current].data.value = HIGH_PRIORITY_CLASS; + else if(!_stricmp(argv[i+1],"low")) + new_entries[current].data.value = IDLE_PRIORITY_CLASS; + else if(!_stricmp(argv[i+1],"realtime")) + new_entries[current].data.value = REALTIME_PRIORITY_CLASS; + else { + fprintf(stderr,"%s: Unrecognized keyword value %s.\n", + argv[0],argv[i+1]); + return 1; + } + ++i; + } + break; + + case Env: + if(i+1 >= argc || + *argv[i+1] == '-' || *argv[i+1] == '/'){ + fprintf(stderr,"%s: %s requires a parameter.\n", + argv[0],argv[i]); + return 1; + } + new_entries[current].data.bytes = + edit_env(argv[i+1], + new_entries[current].data.bytes); + ++i; + break; + case InternalServiceName: + if (!add) { + fprintf(stderr,"%s: %s only allowed when adding a new service.\n", + argv[0],argv[i]); + return 1; + } + if(i+1 >= argc){ + fprintf(stderr,"%s: %s requires a parameter.\n", + argv[0],argv[i]); + return 1; + } + new_entries[InternalServiceName].data.expand.unexpanded = + new_entries[InternalServiceName].data.bytes = argv[i+1]; + ++i; + /* Discard old, should maybe be fred' but we'll exit anyway */ + real_service_name = new_entries[InternalServiceName].data.bytes; + break; + default: + fprintf(stderr,"%s: Unrecognized option %s.\n", argv[0], + argv[i]); + return 1; + } + } + if(*new_entries[SName].data.bytes && + *new_entries[Name].data.bytes){ +#if 0 + fprintf(stderr,"%s: Both -sname and -name specified.\n", + argv[0]); + return 1; +#else + new_entries[SName].data.bytes = ""; +#endif + } + if(add && !(*new_entries[SName].data.bytes) && + !(*new_entries[Name].data.bytes)){ + fprintf(stderr,"%s: Neither -sname nor -name specified.\n", + argv[0]); + return 1; + } + if(add && !install_service()){ + fprintf(stderr,"%s: Unable to register service with service manager.\n", + argv[0], service_name); + print_last_error(); + return 1; + } + if(!set_interactive(new_entries[DebugType].data.value == + DEBUG_TYPE_CONSOLE)){ + fprintf(stderr,"%s: Warning, could not set correct interactive mode.\n", + argv[0], service_name); + print_last_error(); + /* Not severe or??? */ + } + /* Update registry */ + register_logkeys(); + set_keys(service_name, new_entries); + /* Update service comment if needed */ + if(set_comment) { + if (!set_service_comment(new_entries[Comment].data.bytes)) { + fprintf(stderr,"%s: Warning, could not set correct " + "service description (comment)", + argv[0], service_name); + print_last_error(); + } + } + + /* As I do this, I should also clean up the new entries, which is + somewhat harder as I really dont know what is and what is not + malloced, but we'll exit anyway, so... */ + cleanup_old(); + if(add) + printf("%s: Service %s added to system.\n", + argv[0], service_name); + else + printf("%s: Service %s updated.\n", + argv[0], service_name); + return 0; +} +int do_rename(int argc, char **argv){ + RegEntry *current = empty_reg_tab(); + RegEntry *dummy = empty_reg_tab(); + SC_HANDLE scm; + SC_HANDLE service; + if(argc < 3){ + fprintf(stderr,"%s: No old servicename given!\n",argv[0]); + do_usage(argv[0]); + return 1; + } + if(argc < 4){ + fprintf(stderr,"%s: No new servicename given!\n",argv[0]); + do_usage(argv[0]); + return 1; + } + service_name = argv[3]; + if(fetch_current(dummy)){ + fprintf(stderr,"%s: A service with the name %s already " + "exists.\n", + argv[0],service_name); + return 1; + } + service_name = argv[2]; + + if(!fetch_current(current)){ + fprintf(stderr,"%s: Error, old service name %s does not exist.\n", + argv[0],service_name); + return 1; + } + real_service_name = _strdup(current[InternalServiceName].data.bytes); + if(!open_service_config(&scm,&service)){ + fprintf(stderr,"%s: Error, unable to communicate with service control" + " manager.\n", + argv[0]); + print_last_error(); + return 1; + } + if(!ChangeServiceConfig(service, + SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + argv[3])){ + fprintf(stderr,"%s: Error, unable to communicate with service control" + " manager.\n", + argv[0]); + print_last_error(); + CloseServiceHandle(scm); + CloseServiceHandle(service); + return 1; + } + CloseServiceHandle(scm); + CloseServiceHandle(service); + + if(remove_keys(service_name) != 0) + fprintf(stderr,"%s: Warning, old service parameter keys could not " + "be removed, continuing.\n", argv[0]); + /* Update registry */ + register_logkeys(); + set_keys(argv[3], current); + + printf("%s: Service %s renamed to %s.\n", + argv[0], service_name, argv[3]); + return 0; +} + +int do_remove(int argc, char **argv){ + RegEntry *current = empty_reg_tab(); + int rem_res; + BOOL found; + + if(argc < 3){ + fprintf(stderr,"%s: No servicename given!\n",argv[0]); + do_usage(argv[0]); + return 1; + } + service_name = argv[2]; + found = fetch_current(current); + if(found){ + real_service_name = _strdup(current[InternalServiceName].data.bytes); + } else { + real_service_name = _strdup(service_name); + } + if(found) + free_keys(current); + if(stop_service() && !wait_service_trans(SERVICE_RUNNING, + SERVICE_STOP_PENDING, + SERVICE_STOPPED, 60)){ + fprintf(stderr,"%s: Failed to stop running service %s.\n", + argv[0],service_name); + print_last_error(); + return 1; + } + if(!remove_service()){ + fprintf(stderr,"%s: Unable to remove service (not enough " + "privileges?)\n",argv[0]); + print_last_error(); + return 1; + } + + if((rem_res = remove_keys(service_name)) > 0){ + fprintf(stderr,"%s: Warning, service parameter keys belonged to old " + "erlsrv version.\n", argv[0]); + /* Backward compatibility... */ + } else if(rem_res < 0) { + fprintf(stderr,"%s: Error, service parameter keys nonexistent.\n", + argv[0]); + return 1; + } + printf("%s: Service %s removed from system.\n", + argv[0], service_name); + return 0; +} + +BOOL list_one(char *servicename, RegEntry *keys, BOOL longlist){ + char *onfail; + char *prio; + char *debugtype; + switch(keys[OnFail].data.value){ + case ON_FAIL_RESTART: + onfail = "restart"; + break; + case ON_FAIL_RESTART_ALWAYS: + onfail = "restart_always"; + break; + case ON_FAIL_REBOOT: + onfail = "reboot"; + break; + default: + onfail = "ignore"; + } + switch(keys[DebugType].data.value){ + case DEBUG_TYPE_NEW: + debugtype = "new"; + break; + case DEBUG_TYPE_REUSE: + debugtype = "reuse"; + break; + case DEBUG_TYPE_CONSOLE: + debugtype = "console"; + break; + default: + debugtype = "none"; + } + switch(keys[Priority].data.value){ + case HIGH_PRIORITY_CLASS: + prio = "high"; + break; + case IDLE_PRIORITY_CLASS: + prio = "low"; + break; + case REALTIME_PRIORITY_CLASS: + prio = "realtime"; + break; + case NORMAL_PRIORITY_CLASS: + prio = "default"; + break; + default: + prio = "unknown/faulty"; + } + + + if(longlist){ + char *env = envdup(keys[Env].data.bytes); + char **arg = env_to_arg(env); + char **pek = arg; + printf("Service name: %s\n", + servicename); + printf("StopAction: %s\n", + keys[StopAction].data.bytes); + printf("OnFail: %s\n",onfail); + printf("Machine: %s\n", + keys[Machine].data.expand.unexpanded); + printf("WorkDir: %s\n", + keys[WorkDir].data.expand.unexpanded); + if(*keys[SName].data.bytes) + printf("SName: %s\n", + keys[SName].data.bytes); + else + printf("Name: %s\n", + keys[Name].data.bytes); + printf("Priority: %s\n",prio); + printf("DebugType: %s\n",debugtype); + printf("Args: %s\n", + keys[Args].data.expand.unexpanded); + printf("InternalServiceName: %s\n", + keys[InternalServiceName].data.bytes); + printf("Comment: %s\n", + keys[Comment].data.bytes); + printf("Env:\n"); + while(*pek){ + printf("\t%s\n",*pek); + ++pek; + } + /* env is easier to free...*/ + env = arg_to_env(arg); + free(env); + } else { + printf("%s\t%s\t%s\t%s\t%s\n", + servicename, + (*keys[Name].data.bytes) ? + keys[Name].data.bytes : + keys[SName].data.bytes, + prio, + onfail, + keys[Args].data.expand.unexpanded); + } + return TRUE; +} + + +int do_list(int argc, char **argv){ + if(argc < 3){ + RegEntryDesc *all_keys = get_all_keys(); + if(!all_keys){ + fprintf(stderr,"%s: No services found in registry.\n", + argv[0]); + return 0; + } + printf("Service\t(S)Name\tPrio\tOnFail\tArgs\n"); + while(all_keys->servicename){ + list_one(all_keys->servicename,all_keys->entries,FALSE); + ++all_keys; + } + return 0; + } else { + RegEntry *keys; + service_name = argv[2]; + keys = get_keys(service_name); + if(!keys){ + fprintf(stderr,"%s: Could not retrieve any " + "registered data for %s.\n",argv[0],service_name); + return 1; + } + list_one(service_name, keys, TRUE); + } + return 0; +} + +#define READ_CHUNK 100 +#define ARGV_CHUNK 20 + +char *safe_get_line(void){ + int lsize = READ_CHUNK; + char *line = malloc(READ_CHUNK); + int pos = 0; + int ch; + + while((ch = getchar()) != EOF && ch != '\n'){ + if(pos + 1 >= lsize){ + line = realloc(line,(lsize += READ_CHUNK)); + assert(line); + } + line[pos++] = ch; + } + if(ch == EOF || !pos){ + free(line); + return NULL; + } + line[pos] = '\0'; + return line; +} + + +void read_arguments(int *pargc, char ***pargv){ + int argc = 0; + int asize = ARGV_CHUNK; + char **argv = malloc(ARGV_CHUNK*sizeof(char *)); + char *tmp; + + argv[0] = (*pargv)[0]; + argc = 1; + while((tmp = safe_get_line()) != NULL){ + if(argc + 1 >= asize){ + argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(char *)); + assert(argv); + } + argv[argc++] = tmp; + } + argv[argc] = NULL; + *pargc = argc; + *pargv = argv; +} + + +int interactive_main(int argc, char **argv){ + char *action = argv[1]; + + if(!_stricmp(action,"readargs")){ + read_arguments(&argc,&argv); + action = argv[1]; + } + if(!_stricmp(action,"set") || !_stricmp(action,"add")) + return do_add_or_set(argc,argv); + if(!_stricmp(action,"rename")) + return do_rename(argc,argv); + if(!_stricmp(action,"remove")) + return do_remove(argc,argv); + if(!_stricmp(action,"list")) + return do_list(argc,argv); + if(!_stricmp(action,"start") || + !_stricmp(action,"stop") || + !_stricmp(action,"enable") || + !_stricmp(action,"disable")) + return do_manage(argc,argv); + if(_stricmp(action,"?") && + _stricmp(action,"/?") && + _stricmp(action,"-?") && + *action != 'h' && + *action != 'H') + fprintf(stderr,"%s: action %s not implemented.\n",argv[0],action); + do_usage(argv[0]); + return 1; +} + diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h new file mode 100644 index 0000000000..deacf81899 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h @@ -0,0 +1,24 @@ +/* + * %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% + */ +#ifndef _ERLSRV_INTERACTIVE_H +#define _ERLSRV_INTERACTIVE_H + +int interactive_main(int argc, char **argv); + +#endif /* _ERLSRV_INTERACTIVE_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_logmess.mc b/erts/etc/win32/erlsrv/erlsrv_logmess.mc new file mode 100644 index 0000000000..354ac14c9f --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_logmess.mc @@ -0,0 +1,33 @@ +;/*MessageIDTypedef=WORD*/ +; +;/*MessageID=0x1*/ +;/*SymbolicName=CAT_GENERIC*/ +;/*Language=English*/ +;/*Generic Category*/ +;/*.*/ +; +MessageIDTypedef=DWORD + +MessageID=0x10 +Severity=Warning +Facility=Application +SymbolicName=MSG_WARNING +Language=English +%1: %2 +. +MessageID=0x11 +Severity=Error +Facility=Application +SymbolicName=MSG_ERROR +Language=English +%1: %2 +. +MessageID=0x12 +Severity=Informational +Facility=Application +SymbolicName=MSG_INFO +Language=English +%1: %2 +. + + diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c new file mode 100644 index 0000000000..920a4a1827 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_main.c @@ -0,0 +1,44 @@ +/* + * %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% + */ +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "erlsrv_global.h" +#include "erlsrv_interactive.h" +#include "erlsrv_service.h" + +int main(int argc, char **argv){ + if(argc > 1) + return interactive_main(argc,argv); + else + return service_main(argc,argv); +} + + + + + + + + + + + diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c new file mode 100644 index 0000000000..c1aa9f2b67 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_registry.c @@ -0,0 +1,404 @@ +/* + * %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% + */ +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "erlsrv_global.h" +#include "erlsrv_registry.h" + +#define LOG_TYPE "System" +#define LOG_ROOT \ +"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" LOG_TYPE "\\" +#define LOG_APP_KEY APP_NAME + + +#define BASE_KEY HKEY_LOCAL_MACHINE +#define PRODUCT_NAME APP_NAME +#define OLD_PRODUCT_VERSION "1.0" +#define PRODUCT_VERSION "1.1" +#define PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" PRODUCT_VERSION +#define OLD_PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" OLD_PRODUCT_VERSION + +#define MAX_KEY_LEN MAX_PATH + +static const char * const noString = "\0"; + +#define MAX_MANDATORY_REG_ENTRY 10 /* InternalServiceName == reg_entries[10] */ +static RegEntry reg_entries[] = { + {"StopAction",REG_SZ,NULL}, + {"OnFail",REG_DWORD,NULL}, + {"Machine",REG_EXPAND_SZ,NULL}, + {"Env", REG_MULTI_SZ,NULL}, + {"WorkDir", REG_EXPAND_SZ,NULL}, + {"Priority",REG_DWORD,NULL}, + {"SName",REG_SZ,NULL}, + {"Name",REG_SZ,NULL}, + {"Args",REG_EXPAND_SZ,NULL}, + {"DebugType",REG_DWORD,NULL}, + {"InternalServiceName",REG_SZ,NULL}, + /* Non mandatory follows */ + {"Comment",REG_SZ,NULL} +}; + + +int num_reg_entries = sizeof(reg_entries)/sizeof(RegEntry); + +RegEntry *empty_reg_tab(void){ + RegEntry *ret = malloc(num_reg_entries * sizeof(RegEntry)); + memcpy(ret,reg_entries,num_reg_entries * sizeof(RegEntry)); + return ret; +} + +void free_keys(RegEntry *keys){ + int i; + + for(i=0;i<num_reg_entries && keys[i].name != NULL;++i){ + if((keys[i].type == REG_SZ || keys[i].type == REG_EXPAND_SZ || + keys[i].type == REG_MULTI_SZ) && + keys[i].data.bytes != noString){ + free(keys[i].data.bytes); + if(keys[i].type == REG_EXPAND_SZ && + keys[i].data.expand.unexpanded != noString) + free(keys[i].data.expand.unexpanded); + } + } + free(keys); +} + +void free_all_keys(RegEntryDesc *descs){ + RegEntryDesc *tmp = descs; + for(;tmp->servicename != NULL; ++tmp){ + free_keys(tmp->entries); + free(tmp->servicename); + } + free(descs); +} + +RegEntry *get_keys(char *servicename){ + RegEntry *res = NULL; + HKEY prog_key; + int key_opened = 0; + int i; + DWORD ret; + char *copy; + char *tmpbuf; + DWORD tmpbuflen; + + char key_to_open[MAX_KEY_LEN]; + + DWORD val_type; + char *val_data = malloc(MAX_KEY_LEN); + DWORD val_datalen; + DWORD val_datasiz = MAX_KEY_LEN; + + if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN) + goto error; + sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename); + + if(RegOpenKeyEx(BASE_KEY, + key_to_open, + 0, + KEY_QUERY_VALUE, + &prog_key) != ERROR_SUCCESS) + goto error; + key_opened = 1; + + res = malloc(num_reg_entries*sizeof(RegEntry)); + for(i=0;i<num_reg_entries;++i) + res[i].name = NULL; + + for(i=0;i<num_reg_entries;++i){ + for(;;){ + val_datalen = val_datasiz; + ret = RegQueryValueEx(prog_key, + reg_entries[i].name, + NULL, + &val_type, + (BYTE *) val_data, + &val_datalen); + if(ret == ERROR_SUCCESS){ + if(reg_entries[i].type == val_type) + break; + else + goto error; + } else if(ret == ERROR_MORE_DATA){ + val_data = realloc(val_data,val_datasiz = val_datalen); + } else if (i > MAX_MANDATORY_REG_ENTRY && ret == ERROR_FILE_NOT_FOUND) { + /* Non mandatory entries, look at the type... */ + switch (reg_entries[i].type){ + case REG_EXPAND_SZ: + case REG_SZ: + case REG_MULTI_SZ: + val_datalen = 0; + break; + case REG_DWORD: + { + DWORD dummy = 0; + memcpy(val_data,&dummy,(val_datalen = sizeof(DWORD))); + } + break; + default: + goto error; + } + break; /* for(;;) */ + } else { + goto error; + } + } + res[i] = reg_entries[i]; + copy = NULL; + switch(reg_entries[i].type){ + case REG_EXPAND_SZ: + if(!val_datalen || val_data[0] == '\0'){ + copy = (char *) noString; + res[i].data.expand.unexpanded = (char *) noString; + } else { + tmpbuf = malloc(MAX_KEY_LEN); + tmpbuflen = (DWORD) MAX_KEY_LEN; + for(;;){ + ret = ExpandEnvironmentStrings(val_data,tmpbuf,tmpbuflen); + if(!ret){ + free(tmpbuf); + goto error; + }else if(ret > tmpbuflen){ + tmpbuf=realloc(tmpbuf,tmpbuflen=ret); + } else { + copy = strdup(tmpbuf); + free(tmpbuf); + break; + } + } + res[i].data.expand.unexpanded = strdup(val_data); + } + case REG_MULTI_SZ: + case REG_SZ: + if(!copy){ + if(!val_datalen || + ((val_datalen == 1 && val_data[0] == '\0') || + (val_datalen == 2 && val_data[0] == '\0' && + val_data[1] == '\0'))){ + copy = (char *) noString; + } else { + copy = malloc(val_datalen); + memcpy(copy,val_data,val_datalen); + } + } + res[i].data.bytes = copy; + break; + case REG_DWORD: + memcpy(&res[i].data.value,val_data,sizeof(DWORD)); + break; + default: + goto error; + } + } + RegCloseKey(prog_key); + free(val_data); + return res; +error: + free(val_data); + if(res != NULL) + free_keys(res); + if(key_opened) + RegCloseKey(prog_key); + return NULL; +} + +int set_keys(char *servicename, RegEntry *keys){ + HKEY prog_key; + int key_opened = 0; + int i; + char key_to_open[MAX_KEY_LEN]; + DWORD disposition; + + if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN) + goto error; + sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename); + + if(RegOpenKeyEx(BASE_KEY, + key_to_open, + 0, + KEY_SET_VALUE, + &prog_key) != ERROR_SUCCESS){ + if(RegCreateKeyEx(BASE_KEY, + key_to_open, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, + &prog_key, + &disposition) != ERROR_SUCCESS) + goto error; + } + key_opened = 1; + + + for(i=0;i<num_reg_entries;++i){ + void *ptr; + DWORD siz; + int j; + switch(keys[i].type){ + case REG_SZ: + ptr = keys[i].data.bytes; + siz = strlen(ptr)+1; + break; + case REG_EXPAND_SZ: + ptr = keys[i].data.expand.unexpanded; + siz = strlen(ptr)+1; + break; + case REG_MULTI_SZ: + ptr = keys[i].data.bytes; + for(j=0;!(((char *)ptr)[j] == '\0' && + ((char *)ptr)[j+1] == '\0');++j) + ; + siz=(DWORD)j+2; + break; + case REG_DWORD: + ptr = &keys[i].data.value; + siz = sizeof(DWORD); + break; + default: + goto error; + } +#ifdef HARDDEBUG + fprintf(stderr,"%s %s:%d\n",keys[i].name, + (keys[i].type == REG_DWORD) ? "(dword)" : ptr,siz); +#endif + if(RegSetValueEx(prog_key, + keys[i].name, + 0, + keys[i].type, + ptr, + siz) != ERROR_SUCCESS) + goto error; + } + RegCloseKey(prog_key); + return 0; +error: + if(key_opened) + RegCloseKey(prog_key); + return 1; +} + +static int do_remove_keys(char *servicename, const char *prog_key_name){ + HKEY prog_key; + if(RegOpenKeyEx(BASE_KEY, + prog_key_name, + 0, + KEY_ALL_ACCESS, + &prog_key) != ERROR_SUCCESS) + return -1; + if(RegDeleteKey(prog_key,servicename) != ERROR_SUCCESS){ + RegCloseKey(prog_key); + return -1; + } + RegCloseKey(prog_key); + return 0; +} + +int remove_keys(char *servicename){ + int ret; + + if((ret = do_remove_keys(servicename, PROG_KEY)) < 0){ + if(!do_remove_keys(servicename, OLD_PROG_KEY)) + return 1; + else + return -1; + } + return ret; +} + + +RegEntryDesc *get_all_keys(void){ + RegEntryDesc *res = malloc(10*sizeof(RegEntryDesc)); + int res_siz = 10; + int ndx = 0; + HKEY prog_key; + int key_opened = 0; + DWORD enum_index; + char name[MAX_KEY_LEN]; + DWORD namelen; + char class[MAX_KEY_LEN]; + DWORD classlen; + FILETIME ft; + + res[ndx].servicename = NULL; + if(RegOpenKeyEx(BASE_KEY, PROG_KEY, 0, + KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, + &prog_key) != ERROR_SUCCESS) + goto error; + key_opened = 1; + for(enum_index = 0, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN; + ERROR_SUCCESS == RegEnumKeyEx(prog_key, + enum_index, + name, + &namelen, + NULL, + class, + &classlen, + &ft); + ++enum_index, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN){ + if(ndx >= res_siz - 1) + res = realloc(res, (res_siz += 10)*sizeof(RegEntryDesc)); + if(!(res[ndx].entries = get_keys(name))) + goto error; + res[ndx].servicename = strdup(name); + res[++ndx].servicename = NULL; + } + RegCloseKey(prog_key); + return res; +error: + if(key_opened) + RegCloseKey(prog_key); + free_all_keys(res); + return NULL; +} + +int register_logkeys(void){ + HKEY key; + DWORD disposition; + DWORD types = EVENTLOG_ERROR_TYPE | + EVENTLOG_WARNING_TYPE | + EVENTLOG_INFORMATION_TYPE; + DWORD catcount=1; + char filename[2048]; + DWORD fnsiz=2048; + + if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, + LOG_ROOT LOG_APP_KEY, 0, + NULL, REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, NULL, + &key, &disposition) != ERROR_SUCCESS) + return -1; + if(!GetModuleFileName(NULL, filename, fnsiz)) + return -1; + if(RegSetValueEx(key, "EventMessageFile", + 0, REG_EXPAND_SZ, (LPBYTE) filename, + strlen(filename)+1) != ERROR_SUCCESS) + return -1; + if(RegSetValueEx(key, "TypesSupported", + 0, REG_DWORD, (LPBYTE) &types, + sizeof(DWORD)) != ERROR_SUCCESS) + return -1; + return 0; +} + diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h new file mode 100644 index 0000000000..fbccc5416a --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_registry.h @@ -0,0 +1,76 @@ +/* + * %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% + */ +#ifndef _ERLSRV_REGISTRY_H +#define _ERLSRV_REGISTRY_H + +typedef struct _reg_entry { + char *name; + DWORD type; + union { + char *bytes; + DWORD value; + struct { + char *bytes; + char *unexpanded; + } expand; + } data; +} RegEntry; + +typedef struct _reg_entry_desc { + char *servicename; + RegEntry *entries; +} RegEntryDesc; + +enum { + StopAction, + OnFail, + Machine, + Env, + WorkDir, + Priority, + SName, + Name, + Args, + DebugType, + InternalServiceName, + Comment +}; + +#define ON_FAIL_IGNORE 0 +#define ON_FAIL_RESTART 1 +#define ON_FAIL_REBOOT 2 +#define ON_FAIL_RESTART_ALWAYS 3 + +#define DEBUG_TYPE_NO_DEBUG 0 +#define DEBUG_TYPE_NEW 1 +#define DEBUG_TYPE_REUSE 2 +#define DEBUG_TYPE_CONSOLE 3 + +extern int num_reg_entries; + +RegEntry *empty_reg_tab(void); +void free_keys(RegEntry *keys); +void free_all_keys(RegEntryDesc *descs); +RegEntry *get_keys(char *servicename); +int set_keys(char *servicename, RegEntry *keys); +RegEntryDesc *get_all_keys(void); +int remove_keys(char *servicename); +int register_logkeys(void); +#endif /* _ERLSRV_REGISTRY_H */ + diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c new file mode 100644 index 0000000000..a58ee862c5 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_service.c @@ -0,0 +1,966 @@ +/* + * %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% + */ +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "erlsrv_global.h" +#include "erlsrv_registry.h" +#include "erlsrv_util.h" +#include "erlsrv_service.h" + +static HANDLE eventStop; + +static HANDLE eventKillErlang; + +static CRITICAL_SECTION crit; + +static SERVICE_STATUS_HANDLE statusHandle; + +static DWORD currentState; + +static void fill_status(SERVICE_STATUS *status){ + status->dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status->dwCurrentState = 0; + status->dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + status->dwWin32ExitCode = NO_ERROR; + status->dwServiceSpecificExitCode = 0; + status->dwCheckPoint = 0; + status->dwWaitHint = 0; +} + +static BOOL set_start_pending(int waithint, int checkpoint){ + SERVICE_STATUS stat; + fill_status(&stat); + EnterCriticalSection(&crit); + currentState = stat.dwCurrentState = SERVICE_START_PENDING; + LeaveCriticalSection(&crit); + stat.dwControlsAccepted = 0; + stat.dwCheckPoint = checkpoint; + stat.dwWaitHint = waithint; + return SetServiceStatus(statusHandle, &stat); +} + +static BOOL set_stop_pending(int waithint, int checkpoint){ + SERVICE_STATUS stat; + fill_status(&stat); + EnterCriticalSection(&crit); + currentState = stat.dwCurrentState = SERVICE_STOP_PENDING; + LeaveCriticalSection(&crit); + stat.dwControlsAccepted = 0; + stat.dwCheckPoint = checkpoint; + stat.dwWaitHint = waithint; + return SetServiceStatus(statusHandle, &stat); +} + +static BOOL set_running(){ + SERVICE_STATUS stat; + fill_status(&stat); + EnterCriticalSection(&crit); + currentState = stat.dwCurrentState = SERVICE_RUNNING; + LeaveCriticalSection(&crit); + return SetServiceStatus(statusHandle, &stat); +} + +static BOOL set_stopped(int error){ + SERVICE_STATUS stat; + fill_status(&stat); + EnterCriticalSection(&crit); + currentState = stat.dwCurrentState = SERVICE_STOPPED; + LeaveCriticalSection(&crit); + stat.dwWin32ExitCode = error; + return SetServiceStatus(statusHandle, &stat); +} + +static BOOL reset_current(){ + SERVICE_STATUS stat; + fill_status(&stat); + EnterCriticalSection(&crit); + stat.dwCurrentState = currentState; + LeaveCriticalSection(&crit); + return SetServiceStatus(statusHandle, &stat); +} + +static VOID WINAPI handler(DWORD control){ + char buffer[1024]; + sprintf(buffer,"handler called with control = %d.",(int) control); + log_debug(buffer); + switch(control){ + case SERVICE_CONTROL_STOP: + set_stop_pending(30000,1); + SetEvent(eventStop); + return; + case SERVICE_CONTROL_SHUTDOWN: + return; + default: + reset_current(); + break; + } + return; +} + +typedef struct _server_info { + RegEntry *keys; + PROCESS_INFORMATION info; + HANDLE erl_stdin; + char *event_name; +} ServerInfo; + + +typedef struct { + BOOL initialized; + TOKEN_DEFAULT_DACL *defdacl; + PACL newacl; + PSID adminsid; +} SaveAclStruct; + + +static BOOL reset_acl(SaveAclStruct *save_acl){ + HANDLE tokenh; + + if(!save_acl->initialized) + return FALSE; + if(!OpenProcessToken(GetCurrentProcess(), + TOKEN_READ|TOKEN_WRITE,&tokenh)){ + log_warning("Failed to open access token."); + return FALSE; + } + save_acl->initialized = FALSE; + if(!SetTokenInformation(tokenh, + TokenDefaultDacl, + save_acl->defdacl, + sizeof(TOKEN_DEFAULT_DACL))){ + log_warning("Failed to get default ACL from token."); + CloseHandle(tokenh); + LocalFree(save_acl->defdacl); + LocalFree(save_acl->newacl); + FreeSid(save_acl->adminsid); + return FALSE; + } + CloseHandle(tokenh); + LocalFree(save_acl->defdacl); + LocalFree(save_acl->newacl); + FreeSid(save_acl->adminsid); + return TRUE; +} + + +static BOOL new_acl(SaveAclStruct *save_acl){ + HANDLE tokenh; + TOKEN_DEFAULT_DACL newdacl; + DWORD required; + PACL oldacl; + PACL newacl; + int i; + ACL_SIZE_INFORMATION si; + size_t newsize; + PSID extra_sid; + SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; + TOKEN_DEFAULT_DACL dummy; + + save_acl->initialized = FALSE; + if(!OpenProcessToken(GetCurrentProcess(), + TOKEN_READ|TOKEN_WRITE,&tokenh)){ + log_warning("Failed to open access token."); + return FALSE; + } + save_acl->defdacl = &dummy; + required = sizeof(TOKEN_DEFAULT_DACL); + GetTokenInformation(tokenh, + TokenDefaultDacl, + &(save_acl->defdacl), + sizeof(TOKEN_DEFAULT_DACL), + &required); + if(required == 0){ + log_warning("Failed to get any ACL info from token."); + CloseHandle(tokenh); + return FALSE; + } + save_acl->defdacl = LocalAlloc(LPTR,required); + if(!GetTokenInformation(tokenh, + TokenDefaultDacl, + save_acl->defdacl, + required, + &required)){ +#ifdef HARDDEBUG + { + char *mes; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &mes, + 0, + NULL ); + log_info(mes); + LocalFree(mes); + } +#endif + log_warning("Failed to get default ACL from token."); + CloseHandle(tokenh); + return FALSE; + } + + oldacl = save_acl->defdacl->DefaultDacl; + if(!GetAclInformation(oldacl, &si, sizeof(si), + AclSizeInformation)){ + log_warning("Failed to get size information for ACL"); + CloseHandle(tokenh); + return FALSE; + } + + if(!AllocateAndInitializeSid(&nt_auth, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, + 0, + 0, + 0, + 0, + 0, + &extra_sid)){ + log_warning("Failed to initialize administrator SID."); + CloseHandle(tokenh); + return FALSE; + } + + newsize = si.AclBytesInUse + sizeof(ACL) + + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(extra_sid); + + newacl = LocalAlloc(LPTR,newsize); + + if(!InitializeAcl(newacl, newsize, ACL_REVISION)){ + log_warning("Failed to initialize new ACL."); + LocalFree(newacl); + FreeSid(extra_sid); + CloseHandle(tokenh); + return FALSE; + } + + for(i=0;i<((int)si.AceCount);++i){ + ACE_HEADER *ace_header; + if (!GetAce (oldacl, i, &ace_header)){ + log_warning("Failed to get ACE from old ACL."); + LocalFree(newacl); + FreeSid(extra_sid); + CloseHandle(tokenh); + return FALSE; + } + if(!AddAce(newacl,ACL_REVISION,0xffffffff,ace_header, + ace_header->AceSize)){ + log_warning("Failed to set ACE in new ACL."); + LocalFree(newacl); + FreeSid(extra_sid); + CloseHandle(tokenh); + return FALSE; + } + } + if(!AddAccessAllowedAce(newacl, + ACL_REVISION2, + PROCESS_ALL_ACCESS, + extra_sid)){ + log_warning("Failed to add system ACE to new ACL."); + LocalFree(newacl); + FreeSid(extra_sid); + return FALSE; + } + + newdacl.DefaultDacl = newacl; + if(!SetTokenInformation(tokenh, + TokenDefaultDacl, + &newdacl, + sizeof(newdacl))){ + log_warning("Failed to set token information"); + LocalFree(newacl); + FreeSid(extra_sid); + CloseHandle(tokenh); + return FALSE; + } + save_acl->initialized = TRUE; + save_acl->newacl = newacl; + save_acl->adminsid = extra_sid; + CloseHandle(tokenh); + + return TRUE; +} + +static char **find_arg(char **arg, char *str){ + char *tmp; + int len; + + str = strdup(str); + if((tmp = strchr(str,'=')) == NULL) + goto fail; + tmp++; + *tmp = '\0'; + len = tmp - str; + while(*arg != NULL){ + if(!_strnicmp(*arg,str,len)){ + free(str); + return arg; + } + ++arg; + } +fail: + free(str); + return NULL; +} + +static char **merge_environment(char *current, char *add){ + char **c_arg = env_to_arg(envdup(current)); + char **a_arg = env_to_arg(envdup(add)); + char **new; + char **tmp; + int i,j; + + for(i=0;c_arg[i] != NULL;++i) + ; + for(j=0;a_arg[j] != NULL;++j) + ; + + new = malloc(sizeof(char *)*(i + j + 3)); + + for(i = 0; c_arg[i] != NULL; ++i) + new[i] = strdup(c_arg[i]); + + new[i] = NULL; + + for(j = 0; a_arg[j] != NULL; ++j){ + if((tmp = find_arg(new,a_arg[j])) != NULL){ + free(*tmp); + *tmp = strdup(a_arg[j]); + } else { + new[i++] = strdup(a_arg[j]); + new[i] = NULL; + } + } + free(arg_to_env(c_arg)); + free(arg_to_env(a_arg)); + return new; +} + + +static char *get_next_debug_file(char *prefix){ + char *buffer = malloc(strlen(prefix)+12); + int i; + for(i=1;i<100;++i){ + sprintf(buffer,"%s.%d",prefix,i); + if(GetFileAttributes(buffer) == 0xFFFFFFFF) + return buffer; + } + return NULL; +} + + + +static BOOL start_a_service(ServerInfo *srvi){ + STARTUPINFO start; + char execbuff[MAX_PATH*4]; /* FIXME: Can get overflow! */ + char namebuff[MAX_PATH]; + char errbuff[MAX_PATH*4]; /* hmmm.... */ + HANDLE write_pipe = NULL, read_pipe = NULL; + SECURITY_ATTRIBUTES pipe_security; + SECURITY_ATTRIBUTES attr; + HANDLE nul; + SaveAclStruct save_acl; + char *my_environ; + BOOL console_allocated = FALSE; + + if(!(*(srvi->keys[Env].data.bytes))){ + my_environ = NULL; + } else { + char *tmp; + char **merged = merge_environment((tmp = GetEnvironmentStrings()), + srvi->keys[Env].data.bytes); + FreeEnvironmentStrings(tmp); + my_environ = arg_to_env(merged); + } + + if(!*(srvi->keys[Machine].data.bytes) || + (!*(srvi->keys[SName].data.bytes) && + !*(srvi->keys[Name].data.bytes))){ + log_error("Not enough parameters for erlang service."); + if(my_environ) + free(my_environ); + return FALSE; + } + + if(*(srvi->keys[SName].data.bytes)) + sprintf(namebuff,"-nohup -sname %s",srvi->keys[SName].data.bytes); + else + sprintf(namebuff,"-nohup -name %s",srvi->keys[Name].data.bytes); + + if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) + strcat(namebuff," -keep_window"); + + if (srvi->event_name != NULL) { + sprintf(execbuff,"\"%s\" -service_event %s %s %s", + srvi->keys[Machine].data.bytes, + srvi->event_name, + namebuff, + srvi->keys[Args].data.bytes); + } else { + sprintf(execbuff,"\"%s\" %s %s", + srvi->keys[Machine].data.bytes, + namebuff, + srvi->keys[Args].data.bytes); + } + + memset (&start, 0, sizeof (start)); + start.cb = sizeof (start); + start.dwFlags = STARTF_USESHOWWINDOW; + start.wShowWindow = SW_HIDE; + + /* Console debugging implies no working StopAction */ + if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) { + COORD coord = {80,999}; + if(console_allocated = AllocConsole()) + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coord); + else + log_warning("Unable to allocate debugging console!"); + } else if(*(srvi->keys[StopAction].data.bytes) || + srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){ + pipe_security.nLength = sizeof(pipe_security); + pipe_security.lpSecurityDescriptor = NULL; + pipe_security.bInheritHandle = TRUE; + if(!CreatePipe(&read_pipe,&write_pipe,&pipe_security,0)){ + log_error("Could not create pipe for erlang service."); + if(my_environ) + free(my_environ); + return FALSE; + } + if(srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){ + char *filename; + if(*(srvi->keys[WorkDir].data.bytes)){ + filename = malloc(strlen(srvi->keys[WorkDir].data.bytes) + 1 + + strlen(service_name)+strlen(".debug")+1); + sprintf(filename,"%s\\%s.debug", + srvi->keys[WorkDir].data.bytes, + service_name); + } else { + filename = malloc(strlen(service_name)+strlen(".debug")+1); + sprintf(filename,"%s.debug",service_name); + } + log_debug(filename); + + if(srvi->keys[DebugType].data.value == DEBUG_TYPE_NEW){ + char *tmpfn = get_next_debug_file(filename); + if(tmpfn){ + free(filename); + filename = tmpfn; + } else { + log_warning("Number of debug files exceeds system defined " + "limit, reverting to DebugType: reuse. "); + } + } + + + nul = CreateFile(filename, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &pipe_security, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + free(filename); + } else { /* Not debugging */ + nul = CreateFile("NUL", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &pipe_security, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } + if(nul == NULL){ + log_error((srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG) + ? "Could not create debug file. " + "(Working directory not valid?)" + : "Cold not open NUL!"); + start.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + start.hStdError = GetStdHandle(STD_ERROR_HANDLE); + } + start.hStdOutput = nul; + start.hStdError = nul; + start.hStdInput = read_pipe; + start.dwFlags |= STARTF_USESTDHANDLES; + } + + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = TRUE; + + new_acl(&save_acl); + + if(!CreateProcess(NULL, + execbuff, + &attr, + NULL, + (read_pipe != NULL), + CREATE_DEFAULT_ERROR_MODE | + (srvi->keys[Priority].data.value), + my_environ, + (*(srvi->keys[WorkDir].data.bytes)) ? + srvi->keys[WorkDir].data.bytes : NULL, + &start, + &(srvi->info))){ + sprintf(errbuff,"Could not start erlang service" + "with commandline \"%s\".", + service_name, + execbuff + ); + log_error(errbuff); + if(read_pipe != NULL){ + CloseHandle(read_pipe); + CloseHandle(write_pipe); + if(nul != NULL) + CloseHandle(nul); + } + if(console_allocated) + FreeConsole(); + reset_acl(&save_acl); + if(my_environ) + free(my_environ); + return FALSE; + } + if(console_allocated) + FreeConsole(); +#ifdef HARDDEBUG + sprintf(errbuff, + "Started %s with the following commandline: " + "%s",service_name,execbuff); + log_debug(errbuff); +#endif + if(read_pipe != NULL){ + CloseHandle(read_pipe); + if(nul != NULL) + CloseHandle(nul); + srvi->erl_stdin = write_pipe; + } + + reset_acl(&save_acl); + if(my_environ) + free(my_environ); + return TRUE; +} + +static HANDLE create_erlang_event(char *event_name) +{ + HANDLE e; + if ((e = OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) { + if ((e = CreateEvent(NULL, TRUE, FALSE, event_name)) == NULL) { + log_warning("Could not create or access erlang termination event"); + } + } else { + if (!ResetEvent(e)) { + log_warning("Could not reset erlang termination event."); + } + } + return e; +} + +static BOOL stop_erlang(ServerInfo *srvi, int waithint, + int *checkpoint){ + DWORD written = 0; + char *action = srvi->keys[StopAction].data.bytes; + DWORD towrite = strlen(action)+1; + char *toerl; + DWORD exitcode; + int i; + int kill; + + if(towrite > 2 && srvi->erl_stdin != NULL){ + toerl = malloc(towrite+1); + strcpy(toerl,action); + strcat(toerl,"\n"); + WriteFile(srvi->erl_stdin, toerl, towrite, &written,0); + free(toerl); + /* Give it 45 seconds to terminate */ + for(i=0;i<45;++i){ + if(WaitForSingleObject(srvi->info.hProcess, 1000) == + WAIT_OBJECT_0){ + GetExitCodeProcess(srvi->info.hProcess,&exitcode); + CloseHandle(srvi->info.hProcess); + CloseHandle(srvi->info.hThread); + return TRUE; + } + ++(*checkpoint); + set_stop_pending(waithint,*checkpoint); + } + log_warning("StopAction did not terminate erlang. Trying forced kill."); + } + log_debug("Terminating erlang..."); + kill = 1; + if(eventKillErlang != NULL && SetEvent(eventKillErlang) != 0){ + for(i=0;i<10;++i){ + if(WaitForSingleObject(srvi->info.hProcess, 1000) == WAIT_OBJECT_0){ + kill = 0; + break; + } + ++(*checkpoint); + set_stop_pending(waithint,*checkpoint); + } + } else { +#ifdef HARDDEBUG + { + char *mes; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &mes, + 0, + NULL ); + log_info(mes); + LocalFree(mes); + } +#endif + log_debug("Could not send control event to Erlang process"); + } + if(kill){ + log_warning("Using TerminateProcess to kill erlang."); + if(!TerminateProcess(srvi->info.hProcess,NO_ERROR)) + log_error("TerminateProcess failed"); + } + GetExitCodeProcess(srvi->info.hProcess,&exitcode); + CloseHandle(srvi->info.hProcess); + CloseHandle(srvi->info.hThread); + if (eventKillErlang != NULL) { + ResetEvent(eventKillErlang); + } + return TRUE; +} + +static BOOL enable_privilege(void) { + 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 pull_service_name(void){ + SC_HANDLE scm; + DWORD sz = 1024; + static char service_name_buff[1024]; + if((scm = OpenSCManager(NULL, + NULL, + GENERIC_READ)) + == NULL){ + return FALSE; + } + if(!GetServiceDisplayName(scm,real_service_name,service_name_buff,&sz)) + return FALSE; + CloseServiceHandle(scm); + service_name = service_name_buff; + return TRUE; +} + + +static VOID WINAPI service_main_loop(DWORD argc, char **argv){ + int waithint = 30000; + int checkpoint = 1; + RegEntry *keys; + RegEntry *save_keys; + ServerInfo srvi; + HANDLE harr[2]; + FILETIME creationt,exitt,kernelt,usert; + LONGLONG creationl,exitl,diffl; + char event_name[MAX_PATH] = "ErlSrv_"; + char executable_name[MAX_PATH]; +#ifdef DEBUG + char errorbuff[2048]; /* FIXME... */ +#endif + int success_wait = NO_SUCCESS_WAIT; + + real_service_name = argv[0]; + if(!pull_service_name()){ + log_error("Could not get Display name of erlang service."); + set_stopped(ERROR_CANTREAD); + return; + } + + SetEnvironmentVariable((LPCTSTR) SERVICE_ENV, (LPCTSTR) service_name); + + strncat(event_name, service_name, MAX_PATH - strlen(event_name)); + event_name[MAX_PATH - 1] = '\0'; + + if(!GetModuleFileName(NULL, executable_name, MAX_PATH)){ + log_error("Unable to retrieve module file name, " EXECUTABLE_ENV + " will not be set."); + } else { + char quoted_exe_name[MAX_PATH+4]; + sprintf(quoted_exe_name, "\"%s\"", executable_name); + SetEnvironmentVariable((LPCTSTR) EXECUTABLE_ENV, + (LPCTSTR) quoted_exe_name); + } + + log_debug("Here we go, service_main_loop..."); + currentState = SERVICE_START_PENDING; + InitializeCriticalSection(&crit); + eventStop = CreateEvent(NULL,FALSE,FALSE,NULL); + if ((eventKillErlang = create_erlang_event(event_name)) != NULL) { + srvi.event_name = event_name; + } else { + srvi.event_name = NULL; + } + statusHandle = RegisterServiceCtrlHandler(real_service_name, &handler); + if(!statusHandle) + return; + set_start_pending(waithint,checkpoint); + keys = get_keys(service_name); + if(!keys){ + log_error("Could not get registry keys for erlang service."); + set_stopped(ERROR_CANTREAD); + return; + } + srvi.keys = keys; + srvi.erl_stdin = NULL; + + ++checkpoint; + if(!start_a_service(&srvi)){ + log_error("Could not start erlang machine"); + set_stopped(ERROR_PROCESS_ABORTED); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + set_start_pending(waithint,checkpoint); + set_running(); + success_wait = INITIAL_SUCCESS_WAIT; + harr[0] = srvi.info.hProcess; + harr[1] = eventStop; + for(;;){ + DWORD ret; + ret = WaitForMultipleObjects((DWORD) 2, + harr, + FALSE, + (success_wait == NO_SUCCESS_WAIT) ? + INFINITE : + SUCCESS_WAIT_TIME); + if(ret == WAIT_TIMEOUT){ + /* Just do the "success reporting" and continue */ + if(success_wait == INITIAL_SUCCESS_WAIT){ + log_info("Erlang service started successfully."); + } else { + log_warning("Erlang service restarted"); + } + success_wait = NO_SUCCESS_WAIT; + continue; + } + if(ret == WAIT_FAILED || (int)(ret-WAIT_OBJECT_0) >= 2){ + set_stopped(WAIT_FAILED); + log_error("Internal error, could not wait for objects."); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + ret -= WAIT_OBJECT_0; + if(((int) ret) == 1){ + /* Stop service... */ + checkpoint = 2; /* 1 is taken by the handler */ + set_stop_pending(waithint,checkpoint); + if(stop_erlang(&srvi,waithint,&checkpoint)){ + log_debug("Erlang machine is stopped"); + CloseHandle(eventStop); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + set_stopped(NO_ERROR); + if(srvi.erl_stdin) + CloseHandle(srvi.erl_stdin); + free_keys(keys); + return; + } else { + log_warning("Unable to stop erlang service."); + set_running(); + continue; + } + } + /* Reload the registry keys, they may have changed. */ + save_keys = keys; + keys = get_keys(service_name); + if(!keys){ + log_error("Could not reload registry keys."); + keys = srvi.keys = save_keys; + } else { +#ifdef HARDDEBUG + sprintf(errorbuff,"Reloaded the registry keys because %s stopped.", + service_name); + log_debug(errorbuff); +#endif /* HARDDEBUG */ + free_keys(save_keys); + srvi.keys = keys; + } + if(srvi.keys[OnFail].data.value == ON_FAIL_RESTART || + srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){ + if(!GetProcessTimes(srvi.info.hProcess,&creationt, + &exitt,&kernelt,&usert)){ + DWORD rcode = GetLastError(); + log_error("Could not get process time of terminated process."); + CloseHandle(srvi.info.hProcess); + CloseHandle(srvi.info.hThread); + CloseHandle(eventStop); + if(srvi.erl_stdin) + CloseHandle(srvi.erl_stdin); + set_stopped(rcode); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + CloseHandle(srvi.info.hProcess); + CloseHandle(srvi.info.hThread); + if(srvi.erl_stdin) + CloseHandle(srvi.erl_stdin); + srvi.erl_stdin = NULL; + memcpy(&creationl,&creationt,sizeof(FILETIME)); + memcpy(&exitl,&exitt,sizeof(FILETIME)); + diffl = exitl - creationl; + diffl /= 10000000; +#ifdef DEBUG + sprintf(errorbuff,"Process lived for %d seconds", (int) diffl); + log_debug(errorbuff); +#endif + + if(diffl > CYCLIC_RESTART_LIMIT || + srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){ + if(!start_a_service(&srvi)){ + log_error("Unable to restart failed erlang service, " + "aborting."); + CloseHandle(eventStop); + set_stopped(ERROR_PROCESS_ABORTED); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + log_warning("Restarted erlang machine."); + if(diffl <= CYCLIC_RESTART_LIMIT) + log_warning("Possible cyclic restarting of erlang machine."); + success_wait = RESTART_SUCCESS_WAIT; + harr[0] = srvi.info.hProcess; + } else { + if(success_wait == INITIAL_SUCCESS_WAIT){ + log_error("Erlang machine stopped instantly " + "(distribution name conflict?). " + "The service is not restarted, ignoring OnFail option."); + } else { + log_error("Erlang machine seems to die " + "continously, not restarted."); + } + CloseHandle(eventStop); + set_stopped(ERROR_PROCESS_ABORTED); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + } else if(srvi.keys[OnFail].data.value == ON_FAIL_REBOOT){ + log_error("Rebooting because erlang machine stopped."); + enable_privilege(); + if(!InitiateSystemShutdown("",NULL,0,TRUE,TRUE)){ + log_error("Failed to reboot!"); +#ifdef HARDDEBUG + { + char *mes; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &mes, + 0, + NULL ); + log_debug(mes); + LocalFree(mes); + } +#endif + CloseHandle(srvi.info.hProcess); + CloseHandle(eventStop); + if(srvi.erl_stdin != NULL) + CloseHandle(srvi.erl_stdin); + set_stopped(NO_ERROR); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + } else { + DWORD ecode = NO_ERROR; + if(success_wait == NO_SUCCESS_WAIT){ + log_warning("Erlang machine volountarily stopped. " + "The service is not restarted as OnFail " + "is set to ignore."); + } else { + log_error("Erlang machine stopped instantly " + "(distribution name conflict?). " + "The service is not restarted as OnFail is set to ignore."); + ecode = ERROR_PROCESS_ABORTED; + } + CloseHandle(srvi.info.hProcess); + CloseHandle(eventStop); + if(srvi.erl_stdin != NULL) + CloseHandle(srvi.erl_stdin); + set_stopped(ecode); + if (eventKillErlang != NULL) { + CloseHandle(eventKillErlang); + } + free_keys(keys); + return; + } + } +} + +int service_main(int argc, char **argv){ + char dummy_name[] = ""; + SERVICE_TABLE_ENTRY serviceTable[] = + { + { dummy_name, + (LPSERVICE_MAIN_FUNCTION) service_main_loop}, + { NULL, NULL } + }; + BOOL success; + success = + StartServiceCtrlDispatcher(serviceTable); + if (!success) + log_error("Could not initiate service"); + log_debug("service_main done its job"); + return 0; +} + diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h new file mode 100644 index 0000000000..3eab275836 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_service.h @@ -0,0 +1,32 @@ +/* + * %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% + */ +#ifndef _ERLSRV_SERVICE_H +#define _ERLSRV_SERVICE_H + +#define CYCLIC_RESTART_LIMIT 10 /* Seconds */ +#define SUCCESS_WAIT_TIME (10*1000) /* Wait 5 s before reporting a service + as really started */ +#define NO_SUCCESS_WAIT 0 +#define INITIAL_SUCCESS_WAIT 1 +#define RESTART_SUCCESS_WAIT 2 + + +int service_main(int argc, char **argv); + +#endif /* _ERLSRV_SERVICE_H */ diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c new file mode 100644 index 0000000000..da3c6f5ef7 --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_util.c @@ -0,0 +1,154 @@ +/* + * %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% + */ +#include <windows.h> +#include <winsvc.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "erlsrv_global.h" +#include "erlsrv_util.h" +#include "erlsrv_logmess.h" + +char *service_name = ""; +char *real_service_name = ""; + +void log_warning(char *mess){ + HANDLE logh; + char *strings[] = {service_name, mess , NULL}; + + if(!(logh = RegisterEventSource(NULL,APP_NAME))) + return; + ReportEvent(logh, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING, + NULL, 2, 0, strings, NULL); + DeregisterEventSource(logh); +} + +void log_error(char *mess){ + HANDLE logh; + char *strings[] = {service_name, mess , NULL}; + + if(!(logh = RegisterEventSource(NULL,APP_NAME))) + return; + ReportEvent(logh, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, + NULL, 2, 0, strings, NULL); + DeregisterEventSource(logh); +} + +void log_info(char *mess){ + HANDLE logh; + char *strings[] = {service_name, mess , NULL}; + + if(!(logh = RegisterEventSource(NULL,APP_NAME))) + return; + ReportEvent(logh, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO, + NULL, 2, 0, strings, NULL); + DeregisterEventSource(logh); +} + +#ifndef NDEBUG +void log_debug(char *mess){ + char *buff=malloc(strlen(mess)+100); + sprintf(buff,"DEBUG! %s",mess); + log_info(buff); + free(buff); +} +#endif + +char *envdup(char *env){ + char *tmp; + int len; + for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) + ; + len = (tmp - env) + 1; + if(len == 1) + ++len; + tmp = malloc(len); + memcpy(tmp,env,len); + return tmp; +} + +char **env_to_arg(char *env){ + char **ret; + char *tmp; + int i; + int num_strings = 0; + for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) + ++num_strings; + /* malloc enough to insert ONE string */ + ret = malloc(sizeof(char *) * (num_strings + 2)); + i = 0; + for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){ + ret[i++] = strdup(tmp); + } + ret[i] = NULL; + free(env); + return ret; +} + +static int compare(const void *a, const void *b){ + char *s1 = *((char **) a); + char *s2 = *((char **) b); + char *e1 = strchr(s1,'='); + char *e2 = strchr(s2,'='); + int ret; + int len; + + if(!e1) + e1 = s1 + strlen(s1); + if(!e2) + e2 = s2 + strlen(s2); + + if((e1 - s1) > (e2 - s2)) + len = (e2 - s2); + else + len = (e1 - s1); + + ret = _strnicmp(s1,s2,len); + if(ret == 0) + return ((e1 - s1) - (e2 - s2)); + else + return ret; +} + +char *arg_to_env(char **arg){ + char *block; + char *pek; + int i; + int totlen = 1; /* extra '\0' */ + + for(i=0;arg[i] != NULL;++i) + totlen += strlen(arg[i])+1; + /* sort the environment vector */ + qsort(arg,i,sizeof(char *),&compare); + if(totlen == 1){ + block = malloc(2); + block[0] = block[1] = '\0'; + } else { + block = malloc(totlen); + pek = block; + for(i=0; arg[i] != NULL; ++i){ + strcpy(pek, arg[i]); + free(arg[i]); + pek += strlen(pek)+1; + } + *pek = '\0'; + } + free(arg); + return block; +} diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h new file mode 100644 index 0000000000..b98a6cd3ef --- /dev/null +++ b/erts/etc/win32/erlsrv/erlsrv_util.h @@ -0,0 +1,50 @@ +/* + * %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% + */ +#ifndef _ERLSRV_UTIL_H +#define _ERLSRV_UTIL_H + +extern char *service_name; +extern char *real_service_name; +void log_warning(char *mess); +void log_error(char *mess); +void log_info(char *mess); + +char *envdup(char *env); +/* +** Call before env_to_arg to get a 'freeable' environment block. +*/ + +char *arg_to_env(char **arg); +/* +** Frees the argument list before returning! +*/ + +char **env_to_arg(char *env); +/* +** Frees the environment block before returning! +*/ + + +#ifndef NDEBUG +void log_debug(char *mess); +#else +#define log_debug(mess) /* Debug removed */ +#endif + +#endif /* _ERLSRV_UTIL_H */ diff --git a/erts/etc/win32/hrl_icon.ico b/erts/etc/win32/hrl_icon.ico Binary files differnew file mode 100644 index 0000000000..d22abb396b --- /dev/null +++ b/erts/etc/win32/hrl_icon.ico diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c new file mode 100644 index 0000000000..52f6c41d1d --- /dev/null +++ b/erts/etc/win32/init_file.c @@ -0,0 +1,565 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2003-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 <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include "init_file.h" + +#define ALLOC malloc +#define REALLOC realloc +#define FREE free + +#define CONTEXT_BUFFER_SIZE 1024 + +typedef struct { + HANDLE fd; + int eof; + int num; + int pos; + char buff[CONTEXT_BUFFER_SIZE]; +} FileContext; + +static char *read_one_line(FileContext *fc) +{ + char *buff = ALLOC(10); + int size = 10; + int num = 0; + int skipping; + int escaped; + +#define PUSH(Char) \ + do { \ + if (num == size) { \ + size += 10; \ + buff = REALLOC(buff,size); \ + } \ + buff[num++] = (Char); \ + } while(0) + +#define POP() (buff[--num]) +#define TOP() (buff[(num-1)]) + + skipping = 0; + escaped = 0; + for(;;) { + char current; + if (fc->eof) { + break; + } + if (fc->pos == fc->num) { + if (!ReadFile(fc->fd, fc->buff, CONTEXT_BUFFER_SIZE, + &(fc->num), NULL) || !(fc->num)) { + fc->eof = 1; + break; + } + fc->pos = 0; + } + current = fc->buff[fc->pos]; + ++(fc->pos); + switch (current) { + case ' ': + if (!skipping && num) { + PUSH(current); + } + escaped = 0; + break; + case ';': + if (!skipping) { + if (!escaped) { + skipping = 1; + } else { + PUSH(current); + } + } + escaped = 0; + break; + case '\\': + if (!skipping) { + if (!escaped) { + escaped = 1; + } else { + PUSH(current); + escaped = 0; + } + } + break; + case '\r': + break; + case '\n': + if (!escaped) { + while (num && TOP() == ' ') { + POP(); + } + if (num) { + goto done; + } + } + skipping = 0; + escaped = 0; + break; + default: + if (!skipping) { + PUSH(current); + } + escaped = 0; + break; + } + } + /* EOF comes here */ + while (num && TOP() == ' ') { + POP(); + } + if (!num) { + FREE(buff); + return NULL; + } + done: + PUSH('\0'); + return buff; +#undef PUSH +#undef POP +#undef TOP +} + +static int is_section_header(char *line) +{ + int x = strlen(line); + return (x > 2 && *line == '[' && line[x-1] == ']'); +} + +static int is_key_value(char *line) +{ + char *p = strchr(line,'='); + + return (p != NULL && p > line); +} + +static char *digout_section_name(char *line) + /* Moving it because it shall later be freed. */ +{ + int x = strlen(line); + memmove(line,line+1,x-1); + line[x-2] = '\0'; + return line; +} + +static void digout_key_value(char *line, char **key, char **value) +{ + char *e = strchr(line,'='); + *key = line; + *value = (e+1); + *e = '\0'; + while (*(--e) == ' ') { + *e = '\0'; + } + while (**value == ' ') { + ++(*value); + } +} + +InitFile *load_init_file(char *filename) +{ + HANDLE infile; + InitFile *inif; + InitSection *inis; + InitEntry *inie; + FileContext fc; + char *line; + char **lines; + int size_lines; + int num_lines; + + int i; + + if ( (infile = CreateFile(filename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { + return NULL; + } + + size_lines = 10; + num_lines = 0; + lines = ALLOC(size_lines * sizeof(char *)); + + fc.fd = infile; + fc.eof = 0; + fc.num = 0; + fc.pos = 0; + while ((line = read_one_line(&fc)) != NULL) { + if (num_lines == size_lines) { + size_lines += 10; + lines = REALLOC(lines,size_lines * sizeof(char *)); + } + lines[num_lines] = line; + ++num_lines; + } + CloseHandle(infile); + /* Now check the lines before doing anything else, so that + we don't need any error handling while creating the data + structures */ + /* + The file should contain: + [section] + Key=Value + ... + [section] + ... + */ + i = 0; + while (i < num_lines && is_section_header(lines[i])) { + ++i; + while (i < num_lines && is_key_value(lines[i])) { + ++i; + } + } + if (i < num_lines) { + for (i = 0; i < num_lines; ++i) { + FREE(lines[i]); + } + FREE(lines); + return NULL; + } + + /* So, now we know it's consistent... */ + i = 0; + inif = ALLOC(sizeof(InitFile)); + inif->num_sections = 0; + inif->size_sections = 10; + inif->sections = ALLOC(sizeof(InitSection *) * 10); + while (i < num_lines) { + inis = ALLOC(sizeof(InitSection)); + inis->num_entries = 0; + inis->size_entries = 10; + inis->section_name = digout_section_name(lines[i]); + inis->entries = ALLOC(sizeof(InitEntry *) * 10); + ++i; + while (i < num_lines && is_key_value(lines[i])) { + inie = ALLOC(sizeof(InitEntry)); + digout_key_value(lines[i], &(inie->key), &(inie->value)); + if (inis->num_entries == inis->size_entries) { + inis->size_entries += 10; + inis->entries = + REALLOC(inis->entries, + sizeof(InitEntry *) * inis->size_entries); + } + inis->entries[inis->num_entries] = inie; + ++(inis->num_entries); + ++i; + } + if (inif->num_sections == inif->size_sections) { + inif->size_sections += 10; + inif->sections = + REALLOC(inif->sections, + sizeof(InitSection *) * inif->size_sections); + } + inif->sections[inif->num_sections] = inis; + ++(inif->num_sections); + } + FREE(lines); /* Only the array of strings, not the actual strings, they + are kept in the data structures. */ + return inif; +} + +int store_init_file(InitFile *inif, char *filename) +{ + char *buff; + int size = 10; + int num = 0; + int i,j; + HANDLE outfile; + +#define PUSH(Char) \ + do { \ + if (num == size) { \ + size += 10; \ + buff = REALLOC(buff,size); \ + } \ + buff[num++] = (Char); \ + } while(0) + + if ( (outfile = CreateFile(filename, + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) { + return INIT_FILE_OPEN_ERROR; + } + buff = ALLOC(size); + + for(i = 0; i < inif->num_sections; ++i) { + int len; + int written; + InitSection *inis = inif->sections[i]; + + if (!WriteFile(outfile,"[",1,&written,NULL) || written != 1) { + goto write_error; + } + len = strlen(inis->section_name); + if (!WriteFile(outfile,inis->section_name,len,&written,NULL) || + written != len) { + goto write_error; + } + if (!WriteFile(outfile,"]\n",2,&written,NULL) || written != 2) { + goto write_error; + } + for (j = 0; j < inis->num_entries; ++j) { + InitEntry *inie = inis->entries[j]; + char *p = inie->key; + num = 0; + for (;*p != '\0';++p) { + switch (*p) { + case '\\': + case ';': + PUSH('\\'); + default: + PUSH(*p); + break; + } + } + PUSH('='); + p = inie->value; + for (;*p != '\0';++p) { + switch (*p) { + case '\\': + case ';': + PUSH('\\'); + default: + PUSH(*p); + break; + } + } + PUSH('\n'); + if (!WriteFile(outfile,buff,num,&written,NULL) || written != num) { + goto write_error; + } + } + } + FREE(buff); + CloseHandle(outfile); + return INIT_FILE_NO_ERROR; + write_error: + FREE(buff); + CloseHandle(outfile); + return INIT_FILE_WRITE_ERROR; +#undef PUSH +} + +InitFile *create_init_file(void) +{ + InitFile *inif = ALLOC(sizeof(InitFile)); + inif->num_sections = 0; + inif->size_sections = 10; + inif->sections = ALLOC(sizeof(InitSection *) * 10); + return inif; +} + +InitSection *create_init_section(char *section_name) +{ + InitSection *inis = ALLOC(sizeof(InitSection)); + inis->num_entries = 0; + inis->section_name = ALLOC(sizeof(char) * (strlen(section_name) + 1)); + strcpy(inis->section_name, section_name); + inis->size_entries = 10; + inis->entries = ALLOC(sizeof(InitEntry *) * 10); + return inis; +} + +static void free_init_entry(InitEntry *inie) +{ + FREE(inie->key); + /* Value is part of the same buffer */ + FREE(inie); +} + +void free_init_section(InitSection *inis) +{ + int i; + for (i = 0;i < inis->num_entries; ++i) { + free_init_entry(inis->entries[i]); + } + FREE(inis->entries); + FREE(inis->section_name); + FREE(inis); +} + +void free_init_file(InitFile *inif) +{ + int i; + for (i = 0; i < inif->num_sections; ++i) { + free_init_section(inif->sections[i]); + } + FREE(inif->sections); + FREE(inif); +} + +static int find_init_section(InitFile *inif, char *section_name) +{ + int i; + for (i = 0; i < inif->num_sections; ++i) { + if (!strcmp(inif->sections[i]->section_name, section_name)) { + return i; + } + } + return -1; +} + +int delete_init_section(InitFile *inif, char *section_name) +{ + int i; + + if ((i = find_init_section(inif, section_name)) < 0) { + return INIT_FILE_NOT_PRESENT; + } + + free_init_section(inif->sections[i]); + --(inif->num_sections); + inif->sections[i] = inif->sections[inif->num_sections]; + + return INIT_FILE_PRESENT; +} + +int add_init_section(InitFile *inif, InitSection *inis) +{ + int i; + InitSection *oinis; + if ((i = find_init_section(inif, inis->section_name)) >= 0) { + oinis = inif->sections[i]; + inif->sections[i] = inis; + free_init_section(oinis); + return INIT_FILE_PRESENT; + } + if (inif->num_sections == inif->size_sections) { + inif->size_sections += 10; + inif->sections = REALLOC(inif->sections, + sizeof(InitSection *) * inif->size_sections); + } + inif->sections[inif->num_sections] = inis; + ++(inif->num_sections); + return INIT_FILE_NOT_PRESENT; +} + +InitSection *lookup_init_section(InitFile *inif, char *section_name) +{ + int i; + if ((i = find_init_section(inif,section_name)) < 0) { + return NULL; + } + return inif->sections[i]; +} + +char *nth_init_section_name(InitFile *inif, int n) +{ + if (n >= inif->num_sections) { + return NULL; + } + return inif->sections[n]->section_name; +} + +/* Inefficient... */ +InitSection *copy_init_section(InitSection *inis, char *new_name) +{ + int i; + char *key; + InitSection *ninis = create_init_section(new_name); + i = 0; + while ((key = nth_init_entry_key(inis,i)) != NULL) { + add_init_entry(ninis, key, lookup_init_entry(inis, key)); + ++i; + } + return ninis; +} + +static int find_init_entry(InitSection *inis, char *key) +{ + int i; + for (i = 0; i < inis->num_entries; ++i) { + if (!strcmp(inis->entries[i]->key,key)) { + return i; + } + } + return -1; +} + +int add_init_entry(InitSection *inis, char *key, char *value) +{ + int keylen = strlen(key); + char *buff = ALLOC(sizeof(char) * (keylen + strlen(value) + 2)); + InitEntry *inie; + char *obuff; + int i; + + strcpy(buff,key); + strcpy(buff+keylen+1,value); + + if ((i = find_init_entry(inis,key)) >= 0) { + inie = inis->entries[i]; + FREE(inie->key); + inie->key = buff; + inie->value = buff+keylen+1; + return INIT_FILE_PRESENT; + } + inie = ALLOC(sizeof(InitEntry)); + inie->key = buff; + inie->value = buff+keylen+1; + if (inis->num_entries == inis->size_entries) { + inis->size_entries += 10; + inis->entries = REALLOC(inis->entries, + sizeof(InitEntry *) * inis->size_entries); + } + inis->entries[inis->num_entries] = inie; + ++(inis->num_entries); + return INIT_FILE_NOT_PRESENT; +} + +char *lookup_init_entry(InitSection *inis, char *key) +{ + int i; + if ((i = find_init_entry(inis,key)) < 0) { + return NULL; + } + return inis->entries[i]->value; +} + +char *nth_init_entry_key(InitSection *inis, int n) +{ + if (n >= inis->num_entries) { + return NULL; + } + return inis->entries[n]->key; +} + +int delete_init_entry(InitSection *inis, char *key) +{ + int i; + InitEntry *inie; + if ((i = find_init_entry(inis, key)) < 0) { + return INIT_FILE_NOT_PRESENT; + } + free_init_entry(inis->entries[i]); + --(inis->num_entries); + inis->entries[i] = inis->entries[inis->num_entries]; + return INIT_FILE_PRESENT; +} + diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h new file mode 100644 index 0000000000..48d2d2df62 --- /dev/null +++ b/erts/etc/win32/init_file.h @@ -0,0 +1,93 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2003-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% + */ + +typedef struct { + char *key; + char *value; /* Key and value points into same buffer */ +} InitEntry; + +typedef struct { + int num_entries; + int size_entries; + char *section_name; + InitEntry **entries; +} InitSection; + +typedef struct { + int num_sections; + int size_sections; + InitSection **sections; +} InitFile; + +/* Load a file structure from a disk file */ +InitFile *load_init_file(char *filename); + +/* Stores a file structure into a disk file */ +int store_init_file(InitFile *inif, char *filename); + +/* Create an empty file structure */ +InitFile *create_init_file(void); + +/* Free a file structure and everything associateed (including sections,keys + and values and anything looked up but not copied) */ +void free_init_file(InitFile *inif); + +/* Create empty section */ +InitSection *create_init_section(char *section_name); + +/* Add section to file Overwrites and destroys old sections with same key */ +int add_init_section(InitFile *inif, InitSection *inis); + +/* Kills a named section from a file. Destroys so that previously looked up + sections (with this key) need to be copied before the delete */ +int delete_init_section(InitFile *inif, char *section_name); + +/* lookup returns pointer into existing data. If data is to be preserved + across deletes or overwrites, it has to be copied */ +InitSection *lookup_init_section(InitFile *inif, char *section_name); + +/* Returns the name of the nth init section, n is >= 0, ret NULL when done */ +char *nth_init_section_name(InitFile *inif, int n); + +/* To save an init section so that delete or overwrite does not destroy it, + one needs to copy it */ +InitSection *copy_init_section(InitSection *inis, char *new_name); + +/* Frees up everything in the section, keys and values as well. */ +void free_init_section(InitSection *inis); + +/* Key and value are copied in add_entry */ +int add_init_entry(InitSection *inis, char *key, char *value); + +/* Returns pointer into internal string, use strcpy to save across + updates/deletes */ +char *lookup_init_entry(InitSection *inis, char *key); + +/* Returns the name of the nth entry key, n is >= 0, ret NULL when done */ +char *nth_init_entry_key(InitSection *inis, int n); + +/* Destroys entry, prevoiusly looked up entries need be + copied before deleted */ +int delete_init_entry(InitSection *inis, char *key); + +#define INIT_FILE_NO_ERROR 0 +#define INIT_FILE_OPEN_ERROR -1 +#define INIT_FILE_WRITE_ERROR -2 +#define INIT_FILE_PRESENT 0 +#define INIT_FILE_NOT_PRESENT 1 diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile new file mode 100644 index 0000000000..ebb3ad9a96 --- /dev/null +++ b/erts/etc/win32/nsis/Makefile @@ -0,0 +1,88 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2003-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 +include $(ERL_TOP)/make/$(TARGET)/otp.mk +include $(ERL_TOP)/erts/vsn.mk + +VERSION_HEADER = erlang.nsh +MAKENSIS = makensis +MAKENSISFLAGS = /V2 +CUSTOM_MODERN=custom_modern.exe + +# This is not the way we usually do in our makefiles, +# but making release is the ONLY thing we do with this one, +# Its not called during ordinary recursive make. +all: release + +opt debug depend: + @echo Nothing to do for "'"$@"'" on $(TARGET) + +clean: + rm -f $(VERSION_HEADER) + +include $(ERL_TOP)/make/otp_release_targets.mk + +TARGET_DIR = $(RELEASE_PATH) +WTESTROOT=$(shell (cygpath -d $(RELEASE_PATH) 2>/dev/null || cygpath -w $(RELEASE_PATH))) +WTARGET_DIR=$(shell (cygpath -d $(TARGET_DIR) 2>/dev/null || cygpath -d $(TARGET_DIR))) + +REDIST_FILE=$(shell (sh ./find_redist.sh || echo "")) +REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh || echo "")) + +release_spec: + @NSIS_VER=`makensis /hdrinfo | head -1 | awk '{print $$2}'`; \ + case $$NSIS_VER in \ + v2.0b*) \ + echo '!define MUI_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\ + echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\ + echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\ + echo '!define OUTFILEDIR "$(WTARGET_DIR)"' >> $(VERSION_HEADER);\ + if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\ + then\ + echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \ + fi;\ + $(MAKENSIS) erlang.nsi;;\ + v2.*) \ + echo '!define OTP_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\ + echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\ + echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\ + echo '!define OUTFILEDIR "$(WTARGET_DIR)"' >> $(VERSION_HEADER);\ + if [ -f $(CUSTOM_MODERN) ];\ + then \ + echo '!define HAVE_CUSTOM_MODERN 1' >> $(VERSION_HEADER); \ + fi;\ + if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\ + then \ + cp $(REDIST_FILE) $(RELEASE_PATH)/vcredist_x86.exe;\ + echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \ + echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\ + fi;\ + if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\ + then \ + echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \ + fi;\ + echo "Running $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi";\ + $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi;;\ + *) \ + echo 'Unsupported NSIS version';;\ + esac + +release_docs release_docs_spec docs: + diff --git a/erts/etc/win32/nsis/custom_modern.exe b/erts/etc/win32/nsis/custom_modern.exe Binary files differnew file mode 100755 index 0000000000..0f56b8b239 --- /dev/null +++ b/erts/etc/win32/nsis/custom_modern.exe diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh new file mode 100755 index 0000000000..e0047dea8b --- /dev/null +++ b/erts/etc/win32/nsis/dll_version_helper.sh @@ -0,0 +1,49 @@ +#! /bin/sh +# +# %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% +# +# This little helper digs out the current version of microsoft CRT +# by compiling hello world and "parsing" the manifest file... + +# To debug using a fake version: + +# echo "8.0.50727.763" +# exit 0 + +cat > hello.c <<EOF +#include <stdio.h> + +int main(void) +{ + printf("Hello world\n"); + return 0; +} + +EOF +cl /MD hello.c > /dev/null 2>&1 +if [ '!' -f hello.exe.manifest ]; then + echo "This compiler does not generate manifest files - OK if using mingw" >&2 + exit 0 +fi +VERSION=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*version=.\([0-9\.]*\).*,\1,g' | grep -v '<'` +rm -f hello.c hello.obj hello.exe hello.exe.manifest +if [ -z "$VERSION" ]; then + exit 1 +fi +echo $VERSION +exit 0 diff --git a/erts/etc/win32/nsis/erlang.nsi b/erts/etc/win32/nsis/erlang.nsi new file mode 100644 index 0000000000..f4fd2b4cdb --- /dev/null +++ b/erts/etc/win32/nsis/erlang.nsi @@ -0,0 +1,386 @@ +; NSIS Modern User Interface version 1.63 +; Erlang OTP installation script based on "Start Menu Folder Selection +; Example Script" +; Original example written by Joost Verburg +; Modified for Erlang by Patrik + +; Verbosity does not come naturally with MUI, have to set it back now and then. + !verbose 1 + !define MUI_MANUALVERBOSE 1 + + !define MUI_PRODUCT "Erlang OTP" + + !include "erlang.nsh" ; All release specific parameters come from this + + + !include "MUI.nsh" + +;-------------------------------- +;Configuration + + ;SetCompressor bzip2 + +;General + OutFile "${OUTFILEDIR}\otp_win32_${MUI_VERSION}.exe" + +;Folder selection page + InstallDir "$PROGRAMFILES\erl${ERTS_VERSION}" + +;Remember install folder + InstallDirRegKey HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" "" + +;$9 is being used to store the Start Menu Folder. +;Do not use this variable in your script (or Push/Pop it)! + +;To change this variable, use MUI_STARTMENUPAGE_VARIABLE. +;Have a look at the Readme for info about other options (default folder, +;registry). + +; Set the default start menu folder + + !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${MUI_PRODUCT} ${MUI_VERSION}" + +; Registry keys where start menu folder is stored + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" + !define MUI_STARTMENUPAGE_REGISTRY_KEY \ + "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + +; Temporary variable used here and there... + !define TEMP $R0 + +;-------------------------------- +;Modern UI Configuration + !define MUI_ICON "erlang_inst.ico" + !define MUI_UNICON "erlang_uninst.ico" + !define MUI_WELCOMEPAGE + !define MUI_COMPONENTSPAGE + !define MUI_DIRECTORYPAGE + !define MUI_STARTMENUPAGE + + !define MUI_ABORTWARNING + + !define MUI_UNINSTALLER + !define MUI_UNCONFIRMPAGE + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +;Language Strings + +;Description + LangString DESC_SecErlang ${LANG_ENGLISH} "Erlang OTP." + LangString DESC_SecErlangDev ${LANG_ENGLISH} \ + "Erlang OTP development environment (required)." + LangString DESC_SecErlangAssoc ${LANG_ENGLISH} \ + "Erlang filetype associations (.erl, .hrl, .beam)." +!ifdef HAVE_DOCS + LangString DESC_SecErlangDoc ${LANG_ENGLISH} "Documentation." +!endif +;-------------------------------- +;Installer Sections + +SubSection /e "Erlang" SecErlang +Section "Development" SecErlangDev +SectionIn 1 RO + + StrCmp ${MUI_STARTMENUPAGE_VARIABLE} "" 0 skip_silent_mode + StrCpy ${MUI_STARTMENUPAGE_VARIABLE} \ + "${MUI_STARTMENUPAGE_DEFAULTFOLDER}" +skip_silent_mode: + + SetOutPath "$INSTDIR" + File "${TESTROOT}\Install.ini" + File "${TESTROOT}\Install.exe" + File /r "${TESTROOT}\releases" + File /r "${TESTROOT}\lib" + File /r "${TESTROOT}\erts-${ERTS_VERSION}" + File /r "${TESTROOT}\usr" + +;Store install folder + WriteRegStr HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" "" $INSTDIR + +; Run the setup program + ExecWait '"$INSTDIR\Install.exe" -s' + +; The startmenu stuff + !insertmacro MUI_STARTMENU_WRITE_BEGIN +; Set back verbosity... + !verbose 1 +; Try to use the Common startmenu... + SetShellVarContext All + ClearErrors + CreateDirectory "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}" + IfErrors 0 continue_create + ;MessageBox MB_OK "Error creating file" + SetShellVarContext current + CreateDirectory "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}" +continue_create: + WriteUninstaller "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang.lnk" \ + "$INSTDIR\bin\werl.exe" + CreateShortCut \ + "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Uninstall.lnk" \ + "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 + + !insertmacro MUI_STARTMENU_WRITE_END +; And once again, the verbosity... + !verbose 1 +;Create uninstaller +; WriteUninstaller "$INSTDIR\Uninstall.exe" + + WriteRegStr HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "DisplayName" "Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" + WriteRegStr HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "UninstallString" "$INSTDIR\Uninstall.exe" + WriteRegDWORD HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "NoModify" 1 + WriteRegDWORD HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "NoRepair" 1 + +; Check that the registry could be written, we only check one key, +; but it should be sufficient... + ReadRegStr ${TEMP} "${MUI_STARTMENUPAGE_REGISTRY_ROOT}" \ + "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \ + "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}" + + StrCmp ${TEMP} "" 0 done + +; Now we're done if we are a superuser. If the registry stuff failed, we +; do the things below... + + WriteRegStr HKCU "Software\Ericsson\Erlang\${ERTS_VERSION}" \ + "" $INSTDIR + WriteRegStr HKCU "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \ + "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}" \ + "${MUI_STARTMENUPAGE_VARIABLE}" + WriteRegStr HKCU \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "DisplayName" "Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" + WriteRegStr HKCU \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "UninstallString" "$INSTDIR\Uninstall.exe" + WriteRegDWORD HKCU \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "NoModify" 1 + WriteRegDWORD HKCU \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \ + "NoRepair" 1 + +done: +SectionEnd ; SecErlangDev + +Section "Associations" SecErlangAssoc + + ;File /r "${TESTROOT}\usr\lib\icons" + +; .erl + ; back up old value of .erl + ReadRegStr $1 HKCR ".erl" "" + StrCmp $1 "" OwnErl + StrCmp $1 "ErlangSource" OwnErl + WriteRegStr HKCR ".erl" "backup_val" $1 +OwnErl: + WriteRegStr HKCR ".erl" "" "ErlangSource" + ReadRegStr $0 HKCR "ErlangSource" "" + StrCmp $0 "" 0 skipErlAssoc + WriteRegStr HKCR "ErlangSource" "" "Erlang source file" + WriteRegStr HKCR "ErlangSource\shell" "" "open" + WriteRegStr HKCR "ErlangSource\shell\compile" "" "Compile" + WriteRegStr HKCR "ErlangSource\shell\compile\command" "" \ + '"$INSTDIR\bin\erlc.exe" "%1"' + WriteRegStr HKCR "ErlangSource\DefaultIcon" "" \ + $INSTDIR\usr\lib\icons\erl_icon.ico + WriteRegStr HKCR "ErlangSource\shell\open\command" \ + "" 'write.exe "%1"' +skipErlAssoc: + +; .hrl + ; back up old value of .hrl + ReadRegStr $1 HKCR ".hrl" "" + StrCmp $1 "" OwnHrl + StrCmp $1 "ErlangHeader" OwnHrl + WriteRegStr HKCR ".hrl" "backup_val" $1 +OwnHrl: + WriteRegStr HKCR ".hrl" "" "ErlangHeader" + ReadRegStr $0 HKCR "ErlangHeader" "" + StrCmp $0 "" 0 skipHrlAssoc + WriteRegStr HKCR "ErlangHeader" "" "Erlang header file" + WriteRegStr HKCR "ErlangHeader\shell" "" "open" + WriteRegStr HKCR "ErlangHeader\DefaultIcon" "" \ + $INSTDIR\usr\lib\icons\hrl_icon.ico + WriteRegStr HKCR "ErlangHeader\shell\open\command" \ + "" 'write.exe "%1"' +skipHrlAssoc: + +; .beam + ; back up old value of .beam + ReadRegStr $1 HKCR ".beam" "" + StrCmp $1 "" OwnBeam + StrCmp $1 "ErlangBeam" OwnBeam + WriteRegStr HKCR ".beam" "backup_val" $1 +OwnBeam: + WriteRegStr HKCR ".beam" "" "ErlangBeam" + ReadRegStr $0 HKCR "ErlangBeam" "" + StrCmp $0 "" 0 skipBeamAssoc + WriteRegStr HKCR "ErlangBeam" "" "Erlang beam code" + WriteRegStr HKCR "ErlangBeam\DefaultIcon" "" \ + $INSTDIR\usr\lib\icons\beam_icon.ico +skipBeamAssoc: + +SectionEnd ; SecErlangAssoc +SubSectionEnd + +!ifdef HAVE_DOCS +Section "Erlang Documentation" SecErlangDoc + + SetOutPath "$INSTDIR" + File /r "${TESTROOT}\docs\*" + +; The startmenu stuff + !insertmacro MUI_STARTMENU_WRITE_BEGIN +; Set back verbosity... + !verbose 1 +; Try to use the Common startmenu... + SetShellVarContext All + ClearErrors + CreateShortCut "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang Documentation.lnk" \ + "$INSTDIR\doc\index.html" + IfErrors 0 continue_create + ;MessageBox MB_OK "Error creating file" + SetShellVarContext current + CreateShortCut \ + "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang Documentation.lnk" \ + "$INSTDIR\doc\index.html" +continue_create: + + !insertmacro MUI_STARTMENU_WRITE_END +; And once again, the verbosity... + !verbose 1 +SectionEnd ; ErlangDoc +!endif + + +;Display the Finish header +;Insert this macro after the sections if you are not using a finish page + !insertmacro MUI_SECTIONS_FINISHHEADER + +;-------------------------------- +;Descriptions + + !insertmacro MUI_FUNCTIONS_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SecErlang} $(DESC_SecErlang) + !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDev} $(DESC_SecErlangDev) + !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangAssoc} \ + $(DESC_SecErlangAssoc) +!ifdef HAVE_DOCS + !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDoc} $(DESC_SecErlangDoc) +!endif + !insertmacro MUI_FUNCTIONS_DESCRIPTION_END + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + + RMDir /r "$INSTDIR" + +;Remove shortcut + ReadRegStr ${TEMP} "${MUI_STARTMENUPAGE_REGISTRY_ROOT}" \ + "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \ + "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}" + StrCmp ${TEMP} "" 0 end_try +; Try HKCU instead... + ReadRegStr ${TEMP} HKCU \ + "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \ + "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}" +; If this failed to, we have no shortcuts (eh?) + StrCmp ${TEMP} "" noshortcuts +end_try: + SetShellVarContext All + ClearErrors +; If we cannot find the shortcut, switch to current user context + GetFileTime "$SMPROGRAMS\${TEMP}\Erlang.lnk" $R1 $R2 + IfErrors 0 continue_delete + ;MessageBox MB_OK "Error removing file" + SetShellVarContext current +continue_delete: + Delete "$SMPROGRAMS\${TEMP}\Erlang.lnk" + Delete "$SMPROGRAMS\${TEMP}\Uninstall.lnk" + Delete "$SMPROGRAMS\${TEMP}\Erlang Documentation.lnk" + RMDir "$SMPROGRAMS\${TEMP}" ;Only if empty + +noshortcuts: +; We delete both in HKCU and HKLM, we don't really know were they might be... + DeleteRegKey /ifempty HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" + DeleteRegKey /ifempty HKCU "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" + DeleteRegKey HKLM \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" + DeleteRegKey HKCU \ + "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" + + +; Now remove shell/file associations we'we made... +; .erl + ReadRegStr $1 HKCR ".erl" "" + StrCmp $1 "ErlangSource" 0 NoOwnSourceExt + ReadRegStr $1 HKCR ".erl" "backup_val" + StrCmp $1 "" 0 RestoreBackupSource + DeleteRegKey HKCR ".erl" + Goto NoOwnSourceExt +RestoreBackupSource: + WriteRegStr HKCR ".erl" "" $1 + DeleteRegValue HKCR ".erl" "backup_val" +NoOwnSourceExt: + + ReadRegStr $1 HKCR "ErlangSource\DefaultIcon" "" + StrCmp $1 "$INSTDIR\usr\lib\icons\erl_icon.ico" 0 NoOwnSource + DeleteRegKey HKCR "ErlangSource" +NoOwnSource: + +;.hrl + ReadRegStr $1 HKCR ".hrl" "" + StrCmp $1 "ErlangHeader" 0 NoOwnHeaderExt + ReadRegStr $1 HKCR ".hrl" "backup_val" + StrCmp $1 "" 0 RestoreBackupHeader + DeleteRegKey HKCR ".hrl" + Goto NoOwnHeaderExt +RestoreBackupHeader: + WriteRegStr HKCR ".hrl" "" $1 + DeleteRegValue HKCR ".hrl" "backup_val" +NoOwnHeaderExt: + + ReadRegStr $1 HKCR "ErlangHeader\DefaultIcon" "" + StrCmp $1 "$INSTDIR\usr\lib\icons\hrl_icon.ico" 0 NoOwnHeader + DeleteRegKey HKCR "ErlangHeader" +NoOwnHeader: + +;.beam + ReadRegStr $1 HKCR ".beam" "" + StrCmp $1 "ErlangBeam" 0 NoOwnBeamExt + ReadRegStr $1 HKCR ".beam" "backup_val" + StrCmp $1 "" 0 RestoreBackupBeam + DeleteRegKey HKCR ".beam" + Goto NoOwnBeamExt +RestoreBackupBeam: + WriteRegStr HKCR ".beam" "" $1 + DeleteRegValue HKCR ".beam" "backup_val" +NoOwnBeamExt: + + ReadRegStr $1 HKCR "ErlangBeam\DefaultIcon" "" + StrCmp $1 "$INSTDIR\usr\lib\icons\beam_icon.ico" 0 NoOwnBeam + DeleteRegKey HKCR "ErlangBeam" +NoOwnBeam: + +;Display the Finish header + !insertmacro MUI_UNFINISHHEADER + +SectionEnd + !verbose 3 diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi new file mode 100644 index 0000000000..43e5d91604 --- /dev/null +++ b/erts/etc/win32/nsis/erlang20.nsi @@ -0,0 +1,440 @@ +; NSIS Modern User Interface version 1.63
+; Erlang OTP installation script based on "Start Menu Folder Selection
+; Example Script"
+; Original example written by Joost Verburg
+; Modified for Erlang by Patrik
+
+; Verbosity does not come naturally with MUI, have to set it back now and then.
+ !verbose 1
+ !define MUI_MANUALVERBOSE 1
+
+ !define OTP_PRODUCT "Erlang OTP"
+
+ !include "erlang.nsh" ; All release specific parameters come from this
+
+ Name "${OTP_PRODUCT} ${OTP_VERSION}"
+
+ !include "MUI.nsh"
+ !include "WordFunc.nsh"
+;--------------------------------
+;Configuration
+
+ SetCompressor bzip2
+
+Var MYTEMP
+;Var MUI_TEMP
+Var STARTMENU_FOLDER
+
+
+!define MY_STARTMENUPAGE_REGISTRY_ROOT HKLM
+!define MY_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+!define MY_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
+
+;General
+ OutFile "${OUTFILEDIR}\otp_win32_${OTP_VERSION}.exe"
+
+;Folder selection page
+ InstallDir "$PROGRAMFILES\erl${ERTS_VERSION}"
+
+;Remember install folder
+ InstallDirRegKey HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" ""
+
+; Set the default start menu folder
+
+ !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${OTP_PRODUCT} ${OTP_VERSION}"
+
+;--------------------------------
+;Modern UI Configuration
+!ifdef HAVE_CUSTOM_MODERN
+ !define MUI_UI "custom_modern.exe"
+!endif
+ !define MUI_ICON "erlang_inst.ico"
+ !define MUI_UNICON "erlang_uninst.ico"
+
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+; Registry keys where start menu folder is stored
+
+ !define MUI_STARTMENUPAGE_REGISTRY_ROOT ${MY_STARTMENUPAGE_REGISTRY_ROOT}
+ !define MUI_STARTMENUPAGE_REGISTRY_KEY "${MY_STARTMENUPAGE_REGISTRY_KEY}"
+ !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+
+ !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
+
+ !insertmacro MUI_PAGE_INSTFILES
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+;Description
+ LangString DESC_SecErlang ${LANG_ENGLISH} "Erlang OTP."
+ LangString DESC_SecErlangDev ${LANG_ENGLISH} \
+ "Erlang OTP development environment (required)."
+ LangString DESC_SecErlangAssoc ${LANG_ENGLISH} \
+ "Erlang filetype associations (.erl, .hrl, .beam)."
+!ifdef HAVE_DOCS
+ LangString DESC_SecErlangDoc ${LANG_ENGLISH} "Documentation."
+!endif
+!ifdef HAVE_REDIST_FILE
+ LangString DESC_SecMSRedist ${LANG_ENGLISH} "Microsoft redistributable C runtime libraries, these are mandatory for Erlang runtime and development. Always installed if not already present."
+!endif
+;--------------------------------
+; WordFunc
+!ifdef HAVE_REDIST_FILE
+ !insertmacro VersionCompare
+!endif
+;--------------------------------
+;Installer Sections
+
+!ifdef HAVE_REDIST_FILE
+Section "Microsoft redistributable libraries." SecMSRedist
+
+ SetOutPath "$INSTDIR"
+ File "${TESTROOT}\vcredist_x86.exe"
+
+; Set back verbosity...
+ !verbose 1
+; Run the setup program
+ ExecWait '"$INSTDIR\vcredist_x86.exe"'
+
+ !verbose 1
+SectionEnd ; MSRedist
+!endif
+
+SubSection /e "Erlang" SecErlang
+Section "Development" SecErlangDev
+SectionIn 1 RO
+
+ SetOutPath "$INSTDIR"
+ File "${TESTROOT}\Install.ini"
+ File "${TESTROOT}\Install.exe"
+ SetOutPath "$INSTDIR\releases"
+ File /r "${TESTROOT}\releases\"
+ SetOutPath "$INSTDIR\lib"
+ File /r "${TESTROOT}\lib\"
+ SetOutPath "$INSTDIR\erts-${ERTS_VERSION}"
+ File /r "${TESTROOT}\erts-${ERTS_VERSION}\"
+ SetOutPath "$INSTDIR\usr"
+ File /r "${TESTROOT}\usr\"
+
+;Store install folder
+ WriteRegStr HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" "" $INSTDIR
+
+; Run the setup program
+ Exec '"$INSTDIR\Install.exe" -s'
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
+continue_create:
+ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang.lnk" \
+ "$INSTDIR\bin\werl.exe"
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+; Check that the registry could be written, we only check one key,
+; but it should be sufficient...
+ ReadRegStr $MYTEMP ${MY_STARTMENUPAGE_REGISTRY_ROOT} "${MY_STARTMENUPAGE_REGISTRY_KEY}" "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+
+ StrCmp $MYTEMP "" 0 done_startmenu
+
+; If startmenu was skipped, this might be unnecessary, but wont hurt...
+ WriteRegStr HKCU "Software\Ericsson\Erlang\${ERTS_VERSION}" \
+ "" $INSTDIR
+ WriteRegStr HKCU "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}" \
+ "$STARTMENU_FOLDER"
+
+
+done_startmenu:
+;Create uninstaller
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+; Check that the registry could be written, we only check one key,
+; but it should be sufficient...
+ ReadRegStr $MYTEMP HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair"
+
+ StrCmp $MYTEMP "" 0 done
+
+; Now we're done if we are a superuser. If the registry stuff failed, we
+; do the things below...
+
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+done:
+SectionEnd ; SecErlangDev
+
+Section "Associations" SecErlangAssoc
+
+ ;File /r "${TESTROOT}\usr\lib\icons"
+
+; .erl
+ DeleteRegKey HKCR ".erl"
+ DeleteRegKey HKCR "ErlangSource"
+ WriteRegStr HKCR ".erl" "" "ErlangSource"
+ WriteRegStr HKCR "ErlangSource" "" "Erlang source file"
+ WriteRegStr HKCR "ErlangSource\shell\compile" "" "Compile"
+ WriteRegStr HKCR "ErlangSource\shell\compile\command" "" \
+ '"$INSTDIR\bin\erlc.exe" "%1"'
+ WriteRegStr HKCR "ErlangSource\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\erl_icon.ico
+; .hrl
+ DeleteRegKey HKCR ".hrl"
+ DeleteRegKey HKCR "ErlangHeader"
+ WriteRegStr HKCR ".hrl" "" "ErlangHeader"
+ WriteRegStr HKCR "ErlangHeader" "" "Erlang header file"
+ WriteRegStr HKCR "ErlangHeader\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\hrl_icon.ico
+
+; .beam
+ DeleteRegKey HKCR ".beam"
+ DeleteRegKey HKCR "ErlangBeam"
+ WriteRegStr HKCR ".beam" "" "ErlangBeam"
+ WriteRegStr HKCR "ErlangBeam" "" "Erlang beam code"
+ WriteRegStr HKCR "ErlangBeam\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\beam_icon.ico
+
+
+ SearchPath $1 "write.exe"
+ StrCmp $1 "" writeNotFound
+ WriteRegStr HKCR "ErlangSource\shell" "" "Open"
+ WriteRegStr HKCR "ErlangSource\shell\open\command" "" \
+ '"$1" "%1"'
+ WriteRegStr HKCR "ErlangHeader\shell" "" "Open"
+ WriteRegStr HKCR "ErlangHeader\shell\open\command" "" \
+ '"$1" "%1"'
+
+
+writeNotFound:
+SectionEnd ; SecErlangAssoc
+SubSectionEnd
+
+!ifdef HAVE_DOCS
+Section "Erlang Documentation" SecErlangDoc
+
+ SetOutPath "$INSTDIR"
+ File /r "${TESTROOT}\docs\*"
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateShortCut \
+ "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+continue_create:
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+SectionEnd ; ErlangDoc
+!endif
+
+!ifdef HAVE_REDIST_FILE
+Function DllVersionGoodEnough
+ IntCmp 0 $R0 normal0 normal0 negative0
+ normal0:
+ IntOp $R2 $R0 >> 16
+ Goto continue0
+ negative0:
+ IntOp $R2 $R0 & 0x7FFF0000
+ IntOp $R2 $R2 >> 16
+ IntOp $R2 $R2 | 0x8000
+ continue0:
+ IntOp $R3 $R0 & 0x0000FFFF
+ IntCmp 0 $R1 normal1 normal1 negative1
+ normal1:
+ IntOp $R4 $R1 >> 16
+ Goto continue1
+ negative1:
+ IntOp $R4 $R1 & 0x7FFF0000
+ IntOp $R4 $R4 >> 16
+ IntOp $R4 $R4 | 0x8000
+ continue1:
+ IntOp $R5 $R1 & 0x0000FFFF
+ StrCpy $2 "$R2.$R3.$R4.$R5"
+ ${VersionCompare} $2 ${REDIST_DLL_VERSION} $R0
+ Return
+FunctionEnd
+
+Function .onInit
+ SectionGetFlags 0 $MYTEMP
+; MessageBox MB_YESNO "Found $SYSDIR\msvcr80.dll" IDYES FoundLbl
+ IfFileExists $SYSDIR\msvcr80.dll MaybeFoundInSystemLbl
+ SearchSxsLbl:
+ FindFirst $0 $1 $WINDIR\WinSxS\x86*
+ LoopLbl:
+ StrCmp $1 "" NotFoundLbl
+ IfFileExists $WINDIR\WinSxS\$1\msvcr80.dll MaybeFoundInSxsLbl
+ FindNext $0 $1
+ Goto LoopLbl
+ MaybeFoundInSxsLbl:
+ GetDllVersion $WINDIR\WinSxS\$1\msvcr80.dll $R0 $R1
+ Call DllVersionGoodEnough
+ FindNext $0 $1
+ IntCmp 2 $R0 LoopLbl
+ Goto FoundLbl
+ MaybeFoundInSystemLbl:
+ GetDllVersion $SYSDIR\msvcr80.dll $R0 $R1
+ Call DllVersionGoodEnough
+ IntCmp 2 $R0 SearchSxSLbl
+ FoundLbl:
+ IntOp $MYTEMP $MYTEMP & 4294967294
+ SectionSetFlags 0 $MYTEMP
+ SectionSetText 0 "Microsoft DLL's (present)"
+ Return
+ NotFoundLbl:
+ IntOp $MYTEMP $MYTEMP | 16
+ SectionSetFlags 0 $MYTEMP
+ SectionSetText 0 "Microsoft DLL's (needed)"
+ Return
+FunctionEnd
+!endif
+
+
+;Display the Finish header
+;Insert this macro after the sections if you are not using a finish page
+; !insertmacro MUI_SECTIONS_FINISHHEADER
+
+;--------------------------------
+;Descriptions
+
+ !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlang} $(DESC_SecErlang)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDev} $(DESC_SecErlangDev)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangAssoc} \
+ $(DESC_SecErlangAssoc)
+!ifdef HAVE_DOCS
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDoc} $(DESC_SecErlangDoc)
+!endif
+!ifdef HAVE_REDIST_FILE
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecMSRedist} $(DESC_SecMSRedist)
+!endif
+ !insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+ RMDir /r "$INSTDIR"
+
+;Remove shortcut
+ ReadRegStr $MYTEMP "${MY_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+ StrCmp $MYTEMP "" 0 end_try
+; Try HKCU instead...
+ ReadRegStr $MYTEMP "${MY_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+; If this failed to, we have no shortcuts (eh?)
+ StrCmp $MYTEMP "" noshortcuts
+end_try:
+ SetShellVarContext All
+ ClearErrors
+; If we cannot find the shortcut, switch to current user context
+ GetFileTime "$SMPROGRAMS\$MYTEMP\Erlang.lnk" $R1 $R2
+ IfErrors 0 continue_delete
+ ;MessageBox MB_OK "Error removing file"
+ SetShellVarContext current
+continue_delete:
+ Delete "$SMPROGRAMS\$MYTEMP\Erlang.lnk"
+ Delete "$SMPROGRAMS\$MYTEMP\Uninstall.lnk"
+ Delete "$SMPROGRAMS\$MYTEMP\Erlang Documentation.lnk"
+ RMDir "$SMPROGRAMS\$MYTEMP" ;Only if empty
+
+noshortcuts:
+; We delete both in HKCU and HKLM, we don't really know were they might be...
+ DeleteRegKey /ifempty HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey /ifempty HKCU "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ DeleteRegKey HKCU \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+
+
+; Now remove shell/file associations we'we made...
+; .erl
+ ReadRegStr $1 HKCR ".erl" ""
+ StrCmp $1 "ErlangSource" 0 NoOwnSource
+ ReadRegStr $1 HKCR "ErlangSource\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\erl_icon.ico" 0 NoOwnSource
+ DeleteRegKey HKCR ".erl"
+ DeleteRegKey HKCR "ErlangSource"
+NoOwnSource:
+; .hrl
+ ReadRegStr $1 HKCR ".hrl" ""
+ StrCmp $1 "ErlangHeader" 0 NoOwnHeader
+ ReadRegStr $1 HKCR "ErlangHeader\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\hrl_icon.ico" 0 NoOwnHeader
+ DeleteRegKey HKCR ".hrl"
+ DeleteRegKey HKCR "ErlangHeader"
+NoOwnHeader:
+
+; .beam
+ ReadRegStr $1 HKCR ".beam" ""
+ StrCmp $1 "ErlangBeam" 0 NoOwnBeam
+ ReadRegStr $1 HKCR "ErlangBeam\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\beam_icon.ico" 0 NoOwnBeam
+ DeleteRegKey HKCR ".beam"
+ DeleteRegKey HKCR "ErlangBeam"
+NoOwnBeam:
+
+;Display the Finish header
+; !insertmacro MUI_UNFINISHHEADER
+
+SectionEnd
+ !verbose 3
diff --git a/erts/etc/win32/nsis/erlang_inst.ico b/erts/etc/win32/nsis/erlang_inst.ico Binary files differnew file mode 100644 index 0000000000..edbd8a6f2c --- /dev/null +++ b/erts/etc/win32/nsis/erlang_inst.ico diff --git a/erts/etc/win32/nsis/erlang_uninst.ico b/erts/etc/win32/nsis/erlang_uninst.ico Binary files differnew file mode 100755 index 0000000000..edbd8a6f2c --- /dev/null +++ b/erts/etc/win32/nsis/erlang_uninst.ico diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh new file mode 100755 index 0000000000..c5572839c5 --- /dev/null +++ b/erts/etc/win32/nsis/find_redist.sh @@ -0,0 +1,122 @@ +#! /bin/sh +# +# %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% +# + +# first find some tool we know exists, i.e. cl.exe +lookup_prog_in_path () +{ + PROG=$1 + save_ifs=$IFS + IFS=: + for p in $PATH; do + # In cygwin the programs are not always executable and have .exe suffix... + if [ "X$TARGET" = "Xwin32" ]; then + if [ -f $p/$PROG.exe ]; then + echo $p/$PROG + break; + fi + else + if [ -x $p/$PROG ]; then + echo $p/$PROG + break; + fi + fi + done + IFS=$save_ifs +} + +remove_path_element() +{ + EL=$1 + PA=$2 + ACC="" + save_ifs=$IFS + IFS=/ + set $PA + N=$# + while [ $N -gt 1 ]; do + if [ '!' -z "$1" ]; then + ACC="${ACC}/$1" + fi + N=`expr $N - 1` + shift + done + UP=`echo $1 | tr [:lower:] [:upper:]` + ELUP=`echo $EL | tr [:lower:] [:upper:]` + IFS=$save_ifs + if [ "$UP" = "$ELUP" ]; then + echo "$ACC" + else + echo "${ACC}/$1" + fi + + #echo "ACC=$ACC" >&2 + #echo "1=$1" >&2 +} + +add_path_element() +{ + EL=$1 + PA=$2 + + ELUP=`echo $EL | tr [:lower:] [:upper:]` + #echo "PA=$PA" >&2 + for x in ${PA}/*; do + #echo "X=$x" >&2 + UP=`basename "$x" | tr [:lower:] [:upper:]` + #echo "UP=$UP" >&2 + if [ "$UP" = "$ELUP" ]; then + echo "$x" + return 0; + fi + done + echo "$PA" +} + +CLPATH=`lookup_prog_in_path cl` + +if [ -z "$CLPATH" ]; then + echo "Can not locate cl.exe and vcredist_x86.exe - OK if using mingw" >&2 + exit 1 +fi + +#echo $CLPATH +BPATH=$CLPATH +for x in cl bin vc; do + #echo $x + NBPATH=`remove_path_element $x "$BPATH"` + if [ "$NBPATH" = "$BPATH" ]; then + echo "Failed to locate vcredist_x86.exe because cl.exe was in an unexpected location" >&2 + exit 2 + fi + BPATH="$NBPATH" +done +#echo $BPATH +for x in sdk v2.0 bootstrapper packages vcredist_x86 vcredist_x86.exe; do + #echo "x=$x" + #echo "BPATH=$BPATH" + NBPATH=`add_path_element $x "$BPATH"` + if [ "$NBPATH" = "$BPATH" ]; then + echo "Failed to locate vcredist_x86.exe because directory structure was unexpected" >&2 + exit 3 + fi + BPATH="$NBPATH" +done +echo $BPATH +exit 0
\ No newline at end of file diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c new file mode 100644 index 0000000000..49b5ad2f34 --- /dev/null +++ b/erts/etc/win32/port_entry.c @@ -0,0 +1,75 @@ +/* + * %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% + */ +/* +** This is an entry point for port programs, +** it is used to set the console control handler of the process when +** erlang process is run as a service. +** Note that this entry point is only for +** Console programs, Windowing programs can just route the WM_QUERYENDSESSION +** and WM_ENDSESSION to the default window procedure to aquire the same +** functionality. +** +** Creator Patrik Nyblom +** +** Notes: +** You would really not want to use ANY of the standard library in this +** routine, the standard library is not yet initiated... +*/ +#include <windows.h> + +/* +** The runtime libraries startup routine in the Microsoft Visual C CRT +*/ +extern void mainCRTStartup(void); + +/* +** A Console control handler that ignores the logoff events, +** and lets the default handler take care of other events. +*/ +BOOL WINAPI erl_port_default_handler(DWORD ctrl){ + if(ctrl == CTRL_LOGOFF_EVENT) + return TRUE; + return FALSE; +} + +/* +** This is the entry point, it takes no parameters and never returns. +*/ +void erl_port_entry(void){ + char buffer[2]; + /* + * We assume we're running as a service if this environment variable + * is defined + */ + if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,(DWORD) 2)){ +#ifdef HARDDEBUG + DWORD dummy; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), + "Setting handler\r\n",17,&dummy, NULL); +#endif /* HARDDEBUG */ + /* + ** Actually set the control handler + */ + SetConsoleCtrlHandler(&erl_port_default_handler, TRUE); + } + /* + ** Call the CRT's real startup routine. + */ + mainCRTStartup(); +} diff --git a/erts/etc/win32/resource.h b/erts/etc/win32/resource.h new file mode 100644 index 0000000000..697931952a --- /dev/null +++ b/erts/etc/win32/resource.h @@ -0,0 +1,33 @@ +/* + * %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% + */ +#define IDR_CONMENU 101 +#define IDMENU_STARTLOG 40001 +#define IDMENU_STOPLOG 40002 +#define IDMENU_EXIT 40003 +#define IDMENU_COPY 40004 +#define IDMENU_PASTE 40005 +#define IDMENU_FONT 40006 +#define IDMENU_TOOLBAR 40007 +#define IDMENU_ABOUT 40008 +#define IDMENU_SELALL 40009 +#define IDMENU_SELECTBKG 40010 +#define ID_BREAK 40011 +#define ID_VERSIONSTRING 40000 +#define ID_COMBOBOX 3 + diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c new file mode 100644 index 0000000000..dcf8c8b281 --- /dev/null +++ b/erts/etc/win32/start_erl.c @@ -0,0 +1,677 @@ +/* + * %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% + */ +/* + * start_erl.c - Windows NT start_erl + * + * Author: Mattias Nilsson + */ + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> +#include <assert.h> + +char *progname; + +/* + * If CASE_SENSITIVE_OPTIONS is specified, options are case sensitive + * (lower case). + * The reason for this switch is that _strnicmp is Microsoft specific. + */ +#ifndef CASE_SENSITIVE_OPTIONS +#define strnicmp strncmp +#else +#define strnicmp _strnicmp +#endif + +#define RELEASE_SUBDIR "\\releases" +#define REGISTRY_BASE "Software\\Ericsson\\Erlang\\" +#define DEFAULT_DATAFILE "start_erl.data" + +/* Global variables holding option values and command lines */ +char *CommandLineStart = NULL; +char *ErlCommandLine = NULL; +char *MyCommandLine = NULL; +char *DataFileName = NULL; +char *RelDir = NULL; +char *BootFlagsFile = NULL; +char *BootFlags = NULL; +char *RegistryKey = NULL; +char *BinDir = NULL; +char *RootDir = NULL; +char *VsnDir = NULL; +char *Version = NULL; +char *Release = NULL; +BOOL NoConfig=FALSE; +PROCESS_INFORMATION ErlProcessInfo; + +/* + * Error reason printout 'the microsoft way' + */ +void ShowLastError(void) +{ + LPVOID lpMsgBuf; + DWORD dwErr; + + dwErr = GetLastError(); + if( dwErr == ERROR_SUCCESS ) + return; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + fprintf(stderr, lpMsgBuf); + LocalFree( lpMsgBuf ); +} + +/* + * Exit the program and give a nice and firm explanation of why + * and how you can avoid it. + */ +void exit_help(char *err) +{ + ShowLastError(); + fprintf(stderr, "** Error: %s\n", err); + + printf("Usage:\n%s\n" + " [<erlang options>] ++\n" + " [-data <datafile>]\n" + " [-reldir <releasedir>]\n" + " [-bootflags <bootflagsfile>]\n" + " [-noconfig]\n", progname); + + exit(0); +} + + +/* + * Splits the command line into two strings: + * 1. Options to the Erlang node (ErlCommandLine) + * 2. Options to this program (MyCommandLine) + */ +void split_commandline(void) +{ + char *cmdline = CommandLineStart; + + progname=cmdline; + + /* Remove the first (quoted) string (our program name) */ + if(*cmdline == '"') { + cmdline++; /* Skip " */ + while( (*cmdline != '\0') && (*cmdline++) != '"' ) + ; + } else { + while( (*cmdline != '\0') && (*cmdline++) != ' ' ) + ; + } + + while( (*cmdline) == ' ' ) + cmdline++; + + if( *cmdline == '\0') { + ErlCommandLine = ""; + MyCommandLine = ""; + return; + } + + cmdline[-1] = '\0'; + + /* Start from the end of the string and search for "++ " + (PLUS PLUS SPACE) */ + ErlCommandLine = cmdline; + if(strncmp(cmdline,"++ ",3)) + cmdline = strstr(cmdline," ++ "); + if( cmdline == NULL ) { + MyCommandLine = ""; + return; + } + /* Terminate the ErlCommandLine where MyCommandLine starts */ + *cmdline++ = '\0'; + + /* Skip 'whitespace--whitespace' (WHITESPACE MINUS MINUS WHITESPACE) */ + while( (*cmdline) == ' ' ) + cmdline++; + while( (*cmdline) == '+' ) + cmdline++; + while( (*cmdline) == ' ' ) + cmdline++; + + MyCommandLine = cmdline; + +#ifdef _DEBUG + fprintf(stderr, "ErlCommandLine: '%s'\n", ErlCommandLine); + fprintf(stderr, "MyCommandLine: '%s'\n", MyCommandLine); +#endif +} + + +/* + * 'Smart' unquoting of a string: \" becomes " and " becomes (nothing) + * Skips any leading spaces and parses up to NULL or end of quoted string. + * Calls exit_help() if an unterminated quote is detected. + */ +char * unquote_optionarg(char *str, char **strp) +{ + char *newstr = (char *)malloc(strlen(str)+1); /* This one is realloc:ed later */ + int i=0, inquote=0; + + assert(newstr); + assert(str); + + /* Skip leading spaces */ + while( *str == ' ' ) + str++; + + /* Loop while in quote or until EOS or unquoted space + */ + while( (inquote==1) || ( (*str!=0) && (*str!=' ') ) ) { + switch( *str ) { + case '\\': + /* If we are inside a quoted string we should convert \c to c */ + if( inquote && str[1] == '"' ) + str++; + newstr[i++]=*str++; + break; + case '"': + inquote = 1-inquote; + *str++; + break; + default: + newstr[i++]=*str++; + break; + } + + if( (*str == 0) && (inquote==1) ) { + exit_help("Unterminated quote."); + } + } + newstr[i++] = 0x00; + + /* Update the supplied pointer (used for continued parsing of options) */ + *strp = str; + + /* Adjust memblock of newstr */ + newstr = (char *)realloc(newstr, i); + assert(newstr); + return(newstr); +} + + +/* + * Parses MyCommandLine and tries to fill in all the required option variables + * (one way or another). + */ +void parse_commandline(void) +{ + char *cmdline = MyCommandLine; + + while( *cmdline != '\0' ) { + switch( *cmdline ) { + case '-': /* Handle both -arg and /arg */ + case '/': + *cmdline++; + if( strnicmp(cmdline, "data", 4) == 0) { + DataFileName = unquote_optionarg(cmdline+4, &cmdline); + } else if( strnicmp(cmdline, "reldir", 6) == 0) { + RelDir = unquote_optionarg(cmdline+6, &cmdline); +#ifdef _DEBUG + fprintf(stderr, "RelDir: '%s'\n", RelDir); +#endif + } else if( strnicmp(cmdline, "bootflags", 9) == 0) { + BootFlagsFile = unquote_optionarg(cmdline+9, &cmdline); + } else if( strnicmp(cmdline, "noconfig", 8) == 0) { + NoConfig=TRUE; +#ifdef _DEBUG + fprintf(stderr, "NoConfig=TRUE\n"); +#endif + } else { + fprintf(stderr, "Unkown option: '%s'\n", cmdline); + exit_help("Unknown command line option"); + } + break; + default: + cmdline++; + break; + } + } +} + + +/* + * Read the data file specified and get the version and release number + * from it. + * + * This function also construct the correct RegistryKey from the version information + * retrieved. + */ +void read_datafile(void) +{ + FILE *fp; + char *newname; + long size; + + if(!DataFileName){ + DataFileName = malloc(strlen(DEFAULT_DATAFILE) + 1); + strcpy(DataFileName,DEFAULT_DATAFILE); + } + /* Is DataFileName relative or absolute ? */ + if( (DataFileName[0] != '\\') && (strncmp(DataFileName+1, ":\\", 2)!=0) ) { + /* Relative name, we have to prepend RelDir to it. */ + if( !RelDir ) { + exit_help("Need -reldir when -data filename has relative path."); + } else { + newname = (char *)malloc(strlen(DataFileName)+strlen(RelDir)+2); + assert(newname); + sprintf(newname, "%s\\%s", RelDir, DataFileName); + free(DataFileName); + DataFileName=newname; + } + } + +#ifdef _DEBUG + fprintf(stderr, "DataFileName: '%s'\n", DataFileName); +#endif + + if( (fp=fopen(DataFileName, "rb")) == NULL) { + exit_help("Cannot find the datafile."); + } + + fseek(fp, 0, SEEK_END); + size=ftell(fp); + fseek(fp, 0, SEEK_SET); + + Version = (char *)malloc(size+1); + Release = (char *)malloc(size+1); + assert(Version); + assert(Release); + + if( (fscanf(fp, "%s %s", Version, Release)) == 0) { + fclose(fp); + exit_help("Format error in datafile."); + } + + fclose(fp); + +#ifdef _DEBUG + fprintf(stderr, "DataFile version: '%s'\n", Version); + fprintf(stderr, "DataFile release: '%s'\n", Release); +#endif +} + + +/* + * Read the registry keys we need + */ +void read_registry_keys(void) +{ + HKEY hReg; + ULONG lLen; + + /* Create the RegistryKey name */ + RegistryKey = (char *) malloc(strlen(REGISTRY_BASE) + + strlen(Version) + 1); + assert(RegistryKey); + sprintf(RegistryKey, REGISTRY_BASE "%s", Version); + + /* We always need to find BinDir */ + if( (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + RegistryKey, + 0, + KEY_READ, + &hReg)) != ERROR_SUCCESS ) { + exit_help("Could not open registry key."); + } + + /* First query size of data */ + if( (RegQueryValueEx(hReg, + "Bindir", + NULL, + NULL, + NULL, + &lLen)) != ERROR_SUCCESS) { + exit_help("Failed to query BinDir of release.\n"); + } + + /* Allocate enough space */ + BinDir = (char *)malloc(lLen+1); + assert(BinDir); + /* Retrieve the value */ + if( (RegQueryValueEx(hReg, + "Bindir", + NULL, + NULL, + (unsigned char *) BinDir, + &lLen)) != ERROR_SUCCESS) { + exit_help("Failed to query BinDir of release (2).\n"); + } + +#ifdef _DEBUG + fprintf(stderr, "Bindir: '%s'\n", BinDir); +#endif + + /* We also need the rootdir, in case we need to build RelDir later */ + + /* First query size of data */ + if( (RegQueryValueEx(hReg, + "Rootdir", + NULL, + NULL, + NULL, + &lLen)) != ERROR_SUCCESS) { + exit_help("Failed to query RootDir of release.\n"); + } + + /* Allocate enough space */ + RootDir = (char *) malloc(lLen+1); + assert(RootDir); + /* Retrieve the value */ + if( (RegQueryValueEx(hReg, + "Rootdir", + NULL, + NULL, + (unsigned char *) RootDir, + &lLen)) != ERROR_SUCCESS) { + exit_help("Failed to query RootDir of release (2).\n"); + } + +#ifdef _DEBUG + fprintf(stderr, "Rootdir: '%s'\n", RootDir); +#endif + + RegCloseKey(hReg); +} + +/* + * Read the bootflags. This file contains extra command line options to erl.exe + */ +void read_bootflags(void) +{ + FILE *fp; + long fsize; + char *newname; + + if(BootFlagsFile) { + /* Is BootFlagsFile relative or absolute ? */ + if( (BootFlagsFile[0] != '\\') && + (strncmp(BootFlagsFile+1, ":\\", 2)!=0) ) { + /* Relative name, we have to prepend RelDir\\Version to it. */ + if( !RelDir ) { + exit_help("Need -reldir when -bootflags " + "filename has relative path."); + } else { + newname = (char *)malloc(strlen(BootFlagsFile)+strlen(RelDir)+strlen(Release)+3); + assert(newname); + sprintf(newname, "%s\\%s\\%s", RelDir, Release, BootFlagsFile); + free(BootFlagsFile); + BootFlagsFile=newname; + } + } + +#ifdef _DEBUG + fprintf(stderr, "BootFlagsFile: '%s'\n", BootFlagsFile); +#endif + + + + if( (fp=fopen(BootFlagsFile, "rb")) == NULL) { + exit_help("Could not open BootFlags file."); + } + + fseek(fp, 0, SEEK_END); + fsize=ftell(fp); + fseek(fp, 0, SEEK_SET); + + BootFlags = (char *)malloc(fsize+1); + assert(BootFlags); + if( (fgets(BootFlags, fsize+1, fp)) == NULL) { + exit_help("Error while reading BootFlags file"); + } + fclose(fp); + + /* Adjust buffer size */ + BootFlags = (char *)realloc(BootFlags, strlen(BootFlags)+1); + assert(BootFlags); + + /* Strip \r\n from BootFlags */ + fsize = strlen(BootFlags); + while( fsize > 0 && + ( (BootFlags[fsize-1] == '\r') || + (BootFlags[fsize-1] == '\n') ) ) { + BootFlags[--fsize]=0; + } + + } else { + /* Set empty BootFlags */ + BootFlags = ""; + } + +#ifdef _DEBUG + fprintf(stderr, "BootFlags: '%s'\n", BootFlags); +#endif +} + + +long start_new_node(void) +{ + char *CommandLine; + unsigned long i; + STARTUPINFO si; + DWORD dwExitCode; + + i = strlen(RelDir) + strlen(Release) + 4; + VsnDir = (char *)malloc(i); + assert(VsnDir); + sprintf(VsnDir, "%s\\%s", RelDir, Release); + + if( NoConfig ) { + i = strlen(BinDir) + strlen(ErlCommandLine) + + strlen(BootFlags) + 64; + CommandLine = (char *)malloc(i); + assert(CommandLine); + sprintf(CommandLine, + "\"%s\\erl.exe\" -boot \"%s\\start\" %s %s", + BinDir, + VsnDir, + ErlCommandLine, + BootFlags); + } else { + i = strlen(BinDir) + strlen(ErlCommandLine) + + strlen(BootFlags) + strlen(VsnDir)*2 + 64; + CommandLine = (char *)malloc(i); + assert(CommandLine); + sprintf(CommandLine, + "\"%s\\erl.exe\" -boot \"%s\\start\" -config \"%s\\sys\" %s %s", + BinDir, + VsnDir, + VsnDir, + ErlCommandLine, + BootFlags); + } + +#ifdef _DEBUG + fprintf(stderr, "CommandLine: '%s'\n", CommandLine); +#endif + + /* Initialize the STARTUPINFO structure. */ + memset(&si, 0, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.lpTitle = NULL; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + /* Create the new Erlang process */ + if( (CreateProcess( + NULL, /* pointer to name of executable module */ + CommandLine, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + TRUE, /* handle inheritance flag */ + GetPriorityClass(GetCurrentProcess()), + /* creation flags */ + NULL, /* pointer to new environment block */ + BinDir,/* pointer to current directory name */ + &si, /* pointer to STARTUPINFO */ + &ErlProcessInfo /* pointer to PROCESS_INFORMATION */ + )) == FALSE) { + ShowLastError(); + exit_help("Failed to start new node"); + } + +#ifdef _DEBUG + fprintf(stderr, "Waiting for Erlang to terminate.\n"); +#endif + if(MsgWaitForMultipleObjects(1,&ErlProcessInfo.hProcess, FALSE, + INFINITE, QS_POSTMESSAGE) == WAIT_OBJECT_0+1){ + if(PostThreadMessage(ErlProcessInfo.dwThreadId, + WM_USER, + (WPARAM) 0, + (LPARAM) 0)){ + /* Wait 10 seconds for erl process to die, elsee terminate it. */ + if(WaitForSingleObject(ErlProcessInfo.hProcess, 10000) + != WAIT_OBJECT_0){ + TerminateProcess(ErlProcessInfo.hProcess,0); + } + } else { + TerminateProcess(ErlProcessInfo.hProcess,0); + } + } + GetExitCodeProcess(ErlProcessInfo.hProcess, &dwExitCode); +#ifdef _DEBUG + fprintf(stderr, "Erlang terminated.\n"); +#endif + + free(CommandLine); + return(dwExitCode); +} + + +/* + * Try to make the needed options complete by looking through the data file, + * environment variables and registry entries. + */ +void complete_options(void) +{ + /* Try to find a descent RelDir */ + if( !RelDir ) { + DWORD sz = 32; + while (1) { + DWORD nsz; + if (RelDir) + free(RelDir); + RelDir = malloc(sz); + if (!RelDir) { + fprintf(stderr, "** Error : failed to allocate memory\n"); + exit(1); + } + SetLastError(0); + nsz = GetEnvironmentVariable((LPCTSTR) "RELDIR", + (LPTSTR) RelDir, + sz); + if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + free(RelDir); + RelDir = NULL; + break; + } + else if (nsz <= sz) + break; + else + sz = nsz; + } + if (RelDir == NULL) { + if(DataFileName){ + /* Needs to be absolute for this to work, but we + can try... */ + read_datafile(); + read_registry_keys(); + } else { + /* Impossible to find all data... */ + exit_help("Need either Release directory or an absolute " + "datafile name."); + } + /* Ok, construct our own RelDir from RootDir */ + RelDir = (char *) malloc(strlen(RootDir)+strlen(RELEASE_SUBDIR)+1); + assert(RelDir); + sprintf(RelDir, "%s" RELEASE_SUBDIR, RootDir); + } else { + read_datafile(); + read_registry_keys(); + } + } else { + read_datafile(); + read_registry_keys(); + } + read_bootflags(); + +#ifdef _DEBUG + fprintf(stderr, "RelDir: '%s'\n", RelDir); +#endif +} + + + + +BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType ) +{ + if(dwCtrlType == CTRL_LOGOFF_EVENT) { + return TRUE; + } + + return FALSE; +} + + + + +int main(void) +{ + DWORD dwExitCode; + char *cmdline; + + /* Make sure a logoff does not distrurb us. */ + SetConsoleCtrlHandler(LogoffHandlerRoutine, TRUE); + + cmdline = GetCommandLine(); + assert(cmdline); + + CommandLineStart = (char *) malloc(strlen(cmdline) + 1); + assert(CommandLineStart); + strcpy(CommandLineStart,cmdline); + + split_commandline(); + parse_commandline(); + complete_options(); + + /* We now have all the options we need in order to fire up a new node.. */ + dwExitCode = start_new_node(); + + return( (int) dwExitCode ); +} + + diff --git a/erts/etc/win32/toolbar.bmp b/erts/etc/win32/toolbar.bmp Binary files differnew file mode 100644 index 0000000000..e0df8454fd --- /dev/null +++ b/erts/etc/win32/toolbar.bmp diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c new file mode 100644 index 0000000000..0eed8e28b9 --- /dev/null +++ b/erts/etc/win32/win_erlexec.c @@ -0,0 +1,405 @@ +/* + * %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% + */ +/* + * Extra support for running the emulator on Windows. + * Most of this only used when beam is run as a separate process. + */ + +#pragma comment(linker,"/manifestdependency:\"type='win32' "\ + "name='Microsoft.Windows.Common-Controls' "\ + "version='6.0.0.0' processorArchitecture='*' "\ + "publicKeyToken='6595b64144ccf1df' language='*'\"") + +#include <windows.h> +#include <winuser.h> +#include <wincon.h> +#include <process.h> +#include "sys.h" +#include "erl_driver.h" + +extern int nohup; +extern int keep_window; +void error(char* format, ...); + +/* + * Local functions. + */ +#define LOAD_BEAM_DYNAMICALLY 1 +static int start(char* emu, char** argv); +static void start_winsock(void); +static char* last_error(void); +static char* last_wsa_error(void); +static char* win32_errorstr(int error); +static int has_console(void); +static char** fnuttify_argv(char **argv); +static void free_fnuttified(char **v); +static int windowed = 0; + +#ifdef LOAD_BEAM_DYNAMICALLY +typedef int SysGetKeyFunction(int); +typedef void ErlStartFunction(int, char **); +typedef void SysPrimitiveInitFunction(HMODULE); +static SysGetKeyFunction *sys_get_key_p; +static ErlStartFunction *erl_start_p; +static SysPrimitiveInitFunction *sys_primitive_init_p; + +static HMODULE load_win_beam_dll(char *name) +{ + HMODULE beam_module; + beam_module=LoadLibrary(name); + if (beam_module == INVALID_HANDLE_VALUE || beam_module == NULL) { + error("Unable to load emulator DLL\n(%s)",name); + return NULL; + } + sys_get_key_p = (SysGetKeyFunction *) + GetProcAddress(beam_module, "sys_get_key"); + erl_start_p = (ErlStartFunction *) + GetProcAddress(beam_module, "erl_start"); + sys_primitive_init_p = (SysPrimitiveInitFunction *) + GetProcAddress(beam_module, "sys_primitive_init"); + return beam_module; +} +#endif + +#define DLL_ENV "ERL_EMULATOR_DLL" + +static void +set_env(char *key, char *value) +{ + if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value)) + error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value); +} + +static char * +get_env(char *key) +{ + DWORD size = 32; + char *value = NULL; + while (1) { + DWORD nsz; + if (value) + free(value); + value = malloc(size); + if (!value) + error("GetEnvironmentVariable(\"%s\") failed", key); + 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; + } +} + +free_env_val(char *value) +{ + if (value) + free(value); +} + + +int +start_win_emulator(char* emu, char *start_prog, char** argv, int start_detached) +{ + int result; + + windowed = 1; + if (start_detached) { + char *buff; + close(0); + close(1); + close(2); + + set_env("ERL_CONSOLE_MODE", "detached"); + set_env(DLL_ENV, emu); + + argv[0] = start_prog; + argv = fnuttify_argv(argv); + result = spawnv(_P_DETACH, start_prog, argv); + free_fnuttified(argv); + } else { + int argc = 0; +#ifdef LOAD_BEAM_DYNAMICALLY + HMODULE beam_module = load_win_beam_dll(emu); +#endif + set_env("ERL_CONSOLE_MODE", "window"); + while (argv[argc] != NULL) { + ++argc; + } +#ifdef ARGS_HARDDEBUG + { + char sbuf[2048] = ""; + int i; + for (i = 0; i < argc; ++i) { + strcat(sbuf,"|"); + strcat(sbuf, argv[i]); + strcat(sbuf,"| "); + } + MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR); + } +#endif +#ifdef LOAD_BEAM_DYNAMICALLY + (*sys_primitive_init_p)(beam_module); + (*erl_start_p)(argc,argv); +#else + erl_start(argc, argv); +#endif + result = 0; + } + if (result == -1) { + error("Failed to execute %s: %s", emu, win32_errorstr(_doserrno)); + } + return 0; +} + +void __cdecl +do_keep_window(void) +{ + printf("\nPress any key to close window.\n"); +#ifdef LOAD_BEAM_DYNAMICALLY + (*sys_get_key_p)(0); +#else + sys_get_key(0); +#endif +} + +int +start_emulator(char* emu, char *start_prog, char** argv, int start_detached) +{ + int result; + static char console_mode[] = "tty:ccc"; + char* fd_type; + char* title; + +#ifdef HARDDEBUG + fprintf(stderr,"emu = %s, start_prog = %s\n",emu, start_prog); +#endif + + fd_type = strchr(console_mode, ':'); + fd_type++; + _flushall(); + + /* + * If no console, we will spawn the emulator detached. + */ + + if (start_detached) { + char *buff; + close(0); + close(1); + close(2); + set_env("ERL_CONSOLE_MODE", "detached"); + set_env(DLL_ENV, emu); + + argv[0] = start_prog; + argv = fnuttify_argv(argv); +#ifdef ARGS_HARDDEBUG + { + char buffer[2048]; + int i; + sprintf(buffer,"Start detached [%s]\n",start_prog); + for(i=0;argv[i] != NULL;++i) { + strcat(buffer,"|"); + strcat(buffer,argv[i]); + strcat(buffer,"|\n"); + } + MessageBox(NULL, buffer,"Start detached",MB_OK); + } +#endif + result = spawnv(_P_DETACH, start_prog, argv); + free_fnuttified(argv); + if (result == -1) { +#ifdef ARGS_HARDDEBUG + MessageBox(NULL, "_spawnv failed","Start detached",MB_OK); +#endif + return 1; + } + SetPriorityClass((HANDLE) result, GetPriorityClass(GetCurrentProcess())); + } else { + int argc = 0; +#ifdef LOAD_BEAM_DYNAMICALLY + HMODULE beam_module = load_win_beam_dll(emu); +#endif + + /* + * Start the emulator. + */ + + title = get_env("ERL_WINDOW_TITLE"); + if (title) { + SetConsoleTitle(title); + } + free_env_val(title); + + set_env("ERL_CONSOLE_MODE", console_mode); + while (argv[argc] != NULL) { + ++argc; + } + if (keep_window) { + atexit(do_keep_window); + } +#ifdef ARGS_HARDDEBUG + { + char sbuf[2048] = ""; + int i; + for (i = 0; i < argc; ++i) { + strcat(sbuf,"|"); + strcat(sbuf, argv[i]); + strcat(sbuf,"|\n"); + } + MessageBox(NULL, sbuf, "erl", MB_OK); + } +#endif +#ifdef LOAD_BEAM_DYNAMICALLY + (*sys_primitive_init_p)(beam_module); + (*erl_start_p)(argc,argv); +#else + erl_start(argc, argv); +#endif + } + return 0; +} + +void +error(char* format, ...) +{ + char sbuf[2048]; + va_list ap; + + va_start(ap, format); + vsprintf(sbuf, format, ap); + va_end(ap); + + if (!windowed && has_console()) { + fprintf(stderr, "%s\n", sbuf); + } else { + MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR); + } + exit(1); +} + +static char* +last_error(void) +{ + return win32_errorstr(GetLastError()); +} + +/* + * Returns a human-readable description of the last error. + * The returned pointer will be valid only as long as last-error() + * isn't called again. + */ + +static char* +win32_errorstr(int error) +{ + static LPTSTR lpBufPtr = NULL; + + if (lpBufPtr) + LocalFree(lpBufPtr); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpBufPtr, + 0, + NULL); + SetLastError(error); + return lpBufPtr; +} + +static int +has_console(void) +{ + HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + return 1; + } else { + return 0; + } +} + +static char** fnuttify_argv(char **argv) +{ + char **v; + char *p; + char *q; + int c; + int i; + int n; + int m; + + for (c = 0; argv[c]; ++c) + ; + + v = malloc(sizeof(char *) * (c+1)); + v[c] = NULL; + for (i = 0; i < c; ++i) { + p = argv[i]; + n = m = 0; + while (*p) { + if (*p == ' ') { + m = 2; + } else if (*p == '"') { + m = 2; + ++n; + } + ++p; + } + v[i] = malloc((p - argv[i]) + 1 + n + m); + p = argv[i]; + q = v[i]; + if (n || m) { + if (m) { + *q++ = '"'; + } + while (*p) { + if (*p == '"') { + *q++ = '\\'; + } + *q++ = *p++; + } + if (m) { + *q++ = '"'; + } + *q = '\0'; + } else { + strcpy(q,p); + } + } + return v; +} + +static void free_fnuttified(char **v) +{ + char **t = v; + + while(*t) { + free(*t); + ++t; + } + free(v); +} |