From 6f09315364ddfcc3e23c553cbd8acc30abdc9f65 Mon Sep 17 00:00:00 2001 From: Sam Kottler Date: Fri, 7 Feb 2014 18:13:30 +0100 Subject: [PATCH 01/13] A few changes to get EPEL7 boostrapped and built --- erlang.spec | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/erlang.spec b/erlang.spec index cd39c58..e9e38ba 100644 --- a/erlang.spec +++ b/erlang.spec @@ -5,6 +5,10 @@ # Use %%{nil} for %%{upstream_rel} for tracking source like otp_src_R14B.tar.gz, # and 01 %%{upstream_rel} for tracking source like otp_src_R14B01.tar.gz. +%global need_bootstrap_set 1 + +%{!?need_bootstrap: %global need_bootstrap %{need_bootstrap_set}} + %if 0%{upstream_rel} %global upstream_rel_for_rpm %{upstream_rel} %else @@ -28,7 +32,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.1%{?dist} +Release: %{upstream_rel_for_rpm}.2%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -105,12 +109,15 @@ BuildRequires: m4 %else BuildRequires: fop BuildRequires: libxslt + +%if 0%{?need_bootstrap} < 1 # Required for building docs (escript) BuildRequires: erlang %endif %endif +%endif -%if 0%{?el6}%{?el7}%{?fedora} +%if 0%{?el6}%{?fedora} BuildRequires: emacs BuildRequires: xemacs BuildRequires: emacs-el @@ -130,6 +137,7 @@ Requires: erlang-cosTime%{?_isa} = %{version}-%{release} Requires: erlang-cosTransactions%{?_isa} = %{version}-%{release} Requires: erlang-crypto%{?_isa} = %{version}-%{release} Requires: erlang-debugger%{?_isa} = %{version}-%{release} + Requires: erlang-dialyzer%{?_isa} = %{version}-%{release} Requires: erlang-diameter%{?_isa} = %{version}-%{release} Requires: erlang-edoc%{?_isa} = %{version}-%{release} @@ -530,7 +538,7 @@ Group: Development/Languages Requires: %{name}-erts%{?_isa} = %{version}-%{release} # FIXME see erlang-ic also #Requires: jpackage-utils -%if 0%{?fedora} +%if 0%{?fedora}%{?el7} BuildRequires: java-devel %else %ifarch %{ix86} x86_64 @@ -894,7 +902,7 @@ Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} %description xmerl Provides support for XML 1.0. -%if 0%{?el6}%{?el7}%{?fedora} +%if 0%{?el6}%{?fedora} %package -n emacs-erlang Summary: Compiled elisp files for erlang-mode under GNU Emacs Requires: emacs-common-erlang = %{version}-%{release} @@ -985,7 +993,7 @@ make clean # GNU Emacs/XEmacs related stuff erlang_tools_vsn="$(sed -n 's/TOOLS_VSN = //p' lib/tools/vsn.mk)" -%if 0%{?el6}%{?el7}%{?fedora} +%if 0%{?el6}%{?fedora} # GNU Emacs related stuff cat > emacs-erlang-init.el << EOF (setq load-path (cons "%{_emacs_sitelispdir}/erlang" load-path)) @@ -1032,7 +1040,7 @@ make docs %install rm -rf $RPM_BUILD_ROOT -%if 0%{?el6}%{?el7}%{?fedora} +%if 0%{?el6}%{?fedora} # GNU Emacs/XEmacs related stuff erlang_tools_vsn="$(sed -n 's/TOOLS_VSN = //p' lib/tools/vsn.mk)" @@ -2261,7 +2269,7 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/erlang/man/man3/xmerl_xsd.* %endif -%if 0%{?el6}%{?el7}%{?fedora} +%if 0%{?el6}%{?fedora} %files -n emacs-erlang %dir %{_emacs_sitelispdir}/erlang %doc %{_emacs_sitelispdir}/erlang/README @@ -2283,6 +2291,9 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Fri Feb 7 2014 Sam Kottler - R16B-03.2 +- Fix macro usage for EPEL7 build and added need_bootstrap + * Tue Dec 24 2013 Peter Lemenkov - R16B-03.1 - Ver. R16B03 From e73e8a3d3fa34590794f6faeb9a68115869b4699 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 12 Nov 2014 09:09:56 +0300 Subject: [PATCH 02/13] Trimmed dependency chain Signed-off-by: Peter Lemenkov --- erlang.spec | 219 ++++++++---------- ...ll-internal-hrl-files-when-necessary.patch | 97 ++++++++ otp-0015-Expose-NIF-version.patch | 90 +++++++ ...it-off-webtool-dependency-from-tools.patch | 40 ++++ sources | 3 - 5 files changed, 329 insertions(+), 120 deletions(-) create mode 100644 otp-0014-Install-internal-hrl-files-when-necessary.patch create mode 100644 otp-0015-Expose-NIF-version.patch create mode 100644 otp-0016-Split-off-webtool-dependency-from-tools.patch diff --git a/erlang.spec b/erlang.spec index dbac58a..a221252 100644 --- a/erlang.spec +++ b/erlang.spec @@ -17,13 +17,6 @@ %bcond_without doc -# No fop for EPEL5, and only for x86/x86_64 in EPEL6, so just disable there too -%if 0%{?el5}%{?el6} -%global use_prebuilt_docs 1 -%else -%global use_prebuilt_docs 0 -%endif - %ifarch %{arm} %{ix86} x86_64 ppc %{power64} %global __with_hipe 1 %endif @@ -32,18 +25,16 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.7%{?dist} +Release: %{upstream_rel_for_rpm}.8%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages License: ERPL URL: http://www.erlang.org -Source0: http://www.erlang.org/download/otp_src_%{upstream_ver}%{upstream_rel}-1.tar.gz -%if %{use_prebuilt_docs} -Source1: http://erlang.org/download/otp_doc_html_%{upstream_ver}%{upstream_rel}-1.tar.gz -Source2: http://erlang.org/download/otp_doc_man_%{upstream_ver}%{upstream_rel}-1.tar.gz +%if 0%{?el7}%{?fedora} +VCS: scm:git:https://github.com/erlang/otp %endif -Source4: http://www.erlang.org/download/otp_src_%{upstream_ver}%{upstream_rel}-1.readme +Source0: http://www.erlang.org/download/otp_src_%{upstream_ver}%{upstream_rel}-1.tar.gz Source5: epmd.service Source6: epmd.socket Source7: epmd@.service @@ -104,11 +95,17 @@ Patch12: otp-0012-Add-systemd-support-to-epmd.patch # Fedora specific patch # Added systemd notify support to EPMD Patch13: otp-0013-Added-systemd-notify-support-to-EPMD.patch +# Fedora specific patch +# Install internal hrl files when necessary +Patch14: otp-0014-Install-internal-hrl-files-when-necessary.patch +# Fedora specific patch +# Expose NIF version +Patch15: otp-0015-Expose-NIF-version.patch +# Fedora specific patch +# Split off webtool dependency from tools +Patch16: otp-0016-Split-off-webtool-dependency-from-tools.patch # end of autogenerated patch tag list -# BuildRoot not strictly needed since F10, but keep it for spec file robustness -BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) - BuildRequires: lksctp-tools-devel BuildRequires: ncurses-devel BuildRequires: openssl-devel @@ -116,8 +113,7 @@ BuildRequires: zlib-devel BuildRequires: flex BuildRequires: m4 %if %{with doc} -%if %{use_prebuilt_docs} -%else +# BEWARE. No fop for EPEL5, and only for x86/x86_64 in EPEL6, so we cannot regenerate docs here BuildRequires: fop BuildRequires: libxslt @@ -126,14 +122,11 @@ BuildRequires: libxslt BuildRequires: erlang %endif %endif -%endif -%if 0%{?el6}%{?fedora} BuildRequires: emacs BuildRequires: xemacs BuildRequires: emacs-el BuildRequires: xemacs-packages-extra-el -%endif %if 0%{?el7}%{?fedora} # for @@ -144,63 +137,64 @@ Requires(postun):systemd Requires: systemd %endif -Requires: erlang-appmon%{?_isa} = %{version}-%{release} -Requires: erlang-asn1%{?_isa} = %{version}-%{release} -Requires: erlang-common_test%{?_isa} = %{version}-%{release} -Requires: erlang-compiler%{?_isa} = %{version}-%{release} -Requires: erlang-cosEvent%{?_isa} = %{version}-%{release} -Requires: erlang-cosEventDomain%{?_isa} = %{version}-%{release} -Requires: erlang-cosFileTransfer%{?_isa} = %{version}-%{release} -Requires: erlang-cosNotification%{?_isa} = %{version}-%{release} -Requires: erlang-cosProperty%{?_isa} = %{version}-%{release} -Requires: erlang-cosTime%{?_isa} = %{version}-%{release} -Requires: erlang-cosTransactions%{?_isa} = %{version}-%{release} -Requires: erlang-crypto%{?_isa} = %{version}-%{release} -Requires: erlang-debugger%{?_isa} = %{version}-%{release} +BuildRequires: erlang-rpm-macros -Requires: erlang-dialyzer%{?_isa} = %{version}-%{release} -Requires: erlang-diameter%{?_isa} = %{version}-%{release} -Requires: erlang-edoc%{?_isa} = %{version}-%{release} -Requires: erlang-eldap%{?_isa} = %{version}-%{release} -Requires: erlang-erl_docgen%{?_isa} = %{version}-%{release} -Requires: erlang-erl_interface%{?_isa} = %{version}-%{release} -Requires: erlang-erts%{?_isa} = %{version}-%{release} -Requires: erlang-et%{?_isa} = %{version}-%{release} -Requires: erlang-eunit%{?_isa} = %{version}-%{release} -Requires: erlang-examples%{?_isa} = %{version}-%{release} -Requires: erlang-gs%{?_isa} = %{version}-%{release} -Requires: erlang-hipe%{?_isa} = %{version}-%{release} -Requires: erlang-ic%{?_isa} = %{version}-%{release} -Requires: erlang-inets%{?_isa} = %{version}-%{release} -Requires: erlang-jinterface%{?_isa} = %{version}-%{release} -Requires: erlang-kernel%{?_isa} = %{version}-%{release} -Requires: erlang-megaco%{?_isa} = %{version}-%{release} -Requires: erlang-mnesia%{?_isa} = %{version}-%{release} -Requires: erlang-observer%{?_isa} = %{version}-%{release} -Requires: erlang-odbc%{?_isa} = %{version}-%{release} -Requires: erlang-orber%{?_isa} = %{version}-%{release} -Requires: erlang-os_mon%{?_isa} = %{version}-%{release} -Requires: erlang-otp_mibs%{?_isa} = %{version}-%{release} -Requires: erlang-parsetools%{?_isa} = %{version}-%{release} -Requires: erlang-percept%{?_isa} = %{version}-%{release} -Requires: erlang-pman%{?_isa} = %{version}-%{release} -Requires: erlang-public_key%{?_isa} = %{version}-%{release} -Requires: erlang-reltool%{?_isa} = %{version}-%{release} -Requires: erlang-runtime_tools%{?_isa} = %{version}-%{release} -Requires: erlang-sasl%{?_isa} = %{version}-%{release} -Requires: erlang-snmp%{?_isa} = %{version}-%{release} -Requires: erlang-ssh%{?_isa} = %{version}-%{release} -Requires: erlang-ssl%{?_isa} = %{version}-%{release} -Requires: erlang-stdlib%{?_isa} = %{version}-%{release} -Requires: erlang-syntax_tools%{?_isa} = %{version}-%{release} -Requires: erlang-test_server%{?_isa} = %{version}-%{release} -Requires: erlang-toolbar%{?_isa} = %{version}-%{release} -Requires: erlang-tools%{?_isa} = %{version}-%{release} -Requires: erlang-tv%{?_isa} = %{version}-%{release} -Requires: erlang-typer%{?_isa} = %{version}-%{release} -Requires: erlang-webtool%{?_isa} = %{version}-%{release} -Requires: erlang-wx%{?_isa} = %{version}-%{release} -Requires: erlang-xmerl%{?_isa} = %{version}-%{release} +Requires: %{name}-appmon%{?_isa} = %{version}-%{release} +Requires: %{name}-asn1%{?_isa} = %{version}-%{release} +Requires: %{name}-common_test%{?_isa} = %{version}-%{release} +Requires: %{name}-compiler%{?_isa} = %{version}-%{release} +Requires: %{name}-cosEvent%{?_isa} = %{version}-%{release} +Requires: %{name}-cosEventDomain%{?_isa} = %{version}-%{release} +Requires: %{name}-cosFileTransfer%{?_isa} = %{version}-%{release} +Requires: %{name}-cosNotification%{?_isa} = %{version}-%{release} +Requires: %{name}-cosProperty%{?_isa} = %{version}-%{release} +Requires: %{name}-cosTime%{?_isa} = %{version}-%{release} +Requires: %{name}-cosTransactions%{?_isa} = %{version}-%{release} +Requires: %{name}-crypto%{?_isa} = %{version}-%{release} +Requires: %{name}-debugger%{?_isa} = %{version}-%{release} +Requires: %{name}-dialyzer%{?_isa} = %{version}-%{release} +Requires: %{name}-diameter%{?_isa} = %{version}-%{release} +Requires: %{name}-edoc%{?_isa} = %{version}-%{release} +Requires: %{name}-eldap%{?_isa} = %{version}-%{release} +Requires: %{name}-erl_docgen%{?_isa} = %{version}-%{release} +Requires: %{name}-erl_interface%{?_isa} = %{version}-%{release} +Requires: %{name}-erts%{?_isa} = %{version}-%{release} +Requires: %{name}-et%{?_isa} = %{version}-%{release} +Requires: %{name}-eunit%{?_isa} = %{version}-%{release} +Requires: %{name}-examples%{?_isa} = %{version}-%{release} +Requires: %{name}-gs%{?_isa} = %{version}-%{release} +Requires: %{name}-hipe%{?_isa} = %{version}-%{release} +Requires: %{name}-ic%{?_isa} = %{version}-%{release} +Requires: %{name}-inets%{?_isa} = %{version}-%{release} +Requires: %{name}-jinterface%{?_isa} = %{version}-%{release} +Requires: %{name}-kernel%{?_isa} = %{version}-%{release} +Requires: %{name}-megaco%{?_isa} = %{version}-%{release} +Requires: %{name}-mnesia%{?_isa} = %{version}-%{release} +Requires: %{name}-observer%{?_isa} = %{version}-%{release} +Requires: %{name}-odbc%{?_isa} = %{version}-%{release} +Requires: %{name}-orber%{?_isa} = %{version}-%{release} +Requires: %{name}-os_mon%{?_isa} = %{version}-%{release} +Requires: %{name}-otp_mibs%{?_isa} = %{version}-%{release} +Requires: %{name}-parsetools%{?_isa} = %{version}-%{release} +Requires: %{name}-percept%{?_isa} = %{version}-%{release} +Requires: %{name}-pman%{?_isa} = %{version}-%{release} +Requires: %{name}-public_key%{?_isa} = %{version}-%{release} +Requires: %{name}-reltool%{?_isa} = %{version}-%{release} +Requires: %{name}-runtime_tools%{?_isa} = %{version}-%{release} +Requires: %{name}-sasl%{?_isa} = %{version}-%{release} +Requires: %{name}-snmp%{?_isa} = %{version}-%{release} +Requires: %{name}-ssh%{?_isa} = %{version}-%{release} +Requires: %{name}-ssl%{?_isa} = %{version}-%{release} +Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} +Requires: %{name}-syntax_tools%{?_isa} = %{version}-%{release} +Requires: %{name}-test_server%{?_isa} = %{version}-%{release} +Requires: %{name}-toolbar%{?_isa} = %{version}-%{release} +Requires: %{name}-tools%{?_isa} = %{version}-%{release} +Requires: %{name}-tv%{?_isa} = %{version}-%{release} +Requires: %{name}-typer%{?_isa} = %{version}-%{release} +Requires: %{name}-webtool%{?_isa} = %{version}-%{release} +Requires: %{name}-wx%{?_isa} = %{version}-%{release} +Requires: %{name}-xmerl%{?_isa} = %{version}-%{release} Obsoletes: erlang-docbuilder Obsoletes: erlang-inviso @@ -405,15 +399,15 @@ Requires: %{name}-syntax_tools%{?_isa} = %{version}-%{release} %description diameter Diameter (RFC 3588) library +%if %{with doc} %package doc Summary: Erlang documentation Group: Development/Languages -%if 0%{?el6}%{?el7}%{?fedora} BuildArch: noarch -%endif %description doc Documentation for Erlang. +%endif %package edoc Summary: A utility used to generate documentation out of tags in source files @@ -464,8 +458,12 @@ Summary: Functionality necessary to run the Erlang System itself Group: Development/Languages Requires: %{name}-kernel%{?_isa} = %{version}-%{release} Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} +# This library is dlopened so it can't be picked up automatically by the RPM +# dependency checker Requires: lksctp-tools +# See erts/emulator/beam/erl_driver.h or call erlang:system_info(driver_version). Provides: erlang(erl_drv_version) = 2.2 +# See erts/emulator/beam/erl_nif.h or call erlang:system_info(nif_version). Provides: erlang(erl_nif_version) = 2.4 %description erts @@ -558,7 +556,7 @@ Group: Development/Languages Requires: %{name}-erts%{?_isa} = %{version}-%{release} # FIXME see erlang-ic also #Requires: jpackage-utils -%if 0%{?fedora}%{?el7} +%if 0%{?el7}%{?fedora} BuildRequires: java-devel %else %ifarch %{ix86} x86_64 @@ -788,7 +786,6 @@ Secure Shell application with sftp and ssh support. %package ssl Summary: Secure Socket Layer support Group: Development/Languages -#Requires: %{name}-asn1%{?_isa} = %{version}-%{release} Requires: %{name}-crypto%{?_isa} = %{version}-%{release} Requires: %{name}-erts%{?_isa} = %{version}-%{release} Requires: %{name}-kernel%{?_isa} = %{version}-%{release} @@ -828,7 +825,6 @@ Requires: %{name}-inets%{?_isa} = %{version}-%{release} Requires: %{name}-kernel%{?_isa} = %{version}-%{release} Requires: %{name}-observer%{?_isa} = %{version}-%{release} Requires: %{name}-runtime_tools%{?_isa} = %{version}-%{release} -#Requires: %{name}-sasl%{?_isa} = %{version}-%{release} Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} Requires: %{name}-tools%{?_isa} = %{version}-%{release} @@ -855,7 +851,12 @@ Requires: %{name}-inets%{?_isa} = %{version}-%{release} Requires: %{name}-kernel%{?_isa} = %{version}-%{release} Requires: %{name}-runtime_tools%{?_isa} = %{version}-%{release} Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} -Requires: %{name}-webtool%{?_isa} = %{version}-%{release} +# This is a weak dependency triggered by the "cover_web" application. +# Unfortunately Recommends/Suggests tags are supported only in Fedora 21+ and +# RHEL 8+ (eventually) +%if 0%{?fedora} >= 21 || 0%{?rhel} >= 8 +Suggests: %{name}-webtool%{?_isa} = %{version}-%{release} +%endif Provides: emacs-common-erlang = %{version}-%{release} %description tools @@ -922,7 +923,6 @@ Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} %description xmerl Provides support for XML 1.0. -%if 0%{?el6}%{?fedora} %package -n emacs-erlang Summary: Compiled elisp files for erlang-mode under GNU Emacs Requires: emacs-common-erlang = %{version}-%{release} @@ -960,7 +960,6 @@ BuildArch: noarch %description -n xemacs-erlang-el Erlang mode for XEmacs (source lisp files). -%endif %prep %setup -q -n otp_src_%{upstream_ver}%{upstream_rel}-1 @@ -983,6 +982,9 @@ Erlang mode for XEmacs (source lisp files). %patch11 -p1 -b .Add_systemd_option_to_empd_Check_for_include_system %patch12 -p1 -b .Add_systemd_support_to_epmd %patch13 -p1 -b .Added_systemd_notify_support_to_EPMD +%patch14 -p1 -b .Install_internal_hrl_files_when_necessary +%patch15 -p1 -b .Expose_NIF_version +%patch16 -p1 -b .Split_off_webtool_dependency_from_tools # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -1005,9 +1007,9 @@ rm -f lib/ssl/examples/certs/etc/erlangCA/index.txt.old %build %ifarch sparcv9 sparc64 -CFLAGS="$RPM_OPT_FLAGS -mcpu=ultrasparc -fno-strict-aliasing" %configure --enable-shared-zlib --enable-sctp %{?__with_hipe:--enable-hipe} +CFLAGS="$RPM_OPT_FLAGS -mcpu=ultrasparc -fno-strict-aliasing" %configure --enable-shared-zlib --enable-sctp --enable-systemd %{?__with_hipe:--enable-hipe} %else -CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing" %configure --enable-shared-zlib --enable-sctp %{?__with_hipe:--enable-hipe} +CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing" %configure --enable-shared-zlib --enable-sctp --enable-systemd %{?__with_hipe:--enable-hipe} %endif # Remove pre-built BEAM files @@ -1016,7 +1018,6 @@ make clean # GNU Emacs/XEmacs related stuff erlang_tools_vsn="$(sed -n 's/TOOLS_VSN = //p' lib/tools/vsn.mk)" -%if 0%{?el6}%{?fedora} # GNU Emacs related stuff cat > emacs-erlang-init.el << EOF (setq load-path (cons "%{_emacs_sitelispdir}/erlang" load-path)) @@ -1043,12 +1044,10 @@ rm -f xemacs-erlang/erlang-flymake.el pushd xemacs-erlang %{_xemacs_bytecompile} *.el popd -%endif make + %if %{with doc} -%if %{use_prebuilt_docs} -%else # should use FOP_OPTS after #832323 is resolved %ifnarch ppc %{power64} export BASE_OPTIONS=-Xmx1024m @@ -1057,13 +1056,9 @@ export BASE_OPTIONS=-Xmx1536m %endif make docs %endif -%endif %install -rm -rf $RPM_BUILD_ROOT - -%if 0%{?el6}%{?fedora} # GNU Emacs/XEmacs related stuff erlang_tools_vsn="$(sed -n 's/TOOLS_VSN = //p' lib/tools/vsn.mk)" @@ -1089,25 +1084,12 @@ for f in lib/tools/emacs/{README,*.el}; do done rm -f "$RPM_BUILD_ROOT%{_xemacs_sitelispdir}/erlang/erlang-flymake.el" install -m 0644 xemacs-erlang/*.elc "$RPM_BUILD_ROOT%{_xemacs_sitelispdir}/erlang/" -%endif make DESTDIR=$RPM_BUILD_ROOT install + %if %{with doc} -%if %{use_prebuilt_docs} -# extract prebuilt docs and man-pages -tar xf %{SOURCE1} -C $RPM_BUILD_ROOT%{_libdir}/erlang -tar xf %{SOURCE2} -C $RPM_BUILD_ROOT%{_libdir}/erlang -# rm Win32-specific functionality -rm -f $RPM_BUILD_ROOT%{_libdir}/erlang/man/man3/nteventlog.* -# rm VxWorks specific -rm -f $RPM_BUILD_ROOT%{_libdir}/erlang/man/man3/erl_set_memory_block.* -# rm unneeded files -rm -f $RPM_BUILD_ROOT%{_libdir}/erlang/erts-*/info -rm -f $RPM_BUILD_ROOT%{_libdir}/erlang/lib/*-*/info -%else env ERL_LIBS="$RPM_BUILD_ROOT%{_libdir}/erlang/lib" make DESTDIR=$RPM_BUILD_ROOT install-docs %endif -%endif # Do not install info files - they are almost empty and useless find $RPM_BUILD_ROOT%{_libdir}/erlang -type f -name info -exec rm -f {} \; @@ -1194,10 +1176,6 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ -c "Erlang Port Mapper Daemon" epmd 2>/dev/null || : -%clean -rm -rf $RPM_BUILD_ROOT - - %files %if %{with doc} %dir %{_docdir}/%{n_uvr}/ @@ -1410,8 +1388,8 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/erlang/man/man4/diameter_dict.* %endif -%files doc %if %{with doc} +%files doc %doc %{_docdir}/%{n_uvr}/doc %doc %{_docdir}/%{n_uvr}/erts-*/ %doc %{_docdir}/%{n_uvr}/lib/ @@ -1436,7 +1414,9 @@ rm -rf $RPM_BUILD_ROOT %files erl_docgen %{_libdir}/erlang/lib/erl_docgen-*/ +%if %{with doc} %{_libdir}/erlang/man/man6/erl_docgen.* +%endif %files erl_interface %{_libdir}/erlang/lib/erl_interface-*/ @@ -1455,7 +1435,9 @@ rm -rf $RPM_BUILD_ROOT %endif %files erts -# TODO these directories should be packaged separately +# TODO +# In order to have a parallel-installable Erlang packages these directories +# should be packaged separately %dir %{_libdir}/erlang/ %dir %{_libdir}/erlang/bin/ %dir %{_libdir}/erlang/lib/ @@ -1556,6 +1538,7 @@ rm -rf $RPM_BUILD_ROOT %dir %{_libdir}/erlang/lib/eunit-*/ %{_libdir}/erlang/lib/eunit-*/ebin %{_libdir}/erlang/lib/eunit-*/include +%{_libdir}/erlang/lib/eunit-*/src %if %{with doc} %{_libdir}/erlang/man/man3/eunit.* %{_libdir}/erlang/man/man3/eunit_surefire.* @@ -2313,7 +2296,6 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/erlang/man/man3/xmerl_xsd.* %endif -%if 0%{?el6}%{?fedora} %files -n emacs-erlang %dir %{_emacs_sitelispdir}/erlang %doc %{_emacs_sitelispdir}/erlang/README @@ -2331,10 +2313,13 @@ rm -rf $RPM_BUILD_ROOT %files -n xemacs-erlang-el %{_xemacs_sitelispdir}/erlang/*.el -%endif %changelog +* Tue Nov 11 2014 Peter Lemenkov - R16B-03.8 +- Trimmed dependency chain +- Cleaned up spec-file + * Wed Jun 11 2014 Peter Lemenkov - R16B-03.7 - Added missing template for epmd@.socket diff --git a/otp-0014-Install-internal-hrl-files-when-necessary.patch b/otp-0014-Install-internal-hrl-files-when-necessary.patch new file mode 100644 index 0000000..14cf312 --- /dev/null +++ b/otp-0014-Install-internal-hrl-files-when-necessary.patch @@ -0,0 +1,97 @@ +From: Peter Lemenkov +Date: Tue, 26 Aug 2014 13:53:49 +0400 +Subject: [PATCH] Install internal hrl files when necessary + +Sometimes we install *.erl files. Some these files include a private +*.hrl files, so in order to make these *.erl files usable we have to +install these private includes as well. + +Signed-off-by: Peter Lemenkov + +Conflicts: + lib/eunit/src/Makefile + lib/percept/src/Makefile + lib/test_server/src/Makefile + +diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile +index 5a6f298..b40ef23 100644 +--- a/lib/debugger/src/Makefile ++++ b/lib/debugger/src/Makefile +@@ -77,7 +77,7 @@ MODULES= \ + + HRL_FILES= + +-INTERNAL_HRL_FILES= dbg_ieval.hrl ++INTERNAL_HRL_FILES= dbg_ieval.hrl dbg_wx_filedialog_win.hrl + + ERL_FILES= $(MODULES:%=%.erl) + +diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile +index a5e147d..8483e28 100644 +--- a/lib/eunit/src/Makefile ++++ b/lib/eunit/src/Makefile +@@ -46,6 +46,8 @@ SOURCES= \ + + INCLUDE_FILES = eunit.hrl + ++INTERNAL_HRL_FILES= eunit_internal.hrl ++ + PARSE_TRANSFORM_BIN = $(PARSE_TRANSFORM:%.erl=$(EBIN)/%.$(EMULATOR)) + + TARGET_FILES= $(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) +@@ -117,6 +119,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk + release_spec: opt + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) "$(RELSYSDIR)/ebin" ++ $(INSTALL_DIR) "$(RELSYSDIR)/src" ++ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) "$(RELSYSDIR)/include" + +diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile +index dbda2a2..3e1792f 100644 +--- a/lib/kernel/src/Makefile ++++ b/lib/kernel/src/Makefile +@@ -122,6 +122,7 @@ HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \ + ../include/net_address.hrl + + INTERNAL_HRL_FILES= application_master.hrl disk_log.hrl \ ++ erl_epmd.hrl hipe_ext_format.hrl \ + inet_dns.hrl inet_res.hrl \ + inet_boot.hrl inet_config.hrl inet_int.hrl \ + inet_dns_record_adts.hrl +diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile +index e501539..5902da3 100644 +--- a/lib/percept/src/Makefile ++++ b/lib/percept/src/Makefile +@@ -50,6 +50,8 @@ MODULES= \ + + #HRL_FILES= ../include/ + ++INTERNAL_HRL_FILES= egd.hrl percept.hrl ++ + ERL_FILES= $(MODULES:%=%.erl) + + TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) +@@ -93,6 +95,8 @@ docs: + include $(ERL_TOP)/make/otp_release_targets.mk + + release_spec: opt ++ $(INSTALL_DIR) "$(RELSYSDIR)/src" ++ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + # $(INSTALL_DIR) "$(RELSYSDIR)/include" + # $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" +diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile +index bcb1bc3..deb7caf 100644 +--- a/lib/test_server/src/Makefile ++++ b/lib/test_server/src/Makefile +@@ -123,7 +123,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk + + release_spec: opt + $(INSTALL_DIR) "$(RELSYSDIR)/src" +- $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" ++ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" diff --git a/otp-0015-Expose-NIF-version.patch b/otp-0015-Expose-NIF-version.patch new file mode 100644 index 0000000..3ae0096 --- /dev/null +++ b/otp-0015-Expose-NIF-version.patch @@ -0,0 +1,90 @@ +From: Peter Lemenkov +Date: Sun, 2 Nov 2014 19:49:55 +0300 +Subject: [PATCH] Expose NIF version + +This patch allows checking for NIF API version in a way similar to +driver version. E.g. by calling erlang:system_info(nif_version). + +Signed-off-by: Peter Lemenkov + +Conflicts: + erts/emulator/test/driver_SUITE.erl + +diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml +index e3ef48a..b9d7230 100644 +--- a/erts/doc/src/erlang.xml ++++ b/erts/doc/src/erlang.xml +@@ -5903,6 +5903,11 @@ ok + erlang:system_info(multi_scheduling), and + erlang:system_info(schedulers).

+ ++ nif_version ++ ++

Returns a string containing the erlang NIF version ++ used by the runtime system. It will be on the form "<major ver>.<minor ver>".

++
+ otp_release + +

Returns a string containing the OTP release number.

+diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c +index d7f1e2d..7a7f23e 100755 +--- a/erts/emulator/beam/erl_bif_info.c ++++ b/erts/emulator/beam/erl_bif_info.c +@@ -27,6 +27,7 @@ + #include "erl_process.h" + #include "error.h" + #include "erl_driver.h" ++#include "erl_nif.h" + #include "bif.h" + #include "big.h" + #include "erl_version.h" +@@ -2428,6 +2429,13 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) + ERL_DRV_EXTENDED_MINOR_VERSION); + hp = HAlloc(BIF_P, 2*n); + BIF_RET(buf_to_intlist(&hp, buf, n, NIL)); ++ } else if (ERTS_IS_ATOM_STR("nif_version", BIF_ARG_1)) { ++ char buf[42]; ++ int n = erts_snprintf(buf, 42, "%d.%d", ++ ERL_NIF_MAJOR_VERSION, ++ ERL_NIF_MINOR_VERSION); ++ hp = HAlloc(BIF_P, 2*n); ++ BIF_RET(buf_to_intlist(&hp, buf, n, NIL)); + } else if (ERTS_IS_ATOM_STR("smp_support", BIF_ARG_1)) { + #ifdef ERTS_SMP + BIF_RET(am_true); +diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl +index 2919265..c0f14ad 100644 +--- a/erts/preloaded/src/erlang.erl ++++ b/erts/preloaded/src/erlang.erl +@@ -2124,6 +2124,7 @@ tuple_to_list(_Tuple) -> + (modified_timing_level) -> integer() | undefined; + (multi_scheduling) -> disabled | blocked | enabled; + (multi_scheduling_blockers) -> [PID :: pid()]; ++ (nif_version) -> string(); + (otp_release) -> string(); + (port_count) -> non_neg_integer(); + (port_limit) -> pos_integer(); +diff --git a/lib/runtime_tools/src/system_information.erl b/lib/runtime_tools/src/system_information.erl +index 1d4b878..64f1b82 100644 +--- a/lib/runtime_tools/src/system_information.erl ++++ b/lib/runtime_tools/src/system_information.erl +@@ -344,6 +344,7 @@ erlang_system_info() -> + logical_processors_online, + logical_processors_available, + driver_version, ++ nif_version, + emu_args, + ethread_info, + beam_jump_table, +diff --git a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat +index 0900ead..9ded5a1 100644 +--- a/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat ++++ b/lib/runtime_tools/test/system_information_SUITE_data/information_test_report.dat +@@ -9720,6 +9720,7 @@ + {logical_processors_online,4}, + {logical_processors_available,4}, + {driver_version,"2.1"}, ++ {nif_version,"1.1"}, + {taints,[]}]}, + {erts_compile_info, + [{ldflags,[]}, diff --git a/otp-0016-Split-off-webtool-dependency-from-tools.patch b/otp-0016-Split-off-webtool-dependency-from-tools.patch new file mode 100644 index 0000000..f2de8ac --- /dev/null +++ b/otp-0016-Split-off-webtool-dependency-from-tools.patch @@ -0,0 +1,40 @@ +From: Peter Lemenkov +Date: Sat, 8 Nov 2014 22:54:57 +0300 +Subject: [PATCH] Split off webtool dependency from tools + +Signed-off-by: Peter Lemenkov + +diff --git a/lib/tools/src/cover_web.erl b/lib/tools/src/cover_web.erl +index 69f2f3b..9085300 100644 +--- a/lib/tools/src/cover_web.erl ++++ b/lib/tools/src/cover_web.erl +@@ -50,14 +50,25 @@ + %%%---------------------------------------------------------------------- + %% Start webtool and webcover from erlang shell + start() -> +- webtool:start(), +- webtool:start_tools([],"app=webcover"), ++ try ++ % Disable automatic dependency picking up ++ erlang:apply(webtool, start, []), ++ erlang:apply(webtool, start_tools, [[],"app=webcover"]) ++ catch ++ error:undef -> error_logger:error_msg("No erlang-webtool found.~nPlease install erlang-webtool package first.~n") ++ end, + ok. + + %% Stop webtool and webcover from erlang shell + stop() -> +- webtool:stop_tools([],"app=webcover"), +- webtool:stop(). ++ try ++ % Disable automatic dependency picking up ++ erlang:apply(webtool, stop_tools, [[],"app=webcover"]), ++ erlang:apply(webtool, stop, []) ++ catch ++ error:undef -> error_logger:error_msg("No erlang-webtool found.~nPlease install erlang-webtool package first.~n") ++ end, ++ ok. + + + diff --git a/sources b/sources index df29746..aa69160 100644 --- a/sources +++ b/sources @@ -1,4 +1 @@ -d27250e9ee98d6388e7f2e65379a0406 otp_src_R16B03-1.readme -eff44490c9bbae3a5c5741bec2390ba3 otp_doc_html_R16B03-1.tar.gz -39113c0d2515bdd8cd7e0f975a380122 otp_doc_man_R16B03-1.tar.gz e5ece977375197338c1b93b3d88514f8 otp_src_R16B03-1.tar.gz From 03a198125ba8d6cf720b02d9b2ade7fb7e8a1d75 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 17 Nov 2014 16:43:43 +0300 Subject: [PATCH 03/13] Fixed CVE-2014-1693 (backported fix from ver. 17.x.x, see patch no. 17) Signed-off-by: Peter Lemenkov --- erlang.spec | 9 +- ...p-ftp.erl-Check-the-filenames-userna.patch | 430 ++++++++++++++++++ 2 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch diff --git a/erlang.spec b/erlang.spec index a221252..659fdbd 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.8%{?dist} +Release: %{upstream_rel_for_rpm}.9%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -104,6 +104,9 @@ Patch15: otp-0015-Expose-NIF-version.patch # Fedora specific patch # Split off webtool dependency from tools Patch16: otp-0016-Split-off-webtool-dependency-from-tools.patch +# Fedora specific patch +# lib/inets/src/ftp/ftp.erl: Check the filenames, usernames, +Patch17: otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -985,6 +988,7 @@ Erlang mode for XEmacs (source lisp files). %patch14 -p1 -b .Install_internal_hrl_files_when_necessary %patch15 -p1 -b .Expose_NIF_version %patch16 -p1 -b .Split_off_webtool_dependency_from_tools +%patch17 -p1 -b .lib_inets_src_ftp_ftp_erl_Check_the_filenames_userna # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2316,6 +2320,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Mon Nov 17 2014 Peter Lemenkov - R16B-03.9 +- Fixed CVE-2014-1693 (backported fix from ver. 17.x.x, see patch no. 17) + * Tue Nov 11 2014 Peter Lemenkov - R16B-03.8 - Trimmed dependency chain - Cleaned up spec-file diff --git a/otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch b/otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch new file mode 100644 index 0000000..7b7ad77 --- /dev/null +++ b/otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch @@ -0,0 +1,430 @@ +From: Sergei Golovan +Date: Sun, 9 Feb 2014 23:06:25 +0400 +Subject: [PATCH] lib/inets/src/ftp/ftp.erl: Check the filenames, usernames, + passwords etc. for and in them and return error if these + offending chars are found. See + http://erlang.org/pipermail/erlang-bugs/2014-January/003998.html for + details. lib/inets/test/ftp_suite_lib.erl: Added checks for in file + and directory names. + + +diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl +index 520db1b..5674599 100644 +--- a/lib/inets/src/ftp/ftp.erl ++++ b/lib/inets/src/ftp/ftp.erl +@@ -192,7 +192,12 @@ do_open(Pid, OpenOptions, TLSOpts) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + + user(Pid, User, Pass) -> +- call(Pid, {user, User, Pass}, atom). ++ case {is_name_sane(User), is_name_sane(Pass)} of ++ {true, true} -> ++ call(Pid, {user, User, Pass}, atom); ++ _ -> ++ {error, euser} ++ end. + + -spec user(Pid :: pid(), + User :: string(), +@@ -201,7 +206,12 @@ user(Pid, User, Pass) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + + user(Pid, User, Pass, Acc) -> +- call(Pid, {user, User, Pass, Acc}, atom). ++ case {is_name_sane(User), is_name_sane(Pass), is_name_sane(Acc)} of ++ {true, true, true} -> ++ call(Pid, {user, User, Pass, Acc}, atom); ++ _ -> ++ {error, euser} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -216,7 +226,12 @@ user(Pid, User, Pass, Acc) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + + account(Pid, Acc) -> +- call(Pid, {account, Acc}, atom). ++ case is_name_sane(Acc) of ++ true -> ++ call(Pid, {account, Acc}, atom); ++ _ -> ++ {error, eacct} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -262,7 +277,12 @@ lpwd(Pid) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + cd(Pid, Dir) -> +- call(Pid, {cd, Dir}, atom). ++ case is_name_sane(Dir) of ++ true -> ++ call(Pid, {cd, Dir}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -305,7 +325,12 @@ ls(Pid) -> + {'error', Reason :: restriction_reason() | common_reason()}. + + ls(Pid, Dir) -> +- call(Pid, {dir, long, Dir}, string). ++ case is_name_sane(Dir) of ++ true -> ++ call(Pid, {dir, long, Dir}, string); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -333,7 +358,12 @@ nlist(Pid) -> + {'error', Reason :: restriction_reason() | common_reason()}. + + nlist(Pid, Dir) -> +- call(Pid, {dir, short, Dir}, string). ++ case is_name_sane(Dir) of ++ true -> ++ call(Pid, {dir, short, Dir}, string); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -349,7 +379,12 @@ nlist(Pid, Dir) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + rename(Pid, Old, New) -> +- call(Pid, {rename, Old, New}, string). ++ case {is_name_sane(Old), is_name_sane(New)} of ++ {true, true} -> ++ call(Pid, {rename, Old, New}, string); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -365,7 +400,12 @@ rename(Pid, Old, New) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + delete(Pid, File) -> +- call(Pid, {delete, File}, string). ++ case is_name_sane(File) of ++ true -> ++ call(Pid, {delete, File}, string); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -380,7 +420,12 @@ delete(Pid, File) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + mkdir(Pid, Dir) -> +- call(Pid, {mkdir, Dir}, atom). ++ case is_name_sane(Dir) of ++ true -> ++ call(Pid, {mkdir, Dir}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -395,7 +440,12 @@ mkdir(Pid, Dir) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + rmdir(Pid, Dir) -> +- call(Pid, {rmdir, Dir}, atom). ++ case is_name_sane(Dir) of ++ true -> ++ call(Pid, {rmdir, Dir}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -437,7 +487,12 @@ recv(Pid, RemotFileName) -> + 'ok' | {'error', Reason :: term()}. + + recv(Pid, RemotFileName, LocalFileName) -> +- call(Pid, {recv, RemotFileName, LocalFileName}, atom). ++ case is_name_sane(RemotFileName) of ++ true -> ++ call(Pid, {recv, RemotFileName, LocalFileName}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -456,7 +511,12 @@ recv(Pid, RemotFileName, LocalFileName) -> + {'error', Reason :: restriction_reason() | common_reason()}. + + recv_bin(Pid, RemoteFile) -> +- call(Pid, {recv_bin, RemoteFile}, bin). ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {recv_bin, RemoteFile}, bin); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -473,7 +533,12 @@ recv_bin(Pid, RemoteFile) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + recv_chunk_start(Pid, RemoteFile) -> +- call(Pid, {recv_chunk_start, RemoteFile}, atom). ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {recv_chunk_start, RemoteFile}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -521,7 +586,12 @@ send(Pid, LocalFileName) -> + shortage_reason()}. + + send(Pid, LocalFileName, RemotFileName) -> +- call(Pid, {send, LocalFileName, RemotFileName}, atom). ++ case is_name_sane(RemotFileName) of ++ true -> ++ call(Pid, {send, LocalFileName, RemotFileName}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -541,7 +611,12 @@ send(Pid, LocalFileName, RemotFileName) -> + shortage_reason()}. + + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> +- call(Pid, {send_bin, Bin, RemoteFile}, atom); ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {send_bin, Bin, RemoteFile}, atom); ++ _ -> ++ {error, efnamena} ++ end; + send_bin(_Pid, _Bin, _RemoteFile) -> + {error, enotbinary}. + +@@ -559,7 +634,12 @@ send_bin(_Pid, _Bin, _RemoteFile) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + + send_chunk_start(Pid, RemoteFile) -> +- call(Pid, {send_chunk_start, RemoteFile}, atom). ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {send_chunk_start, RemoteFile}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -575,7 +655,12 @@ send_chunk_start(Pid, RemoteFile) -> + 'ok' | {'error', Reason :: term()}. + + append_chunk_start(Pid, RemoteFile) -> +- call(Pid, {append_chunk_start, RemoteFile}, atom). ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {append_chunk_start, RemoteFile}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -683,7 +768,12 @@ append(Pid, LocalFileName) -> + 'ok' | {'error', Reason :: term()}. + + append(Pid, LocalFileName, RemotFileName) -> +- call(Pid, {append, LocalFileName, RemotFileName}, atom). ++ case is_name_sane(RemotFileName) of ++ true -> ++ call(Pid, {append, LocalFileName, RemotFileName}, atom); ++ _ -> ++ {error, efnamena} ++ end. + + + %%-------------------------------------------------------------------------- +@@ -705,7 +795,12 @@ append(Pid, LocalFileName, RemotFileName) -> + shortage_reason()}. + + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> +- call(Pid, {append_bin, Bin, RemoteFile}, atom); ++ case is_name_sane(RemoteFile) of ++ true -> ++ call(Pid, {append_bin, Bin, RemoteFile}, atom); ++ _ -> ++ {error, efnamena} ++ end; + append_bin(_Pid, _Bin, _RemoteFile) -> + {error, enotbinary}. + +@@ -2302,6 +2397,15 @@ send_bin(State, Bin) -> + mk_cmd(Fmt, Args) -> + [io_lib:format(Fmt, Args)| [?CR, ?LF]]. % Deep list ok. + ++is_name_sane([]) -> ++ true; ++is_name_sane([?CR| _]) -> ++ false; ++is_name_sane([?LF| _]) -> ++ false; ++is_name_sane([_| Rest]) -> ++ is_name_sane(Rest). ++ + pwd_result(Lines) -> + {_, [?DOUBLE_QUOTE | Rest]} = + lists:splitwith(fun(?DOUBLE_QUOTE) -> false; (_) -> true end, Lines), +diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl +index 35f21cc..daee1bd 100644 +--- a/lib/inets/test/ftp_suite_lib.erl ++++ b/lib/inets/test/ftp_suite_lib.erl +@@ -1266,6 +1266,8 @@ read_log_6035([]) -> + %%-------------------------------------------------------------------- + do_user(Pid) -> + {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), ++ {error, euser} = ftp:user(Pid, ?FTP_USER++"\r\nPASS "++?FTP_PASS, ?FTP_PASS), ++ {error, euser} = ftp:user(Pid, ?FTP_USER, ?FTP_PASS++"\r\nCWD ."), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + ok. + +@@ -1278,6 +1280,7 @@ do_pwd(Pid) -> + do_cd(Pid) -> + ok = ftp:cd(Pid, "/pub"), + {error, epath} = ftp:cd(Pid, ?BAD_DIR), ++ {error, efnamena} = ftp:cd(Pid, "/pub\r\nCWD ."), + ok. + + do_lcd(Pid, Dir) -> +@@ -1294,11 +1297,14 @@ do_ls(Pid) -> + %% directory, but can also be a filename or a group + %% of files (including wildcards). + {ok, _} = ftp:ls(Pid, "incom*"), ++ %% but \r\n can't be in the wildcard ++ {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."), + ok. + + do_nlist(Pid, WildcardSupport) -> + {ok, _} = ftp:nlist(Pid), + {ok, _} = ftp:nlist(Pid, "incoming"), ++ {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."), + %% neither nlist nor ls operates on a directory + %% they operate on a pathname, which *can* be a + %% directory, but can also be a filename or a group +@@ -1324,6 +1330,8 @@ do_rename(Pid, Config) -> + ftp:delete(Pid, NewLFile), % reset + ok = ftp:send(Pid, LFile), + {error, epath} = ftp:rename(Pid, NewLFile, LFile), ++ {error, efnamena} = ftp:rename(Pid, NewLFile++"\r\nRNTO "++LFile++"\r\nRNFR "++NewLFile, LFile), ++ {error, efnamena} = ftp:rename(Pid, NewLFile, LFile++"\r\nCWD ."), + ok = ftp:rename(Pid, LFile, NewLFile), + ftp:delete(Pid, LFile), % cleanup + ftp:delete(Pid, NewLFile), % cleanup +@@ -1338,6 +1346,7 @@ do_delete(Pid, Config) -> + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), + ftp:delete(Pid,LFile), % reset ++ {error, efnamena} = ftp:delete(Pid,LFile++"\r\nCWD ."), + ok = ftp:send(Pid, LFile), + ok = ftp:delete(Pid,LFile), + ok. +@@ -1348,6 +1357,8 @@ do_mkdir(Pid) -> + integer_to_list(B) ++ "_" ++ integer_to_list(C), + ok = ftp:cd(Pid, "incoming"), + {ok, CurrDir} = ftp:pwd(Pid), ++ {error, efnamena} = ftp:mkdir(Pid, NewDir++"\r\nCWD ."), ++ {error, efnamena} = ftp:rmdir(Pid, NewDir++"\r\nCWD ."), + ok = ftp:mkdir(Pid, NewDir), + ok = ftp:cd(Pid, NewDir), + ok = ftp:cd(Pid, CurrDir), +@@ -1363,6 +1374,7 @@ do_send(Pid, Config) -> + ok = file:write_file(AbsLFile, list_to_binary(Contents)), + ok = ftp:cd(Pid, "incoming"), + ok = ftp:lcd(Pid, PrivDir), ++ {error, efnamena} = ftp:send(Pid, LFile, RFile++"1\r\nCWD ."), + ok = ftp:send(Pid, LFile, RFile), + {ok, RFilesString} = ftp:nlist(Pid), + RFiles = split(RFilesString), +@@ -1392,6 +1404,7 @@ do_append(Pid, Config) -> + ftp:delete(Pid, RFile), + ftp:delete(Pid, LFile), + ++ {error, efnamena} = ftp:append(Pid, LFile, RFile++"1\r\nCWD ."), + ok = ftp:append(Pid, LFile, RFile), + ok = ftp:append(Pid, LFile, RFile), + ok = ftp:append(Pid, LFile), +@@ -1413,6 +1426,7 @@ do_send_bin(Pid, Config) -> + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:send_bin(Pid, Contents, File), ++ {error, efnamena} = ftp:send_bin(Pid, Bin, File++"1\r\nCWD ."), + ok = ftp:send_bin(Pid, Bin, File), + {ok, RFilesString} = ftp:nlist(Pid), + RFiles = split(RFilesString), +@@ -1426,6 +1440,7 @@ do_append_bin(Pid, Config) -> + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:append_bin(Pid, Contents, File), ++ {error, efnamena} = ftp:append_bin(Pid, Bin, File++"1\r\nCWD ."), + ok = ftp:append_bin(Pid, Bin, File), + ok = ftp:append_bin(Pid, Bin, File), + %% Control the contents of the file +@@ -1438,6 +1453,7 @@ do_send_chunk(Pid, Config) -> + Contents = "ftp_SUITE test ...", + Bin = list_to_binary(Contents), + ok = ftp:cd(Pid, "incoming"), ++ {error, efnamena} = ftp:send_chunk_start(Pid, File++"1\r\nCWD ."), + ok = ftp:send_chunk_start(Pid, File), + {error, echunk} = ftp:cd(Pid, "incoming"), + {error, enotbinary} = ftp:send_chunk(Pid, Contents), +@@ -1454,6 +1470,7 @@ do_append_chunk(Pid, Config) -> + File = ?config(file, Config), + Contents = ["ER","LE","RL"], + ok = ftp:cd(Pid, "incoming"), ++ {error, efnamena} = ftp:append_chunk_start(Pid, File++"1\r\nCWD ."), + ok = ftp:append_chunk_start(Pid, File), + {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)), + ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))), +@@ -1480,6 +1497,7 @@ do_recv(Pid, Config) -> + ok = file:delete(AbsFile), % cleanup + test_server:sleep(100), + ok = ftp:lcd(Pid, PrivDir), ++ {error, efnamena} = ftp:recv(Pid, File++"\r\nCWD ."), + ok = ftp:recv(Pid, File), + {ok, Files} = file:list_dir(PrivDir), + true = lists:member(File, Files), +@@ -1495,6 +1513,7 @@ do_recv_bin(Pid, Config) -> + ok = ftp:cd(Pid, "incoming"), + ok = ftp:send_bin(Pid, Bin1, File), + test_server:sleep(100), ++ {error, efnamena} = ftp:recv_bin(Pid, File++"\r\nCWD ."), + {ok, Bin2} = ftp:recv_bin(Pid, File), + ok = ftp:delete(Pid, File), % cleanup + Contents2 = binary_to_list(Bin2), +@@ -1520,6 +1539,7 @@ do_recv_chunk(Pid, Config) -> + ok = ftp:send_bin(Pid, Bin1, File), + test_server:sleep(100), + {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>), ++ {error, efnamena} = ftp:recv_chunk_start(Pid, File++"\r\nCWD ."), + ok = ftp:recv_chunk_start(Pid, File), + {ok, Contents2} = recv_chunk(Pid, <<>>), + ok = ftp:delete(Pid, File), % cleanup From 961faf17fa13ee2f34fd684e3784b8f1444d6d48 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 1 Dec 2014 21:37:45 +0300 Subject: [PATCH 04/13] Disable SSLv3 Disable SSLv3 (see rhbz #1169375). Also backport useful os:getenv/2 from master (see https://github.com/erlang/otp/pull/535 ) Signed-off-by: Peter Lemenkov --- erlang.spec | 14 ++- otp-0018-Introduce-os-getenv-2.patch | 63 ++++++++++++ ...pport-for-SSLv3-protocol-because-it-.patch | 99 +++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 otp-0018-Introduce-os-getenv-2.patch create mode 100644 otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch diff --git a/erlang.spec b/erlang.spec index 659fdbd..14887e4 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.9%{?dist} +Release: %{upstream_rel_for_rpm}.10%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -107,6 +107,12 @@ Patch16: otp-0016-Split-off-webtool-dependency-from-tools.patch # Fedora specific patch # lib/inets/src/ftp/ftp.erl: Check the filenames, usernames, Patch17: otp-0017-lib-inets-src-ftp-ftp.erl-Check-the-filenames-userna.patch +# Fedora specific patch +# Introduce os:getenv/2 +Patch18: otp-0018-Introduce-os-getenv-2.patch +# Fedora specific patch +# Patch removes support for SSLv3 protocol because it is proved +Patch19: otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -989,6 +995,8 @@ Erlang mode for XEmacs (source lisp files). %patch15 -p1 -b .Expose_NIF_version %patch16 -p1 -b .Split_off_webtool_dependency_from_tools %patch17 -p1 -b .lib_inets_src_ftp_ftp_erl_Check_the_filenames_userna +%patch18 -p1 -b .Introduce_os_getenv_2 +%patch19 -p1 -b .Patch_removes_support_for_SSLv3_protocol_because_it_ # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2320,6 +2328,10 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Mon Dec 01 2014 Peter Lemenkov - R16B-03.10 +- Disable SSLv3 (see rhbz #1169375) +- Backport useful os:getenv/2 from master (see https://github.com/erlang/otp/pull/535 ) + * Mon Nov 17 2014 Peter Lemenkov - R16B-03.9 - Fixed CVE-2014-1693 (backported fix from ver. 17.x.x, see patch no. 17) diff --git a/otp-0018-Introduce-os-getenv-2.patch b/otp-0018-Introduce-os-getenv-2.patch new file mode 100644 index 0000000..7ee1429 --- /dev/null +++ b/otp-0018-Introduce-os-getenv-2.patch @@ -0,0 +1,63 @@ +From: Peter Lemenkov +Date: Sat, 8 Nov 2014 15:11:04 +0300 +Subject: [PATCH] Introduce os:getenv/2 + +Signed-off-by: Peter Lemenkov + +diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml +index 9122267..7ec1f8e 100644 +--- a/lib/kernel/doc/src/os.xml ++++ b/lib/kernel/doc/src/os.xml +@@ -100,6 +100,19 @@ DirOut = os:cmd("dir"), % on Win32 platform + + + ++ ++ Get the value of an environment variable ++ ++

Returns the Value of the environment variable ++ VarName, or DefaultValue if the environment variable ++ is undefined.

++

If Unicode file name encoding is in effect (see the erl manual ++ page), the strings (both VarName and ++ Value) may contain characters with codepoints > 255.

++
++
++ + + Return the process identifier of the emulator process + +diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl +index 9415593..d5ef994 100644 +--- a/lib/kernel/src/os.erl ++++ b/lib/kernel/src/os.erl +@@ -26,7 +26,7 @@ + + %%% BIFs + +--export([getenv/0, getenv/1, getpid/0, putenv/2, timestamp/0, unsetenv/1]). ++-export([getenv/0, getenv/1, getenv/2, getpid/0, putenv/2, timestamp/0, unsetenv/1]). + + -spec getenv() -> [string()]. + +@@ -39,6 +39,19 @@ getenv() -> erlang:nif_error(undef). + getenv(_) -> + erlang:nif_error(undef). + ++-spec getenv(VarName, DefaultValue) -> Value when ++ VarName :: string(), ++ DefaultValue :: string(), ++ Value :: string(). ++ ++getenv(VarName, DefaultValue) -> ++ case os:getenv(VarName) of ++ false -> ++ DefaultValue; ++ Value -> ++ Value ++ end. ++ + -spec getpid() -> Value when + Value :: string(). + diff --git a/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch b/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch new file mode 100644 index 0000000..359c011 --- /dev/null +++ b/otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch @@ -0,0 +1,99 @@ +From: Sergei Golovan +Date: Sun, 30 Nov 2014 20:20:41 +0300 +Subject: [PATCH] Patch removes support for SSLv3 protocol because it is proved + to be insecure and nobody should use it anymore. + + +diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml +index 1d74faf..912acc2 100644 +--- a/lib/ssl/doc/src/ssl.xml ++++ b/lib/ssl/doc/src/ssl.xml +@@ -123,7 +123,7 @@ + +

sslsocket() - opaque to the user.

+ +-

protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'

++

protocol() = tlsv1 | 'tlsv1.1' | 'tlsv1.2'

+ +

ciphers() = [ciphersuite()] | string() (according to old API)

+ +diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml +index 0ee5b23..c65f8a3 100644 +--- a/lib/ssl/doc/src/ssl_app.xml ++++ b/lib/ssl/doc/src/ssl_app.xml +@@ -47,10 +47,10 @@ +

+

Note that the environment parameters can be set on the command line, + for instance,

+-

erl ... -ssl protocol_version '[sslv3, tlsv1]' .... ++

erl ... -ssl protocol_version '[tlsv1.1, tlsv1]' .... +

+ +- ]]>. ++ ]]>. + +

Protocol that will be supported by started clients and + servers. If this option is not set it will default to all +@@ -58,6 +58,9 @@ + Note that this option may be overridden by the version option + to ssl:connect/[2,3] and ssl:listen/2. +

++

For Debian GNU/Linux distribution the sslv3 protocol was ++ disabled due to its security issues. ++

+
+ + ]]> +diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl +index 0186f9f..6f84830 100644 +--- a/lib/ssl/src/ssl_internal.hrl ++++ b/lib/ssl/src/ssl_internal.hrl +@@ -67,8 +67,8 @@ + -define(TRUE, 0). + -define(FALSE, 1). + +--define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). +--define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]). ++-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1]). ++-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1]). + -define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]). + -define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]). + +diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl +index c17fa53..f4be9be 100644 +--- a/lib/ssl/src/ssl_record.hrl ++++ b/lib/ssl/src/ssl_record.hrl +@@ -144,6 +144,7 @@ + %% }). + + -define(LOWEST_MAJOR_SUPPORTED_VERSION, 3). ++-define(LOWEST_MINOR_SUPPORTED_VERSION, 1). + + + -record(generic_stream_cipher, { +diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl +index 8810755..3c5c7e9 100644 +--- a/lib/ssl/src/tls_record.erl ++++ b/lib/ssl/src/tls_record.erl +@@ -269,13 +269,19 @@ supported_protocol_versions([_|_] = Vsns) -> + %% + %%-------------------------------------------------------------------- + is_acceptable_version({N,_}) +- when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> ++ when N > ?LOWEST_MAJOR_SUPPORTED_VERSION -> ++ true; ++is_acceptable_version({N,M}) ++ when N == ?LOWEST_MAJOR_SUPPORTED_VERSION andalso M >= ?LOWEST_MINOR_SUPPORTED_VERSION -> + true; + is_acceptable_version(_) -> + false. + + is_acceptable_version({N,_} = Version, Versions) +- when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION -> ++ when N > ?LOWEST_MAJOR_SUPPORTED_VERSION -> ++ lists:member(Version, Versions); ++is_acceptable_version({N,M} = Version, Versions) ++ when N == ?LOWEST_MAJOR_SUPPORTED_VERSION andalso M >= ?LOWEST_MINOR_SUPPORTED_VERSION -> + lists:member(Version, Versions); + is_acceptable_version(_,_) -> + false. From 686f250c6fba552ce0c4fb13a67c0768978a56d6 Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Fri, 7 Aug 2015 11:43:21 -0400 Subject: [PATCH 05/13] Add patch for CVE-2015-2774 - TLS-1.0 POODLE vulnerability (rhbz#1206712) --- erlang.spec | 10 +- ...-1-backport-of-TLS-1.0-padding-check.patch | 581 ++++++++++++++++++ 2 files changed, 590 insertions(+), 1 deletion(-) create mode 100644 otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch diff --git a/erlang.spec b/erlang.spec index 14887e4..e8215a9 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.10%{?dist} +Release: %{upstream_rel_for_rpm}.11%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -115,6 +115,9 @@ Patch18: otp-0018-Introduce-os-getenv-2.patch Patch19: otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch # end of autogenerated patch tag list +# https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2015-2774 +Patch99: otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch + BuildRequires: lksctp-tools-devel BuildRequires: ncurses-devel BuildRequires: openssl-devel @@ -999,6 +1002,8 @@ Erlang mode for XEmacs (source lisp files). %patch19 -p1 -b .Patch_removes_support_for_SSLv3_protocol_because_it_ # end of autogenerated prep patch list +%patch99 -p1 + # FIXME we should come up with a better solution # remove shipped zlib sources #rm -f erts/emulator/zlib/*.[ch] @@ -2328,6 +2333,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Fri Aug 7 2015 John Eckersberg - R16B-03.11 +- Add patch for CVE-2015-2774 - TLS-1.0 POODLE vulnerability (rhbz#1206712) + * Mon Dec 01 2014 Peter Lemenkov - R16B-03.10 - Disable SSLv3 (see rhbz #1169375) - Backport useful os:getenv/2 from master (see https://github.com/erlang/otp/pull/535 ) diff --git a/otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch b/otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch new file mode 100644 index 0000000..ce93920 --- /dev/null +++ b/otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch @@ -0,0 +1,581 @@ +From 690bc386543e193d93b359d36ca19ae92af691bd Mon Sep 17 00:00:00 2001 +From: John Eckersberg +Date: Fri, 7 Aug 2015 11:39:27 -0400 +Subject: [PATCH] R16B03-1 backport of TLS-1.0 padding check + +commit 519b07bca04bdd8585b48f2de6b7124ca6455358 +Author: Ingela Anderton Andin +Date: Tue Jan 13 15:16:20 2015 +0100 + + ssl: Reenable padding check for TLS-1.0 and provide backwards compatible + disable option +--- + lib/ssl/doc/src/ssl.xml | 18 +++- + lib/ssl/src/dtls_record.erl | 4 +- + lib/ssl/src/ssl.erl | 8 +- + lib/ssl/src/ssl_cipher.erl | 43 +++++---- + lib/ssl/src/ssl_internal.hrl | 3 +- + lib/ssl/src/ssl_record.erl | 10 +- + lib/ssl/src/tls_connection.erl | 7 +- + lib/ssl/src/tls_record.erl | 33 ++++++- + lib/ssl/test/ssl_cipher_SUITE.erl | 189 ++++++++++++++++++++++---------------- + 9 files changed, 196 insertions(+), 119 deletions(-) + +diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml +index 1d74faf..8184edc 100644 +--- a/lib/ssl/doc/src/ssl.xml ++++ b/lib/ssl/doc/src/ssl.xml +@@ -334,11 +334,23 @@ fun(srp, Username :: string(), UserState :: term()) -> +

+
+ ++ {padding_check, boolean()} ++ ++

This option only affects TLS-1.0 connections. ++ If set to false it disables the block cipher padding check ++ to be able to interoperate with legacy software. ++

++ ++

Using this option makes TLS vulnerable to ++ the Poodle attack

++ ++
++ + +- ++ + +- +-
++ ++
+ SSL OPTION DESCRIPTIONS - CLIENT SIDE + +

Options described here are client specific or has a slightly different +diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl +index b0a7976..49dc9d7 100644 +--- a/lib/ssl/src/dtls_record.erl ++++ b/lib/ssl/src/dtls_record.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 2013-2013. All Rights Reserved. ++%% Copyright Ericsson AB 2013-2015. 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 +@@ -140,7 +140,7 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, + = ConnnectionStates0) -> + CompressAlg = SecParams#security_parameters.compression_algorithm, + {PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version), +- CipherFragment, ReadState0), ++ CipherFragment, ReadState0, true), + MacHash = calc_mac_hash(Type, Version, Epoch, Seq, PlainFragment, ReadState1), + case ssl_record:is_correct_mac(Mac, MacHash) of + true -> +diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl +index a7fd9f5..802d29d 100644 +--- a/lib/ssl/src/ssl.erl ++++ b/lib/ssl/src/ssl.erl +@@ -640,7 +640,8 @@ handle_options(Opts0, _Role) -> + make_next_protocol_selector( + handle_option(client_preferred_next_protocols, Opts, undefined)), + log_alert = handle_option(log_alert, Opts, true), +- server_name_indication = handle_option(server_name_indication, Opts, undefined) ++ server_name_indication = handle_option(server_name_indication, Opts, undefined), ++ padding_check = proplists:get_value(padding_check, Opts, true) + }, + + CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), +@@ -652,7 +653,8 @@ handle_options(Opts0, _Role) -> + reuse_session, reuse_sessions, ssl_imp, + cb_info, renegotiate_at, secure_renegotiate, hibernate_after, + erl_dist, next_protocols_advertised, +- client_preferred_next_protocols, log_alert, server_name_indication], ++ client_preferred_next_protocols, log_alert, server_name_indication, ++ padding_check], + + SockOpts = lists:foldl(fun(Key, PropList) -> + proplists:delete(Key, PropList) +@@ -840,6 +842,8 @@ validate_option(server_name_indication, disable) -> + disable; + validate_option(server_name_indication, undefined) -> + undefined; ++validate_option(padding_check, Value) when is_boolean(Value) -> ++ Value; + validate_option(Opt, Value) -> + throw({error, {options, {Opt, Value}}}). + +diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl +index b2077c6..e546e37 100644 +--- a/lib/ssl/src/ssl_cipher.erl ++++ b/lib/ssl/src/ssl_cipher.erl +@@ -33,7 +33,7 @@ + -include_lib("public_key/include/public_key.hrl"). + + -export([security_parameters/2, security_parameters/3, suite_definition/1, +- decipher/5, cipher/5, ++ decipher/6, cipher/5, + suite/1, suites/1, ec_keyed_suites/0, anonymous_suites/0, psk_suites/1, srp_suites/0, + openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1, + hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2]). +@@ -127,15 +127,16 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, + {T, CS0#cipher_state{iv=NextIV}}. + + %%-------------------------------------------------------------------- +--spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) -> ++-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), ++ tls_version(), boolean()) -> + {binary(), binary(), #cipher_state{}} | #alert{}. + %% + %% Description: Decrypts the data and the MAC using cipher described + %% by cipher_enum() and updating the cipher state. + %%------------------------------------------------------------------- +-decipher(?NULL, _HashSz, CipherState, Fragment, _) -> ++decipher(?NULL, _HashSz, CipherState, Fragment, _, _) -> + {Fragment, <<>>, CipherState}; +-decipher(?RC4, HashSz, CipherState, Fragment, _) -> ++decipher(?RC4, HashSz, CipherState, Fragment, _, _) -> + State0 = case CipherState#cipher_state.state of + undefined -> crypto:stream_init(rc4, CipherState#cipher_state.key); + S -> S +@@ -155,23 +156,23 @@ decipher(?RC4, HashSz, CipherState, Fragment, _) -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; + +-decipher(?DES, HashSz, CipherState, Fragment, Version) -> ++decipher(?DES, HashSz, CipherState, Fragment, Version, PaddingCheck) -> + block_decipher(fun(Key, IV, T) -> + crypto:block_decrypt(des_cbc, Key, IV, T) +- end, CipherState, HashSz, Fragment, Version); +-decipher(?'3DES', HashSz, CipherState, Fragment, Version) -> ++ end, CipherState, HashSz, Fragment, Version, PaddingCheck); ++decipher(?'3DES', HashSz, CipherState, Fragment, Version, PaddingCheck) -> + block_decipher(fun(<>, IV, T) -> + crypto:block_decrypt(des3_cbc, [K1, K2, K3], IV, T) +- end, CipherState, HashSz, Fragment, Version); +-decipher(?AES, HashSz, CipherState, Fragment, Version) -> ++ end, CipherState, HashSz, Fragment, Version, PaddingCheck); ++decipher(?AES, HashSz, CipherState, Fragment, Version, PaddingCheck) -> + block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> + crypto:block_decrypt(aes_cbc128, Key, IV, T); + (Key, IV, T) when byte_size(Key) =:= 32 -> + crypto:block_decrypt(aes_cbc256, Key, IV, T) +- end, CipherState, HashSz, Fragment, Version). ++ end, CipherState, HashSz, Fragment, Version, PaddingCheck). + + block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, +- HashSz, Fragment, Version) -> ++ HashSz, Fragment, Version, PaddingCheck) -> + try + Text = Fun(Key, IV, Fragment), + NextIV = next_iv(Fragment, IV), +@@ -179,7 +180,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, + Content = GBC#generic_block_cipher.content, + Mac = GBC#generic_block_cipher.mac, + CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv}, +- case is_correct_padding(GBC, Version) of ++ case is_correct_padding(GBC, Version, PaddingCheck) of + true -> + {Content, Mac, CipherState1}; + false -> +@@ -1266,16 +1267,18 @@ generic_stream_cipher_from_bin(T, HashSz) -> + #generic_stream_cipher{content=Content, + mac=Mac}. + +-%% For interoperability reasons we do not check the padding content in +-%% SSL 3.0 and TLS 1.0 as it is not strictly required and breaks +-%% interopability with for instance Google. + is_correct_padding(#generic_block_cipher{padding_length = Len, +- padding = Padding}, {3, N}) +- when N == 0; N == 1 -> +- Len == byte_size(Padding); +-%% Padding must be check in TLS 1.1 and after ++ padding = Padding}, {3, 0}, _) -> ++ Len == byte_size(Padding); %% Only length check is done in SSL 3.0 spec ++%% For interoperability reasons it is possible to disable ++%% the padding check when using TLS 1.0, as it is not strictly required ++%% in the spec (only recommended), howerver this makes TLS 1.0 vunrable to the Poodle attack ++%% so by default this clause will not match ++is_correct_padding(GenBlockCipher, {3, 1}, false) -> ++ is_correct_padding(GenBlockCipher, {3, 0}, false); ++%% Padding must be checked in TLS 1.1 and after + is_correct_padding(#generic_block_cipher{padding_length = Len, +- padding = Padding}, _) -> ++ padding = Padding}, _, _) -> + Len == byte_size(Padding) andalso + list_to_binary(lists:duplicate(Len, Len)) == Padding. + +diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl +index 0186f9f..c25c8f1 100644 +--- a/lib/ssl/src/ssl_internal.hrl ++++ b/lib/ssl/src/ssl_internal.hrl +@@ -114,7 +114,8 @@ + next_protocols_advertised = undefined, %% [binary()], + next_protocol_selector = undefined, %% fun([binary()]) -> binary()) + log_alert :: boolean(), +- server_name_indication = undefined ++ server_name_indication = undefined, ++ padding_check = true + }). + + -record(config, {ssl, %% SSL parameters +diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl +index 018c8be..ed9efe2 100644 +--- a/lib/ssl/src/ssl_record.erl ++++ b/lib/ssl/src/ssl_record.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 2013-2013. All Rights Reserved. ++%% Copyright Ericsson AB 2013-2015. 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 +@@ -48,7 +48,7 @@ + -export([compress/3, uncompress/3, compressions/0]). + + %% Payload encryption/decryption +--export([cipher/4, decipher/3, is_correct_mac/2]). ++-export([cipher/4, decipher/4, is_correct_mac/2]). + + %%==================================================================== + %% Internal application API +@@ -372,7 +372,7 @@ cipher(Version, Fragment, + ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version), + {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}. + %%-------------------------------------------------------------------- +--spec decipher(tls_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}. ++-spec decipher(tls_version(), binary(), #connection_state{}, boolean()) -> {binary(), binary(), #connection_state{}}. + %% + %% Description: Payload decryption + %%-------------------------------------------------------------------- +@@ -382,8 +382,8 @@ decipher(Version, CipherFragment, + BulkCipherAlgo, + hash_size = HashSz}, + cipher_state = CipherS0 +- } = ReadState) -> +- case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version) of ++ } = ReadState, PaddingCheck) -> ++ case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version, PaddingCheck) of + {PlainFragment, Mac, CipherS1} -> + CS1 = ReadState#connection_state{cipher_state = CipherS1}, + {PlainFragment, Mac, CS1}; +diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl +index ffa04ee..007c9b1 100644 +--- a/lib/ssl/src/tls_connection.erl ++++ b/lib/ssl/src/tls_connection.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 2007-2013. All Rights Reserved. ++%% Copyright Ericsson AB 2007-2015. 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 +@@ -499,8 +499,9 @@ next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_ci + next_record(#state{protocol_buffers = + #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]} + = Buffers, +- connection_states = ConnStates0} = State) -> +- case tls_record:decode_cipher_text(CT, ConnStates0) of ++ connection_states = ConnStates0, ++ ssl_options = #ssl_options{padding_check = Check}} = State) -> ++ case tls_record:decode_cipher_text(CT, ConnStates0, Check) of + {Plain, ConnStates} -> + {Plain, State#state{protocol_buffers = + Buffers#protocol_buffers{tls_cipher_texts = Rest}, +diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl +index 8810755..dcf03b4 100644 +--- a/lib/ssl/src/tls_record.erl ++++ b/lib/ssl/src/tls_record.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 2007-2013. All Rights Reserved. ++%% Copyright Ericsson AB 2007-2015. 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 +@@ -34,7 +34,7 @@ + -export([get_tls_records/2]). + + %% Decoding +--export([decode_cipher_text/2]). ++-export([decode_cipher_text/3]). + + %% Encoding + -export([encode_plain_text/4]). +@@ -137,7 +137,7 @@ encode_plain_text(Type, Version, Data, + {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}. + + %%-------------------------------------------------------------------- +--spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> ++-spec decode_cipher_text(#ssl_tls{}, #connection_states{}, boolean()) -> + {#ssl_tls{}, #connection_states{}}| #alert{}. + %% + %% Description: Decode cipher text +@@ -164,6 +164,33 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end. + ++decode_cipher_text(#ssl_tls{type = Type, version = Version, ++ fragment = CipherFragment} = CipherText, ++ #connection_states{current_read = ++ #connection_state{ ++ compression_state = CompressionS0, ++ sequence_number = Seq, ++ security_parameters= ++ #security_parameters{compression_algorithm=CompAlg} ++ } = ReadState0} = ConnnectionStates0, PaddingCheck) -> ++ case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of ++ {PlainFragment, Mac, ReadState1} -> ++ MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1), ++ case ssl_record:is_correct_mac(Mac, MacHash) of ++ true -> ++ {Plain, CompressionS1} = ssl_record:uncompress(CompAlg, ++ PlainFragment, CompressionS0), ++ ConnnectionStates = ConnnectionStates0#connection_states{ ++ current_read = ReadState1#connection_state{ ++ sequence_number = Seq + 1, ++ compression_state = CompressionS1}}, ++ {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; ++ false -> ++ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) ++ end; ++ #alert{} = Alert -> ++ Alert ++ end. + %%-------------------------------------------------------------------- + -spec protocol_version(tls_atom_version() | tls_version()) -> + tls_version() | tls_atom_version(). +diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl +index 45e9178..3433f9a 100644 +--- a/lib/ssl/test/ssl_cipher_SUITE.erl ++++ b/lib/ssl/test/ssl_cipher_SUITE.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 2008-2013. All Rights Reserved. ++%% Copyright Ericsson AB 2008-2015. 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 +@@ -38,7 +38,7 @@ + suite() -> [{ct_hooks,[ts_install_cth]}]. + + all() -> +- [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. ++ [aes_decipher_good, aes_decipher_fail, padding_test]. + + groups() -> + []. +@@ -73,93 +73,122 @@ end_per_testcase(_TestCase, Config) -> + %% Test Cases -------------------------------------------------------- + %%-------------------------------------------------------------------- + aes_decipher_good() -> +- [{doc,"Decipher a known cryptotext."}]. ++ [{doc,"Decipher a known cryptotext using a correct key"}]. + + aes_decipher_good(Config) when is_list(Config) -> + HashSz = 32, +- CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, +- key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>}, +- Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, +- 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, +- 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, +- 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, +- Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>, +- Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, +- Version = {3,0}, +- {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), +- Version1 = {3,1}, +- {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), +- ok. ++ CipherState = correct_cipher_state(), ++ decipher_check_good(HashSz, CipherState, {3,0}), ++ decipher_check_good(HashSz, CipherState, {3,1}), ++ decipher_check_good(HashSz, CipherState, {3,2}), ++ decipher_check_good(HashSz, CipherState, {3,3}). + + %%-------------------------------------------------------------------- +- +-aes_decipher_good_tls11() -> +- [{doc,"Decipher a known TLS 1.1 cryptotext."}]. +- +-%% the fragment is actuall a TLS 1.1 record, with +-%% Version = TLS 1.1, we get the correct NextIV in #cipher_state +-aes_decipher_good_tls11(Config) when is_list(Config) -> +- HashSz = 32, +- CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, +- key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>}, +- Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, +- 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, +- 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, +- 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, +- Content = <<"HELLO\n">>, +- NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>, +- Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, +- Version = {3,2}, +- {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), +- Version1 = {3,2}, +- {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), +- ok. +- +-%%-------------------------------------------------------------------- +- + aes_decipher_fail() -> +- [{doc,"Decipher a known cryptotext."}]. ++ [{doc,"Decipher a known cryptotext using a incorrect key"}]. + +-%% same as above, last byte of key replaced + aes_decipher_fail(Config) when is_list(Config) -> + HashSz = 32, +- CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, +- key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}, +- Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, +- 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, +- 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, +- 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, +- Version = {3,0}, +- {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), +- 32 = byte_size(Content), +- 32 = byte_size(Mac), +- Version1 = {3,1}, +- {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), +- 32 = byte_size(Content1), +- 32 = byte_size(Mac1), +- ok. ++ CipherState = incorrect_cipher_state(), ++ decipher_check_fail(HashSz, CipherState, {3,0}), ++ decipher_check_fail(HashSz, CipherState, {3,1}), ++ decipher_check_fail(HashSz, CipherState, {3,2}), ++ decipher_check_fail(HashSz, CipherState, {3,3}). + + %%-------------------------------------------------------------------- +- +-aes_decipher_fail_tls11() -> +- [{doc,"Decipher a known TLS 1.1 cryptotext."}]. +- +-%% same as above, last byte of key replaced +-%% stricter padding checks in TLS 1.1 mean we get an alert instead +-aes_decipher_fail_tls11(Config) when is_list(Config) -> +- HashSz = 32, +- CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, +- key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}, +- Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, +- 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, +- 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, +- 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, +- Version = {3,2}, +- #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = +- ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), +- Version1 = {3,3}, +- #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = +- ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), +- ok. +- ++padding_test(Config) when is_list(Config) -> ++ HashSz = 16, ++ CipherState = correct_cipher_state(), ++ pad_test(HashSz, CipherState, {3,0}), ++ pad_test(HashSz, CipherState, {3,1}), ++ pad_test(HashSz, CipherState, {3,2}), ++ pad_test(HashSz, CipherState, {3,3}). ++ ++%%-------------------------------------------------------------------- ++% Internal functions -------------------------------------------------------- + %%-------------------------------------------------------------------- ++decipher_check_good(HashSz, CipherState, Version) -> ++ {Content, NextIV, Mac} = content_nextiv_mac(Version), ++ {Content, Mac, _} = ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, aes_fragment(Version), Version, true). ++ ++decipher_check_fail(HashSz, CipherState, Version) -> ++ {Content, NextIV, Mac} = content_nextiv_mac(Version), ++ true = {Content, Mac, #cipher_state{iv = NextIV}} =/= ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, aes_fragment(Version), Version, true). ++ ++pad_test(HashSz, CipherState, {3,0} = Version) -> ++ %% 3.0 does not have padding test ++ {Content, NextIV, Mac} = badpad_content_nextiv_mac(Version), ++ {Content, Mac, #cipher_state{iv = NextIV}} = ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, true), ++ {Content, Mac, #cipher_state{iv = NextIV}} = ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,0}), {3,0}, false); ++pad_test(HashSz, CipherState, {3,1} = Version) -> ++ %% 3.1 should have padding test, but may be disabled ++ {Content, NextIV, Mac} = badpad_content_nextiv_mac(Version), ++ BadCont = badpad_content(Content), ++ {Content, Mac, #cipher_state{iv = NextIV}} = ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,1}) , {3,1}, false), ++ {BadCont, Mac, #cipher_state{iv = NextIV}} = ++ ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, badpad_aes_fragment({3,1}), {3,1}, true); ++pad_test(HashSz, CipherState, Version) -> ++ %% 3.2 and 3.3 must have padding test ++ {Content, NextIV, Mac} = badpad_content_nextiv_mac(Version), ++ BadCont = badpad_content(Content), ++ {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, ++ badpad_aes_fragment(Version), Version, false), ++ {BadCont, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES_CBC, HashSz, CipherState, ++ badpad_aes_fragment(Version), Version, true). ++ ++aes_fragment({3,N}) when N == 0; N == 1-> ++ <<197,9,6,109,242,87,80,154,85,250,110,81,119,95,65,185,53,206,216,153,246,169, ++ 119,177,178,238,248,174,253,220,242,81,33,0,177,251,91,44,247,53,183,198,165, ++ 63,20,194,159,107>>; ++ ++aes_fragment(_) -> ++ <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, ++ 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, ++ 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, ++ 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>. ++ ++badpad_aes_fragment({3,N}) when N == 0; N == 1 -> ++ <<186,139,125,10,118,21,26,248,120,108,193,104,87,118,145,79,225,55,228,10,105, ++ 30,190,37,1,88,139,243,210,99,65,41>>; ++badpad_aes_fragment(_) -> ++ <<137,31,14,77,228,80,76,103,183,125,55,250,68,190,123,131,117,23,229,180,207, ++ 94,121,137,117,157,109,99,113,61,190,138,131,229,201,120,142,179,172,48,77, ++ 234,19,240,33,38,91,93>>. ++ ++content_nextiv_mac({3,N}) when N == 0; N == 1 -> ++ {<<"HELLO\n">>, ++ <<72,196,247,97,62,213,222,109,210,204,217,186,172,184, 197,148>>, ++ <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>}; ++content_nextiv_mac(_) -> ++ {<<"HELLO\n">>, ++ <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>, ++ <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>}. ++ ++badpad_content_nextiv_mac({3,N}) when N == 0; N == 1 -> ++ {<<"HELLO\n">>, ++ <<225,55,228,10,105,30,190,37,1,88,139,243,210,99,65,41>>, ++ <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>> ++ }; ++badpad_content_nextiv_mac(_) -> ++ {<<"HELLO\n">>, ++ <<133,211,45,189,179,229,56,86,11,178,239,159,14,160,253,140>>, ++ <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>> ++ }. ++ ++badpad_content(Content) -> ++ %% BadContent will fail mac test ++ <<16#F0, Content/binary>>. ++ ++correct_cipher_state() -> ++ #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, ++ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>}. ++ ++incorrect_cipher_state() -> ++ #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, ++ key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}. ++ +-- +2.4.3 + From 92d16c1e81451a209a589e5c7b55b6bb17b66eac Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 11 Jan 2016 17:40:03 +0300 Subject: [PATCH 06/13] Enable crash dump creation during a large distrubution error Signed-off-by: Peter Lemenkov --- erlang.spec | 18 +++++++---- ...-1-backport-of-TLS-1.0-padding-check.patch | 21 ++----------- ...-to-crash-dump-on-large-distribution.patch | 31 +++++++++++++++++++ 3 files changed, 46 insertions(+), 24 deletions(-) rename otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch => otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch (97%) create mode 100644 otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch diff --git a/erlang.spec b/erlang.spec index e8215a9..a37e029 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.11%{?dist} +Release: %{upstream_rel_for_rpm}.12%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -113,11 +113,14 @@ Patch18: otp-0018-Introduce-os-getenv-2.patch # Fedora specific patch # Patch removes support for SSLv3 protocol because it is proved Patch19: otp-0019-Patch-removes-support-for-SSLv3-protocol-because-it-.patch +# Fedora specific patch +# R16B03-1 backport of TLS-1.0 padding check +Patch20: otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch +# Fedora specific patch +# Add patch to crash dump on large distribution +Patch21: otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch # end of autogenerated patch tag list -# https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2015-2774 -Patch99: otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch - BuildRequires: lksctp-tools-devel BuildRequires: ncurses-devel BuildRequires: openssl-devel @@ -1000,10 +1003,10 @@ Erlang mode for XEmacs (source lisp files). %patch17 -p1 -b .lib_inets_src_ftp_ftp_erl_Check_the_filenames_userna %patch18 -p1 -b .Introduce_os_getenv_2 %patch19 -p1 -b .Patch_removes_support_for_SSLv3_protocol_because_it_ +%patch20 -p1 -b .R16B03_1_backport_of_TLS_1_0_padding_check +%patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution # end of autogenerated prep patch list -%patch99 -p1 - # FIXME we should come up with a better solution # remove shipped zlib sources #rm -f erts/emulator/zlib/*.[ch] @@ -2333,6 +2336,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Mon Jan 11 2016 Peter Lemenkov - R16B-03.12 +- Enable crash dump creation during a large distrubution error + * Fri Aug 7 2015 John Eckersberg - R16B-03.11 - Add patch for CVE-2015-2774 - TLS-1.0 POODLE vulnerability (rhbz#1206712) diff --git a/otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch b/otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch similarity index 97% rename from otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch rename to otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch index ce93920..18645fb 100644 --- a/otp-0099-R16B03-1-backport-of-TLS-1.0-padding-check.patch +++ b/otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch @@ -1,4 +1,3 @@ -From 690bc386543e193d93b359d36ca19ae92af691bd Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Fri, 7 Aug 2015 11:39:27 -0400 Subject: [PATCH] R16B03-1 backport of TLS-1.0 padding check @@ -9,20 +8,9 @@ Date: Tue Jan 13 15:16:20 2015 +0100 ssl: Reenable padding check for TLS-1.0 and provide backwards compatible disable option ---- - lib/ssl/doc/src/ssl.xml | 18 +++- - lib/ssl/src/dtls_record.erl | 4 +- - lib/ssl/src/ssl.erl | 8 +- - lib/ssl/src/ssl_cipher.erl | 43 +++++---- - lib/ssl/src/ssl_internal.hrl | 3 +- - lib/ssl/src/ssl_record.erl | 10 +- - lib/ssl/src/tls_connection.erl | 7 +- - lib/ssl/src/tls_record.erl | 33 ++++++- - lib/ssl/test/ssl_cipher_SUITE.erl | 189 ++++++++++++++++++++++---------------- - 9 files changed, 196 insertions(+), 119 deletions(-) diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml -index 1d74faf..8184edc 100644 +index 912acc2..e496eb1 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -334,11 +334,23 @@ fun(srp, Username :: string(), UserState :: term()) -> @@ -208,7 +196,7 @@ index b2077c6..e546e37 100644 list_to_binary(lists:duplicate(Len, Len)) == Padding. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl -index 0186f9f..c25c8f1 100644 +index 6f84830..8c12141 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -114,7 +114,8 @@ @@ -289,7 +277,7 @@ index ffa04ee..007c9b1 100644 {Plain, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = Rest}, diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl -index 8810755..dcf03b4 100644 +index 3c5c7e9..dd99d10 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -1,7 +1,7 @@ @@ -576,6 +564,3 @@ index 45e9178..3433f9a 100644 + #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, + key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}. + --- -2.4.3 - diff --git a/otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch b/otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch new file mode 100644 index 0000000..6849686 --- /dev/null +++ b/otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch @@ -0,0 +1,31 @@ +From: John Eckersberg +Date: Wed, 16 Dec 2015 11:03:42 -0500 +Subject: [PATCH] Add patch to crash dump on large distribution + +https://bugzilla.redhat.com/show_bug.cgi?id=1291822 +https://bugzilla.redhat.com/show_bug.cgi?id=1291855 +https://bugzilla.redhat.com/show_bug.cgi?id=1291856 +https://bugzilla.redhat.com/show_bug.cgi?id=1291857 + +diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c +index 6ecf3f0..7d1f4ea 100644 +--- a/erts/emulator/beam/dist.c ++++ b/erts/emulator/beam/dist.c +@@ -1880,7 +1880,7 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf) + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); + + if (size > (Uint) INT_MAX) +- erl_exit(ERTS_ABORT_EXIT, ++ erl_exit(ERTS_DUMP_EXIT, + "Absurdly large distribution output data buffer " + "(%beu bytes) passed.\n", + size); +@@ -1919,7 +1919,7 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf) + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); + + if (size > (Uint) INT_MAX) +- erl_exit(ERTS_ABORT_EXIT, ++ erl_exit(ERTS_DUMP_EXIT, + "Absurdly large distribution output data buffer " + "(%beu bytes) passed.\n", + size); From 52781ec84e5427cfbe94b56af357fb2b0e4c749b Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Sun, 17 Jan 2016 16:46:11 -0500 Subject: [PATCH 07/13] fix inet_dist_listen_{min,max} for ipv6 Also add patch for epmd ipv6 support Resolves: rhbz#1299253 Resolves: rhbz#1299251 --- erlang.spec | 14 +- ...t6_tcp_dist-reuse-inet_tcp_dist-code.patch | 900 ++++++++++++++++++ ...-epmd-support-IPv6-node-registration.patch | 645 +++++++++++++ 3 files changed, 1558 insertions(+), 1 deletion(-) create mode 100644 otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch create mode 100644 otp-0023-epmd-support-IPv6-node-registration.patch diff --git a/erlang.spec b/erlang.spec index a37e029..c9d617a 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.12%{?dist} +Release: %{upstream_rel_for_rpm}.13%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -119,6 +119,12 @@ Patch20: otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch # Fedora specific patch # Add patch to crash dump on large distribution Patch21: otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch +# Fedora specific patch +# kernel: inet6_tcp_dist: reuse inet_tcp_dist code +Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch +# Fedora specific patch +# Add patch for epmd ipv6 support +Patch23: otp-0023-epmd-support-IPv6-node-registration.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -1005,6 +1011,8 @@ Erlang mode for XEmacs (source lisp files). %patch19 -p1 -b .Patch_removes_support_for_SSLv3_protocol_because_it_ %patch20 -p1 -b .R16B03_1_backport_of_TLS_1_0_padding_check %patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution +%patch22 -p1 -b .kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code +%patch23 -p1 -b .Add_patch_for_epmd_ipv6_support # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2336,6 +2344,10 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Sun Jan 17 2016 John Eckersberg - R16B-03.13 +- Fix inet_dist_listen_{min,max} for ipv6 (rhbz#1299251) +- Add patch for epmd ipv6 support (rhbz#1299253) + * Mon Jan 11 2016 Peter Lemenkov - R16B-03.12 - Enable crash dump creation during a large distrubution error diff --git a/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch b/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch new file mode 100644 index 0000000..e08602a --- /dev/null +++ b/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch @@ -0,0 +1,900 @@ +From: Danil Zagoskin +Date: Sat, 2 May 2015 00:40:53 +0300 +Subject: [PATCH] kernel: inet6_tcp_dist: reuse inet_tcp_dist code + +inet6_tcp_dist module is an old copy of inet_tcp_dist with changed +address family. +New features (such as listening port range, interface and generic +options) are implemented in inet_tcp_dist only, inet6_tcp_dist looks +abandoned (it does not even have tests). +This patch makes inet_tcp_dist internals work with abstract driver, +and inet6_tcp_dist becomes just a thin wrapper for it. +--- + lib/kernel/src/inet6_tcp.erl | 19 ++ + lib/kernel/src/inet6_tcp_dist.erl | 365 +------------------------------------- + lib/kernel/src/inet_tcp.erl | 16 +- + lib/kernel/src/inet_tcp_dist.erl | 197 ++++++++++++-------- + 4 files changed, 159 insertions(+), 438 deletions(-) + +diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl +index c714b2b..b31f05b 100644 +--- a/lib/kernel/src/inet6_tcp.erl ++++ b/lib/kernel/src/inet6_tcp.erl +@@ -24,10 +24,29 @@ + -export([controlling_process/2]). + -export([fdopen/2]). + ++-export([family/0, mask/2, parse_address/1]). + -export([getserv/1, getaddr/1, getaddr/2, getaddrs/1, getaddrs/2]). + + -include("inet_int.hrl"). + ++%% my address family ++family() -> inet6. ++ ++%% Apply netmask on address ++mask({M1,M2,M3,M4,M5,M6,M7,M8}, {IP1,IP2,IP3,IP4,IP5,IP6,IP7,IP8}) -> ++ {M1 band IP1, ++ M2 band IP2, ++ M3 band IP3, ++ M4 band IP4, ++ M5 band IP5, ++ M6 band IP6, ++ M7 band IP7, ++ M8 band IP8 }. ++ ++%% Parse address string ++parse_address(Host) -> ++ inet_parse:ipv6strict_address(Host). ++ + %% inet_tcp port lookup + getserv(Port) when is_integer(Port) -> {ok, Port}; + getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,tcp). +diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl +index 2cb0e10..1488bf4 100644 +--- a/lib/kernel/src/inet6_tcp_dist.erl ++++ b/lib/kernel/src/inet6_tcp_dist.erl +@@ -23,28 +23,6 @@ + -export([listen/1, accept/1, accept_connection/5, + setup/5, close/1, select/1, is_node_name/1]). + +-%% internal exports +- +--export([accept_loop/2,do_accept/6,do_setup/6, getstat/1,tick/1]). +- +--import(error_logger,[error_msg/2]). +- +--include("net_address.hrl"). +- +- +- +--define(to_port(Socket, Data, Opts), +- case inet6_tcp:send(Socket, Data, Opts) of +- {error, closed} -> +- self() ! {tcp_closed, Socket}, +- {error, closed}; +- R -> +- R +- end). +- +- +--include("dist.hrl"). +--include("dist_util.hrl"). + + %% ------------------------------------------------------------ + %% Select this protocol based on node name +@@ -52,14 +30,7 @@ + %% ------------------------------------------------------------ + + select(Node) -> +- case split_node(atom_to_list(Node), $@, []) of +- [_, Host] -> +- case inet:getaddr(Host,inet6) of +- {ok,_} -> true; +- _ -> false +- end; +- _ -> false +- end. ++ inet_tcp_dist:gen_select(inet6_tcp, Node). + + %% ------------------------------------------------------------ + %% Create the listen socket, i.e. the port that this erlang +@@ -67,59 +38,14 @@ select(Node) -> + %% ------------------------------------------------------------ + + listen(Name) -> +- case inet6_tcp:listen(0, [{active, false}, {packet,2}]) of +- {ok, Socket} -> +- TcpAddress = get_tcp_address(Socket), +- {_,Port} = TcpAddress#net_address.address, +- case erl_epmd:register_node(Name, Port) of +- {ok, Creation} -> +- {ok, {Socket, TcpAddress, Creation}}; +- Error -> +- Error +- end; +- Error -> +- Error +- end. ++ inet_tcp_dist:gen_listen(inet6_tcp, Name). + + %% ------------------------------------------------------------ + %% Accepts new connection attempts from other Erlang nodes. + %% ------------------------------------------------------------ + + accept(Listen) -> +- spawn_opt(?MODULE, accept_loop, [self(), Listen], [link, {priority, max}]). +- +-accept_loop(Kernel, Listen) -> +- case inet6_tcp:accept(Listen) of +- {ok, Socket} -> +- Kernel ! {accept,self(),Socket,inet6,tcp}, +- controller(Kernel, Socket), +- accept_loop(Kernel, Listen); +- Error -> +- exit(Error) +- end. +- +-controller(Kernel, Socket) -> +- receive +- {Kernel, controller, Pid} -> +- flush_controller(Pid, Socket), +- inet6_tcp:controlling_process(Socket, Pid), +- flush_controller(Pid, Socket), +- Pid ! {self(), controller}; +- {Kernel, unsupported_protocol} -> +- exit(unsupported_protocol) +- end. +- +-flush_controller(Pid, Socket) -> +- receive +- {tcp, Socket, Data} -> +- Pid ! {tcp, Socket, Data}, +- flush_controller(Pid, Socket); +- {tcp_closed, Socket} -> +- Pid ! {tcp_closed, Socket}, +- flush_controller(Pid, Socket) +- after 0 -> +- ok +- end. ++ inet_tcp_dist:gen_accept(inet6_tcp, Listen). + + %% ------------------------------------------------------------ + %% Accepts a new connection attempt from another Erlang node. +@@ -127,85 +53,7 @@ flush_controller(Pid, Socket) -> + %% ------------------------------------------------------------ + + accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) -> +- spawn_opt(?MODULE, do_accept, +- [self(), AcceptPid, Socket, MyNode, Allowed, SetupTime], +- [link, {priority, max}]). +- +-do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> +- receive +- {AcceptPid, controller} -> +- Timer = dist_util:start_timer(SetupTime), +- case check_ip(Socket) of +- true -> +- HSData = #hs_data{ +- kernel_pid = Kernel, +- this_node = MyNode, +- socket = Socket, +- timer = Timer, +- this_flags = 0, +- allowed = Allowed, +- f_send = fun(S,D) -> inet6_tcp:send(S,D) end, +- f_recv = fun(S,N,T) -> inet6_tcp:recv(S,N,T) +- end, +- f_setopts_pre_nodeup = +- fun(S) -> +- inet:setopts(S, +- [{active, false}, +- {packet, 4}, +- nodelay()]) +- end, +- f_setopts_post_nodeup = +- fun(S) -> +- inet:setopts(S, +- [{active, true}, +- {deliver, port}, +- {packet, 4}, +- nodelay()]) +- end, +- f_getll = fun(S) -> +- inet:getll(S) +- end, +- f_address = fun get_remote_id/2, +- mf_tick = fun ?MODULE:tick/1, +- mf_getstat = fun ?MODULE:getstat/1 +- }, +- dist_util:handshake_other_started(HSData); +- {false,IP} -> +- error_msg("** Connection attempt from " +- "disallowed IP ~w ** ~n", [IP]), +- ?shutdown(no_node) +- end +- end. +- +- +-%% we may not always want the nodelay behaviour +-%% for performance reasons +- +-nodelay() -> +- case application:get_env(kernel, dist_nodelay) of +- undefined -> +- {nodelay, true}; +- {ok, true} -> +- {nodelay, true}; +- {ok, false} -> +- {nodelay, false}; +- _ -> +- {nodelay, true} +- end. +- +- +-%% ------------------------------------------------------------ +-%% Get remote information about a Socket. +-%% ------------------------------------------------------------ +- +-get_remote_id(Socket, Node) -> +- {ok, Address} = inet:peername(Socket), +- [_, Host] = split_node(atom_to_list(Node), $@, []), +- #net_address { +- address = Address, +- host = Host, +- protocol = tcp, +- family = inet6 }. ++ inet_tcp_dist:gen_accept_connection(inet6_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime). + + %% ------------------------------------------------------------ + %% Setup a new connection to another Erlang node. +@@ -213,214 +61,13 @@ get_remote_id(Socket, Node) -> + %% ------------------------------------------------------------ + + setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> +- spawn_opt(?MODULE, do_setup, +- [self(), Node, Type, MyNode, LongOrShortNames, SetupTime], +- [link, {priority, max}]). +- +-do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> +- ?trace("~p~n",[{?MODULE,self(),setup,Node}]), +- [Name, Address] = splitnode(Node, LongOrShortNames), +- case inet:getaddr(Address, inet6) of +- {ok, Ip} -> +- Timer = dist_util:start_timer(SetupTime), +- case erl_epmd:port_please(Name, Ip) of +- {port, TcpPort, Version} -> +- ?trace("port_please(~p) -> version ~p~n", +- [Node,Version]), +- dist_util:reset_timer(Timer), +- case inet6_tcp:connect(Ip, TcpPort, +- [{active, false}, +- {packet,2}]) of +- {ok, Socket} -> +- HSData = #hs_data{ +- kernel_pid = Kernel, +- other_node = Node, +- this_node = MyNode, +- socket = Socket, +- timer = Timer, +- this_flags = 0, +- other_version = Version, +- f_send = fun inet6_tcp:send/2, +- f_recv = fun inet6_tcp:recv/3, +- f_setopts_pre_nodeup = +- fun(S) -> +- inet:setopts +- (S, +- [{active, false}, +- {packet, 4}, +- nodelay()]) +- end, +- f_setopts_post_nodeup = +- fun(S) -> +- inet:setopts +- (S, +- [{active, true}, +- {deliver, port}, +- {packet, 4}, +- nodelay()]) +- end, +- f_getll = fun inet:getll/1, +- f_address = +- fun(_,_) -> +- #net_address { +- address = {Ip,TcpPort}, +- host = Address, +- protocol = tcp, +- family = inet6} +- end, +- mf_tick = fun ?MODULE:tick/1, +- mf_getstat = fun ?MODULE:getstat/1, +- request_type = Type +- }, +- dist_util:handshake_we_started(HSData); +- _ -> +- %% Other Node may have closed since +- %% port_please ! +- ?trace("other node (~p) " +- "closed since port_please.~n", +- [Node]), +- ?shutdown(Node) +- end; +- _ -> +- ?trace("port_please (~p) " +- "failed.~n", [Node]), +- ?shutdown(Node) +- end; +- __Other -> +- ?trace("inet_getaddr(~p) " +- "failed (~p).~n", [Node,__Other]), +- ?shutdown(Node) +- end. ++ inet_tcp_dist:gen_setup(inet6_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime). + + %% + %% Close a socket. + %% + close(Socket) -> + inet6_tcp:close(Socket). +- +- +-%% If Node is illegal terminate the connection setup!! +-splitnode(Node, LongOrShortNames) -> +- case split_node(atom_to_list(Node), $@, []) of +- [Name|Tail] when Tail =/= [] -> +- Host = lists:append(Tail), +- case split_node(Host, $., []) of +- [_] when LongOrShortNames =:= longnames -> +- case inet_parse:ipv6strict_address(Host) of +- {ok, _} -> +- [Name, Host]; +- _ -> +- error_msg("** System running to use " +- "fully qualified " +- "hostnames **~n" +- "** Hostname ~s is illegal **~n", +- [Host]), +- ?shutdown(Node) +- end; +- L when length(L) > 1, LongOrShortNames =:= shortnames -> +- error_msg("** System NOT running to use fully qualified " +- "hostnames **~n" +- "** Hostname ~s is illegal **~n", +- [Host]), +- ?shutdown(Node); +- _ -> +- [Name, Host] +- end; +- [_] -> +- error_msg("** Nodename ~p illegal, no '@' character **~n", +- [Node]), +- ?shutdown(Node); +- _ -> +- error_msg("** Nodename ~p illegal **~n", [Node]), +- ?shutdown(Node) +- end. +- +-split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])]; +-split_node([H|T], Chr, Ack) -> split_node(T, Chr, [H|Ack]); +-split_node([], _, Ack) -> [lists:reverse(Ack)]. +- +-%% ------------------------------------------------------------ +-%% Fetch local information about a Socket. +-%% ------------------------------------------------------------ +-get_tcp_address(Socket) -> +- {ok, Address} = inet:sockname(Socket), +- {ok, Host} = inet:gethostname(), +- #net_address { +- address = Address, +- host = Host, +- protocol = tcp, +- family = inet6 +- }. +- +-%% ------------------------------------------------------------ +-%% Do only accept new connection attempts from nodes at our +-%% own LAN, if the check_ip environment parameter is true. +-%% ------------------------------------------------------------ +-check_ip(Socket) -> +- case application:get_env(check_ip) of +- {ok, true} -> +- case get_ifs(Socket) of +- {ok, IFs, IP} -> +- check_ip(IFs, IP); +- _ -> +- ?shutdown(no_node) +- end; +- _ -> +- true +- end. +- +-get_ifs(Socket) -> +- case inet:peername(Socket) of +- {ok, {IP, _}} -> +- case inet:getif(Socket) of +- {ok, IFs} -> {ok, IFs, IP}; +- Error -> Error +- end; +- Error -> +- Error +- end. +- +-check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) -> +- case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of +- {M, M} -> true; +- _ -> check_ip(IFs, PeerIP) +- end; +-check_ip([], PeerIP) -> +- {false, PeerIP}. + +-mask({M1,M2,M3,M4,M5,M6,M7,M8}, {IP1,IP2,IP3,IP4,IP5,IP6,IP7,IP8}) -> +- {M1 band IP1, +- M2 band IP2, +- M3 band IP3, +- M4 band IP4, +- M5 band IP5, +- M6 band IP6, +- M7 band IP7, +- M8 band IP8 }. +- + is_node_name(Node) when is_atom(Node) -> +- case split_node(atom_to_list(Node), $@, []) of +- [_,_Host] -> true; +- _ -> false +- end; +-is_node_name(_Node) -> +- false. +-tick(Sock) -> +- ?to_port(Sock,[],[force]). +-getstat(Socket) -> +- case inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of +- {ok, Stat} -> +- split_stat(Stat,0,0,0); +- Error -> +- Error +- end. +- +-split_stat([{recv_cnt, R}|Stat], _, W, P) -> +- split_stat(Stat, R, W, P); +-split_stat([{send_cnt, W}|Stat], R, _, P) -> +- split_stat(Stat, R, W, P); +-split_stat([{send_pend, P}|Stat], R, W, _) -> +- split_stat(Stat, R, W, P); +-split_stat([], R, W, P) -> +- {ok, R, W, P}. +- ++ inet_tcp_dist:is_node_name(Node). +diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl +index 4c2db16..71d35ca 100644 +--- a/lib/kernel/src/inet_tcp.erl ++++ b/lib/kernel/src/inet_tcp.erl +@@ -26,11 +26,25 @@ + -export([controlling_process/2]). + -export([fdopen/2]). + ++-export([family/0, mask/2, parse_address/1]). + -export([getserv/1, getaddr/1, getaddr/2, getaddrs/1, getaddrs/2]). + +- + -include("inet_int.hrl"). + ++%% my address family ++family() -> inet. ++ ++%% Apply netmask on address ++mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) -> ++ {M1 band IP1, ++ M2 band IP2, ++ M3 band IP3, ++ M4 band IP4}. ++ ++%% Parse address string ++parse_address(Host) -> ++ inet_parse:ipv4strict_address(Host). ++ + %% inet_tcp port lookup + getserv(Port) when is_integer(Port) -> {ok, Port}; + getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,tcp). +diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl +index 8005eff..0739cf3 100644 +--- a/lib/kernel/src/inet_tcp_dist.erl ++++ b/lib/kernel/src/inet_tcp_dist.erl +@@ -23,9 +23,13 @@ + -export([listen/1, accept/1, accept_connection/5, + setup/5, close/1, select/1, is_node_name/1]). + ++%% Generalized dist API ++-export([gen_listen/2, gen_accept/2, gen_accept_connection/6, ++ gen_setup/6, gen_select/2]). ++ + %% internal exports + +--export([accept_loop/2,do_accept/6,do_setup/6,getstat/1,tick/1]). ++-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]). + + -import(error_logger,[error_msg/2]). + +@@ -33,15 +37,6 @@ + + + +--define(to_port(Socket, Data, Opts), +- case inet_tcp:send(Socket, Data, Opts) of +- {error, closed} -> +- self() ! {tcp_closed, Socket}, +- {error, closed}; +- R -> +- R +- end). +- + + -include("dist.hrl"). + -include("dist_util.hrl"). +@@ -52,8 +47,15 @@ + %% ------------------------------------------------------------ + + select(Node) -> ++ gen_select(inet_tcp, Node). ++ ++gen_select(Driver, Node) -> + case split_node(atom_to_list(Node), $@, []) of +- [_,_Host] -> true; ++ [_, Host] -> ++ case inet:getaddr(Host, Driver:family()) of ++ {ok,_} -> true; ++ _ -> false ++ end; + _ -> false + end. + +@@ -63,9 +65,12 @@ select(Node) -> + %% ------------------------------------------------------------ + + listen(Name) -> +- case do_listen([{active, false}, {packet,2}, {reuseaddr, true}]) of ++ gen_listen(inet_tcp, Name). ++ ++gen_listen(Driver, Name) -> ++ case do_listen(Driver, [{active, false}, {packet,2}, {reuseaddr, true}]) of + {ok, Socket} -> +- TcpAddress = get_tcp_address(Socket), ++ TcpAddress = get_tcp_address(Driver, Socket), + {_,Port} = TcpAddress#net_address.address, + case erl_epmd:register_node(Name, Port) of + {ok, Creation} -> +@@ -77,7 +82,7 @@ listen(Name) -> + Error + end. + +-do_listen(Options0) -> ++do_listen(Driver, Options) -> + {First,Last} = case application:get_env(kernel,inet_dist_listen_min) of + {ok,N} when is_integer(N) -> + case application:get_env(kernel, +@@ -90,46 +95,60 @@ do_listen(Options0) -> + _ -> + {0,0} + end, +- Options = case application:get_env(kernel, inet_dist_use_interface) of +- {ok, Ip} -> +- [{ip, Ip} | Options0]; +- _ -> +- Options0 +- end, +- do_listen(First, Last, [{backlog,128}|Options]). +- +-do_listen(First,Last,_) when First > Last -> ++ do_listen(Driver, First, Last, listen_options([{backlog,128}|Options])). ++ ++do_listen(_Driver, First,Last,_) when First > Last -> + {error,eaddrinuse}; +-do_listen(First,Last,Options) -> +- case inet_tcp:listen(First, Options) of ++do_listen(Driver, First,Last,Options) -> ++ case Driver:listen(First, Options) of + {error, eaddrinuse} -> +- do_listen(First+1,Last,Options); ++ do_listen(Driver, First+1,Last,Options); + Other -> + Other + end. + ++listen_options(Opts0) -> ++ Opts1 = ++ case application:get_env(kernel, inet_dist_use_interface) of ++ {ok, Ip} -> ++ [{ip, Ip} | Opts0]; ++ _ -> ++ Opts0 ++ end, ++ case application:get_env(kernel, inet_dist_listen_options) of ++ {ok,ListenOpts} -> ++ erlang:display({inet_dist_listen_options, ListenOpts}), ++ ListenOpts ++ Opts1; ++ _ -> ++ Opts1 ++ end. ++ ++ + %% ------------------------------------------------------------ + %% Accepts new connection attempts from other Erlang nodes. + %% ------------------------------------------------------------ + + accept(Listen) -> +- spawn_opt(?MODULE, accept_loop, [self(), Listen], [link, {priority, max}]). ++ gen_accept(inet_tcp, Listen). + +-accept_loop(Kernel, Listen) -> +- case inet_tcp:accept(Listen) of ++gen_accept(Driver, Listen) -> ++ spawn_opt(?MODULE, accept_loop, [Driver, self(), Listen], [link, {priority, max}]). ++ ++accept_loop(Driver, Kernel, Listen) -> ++ case Driver:accept(Listen) of + {ok, Socket} -> +- Kernel ! {accept,self(),Socket,inet,tcp}, +- controller(Kernel, Socket), +- accept_loop(Kernel, Listen); ++ Kernel ! {accept,self(),Socket,Driver:family(),tcp}, ++ _ = controller(Driver, Kernel, Socket), ++ accept_loop(Driver, Kernel, Listen); + Error -> + exit(Error) + end. + +-controller(Kernel, Socket) -> ++controller(Driver, Kernel, Socket) -> + receive + {Kernel, controller, Pid} -> + flush_controller(Pid, Socket), +- inet_tcp:controlling_process(Socket, Pid), ++ Driver:controlling_process(Socket, Pid), + flush_controller(Pid, Socket), + Pid ! {self(), controller}; + {Kernel, unsupported_protocol} -> +@@ -154,15 +173,18 @@ flush_controller(Pid, Socket) -> + %% ------------------------------------------------------------ + + accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) -> ++ gen_accept_connection(inet_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime). ++ ++gen_accept_connection(Driver, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> + spawn_opt(?MODULE, do_accept, +- [self(), AcceptPid, Socket, MyNode, Allowed, SetupTime], ++ [Driver, self(), AcceptPid, Socket, MyNode, Allowed, SetupTime], + [link, {priority, max}]). + +-do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> ++do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> + receive + {AcceptPid, controller} -> + Timer = dist_util:start_timer(SetupTime), +- case check_ip(Socket) of ++ case check_ip(Driver, Socket) of + true -> + HSData = #hs_data{ + kernel_pid = Kernel, +@@ -171,9 +193,8 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> + timer = Timer, + this_flags = 0, + allowed = Allowed, +- f_send = fun(S,D) -> inet_tcp:send(S,D) end, +- f_recv = fun(S,N,T) -> inet_tcp:recv(S,N,T) +- end, ++ f_send = fun Driver:send/2, ++ f_recv = fun Driver:recv/3, + f_setopts_pre_nodeup = + fun(S) -> + inet:setopts(S, +@@ -192,8 +213,8 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> + f_getll = fun(S) -> + inet:getll(S) + end, +- f_address = fun get_remote_id/2, +- mf_tick = fun ?MODULE:tick/1, ++ f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end, ++ mf_tick = fun(S) -> tick(Driver, S) end, + mf_getstat = fun ?MODULE:getstat/1 + }, + dist_util:handshake_other_started(HSData); +@@ -224,13 +245,13 @@ nodelay() -> + %% ------------------------------------------------------------ + %% Get remote information about a Socket. + %% ------------------------------------------------------------ +-get_remote_id(Socket, Node) -> ++get_remote_id(Driver, Socket, Node) -> + case inet:peername(Socket) of + {ok,Address} -> + case split_node(atom_to_list(Node), $@, []) of + [_,Host] -> + #net_address{address=Address,host=Host, +- protocol=tcp,family=inet}; ++ protocol=tcp,family=Driver:family()}; + _ -> + %% No '@' or more than one '@' in node name. + ?shutdown(no_node) +@@ -245,14 +266,18 @@ get_remote_id(Socket, Node) -> + %% ------------------------------------------------------------ + + setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> ++ gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime). ++ ++gen_setup(Driver, Node, Type, MyNode, LongOrShortNames, SetupTime) -> + spawn_opt(?MODULE, do_setup, +- [self(), Node, Type, MyNode, LongOrShortNames, SetupTime], ++ [Driver, self(), Node, Type, MyNode, LongOrShortNames, SetupTime], + [link, {priority, max}]). + +-do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> ++do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> + ?trace("~p~n",[{inet_tcp_dist,self(),setup,Node}]), +- [Name, Address] = splitnode(Node, LongOrShortNames), +- case inet:getaddr(Address, inet) of ++ [Name, Address] = splitnode(Driver, Node, LongOrShortNames), ++ AddressFamily = Driver:family(), ++ case inet:getaddr(Address, AddressFamily) of + {ok, Ip} -> + Timer = dist_util:start_timer(SetupTime), + case erl_epmd:port_please(Name, Ip) of +@@ -260,9 +285,11 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> + ?trace("port_please(~p) -> version ~p~n", + [Node,Version]), + dist_util:reset_timer(Timer), +- case inet_tcp:connect(Ip, TcpPort, +- [{active, false}, +- {packet,2}]) of ++ case ++ Driver:connect( ++ Ip, TcpPort, ++ connect_options([{active, false}, {packet, 2}])) ++ of + {ok, Socket} -> + HSData = #hs_data{ + kernel_pid = Kernel, +@@ -272,8 +299,8 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> + timer = Timer, + this_flags = 0, + other_version = Version, +- f_send = fun inet_tcp:send/2, +- f_recv = fun inet_tcp:recv/3, ++ f_send = fun Driver:send/2, ++ f_recv = fun Driver:recv/3, + f_setopts_pre_nodeup = + fun(S) -> + inet:setopts +@@ -298,9 +325,9 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> + address = {Ip,TcpPort}, + host = Address, + protocol = tcp, +- family = inet} ++ family = AddressFamily} + end, +- mf_tick = fun ?MODULE:tick/1, ++ mf_tick = fun(S) -> tick(Driver, S) end, + mf_getstat = fun ?MODULE:getstat/1, + request_type = Type + }, +@@ -324,6 +351,15 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) -> + ?shutdown(Node) + end. + ++connect_options(Opts) -> ++ case application:get_env(kernel, inet_dist_connect_options) of ++ {ok,ConnectOpts} -> ++ erlang:display({inet_dist_connect_options, ConnectOpts}), ++ ConnectOpts ++ Opts; ++ _ -> ++ Opts ++ end. ++ + %% + %% Close a socket. + %% +@@ -332,18 +368,23 @@ close(Socket) -> + + + %% If Node is illegal terminate the connection setup!! +-splitnode(Node, LongOrShortNames) -> ++splitnode(Driver, Node, LongOrShortNames) -> + case split_node(atom_to_list(Node), $@, []) of + [Name|Tail] when Tail =/= [] -> + Host = lists:append(Tail), + case split_node(Host, $., []) of + [_] when LongOrShortNames =:= longnames -> +- error_msg("** System running to use " +- "fully qualified " +- "hostnames **~n" +- "** Hostname ~s is illegal **~n", +- [Host]), +- ?shutdown(Node); ++ case Driver:parse_address(Host) of ++ {ok, _} -> ++ [Name, Host]; ++ _ -> ++ error_msg("** System running to use " ++ "fully qualified " ++ "hostnames **~n" ++ "** Hostname ~s is illegal **~n", ++ [Host]), ++ ?shutdown(Node) ++ end; + L when length(L) > 1, LongOrShortNames =:= shortnames -> + error_msg("** System NOT running to use fully qualified " + "hostnames **~n" +@@ -369,26 +410,26 @@ split_node([], _, Ack) -> [lists:reverse(Ack)]. + %% ------------------------------------------------------------ + %% Fetch local information about a Socket. + %% ------------------------------------------------------------ +-get_tcp_address(Socket) -> ++get_tcp_address(Driver, Socket) -> + {ok, Address} = inet:sockname(Socket), + {ok, Host} = inet:gethostname(), + #net_address { + address = Address, + host = Host, + protocol = tcp, +- family = inet ++ family = Driver:family() + }. + + %% ------------------------------------------------------------ + %% Do only accept new connection attempts from nodes at our + %% own LAN, if the check_ip environment parameter is true. + %% ------------------------------------------------------------ +-check_ip(Socket) -> ++check_ip(Driver, Socket) -> + case application:get_env(check_ip) of + {ok, true} -> + case get_ifs(Socket) of + {ok, IFs, IP} -> +- check_ip(IFs, IP); ++ check_ip(Driver, IFs, IP); + _ -> + ?shutdown(no_node) + end; +@@ -407,20 +448,14 @@ get_ifs(Socket) -> + Error + end. + +-check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) -> +- case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of ++check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) -> ++ case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of + {M, M} -> true; +- _ -> check_ip(IFs, PeerIP) ++ _ -> check_ip(Driver, IFs, PeerIP) + end; +-check_ip([], PeerIP) -> ++check_ip(_Driver, [], PeerIP) -> + {false, PeerIP}. + +-mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) -> +- {M1 band IP1, +- M2 band IP2, +- M3 band IP3, +- M4 band IP4}. +- + is_node_name(Node) when is_atom(Node) -> + case split_node(atom_to_list(Node), $@, []) of + [_, _Host] -> true; +@@ -429,8 +464,14 @@ is_node_name(Node) when is_atom(Node) -> + is_node_name(_Node) -> + false. + +-tick(Sock) -> +- ?to_port(Sock,[],[force]). ++tick(Driver, Socket) -> ++ case Driver:send(Socket, [], [force]) of ++ {error, closed} -> ++ self() ! {tcp_closed, Socket}, ++ {error, closed}; ++ R -> ++ R ++ end. + + getstat(Socket) -> + case inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of +-- +2.5.0 + diff --git a/otp-0023-epmd-support-IPv6-node-registration.patch b/otp-0023-epmd-support-IPv6-node-registration.patch new file mode 100644 index 0000000..0c39261 --- /dev/null +++ b/otp-0023-epmd-support-IPv6-node-registration.patch @@ -0,0 +1,645 @@ +diff -Naur otp_src_R16B03-1.orig/erts/doc/src/epmd.xml otp_src_R16B03-1/erts/doc/src/epmd.xml +--- otp_src_R16B03-1.orig/erts/doc/src/epmd.xml 2016-01-16 14:06:42.802537559 -0500 ++++ otp_src_R16B03-1/erts/doc/src/epmd.xml 2016-01-16 14:07:02.565424309 -0500 +@@ -36,7 +36,7 @@ + +

Erlang Port Mapper Daemon

+ +- ++ + +

Starts the port mapper daemon

+
+diff -Naur otp_src_R16B03-1.orig/erts/doc/src/erl.xml otp_src_R16B03-1/erts/doc/src/erl.xml +--- otp_src_R16B03-1.orig/erts/doc/src/erl.xml 2016-01-16 14:06:42.802537559 -0500 ++++ otp_src_R16B03-1/erts/doc/src/erl.xml 2016-01-16 14:07:02.565424309 -0500 +@@ -381,6 +381,28 @@ + similar to . See + code(3).

+ ++ ++ ++

Specify a protocol for Erlang distribution.

++ ++ inet_tcp ++ ++

TCP over IPv4 (the default)

++
++ inet_ssl ++ ++

distribution over SSL

++
++ inet6_tcp ++ ++

TCP over IPv6

++
++
++

For example, to start up IPv6 distributed nodes:

++
++% erl -name test@ipv6node.example.com -proto_dist inet6_tcp
++
++
+ + +

Starts Erlang with a remote shell connected to .

+diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd.c otp_src_R16B03-1/erts/epmd/src/epmd.c +--- otp_src_R16B03-1.orig/erts/epmd/src/epmd.c 2016-01-16 14:06:42.823537438 -0500 ++++ otp_src_R16B03-1/erts/epmd/src/epmd.c 2016-01-16 14:07:02.565424309 -0500 +@@ -342,7 +342,7 @@ + for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */ + close(fd); + /* Syslog on linux will try to write to whatever if we dont +- inform it of that the log is closed. */ ++ inform it that the log is closed. */ + closelog(); + + /* These chouldn't be needed but for safety... */ +diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c otp_src_R16B03-1/erts/epmd/src/epmd_cli.c +--- otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c 2016-01-16 14:06:42.823537438 -0500 ++++ otp_src_R16B03-1/erts/epmd/src/epmd_cli.c 2016-01-16 14:07:02.565424309 -0500 +@@ -135,19 +135,33 @@ + static int conn_to_epmd(EpmdVars *g) + { + struct EPMD_SOCKADDR_IN address; ++ size_t salen = 0; + int connect_sock; +- +- connect_sock = socket(FAMILY, SOCK_STREAM, 0); +- if (connect_sock<0) +- goto error; ++ unsigned short sport = g->port; ++ ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ SET_ADDR6(address, in6addr_loopback, sport); ++ salen = sizeof(struct sockaddr_in6); ++ ++ connect_sock = socket(AF_INET6, SOCK_STREAM, 0); ++ if (connect_sock>=0) { ++ ++ if (connect(connect_sock, (struct sockaddr*)&address, salen) == 0) ++ return connect_sock; + +- { /* store port number in unsigned short */ +- unsigned short sport = g->port; +- SET_ADDR(address, EPMD_ADDR_LOOPBACK, sport); ++ close(connect_sock); + } ++#endif ++ SET_ADDR(address, htonl(INADDR_LOOPBACK), sport); ++ salen = sizeof(struct sockaddr_in); + +- if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) ++ connect_sock = socket(AF_INET, SOCK_STREAM, 0); ++ if (connect_sock<0) ++ goto error; ++ ++ if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0) + goto error; ++ + return connect_sock; + + error: +diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h otp_src_R16B03-1/erts/epmd/src/epmd_int.h +--- otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h 2016-01-16 14:06:42.823537438 -0500 ++++ otp_src_R16B03-1/erts/epmd/src/epmd_int.h 2016-01-16 14:07:02.565424309 -0500 +@@ -44,9 +44,14 @@ + #include + + #ifdef __WIN32__ ++# ifdef _WIN32_WINVER ++# undef _WIN32_WINVER ++# endif ++# define _WIN32_WINVER 0x0501 + # ifndef WINDOWS_H_INCLUDES_WINSOCK2_H + # include + # endif ++# include + # include + # include + #endif +@@ -167,33 +172,53 @@ + /* ************************************************************************ */ + /* Macros that let us use IPv6 */ + +-#if defined(HAVE_IN6) && defined(AF_INET6) && defined(EPMD6) ++#if HAVE_IN6 ++# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY ++# if HAVE_DECL_IN6ADDR_ANY_INIT ++static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; ++# else ++static const struct in6_addr in6addr_any = ++ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; ++# endif /* HAVE_IN6ADDR_ANY_INIT */ ++# endif /* ! HAVE_DECL_IN6ADDR_ANY */ ++ ++# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK ++# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT ++static const struct in6_addr in6addr_loopback = ++ { { IN6ADDR_LOOPBACK_INIT } }; ++# else ++static const struct in6_addr in6addr_loopback = ++ { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; ++# endif /* HAVE_IN6ADDR_LOOPBACK_INIT */ ++# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ ++#endif /* HAVE_IN6 */ ++ ++#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) ++ ++#if defined(HAVE_IN6) && defined(AF_INET6) + +-#define EPMD_SOCKADDR_IN sockaddr_in6 +-#define EPMD_IN_ADDR in6_addr +-#define EPMD_S_ADDR s6_addr +-#define EPMD_ADDR_LOOPBACK in6addr_loopback.s6_addr +-#define EPMD_ADDR_ANY in6addr_any.s6_addr ++#define EPMD_SOCKADDR_IN sockaddr_storage + #define FAMILY AF_INET6 + +-#define SET_ADDR(dst, addr, port) do { \ +- memset((char*)&(dst), 0, sizeof(dst)); \ +- memcpy((char*)&(dst).sin6_addr.s6_addr, (char*)&(addr), 16); \ +- (dst).sin6_family = AF_INET6; \ +- (dst).sin6_flowinfo = 0; \ +- (dst).sin6_port = htons(port); \ ++#define SET_ADDR6(dst, addr, port) do { \ ++ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&(dst); \ ++ memset(sa, 0, sizeof(dst)); \ ++ sa->sin6_family = AF_INET6; \ ++ sa->sin6_addr = (addr); \ ++ sa->sin6_port = htons(port); \ + } while(0) + +-#define IS_ADDR_LOOPBACK(addr) \ +- (memcmp((addr).s6_addr, in6addr_loopback.s6_addr, 16) == 0) ++#define SET_ADDR(dst, addr, port) do { \ ++ struct sockaddr_in *sa = (struct sockaddr_in *)&(dst); \ ++ memset(sa, 0, sizeof(dst)); \ ++ sa->sin_family = AF_INET; \ ++ sa->sin_addr.s_addr = (addr); \ ++ sa->sin_port = htons(port); \ ++ } while(0) + + #else /* Not IP v6 */ + + #define EPMD_SOCKADDR_IN sockaddr_in +-#define EPMD_IN_ADDR in_addr +-#define EPMD_S_ADDR s_addr +-#define EPMD_ADDR_LOOPBACK htonl(INADDR_LOOPBACK) +-#define EPMD_ADDR_ANY htonl(INADDR_ANY) + #define FAMILY AF_INET + + #define SET_ADDR(dst, addr, port) do { \ +@@ -203,8 +228,6 @@ + (dst).sin_port = htons(port); \ + } while(0) + +-#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) +- + #endif /* Not IP v6 */ + + /* ************************************************************************ */ +diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/epmd/src/epmd_srv.c +--- otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c 2016-01-16 14:06:42.823537438 -0500 ++++ otp_src_R16B03-1/erts/epmd/src/epmd_srv.c 2016-01-16 14:09:00.945745937 -0500 +@@ -70,6 +70,7 @@ + + static Connection *conn_init(EpmdVars*); + static int conn_open(EpmdVars*,int); ++static int conn_local_peer_check(EpmdVars*, int); + static int conn_close_fd(EpmdVars*,int); + + static void node_init(EpmdVars*); +@@ -200,10 +201,11 @@ + { + struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS]; + int listensock[MAX_LISTEN_SOCKETS]; +- int num_sockets; ++ int num_sockets = 0; + int i; + int opt; + unsigned short sport = g->port; ++ int bound = 0; + + node_init(g); + g->conn = conn_init(g); +@@ -246,64 +248,82 @@ + if (g->addresses != NULL && /* String contains non-separator characters if: */ + g->addresses[strspn(g->addresses," ,")] != '\000') + { +- char *tmp; +- char *token; +- int loopback_ok = 0; ++ char *tmp = NULL; ++ char *token = NULL; + +- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL) ++ /* Always listen on the loopback. */ ++ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport); ++ num_sockets++; ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport); ++ num_sockets++; ++#endif ++ ++ if ((tmp = strdup(g->addresses)) == NULL) + { + dbg_perror(g,"cannot allocate memory"); + epmd_cleanup_exit(g,1); + } +- strcpy(tmp,g->addresses); + +- for(token = strtok(tmp,", "), num_sockets = 0; ++ for(token = strtok(tmp,", "); + token != NULL; +- token = strtok(NULL,", "), num_sockets++) ++ token = strtok(NULL,", ")) + { +- struct EPMD_IN_ADDR addr; ++ struct in_addr addr; + #ifdef HAVE_INET_PTON +- int ret; ++ struct in6_addr addr6; ++ struct sockaddr_storage *sa = &iserv_addr[num_sockets]; + +- if ((ret = inet_pton(FAMILY,token,&addr)) == -1) ++ if (inet_pton(AF_INET6,token,&addr6) == 1) + { +- dbg_perror(g,"cannot convert IP address to network format"); +- epmd_cleanup_exit(g,1); ++ SET_ADDR6(iserv_addr[num_sockets],addr6,sport); ++ } ++ else if (inet_pton(AF_INET,token,&addr) == 1) ++ { ++ SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport); ++ } ++ else ++#else ++ if ((addr.s_addr = inet_addr(token)) != INADDR_NONE) ++ { ++ SET_ADDR(iserv_addr[num_sockets],addr.s_addr,sport); + } +- else if (ret == 0) +-#elif !defined(EPMD6) +- if ((addr.EPMD_S_ADDR = inet_addr(token)) == INADDR_NONE) ++ else + #endif + { + dbg_tty_printf(g,0,"cannot parse IP address \"%s\"",token); + epmd_cleanup_exit(g,1); + } + ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6)) ++ continue; ++ ++ if (sa->ss_family == AF_INET) ++#endif + if (IS_ADDR_LOOPBACK(addr)) +- loopback_ok = 1; ++ continue; + +- if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1) ++ num_sockets++; ++ ++ if (num_sockets >= MAX_LISTEN_SOCKETS) + { + dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", + MAX_LISTEN_SOCKETS); + epmd_cleanup_exit(g,1); + } +- +- SET_ADDR(iserv_addr[num_sockets],addr.EPMD_S_ADDR,sport); + } + + free(tmp); +- +- if (!loopback_ok) +- { +- SET_ADDR(iserv_addr[num_sockets],EPMD_ADDR_LOOPBACK,sport); +- num_sockets++; +- } + } + else + { +- SET_ADDR(iserv_addr[0],EPMD_ADDR_ANY,sport); +- num_sockets = 1; ++ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport); ++ num_sockets++; ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport); ++ num_sockets++; ++#endif + } + #ifdef HAVE_SYSTEMD_SD_DAEMON_H + } +@@ -334,13 +354,39 @@ + #endif + for (i = 0; i < num_sockets; i++) + { +- if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) ++ struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i]; ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ size_t salen = (sa->sa_family == AF_INET6 ? ++ sizeof(struct sockaddr_in6) : ++ sizeof(struct sockaddr_in)); ++#else ++ size_t salen = sizeof(struct sockaddr_in); ++#endif ++ ++ if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0) + { +- dbg_perror(g,"error opening stream socket"); +- epmd_cleanup_exit(g,1); ++ switch (errno) { ++ case EAFNOSUPPORT: ++ case EPROTONOSUPPORT: ++ continue; ++ default: ++ dbg_perror(g,"error opening stream socket"); ++ epmd_cleanup_exit(g,1); ++ } + } + g->listenfd[i] = listensock[i]; +- ++ ++#if HAVE_DECL_IPV6_V6ONLY ++ opt = 1; ++ if (sa->sa_family == AF_INET6 && ++ setsockopt(listensock[i],IPPROTO_IPV6,IPV6_V6ONLY,&opt, ++ sizeof(opt)) <0) ++ { ++ dbg_perror(g,"can't set IPv6 only socket option"); ++ epmd_cleanup_exit(g,1); ++ } ++#endif ++ + /* + * Note that we must not enable the SO_REUSEADDR on Windows, + * because addresses will be reused even if they are still in use. +@@ -372,8 +418,7 @@ + dbg_perror(g,"failed to set non-blocking mode of listening socket %d", + listensock[i]); + +- if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], +- sizeof(iserv_addr[i])) < 0) ++ if (bind(listensock[i], (struct sockaddr*) &iserv_addr[i], salen) < 0) + { + if (errno == EADDRINUSE) + { +@@ -388,12 +433,20 @@ + } + } + ++ bound++; ++ + if(listen(listensock[i], SOMAXCONN) < 0) { + dbg_perror(g,"failed to listen on socket"); + epmd_cleanup_exit(g,1); + } + select_fd_set(g, listensock[i]); + } ++ ++ if (bound == 0) { ++ dbg_perror(g,"unable to bind any address"); ++ epmd_cleanup_exit(g,1); ++ } ++ + #ifdef HAVE_SYSTEMD_SD_DAEMON_H + } + sd_notifyf(0, "READY=1\n" +@@ -1001,15 +1054,6 @@ + + for (i = 0; i < g->max_conn; i++) { + if (g->conn[i].open == EPMD_FALSE) { +- struct sockaddr_in si; +- struct sockaddr_in di; +-#ifdef HAVE_SOCKLEN_T +- socklen_t st; +-#else +- int st; +-#endif +- st = sizeof(si); +- + g->active_conn++; + s = &g->conn[i]; + +@@ -1020,20 +1064,7 @@ + s->open = EPMD_TRUE; + s->keep = EPMD_FALSE; + +- /* Determine if connection is from localhost */ +- if (getpeername(s->fd,(struct sockaddr*) &si,&st) || +- st < sizeof(si)) { +- /* Failure to get peername is regarded as non local host */ +- s->local_peer = EPMD_FALSE; +- } else { +- /* Only 127.x.x.x and connections from the host's IP address +- allowed, no false positives */ +- s->local_peer = +- (((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) == +- 0x7F000000U) || +- (getsockname(s->fd,(struct sockaddr*) &di,&st) ? +- EPMD_FALSE : si.sin_addr.s_addr == di.sin_addr.s_addr)); +- } ++ s->local_peer = conn_local_peer_check(g, s->fd); + dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" : + "Non-local peer connected"); + +@@ -1041,7 +1072,7 @@ + s->got = 0; + s->mod_time = current_time(g); /* Note activity */ + +- s->buf = (char *)malloc(INBUF_SIZE); ++ s->buf = malloc(INBUF_SIZE); + + if (s->buf == NULL) { + dbg_printf(g,0,"epmd: Insufficient memory"); +@@ -1059,6 +1090,60 @@ + return EPMD_FALSE; + } + ++static int conn_local_peer_check(EpmdVars *g, int fd) ++{ ++ struct EPMD_SOCKADDR_IN si; ++ struct EPMD_SOCKADDR_IN di; ++ ++ struct sockaddr_in *si4 = (struct sockaddr_in *)&si; ++ struct sockaddr_in *di4 = (struct sockaddr_in *)&di; ++ ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si; ++ struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di; ++#endif ++ ++#ifdef HAVE_SOCKLEN_T ++ socklen_t st; ++#else ++ int st; ++#endif ++ ++ st = sizeof(si); ++ ++ /* Determine if connection is from localhost */ ++ if (getpeername(fd,(struct sockaddr*) &si,&st) || ++ st > sizeof(si)) { ++ /* Failure to get peername is regarded as non local host */ ++ return EPMD_FALSE; ++ } ++ ++ /* Only 127.x.x.x and connections from the host's IP address ++ allowed, no false positives */ ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr))) ++ return EPMD_TRUE; ++ ++ if (si.ss_family == AF_INET) ++#endif ++ if ((((unsigned) ntohl(si4->sin_addr.s_addr)) & 0xFF000000U) == ++ 0x7F000000U) ++ return EPMD_TRUE; ++ ++ if (getsockname(fd,(struct sockaddr*) &di,&st)) ++ return EPMD_FALSE; ++ ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ if (si.ss_family == AF_INET6) ++ return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr)); ++ if (si.ss_family == AF_INET) ++#endif ++ return si4->sin_addr.s_addr == di4->sin_addr.s_addr; ++#if defined(HAVE_IN6) && defined(AF_INET6) ++ return EPMD_FALSE; ++#endif ++} ++ + static int conn_close_fd(EpmdVars *g,int fd) + { + int i; +diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl +--- otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:06:42.823537438 -0500 ++++ otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:07:02.566424303 -0500 +@@ -42,6 +42,7 @@ + -export( + [ + register_name/1, ++ register_name_ipv6/1, + register_names_1/1, + register_names_2/1, + register_duplicate_name/1, +@@ -108,7 +109,8 @@ + suite() -> [{ct_hooks,[ts_install_cth]}]. + + all() -> +- [register_name, register_names_1, register_names_2, ++ [register_name, register_name_ipv6, ++ register_names_1, register_names_2, + register_duplicate_name, unicode_name, long_unicode_name, + get_port_nr, slow_get_port_nr, + unregister_others_name_1, unregister_others_name_2, +@@ -165,6 +167,24 @@ + ?line ok = close(Sock), % Unregister + ok. + ++register_name_ipv6(doc) -> ++ ["Register a name over IPv6"]; ++register_name_ipv6(suite) -> ++ []; ++register_name_ipv6(Config) when is_list(Config) -> ++ % Test if the host has an IPv6 loopback address ++ Res = gen_tcp:listen(0, [inet6, {ip, {0,0,0,0,0,0,0,1}}]), ++ case Res of ++ {ok,LSock} -> ++ gen_tcp:close(LSock), ++ ?line ok = epmdrun(), ++ ?line {ok,Sock} = register_node6("foobar6"), ++ ?line ok = close(Sock), % Unregister ++ ok; ++ _Error -> ++ {skip, "Host does not have an IPv6 loopback address"} ++ end. ++ + register_names_1(doc) -> + ["Register and unregister two nodes"]; + register_names_1(suite) -> +@@ -238,13 +258,18 @@ + register_node(Name,Port) -> + register_node_v2(Port,$M,0,5,5,Name,""). + ++register_node6(Name) -> ++ register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,""). ++ + register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> ++ register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra). ++register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> + Utf8Name = unicode:characters_to_binary(Name), + Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot, + put16(HVsn), put16(LVsn), + put16(size(Utf8Name)), binary_to_list(Utf8Name), + size16(Extra), Extra], +- case send_req(Req) of ++ case send_req(Req, Addr) of + {ok,Sock} -> + case recv(Sock,4) of + {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> +@@ -1129,7 +1154,9 @@ + end. + + send_req(Req) -> +- case connect() of ++ send_req(Req, "localhost"). ++send_req(Req, Addr) -> ++ case connect(Addr) of + {ok,Sock} -> + case send(Sock, [size16(Req), Req]) of + ok -> +diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl +--- otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl 2016-01-16 14:06:43.005536396 -0500 ++++ otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl 2016-01-16 14:07:02.566424303 -0500 +@@ -31,7 +31,7 @@ + %% External exports + -export([start/0, start_link/0, stop/0, port_please/2, + port_please/3, names/0, names/1, +- register_node/2, open/0, open/1, open/2]). ++ register_node/2, register_node/3, open/0, open/1, open/2]). + + %% gen_server callbacks + -export([init/1, handle_call/3, handle_cast/2, handle_info/2, +@@ -106,7 +106,9 @@ + + + register_node(Name, PortNo) -> +- gen_server:call(erl_epmd, {register, Name, PortNo}, infinity). ++ register_node(Name, PortNo, inet). ++register_node(Name, PortNo, Family) -> ++ gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity). + + %%%---------------------------------------------------------------------- + %%% Callback functions from gen_server +@@ -124,10 +126,10 @@ + -spec handle_call(calls(), term(), state()) -> + {'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}. + +-handle_call({register, Name, PortNo}, _From, State) -> ++handle_call({register, Name, PortNo, Family}, _From, State) -> + case State#state.socket of + P when P < 0 -> +- case do_register_node(Name, PortNo) of ++ case do_register_node(Name, PortNo, Family) of + {alive, Socket, Creation} -> + S = State#state{socket = Socket, + port_no = PortNo, +@@ -210,8 +212,12 @@ + close(Socket) -> + gen_tcp:close(Socket). + +-do_register_node(NodeName, TcpPort) -> +- case open() of ++do_register_node(NodeName, TcpPort, Family) -> ++ Localhost = case Family of ++ inet -> open({127,0,0,1}); ++ inet6 -> open({0,0,0,0,0,0,0,1}) ++ end, ++ case Localhost of + {ok, Socket} -> + Name = to_string(NodeName), + Extra = "", From 77046292336ce368b7f3310fdced075118075f61 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 27 Jan 2016 16:10:13 +0300 Subject: [PATCH 08/13] Enable error_logger depth fine tuning Signed-off-by: Peter Lemenkov --- erlang.spec | 19 +- ...t6_tcp_dist-reuse-inet_tcp_dist-code.patch | 9 - ...-epmd-support-IPv6-node-registration.patch | 149 ++- ...unused-code-in-error-logger-handlers.patch | 1028 +++++++++++++++++ ...h-sasl_report-to-limit-crash-reports.patch | 74 ++ 5 files changed, 1217 insertions(+), 62 deletions(-) create mode 100644 otp-0024-Remove-unused-code-in-error-logger-handlers.patch create mode 100644 otp-0025-Teach-sasl_report-to-limit-crash-reports.patch diff --git a/erlang.spec b/erlang.spec index c9d617a..d0d47ec 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.13%{?dist} +Release: %{upstream_rel_for_rpm}.14%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -123,8 +123,14 @@ Patch21: otp-0021-Add-patch-to-crash-dump-on-large-distribution.patch # kernel: inet6_tcp_dist: reuse inet_tcp_dist code Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch # Fedora specific patch -# Add patch for epmd ipv6 support +# epmd: support IPv6 node registration Patch23: otp-0023-epmd-support-IPv6-node-registration.patch +# Fedora specific patch +# Remove unused code in error logger handlers +Patch24: otp-0024-Remove-unused-code-in-error-logger-handlers.patch +# Fedora specific patch +# Teach sasl_report to limit crash reports +Patch25: otp-0025-Teach-sasl_report-to-limit-crash-reports.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -1011,8 +1017,10 @@ Erlang mode for XEmacs (source lisp files). %patch19 -p1 -b .Patch_removes_support_for_SSLv3_protocol_because_it_ %patch20 -p1 -b .R16B03_1_backport_of_TLS_1_0_padding_check %patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution -%patch22 -p1 -b .kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code -%patch23 -p1 -b .Add_patch_for_epmd_ipv6_support +%patch22 -p1 -b .kernel_inet6_tcp_dist_reuse_inet_tcp_dist_code +%patch23 -p1 -b .epmd_support_IPv6_node_registration +%patch24 -p1 -b .Remove_unused_code_in_error_logger_handlers +%patch25 -p1 -b .Teach_sasl_report_to_limit_crash_reports # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2344,6 +2352,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Wed Jan 27 2016 Peter Lemenkov - R16B-03.14 +- Enable error_logger depth fine tuning + * Sun Jan 17 2016 John Eckersberg - R16B-03.13 - Fix inet_dist_listen_{min,max} for ipv6 (rhbz#1299251) - Add patch for epmd ipv6 support (rhbz#1299253) diff --git a/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch b/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch index e08602a..581f39d 100644 --- a/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch +++ b/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch @@ -9,12 +9,6 @@ options) are implemented in inet_tcp_dist only, inet6_tcp_dist looks abandoned (it does not even have tests). This patch makes inet_tcp_dist internals work with abstract driver, and inet6_tcp_dist becomes just a thin wrapper for it. ---- - lib/kernel/src/inet6_tcp.erl | 19 ++ - lib/kernel/src/inet6_tcp_dist.erl | 365 +------------------------------------- - lib/kernel/src/inet_tcp.erl | 16 +- - lib/kernel/src/inet_tcp_dist.erl | 197 ++++++++++++-------- - 4 files changed, 159 insertions(+), 438 deletions(-) diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl index c714b2b..b31f05b 100644 @@ -895,6 +889,3 @@ index 8005eff..0739cf3 100644 getstat(Socket) -> case inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of --- -2.5.0 - diff --git a/otp-0023-epmd-support-IPv6-node-registration.patch b/otp-0023-epmd-support-IPv6-node-registration.patch index 0c39261..da81020 100644 --- a/otp-0023-epmd-support-IPv6-node-registration.patch +++ b/otp-0023-epmd-support-IPv6-node-registration.patch @@ -1,6 +1,50 @@ -diff -Naur otp_src_R16B03-1.orig/erts/doc/src/epmd.xml otp_src_R16B03-1/erts/doc/src/epmd.xml ---- otp_src_R16B03-1.orig/erts/doc/src/epmd.xml 2016-01-16 14:06:42.802537559 -0500 -+++ otp_src_R16B03-1/erts/doc/src/epmd.xml 2016-01-16 14:07:02.565424309 -0500 +From: Michael Santos +Date: Sun, 18 Oct 2015 16:20:37 -0400 +Subject: [PATCH] epmd: support IPv6 node registration + +Allow IPv6 nodes to register with and query epmd. On systems with +IPv6 support: + +* epmd listens on both the IPv4 and IPv6 ANY or loopback sockets + +* the epmd cli client connects to epmd over the IPv6 loopback + +* distributed nodes started with "-proto_dist inet6_tcp" will register + with epmd over IPv6 + +To work on IPv6 capable systems that have IPv6 support disabled, +epmd ignores errors opening the socket if the protocol is not +supported. Similarly, the epmd client will fall back to IPv4 if the IPv6 +socket is not available. + +The interaction between IPv4 and IPv6 sockets depends on the platform: + +* FreeBSD allows multiple "specific" sockets to bind the same port (such + as 2 sockets listening to the same port on ANY and the loopback). + Binding port 4369 to IPv4 and IPv6 sockets simulataneously is allowed. + +* Linux does not allow the same port to be bound by different sockets. + Setting the IPV6_V6ONLY socket option is required. + +* Windows + + The behaviour differs depending on the version of Windows: + + http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx + + According to the site, sockets on Windows XP with Service Pack 1 (SP1) + and Windows Server 2003 will only listen on either IPv4 or IPv6, so + creating two sockets is required to service IPv4 and IPv6 traffic on + the same port. The IPV6_V6ONLY socket option is not supported. + + For Windows Vista and later, a single socket can handle IPv4 and IPv6 + traffic for the same port. The IPV6_V6ONLY socket option is supported + and is enabled for IPv6 sockets by default. + +diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml +index 3e70054..3c9313e 100644 +--- a/erts/doc/src/epmd.xml ++++ b/erts/doc/src/epmd.xml @@ -36,7 +36,7 @@

Erlang Port Mapper Daemon

@@ -10,9 +54,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/doc/src/epmd.xml otp_src_R16B03-1/erts/doc

Starts the port mapper daemon

-diff -Naur otp_src_R16B03-1.orig/erts/doc/src/erl.xml otp_src_R16B03-1/erts/doc/src/erl.xml ---- otp_src_R16B03-1.orig/erts/doc/src/erl.xml 2016-01-16 14:06:42.802537559 -0500 -+++ otp_src_R16B03-1/erts/doc/src/erl.xml 2016-01-16 14:07:02.565424309 -0500 +diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml +index 528a2d9..dada3a1 100644 +--- a/erts/doc/src/erl.xml ++++ b/erts/doc/src/erl.xml @@ -381,6 +381,28 @@ similar to . See code(3).

@@ -42,10 +87,11 @@ diff -Naur otp_src_R16B03-1.orig/erts/doc/src/erl.xml otp_src_R16B03-1/erts/doc/

Starts Erlang with a remote shell connected to .

-diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd.c otp_src_R16B03-1/erts/epmd/src/epmd.c ---- otp_src_R16B03-1.orig/erts/epmd/src/epmd.c 2016-01-16 14:06:42.823537438 -0500 -+++ otp_src_R16B03-1/erts/epmd/src/epmd.c 2016-01-16 14:07:02.565424309 -0500 -@@ -342,7 +342,7 @@ +diff --git a/erts/epmd/src/epmd.c b/erts/epmd/src/epmd.c +index 1757fa9..ebae0a5 100644 +--- a/erts/epmd/src/epmd.c ++++ b/erts/epmd/src/epmd.c +@@ -342,7 +342,7 @@ static void run_daemon(EpmdVars *g) for (fd = 0; fd < g->max_conn ; fd++) /* close all files ... */ close(fd); /* Syslog on linux will try to write to whatever if we dont @@ -54,10 +100,11 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd.c otp_src_R16B03-1/erts/epmd closelog(); /* These chouldn't be needed but for safety... */ -diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c otp_src_R16B03-1/erts/epmd/src/epmd_cli.c ---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c 2016-01-16 14:06:42.823537438 -0500 -+++ otp_src_R16B03-1/erts/epmd/src/epmd_cli.c 2016-01-16 14:07:02.565424309 -0500 -@@ -135,19 +135,33 @@ +diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c +index 8817bde..c0b3729 100644 +--- a/erts/epmd/src/epmd_cli.c ++++ b/erts/epmd/src/epmd_cli.c +@@ -135,19 +135,33 @@ void epmd_call(EpmdVars *g,int what) static int conn_to_epmd(EpmdVars *g) { struct EPMD_SOCKADDR_IN address; @@ -91,17 +138,18 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_cli.c otp_src_R16B03-1/erts/ - if (connect(connect_sock, (struct sockaddr*)&address, sizeof address) < 0) + connect_sock = socket(AF_INET, SOCK_STREAM, 0); + if (connect_sock<0) -+ goto error; + goto error; + + if (connect(connect_sock, (struct sockaddr*)&address, salen) < 0) - goto error; ++ goto error; + return connect_sock; error: -diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h otp_src_R16B03-1/erts/epmd/src/epmd_int.h ---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h 2016-01-16 14:06:42.823537438 -0500 -+++ otp_src_R16B03-1/erts/epmd/src/epmd_int.h 2016-01-16 14:07:02.565424309 -0500 +diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h +index 363923e..e028449 100644 +--- a/erts/epmd/src/epmd_int.h ++++ b/erts/epmd/src/epmd_int.h @@ -44,9 +44,14 @@ #include @@ -198,10 +246,11 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_int.h otp_src_R16B03-1/erts/ #endif /* Not IP v6 */ /* ************************************************************************ */ -diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/epmd/src/epmd_srv.c ---- otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c 2016-01-16 14:06:42.823537438 -0500 -+++ otp_src_R16B03-1/erts/epmd/src/epmd_srv.c 2016-01-16 14:09:00.945745937 -0500 -@@ -70,6 +70,7 @@ +diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c +index 78524a6..7b4d774 100644 +--- a/erts/epmd/src/epmd_srv.c ++++ b/erts/epmd/src/epmd_srv.c +@@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*); static Connection *conn_init(EpmdVars*); static int conn_open(EpmdVars*,int); @@ -209,7 +258,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ static int conn_close_fd(EpmdVars*,int); static void node_init(EpmdVars*); -@@ -200,10 +201,11 @@ +@@ -200,10 +201,11 @@ void run(EpmdVars *g) { struct EPMD_SOCKADDR_IN iserv_addr[MAX_LISTEN_SOCKETS]; int listensock[MAX_LISTEN_SOCKETS]; @@ -222,7 +271,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ node_init(g); g->conn = conn_init(g); -@@ -246,64 +248,82 @@ +@@ -246,64 +248,82 @@ void run(EpmdVars *g) if (g->addresses != NULL && /* String contains non-separator characters if: */ g->addresses[strspn(g->addresses," ,")] != '\000') { @@ -231,8 +280,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ - int loopback_ok = 0; + char *tmp = NULL; + char *token = NULL; - -- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL) ++ + /* Always listen on the loopback. */ + SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport); + num_sockets++; @@ -240,7 +288,8 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ + SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport); + num_sockets++; +#endif -+ + +- if ((tmp = (char *)malloc(strlen(g->addresses) + 1)) == NULL) + if ((tmp = strdup(g->addresses)) == NULL) { dbg_perror(g,"cannot allocate memory"); @@ -297,10 +346,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ if (IS_ADDR_LOOPBACK(addr)) - loopback_ok = 1; + continue; ++ ++ num_sockets++; - if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1) -+ num_sockets++; -+ + if (num_sockets >= MAX_LISTEN_SOCKETS) { dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", @@ -332,7 +381,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ } #ifdef HAVE_SYSTEMD_SD_DAEMON_H } -@@ -334,13 +354,39 @@ +@@ -334,13 +354,39 @@ void run(EpmdVars *g) #endif for (i = 0; i < num_sockets; i++) { @@ -376,7 +425,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ /* * Note that we must not enable the SO_REUSEADDR on Windows, * because addresses will be reused even if they are still in use. -@@ -372,8 +418,7 @@ +@@ -372,8 +418,7 @@ void run(EpmdVars *g) dbg_perror(g,"failed to set non-blocking mode of listening socket %d", listensock[i]); @@ -386,7 +435,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ { if (errno == EADDRINUSE) { -@@ -388,12 +433,20 @@ +@@ -388,12 +433,20 @@ void run(EpmdVars *g) } } @@ -407,7 +456,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ #ifdef HAVE_SYSTEMD_SD_DAEMON_H } sd_notifyf(0, "READY=1\n" -@@ -1001,15 +1054,6 @@ +@@ -1001,15 +1054,6 @@ static int conn_open(EpmdVars *g,int fd) for (i = 0; i < g->max_conn; i++) { if (g->conn[i].open == EPMD_FALSE) { @@ -423,7 +472,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ g->active_conn++; s = &g->conn[i]; -@@ -1020,20 +1064,7 @@ +@@ -1020,20 +1064,7 @@ static int conn_open(EpmdVars *g,int fd) s->open = EPMD_TRUE; s->keep = EPMD_FALSE; @@ -445,7 +494,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" : "Non-local peer connected"); -@@ -1041,7 +1072,7 @@ +@@ -1041,7 +1072,7 @@ static int conn_open(EpmdVars *g,int fd) s->got = 0; s->mod_time = current_time(g); /* Note activity */ @@ -454,7 +503,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ if (s->buf == NULL) { dbg_printf(g,0,"epmd: Insufficient memory"); -@@ -1059,6 +1090,60 @@ +@@ -1059,6 +1090,60 @@ static int conn_open(EpmdVars *g,int fd) return EPMD_FALSE; } @@ -515,9 +564,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/src/epmd_srv.c otp_src_R16B03-1/erts/ static int conn_close_fd(EpmdVars *g,int fd) { int i; -diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl ---- otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:06:42.823537438 -0500 -+++ otp_src_R16B03-1/erts/epmd/test/epmd_SUITE.erl 2016-01-16 14:07:02.566424303 -0500 +diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl +index cc24a55..ddfe5ab 100644 +--- a/erts/epmd/test/epmd_SUITE.erl ++++ b/erts/epmd/test/epmd_SUITE.erl @@ -42,6 +42,7 @@ -export( [ @@ -536,7 +586,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/ register_duplicate_name, unicode_name, long_unicode_name, get_port_nr, slow_get_port_nr, unregister_others_name_1, unregister_others_name_2, -@@ -165,6 +167,24 @@ +@@ -165,6 +167,24 @@ register_name(Config) when is_list(Config) -> ?line ok = close(Sock), % Unregister ok. @@ -561,7 +611,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/ register_names_1(doc) -> ["Register and unregister two nodes"]; register_names_1(suite) -> -@@ -238,13 +258,18 @@ +@@ -238,13 +258,18 @@ register_node(Name) -> register_node(Name,Port) -> register_node_v2(Port,$M,0,5,5,Name,""). @@ -581,7 +631,7 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/ {ok,Sock} -> case recv(Sock,4) of {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> -@@ -1129,7 +1154,9 @@ +@@ -1129,7 +1154,9 @@ send_direct(Sock, Bytes) -> end. send_req(Req) -> @@ -592,9 +642,10 @@ diff -Naur otp_src_R16B03-1.orig/erts/epmd/test/epmd_SUITE.erl otp_src_R16B03-1/ {ok,Sock} -> case send(Sock, [size16(Req), Req]) of ok -> -diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl ---- otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl 2016-01-16 14:06:43.005536396 -0500 -+++ otp_src_R16B03-1/lib/kernel/src/erl_epmd.erl 2016-01-16 14:07:02.566424303 -0500 +diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl +index 91af49f..21a3dec 100644 +--- a/lib/kernel/src/erl_epmd.erl ++++ b/lib/kernel/src/erl_epmd.erl @@ -31,7 +31,7 @@ %% External exports -export([start/0, start_link/0, stop/0, port_please/2, @@ -604,7 +655,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, -@@ -106,7 +106,9 @@ +@@ -106,7 +106,9 @@ names1(HostName) -> register_node(Name, PortNo) -> @@ -615,7 +666,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li %%%---------------------------------------------------------------------- %%% Callback functions from gen_server -@@ -124,10 +126,10 @@ +@@ -124,10 +126,10 @@ init(_) -> -spec handle_call(calls(), term(), state()) -> {'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}. @@ -628,7 +679,7 @@ diff -Naur otp_src_R16B03-1.orig/lib/kernel/src/erl_epmd.erl otp_src_R16B03-1/li {alive, Socket, Creation} -> S = State#state{socket = Socket, port_no = PortNo, -@@ -210,8 +212,12 @@ +@@ -210,8 +212,12 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) -> close(Socket) -> gen_tcp:close(Socket). diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch new file mode 100644 index 0000000..a3ea0ac --- /dev/null +++ b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch @@ -0,0 +1,1028 @@ +From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= +Date: Thu, 20 Aug 2015 09:44:43 +0200 +Subject: [PATCH] Remove unused code in error logger handlers + +A long time ago, errors from the emulator itself was sent as +messages that would end up in the handle_info/2 function. +Those clauses in handle_info/2 can be removed. + +The code for handling events tagged 'info' instead of 'info_msg' +can also be taken out. + +error_logger_file_h: Refactor and modernize code + +Refactor, simplify, and modernize the code to facilitate future +improvements in the following commits. + +Teach error_logger_file_h to truncate big messages + +Add the possibility to truncate big messages to avoid running out +of memory. + +error_logger_tty_h: Refactor and modernize code + +Refactor, simplify, and modernize the code to facilitate future +improvements in the following commits. + +Teach error_logger_tty_h to truncate big messages + +Add the possibility to truncate big messages to avoid running out +of memory. + +stdlib: Fix leaking files after error_logger:logfile(close) + +Introduced when changing state from tuple to record. + +Make the scanned form of the io_lib format strings available for processing + +This adds three new functions to io_lib - scan_format/2, unscan_format/1, +and build_text/1 - which expose the parsed form of the format control +sequences to make it possible to easily modify or filter the input to +io_lib:format/2. This can e.g. be used in order to replace unbounded-size +control sequences like ~w or ~p with corresponding depth-limited ~W and ~P +before doing the actual formatting. + +diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml +index 90f24c4..6613dcd 100644 +--- a/lib/stdlib/doc/src/io.xml ++++ b/lib/stdlib/doc/src/io.xml +@@ -505,7 +505,8 @@ ok +

Writes the data with standard syntax in the same way as + ~w, but breaks terms whose printed representation + is longer than one line into many lines and indents each +- line sensibly. It also tries to detect lists of ++ line sensibly. Left justification is not supported. ++ It also tries to detect lists of + printable characters and to output these as strings. The + Unicode translation modifier is used for determining + what characters are printable. For example:

+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml +index 68352ff..0c1c84d 100644 +--- a/lib/stdlib/doc/src/io_lib.xml ++++ b/lib/stdlib/doc/src/io_lib.xml +@@ -4,7 +4,7 @@ + +
+ +- 19962013 ++ 19962014 + Ericsson AB. All Rights Reserved. + + +@@ -59,6 +59,35 @@ + + + ++ ++ ++

Description:

++ ++

control_char is the type of control ++ sequence: $P, $w, and so on;

++
++

args is a list of the arguments used by the ++ control sequence, or an empty list if the control sequence ++ does not take any arguments;

++
++

width is the field width;

++
++

adjust is the adjustment;

++
++

precision is the precision of the printed ++ argument;

++
++

pad_char is the padding character;

++
++

encoding is set to true if the translation ++ modifier t is present;

++
++

strings is set to false if the modifier ++ l is present.

++
++
++
++
+ + + +@@ -260,6 +289,45 @@ + + + ++ ++ Parse all control sequences in the format string ++ ++

Returns a list corresponding to the given format string, ++ where control sequences have been replaced with ++ corresponding tuples. This list can be passed to io_lib:build_text/1 to have ++ the same effect as io_lib:format(Format, Args), or to ++ io_lib:unscan_format/1 ++ in order to get the corresponding pair of Format and ++ Args (with every * and corresponding argument ++ expanded to numeric values).

++

A typical use of this function is to replace unbounded-size ++ control sequences like ~w and ~p with the ++ depth-limited variants ~W and ~P before ++ formatting to text, e.g. in a logger.

++
++
++ ++ ++ Revert a pre-parsed format list to a plain character list ++ and a list of arguments ++ ++

See io_lib:scan_format/2 for ++ details.

++
++
++ ++ ++ Build the output text for a pre-parsed format list ++ ++

See io_lib:scan_format/2 for ++ details.

++
++
++ + + Indentation after printing string + +diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl +index e9364ed..ca44d01 100644 +--- a/lib/stdlib/src/error_logger_file_h.erl ++++ b/lib/stdlib/src/error_logger_file_h.erl +@@ -23,24 +23,28 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. +-%%% Writes all events formatted to file. +-%%% Handles events tagged error, emulator and info. ++%%% event handler. Writes all events formatted to file. + %%% + %%% It can only be started from error_logger:swap_handler({logfile, File}) +-%%% or error_logger:logfile(File) ++%%% or error_logger:logfile(File). + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + ++-record(st, ++ {fd, ++ filename, ++ prev_handler, ++ depth=unlimited :: 'unlimited' | non_neg_integer()}). ++ + %% This one is used when we takeover from the simple error_logger. + init({File, {error_logger, Buf}}) -> + case init(File, error_logger) of +- {ok, {Fd, File, PrevHandler}} -> +- write_events(Fd, Buf), +- {ok, {Fd, File, PrevHandler}}; ++ {ok, State} -> ++ write_events(State, Buf), ++ {ok, State}; + Error -> + Error + end; +@@ -52,49 +56,45 @@ init(File, PrevHandler) -> + process_flag(trap_exit, true), + case file:open(File, [write]) of + {ok,Fd} -> +- {ok, {Fd, File, PrevHandler}}; ++ Depth = get_depth(), ++ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler, ++ depth=Depth}, ++ {ok, State}; + Error -> + Error + end. +- ++ ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. ++ + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; +-handle_event(Event, {Fd, File, PrevHandler}) -> +- write_event(Fd, tag_event(Event)), +- {ok, {Fd, File, PrevHandler}}; +-handle_event(_, State) -> ++handle_event(Event, State) -> ++ write_event(State, Event), + {ok, State}. + +-handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) -> ++handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) -> + case PrevHandler of + [] -> + remove_handler; + _ -> + {swap_handler, install_prev, [], PrevHandler, go_back} + end; +-handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler}) +- when node(GL) == node() -> +- write_event(Fd, tag_event({emulator, GL, Chars})), +- {ok, {Fd, File, PrevHandler}}; +-handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) -> +- write_event(Fd, tag_event({emulator, noproc, Chars})), +- {ok, {Fd, File, PrevHandler}}; + handle_info(_, State) -> + {ok, State}. + +-handle_call(filename, {Fd, File, Prev}) -> +- {ok, File, {Fd, File, Prev}}; ++handle_call(filename, #st{filename=File}=State) -> ++ {ok, File, State}; + handle_call(_Query, State) -> + {ok, {error, bad_query}, State}. + +-terminate(_Reason, State) -> +- case State of +- {Fd, _File, _Prev} -> +- ok = file:close(Fd); +- _ -> +- ok +- end, +- []. ++terminate(_Reason, #st{fd=Fd}) -> ++ file:close(Fd). + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. +@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) -> + %%% Misc. functions. + %%% ------------------------------------------------------ + +-tag_event(Event) -> +- {erlang:universaltime(), Event}. ++write_events(State, [Ev|Es]) -> ++ %% Write the events in reversed order. ++ write_events(State, Es), ++ write_event(State, Ev); ++write_events(_State, []) -> ++ ok. + +-write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)). ++write_event(#st{fd=Fd}=State, Event) -> ++ case parse_event(Event) of ++ ignore -> ++ ok; ++ {Head,Pid,FormatList} -> ++ Time = maybe_utc(erlang:universaltime()), ++ Header = write_time(Time, Head), ++ Body = format_body(State, FormatList), ++ AtNode = if ++ node(Pid) =/= node() -> ++ ["** at node ",atom_to_list(node(Pid))," **\n"]; ++ true -> ++ [] ++ end, ++ io:put_chars(Fd, [Header,Body,AtNode]) ++ end. + +-write_events1(Fd, [Event|Es]) -> +- write_event(Fd, Event), +- write_events1(Fd, Es); +-write_events1(_, []) -> +- ok. ++format_body(State, [{Format,Args}|T]) -> ++ S = try format(State, Format, Args) of ++ S0 -> ++ S0 ++ catch ++ _:_ -> ++ format(State, "ERROR: ~p - ~p\n", [Format,Args]) ++ end, ++ [S|format_body(State, T)]; ++format_body(_State, []) -> ++ []. + +-write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(Fd, {Time, {emulator, _GL, Chars}}) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(Chars, []) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars]) +- end; +-write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> +- T = write_time(maybe_utc(Time)), +- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]); +-write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> +- T = write_time(maybe_utc(Time)), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(_, _) -> +- ok. ++format(#st{depth=unlimited}, Format, Args) -> ++ io_lib:format(Format, Args); ++format(#st{depth=Depth}, Format0, Args) -> ++ Format1 = io_lib:scan_format(Format0, Args), ++ Format = limit_format(Format1, Depth), ++ io_lib:build_text(Format). ++ ++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; ++ C0 =:= $w -> ++ C = C0 - ($a - $A), %To uppercase. ++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)]; ++limit_format([H|T], Depth) -> ++ [H|limit_format(T, Depth)]; ++limit_format([], _) -> ++ []. ++ ++parse_event({error, _GL, {Pid, Format, Args}}) -> ++ {"ERROR REPORT",Pid,[{Format,Args}]}; ++parse_event({info_msg, _GL, {Pid, Format, Args}}) -> ++ {"INFO REPORT",Pid,[{Format, Args}]}; ++parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> ++ {"WARNING REPORT",Pid,[{Format,Args}]}; ++parse_event({error_report, _GL, {Pid, std_error, Args}}) -> ++ {"ERROR REPORT",Pid,format_term(Args)}; ++parse_event({info_report, _GL, {Pid, std_info, Args}}) -> ++ {"INFO REPORT",Pid,format_term(Args)}; ++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> ++ {"WARNING REPORT",Pid,format_term(Args)}; ++parse_event(_) -> ignore. + + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of +@@ -182,30 +184,27 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format_report(Rep) when is_list(Rep) -> +- case string_p(Rep) of ++format_term(Term) when is_list(Term) -> ++ case string_p(Term) of + true -> +- io_lib:format("~s~n",[Rep]); +- _ -> +- format_rep(Rep) ++ [{"~s\n",[Term]}]; ++ false -> ++ format_term_list(Term) + end; +-format_report(Rep) -> +- io_lib:format("~p~n",[Rep]). +- +-format_rep([{Tag,Data}|Rep]) -> +- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); +-format_rep([Other|Rep]) -> +- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); +-format_rep(_) -> ++format_term(Term) -> ++ [{"~p\n",[Term]}]. ++ ++format_term_list([{Tag,Data}|T]) -> ++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; ++format_term_list([Data|T]) -> ++ [{" ~p\n",[Data]}|format_term_list(T)]; ++format_term_list([]) -> ++ []; ++format_term_list(_) -> ++ %% Continue to allow non-proper lists for now. ++ %% FIXME: Remove this clause in OTP 19. + []. + +-add_node(X, Pid) when is_atom(X) -> +- add_node(atom_to_list(X), Pid); +-add_node(X, Pid) when node(Pid) =/= node() -> +- lists:concat([X,"** at node ",node(Pid)," **~n"]); +-add_node(X, _) -> +- X. +- + string_p([]) -> + false; + string_p(Term) -> +@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T); + string_p1([$\f|T]) -> string_p1(T); + string_p1([$\e|T]) -> string_p1(T); + string_p1([H|T]) when is_list(H) -> +- case string_p1(H) of +- true -> string_p1(T); +- _ -> false +- end; ++ string_p1(H) andalso string_p1(T); + string_p1([]) -> true; + string_p1(_) -> false. + +-write_time(Time) -> write_time(Time, "ERROR REPORT"). +- + write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl +index ad5891f..72a052f 100644 +--- a/lib/stdlib/src/error_logger_tty_h.erl ++++ b/lib/stdlib/src/error_logger_tty_h.erl +@@ -22,144 +22,178 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. +-%%% Writes all events formatted to stdout. +-%%% Handles events tagged error, emulator and info. ++%%% event handler. Writes all events formatted to stdout. + %%% + %%% It can only be started from error_logger:swap_handler(tty) +-%%% or error_logger:tty(true) ++%%% or error_logger:tty(true). + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + +--export([write_event/2]). ++-export([write_event/2,write_event/3]). ++ ++-record(st, ++ {user, ++ prev_handler, ++ io_mod=io, ++ depth=unlimited}). + + %% This one is used when we takeover from the simple error_logger. + init({[], {error_logger, Buf}}) -> + User = set_group_leader(), +- write_events(Buf,io), +- {ok, {User, error_logger}}; ++ Depth = get_depth(), ++ State = #st{user=User,prev_handler=error_logger,depth=Depth}, ++ write_events(State, Buf), ++ {ok, State}; + %% This one is used if someone took over from us, and now wants to + %% go back. + init({[], {error_logger_tty_h, PrevHandler}}) -> + User = set_group_leader(), +- {ok, {User, PrevHandler}}; ++ {ok, #st{user=User,prev_handler=PrevHandler}}; + %% This one is used when we are started directly. + init([]) -> + User = set_group_leader(), +- {ok, {User, []}}. ++ Depth = get_depth(), ++ {ok, #st{user=User,prev_handler=[],depth=Depth}}. ++ ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. + + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; + handle_event(Event, State) -> +- write_event(tag_event(Event),io), ++ ok = do_write_event(State, tag_event(Event)), + {ok, State}. + +-handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> ++handle_info({'EXIT', User, _Reason}, ++ #st{user=User,prev_handler=PrevHandler}=State) -> + case PrevHandler of + [] -> + remove_handler; + _ -> +- {swap_handler, install_prev, {User, PrevHandler}, ++ {swap_handler, install_prev, State, + PrevHandler, go_back} + end; +-handle_info({emulator, GL, Chars}, State) when node(GL) == node() -> +- write_event(tag_event({emulator, GL, Chars}),io), +- {ok, State}; +-handle_info({emulator, noproc, Chars}, State) -> +- write_event(tag_event({emulator, noproc, Chars}),io), +- {ok, State}; + handle_info(_, State) -> + {ok, State}. + + handle_call(_Query, State) -> {ok, {error, bad_query}, State}. + +-% unfortunately, we can't unlink from User - links are not counted! +-% if pid(User) -> unlink(User); true -> ok end, + terminate(install_prev, _State) -> + []; +-terminate(_Reason, {_User, PrevHandler}) -> ++terminate(_Reason, #st{prev_handler=PrevHandler}) -> + {error_logger_tty_h, PrevHandler}. + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. + ++%% Exported (but unoffical) API. ++write_event(Event, IoMod) -> ++ do_write_event(#st{io_mod=IoMod}, Event). ++ ++write_event(Event, IoMod, Depth) -> ++ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event). ++ ++ + %%% ------------------------------------------------------ + %%% Misc. functions. + %%% ------------------------------------------------------ + + set_group_leader() -> + case whereis(user) of +- User when is_pid(User) -> link(User), group_leader(User,self()), User; +- _ -> false ++ User when is_pid(User) -> ++ link(User), ++ group_leader(User,self()), ++ User; ++ _ -> ++ false + end. + + tag_event(Event) -> + {erlang:universaltime(), Event}. + +-write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod). +- +-write_events1([Event|Es],IOMod) -> +- write_event(Event,IOMod), +- write_events1(Es,IOMod); +-write_events1([],_IOMod) -> ++write_events(State, [Ev|Es]) -> ++ %% Write the events in reverse order. ++ _ = write_events(State, Es), ++ _ = do_write_event(State, Ev), ++ ok; ++write_events(_State, []) -> + ok. + +-write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) ++do_write_event(State, {Time0, Event}) -> ++ case parse_event(Event) of ++ ignore -> ++ ok; ++ {Head,Pid,FormatList} -> ++ Time = maybe_utc(Time0), ++ Header = write_time(Time, Head), ++ Body = format_body(State, FormatList), ++ AtNode = if ++ node(Pid) =/= node() -> ++ ["** at node ",atom_to_list(node(Pid))," **\n"]; ++ true -> ++ [] ++ end, ++ Str = [Header,Body,AtNode], ++ case State#st.io_mod of ++ io_lib -> ++ Str; ++ io -> ++ io:put_chars(user, Str) ++ end + end; +-write_event({Time, {emulator, _GL, Chars}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(Chars, []) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- format(IOMod, T ++ "ERROR: ~p ~n", [Chars]) +- end; +-write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- format(IOMod, T ++ add_node("~p~n",Pid),[Info]); +-write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) +- end; +-write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) +- end; +-write_event({_Time, _Error},_IOMod) -> ++do_write_event(_, _) -> + ok. + ++format_body(State, [{Format,Args}|T]) -> ++ S = try format(State, Format, Args) of ++ S0 -> ++ S0 ++ catch ++ _:_ -> ++ format(State, "ERROR: ~p - ~p\n", [Format,Args]) ++ end, ++ [S|format_body(State, T)]; ++format_body(_State, []) -> ++ []. ++ ++format(#st{depth=unlimited}, Format, Args) -> ++ io_lib:format(Format, Args); ++format(#st{depth=Depth}, Format0, Args) -> ++ Format1 = io_lib:scan_format(Format0, Args), ++ Format = limit_format(Format1, Depth), ++ io_lib:build_text(Format). ++ ++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; ++ C0 =:= $w -> ++ C = C0 - ($a - $A), %To uppercase. ++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)]; ++limit_format([H|T], Depth) -> ++ [H|limit_format(T, Depth)]; ++limit_format([], _) -> ++ []. ++ ++parse_event({error, _GL, {Pid, Format, Args}}) -> ++ {"ERROR REPORT",Pid,[{Format,Args}]}; ++parse_event({info_msg, _GL, {Pid, Format, Args}}) -> ++ {"INFO REPORT",Pid,[{Format, Args}]}; ++parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> ++ {"WARNING REPORT",Pid,[{Format,Args}]}; ++parse_event({error_report, _GL, {Pid, std_error, Args}}) -> ++ {"ERROR REPORT",Pid,format_term(Args)}; ++parse_event({info_report, _GL, {Pid, std_info, Args}}) -> ++ {"INFO REPORT",Pid,format_term(Args)}; ++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> ++ {"WARNING REPORT",Pid,format_term(Args)}; ++parse_event(_) -> ignore. ++ + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of + {ok, Val} -> Val; +@@ -175,33 +209,26 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format(IOMod, String) -> format(IOMod, String, []). +-format(io_lib, String, Args) -> io_lib:format(String, Args); +-format(io, String, Args) -> io:format(user, String, Args). +- +-format_report(Rep) when is_list(Rep) -> +- case string_p(Rep) of ++format_term(Term) when is_list(Term) -> ++ case string_p(Term) of + true -> +- io_lib:format("~s~n",[Rep]); +- _ -> +- format_rep(Rep) ++ [{"~s\n",[Term]}]; ++ false -> ++ format_term_list(Term) + end; +-format_report(Rep) -> +- io_lib:format("~p~n",[Rep]). +- +-format_rep([{Tag,Data}|Rep]) -> +- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); +-format_rep([Other|Rep]) -> +- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); +-format_rep(_) -> +- []. ++format_term(Term) -> ++ [{"~p\n",[Term]}]. + +-add_node(X, Pid) when is_atom(X) -> +- add_node(atom_to_list(X), Pid); +-add_node(X, Pid) when node(Pid) =/= node() -> +- lists:concat([X,"** at node ",node(Pid)," **~n"]); +-add_node(X, _) -> +- X. ++format_term_list([{Tag,Data}|T]) -> ++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; ++format_term_list([Data|T]) -> ++ [{" ~p\n",[Data]}|format_term_list(T)]; ++format_term_list([]) -> ++ []; ++format_term_list(_) -> ++ %% Continue to allow non-proper lists for now. ++ %% FIXME: Remove this clause in OTP 19. ++ []. + + string_p([]) -> + false; +@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) -> + string_p1([]) -> true; + string_p1(_) -> false. + +-write_time(Time) -> write_time(Time, "ERROR REPORT"). + write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl +index 9e69601..3c173dc 100644 +--- a/lib/stdlib/src/io_lib.erl ++++ b/lib/stdlib/src/io_lib.erl +@@ -2,7 +2,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2013. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2014. 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 +@@ -61,6 +61,7 @@ + -module(io_lib). + + -export([fwrite/2,fread/2,fread/3,format/2]). ++-export([scan_format/2,unscan_format/1,build_text/1]). + -export([print/1,print/4,indentation/2]). + + -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). +@@ -84,7 +85,7 @@ + deep_unicode_char_list/1]). + + -export_type([chars/0, latin1_string/0, continuation/0, +- fread_error/0, fread_item/0]). ++ fread_error/0, fread_item/0, format_spec/0]). + + %%---------------------------------------------------------------------- + +@@ -109,6 +110,18 @@ + + -type fread_item() :: string() | atom() | integer() | float(). + ++-type format_spec() :: ++ { ++ ControlChar :: char(), ++ Args :: [any()], ++ Width :: 'none' | integer(), ++ Adjust :: 'left' | 'right', ++ Precision :: 'none' | integer(), ++ PadChar :: char(), ++ Encoding :: 'unicode' | 'latin1', ++ Strings :: boolean() ++ }. ++ + %%---------------------------------------------------------------------- + + %% Interface calls to sub-modules. +@@ -157,6 +170,31 @@ format(Format, Args) -> + Other + end. + ++-spec scan_format(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | format_spec()]. ++ ++scan_format(Format, Args) -> ++ try io_lib_format:scan(Format, Args) ++ catch ++ _:_ -> erlang:error(badarg, [Format, Args]) ++ end. ++ ++-spec unscan_format(FormatList) -> {Format, Data} when ++ FormatList :: [char() | format_spec()], ++ Format :: io:format(), ++ Data :: [term()]. ++ ++unscan_format(FormatList) -> ++ io_lib_format:unscan(FormatList). ++ ++-spec build_text(FormatList) -> chars() when ++ FormatList :: [char() | format_spec()]. ++ ++build_text(FormatList) -> ++ io_lib_format:build(FormatList). ++ + -spec print(Term) -> chars() when + Term :: term(). + +diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl +index 56e15a1..37b47d7 100644 +--- a/lib/stdlib/src/io_lib_format.erl ++++ b/lib/stdlib/src/io_lib_format.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2013. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2014. 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 +@@ -20,10 +20,9 @@ + + %% Formatting functions of io library. + +--export([fwrite/2,fwrite_g/1,indentation/2]). ++-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]). + +-%% fwrite(Format, ArgList) -> string(). +-%% Format the arguments in ArgList after string Format. Just generate ++%% Format the arguments in Args after string Format. Just generate + %% an error if there is an error in the arguments. + %% + %% To do the printing command correctly we need to calculate the +@@ -37,15 +36,83 @@ + %% and it also splits the handling of the control characters into two + %% parts. + +-fwrite(Format, Args) when is_atom(Format) -> +- fwrite(atom_to_list(Format), Args); +-fwrite(Format, Args) when is_binary(Format) -> +- fwrite(binary_to_list(Format), Args); ++-spec fwrite(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | io_lib:format_spec()]. ++ + fwrite(Format, Args) -> +- Cs = collect(Format, Args), ++ build(scan(Format, Args)). ++ ++%% Build the output text for a pre-parsed format list. ++ ++-spec build(FormatList) -> io_lib:chars() when ++ FormatList :: [char() | io_lib:format_spec()]. ++ ++build(Cs) -> + Pc = pcount(Cs), + build(Cs, Pc, 0). + ++%% Parse all control sequences in the format string. ++ ++-spec scan(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | io_lib:format_spec()]. ++ ++scan(Format, Args) when is_atom(Format) -> ++ scan(atom_to_list(Format), Args); ++scan(Format, Args) when is_binary(Format) -> ++ scan(binary_to_list(Format), Args); ++scan(Format, Args) -> ++ collect(Format, Args). ++ ++%% Revert a pre-parsed format list to a plain character list and a ++%% list of arguments. ++ ++-spec unscan(FormatList) -> {Format, Data} when ++ FormatList :: [char() | io_lib:format_spec()], ++ Format :: io:format(), ++ Data :: [term()]. ++ ++unscan(Cs) -> ++ {print(Cs), args(Cs)}. ++ ++args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) -> ++ As ++ args(Cs); ++args([_C | Cs]) -> ++ args(Cs); ++args([]) -> ++ []. ++ ++print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) -> ++ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs); ++print([C | Cs]) -> ++ [C | print(Cs)]; ++print([]) -> ++ []. ++ ++print(C, F, Ad, P, Pad, Encoding, Strings) -> ++ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++ ++ print_pad_char(Pad) ++ print_encoding(Encoding) ++ ++ print_strings(Strings) ++ [C]. ++ ++print_field_width(none, _Ad) -> ""; ++print_field_width(F, left) -> integer_to_list(-F); ++print_field_width(F, right) -> integer_to_list(F). ++ ++print_precision(none) -> ""; ++print_precision(P) -> [$. | integer_to_list(P)]. ++ ++print_pad_char($\s) -> ""; % default, no need to make explicit ++print_pad_char(Pad) -> [$., Pad]. ++ ++print_encoding(unicode) -> "t"; ++print_encoding(latin1) -> "". ++ ++print_strings(false) -> "l"; ++print_strings(true) -> "". ++ + collect([$~|Fmt0], Args0) -> + {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0), + [C|collect(Fmt1, Args1)]; +@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); + pcount([_|Cs], Acc) -> pcount(Cs, Acc); + pcount([], Acc) -> Acc. + +-%% build([Control], Pc, Indentation) -> string(). ++%% build([Control], Pc, Indentation) -> io_lib:chars(). + %% Interpret the control structures. Count the number of print + %% remaining and only calculate indentation when necessary. Must also + %% be smart when calculating indentation for characters in format. +@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1; + decr_pc($P, Pc) -> Pc - 1; + decr_pc(_, Pc) -> Pc. + +-%% indentation(String, Indentation) -> Indentation. ++ + %% Calculate the indentation of the end of a string given its start + %% indentation. We assume tabs at 8 cols. + ++-spec indentation(String, StartIndent) -> integer() when ++ String :: io_lib:chars(), ++ StartIndent :: integer(). ++ + indentation([$\n|Cs], _I) -> indentation(Cs, 0); + indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8); + indentation([C|Cs], I) when is_integer(C) -> +@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 -> + float_data([_|Cs], Ds) -> + float_data(Cs, Ds). + +-%% fwrite_g(Float) + %% Writes the shortest, correctly rounded string that converts + %% to Float when read back with list_to_float/1. + %% +@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) -> + %% in Proceedings of the SIGPLAN '96 Conference on Programming + %% Language Design and Implementation. + ++-spec fwrite_g(float()) -> string(). ++ + fwrite_g(0.0) -> + "0.0"; + fwrite_g(Float) when is_float(Float) -> +@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) + term([Prefix|S], F, Adj, none, Pad) + end. + +-%% char(Char, Field, Adjust, Precision, PadChar) -> string(). ++%% char(Char, Field, Adjust, Precision, PadChar) -> chars(). + + char(C, none, _Adj, none, _Pad) -> [C]; + char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch new file mode 100644 index 0000000..e9e6450 --- /dev/null +++ b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch @@ -0,0 +1,74 @@ +From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= +Date: Tue, 25 Aug 2015 15:20:23 +0200 +Subject: [PATCH] Teach sasl_report to limit crash reports + + +diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl +index c3e6fed..aa84e4f 100644 +--- a/lib/sasl/src/sasl_report.erl ++++ b/lib/sasl/src/sasl_report.erl +@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) -> + Context = sup_get(errorContext, Report), + Reason = sup_get(reason, Report), + Offender = sup_get(offender, Report), +- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: " +- "~80.18p~n Offender: ~80.18p~n~n", +- write_report_action(IO, Fd, Head ++ FmtString, +- [Name,Context,Reason,Offender]); ++ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]), ++ write_report_action(IO, Fd, Head, FmtString, Args); + write_report2(IO, Fd, Head, progress, Report) -> + Format = format_key_val(Report), +- write_report_action(IO, Fd, Head ++ "~s", [Format]); ++ write_report_action(IO, Fd, Head, "~s", [Format]); + write_report2(IO, Fd, Head, crash_report, Report) -> +- Format = proc_lib:format(Report), +- write_report_action(IO, Fd, Head ++ "~s", [Format]). ++ Depth = get_depth(), ++ Format = proc_lib:format(Report, latin1, Depth), ++ write_report_action(IO, Fd, Head, "~s", [Format]). ++ ++supervisor_format(Args0) -> ++ case get_depth() of ++ unlimited -> ++ {" Supervisor: ~p~n" ++ " Context: ~p~n" ++ " Reason: ~80.18p~n" ++ " Offender: ~80.18p~n~n", ++ Args0}; ++ Depth -> ++ [A,B,C,D] = Args0, ++ Args = [A,Depth,B,Depth,C,Depth,D,Depth], ++ {" Supervisor: ~P~n" ++ " Context: ~P~n" ++ " Reason: ~80.18P~n" ++ " Offender: ~80.18P~n~n", ++ Args} ++ end. + +-write_report_action(io, Fd, Format, Args) -> +- io:format(Fd, Format, Args); +-write_report_action(io_lib, _Fd, Format, Args) -> +- io_lib:format(Format, Args). ++write_report_action(IO, Fd, Head, Format, Args) -> ++ S = [Head|io_lib:format(Format, Args)], ++ case IO of ++ io -> io:put_chars(Fd, S); ++ io_lib -> S ++ end. + + format_key_val([{Tag,Data}|Rep]) -> + io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep); + format_key_val(_) -> + []. + ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. + + sup_get(Tag, Report) -> + case lists:keysearch(Tag, 1, Report) of From db107ef77c6f466dd4154135b93a8c730733d941 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Fri, 19 Feb 2016 18:42:20 +0300 Subject: [PATCH 09/13] Add missing dependency Signed-off-by: Peter Lemenkov --- erlang.spec | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erlang.spec b/erlang.spec index d0d47ec..872dc21 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.14%{?dist} +Release: %{upstream_rel_for_rpm}.15%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -483,6 +483,8 @@ Low level interface to C. %package erts Summary: Functionality necessary to run the Erlang System itself Group: Development/Languages +# epmd user, epmd group +Requires(pre): shadow-utils Requires: %{name}-kernel%{?_isa} = %{version}-%{release} Requires: %{name}-stdlib%{?_isa} = %{version}-%{release} # This library is dlopened so it can't be picked up automatically by the RPM @@ -2352,6 +2354,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Fri Feb 19 2016 Peter Lemenkov - R16B-03.15 +- Add missing dependency + * Wed Jan 27 2016 Peter Lemenkov - R16B-03.14 - Enable error_logger depth fine tuning From 9f08c84c22b5fe96acf2d4163157e93b955ce401 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 28 Feb 2016 00:05:49 +0300 Subject: [PATCH 10/13] Fixed issue with nodes registration over IPv6 Signed-off-by: Peter Lemenkov --- erlang.spec | 5 +- ...-epmd-support-IPv6-node-registration.patch | 205 +++++++++++------- 2 files changed, 127 insertions(+), 83 deletions(-) diff --git a/erlang.spec b/erlang.spec index 872dc21..af332c7 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.15%{?dist} +Release: %{upstream_rel_for_rpm}.16%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -2354,6 +2354,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Sun Feb 28 2016 Peter Lemenkov - R16B-03.16 +- Fixed issue with nodes registration over IPv6 + * Fri Feb 19 2016 Peter Lemenkov - R16B-03.15 - Add missing dependency diff --git a/otp-0023-epmd-support-IPv6-node-registration.patch b/otp-0023-epmd-support-IPv6-node-registration.patch index da81020..7d46bae 100644 --- a/otp-0023-epmd-support-IPv6-node-registration.patch +++ b/otp-0023-epmd-support-IPv6-node-registration.patch @@ -17,30 +17,22 @@ epmd ignores errors opening the socket if the protocol is not supported. Similarly, the epmd client will fall back to IPv4 if the IPv6 socket is not available. -The interaction between IPv4 and IPv6 sockets depends on the platform: - -* FreeBSD allows multiple "specific" sockets to bind the same port (such - as 2 sockets listening to the same port on ANY and the loopback). - Binding port 4369 to IPv4 and IPv6 sockets simulataneously is allowed. - -* Linux does not allow the same port to be bound by different sockets. - Setting the IPV6_V6ONLY socket option is required. - -* Windows - - The behaviour differs depending on the version of Windows: - - http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx - - According to the site, sockets on Windows XP with Service Pack 1 (SP1) - and Windows Server 2003 will only listen on either IPv4 or IPv6, so - creating two sockets is required to service IPv4 and IPv6 traffic on - the same port. The IPV6_V6ONLY socket option is not supported. - - For Windows Vista and later, a single socket can handle IPv4 and IPv6 - traffic for the same port. The IPV6_V6ONLY socket option is supported - and is enabled for IPv6 sockets by default. +Update the minimum supported version of Windows to Windows Vista to +support IPv6. +diff --git a/erts/configure.in b/erts/configure.in +index d9bc1ec..756f3cb 100644 +--- a/erts/configure.in ++++ b/erts/configure.in +@@ -424,7 +424,7 @@ case $host_os in + win32) + # The ethread library requires _WIN32_WINNT of at least 0x0403. + # -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS. +- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501" ++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600" + ;; + darwin*) + CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE" diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 3e70054..3c9313e 100644 --- a/erts/doc/src/epmd.xml @@ -55,13 +47,18 @@ index 3e70054..3c9313e 100644

Starts the port mapper daemon

diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml -index 528a2d9..dada3a1 100644 +index 528a2d9..0b8f821 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml -@@ -381,6 +381,28 @@ +@@ -381,6 +381,33 @@ similar to . See code(3).

++ ++ ++

Replaces the path specified in the boot script. See ++ script(4).

++
+ + +

Specify a protocol for Erlang distribution.

@@ -70,9 +67,9 @@ index 528a2d9..dada3a1 100644 + +

TCP over IPv4 (the default)

+
-+ inet_ssl ++ inet_tls + -+

distribution over SSL

++

distribution over TLS/SSL

+
+ inet6_tcp + @@ -101,7 +98,7 @@ index 1757fa9..ebae0a5 100644 /* These chouldn't be needed but for safety... */ diff --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c -index 8817bde..c0b3729 100644 +index 8817bde..ea7dac7 100644 --- a/erts/epmd/src/epmd_cli.c +++ b/erts/epmd/src/epmd_cli.c @@ -135,19 +135,33 @@ void epmd_call(EpmdVars *g,int what) @@ -116,7 +113,7 @@ index 8817bde..c0b3729 100644 - goto error; + unsigned short sport = g->port; + -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + SET_ADDR6(address, in6addr_loopback, sport); + salen = sizeof(struct sockaddr_in6); + @@ -147,17 +144,10 @@ index 8817bde..c0b3729 100644 error: diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h -index 363923e..e028449 100644 +index 363923e..c6d9173 100644 --- a/erts/epmd/src/epmd_int.h +++ b/erts/epmd/src/epmd_int.h -@@ -44,9 +44,14 @@ - #include - - #ifdef __WIN32__ -+# ifdef _WIN32_WINVER -+# undef _WIN32_WINVER -+# endif -+# define _WIN32_WINVER 0x0501 +@@ -47,6 +47,7 @@ # ifndef WINDOWS_H_INCLUDES_WINSOCK2_H # include # endif @@ -165,6 +155,17 @@ index 363923e..e028449 100644 # include # include #endif +@@ -114,6 +115,10 @@ + # include + #endif + ++#if defined(HAVE_IN6) && defined(AF_INET6) && defined(HAVE_INET_PTON) ++# define EPMD6 ++#endif ++ + /* ************************************************************************ */ + /* Replace some functions by others by making the function name a macro */ + @@ -167,33 +172,53 @@ /* ************************************************************************ */ /* Macros that let us use IPv6 */ @@ -193,7 +194,7 @@ index 363923e..e028449 100644 + +#define IS_ADDR_LOOPBACK(addr) ((addr).s_addr == htonl(INADDR_LOOPBACK)) + -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) -#define EPMD_SOCKADDR_IN sockaddr_in6 -#define EPMD_IN_ADDR in6_addr @@ -247,7 +248,7 @@ index 363923e..e028449 100644 /* ************************************************************************ */ diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c -index 78524a6..7b4d774 100644 +index 78524a6..28e30d4 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -70,6 +70,7 @@ static time_t current_time(EpmdVars*); @@ -284,7 +285,7 @@ index 78524a6..7b4d774 100644 + /* Always listen on the loopback. */ + SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport); + num_sockets++; -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + SET_ADDR6(iserv_addr[num_sockets],in6addr_loopback,sport); + num_sockets++; +#endif @@ -304,9 +305,10 @@ index 78524a6..7b4d774 100644 + token = strtok(NULL,", ")) { - struct EPMD_IN_ADDR addr; -+ struct in_addr addr; - #ifdef HAVE_INET_PTON +-#ifdef HAVE_INET_PTON - int ret; ++ struct in_addr addr; ++#if defined(EPMD6) + struct in6_addr addr6; + struct sockaddr_storage *sa = &iserv_addr[num_sockets]; @@ -337,7 +339,7 @@ index 78524a6..7b4d774 100644 epmd_cleanup_exit(g,1); } -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + if (sa->ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&addr6)) + continue; + @@ -346,10 +348,10 @@ index 78524a6..7b4d774 100644 if (IS_ADDR_LOOPBACK(addr)) - loopback_ok = 1; + continue; -+ -+ num_sockets++; - if (num_sockets - loopback_ok == MAX_LISTEN_SOCKETS - 1) ++ num_sockets++; ++ + if (num_sockets >= MAX_LISTEN_SOCKETS) { dbg_tty_printf(g,0,"cannot listen on more than %d IP addresses", @@ -374,7 +376,7 @@ index 78524a6..7b4d774 100644 - num_sockets = 1; + SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_ANY),sport); + num_sockets++; -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport); + num_sockets++; +#endif @@ -387,7 +389,7 @@ index 78524a6..7b4d774 100644 { - if ((listensock[i] = socket(FAMILY,SOCK_STREAM,0)) < 0) + struct sockaddr *sa = (struct sockaddr *)&iserv_addr[i]; -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + size_t salen = (sa->sa_family == AF_INET6 ? + sizeof(struct sockaddr_in6) : + sizeof(struct sockaddr_in)); @@ -398,7 +400,6 @@ index 78524a6..7b4d774 100644 + if ((listensock[i] = socket(sa->sa_family,SOCK_STREAM,0)) < 0) { - dbg_perror(g,"error opening stream socket"); -- epmd_cleanup_exit(g,1); + switch (errno) { + case EAFNOSUPPORT: + case EPROTONOSUPPORT: @@ -407,9 +408,8 @@ index 78524a6..7b4d774 100644 + dbg_perror(g,"error opening stream socket"); + epmd_cleanup_exit(g,1); + } - } - g->listenfd[i] = listensock[i]; -- ++ } ++ g->listenfd[bound++] = listensock[i]; + +#if HAVE_DECL_IPV6_V6ONLY + opt = 1; @@ -418,8 +418,10 @@ index 78524a6..7b4d774 100644 + sizeof(opt)) <0) + { + dbg_perror(g,"can't set IPv6 only socket option"); -+ epmd_cleanup_exit(g,1); -+ } + epmd_cleanup_exit(g,1); + } +- g->listenfd[i] = listensock[i]; +- +#endif + /* @@ -435,28 +437,30 @@ index 78524a6..7b4d774 100644 { if (errno == EADDRINUSE) { -@@ -388,12 +433,20 @@ void run(EpmdVars *g) - } - } - -+ bound++; -+ - if(listen(listensock[i], SOMAXCONN) < 0) { - dbg_perror(g,"failed to listen on socket"); - epmd_cleanup_exit(g,1); +@@ -394,6 +439,11 @@ void run(EpmdVars *g) } select_fd_set(g, listensock[i]); } -+ + if (bound == 0) { + dbg_perror(g,"unable to bind any address"); + epmd_cleanup_exit(g,1); + } -+ ++ num_sockets = bound; #ifdef HAVE_SYSTEMD_SD_DAEMON_H } sd_notifyf(0, "READY=1\n" -@@ -1001,15 +1054,6 @@ static int conn_open(EpmdVars *g,int fd) +@@ -438,8 +488,8 @@ void run(EpmdVars *g) + } + + for (i = 0; i < num_sockets; i++) +- if (FD_ISSET(listensock[i],&read_mask)) { +- if (do_accept(g, listensock[i]) && g->active_conn < g->max_conn) { ++ if (FD_ISSET(g->listenfd[i],&read_mask)) { ++ if (do_accept(g, g->listenfd[i]) && g->active_conn < g->max_conn) { + /* + * The accept() succeeded, and we have at least one file + * descriptor still free, which means that another accept() +@@ -1001,15 +1051,6 @@ static int conn_open(EpmdVars *g,int fd) for (i = 0; i < g->max_conn; i++) { if (g->conn[i].open == EPMD_FALSE) { @@ -472,7 +476,7 @@ index 78524a6..7b4d774 100644 g->active_conn++; s = &g->conn[i]; -@@ -1020,20 +1064,7 @@ static int conn_open(EpmdVars *g,int fd) +@@ -1020,20 +1061,7 @@ static int conn_open(EpmdVars *g,int fd) s->open = EPMD_TRUE; s->keep = EPMD_FALSE; @@ -494,7 +498,7 @@ index 78524a6..7b4d774 100644 dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" : "Non-local peer connected"); -@@ -1041,7 +1072,7 @@ static int conn_open(EpmdVars *g,int fd) +@@ -1041,7 +1069,7 @@ static int conn_open(EpmdVars *g,int fd) s->got = 0; s->mod_time = current_time(g); /* Note activity */ @@ -503,7 +507,7 @@ index 78524a6..7b4d774 100644 if (s->buf == NULL) { dbg_printf(g,0,"epmd: Insufficient memory"); -@@ -1059,6 +1090,60 @@ static int conn_open(EpmdVars *g,int fd) +@@ -1059,6 +1087,60 @@ static int conn_open(EpmdVars *g,int fd) return EPMD_FALSE; } @@ -515,7 +519,7 @@ index 78524a6..7b4d774 100644 + struct sockaddr_in *si4 = (struct sockaddr_in *)&si; + struct sockaddr_in *di4 = (struct sockaddr_in *)&di; + -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&si; + struct sockaddr_in6 *di6 = (struct sockaddr_in6 *)&di; +#endif @@ -537,7 +541,7 @@ index 78524a6..7b4d774 100644 + + /* Only 127.x.x.x and connections from the host's IP address + allowed, no false positives */ -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + if (si.ss_family == AF_INET6 && IN6_IS_ADDR_LOOPBACK(&(si6->sin6_addr))) + return EPMD_TRUE; + @@ -550,13 +554,13 @@ index 78524a6..7b4d774 100644 + if (getsockname(fd,(struct sockaddr*) &di,&st)) + return EPMD_FALSE; + -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + if (si.ss_family == AF_INET6) + return IN6_ARE_ADDR_EQUAL( &(si6->sin6_addr), &(di6->sin6_addr)); + if (si.ss_family == AF_INET) +#endif + return si4->sin_addr.s_addr == di4->sin_addr.s_addr; -+#if defined(HAVE_IN6) && defined(AF_INET6) ++#if defined(EPMD6) + return EPMD_FALSE; +#endif +} @@ -565,7 +569,7 @@ index 78524a6..7b4d774 100644 { int i; diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl -index cc24a55..ddfe5ab 100644 +index cc24a55..8dfc21f 100644 --- a/erts/epmd/test/epmd_SUITE.erl +++ b/erts/epmd/test/epmd_SUITE.erl @@ -42,6 +42,7 @@ @@ -611,7 +615,7 @@ index cc24a55..ddfe5ab 100644 register_names_1(doc) -> ["Register and unregister two nodes"]; register_names_1(suite) -> -@@ -238,13 +258,18 @@ register_node(Name) -> +@@ -238,13 +258,14 @@ register_node(Name) -> register_node(Name,Port) -> register_node_v2(Port,$M,0,5,5,Name,""). @@ -619,19 +623,20 @@ index cc24a55..ddfe5ab 100644 + register_node_v2({0,0,0,0,0,0,0,1},?DUMMY_PORT,$M,0,5,5,Name,""). + register_node_v2(Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> +- Utf8Name = unicode:characters_to_binary(Name), +- Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot, +- put16(HVsn), put16(LVsn), +- put16(size(Utf8Name)), binary_to_list(Utf8Name), +- size16(Extra), Extra], +- case send_req(Req) of + register_node_v2("localhost", Port, NodeType, Prot, HVsn, LVsn, Name, Extra). +register_node_v2(Addr, Port, NodeType, Prot, HVsn, LVsn, Name, Extra) -> - Utf8Name = unicode:characters_to_binary(Name), - Req = [?EPMD_ALIVE2_REQ, put16(Port), NodeType, Prot, - put16(HVsn), put16(LVsn), - put16(size(Utf8Name)), binary_to_list(Utf8Name), - size16(Extra), Extra], -- case send_req(Req) of ++ Req = alive2_req(Port, NodeType, Prot, HVsn, LVsn, Name, Extra), + case send_req(Req, Addr) of {ok,Sock} -> case recv(Sock,4) of {ok, [?EPMD_ALIVE2_RESP,_Res=0,_C0,_C1]} -> -@@ -1129,7 +1154,9 @@ send_direct(Sock, Bytes) -> +@@ -1129,7 +1150,9 @@ send_direct(Sock, Bytes) -> end. send_req(Req) -> @@ -642,6 +647,19 @@ index cc24a55..ddfe5ab 100644 {ok,Sock} -> case send(Sock, [size16(Req), Req]) of ok -> +diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in +index d511f2e..99ee635 100644 +--- a/lib/erl_interface/configure.in ++++ b/lib/erl_interface/configure.in +@@ -250,7 +250,7 @@ case "$threads_disabled" in + ;; + win32_threads) + EI_THREADS="true" +- THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500" ++ THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600" + ;; + pthread) + EI_THREADS="true" diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 91af49f..21a3dec 100644 --- a/lib/kernel/src/erl_epmd.erl @@ -694,3 +712,26 @@ index 91af49f..21a3dec 100644 {ok, Socket} -> Name = to_string(NodeName), Extra = "", +diff --git a/lib/wx/configure.in b/lib/wx/configure.in +index 3756786..be73888 100755 +--- a/lib/wx/configure.in ++++ b/lib/wx/configure.in +@@ -163,14 +163,14 @@ case $host_os in + CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS" + ;; + mingw32) +- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE" +- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500" ++ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE" ++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600" + AC_MSG_WARN([Reverting to 32-bit time_t]) + CPPFLAGS="$CPPFLAGS -D_USE_32BIT_TIME_T" + ;; + win32) +- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE" +- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500" ++ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE" ++ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600" + ;; + *) + CFLAGS="$CFLAGS -Wno-deprecated-declarations" From 6515854c294bc6be60987407a54d9680fd8faf65 Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Thu, 7 Apr 2016 11:17:41 -0400 Subject: [PATCH 11/13] Revert "Enable error_logger depth fine tuning" (rhbz#1324922) This reverts commit 77046292336ce368b7f3310fdced075118075f61. --- erlang.spec | 13 +- ...unused-code-in-error-logger-handlers.patch | 1028 ----------------- ...h-sasl_report-to-limit-crash-reports.patch | 74 -- 3 files changed, 4 insertions(+), 1111 deletions(-) delete mode 100644 otp-0024-Remove-unused-code-in-error-logger-handlers.patch delete mode 100644 otp-0025-Teach-sasl_report-to-limit-crash-reports.patch diff --git a/erlang.spec b/erlang.spec index af332c7..83d9e27 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.16%{?dist} +Release: %{upstream_rel_for_rpm}.17%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -125,12 +125,6 @@ Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch # Fedora specific patch # epmd: support IPv6 node registration Patch23: otp-0023-epmd-support-IPv6-node-registration.patch -# Fedora specific patch -# Remove unused code in error logger handlers -Patch24: otp-0024-Remove-unused-code-in-error-logger-handlers.patch -# Fedora specific patch -# Teach sasl_report to limit crash reports -Patch25: otp-0025-Teach-sasl_report-to-limit-crash-reports.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -1021,8 +1015,6 @@ Erlang mode for XEmacs (source lisp files). %patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution %patch22 -p1 -b .kernel_inet6_tcp_dist_reuse_inet_tcp_dist_code %patch23 -p1 -b .epmd_support_IPv6_node_registration -%patch24 -p1 -b .Remove_unused_code_in_error_logger_handlers -%patch25 -p1 -b .Teach_sasl_report_to_limit_crash_reports # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2354,6 +2346,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Thu Apr 7 2016 John Eckersberg - R16B-03.17 +- Revert "Enable error_logger depth fine tuning" (rhbz#1324922) + * Sun Feb 28 2016 Peter Lemenkov - R16B-03.16 - Fixed issue with nodes registration over IPv6 diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch deleted file mode 100644 index a3ea0ac..0000000 --- a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch +++ /dev/null @@ -1,1028 +0,0 @@ -From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= -Date: Thu, 20 Aug 2015 09:44:43 +0200 -Subject: [PATCH] Remove unused code in error logger handlers - -A long time ago, errors from the emulator itself was sent as -messages that would end up in the handle_info/2 function. -Those clauses in handle_info/2 can be removed. - -The code for handling events tagged 'info' instead of 'info_msg' -can also be taken out. - -error_logger_file_h: Refactor and modernize code - -Refactor, simplify, and modernize the code to facilitate future -improvements in the following commits. - -Teach error_logger_file_h to truncate big messages - -Add the possibility to truncate big messages to avoid running out -of memory. - -error_logger_tty_h: Refactor and modernize code - -Refactor, simplify, and modernize the code to facilitate future -improvements in the following commits. - -Teach error_logger_tty_h to truncate big messages - -Add the possibility to truncate big messages to avoid running out -of memory. - -stdlib: Fix leaking files after error_logger:logfile(close) - -Introduced when changing state from tuple to record. - -Make the scanned form of the io_lib format strings available for processing - -This adds three new functions to io_lib - scan_format/2, unscan_format/1, -and build_text/1 - which expose the parsed form of the format control -sequences to make it possible to easily modify or filter the input to -io_lib:format/2. This can e.g. be used in order to replace unbounded-size -control sequences like ~w or ~p with corresponding depth-limited ~W and ~P -before doing the actual formatting. - -diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml -index 90f24c4..6613dcd 100644 ---- a/lib/stdlib/doc/src/io.xml -+++ b/lib/stdlib/doc/src/io.xml -@@ -505,7 +505,8 @@ ok -

Writes the data with standard syntax in the same way as - ~w, but breaks terms whose printed representation - is longer than one line into many lines and indents each -- line sensibly. It also tries to detect lists of -+ line sensibly. Left justification is not supported. -+ It also tries to detect lists of - printable characters and to output these as strings. The - Unicode translation modifier is used for determining - what characters are printable. For example:

-diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml -index 68352ff..0c1c84d 100644 ---- a/lib/stdlib/doc/src/io_lib.xml -+++ b/lib/stdlib/doc/src/io_lib.xml -@@ -4,7 +4,7 @@ - -
- -- 19962013 -+ 19962014 - Ericsson AB. All Rights Reserved. - - -@@ -59,6 +59,35 @@ - - - -+ -+ -+

Description:

-+ -+

control_char is the type of control -+ sequence: $P, $w, and so on;

-+
-+

args is a list of the arguments used by the -+ control sequence, or an empty list if the control sequence -+ does not take any arguments;

-+
-+

width is the field width;

-+
-+

adjust is the adjustment;

-+
-+

precision is the precision of the printed -+ argument;

-+
-+

pad_char is the padding character;

-+
-+

encoding is set to true if the translation -+ modifier t is present;

-+
-+

strings is set to false if the modifier -+ l is present.

-+
-+
-+
-+
- - - -@@ -260,6 +289,45 @@ - - - -+ -+ Parse all control sequences in the format string -+ -+

Returns a list corresponding to the given format string, -+ where control sequences have been replaced with -+ corresponding tuples. This list can be passed to io_lib:build_text/1 to have -+ the same effect as io_lib:format(Format, Args), or to -+ io_lib:unscan_format/1 -+ in order to get the corresponding pair of Format and -+ Args (with every * and corresponding argument -+ expanded to numeric values).

-+

A typical use of this function is to replace unbounded-size -+ control sequences like ~w and ~p with the -+ depth-limited variants ~W and ~P before -+ formatting to text, e.g. in a logger.

-+
-+
-+ -+ -+ Revert a pre-parsed format list to a plain character list -+ and a list of arguments -+ -+

See io_lib:scan_format/2 for -+ details.

-+
-+
-+ -+ -+ Build the output text for a pre-parsed format list -+ -+

See io_lib:scan_format/2 for -+ details.

-+
-+
-+ - - Indentation after printing string - -diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl -index e9364ed..ca44d01 100644 ---- a/lib/stdlib/src/error_logger_file_h.erl -+++ b/lib/stdlib/src/error_logger_file_h.erl -@@ -23,24 +23,28 @@ - - %%% - %%% A handler that can be connected to the error_logger --%%% event handler. --%%% Writes all events formatted to file. --%%% Handles events tagged error, emulator and info. -+%%% event handler. Writes all events formatted to file. - %%% - %%% It can only be started from error_logger:swap_handler({logfile, File}) --%%% or error_logger:logfile(File) -+%%% or error_logger:logfile(File). - %%% - - -export([init/1, - handle_event/2, handle_call/2, handle_info/2, - terminate/2, code_change/3]). - -+-record(st, -+ {fd, -+ filename, -+ prev_handler, -+ depth=unlimited :: 'unlimited' | non_neg_integer()}). -+ - %% This one is used when we takeover from the simple error_logger. - init({File, {error_logger, Buf}}) -> - case init(File, error_logger) of -- {ok, {Fd, File, PrevHandler}} -> -- write_events(Fd, Buf), -- {ok, {Fd, File, PrevHandler}}; -+ {ok, State} -> -+ write_events(State, Buf), -+ {ok, State}; - Error -> - Error - end; -@@ -52,49 +56,45 @@ init(File, PrevHandler) -> - process_flag(trap_exit, true), - case file:open(File, [write]) of - {ok,Fd} -> -- {ok, {Fd, File, PrevHandler}}; -+ Depth = get_depth(), -+ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler, -+ depth=Depth}, -+ {ok, State}; - Error -> - Error - end. -- -+ -+get_depth() -> -+ case application:get_env(kernel, error_logger_format_depth) of -+ {ok, Depth} when is_integer(Depth) -> -+ max(10, Depth); -+ undefined -> -+ unlimited -+ end. -+ - handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> - {ok, State}; --handle_event(Event, {Fd, File, PrevHandler}) -> -- write_event(Fd, tag_event(Event)), -- {ok, {Fd, File, PrevHandler}}; --handle_event(_, State) -> -+handle_event(Event, State) -> -+ write_event(State, Event), - {ok, State}. - --handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) -> -+handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) -> - case PrevHandler of - [] -> - remove_handler; - _ -> - {swap_handler, install_prev, [], PrevHandler, go_back} - end; --handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler}) -- when node(GL) == node() -> -- write_event(Fd, tag_event({emulator, GL, Chars})), -- {ok, {Fd, File, PrevHandler}}; --handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) -> -- write_event(Fd, tag_event({emulator, noproc, Chars})), -- {ok, {Fd, File, PrevHandler}}; - handle_info(_, State) -> - {ok, State}. - --handle_call(filename, {Fd, File, Prev}) -> -- {ok, File, {Fd, File, Prev}}; -+handle_call(filename, #st{filename=File}=State) -> -+ {ok, File, State}; - handle_call(_Query, State) -> - {ok, {error, bad_query}, State}. - --terminate(_Reason, State) -> -- case State of -- {Fd, _File, _Prev} -> -- ok = file:close(Fd); -- _ -> -- ok -- end, -- []. -+terminate(_Reason, #st{fd=Fd}) -> -+ file:close(Fd). - - code_change(_OldVsn, State, _Extra) -> - {ok, State}. -@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) -> - %%% Misc. functions. - %%% ------------------------------------------------------ - --tag_event(Event) -> -- {erlang:universaltime(), Event}. -+write_events(State, [Ev|Es]) -> -+ %% Write the events in reversed order. -+ write_events(State, Es), -+ write_event(State, Ev); -+write_events(_State, []) -> -+ ok. - --write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)). -+write_event(#st{fd=Fd}=State, Event) -> -+ case parse_event(Event) of -+ ignore -> -+ ok; -+ {Head,Pid,FormatList} -> -+ Time = maybe_utc(erlang:universaltime()), -+ Header = write_time(Time, Head), -+ Body = format_body(State, FormatList), -+ AtNode = if -+ node(Pid) =/= node() -> -+ ["** at node ",atom_to_list(node(Pid))," **\n"]; -+ true -> -+ [] -+ end, -+ io:put_chars(Fd, [Header,Body,AtNode]) -+ end. - --write_events1(Fd, [Event|Es]) -> -- write_event(Fd, Event), -- write_events1(Fd, Es); --write_events1(_, []) -> -- ok. -+format_body(State, [{Format,Args}|T]) -> -+ S = try format(State, Format, Args) of -+ S0 -> -+ S0 -+ catch -+ _:_ -> -+ format(State, "ERROR: ~p - ~p\n", [Format,Args]) -+ end, -+ [S|format_body(State, T)]; -+format_body(_State, []) -> -+ []. - --write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> -- T = write_time(maybe_utc(Time)), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- io:format(Fd, T ++ S, []); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- io:format(Fd, T ++ F, [Format,Args]) -- end; --write_event(Fd, {Time, {emulator, _GL, Chars}}) -> -- T = write_time(maybe_utc(Time)), -- case catch io_lib:format(Chars, []) of -- S when is_list(S) -> -- io:format(Fd, T ++ S, []); -- _ -> -- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars]) -- end; --write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> -- T = write_time(maybe_utc(Time)), -- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]); --write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> -- T = write_time(maybe_utc(Time)), -- S = format_report(Rep), -- io:format(Fd, T ++ S ++ add_node("", Pid), []); --write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> -- T = write_time(maybe_utc(Time), "INFO REPORT"), -- S = format_report(Rep), -- io:format(Fd, T ++ S ++ add_node("", Pid), []); --write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> -- T = write_time(maybe_utc(Time), "INFO REPORT"), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- io:format(Fd, T ++ S, []); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- io:format(Fd, T ++ F, [Format,Args]) -- end; --write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) -> -- T = write_time(maybe_utc(Time), "WARNING REPORT"), -- S = format_report(Rep), -- io:format(Fd, T ++ S ++ add_node("", Pid), []); --write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) -> -- T = write_time(maybe_utc(Time), "WARNING REPORT"), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- io:format(Fd, T ++ S, []); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- io:format(Fd, T ++ F, [Format,Args]) -- end; --write_event(_, _) -> -- ok. -+format(#st{depth=unlimited}, Format, Args) -> -+ io_lib:format(Format, Args); -+format(#st{depth=Depth}, Format0, Args) -> -+ Format1 = io_lib:scan_format(Format0, Args), -+ Format = limit_format(Format1, Depth), -+ io_lib:build_text(Format). -+ -+limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; -+ C0 =:= $w -> -+ C = C0 - ($a - $A), %To uppercase. -+ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)]; -+limit_format([H|T], Depth) -> -+ [H|limit_format(T, Depth)]; -+limit_format([], _) -> -+ []. -+ -+parse_event({error, _GL, {Pid, Format, Args}}) -> -+ {"ERROR REPORT",Pid,[{Format,Args}]}; -+parse_event({info_msg, _GL, {Pid, Format, Args}}) -> -+ {"INFO REPORT",Pid,[{Format, Args}]}; -+parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> -+ {"WARNING REPORT",Pid,[{Format,Args}]}; -+parse_event({error_report, _GL, {Pid, std_error, Args}}) -> -+ {"ERROR REPORT",Pid,format_term(Args)}; -+parse_event({info_report, _GL, {Pid, std_info, Args}}) -> -+ {"INFO REPORT",Pid,format_term(Args)}; -+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> -+ {"WARNING REPORT",Pid,format_term(Args)}; -+parse_event(_) -> ignore. - - maybe_utc(Time) -> - UTC = case application:get_env(sasl, utc_log) of -@@ -182,30 +184,27 @@ maybe_utc(Time) -> - maybe_utc(Time, true) -> {utc, Time}; - maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. - --format_report(Rep) when is_list(Rep) -> -- case string_p(Rep) of -+format_term(Term) when is_list(Term) -> -+ case string_p(Term) of - true -> -- io_lib:format("~s~n",[Rep]); -- _ -> -- format_rep(Rep) -+ [{"~s\n",[Term]}]; -+ false -> -+ format_term_list(Term) - end; --format_report(Rep) -> -- io_lib:format("~p~n",[Rep]). -- --format_rep([{Tag,Data}|Rep]) -> -- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); --format_rep([Other|Rep]) -> -- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); --format_rep(_) -> -+format_term(Term) -> -+ [{"~p\n",[Term]}]. -+ -+format_term_list([{Tag,Data}|T]) -> -+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; -+format_term_list([Data|T]) -> -+ [{" ~p\n",[Data]}|format_term_list(T)]; -+format_term_list([]) -> -+ []; -+format_term_list(_) -> -+ %% Continue to allow non-proper lists for now. -+ %% FIXME: Remove this clause in OTP 19. - []. - --add_node(X, Pid) when is_atom(X) -> -- add_node(atom_to_list(X), Pid); --add_node(X, Pid) when node(Pid) =/= node() -> -- lists:concat([X,"** at node ",node(Pid)," **~n"]); --add_node(X, _) -> -- X. -- - string_p([]) -> - false; - string_p(Term) -> -@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T); - string_p1([$\f|T]) -> string_p1(T); - string_p1([$\e|T]) -> string_p1(T); - string_p1([H|T]) when is_list(H) -> -- case string_p1(H) of -- true -> string_p1(T); -- _ -> false -- end; -+ string_p1(H) andalso string_p1(T); - string_p1([]) -> true; - string_p1(_) -> false. - --write_time(Time) -> write_time(Time, "ERROR REPORT"). -- - write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) -> - io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", - [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); -diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl -index ad5891f..72a052f 100644 ---- a/lib/stdlib/src/error_logger_tty_h.erl -+++ b/lib/stdlib/src/error_logger_tty_h.erl -@@ -22,144 +22,178 @@ - - %%% - %%% A handler that can be connected to the error_logger --%%% event handler. --%%% Writes all events formatted to stdout. --%%% Handles events tagged error, emulator and info. -+%%% event handler. Writes all events formatted to stdout. - %%% - %%% It can only be started from error_logger:swap_handler(tty) --%%% or error_logger:tty(true) -+%%% or error_logger:tty(true). - %%% - - -export([init/1, - handle_event/2, handle_call/2, handle_info/2, - terminate/2, code_change/3]). - ---export([write_event/2]). -+-export([write_event/2,write_event/3]). -+ -+-record(st, -+ {user, -+ prev_handler, -+ io_mod=io, -+ depth=unlimited}). - - %% This one is used when we takeover from the simple error_logger. - init({[], {error_logger, Buf}}) -> - User = set_group_leader(), -- write_events(Buf,io), -- {ok, {User, error_logger}}; -+ Depth = get_depth(), -+ State = #st{user=User,prev_handler=error_logger,depth=Depth}, -+ write_events(State, Buf), -+ {ok, State}; - %% This one is used if someone took over from us, and now wants to - %% go back. - init({[], {error_logger_tty_h, PrevHandler}}) -> - User = set_group_leader(), -- {ok, {User, PrevHandler}}; -+ {ok, #st{user=User,prev_handler=PrevHandler}}; - %% This one is used when we are started directly. - init([]) -> - User = set_group_leader(), -- {ok, {User, []}}. -+ Depth = get_depth(), -+ {ok, #st{user=User,prev_handler=[],depth=Depth}}. -+ -+get_depth() -> -+ case application:get_env(kernel, error_logger_format_depth) of -+ {ok, Depth} when is_integer(Depth) -> -+ max(10, Depth); -+ undefined -> -+ unlimited -+ end. - - handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> - {ok, State}; - handle_event(Event, State) -> -- write_event(tag_event(Event),io), -+ ok = do_write_event(State, tag_event(Event)), - {ok, State}. - --handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> -+handle_info({'EXIT', User, _Reason}, -+ #st{user=User,prev_handler=PrevHandler}=State) -> - case PrevHandler of - [] -> - remove_handler; - _ -> -- {swap_handler, install_prev, {User, PrevHandler}, -+ {swap_handler, install_prev, State, - PrevHandler, go_back} - end; --handle_info({emulator, GL, Chars}, State) when node(GL) == node() -> -- write_event(tag_event({emulator, GL, Chars}),io), -- {ok, State}; --handle_info({emulator, noproc, Chars}, State) -> -- write_event(tag_event({emulator, noproc, Chars}),io), -- {ok, State}; - handle_info(_, State) -> - {ok, State}. - - handle_call(_Query, State) -> {ok, {error, bad_query}, State}. - --% unfortunately, we can't unlink from User - links are not counted! --% if pid(User) -> unlink(User); true -> ok end, - terminate(install_prev, _State) -> - []; --terminate(_Reason, {_User, PrevHandler}) -> -+terminate(_Reason, #st{prev_handler=PrevHandler}) -> - {error_logger_tty_h, PrevHandler}. - - code_change(_OldVsn, State, _Extra) -> - {ok, State}. - -+%% Exported (but unoffical) API. -+write_event(Event, IoMod) -> -+ do_write_event(#st{io_mod=IoMod}, Event). -+ -+write_event(Event, IoMod, Depth) -> -+ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event). -+ -+ - %%% ------------------------------------------------------ - %%% Misc. functions. - %%% ------------------------------------------------------ - - set_group_leader() -> - case whereis(user) of -- User when is_pid(User) -> link(User), group_leader(User,self()), User; -- _ -> false -+ User when is_pid(User) -> -+ link(User), -+ group_leader(User,self()), -+ User; -+ _ -> -+ false - end. - - tag_event(Event) -> - {erlang:universaltime(), Event}. - --write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod). -- --write_events1([Event|Es],IOMod) -> -- write_event(Event,IOMod), -- write_events1(Es,IOMod); --write_events1([],_IOMod) -> -+write_events(State, [Ev|Es]) -> -+ %% Write the events in reverse order. -+ _ = write_events(State, Es), -+ _ = do_write_event(State, Ev), -+ ok; -+write_events(_State, []) -> - ok. - --write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) -> -- T = write_time(maybe_utc(Time)), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- format(IOMod, T ++ S); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- format(IOMod, T ++ F, [Format,Args]) -+do_write_event(State, {Time0, Event}) -> -+ case parse_event(Event) of -+ ignore -> -+ ok; -+ {Head,Pid,FormatList} -> -+ Time = maybe_utc(Time0), -+ Header = write_time(Time, Head), -+ Body = format_body(State, FormatList), -+ AtNode = if -+ node(Pid) =/= node() -> -+ ["** at node ",atom_to_list(node(Pid))," **\n"]; -+ true -> -+ [] -+ end, -+ Str = [Header,Body,AtNode], -+ case State#st.io_mod of -+ io_lib -> -+ Str; -+ io -> -+ io:put_chars(user, Str) -+ end - end; --write_event({Time, {emulator, _GL, Chars}},IOMod) -> -- T = write_time(maybe_utc(Time)), -- case catch io_lib:format(Chars, []) of -- S when is_list(S) -> -- format(IOMod, T ++ S); -- _ -> -- format(IOMod, T ++ "ERROR: ~p ~n", [Chars]) -- end; --write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) -> -- T = write_time(maybe_utc(Time)), -- format(IOMod, T ++ add_node("~p~n",Pid),[Info]); --write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) -> -- T = write_time(maybe_utc(Time)), -- S = format_report(Rep), -- format(IOMod, T ++ S ++ add_node("", Pid)); --write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) -> -- T = write_time(maybe_utc(Time), "INFO REPORT"), -- S = format_report(Rep), -- format(IOMod, T ++ S ++ add_node("", Pid)); --write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) -> -- T = write_time(maybe_utc(Time), "INFO REPORT"), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- format(IOMod, T ++ S); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- format(IOMod, T ++ F, [Format,Args]) -- end; --write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) -> -- T = write_time(maybe_utc(Time), "WARNING REPORT"), -- S = format_report(Rep), -- format(IOMod, T ++ S ++ add_node("", Pid)); --write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) -> -- T = write_time(maybe_utc(Time), "WARNING REPORT"), -- case catch io_lib:format(add_node(Format,Pid), Args) of -- S when is_list(S) -> -- format(IOMod, T ++ S); -- _ -> -- F = add_node("ERROR: ~p - ~p~n", Pid), -- format(IOMod, T ++ F, [Format,Args]) -- end; --write_event({_Time, _Error},_IOMod) -> -+do_write_event(_, _) -> - ok. - -+format_body(State, [{Format,Args}|T]) -> -+ S = try format(State, Format, Args) of -+ S0 -> -+ S0 -+ catch -+ _:_ -> -+ format(State, "ERROR: ~p - ~p\n", [Format,Args]) -+ end, -+ [S|format_body(State, T)]; -+format_body(_State, []) -> -+ []. -+ -+format(#st{depth=unlimited}, Format, Args) -> -+ io_lib:format(Format, Args); -+format(#st{depth=Depth}, Format0, Args) -> -+ Format1 = io_lib:scan_format(Format0, Args), -+ Format = limit_format(Format1, Depth), -+ io_lib:build_text(Format). -+ -+limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; -+ C0 =:= $w -> -+ C = C0 - ($a - $A), %To uppercase. -+ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)]; -+limit_format([H|T], Depth) -> -+ [H|limit_format(T, Depth)]; -+limit_format([], _) -> -+ []. -+ -+parse_event({error, _GL, {Pid, Format, Args}}) -> -+ {"ERROR REPORT",Pid,[{Format,Args}]}; -+parse_event({info_msg, _GL, {Pid, Format, Args}}) -> -+ {"INFO REPORT",Pid,[{Format, Args}]}; -+parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> -+ {"WARNING REPORT",Pid,[{Format,Args}]}; -+parse_event({error_report, _GL, {Pid, std_error, Args}}) -> -+ {"ERROR REPORT",Pid,format_term(Args)}; -+parse_event({info_report, _GL, {Pid, std_info, Args}}) -> -+ {"INFO REPORT",Pid,format_term(Args)}; -+parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> -+ {"WARNING REPORT",Pid,format_term(Args)}; -+parse_event(_) -> ignore. -+ - maybe_utc(Time) -> - UTC = case application:get_env(sasl, utc_log) of - {ok, Val} -> Val; -@@ -175,33 +209,26 @@ maybe_utc(Time) -> - maybe_utc(Time, true) -> {utc, Time}; - maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. - --format(IOMod, String) -> format(IOMod, String, []). --format(io_lib, String, Args) -> io_lib:format(String, Args); --format(io, String, Args) -> io:format(user, String, Args). -- --format_report(Rep) when is_list(Rep) -> -- case string_p(Rep) of -+format_term(Term) when is_list(Term) -> -+ case string_p(Term) of - true -> -- io_lib:format("~s~n",[Rep]); -- _ -> -- format_rep(Rep) -+ [{"~s\n",[Term]}]; -+ false -> -+ format_term_list(Term) - end; --format_report(Rep) -> -- io_lib:format("~p~n",[Rep]). -- --format_rep([{Tag,Data}|Rep]) -> -- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); --format_rep([Other|Rep]) -> -- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); --format_rep(_) -> -- []. -+format_term(Term) -> -+ [{"~p\n",[Term]}]. - --add_node(X, Pid) when is_atom(X) -> -- add_node(atom_to_list(X), Pid); --add_node(X, Pid) when node(Pid) =/= node() -> -- lists:concat([X,"** at node ",node(Pid)," **~n"]); --add_node(X, _) -> -- X. -+format_term_list([{Tag,Data}|T]) -> -+ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; -+format_term_list([Data|T]) -> -+ [{" ~p\n",[Data]}|format_term_list(T)]; -+format_term_list([]) -> -+ []; -+format_term_list(_) -> -+ %% Continue to allow non-proper lists for now. -+ %% FIXME: Remove this clause in OTP 19. -+ []. - - string_p([]) -> - false; -@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) -> - string_p1([]) -> true; - string_p1(_) -> false. - --write_time(Time) -> write_time(Time, "ERROR REPORT"). - write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) -> - io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", - [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); -diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl -index 9e69601..3c173dc 100644 ---- a/lib/stdlib/src/io_lib.erl -+++ b/lib/stdlib/src/io_lib.erl -@@ -2,7 +2,7 @@ - %% - %% %CopyrightBegin% - %% --%% Copyright Ericsson AB 1996-2013. All Rights Reserved. -+%% Copyright Ericsson AB 1996-2014. 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 -@@ -61,6 +61,7 @@ - -module(io_lib). - - -export([fwrite/2,fread/2,fread/3,format/2]). -+-export([scan_format/2,unscan_format/1,build_text/1]). - -export([print/1,print/4,indentation/2]). - - -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). -@@ -84,7 +85,7 @@ - deep_unicode_char_list/1]). - - -export_type([chars/0, latin1_string/0, continuation/0, -- fread_error/0, fread_item/0]). -+ fread_error/0, fread_item/0, format_spec/0]). - - %%---------------------------------------------------------------------- - -@@ -109,6 +110,18 @@ - - -type fread_item() :: string() | atom() | integer() | float(). - -+-type format_spec() :: -+ { -+ ControlChar :: char(), -+ Args :: [any()], -+ Width :: 'none' | integer(), -+ Adjust :: 'left' | 'right', -+ Precision :: 'none' | integer(), -+ PadChar :: char(), -+ Encoding :: 'unicode' | 'latin1', -+ Strings :: boolean() -+ }. -+ - %%---------------------------------------------------------------------- - - %% Interface calls to sub-modules. -@@ -157,6 +170,31 @@ format(Format, Args) -> - Other - end. - -+-spec scan_format(Format, Data) -> FormatList when -+ Format :: io:format(), -+ Data :: [term()], -+ FormatList :: [char() | format_spec()]. -+ -+scan_format(Format, Args) -> -+ try io_lib_format:scan(Format, Args) -+ catch -+ _:_ -> erlang:error(badarg, [Format, Args]) -+ end. -+ -+-spec unscan_format(FormatList) -> {Format, Data} when -+ FormatList :: [char() | format_spec()], -+ Format :: io:format(), -+ Data :: [term()]. -+ -+unscan_format(FormatList) -> -+ io_lib_format:unscan(FormatList). -+ -+-spec build_text(FormatList) -> chars() when -+ FormatList :: [char() | format_spec()]. -+ -+build_text(FormatList) -> -+ io_lib_format:build(FormatList). -+ - -spec print(Term) -> chars() when - Term :: term(). - -diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl -index 56e15a1..37b47d7 100644 ---- a/lib/stdlib/src/io_lib_format.erl -+++ b/lib/stdlib/src/io_lib_format.erl -@@ -1,7 +1,7 @@ - %% - %% %CopyrightBegin% - %% --%% Copyright Ericsson AB 1996-2013. All Rights Reserved. -+%% Copyright Ericsson AB 1996-2014. 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 -@@ -20,10 +20,9 @@ - - %% Formatting functions of io library. - ---export([fwrite/2,fwrite_g/1,indentation/2]). -+-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]). - --%% fwrite(Format, ArgList) -> string(). --%% Format the arguments in ArgList after string Format. Just generate -+%% Format the arguments in Args after string Format. Just generate - %% an error if there is an error in the arguments. - %% - %% To do the printing command correctly we need to calculate the -@@ -37,15 +36,83 @@ - %% and it also splits the handling of the control characters into two - %% parts. - --fwrite(Format, Args) when is_atom(Format) -> -- fwrite(atom_to_list(Format), Args); --fwrite(Format, Args) when is_binary(Format) -> -- fwrite(binary_to_list(Format), Args); -+-spec fwrite(Format, Data) -> FormatList when -+ Format :: io:format(), -+ Data :: [term()], -+ FormatList :: [char() | io_lib:format_spec()]. -+ - fwrite(Format, Args) -> -- Cs = collect(Format, Args), -+ build(scan(Format, Args)). -+ -+%% Build the output text for a pre-parsed format list. -+ -+-spec build(FormatList) -> io_lib:chars() when -+ FormatList :: [char() | io_lib:format_spec()]. -+ -+build(Cs) -> - Pc = pcount(Cs), - build(Cs, Pc, 0). - -+%% Parse all control sequences in the format string. -+ -+-spec scan(Format, Data) -> FormatList when -+ Format :: io:format(), -+ Data :: [term()], -+ FormatList :: [char() | io_lib:format_spec()]. -+ -+scan(Format, Args) when is_atom(Format) -> -+ scan(atom_to_list(Format), Args); -+scan(Format, Args) when is_binary(Format) -> -+ scan(binary_to_list(Format), Args); -+scan(Format, Args) -> -+ collect(Format, Args). -+ -+%% Revert a pre-parsed format list to a plain character list and a -+%% list of arguments. -+ -+-spec unscan(FormatList) -> {Format, Data} when -+ FormatList :: [char() | io_lib:format_spec()], -+ Format :: io:format(), -+ Data :: [term()]. -+ -+unscan(Cs) -> -+ {print(Cs), args(Cs)}. -+ -+args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) -> -+ As ++ args(Cs); -+args([_C | Cs]) -> -+ args(Cs); -+args([]) -> -+ []. -+ -+print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) -> -+ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs); -+print([C | Cs]) -> -+ [C | print(Cs)]; -+print([]) -> -+ []. -+ -+print(C, F, Ad, P, Pad, Encoding, Strings) -> -+ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++ -+ print_pad_char(Pad) ++ print_encoding(Encoding) ++ -+ print_strings(Strings) ++ [C]. -+ -+print_field_width(none, _Ad) -> ""; -+print_field_width(F, left) -> integer_to_list(-F); -+print_field_width(F, right) -> integer_to_list(F). -+ -+print_precision(none) -> ""; -+print_precision(P) -> [$. | integer_to_list(P)]. -+ -+print_pad_char($\s) -> ""; % default, no need to make explicit -+print_pad_char(Pad) -> [$., Pad]. -+ -+print_encoding(unicode) -> "t"; -+print_encoding(latin1) -> "". -+ -+print_strings(false) -> "l"; -+print_strings(true) -> "". -+ - collect([$~|Fmt0], Args0) -> - {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0), - [C|collect(Fmt1, Args1)]; -@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); - pcount([_|Cs], Acc) -> pcount(Cs, Acc); - pcount([], Acc) -> Acc. - --%% build([Control], Pc, Indentation) -> string(). -+%% build([Control], Pc, Indentation) -> io_lib:chars(). - %% Interpret the control structures. Count the number of print - %% remaining and only calculate indentation when necessary. Must also - %% be smart when calculating indentation for characters in format. -@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1; - decr_pc($P, Pc) -> Pc - 1; - decr_pc(_, Pc) -> Pc. - --%% indentation(String, Indentation) -> Indentation. -+ - %% Calculate the indentation of the end of a string given its start - %% indentation. We assume tabs at 8 cols. - -+-spec indentation(String, StartIndent) -> integer() when -+ String :: io_lib:chars(), -+ StartIndent :: integer(). -+ - indentation([$\n|Cs], _I) -> indentation(Cs, 0); - indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8); - indentation([C|Cs], I) when is_integer(C) -> -@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 -> - float_data([_|Cs], Ds) -> - float_data(Cs, Ds). - --%% fwrite_g(Float) - %% Writes the shortest, correctly rounded string that converts - %% to Float when read back with list_to_float/1. - %% -@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) -> - %% in Proceedings of the SIGPLAN '96 Conference on Programming - %% Language Design and Implementation. - -+-spec fwrite_g(float()) -> string(). -+ - fwrite_g(0.0) -> - "0.0"; - fwrite_g(Float) when is_float(Float) -> -@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) - term([Prefix|S], F, Adj, none, Pad) - end. - --%% char(Char, Field, Adjust, Precision, PadChar) -> string(). -+%% char(Char, Field, Adjust, Precision, PadChar) -> chars(). - - char(C, none, _Adj, none, _Pad) -> [C]; - char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch deleted file mode 100644 index e9e6450..0000000 --- a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= -Date: Tue, 25 Aug 2015 15:20:23 +0200 -Subject: [PATCH] Teach sasl_report to limit crash reports - - -diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl -index c3e6fed..aa84e4f 100644 ---- a/lib/sasl/src/sasl_report.erl -+++ b/lib/sasl/src/sasl_report.erl -@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) -> - Context = sup_get(errorContext, Report), - Reason = sup_get(reason, Report), - Offender = sup_get(offender, Report), -- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: " -- "~80.18p~n Offender: ~80.18p~n~n", -- write_report_action(IO, Fd, Head ++ FmtString, -- [Name,Context,Reason,Offender]); -+ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]), -+ write_report_action(IO, Fd, Head, FmtString, Args); - write_report2(IO, Fd, Head, progress, Report) -> - Format = format_key_val(Report), -- write_report_action(IO, Fd, Head ++ "~s", [Format]); -+ write_report_action(IO, Fd, Head, "~s", [Format]); - write_report2(IO, Fd, Head, crash_report, Report) -> -- Format = proc_lib:format(Report), -- write_report_action(IO, Fd, Head ++ "~s", [Format]). -+ Depth = get_depth(), -+ Format = proc_lib:format(Report, latin1, Depth), -+ write_report_action(IO, Fd, Head, "~s", [Format]). -+ -+supervisor_format(Args0) -> -+ case get_depth() of -+ unlimited -> -+ {" Supervisor: ~p~n" -+ " Context: ~p~n" -+ " Reason: ~80.18p~n" -+ " Offender: ~80.18p~n~n", -+ Args0}; -+ Depth -> -+ [A,B,C,D] = Args0, -+ Args = [A,Depth,B,Depth,C,Depth,D,Depth], -+ {" Supervisor: ~P~n" -+ " Context: ~P~n" -+ " Reason: ~80.18P~n" -+ " Offender: ~80.18P~n~n", -+ Args} -+ end. - --write_report_action(io, Fd, Format, Args) -> -- io:format(Fd, Format, Args); --write_report_action(io_lib, _Fd, Format, Args) -> -- io_lib:format(Format, Args). -+write_report_action(IO, Fd, Head, Format, Args) -> -+ S = [Head|io_lib:format(Format, Args)], -+ case IO of -+ io -> io:put_chars(Fd, S); -+ io_lib -> S -+ end. - - format_key_val([{Tag,Data}|Rep]) -> - io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep); - format_key_val(_) -> - []. - -+get_depth() -> -+ case application:get_env(kernel, error_logger_format_depth) of -+ {ok, Depth} when is_integer(Depth) -> -+ max(10, Depth); -+ undefined -> -+ unlimited -+ end. - - sup_get(Tag, Report) -> - case lists:keysearch(Tag, 1, Report) of From 4627cfdff14a120761c13d63c39371c082aa71f0 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 9 Nov 2016 17:40:36 +0300 Subject: [PATCH 12/13] Backports for mnesia, and epmd in ipv6-only env - Respect -proto_dist switch while connecting to EPMD - Cherry-picked fix for OTP-13423. Mnesia transactions could hang while waiting on a response from a node who had stopped. Signed-off-by: Peter Lemenkov --- erlang.spec | 27 +- ...unused-code-in-error-logger-handlers.patch | 1028 ++++++++++++++++ ...h-sasl_report-to-limit-crash-reports.patch | 74 ++ ...unused-code-in-error-logger-handlers.patch | 1064 +++++++++++++++++ ...dist-switch-while-connection-to-EPMD.patch | 34 + ...ia_down-messages-to-waiting-transact.patch | 38 + 6 files changed, 2264 insertions(+), 1 deletion(-) create mode 100644 otp-0024-Remove-unused-code-in-error-logger-handlers.patch create mode 100644 otp-0025-Teach-sasl_report-to-limit-crash-reports.patch create mode 100644 otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch create mode 100644 otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch create mode 100644 otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch diff --git a/erlang.spec b/erlang.spec index 83d9e27..e117c20 100644 --- a/erlang.spec +++ b/erlang.spec @@ -25,7 +25,7 @@ Name: erlang Version: %{upstream_ver} -Release: %{upstream_rel_for_rpm}.17%{?dist} +Release: %{upstream_rel_for_rpm}.18%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -125,6 +125,21 @@ Patch22: otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch # Fedora specific patch # epmd: support IPv6 node registration Patch23: otp-0023-epmd-support-IPv6-node-registration.patch +# Fedora specific patch +# Remove unused code in error logger handlers +Patch24: otp-0024-Remove-unused-code-in-error-logger-handlers.patch +# Fedora specific patch +# Teach sasl_report to limit crash reports +Patch25: otp-0025-Teach-sasl_report-to-limit-crash-reports.patch +# Fedora specific patch +# Revert "Remove unused code in error logger handlers" +Patch26: otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch +# Fedora specific patch +# Respect -proto_dist switch while connection to EPMD +Patch27: otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch +# Fedora specific patch +# mnesia: Send mnesia_down messages to waiting transactions +Patch28: otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -1015,6 +1030,11 @@ Erlang mode for XEmacs (source lisp files). %patch21 -p1 -b .Add_patch_to_crash_dump_on_large_distribution %patch22 -p1 -b .kernel_inet6_tcp_dist_reuse_inet_tcp_dist_code %patch23 -p1 -b .epmd_support_IPv6_node_registration +%patch24 -p1 -b .Remove_unused_code_in_error_logger_handlers +%patch25 -p1 -b .Teach_sasl_report_to_limit_crash_reports +%patch26 -p1 -b .Revert_Remove_unused_code_in_error_logger_handlers +%patch27 -p1 -b .Respect_proto_dist_switch_while_connection_to_EPMD +%patch28 -p1 -b .mnesia_Send_mnesia_down_messages_to_waiting_transact # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2346,6 +2366,11 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Wed Nov 9 2016 Peter Lemenkov - R16B-03.18 +- Respect -proto_dist switch while connecting to EPMD +- Cherry-picked fix for OTP-13423. Mnesia transactions could hang while waiting + on a response from a node who had stopped. + * Thu Apr 7 2016 John Eckersberg - R16B-03.17 - Revert "Enable error_logger depth fine tuning" (rhbz#1324922) diff --git a/otp-0024-Remove-unused-code-in-error-logger-handlers.patch b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch new file mode 100644 index 0000000..a3ea0ac --- /dev/null +++ b/otp-0024-Remove-unused-code-in-error-logger-handlers.patch @@ -0,0 +1,1028 @@ +From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= +Date: Thu, 20 Aug 2015 09:44:43 +0200 +Subject: [PATCH] Remove unused code in error logger handlers + +A long time ago, errors from the emulator itself was sent as +messages that would end up in the handle_info/2 function. +Those clauses in handle_info/2 can be removed. + +The code for handling events tagged 'info' instead of 'info_msg' +can also be taken out. + +error_logger_file_h: Refactor and modernize code + +Refactor, simplify, and modernize the code to facilitate future +improvements in the following commits. + +Teach error_logger_file_h to truncate big messages + +Add the possibility to truncate big messages to avoid running out +of memory. + +error_logger_tty_h: Refactor and modernize code + +Refactor, simplify, and modernize the code to facilitate future +improvements in the following commits. + +Teach error_logger_tty_h to truncate big messages + +Add the possibility to truncate big messages to avoid running out +of memory. + +stdlib: Fix leaking files after error_logger:logfile(close) + +Introduced when changing state from tuple to record. + +Make the scanned form of the io_lib format strings available for processing + +This adds three new functions to io_lib - scan_format/2, unscan_format/1, +and build_text/1 - which expose the parsed form of the format control +sequences to make it possible to easily modify or filter the input to +io_lib:format/2. This can e.g. be used in order to replace unbounded-size +control sequences like ~w or ~p with corresponding depth-limited ~W and ~P +before doing the actual formatting. + +diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml +index 90f24c4..6613dcd 100644 +--- a/lib/stdlib/doc/src/io.xml ++++ b/lib/stdlib/doc/src/io.xml +@@ -505,7 +505,8 @@ ok +

Writes the data with standard syntax in the same way as + ~w, but breaks terms whose printed representation + is longer than one line into many lines and indents each +- line sensibly. It also tries to detect lists of ++ line sensibly. Left justification is not supported. ++ It also tries to detect lists of + printable characters and to output these as strings. The + Unicode translation modifier is used for determining + what characters are printable. For example:

+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml +index 68352ff..0c1c84d 100644 +--- a/lib/stdlib/doc/src/io_lib.xml ++++ b/lib/stdlib/doc/src/io_lib.xml +@@ -4,7 +4,7 @@ + +
+ +- 19962013 ++ 19962014 + Ericsson AB. All Rights Reserved. + + +@@ -59,6 +59,35 @@ + + + ++ ++ ++

Description:

++ ++

control_char is the type of control ++ sequence: $P, $w, and so on;

++
++

args is a list of the arguments used by the ++ control sequence, or an empty list if the control sequence ++ does not take any arguments;

++
++

width is the field width;

++
++

adjust is the adjustment;

++
++

precision is the precision of the printed ++ argument;

++
++

pad_char is the padding character;

++
++

encoding is set to true if the translation ++ modifier t is present;

++
++

strings is set to false if the modifier ++ l is present.

++
++
++
++
+ + + +@@ -260,6 +289,45 @@ + + + ++ ++ Parse all control sequences in the format string ++ ++

Returns a list corresponding to the given format string, ++ where control sequences have been replaced with ++ corresponding tuples. This list can be passed to io_lib:build_text/1 to have ++ the same effect as io_lib:format(Format, Args), or to ++ io_lib:unscan_format/1 ++ in order to get the corresponding pair of Format and ++ Args (with every * and corresponding argument ++ expanded to numeric values).

++

A typical use of this function is to replace unbounded-size ++ control sequences like ~w and ~p with the ++ depth-limited variants ~W and ~P before ++ formatting to text, e.g. in a logger.

++
++
++ ++ ++ Revert a pre-parsed format list to a plain character list ++ and a list of arguments ++ ++

See io_lib:scan_format/2 for ++ details.

++
++
++ ++ ++ Build the output text for a pre-parsed format list ++ ++

See io_lib:scan_format/2 for ++ details.

++
++
++ + + Indentation after printing string + +diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl +index e9364ed..ca44d01 100644 +--- a/lib/stdlib/src/error_logger_file_h.erl ++++ b/lib/stdlib/src/error_logger_file_h.erl +@@ -23,24 +23,28 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. +-%%% Writes all events formatted to file. +-%%% Handles events tagged error, emulator and info. ++%%% event handler. Writes all events formatted to file. + %%% + %%% It can only be started from error_logger:swap_handler({logfile, File}) +-%%% or error_logger:logfile(File) ++%%% or error_logger:logfile(File). + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + ++-record(st, ++ {fd, ++ filename, ++ prev_handler, ++ depth=unlimited :: 'unlimited' | non_neg_integer()}). ++ + %% This one is used when we takeover from the simple error_logger. + init({File, {error_logger, Buf}}) -> + case init(File, error_logger) of +- {ok, {Fd, File, PrevHandler}} -> +- write_events(Fd, Buf), +- {ok, {Fd, File, PrevHandler}}; ++ {ok, State} -> ++ write_events(State, Buf), ++ {ok, State}; + Error -> + Error + end; +@@ -52,49 +56,45 @@ init(File, PrevHandler) -> + process_flag(trap_exit, true), + case file:open(File, [write]) of + {ok,Fd} -> +- {ok, {Fd, File, PrevHandler}}; ++ Depth = get_depth(), ++ State = #st{fd=Fd,filename=File,prev_handler=PrevHandler, ++ depth=Depth}, ++ {ok, State}; + Error -> + Error + end. +- ++ ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. ++ + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; +-handle_event(Event, {Fd, File, PrevHandler}) -> +- write_event(Fd, tag_event(Event)), +- {ok, {Fd, File, PrevHandler}}; +-handle_event(_, State) -> ++handle_event(Event, State) -> ++ write_event(State, Event), + {ok, State}. + +-handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) -> ++handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) -> + case PrevHandler of + [] -> + remove_handler; + _ -> + {swap_handler, install_prev, [], PrevHandler, go_back} + end; +-handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler}) +- when node(GL) == node() -> +- write_event(Fd, tag_event({emulator, GL, Chars})), +- {ok, {Fd, File, PrevHandler}}; +-handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) -> +- write_event(Fd, tag_event({emulator, noproc, Chars})), +- {ok, {Fd, File, PrevHandler}}; + handle_info(_, State) -> + {ok, State}. + +-handle_call(filename, {Fd, File, Prev}) -> +- {ok, File, {Fd, File, Prev}}; ++handle_call(filename, #st{filename=File}=State) -> ++ {ok, File, State}; + handle_call(_Query, State) -> + {ok, {error, bad_query}, State}. + +-terminate(_Reason, State) -> +- case State of +- {Fd, _File, _Prev} -> +- ok = file:close(Fd); +- _ -> +- ok +- end, +- []. ++terminate(_Reason, #st{fd=Fd}) -> ++ file:close(Fd). + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. +@@ -103,69 +103,71 @@ code_change(_OldVsn, State, _Extra) -> + %%% Misc. functions. + %%% ------------------------------------------------------ + +-tag_event(Event) -> +- {erlang:universaltime(), Event}. ++write_events(State, [Ev|Es]) -> ++ %% Write the events in reversed order. ++ write_events(State, Es), ++ write_event(State, Ev); ++write_events(_State, []) -> ++ ok. + +-write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)). ++write_event(#st{fd=Fd}=State, Event) -> ++ case parse_event(Event) of ++ ignore -> ++ ok; ++ {Head,Pid,FormatList} -> ++ Time = maybe_utc(erlang:universaltime()), ++ Header = write_time(Time, Head), ++ Body = format_body(State, FormatList), ++ AtNode = if ++ node(Pid) =/= node() -> ++ ["** at node ",atom_to_list(node(Pid))," **\n"]; ++ true -> ++ [] ++ end, ++ io:put_chars(Fd, [Header,Body,AtNode]) ++ end. + +-write_events1(Fd, [Event|Es]) -> +- write_event(Fd, Event), +- write_events1(Fd, Es); +-write_events1(_, []) -> +- ok. ++format_body(State, [{Format,Args}|T]) -> ++ S = try format(State, Format, Args) of ++ S0 -> ++ S0 ++ catch ++ _:_ -> ++ format(State, "ERROR: ~p - ~p\n", [Format,Args]) ++ end, ++ [S|format_body(State, T)]; ++format_body(_State, []) -> ++ []. + +-write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(Fd, {Time, {emulator, _GL, Chars}}) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(Chars, []) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- io:format(Fd, T ++ "ERROR: ~p ~n", [Chars]) +- end; +-write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> +- T = write_time(maybe_utc(Time)), +- io:format(Fd, T ++ add_node("~p~n",Pid),[Info]); +-write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> +- T = write_time(maybe_utc(Time)), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- S = format_report(Rep), +- io:format(Fd, T ++ S ++ add_node("", Pid), []); +-write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- io:format(Fd, T ++ S, []); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- io:format(Fd, T ++ F, [Format,Args]) +- end; +-write_event(_, _) -> +- ok. ++format(#st{depth=unlimited}, Format, Args) -> ++ io_lib:format(Format, Args); ++format(#st{depth=Depth}, Format0, Args) -> ++ Format1 = io_lib:scan_format(Format0, Args), ++ Format = limit_format(Format1, Depth), ++ io_lib:build_text(Format). ++ ++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; ++ C0 =:= $w -> ++ C = C0 - ($a - $A), %To uppercase. ++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)]; ++limit_format([H|T], Depth) -> ++ [H|limit_format(T, Depth)]; ++limit_format([], _) -> ++ []. ++ ++parse_event({error, _GL, {Pid, Format, Args}}) -> ++ {"ERROR REPORT",Pid,[{Format,Args}]}; ++parse_event({info_msg, _GL, {Pid, Format, Args}}) -> ++ {"INFO REPORT",Pid,[{Format, Args}]}; ++parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> ++ {"WARNING REPORT",Pid,[{Format,Args}]}; ++parse_event({error_report, _GL, {Pid, std_error, Args}}) -> ++ {"ERROR REPORT",Pid,format_term(Args)}; ++parse_event({info_report, _GL, {Pid, std_info, Args}}) -> ++ {"INFO REPORT",Pid,format_term(Args)}; ++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> ++ {"WARNING REPORT",Pid,format_term(Args)}; ++parse_event(_) -> ignore. + + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of +@@ -182,30 +184,27 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format_report(Rep) when is_list(Rep) -> +- case string_p(Rep) of ++format_term(Term) when is_list(Term) -> ++ case string_p(Term) of + true -> +- io_lib:format("~s~n",[Rep]); +- _ -> +- format_rep(Rep) ++ [{"~s\n",[Term]}]; ++ false -> ++ format_term_list(Term) + end; +-format_report(Rep) -> +- io_lib:format("~p~n",[Rep]). +- +-format_rep([{Tag,Data}|Rep]) -> +- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); +-format_rep([Other|Rep]) -> +- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); +-format_rep(_) -> ++format_term(Term) -> ++ [{"~p\n",[Term]}]. ++ ++format_term_list([{Tag,Data}|T]) -> ++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; ++format_term_list([Data|T]) -> ++ [{" ~p\n",[Data]}|format_term_list(T)]; ++format_term_list([]) -> ++ []; ++format_term_list(_) -> ++ %% Continue to allow non-proper lists for now. ++ %% FIXME: Remove this clause in OTP 19. + []. + +-add_node(X, Pid) when is_atom(X) -> +- add_node(atom_to_list(X), Pid); +-add_node(X, Pid) when node(Pid) =/= node() -> +- lists:concat([X,"** at node ",node(Pid)," **~n"]); +-add_node(X, _) -> +- X. +- + string_p([]) -> + false; + string_p(Term) -> +@@ -221,15 +220,10 @@ string_p1([$\b|T]) -> string_p1(T); + string_p1([$\f|T]) -> string_p1(T); + string_p1([$\e|T]) -> string_p1(T); + string_p1([H|T]) when is_list(H) -> +- case string_p1(H) of +- true -> string_p1(T); +- _ -> false +- end; ++ string_p1(H) andalso string_p1(T); + string_p1([]) -> true; + string_p1(_) -> false. + +-write_time(Time) -> write_time(Time, "ERROR REPORT"). +- + write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl +index ad5891f..72a052f 100644 +--- a/lib/stdlib/src/error_logger_tty_h.erl ++++ b/lib/stdlib/src/error_logger_tty_h.erl +@@ -22,144 +22,178 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. +-%%% Writes all events formatted to stdout. +-%%% Handles events tagged error, emulator and info. ++%%% event handler. Writes all events formatted to stdout. + %%% + %%% It can only be started from error_logger:swap_handler(tty) +-%%% or error_logger:tty(true) ++%%% or error_logger:tty(true). + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + +--export([write_event/2]). ++-export([write_event/2,write_event/3]). ++ ++-record(st, ++ {user, ++ prev_handler, ++ io_mod=io, ++ depth=unlimited}). + + %% This one is used when we takeover from the simple error_logger. + init({[], {error_logger, Buf}}) -> + User = set_group_leader(), +- write_events(Buf,io), +- {ok, {User, error_logger}}; ++ Depth = get_depth(), ++ State = #st{user=User,prev_handler=error_logger,depth=Depth}, ++ write_events(State, Buf), ++ {ok, State}; + %% This one is used if someone took over from us, and now wants to + %% go back. + init({[], {error_logger_tty_h, PrevHandler}}) -> + User = set_group_leader(), +- {ok, {User, PrevHandler}}; ++ {ok, #st{user=User,prev_handler=PrevHandler}}; + %% This one is used when we are started directly. + init([]) -> + User = set_group_leader(), +- {ok, {User, []}}. ++ Depth = get_depth(), ++ {ok, #st{user=User,prev_handler=[],depth=Depth}}. ++ ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. + + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; + handle_event(Event, State) -> +- write_event(tag_event(Event),io), ++ ok = do_write_event(State, tag_event(Event)), + {ok, State}. + +-handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> ++handle_info({'EXIT', User, _Reason}, ++ #st{user=User,prev_handler=PrevHandler}=State) -> + case PrevHandler of + [] -> + remove_handler; + _ -> +- {swap_handler, install_prev, {User, PrevHandler}, ++ {swap_handler, install_prev, State, + PrevHandler, go_back} + end; +-handle_info({emulator, GL, Chars}, State) when node(GL) == node() -> +- write_event(tag_event({emulator, GL, Chars}),io), +- {ok, State}; +-handle_info({emulator, noproc, Chars}, State) -> +- write_event(tag_event({emulator, noproc, Chars}),io), +- {ok, State}; + handle_info(_, State) -> + {ok, State}. + + handle_call(_Query, State) -> {ok, {error, bad_query}, State}. + +-% unfortunately, we can't unlink from User - links are not counted! +-% if pid(User) -> unlink(User); true -> ok end, + terminate(install_prev, _State) -> + []; +-terminate(_Reason, {_User, PrevHandler}) -> ++terminate(_Reason, #st{prev_handler=PrevHandler}) -> + {error_logger_tty_h, PrevHandler}. + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. + ++%% Exported (but unoffical) API. ++write_event(Event, IoMod) -> ++ do_write_event(#st{io_mod=IoMod}, Event). ++ ++write_event(Event, IoMod, Depth) -> ++ do_write_event(#st{io_mod=IoMod,depth=Depth}, Event). ++ ++ + %%% ------------------------------------------------------ + %%% Misc. functions. + %%% ------------------------------------------------------ + + set_group_leader() -> + case whereis(user) of +- User when is_pid(User) -> link(User), group_leader(User,self()), User; +- _ -> false ++ User when is_pid(User) -> ++ link(User), ++ group_leader(User,self()), ++ User; ++ _ -> ++ false + end. + + tag_event(Event) -> + {erlang:universaltime(), Event}. + +-write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod). +- +-write_events1([Event|Es],IOMod) -> +- write_event(Event,IOMod), +- write_events1(Es,IOMod); +-write_events1([],_IOMod) -> ++write_events(State, [Ev|Es]) -> ++ %% Write the events in reverse order. ++ _ = write_events(State, Es), ++ _ = do_write_event(State, Ev), ++ ok; ++write_events(_State, []) -> + ok. + +-write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) ++do_write_event(State, {Time0, Event}) -> ++ case parse_event(Event) of ++ ignore -> ++ ok; ++ {Head,Pid,FormatList} -> ++ Time = maybe_utc(Time0), ++ Header = write_time(Time, Head), ++ Body = format_body(State, FormatList), ++ AtNode = if ++ node(Pid) =/= node() -> ++ ["** at node ",atom_to_list(node(Pid))," **\n"]; ++ true -> ++ [] ++ end, ++ Str = [Header,Body,AtNode], ++ case State#st.io_mod of ++ io_lib -> ++ Str; ++ io -> ++ io:put_chars(user, Str) ++ end + end; +-write_event({Time, {emulator, _GL, Chars}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- case catch io_lib:format(Chars, []) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- format(IOMod, T ++ "ERROR: ~p ~n", [Chars]) +- end; +-write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- format(IOMod, T ++ add_node("~p~n",Pid),[Info]); +-write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time)), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time), "INFO REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) +- end; +-write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- S = format_report(Rep), +- format(IOMod, T ++ S ++ add_node("", Pid)); +-write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) -> +- T = write_time(maybe_utc(Time), "WARNING REPORT"), +- case catch io_lib:format(add_node(Format,Pid), Args) of +- S when is_list(S) -> +- format(IOMod, T ++ S); +- _ -> +- F = add_node("ERROR: ~p - ~p~n", Pid), +- format(IOMod, T ++ F, [Format,Args]) +- end; +-write_event({_Time, _Error},_IOMod) -> ++do_write_event(_, _) -> + ok. + ++format_body(State, [{Format,Args}|T]) -> ++ S = try format(State, Format, Args) of ++ S0 -> ++ S0 ++ catch ++ _:_ -> ++ format(State, "ERROR: ~p - ~p\n", [Format,Args]) ++ end, ++ [S|format_body(State, T)]; ++format_body(_State, []) -> ++ []. ++ ++format(#st{depth=unlimited}, Format, Args) -> ++ io_lib:format(Format, Args); ++format(#st{depth=Depth}, Format0, Args) -> ++ Format1 = io_lib:scan_format(Format0, Args), ++ Format = limit_format(Format1, Depth), ++ io_lib:build_text(Format). ++ ++limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; ++ C0 =:= $w -> ++ C = C0 - ($a - $A), %To uppercase. ++ [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)]; ++limit_format([H|T], Depth) -> ++ [H|limit_format(T, Depth)]; ++limit_format([], _) -> ++ []. ++ ++parse_event({error, _GL, {Pid, Format, Args}}) -> ++ {"ERROR REPORT",Pid,[{Format,Args}]}; ++parse_event({info_msg, _GL, {Pid, Format, Args}}) -> ++ {"INFO REPORT",Pid,[{Format, Args}]}; ++parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> ++ {"WARNING REPORT",Pid,[{Format,Args}]}; ++parse_event({error_report, _GL, {Pid, std_error, Args}}) -> ++ {"ERROR REPORT",Pid,format_term(Args)}; ++parse_event({info_report, _GL, {Pid, std_info, Args}}) -> ++ {"INFO REPORT",Pid,format_term(Args)}; ++parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> ++ {"WARNING REPORT",Pid,format_term(Args)}; ++parse_event(_) -> ignore. ++ + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of + {ok, Val} -> Val; +@@ -175,33 +209,26 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format(IOMod, String) -> format(IOMod, String, []). +-format(io_lib, String, Args) -> io_lib:format(String, Args); +-format(io, String, Args) -> io:format(user, String, Args). +- +-format_report(Rep) when is_list(Rep) -> +- case string_p(Rep) of ++format_term(Term) when is_list(Term) -> ++ case string_p(Term) of + true -> +- io_lib:format("~s~n",[Rep]); +- _ -> +- format_rep(Rep) ++ [{"~s\n",[Term]}]; ++ false -> ++ format_term_list(Term) + end; +-format_report(Rep) -> +- io_lib:format("~p~n",[Rep]). +- +-format_rep([{Tag,Data}|Rep]) -> +- io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); +-format_rep([Other|Rep]) -> +- io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); +-format_rep(_) -> +- []. ++format_term(Term) -> ++ [{"~p\n",[Term]}]. + +-add_node(X, Pid) when is_atom(X) -> +- add_node(atom_to_list(X), Pid); +-add_node(X, Pid) when node(Pid) =/= node() -> +- lists:concat([X,"** at node ",node(Pid)," **~n"]); +-add_node(X, _) -> +- X. ++format_term_list([{Tag,Data}|T]) -> ++ [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; ++format_term_list([Data|T]) -> ++ [{" ~p\n",[Data]}|format_term_list(T)]; ++format_term_list([]) -> ++ []; ++format_term_list(_) -> ++ %% Continue to allow non-proper lists for now. ++ %% FIXME: Remove this clause in OTP 19. ++ []. + + string_p([]) -> + false; +@@ -225,7 +252,6 @@ string_p1([H|T]) when is_list(H) -> + string_p1([]) -> true; + string_p1(_) -> false. + +-write_time(Time) -> write_time(Time, "ERROR REPORT"). + write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl +index 9e69601..3c173dc 100644 +--- a/lib/stdlib/src/io_lib.erl ++++ b/lib/stdlib/src/io_lib.erl +@@ -2,7 +2,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2013. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2014. 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 +@@ -61,6 +61,7 @@ + -module(io_lib). + + -export([fwrite/2,fread/2,fread/3,format/2]). ++-export([scan_format/2,unscan_format/1,build_text/1]). + -export([print/1,print/4,indentation/2]). + + -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). +@@ -84,7 +85,7 @@ + deep_unicode_char_list/1]). + + -export_type([chars/0, latin1_string/0, continuation/0, +- fread_error/0, fread_item/0]). ++ fread_error/0, fread_item/0, format_spec/0]). + + %%---------------------------------------------------------------------- + +@@ -109,6 +110,18 @@ + + -type fread_item() :: string() | atom() | integer() | float(). + ++-type format_spec() :: ++ { ++ ControlChar :: char(), ++ Args :: [any()], ++ Width :: 'none' | integer(), ++ Adjust :: 'left' | 'right', ++ Precision :: 'none' | integer(), ++ PadChar :: char(), ++ Encoding :: 'unicode' | 'latin1', ++ Strings :: boolean() ++ }. ++ + %%---------------------------------------------------------------------- + + %% Interface calls to sub-modules. +@@ -157,6 +170,31 @@ format(Format, Args) -> + Other + end. + ++-spec scan_format(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | format_spec()]. ++ ++scan_format(Format, Args) -> ++ try io_lib_format:scan(Format, Args) ++ catch ++ _:_ -> erlang:error(badarg, [Format, Args]) ++ end. ++ ++-spec unscan_format(FormatList) -> {Format, Data} when ++ FormatList :: [char() | format_spec()], ++ Format :: io:format(), ++ Data :: [term()]. ++ ++unscan_format(FormatList) -> ++ io_lib_format:unscan(FormatList). ++ ++-spec build_text(FormatList) -> chars() when ++ FormatList :: [char() | format_spec()]. ++ ++build_text(FormatList) -> ++ io_lib_format:build(FormatList). ++ + -spec print(Term) -> chars() when + Term :: term(). + +diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl +index 56e15a1..37b47d7 100644 +--- a/lib/stdlib/src/io_lib_format.erl ++++ b/lib/stdlib/src/io_lib_format.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2013. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2014. 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 +@@ -20,10 +20,9 @@ + + %% Formatting functions of io library. + +--export([fwrite/2,fwrite_g/1,indentation/2]). ++-export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]). + +-%% fwrite(Format, ArgList) -> string(). +-%% Format the arguments in ArgList after string Format. Just generate ++%% Format the arguments in Args after string Format. Just generate + %% an error if there is an error in the arguments. + %% + %% To do the printing command correctly we need to calculate the +@@ -37,15 +36,83 @@ + %% and it also splits the handling of the control characters into two + %% parts. + +-fwrite(Format, Args) when is_atom(Format) -> +- fwrite(atom_to_list(Format), Args); +-fwrite(Format, Args) when is_binary(Format) -> +- fwrite(binary_to_list(Format), Args); ++-spec fwrite(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | io_lib:format_spec()]. ++ + fwrite(Format, Args) -> +- Cs = collect(Format, Args), ++ build(scan(Format, Args)). ++ ++%% Build the output text for a pre-parsed format list. ++ ++-spec build(FormatList) -> io_lib:chars() when ++ FormatList :: [char() | io_lib:format_spec()]. ++ ++build(Cs) -> + Pc = pcount(Cs), + build(Cs, Pc, 0). + ++%% Parse all control sequences in the format string. ++ ++-spec scan(Format, Data) -> FormatList when ++ Format :: io:format(), ++ Data :: [term()], ++ FormatList :: [char() | io_lib:format_spec()]. ++ ++scan(Format, Args) when is_atom(Format) -> ++ scan(atom_to_list(Format), Args); ++scan(Format, Args) when is_binary(Format) -> ++ scan(binary_to_list(Format), Args); ++scan(Format, Args) -> ++ collect(Format, Args). ++ ++%% Revert a pre-parsed format list to a plain character list and a ++%% list of arguments. ++ ++-spec unscan(FormatList) -> {Format, Data} when ++ FormatList :: [char() | io_lib:format_spec()], ++ Format :: io:format(), ++ Data :: [term()]. ++ ++unscan(Cs) -> ++ {print(Cs), args(Cs)}. ++ ++args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) -> ++ As ++ args(Cs); ++args([_C | Cs]) -> ++ args(Cs); ++args([]) -> ++ []. ++ ++print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) -> ++ print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs); ++print([C | Cs]) -> ++ [C | print(Cs)]; ++print([]) -> ++ []. ++ ++print(C, F, Ad, P, Pad, Encoding, Strings) -> ++ [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++ ++ print_pad_char(Pad) ++ print_encoding(Encoding) ++ ++ print_strings(Strings) ++ [C]. ++ ++print_field_width(none, _Ad) -> ""; ++print_field_width(F, left) -> integer_to_list(-F); ++print_field_width(F, right) -> integer_to_list(F). ++ ++print_precision(none) -> ""; ++print_precision(P) -> [$. | integer_to_list(P)]. ++ ++print_pad_char($\s) -> ""; % default, no need to make explicit ++print_pad_char(Pad) -> [$., Pad]. ++ ++print_encoding(unicode) -> "t"; ++print_encoding(latin1) -> "". ++ ++print_strings(false) -> "l"; ++print_strings(true) -> "". ++ + collect([$~|Fmt0], Args0) -> + {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0), + [C|collect(Fmt1, Args1)]; +@@ -141,7 +208,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); + pcount([_|Cs], Acc) -> pcount(Cs, Acc); + pcount([], Acc) -> Acc. + +-%% build([Control], Pc, Indentation) -> string(). ++%% build([Control], Pc, Indentation) -> io_lib:chars(). + %% Interpret the control structures. Count the number of print + %% remaining and only calculate indentation when necessary. Must also + %% be smart when calculating indentation for characters in format. +@@ -162,10 +229,14 @@ decr_pc($p, Pc) -> Pc - 1; + decr_pc($P, Pc) -> Pc - 1; + decr_pc(_, Pc) -> Pc. + +-%% indentation(String, Indentation) -> Indentation. ++ + %% Calculate the indentation of the end of a string given its start + %% indentation. We assume tabs at 8 cols. + ++-spec indentation(String, StartIndent) -> integer() when ++ String :: io_lib:chars(), ++ StartIndent :: integer(). ++ + indentation([$\n|Cs], _I) -> indentation(Cs, 0); + indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8); + indentation([C|Cs], I) when is_integer(C) -> +@@ -366,7 +437,6 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 -> + float_data([_|Cs], Ds) -> + float_data(Cs, Ds). + +-%% fwrite_g(Float) + %% Writes the shortest, correctly rounded string that converts + %% to Float when read back with list_to_float/1. + %% +@@ -374,6 +444,8 @@ float_data([_|Cs], Ds) -> + %% in Proceedings of the SIGPLAN '96 Conference on Programming + %% Language Design and Implementation. + ++-spec fwrite_g(float()) -> string(). ++ + fwrite_g(0.0) -> + "0.0"; + fwrite_g(Float) when is_float(Float) -> +@@ -642,7 +714,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) + term([Prefix|S], F, Adj, none, Pad) + end. + +-%% char(Char, Field, Adjust, Precision, PadChar) -> string(). ++%% char(Char, Field, Adjust, Precision, PadChar) -> chars(). + + char(C, none, _Adj, none, _Pad) -> [C]; + char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch new file mode 100644 index 0000000..e9e6450 --- /dev/null +++ b/otp-0025-Teach-sasl_report-to-limit-crash-reports.patch @@ -0,0 +1,74 @@ +From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= +Date: Tue, 25 Aug 2015 15:20:23 +0200 +Subject: [PATCH] Teach sasl_report to limit crash reports + + +diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl +index c3e6fed..aa84e4f 100644 +--- a/lib/sasl/src/sasl_report.erl ++++ b/lib/sasl/src/sasl_report.erl +@@ -61,27 +61,53 @@ write_report2(IO, Fd, Head, supervisor_report, Report) -> + Context = sup_get(errorContext, Report), + Reason = sup_get(reason, Report), + Offender = sup_get(offender, Report), +- FmtString = " Supervisor: ~p~n Context: ~p~n Reason: " +- "~80.18p~n Offender: ~80.18p~n~n", +- write_report_action(IO, Fd, Head ++ FmtString, +- [Name,Context,Reason,Offender]); ++ {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]), ++ write_report_action(IO, Fd, Head, FmtString, Args); + write_report2(IO, Fd, Head, progress, Report) -> + Format = format_key_val(Report), +- write_report_action(IO, Fd, Head ++ "~s", [Format]); ++ write_report_action(IO, Fd, Head, "~s", [Format]); + write_report2(IO, Fd, Head, crash_report, Report) -> +- Format = proc_lib:format(Report), +- write_report_action(IO, Fd, Head ++ "~s", [Format]). ++ Depth = get_depth(), ++ Format = proc_lib:format(Report, latin1, Depth), ++ write_report_action(IO, Fd, Head, "~s", [Format]). ++ ++supervisor_format(Args0) -> ++ case get_depth() of ++ unlimited -> ++ {" Supervisor: ~p~n" ++ " Context: ~p~n" ++ " Reason: ~80.18p~n" ++ " Offender: ~80.18p~n~n", ++ Args0}; ++ Depth -> ++ [A,B,C,D] = Args0, ++ Args = [A,Depth,B,Depth,C,Depth,D,Depth], ++ {" Supervisor: ~P~n" ++ " Context: ~P~n" ++ " Reason: ~80.18P~n" ++ " Offender: ~80.18P~n~n", ++ Args} ++ end. + +-write_report_action(io, Fd, Format, Args) -> +- io:format(Fd, Format, Args); +-write_report_action(io_lib, _Fd, Format, Args) -> +- io_lib:format(Format, Args). ++write_report_action(IO, Fd, Head, Format, Args) -> ++ S = [Head|io_lib:format(Format, Args)], ++ case IO of ++ io -> io:put_chars(Fd, S); ++ io_lib -> S ++ end. + + format_key_val([{Tag,Data}|Rep]) -> + io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep); + format_key_val(_) -> + []. + ++get_depth() -> ++ case application:get_env(kernel, error_logger_format_depth) of ++ {ok, Depth} when is_integer(Depth) -> ++ max(10, Depth); ++ undefined -> ++ unlimited ++ end. + + sup_get(Tag, Report) -> + case lists:keysearch(Tag, 1, Report) of diff --git a/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch b/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch new file mode 100644 index 0000000..dda297d --- /dev/null +++ b/otp-0026-Revert-Remove-unused-code-in-error-logger-handlers.patch @@ -0,0 +1,1064 @@ +From: Peter Lemenkov +Date: Wed, 9 Nov 2016 17:28:28 +0300 +Subject: [PATCH] Revert "Remove unused code in error logger handlers" + +This reverts commit f022259a41dbf5b078bea38fd3a7b98f0d4ed587. + +Revert "Teach sasl_report to limit crash reports" + +This reverts commit 33254666c90aedb190c6cffe36a027053918d92c. + +diff --git a/lib/sasl/src/sasl_report.erl b/lib/sasl/src/sasl_report.erl +index aa84e4f..c3e6fed 100644 +--- a/lib/sasl/src/sasl_report.erl ++++ b/lib/sasl/src/sasl_report.erl +@@ -61,53 +61,27 @@ write_report2(IO, Fd, Head, supervisor_report, Report) -> + Context = sup_get(errorContext, Report), + Reason = sup_get(reason, Report), + Offender = sup_get(offender, Report), +- {FmtString,Args} = supervisor_format([Name,Context,Reason,Offender]), +- write_report_action(IO, Fd, Head, FmtString, Args); ++ FmtString = " Supervisor: ~p~n Context: ~p~n Reason: " ++ "~80.18p~n Offender: ~80.18p~n~n", ++ write_report_action(IO, Fd, Head ++ FmtString, ++ [Name,Context,Reason,Offender]); + write_report2(IO, Fd, Head, progress, Report) -> + Format = format_key_val(Report), +- write_report_action(IO, Fd, Head, "~s", [Format]); ++ write_report_action(IO, Fd, Head ++ "~s", [Format]); + write_report2(IO, Fd, Head, crash_report, Report) -> +- Depth = get_depth(), +- Format = proc_lib:format(Report, latin1, Depth), +- write_report_action(IO, Fd, Head, "~s", [Format]). +- +-supervisor_format(Args0) -> +- case get_depth() of +- unlimited -> +- {" Supervisor: ~p~n" +- " Context: ~p~n" +- " Reason: ~80.18p~n" +- " Offender: ~80.18p~n~n", +- Args0}; +- Depth -> +- [A,B,C,D] = Args0, +- Args = [A,Depth,B,Depth,C,Depth,D,Depth], +- {" Supervisor: ~P~n" +- " Context: ~P~n" +- " Reason: ~80.18P~n" +- " Offender: ~80.18P~n~n", +- Args} +- end. ++ Format = proc_lib:format(Report), ++ write_report_action(IO, Fd, Head ++ "~s", [Format]). + +-write_report_action(IO, Fd, Head, Format, Args) -> +- S = [Head|io_lib:format(Format, Args)], +- case IO of +- io -> io:put_chars(Fd, S); +- io_lib -> S +- end. ++write_report_action(io, Fd, Format, Args) -> ++ io:format(Fd, Format, Args); ++write_report_action(io_lib, _Fd, Format, Args) -> ++ io_lib:format(Format, Args). + + format_key_val([{Tag,Data}|Rep]) -> + io_lib:format(" ~16w: ~p~n",[Tag,Data]) ++ format_key_val(Rep); + format_key_val(_) -> + []. + +-get_depth() -> +- case application:get_env(kernel, error_logger_format_depth) of +- {ok, Depth} when is_integer(Depth) -> +- max(10, Depth); +- undefined -> +- unlimited +- end. + + sup_get(Tag, Report) -> + case lists:keysearch(Tag, 1, Report) of +diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml +index 6613dcd..90f24c4 100644 +--- a/lib/stdlib/doc/src/io.xml ++++ b/lib/stdlib/doc/src/io.xml +@@ -505,8 +505,7 @@ ok +

Writes the data with standard syntax in the same way as + ~w, but breaks terms whose printed representation + is longer than one line into many lines and indents each +- line sensibly. Left justification is not supported. +- It also tries to detect lists of ++ line sensibly. It also tries to detect lists of + printable characters and to output these as strings. The + Unicode translation modifier is used for determining + what characters are printable. For example:

+diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml +index 0c1c84d..68352ff 100644 +--- a/lib/stdlib/doc/src/io_lib.xml ++++ b/lib/stdlib/doc/src/io_lib.xml +@@ -4,7 +4,7 @@ + +
+ +- 19962014 ++ 19962013 + Ericsson AB. All Rights Reserved. + + +@@ -59,35 +59,6 @@ + + + +- +- +-

Description:

+- +-

control_char is the type of control +- sequence: $P, $w, and so on;

+-
+-

args is a list of the arguments used by the +- control sequence, or an empty list if the control sequence +- does not take any arguments;

+-
+-

width is the field width;

+-
+-

adjust is the adjustment;

+-
+-

precision is the precision of the printed +- argument;

+-
+-

pad_char is the padding character;

+-
+-

encoding is set to true if the translation +- modifier t is present;

+-
+-

strings is set to false if the modifier +- l is present.

+-
+-
+-
+-
+ + + +@@ -289,45 +260,6 @@ + + + +- +- Parse all control sequences in the format string +- +-

Returns a list corresponding to the given format string, +- where control sequences have been replaced with +- corresponding tuples. This list can be passed to io_lib:build_text/1 to have +- the same effect as io_lib:format(Format, Args), or to +- io_lib:unscan_format/1 +- in order to get the corresponding pair of Format and +- Args (with every * and corresponding argument +- expanded to numeric values).

+-

A typical use of this function is to replace unbounded-size +- control sequences like ~w and ~p with the +- depth-limited variants ~W and ~P before +- formatting to text, e.g. in a logger.

+-
+-
+- +- +- Revert a pre-parsed format list to a plain character list +- and a list of arguments +- +-

See io_lib:scan_format/2 for +- details.

+-
+-
+- +- +- Build the output text for a pre-parsed format list +- +-

See io_lib:scan_format/2 for +- details.

+-
+-
+- + + Indentation after printing string + +diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl +index ca44d01..e9364ed 100644 +--- a/lib/stdlib/src/error_logger_file_h.erl ++++ b/lib/stdlib/src/error_logger_file_h.erl +@@ -23,28 +23,24 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. Writes all events formatted to file. ++%%% event handler. ++%%% Writes all events formatted to file. ++%%% Handles events tagged error, emulator and info. + %%% + %%% It can only be started from error_logger:swap_handler({logfile, File}) +-%%% or error_logger:logfile(File). ++%%% or error_logger:logfile(File) + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + +--record(st, +- {fd, +- filename, +- prev_handler, +- depth=unlimited :: 'unlimited' | non_neg_integer()}). +- + %% This one is used when we takeover from the simple error_logger. + init({File, {error_logger, Buf}}) -> + case init(File, error_logger) of +- {ok, State} -> +- write_events(State, Buf), +- {ok, State}; ++ {ok, {Fd, File, PrevHandler}} -> ++ write_events(Fd, Buf), ++ {ok, {Fd, File, PrevHandler}}; + Error -> + Error + end; +@@ -56,45 +52,49 @@ init(File, PrevHandler) -> + process_flag(trap_exit, true), + case file:open(File, [write]) of + {ok,Fd} -> +- Depth = get_depth(), +- State = #st{fd=Fd,filename=File,prev_handler=PrevHandler, +- depth=Depth}, +- {ok, State}; ++ {ok, {Fd, File, PrevHandler}}; + Error -> + Error + end. +- +-get_depth() -> +- case application:get_env(kernel, error_logger_format_depth) of +- {ok, Depth} when is_integer(Depth) -> +- max(10, Depth); +- undefined -> +- unlimited +- end. +- ++ + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; +-handle_event(Event, State) -> +- write_event(State, Event), ++handle_event(Event, {Fd, File, PrevHandler}) -> ++ write_event(Fd, tag_event(Event)), ++ {ok, {Fd, File, PrevHandler}}; ++handle_event(_, State) -> + {ok, State}. + +-handle_info({'EXIT', Fd, _Reason}, #st{fd=Fd,prev_handler=PrevHandler}) -> ++handle_info({'EXIT', Fd, _Reason}, {Fd, _File, PrevHandler}) -> + case PrevHandler of + [] -> + remove_handler; + _ -> + {swap_handler, install_prev, [], PrevHandler, go_back} + end; ++handle_info({emulator, GL, Chars}, {Fd, File, PrevHandler}) ++ when node(GL) == node() -> ++ write_event(Fd, tag_event({emulator, GL, Chars})), ++ {ok, {Fd, File, PrevHandler}}; ++handle_info({emulator, noproc, Chars}, {Fd, File, PrevHandler}) -> ++ write_event(Fd, tag_event({emulator, noproc, Chars})), ++ {ok, {Fd, File, PrevHandler}}; + handle_info(_, State) -> + {ok, State}. + +-handle_call(filename, #st{filename=File}=State) -> +- {ok, File, State}; ++handle_call(filename, {Fd, File, Prev}) -> ++ {ok, File, {Fd, File, Prev}}; + handle_call(_Query, State) -> + {ok, {error, bad_query}, State}. + +-terminate(_Reason, #st{fd=Fd}) -> +- file:close(Fd). ++terminate(_Reason, State) -> ++ case State of ++ {Fd, _File, _Prev} -> ++ ok = file:close(Fd); ++ _ -> ++ ok ++ end, ++ []. + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. +@@ -103,71 +103,69 @@ code_change(_OldVsn, State, _Extra) -> + %%% Misc. functions. + %%% ------------------------------------------------------ + +-write_events(State, [Ev|Es]) -> +- %% Write the events in reversed order. +- write_events(State, Es), +- write_event(State, Ev); +-write_events(_State, []) -> +- ok. ++tag_event(Event) -> ++ {erlang:universaltime(), Event}. + +-write_event(#st{fd=Fd}=State, Event) -> +- case parse_event(Event) of +- ignore -> +- ok; +- {Head,Pid,FormatList} -> +- Time = maybe_utc(erlang:universaltime()), +- Header = write_time(Time, Head), +- Body = format_body(State, FormatList), +- AtNode = if +- node(Pid) =/= node() -> +- ["** at node ",atom_to_list(node(Pid))," **\n"]; +- true -> +- [] +- end, +- io:put_chars(Fd, [Header,Body,AtNode]) +- end. ++write_events(Fd, Events) -> write_events1(Fd, lists:reverse(Events)). + +-format_body(State, [{Format,Args}|T]) -> +- S = try format(State, Format, Args) of +- S0 -> +- S0 +- catch +- _:_ -> +- format(State, "ERROR: ~p - ~p\n", [Format,Args]) +- end, +- [S|format_body(State, T)]; +-format_body(_State, []) -> +- []. +- +-format(#st{depth=unlimited}, Format, Args) -> +- io_lib:format(Format, Args); +-format(#st{depth=Depth}, Format0, Args) -> +- Format1 = io_lib:scan_format(Format0, Args), +- Format = limit_format(Format1, Depth), +- io_lib:build_text(Format). +- +-limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; +- C0 =:= $w -> +- C = C0 - ($a - $A), %To uppercase. +- [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}||limit_format(T, Depth)]; +-limit_format([H|T], Depth) -> +- [H|limit_format(T, Depth)]; +-limit_format([], _) -> +- []. ++write_events1(Fd, [Event|Es]) -> ++ write_event(Fd, Event), ++ write_events1(Fd, Es); ++write_events1(_, []) -> ++ ok. + +-parse_event({error, _GL, {Pid, Format, Args}}) -> +- {"ERROR REPORT",Pid,[{Format,Args}]}; +-parse_event({info_msg, _GL, {Pid, Format, Args}}) -> +- {"INFO REPORT",Pid,[{Format, Args}]}; +-parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> +- {"WARNING REPORT",Pid,[{Format,Args}]}; +-parse_event({error_report, _GL, {Pid, std_error, Args}}) -> +- {"ERROR REPORT",Pid,format_term(Args)}; +-parse_event({info_report, _GL, {Pid, std_info, Args}}) -> +- {"INFO REPORT",Pid,format_term(Args)}; +-parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> +- {"WARNING REPORT",Pid,format_term(Args)}; +-parse_event(_) -> ignore. ++write_event(Fd, {Time, {error, _GL, {Pid, Format, Args}}}) -> ++ T = write_time(maybe_utc(Time)), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ io:format(Fd, T ++ S, []); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ io:format(Fd, T ++ F, [Format,Args]) ++ end; ++write_event(Fd, {Time, {emulator, _GL, Chars}}) -> ++ T = write_time(maybe_utc(Time)), ++ case catch io_lib:format(Chars, []) of ++ S when is_list(S) -> ++ io:format(Fd, T ++ S, []); ++ _ -> ++ io:format(Fd, T ++ "ERROR: ~p ~n", [Chars]) ++ end; ++write_event(Fd, {Time, {info, _GL, {Pid, Info, _}}}) -> ++ T = write_time(maybe_utc(Time)), ++ io:format(Fd, T ++ add_node("~p~n",Pid),[Info]); ++write_event(Fd, {Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> ++ T = write_time(maybe_utc(Time)), ++ S = format_report(Rep), ++ io:format(Fd, T ++ S ++ add_node("", Pid), []); ++write_event(Fd, {Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> ++ T = write_time(maybe_utc(Time), "INFO REPORT"), ++ S = format_report(Rep), ++ io:format(Fd, T ++ S ++ add_node("", Pid), []); ++write_event(Fd, {Time, {info_msg, _GL, {Pid, Format, Args}}}) -> ++ T = write_time(maybe_utc(Time), "INFO REPORT"), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ io:format(Fd, T ++ S, []); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ io:format(Fd, T ++ F, [Format,Args]) ++ end; ++write_event(Fd, {Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) -> ++ T = write_time(maybe_utc(Time), "WARNING REPORT"), ++ S = format_report(Rep), ++ io:format(Fd, T ++ S ++ add_node("", Pid), []); ++write_event(Fd, {Time, {warning_msg, _GL, {Pid, Format, Args}}}) -> ++ T = write_time(maybe_utc(Time), "WARNING REPORT"), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ io:format(Fd, T ++ S, []); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ io:format(Fd, T ++ F, [Format,Args]) ++ end; ++write_event(_, _) -> ++ ok. + + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of +@@ -184,27 +182,30 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format_term(Term) when is_list(Term) -> +- case string_p(Term) of ++format_report(Rep) when is_list(Rep) -> ++ case string_p(Rep) of + true -> +- [{"~s\n",[Term]}]; +- false -> +- format_term_list(Term) ++ io_lib:format("~s~n",[Rep]); ++ _ -> ++ format_rep(Rep) + end; +-format_term(Term) -> +- [{"~p\n",[Term]}]. +- +-format_term_list([{Tag,Data}|T]) -> +- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; +-format_term_list([Data|T]) -> +- [{" ~p\n",[Data]}|format_term_list(T)]; +-format_term_list([]) -> +- []; +-format_term_list(_) -> +- %% Continue to allow non-proper lists for now. +- %% FIXME: Remove this clause in OTP 19. ++format_report(Rep) -> ++ io_lib:format("~p~n",[Rep]). ++ ++format_rep([{Tag,Data}|Rep]) -> ++ io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); ++format_rep([Other|Rep]) -> ++ io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); ++format_rep(_) -> + []. + ++add_node(X, Pid) when is_atom(X) -> ++ add_node(atom_to_list(X), Pid); ++add_node(X, Pid) when node(Pid) =/= node() -> ++ lists:concat([X,"** at node ",node(Pid)," **~n"]); ++add_node(X, _) -> ++ X. ++ + string_p([]) -> + false; + string_p(Term) -> +@@ -220,10 +221,15 @@ string_p1([$\b|T]) -> string_p1(T); + string_p1([$\f|T]) -> string_p1(T); + string_p1([$\e|T]) -> string_p1(T); + string_p1([H|T]) when is_list(H) -> +- string_p1(H) andalso string_p1(T); ++ case string_p1(H) of ++ true -> string_p1(T); ++ _ -> false ++ end; + string_p1([]) -> true; + string_p1(_) -> false. + ++write_time(Time) -> write_time(Time, "ERROR REPORT"). ++ + write_time({utc,{{Y,Mo,D},{H,Mi,S}}}, Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl +index 72a052f..ad5891f 100644 +--- a/lib/stdlib/src/error_logger_tty_h.erl ++++ b/lib/stdlib/src/error_logger_tty_h.erl +@@ -22,178 +22,144 @@ + + %%% + %%% A handler that can be connected to the error_logger +-%%% event handler. Writes all events formatted to stdout. ++%%% event handler. ++%%% Writes all events formatted to stdout. ++%%% Handles events tagged error, emulator and info. + %%% + %%% It can only be started from error_logger:swap_handler(tty) +-%%% or error_logger:tty(true). ++%%% or error_logger:tty(true) + %%% + + -export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2, code_change/3]). + +--export([write_event/2,write_event/3]). +- +--record(st, +- {user, +- prev_handler, +- io_mod=io, +- depth=unlimited}). ++-export([write_event/2]). + + %% This one is used when we takeover from the simple error_logger. + init({[], {error_logger, Buf}}) -> + User = set_group_leader(), +- Depth = get_depth(), +- State = #st{user=User,prev_handler=error_logger,depth=Depth}, +- write_events(State, Buf), +- {ok, State}; ++ write_events(Buf,io), ++ {ok, {User, error_logger}}; + %% This one is used if someone took over from us, and now wants to + %% go back. + init({[], {error_logger_tty_h, PrevHandler}}) -> + User = set_group_leader(), +- {ok, #st{user=User,prev_handler=PrevHandler}}; ++ {ok, {User, PrevHandler}}; + %% This one is used when we are started directly. + init([]) -> + User = set_group_leader(), +- Depth = get_depth(), +- {ok, #st{user=User,prev_handler=[],depth=Depth}}. +- +-get_depth() -> +- case application:get_env(kernel, error_logger_format_depth) of +- {ok, Depth} when is_integer(Depth) -> +- max(10, Depth); +- undefined -> +- unlimited +- end. ++ {ok, {User, []}}. + + handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> + {ok, State}; + handle_event(Event, State) -> +- ok = do_write_event(State, tag_event(Event)), ++ write_event(tag_event(Event),io), + {ok, State}. + +-handle_info({'EXIT', User, _Reason}, +- #st{user=User,prev_handler=PrevHandler}=State) -> ++handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> + case PrevHandler of + [] -> + remove_handler; + _ -> +- {swap_handler, install_prev, State, ++ {swap_handler, install_prev, {User, PrevHandler}, + PrevHandler, go_back} + end; ++handle_info({emulator, GL, Chars}, State) when node(GL) == node() -> ++ write_event(tag_event({emulator, GL, Chars}),io), ++ {ok, State}; ++handle_info({emulator, noproc, Chars}, State) -> ++ write_event(tag_event({emulator, noproc, Chars}),io), ++ {ok, State}; + handle_info(_, State) -> + {ok, State}. + + handle_call(_Query, State) -> {ok, {error, bad_query}, State}. + ++% unfortunately, we can't unlink from User - links are not counted! ++% if pid(User) -> unlink(User); true -> ok end, + terminate(install_prev, _State) -> + []; +-terminate(_Reason, #st{prev_handler=PrevHandler}) -> ++terminate(_Reason, {_User, PrevHandler}) -> + {error_logger_tty_h, PrevHandler}. + + code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +-%% Exported (but unoffical) API. +-write_event(Event, IoMod) -> +- do_write_event(#st{io_mod=IoMod}, Event). +- +-write_event(Event, IoMod, Depth) -> +- do_write_event(#st{io_mod=IoMod,depth=Depth}, Event). +- +- + %%% ------------------------------------------------------ + %%% Misc. functions. + %%% ------------------------------------------------------ + + set_group_leader() -> + case whereis(user) of +- User when is_pid(User) -> +- link(User), +- group_leader(User,self()), +- User; +- _ -> +- false ++ User when is_pid(User) -> link(User), group_leader(User,self()), User; ++ _ -> false + end. + + tag_event(Event) -> + {erlang:universaltime(), Event}. + +-write_events(State, [Ev|Es]) -> +- %% Write the events in reverse order. +- _ = write_events(State, Es), +- _ = do_write_event(State, Ev), +- ok; +-write_events(_State, []) -> ++write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod). ++ ++write_events1([Event|Es],IOMod) -> ++ write_event(Event,IOMod), ++ write_events1(Es,IOMod); ++write_events1([],_IOMod) -> + ok. + +-do_write_event(State, {Time0, Event}) -> +- case parse_event(Event) of +- ignore -> +- ok; +- {Head,Pid,FormatList} -> +- Time = maybe_utc(Time0), +- Header = write_time(Time, Head), +- Body = format_body(State, FormatList), +- AtNode = if +- node(Pid) =/= node() -> +- ["** at node ",atom_to_list(node(Pid))," **\n"]; +- true -> +- [] +- end, +- Str = [Header,Body,AtNode], +- case State#st.io_mod of +- io_lib -> +- Str; +- io -> +- io:put_chars(user, Str) +- end ++write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) -> ++ T = write_time(maybe_utc(Time)), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ format(IOMod, T ++ S); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ format(IOMod, T ++ F, [Format,Args]) + end; +-do_write_event(_, _) -> ++write_event({Time, {emulator, _GL, Chars}},IOMod) -> ++ T = write_time(maybe_utc(Time)), ++ case catch io_lib:format(Chars, []) of ++ S when is_list(S) -> ++ format(IOMod, T ++ S); ++ _ -> ++ format(IOMod, T ++ "ERROR: ~p ~n", [Chars]) ++ end; ++write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) -> ++ T = write_time(maybe_utc(Time)), ++ format(IOMod, T ++ add_node("~p~n",Pid),[Info]); ++write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) -> ++ T = write_time(maybe_utc(Time)), ++ S = format_report(Rep), ++ format(IOMod, T ++ S ++ add_node("", Pid)); ++write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) -> ++ T = write_time(maybe_utc(Time), "INFO REPORT"), ++ S = format_report(Rep), ++ format(IOMod, T ++ S ++ add_node("", Pid)); ++write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) -> ++ T = write_time(maybe_utc(Time), "INFO REPORT"), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ format(IOMod, T ++ S); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ format(IOMod, T ++ F, [Format,Args]) ++ end; ++write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) -> ++ T = write_time(maybe_utc(Time), "WARNING REPORT"), ++ S = format_report(Rep), ++ format(IOMod, T ++ S ++ add_node("", Pid)); ++write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) -> ++ T = write_time(maybe_utc(Time), "WARNING REPORT"), ++ case catch io_lib:format(add_node(Format,Pid), Args) of ++ S when is_list(S) -> ++ format(IOMod, T ++ S); ++ _ -> ++ F = add_node("ERROR: ~p - ~p~n", Pid), ++ format(IOMod, T ++ F, [Format,Args]) ++ end; ++write_event({_Time, _Error},_IOMod) -> + ok. + +-format_body(State, [{Format,Args}|T]) -> +- S = try format(State, Format, Args) of +- S0 -> +- S0 +- catch +- _:_ -> +- format(State, "ERROR: ~p - ~p\n", [Format,Args]) +- end, +- [S|format_body(State, T)]; +-format_body(_State, []) -> +- []. +- +-format(#st{depth=unlimited}, Format, Args) -> +- io_lib:format(Format, Args); +-format(#st{depth=Depth}, Format0, Args) -> +- Format1 = io_lib:scan_format(Format0, Args), +- Format = limit_format(Format1, Depth), +- io_lib:build_text(Format). +- +-limit_format([{C0,As,F,Ad,P,Pad,Enc,Str}|T], Depth) when C0 =:= $p; +- C0 =:= $w -> +- C = C0 - ($a - $A), %To uppercase. +- [{C,As++[Depth],F,Ad,P,Pad,Enc,Str}|limit_format(T, Depth)]; +-limit_format([H|T], Depth) -> +- [H|limit_format(T, Depth)]; +-limit_format([], _) -> +- []. +- +-parse_event({error, _GL, {Pid, Format, Args}}) -> +- {"ERROR REPORT",Pid,[{Format,Args}]}; +-parse_event({info_msg, _GL, {Pid, Format, Args}}) -> +- {"INFO REPORT",Pid,[{Format, Args}]}; +-parse_event({warning_msg, _GL, {Pid, Format, Args}}) -> +- {"WARNING REPORT",Pid,[{Format,Args}]}; +-parse_event({error_report, _GL, {Pid, std_error, Args}}) -> +- {"ERROR REPORT",Pid,format_term(Args)}; +-parse_event({info_report, _GL, {Pid, std_info, Args}}) -> +- {"INFO REPORT",Pid,format_term(Args)}; +-parse_event({warning_report, _GL, {Pid, std_warning, Args}}) -> +- {"WARNING REPORT",Pid,format_term(Args)}; +-parse_event(_) -> ignore. +- + maybe_utc(Time) -> + UTC = case application:get_env(sasl, utc_log) of + {ok, Val} -> Val; +@@ -209,27 +175,34 @@ maybe_utc(Time) -> + maybe_utc(Time, true) -> {utc, Time}; + maybe_utc(Time, _) -> {local, calendar:universal_time_to_local_time(Time)}. + +-format_term(Term) when is_list(Term) -> +- case string_p(Term) of ++format(IOMod, String) -> format(IOMod, String, []). ++format(io_lib, String, Args) -> io_lib:format(String, Args); ++format(io, String, Args) -> io:format(user, String, Args). ++ ++format_report(Rep) when is_list(Rep) -> ++ case string_p(Rep) of + true -> +- [{"~s\n",[Term]}]; +- false -> +- format_term_list(Term) ++ io_lib:format("~s~n",[Rep]); ++ _ -> ++ format_rep(Rep) + end; +-format_term(Term) -> +- [{"~p\n",[Term]}]. +- +-format_term_list([{Tag,Data}|T]) -> +- [{" ~p: ~p\n",[Tag,Data]}|format_term_list(T)]; +-format_term_list([Data|T]) -> +- [{" ~p\n",[Data]}|format_term_list(T)]; +-format_term_list([]) -> +- []; +-format_term_list(_) -> +- %% Continue to allow non-proper lists for now. +- %% FIXME: Remove this clause in OTP 19. ++format_report(Rep) -> ++ io_lib:format("~p~n",[Rep]). ++ ++format_rep([{Tag,Data}|Rep]) -> ++ io_lib:format(" ~p: ~p~n",[Tag,Data]) ++ format_rep(Rep); ++format_rep([Other|Rep]) -> ++ io_lib:format(" ~p~n",[Other]) ++ format_rep(Rep); ++format_rep(_) -> + []. + ++add_node(X, Pid) when is_atom(X) -> ++ add_node(atom_to_list(X), Pid); ++add_node(X, Pid) when node(Pid) =/= node() -> ++ lists:concat([X,"** at node ",node(Pid)," **~n"]); ++add_node(X, _) -> ++ X. ++ + string_p([]) -> + false; + string_p(Term) -> +@@ -252,6 +225,7 @@ string_p1([H|T]) when is_list(H) -> + string_p1([]) -> true; + string_p1(_) -> false. + ++write_time(Time) -> write_time(Time, "ERROR REPORT"). + write_time({utc,{{Y,Mo,D},{H,Mi,S}}},Type) -> + io_lib:format("~n=~s==== ~p-~s-~p::~s:~s:~s UTC ===~n", + [Type,D,month(Mo),Y,t(H),t(Mi),t(S)]); +diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl +index 3c173dc..9e69601 100644 +--- a/lib/stdlib/src/io_lib.erl ++++ b/lib/stdlib/src/io_lib.erl +@@ -2,7 +2,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2014. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2013. 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 +@@ -61,7 +61,6 @@ + -module(io_lib). + + -export([fwrite/2,fread/2,fread/3,format/2]). +--export([scan_format/2,unscan_format/1,build_text/1]). + -export([print/1,print/4,indentation/2]). + + -export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). +@@ -85,7 +84,7 @@ + deep_unicode_char_list/1]). + + -export_type([chars/0, latin1_string/0, continuation/0, +- fread_error/0, fread_item/0, format_spec/0]). ++ fread_error/0, fread_item/0]). + + %%---------------------------------------------------------------------- + +@@ -110,18 +109,6 @@ + + -type fread_item() :: string() | atom() | integer() | float(). + +--type format_spec() :: +- { +- ControlChar :: char(), +- Args :: [any()], +- Width :: 'none' | integer(), +- Adjust :: 'left' | 'right', +- Precision :: 'none' | integer(), +- PadChar :: char(), +- Encoding :: 'unicode' | 'latin1', +- Strings :: boolean() +- }. +- + %%---------------------------------------------------------------------- + + %% Interface calls to sub-modules. +@@ -170,31 +157,6 @@ format(Format, Args) -> + Other + end. + +--spec scan_format(Format, Data) -> FormatList when +- Format :: io:format(), +- Data :: [term()], +- FormatList :: [char() | format_spec()]. +- +-scan_format(Format, Args) -> +- try io_lib_format:scan(Format, Args) +- catch +- _:_ -> erlang:error(badarg, [Format, Args]) +- end. +- +--spec unscan_format(FormatList) -> {Format, Data} when +- FormatList :: [char() | format_spec()], +- Format :: io:format(), +- Data :: [term()]. +- +-unscan_format(FormatList) -> +- io_lib_format:unscan(FormatList). +- +--spec build_text(FormatList) -> chars() when +- FormatList :: [char() | format_spec()]. +- +-build_text(FormatList) -> +- io_lib_format:build(FormatList). +- + -spec print(Term) -> chars() when + Term :: term(). + +diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl +index 37b47d7..56e15a1 100644 +--- a/lib/stdlib/src/io_lib_format.erl ++++ b/lib/stdlib/src/io_lib_format.erl +@@ -1,7 +1,7 @@ + %% + %% %CopyrightBegin% + %% +-%% Copyright Ericsson AB 1996-2014. All Rights Reserved. ++%% Copyright Ericsson AB 1996-2013. 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 +@@ -20,9 +20,10 @@ + + %% Formatting functions of io library. + +--export([fwrite/2,fwrite_g/1,indentation/2,scan/2,unscan/1,build/1]). ++-export([fwrite/2,fwrite_g/1,indentation/2]). + +-%% Format the arguments in Args after string Format. Just generate ++%% fwrite(Format, ArgList) -> string(). ++%% Format the arguments in ArgList after string Format. Just generate + %% an error if there is an error in the arguments. + %% + %% To do the printing command correctly we need to calculate the +@@ -36,83 +37,15 @@ + %% and it also splits the handling of the control characters into two + %% parts. + +--spec fwrite(Format, Data) -> FormatList when +- Format :: io:format(), +- Data :: [term()], +- FormatList :: [char() | io_lib:format_spec()]. +- ++fwrite(Format, Args) when is_atom(Format) -> ++ fwrite(atom_to_list(Format), Args); ++fwrite(Format, Args) when is_binary(Format) -> ++ fwrite(binary_to_list(Format), Args); + fwrite(Format, Args) -> +- build(scan(Format, Args)). +- +-%% Build the output text for a pre-parsed format list. +- +--spec build(FormatList) -> io_lib:chars() when +- FormatList :: [char() | io_lib:format_spec()]. +- +-build(Cs) -> ++ Cs = collect(Format, Args), + Pc = pcount(Cs), + build(Cs, Pc, 0). + +-%% Parse all control sequences in the format string. +- +--spec scan(Format, Data) -> FormatList when +- Format :: io:format(), +- Data :: [term()], +- FormatList :: [char() | io_lib:format_spec()]. +- +-scan(Format, Args) when is_atom(Format) -> +- scan(atom_to_list(Format), Args); +-scan(Format, Args) when is_binary(Format) -> +- scan(binary_to_list(Format), Args); +-scan(Format, Args) -> +- collect(Format, Args). +- +-%% Revert a pre-parsed format list to a plain character list and a +-%% list of arguments. +- +--spec unscan(FormatList) -> {Format, Data} when +- FormatList :: [char() | io_lib:format_spec()], +- Format :: io:format(), +- Data :: [term()]. +- +-unscan(Cs) -> +- {print(Cs), args(Cs)}. +- +-args([{_C,As,_F,_Ad,_P,_Pad,_Enc,_Str} | Cs]) -> +- As ++ args(Cs); +-args([_C | Cs]) -> +- args(Cs); +-args([]) -> +- []. +- +-print([{C,_As,F,Ad,P,Pad,Enc,Str} | Cs ]) -> +- print(C, F, Ad, P, Pad, Enc, Str) ++ print(Cs); +-print([C | Cs]) -> +- [C | print(Cs)]; +-print([]) -> +- []. +- +-print(C, F, Ad, P, Pad, Encoding, Strings) -> +- [$~] ++ print_field_width(F, Ad) ++ print_precision(P) ++ +- print_pad_char(Pad) ++ print_encoding(Encoding) ++ +- print_strings(Strings) ++ [C]. +- +-print_field_width(none, _Ad) -> ""; +-print_field_width(F, left) -> integer_to_list(-F); +-print_field_width(F, right) -> integer_to_list(F). +- +-print_precision(none) -> ""; +-print_precision(P) -> [$. | integer_to_list(P)]. +- +-print_pad_char($\s) -> ""; % default, no need to make explicit +-print_pad_char(Pad) -> [$., Pad]. +- +-print_encoding(unicode) -> "t"; +-print_encoding(latin1) -> "". +- +-print_strings(false) -> "l"; +-print_strings(true) -> "". +- + collect([$~|Fmt0], Args0) -> + {C,Fmt1,Args1} = collect_cseq(Fmt0, Args0), + [C|collect(Fmt1, Args1)]; +@@ -208,7 +141,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); + pcount([_|Cs], Acc) -> pcount(Cs, Acc); + pcount([], Acc) -> Acc. + +-%% build([Control], Pc, Indentation) -> io_lib:chars(). ++%% build([Control], Pc, Indentation) -> string(). + %% Interpret the control structures. Count the number of print + %% remaining and only calculate indentation when necessary. Must also + %% be smart when calculating indentation for characters in format. +@@ -229,14 +162,10 @@ decr_pc($p, Pc) -> Pc - 1; + decr_pc($P, Pc) -> Pc - 1; + decr_pc(_, Pc) -> Pc. + +- ++%% indentation(String, Indentation) -> Indentation. + %% Calculate the indentation of the end of a string given its start + %% indentation. We assume tabs at 8 cols. + +--spec indentation(String, StartIndent) -> integer() when +- String :: io_lib:chars(), +- StartIndent :: integer(). +- + indentation([$\n|Cs], _I) -> indentation(Cs, 0); + indentation([$\t|Cs], I) -> indentation(Cs, ((I + 8) div 8) * 8); + indentation([C|Cs], I) when is_integer(C) -> +@@ -437,6 +366,7 @@ float_data([D|Cs], Ds) when D >= $0, D =< $9 -> + float_data([_|Cs], Ds) -> + float_data(Cs, Ds). + ++%% fwrite_g(Float) + %% Writes the shortest, correctly rounded string that converts + %% to Float when read back with list_to_float/1. + %% +@@ -444,8 +374,6 @@ float_data([_|Cs], Ds) -> + %% in Proceedings of the SIGPLAN '96 Conference on Programming + %% Language Design and Implementation. + +--spec fwrite_g(float()) -> string(). +- + fwrite_g(0.0) -> + "0.0"; + fwrite_g(Float) when is_float(Float) -> +@@ -714,7 +642,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) + term([Prefix|S], F, Adj, none, Pad) + end. + +-%% char(Char, Field, Adjust, Precision, PadChar) -> chars(). ++%% char(Char, Field, Adjust, Precision, PadChar) -> string(). + + char(C, none, _Adj, none, _Pad) -> [C]; + char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch b/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch new file mode 100644 index 0000000..3e817d5 --- /dev/null +++ b/otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch @@ -0,0 +1,34 @@ +From: Peter Lemenkov +Date: Thu, 14 Jul 2016 17:51:16 +0300 +Subject: [PATCH] Respect -proto_dist switch while connection to EPMD + +Signed-off-by: Peter Lemenkov + +diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl +index 21a3dec..0ba695c 100644 +--- a/lib/kernel/src/erl_epmd.erl ++++ b/lib/kernel/src/erl_epmd.erl +@@ -107,6 +107,10 @@ names1(HostName) -> + + register_node(Name, PortNo) -> + register_node(Name, PortNo, inet). ++register_node(Name, PortNo, inet_tcp) -> ++ register_node(Name, PortNo, inet); ++register_node(Name, PortNo, inet6_tcp) -> ++ register_node(Name, PortNo, inet6); + register_node(Name, PortNo, Family) -> + gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity). + +diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl +index 0739cf3..c64ddc8 100644 +--- a/lib/kernel/src/inet_tcp_dist.erl ++++ b/lib/kernel/src/inet_tcp_dist.erl +@@ -72,7 +72,7 @@ gen_listen(Driver, Name) -> + {ok, Socket} -> + TcpAddress = get_tcp_address(Driver, Socket), + {_,Port} = TcpAddress#net_address.address, +- case erl_epmd:register_node(Name, Port) of ++ case erl_epmd:register_node(Name, Port, Driver) of + {ok, Creation} -> + {ok, {Socket, TcpAddress, Creation}}; + Error -> diff --git a/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch b/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch new file mode 100644 index 0000000..9514c34 --- /dev/null +++ b/otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch @@ -0,0 +1,38 @@ +From: Dan Gudmundsson +Date: Thu, 17 Mar 2016 10:23:41 +0100 +Subject: [PATCH] mnesia: Send mnesia_down messages to waiting transactions + +Mnesia didn't forward mnesia_down to transactions which where already +decided to be aborted, but that could lead to hanging transactions +still waiting for messages from the node which had stopped. + +diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl +index 17af0ca..329192e 100644 +--- a/lib/mnesia/src/mnesia_tm.erl ++++ b/lib/mnesia/src/mnesia_tm.erl +@@ -1714,13 +1714,10 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) -> + ?eval_debug_fun({?MODULE, commit_participant, undo_prepare}, + [{tid, Tid}]); + +- {'EXIT', _, _} -> ++ {'EXIT', _MnesiaTM, Reason} -> ++ reply(Coord, {do_abort, Tid, self(), {bad_commit,Reason}}), + mnesia_recover:log_decision(D#decision{outcome = aborted}), +- ?eval_debug_fun({?MODULE, commit_participant, exit_log_abort}, +- [{tid, Tid}]), +- mnesia_schema:undo_prepare_commit(Tid, C0), +- ?eval_debug_fun({?MODULE, commit_participant, exit_undo_prepare}, +- [{tid, Tid}]); ++ mnesia_schema:undo_prepare_commit(Tid, C0); + + Msg -> + verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n", +@@ -2236,8 +2233,6 @@ reconfigure_coordinators(N, [{Tid, [Store | _]} | Coordinators]) -> + true -> + send_mnesia_down(Tid, Store, N) + end; +- aborted -> +- ignore; % avoid spurious mnesia_down messages + _ -> + %% Tell the coordinator about the mnesia_down + send_mnesia_down(Tid, Store, N) From 94ed2172da57885c8e1e996ba443ad4bccc14df4 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 9 Nov 2016 19:59:54 +0300 Subject: [PATCH 13/13] Fix for doc generation with OpenJDK 1.8.0 Signed-off-by: Peter Lemenkov --- erlang.spec | 5 + otp-0029-Fix-a-few-javadoc-errors.patch | 139 ++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 otp-0029-Fix-a-few-javadoc-errors.patch diff --git a/erlang.spec b/erlang.spec index e117c20..a89b8fb 100644 --- a/erlang.spec +++ b/erlang.spec @@ -140,6 +140,9 @@ Patch27: otp-0027-Respect-proto_dist-switch-while-connection-to-EPMD.patch # Fedora specific patch # mnesia: Send mnesia_down messages to waiting transactions Patch28: otp-0028-mnesia-Send-mnesia_down-messages-to-waiting-transact.patch +# Fedora specific patch +# Fix a few javadoc errors +Patch29: otp-0029-Fix-a-few-javadoc-errors.patch # end of autogenerated patch tag list BuildRequires: lksctp-tools-devel @@ -1035,6 +1038,7 @@ Erlang mode for XEmacs (source lisp files). %patch26 -p1 -b .Revert_Remove_unused_code_in_error_logger_handlers %patch27 -p1 -b .Respect_proto_dist_switch_while_connection_to_EPMD %patch28 -p1 -b .mnesia_Send_mnesia_down_messages_to_waiting_transact +%patch29 -p1 -b .Fix_a_few_javadoc_errors # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2370,6 +2374,7 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ - Respect -proto_dist switch while connecting to EPMD - Cherry-picked fix for OTP-13423. Mnesia transactions could hang while waiting on a response from a node who had stopped. +- Fix for doc generation with OpenJDK 1.8.0 * Thu Apr 7 2016 John Eckersberg - R16B-03.17 - Revert "Enable error_logger depth fine tuning" (rhbz#1324922) diff --git a/otp-0029-Fix-a-few-javadoc-errors.patch b/otp-0029-Fix-a-few-javadoc-errors.patch new file mode 100644 index 0000000..c808303 --- /dev/null +++ b/otp-0029-Fix-a-few-javadoc-errors.patch @@ -0,0 +1,139 @@ +From: Anthony Ramine +Date: Sun, 8 Jun 2014 12:37:10 +0200 +Subject: [PATCH] Fix a few javadoc errors +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reported-by: Boris Mühmer + +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java +index 9ba6a4a..7aa30ee 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java +@@ -266,7 +266,7 @@ public abstract class AbstractConnection extends Thread { + * + * @param dest + * the Erlang PID of the remote process. +- * @param msg ++ * @param payload + * the encoded message to send. + * + * @exception java.io.IOException +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java +index 8e8bd47..e7a9d10 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java +@@ -404,7 +404,7 @@ public class OtpConnection extends AbstractConnection { + * + * @param dest + * the Erlang PID of the remote process. +- * @param msg ++ * @param payload + * the encoded message to send. + * + * @exception java.io.IOException +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java +index 7e3e2a7..84b1355 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java +@@ -51,8 +51,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable, + /** + * Create an Erlang integer from the given value. + * +- * @param val +- * the long value to use. ++ * @param v ++ * the big integer value to use. + */ + public OtpErlangLong(final BigInteger v) { + if (v == null) { +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java +index fe81ce3..f75e435 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java +@@ -162,7 +162,7 @@ public class OtpErlangPid extends OtpErlangObject implements Serializable, + * Determine if two PIDs are equal. PIDs are equal if their components are + * equal. + * +- * @param port ++ * @param o + * the other PID to compare to. + * + * @return true if the PIDs are equal, false otherwise. +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java +index 6766b52..a5e202c 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java +@@ -41,8 +41,6 @@ public class OtpErlangString extends OtpErlangObject implements Serializable, + + /** + * Create an Erlang string from a list of integers. +- * +- * @return an Erlang string with Unicode code units. + * + * @throws OtpErlangException + * for non-proper and non-integer lists. +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java +index 0fd93b0..4a4a1e7 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java +@@ -69,6 +69,7 @@ package com.ericsson.otp.erlang; + * notify other parties in a timely manner. + *

+ * ++ *

+ * When retrieving messages from a mailbox that has received an exit signal, an + * {@link OtpErlangExit OtpErlangExit} exception will be raised. Note that the + * exception is queued in the mailbox along with other messages, and will not be +@@ -420,7 +421,6 @@ public class OtpMbox { + + /** + * Equivalent to exit(new OtpErlangAtom(reason)). +- *

+ * + * @see #exit(OtpErlangObject) + */ +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java +index 6f507bf..31a5d0f 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java +@@ -30,14 +30,14 @@ package com.ericsson.otp.erlang; + *

+ * + *

+- * The header information that is available is as follows: ++ * The header information that is available is as follows:

    + *
  • a tag indicating the type of message + *
  • the intended recipient of the message, either as a + * {@link OtpErlangPid pid} or as a String, but never both. + *
  • (sometimes) the sender of the message. Due to some eccentric + * characteristics of the Erlang distribution protocol, not all messages have + * information about the sending process. In particular, only messages whose tag +- * is {@link OtpMsg#regSendTag regSendTag} contain sender information. ++ * is {@link OtpMsg#regSendTag regSendTag} contain sender information.
+ * + *

+ * Message are sent using the Erlang external format (see separate +diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java +index 78f47aa..fd4eba3 100644 +--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java ++++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java +@@ -202,7 +202,7 @@ public class OtpOutputStream extends ByteArrayOutputStream { + /** + * Write an array of bytes to the stream. + * +- * @param buf ++ * @param bytes + * the array of bytes to write. + * + */ +@@ -637,7 +637,7 @@ public class OtpOutputStream extends ByteArrayOutputStream { + * Write a positive short to the stream. The short is interpreted as a two's + * complement unsigned short even if it is negative. + * +- * @param s ++ * @param us + * the short to use. + */ + public void write_ushort(final short us) {