diff --git a/erlang.spec b/erlang.spec index dbac58a..a89b8fb 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}.18%{?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,56 @@ 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 +# 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 +# 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 +# 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 +# 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 +# Fedora specific patch +# Fix a few javadoc errors +Patch29: otp-0029-Fix-a-few-javadoc-errors.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 +152,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 +161,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 +176,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 +438,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 @@ -462,10 +495,16 @@ 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 +# 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 +597,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 +827,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 +866,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 +892,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 +964,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 +1001,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 +1023,22 @@ 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 +%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 +%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 +%patch29 -p1 -b .Fix_a_few_javadoc_errors # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -1005,9 +1061,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 +1072,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 +1098,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 +1110,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 +1138,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 +1230,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 +1442,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 +1468,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 +1489,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 +1592,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 +2350,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 +2367,48 @@ rm -rf $RPM_BUILD_ROOT %files -n xemacs-erlang-el %{_xemacs_sitelispdir}/erlang/*.el -%endif %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. +- 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) + +* 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 + +* 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) + +* 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) + +* 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) + +* 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/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 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. diff --git a/otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch b/otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch new file mode 100644 index 0000000..18645fb --- /dev/null +++ b/otp-0020-R16B03-1-backport-of-TLS-1.0-padding-check.patch @@ -0,0 +1,566 @@ +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 + +diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml +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()) -> +

+
+ ++ {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 6f84830..8c12141 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 3c5c7e9..dd99d10 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>>}. ++ 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); 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..581f39d --- /dev/null +++ b/otp-0022-kernel-inet6_tcp_dist-reuse-inet_tcp_dist-code.patch @@ -0,0 +1,891 @@ +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. + +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 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..7d46bae --- /dev/null +++ b/otp-0023-epmd-support-IPv6-node-registration.patch @@ -0,0 +1,737 @@ +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. + +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 ++++ b/erts/doc/src/epmd.xml +@@ -36,7 +36,7 @@ + +

Erlang Port Mapper Daemon

+ +- ++ + +

Starts the port mapper daemon

+
+diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml +index 528a2d9..0b8f821 100644 +--- a/erts/doc/src/erl.xml ++++ b/erts/doc/src/erl.xml +@@ -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.

++ ++ inet_tcp ++ ++

TCP over IPv4 (the default)

++
++ inet_tls ++ ++

distribution over TLS/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 --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 +- 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 --git a/erts/epmd/src/epmd_cli.c b/erts/epmd/src/epmd_cli.c +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) + 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(EPMD6) ++ 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 --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h +index 363923e..c6d9173 100644 +--- a/erts/epmd/src/epmd_int.h ++++ b/erts/epmd/src/epmd_int.h +@@ -47,6 +47,7 @@ + # ifndef WINDOWS_H_INCLUDES_WINSOCK2_H + # include + # endif ++# include + # 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 */ + +-#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(EPMD6) + +-#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 --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c +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*); + + 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 @@ void run(EpmdVars *g) + { + 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 @@ void run(EpmdVars *g) + 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; ++ ++ /* Always listen on the loopback. */ ++ SET_ADDR(iserv_addr[num_sockets],htonl(INADDR_LOOPBACK),sport); ++ num_sockets++; ++#if defined(EPMD6) ++ 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"); + 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; +-#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]; + +- 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(EPMD6) ++ 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(EPMD6) ++ SET_ADDR6(iserv_addr[num_sockets],in6addr_any,sport); ++ num_sockets++; ++#endif + } + #ifdef HAVE_SYSTEMD_SD_DAEMON_H + } +@@ -334,13 +354,39 @@ void run(EpmdVars *g) + #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(EPMD6) ++ 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"); ++ switch (errno) { ++ case EAFNOSUPPORT: ++ case EPROTONOSUPPORT: ++ continue; ++ default: ++ dbg_perror(g,"error opening stream socket"); ++ epmd_cleanup_exit(g,1); ++ } ++ } ++ g->listenfd[bound++] = 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); + } +- g->listenfd[i] = listensock[i]; +- ++#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 @@ void run(EpmdVars *g) + 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) + { +@@ -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" +@@ -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) { +- 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 +1061,7 @@ static int conn_open(EpmdVars *g,int fd) + 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 +1069,7 @@ static int conn_open(EpmdVars *g,int fd) + 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 +1087,60 @@ static int conn_open(EpmdVars *g,int fd) + 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(EPMD6) ++ 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(EPMD6) ++ 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(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(EPMD6) ++ return EPMD_FALSE; ++#endif ++} ++ + static int conn_close_fd(EpmdVars *g,int fd) + { + int i; +diff --git a/erts/epmd/test/epmd_SUITE.erl b/erts/epmd/test/epmd_SUITE.erl +index cc24a55..8dfc21f 100644 +--- a/erts/epmd/test/epmd_SUITE.erl ++++ b/erts/epmd/test/epmd_SUITE.erl +@@ -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 @@ register_name(Config) when is_list(Config) -> + ?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,14 @@ register_node(Name) -> + 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) -> +- 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) -> ++ 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 +1150,9 @@ send_direct(Sock, Bytes) -> + 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 --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 ++++ b/lib/kernel/src/erl_epmd.erl +@@ -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 @@ names1(HostName) -> + + + 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 @@ init(_) -> + -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 @@ 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). + +-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 = "", +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" 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) 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) { 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