aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c43
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml2
-rw-r--r--lib/asn1/doc/src/notes.xml24
-rw-r--r--lib/asn1/src/asn1_records.hrl12
-rw-r--r--lib/asn1/src/asn1ct.erl19
-rw-r--r--lib/asn1/src/asn1ct_check.erl350
-rw-r--r--lib/asn1/src/asn1ct_parser2.erl23
-rw-r--r--lib/asn1/src/asn1ct_tok.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE.erl18
-rw-r--r--lib/asn1/test/asn1_SUITE_data/InfObj.asn45
-rw-r--r--lib/asn1/test/asn1_SUITE_data/ParamBasic.asn122
-rw-r--r--lib/asn1/test/asn1_test_lib.erl51
-rw-r--r--lib/asn1/test/ber_decode_error.erl14
-rw-r--r--lib/asn1/test/error_SUITE.erl17
-rw-r--r--lib/asn1/test/testInfObj.erl36
-rw-r--r--lib/asn1/test/testParamBasic.erl3
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/Makefile1
-rw-r--r--lib/common_test/doc/src/notes.xml48
-rw-r--r--lib/common_test/doc/src/ref_man.xml1
-rw-r--r--lib/common_test/src/Makefile3
-rw-r--r--lib/common_test/src/common_test.app.src4
-rw-r--r--lib/common_test/src/ct_cover.erl18
-rw-r--r--lib/common_test/src/ct_property_test.erl186
-rw-r--r--lib/common_test/test/ct_cover_SUITE.erl17
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/notes.xml16
-rw-r--r--lib/compiler/src/sys_core_fold.erl14
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/crypto.c22
-rw-r--r--lib/crypto/doc/src/crypto.xml2
-rw-r--r--lib/crypto/doc/src/notes.xml17
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/src/Makefile2
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml5
-rw-r--r--lib/dialyzer/doc/src/notes.xml30
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl11
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl18
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl43
-rw-r--r--lib/dialyzer/test/dialyzer_SUITE.erl39
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug5.erl10
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring3
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/limit.erl20
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl8
-rw-r--r--lib/dialyzer/test/underspecs_SUITE_data/results/arr4
-rw-r--r--lib/dialyzer/test/underspecs_SUITE_data/src/arr.erl41
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml59
-rw-r--r--lib/diameter/include/diameter_gen.hrl88
-rw-r--r--lib/diameter/src/base/diameter_codec.erl30
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl2
-rw-r--r--lib/diameter/src/base/diameter_service.erl65
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl93
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl33
-rw-r--r--lib/diameter/src/diameter.appup.src22
-rw-r--r--lib/diameter/src/info/diameter_dbg.erl38
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/doc/src/notes.xml16
-rw-r--r--lib/edoc/src/edoc_layout.erl17
-rw-r--r--lib/edoc/src/edoc_types.erl2
-rw-r--r--lib/edoc/test/edoc_SUITE_data/map_module.erl42
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml4
-rw-r--r--lib/eldap/doc/src/notes.xml31
-rw-r--r--lib/eldap/src/eldap.erl65
-rw-r--r--lib/eldap/test/Makefile1
-rw-r--r--lib/eldap/test/README2
-rw-r--r--lib/eldap/test/eldap_connections_SUITE.erl126
-rw-r--r--lib/eldap/vsn.mk3
-rw-r--r--lib/erl_docgen/doc/src/notes.xml18
-rw-r--r--lib/erl_docgen/src/docgen_otp_specs.erl22
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/configure.in20
-rw-r--r--lib/erl_interface/doc/src/notes.xml32
-rw-r--r--lib/erl_interface/ebin/.gitignore0
-rw-r--r--lib/erl_interface/src/Makefile.in26
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c2
-rw-r--r--lib/erl_interface/src/erl_interface.app.src32
-rw-r--r--lib/erl_interface/src/legacy/erl_connect.c2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/eunit/doc/src/notes.xml15
-rw-r--r--lib/eunit/src/Makefile5
-rw-r--r--lib/eunit/src/eunit_data.erl9
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/hipe/cerl/cerl_prettypr.erl17
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl20
-rw-r--r--lib/hipe/cerl/erl_types.erl4
-rw-r--r--lib/hipe/doc/src/notes.xml22
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/ic/c_src/oe_ei_encode_pid.c2
-rw-r--r--lib/ic/doc/src/notes.xml17
-rw-r--r--lib/ic/vsn.mk2
-rw-r--r--lib/inets/doc/src/httpc.xml2
-rw-r--r--lib/inets/doc/src/httpd.xml10
-rw-r--r--lib/inets/doc/src/notes.xml53
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl2
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl9
-rw-r--r--lib/inets/src/inets_app/inets.appup.src8
-rw-r--r--lib/inets/test/ftp_property_test_SUITE.erl52
-rw-r--r--lib/inets/test/httpd_SUITE.erl181
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl2
-rw-r--r--lib/inets/test/httpd_test_lib.erl11
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl31
-rw-r--r--lib/inets/test/property_test/README12
-rw-r--r--lib/inets/test/property_test/ftp_simple_client_server.erl306
-rw-r--r--lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf26
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml50
-rw-r--r--lib/jinterface/ebin/.gitignore0
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java144
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java10
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile19
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java13
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java13
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java16
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java10
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java13
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java28
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java20
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java13
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java6
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java10
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java10
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java8
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java47
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java34
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java28
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java11
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java67
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java35
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/jinterface.app.src32
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl18
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/FunEquals.java73
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/GetNames.java6
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Makefile.src3
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java18
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java9
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java17
-rw-r--r--lib/jinterface/test/nc_SUITE_data/echo_server.java3
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/error_logger.xml2
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml7
-rw-r--r--lib/kernel/doc/src/gen_udp.xml7
-rw-r--r--lib/kernel/doc/src/notes.xml35
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/erl_epmd.erl15
-rw-r--r--lib/kernel/src/inet.erl33
-rw-r--r--lib/kernel/src/net_adm.erl9
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl41
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src9
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c60
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl2
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl4
-rw-r--r--lib/kernel/test/kernel_SUITE.erl18
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/configure.in20
-rw-r--r--lib/megaco/doc/src/notes.xml19
-rw-r--r--lib/megaco/src/app/megaco.appup.src10
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/notes.xml37
-rw-r--r--lib/mnesia/src/mnesia_controller.erl15
-rw-r--r--lib/mnesia/src/mnesia_frag.erl2
-rw-r--r--lib/mnesia/src/mnesia_loader.erl3
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml15
-rw-r--r--lib/observer/src/observer_wx.erl9
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/c_src/odbcserver.c19
-rw-r--r--lib/odbc/c_src/odbcserver.h4
-rw-r--r--lib/odbc/configure.in20
-rw-r--r--lib/odbc/doc/src/error_handling.xml2
-rw-r--r--lib/odbc/doc/src/notes.xml25
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl37
-rw-r--r--lib/odbc/vsn.mk2
-rw-r--r--lib/orber/doc/src/notes.xml17
-rw-r--r--lib/orber/src/orber_iiop_outproxy.erl37
-rw-r--r--lib/orber/src/orber_iiop_pm.erl34
-rw-r--r--lib/orber/src/orber_socket.erl41
-rw-r--r--lib/orber/vsn.mk3
-rw-r--r--lib/os_mon/doc/src/disksup.xml11
-rw-r--r--lib/os_mon/doc/src/notes.xml17
-rw-r--r--lib/os_mon/src/cpu_sup.erl12
-rw-r--r--lib/os_mon/src/disksup.erl19
-rw-r--r--lib/os_mon/test/disksup_SUITE.erl38
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/ose/doc/src/notes.xml59
-rw-r--r--lib/ose/doc/src/ose_intro.xml4
-rw-r--r--lib/ose/vsn.mk2
-rw-r--r--lib/otp_mibs/src/Makefile2
-rw-r--r--lib/percept/src/Makefile3
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn110
-rw-r--r--lib/public_key/doc/src/cert_records.xml7
-rw-r--r--lib/public_key/doc/src/notes.xml16
-rw-r--r--lib/public_key/doc/src/public_key.xml57
-rw-r--r--lib/public_key/doc/src/public_key_records.xml68
-rw-r--r--lib/public_key/src/pubkey_pbe.erl76
-rw-r--r--lib/public_key/src/pubkey_pem.erl22
-rw-r--r--lib/public_key/src/public_key.erl43
-rw-r--r--lib/public_key/test/pbe_SUITE.erl70
-rw-r--r--lib/public_key/test/pbe_SUITE_data/old_aes_128_cbc_enc_key.pem (renamed from lib/public_key/test/pbe_SUITE_data/aes_128_cbc_enc_key)0
-rw-r--r--lib/public_key/test/pbe_SUITE_data/pbes1_des_cbc_md5_enc_key.pem17
-rw-r--r--lib/public_key/test/pbe_SUITE_data/pbes2_des_cbc_enc_key.pem (renamed from lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem)0
-rw-r--r--lib/public_key/test/pbe_SUITE_data/pbes2_des_ede3_cbc_enc_key.pem (renamed from lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem)0
-rw-r--r--lib/public_key/test/pbe_SUITE_data/pbes2_rc2_cbc_enc_key.pem (renamed from lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem)0
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml12
-rw-r--r--lib/sasl/doc/src/notes.xml20
-rw-r--r--lib/sasl/test/sasl_SUITE.erl18
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/.gitignore3
-rw-r--r--lib/snmp/doc/src/notes.xml19
-rw-r--r--lib/snmp/doc/src/snmp_agent_config_files.xml56
-rw-r--r--lib/snmp/doc/src/snmp_agent_netif.xml34
-rw-r--r--lib/snmp/doc/src/snmp_manager_config_files.xml63
-rw-r--r--lib/snmp/doc/src/snmp_manager_netif.xml45
-rw-r--r--lib/snmp/doc/src/snmp_target_mib.xml22
-rw-r--r--lib/snmp/doc/src/snmpa_conf.xml66
-rw-r--r--lib/snmp/doc/src/snmpa_mpd.xml67
-rw-r--r--lib/snmp/doc/src/snmpa_network_interface_filter.xml40
-rw-r--r--lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml32
-rw-r--r--lib/snmp/doc/src/snmpm.xml14
-rw-r--r--lib/snmp/doc/src/snmpm_conf.xml8
-rw-r--r--lib/snmp/doc/src/snmpm_mpd.xml14
-rw-r--r--lib/snmp/doc/src/snmpm_network_interface.xml12
-rw-r--r--lib/snmp/doc/src/snmpm_network_interface_filter.xml34
-rw-r--r--lib/snmp/doc/src/snmpm_user.xml15
-rw-r--r--lib/snmp/src/agent/snmp_framework_mib.erl34
-rw-r--r--lib/snmp/src/agent/snmp_target_mib.erl48
-rw-r--r--lib/snmp/src/agent/snmpa_conf.erl38
-rw-r--r--lib/snmp/src/app/snmp.appup.src2
-rw-r--r--lib/snmp/src/manager/depend.mk5
-rw-r--r--lib/snmp/src/manager/snmpm_conf.erl8
-rw-r--r--lib/snmp/src/manager/snmpm_config.erl460
-rw-r--r--lib/snmp/src/manager/snmpm_net_if.erl681
-rw-r--r--lib/snmp/src/manager/snmpm_net_if_mt.erl1308
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl11
-rw-r--r--lib/snmp/src/misc/snmp_conf.erl8
-rw-r--r--lib/snmp/src/misc/snmp_config.erl46
-rw-r--r--lib/snmp/test/snmp_agent_test.erl29
-rw-r--r--lib/snmp/test/snmp_manager_config_test.erl2
-rw-r--r--lib/snmp/test/snmp_manager_test.erl23
-rw-r--r--lib/snmp/test/snmp_manager_user.erl15
-rw-r--r--lib/snmp/test/snmp_to_snmpnet_SUITE.erl180
-rw-r--r--lib/snmp/test/snmp_to_snmpnet_SUITE_data/snmpd.conf12
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/doc/src/notes.xml100
-rw-r--r--lib/ssh/doc/src/ssh.xml4
-rw-r--r--lib/ssh/src/Makefile2
-rw-r--r--lib/ssh/src/ssh.appup.src16
-rw-r--r--lib/ssh/src/ssh.erl21
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl13
-rw-r--r--lib/ssh/src/ssh_cli.erl12
-rw-r--r--lib/ssh/src/ssh_connect.hrl3
-rw-r--r--lib/ssh/src/ssh_connection.erl5
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl86
-rw-r--r--lib/ssh/src/ssh_system_sup.erl8
-rw-r--r--lib/ssh/src/ssh_transport.erl107
-rw-r--r--lib/ssh/test/property_test/README12
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server.erl607
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_dsa13
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_rsa15
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key13
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key.pub11
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_encode_decode.erl397
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_subsys.erl63
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl108
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl290
-rw-r--r--lib/ssh/test/ssh_property_test_SUITE.erl107
-rw-r--r--lib/ssh/test/ssh_test_lib.erl3
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl164
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml42
-rw-r--r--lib/ssl/doc/src/ssl.xml28
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml2
-rw-r--r--lib/ssl/src/Makefile2
-rw-r--r--lib/ssl/src/ssl.appup.src10
-rw-r--r--lib/ssl/src/ssl.erl37
-rw-r--r--lib/ssl/src/ssl_certificate.erl113
-rw-r--r--lib/ssl/src/ssl_connection.erl4
-rw-r--r--lib/ssl/src/ssl_handshake.erl59
-rw-r--r--lib/ssl/src/ssl_internal.hrl1
-rw-r--r--lib/ssl/src/tls_connection.erl16
-rw-r--r--lib/ssl/test/erl_make_certs.erl6
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl184
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/CA.pem14
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/ec1.crt11
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/ec1.key8
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/ec2.crt11
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/ec2.key8
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/rsa1.crt20
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/rsa1.key51
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/rsa2.crt20
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE_data/rsa2.key51
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl197
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl16
-rw-r--r--lib/ssl/test/ssl_test_lib.erl37
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl75
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/maps.xml17
-rw-r--r--lib/stdlib/doc/src/notes.xml73
-rw-r--r--lib/stdlib/doc/src/string.xml6
-rw-r--r--lib/stdlib/doc/src/sys.xml5
-rw-r--r--lib/stdlib/src/dets.erl5
-rw-r--r--lib/stdlib/src/erl_eval.erl2
-rw-r--r--lib/stdlib/src/erl_parse.yrl14
-rw-r--r--lib/stdlib/src/erl_pp.erl10
-rw-r--r--lib/stdlib/src/erl_scan.erl4
-rw-r--r--lib/stdlib/src/filelib.erl2
-rw-r--r--lib/stdlib/src/gen_event.erl48
-rw-r--r--lib/stdlib/src/gen_fsm.erl49
-rw-r--r--lib/stdlib/src/gen_server.erl51
-rw-r--r--lib/stdlib/src/io_lib_format.erl2
-rw-r--r--lib/stdlib/src/maps.erl17
-rw-r--r--lib/stdlib/src/otp_internal.erl14
-rw-r--r--lib/stdlib/src/proc_lib.erl6
-rw-r--r--lib/stdlib/src/shell.erl8
-rw-r--r--lib/stdlib/src/stdlib.app.src2
-rw-r--r--lib/stdlib/src/stdlib.appup.src4
-rw-r--r--lib/stdlib/src/sys.erl10
-rw-r--r--lib/stdlib/test/dets_SUITE.erl6
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl28
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl246
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl1
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl39
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl41
-rw-r--r--lib/stdlib/test/io_SUITE.erl10
-rw-r--r--lib/stdlib/test/maps_SUITE.erl33
-rw-r--r--lib/stdlib/test/shell_SUITE.erl5
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl18
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/test_server/src/Makefile2
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/emacs/erlang-skels.el2
-rw-r--r--lib/tools/emacs/erlang.el3
-rw-r--r--lib/tools/src/lcnt.erl359
-rw-r--r--lib/tools/test/lcnt_SUITE.erl153
-rw-r--r--lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcntbin0 -> 601135 bytes
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/configure.in21
-rw-r--r--lib/wx/doc/src/notes.xml17
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/doc/src/motorcycles2html.erl17
361 files changed, 8968 insertions, 4203 deletions
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index 8a0e4b1cf0..53e3aa1678 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -941,16 +941,31 @@ static int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *
int maybe_ret;
unsigned int len = 0;
unsigned int lenoflen = 0;
- int indef = 0;
unsigned char *tmp_out_buff;
ERL_NIF_TERM term = 0, curr_head = 0;
if (((in_buf[*ib_index]) & 0x80) == ASN1_SHORT_DEFINITE_LENGTH) {
len = in_buf[*ib_index];
- } else if (in_buf[*ib_index] == ASN1_INDEFINITE_LENGTH
- )
- indef = 1;
- else /* long definite length */{
+ } else if (in_buf[*ib_index] == ASN1_INDEFINITE_LENGTH) {
+ (*ib_index)++;
+ curr_head = enif_make_list(env, 0);
+ if (*ib_index+1 >= in_buf_len) {
+ return ASN1_INDEF_LEN_ERROR;
+ }
+ while (!(in_buf[*ib_index] == 0 && in_buf[*ib_index + 1] == 0)) {
+ maybe_ret = ber_decode(env, &term, in_buf, ib_index, in_buf_len);
+ if (maybe_ret <= ASN1_ERROR) {
+ return maybe_ret;
+ }
+ curr_head = enif_make_list_cell(env, term, curr_head);
+ if (*ib_index+1 >= in_buf_len) {
+ return ASN1_INDEF_LEN_ERROR;
+ }
+ }
+ enif_make_reverse_list(env, curr_head, value);
+ (*ib_index) += 2; /* skip the indefinite length end bytes */
+ return ASN1_OK;
+ } else /* long definite length */{
lenoflen = (in_buf[*ib_index] & 0x7f); /*length of length */
if (lenoflen > (in_buf_len - (*ib_index + 1)))
return ASN1_LEN_ERROR;
@@ -965,23 +980,7 @@ static int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *
if (len > (in_buf_len - (*ib_index + 1)))
return ASN1_VALUE_ERROR;
(*ib_index)++;
- if (indef == 1) { /* in this case it is desireably to check that indefinite length
- end bytes exist in inbuffer */
- curr_head = enif_make_list(env, 0);
- while (!(in_buf[*ib_index] == 0 && in_buf[*ib_index + 1] == 0)) {
- if (*ib_index >= in_buf_len)
- return ASN1_INDEF_LEN_ERROR;
-
- if ((maybe_ret = ber_decode(env, &term, in_buf, ib_index, in_buf_len))
- <= ASN1_ERROR
- )
- return maybe_ret;
- curr_head = enif_make_list_cell(env, term, curr_head);
- }
- enif_make_reverse_list(env, curr_head, value);
- (*ib_index) += 2; /* skip the indefinite length end bytes */
- } else if (form == ASN1_CONSTRUCTED)
- {
+ if (form == ASN1_CONSTRUCTED) {
int end_index = *ib_index + len;
if (end_index > in_buf_len)
return ASN1_LEN_ERROR;
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index 020e58c615..8b33497dd3 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -1390,7 +1390,7 @@ GENERAL-PROCEDURES GENERAL-PROCEDURE ::= {
instance, if a Type is used in a definition with certain
purpose, one want the type-name to express the intention. This
can be done with parameterization.</p>
- <p>When many types (or an other ASN.1 entity) only differs in some
+ <p>When many types (or another ASN.1 entity) only differs in some
minor cases, but the structure of the types are similar, only
one general type can be defined and the differences may be supplied
through parameters. </p>
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 11de9ad98f..a7032737bd 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -31,6 +31,30 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 3.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Several problems where the ASN.1 compiler would crash
+ when attempting to compile correct specifications have
+ been corrected.</p>
+ <p>
+ Own Id: OTP-12125</p>
+ </item>
+ <item>
+ <p>
+ Robustness when decoding incorrect BER messages has been
+ improved.</p>
+ <p>
+ Own Id: OTP-12145</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 3.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl
index 396ba0fcfa..6c1cf1b12a 100644
--- a/lib/asn1/src/asn1_records.hrl
+++ b/lib/asn1/src/asn1_records.hrl
@@ -37,7 +37,7 @@
-record('ObjectClassFieldType',{classname,class,fieldname,type}).
-record(typedef,{checked=false,pos,name,typespec}).
--record(classdef,{checked=false,pos,name,typespec}).
+-record(classdef, {checked=false,pos,name,module,typespec}).
-record(valuedef,{checked=false,pos,name,type,value,module}).
-record(ptypedef,{checked=false,pos,name,args,typespec}).
-record(pvaluedef,{checked=false,pos,name,args,type,value}).
@@ -45,7 +45,6 @@
-record(pobjectdef,{checked=false,pos,name,args,class,def}).
-record(pobjectsetdef,{checked=false,pos,name,args,class,def}).
--record(identifier,{pos,val}).
-record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no,
'ContainedSubtype'=no, 'TypeConstraint'=no,'InnerSubtyping'=no,e=no,'Other'=no}).
-record(simpletableattributes,{objectsetname,c_name,c_index,usedclassfield,
@@ -73,6 +72,15 @@
% Externalvaluereference -> modulename '.' typename
-record('Externalvaluereference',{pos,module,value}).
+%% Used to hold a tag for a field in a SEQUENCE/SET. It can also
+%% be used for identifiers in OBJECT IDENTIFIER values, since the
+%% parser cannot always distinguish a SEQUENCE with one element from
+%% an OBJECT IDENTIFIER.
+-record(seqtag,
+ {pos :: integer(),
+ module :: atom(),
+ val :: atom()}).
+
-record(state,{module,mname,type,tname,value,vname,erule,parameters=[],
inputmodules,abscomppath=[],recordtopname=[],options,
sourcedir}).
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index 8470e5a1b4..df341e5aab 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -43,7 +43,7 @@
add_tobe_refed_func/1,add_generated_refed_func/1,
maybe_rename_function/3,current_sindex/0,
set_current_sindex/1,maybe_saved_sindex/2,
- parse_and_save/2,verbose/3,warning/3,warning/4,error/3]).
+ parse_and_save/2,verbose/3,warning/3,warning/4,error/3,format_error/1]).
-export([get_bit_string_format/0,use_legacy_types/0]).
-include("asn1_records.hrl").
@@ -143,7 +143,8 @@ parse_and_save_passes() ->
{pass,save,fun save_pass/1}].
common_passes() ->
- [{pass,check,fun check_pass/1},
+ [{iff,parse,{pass,parse_listing,fun parse_listing/1}},
+ {pass,check,fun check_pass/1},
{iff,abs,{pass,abs_listing,fun abs_listing/1}},
{pass,generate,fun generate_pass/1},
{unless,noobj,{pass,compile,fun compile_pass/1}}].
@@ -243,6 +244,16 @@ save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) ->
asn1_db:dbsave(DbFile,M#module.name),
{ok,St}.
+parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
+ OutFile = OutFile0 ++ ".parse",
+ case file:write_file(OutFile, io_lib:format("~p\n", [Code])) of
+ ok ->
+ done;
+ {error,Reason} ->
+ Error = {write_error,OutFile,Reason},
+ {error,St#st{error=[{structured_error,{OutFile0,none},?MODULE,Error}]}}
+ end.
+
abs_listing(#st{code={M,_},outfile=OutFile}) ->
pretty2(M#module.name, OutFile++".abs"),
done.
@@ -2430,6 +2441,10 @@ verbose(Format, Args, S) ->
ok
end.
+format_error({write_error,File,Reason}) ->
+ io_lib:format("writing output file ~s failed: ~s",
+ [File,file:format_error(Reason)]).
+
is_error(S) when is_record(S, state) ->
is_error(S#state.options);
is_error(O) ->
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index e788aa5c6c..5d8740b92e 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -91,7 +91,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
save_asn1db_uptodate(S,S#state.erule,S#state.mname),
put(top_module,S#state.mname),
- _ = checkp(S, ParameterizedTypes), %must do this before the templates are used
+ ParamError = checkp(S, ParameterizedTypes), %must do this before the templates are used
%% table to save instances of parameterized objects,object sets
asn1ct_table:new(parameterized_objects),
@@ -160,8 +160,10 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
Exporterror = check_exports(S,S#state.module),
ImportError = check_imports(S,S#state.module),
- case {Terror3,Verror5,Cerror,Oerror,Exporterror,ImportError} of
- {[],[],[],[],[],[]} ->
+ AllErrors = lists:flatten([ParamError,Terror3,Verror5,Cerror,
+ Oerror,Exporterror,ImportError]),
+ case AllErrors of
+ [] ->
ContextSwitchTs = context_switch_in_spec(),
InstanceOf = instance_of_in_spec(S#state.mname),
NewTypes = lists:subtract(Types,AddClasses) ++ ContextSwitchTs
@@ -175,8 +177,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
lists:subtract(NewObjects,ExclO)++InlinedObjects,
lists:subtract(NewObjectSets,ExclOS)++ParObjectSetNames}};
_ ->
- {error,lists:flatten([Terror3,Verror5,Cerror,
- Oerror,Exporterror,ImportError])}
+ {error,AllErrors}
end.
context_switch_in_spec() ->
@@ -549,14 +550,10 @@ check_class(S = #state{mname=M,tname=T},ClassSpec)
#objectclass{fields=Def}; % in case of recursive definitions
Tref = #'Externaltypereference'{type=TName} ->
{MName,RefType} = get_referenced_type(S,Tref),
- case is_class(S,RefType) of
- true ->
- NewState = update_state(S#state{type=RefType,
- tname=TName},MName),
- check_class(NewState,get_class_def(S,RefType));
- _ ->
- error({class,{internal_error,RefType},S})
- end;
+ #classdef{} = CD = get_class_def(S, RefType),
+ NewState = update_state(S#state{type=RefType,
+ tname=TName}, MName),
+ check_class(NewState, CD);
{pt,ClassRef,Params} ->
%% parameterized class
{_,PClassDef} = get_referenced_type(S,ClassRef),
@@ -950,6 +947,8 @@ prepare_objset(ObjDef={object,definedsyntax,_ObjFields}) ->
{set,[ObjDef],false};
prepare_objset({ObjDef=#type{},Ext}) when is_list(Ext) ->
{set,[ObjDef|Ext],true};
+prepare_objset({#type{}=Type,#type{}=Ext}) ->
+ {set,[Type,Ext],true};
prepare_objset(Ret) ->
Ret.
@@ -1277,10 +1276,25 @@ get_fieldname_element(_S,Def,[{_RefType,_FieldName}|_RestFName])
check_fieldname_element(S,{value,{_,Def}}) ->
check_fieldname_element(S,Def);
-check_fieldname_element(S,TDef) when is_record(TDef,typedef) ->
- check_type(S,TDef,TDef#typedef.typespec);
-check_fieldname_element(S,VDef) when is_record(VDef,valuedef) ->
- check_value(S,VDef);
+check_fieldname_element(S, #typedef{typespec=Ts}=TDef) ->
+ case Ts of
+ #'Object'{} ->
+ check_object(S, TDef, Ts);
+ _ ->
+ check_type(S, TDef, Ts)
+ end;
+check_fieldname_element(S, #valuedef{}=VDef) ->
+ try
+ check_value(S, VDef)
+ catch
+ throw:{objectdef} ->
+ #valuedef{checked=C,pos=Pos,name=N,type=Type,
+ value=Def} = VDef,
+ ClassName = Type#type.def,
+ NewSpec = #'Object'{classname=ClassName,def=Def},
+ NewDef = #typedef{checked=C,pos=Pos,name=N,typespec=NewSpec},
+ check_fieldname_element(S, NewDef)
+ end;
check_fieldname_element(S,Eref)
when is_record(Eref,'Externaltypereference');
is_record(Eref,'Externalvaluereference') ->
@@ -1803,12 +1817,10 @@ convert_to_defaultfield(S,ObjFieldName,[OFS|RestOFS],CField)->
FieldName);
ValSetting = #valuedef{} ->
ValSetting;
- ValSetting = {'CHOICE',{Alt,_ChVal}} when is_atom(Alt) ->
- #valuedef{type=element(3,CField),
- value=ValSetting,
- module=S#state.mname};
ValSetting ->
- #identifier{val=ValSetting}
+ #valuedef{type=element(3,CField),
+ value=ValSetting,
+ module=S#state.mname}
end,
?dbg("fixedtypevaluefield ValRef: ~p~n",[ValRef]),
case ValRef of
@@ -2292,22 +2304,23 @@ validate_oid(_, S, OID, [Id|Vrest], Acc)
error({value, {"illegal "++to_string(OID),[Id,Vrest],Acc}, S})
end
end;
-validate_oid(_, S, OID, [{Atom,Value}],[])
+validate_oid(_, S, OID, [{#seqtag{module=Mod,val=Atom},Value}], [])
when is_atom(Atom),is_integer(Value) ->
%% this case when an OBJECT IDENTIFIER value has been parsed as a
%% SEQUENCE value
- Rec = #'Externalvaluereference'{module=S#state.mname,
+ Rec = #'Externalvaluereference'{module=Mod,
value=Atom},
validate_objectidentifier1(S, OID, [Rec,Value]);
-validate_oid(_, S, OID, [{Atom,EVRef}],[])
+validate_oid(_, S, OID, [{#seqtag{module=Mod,val=Atom},EVRef}], [])
when is_atom(Atom),is_record(EVRef,'Externalvaluereference') ->
%% this case when an OBJECT IDENTIFIER value has been parsed as a
%% SEQUENCE value OTP-4354
- Rec = #'Externalvaluereference'{module=EVRef#'Externalvaluereference'.module,
+ Rec = #'Externalvaluereference'{module=Mod,
value=Atom},
validate_objectidentifier1(S, OID, [Rec,EVRef]);
-validate_oid(_, S, OID, [Atom|Rest],Acc) when is_atom(Atom) ->
- Rec = #'Externalvaluereference'{module=S#state.mname,
+validate_oid(_, S, OID, [#seqtag{module=Mod,val=Atom}|Rest], Acc)
+ when is_atom(Atom) ->
+ Rec = #'Externalvaluereference'{module=Mod,
value=Atom},
validate_oid(true,S, OID, [Rec|Rest],Acc);
validate_oid(_, S, OID, V, Acc) ->
@@ -2689,20 +2702,20 @@ normalize_set(S,Value,Components,NameList) ->
normalized_record('SET',S,SortedVal,Components,NameList)
end.
-sort_value(Components,Value) ->
- ComponentNames = lists:map(fun(#'ComponentType'{name=Cname}) -> Cname end,
- Components),
- sort_value1(ComponentNames,Value,[]).
-sort_value1(_,V=#'Externalvaluereference'{},_) ->
- %% sort later, get the value in normalize_seq_or_set
- V;
-sort_value1([N|Ns],Value,Acc) ->
- case lists:keysearch(N,1,Value) of
- {value,V} ->sort_value1(Ns,Value,[V|Acc]);
- _ -> sort_value1(Ns,Value,Acc)
- end;
-sort_value1([],_,Acc) ->
- lists:reverse(Acc).
+sort_value(Components, Value0) when is_list(Value0) ->
+ {Keys0,_} = lists:mapfoldl(fun(#'ComponentType'{name=N}, I) ->
+ {{N,I},I+1}
+ end, 0, Components),
+ Keys = gb_trees:from_orddict(orddict:from_list(Keys0)),
+ Value1 = [{case gb_trees:lookup(N, Keys) of
+ {value,K} -> K;
+ none -> 'end'
+ end,Pair} || {#seqtag{val=N},_}=Pair <- Value0],
+ Value = lists:sort(Value1),
+ [Pair || {_,Pair} <- Value];
+sort_value(_Components, #'Externalvaluereference'{}=Value) ->
+ %% Sort later.
+ Value.
sort_val_if_set(['SET'|_],Val,Type) ->
sort_value(Type,Val);
@@ -2735,9 +2748,9 @@ is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) ->
is_record_normalized(_,_,_,_) ->
false.
-normalize_seq_or_set(SorS,S,[{Cname,V}|Vs],
+normalize_seq_or_set(SorS, S, [{#seqtag{val=Cname},V}|Vs],
[#'ComponentType'{name=Cname,typespec=TS}|Cs],
- NameList,Acc) ->
+ NameList, Acc) ->
NewNameList =
case TS#type.def of
#'Externaltypereference'{type=TName} ->
@@ -2915,8 +2928,7 @@ get_canonic_type(S,Type,NameList) ->
check_ptype(S,Type,Ts) when is_record(Ts,type) ->
- %Tag = Ts#type.tag,
- %Constr = Ts#type.constraint,
+ check_formal_parameters(S, Type#ptypedef.args),
Def = Ts#type.def,
NewDef=
case Def of
@@ -2942,6 +2954,16 @@ check_ptype(S,Type,Ts) when is_record(Ts,type) ->
check_ptype(_S,_PTDef,Ts) when is_record(Ts,objectclass) ->
throw({asn1_param_class,Ts}).
+check_formal_parameters(S, Args) ->
+ _ = [check_formal_parameter(S, A) || A <- Args],
+ ok.
+
+check_formal_parameter(_, {_,_}) ->
+ ok;
+check_formal_parameter(_, #'Externaltypereference'{}) ->
+ ok;
+check_formal_parameter(S, #'Externalvaluereference'{value=Name}=Ref) ->
+ asn1_error(S, Ref, {illegal_typereference,Name}).
% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
% check_class(S,ObjSpec);
@@ -2989,9 +3011,9 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
{TmpRefMod,TmpRefDef} ->
{TmpRefMod,TmpRefDef,false}
end,
- case is_class(S,RefTypeDef) of
- true -> throw({asn1_class,RefTypeDef});
- _ -> ok
+ case get_class_def(S, RefTypeDef) of
+ none -> ok;
+ #classdef{} -> throw({asn1_class,RefTypeDef})
end,
Ct = TestFun(Ext),
{RefType,ExtRef} =
@@ -3372,23 +3394,17 @@ get_type_from_object(S,Object,TypeField)
ObjSpec = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
get_fieldname_element(S,ObjectDef#typedef{typespec=ObjSpec},TypeField).
-is_class(_S,#classdef{}) ->
- true;
-is_class(S,#typedef{typespec=#type{def=Eref}})
- when is_record(Eref,'Externaltypereference')->
- is_class(S,Eref);
-is_class(S,Eref) when is_record(Eref,'Externaltypereference')->
- {_,NextDef} = get_referenced_type(S,Eref),
- is_class(S,NextDef);
-is_class(_,_) ->
- false.
-
-get_class_def(_S,CD=#classdef{}) ->
+%% get_class_def(S, Type) -> #classdef{} | 'none'.
+get_class_def(S, #typedef{typespec=#type{def=#'Externaltypereference'{}=Eref}}) ->
+ {_,NextDef} = get_referenced_type(S, Eref),
+ get_class_def(S, NextDef);
+get_class_def(S, #'Externaltypereference'{}=Eref) ->
+ {_,NextDef} = get_referenced_type(S, Eref),
+ get_class_def(S, NextDef);
+get_class_def(_S, #classdef{}=CD) ->
CD;
-get_class_def(S,#typedef{typespec=#type{def=Eref}})
- when is_record(Eref,'Externaltypereference') ->
- {_,NextDef} = get_referenced_type(S,Eref),
- get_class_def(S,NextDef).
+get_class_def(_S, _) ->
+ none.
maybe_illicit_implicit_tag(Kind,Tag) ->
case Tag of
@@ -3595,109 +3611,54 @@ match_args(_,_, _, _) ->
%% categorize_arg(S,FormalArg,ActualArg) -> {FormalArg,CatgorizedActualArg}
%%
categorize_arg(S,{Governor,Param},ActArg) ->
- case {governor_category(S,Governor),parameter_name_style(Param,ActArg)} of
-%% {absent,beginning_uppercase} -> %% a type
-%% categorize(S,type,ActArg);
- {type,beginning_lowercase} -> %% a value
- categorize(S,value,Governor,ActArg);
- {type,beginning_uppercase} -> %% a value set
- categorize(S,value_set,ActArg);
-%% {absent,entirely_uppercase} -> %% a class
-%% categorize(S,class,ActArg);
+ case {governor_category(S, Governor),parameter_name_style(Param)} of
+ {type,beginning_lowercase} -> %a value
+ categorize(S, value, Governor, ActArg);
+ {type,beginning_uppercase} -> %a value set
+ categorize(ActArg);
{{class,ClassRef},beginning_lowercase} ->
- categorize(S,object,ActArg,ClassRef);
+ categorize(S, object, ActArg, ClassRef);
{{class,ClassRef},beginning_uppercase} ->
- categorize(S,object_set,ActArg,ClassRef);
- _ ->
- [ActArg]
+ categorize(S, object_set, ActArg, ClassRef)
end;
-categorize_arg(S,FormalArg,ActualArg) ->
- %% governor is absent => a type or a class
- case FormalArg of
- #'Externaltypereference'{type=Name} ->
- case is_class_name(Name) of
- true ->
- categorize(S,class,ActualArg);
- _ ->
- categorize(S,type,ActualArg)
- end;
- FA ->
- throw({error,{unexpected_formal_argument,FA}})
- end.
-
-governor_category(S,#type{def=Eref})
- when is_record(Eref,'Externaltypereference') ->
- governor_category(S,Eref);
-governor_category(_S,#type{}) ->
+categorize_arg(_S, _FormalArg, ActualArg) ->
+ %% Governor is absent -- must be a type or a class. We have already
+ %% checked that the FormalArg begins with an uppercase letter.
+ categorize(ActualArg).
+
+%% governor_category(S, Item) -> type | {class,#'Externaltypereference'{}}
+%% Determine whether Item is a type or a class.
+governor_category(S, #type{def=#'Externaltypereference'{}=Eref}) ->
+ governor_category(S, Eref);
+governor_category(_S, #type{}) ->
type;
-governor_category(S,Ref) when is_record(Ref,'Externaltypereference') ->
- case is_class(S,Ref) of
- true ->
- {class,Ref};
- _ ->
+governor_category(S, #'Externaltypereference'{}=Ref) ->
+ case get_class_def(S, Ref) of
+ #classdef{pos=Pos,module=Mod,name=Name} ->
+ {class,#'Externaltypereference'{pos=Pos,module=Mod,type=Name}};
+ none ->
type
- end;
-governor_category(_,Class)
- when Class == 'TYPE-IDENTIFIER'; Class == 'ABSTRACT-SYNTAX' ->
- class.
-%% governor_category(_,_) ->
-%% absent.
+ end.
%% parameter_name_style(Param,Data) -> Result
%% gets the Parameter and the name of the Data and if it exists tells
%% whether it begins with a lowercase letter or is partly or entirely
%% spelled with uppercase letters. Otherwise returns undefined
%%
-parameter_name_style(_,#'Externaltypereference'{type=Name}) ->
- name_category(Name);
-parameter_name_style(_,#'Externalvaluereference'{value=Name}) ->
- name_category(Name);
-parameter_name_style(_,{valueset,_}) ->
- %% It is a object set or value set
+parameter_name_style(#'Externaltypereference'{}) ->
beginning_uppercase;
-parameter_name_style(#'Externalvaluereference'{},_) ->
- beginning_lowercase;
-parameter_name_style(#'Externaltypereference'{type=Name},_) ->
- name_category(Name);
-parameter_name_style(_,_) ->
- undefined.
-
-name_category(Atom) when is_atom(Atom) ->
- name_category(atom_to_list(Atom));
-name_category([H|T]) ->
- case is_lowercase(H) of
- true ->
- beginning_lowercase;
- _ ->
- case is_class_name(T) of
- true ->
- entirely_uppercase;
- _ ->
- beginning_uppercase
- end
- end;
-name_category(_) ->
- undefined.
+parameter_name_style(#'Externalvaluereference'{}) ->
+ beginning_lowercase.
is_lowercase(X) when X >= $A,X =< $W ->
false;
is_lowercase(_) ->
true.
-
-is_class_name(Name) when is_atom(Name) ->
- is_class_name(atom_to_list(Name));
-is_class_name(Name) ->
- case [X||X <- Name, X >= $a,X =< $w] of
- [] ->
- true;
- _ ->
- false
- end.
-%% categorize(S,Category,Parameter) -> CategorizedParameter
+%% categorize(Parameter) -> CategorizedParameter
%% If Parameter has an abstract syntax of another category than
%% Category, transform it to a known syntax.
-categorize(_S,type,{object,_,Type}) ->
+categorize({object,_,Type}) ->
%% One example of this case is an object with a parameterized type
%% having a locally defined type as parameter.
Def = fun(D = #type{}) ->
@@ -3709,11 +3670,12 @@ categorize(_S,type,{object,_,Type}) ->
D
end,
[Def(X)||X<-Type];
-categorize(_S,type,Def) when is_record(Def,type) ->
+categorize(#type{}=Def) ->
[#typedef{name = new_reference_name("type_argument"),
typespec = Def#type{inlined=yes}}];
-categorize(_,_,Def) ->
+categorize(Def) ->
[Def].
+
categorize(S,object_set,Def,ClassRef) ->
NewObjSetSpec =
check_object(S,Def,#'ObjectSet'{class = ClassRef,
@@ -4546,55 +4508,43 @@ check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) ->
#'Externaltypereference'{pos=Pos,module=ModName,type=Name}
end.
+get_referenced_type(S, T) ->
+ case do_get_referenced_type(S, T) of
+ {_,#type{def=#'Externaltypereference'{}=ERef}} ->
+ get_referenced_type(S, ERef);
+ {_,#type{def=#'Externalvaluereference'{}=VRef}} ->
+ get_referenced_type(S, VRef);
+ {_,_}=Res ->
+ Res
+ end.
-get_referenced_type(S,Ext) when is_record(Ext,'Externaltypereference') ->
- case match_parameters(S,Ext, S#state.parameters) of
- Ext ->
- #'Externaltypereference'{pos=Pos,module=Emod,type=Etype} = Ext,
- case S#state.mname of
- Emod -> % a local reference in this module
- get_referenced1(S,Emod,Etype,Pos);
- _ ->% always when multi file compiling
- case lists:member(Emod,S#state.inputmodules) of
- true ->
- get_referenced1(S,Emod,Etype,Pos);
- false ->
- get_referenced(S,Emod,Etype,Pos)
- end
- end;
- ERef = #'Externaltypereference'{} ->
- get_referenced_type(S,ERef);
- Other ->
- {undefined,Other}
- end;
-get_referenced_type(S=#state{mname=Emod},
- ERef=#'Externalvaluereference'{pos=P,module=Emod,
- value=Eval}) ->
- case match_parameters(S,ERef,S#state.parameters) of
- ERef ->
- get_referenced1(S,Emod,Eval,P);
- OtherERef when is_record(OtherERef,'Externalvaluereference') ->
- get_referenced_type(S,OtherERef);
- Value ->
- {Emod,Value}
- end;
-get_referenced_type(S,ERef=#'Externalvaluereference'{pos=Pos,module=Emod,
- value=Eval}) ->
- case match_parameters(S,ERef,S#state.parameters) of
- ERef ->
- case lists:member(Emod,S#state.inputmodules) of
- true ->
- get_referenced1(S,Emod,Eval,Pos);
- false ->
- get_referenced(S,Emod,Eval,Pos)
- end;
- OtherERef ->
- get_referenced_type(S,OtherERef)
- end;
-get_referenced_type(S,#identifier{val=Name,pos=Pos}) ->
- get_referenced1(S,undefined,Name,Pos);
-get_referenced_type(_S,Type) ->
- {undefined,Type}.
+do_get_referenced_type(#state{parameters=Ps}=S, T0) ->
+ case match_parameters(S, T0, Ps) of
+ T0 ->
+ do_get_ref_type_1(S, T0);
+ T ->
+ do_get_referenced_type(S, T)
+ end.
+
+do_get_ref_type_1(S, #'Externaltypereference'{pos=P,
+ module=M,
+ type=T}) ->
+ do_get_ref_type_2(S, P, M, T);
+do_get_ref_type_1(S, #'Externalvaluereference'{pos=P,
+ module=M,
+ value=V}) ->
+ do_get_ref_type_2(S, P, M, V);
+do_get_ref_type_1(_, T) ->
+ {undefined,T}.
+
+do_get_ref_type_2(#state{mname=Current,inputmodules=Modules}=S,
+ Pos, M, T) ->
+ case M =:= Current orelse lists:member(M, Modules) of
+ true ->
+ get_referenced1(S, M, T, Pos);
+ false ->
+ get_referenced(S, M, T, Pos)
+ end.
%% get_referenced/3
%% The referenced entity Ename may in case of an imported parameterized
@@ -6760,6 +6710,8 @@ format_error({illegal_instance_of,Class}) ->
[Class]);
format_error(illegal_octet_string_value) ->
"expecting a bstring or an hstring as value for an OCTET STRING";
+format_error({illegal_typereference,Name}) ->
+ io_lib:format("'~p' is used as a typereference, but does not start with an uppercase letter", [Name]);
format_error({invalid_fields,Fields,Obj}) ->
io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]);
format_error({invalid_bit_number,Bit}) ->
@@ -7006,7 +6958,7 @@ include_default_class1(_,[]) ->
include_default_class1(Module,[{Name,TS}|Rest]) ->
case asn1_db:dbget(Module,Name) of
undefined ->
- C = #classdef{checked=true,name=Name,
+ C = #classdef{checked=true,module=Module,name=Name,
typespec=TS},
asn1_db:dbput(Module,Name,C);
_ -> ok
diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl
index 283616b157..3891fce8d3 100644
--- a/lib/asn1/src/asn1ct_parser2.erl
+++ b/lib/asn1/src/asn1ct_parser2.erl
@@ -25,7 +25,8 @@
%% Only used internally within this module.
-record(typereference, {pos,val}).
--record(constraint,{c,e}).
+-record(constraint, {c,e}).
+-record(identifier, {pos,val}).
%% parse all types in module
parse(Tokens) ->
@@ -112,6 +113,9 @@ parse_ModuleDefinition(Tokens) ->
parse_Exports([{'EXPORTS',_L1},{';',_L2}|Rest]) ->
{{exports,[]},Rest};
+parse_Exports([{'EXPORTS',_},{'ALL',_},{';',_}|Rest]) ->
+ %% Same as no exports definition.
+ {{exports,all},Rest};
parse_Exports([{'EXPORTS',_L1}|Rest]) ->
{SymbolList,Rest2} = parse_SymbolList(Rest),
case Rest2 of
@@ -1037,10 +1041,6 @@ parse_DefinedObjectClass([{typereference,_,_ModName},{'.',_},Tr={typereference,_
parse_DefinedObjectClass([Tr={typereference,_,_ObjClName}|Rest]) ->
% {{objectclassname,tref2Exttref(Tr)},Rest};
{tref2Exttref(Tr),Rest};
-parse_DefinedObjectClass([{'TYPE-IDENTIFIER',_}|Rest]) ->
- {'TYPE-IDENTIFIER',Rest};
-parse_DefinedObjectClass([{'ABSTRACT-SYNTAX',_}|Rest]) ->
- {'ABSTRACT-SYNTAX',Rest};
parse_DefinedObjectClass(Tokens) ->
throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
[got,get_token(hd(Tokens)),expected,
@@ -1051,7 +1051,8 @@ parse_DefinedObjectClass(Tokens) ->
parse_ObjectClassAssignment([{typereference,L1,ObjClName},{'::=',_}|Rest]) ->
{Type,Rest2} = parse_ObjectClass(Rest),
- {#classdef{pos=L1,name=ObjClName,typespec=Type},Rest2};
+ {#classdef{pos=L1,name=ObjClName,module=resolve_module(Type),
+ typespec=Type},Rest2};
parse_ObjectClassAssignment(Tokens) ->
throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
[got,get_token(hd(Tokens)),expected,
@@ -2134,8 +2135,7 @@ parse_ParameterizedObjectSetAssignment(Tokens) ->
%% Parameter = {Governor,Reference} | Reference
%% Governor = Type | DefinedObjectClass
%% Type = #type{}
-%% DefinedObjectClass = #'Externaltypereference'{} |
-%% 'ABSTRACT-SYNTAX' | 'TYPE-IDENTIFIER'
+%% DefinedObjectClass = #'Externaltypereference'{}
%% Reference = #'Externaltypereference'{} | #'Externalvaluereference'{}
parse_ParameterList([{'{',_}|Rest]) ->
parse_ParameterList(Rest,[]);
@@ -2863,13 +2863,14 @@ parse_SequenceValue(Tokens) ->
throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
[got,get_token(hd(Tokens)),expected,'{']}}).
-parse_SequenceValue([{identifier,_,IdName}|Rest],Acc) ->
+parse_SequenceValue([{identifier,Pos,IdName}|Rest],Acc) ->
{Value,Rest2} = parse_Value(Rest),
+ SeqTag = #seqtag{pos=Pos,module=get(asn1_module),val=IdName},
case Rest2 of
[{',',_}|Rest3] ->
- parse_SequenceValue(Rest3,[{IdName,Value}|Acc]);
+ parse_SequenceValue(Rest3, [{SeqTag,Value}|Acc]);
[{'}',_}|Rest3] ->
- {lists:reverse([{IdName,Value}|Acc]),Rest3};
+ {lists:reverse(Acc, [{SeqTag,Value}]),Rest3};
_ ->
throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
[got,get_token(hd(Rest2)),expected,'}']}})
diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl
index 33f4379173..8687ed955c 100644
--- a/lib/asn1/src/asn1ct_tok.erl
+++ b/lib/asn1/src/asn1ct_tok.erl
@@ -309,7 +309,6 @@ check_hex(_) ->
%% returns rstrtype if A is a reserved word in the group
%% RestrictedCharacterStringType
reserved_word('ABSENT') -> true;
-%reserved_word('ABSTRACT-SYNTAX') -> true; % impl as predef item
reserved_word('ALL') -> true;
reserved_word('ANY') -> true;
reserved_word('APPLICATION') -> true;
@@ -380,7 +379,6 @@ reserved_word('T61String') -> rstrtype;
reserved_word('TAGS') -> true;
reserved_word('TeletexString') -> rstrtype;
reserved_word('TRUE') -> true;
-%% reserved_word('TYPE-IDENTIFIER') -> true; % impl as predef item
reserved_word('UNION') -> true;
reserved_word('UNIQUE') -> true;
reserved_word('UNIVERSAL') -> true;
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 11d1b82fb4..888339a4d2 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -134,14 +134,13 @@ groups() ->
testChoiceIndefinite,
per_open_type,
testInfObjectClass,
- testParameterizedInfObj,
+ testParam,
testFragmented,
testMergeCompile,
testobj,
testDeepTConstr,
testExport,
testImport,
- testParamBasic,
testDER,
testDEFAULT,
testMvrasn6,
@@ -520,12 +519,6 @@ testSetDefault(Config, Rule, Opts) ->
asn1_test_lib:compile("SetDefault", Config, [Rule|Opts]),
testSetDefault:main(Rule).
-testParamBasic(Config) ->
- test(Config, fun testParamBasic/3, [ber,{ber,[der]},per,uper]).
-testParamBasic(Config, Rule, Opts) ->
- asn1_test_lib:compile("ParamBasic", Config, [Rule|Opts]),
- testParamBasic:main(Rule).
-
testSetOptional(Config) -> test(Config, fun testSetOptional/3).
testSetOptional(Config, Rule, Opts) ->
asn1_test_lib:compile("SetOptional", Config, [Rule|Opts]),
@@ -758,11 +751,12 @@ testInfObjectClass(Config, Rule, Opts) ->
testInfObjectClass:main(Rule),
testInfObj:main(Rule).
-testParameterizedInfObj(Config) ->
- test(Config, fun testParameterizedInfObj/3).
-testParameterizedInfObj(Config, Rule, Opts) ->
- Files = ["Param","Param2"],
+testParam(Config) ->
+ test(Config, fun testParam/3, [ber,{ber,[der]},per,uper]).
+testParam(Config, Rule, Opts) ->
+ Files = ["ParamBasic","Param","Param2"],
asn1_test_lib:compile_all(Files, Config, [Rule|Opts]),
+ testParamBasic:main(Rule),
testParameterizedInfObj:main(Config, Rule),
asn1_test_lib:compile("Param", Config,
[legacy_erlang_types,Rule|Opts]),
diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn
index 880e81c3b1..719119f418 100644
--- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn
+++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn
@@ -255,6 +255,51 @@ Multiple-Optionals ::= SEQUENCE {
t3 [2] MULTIPLE-OPTIONALS.&T3 ({Multiple-Optionals-Set}{@id}) OPTIONAL
}
+-- Test different ways of constructing object sets.
+
+OBJECT-SET-TEST ::= CLASS {
+ &id INTEGER UNIQUE,
+ &Type
+} WITH SYNTAX {
+ &id IS &Type
+}
+
+ObjectSetTest{OBJECT-SET-TEST:ObjectSet} ::=
+ SEQUENCE {
+ id OBJECT-SET-TEST.&id ({ObjectSet}),
+ type OBJECT-SET-TEST.&Type ({ObjectSet}{@id})
+ }
+
+ost1 OBJECT-SET-TEST ::= { 1 IS BIT STRING }
+ost2 OBJECT-SET-TEST ::= { 2 IS OCTET STRING }
+ost3 OBJECT-SET-TEST ::= { 3 IS ENUMERATED {donald,scrooge} }
+ost4 OBJECT-SET-TEST ::= { 4 IS BOOLEAN }
+ost5 OBJECT-SET-TEST ::= { 5 IS INTEGER (0..15) }
+
+Ost12 OBJECT-SET-TEST ::= { ost1 | ost2 }
+Ost123 OBJECT-SET-TEST ::= { ost3 | Ost12 }
+Ost1234 OBJECT-SET-TEST ::= { Ost123 | ost4 }
+Ost45 OBJECT-SET-TEST ::= { ost4 | ost5 }
+Ost12345 OBJECT-SET-TEST ::= { Ost123 | Ost45 }
+
+OstSeq12 ::= ObjectSetTest{ {Ost12} }
+OstSeq123 ::= ObjectSetTest{ {Ost123} }
+OstSeq1234 ::= ObjectSetTest{ {Ost1234} }
+OstSeq45 ::= ObjectSetTest{ {Ost45} }
+OstSeq12345 ::= ObjectSetTest{ {Ost12345} }
+
+ExOst12 OBJECT-SET-TEST ::= { ost1, ..., ost2 }
+ExOst123 OBJECT-SET-TEST ::= { ost3, ..., ExOst12 }
+--ExOst1234 OBJECT-SET-TEST ::= { ExOst123, ..., ost4 }
+ExOst45 OBJECT-SET-TEST ::= { ost4, ..., ost5 }
+ExOst12345 OBJECT-SET-TEST ::= { ExOst123, ..., ExOst45 }
+
+ExOstSeq12 ::= ObjectSetTest{ {ExOst12} }
+ExOstSeq123 ::= ObjectSetTest{ {ExOst123} }
+--ExOstSeq1234 ::= ObjectSetTest{ {ExOst1234} }
+ExOstSeq45 ::= ObjectSetTest{ {ExOst45} }
+ExOstSeq12345 ::= ObjectSetTest{ {ExOst12345} }
+
END
diff --git a/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1 b/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1
index 491bdf8956..68fc782f33 100644
--- a/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1
+++ b/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1
@@ -20,4 +20,26 @@ T21 ::= General2{PrintableString}
T22 ::= General2{BIT STRING}
+
+--
+-- Test a class parameter that is the governor for another parameter.
+--
+
+AlgorithmIdentifier{ALGORITHM-TYPE, ALGORITHM-TYPE:AlgorithmSet} ::=
+ SEQUENCE {
+ algorithm ALGORITHM-TYPE.&id ({AlgorithmSet}),
+ type ALGORITHM-TYPE.&Type ({AlgorithmSet}{@algorithm})
+ }
+
+AnAlgorithm ::= AlgorithmIdentifier{ SIGNATURE-ALGORITHM,
+ { {KEY 1 CONTAINING INTEGER} |
+ {KEY 2 CONTAINING BOOLEAN} } }
+
+SIGNATURE-ALGORITHM ::= CLASS {
+ &id INTEGER UNIQUE,
+ &Type
+} WITH SYNTAX {
+ KEY &id CONTAINING &Type
+}
+
END
diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl
index 06e9b2c093..da07cd1118 100644
--- a/lib/asn1/test/asn1_test_lib.erl
+++ b/lib/asn1/test/asn1_test_lib.erl
@@ -112,6 +112,7 @@ roundtrip(Mod, Type, Value) ->
roundtrip(Mod, Type, Value, ExpectedValue) ->
{ok,Encoded} = Mod:encode(Type, Value),
{ok,ExpectedValue} = Mod:decode(Type, Encoded),
+ test_ber_indefinite(Mod, Type, Encoded, ExpectedValue),
ok.
roundtrip_enc(Mod, Type, Value) ->
@@ -120,6 +121,7 @@ roundtrip_enc(Mod, Type, Value) ->
roundtrip_enc(Mod, Type, Value, ExpectedValue) ->
{ok,Encoded} = Mod:encode(Type, Value),
{ok,ExpectedValue} = Mod:decode(Type, Encoded),
+ test_ber_indefinite(Mod, Type, Encoded, ExpectedValue),
Encoded.
%%%
@@ -129,3 +131,52 @@ roundtrip_enc(Mod, Type, Value, ExpectedValue) ->
hex2num(C) when $0 =< C, C =< $9 -> C - $0;
hex2num(C) when $A =< C, C =< $F -> C - $A + 10;
hex2num(C) when $a =< C, C =< $f -> C - $a + 10.
+
+test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) ->
+ case Mod:encoding_rule() of
+ ber ->
+ Indefinite = iolist_to_binary(ber_indefinite(Encoded)),
+ {ok,ExpectedValue} = Mod:decode(Type, Indefinite);
+ _ ->
+ ok
+ end.
+
+%% Rewrite all definite lengths for constructed values to an
+%% indefinite length.
+ber_indefinite(Bin0) ->
+ case ber_get_tag(Bin0) of
+ done ->
+ [];
+ primitive ->
+ Bin0;
+ {constructed,Tag,Bin1} ->
+ {Len,Bin2} = ber_get_len(Bin1),
+ <<Val0:Len/binary,Bin/binary>> = Bin2,
+ Val = iolist_to_binary(ber_indefinite(Val0)),
+ [<<Tag/binary,16#80,Val/binary,0,0>>|ber_indefinite(Bin)]
+ end.
+
+ber_get_tag(<<>>) ->
+ done;
+ber_get_tag(<<_:2,0:1,_:5,_/binary>>) ->
+ primitive;
+ber_get_tag(<<_:2,1:1,_:5,_/binary>>=Bin0) ->
+ TagLen = ber_tag_length(Bin0),
+ <<Tag:TagLen/binary,Bin/binary>> = Bin0,
+ {constructed,Tag,Bin}.
+
+ber_tag_length(<<_:3,2#11111:5,T/binary>>) ->
+ ber_tag_length_1(T, 1);
+ber_tag_length(_) ->
+ 1.
+
+ber_tag_length_1(<<1:1,_:7,T/binary>>, N) ->
+ ber_tag_length_1(T, N+1);
+ber_tag_length_1(<<0:1,_:7,_/binary>>, N) ->
+ N+1.
+
+ber_get_len(<<0:1,L:7,T/binary>>) ->
+ {L,T};
+ber_get_len(<<1:1,Octets:7,T0/binary>>) ->
+ <<L:Octets/unit:8,T/binary>> = T0,
+ {L,T}.
diff --git a/lib/asn1/test/ber_decode_error.erl b/lib/asn1/test/ber_decode_error.erl
index 8be92292ee..6fd2450c62 100644
--- a/lib/asn1/test/ber_decode_error.erl
+++ b/lib/asn1/test/ber_decode_error.erl
@@ -51,4 +51,18 @@ run([]) ->
{error,{asn1,{invalid_value,_}}} =
(catch 'Constructed':decode('I', <<8,7>>)),
+ %% Short indefinite length. Make sure that the decoder doesn't look
+ %% beyond the end of binary when looking for a 0,0 terminator.
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('S', sub(<<8,16#80,0,0>>, 3))),
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('S', sub(<<8,16#80,0,0>>, 2))),
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('S', sub(<<40,16#80,1,1,255,0,0>>, 6))),
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('S', sub(<<40,16#80,1,1,255,0,0>>, 5))),
ok.
+
+sub(Bin, Bytes) ->
+ <<B:Bytes/binary,_/binary>> = Bin,
+ B.
diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl
index 8a0414708d..1edd60f7c8 100644
--- a/lib/asn1/test/error_SUITE.erl
+++ b/lib/asn1/test/error_SUITE.erl
@@ -20,7 +20,8 @@
-module(error_SUITE).
-export([suite/0,all/0,groups/0,
already_defined/1,bitstrings/1,enumerated/1,
- imports/1,instance_of/1,integers/1,objects/1,values/1]).
+ imports/1,instance_of/1,integers/1,objects/1,
+ parameterization/1,values/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -38,6 +39,7 @@ groups() ->
instance_of,
integers,
objects,
+ parameterization,
values]}].
parallel() ->
@@ -219,6 +221,19 @@ objects(Config) ->
} = run(P, Config),
ok.
+parameterization(Config) ->
+ M = 'Parameterization',
+ P = {M,
+ <<"Parameterization DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n"
+ " NotUppercase{lowercase} ::= INTEGER (lowercase)\n"
+ "END\n">>},
+ {error,
+ [{structured_error,{'Parameterization',2},asn1ct_check,
+ {illegal_typereference,lowercase}}
+ ]
+ } = run(P, Config),
+ ok.
+
values(Config) ->
M = 'Values',
P = {M,
diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl
index 311595cfda..37c134b1b9 100644
--- a/lib/asn1/test/testInfObj.erl
+++ b/lib/asn1/test/testInfObj.erl
@@ -118,7 +118,41 @@ main(_Erule) ->
roundtrip('InfObj', 'Multiple-Optionals',
{'Multiple-Optionals',1,42,true,asn1_NOVALUE}),
roundtrip('InfObj', 'Multiple-Optionals',
- {'Multiple-Optionals',1,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}).
+ {'Multiple-Optionals',1,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}),
+
+ test_objset('OstSeq12', [1,2]),
+ test_objset('OstSeq123', [1,2,3]),
+ test_objset('OstSeq1234', [1,2,3,4]),
+ test_objset('OstSeq45', [4,5]),
+ test_objset('OstSeq12345', [1,2,3,4,5]),
+
+ test_objset('ExOstSeq12', [1,2]),
+ test_objset('ExOstSeq123', [1,2,3]),
+ %%test_objset('ExOstSeq1234', [1,2,3,4]),
+ test_objset('ExOstSeq45', [4,5]),
+ test_objset('ExOstSeq12345', [1,2,3,4,5]),
+
+ ok.
+
+test_objset(Type, Keys) ->
+ _ = [test_object(Type, Key) || Key <- Keys],
+ _ = [(catch test_object(Type, Key)) ||
+ Key <- lists:seq(1, 5) -- Keys],
+ ok.
+
+test_object(T, 1) ->
+ roundtrip('InfObj', T, {T,1,<<42:7>>});
+test_object(T, 2) ->
+ roundtrip('InfObj', T, {T,2,<<"abc">>});
+test_object(T, 3) ->
+ roundtrip('InfObj', T, {T,3,donald}),
+ roundtrip('InfObj', T, {T,3,scrooge});
+test_object(T, 4) ->
+ roundtrip('InfObj', T, {T,4,true}),
+ roundtrip('InfObj', T, {T,4,false});
+test_object(T, 5) ->
+ roundtrip('InfObj', T, {T,5,0}),
+ roundtrip('InfObj', T, {T,5,15}).
roundtrip(M, T, V) ->
asn1_test_lib:roundtrip(M, T, V).
diff --git a/lib/asn1/test/testParamBasic.erl b/lib/asn1/test/testParamBasic.erl
index 3db89ca174..39f7947e8d 100644
--- a/lib/asn1/test/testParamBasic.erl
+++ b/lib/asn1/test/testParamBasic.erl
@@ -43,6 +43,9 @@ main(Rules) ->
#'T12'{number=11,string = <<10:4>>});
_ -> ok
end,
+ roundtrip('AnAlgorithm', {'AnAlgorithm',1,42}),
+ roundtrip('AnAlgorithm', {'AnAlgorithm',2,true}),
+ roundtrip('AnAlgorithm', {'AnAlgorithm',2,false}),
ok.
roundtrip(Type, Value) ->
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 37c843204a..d87c50637d 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 3.0.1
+ASN1_VSN = 3.0.2
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index 99161ce68a..57233a7f6c 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -47,6 +47,7 @@ CT_MODULES = \
ct_snmp \
unix_telnet \
ct_slave \
+ ct_property_test \
ct_netconfc
CT_XML_FILES = $(CT_MODULES:=.xml)
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index b53ba32e6c..f4ce5369f7 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -32,6 +32,54 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.8.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Ticket OTP-11971 introduced a runtime dependency towards
+ test_server-3.7.1, since the interface between
+ test_server and common_test was changed. Erroneously, the
+ common_test.app file was not updated according to this.
+ This has now been corrected.</p>
+ <p>
+ Own Id: OTP-12037</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warning: this is experimental and may disappear or change
+ without previous warning.</p>
+ <p>
+ Experimental support for running Quickcheck and PropEr
+ tests from common_test suites is added to common_test.
+ See the reference manual for the new module
+ <c>ct_property_testing</c>.</p>
+ <p>
+ Experimental property tests are added under
+ <c>lib/{inet,ssh}/test/property_test</c>. They can be run
+ directly or from the commont_test suites
+ <c>inet/ftp_property_test_SUITE.erl</c> and
+ <c>ssh/test/ssh_property_test_SUITE.erl</c>.</p>
+ <p>
+ See the code in the <c>test</c> directories and the man
+ page for details.</p>
+ <p>
+ (Thanks to Tuncer Ayaz for a patch adding Triq)</p>
+ <p>
+ Own Id: OTP-12119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.8.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index 2f5c892e60..c266b70d00 100644
--- a/lib/common_test/doc/src/ref_man.xml
+++ b/lib/common_test/doc/src/ref_man.xml
@@ -78,6 +78,7 @@
<xi:include href="unix_telnet.xml"/>
<xi:include href="ct_slave.xml"/>
<xi:include href="ct_hooks.xml"/>
+ <xi:include href="ct_property_test.xml"/>
</application>
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 4600c0ad78..8d74546880 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -74,7 +74,8 @@ MODULES= \
ct_netconfc \
ct_conn_log_h \
cth_conn_log \
- ct_groups
+ ct_groups \
+ ct_property_test
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index e28751fb59..580d5dbd7b 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -1,7 +1,7 @@
% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -64,7 +64,7 @@
{applications, [kernel,stdlib]},
{env, []},
{runtime_dependencies,["xmerl-1.3.7","webtool-0.8.10","tools-2.6.14",
- "test_server-3.7","stdlib-2.0","ssh-3.0.1",
+ "test_server-3.7.1","stdlib-2.0","ssh-3.0.1",
"snmp-4.25.1","sasl-2.4","runtime_tools-1.8.14",
"kernel-3.0","inets-5.10","erts-6.0",
"debugger-4.0","crypto-3.3","compiler-5.0"]}]}.
diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl
index cf2860ae25..c7f446dee9 100644
--- a/lib/common_test/src/ct_cover.erl
+++ b/lib/common_test/src/ct_cover.erl
@@ -128,20 +128,20 @@ get_spec(File) ->
catch get_spec_test(File).
get_spec_test(File) ->
- FullName = filename:absname(File),
- case filelib:is_file(FullName) of
+ Dir = filename:dirname(File), % always abs path in here, set in ct_run
+ case filelib:is_file(File) of
true ->
- case file:consult(FullName) of
+ case file:consult(File) of
{ok,Terms} ->
Import =
case lists:keysearch(import, 1, Terms) of
{value,{_,Imps=[S|_]}} when is_list(S) ->
ImpsFN = lists:map(fun(F) ->
- filename:absname(F)
+ filename:absname(F,Dir)
end, Imps),
test_files(ImpsFN, ImpsFN);
{value,{_,Imp=[IC|_]}} when is_integer(IC) ->
- ImpFN = filename:absname(Imp),
+ ImpFN = filename:absname(Imp,Dir),
test_files([ImpFN], [ImpFN]);
_ ->
[]
@@ -149,9 +149,9 @@ get_spec_test(File) ->
Export =
case lists:keysearch(export, 1, Terms) of
{value,{_,Exp=[EC|_]}} when is_integer(EC) ->
- filename:absname(Exp);
+ filename:absname(Exp,Dir);
{value,{_,[Exp]}} ->
- filename:absname(Exp);
+ filename:absname(Exp,Dir);
_ ->
undefined
end,
@@ -179,7 +179,7 @@ get_spec_test(File) ->
E;
[CoverSpec] ->
CoverSpec1 = remove_excludes_and_dups(CoverSpec),
- {FullName,Nodes,Import,Export,CoverSpec1};
+ {File,Nodes,Import,Export,CoverSpec1};
_ ->
{error,multiple_apps_in_cover_spec}
end;
@@ -190,7 +190,7 @@ get_spec_test(File) ->
{error,{invalid_cover_spec,Error}}
end;
false ->
- {error,{cant_read_cover_spec_file,FullName}}
+ {error,{cant_read_cover_spec_file,File}}
end.
collect_apps([{level,Level}|Ts], Apps) ->
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
new file mode 100644
index 0000000000..52acda5388
--- /dev/null
+++ b/lib/common_test/src/ct_property_test.erl
@@ -0,0 +1,186 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%% @doc EXPERIMENTAL support in common-test for calling property based tests.
+%%%
+%%% <p>This module is a first step towards running Property Based testing in the
+%%% Common Test framework. A property testing tool like QuickCheck or PropEr is
+%%% assumed to be installed.</p>
+%%%
+%%% <p>The idea is to have a common_test testsuite calling a property testing
+%%% tool with special property test suites as defined by that tool. In this manual
+%%% we assume the usual Erlang Application directory structure. The tests are
+%%% collected in the application's <c>test</c> directory. The test directory
+%%% has a sub-directory called <c>property_test</c> where everything needed for
+%%% the property tests are collected.</p>
+%%%
+%%% <p>A typical ct test suite using <c>ct_property_test</c> is organized as follows:
+%%% </p>
+%%% ```
+%%% -include_lib("common_test/include/ct.hrl").
+%%%
+%%% all() -> [prop_ftp_case].
+%%%
+%%% init_per_suite(Config) ->
+%%% ct_property_test:init_per_suite(Config).
+%%%
+%%% %%%---- test case
+%%% prop_ftp_case(Config) ->
+%%% ct_property_test:quickcheck(
+%%% ftp_simple_client_server:prop_ftp(Config),
+%%% Config
+%%% ).
+%%% '''
+%%%
+%%% <warning>
+%%% <p>
+%%% This is experimental code which may be changed or removed
+%%% anytime without any warning.
+%%% </p>
+%%% </warning>
+%%%
+%%% @end
+
+-module(ct_property_test).
+
+%% API
+-export([init_per_suite/1,
+ quickcheck/2]).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%%-----------------------------------------------------------------
+%%% @spec init_per_suite(Config) -> Config | {skip,Reason}
+%%%
+%%% @doc Initializes Config for property testing.
+%%%
+%%% <p>The function investigates if support is available for either Quickcheck, PropEr,
+%%% or Triq.
+%%% The options <c>{property_dir,AbsPath}</c> and
+%%% <c>{property_test_tool,Tool}</c> is set in the Config returned.</p>
+%%% <p>The function is intended to be called in the init_per_suite in the test suite.</p>
+%%% <p>The property tests are assumed to be in the subdirectory <c>property_test</c>.</p>
+%%% @end
+
+init_per_suite(Config) ->
+ case which_module_exists([eqc,proper,triq]) of
+ {ok,ToolModule} ->
+ ct:pal("Found property tester ~p",[ToolModule]),
+ Path = property_tests_path("property_test", Config),
+ case compile_tests(Path,ToolModule) of
+ error ->
+ {fail, "Property test compilation failed in "++Path};
+ up_to_date ->
+ add_code_pathz(Path),
+ [{property_dir,Path},
+ {property_test_tool,ToolModule} | Config]
+ end;
+
+ not_found ->
+ ct:pal("No property tester found",[]),
+ {skip, "No property testing tool found"}
+ end.
+
+%%%-----------------------------------------------------------------
+%%% @spec quickcheck(Property, Config) -> true | {fail,Reason}
+%%%
+%%% @doc Call quickcheck and return the result in a form suitable for common_test.
+%%%
+%%% <p>The function is intended to be called in the test cases in the test suite.</p>
+%%% @end
+
+quickcheck(Property, Config) ->
+ Tool = proplists:get_value(property_test_tool,Config),
+ F = function_name(quickcheck, Tool),
+ mk_ct_return( Tool:F(Property), Tool ).
+
+
+%%%================================================================
+%%%
+%%% Local functions
+%%%
+
+%%% Make return values back to the calling Common Test suite
+mk_ct_return(true, _Tool) ->
+ true;
+mk_ct_return(Other, Tool) ->
+ try lists:last(hd(Tool:counterexample()))
+ of
+ {set,{var,_},{call,M,F,Args}} ->
+ {fail, io_lib:format("~p:~p/~p returned bad result",[M,F,length(Args)])}
+ catch
+ _:_ ->
+ {fail, Other}
+ end.
+
+%%% Check if a property testing tool is found
+which_module_exists([Module|Modules]) ->
+ case module_exists(Module) of
+ true -> {ok,Module};
+ false -> which_module_exists(Modules)
+ end;
+which_module_exists(_) ->
+ not_found.
+
+module_exists(Module) ->
+ is_list(catch Module:module_info()).
+
+%%% The path to the property tests
+property_tests_path(Dir, Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ filename:join(lists:droplast(filename:split(DataDir))++[Dir]).
+
+%%% Extend the code path with Dir if it not already present
+add_code_pathz(Dir) ->
+ case lists:member(Dir, code:get_path()) of
+ true -> ok;
+ false -> code:add_pathz(Dir)
+ end.
+
+compile_tests(Path, ToolModule) ->
+ MacroDefs = macro_def(ToolModule),
+ {ok,Cwd} = file:get_cwd(),
+ ok = file:set_cwd(Path),
+ {ok,FileNames} = file:list_dir("."),
+ BeamFiles = [F || F<-FileNames,
+ filename:extension(F) == ".beam"],
+ [file:delete(F) || F<-BeamFiles],
+ ct:pal("Compiling in ~p:~n Deleted ~p~n MacroDefs=~p",[Path,BeamFiles,MacroDefs]),
+ Result = make:all([load|MacroDefs]),
+ file:set_cwd(Cwd),
+ Result.
+
+
+macro_def(eqc) -> [{d, 'EQC'}];
+macro_def(proper) -> [{d, 'PROPER'}];
+macro_def(triq) -> [{d, 'TRIQ'}].
+
+function_name(quickcheck, triq) -> check;
+function_name(F, _) -> F.
+
diff --git a/lib/common_test/test/ct_cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE.erl
index 47080b5577..87ba4ae1b9 100644
--- a/lib/common_test/test/ct_cover_SUITE.erl
+++ b/lib/common_test/test/ct_cover_SUITE.erl
@@ -76,7 +76,8 @@ all() ->
cover_node_option,
ct_cover_add_remove_nodes,
otp_9956,
- cross
+ cross,
+ export_import
].
%%--------------------------------------------------------------------
@@ -199,6 +200,20 @@ cross(Config) ->
ok.
+export_import(Config) ->
+ DataDir = ?config(data_dir,Config),
+ false = check_cover(Config),
+ CoverSpec1 =
+ default_cover_file_content() ++ [{export,"export_import.coverdata"}],
+ CoverFile1 = create_cover_file(export_import1,CoverSpec1,Config),
+ {ok,Events1} = run_test(export_import1,default,[{cover,CoverFile1}],Config),
+ check_calls(Events1,1),
+ CoverSpec2 =
+ default_cover_file_content() ++ [{import,"export_import.coverdata"}],
+ CoverFile2 = create_cover_file(export_import2,CoverSpec2,Config),
+ {ok,Events2} = run_test(export_import2,default,[{cover,CoverFile2}],Config),
+ check_calls(Events2,2),
+ ok.
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index def8a6a6f4..00c0925b40 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.8.1
+COMMON_TEST_VSN = 1.8.2
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 55e9661d7d..d48a0a5599 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -31,6 +31,22 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 5.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Corrected a bug with incorrect code generation when
+ inlining was turned on.</p>
+ <p>
+ Own Id: OTP-12132</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 5.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index ce40213bad..82817a987a 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -2248,23 +2248,23 @@ letify(#c_var{name=Vname}=Var, Val, Body) ->
%% opt_case_in_let(LetExpr) -> LetExpr'
-opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let) ->
- opt_case_in_let_0(Vs, Arg, B, Let).
+opt_case_in_let(#c_let{vars=Vs,arg=Arg,body=B}=Let, Sub) ->
+ opt_case_in_let_0(Vs, Arg, B, Let, Sub).
opt_case_in_let_0([#c_var{name=V}], Arg,
- #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let) ->
+ #c_case{arg=#c_var{name=V},clauses=Cs}=Case, Let, Sub) ->
case opt_case_in_let_1(V, Arg, Cs) of
impossible ->
case is_simple_case_arg(Arg) andalso
not core_lib:is_var_used(V, Case#c_case{arg=#c_literal{val=nil}}) of
true ->
- expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new());
+ expr(opt_bool_case(Case#c_case{arg=Arg,clauses=Cs}), sub_new(Sub));
false ->
Let
end;
Expr -> Expr
end;
-opt_case_in_let_0(_, _, _, Let) -> Let.
+opt_case_in_let_0(_, _, _, Let, _) -> Let.
opt_case_in_let_1(V, Arg, Cs) ->
try
@@ -2607,7 +2607,7 @@ opt_simple_let_2(Let0, Vs0, Arg0, Body0, effect, Sub) ->
expr(#c_seq{arg=Arg,body=Body}, effect, sub_new_preserve_types(Sub));
true ->
Let = Let0#c_let{vars=Vs,arg=Arg,body=Body},
- opt_case_in_let_arg(opt_case_in_let(Let), effect, Sub)
+ opt_case_in_let_arg(opt_case_in_let(Let, Sub), effect, Sub)
end
end;
opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
@@ -2630,7 +2630,7 @@ opt_simple_let_2(Let, Vs0, Arg0, Body, value, Sub) ->
expr(#c_seq{arg=Arg,body=Body}, value, sub_new_preserve_types(Sub));
{Vs,Arg,Body} ->
opt_case_in_let_arg(
- opt_case_in_let(Let#c_let{vars=Vs,arg=Arg,body=Body}),
+ opt_case_in_let(Let#c_let{vars=Vs,arg=Arg,body=Body}, Sub),
value, Sub)
end.
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index 0a86352f40..d042596557 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 5.0.1
+COMPILER_VSN = 5.0.2
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index e55a03d26a..e7215eeb64 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -462,9 +462,11 @@ static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*);
/*
#define PRINTF_ERR0(FMT) enif_fprintf(stderr, FMT "\n")
#define PRINTF_ERR1(FMT, A1) enif_fprintf(stderr, FMT "\n", A1)
+#define PRINTF_ERR2(FMT, A1, A2) enif_fprintf(stderr, FMT "\n", A1, A2)
*/
#define PRINTF_ERR0(FMT)
#define PRINTF_ERR1(FMT,A1)
+#define PRINTF_ERR2(FMT,A1,A2)
#ifdef __OSE__
@@ -506,6 +508,23 @@ static int init_ose_crypto() {
#define CHECK_OSE_CRYPTO()
#endif
+
+static int verify_lib_version(void)
+{
+ const unsigned long libv = SSLeay();
+ const unsigned long hdrv = OPENSSL_VERSION_NUMBER;
+
+# define MAJOR_VER(V) ((unsigned long)(V) >> (7*4))
+
+ if (MAJOR_VER(libv) != MAJOR_VER(hdrv)) {
+ PRINTF_ERR2("CRYPTO: INCOMPATIBLE SSL VERSION"
+ " lib=%lx header=%lx\n", libv, hdrv);
+ return 0;
+ }
+ return 1;
+}
+
+
#ifdef HAVE_DYNAMIC_CRYPTO_LIB
# if defined(DEBUG)
@@ -554,6 +573,9 @@ static int init(ErlNifEnv* env, ERL_NIF_TERM load_info)
if (!INIT_OSE_CRYPTO())
return 0;
+ if (!verify_lib_version())
+ return 0;
+
/* load_info: {301, <<"/full/path/of/this/library">>} */
if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)
|| tpl_arity != 2
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 7712173ed8..98384978a5 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -693,7 +693,7 @@
<p>Decrypts <c>CipherText</c> according to the stream cipher <c>Type</c> specified in stream_init/3.
<c>PlainText</c> can be any number of bytes. The initial <c>State</c> is created using
<seealso marker="#stream_init-2">stream_init</seealso>.
- <c>NewState</c> must be passed into the next call to <c>stream_encrypt</c>.</p>
+ <c>NewState</c> must be passed into the next call to <c>stream_decrypt</c>.</p>
</desc>
</func>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 1bd2034b93..82b6de9acd 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -30,6 +30,23 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 3.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Make <c>crypto</c> verify major version number of OpenSSL
+ header files and runtime library. Loading of
+ <c>crypto</c> will fail if there is a version mismatch.</p>
+ <p>
+ Own Id: OTP-12146 Aux Id: seq12700 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 3.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index b2bb1d7dfb..2a7f3c4558 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 3.4
+CRYPTO_VSN = 3.4.1
diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile
index 90189dd297..d61519f1ad 100644
--- a/lib/debugger/src/Makefile
+++ b/lib/debugger/src/Makefile
@@ -63,7 +63,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/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 4e26a9e95e..e482b1e6f8 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -238,7 +238,10 @@
<item>Include warnings for functions that only return by means of an
exception.</item>
<tag><c><![CDATA[-Wrace_conditions]]></c>***</tag>
- <item>Include warnings for possible race conditions.</item>
+ <item>Include warnings for possible race conditions. Note that the
+ analysis that finds data races performs intra-procedural data flow analysis
+ and can sometimes explode in time. Enable it at your own risk.
+ </item>
<tag><c><![CDATA[-Wunderspecs]]></c>***</tag>
<item>Warn about underspecified functions
(the -spec is strictly more allowing than the success typing).</item>
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index bdd9c61c5c..d35639aa32 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -31,6 +31,36 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> A bug concerning <c>is_record/2,3</c> has been fixed,
+ as well as some cases where Dialyzer could crash due to
+ reaching system limits. </p>
+ <p>
+ Own Id: OTP-12018</p>
+ </item>
+ <item>
+ <p> When given the <c>-Wunderspecs</c> flag Dialyzer
+ sometimes output bogus warnings for parametrized types.
+ This bug has been fixed. </p>
+ <p>
+ Own Id: OTP-12111</p>
+ </item>
+ <item>
+ <p>Dialyzer now fetch the compile options from beam
+ files, and use them when creating core from the abstract
+ code. Previously the options were ignored. </p>
+ <p>
+ Own Id: OTP-12150</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.7.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 6a33a2acb3..af1c2b7e3a 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -373,7 +373,16 @@ compile_byte(File, Callgraph, CServer, UseContracts) ->
{error, " Could not get abstract code for: " ++ File ++ "\n" ++
" Recompile with +debug_info or analyze starting from source code"};
{ok, AbstrCode} ->
- compile_common(File, AbstrCode, [], Callgraph, CServer, UseContracts)
+ compile_byte(File, AbstrCode, Callgraph, CServer, UseContracts)
+ end.
+
+compile_byte(File, AbstrCode, Callgraph, CServer, UseContracts) ->
+ case dialyzer_utils:get_compile_options_from_beam(File) of
+ error ->
+ {error, " Could not get compile options for: " ++ File ++ "\n" ++
+ " Recompile or analyze starting from source code"};
+ {ok, CompOpts} ->
+ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts)
end.
compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts) ->
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 1d2dfc7b2d..f27fc1a842 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -20,8 +20,6 @@
-module(dialyzer_contracts).
--compile(export_all).
-
-export([check_contract/2,
check_contracts/4,
contracts_without_fun/3,
@@ -686,7 +684,7 @@ picky_contract_check(CSig0, Sig0, MFA, FileLine, Contract, RecDict, Acc) ->
true -> Acc;
false ->
case extra_contract_warning(MFA, FileLine, Contract,
- CSig, Sig, RecDict) of
+ CSig0, Sig0, RecDict) of
no_warning -> Acc;
{warning, Warning} -> [Warning|Acc]
end
@@ -752,7 +750,8 @@ is_remote_types_related(Contract, CSig, Sig, RecDict) ->
t_from_forms_without_remote([{FType, []}], RecDict) ->
Type0 = erl_types:t_from_form(FType, RecDict),
- {ok, erl_types:subst_all_remote(Type0, erl_types:t_none())};
+ Type1 = erl_types:subst_all_remote(Type0, erl_types:t_none()),
+ {ok, erl_types:subst_all_vars_to_any(Type1)};
t_from_forms_without_remote([{_FType, _Constrs}], _RecDict) ->
%% 'When' constraints
unsupported;
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 92aab68ad6..03005e689f 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -93,6 +93,8 @@
-define(TYPE_LIMIT, 3).
+-define(BITS, 128).
+
-record(state, {callgraph :: dialyzer_callgraph:callgraph(),
envs :: env_tab(),
fun_tab :: fun_tab(),
@@ -1610,10 +1612,18 @@ bind_bin_segs([Seg|Segs], BinType, Acc, Map, State) ->
SizeVal = lists:max(List),
Flags = cerl:concrete(cerl:bitstr_flags(Seg)),
N = SizeVal * UnitVal,
- case lists:member(signed, Flags) of
- true -> t_from_range(-(1 bsl (N - 1)), 1 bsl (N - 1) - 1);
- false -> t_from_range(0, 1 bsl N - 1)
- end
+ case N >= ?BITS of
+ true ->
+ case lists:member(signed, Flags) of
+ true -> t_from_range(neg_inf, pos_inf);
+ false -> t_from_range(0, pos_inf)
+ end;
+ false ->
+ case lists:member(signed, Flags) of
+ true -> t_from_range(-(1 bsl (N - 1)), 1 bsl (N - 1) - 1);
+ false -> t_from_range(0, 1 bsl N - 1)
+ end
+ end
end
end,
{Map2, [_]} = bind_pat_vars([Val], [ValConstr], [], Map1, State, false),
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index e1bcd72c0b..4e2ec67b35 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -31,6 +31,7 @@
format_sig/1,
format_sig/2,
get_abstract_code_from_beam/1,
+ get_compile_options_from_beam/1,
get_abstract_code_from_src/1,
get_abstract_code_from_src/2,
get_core_from_abstract_code/1,
@@ -136,6 +137,26 @@ get_abstract_code_from_beam(File) ->
error
end.
+-spec get_compile_options_from_beam(file:filename()) -> 'error' | {'ok', [compile:option()]}.
+
+get_compile_options_from_beam(File) ->
+ case beam_lib:chunks(File, [compile_info]) of
+ {ok, {_, List}} ->
+ case lists:keyfind(compile_info, 1, List) of
+ {compile_info, CompInfo} -> compile_info_to_options(CompInfo);
+ _ -> error
+ end;
+ _ ->
+ %% No or unsuitable compile info.
+ error
+ end.
+
+compile_info_to_options(CompInfo) ->
+ case lists:keyfind(options, 1, CompInfo) of
+ {options, CompOpts} -> {ok, CompOpts};
+ _ -> error
+ end.
+
-type get_core_from_abs_ret() :: {'ok', cerl:c_module()} | 'error'.
-spec get_core_from_abstract_code(abstract_code()) -> get_core_from_abs_ret().
@@ -150,7 +171,9 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
%% performed them. In some cases we end up in trouble when
%% performing them again.
AbstrCode1 = cleanup_parse_transforms(AbstrCode),
- try compile:forms(AbstrCode1, Opts ++ src_compiler_opts()) of
+ %% Remove parse_transforms (and other options) from compile options.
+ Opts2 = cleanup_compile_options(Opts),
+ try compile:forms(AbstrCode1, Opts2 ++ src_compiler_opts()) of
{ok, _, Core} -> {ok, Core};
_What -> error
catch
@@ -419,6 +442,24 @@ cleanup_parse_transforms([Other|Left]) ->
cleanup_parse_transforms([]) ->
[].
+-spec cleanup_compile_options([compile:option()]) -> [compile:option()].
+
+%% Using abstract, not asm or core.
+cleanup_compile_options([from_asm|Opts]) ->
+ Opts;
+cleanup_compile_options([asm|Opts]) ->
+ Opts;
+cleanup_compile_options([from_core|Opts]) ->
+ Opts;
+%% The parse transform will already have been applied, may cause problems if it
+%% is re-applied.
+cleanup_compile_options([{parse_transform, _}|Opts]) ->
+ Opts;
+cleanup_compile_options([Other|Opts]) ->
+ [Other|cleanup_compile_options(Opts)];
+cleanup_compile_options([]) ->
+ [].
+
-spec format_errors([{module(), string()}]) -> [string()].
format_errors([{Mod, Errors}|Left]) ->
diff --git a/lib/dialyzer/test/dialyzer_SUITE.erl b/lib/dialyzer/test/dialyzer_SUITE.erl
index 1b62291a00..8507525597 100644
--- a/lib/dialyzer/test/dialyzer_SUITE.erl
+++ b/lib/dialyzer/test/dialyzer_SUITE.erl
@@ -30,12 +30,12 @@
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
--export([app_test/1, appup_test/1]).
+-export([app_test/1, appup_test/1, beam_tests/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test, appup_test].
+ [app_test, appup_test, beam_tests].
groups() ->
[].
@@ -75,3 +75,38 @@ app_test(Config) when is_list(Config) ->
%% Test that the .appup file does not contain any `basic' errors
appup_test(Config) when is_list(Config) ->
ok = ?t:appup_test(dialyzer).
+
+beam_tests(Config) when is_list(Config) ->
+ Prog = <<"
+ -module(no_auto_import).
+
+ %% Copied from erl_lint_SUITE.erl, clash6
+
+ -export([size/1]).
+
+ size([]) ->
+ 0;
+ size({N,_}) ->
+ N;
+ size([_|T]) ->
+ 1+size(T).
+ ">>,
+ Opts = [no_auto_import],
+ {ok, BeamFile} = compile(Config, Prog, no_auto_import, Opts),
+ [] = run_dialyzer([BeamFile]),
+ ok.
+
+compile(Config, Prog, Module, CompileOpts) ->
+ Source = lists:concat([Module, ".erl"]),
+ PrivDir = ?config(priv_dir,Config),
+ Filename = filename:join([PrivDir, Source]),
+ ok = file:write_file(Filename, Prog),
+ Opts = [{outdir, PrivDir}, debug_info | CompileOpts],
+ {ok, Module} = compile:file(Filename, Opts),
+ {ok, filename:join([PrivDir, lists:concat([Module, ".beam"])])}.
+
+run_dialyzer(Files) ->
+ dialyzer:run([{analysis_type, plt_build},
+ {files, Files},
+ {from, byte_code},
+ {check_plt, false}]).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug5.erl b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug5.erl
new file mode 100644
index 0000000000..28d739de8e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/opaque/opaque_bug5.erl
@@ -0,0 +1,10 @@
+%% Second arg of is_record call wasn't checked properly
+
+-module(opaque_bug5).
+
+-export([b/0]).
+
+b() ->
+ is_record(id({a}), id(a)).
+
+id(I) -> I.
diff --git a/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring
new file mode 100644
index 0000000000..0ad6eee766
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/pretty_bitstring
@@ -0,0 +1,3 @@
+
+pretty_bitstring.erl:7: Function t/0 has no local return
+pretty_bitstring.erl:8: The call binary:copy(#{#<1>(8, 1, 'integer', ['unsigned', 'big']), #<2>(8, 1, 'integer', ['unsigned', 'big']), #<3>(3, 1, 'integer', ['unsigned', 'big'])}#,2) breaks the contract (Subject,N) -> binary() when is_subtype(Subject,binary()), is_subtype(N,non_neg_integer())
diff --git a/lib/dialyzer/test/small_SUITE_data/src/limit.erl b/lib/dialyzer/test/small_SUITE_data/src/limit.erl
new file mode 100644
index 0000000000..97ee585b77
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/limit.erl
@@ -0,0 +1,20 @@
+%% Misc cases where Dialyzer would fail with system_limit or crash
+
+-module(limit).
+
+-export([tu/0, big/1, b2/0]).
+
+tu() ->
+ erlang:make_tuple(1 bsl 24, def, [{5,e},{1,a},{3,c}]).
+
+big(<<Int:1152921504606846976/unit:128,0,_/binary>>) -> {5,Int}.
+
+b2() ->
+ Maxbig = maxbig(),
+ _ = bnot Maxbig,
+ ok.
+
+maxbig() ->
+ %% We assume that the maximum arity is (1 bsl 19) - 1.
+ Ws = erlang:system_info(wordsize),
+ (((1 bsl ((16777184 * (Ws div 4))-1)) - 1) bsl 1) + 1.
diff --git a/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl b/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl
new file mode 100644
index 0000000000..3dbf5ab7a7
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/pretty_bitstring.erl
@@ -0,0 +1,8 @@
+%% Prettyprint bitstrings.
+
+-module(pretty_bitstring).
+
+-export([t/0]).
+
+t() ->
+ binary:copy(<<1,2,3:3>>,2).
diff --git a/lib/dialyzer/test/underspecs_SUITE_data/results/arr b/lib/dialyzer/test/underspecs_SUITE_data/results/arr
new file mode 100644
index 0000000000..9497d12eec
--- /dev/null
+++ b/lib/dialyzer/test/underspecs_SUITE_data/results/arr
@@ -0,0 +1,4 @@
+
+arr.erl:14: Type specification arr:test2(array:array(T),non_neg_integer(),T) -> array:array(T) is a supertype of the success typing: arr:test2(array:array(_),pos_integer(),_) -> array:array(_)
+arr.erl:24: Type specification arr:test4(array:array(T),non_neg_integer(),_) -> array:array(T) is a supertype of the success typing: arr:test4(array:array(_),pos_integer(),_) -> array:array(_)
+arr.erl:29: Type specification arr:test5(array:array(T),non_neg_integer(),T) -> array:array(T) is a supertype of the success typing: arr:test5(array:array(_),non_neg_integer(),integer()) -> array:array(_)
diff --git a/lib/dialyzer/test/underspecs_SUITE_data/src/arr.erl b/lib/dialyzer/test/underspecs_SUITE_data/src/arr.erl
new file mode 100644
index 0000000000..3b265ccec2
--- /dev/null
+++ b/lib/dialyzer/test/underspecs_SUITE_data/src/arr.erl
@@ -0,0 +1,41 @@
+-module(arr).
+
+%% http://erlang.org/pipermail/erlang-questions/2014-August/080445.html
+
+-define(A, array).
+
+-export([test/3, test2/3, test3/3, test4/3, test5/3, test6/3]).
+
+-spec test(?A:array(T), non_neg_integer(), T) -> ?A:array(T).
+
+test(Array, N, Value) ->
+ ?A:set(N, Value, Array).
+
+-spec test2(?A:array(T), non_neg_integer(), T) -> ?A:array(T).
+
+test2(Array, N, Value) when N > 0 ->
+ ?A:set(N, Value, Array).
+
+-spec test3(?A:array(T), non_neg_integer(), _) -> ?A:array(T).
+
+test3(Array, N, Value) ->
+ ?A:set(N, Value, Array).
+
+-spec test4(?A:array(T), non_neg_integer(), _) -> ?A:array(T).
+
+test4(Array, N, Value) when N > 0 ->
+ ?A:set(N, Value, Array).
+
+-spec test5(?A:array(T), non_neg_integer(), T) -> ?A:array(T).
+
+test5(Array, N, Value) when is_integer(Value) ->
+ ?A:set(N, Value, Array).
+
+%% One would ideally want a warning also for test6(), but the current
+%% analysis of parametrized opaque types is not strong enough to
+%% discover this.
+-spec test6(?A:array(integer()), non_neg_integer(), integer()) ->
+ ?A:array(any()).
+
+test6(Array, N, Value) ->
+ ?A:set(N, Value, Array).
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index b0cb3ec4f9..58cc77c2fa 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.7.1
+DIALYZER_VSN = 2.7.2
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index d89e1dfd26..7f69bdbfbf 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -42,6 +42,59 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Don't leave extra bit in decoded AVP data.</p>
+ <p>
+ An extra bit could be communicated in the data field of a
+ diameter_avp record in the case of length errors. Of no
+ consequence for code using the record encoding of
+ Diameter messages, but code examining diameter_avp
+ records would see this bit.</p>
+ <p>
+ Dictionary files must be recompiled for the fix to have
+ effect.</p>
+ <p>
+ Own Id: OTP-12074</p>
+ </item>
+ <item>
+ <p>
+ Fix counting of outgoing requests and answers setting the
+ E-bit.</p>
+ <p>
+ OTP-11721 broke these counters for all outgoing requests
+ except DWR, and caused answers setting the E-bit to be
+ counted as unknown messages.</p>
+ <p>
+ Own Id: OTP-12080</p>
+ </item>
+ <item>
+ <p>
+ Fix Failed-AVP decode.</p>
+ <p>
+ The best-effort decode only worked for AVPs in the common
+ dictionary, not for those in the dictionary of the
+ application identified in the Diameter Header of the
+ answer message in question.</p>
+ <p>
+ Failed-AVP in an answer decoded with the RFC 3588 common
+ dictionary (diameter_gen_base_rfc3588) was regarded as an
+ error. The RFC 6733 dictionary was unaffected.</p>
+ <p>
+ Dictionary files must be recompiled for the fix to have
+ effect.</p>
+ <p>
+ Own Id: OTP-12094</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -114,6 +167,9 @@ first.</p>
M-bit, resulting in 5001) failed if the AVP contained a
complete header.</p>
<p>
+ Dictionary files must be recompiled for the fix to have
+ effect.</p>
+ <p>
Own Id: OTP-11946</p>
</item>
<item>
@@ -152,6 +208,9 @@ first.</p>
otherwise not. AVPs of type Grouped are decoded as much
as possible, as deeply as possible.</p>
<p>
+ Dictionary files must be recompiled for the fix to have
+ effect.</p>
+ <p>
Own Id: OTP-11936 Aux Id: OTP-11007 </p>
</item>
<item>
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index 7e91ce375f..bc25f7d472 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -311,19 +311,55 @@ d(Name, Avp, Acc) ->
Failed = relax(Name), %% Not AvpName or else a failed Failed-AVP
%% decode is packed into 'AVP'.
- try avp(decode, Data, AvpName) of
+ Mod = dict(Failed), %% Dictionary to decode in.
+
+ try Mod:avp(decode, Data, AvpName) of
V ->
{Avps, T} = Acc,
{H, A} = ungroup(V, Avp),
{[H | Avps], pack_avp(Name, A, T)}
catch
error: Reason ->
- d(undefined == Failed orelse is_failed(), Reason, Name, Avp, Acc)
+ d(undefined == Failed orelse is_failed(),
+ Reason,
+ Name,
+ trim(Avp),
+ Acc)
after
reset(?STRICT_KEY, Strict),
reset(?FAILED_KEY, Failed)
end.
+%% trim/1
+%%
+%% Remove any extra bit that was added in diameter_codec to induce a
+%% 5014 error.
+
+trim(#diameter_avp{data = <<0:1, Bin/binary>>} = Avp) ->
+ Avp#diameter_avp{data = Bin};
+
+trim(Avp) ->
+ Avp.
+
+%% dict/1
+%%
+%% Retrieve the dictionary for the best-effort decode of Failed-AVP,
+%% as put by diameter_codec:decode/2. See that function for the
+%% explanation.
+
+dict(true) ->
+ case get({diameter_codec, dictionary}) of
+ undefined ->
+ ?MODULE;
+ Mod ->
+ Mod
+ end;
+
+dict(_) ->
+ ?MODULE.
+
+%% d/5
+
%% Ignore a decode error within Failed-AVP ...
d(true, _, Name, Avp, Acc) ->
decode_AVP(Name, Avp, Acc);
@@ -341,6 +377,8 @@ d(false, Reason, Name, Avp, {Avps, Acc}) ->
{Rec, Failed} = Acc,
{[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}.
+%% relax/2
+
%% Set false in the process dictionary as soon as we see a Grouped AVP
%% that doesn't set the M-bit, so that is_strict() can say whether or
%% not to ignore the M-bit on an encapsulated AVP.
@@ -357,22 +395,23 @@ relax(_, _) ->
is_strict() ->
false /= getr(?STRICT_KEY).
+%% relax/1
+%%
%% Set true in the process dictionary as soon as we see Failed-AVP.
%% Matching on 'Failed-AVP' assumes that this is the RFC AVP.
%% Strictly, this doesn't need to be the case.
+
relax('Failed-AVP') ->
- case getr(?FAILED_KEY) of
- undefined ->
- putr(?FAILED_KEY, true);
- true = Yes ->
- Yes
- end;
+ is_failed() orelse putr(?FAILED_KEY, true);
+
relax(_) ->
is_failed().
is_failed() ->
true == getr(?FAILED_KEY).
+%% reset/2
+
reset(Key, undefined) ->
eraser(Key);
reset(_, _) ->
@@ -453,8 +492,8 @@ pack_AVP(_, #diameter_avp{data = <<0:1, Data/binary>>} = Avp, Acc) ->
{Rec, Failed} = Acc,
{Rec, [{5014, Avp#diameter_avp{data = Data}} | Failed]};
-pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
- case pack_arity(Name, M) of
+pack_AVP(Name, #diameter_avp{is_mandatory = M, name = AvpName} = Avp, Acc) ->
+ case pack_arity(Name, AvpName, M) of
0 ->
{Rec, Failed} = Acc,
{Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]};
@@ -462,10 +501,13 @@ pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) ->
pack(Arity, 'AVP', Avp, Acc)
end.
-%% Give Failed-AVP special treatment since it'll contain any
-%% unrecognized mandatory AVP's.
-pack_arity(Name, M) ->
- NF = Name /= 'Failed-AVP' andalso not is_failed(),
+%% Give Failed-AVP special treatment since (1) it'll contain any
+%% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to
+%% allow for Failed-AVP in an answer-message.
+
+pack_arity(Name, AvpName, M) ->
+ IsFailed = Name == 'Failed-AVP' orelse is_failed(),
+
%% Not testing just Name /= 'Failed-AVP' means we're changing the
%% packing of AVPs nested within Failed-AVP, but the point of
%% ignoring errors within Failed-AVP is to decode as much as
@@ -473,12 +515,18 @@ pack_arity(Name, M) ->
%% packed into a dedicated field defeats that point. Note that we
%% can't just test not is_failed() since this will be 'true' when
%% packing an unknown AVP directly within Failed-AVP.
- case NF andalso M andalso is_strict() of
- true ->
- 0;
- false ->
- avp_arity(Name, 'AVP')
- end.
+
+ pack_arity(IsFailed
+ orelse {Name, AvpName} == {'answer-message', 'Failed-AVP'}
+ orelse not M
+ orelse not is_strict(),
+ Name).
+
+pack_arity(true, Name) ->
+ avp_arity(Name, 'AVP');
+
+pack_arity(false, _) ->
+ 0.
%% 3588:
%%
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 06a4f5de64..a2b04bfd63 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -237,15 +237,35 @@ rec2msg(Mod, Rec) ->
%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
--spec decode(module(), #diameter_packet{} | binary())
+-spec decode(module() | {module(), module()}, #diameter_packet{} | binary())
-> #diameter_packet{}.
+%% An Answer setting the E-bit. The application dictionary is needed
+%% for the best-effort decode of Failed-AVP, and the best way to make
+%% this available to the AVP decode in diameter_gen.hrl, without
+%% having to rewrite the entire codec generation, is to place it in
+%% the process dictionary. It's the code in diameter_gen.hrl (that's
+%% included by every generated codec module) that looks for the entry.
+%% Not ideal, but it solves the problem relatively simply.
+decode({Mod, Mod}, Pkt) ->
+ decode(Mod, Pkt);
+decode({Mod, AppMod}, Pkt) ->
+ Key = {?MODULE, dictionary},
+ put(Key, AppMod),
+ try
+ decode(Mod, Pkt)
+ after
+ erase(Key)
+ end;
+
+%% Or not: a request, or an answer not setting the E-bit.
decode(Mod, Pkt) ->
decode(Mod:id(), Mod, Pkt).
-%% If we're a relay application then just extract the avp's without
-%% any decoding of their data since we don't know the application in
-%% question.
+%% decode/3
+
+%% Relay application: just extract the avp's without any decoding of
+%% their data since we don't know the application in question.
decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
case collect_avps(Pkt) of
{E, As} ->
@@ -274,6 +294,8 @@ decode(Id, Mod, Bin)
when is_binary(Bin) ->
decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
+%% decode_avps/4
+
decode_avps(MsgName, Mod, Pkt, {E, Avps}) ->
?LOG(invalid_avp_length, Pkt#diameter_packet.header),
#diameter_packet{errors = Failed}
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 31e570ae20..86fc43cdc5 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -477,6 +477,7 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
hop_by_hop_id = Hid}}
= Pkt
= encode(CER, Dict),
+ incr(send, Pkt, Dict),
send(TPid, Pkt),
?LOG(send, 'CER'),
start_timer(Tmo, S#state{state = {'Wait-CEA', Hid, Eid}}).
@@ -1100,6 +1101,7 @@ send_dpr(Reason, Opts, #state{transport = TPid,
{'Origin-Realm', OR},
{'Disconnect-Cause', Cause}],
Dict),
+ incr(send, Pkt, Dict),
send(TPid, Pkt),
dpa_timer(Tmo),
?LOG(send, 'DPR'),
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index b7cd311e02..ab56ca9cef 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1573,7 +1573,8 @@ transports(#state{watchdogT = WatchdogT}) ->
-define(OTHER_INFO, [connections,
name,
peers,
- statistics]).
+ statistics,
+ info]).
service_info(Item, S)
when is_atom(Item) ->
@@ -1663,6 +1664,7 @@ complete_info(Item, #state{service = Svc} = S) ->
keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
statistics -> info_stats(S);
+ info -> info_info(S);
connections -> info_connections(S);
peers -> info_peers(S)
end.
@@ -1745,12 +1747,11 @@ peer_acc(PeerT, Acc, #watchdog{pid = Pid,
state = WS,
started = At,
peer = TPid}) ->
- dict:append(Ref,
- [{type, Type},
- {options, Opts},
- {watchdog, {Pid, At, WS}}
- | info_peer(PeerT, TPid, WS)],
- Acc).
+ Info = [{type, Type},
+ {options, Opts},
+ {watchdog, {Pid, At, WS}}
+ | info_peer(PeerT, TPid, WS)],
+ dict:append(Ref, Info ++ [{info, info_process_info(Info)}], Acc).
info_peer(PeerT, TPid, WS)
when is_pid(TPid), WS /= ?WD_DOWN ->
@@ -1762,6 +1763,49 @@ info_peer(PeerT, TPid, WS)
info_peer(_, _, _) ->
[].
+info_process_info(Info) ->
+ lists:flatmap(fun ipi/1, Info).
+
+ipi({watchdog, {Pid, _, _}}) ->
+ info_pid(Pid);
+
+ipi({peer, {Pid, _}}) ->
+ info_pid(Pid);
+
+ipi({port, [{owner, Pid} | _]}) ->
+ info_pid(Pid);
+
+ipi(_) ->
+ [].
+
+info_pid(Pid) ->
+ case process_info(Pid, [message_queue_len, memory, binary]) of
+ undefined ->
+ [];
+ L ->
+ [{Pid, lists:map(fun({K,V}) -> {K, map_info(K,V)} end, L)}]
+ end.
+
+%% The binary list consists of 3-tuples {Ptr, Size, Count}, where Ptr
+%% is a C pointer value, Size is the size of a referenced binary in
+%% bytes, and Count is a global reference count. The same Ptr can
+%% occur multiple times, once for each reference on the process heap.
+%% In this case, the corresponding tuples will have Size in common but
+%% Count may differ just because no global lock is taken when the
+%% value is retrieved.
+%%
+%% The list can be quite large, and we aren't often interested in the
+%% pointers or counts, so whittle this down to the number of binaries
+%% referenced and their total byte count.
+map_info(binary, L) ->
+ SzD = lists:foldl(fun({P,S,_}, D) -> dict:store(P,S,D) end,
+ dict:new(),
+ L),
+ {dict:size(SzD), dict:fold(fun(_,S,N) -> S + N end, 0, SzD)};
+
+map_info(_, T) ->
+ T.
+
%% The point of extracting the config here is so that 'transport' info
%% has one entry for each transport ref, the peer table only
%% containing entries that have a living watchdog.
@@ -1819,6 +1863,13 @@ mk_app(#diameter_app{} = A) ->
info_pending(#state{} = S) ->
diameter_traffic:pending(transports(S)).
+%% info_info/1
+%%
+%% Extract process_info from connections info.
+
+info_info(S) ->
+ [I || L <- conn_list(S), {info, I} <- L].
+
%% info_connections/1
%%
%% One entry per transport connection. Statistics for each entry are
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 5fac61f416..280d09d7e8 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -129,6 +129,11 @@ incr(Dir, #diameter_header{} = H, TPid, Dict) ->
%% incr_error/4
%% ---------------------------------------------------------------------------
+%% Identify messages using the application dictionary, not the encode
+%% dictionary, which may differ in the case of answer-message.
+incr_error(Dir, T, Pid, {_Dict, AppDict}) ->
+ incr_error(Dir, T, Pid, AppDict);
+
%% Decoded message without errors.
incr_error(recv, #diameter_packet{errors = []}, _, _) ->
ok;
@@ -169,7 +174,7 @@ incr_error(Dir, Id, TPid) ->
incr_rc(Dir, Pkt, TPid, Dict0) ->
try
- incr_rc(Dir, Pkt, Dict0, TPid, Dict0)
+ incr_result(Dir, Pkt, TPid, {Dict0, Dict0, Dict0})
catch
exit: {E,_} when E == no_result_code;
E == invalid_error_bit ->
@@ -471,7 +476,7 @@ send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
#diameter_packet{errors = [RC|_]} = Pkt,
send_A(answer_message(RC, Caps, Dict0, Pkt),
TPid,
- Dict0,
+ {Dict0, Dict0},
Pkt,
[],
[]);
@@ -479,7 +484,7 @@ send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
TPid,
- Dict0,
+ {App#diameter_app.dictionary, Dict0},
Pkt,
EvalPktFs,
EvalFs);
@@ -489,8 +494,8 @@ send_A(_, _, _, _) ->
%% send_A/6
-send_A(T, TPid, Dict0, ReqPkt, EvalPktFs, EvalFs) ->
- reply(T, TPid, Dict0, EvalPktFs, ReqPkt),
+send_A(T, TPid, DictT, ReqPkt, EvalPktFs, EvalFs) ->
+ reply(T, TPid, DictT, EvalPktFs, ReqPkt),
lists:foreach(fun diameter_lib:eval/1, EvalFs).
%% answer/6
@@ -648,32 +653,32 @@ is_loop(Code, Vid, OH, Dict0, Avps) ->
%% reply/5
%% Local answer ...
-reply({Dict, Ans}, TPid, Dict0, Fs, ReqPkt) ->
- reply(Ans, Dict, TPid, Dict0, Fs, ReqPkt);
+reply({Dict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
+ local(Ans, TPid, {Dict, AppDict, Dict0}, Fs, ReqPkt);
%% ... or relayed.
reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
eval_packet(Pkt, Fs),
send(TPid, Pkt).
-%% reply/6
+%% local/5
%%
%% Send a locally originating reply.
%% Skip the setting of Result-Code and Failed-AVP's below. This is
%% undocumented and shouldn't be relied on.
-reply([Msg], Dict, TPid, Dict0, Fs, ReqPkt)
+local([Msg], TPid, DictT, Fs, ReqPkt)
when is_list(Msg);
is_tuple(Msg) ->
- reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt#diameter_packet{errors = []});
+ local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt) ->
- Pkt = encode(Dict,
+local(Msg, TPid, {Dict, AppDict, Dict0} = DictT, Fs, ReqPkt) ->
+ Pkt = encode({Dict, AppDict},
TPid,
reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0),
Fs),
- incr(send, Pkt, TPid, Dict),
- incr_rc(send, Pkt, Dict, TPid, Dict0), %% count outgoing
+ incr(send, Pkt, TPid, AppDict),
+ incr_result(send, Pkt, TPid, DictT), %% count outgoing
send(TPid, Pkt).
%% reset/3
@@ -1038,29 +1043,29 @@ find(Pred, [H|T]) ->
%% code, the missing vendor id, and a zero filled payload of the minimum
%% required length for the omitted AVP will be added.
-%% incr_rc/5
+%% incr_result/5
%%
%% Increment a stats counter for result codes in incoming and outgoing
%% answers.
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr_rc(_, #diameter_packet{msg = undefined = No}, _, _, _) ->
+incr_result(_, #diameter_packet{msg = undefined = No}, _, _) ->
No;
%% Incoming or outgoing. Outgoing with encode errors never gets here
%% since encode fails.
-incr_rc(Dir, Pkt, Dict, TPid, Dict0) ->
+incr_result(Dir, Pkt, TPid, {Dict, AppDict, Dict0}) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Msg,
errors = Es}
= Pkt,
- Id = msg_id(Hdr, Dict),
+ Id = msg_id(Hdr, AppDict),
%% Count incoming decode errors.
- recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, Dict),
+ recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
%% Exit on a missing result code.
T = rc_counter(Dict, Msg),
@@ -1074,12 +1079,27 @@ incr_rc(Dir, Pkt, Dict, TPid, Dict0) ->
incr(TPid, {Id, Dir, Ctr}),
Ctr.
-%% Only count on known keeps so as not to be vulnerable to attack:
-%% there are 2^32 (application ids) * 2^24 (command codes) * 2 (R-bits)
-%% = 2^57 Ids for an attacker to choose from.
+%% msg_id/2
+
+msg_id(#diameter_packet{header = H}, Dict) ->
+ msg_id(H, Dict);
+
+%% Only count on known keys so as not to be vulnerable to attack:
+%% there are 2^32 (application ids) * 2^24 (command codes) = 2^56
+%% pairs for an attacker to choose from.
msg_id(Hdr, Dict) ->
{_ApplId, Code, R} = Id = diameter_codec:msg_id(Hdr),
- choose('' == Dict:msg_name(Code, 0 == R), unknown, Id).
+ case Dict:msg_name(Code, 0 == R) of
+ '' ->
+ unknown(Dict:id(), R);
+ _ ->
+ Id
+ end.
+
+unknown(?APP_ID_RELAY, R) ->
+ {relay, R};
+unknown(_, _) ->
+ unknown.
%% No E-bit: can't be 3xxx.
is_result(RC, false, _Dict0) ->
@@ -1396,6 +1416,7 @@ send_R(Pkt0,
packet = Pkt0},
try
+ incr(send, Pkt, TPid, Dict),
TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
handle_answer(SvcName,
@@ -1431,14 +1452,14 @@ handle_answer(SvcName, App, {error, Req, Reason}) ->
handle_error(App, Req, Reason, SvcName);
handle_answer(SvcName,
- #diameter_app{dictionary = Dict,
+ #diameter_app{dictionary = AppDict,
id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
- Mod = dict(Dict, Dict0, Pkt),
- handle_A(errors(Id, diameter_codec:decode(Mod, Pkt)),
+ Dict = dict(AppDict, Dict0, Pkt),
+ handle_A(errors(Id, diameter_codec:decode({Dict, AppDict}, Pkt)),
SvcName,
- Mod,
+ Dict,
Dict0,
App,
Req).
@@ -1448,10 +1469,12 @@ handle_answer(SvcName,
%% want to examine the answer?
handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
- incr(recv, Pkt, TPid, Dict),
+ AppDict = App#diameter_app.dictionary,
+
+ incr(recv, Pkt, TPid, AppDict),
try
- incr_rc(recv, Pkt, Dict, TPid, Dict0) %% count incoming
+ incr_result(recv, Pkt, TPid, {Dict, AppDict, Dict0}) %% count incoming
of
_ -> answer(Pkt, SvcName, App, Req)
catch
@@ -1468,6 +1491,8 @@ handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
answer(Pkt#diameter_packet{errors = [E|Es]}, SvcName, App, Req)
end.
+%% answer/4
+
answer(Pkt,
SvcName,
#diameter_app{module = ModX,
@@ -1568,13 +1593,18 @@ encode(Dict, TPid, Pkt, Fs) ->
%% an encoded binary. This isn't the usual case and doesn't properly
%% support retransmission but is useful for test.
+encode(Dict, TPid, Pkt)
+ when is_atom(Dict) ->
+ encode({Dict, Dict}, TPid, Pkt);
+
%% A message to be encoded.
-encode(Dict, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+ {Dict, AppDict} = DictT,
try
diameter_codec:encode(Dict, Pkt)
catch
exit: {diameter_codec, encode, T} = Reason ->
- incr_error(send, T, TPid, Dict),
+ incr_error(send, T, TPid, AppDict),
exit(Reason)
end;
@@ -1683,6 +1713,7 @@ resend_request(Pkt0,
caps = Caps},
?LOG(retransmission, Pkt#diameter_packet.header),
+ incr(TPid, {msg_id(Pkt, Dict), send, retransmission}),
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 5a068c1a25..d91a776321 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.erl
@@ -132,7 +132,7 @@ gen(parse, ParseD, _Mod) ->
[?VERSION | ParseD];
gen(forms, ParseD, Mod) ->
- pp(erl_forms(Mod, ParseD));
+ preprocess(Mod, erl_forms(Mod, ParseD));
gen(hrl, ParseD, Mod) ->
gen_hrl(Mod, ParseD);
@@ -838,19 +838,19 @@ rec_name(Name, Prefix) ->
Prefix ++ Name.
%% ===========================================================================
-%% pp/1
+%% preprocess/2
%%
%% Preprocess forms as generated by 'forms' option. In particular,
%% replace the include_lib attributes in generated forms by the
%% corresponding forms, extracting the latter from an existing
%% dictionary (diameter_gen_relay). The resulting forms can be
%% compiled to beam using compile:forms/2 (which does no preprocessing
-%% or it's own; DiY currently appears to be the only way to preprocess
+%% of it's own; DiY currently appears to be the only way to preprocess
%% a forms list).
-pp(Forms) ->
+preprocess(Mod, Forms) ->
{_, Beam, _} = code:get_object_code(diameter_gen_relay),
- pp(Forms, abstract_code(Beam)).
+ pp(Forms, remod(Mod, abstract_code(Beam))).
pp(Forms, {ok, Code}) ->
Files = files(Code, []),
@@ -859,6 +859,25 @@ pp(Forms, {ok, Code}) ->
pp(Forms, {error, Reason}) ->
erlang:error({forms, Reason, Forms}).
+%% Replace literal diameter_gen_relay atoms in the extracted forms.
+%% ?MODULE for example.
+
+remod(Mod, L)
+ when is_list(L) ->
+ [remod(Mod, T) || T <- L];
+
+remod(Mod, {atom, _, diameter_gen_relay} = T) ->
+ setelement(3, T, Mod);
+
+remod(Mod, T)
+ when is_tuple(T) ->
+ list_to_tuple(remod(Mod, tuple_to_list(T)));
+
+remod(_, T) ->
+ T.
+
+%% Replace include_lib by the corresponding forms.
+
include({attribute, _, include_lib, Path}, Files) ->
Inc = filename:basename(Path),
[{Inc, Forms}] = [T || {F, _} = T <- Files, F == Inc], %% expect one
@@ -867,6 +886,8 @@ include({attribute, _, include_lib, Path}, Files) ->
include(T, _) ->
[T].
+%% Extract abstract code.
+
abstract_code(Beam) ->
case beam_lib:chunks(Beam, [abstract_code]) of
{ok, {_Mod, [{abstract_code, {_Vsn, Code}}]}} ->
@@ -877,6 +898,8 @@ abstract_code(Beam) ->
{E, Reason}
end.
+%% Extract filename/forms pairs for included forms.
+
files([{attribute, _, file, {Path, _}} | T], Acc) ->
{Body, Rest} = lists:splitwith(fun({attribute, _, file, _}) -> false;
(_) -> true
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index 3b6e259f5a..40580e3ce6 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -46,7 +46,16 @@
{load_module, diameter_gen_base_accounting},
{load_module, diameter_gen_relay},
{load_module, diameter_codec},
- {load_module, diameter_sctp}]}
+ {load_module, diameter_sctp}]},
+ {"1.7", [{load_module, diameter_service}, %% 17.1
+ {load_module, diameter_codec},
+ {load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_gen_acct_rfc6733},
+ {load_module, diameter_gen_base_rfc3588},
+ {load_module, diameter_gen_base_accounting},
+ {load_module, diameter_gen_relay},
+ {load_module, diameter_traffic},
+ {load_module, diameter_peer_fsm}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -75,6 +84,15 @@
{load_module, diameter_peer_fsm},
{load_module, diameter_watchdog},
{load_module, diameter_traffic},
- {load_module, diameter_lib}]}
+ {load_module, diameter_lib}]},
+ {"1.7", [{load_module, diameter_peer_fsm},
+ {load_module, diameter_traffic},
+ {load_module, diameter_gen_relay},
+ {load_module, diameter_gen_base_accounting},
+ {load_module, diameter_gen_base_rfc3588},
+ {load_module, diameter_gen_acct_rfc6733},
+ {load_module, diameter_gen_base_rfc6733},
+ {load_module, diameter_codec},
+ {load_module, diameter_service}]}
]
}.
diff --git a/lib/diameter/src/info/diameter_dbg.erl b/lib/diameter/src/info/diameter_dbg.erl
index b5b3983afa..b536e5e80b 100644
--- a/lib/diameter/src/info/diameter_dbg.erl
+++ b/lib/diameter/src/info/diameter_dbg.erl
@@ -32,7 +32,8 @@
compiled/0,
procs/0,
latest/0,
- nl/0]).
+ nl/0,
+ sizes/0]).
-export([diameter_config/0,
diameter_peer/0,
@@ -69,7 +70,16 @@
-define(VALUES(Rec), tl(tuple_to_list(Rec))).
%% ----------------------------------------------------------
-%% # table(TableName)
+%% # sizes/0
+%%
+%% Return sizes of named tables.
+%% ----------------------------------------------------------
+
+sizes() ->
+ [{T, ets:info(T, size)} || T <- ?LOCAL, T /= diameter_peer].
+
+%% ----------------------------------------------------------
+%% # table/1
%%
%% Pretty-print a diameter table. Returns the number of records
%% printed, or undefined.
@@ -97,7 +107,7 @@ split([F|Fs], [V|Vs]) ->
{F, Fs, V, Vs}.
%% ----------------------------------------------------------
-%% # TableName()
+%% # TableName/0
%% ----------------------------------------------------------
-define(TABLE(Name), Name() -> table(Name)).
@@ -111,7 +121,7 @@ split([F|Fs], [V|Vs]) ->
?TABLE(diameter_stats).
%% ----------------------------------------------------------
-%% # tables()
+%% # tables/0
%%
%% Pretty-print diameter tables from all nodes. Returns the number of
%% records printed.
@@ -127,7 +137,7 @@ split(_, Fs, Vs) ->
split(Fs, Vs).
%% ----------------------------------------------------------
-%% # modules()
+%% # modules/0
%% ----------------------------------------------------------
modules() ->
@@ -140,49 +150,49 @@ appdir() ->
[_|_] = code:lib_dir(?APP, ebin).
%% ----------------------------------------------------------
-%% # versions()
+%% # versions/0
%% ----------------------------------------------------------
versions() ->
?I:versions(modules()).
%% ----------------------------------------------------------
-%% # versions()
+%% # version_info/0
%% ----------------------------------------------------------
version_info() ->
?I:version_info(modules()).
%% ----------------------------------------------------------
-%% # compiled()
+%% # compiled/0
%% ----------------------------------------------------------
compiled() ->
?I:compiled(modules()).
%% ----------------------------------------------------------
-%% procs()
+%% # procs/0
%% ----------------------------------------------------------
procs() ->
?I:procs(?APP).
%% ----------------------------------------------------------
-%% # latest()
+%% # latest/0
%% ----------------------------------------------------------
latest() ->
?I:latest(modules()).
%% ----------------------------------------------------------
-%% # nl()
+%% # nl/0
%% ----------------------------------------------------------
nl() ->
lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()).
%% ----------------------------------------------------------
-%% # pp(Bin)
+%% # pp/1
%%
%% Description: Pretty-print a message binary.
%% ----------------------------------------------------------
@@ -317,7 +327,7 @@ ppp({Field, Value}) ->
io:format(": ~-22s : ~p~n", [Field, Value]).
%% ----------------------------------------------------------
-%% # subscriptions()
+%% # subscriptions/0
%%
%% Returns a list of {SvcName, Pid}.
%% ----------------------------------------------------------
@@ -326,7 +336,7 @@ subscriptions() ->
diameter_service:subscriptions().
%% ----------------------------------------------------------
-%% # children()
+%% # children/0
%% ----------------------------------------------------------
children() ->
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 560c2aed50..4e54e4eafc 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -18,5 +18,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.7
+DIAMETER_VSN = 1.7.1
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index b3440ce6e1..52b7529f70 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -31,6 +31,22 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix spec to doc generation from erl_docgen and edoc for
+ maps</p>
+ <p>
+ Own Id: OTP-12058</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.14</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index e164ff060f..a102d432bc 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -831,8 +831,6 @@ t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
t_nonempty_list(Es);
t_type([#xmlElement{name = map, content = Es}]) ->
t_map(Es);
-t_type([#xmlElement{name = map_field, content=Es}]) ->
- t_map_field(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
@@ -882,10 +880,11 @@ t_fun(Es) ->
[") -> "] ++ t_utype(get_elem(type, Es))).
t_map(Es) ->
- ["#{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+ Fs = get_elem(map_field, Es),
+ ["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]).
-t_map_field([K,V]) ->
- [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
+t_map_field(#xmlElement{content = [K,V]}) ->
+ t_utype_elem(K) ++ [" => "] ++ t_utype_elem(V).
t_record(E, Es) ->
Name = ["#"] ++ t_type(get_elem(atom, Es)),
@@ -1082,6 +1081,8 @@ ot_type([#xmlElement{name = nonempty_list, content = Es}]) ->
ot_nonempty_list(Es);
ot_type([#xmlElement{name = tuple, content = Es}]) ->
ot_tuple(Es);
+ot_type([#xmlElement{name = map, content = Es}]) ->
+ ot_map(Es);
ot_type([#xmlElement{name = 'fun', content = Es}]) ->
ot_fun(Es);
ot_type([#xmlElement{name = record, content = Es}]) ->
@@ -1138,6 +1139,12 @@ ot_nonempty_list(Es) ->
ot_tuple(Es) ->
{type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
+ot_map(Es) ->
+ {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
+
+ot_map_field(#xmlElement{content=[K,V]}) ->
+ {type,0,map_field_assoc,ot_utype_elem(K), ot_utype_elem(V)}.
+
ot_fun(Es) ->
Range = ot_utype(get_elem(type, Es)),
Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)],
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index d4e00d3ecd..8a6c8eb33e 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -143,7 +143,7 @@ to_xml(#t_fun{args = As, range = T}, Env) ->
{'fun', [{argtypes, map(fun wrap_utype/2, As, Env)},
wrap_utype(T, Env)]};
to_xml(#t_map{ types = Ts}, Env) ->
- {map, map(fun wrap_utype/2, Ts, Env)};
+ {map, map(fun to_xml/2, Ts, Env)};
to_xml(#t_map_field{ k_type=K, v_type=V}, Env) ->
{map_field, [wrap_utype(K,Env), wrap_utype(V, Env)]};
to_xml(#t_tuple{types = Ts}, Env) ->
diff --git a/lib/edoc/test/edoc_SUITE_data/map_module.erl b/lib/edoc/test/edoc_SUITE_data/map_module.erl
index 94ee7e6f26..f242721637 100644
--- a/lib/edoc/test/edoc_SUITE_data/map_module.erl
+++ b/lib/edoc/test/edoc_SUITE_data/map_module.erl
@@ -1,6 +1,6 @@
-module(map_module).
--export([foo1/1,foo2/3]).
+-export([foo1/1,foo2/3,start_child/2]).
%% @type wazzup() = integer()
%% @type some_type() = map()
@@ -25,3 +25,43 @@ foo1(#{ a:= 1, b := V}) -> V.
Map :: #{ get => 'value', 'value' => binary()}) -> binary().
foo2(Type1, {a,#{ "a" := _}}, #{get := value, value := B}) when is_map(Type1) -> B.
+
+%% from supervisor 18.0
+
+-type child() :: 'undefined' | pid().
+-type child_id() :: term().
+-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}.
+-type modules() :: [module()] | 'dynamic'.
+-type restart() :: 'permanent' | 'transient' | 'temporary'.
+-type shutdown() :: 'brutal_kill' | timeout().
+-type worker() :: 'worker' | 'supervisor'.
+-type sup_ref() :: (Name :: atom())
+ | {Name :: atom(), Node :: node()}
+ | {'global', Name :: atom()}
+ | {'via', Module :: module(), Name :: any()}
+ | pid().
+-type child_spec() :: #{name => child_id(), % mandatory
+ start => mfargs(), % mandatory
+ restart => restart(), % optional
+ shutdown => shutdown(), % optional
+ type => worker(), % optional
+ modules => modules()} % optional
+ | {Id :: child_id(),
+ StartFunc :: mfargs(),
+ Restart :: restart(),
+ Shutdown :: shutdown(),
+ Type :: worker(),
+ Modules :: modules()}.
+
+-type startchild_err() :: 'already_present'
+ | {'already_started', Child :: child()} | term().
+-type startchild_ret() :: {'ok', Child :: child()}
+ | {'ok', Child :: child(), Info :: term()}
+ | {'error', startchild_err()}.
+
+
+-spec start_child(SupRef, ChildSpec) -> startchild_ret() when
+ SupRef :: sup_ref(),
+ ChildSpec :: child_spec() | (List :: [term()]).
+start_child(Supervisor, ChildSpec) ->
+ {Supervisor,ChildSpec}.
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 281a792118..b1cf115b29 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.14
+EDOC_VSN = 0.7.15
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 8009a8d6a3..dbd478fb17 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -75,7 +75,9 @@ filter() See present/1, substrings/2,
<p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p>
<p>The log function takes three arguments, <c>fun(Level, FormatString, [FormatArg]) end</c>.</p>
<p>Timeout set the maximum time in milliseconds that each server request may take.</p>
- <p>Currently, the only TCP socket option accepted is <c>inet6</c>. Default is <c>inet</c>.</p>
+ <p>All TCP socket options are accepted except
+ <c>active</c>, <c>binary</c>, <c>deliver</c>, <c>list</c>, <c>mode</c> and <c>packet</c>
+ </p>
</desc>
</func>
<func>
diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml
index 089bb731d4..f92d100757 100644
--- a/lib/eldap/doc/src/notes.xml
+++ b/lib/eldap/doc/src/notes.xml
@@ -30,7 +30,36 @@
</header>
<p>This document describes the changes made to the Eldap application.</p>
- <section><title>Eldap 1.0.3</title>
+ <section><title>Eldap 1.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>eldap:open/2</c> and <c>eldap:open/3</c> gave wrong
+ return values for option errors.</p>
+ <p>
+ Own Id: OTP-12182</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Nearly all TCP options are possible to give in the
+ <c>eldap:open/2</c> call.</p>
+ <p>
+ Own Id: OTP-12171</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eldap 1.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index 1cd328cde3..416334e365 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -12,6 +12,7 @@
-vc('$Id$ ').
-export([open/1,open/2,simple_bind/3,controlling_process/2,
start_tls/2, start_tls/3,
+ getopts/2,
baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
approxMatch/2,search/2,substrings/2,present/1,
@@ -92,6 +93,15 @@ start_tls(Handle, TlsOptions, Timeout) ->
recv(Handle).
%%% --------------------------------------------------------------------
+%%% Ask for option values on the socket.
+%%% Warning: This is an undocumented function for testing purposes only.
+%%% Use at own risk...
+%%% --------------------------------------------------------------------
+getopts(Handle, OptNames) when is_pid(Handle), is_list(OptNames) ->
+ send(Handle, {getopts, OptNames}),
+ recv(Handle).
+
+%%% --------------------------------------------------------------------
%%% Shutdown connection (and process) asynchronous.
%%% --------------------------------------------------------------------
@@ -374,24 +384,35 @@ parse_args([{sslopts, Opts}|T], Cpid, Data) when is_list(Opts) ->
parse_args([{sslopts, _}|T], Cpid, Data) ->
parse_args(T, Cpid, Data);
parse_args([{tcpopts, Opts}|T], Cpid, Data) when is_list(Opts) ->
- parse_args(T, Cpid, Data#eldap{tcp_opts = inet6_opt(Opts) ++ Data#eldap.tcp_opts});
+ parse_args(T, Cpid, Data#eldap{tcp_opts = tcp_opts(Opts,Cpid,Data#eldap.tcp_opts)});
parse_args([{log, F}|T], Cpid, Data) when is_function(F) ->
parse_args(T, Cpid, Data#eldap{log = F});
parse_args([{log, _}|T], Cpid, Data) ->
parse_args(T, Cpid, Data);
parse_args([H|_], Cpid, _) ->
send(Cpid, {error,{wrong_option,H}}),
+ unlink(Cpid),
exit(wrong_option);
parse_args([], _, Data) ->
Data.
-inet6_opt(Opts) ->
- case proplists:get_value(inet6, Opts) of
+tcp_opts([Opt|Opts], Cpid, Acc) ->
+ Key = if is_atom(Opt) -> Opt;
+ is_tuple(Opt) -> element(1,Opt)
+ end,
+ case lists:member(Key,[active,binary,deliver,list,mode,packet]) of
+ false ->
+ tcp_opts(Opts, Cpid, [Opt|Acc]);
true ->
- [inet6];
- _ ->
- []
- end.
+ tcp_opts_error(Opt, Cpid)
+ end;
+tcp_opts([], _Cpid, Acc) -> Acc.
+
+tcp_opts_error(Opt, Cpid) ->
+ send(Cpid, {error, {{forbidden_tcp_option,Opt},
+ "This option affects the eldap functionality and can't be set by user"}}),
+ unlink(Cpid),
+ exit(forbidden_tcp_option).
%%% Try to connect to the hosts in the listed order,
%%% and stop with the first one to which a successful
@@ -466,6 +487,36 @@ loop(Cpid, Data) ->
unlink(Cpid),
exit(closed);
+ {From, {getopts, OptNames}} ->
+ Result =
+ try
+ [case OptName of
+ port -> {port, Data#eldap.port};
+ log -> {log, Data#eldap.log};
+ timeout -> {timeout, Data#eldap.timeout};
+ ssl -> {ssl, Data#eldap.ldaps};
+ {sslopts, SslOptNames} when Data#eldap.using_tls==true ->
+ case ssl:getopts(Data#eldap.fd, SslOptNames) of
+ {ok,SslOptVals} -> {sslopts, SslOptVals};
+ {error,Reason} -> throw({error,Reason})
+ end;
+ {sslopts, _} ->
+ throw({error,no_tls});
+ {tcpopts, TcpOptNames} ->
+ case inet:getopts(Data#eldap.fd, TcpOptNames) of
+ {ok,TcpOptVals} -> {tcpopts, TcpOptVals};
+ {error,Posix} -> throw({error,Posix})
+ end
+ end || OptName <- OptNames]
+ of
+ OptsList -> {ok,OptsList}
+ catch
+ throw:Error -> Error;
+ Class:Error -> {error,{Class,Error}}
+ end,
+ send(From, Result),
+ ?MODULE:loop(Cpid, Data);
+
{Cpid, 'EXIT', Reason} ->
?PRINT("Got EXIT from Cpid, reason=~p~n",[Reason]),
exit(Reason);
diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile
index 3c5810eece..24e71cebaa 100644
--- a/lib/eldap/test/Makefile
+++ b/lib/eldap/test/Makefile
@@ -28,6 +28,7 @@ INCLUDES= -I. -I ../include
# ----------------------------------------------------
MODULES= \
+ eldap_connections_SUITE \
eldap_basic_SUITE
ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/eldap/test/README b/lib/eldap/test/README
index 8774db1504..ec774c1ae3 100644
--- a/lib/eldap/test/README
+++ b/lib/eldap/test/README
@@ -19,7 +19,7 @@ This will however not work, since slapd is guarded by apparmor that checks that
To make a local extension of alowed operations:
sudo emacs /etc/apparmor.d/local/usr.sbin.slapd
-and, after the change (yes, at least on Ubuntu it is right to edit ../local/.. but run with an other file) :
+and, after the change (yes, at least on Ubuntu it is right to edit ../local/.. but run with another file):
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.slapd
diff --git a/lib/eldap/test/eldap_connections_SUITE.erl b/lib/eldap/test/eldap_connections_SUITE.erl
new file mode 100644
index 0000000000..4c8aa9c2cf
--- /dev/null
+++ b/lib/eldap/test/eldap_connections_SUITE.erl
@@ -0,0 +1,126 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(eldap_connections_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+%-include_lib("eldap/include/eldap.hrl").
+
+
+all() ->
+ [
+ tcp_connection,
+ tcp_inet6_connection,
+ tcp_connection_option,
+ tcp_inet6_connection_option
+ ].
+
+
+init_per_suite(Config) -> Config.
+
+end_per_suite(_Config) -> ok.
+
+
+init_per_testcase(_TestCase, Config) ->
+ {ok,Sl} = gen_tcp:listen(0,[]),
+ {ok,Sl6} = gen_tcp:listen(0,[inet6]),
+ [{listen_socket,Sl}, {listen_socket6,Sl6} | Config].
+
+end_per_testcase(_TestCase, Config) ->
+ catch gen_tcp:close( proplists:get_value(listen_socket, Config) ),
+ catch gen_tcp:close( proplists:get_value(listen_socket6, Config) ),
+ ok.
+
+%%%================================================================
+%%%
+%%% Test cases
+%%%
+%%%----------------------------------------------------------------
+tcp_connection(Config) ->
+ do_tcp_connection(Config, listen_socket, "localhost", []).
+
+tcp_inet6_connection(Config) ->
+ do_tcp_connection(Config, listen_socket6, "::", [{tcpopts,[inet6]}]).
+
+
+do_tcp_connection(Config, SockKey, Host, Opts) ->
+ Sl = proplists:get_value(SockKey, Config),
+ {ok,{_,Port}} = inet:sockname(Sl),
+ case eldap:open([Host], [{port,Port}|Opts]) of
+ {ok,_H} ->
+ case gen_tcp:accept(Sl,1000) of
+ {ok,_S} -> ok;
+ {error,timeout} -> ct:fail("server side accept timeout",[])
+ end;
+ Other -> ct:fail("eldap:open failed: ~p",[Other])
+ end.
+
+%%%----------------------------------------------------------------
+tcp_connection_option(Config) ->
+ do_tcp_connection_option(Config, listen_socket, "localhost", []).
+
+tcp_inet6_connection_option(Config) ->
+ do_tcp_connection_option(Config, listen_socket6, "::", [{tcpopts,[inet6]}]).
+
+
+do_tcp_connection_option(Config, SockKey, Host, Opts) ->
+ Sl = proplists:get_value(SockKey, Config),
+ {ok,{_,Port}} = inet:sockname(Sl),
+
+ %% Make an option value to test. The option must be implemented on all
+ %% platforms that we test on. Must check what the default value is
+ %% so we don't happen to choose that particular value.
+ {ok,[{linger,DefaultLinger}]} = inet:getopts(Sl, [linger]),
+ TestLinger = case DefaultLinger of
+ {false,_} -> {true,5};
+ {true,_} -> {false,0}
+ end,
+
+ case catch eldap:open([Host],
+ [{port,Port},{tcpopts,[{linger,TestLinger}]}|Opts]) of
+ {ok,H} ->
+ case gen_tcp:accept(Sl,1000) of
+ {ok,_} ->
+ case eldap:getopts(H, [{tcpopts,[linger]}]) of
+ {ok,[{tcpopts,[{linger,ActualLinger}]}]} ->
+ case ActualLinger of
+ TestLinger ->
+ ok;
+ DefaultLinger ->
+ ct:fail("eldap:getopts: 'linger' didn't change,"
+ " got ~p (=default) expected ~p",
+ [ActualLinger,TestLinger]);
+ _ ->
+ ct:fail("eldap:getopts: bad 'linger', got ~p expected ~p",
+ [ActualLinger,TestLinger])
+ end;
+ Other ->
+ ct:fail("eldap:getopts: bad result ~p",[Other])
+ end;
+ {error,timeout} ->
+ ct:fail("server side accept timeout",[])
+ end;
+
+ Other ->
+ ct:fail("eldap:open failed: ~p",[Other])
+ end.
+
+%%%----------------------------------------------------------------
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index efdc30b476..5e32f92fa8 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1,2 +1 @@
-ELDAP_VSN = 1.0.3
-
+ELDAP_VSN = 1.0.4 \ No newline at end of file
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index e546eb97fc..f194fb6d6c 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -30,7 +30,23 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.3.5</title>
+ <section><title>Erl_Docgen 0.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix spec to doc generation from erl_docgen and edoc for
+ maps</p>
+ <p>
+ Own Id: OTP-12058</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index 886194598f..1075c47801 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -388,8 +388,8 @@ t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
t_nonempty_list(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
-t_type([#xmlElement{name = map}]) ->
- t_map();
+t_type([#xmlElement{name = map, content = Es}]) ->
+ t_map(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
["fun("] ++ t_fun(Es) ++ [")"];
t_type([E = #xmlElement{name = record, content = Es}]) ->
@@ -432,8 +432,12 @@ t_nonempty_list(Es) ->
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
-t_map() ->
- ["map()"].
+t_map(Es) ->
+ Fs = get_elem(map_field, Es),
+ ["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]).
+
+t_map_field(#xmlElement{content = [K,V]}) ->
+ [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
@@ -550,12 +554,14 @@ ot_type([#xmlElement{name = nonempty_list, content = Es}]) ->
ot_nonempty_list(Es);
ot_type([#xmlElement{name = tuple, content = Es}]) ->
ot_tuple(Es);
+ot_type([#xmlElement{name = map, content = Es}]) ->
+ ot_map(Es);
ot_type([#xmlElement{name = 'fun', content = Es}]) ->
ot_fun(Es);
ot_type([#xmlElement{name = record, content = Es}]) ->
ot_record(Es);
ot_type([#xmlElement{name = abstype, content = Es}]) ->
- ot_abstype(Es);
+ ot_abstype(Es);
ot_type([#xmlElement{name = union, content = Es}]) ->
ot_union(Es).
@@ -606,6 +612,12 @@ ot_nonempty_list(Es) ->
ot_tuple(Es) ->
{type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
+ot_map(Es) ->
+ {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
+
+ot_map_field(#xmlElement{content=[K,V]}) ->
+ {type,0,map_field_assoc, ot_utype_elem(K), ot_utype_elem(V)}.
+
ot_fun(Es) ->
Range = ot_utype(get_elem(type, Es)),
Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)],
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index 0f89922275..8bfcc08698 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.3.5
+ERL_DOCGEN_VSN = 0.3.6
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index d511f2e240..ef78f0f87b 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -311,6 +311,26 @@ else
fi
fi
+dnl ----------------------------------------------------------------------
+dnl Enable -fsanitize= flags.
+dnl ----------------------------------------------------------------------
+
+m4_define(DEFAULT_SANITIZERS, [address,undefined])
+AC_ARG_ENABLE(
+ sanitizers,
+ AS_HELP_STRING(
+ [--enable-sanitizers@<:@=comma-separated list of sanitizers@:>@],
+ [Default=DEFAULT_SANITIZERS]),
+[
+case "$enableval" in
+ no) sanitizers= ;;
+ yes) sanitizers="-fsanitize=DEFAULT_SANITIZERS" ;;
+ *) sanitizers="-fsanitize=$enableval" ;;
+esac
+CFLAGS="$CFLAGS $sanitizers"
+LDFLAGS="$LDFLAGS $sanitizers"
+])
+
# ---------------------------------------------------------------------------
# XXX
# ---------------------------------------------------------------------------
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 3f85af8956..a055e854e3 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -30,6 +30,38 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.19</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Added a <c>.app</c> file for the application.</p>
+ <p>
+ Own Id: OTP-12178</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.18</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Implement --enable-sanitizers[=sanitizers]. Similar to
+ debugging with Valgrind, it's very useful to enable
+ -fsanitize= switches to catch bugs at runtime.</p>
+ <p>
+ Own Id: OTP-12153</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.7.17</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/ebin/.gitignore b/lib/erl_interface/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/erl_interface/ebin/.gitignore
diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in
index e36b39c1fb..7d914a02ca 100644
--- a/lib/erl_interface/src/Makefile.in
+++ b/lib/erl_interface/src/Makefile.in
@@ -40,6 +40,13 @@ include $(TARGET)/eidefs.mk
include $(ERL_TOP)/make/output.mk
+EBINDIR=../ebin
+
+APP_FILE= erl_interface.app
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBINDIR)/$(APP_FILE)
+
USING_MINGW=@MIXED_CYGWIN_MINGW@
USING_MSYS_VC==@MIXED_MSYS_VC@
USING_CYGWIN_VC==@MIXED_MSYS_VC@
@@ -212,7 +219,8 @@ ifeq ($(USING_VC),yes)
TARGETS = \
$(OBJ_TARGETS) \
- $(EXE_TARGETS)
+ $(EXE_TARGETS) \
+ $(APP_TARGET)
OBJ_TARGETS = \
$(MT_EILIB) \
@@ -241,7 +249,8 @@ else
ifeq ($USING_MINGW,yes)
TARGETS = \
$(OBJ_TARGETS) \
- $(EXE_TARGETS)
+ $(EXE_TARGETS) \
+ $(APP_TARGET)
OBJ_TARGETS = \
$(MD_EILIB) \
@@ -259,7 +268,8 @@ ifdef THR_DEFS
TARGETS = \
$(OBJ_TARGETS) \
- $(EXE_TARGETS)
+ $(EXE_TARGETS) \
+ $(APP_TARGET)
OBJ_TARGETS = \
$(ST_EILIB) \
@@ -281,7 +291,8 @@ else
TARGETS = \
$(OBJ_TARGETS) \
- $(EXE_TARGETS)
+ $(EXE_TARGETS) \
+ $(APP_TARGET)
OBJ_TARGETS = \
$(ST_EILIB) \
@@ -597,12 +608,15 @@ $(MDD_OBJDIR)/%.o: %.c
# Create directories
###########################################################################
-_create_dirs := $(shell mkdir -p $(BINDIR) $(OBJDIR) $(ST_OBJDIR) $(MT_OBJDIR) $(MD_OBJDIR) $(MDD_OBJDIR))
+_create_dirs := $(shell mkdir -p $(EBINDIR) $(BINDIR) $(OBJDIR) $(ST_OBJDIR) $(MT_OBJDIR) $(MD_OBJDIR) $(MDD_OBJDIR))
###########################################################################
# Special rules
###########################################################################
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(ERL_INTERFACE_VSN);' $< > $@
+
ifeq ($(TARGET),win32)
# Windows archive creation
@@ -857,6 +871,7 @@ release: opt
$(INSTALL_DIR) "$(RELSYSDIR)/include"
$(INSTALL_DIR) "$(RELSYSDIR)/lib"
$(INSTALL_DIR) "$(RELSYSDIR)/bin"
+ $(INSTALL_DIR) "$(RELSYSDIR)/ebin"
$(INSTALL_DIR) "$(RELSYSDIR)/src/auxdir"
$(INSTALL_DIR) "$(RELSYSDIR)/src/connect"
$(INSTALL_DIR) "$(RELSYSDIR)/src/decode"
@@ -868,6 +883,7 @@ release: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src/registry"
$(INSTALL_DIR) "$(RELEASE_PATH)/usr/include"
$(INSTALL_DIR) "$(RELEASE_PATH)/usr/lib"
+ $(INSTALL_DATA) $(APP_TARGET) "$(RELSYSDIR)/ebin/$(APP_FILE)"
$(INSTALL_DATA) $(HEADERS) "$(RELSYSDIR)/include"
$(INSTALL_DATA) $(HEADERS) "$(RELEASE_PATH)/usr/include"
$(INSTALL_DATA) $(OBJ_TARGETS) "$(RELSYSDIR)/lib"
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 2e8418d61e..45c000ef76 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -761,7 +761,7 @@ int ei_close_connection(int fd)
#endif
/*
- * Accept and initiate a connection from an other
+ * Accept and initiate a connection from another
* Erlang node. Return a file descriptor at success,
* otherwise -1;
*/
diff --git a/lib/erl_interface/src/erl_interface.app.src b/lib/erl_interface/src/erl_interface.app.src
new file mode 100644
index 0000000000..11f884c36b
--- /dev/null
+++ b/lib/erl_interface/src/erl_interface.app.src
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% This is an -*- erlang -*- file.
+%%
+
+{application, erl_interface,
+ [
+ {description, "Erl Interface"},
+ {vsn, "%VSN%"},
+ {modules, []},
+ {registered, []},
+ {applications, []},
+ {env, []},
+ {runtime_dependencies, []}
+ ]
+}.
diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c
index ae0265a388..d70d914b79 100644
--- a/lib/erl_interface/src/legacy/erl_connect.c
+++ b/lib/erl_interface/src/legacy/erl_connect.c
@@ -190,7 +190,7 @@ int erl_close_connection(int fd)
}
/*
- * Accept and initiate a connection from an other
+ * Accept and initiate a connection from another
* Erlang node. Return a file descriptor at success,
* otherwise -1;
*/
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index b1e612a9eb..e39aa4f514 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.7.17
+EI_VSN = 3.7.19
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 72ffda3cd5..e5a190e3e9 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -32,6 +32,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.2.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor refactoring.</p>
+ <p>
+ Own Id: OTP-12051</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.2.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile
index e6dab67363..47aef104ff 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))
@@ -78,7 +80,7 @@ all: $(OBJECTS)
clean:
- rm -f $(OBJECTS)
+ rm -f $(OBJECTS) $(PARSE_TRANSFORM_BIN)
rm -f core *~
distclean: clean
@@ -119,6 +121,7 @@ release_spec: opt
$(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) "$(RELSYSDIR)/ebin"
$(INSTALL_DIR) "$(RELSYSDIR)/src"
$(INSTALL_DATA) $(PARSE_TRANSFORM) $(SOURCES) "$(RELSYSDIR)/src"
+ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src"
$(INSTALL_DIR) "$(RELSYSDIR)/include"
$(INSTALL_DATA) $(INCLUDE_DELIVERABLES) "$(RELSYSDIR)/include"
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index 0350f9bf6e..cbbc6fbc15 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -440,13 +440,8 @@ parse_function({M, F}) when is_atom(M), is_atom(F) ->
parse_function(F) ->
bad_test(F).
-check_arity(F, N, T) when is_function(F) ->
- case erlang:fun_info(F, arity) of
- {arity, N} ->
- ok;
- _ ->
- bad_test(T)
- end;
+check_arity(F, N, _) when is_function(F, N) ->
+ ok;
check_arity(_, _, T) ->
bad_test(T).
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index f04c0536fe..855e1dac88 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.2.7
+EUNIT_VSN = 2.2.8
diff --git a/lib/hipe/cerl/cerl_prettypr.erl b/lib/hipe/cerl/cerl_prettypr.erl
index 9a3873f46d..f4a67439d6 100644
--- a/lib/hipe/cerl/cerl_prettypr.erl
+++ b/lib/hipe/cerl/cerl_prettypr.erl
@@ -1,7 +1,7 @@
%% =====================================================================
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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
@@ -476,13 +476,20 @@ lay_literal(Node, Ctxt) ->
%% that could represent printable characters - we
%% always print an integer.
text(int_lit(Node));
- V when is_binary(V) ->
- lay_binary(c_binary([c_bitstr(abstract(B),
- abstract(8),
+ V when is_bitstring(V) ->
+ Val = fun(I) when is_integer(I) -> I;
+ (B) when is_bitstring(B) ->
+ BZ = bit_size(B), <<BV:BZ>> = B, BV
+ end,
+ Sz = fun(I) when is_integer(I) -> 8;
+ (B) when is_bitstring(B) -> bit_size(B)
+ end,
+ lay_binary(c_binary([c_bitstr(abstract(Val(B)),
+ abstract(Sz(B)),
abstract(1),
abstract(integer),
abstract([unsigned, big]))
- || B <- binary_to_list(V)]),
+ || B <- bitstring_to_list(V)]),
Ctxt);
[] ->
text("[]");
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index a460f16272..74e93bf098 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-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
@@ -1891,7 +1891,11 @@ infinity_add(neg_inf, _Number) -> neg_inf;
infinity_add(_Number, pos_inf) -> pos_inf;
infinity_add(_Number, neg_inf) -> neg_inf;
infinity_add(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
- Number1 + Number2.
+ try Number1 + Number2
+ catch
+ error:system_limit when Number1 < 0 -> neg_inf;
+ error:system_limit -> pos_inf
+ end.
infinity_mult(neg_inf, Number) ->
Greater = infinity_geq(Number, 0),
@@ -1902,7 +1906,13 @@ infinity_mult(pos_inf, Number) -> infinity_inv(infinity_mult(neg_inf, Number));
infinity_mult(Number, pos_inf) -> infinity_inv(infinity_mult(neg_inf, Number));
infinity_mult(Number, neg_inf) -> infinity_mult(neg_inf, Number);
infinity_mult(Number1, Number2) when is_integer(Number1), is_integer(Number2)->
- Number1 * Number2.
+ try Number1 * Number2
+ catch
+ error:system_limit ->
+ if (Number1 >= 0) =:= (Number2 >= 0) -> pos_inf;
+ true -> neg_inf
+ end
+ end.
width({Min, Max}) -> infinity_max([width(Min), width(Max)]);
width(pos_inf) -> pos_inf;
@@ -2633,7 +2643,9 @@ opaque_args(M, F, A, Xs, Opaques) ->
true ->
case t_tuple_subtypes(X, Opaques) of
unknown -> false;
- List when length(List) >= 1 -> opaque_recargs(List, Y, Opaques)
+ List when length(List) >= 1 ->
+ (t_is_atom(Y, Opaques) andalso
+ opaque_recargs(List, Y, Opaques))
end;
false -> t_has_opaque_subtype(X, Opaques)
end];
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 0927c17b6b..4b2bec5fa8 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -262,6 +262,8 @@
-define(TAG_IMMED1_SIZE, 4).
-define(BITS, (erlang:system_info(wordsize) * 8) - ?TAG_IMMED1_SIZE).
+-define(MAX_TUPLE_SIZE, (1 bsl 10)).
+
%%-----------------------------------------------------------------------------
%% Type tags and qualifiers
%%
@@ -1770,6 +1772,8 @@ t_tuple() ->
-spec t_tuple(non_neg_integer() | [erl_type()]) -> erl_type().
+t_tuple(N) when is_integer(N), N > ?MAX_TUPLE_SIZE ->
+ t_tuple();
t_tuple(N) when is_integer(N) ->
?tuple(lists:duplicate(N, ?any), N, ?any);
t_tuple(List) ->
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index e8552eabcc..2962e4a9ac 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -30,6 +30,28 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.11.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The pretty-printing of bitstrings has been corrected.
+ </p>
+ <p>
+ Own Id: OTP-12015</p>
+ </item>
+ <item>
+ <p> A bug concerning <c>is_record/2,3</c> has been fixed,
+ as well as some cases where Dialyzer could crash due to
+ reaching system limits. </p>
+ <p>
+ Own Id: OTP-12018</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index c30695d4f0..cf1976d8d6 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.11
+HIPE_VSN = 3.11.1
diff --git a/lib/ic/c_src/oe_ei_encode_pid.c b/lib/ic/c_src/oe_ei_encode_pid.c
index b7083f84a0..609f441cf8 100644
--- a/lib/ic/c_src/oe_ei_encode_pid.c
+++ b/lib/ic/c_src/oe_ei_encode_pid.c
@@ -23,7 +23,7 @@
int oe_ei_encode_pid(CORBA_Environment *ev, const erlang_pid *p) {
int size = ev->_iout;
- (int) ei_encode_pid(NULL, &size, p);
+ ei_encode_pid(NULL, &size, p);
if (size >= ev->_outbufsz) {
char *buf = ev->_outbuf;
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index 03af316f75..bacac09f11 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -30,7 +30,22 @@
<file>notes.xml</file>
</header>
- <section><title>IC 4.3.5</title>
+ <section><title>IC 4.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix compiler warnings reported by LLVM</p>
+ <p>
+ Own Id: OTP-12138</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>IC 4.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index 2ffbbad444..bb273c7b57 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1 +1 @@
-IC_VSN = 4.3.5
+IC_VSN = 4.3.6
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 37eb7ba718..06cb035370 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -332,7 +332,7 @@ filename() = string()
<p>Defaults to <c>true</c>. </p>
</item>
- <tag><c><![CDATA[header_as_is]]></c></tag>
+ <tag><c><![CDATA[headers_as_is]]></c></tag>
<item>
<p>Shall the headers provided by the user be made
lower case or be regarded as case sensitive. </p>
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 3830b2e5ab..4ca038cc99 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -139,7 +139,7 @@
<marker id="prop_server_root"></marker>
<tag>{server_root, path()} </tag>
<item>
- <p>Defines the servers home directory where log files etc can
+ <p>Defines the server's home directory where log files etc can
be stored. Relative paths specified in other properties refer
to this directory. </p>
</item>
@@ -904,7 +904,7 @@ bytes
<p>Fetches information about the HTTP server. When called
with only the pid all properties are fetched, when called
with a list of specific properties they are fetched.
- Available properties are the same as the servers start options.
+ Available properties are the same as the server's start options.
</p>
<note><p>Pid is the pid returned from inets:start/[2,3].
@@ -930,7 +930,7 @@ bytes
<p>Fetches information about the HTTP server. When called with
only the Address and Port all properties are fetched, when
called with a list of specific properties they are fetched.
- Available properties are the same as the servers start
+ Available properties are the same as the server's start
options.
</p>
@@ -956,7 +956,7 @@ bytes
server. Incoming requests will be answered with a temporary
down message during the time the it takes to reload.</p>
- <note><p>Available properties are the same as the servers
+ <note><p>Available properties are the same as the server's
start options, although the properties bind_address and
port can not be changed.</p></note>
@@ -1068,7 +1068,7 @@ bytes
<type>
<v>OldData = list()</v>
<v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}}] </v>
- <v>StausCode = integer()</v>
+ <v>StatusCode = integer()</v>
<v>Body = io_list() | nobody | {Fun, Arg}</v>
<v>Head = [HeaderOption]</v>
<v>HeaderOption = {Option, Value} | {code, StatusCode}</v>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index d586536b0a..921de8e490 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -32,7 +32,58 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 5.10.2</title>
+ <section><title>Inets 5.10.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix some spelling mistakes in documentation</p>
+ <p>
+ Own Id: OTP-12152</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpd: Seperate timeout for TLS/SSL handshake from
+ keepalive timeout</p>
+ <p>
+ Own Id: OTP-12013</p>
+ </item>
+ <item>
+ <p>
+ Warning: this is experimental and may disappear or change
+ without previous warning.</p>
+ <p>
+ Experimental support for running Quickcheck and PropEr
+ tests from common_test suites is added to common_test.
+ See the reference manual for the new module
+ <c>ct_property_testing</c>.</p>
+ <p>
+ Experimental property tests are added under
+ <c>lib/{inet,ssh}/test/property_test</c>. They can be run
+ directly or from the commont_test suites
+ <c>inet/ftp_property_test_SUITE.erl</c> and
+ <c>ssh/test/ssh_property_test_SUITE.erl</c>.</p>
+ <p>
+ See the code in the <c>test</c> directories and the man
+ page for details.</p>
+ <p>
+ (Thanks to Tuncer Ayaz for a patch adding Triq)</p>
+ <p>
+ Own Id: OTP-12119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 5.10.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 5ae6760f08..d152d9f0be 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1793,7 +1793,7 @@ tls_tunnel_request(#request{headers = Headers,
host_header(#http_request_h{host = Host}, _) ->
Host;
-%% Handles header_as_is
+%% Handles headers_as_is
host_header(_, URI) ->
{ok, {_, _, Host, _, _, _}} = http_uri:parse(URI),
Host.
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index b3c9cbc46a..9bea58cc9e 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -35,6 +35,7 @@
-include("http_internal.hrl").
-include("httpd_internal.hrl").
+-define(HANDSHAKE_TIMEOUT, 5000).
-record(state, {mod, %% #mod{}
manager, %% pid()
status, %% accept | busy | blocked
@@ -96,15 +97,13 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
{SocketType, Socket} = await_socket_ownership_transfer(AcceptTimeout),
- TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
- Then = erlang:now(),
+ KeepAliveTimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
- case http_transport:negotiate(SocketType, Socket, TimeOut) of
+ case http_transport:negotiate(SocketType, Socket, ?HANDSHAKE_TIMEOUT) of
{error, _Error} ->
exit(shutdown); %% Can be 'normal'.
ok ->
- NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000,
- continue_init(Manager, ConfigDB, SocketType, Socket, NewTimeout)
+ continue_init(Manager, ConfigDB, SocketType, Socket, KeepAliveTimeOut)
end.
continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index 6991fb6d04..4bc49e1e67 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -17,6 +17,10 @@
%% %CopyrightEnd%
{"%VSN%",
[
+ {"5.10.2",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge,
+ []}]},
{"5.10.1",
[{load_module, httpc_handler, soft_purge, soft_purge, []},
{load_module, httpd, soft_purge, soft_purge, []},
@@ -34,6 +38,10 @@
{<<"5\\..*">>,[{restart_application, inets}]}
],
[
+ {"5.10.2",
+ [
+ {load_module, httpd_request_handler, soft_purge, soft_purge,
+ []}]},
{"5.10.1",
[{load_module, httpc_handler, soft_purge, soft_purge, []},
{load_module, httpd, soft_purge, soft_purge, []},
diff --git a/lib/inets/test/ftp_property_test_SUITE.erl b/lib/inets/test/ftp_property_test_SUITE.erl
new file mode 100644
index 0000000000..c7077421f4
--- /dev/null
+++ b/lib/inets/test/ftp_property_test_SUITE.erl
@@ -0,0 +1,52 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%% Run like this:
+%%% ct:run_test([{suite,"ftp_property_test_SUITE"}, {logdir,"/ldisk/OTP/LOG"}]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(ftp_property_test_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+all() -> [prop_ftp_case].
+
+
+init_per_suite(Config) ->
+ inets:start(),
+ ct_property_test:init_per_suite(Config).
+
+
+%%%---- test case
+prop_ftp_case(Config) ->
+ ct_property_test:quickcheck(
+ ftp_simple_client_server:prop_ftp(Config),
+ Config
+ ).
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 4be20d3a69..4010597657 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -39,6 +39,7 @@
-define(FAIL_EXPIRE_TIME,1).
%% Seconds before successful auths timeout.
-define(AUTH_TIMEOUT,5).
+-define(URL_START, "http://").
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -63,7 +64,9 @@ all() ->
{group, http_htaccess},
{group, https_htaccess},
{group, http_security},
- {group, https_security}
+ {group, https_security},
+ {group, http_reload},
+ {group, https_reload}
].
groups() ->
@@ -84,7 +87,18 @@ groups() ->
{https_htaccess, [], [{group, htaccess}]},
{http_security, [], [{group, security}]},
{https_security, [], [{group, security}]},
+ {http_reload, [], [{group, reload}]},
+ {https_reload, [], [{group, reload}]},
{limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]},
+ {reload, [], [non_disturbing_reconfiger_dies,
+ disturbing_reconfiger_dies,
+ non_disturbing_1_1,
+ non_disturbing_1_0,
+ non_disturbing_0_9,
+ disturbing_1_1,
+ disturbing_1_0,
+ disturbing_0_9
+ ]},
{basic_auth, [], [basic_auth_1_1, basic_auth_1_0, basic_auth_0_9]},
{auth_api, [], [auth_api_1_1, auth_api_1_0, auth_api_0_9
]},
@@ -134,8 +148,24 @@ init_per_suite(Config) ->
inets_test_lib:del_dirs(ServerRoot),
DocRoot = filename:join(ServerRoot, "htdocs"),
setup_server_dirs(ServerRoot, DocRoot, DataDir),
+ {ok, Hostname0} = inet:gethostname(),
+ Inet =
+ case (catch ct:get_config(ipv6_hosts)) of
+ undefined ->
+ inet;
+ Hosts when is_list(Hosts) ->
+ case lists:member(list_to_atom(Hostname0), Hosts) of
+ true ->
+ inet6;
+ false ->
+ inet
+ end;
+ _ ->
+ inet
+ end,
[{server_root, ServerRoot},
{doc_root, DocRoot},
+ {ipfamily, Inet},
{node, node()},
{host, inets_test_lib:hostname()},
{address, getaddr()} | Config].
@@ -150,7 +180,8 @@ init_per_group(Group, Config0) when Group == https_basic;
Group == https_auth_api;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
- Group == https_security
+ Group == https_security;
+ Group == https_reload
->
init_ssl(Group, Config0);
init_per_group(Group, Config0) when Group == http_basic;
@@ -159,7 +190,8 @@ init_per_group(Group, Config0) when Group == http_basic;
Group == http_auth_api;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
- Group == http_security
+ Group == http_security;
+ Group == http_reload
->
ok = start_apps(Group),
init_httpd(Group, [{type, ip_comm} | Config0]);
@@ -202,17 +234,19 @@ end_per_group(Group, _Config) when Group == http_basic;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
Group == http_htaccess;
- Group == http_security
+ Group == http_security;
+ Group == http_reload
->
inets:stop();
end_per_group(Group, _Config) when Group == https_basic;
Group == https_limit;
Group == https_basic_auth;
Group == https_auth_api;
- Group == http_auth_api_dets;
- Group == http_auth_api_mnesia;
+ Group == https_auth_api_dets;
+ Group == https_auth_api_mnesia;
Group == https_htaccess;
- Group == http_security
+ Group == https_security;
+ Group == https_reload
->
ssl:stop(),
inets:stop();
@@ -506,7 +540,7 @@ ipv6(Config) when is_list(Config) ->
true ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
- URI = http_request("GET /", Version, Host),
+ URI = http_request("GET / ", Version, Host),
httpd_test_lib:verify_request(?config(type, Config), Host,
?config(port, Config), [inet6],
?config(code, Config),
@@ -1088,12 +1122,114 @@ security(Config) ->
[{statuscode, 401}]),
true = unblock_user(Node, "two", Port, OpenDir).
+
+%%-------------------------------------------------------------------------
+non_disturbing_reconfiger_dies(Config) when is_list(Config) ->
+ do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], non_disturbing).
+disturbing_reconfiger_dies(Config) when is_list(Config) ->
+ do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], disturbing).
+
+do_reconfiger_dies(Config, DisturbingType) ->
+ Server = ?config(server_pid, Config),
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Type = ?config(type, Config),
+
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ Pid = spawn(fun() -> httpd:reload_config([{server_name, "httpd_kill_" ++ Version},
+ {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], DisturbingType)
+ end),
+ monitor(process, Pid),
+ exit(Pid, kill),
+ receive
+ {'DOWN', _, _, _, _} ->
+ ok
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_test"}] = httpd:info(Server, [server_name]).
+%%-------------------------------------------------------------------------
+disturbing_1_1(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/1.1"} | Config]).
+
+disturbing_1_0(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/1.0"} | Config]).
+
+disturbing_0_9(Config) when is_list(Config) ->
+ disturbing([{http_version, "HTTP/0.9"} | Config]).
+
+disturbing(Config) when is_list(Config)->
+ Server = ?config(server_pid, Config),
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Type = ?config(type, Config),
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd:reload_config([{server_name, "httpd_disturbing_" ++ Version}, {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], disturbing),
+ Close = list_to_atom((typestr(Type)) ++ "_closed"),
+ receive
+ {Close, Socket} ->
+ ok;
+ Msg ->
+ ct:fail({{expected, {Close, Socket}}, {got, Msg}})
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
+%%-------------------------------------------------------------------------
+non_disturbing_1_1(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/1.1"} | Config]).
+
+non_disturbing_1_0(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/1.0"} | Config]).
+
+non_disturbing_0_9(Config) when is_list(Config) ->
+ non_disturbing([{http_version, "HTTP/0.9"} | Config]).
+
+non_disturbing(Config) when is_list(Config)->
+ Server = ?config(server_pid, Config),
+ Version = ?config(http_version, Config),
+ Host = ?config(host, Config),
+ Port = ?config(port, Config),
+ Type = ?config(type, Config),
+
+ HttpdConfig = httpd:info(Server),
+ BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
+ {ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
+ inets_test_lib:send(Type, Socket, BlockRequest),
+ ct:sleep(100), %% Avoid possible timing issues
+ ok = httpd:reload_config([{server_name, "httpd_non_disturbing_" ++ Version}, {port, Port}|
+ proplists:delete(server_name, HttpdConfig)], non_disturbing),
+ Transport = type(Type),
+ receive
+ {Transport, Socket, Msg} ->
+ ct:pal("Received message ~p~n", [Msg]),
+ ok
+ after 2000 ->
+ ct:fail(timeout)
+ end,
+ inets_test_lib:close(Type, Socket),
+ [{server_name, "httpd_non_disturbing_" ++ Version}] = httpd:info(Server, [server_name]).
%%--------------------------------------------------------------------
%% Internal functions -----------------------------------
%%--------------------------------------------------------------------
+url(http, End, Config) ->
+ Port = ?config(port, Config),
+ {ok,Host} = inet:gethostname(),
+ ?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
+
do_max_clients(Config) ->
Version = ?config(http_version, Config),
Host = ?config(host, Config),
@@ -1171,7 +1307,9 @@ start_apps(Group) when Group == https_basic;
Group == https_auth_api_dets;
Group == https_auth_api_mnesia;
Group == http_htaccess;
- Group == http_security ->
+ Group == http_security;
+ Group == http_reload
+ ->
inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]);
start_apps(Group) when Group == http_basic;
Group == http_limit;
@@ -1180,7 +1318,8 @@ start_apps(Group) when Group == http_basic;
Group == http_auth_api_dets;
Group == http_auth_api_mnesia;
Group == https_htaccess;
- Group == https_security ->
+ Group == https_security;
+ Group == https_reload->
inets_test_lib:start_apps([inets]).
server_start(_, HttpdConfig) ->
@@ -1224,6 +1363,10 @@ server_config(http_basic, Config) ->
basic_conf() ++ server_config(http, Config);
server_config(https_basic, Config) ->
basic_conf() ++ server_config(https, Config);
+server_config(http_reload, Config) ->
+ [{keep_alive_timeout, 2}] ++ server_config(http, Config);
+server_config(https_reload, Config) ->
+ [{keep_alive_timeout, 2}] ++ server_config(https, Config);
server_config(http_limit, Config) ->
[{max_clients, 1}] ++ server_config(http, Config);
server_config(https_limit, Config) ->
@@ -1270,7 +1413,7 @@ server_config(http, Config) ->
{server_root, ServerRoot},
{document_root, ?config(doc_root, Config)},
{bind_address, any},
- {ipfamily, inet},
+ {ipfamily, ?config(ipfamily, Config)},
{max_header_size, 256},
{max_header_action, close},
{directory_index, ["index.html", "welcome.html"]},
@@ -1539,9 +1682,10 @@ cleanup_mnesia() ->
transport_opts(ssl, Config) ->
PrivDir = ?config(priv_dir, Config),
- [{cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
-transport_opts(_, _) ->
- [].
+ [?config(ipfamily, Config),
+ {cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
+transport_opts(_, Config) ->
+ [?config(ipfamily, Config)].
%%% mod_range
@@ -1792,3 +1936,12 @@ event(What, Port, Dir, Data) ->
global:send(mod_security_test, Msg)
end.
+type(ip_comm) ->
+ tcp;
+type(_) ->
+ ssl.
+
+typestr(ip_comm) ->
+ "tcp";
+typestr(_) ->
+ "ssl".
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 1fcc5f257e..baef699629 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -129,7 +129,7 @@ end_per_suite(_Config) ->
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) ->
+init_per_testcase(_Case, Config) ->
Config.
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 36a5bb9e71..647fa6f6c1 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -91,16 +91,7 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut)
when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) ->
verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut).
-verify_request(SocketType, Host, Port, TranspOpts0, Node, RequestStr, Options, TimeOut) ->
- %% For now, until we modernize the httpd tests
- TranspOpts =
- case lists:member(inet6, TranspOpts0) of
- true ->
- TranspOpts0;
- false ->
- [inet | TranspOpts0]
- end,
-
+verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) ->
try inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts) of
{ok, Socket} ->
ok = inets_test_lib:send(SocketType, Socket, RequestStr),
diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
index 19c2bc129e..74c11f71ba 100644
--- a/lib/inets/test/old_httpd_SUITE.erl
+++ b/lib/inets/test/old_httpd_SUITE.erl
@@ -186,20 +186,23 @@ groups() ->
%% Only used through load_config
%% but we still need these tests
%% should be cleaned up and moved to new test suite
- ip_restart_no_block,
- ip_restart_disturbing_block,
- ip_restart_non_disturbing_block,
- ip_block_disturbing_idle,
- ip_block_non_disturbing_idle,
- ip_block_503,
- ip_block_disturbing_active,
- ip_block_non_disturbing_active,
- ip_block_disturbing_active_timeout_not_released,
- ip_block_disturbing_active_timeout_released,
- ip_block_non_disturbing_active_timeout_not_released,
- ip_block_non_disturbing_active_timeout_released,
- ip_block_disturbing_blocker_dies,
- ip_block_non_disturbing_blocker_dies
+ %%ip_restart_no_block,
+ %%ip_restart_disturbing_block,
+ %%ip_restart_non_disturbing_block,
+ %% Tested in inets_SUITE
+ %%ip_block_disturbing_idle,
+ %%ip_block_non_disturbing_idle,
+ ip_block_503
+ %% Tested in new httpd_SUITE
+ %%ip_block_disturbing_active,
+ %%ip_block_non_disturbing_active,
+ %%ip_block_disturbing_blocker_dies,
+ %%ip_block_non_disturbing_blocker_dies
+ %% No longer relevant
+ %%ip_block_disturbing_active_timeout_not_released,
+ %%ip_block_disturbing_active_timeout_released,
+ %%ip_block_non_disturbing_active_timeout_not_released,
+ %%ip_block_non_disturbing_active_timeout_released,
]},
{ssl, [], [{group, essl}]},
{essl, [],
diff --git a/lib/inets/test/property_test/README b/lib/inets/test/property_test/README
new file mode 100644
index 0000000000..57602bf719
--- /dev/null
+++ b/lib/inets/test/property_test/README
@@ -0,0 +1,12 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The test in this directory are written assuming that the user has a QuickCheck license. They are to be run manually. Some may be possible to be run with other tools, e.g. PropEr.
+
diff --git a/lib/inets/test/property_test/ftp_simple_client_server.erl b/lib/inets/test/property_test/ftp_simple_client_server.erl
new file mode 100644
index 0000000000..40e630ee5c
--- /dev/null
+++ b/lib/inets/test/property_test/ftp_simple_client_server.erl
@@ -0,0 +1,306 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ftp_simple_client_server).
+
+-compile(export_all).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-define(EQC,true).
+%%-define(PROPER,true).
+-endif.
+-endif.
+
+
+-ifdef(EQC).
+
+-include_lib("eqc/include/eqc.hrl").
+-include_lib("eqc/include/eqc_statem.hrl").
+-define(MOD_eqc, eqc).
+-define(MOD_eqc_gen, eqc_gen).
+-define(MOD_eqc_statem, eqc_statem).
+
+-else.
+-ifdef(PROPER).
+
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc, proper).
+-define(MOD_eqc_gen, proper_gen).
+-define(MOD_eqc_statem, proper_statem).
+
+-endif.
+-endif.
+
+-record(state, {
+ initialized = false,
+ priv_dir,
+ data_dir,
+ servers = [], % [ {IP,Port,Userid,Pwd} ]
+ clients = [], % [ client_ref() ]
+ store = [] % [ {Name,Contents} ]
+ }).
+
+-define(fmt(F,A), io:format(F,A)).
+%%-define(fmt(F,A), ok).
+
+-define(v(K,L), proplists:get_value(K,L)).
+
+%%%================================================================
+%%%
+%%% Properties
+%%%
+
+%% This function is for normal eqc calls:
+prop_ftp() ->
+ {ok,PWD} = file:get_cwd(),
+ prop_ftp(filename:join([PWD,?MODULE_STRING++"_data"]),
+ filename:join([PWD,?MODULE_STRING,"_files"])).
+
+%% This function is for calls from common_test test cases:
+prop_ftp(Config) ->
+ prop_ftp(filename:join([?v(property_dir,Config), ?MODULE_STRING++"_data"]),
+ ?v(priv_dir,Config) ).
+
+
+prop_ftp(DataDir, PrivDir) ->
+ S0 = #state{data_dir = DataDir,
+ priv_dir = PrivDir},
+ ?FORALL(Cmds, more_commands(10,commands(?MODULE,S0)),
+ aggregate(command_names(Cmds),
+ begin {_H,S,Result} = run_commands(?MODULE,Cmds),
+ % io:format('**** Result=~p~n',[Result]),
+ % io:format('**** S=~p~n',[S]),
+ % io:format('**** _H=~p~n',[_H]),
+ % io:format('**** Cmds=~p~n',[Cmds]),
+ [cmnd_stop_server(X) || X <- S#state.servers],
+ [inets:stop(ftpc,X) || {ok,X} <- S#state.clients],
+ Result==ok
+ end)
+ ).
+
+%%%================================================================
+%%%
+%%% State model
+%%%
+
+%% @doc Returns the state in which each test case starts. (Unless a different
+%% initial state is supplied explicitly to, e.g. commands/2.)
+-spec initial_state() ->?MOD_eqc_statem:symbolic_state().
+initial_state() ->
+ ?fmt("Initial_state()~n",[]),
+ #state{}.
+
+%% @doc Command generator, S is the current state
+-spec command(S :: ?MOD_eqc_statem:symbolic_state()) -> ?MOD_eqc_gen:gen(eqc_statem:call()).
+
+command(#state{initialized=false,
+ priv_dir=PrivDir}) ->
+ {call,?MODULE,cmnd_init,[PrivDir]};
+
+command(#state{servers=[],
+ priv_dir=PrivDir,
+ data_dir=DataDir}) ->
+ {call,?MODULE,cmnd_start_server,[PrivDir,DataDir]};
+
+command(#state{servers=Ss=[_|_],
+ clients=[]}) ->
+ {call,?MODULE,cmnd_start_client,[oneof(Ss)]};
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=Store=[_|_]
+ }) ->
+ frequency([
+ { 5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ { 5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}},
+ {20, {call,?MODULE,cmnd_get,[oneof(Cs),oneof(Store)]}},
+ {10, {call,?MODULE,cmnd_delete,[oneof(Cs),oneof(Store)]}}
+ ]);
+
+command(#state{servers=Ss=[_|_],
+ clients=Cs=[_|_],
+ store=[]
+ }) ->
+ frequency([
+ {5, {call,?MODULE,cmnd_start_client,[oneof(Ss)]}},
+ {5, {call,?MODULE,cmnd_stop_client,[oneof(Cs)]}},
+ {10, {call,?MODULE,cmnd_put,[oneof(Cs),file_path(),file_contents()]}}
+ ]).
+
+%% @doc Precondition, checked before command is added to the command sequence.
+-spec precondition(S :: ?MOD_eqc_statem:symbolic_state(), C :: ?MOD_eqc_statem:call()) -> boolean().
+
+precondition(#state{clients=Cs}, {call, _, cmnd_put, [C,_,_]}) -> lists:member(C,Cs);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_get, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{clients=Cs, store=Store},
+ {call, _, cmnd_delete, [C,X]}) -> lists:member(C,Cs) andalso lists:member(X,Store);
+
+precondition(#state{servers=Ss}, {call, _, cmnd_start_client, _}) -> Ss =/= [];
+
+precondition(#state{clients=Cs}, {call, _, cmnd_stop_client, [C]}) -> lists:member(C,Cs);
+
+precondition(#state{initialized=IsInit}, {call, _, cmnd_init, _}) -> IsInit==false;
+
+precondition(_S, {call, _, _, _}) -> true.
+
+
+%% @doc Postcondition, checked after command has been evaluated
+%% Note: S is the state before next_state(S,_,C)
+-spec postcondition(S :: ?MOD_eqc_statem:dynamic_state(), C :: ?MOD_eqc_statem:call(),
+ Res :: term()) -> boolean().
+
+postcondition(_S, {call, _, cmnd_get, [_,{_Name,Expected}]}, {ok,Value}) ->
+ Value == Expected;
+
+postcondition(S, {call, _, cmnd_delete, [_,{Name,_Expected}]}, ok) ->
+ ?fmt("file:read_file(..) = ~p~n",[file:read_file(filename:join(S#state.priv_dir,Name))]),
+ {error,enoent} == file:read_file(filename:join(S#state.priv_dir,Name));
+
+postcondition(S, {call, _, cmnd_put, [_,Name,Value]}, ok) ->
+ {ok,Bin} = file:read_file(filename:join(S#state.priv_dir,Name)),
+ Bin == unicode:characters_to_binary(Value);
+
+postcondition(_S, {call, _, cmnd_stop_client, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_client, _}, {ok,_}) -> true;
+
+postcondition(_S, {call, _, cmnd_init, _}, ok) -> true;
+
+postcondition(_S, {call, _, cmnd_start_server, _}, {ok,_}) -> true.
+
+
+%% @doc Next state transformation, S is the current state. Returns next state.
+-spec next_state(S :: ?MOD_eqc_statem:symbolic_state(),
+ V :: ?MOD_eqc_statem:var(),
+ C :: ?MOD_eqc_statem:call()) -> ?MOD_eqc_statem:symbolic_state().
+
+next_state(S, _V, {call, _, cmnd_put, [_,Name,Val]}) ->
+ S#state{store = [{Name,Val} | lists:keydelete(Name,1,S#state.store)]};
+
+next_state(S, _V, {call, _, cmnd_delete, [_,{Name,_Val}]}) ->
+ S#state{store = lists:keydelete(Name,1,S#state.store)};
+
+next_state(S, V, {call, _, cmnd_start_client, _}) ->
+ S#state{clients = [V | S#state.clients]};
+
+next_state(S, V, {call, _, cmnd_start_server, _}) ->
+ S#state{servers = [V | S#state.servers]};
+
+next_state(S, _V, {call, _, cmnd_stop_client, [C]}) ->
+ S#state{clients = S#state.clients -- [C]};
+
+next_state(S, _V, {call, _, cmnd_init, _}) ->
+ S#state{initialized=true};
+
+next_state(S, _V, {call, _, _, _}) ->
+ S.
+
+%%%================================================================
+%%%
+%%% Data model
+%%%
+
+file_path() -> non_empty(list(alphanum_char())).
+%%file_path() -> non_empty( list(oneof([alphanum_char(), utf8_char()])) ).
+
+%%file_contents() -> list(alphanum_char()).
+file_contents() -> list(oneof([alphanum_char(), utf8_char()])).
+
+alphanum_char() -> oneof(lists:seq($a,$z) ++ lists:seq($A,$Z) ++ lists:seq($0,$9)).
+
+utf8_char() -> oneof("åäöÅÄÖ話话カタカナひらがな").
+
+%%%================================================================
+%%%
+%%% Commands doing something with the System Under Test
+%%%
+
+cmnd_init(PrivDir) ->
+ ?fmt('Call cmnd_init(~p)~n',[PrivDir]),
+ os:cmd("killall vsftpd"),
+ clear_files(PrivDir),
+ ok.
+
+cmnd_start_server(PrivDir, DataDir) ->
+ ?fmt('Call cmnd_start_server(~p, ~p)~n',[PrivDir,DataDir]),
+ Cmnd = ["vsftpd ", filename:join(DataDir,"vsftpd.conf"),
+ " -oftpd_banner=erlang_otp_testing"
+ " -oanon_root=",PrivDir
+ ],
+ ?fmt("Cmnd=~s~n",[Cmnd]),
+ case os:cmd(Cmnd) of
+ [] ->
+ {ok,{"localhost",9999,"ftp","[email protected]"}};
+ Other ->
+ {error,Other}
+ end.
+
+cmnd_stop_server({ok,{_Host,Port,_Usr,_Pwd}}) ->
+ os:cmd("kill `netstat -tpln | grep "++integer_to_list(Port)++" | awk '{print $7}' | awk -F/ '{print $1}'`").
+
+cmnd_start_client({ok,{Host,Port,Usr,Pwd}}) ->
+ ?fmt('Call cmnd_start_client(~p)...',[{Host,Port,Usr,Pwd}]),
+ case inets:start(ftpc, [{host,Host},{port,Port}]) of
+ {ok,Client} ->
+ ?fmt("~p...",[{ok,Client}]),
+ case ftp:user(Client, Usr, Pwd) of
+ ok ->
+ ?fmt("OK!~n",[]),
+ {ok,Client};
+ Other ->
+ ?fmt("Other1=~p~n",[Other]),
+ inets:stop(ftpc,Client), Other
+ end;
+ Other ->
+ ?fmt("Other2=~p~n",[Other]),
+ Other
+ end.
+
+cmnd_stop_client({ok,Client}) ->
+ ?fmt('Call cmnd_stop_client(~p)~n',[Client]),
+ inets:stop(ftpc, Client). %% -> ok | Other
+
+cmnd_delete({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_delete(~p, ~p)~n',[Client,Name]),
+ R=ftp:delete(Client, Name),
+ ?fmt("R=~p~n",[R]),
+ R.
+
+cmnd_put({ok,Client}, Name, Value) ->
+ ?fmt('Call cmnd_put(~p, ~p, ~p)...',[Client, Name, Value]),
+ R = ftp:send_bin(Client, unicode:characters_to_binary(Value), Name), % ok | {error,Error}
+ ?fmt('~p~n',[R]),
+ R.
+
+cmnd_get({ok,Client}, {Name,_ExpectedValue}) ->
+ ?fmt('Call cmnd_get(~p, ~p)~n',[Client,Name]),
+ case ftp:recv_bin(Client, Name) of
+ {ok,Bin} -> {ok, unicode:characters_to_list(Bin)};
+ Other -> Other
+ end.
+
+
+clear_files(Dir) ->
+ os:cmd(["rm -fr ",filename:join(Dir,"*")]).
diff --git a/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf b/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf
new file mode 100644
index 0000000000..fd48e2abf0
--- /dev/null
+++ b/lib/inets/test/property_test/ftp_simple_client_server_data/vsftpd.conf
@@ -0,0 +1,26 @@
+
+###
+### Some parameters are given in the vsftpd start command.
+###
+### Typical command-line paramters are such that has a file path
+### component like cert files.
+###
+
+
+listen=YES
+listen_port=9999
+run_as_launching_user=YES
+ssl_enable=NO
+#allow_anon_ssl=YES
+
+background=YES
+
+write_enable=YES
+anonymous_enable=YES
+anon_upload_enable=YES
+anon_mkdir_write_enable=YES
+anon_other_write_enable=YES
+anon_world_readable_only=NO
+
+### Shouldn't be necessary....
+require_ssl_reuse=NO
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 79081f371c..029f6ac4d2 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.10.2
+INETS_VSN = 5.10.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index e81a9f82d2..46d89f0cdb 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -30,6 +30,56 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.5.11</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Added a <c>.app</c> file for the application.</p>
+ <p>
+ Own Id: OTP-12178</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Jinterface 1.5.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Array now show meaningful values in exceptions.</p>
+ <p>
+ Own Id: OTP-12049</p>
+ </item>
+ <item>
+ <p>
+ Documentation improvements.</p>
+ <p>
+ Own Id: OTP-12050</p>
+ </item>
+ <item>
+ <p>
+ Include the cause when raising a new IOException, which
+ should make the reason for the exception clearer.</p>
+ <p>
+ Own Id: OTP-12075</p>
+ </item>
+ <item>
+ <p>
+ Arrays (here: md5 and freeVars) must not be compared with
+ equals, which is broken (compares identity).</p>
+ <p>
+ Own Id: OTP-12121</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.5.9</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/jinterface/ebin/.gitignore b/lib/jinterface/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/jinterface/ebin/.gitignore
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 9ba6a4a0ab..b8a973753a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -20,7 +20,6 @@
package com.ericsson.otp.erlang;
import java.io.IOException;
-import java.io.InputStream;
import java.net.Socket;
import java.util.Random;
@@ -87,7 +86,7 @@ public abstract class AbstractConnection extends Thread {
protected boolean connected = false; // connection status
protected Socket socket; // communication channel
protected OtpPeer peer; // who are we connected to
- protected OtpLocalNode self; // this nodes id
+ protected OtpLocalNode localNode; // this nodes id
String name; // local name of this connection
protected boolean cookieOk = false; // already checked the cookie for this
@@ -137,7 +136,7 @@ public abstract class AbstractConnection extends Thread {
*/
protected AbstractConnection(final OtpLocalNode self, final Socket s)
throws IOException, OtpAuthException {
- this.self = self;
+ this.localNode = self;
peer = new OtpPeer();
socket = s;
@@ -181,7 +180,7 @@ public abstract class AbstractConnection extends Thread {
protected AbstractConnection(final OtpLocalNode self, final OtpPeer other)
throws IOException, OtpAuthException {
peer = other;
- this.self = self;
+ this.localNode = self;
socket = null;
int port;
@@ -234,6 +233,7 @@ public abstract class AbstractConnection extends Thread {
if (!connected) {
throw new IOException("Not connected");
}
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag + version
@@ -246,7 +246,7 @@ public abstract class AbstractConnection extends Thread {
header.write_long(regSendTag);
header.write_any(from);
if (sendCookie) {
- header.write_atom(self.cookie());
+ header.write_atom(localNode.cookie());
} else {
header.write_atom("");
}
@@ -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
@@ -278,6 +278,7 @@ public abstract class AbstractConnection extends Thread {
if (!connected) {
throw new IOException("Not connected");
}
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag + version
@@ -289,7 +290,7 @@ public abstract class AbstractConnection extends Thread {
header.write_tuple_head(3);
header.write_long(sendTag);
if (sendCookie) {
- header.write_atom(self.cookie());
+ header.write_atom(localNode.cookie());
} else {
header.write_atom("");
}
@@ -312,6 +313,7 @@ public abstract class AbstractConnection extends Thread {
private void cookieError(final OtpLocalNode local,
final OtpErlangAtom cookie) throws OtpAuthException {
try {
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag + version
@@ -347,6 +349,7 @@ public abstract class AbstractConnection extends Thread {
msg[0] = new OtpErlangAtom("$gen_cast");
msg[1] = new OtpErlangTuple(msgbody);
+ @SuppressWarnings("resource")
final OtpOutputStream payload = new OtpOutputStream(
new OtpErlangTuple(msg));
@@ -384,6 +387,7 @@ public abstract class AbstractConnection extends Thread {
if (!connected) {
throw new IOException("Not connected");
}
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag
@@ -420,6 +424,7 @@ public abstract class AbstractConnection extends Thread {
if (!connected) {
throw new IOException("Not connected");
}
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag
@@ -468,6 +473,7 @@ public abstract class AbstractConnection extends Thread {
if (!connected) {
throw new IOException("Not connected");
}
+ @SuppressWarnings("resource")
final OtpOutputStream header = new OtpOutputStream(headerLen);
// preamble: 4 byte length + "passthrough" tag
@@ -488,6 +494,7 @@ public abstract class AbstractConnection extends Thread {
do_send(header);
}
+ @SuppressWarnings("resource")
@Override
public void run() {
if (!connected) {
@@ -506,7 +513,7 @@ public abstract class AbstractConnection extends Thread {
// don't return until we get a real message
// or a failure of some kind (e.g. EXIT)
// read length and read buffer must be atomic!
- tick_loop: do {
+ do {
// read 4 bytes - get length of incoming packet
// socket.getInputStream().read(lbuf);
readSock(socket, lbuf);
@@ -526,6 +533,7 @@ public abstract class AbstractConnection extends Thread {
final byte[] tmpbuf = new byte[len];
// i = socket.getInputStream().read(tmpbuf);
readSock(socket, tmpbuf);
+ ibuf.close();
ibuf = new OtpInputStream(tmpbuf, flags);
if (ibuf.read1() != passThrough) {
@@ -567,12 +575,12 @@ public abstract class AbstractConnection extends Thread {
}
cookie = (OtpErlangAtom) head.elementAt(1);
if (sendCookie) {
- if (!cookie.atomValue().equals(self.cookie())) {
- cookieError(self, cookie);
+ if (!cookie.atomValue().equals(localNode.cookie())) {
+ cookieError(localNode, cookie);
}
} else {
if (!cookie.atomValue().equals("")) {
- cookieError(self, cookie);
+ cookieError(localNode, cookie);
}
}
cookieOk = true;
@@ -610,12 +618,12 @@ public abstract class AbstractConnection extends Thread {
}
cookie = (OtpErlangAtom) head.elementAt(2);
if (sendCookie) {
- if (!cookie.atomValue().equals(self.cookie())) {
- cookieError(self, cookie);
+ if (!cookie.atomValue().equals(localNode.cookie())) {
+ cookieError(localNode, cookie);
}
} else {
if (!cookie.atomValue().equals("")) {
- cookieError(self, cookie);
+ cookieError(localNode, cookie);
}
}
cookieOk = true;
@@ -749,13 +757,14 @@ public abstract class AbstractConnection extends Thread {
final int oldLevel = traceLevel;
// pin the value
+ int theLevel = level;
if (level < 0) {
- level = 0;
+ theLevel = 0;
} else if (level > 4) {
- level = 4;
+ theLevel = 4;
}
- traceLevel = level;
+ traceLevel = theLevel;
return oldLevel;
}
@@ -908,18 +917,16 @@ public abstract class AbstractConnection extends Thread {
int got = 0;
final int len = b.length;
int i;
- InputStream is = null;
synchronized (this) {
if (s == null) {
throw new IOException("expected " + len
+ " bytes, socket was closed");
}
- is = s.getInputStream();
}
while (got < len) {
- i = is.read(b, got, len - got);
+ i = s.getInputStream().read(b, got, len - got);
if (i < 0) {
throw new IOException("expected " + len
@@ -943,9 +950,9 @@ public abstract class AbstractConnection extends Thread {
try {
sendStatus("ok");
final int our_challenge = genChallenge();
- sendChallenge(peer.distChoose, self.flags, our_challenge);
+ sendChallenge(peer.distChoose, localNode.flags, our_challenge);
final int her_challenge = recvChallengeReply(our_challenge);
- final byte[] our_digest = genDigest(her_challenge, self.cookie());
+ final byte[] our_digest = genDigest(her_challenge, localNode.cookie());
sendChallengeAck(our_digest);
connected = true;
cookieOk = true;
@@ -959,7 +966,9 @@ public abstract class AbstractConnection extends Thread {
} catch (final Exception e) {
final String nn = peer.node();
close();
- throw new IOException("Error accepting connection from " + nn);
+ IOException ioe = new IOException("Error accepting connection from " + nn);
+ ioe.initCause(e);
+ throw ioe;
}
if (traceLevel >= handshakeThreshold) {
System.out.println("<- MD5 ACCEPTED " + peer.host());
@@ -976,10 +985,10 @@ public abstract class AbstractConnection extends Thread {
System.out.println("-> MD5 CONNECT TO " + peer.host() + ":"
+ port);
}
- sendName(peer.distChoose, self.flags);
+ sendName(peer.distChoose, localNode.flags);
recvStatus();
final int her_challenge = recvChallenge();
- final byte[] our_digest = genDigest(her_challenge, self.cookie());
+ final byte[] our_digest = genDigest(her_challenge, localNode.cookie());
final int our_challenge = genChallenge();
sendChallengeReply(our_challenge, our_digest);
recvChallengeAck(our_challenge);
@@ -990,7 +999,9 @@ public abstract class AbstractConnection extends Thread {
throw ae;
} catch (final Exception e) {
close();
- throw new IOException("Cannot connect to peer node");
+ IOException ioe = new IOException("Cannot connect to peer node");
+ ioe.initCause(e);
+ throw ioe;
}
}
@@ -1050,33 +1061,35 @@ public abstract class AbstractConnection extends Thread {
return res;
}
- protected void sendName(final int dist, final int flags) throws IOException {
+ protected void sendName(final int dist, final int aflags) throws IOException {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
- final String str = self.node();
+ final String str = localNode.node();
obuf.write2BE(str.length() + 7); // 7 bytes + nodename
obuf.write1(AbstractNode.NTYPE_R6);
obuf.write2BE(dist);
- obuf.write4BE(flags);
+ obuf.write4BE(aflags);
obuf.write(str.getBytes());
obuf.writeTo(socket.getOutputStream());
if (traceLevel >= handshakeThreshold) {
- System.out.println("-> " + "HANDSHAKE sendName" + " flags=" + flags
- + " dist=" + dist + " local=" + self);
+ System.out.println("-> " + "HANDSHAKE sendName" + " flags=" + aflags
+ + " dist=" + dist + " local=" + localNode);
}
}
- protected void sendChallenge(final int dist, final int flags,
+ protected void sendChallenge(final int dist, final int aflags,
final int challenge) throws IOException {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
- final String str = self.node();
+ final String str = localNode.node();
obuf.write2BE(str.length() + 11); // 11 bytes + nodename
obuf.write1(AbstractNode.NTYPE_R6);
obuf.write2BE(dist);
- obuf.write4BE(flags);
+ obuf.write4BE(aflags);
obuf.write4BE(challenge);
obuf.write(str.getBytes());
@@ -1084,8 +1097,8 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallenge" + " flags="
- + flags + " dist=" + dist + " challenge=" + challenge
- + " local=" + self);
+ + aflags + " dist=" + dist + " challenge=" + challenge
+ + " local=" + localNode);
}
}
@@ -1096,6 +1109,7 @@ public abstract class AbstractConnection extends Thread {
byte[] tmpbuf;
readSock(socket, lbuf);
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(lbuf, 0);
final int len = ibuf.read2BE();
tmpbuf = new byte[len];
@@ -1103,41 +1117,42 @@ public abstract class AbstractConnection extends Thread {
return tmpbuf;
}
- protected void recvName(final OtpPeer peer) throws IOException {
+ protected void recvName(final OtpPeer apeer) throws IOException {
String hisname = "";
try {
final byte[] tmpbuf = read2BytePackage();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
byte[] tmpname;
final int len = tmpbuf.length;
- peer.ntype = ibuf.read1();
- if (peer.ntype != AbstractNode.NTYPE_R6) {
+ apeer.ntype = ibuf.read1();
+ if (apeer.ntype != AbstractNode.NTYPE_R6) {
throw new IOException("Unknown remote node type");
}
- peer.distLow = peer.distHigh = ibuf.read2BE();
- if (peer.distLow < 5) {
+ apeer.distLow = apeer.distHigh = ibuf.read2BE();
+ if (apeer.distLow < 5) {
throw new IOException("Unknown remote node type");
}
- peer.flags = ibuf.read4BE();
+ apeer.flags = ibuf.read4BE();
tmpname = new byte[len - 7];
ibuf.readN(tmpname);
hisname = OtpErlangString.newString(tmpname);
// Set the old nodetype parameter to indicate hidden/normal status
// When the old handshake is removed, the ntype should also be.
- if ((peer.flags & AbstractNode.dFlagPublished) != 0) {
- peer.ntype = AbstractNode.NTYPE_R4_ERLANG;
+ if ((apeer.flags & AbstractNode.dFlagPublished) != 0) {
+ apeer.ntype = AbstractNode.NTYPE_R4_ERLANG;
} else {
- peer.ntype = AbstractNode.NTYPE_R4_HIDDEN;
+ apeer.ntype = AbstractNode.NTYPE_R4_HIDDEN;
}
- if ((peer.flags & AbstractNode.dFlagExtendedReferences) == 0) {
+ if ((apeer.flags & AbstractNode.dFlagExtendedReferences) == 0) {
throw new IOException(
"Handshake failed - peer cannot handle extended references");
}
- if ((peer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0) {
+ if ((apeer.flags & AbstractNode.dFlagExtendedPidsPorts) == 0) {
throw new IOException(
"Handshake failed - peer cannot handle extended pids and ports");
}
@@ -1147,13 +1162,13 @@ public abstract class AbstractConnection extends Thread {
}
final int i = hisname.indexOf('@', 0);
- peer.node = hisname;
- peer.alive = hisname.substring(0, i);
- peer.host = hisname.substring(i + 1, hisname.length());
+ apeer.node = hisname;
+ apeer.alive = hisname.substring(0, i);
+ apeer.host = hisname.substring(i + 1, hisname.length());
if (traceLevel >= handshakeThreshold) {
- System.out.println("<- " + "HANDSHAKE" + " ntype=" + peer.ntype
- + " dist=" + peer.distHigh + " remote=" + peer);
+ System.out.println("<- " + "HANDSHAKE" + " ntype=" + apeer.ntype
+ + " dist=" + apeer.distHigh + " remote=" + apeer);
}
}
@@ -1163,6 +1178,7 @@ public abstract class AbstractConnection extends Thread {
try {
final byte[] buf = read2BytePackage();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(buf, 0);
peer.ntype = ibuf.read1();
if (peer.ntype != AbstractNode.NTYPE_R6) {
@@ -1195,7 +1211,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("<- " + "HANDSHAKE recvChallenge" + " from="
- + peer.node + " challenge=" + challenge + " local=" + self);
+ + peer.node + " challenge=" + challenge + " local=" + localNode);
}
return challenge;
@@ -1204,6 +1220,7 @@ public abstract class AbstractConnection extends Thread {
protected void sendChallengeReply(final int challenge, final byte[] digest)
throws IOException {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
obuf.write2BE(21);
obuf.write1(ChallengeReply);
@@ -1214,7 +1231,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallengeReply"
+ " challenge=" + challenge + " digest=" + hex(digest)
- + " local=" + self);
+ + " local=" + localNode);
}
}
@@ -1237,6 +1254,7 @@ public abstract class AbstractConnection extends Thread {
try {
final byte[] buf = read2BytePackage();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(buf, 0);
final int tag = ibuf.read1();
if (tag != ChallengeReply) {
@@ -1244,7 +1262,7 @@ public abstract class AbstractConnection extends Thread {
}
challenge = ibuf.read4BE();
ibuf.readN(her_digest);
- final byte[] our_digest = genDigest(our_challenge, self.cookie());
+ final byte[] our_digest = genDigest(our_challenge, localNode.cookie());
if (!digests_equals(her_digest, our_digest)) {
throw new OtpAuthException("Peer authentication error.");
}
@@ -1255,7 +1273,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("<- " + "HANDSHAKE recvChallengeReply"
+ " from=" + peer.node + " challenge=" + challenge
- + " digest=" + hex(her_digest) + " local=" + self);
+ + " digest=" + hex(her_digest) + " local=" + localNode);
}
return challenge;
@@ -1263,6 +1281,7 @@ public abstract class AbstractConnection extends Thread {
protected void sendChallengeAck(final byte[] digest) throws IOException {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
obuf.write2BE(17);
obuf.write1(ChallengeAck);
@@ -1272,7 +1291,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendChallengeAck"
- + " digest=" + hex(digest) + " local=" + self);
+ + " digest=" + hex(digest) + " local=" + localNode);
}
}
@@ -1282,13 +1301,14 @@ public abstract class AbstractConnection extends Thread {
final byte[] her_digest = new byte[16];
try {
final byte[] buf = read2BytePackage();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(buf, 0);
final int tag = ibuf.read1();
if (tag != ChallengeAck) {
throw new IOException("Handshake protocol error");
}
ibuf.readN(her_digest);
- final byte[] our_digest = genDigest(our_challenge, self.cookie());
+ final byte[] our_digest = genDigest(our_challenge, localNode.cookie());
if (!digests_equals(her_digest, our_digest)) {
throw new OtpAuthException("Peer authentication error.");
}
@@ -1301,12 +1321,13 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("<- " + "HANDSHAKE recvChallengeAck" + " from="
+ peer.node + " digest=" + hex(her_digest) + " local="
- + self);
+ + localNode);
}
}
protected void sendStatus(final String status) throws IOException {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
obuf.write2BE(status.length() + 1);
obuf.write1(ChallengeStatus);
@@ -1316,7 +1337,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendStatus" + " status="
- + status + " local=" + self);
+ + status + " local=" + localNode);
}
}
@@ -1324,6 +1345,7 @@ public abstract class AbstractConnection extends Thread {
try {
final byte[] buf = read2BytePackage();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(buf, 0);
final int tag = ibuf.read1();
if (tag != ChallengeStatus) {
@@ -1342,7 +1364,7 @@ public abstract class AbstractConnection extends Thread {
}
if (traceLevel >= handshakeThreshold) {
System.out.println("<- " + "HANDSHAKE recvStatus (ok)" + " local="
- + self);
+ + localNode);
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
index 3ef44b8851..3bb1bbbd18 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java
@@ -128,7 +128,12 @@ public class AbstractNode {
final File dotCookieFile = new File(dotCookieFilename);
br = new BufferedReader(new FileReader(dotCookieFile));
- defaultCookie = br.readLine().trim();
+ final String line = br.readLine();
+ if (line == null) {
+ defaultCookie = "";
+ } else {
+ defaultCookie = line.trim();
+ }
} catch (final IOException e) {
defaultCookie = "";
} finally {
@@ -260,8 +265,7 @@ public class AbstractNode {
final String drive = System.getenv("HOMEDRIVE");
final String path = System.getenv("HOMEPATH");
return (drive != null && path != null) ? drive + path : home;
- } else {
- return home;
}
+ return home;
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java
index 2b085761e3..c8b4fcebde 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Link.java
@@ -41,11 +41,12 @@ class Link {
return local.equals(pid) || remote.equals(pid);
}
- public boolean equals(final OtpErlangPid local, final OtpErlangPid remote) {
- return this.local.equals(local) && this.remote.equals(remote)
- || this.local.equals(remote) && this.remote.equals(local);
+ public boolean equals(final OtpErlangPid alocal, final OtpErlangPid aremote) {
+ return local.equals(alocal) && remote.equals(aremote)
+ || local.equals(aremote) && remote.equals(alocal);
}
+ @Override
public int hashCode() {
if (hashCodeValue == 0) {
OtpErlangObject.Hash hash = new OtpErlangObject.Hash(5);
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
index f476d4594d..fd923f85ae 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile
@@ -32,6 +32,15 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
include $(ERL_TOP)/lib/jinterface/vsn.mk
VSN=$(JINTERFACE_VSN)
+#
+
+EBINDIR=$(ERL_TOP)/lib/jinterface/ebin
+
+APP_FILE= jinterface.app
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBINDIR)/$(APP_FILE)
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -45,7 +54,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/jinterface-$(VSN)
# all java sourcefiles listed in common include file
include $(ERL_TOP)/lib/jinterface/java_src/$(JAVA_CLASS_SUBDIR)/java_files
-TARGET_FILES= $(JAVA_FILES:%=$(JAVA_DEST_ROOT)$(JAVA_CLASS_SUBDIR)%.class)
+TARGET_FILES= $(JAVA_FILES:%=$(JAVA_DEST_ROOT)$(JAVA_CLASS_SUBDIR)%.class) $(APP_TARGET)
JAVA_SRC= $(JAVA_FILES:%=%.java)
JARFILE= OtpErlang.jar
@@ -66,7 +75,7 @@ ifneq ($(V),0)
JARFLAGS=-cfv
endif
-JAVA_OPTIONS =
+JAVA_OPTIONS = -Xlint
ifeq ($(TESTROOT),)
RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)"
@@ -79,6 +88,9 @@ endif
# Make Rules
# ----------------------------------------------------
+$(APP_TARGET): $(APP_SRC) $(ERL_TOP)/lib/jinterface/vsn.mk
+ $(vsn_verbose)sed -e 's;%VSN%;$(JINTERFACE_VSN);' $< > $@
+
debug opt: make_dirs $(JAVA_DEST_ROOT)$(JARFILE)
make_dirs:
@@ -106,6 +118,8 @@ release_spec: opt
$(V_at)$(INSTALL_DATA) $(JAVA_SRC) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang"
$(V_at)$(INSTALL_DIR) "$(RELSYSDIR)/priv"
$(V_at)$(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv"
+ $(V_at)$(INSTALL_DIR) "$(RELSYSDIR)/ebin"
+ $(V_at)$(INSTALL_DATA) $(APP_TARGET) "$(RELSYSDIR)/ebin/$(APP_FILE)"
release_docs_spec:
@@ -113,4 +127,3 @@ release_docs_spec:
# ----------------------------------------------------
-
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 8e8bd473c8..9ad02506fd 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
@@ -358,6 +358,7 @@ public class OtpConnection extends AbstractConnection {
* if the connection is not active or a communication
* error occurs.
*/
+ @SuppressWarnings("resource")
public void send(final OtpErlangPid dest, final OtpErlangObject msg)
throws IOException {
// encode and send the message
@@ -376,6 +377,7 @@ public class OtpConnection extends AbstractConnection {
* if the connection is not active or a communication
* error occurs.
*/
+ @SuppressWarnings("resource")
public void send(final String dest, final OtpErlangObject msg)
throws IOException {
// encode and send the message
@@ -404,7 +406,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/OtpCookedConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
index 5abf6e33f7..43b0cad222 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpCookedConnection.java
@@ -149,6 +149,7 @@ public class OtpCookedConnection extends AbstractConnection {
/*
* send to pid
*/
+ @SuppressWarnings("resource")
void send(final OtpErlangPid from, final OtpErlangPid dest,
final OtpErlangObject msg) throws IOException {
// encode and send the message
@@ -159,6 +160,7 @@ public class OtpCookedConnection extends AbstractConnection {
* send to remote name dest is recipient's registered name, the nodename is
* implied by the choice of connection.
*/
+ @SuppressWarnings("resource")
void send(final OtpErlangPid from, final String dest,
final OtpErlangObject msg) throws IOException {
// encode and send the message
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
index 1868dc7740..8a8ba785d9 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -160,6 +160,7 @@ public class OtpEpmd {
try {
s = new Socket((String) null, EpmdPort.get());
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
obuf.write2BE(node.alive().length() + 1);
obuf.write1(stopReq);
@@ -189,6 +190,7 @@ public class OtpEpmd {
Socket s = null;
try {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
s = new Socket(node.host(), EpmdPort.get());
@@ -219,6 +221,7 @@ public class OtpEpmd {
+ node.host() + " when looking up " + node.alive());
}
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
final int response = ibuf.read1();
@@ -279,6 +282,7 @@ public class OtpEpmd {
Socket s = null;
try {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
s = new Socket((String) null, EpmdPort.get());
@@ -310,13 +314,12 @@ public class OtpEpmd {
final int n = s.getInputStream().read(tmpbuf);
if (n < 0) {
- if (s != null) {
s.close();
- }
throw new IOException("Nameserver not responding on "
+ node.host() + " when publishing " + node.alive());
}
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
final int response = ibuf.read1();
@@ -341,9 +344,7 @@ public class OtpEpmd {
throw new IOException("Nameserver not responding on " + node.host()
+ " when publishing " + node.alive());
} catch (final OtpErlangDecodeException e) {
- if (s != null) {
s.close();
- }
if (traceLevel >= traceThreshold) {
System.out.println("<- (invalid response)");
}
@@ -351,9 +352,7 @@ public class OtpEpmd {
+ " when publishing " + node.alive());
}
- if (s != null) {
s.close();
- }
return null;
}
@@ -366,6 +365,7 @@ public class OtpEpmd {
Socket s = null;
try {
+ @SuppressWarnings("resource")
final OtpOutputStream obuf = new OtpOutputStream();
try {
s = new Socket(address, EpmdPort.get());
@@ -390,6 +390,7 @@ public class OtpEpmd {
out.write(buffer, 0, bytesRead);
}
final byte[] tmpbuf = out.toByteArray();
+ @SuppressWarnings("resource")
final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
ibuf.read4BE(); // read port int
// final int port = ibuf.read4BE();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
index 0371740b26..bff3e2c0e3 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java
@@ -18,17 +18,15 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang atoms. Atoms can be created from
* strings whose length is not more than {@link #maxAtomLength maxAtomLength}
* characters.
*/
-public class OtpErlangAtom extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangAtom extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = -3204386396807876641L;
+ private static final long serialVersionUID = -3204386396807876641L;
/** The maximun allowed length of an atom, in characters */
public static final int maxAtomLength = 0xff; // one byte length
@@ -119,9 +117,8 @@ public class OtpErlangAtom extends OtpErlangObject implements Serializable,
public String toString() {
if (atomNeedsQuoting(atom)) {
return "'" + escapeSpecialChars(atom) + "'";
- } else {
- return atom;
}
+ return atom;
}
/**
@@ -139,8 +136,8 @@ public class OtpErlangAtom extends OtpErlangObject implements Serializable,
return false;
}
- final OtpErlangAtom atom = (OtpErlangAtom) o;
- return this.atom.compareTo(atom.atom) == 0;
+ final OtpErlangAtom other = (OtpErlangAtom) o;
+ return this.atom.compareTo(other.atom) == 0;
}
@Override
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java
index a9eaad540e..0891781f8d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBinary.java
@@ -18,16 +18,14 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang binaries. Anything that can be
* represented as a sequence of bytes can be made into an Erlang binary.
*/
-public class OtpErlangBinary extends OtpErlangBitstr implements Serializable,
- Cloneable {
+public class OtpErlangBinary extends OtpErlangBitstr {
// don't change this!
- static final long serialVersionUID = -3781009633593609217L;
+ private static final long serialVersionUID = -3781009633593609217L;
/**
* Create a binary from a byte array
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java
index 97897fe182..8cb4e0e685 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBitstr.java
@@ -20,17 +20,15 @@ package com.ericsson.otp.erlang;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang bitstrs. An Erlang bitstr is an
* Erlang binary with a length not an integral number of bytes (8-bit). Anything
* can be represented as a sequence of bytes can be made into an Erlang bitstr.
*/
-public class OtpErlangBitstr extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangBitstr extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = -3781009633593609217L;
+ private static final long serialVersionUID = -3781009633593609217L;
protected byte[] bin;
protected int pad_bits;
@@ -63,18 +61,18 @@ public class OtpErlangBitstr extends OtpErlangObject implements Serializable,
check_bitstr(this.bin, this.pad_bits);
}
- private void check_bitstr(final byte[] bin, final int pad_bits) {
- if (pad_bits < 0 || 7 < pad_bits) {
+ private void check_bitstr(final byte[] abin, final int a_pad_bits) {
+ if (a_pad_bits < 0 || 7 < a_pad_bits) {
throw new java.lang.IllegalArgumentException(
"Padding must be in range 0..7");
}
- if (pad_bits != 0 && bin.length == 0) {
+ if (a_pad_bits != 0 && abin.length == 0) {
throw new java.lang.IllegalArgumentException(
"Padding on zero length bitstr");
}
- if (bin.length != 0) {
+ if (abin.length != 0) {
// Make sure padding is zero
- bin[bin.length - 1] &= ~((1 << pad_bits) - 1);
+ abin[abin.length - 1] &= ~((1 << a_pad_bits) - 1);
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java
index b97b5b7d90..eecd2ea288 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangBoolean.java
@@ -18,14 +18,12 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang booleans, which are special cases of
* atoms with values 'true' and 'false'.
*/
-public class OtpErlangBoolean extends OtpErlangAtom implements Serializable,
- Cloneable {
+public class OtpErlangBoolean extends OtpErlangAtom {
// don't change this!
static final long serialVersionUID = 1087178844844988393L;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java
index 2d598c119e..eb6f3d8aba 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangByte.java
@@ -18,15 +18,13 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangByte extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangByte extends OtpErlangLong {
// don't change this!
- static final long serialVersionUID = 5778019796466613446L;
+ private static final long serialVersionUID = 5778019796466613446L;
/**
* Create an Erlang integer from the given value.
@@ -56,6 +54,6 @@ public class OtpErlangByte extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final byte i = byteValue();
+ byteValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java
index b442bbcec1..e7c6dd8ad4 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangChar.java
@@ -18,15 +18,13 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangChar extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangChar extends OtpErlangLong {
// don't change this!
- static final long serialVersionUID = 3225337815669398204L;
+ private static final long serialVersionUID = 3225337815669398204L;
/**
* Create an Erlang integer from the given value.
@@ -56,6 +54,6 @@ public class OtpErlangChar extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final char i = charValue();
+ charValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java
index db55deaedf..6986e26908 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDecodeException.java
@@ -26,6 +26,8 @@ package com.ericsson.otp.erlang;
* @see OtpInputStream
*/
public class OtpErlangDecodeException extends OtpErlangException {
+ private static final long serialVersionUID = 1L;
+
/**
* Provides a detailed message.
*/
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java
index 793940e858..e92ce11431 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangDouble.java
@@ -18,7 +18,6 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang floats and doubles. Erlang defines
@@ -26,10 +25,9 @@ import java.io.Serializable;
* {@link OtpErlangFloat} are used to provide representations corresponding to
* the Java types Double and Float.
*/
-public class OtpErlangDouble extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangDouble extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = 132947104811974021L;
+ private static final long serialVersionUID = 132947104811974021L;
private final double d;
@@ -120,8 +118,8 @@ public class OtpErlangDouble extends OtpErlangObject implements Serializable,
return false;
}
- final OtpErlangDouble d = (OtpErlangDouble) o;
- return this.d == d.d;
+ final OtpErlangDouble other = (OtpErlangDouble) o;
+ return this.d == other.d;
}
@Override
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java
index 8662b74c53..7d48f848f0 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFloat.java
@@ -18,15 +18,13 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang floats and doubles.
*/
-public class OtpErlangFloat extends OtpErlangDouble implements Serializable,
- Cloneable {
+public class OtpErlangFloat extends OtpErlangDouble {
// don't change this!
- static final long serialVersionUID = -2231546377289456934L;
+ private static final long serialVersionUID = -2231546377289456934L;
/**
* Create an Erlang float from the given float value.
@@ -53,6 +51,6 @@ public class OtpErlangFloat extends OtpErlangDouble implements Serializable,
throws OtpErlangDecodeException, OtpErlangRangeException {
super(buf);
- final float f = floatValue();
+ floatValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java
index fc104e9564..05fa0cbb23 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangFun.java
@@ -18,10 +18,9 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
import java.util.Arrays;
-public class OtpErlangFun extends OtpErlangObject implements Serializable {
+public class OtpErlangFun extends OtpErlangObject {
// don't change this!
private static final long serialVersionUID = -3423031125356706472L;
@@ -94,7 +93,7 @@ public class OtpErlangFun extends OtpErlangObject implements Serializable {
return false;
}
} else {
- if (!md5.equals(f.md5)) {
+ if (!Arrays.equals(md5, f.md5)) {
return false;
}
}
@@ -104,7 +103,7 @@ public class OtpErlangFun extends OtpErlangObject implements Serializable {
if (freeVars == null) {
return f.freeVars == null;
}
- return freeVars.equals(f.freeVars);
+ return Arrays.equals(freeVars, f.freeVars);
}
@Override
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java
index d947421459..741fc29dd0 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangInt.java
@@ -18,15 +18,13 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangInt extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangInt extends OtpErlangLong {
// don't change this!
- static final long serialVersionUID = 1229430977614805556L;
+ private static final long serialVersionUID = 1229430977614805556L;
/**
* Create an Erlang integer from the given value.
@@ -56,6 +54,6 @@ public class OtpErlangInt extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final int j = intValue();
+ intValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
index 3456fd7412..9f7c5f5499 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangList.java
@@ -18,7 +18,6 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
@@ -30,9 +29,9 @@ import java.util.NoSuchElementException;
* The arity of the list is the number of elements it contains.
*/
public class OtpErlangList extends OtpErlangObject implements
- Iterable<OtpErlangObject>, Serializable, Cloneable {
+ Iterable<OtpErlangObject> {
// don't change this!
- static final long serialVersionUID = 5999112769036676548L;
+ private static final long serialVersionUID = 5999112769036676548L;
private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
@@ -187,12 +186,11 @@ public class OtpErlangList extends OtpErlangObject implements
public OtpErlangObject[] elements() {
if (arity() == 0) {
return NO_ELEMENTS;
- } else {
+ }
final OtpErlangObject[] res = new OtpErlangObject[arity()];
System.arraycopy(elems, 0, res, 0, res.length);
return res;
}
- }
/**
* Get the string representation of the list.
@@ -326,7 +324,7 @@ public class OtpErlangList extends OtpErlangObject implements
try {
return new OtpErlangList(elements(), getLastTail());
} catch (final OtpErlangException e) {
- return null;
+ throw new AssertionError(this);
}
}
@@ -361,9 +359,8 @@ public class OtpErlangList extends OtpErlangObject implements
if (arity >= n) {
if (arity == n && lastTail != null) {
return lastTail;
- } else {
- return new SubList(this, n);
}
+ return new SubList(this, n);
}
return null;
}
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 7e3e2a7296..c6021a6ae1 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
@@ -18,7 +18,6 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
import java.math.BigInteger;
/**
@@ -30,10 +29,9 @@ import java.math.BigInteger;
* {@link OtpErlangUInt} and {@link OtpErlangUShort} are provided for Corba
* compatibility. See the documentation for IC for more information.
*/
-public class OtpErlangLong extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangLong extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = 1610466859236755096L;
+ private static final long serialVersionUID = 1610466859236755096L;
private long val;
private BigInteger bigVal = null;
@@ -51,8 +49,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) {
@@ -94,9 +92,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
public BigInteger bigIntegerValue() {
if (bigVal != null) {
return bigVal;
- } else {
- return BigInteger.valueOf(val);
}
+ return BigInteger.valueOf(val);
}
/**
@@ -109,9 +106,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
public long longValue() {
if (bigVal != null) {
return bigVal.longValue();
- } else {
- return val;
}
+ return val;
}
/**
@@ -159,7 +155,7 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
}
if (val == 0 || val == -1) {
return 0;
- } else {
+ }
// Binary search for bit length
int i = 32; // mask length
long m = (1L << i) - 1; // AND mask with ones in little end
@@ -193,7 +189,6 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
}
return i;
}
- }
/**
* Return the signum function of this object.
@@ -203,9 +198,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
public int signum() {
if (bigVal != null) {
return bigVal.signum();
- } else {
- return val > 0 ? 1 : val < 0 ? -1 : 0;
}
+ return val > 0 ? 1 : val < 0 ? -1 : 0;
}
/**
@@ -342,9 +336,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
public String toString() {
if (bigVal != null) {
return "" + bigVal;
- } else {
- return "" + val;
}
+ return "" + val;
}
/**
@@ -392,8 +385,7 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
protected int doHashCode() {
if (bigVal != null) {
return bigVal.hashCode();
- } else {
- return BigInteger.valueOf(val).hashCode();
}
+ return BigInteger.valueOf(val).hashCode();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
index 03c18e55a2..7f1a64b87d 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -18,7 +18,6 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang maps. Maps are created from one or
@@ -29,8 +28,7 @@ import java.io.Serializable;
* values can be retrieved as arrays and the value for a key can be queried.
*
*/
-public class OtpErlangMap extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangMap extends OtpErlangObject {
// don't change this!
private static final long serialVersionUID = -6410770117696198497L;
@@ -58,15 +56,23 @@ public class OtpErlangMap extends OtpErlangObject implements Serializable,
/**
* Create a map from an array of terms.
*
- * @param elems
+ * @param keys
* the array of terms to create the map from.
- * @param start
- * the offset of the first term to insert.
+ * @param kstart
+ * the offset of the first key to insert.
+ * @param kcount
+ * the number of keys to insert.
+ * @param values
+ * the array of values to create the map from.
+ * @param vstart
+ * the offset of the first value to insert.
* @param vcount
- * the number of terms to insert.
+ * the number of values to insert.
*
* @exception java.lang.IllegalArgumentException
* if any array is empty (null) or contains null elements.
+ * @exception java.lang.IllegalArgumentException
+ * if kcount and vcount differ.
*/
public OtpErlangMap(final OtpErlangObject[] keys, final int kstart,
final int kcount, final OtpErlangObject[] values, final int vstart,
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
index 81220c5685..5215e5887b 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangObject.java
@@ -171,14 +171,14 @@ public abstract class OtpErlangObject implements Serializable, Cloneable {
for (j = 0, k = 0;
j + 4 < b.length;
j += 4, k += 1, k %= 3) {
- abc[k] += ((int)b[j+0] & 0xFF) + ((int)b[j+1]<<8 & 0xFF00)
- + ((int)b[j+2]<<16 & 0xFF0000) + ((int)b[j+3]<<24);
+ abc[k] += (b[j+0] & 0xFF) + (b[j+1]<<8 & 0xFF00)
+ + (b[j+2]<<16 & 0xFF0000) + (b[j+3]<<24);
mix();
}
for (int n = 0, m = 0xFF;
j < b.length;
j++, n += 8, m <<= 8) {
- abc[k] += (int)b[j]<<n & m;
+ abc[k] += b[j]<<n & m;
}
mix();
}
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 fe81ce302d..4c9f5c78a3 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
@@ -18,16 +18,14 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang PIDs. PIDs represent Erlang
* processes and consist of a nodename and a number of integers.
*/
-public class OtpErlangPid extends OtpErlangObject implements Serializable,
- Cloneable, Comparable<Object> {
+public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> {
// don't change this!
- static final long serialVersionUID = 1664394142301803659L;
+ private static final long serialVersionUID = 1664394142301803659L;
private final String node;
private final int id;
@@ -162,7 +160,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.
@@ -197,14 +195,11 @@ public class OtpErlangPid extends OtpErlangObject implements Serializable,
if (serial == pid.serial) {
if (id == pid.id) {
return node.compareTo(pid.node);
- } else {
+ }
return id - pid.id;
}
- } else {
return serial - pid.serial;
}
- } else {
return creation - pid.creation;
}
}
-}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
index 2a0eab0a9c..8557e17325 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java
@@ -18,15 +18,13 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang ports.
*/
-public class OtpErlangPort extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangPort extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = 4037115468007644704L;
+ private static final long serialVersionUID = 4037115468007644704L;
private final String node;
private final int id;
@@ -40,6 +38,7 @@ public class OtpErlangPort extends OtpErlangObject implements Serializable,
*
* @deprecated use OtpLocalNode:createPort() instead
*/
+ @SuppressWarnings("unused")
private OtpErlangPort(final OtpSelf self) {
final OtpErlangPort p = self.createPort();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
index 8056439962..13a83333fa 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java
@@ -18,17 +18,15 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang refs. There are two styles of Erlang
* refs, old style (one id value) and new style (array of id values). This class
* manages both types.
*/
-public class OtpErlangRef extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangRef extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = -7022666480768586521L;
+ private static final long serialVersionUID = -7022666480768586521L;
private final String node;
private final int creation;
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java
index cd232570dd..6ef56defbd 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangShort.java
@@ -18,13 +18,11 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangShort extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangShort extends OtpErlangLong {
// don't change this!
static final long serialVersionUID = 7162345156603088099L;
@@ -57,7 +55,7 @@ public class OtpErlangShort extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final short j = shortValue();
+ shortValue();
}
}
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 6766b52ce5..1bccfcc567 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
@@ -18,17 +18,14 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
-import java.lang.Character;
import java.io.UnsupportedEncodingException;
/**
* Provides a Java representation of Erlang strings.
*/
-public class OtpErlangString extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangString extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = -7053595217604929233L;
+ private static final long serialVersionUID = -7053595217604929233L;
private final String str;
@@ -41,8 +38,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.
@@ -140,6 +135,7 @@ public class OtpErlangString extends OtpErlangObject implements Serializable,
return false;
}
+ @Override
protected int doHashCode() {
return str.hashCode();
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
index bffce7f14d..dffaa530cd 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangTuple.java
@@ -18,7 +18,6 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang tuples. Tuples are created from one
@@ -29,10 +28,9 @@ import java.io.Serializable;
* indexed from 0 to (arity-1) and can be retrieved individually by using the
* appropriate index.
*/
-public class OtpErlangTuple extends OtpErlangObject implements Serializable,
- Cloneable {
+public class OtpErlangTuple extends OtpErlangObject {
// don't change this!
- static final long serialVersionUID = 9163498658004915935L;
+ private static final long serialVersionUID = 9163498658004915935L;
private static final OtpErlangObject[] NO_ELEMENTS = new OtpErlangObject[0];
@@ -51,9 +49,8 @@ public class OtpErlangTuple extends OtpErlangObject implements Serializable,
if (elem == null) {
throw new java.lang.IllegalArgumentException(
"Tuple element cannot be null");
- } else {
- elems = new OtpErlangObject[] { elem };
}
+ elems = new OtpErlangObject[] { elem };
}
/**
@@ -242,6 +239,7 @@ public class OtpErlangTuple extends OtpErlangObject implements Serializable,
return true;
}
+ @Override
protected int doHashCode() {
OtpErlangObject.Hash hash = new OtpErlangObject.Hash(9);
final int a = arity();
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java
index f01354d821..a02996e437 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUInt.java
@@ -18,13 +18,11 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangUInt extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangUInt extends OtpErlangLong {
// don't change this!
static final long serialVersionUID = -1450956122937471885L;
@@ -40,7 +38,7 @@ public class OtpErlangUInt extends OtpErlangLong implements Serializable,
public OtpErlangUInt(final int i) throws OtpErlangRangeException {
super(i);
- final int j = uIntValue();
+ uIntValue();
}
/**
@@ -62,6 +60,6 @@ public class OtpErlangUInt extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final int j = uIntValue();
+ uIntValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java
index 6b6bc7a56b..e9d251f815 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangUShort.java
@@ -18,13 +18,11 @@
*/
package com.ericsson.otp.erlang;
-import java.io.Serializable;
/**
* Provides a Java representation of Erlang integral types.
*/
-public class OtpErlangUShort extends OtpErlangLong implements Serializable,
- Cloneable {
+public class OtpErlangUShort extends OtpErlangLong {
// don't change this!
static final long serialVersionUID = 300370950578307246L;
@@ -40,7 +38,7 @@ public class OtpErlangUShort extends OtpErlangLong implements Serializable,
public OtpErlangUShort(final short s) throws OtpErlangRangeException {
super(s);
- final short j = uShortValue();
+ uShortValue();
}
/**
@@ -62,6 +60,6 @@ public class OtpErlangUShort extends OtpErlangLong implements Serializable,
throws OtpErlangRangeException, OtpErlangDecodeException {
super(buf);
- final short j = uShortValue();
+ uShortValue();
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java
index 33d25b6021..874c7da104 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpException.java
@@ -22,6 +22,8 @@ package com.ericsson.otp.erlang;
* Base class for the other OTP exception classes.
*/
public abstract class OtpException extends Exception {
+ private static final long serialVersionUID = 1L;
+
/**
* Provides no message.
*/
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 0d1342d796..bab0629382 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -21,6 +21,7 @@ package com.ericsson.otp.erlang;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.Arrays;
/**
* Provides a stream for decoding Erlang terms from external format.
@@ -84,16 +85,17 @@ public class OtpInputStream extends ByteArrayInputStream {
*
* @return the previous position in the stream.
*/
- public int setPos(int pos) {
+ public int setPos(final int pos) {
final int oldpos = super.pos;
+ int apos = pos;
if (pos > super.count) {
- pos = super.count;
+ apos = super.count;
} else if (pos < 0) {
- pos = 0;
+ apos = 0;
}
- super.pos = pos;
+ super.pos = apos;
return oldpos;
}
@@ -107,8 +109,8 @@ public class OtpInputStream extends ByteArrayInputStream {
* @exception OtpErlangDecodeException
* if the next byte cannot be read.
*/
- public int readN(final byte[] buf) throws OtpErlangDecodeException {
- return this.readN(buf, 0, buf.length);
+ public int readN(final byte[] abuf) throws OtpErlangDecodeException {
+ return this.readN(abuf, 0, abuf.length);
}
/**
@@ -120,12 +122,12 @@ public class OtpInputStream extends ByteArrayInputStream {
* @exception OtpErlangDecodeException
* if the next byte cannot be read.
*/
- public int readN(final byte[] buf, final int off, final int len)
+ public int readN(final byte[] abuf, final int off, final int len)
throws OtpErlangDecodeException {
if (len == 0 && available() == 0) {
return 0;
}
- final int i = super.read(buf, off, len);
+ final int i = super.read(abuf, off, len);
if (i < 0) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
@@ -213,7 +215,6 @@ public class OtpInputStream extends ByteArrayInputStream {
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
return (b[0] << 8 & 0xff00) + (b[1] & 0xff);
}
@@ -232,7 +233,6 @@ public class OtpInputStream extends ByteArrayInputStream {
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
return (b[0] << 24 & 0xff000000) + (b[1] << 16 & 0xff0000)
+ (b[2] << 8 & 0xff00) + (b[3] & 0xff);
}
@@ -252,7 +252,6 @@ public class OtpInputStream extends ByteArrayInputStream {
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
return (b[1] << 8 & 0xff00) + (b[0] & 0xff);
}
@@ -271,7 +270,6 @@ public class OtpInputStream extends ByteArrayInputStream {
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
return (b[3] << 24 & 0xff000000) + (b[2] << 16 & 0xff0000)
+ (b[1] << 8 & 0xff00) + (b[0] & 0xff);
}
@@ -287,17 +285,17 @@ public class OtpInputStream extends ByteArrayInputStream {
* @exception OtpErlangDecodeException
* if the next byte cannot be read.
*/
- public long readLE(int n) throws OtpErlangDecodeException {
+ public long readLE(final int n) throws OtpErlangDecodeException {
final byte[] b = new byte[n];
try {
super.read(b);
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
long v = 0;
- while (n-- > 0) {
- v = v << 8 | (long) b[n] & 0xff;
+ int i = n;
+ while (i-- > 0) {
+ v = v << 8 | (long) b[i] & 0xff;
}
return v;
}
@@ -320,7 +318,6 @@ public class OtpInputStream extends ByteArrayInputStream {
} catch (final IOException e) {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- ;
long v = 0;
for (int i = 0; i < n; i++) {
v = v << 8 | (long) b[i] & 0xff;
@@ -349,6 +346,7 @@ public class OtpInputStream extends ByteArrayInputStream {
* @exception OtpErlangDecodeException
* if the next term in the stream is not an atom.
*/
+ @SuppressWarnings("fallthrough")
public String read_atom() throws OtpErlangDecodeException {
int tag;
int len = -1;
@@ -381,7 +379,7 @@ public class OtpInputStream extends ByteArrayInputStream {
case OtpExternal.smallAtomUtf8Tag:
len = read1();
- /* fall through */
+ // fall-through
case OtpExternal.atomUtf8Tag:
if (len < 0) {
len = read2BE();
@@ -819,7 +817,7 @@ public class OtpInputStream extends ByteArrayInputStream {
if (unsigned) {
if (c < 0) {
throw new OtpErlangDecodeException("Value not unsigned: "
- + b);
+ + Arrays.toString(b));
}
while (b[i] == 0) {
i++; // Skip leading zero sign bytes
@@ -844,7 +842,7 @@ public class OtpInputStream extends ByteArrayInputStream {
if (b.length - i > 8) {
// More than 64 bits of value
throw new OtpErlangDecodeException(
- "Value does not fit in long: " + b);
+ "Value does not fit in long: " + Arrays.toString(b));
}
// Convert the necessary bytes
for (v = c < 0 ? -1 : 0; i < b.length; i++) {
@@ -1054,7 +1052,7 @@ public class OtpInputStream extends ByteArrayInputStream {
}
return new OtpErlangFun(pid, module, index, uniq, freeVars);
} else if (tag == OtpExternal.newFunTag) {
- final int n = read4BE();
+ read4BE();
final int arity = read1();
final byte[] md5 = new byte[16];
readN(md5);
@@ -1148,13 +1146,13 @@ public class OtpInputStream extends ByteArrayInputStream {
}
final int size = read4BE();
- final byte[] buf = new byte[size];
+ final byte[] abuf = new byte[size];
final java.util.zip.InflaterInputStream is =
new java.util.zip.InflaterInputStream(this, new java.util.zip.Inflater(), size);
int curPos = 0;
try {
int curRead;
- while(curPos < size && (curRead = is.read(buf, curPos, size - curPos)) != -1) {
+ while(curPos < size && (curRead = is.read(abuf, curPos, size - curPos)) != -1) {
curPos += curRead;
}
if (curPos != size) {
@@ -1165,7 +1163,8 @@ public class OtpInputStream extends ByteArrayInputStream {
throw new OtpErlangDecodeException("Cannot read from input stream");
}
- final OtpInputStream ois = new OtpInputStream(buf, flags);
+ @SuppressWarnings("resource")
+ final OtpInputStream ois = new OtpInputStream(abuf, flags);
return ois.read_any();
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java
index 903a446258..a5a4d86602 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMD5.java
@@ -101,8 +101,11 @@ class OtpMD5 {
private void to_buffer(int to_start, final int[] from, int from_start,
int num) {
- while (num-- > 0) {
- buffer[to_start++] = from[from_start++];
+ int ix = num;
+ int to_ix = to_start;
+ int from_ix = from_start;
+ while (ix-- > 0) {
+ buffer[to_ix++] = from[from_ix++];
}
}
@@ -121,7 +124,7 @@ class OtpMD5 {
count[1] = plus(count[1], shr(inlen, 29));
- /* dumpstate(); */
+ // dumpstate();
if (inlen >= partlen) {
to_buffer(index, bytes, 0, (int) partlen);
@@ -144,6 +147,7 @@ class OtpMD5 {
}
+ @SuppressWarnings("unused")
private void dumpstate() {
System.out.println("state = {" + state[0] + ", " + state[1] + ", "
+ state[2] + ", " + state[3] + "}");
@@ -185,30 +189,30 @@ class OtpMD5 {
private long FF(long a, final long b, final long c, final long d,
final long x, final long s, final long ac) {
- a = plus(a, plus(plus(F(b, c, d), x), ac));
- a = ROTATE_LEFT(a, s);
- return plus(a, b);
+ long tmp = plus(a, plus(plus(F(b, c, d), x), ac));
+ tmp = ROTATE_LEFT(tmp, s);
+ return plus(tmp, b);
}
private long GG(long a, final long b, final long c, final long d,
final long x, final long s, final long ac) {
- a = plus(a, plus(plus(G(b, c, d), x), ac));
- a = ROTATE_LEFT(a, s);
- return plus(a, b);
+ long tmp = plus(a, plus(plus(G(b, c, d), x), ac));
+ tmp = ROTATE_LEFT(tmp, s);
+ return plus(tmp, b);
}
private long HH(long a, final long b, final long c, final long d,
final long x, final long s, final long ac) {
- a = plus(a, plus(plus(H(b, c, d), x), ac));
- a = ROTATE_LEFT(a, s);
- return plus(a, b);
+ long tmp = plus(a, plus(plus(H(b, c, d), x), ac));
+ tmp = ROTATE_LEFT(tmp, s);
+ return plus(tmp, b);
}
private long II(long a, final long b, final long c, final long d,
final long x, final long s, final long ac) {
- a = plus(a, plus(plus(I(b, c, d), x), ac));
- a = ROTATE_LEFT(a, s);
- return plus(a, b);
+ long tmp = plus(a, plus(plus(I(b, c, d), x), ac));
+ tmp = ROTATE_LEFT(tmp, s);
+ return plus(tmp, b);
}
private void decode(final long output[], final int input[],
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 0fd93b09f4..fc592c222c 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.
* </p>
*
+ * <p>
* 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
@@ -127,14 +128,14 @@ public class OtpMbox {
* supercede that name.
* </p>
*
- * @param name
+ * @param aname
* the name to register for the mailbox. Specify null to
* unregister the existing name from this mailbox.
*
* @return true if the name was available, or false otherwise.
*/
- public synchronized boolean registerName(final String name) {
- return home.registerName(name, this);
+ public synchronized boolean registerName(final String aname) {
+ return home.registerName(aname, this);
}
/**
@@ -349,21 +350,21 @@ public class OtpMbox {
* Send a message to a named mailbox created from the same node as this
* mailbox.
*
- * @param name
+ * @param aname
* the registered name of recipient mailbox.
*
* @param msg
* the body of the message to send.
*
*/
- public void send(final String name, final OtpErlangObject msg) {
- home.deliver(new OtpMsg(self, name, (OtpErlangObject) msg.clone()));
+ public void send(final String aname, final OtpErlangObject msg) {
+ home.deliver(new OtpMsg(self, aname, (OtpErlangObject) msg.clone()));
}
/**
* Send a message to a named mailbox created from another node.
*
- * @param name
+ * @param aname
* the registered name of recipient mailbox.
*
* @param node
@@ -374,23 +375,23 @@ public class OtpMbox {
* the body of the message to send.
*
*/
- public void send(final String name, final String node,
+ public void send(final String aname, final String node,
final OtpErlangObject msg) {
try {
final String currentNode = home.node();
if (node.equals(currentNode)) {
- send(name, msg);
+ send(aname, msg);
} else if (node.indexOf('@', 0) < 0
&& node.equals(currentNode.substring(0, currentNode
.indexOf('@', 0)))) {
- send(name, msg);
+ send(aname, msg);
} else {
// other node
final OtpCookedConnection conn = home.getConnection(node);
if (conn == null) {
return;
}
- conn.send(self, name, msg);
+ conn.send(self, aname, msg);
}
} catch (final Exception e) {
}
@@ -420,7 +421,6 @@ public class OtpMbox {
/**
* Equivalent to <code>exit(new OtpErlangAtom(reason))</code>.
- * </p>
*
* @see #exit(OtpErlangObject)
*/
@@ -629,8 +629,8 @@ public class OtpMbox {
* @return the {@link OtpErlangPid pid} corresponding to the registered
* name, or null if the name is not known on this node.
*/
- public OtpErlangPid whereis(final String name) {
- return home.whereis(name);
+ public OtpErlangPid whereis(final String aname) {
+ return home.whereis(aname);
}
/**
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 6f507bf4bb..7c5bc69361 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;
* </p>
*
* <p>
- * The header information that is available is as follows: <lu>
+ * The header information that is available is as follows: <ul>
* <li> a tag indicating the type of message
* <li> the intended recipient of the message, either as a
* {@link OtpErlangPid pid} or as a String, but never both.
* <li> (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. </lu>
+ * is {@link OtpMsg#regSendTag regSendTag} contain sender information. </ul>
*
* <p>
* Message are sent using the Erlang external format (see separate
@@ -129,13 +129,14 @@ public class OtpMsg {
}
// other message types (link, unlink)
- OtpMsg(int tag, final OtpErlangPid from, final OtpErlangPid to) {
+ OtpMsg(final int tag, final OtpErlangPid from, final OtpErlangPid to) {
// convert TT-tags to equiv non-TT versions
+ int atag = tag;
if (tag > 10) {
- tag -= 10;
+ atag -= 10;
}
- this.tag = tag;
+ this.tag = atag;
this.from = from;
this.to = to;
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
index 7ead0b9c54..68addb9f2c 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java
@@ -74,7 +74,7 @@ public class OtpNode extends OtpLocalNode {
OtpNodeStatus handler;
// flags
- private int flags = 0;
+ private int connFlags = 0;
/**
* <p>
@@ -143,12 +143,12 @@ public class OtpNode extends OtpLocalNode {
init(port);
}
- private synchronized void init(final int port) throws IOException {
+ private synchronized void init(final int aport) throws IOException {
if (!initDone) {
connections = new Hashtable<String, OtpCookedConnection>(17,
(float) 0.95);
mboxes = new Mailboxes();
- acceptor = new Acceptor(port);
+ acceptor = new Acceptor(aport);
initDone = true;
}
}
@@ -314,13 +314,13 @@ public class OtpNode extends OtpLocalNode {
* OtpNodeStatus} handler object contains callback methods, that will be
* called when certain events occur.
*
- * @param handler
+ * @param ahandler
* the callback object to register. To clear the handler, specify
* null as the handler to use.
*
*/
- public synchronized void registerStatusHandler(final OtpNodeStatus handler) {
- this.handler = handler;
+ public synchronized void registerStatusHandler(final OtpNodeStatus ahandler) {
+ this.handler = ahandler;
}
/**
@@ -344,7 +344,7 @@ public class OtpNode extends OtpLocalNode {
* ;
* </pre>
*
- * @param node
+ * @param anode
* the name of the node to ping.
*
* @param timeout
@@ -362,11 +362,11 @@ public class OtpNode extends OtpLocalNode {
*
* the reply: <- SEND {2,'',#Pid<[email protected]>} {#Ref<[email protected]>,yes}
*/
- public boolean ping(final String node, final long timeout) {
- if (node.equals(this.node)) {
+ public boolean ping(final String anode, final long timeout) {
+ if (anode.equals(this.node)) {
return true;
- } else if (node.indexOf('@', 0) < 0
- && node.equals(this.node
+ } else if (anode.indexOf('@', 0) < 0
+ && anode.equals(this.node
.substring(0, this.node.indexOf('@', 0)))) {
return true;
}
@@ -375,7 +375,7 @@ public class OtpNode extends OtpLocalNode {
OtpMbox mbox = null;
try {
mbox = createMbox();
- mbox.send("net_kernel", node, getPingTuple(mbox));
+ mbox.send("net_kernel", anode, getPingTuple(mbox));
final OtpErlangObject reply = mbox.receive(timeout);
final OtpErlangTuple t = (OtpErlangTuple) reply;
@@ -392,17 +392,17 @@ public class OtpNode extends OtpLocalNode {
private OtpErlangTuple getPingTuple(final OtpMbox mbox) {
final OtpErlangObject[] ping = new OtpErlangObject[3];
final OtpErlangObject[] pid = new OtpErlangObject[2];
- final OtpErlangObject[] node = new OtpErlangObject[2];
+ final OtpErlangObject[] anode = new OtpErlangObject[2];
pid[0] = mbox.self();
pid[1] = createRef();
- node[0] = new OtpErlangAtom("is_auth");
- node[1] = new OtpErlangAtom(node());
+ anode[0] = new OtpErlangAtom("is_auth");
+ anode[1] = new OtpErlangAtom(node());
ping[0] = new OtpErlangAtom("$gen_call");
ping[1] = new OtpErlangTuple(pid);
- ping[2] = new OtpErlangTuple(node);
+ ping[2] = new OtpErlangTuple(anode);
return new OtpErlangTuple(ping);
}
@@ -450,9 +450,8 @@ public class OtpNode extends OtpLocalNode {
/* special case for netKernel requests */
if (name.equals("net_kernel")) {
return netKernel(m);
- } else {
- mbox = mboxes.get(name);
}
+ mbox = mboxes.get(name);
} else {
mbox = mboxes.get(m.getRecipientPid());
}
@@ -480,23 +479,23 @@ public class OtpNode extends OtpLocalNode {
/*
* find or create a connection to the given node
*/
- OtpCookedConnection getConnection(final String node) {
+ OtpCookedConnection getConnection(final String anode) {
OtpPeer peer = null;
OtpCookedConnection conn = null;
synchronized (connections) {
// first just try looking up the name as-is
- conn = connections.get(node);
+ conn = connections.get(anode);
if (conn == null) {
// in case node had no '@' add localhost info and try again
- peer = new OtpPeer(node);
+ peer = new OtpPeer(anode);
conn = connections.get(peer.node());
if (conn == null) {
try {
conn = new OtpCookedConnection(this, peer);
- conn.setFlags(flags);
+ conn.setFlags(connFlags);
addConnection(conn);
} catch (final Exception e) {
/* false = outgoing */
@@ -522,35 +521,35 @@ public class OtpNode extends OtpLocalNode {
}
/* use these wrappers to call handler functions */
- private synchronized void remoteStatus(final String node, final boolean up,
+ private synchronized void remoteStatus(final String anode, final boolean up,
final Object info) {
if (handler == null) {
return;
}
try {
- handler.remoteStatus(node, up, info);
+ handler.remoteStatus(anode, up, info);
} catch (final Exception e) {
}
}
- synchronized void localStatus(final String node, final boolean up,
+ synchronized void localStatus(final String anode, final boolean up,
final Object info) {
if (handler == null) {
return;
}
try {
- handler.localStatus(node, up, info);
+ handler.localStatus(anode, up, info);
} catch (final Exception e) {
}
}
- synchronized void connAttempt(final String node, final boolean incoming,
+ synchronized void connAttempt(final String anode, final boolean incoming,
final Object info) {
if (handler == null) {
return;
}
try {
- handler.connAttempt(node, incoming, info);
+ handler.connAttempt(anode, incoming, info);
} catch (final Exception e) {
}
}
@@ -684,13 +683,13 @@ public class OtpNode extends OtpLocalNode {
*/
public class Acceptor extends Thread {
private final ServerSocket sock;
- private final int port;
+ private final int acceptorPort;
private volatile boolean done = false;
Acceptor(final int port) throws IOException {
sock = new ServerSocket(port);
- this.port = sock.getLocalPort();
- OtpNode.this.port = this.port;
+ this.acceptorPort = sock.getLocalPort();
+ OtpNode.this.port = this.acceptorPort;
setDaemon(true);
setName("acceptor");
@@ -741,7 +740,7 @@ public class OtpNode extends OtpLocalNode {
}
public int port() {
- return port;
+ return acceptorPort;
}
@Override
@@ -771,7 +770,7 @@ public class OtpNode extends OtpLocalNode {
try {
synchronized (connections) {
conn = new OtpCookedConnection(OtpNode.this, newsock);
- conn.setFlags(flags);
+ conn.setFlags(connFlags);
addConnection(conn);
}
} catch (final OtpAuthException e) {
@@ -802,6 +801,6 @@ public class OtpNode extends OtpLocalNode {
}
public void setFlags(final int flags) {
- this.flags = flags;
+ this.connFlags = flags;
}
}
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 a78423db44..ef60a9f38a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -25,7 +25,6 @@ import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
-import java.util.Arrays;
import java.util.zip.Deflater;
/**
@@ -45,8 +44,11 @@ public class OtpOutputStream extends ByteArrayOutputStream {
public static final int defaultIncrement = 2048;
// static formats, used to encode floats and doubles
+ @SuppressWarnings("unused")
private static final DecimalFormat eform = new DecimalFormat("e+00;e-00");
+ @SuppressWarnings("unused")
private static final BigDecimal ten = new BigDecimal(10.0);
+ @SuppressWarnings("unused")
private static final BigDecimal one = new BigDecimal(1.0);
private int fixedSize = Integer.MAX_VALUE;
@@ -159,9 +161,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* @see java.io.ByteArrayOutputStream#write(byte[])
*/
@Override
- public void write(final byte[] buf) {
+ public void write(final byte[] abuf) {
// don't assume that super.write(byte[]) calls write(buf, 0, buf.length)
- write(buf, 0, buf.length);
+ write(abuf, 0, abuf.length);
}
/* (non-Javadoc)
@@ -202,7 +204,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
/**
* Write an array of bytes to the stream.
*
- * @param buf
+ * @param bytes
* the array of bytes to write.
*
*/
@@ -285,10 +287,11 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* @param b
* the number of bytes to write from the little end.
*/
- public void writeLE(long n, final int b) {
+ public void writeLE(final long n, final int b) {
+ long v = n;
for (int i = 0; i < b; i++) {
- write((byte) (n & 0xff));
- n >>= 8;
+ write((byte) (v & 0xff));
+ v >>= 8;
}
}
@@ -518,16 +521,17 @@ public class OtpOutputStream extends ByteArrayOutputStream {
write_double(f);
}
- public void write_big_integer(BigInteger v) {
+ public void write_big_integer(final BigInteger v) {
if (v.bitLength() < 64) {
this.write_long(v.longValue(), true);
return;
}
final int signum = v.signum();
+ BigInteger val = v;
if (signum < 0) {
- v = v.negate();
+ val = val.negate();
}
- final byte[] magnitude = v.toByteArray();
+ final byte[] magnitude = val.toByteArray();
final int n = magnitude.length;
// Reverse the array to make it little endian.
for (int i = 0, j = n; i < j--; i++) {
@@ -568,7 +572,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
int n;
long mask;
for (mask = 0xFFFFffffL, n = 4; (abs & mask) != abs; n++, mask = mask << 8 | 0xffL) {
- ; // count nonzero bytes
+ // count nonzero bytes
}
write1(OtpExternal.smallBigTag);
write1(n); // length
@@ -637,7 +641,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) {
@@ -827,7 +831,6 @@ public class OtpOutputStream extends ByteArrayOutputStream {
write_nil(); // it should never ever get here...
}
} else { // unicode or longer, must code as list
- final char[] charbuf = s.toCharArray();
final int[] codePoints = OtpErlangString.stringToCodePoints(s);
write_list_head(codePoints.length);
for (final int codePoint : codePoints) {
@@ -867,6 +870,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* the compression level (<tt>0..9</tt>)
*/
public void write_compressed(final OtpErlangObject o, int level) {
+ @SuppressWarnings("resource")
final OtpOutputStream oos = new OtpOutputStream(o);
/*
* similar to erts_term_to_binary() in external.c:
@@ -920,6 +924,11 @@ public class OtpOutputStream extends ByteArrayOutputStream {
"Intermediate stream failed for Erlang object " + o);
} finally {
this.fixedSize = Integer.MAX_VALUE;
+ try {
+ dos.close();
+ } catch (IOException e) {
+ // ignore
+ }
}
}
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/jinterface.app.src b/lib/jinterface/java_src/com/ericsson/otp/erlang/jinterface.app.src
new file mode 100644
index 0000000000..d25d9bc142
--- /dev/null
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/jinterface.app.src
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% This is an -*- erlang -*- file.
+%%
+
+{application, jinterface,
+ [
+ {description, "Jinterface"},
+ {vsn, "%VSN%"},
+ {modules, []},
+ {registered, []},
+ {applications, []},
+ {env, []},
+ {runtime_dependencies, []}
+ ]
+}.
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
index cb725164cd..00abc97ff5 100644
--- a/lib/jinterface/test/jinterface_SUITE.erl
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -38,7 +38,8 @@
java_exit_with_reason_any_term/1,
status_handler_localStatus/1, status_handler_remoteStatus/1,
status_handler_connAttempt/1,
- maps/1
+ maps/1,
+ fun_equals/1
]).
-include_lib("common_test/include/ct.hrl").
@@ -106,7 +107,8 @@ fundamental() ->
register_and_whereis, % RegisterAndWhereis.java
get_names, % GetNames.java
boolean_atom, % BooleanAtom.java
- maps % Maps.java
+ maps, % Maps.java
+ fun_equals % FunEquals.java
].
ping() ->
@@ -691,6 +693,18 @@ maps(Config) when is_list(Config) ->
[]).
%%%-----------------------------------------------------------------
+fun_equals(doc) ->
+ ["FunEquals.java: "
+ "Test OtpErlangFun.equals()"];
+fun_equals(suite) ->
+ [];
+fun_equals(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "FunEquals",
+ []).
+
+%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
%%%-----------------------------------------------------------------
send_receive(TestCaseTag,Fun,Config) ->
diff --git a/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java b/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java
new file mode 100644
index 0000000000..961e462cb3
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java
@@ -0,0 +1,73 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangFun;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangPid;
+
+public class FunEquals {
+
+ /*
+ Implements test case jinterface_SUITE:fun_equals/1
+
+ Test the function OtpErlangFun.equals()
+ */
+
+ public static void main(String argv[]) {
+
+ OtpErlangPid pid = new OtpErlangPid("here", 4, 5, 0);
+ String module = "mod";
+ int arity = 2;
+ byte[] md5 = new byte[]{3,5,7};
+ int index = 2;
+ long old_index = 1;
+ long uniq= 2;
+ OtpErlangObject[] freeVars = new OtpErlangObject[]{
+ new OtpErlangAtom("hej"), new OtpErlangLong(9)
+ };
+
+ OtpErlangFun f1 = new OtpErlangFun(pid, module, arity, md5,
+ index, old_index, uniq, freeVars);
+ OtpErlangFun f2 = new OtpErlangFun(pid, module, arity, copyArray(md5),
+ index, old_index, uniq, copyArray(freeVars));
+
+ if(!f1.equals(f2))
+ fail(1);
+
+ }
+
+ private static void fail(int reason) {
+ System.exit(reason);
+ }
+
+ private static byte[] copyArray(byte[] source) {
+ byte[] result = new byte[source.length];
+ System.arraycopy(source, 0, result, 0, source.length);
+ return result;
+ }
+
+ private static OtpErlangObject[] copyArray(OtpErlangObject[] source) {
+ OtpErlangObject[] result = new OtpErlangObject[source.length];
+ System.arraycopy(source, 0, result, 0, source.length);
+ return result;
+ }
+
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/GetNames.java b/lib/jinterface/test/jinterface_SUITE_data/GetNames.java
index 3d2bc4ac84..54efaad242 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/GetNames.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/GetNames.java
@@ -18,7 +18,9 @@
*/
import java.util.ArrayList;
-import com.ericsson.otp.erlang.*;
+
+import com.ericsson.otp.erlang.OtpMbox;
+import com.ericsson.otp.erlang.OtpNode;
class GetNames {
@@ -37,7 +39,7 @@ class GetNames {
OtpMbox mbox3 = node.createMbox();
node.registerName("mbox3",mbox3);
- ArrayList existing_names = new ArrayList();
+ ArrayList<String> existing_names = new ArrayList<String>();
existing_names.add("mbox3");
existing_names.add("mbox2");
existing_names.add("mbox1");
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
index a15ed1aa63..cd68f1ead5 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
+++ b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
@@ -47,7 +47,8 @@ JAVA_FILES = \
MboxSendReceive.java \
MboxLinkUnlink.java \
NodeStatusHandler.java \
- Maps.java
+ Maps.java \
+ FunEquals.java
CLASS_FILES = $(JAVA_FILES:.java=.class)
diff --git a/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java b/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java
index 5d1d097cc8..470fdb4a14 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java
@@ -17,7 +17,14 @@
* %CopyrightEnd%
*/
-import com.ericsson.otp.erlang.*;
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangExit;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangPid;
+import com.ericsson.otp.erlang.OtpErlangTuple;
+import com.ericsson.otp.erlang.OtpMbox;
+import com.ericsson.otp.erlang.OtpNode;
class MboxLinkUnlink {
@@ -66,7 +73,10 @@ class MboxLinkUnlink {
OtpErlangObject[] msg = {mainMbox.self(),mbox.self()};
mbox.send("erl_link_server", erlNode, new OtpErlangTuple(msg));
OtpErlangObject o = mbox.receive(1000);
- if (o == null) System.exit(1);
+ if (o == null) {
+ System.exit(1);
+ return;
+ }
OtpErlangTuple tuple = (OtpErlangTuple)o;
int tag = (int)((OtpErlangLong)tuple.elementAt(0)).longValue();
@@ -91,6 +101,7 @@ class MboxLinkUnlink {
expected = tuple.elementAt(2);
mbox.receive(1000);
System.exit(2);
+ break;
case erl_link_java_exit:
dbg("Java got \"erl_link_java_exit\"");
mbox.exit(tuple.elementAt(2));
@@ -104,6 +115,7 @@ class MboxLinkUnlink {
expected = tuple.elementAt(2);
mbox.receive(1000);
System.exit(3);
+ break;
case internal_link_linking_exits:
dbg("Java got \"internal_link_linking_exits\"");
mbox2 = node.createMbox();
@@ -113,6 +125,7 @@ class MboxLinkUnlink {
expected = tuple.elementAt(2);
mbox2.receive(1000); // hanging waiting for exit
System.exit(4); // got someting other than exit
+ break;
case internal_link_linked_exits:
dbg("Java got \"internal_link_linked_exits\"");
mbox2 = node.createMbox();
@@ -122,6 +135,7 @@ class MboxLinkUnlink {
expected = tuple.elementAt(2);
mbox.receive(1000); // hanging waiting for exit
System.exit(5); // got someting other than exit
+ break;
case internal_unlink_linking_exits:
dbg("Java got \"internal_unlink_linking_exits\"");
mbox2 = node.createMbox();
diff --git a/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java b/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java
index 2db71bb5cd..44433aa619 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java
@@ -17,7 +17,13 @@
* %CopyrightEnd%
*/
-import com.ericsson.otp.erlang.*;
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangPid;
+import com.ericsson.otp.erlang.OtpErlangTuple;
+import com.ericsson.otp.erlang.OtpMbox;
+import com.ericsson.otp.erlang.OtpNode;
class MboxSendReceive {
@@ -35,6 +41,7 @@ class MboxSendReceive {
private static final int java_internal_send_receive_different_nodes = 3;
private static final int java_internal_send_receive_self = 4;
+ @SuppressWarnings("null")
public static void main(String argv[]) {
String cookie = argv[0];
diff --git a/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java b/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java
index 51ea15b5ef..06ddfa2d61 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java
@@ -17,7 +17,14 @@
* %CopyrightEnd%
*/
-import com.ericsson.otp.erlang.*;
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangBoolean;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangString;
+import com.ericsson.otp.erlang.OtpErlangTuple;
+import com.ericsson.otp.erlang.OtpMbox;
+import com.ericsson.otp.erlang.OtpNode;
+import com.ericsson.otp.erlang.OtpNodeStatus;
public class NodeStatusHandler extends OtpNodeStatus {
/*
@@ -86,7 +93,10 @@ public class NodeStatusHandler extends OtpNodeStatus {
}
OtpErlangObject o = mbox.receive(recTime);
- if (o == null) System.exit(2);
+ if (o == null) {
+ System.exit(2);
+ return;
+ }
if (! ((OtpErlangAtom)o).atomValue().equals("done"))
System.exit(3);
@@ -100,6 +110,7 @@ public class NodeStatusHandler extends OtpNodeStatus {
+ @Override
public void remoteStatus(String node, boolean up, Object info) {
try {
dbg("Got remoteStatus: " + node + " " + up + " " + info);
@@ -120,6 +131,7 @@ public class NodeStatusHandler extends OtpNodeStatus {
}
+ @Override
public void localStatus(String node, boolean up, Object info) {
try {
dbg("Got localStatus: " + node + " " + up + " " + info);
@@ -141,6 +153,7 @@ public class NodeStatusHandler extends OtpNodeStatus {
+@Override
public void connAttempt(String node, boolean incoming, Object info) {
try {
dbg("Got connAttempt: " + node + " " + incoming + " " + info);
diff --git a/lib/jinterface/test/nc_SUITE_data/echo_server.java b/lib/jinterface/test/nc_SUITE_data/echo_server.java
index 2e18e908d4..0e43ea0680 100644
--- a/lib/jinterface/test/nc_SUITE_data/echo_server.java
+++ b/lib/jinterface/test/nc_SUITE_data/echo_server.java
@@ -148,11 +148,13 @@ public class echo_server {
final String atomValue = ((OtpErlangAtom) t).atomValue();
if (atomValue.equals("binary") && i instanceof OtpErlangBinary) {
final OtpErlangBinary b = (OtpErlangBinary) i;
+ @SuppressWarnings("resource")
final OtpInputStream bis = new OtpInputStream(b.binaryValue(),
0);
final OtpErlangObject o = bis.read_any();
return o;
} else if (atomValue.equals("compress")) {
+ @SuppressWarnings("resource")
final OtpOutputStream oos = new OtpOutputStream();
oos.write1(OtpExternal.versionTag);
oos.write_compressed(i);
@@ -206,6 +208,7 @@ public class echo_server {
&& i instanceof OtpErlangString) {
final OtpErlangString s = (OtpErlangString) i;
final String ss = s.stringValue().substring(3, 6);
+ @SuppressWarnings("unused")
final int[] cps = OtpErlangString.stringToCodePoints(ss);
return s;
} else if (atomValue.equals("utf8")) {
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index c50200fab6..eea83c2f3f 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.5.9
+JINTERFACE_VSN = 1.5.11
diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml
index 3815b0877c..df2f0b01ee 100644
--- a/lib/kernel/doc/src/error_logger.xml
+++ b/lib/kernel/doc/src/error_logger.xml
@@ -58,7 +58,7 @@
specific events. (<c>add_report_handler/1,2</c>). Also, there is
a useful event handler in STDLIB for multi-file logging of events,
see <c>log_mf_h(3)</c>.</p>
- <p>Warning events was introduced in Erlang/OTP R9C. To retain
+ <p>Warning events were introduced in Erlang/OTP R9C. To retain
backwards compatibility, these are by default tagged as errors,
thus showing up as error reports in the logs. By using
the command line flag <c><![CDATA[+W <w | i>]]></c>, they can instead
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index dbd0d3c815..820ecd1e30 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -112,7 +112,12 @@ do_recv(Sock, Bs) ->
<item>
<p>If a socket has somehow been connected without using
<c>gen_tcp</c>, use this option to pass the file
- descriptor for it.</p>
+ descriptor for it. If <c>{ip, ip_address()}</c>
+ and/or <c>{port, port_number()}</c> is combined with
+ this option the fd will be bound to the given interface
+ and port before connecting. If these options are not given
+ it is assumed that the fd is already bound appropriately.
+ </p>
</item>
<tag><c>inet</c></tag>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 503725fe18..291d1b0da7 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -84,7 +84,12 @@
<item>
<p>If a socket has somehow been opened without using
<c>gen_udp</c>, use this option to pass the file
- descriptor for it.</p>
+ descriptor for it. If <c><anno>Port</anno></c> is not set to 0
+ and/or <c>{ip, ip_address()}</c> is combined with this option
+ the fd will be bound to the given interface and port after being
+ opened. If these options are not given it is assumed that the fd
+ is already bound appropriately.
+ </p>
</item>
<tag><c>inet6</c></tag>
<item>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 35889f9d11..7eaf2d4a44 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,21 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 3.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Accept inet:ip_address() in net_adm:names/1</p>
+ <p>
+ Own Id: OTP-12154</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 3.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -151,7 +166,6 @@
</list>
</section>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -295,7 +309,26 @@
</item>
</list>
</section>
+</section>
+<section><title>Kernel 2.16.4.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ When using gen_tcp:connect and the <c>fd</c> option with
+ <c>port</c> and/or <c>ip</c>, the <c>port</c> and
+ <c>ip</c> options were ignored. This has been fixed so
+ that if <c>port</c> and/or <c>ip</c> is specified
+ together with <c>fd</c> a bind is requested for that
+ <c>fd</c>. If <c>port</c> and/or <c>ip</c> is not
+ specified bind will not be called.</p>
+ <p>
+ Own Id: OTP-12061</p>
+ </item>
+ </list>
+ </section>
</section>
<section><title>Kernel 2.16.4</title>
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index cb3c0a49f4..c7c70ad257 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/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index b4fae24ef3..f6e2ca0954 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -85,24 +85,19 @@ port_please1(Node,HostName, Timeout) ->
Else
end.
-names() ->
+names() ->
{ok, H} = inet:gethostname(),
names(H).
-names(HostName) when is_atom(HostName) ->
- names1(atom_to_list(HostName));
-names(HostName) when is_list(HostName) ->
- names1(HostName);
-names(EpmdAddr) ->
- get_names(EpmdAddr).
-
-names1(HostName) ->
+names(HostName) when is_atom(HostName); is_list(HostName) ->
case inet:gethostbyname(HostName) of
{ok,{hostent, _Name, _ , _Af, _Size, [EpmdAddr | _]}} ->
get_names(EpmdAddr);
Else ->
Else
- end.
+ end;
+names(EpmdAddr) ->
+ get_names(EpmdAddr).
register_node(Name, PortNo) ->
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 41d422d7d4..d17da2d329 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1257,9 +1257,9 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module)
Error ->
Error
end;
-open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module)
+open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module)
when is_integer(Fd) ->
- fdopen(Fd, Opts, Protocol, Family, Type, Module).
+ fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module).
bindx(S, [Addr], Port0) ->
{IP, Port} = set_bindx_port(Addr, Port0),
@@ -1298,12 +1298,35 @@ change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) ->
{'ok', socket()} | {'error', posix()}.
fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
- case prim_inet:fdopen(Protocol, Family, Type, Fd) of
+ fdopen(Fd, any, 0, Opts, Protocol, Family, Type, Module).
+
+fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) ->
+ IsAnyAddr = (Addr == {0,0,0,0} orelse Addr == {0,0,0,0,0,0,0,0}
+ orelse Addr == any),
+ Bound = Port == 0 andalso IsAnyAddr,
+ case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of
{ok, S} ->
case prim_inet:setopts(S, Opts) of
ok ->
- inet_db:register_socket(S, Module),
- {ok, S};
+ case if
+ Bound ->
+ %% We do not do any binding if default
+ %% port+addr options where given in order
+ %% to keep backwards compatability with
+ %% pre Erlang/TOP 17
+ {ok, ok};
+ is_list(Addr) ->
+ bindx(S, Addr, Port);
+ true ->
+ prim_inet:bind(S, Addr, Port)
+ end of
+ {ok, _} ->
+ inet_db:register_socket(S, Module),
+ {ok, S};
+ Error ->
+ prim_inet:close(S),
+ Error
+ end;
Error ->
prim_inet:close(S), Error
end;
diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl
index 3f5eac7822..2cdfb76417 100644
--- a/lib/kernel/src/net_adm.erl
+++ b/lib/kernel/src/net_adm.erl
@@ -89,18 +89,13 @@ names() ->
-spec names(Host) -> {ok, [{Name, Port}]} | {error, Reason} when
- Host :: atom() | string(),
+ Host :: atom() | string() | inet:ip_address(),
Name :: string(),
Port :: non_neg_integer(),
Reason :: address | file:posix().
names(Hostname) ->
- case inet:gethostbyname(Hostname) of
- {ok, {hostent, _Name, _ , _Af, _Size, [Addr | _]}} ->
- erl_epmd:names(Addr);
- Else ->
- Else
- end.
+ erl_epmd:names(Hostname).
-spec dns_hostname(Host) -> {ok, Name} | {error, Host} when
Host :: atom() | string(),
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index a7af00c12a..c27d265550 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -32,14 +32,16 @@
t_connect_bad/1,
t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
- t_fdopen/1, t_implicit_inet6/1]).
+ t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]).
+
+-export([getsockfd/0,closesockfd/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_fdopen, t_implicit_inet6].
+ t_fdopen, t_fdconnect, t_implicit_inet6].
groups() ->
[{t_accept, [], [t_accept_timeout]},
@@ -185,6 +187,37 @@ t_fdopen(Config) when is_list(Config) ->
?line ok = gen_tcp:close(L),
ok.
+t_fdconnect(Config) when is_list(Config) ->
+ Question = "Aaaa... Long time ago in a small town in Germany,",
+ Question1 = list_to_binary(Question),
+ Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ",
+ ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]],
+ Question1 = iolist_to_binary(Question2),
+ Answer = "there was a shoemaker, Schumacher was his name.",
+ Path = ?config(data_dir, Config),
+ Lib = "gen_tcp_api_SUITE",
+ ok = erlang:load_nif(filename:join(Path,Lib), []),
+ {ok, L} = gen_tcp:listen(0, [{active, false}]),
+ {ok, Port} = inet:port(L),
+ FD = gen_tcp_api_SUITE:getsockfd(),
+ {ok, Client} = gen_tcp:connect(localhost, Port, [{fd,FD},{port,20002},
+ {active,false}]),
+ {ok, Server} = gen_tcp:accept(L),
+ ok = gen_tcp:send(Client, Question),
+ {ok, Question} = gen_tcp:recv(Server, length(Question), 2000),
+ ok = gen_tcp:send(Client, Question1),
+ {ok, Question} = gen_tcp:recv(Server, length(Question), 2000),
+ ok = gen_tcp:send(Client, Question2),
+ {ok, Question} = gen_tcp:recv(Server, length(Question), 2000),
+ ok = gen_tcp:send(Server, Answer),
+ {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000),
+ ok = gen_tcp:close(Client),
+ FD = gen_tcp_api_SUITE:closesockfd(FD),
+ {error,closed} = gen_tcp:recv(Server, 1, 2000),
+ ok = gen_tcp:close(Server),
+ ok = gen_tcp:close(L),
+ ok.
+
%%% implicit inet6 option to api functions
@@ -300,3 +333,7 @@ unused_ip(A, B, C, D) ->
end.
ok({ok,V}) -> V.
+
+
+getsockfd() -> undefined.
+closesockfd(_FD) -> undefined.
diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src b/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..5477598160
--- /dev/null
+++ b/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src
@@ -0,0 +1,9 @@
+
+NIF_LIBS = gen_tcp_api_SUITE@dll@
+SHLIB_EXTRA_LDLIBS = @LIBS@
+
+all: $(NIF_LIBS)
+
+@SHLIB_RULES@
+
+$(NIF_LIBS): gen_tcp_api_SUITE.c
diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c
new file mode 100644
index 0000000000..73a6568b30
--- /dev/null
+++ b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c
@@ -0,0 +1,60 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009-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
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include "erl_nif.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#ifdef __WIN32__
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#define sock_open(af, type, proto) socket((af), (type), (proto))
+
+static ERL_NIF_TERM getsockfd(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int fd;
+
+ fd = sock_open(AF_INET, SOCK_STREAM, 0);
+ return enif_make_int(env, fd);
+}
+
+static ERL_NIF_TERM closesockfd(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int fd;
+
+ enif_get_int(env, argv[0], &fd);
+
+ close(fd);
+
+ return enif_make_int(env, fd);
+}
+
+static ErlNifFunc nif_funcs[] =
+{
+ {"getsockfd", 0, getsockfd},
+ {"closesockfd", 1, closesockfd}
+};
+
+ERL_NIF_INIT(gen_tcp_api_SUITE,nif_funcs,NULL,NULL,NULL,NULL)
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 2df4bf7c95..4e4aeb67e2 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -882,7 +882,7 @@ passive_sockets_server_send(Socket, X) ->
accept_closed_by_other_process(doc) ->
["Tests the return value from gen_tcp:accept when ",
- "the socket is closed from an other process. (OTP-3817)"];
+ "the socket is closed from another process. (OTP-3817)"];
accept_closed_by_other_process(Config) when is_list(Config) ->
?line Parent = self(),
?line {ok, ListenSocket} = gen_tcp:listen(0, []),
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 6bb41999c5..8177123332 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -447,8 +447,8 @@ open_fd(Config) when is_list(Config) ->
{ok,S1} = gen_udp:open(0),
{ok,P2} = inet:port(S1),
{ok,FD} = prim_inet:getfd(S1),
- {error,einval} = gen_udp:open(P2, [inet6, {fd,FD}]),
- {ok,S2} = gen_udp:open(P2, [{fd,FD}]),
+ {error,einval} = gen_udp:open(0, [inet6, {fd,FD}]),
+ {ok,S2} = gen_udp:open(0, [{fd,FD}]),
{ok,S3} = gen_udp:open(0),
{ok,P3} = inet:port(S3),
ok = gen_udp:send(S3, Addr, P2, Msg),
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 1884e8cf58..613efeeb2f 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -79,17 +79,29 @@ appup_test(_Config) ->
appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-appup_tests(App,{OkVsns,NokVsns}) ->
+appup_tests(App,{OkVsns0,NokVsns}) ->
application:load(App),
{_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
AppupFileName = atom_to_list(App) ++ ".appup",
AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
- ct:log("Testing ok versions: ~p~n",[OkVsns]),
+ OkVsns =
+ case OkVsns0 -- [Vsn] of
+ OkVsns0 ->
+ OkVsns0;
+ Ok ->
+ ct:log("Current version, ~p, is same as in previous release.~n"
+ "Removing this from the list of ok versions.",
+ [Vsn]),
+ Ok
+ end,
+ ct:log("Testing that appup allows upgrade from these versions: ~p~n",
+ [OkVsns]),
check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
- ct:log("Testing not ok versions: ~p~n",[NokVsns]),
+ ct:log("Testing that appup does not allow upgrade from these versions: ~p~n",
+ [NokVsns]),
check_appup(NokVsns,UpFrom,error),
check_appup(NokVsns,DownTo,error),
ok.
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 1ecfde190e..be633a304a 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 3.0.2
+KERNEL_VSN = 3.0.3
diff --git a/lib/megaco/configure.in b/lib/megaco/configure.in
index 64daa959b5..e3c24a58b8 100644
--- a/lib/megaco/configure.in
+++ b/lib/megaco/configure.in
@@ -167,6 +167,26 @@ if test "x$GCC" = xyes; then
LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
fi
+dnl ----------------------------------------------------------------------
+dnl Enable -fsanitize= flags.
+dnl ----------------------------------------------------------------------
+
+m4_define(DEFAULT_SANITIZERS, [address,undefined])
+AC_ARG_ENABLE(
+ sanitizers,
+ AS_HELP_STRING(
+ [--enable-sanitizers@<:@=comma-separated list of sanitizers@:>@],
+ [Default=DEFAULT_SANITIZERS]),
+[
+case "$enableval" in
+ no) sanitizers= ;;
+ yes) sanitizers="-fsanitize=DEFAULT_SANITIZERS" ;;
+ *) sanitizers="-fsanitize=$enableval" ;;
+esac
+CFLAGS="$CFLAGS $sanitizers"
+LDFLAGS="$LDFLAGS $sanitizers"
+])
+
dnl
dnl If ${ERL_TOP}/make/otp_ded.mk.in exists and contains DED_MK_VSN > 0,
dnl every thing releted to compiling Dynamic Erlang Drivers can be found
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index dff36fd51c..9a260c3878 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -36,7 +36,24 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.17.1</title>
+ <section><title>Megaco 3.17.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Implement --enable-sanitizers[=sanitizers]. Similar to
+ debugging with Valgrind, it's very useful to enable
+ -fsanitize= switches to catch bugs at runtime.</p>
+ <p>
+ Own Id: OTP-12153</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.17.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index db59f55b55..a3a2e2ea9c 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -174,11 +174,19 @@
%% |
%% v
%% 3.17.0.3
+%% |
+%% v
+%% 3.17.1
+%% |
+%% v
+%% 3.17.2
%%
%%
{"%VSN%",
[
+ {"3.17.1", [{restart_application,megaco}]},
+ {"3.17.0.3", [{restart_application,megaco}]},
{"3.17.0.2", []},
{"3.17.0.1", []},
{"3.17", []},
@@ -190,6 +198,8 @@
}
],
[
+ {"3.17.1", [{restart_application,megaco}]},
+ {"3.17.0.3", [{restart_application,megaco}]},
{"3.17.0.2", []},
{"3.17.0.1", []},
{"3.17", []},
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 373f5199bf..1f4e3b8e95 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.17.1
+MEGACO_VSN = 3.17.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 08212dfede..e5c7d87f52 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -38,7 +38,42 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.12.1</title>
+ <section><title>Mnesia 4.12.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Various logging fixes, including: Add run queue index to
+ the process dump in crash dumps.<br/> Add thread index to
+ enomem slogan when crashing.<br/> Remove error logger
+ message for sending messages to old instances of the same
+ node.</p>
+ <p>
+ Own Id: OTP-12115</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.12.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a race which could make create_table fail if a node
+ was going down during the transaction.</p>
+ <p>
+ Own Id: OTP-12124 Aux Id: seq12694 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.12.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index fe2fd67d71..5a9bae54da 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -300,8 +300,13 @@ mnesia_down(Node) ->
end.
wait_for_schema_commit_lock() ->
- link(whereis(?SERVER_NAME)),
- unsafe_call(wait_for_schema_commit_lock).
+ try
+ Pid = whereis(?SERVER_NAME),
+ link(Pid), %% Keep the link until release_schema_commit_lock
+ gen_server:call(Pid, wait_for_schema_commit_lock, infinity)
+ catch _:_ ->
+ mnesia:abort({node_not_running, node()})
+ end.
block_controller() ->
call(block_controller).
@@ -557,12 +562,6 @@ cast(Msg) ->
abcast(Nodes, Msg) ->
gen_server:abcast(Nodes, ?SERVER_NAME, Msg).
-unsafe_call(Msg) ->
- case whereis(?SERVER_NAME) of
- undefined -> {error, {node_not_running, node()}};
- Pid -> gen_server:call(Pid, Msg, infinity)
- end.
-
call(Msg) ->
case whereis(?SERVER_NAME) of
undefined ->
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index 4a1616e054..66fc20913c 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -939,7 +939,7 @@ do_split(_FH, _OldN, _FragNames, [], Ops) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Delete a fragment from a fragmented table
-%% and merge its records with an other fragment
+%% and merge its records with another fragment
make_multi_del_frag(Tab) ->
verify_multi(Tab),
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index 4afbea1cc2..530317bcdd 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -208,7 +208,8 @@ do_get_network_copy(Tab, Reason, Ns, Storage, Cs) ->
set({Tab, load_node}, Node),
set({Tab, load_reason}, Reason),
mnesia_controller:i_have_tab(Tab),
- dbg_out("Table ~p copied from ~p to ~p~n", [Tab, Node, node()]),
+ dbg_out("Table ~p copied from ~p to ~p (~b entries)~n",
+ [Tab, Node, node(), mnesia:table_info(Tab, size)]),
{loaded, ok};
Err = {error, _} when element(1, Reason) == dumper ->
{not_loaded,Err};
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 173c46898b..d5b96c5c76 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.12.1
+MNESIA_VSN = 4.12.3
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index c135e29520..658ac2c7cf 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -31,6 +31,21 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed statusbar on Windows</p>
+ <p>
+ Own Id: OTP-12162</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 03ca1bf9c1..c86f5ea916 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -112,10 +112,6 @@ setup(#state{frame = Frame} = State) ->
observer_lib:create_menus(DefMenus, MenuBar, default),
wxFrame:setMenuBar(Frame, MenuBar),
- StatusBar = wxStatusBar:new(Frame),
- wxFrame:setStatusBar(Frame, StatusBar),
- wxFrame:setTitle(Frame, atom_to_list(node())),
- wxStatusBar:setStatusText(StatusBar, atom_to_list(node())),
%% Setup panels
Panel = wxPanel:new(Frame, []),
@@ -131,6 +127,11 @@ setup(#state{frame = Frame} = State) ->
wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]),
wxPanel:setSizer(Panel, MainSizer),
+ StatusBar = wxStatusBar:new(Frame),
+ wxFrame:setStatusBar(Frame, StatusBar),
+ wxFrame:setTitle(Frame, atom_to_list(node())),
+ wxStatusBar:setStatusText(StatusBar, atom_to_list(node())),
+
wxNotebook:connect(Notebook, command_notebook_page_changing),
wxFrame:connect(Frame, close_window, [{skip, true}]),
wxMenu:connect(Frame, command_menu_selected),
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index b55cff7332..dbbbde1467 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.0.1
+OBSERVER_VSN = 2.0.2
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index b4655ce373..84c201a656 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -389,6 +389,9 @@ DWORD WINAPI database_handler(const char *port)
close_socket(socket);
clean_socket_lib();
/* Exit will be done by suervisor thread */
+#ifdef WIN32
+ return (DWORD)0;
+#endif
}
/* Description: Calls the appropriate function to handle the database
@@ -631,7 +634,7 @@ static db_result_msg db_query(byte *sql, db_state *state)
&statement_handle(state))))
DO_EXIT(EXIT_ALLOC);
- result = SQLExecDirect(statement_handle(state), sql, SQL_NTS);
+ result = SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS);
/* SQL_SUCCESS_WITH_INFO at this point may indicate an error in user input. */
if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) {
@@ -723,7 +726,7 @@ static db_result_msg db_select_count(byte *sql, db_state *state)
(SQLPOINTER)SQL_SCROLLABLE, (SQLINTEGER)0);
}
- if(!sql_success(SQLExecDirect(statement_handle(state), sql, SQL_NTS))) {
+ if(!sql_success(SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS))) {
diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
clean_state(state);
return encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
@@ -864,7 +867,7 @@ static db_result_msg db_param_query(byte *buffer, db_state *state)
if(params != NULL) {
- result = SQLExecDirect(statement_handle(state), sql, SQL_NTS);
+ result = SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS);
if (!sql_success(result) || result == SQL_NO_DATA) {
diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
}
@@ -955,7 +958,7 @@ static db_result_msg db_describe_table(byte *sql, db_state *state)
&statement_handle(state))))
DO_EXIT(EXIT_ALLOC);
- if (!sql_success(SQLPrepare(statement_handle(state), sql, SQL_NTS))){
+ if (!sql_success(SQLPrepare(statement_handle(state), (SQLCHAR *)sql, SQL_NTS))){
diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state));
msg = encode_error_message(diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError);
clean_state(state);
@@ -1324,7 +1327,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
if (columns(state)[i].type.c == SQL_C_BINARY) {
/* retrived later by retrive_binary_data */
- }else {
+ } else {
if(!sql_success(
SQLBindCol
(statement_handle(state),
@@ -1336,7 +1339,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
DO_EXIT(EXIT_BIND);
}
ei_x_encode_string_len(&dynamic_buffer(state),
- name, name_len);
+ (char *)name, name_len);
}
else {
columns(state)[i].type.len = 0;
@@ -2739,8 +2742,8 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext
the error message is obtained */
for(record_nr = 1; ;record_nr++) {
result = SQLGetDiagRec(handleType, handle, record_nr, current_sql_state,
- &nativeError, current_errmsg_pos,
- (SQLSMALLINT)errmsg_buffer_size, &errmsg_size);
+ &nativeError, (SQLCHAR *)current_errmsg_pos,
+ (SQLSMALLINT)errmsg_buffer_size, &errmsg_size);
if(result == SQL_SUCCESS) {
/* update the sqlstate in the diagnos record, because the SQLGetDiagRec
call succeeded */
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index 916a7cb31d..7112fd2d47 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -119,7 +119,7 @@
/*------------------------ TYPDEFS ----------------------------------*/
-typedef unsigned char byte;
+typedef char byte;
typedef int Boolean;
typedef struct {
@@ -201,4 +201,4 @@ typedef enum {
#define param_query(db_state) (db_state -> param_query)
#define out_params(db_state) (db_state -> out_params)
#define extended_errors(db_state) (db_state -> extended_errors)
-#define extended_error(db_state, errorcode) ( extended_errors(state) ? errorcode : NULL )
+#define extended_error(db_state, errorcode) ( extended_errors(state) ? ((char *)errorcode) : NULL )
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index f86146759c..ea5c51965f 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -228,4 +228,24 @@ if test "x$GCC" = xyes; then
LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CFLAGS])
fi
+dnl ----------------------------------------------------------------------
+dnl Enable -fsanitize= flags.
+dnl ----------------------------------------------------------------------
+
+m4_define(DEFAULT_SANITIZERS, [address,undefined])
+AC_ARG_ENABLE(
+ sanitizers,
+ AS_HELP_STRING(
+ [--enable-sanitizers@<:@=comma-separated list of sanitizers@:>@],
+ [Default=DEFAULT_SANITIZERS]),
+[
+case "$enableval" in
+ no) sanitizers= ;;
+ yes) sanitizers="-fsanitize=DEFAULT_SANITIZERS" ;;
+ *) sanitizers="-fsanitize=$enableval" ;;
+esac
+CFLAGS="$CFLAGS $sanitizers"
+LDFLAGS="$LDFLAGS $sanitizers"
+])
+
AC_OUTPUT(c_src/$host/Makefile:c_src/Makefile.in)
diff --git a/lib/odbc/doc/src/error_handling.xml b/lib/odbc/doc/src/error_handling.xml
index b255865263..0b6179409d 100644
--- a/lib/odbc/doc/src/error_handling.xml
+++ b/lib/odbc/doc/src/error_handling.xml
@@ -88,7 +88,7 @@
<section>
<title>The whole picture </title>
<p>As the Erlang ODBC application relies on third party products
- and communicates with a database that probably runs on an other
+ and communicates with a database that probably runs on another
computer in the network there are plenty of things that might go
wrong. To fully understand the things that might happen it
facilitate to know the design of the Erlang ODBC application,
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index 0ba2b1ff3f..495a675631 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -31,7 +31,30 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.20</title>
+ <section><title>ODBC 2.10.21</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix compiler warnings reported by LLVM</p>
+ <p>
+ Own Id: OTP-12138</p>
+ </item>
+ <item>
+ <p>
+ Implement --enable-sanitizers[=sanitizers]. Similar to
+ debugging with Valgrind, it's very useful to enable
+ -fsanitize= switches to catch bugs at runtime.</p>
+ <p>
+ Own Id: OTP-12153</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.20</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
index 2a16388929..1907069726 100644
--- a/lib/odbc/test/odbc_connect_SUITE.erl
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2002-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
@@ -47,7 +47,7 @@ all() ->
case odbc_test_lib:odbc_check() of
ok ->
[not_exist_db, commit, rollback, not_explicit_commit,
- no_c_node, port_dies, control_process_dies,
+ no_c_executable, port_dies, control_process_dies,
{group, client_dies}, connect_timeout, timeout,
many_timeouts, timeout_reset, disconnect_on_timeout,
connection_closed, disable_scrollable_cursors,
@@ -248,28 +248,31 @@ not_exist_db(_Config) ->
test_server:sleep(100).
%%-------------------------------------------------------------------------
-no_c_node(doc) ->
+no_c_executable(doc) ->
"Test what happens if the port-program can not be found";
-no_c_node(suite) -> [];
-no_c_node(_Config) ->
+no_c_executable(suite) -> [];
+no_c_executable(_Config) ->
process_flag(trap_exit, true),
Dir = filename:nativename(filename:join(code:priv_dir(odbc),
"bin")),
FileName1 = filename:nativename(os:find_executable("odbcserver",
Dir)),
FileName2 = filename:nativename(filename:join(Dir, "odbcsrv")),
- ok = file:rename(FileName1, FileName2),
- Result =
- case catch odbc:connect(?RDBMS:connection_string(),
- odbc_test_lib:platform_options()) of
- {error, port_program_executable_not_found} ->
- ok;
- Else ->
- Else
- end,
-
- ok = file:rename(FileName2, FileName1),
- ok = Result.
+ case file:rename(FileName1, FileName2) of
+ ok ->
+ Result =
+ case catch odbc:connect(?RDBMS:connection_string(),
+ odbc_test_lib:platform_options()) of
+ {error, port_program_executable_not_found} ->
+ ok;
+ Else ->
+ Else
+ end,
+ ok = file:rename(FileName2, FileName1),
+ ok = Result;
+ _ ->
+ {skip, "File permission issues"}
+ end.
%%------------------------------------------------------------------------
port_dies(doc) ->
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index 1af4751248..b374e42d15 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1 +1 @@
-ODBC_VSN = 2.10.20
+ODBC_VSN = 2.10.21
diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index 141d306740..2167a43eee 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -33,7 +33,22 @@
</header>
- <section><title>Orber 3.7</title>
+ <section><title>Orber 3.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixed problem with IPv6 addresses in Service Context
+ when orber is default configured for IPv4. </p>
+ <p>
+ Own Id: OTP-12193</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Orber 3.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/orber/src/orber_iiop_outproxy.erl b/lib/orber/src/orber_iiop_outproxy.erl
index 4ba5b05995..3adb40d01a 100644
--- a/lib/orber/src/orber_iiop_outproxy.erl
+++ b/lib/orber/src/orber_iiop_outproxy.erl
@@ -113,7 +113,7 @@ stop(Pid) ->
init({connect, Host, Port, SocketType, SocketOptions, Parent, Key, NewKey}) ->
process_flag(trap_exit, true),
case catch orber_socket:connect(SocketType, Host, Port,
- get_ip_family_opts(Host) ++ SocketOptions) of
+ orber_socket:get_ip_family_opts(Host) ++ SocketOptions) of
{'EXCEPTION', _E} ->
ignore;
%% We used to reply the below but since this would generate a CRASH REPORT
@@ -508,38 +508,3 @@ clear_queue(Proxy, RequestId, MRef) ->
end
end.
-get_ip_family_opts(Host) ->
- case inet:parse_address(Host) of
- {ok, {_,_,_,_}} ->
- [inet];
- {ok, {_,_,_,_,_,_,_,_}} ->
- [inet6];
- {error, einval} ->
- check_family_for_name(Host, orber_env:ip_version())
- end.
-
-check_family_for_name(Host, inet) ->
- case inet:getaddr(Host, inet) of
- {ok, _Address} ->
- [inet];
- {error, _} ->
- case inet:getaddrs(Host, inet6) of
- {ok, _Address} ->
- [inet6];
- {error, _} ->
- [inet]
- end
- end;
-check_family_for_name(Host, inet6) ->
- case inet:getaddr(Host, inet6) of
- {ok, _Address} ->
- [inet6];
- {error, _} ->
- case inet:getaddr(Host, inet) of
- {ok, _Address} ->
- [inet];
- {error, _} ->
- [inet6]
- end
- end.
-
diff --git a/lib/orber/src/orber_iiop_pm.erl b/lib/orber/src/orber_iiop_pm.erl
index 927d12b3b2..fee2354f11 100644
--- a/lib/orber/src/orber_iiop_pm.erl
+++ b/lib/orber/src/orber_iiop_pm.erl
@@ -775,12 +775,11 @@ do_setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars,
access_allowed(Host, Port, Type, {_,_,UserInterface}) ->
Flags = orber:get_flags(),
- Family = orber_env:ip_version(),
case ?ORB_FLAG_TEST(Flags, ?ORB_ENV_USE_ACL_OUTGOING) of
false when UserInterface == 0 ->
- get_local_interface(Type, Family);
+ get_local_interface(Type);
false ->
- inet:getaddr(UserInterface, Family);
+ inet:getaddr(UserInterface, get_ip_family(UserInterface));
true ->
SearchFor =
case Type of
@@ -789,43 +788,48 @@ access_allowed(Host, Port, Type, {_,_,UserInterface}) ->
ssl ->
ssl_out
end,
- {ok, Ip} = inet:getaddr(Host, Family),
+ {ok, Ip} = inet:getaddr(Host, get_ip_family(Host)),
case orber_acl:match(Ip, SearchFor, true) of
{true, [], 0} ->
- get_local_interface(Type, Family);
+ get_local_interface(Type);
{true, [], Port} ->
- get_local_interface(Type, Family);
+ get_local_interface(Type);
{true, [], {Min, Max}} when Port >= Min, Port =< Max ->
- get_local_interface(Type, Family);
- {true, [Interface], 0} ->
- {ok, NewIp} = inet:getaddr(Interface, Family),
+ get_local_interface(Type);
+ {true, [Interface], 0} ->
+ {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
{ok, NewIp, {Host, Port, 0}};
{true, [Interface], Port} ->
- {ok, NewIp} = inet:getaddr(Interface, Family),
+
+ {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
{ok, NewIp, {Host, Port, 0}};
{true, [Interface], {Min, Max}} when Port >= Min, Port =< Max ->
- {ok, NewIp} = inet:getaddr(Interface, Family),
+
+ {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
{ok, NewIp, {Host, Port, 0}};
_ ->
false
end
end.
-get_local_interface(normal, Family) ->
+get_local_interface(normal) ->
case orber_env:ip_address_local() of
[] ->
ok;
[Interface] ->
- inet:getaddr(Interface, Family)
+ inet:getaddr(Interface, get_ip_family(Interface))
end;
-get_local_interface(ssl, Family) ->
+get_local_interface(ssl) ->
case orber_env:iiop_ssl_ip_address_local() of
[] ->
ok;
[Interface] ->
- inet:getaddr(Interface, Family)
+ inet:getaddr(Interface, get_ip_family(Interface))
end.
+get_ip_family(Addr) ->
+ [Family] = orber_socket:get_ip_family_opts(Addr),
+ Family.
invoke_connection_closed(false) ->
ok;
diff --git a/lib/orber/src/orber_socket.erl b/lib/orber/src/orber_socket.erl
index c8d2f0636b..4507d90cce 100644
--- a/lib/orber/src/orber_socket.erl
+++ b/lib/orber/src/orber_socket.erl
@@ -37,7 +37,8 @@
-export([start/0, connect/4, listen/3, listen/4, accept/2, accept/3, write/3,
controlling_process/3, close/2, peername/2, sockname/2,
peerdata/2, peercert/2, sockdata/2, setopts/3,
- clear/2, shutdown/3, post_accept/2, post_accept/3]).
+ clear/2, shutdown/3, post_accept/2, post_accept/3,
+ get_ip_family_opts/1]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -491,4 +492,40 @@ check_options(ssl, Options, Generation) ->
end.
-
+%%-----------------------------------------------------------------
+%% Check IP Family.
+get_ip_family_opts(Host) ->
+ case inet:parse_address(Host) of
+ {ok, {_,_,_,_}} ->
+ [inet];
+ {ok, {_,_,_,_,_,_,_,_}} ->
+ [inet6];
+ {error, einval} ->
+ check_family_for_name(Host, orber_env:ip_version())
+ end.
+
+check_family_for_name(Host, inet) ->
+ case inet:getaddr(Host, inet) of
+ {ok, _Address} ->
+ [inet];
+ {error, _} ->
+ case inet:getaddr(Host, inet6) of
+ {ok, _Address} ->
+ [inet6];
+ {error, _} ->
+ [inet]
+ end
+ end;
+check_family_for_name(Host, inet6) ->
+ case inet:getaddr(Host, inet6) of
+ {ok, _Address} ->
+ [inet6];
+ {error, _} ->
+ case inet:getaddr(Host, inet) of
+ {ok, _Address} ->
+ [inet];
+ {error, _} ->
+ [inet6]
+ end
+ end.
+
diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk
index 13bdf55c07..28fe9323fb 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1,2 +1 @@
-ORBER_VSN = 3.7
-
+ORBER_VSN = 3.7.1
diff --git a/lib/os_mon/doc/src/disksup.xml b/lib/os_mon/doc/src/disksup.xml
index dbcfd65095..0e76178edb 100644
--- a/lib/os_mon/doc/src/disksup.xml
+++ b/lib/os_mon/doc/src/disksup.xml
@@ -73,6 +73,17 @@
much disk can be utilized before the <c>disk_almost_full</c>
alarm is set. The default is 0.80 (80%).</p>
</item>
+ <tag><c>disksup_posix_only = bool()</c></tag>
+ <item>
+ <p>Specifies whether the <c>disksup</c> helper process should only
+ use POSIX conformant commands (<c>true</c>) or not. The default is
+ <c>false</c>. Setting this parameter to <c>true</c> can be
+ necessary on embedded systems with stripped-down versions
+ of Unix tools like <c>df</c>. The returned disk data and alarms
+ can be different when using this option.</p>
+ <p>The parameter is ignored on platforms that are known to not be
+ posix compatible (Windows and SunOS).</p>
+ </item>
</taglist>
<p>See <seealso marker="kernel:config">config(4)</seealso> for
information about how to change the value of configuration
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 5ac04d4f42..6bc0cf7d43 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -30,6 +30,23 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Adds a new application parameter 'disksup_posix_only', to
+ make diskup use only options defined in the POSIX
+ standard.</p>
+ <p>
+ Own Id: OTP-12053</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.2.15</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl
index 34251178ee..1f088ecbde 100644
--- a/lib/os_mon/src/cpu_sup.erl
+++ b/lib/os_mon/src/cpu_sup.erl
@@ -543,7 +543,8 @@ measurement_server_init() ->
Server = case OS of
{unix, Flavor} when Flavor==sunos;
Flavor==linux ->
- port_server_start();
+ {ok, Pid} = port_server_start_link(),
+ Pid;
{unix, Flavor} when Flavor==darwin;
Flavor==freebsd;
Flavor==dragonfly;
@@ -588,8 +589,9 @@ measurement_server_loop(State) ->
Error -> Pid ! {error, Error}
end,
measurement_server_loop(State);
- {'EXIT', Pid, _n} when State#internal.port == Pid ->
- measurement_server_loop(State#internal{port = port_server_start()});
+ {'EXIT', OldPid, _n} when State#internal.port == OldPid ->
+ {ok, NewPid} = port_server_start_link(),
+ measurement_server_loop(State#internal{port = NewPid});
_Other ->
measurement_server_loop(State)
end.
@@ -605,12 +607,12 @@ port_server_call(Pid, Command) ->
{Pid, {error, Reason}} -> {error, Reason}
end.
-port_server_start() ->
+port_server_start_link() ->
Timeout = 6000,
Pid = spawn_link(fun() -> port_server_init(Timeout) end),
Pid ! {self(), ?ping},
receive
- {Pid, {data,4711}} -> Pid;
+ {Pid, {data,4711}} -> {ok, Pid};
{error,Reason} -> {error, Reason}
after Timeout ->
{error, timeout}
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
index 278da26a20..af5bcc6fe8 100644
--- a/lib/os_mon/src/disksup.erl
+++ b/lib/os_mon/src/disksup.erl
@@ -81,10 +81,12 @@ param_type(disk_space_check_interval, Val) when is_integer(Val),
param_type(disk_almost_full_threshold, Val) when is_number(Val),
0=<Val,
Val=<1 -> true;
+param_type(disksup_posix_only, Val) when Val==true; Val==false -> true;
param_type(_Param, _Val) -> false.
param_default(disk_space_check_interval) -> 30;
-param_default(disk_almost_full_threshold) -> 0.80.
+param_default(disk_almost_full_threshold) -> 0.80;
+param_default(disksup_posix_only) -> false.
%%----------------------------------------------------------------------
%% gen_server callbacks
@@ -94,7 +96,8 @@ init([]) ->
process_flag(trap_exit, true),
process_flag(priority, low),
- OS = get_os(),
+ PosixOnly = os_mon:get_env(disksup, disksup_posix_only),
+ OS = get_os(PosixOnly),
Port = case OS of
{unix, Flavor} when Flavor==sunos4;
Flavor==solaris;
@@ -102,6 +105,7 @@ init([]) ->
Flavor==dragonfly;
Flavor==darwin;
Flavor==linux;
+ Flavor==posix;
Flavor==openbsd;
Flavor==netbsd;
Flavor==irix64;
@@ -205,14 +209,16 @@ format_status(_Opt, [_PDict, #state{os = OS, threshold = Threshold,
%% Internal functions
%%----------------------------------------------------------------------
-get_os() ->
+get_os(PosixOnly) ->
case os:type() of
{unix, sunos} ->
- case os:version() of
+ case os:version() of
{5,_,_} -> {unix, solaris};
{4,_,_} -> {unix, sunos4};
V -> exit({unknown_os_version, V})
- end;
+ end;
+ {unix, _} when PosixOnly ->
+ {unix, posix};
{unix, irix64} -> {unix, irix};
OS ->
OS
@@ -259,6 +265,9 @@ check_disk_space({unix, irix}, Port, Threshold) ->
check_disk_space({unix, linux}, Port, Threshold) ->
Result = my_cmd("/bin/df -lk", Port),
check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, posix}, Port, Threshold) ->
+ Result = my_cmd("df -k -P", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
check_disk_space({unix, dragonfly}, Port, Threshold) ->
Result = my_cmd("/bin/df -k -t ufs,hammer", Port),
check_disks_solaris(skip_to_eol(Result), Threshold);
diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl
index 94661cfa77..f9addd96cf 100644
--- a/lib/os_mon/test/disksup_SUITE.erl
+++ b/lib/os_mon/test/disksup_SUITE.erl
@@ -29,6 +29,7 @@
-export([port/1]).
-export([terminate/1, unavailable/1, restart/1]).
-export([otp_5910/1]).
+-export([posix_only/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -48,7 +49,8 @@ init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?default_timeout),
[{watchdog,Dog} | Config].
-end_per_testcase(unavailable, Config) ->
+end_per_testcase(TC, Config) when TC =:= unavailable;
+ TC =:= posix_only ->
restart(Config),
end_per_testcase(dummy, Config);
end_per_testcase(_Case, Config) ->
@@ -60,11 +62,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
Bugs = [otp_5910],
+ Always = [api, config, alarm, port, posix_only, unavailable] ++ Bugs,
case test_server:os_type() of
- {unix, sunos} ->
- [api, config, alarm, port, unavailable] ++ Bugs;
- {unix, _OSname} -> [api, alarm] ++ Bugs;
- {win32, _OSname} -> [api, alarm] ++ Bugs;
+ {unix, _OSname} -> Always;
+ {win32, _OSname} -> Always;
_OS -> [unavailable]
end.
@@ -83,12 +84,7 @@ api(doc) -> ["Test of API functions"];
api(Config) when is_list(Config) ->
%% get_disk_data()
- [{Id,KByte,Capacity}|_] = get_disk_data(),
- true = io_lib:printable_list(Id),
- true = is_integer(KByte),
- true = is_integer(Capacity),
- true = Capacity>0,
- true = KByte>0,
+ ok = check_get_disk_data(),
%% get_check_interval()
1800000 = disksup:get_check_interval(),
@@ -340,6 +336,7 @@ restart(suite) ->
[];
restart(Config) when is_list(Config) ->
ok = application:set_env(os_mon, start_disksup, true),
+ ok = application:set_env(os_mon, disksup_posix_only, false),
{ok, _Pid} = supervisor:restart_child(os_mon_sup, disksup),
ok.
@@ -405,9 +402,28 @@ otp_5910(Config) when is_list(Config) ->
ok = application:start(os_mon),
ok.
+posix_only(suite) -> [];
+posix_only(doc) -> ["Test disksup_posix_only option"];
+posix_only(Config) when is_list(Config) ->
+ %% Set option and restart disksup
+ ok = application:set_env(os_mon, disksup_posix_only, true),
+ ok = supervisor:terminate_child(os_mon_sup, disksup),
+ {ok, _Child1} = supervisor:restart_child(os_mon_sup, disksup),
+
+ ok = check_get_disk_data().
+
dump_info() ->
io:format("Status: ~p~n", [sys:get_status(disksup)]).
+check_get_disk_data() ->
+ [{Id,KByte,Capacity}|_] = get_disk_data(),
+ true = io_lib:printable_list(Id),
+ true = is_integer(KByte),
+ true = is_integer(Capacity),
+ true = Capacity>0,
+ true = KByte>0,
+ ok.
+
% filter get_disk_data and remove entriew with zero capacity
% "non-normal" filesystems report zero capacity
% - Perhaps errorneous 'df -k -l'?
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 74397c2bc6..f90cc306f0 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.2.15
+OS_MON_VSN = 2.3
diff --git a/lib/ose/doc/src/notes.xml b/lib/ose/doc/src/notes.xml
index 760b92feed..7e86355f98 100644
--- a/lib/ose/doc/src/notes.xml
+++ b/lib/ose/doc/src/notes.xml
@@ -30,4 +30,63 @@
</header>
<p>This document describes the changes made to the OSE application.</p>
+<section><title>Ose 1.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add missing release notes for the OSE application.</p>
+ <p>
+ Own Id: OTP-12177</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ose 1.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix some spelling mistakes in documentation</p>
+ <p>
+ Own Id: OTP-12152</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ose 1.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Erlang/OTP has been ported to the realtime operating
+ system OSE. The port supports both smp and non-smp
+ emulator. For details around the port and how to started
+ see the User's Guide in the <seealso
+ marker="ose:ose_intro">ose</seealso> application. </p>
+ <p>
+ Note that not all parts of Erlang/OTP has been ported. </p>
+ <p>
+ Notable things that work are: non-smp and smp emulators,
+ OSE signal interaction, crypto, asn1, run_erl/to_erl,
+ tcp, epmd, distribution and most if not all non-os
+ specific functionality of Erlang.</p>
+ <p>
+ Notable things that does not work are: udp/sctp, os_mon,
+ erl_interface, binding of schedulers.</p>
+ <p>
+ Own Id: OTP-11334</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
</chapter>
diff --git a/lib/ose/doc/src/ose_intro.xml b/lib/ose/doc/src/ose_intro.xml
index b5e3ef8b33..0ed470890b 100644
--- a/lib/ose/doc/src/ose_intro.xml
+++ b/lib/ose/doc/src/ose_intro.xml
@@ -65,7 +65,7 @@ erl
/home/erlang
--</code>
<p>
- The arguments are printed on seperate lines to make it possible to know
+ The arguments are printed on separate lines to make it possible to know
what has to be quoted with &quot;. Each line is one quotable unit.
So taking the arguments above you can supply them to pm_create or
just execute directly on the command line. For example:</p>
@@ -75,7 +75,7 @@ pid: 0x110059
rtose@acp3400> pm_start 0x110059</code>
<p>
Also note that since we are running erl to figure out the arguments on a
- seperate machine the paths have to be updated. In the example above
+ separate machine the paths have to be updated. In the example above
<c>/usr/local/lib/erlang</c> was replaced by <c>/mst/erlang/</c>. The
goal is to in future releases not have to do the special argument handling
but for now (OTP 17.0) you have to do it.
diff --git a/lib/ose/vsn.mk b/lib/ose/vsn.mk
index 78ffa4d496..70db2ed69d 100644
--- a/lib/ose/vsn.mk
+++ b/lib/ose/vsn.mk
@@ -1 +1 @@
-OSE_VSN = 1.0
+OSE_VSN = 1.0.2
diff --git a/lib/otp_mibs/src/Makefile b/lib/otp_mibs/src/Makefile
index 4f03d0228a..6096240bbd 100644
--- a/lib/otp_mibs/src/Makefile
+++ b/lib/otp_mibs/src/Makefile
@@ -72,7 +72,7 @@ ERL_COMPILE_FLAGS += -I$(INCLUDE) +warn_obsolete_guard
debug opt: $(TARGETS)
clean:
- rm -f $(TARGETS_FILES)
+ rm -f $(TARGET_FILES)
rm -f $(APP_TARGET)
rm -f $(APPUP_TARGET)
rm -f core
diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile
index 6bf0af9dc6..0282d6346a 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)
@@ -95,6 +97,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src"
$(INSTALL_DATA) $(ERL_FILES) "$(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/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index 8d3c76adf5..37196bb9bf 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -452,23 +452,23 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
ecdsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= {
ID ecdsa-with-SHA1
- TYPE NULL } -- XXX Must be empty and not NULL
+ TYPE EcpkParameters } -- XXX Must be empty and not NULL
ecdsa-with-sha224 SIGNATURE-ALGORITHM-CLASS ::= {
ID ecdsa-with-SHA224
- TYPE NULL } -- XXX Must be empty and not NULL
+ TYPE EcpkParameters } -- XXX Must be empty and not NULL
ecdsa-with-sha256 SIGNATURE-ALGORITHM-CLASS ::= {
ID ecdsa-with-SHA256
- TYPE NULL } -- XXX Must be empty and not NULL
+ TYPE EcpkParameters } -- XXX Must be empty and not NULL
ecdsa-with-sha384 SIGNATURE-ALGORITHM-CLASS ::= {
ID ecdsa-with-SHA384
- TYPE NULL } -- XXX Must be empty and not NULL
+ TYPE EcpkParameters } -- XXX Must be empty and not NULL
ecdsa-with-sha512 SIGNATURE-ALGORITHM-CLASS ::= {
ID ecdsa-with-SHA512
- TYPE NULL } -- XXX Must be empty and not NULL
+ TYPE EcpkParameters } -- XXX Must be empty and not NULL
FIELD-ID-CLASS ::= CLASS {
&id OBJECT IDENTIFIER UNIQUE,
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index 397c13b463..b66c66bead 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -36,8 +36,9 @@
<p>This chapter briefly describes erlang records derived from ASN1
specifications used to handle <c> X509 certificates</c> and <c>CertificationRequest</c>.
- The intent is to describe the data types and not to specify the meaning of each
- component for this we refer you to <url
+ The intent is to describe the data types
+and not to specify the semantics of each component. For information on the
+semantics, please see <url
href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280</url> and
<url href="http://www.ietf.org/rfc/rfc5967.txt">PKCS-10</url>.
</p>
@@ -79,7 +80,7 @@
<p><c>
special_string() =
{teletexString, string()} | {printableString, string()} |
- {universalString, string()} | {utf8String, string()} |
+ {universalString, string()} | {utf8String, binary()} |
{bmpString, string()}
</c></p>
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 592d3c797d..fe4bf5ce2d 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -34,6 +34,22 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 0.22.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Added missing encoding support for PBES2, and also
+ completed support for PBES1 that was incomplete.</p>
+ <p>
+ Own Id: OTP-11915</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.22</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 8e93f562d4..e3473f80d7 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -75,7 +75,7 @@
<p><em>Data Types </em></p>
- <p><code>oid() - a tuple of integers as generated by the ASN1 compiler.</code></p>
+ <p><code>oid() - Object Identifier, a tuple of integers as generated by the ASN1 compiler.</code></p>
<p><code>boolean() = true | false</code></p>
@@ -92,7 +92,7 @@
not_encrypted | cipher_info()}</code></p>
<p><code>cipher_info() = {"RC2-CBC | "DES-CBC" | "DES-EDE3-CBC",
- crypto:rand_bytes(8)} | 'PBES2-params'}</code></p>
+ crypto:rand_bytes(8) | {#'PBEParameter{}, digest_type()} |#'PBES2-params'{}}</code></p>
<p><code>public_key() = rsa_public_key() | dsa_public_key() | ec_public_key()</code></p>
<p><code>private_key() = rsa_private_key() | dsa_private_key() | ec_private_key()</code></p>
@@ -113,6 +113,8 @@
<p><code>rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' |
'rsa_no_padding'</code></p>
+
+ <p><code>digest_type() - Union of below digest types</code></p>
<p><code>rsa_digest_type() = 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' |
'sha512'</code></p>
@@ -429,10 +431,12 @@
<name>pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}} </name>
<fsummary> Performs a basic path validation according to RFC 5280.</fsummary>
<type>
- <v> TrustedCert = #'OTPCertificate'{} | der_encode() | unknown_ca | selfsigned_peer </v>
- <d>Normally a trusted certificate but it can also be one of the path validation
- errors <c>unknown_ca </c> or <c>selfsigned_peer </c> that can be discovered while
- constructing the input to this function and that should be run through the <c>verify_fun</c>.</d>
+ <v> TrustedCert = #'OTPCertificate'{} | der_encode() | atom() </v>
+ <d>Normally a trusted certificate but it can also be a path validation
+ error that can be discovered while
+ constructing the input to this function and that should be run through the <c>verify_fun</c>.
+ For example <c>unknown_ca </c> or <c>selfsigned_peer </c>
+ </d>
<v> CertChain = [der_encode()]</v>
<d>A list of DER encoded certificates in trust order ending with the peer certificate.</d>
<v> Options = proplists:proplist()</v>
@@ -440,8 +444,8 @@
rsa_public_key() | integer(), 'NULL' | 'Dss-Parms'{}}</v>
<v> PolicyTree = term() </v>
<d>At the moment this will always be an empty list as Policies are not currently supported</d>
- <v> Reason = cert_expired | invalid_issuer | invalid_signature | unknown_ca |
- selfsigned_peer | name_not_permitted | missing_basic_constraint | invalid_key_usage | crl_reason()
+ <v> Reason = cert_expired | invalid_issuer | invalid_signature | name_not_permitted |
+ missing_basic_constraint | invalid_key_usage | {revoked, crl_reason()} | atom()
</v>
</type>
<desc>
@@ -449,7 +453,7 @@
Performs a basic path validation according to
<url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280.</url>
However CRL validation is done separately by <seealso
- marker="public_key#pkix_crls_validate-3">pkix_crls_validate/3 </seealso> and should be called
+ marker="#pkix_crls_validate-3">pkix_crls_validate/3 </seealso> and should be called
from the supplied <c>verify_fun</c>
</p>
@@ -462,7 +466,7 @@
<code>
fun(OtpCert :: #'OTPCertificate'{},
- Event :: {bad_cert, Reason :: atom()} |
+ Event :: {bad_cert, Reason :: atom() | {revoked, atom()}} |
{extension, #'Extension'{}},
InitialUserState :: term()) ->
{valid, UserState :: term()} |
@@ -491,6 +495,35 @@ fun(OtpCert :: #'OTPCertificate'{},
on.
</item>
</taglist>
+
+ <p> Possible reasons for a bad certificate are: </p>
+ <taglist>
+ <tag>cert_expired</tag>
+ <item>The certificate is no longer valid as its expiration date has passed.</item>
+
+ <tag>invalid_issuer</tag>
+ <item>The certificate issuer name does not match the name of the issuer certificate in the chain.</item>
+
+ <tag>invalid_signature</tag>
+ <item>The certificate was not signed by its issuer certificate in the chain.</item>
+
+ <tag>name_not_permitted</tag>
+ <item>Invalid Subject Alternative Name extension.</item>
+
+ <tag>missing_basic_constraint</tag>
+ <item>Certificate, required to have the basic constraints extension, does not have
+ a basic constraints extension.</item>
+
+ <tag>invalid_key_usage</tag>
+ <item>Certificate key is used in an invalid way according to the key usage extension.</item>
+
+ <tag>{revoked, crl_reason()}</tag>
+ <item>Certificate has been revoked.</item>
+
+ <tag>atom()</tag>
+ <item>Application specific error reason that should be checked by the verify_fun</item>
+ </taglist>
+
</desc>
</func>
@@ -499,14 +532,14 @@ fun(OtpCert :: #'OTPCertificate'{},
<fsummary> Performs CRL validation.</fsummary>
<type>
<v> OTPCertificate = #'OTPCertificate'{}</v>
- <v> DPAndCRLs = [{DP::#'DistributionPoint'{} ,CRL::#'CertificateList'{}}] </v>
+ <v> DPAndCRLs = [{DP::#'DistributionPoint'{}, {DerCRL::der_encoded(), CRL::#'CertificateList'{}}}] </v>
<v> Options = proplists:proplist()</v>
<v> CRLStatus() = valid | {bad_cert, revocation_status_undetermined} |
{bad_cert, {revoked, crl_reason()}}</v>
</type>
<desc>
<p> Performs CRL validation. It is intended to be called from
- the verify fun of <seealso marker="public_key#pkix_path_validation-3"> pkix_path_validation/3
+ the verify fun of <seealso marker="#pkix_path_validation-3"> pkix_path_validation/3
</seealso></p>
<taglist>
<p> Available options are: </p>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index 13bb996f7f..d3534846fa 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -35,17 +35,27 @@
</header>
<p>This chapter briefly describes Erlang records derived from ASN1
- specifications used to handle public and private keys. The intent
- is to describe the data types and not to specify the meaning of
- each component for this we refer you to the relevant standards and RFCs.</p>
+ specifications used to handle public and private keys.
+ The intent is to describe the data types
+ and not to specify the semantics of each component. For information on the
+ semantics, please see the relevant standards and RFCs.</p>
<p>Use the following include directive to get access to the
- records and constant macros used in the following sections.</p>
+ records and constant macros described in the following sections.</p>
<code> -include_lib("public_key/include/public_key.hrl"). </code>
+ <section>
+ <title>Common Data Types</title>
+
+ <p>Common non-standard Erlang
+ data types used to described the record fields in the
+ below sections are defined in <seealso
+ marker="public_key">public key reference manual </seealso></p>
+ </section>
+
<section>
- <title>RSA as defined by the PKCS-1 standard and RFC 3447.</title>
+ <title>RSA as defined by the PKCS-1 standard and <url href="http://www.ietf.org/rfc/rfc3447.txt"> RFC 3447 </url></title>
<code>
#'RSAPublicKey'{
@@ -76,7 +86,8 @@
</section>
<section>
- <title>DSA as defined by Digital Signature Standard (NIST FIPS PUB 186-2)
+ <title>DSA as defined by
+ <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> Digital Signature Standard (NIST FIPS PUB 186-2) </url>
</title>
<code>
@@ -96,4 +107,47 @@
}.
</code>
</section>
+
+ <section>
+ <title>ECC (Elliptic Curve) <url href="http://www.ietf.org/rfc/rfc3447.txt"> RFC 5480 </url>
+ </title>
+
+ <code>
+#'ECPrivateKey'{
+ version, % integer()
+ privateKey, % octet_string()
+ parameters, % der_encoded() - {'EcpkParameters', #'ECParameters'{}} |
+ {'EcpkParameters', {namedCurve, oid()}} |
+ {'EcpkParameters', 'NULL'} % Inherited by CA
+ publicKey % bitstring()
+ }.
+
+#'ECParameters'{
+ version, % integer()
+ fieldID, % #'FieldID'{}
+ curve, % #'Curve'{}
+ base, % octet_string()
+ order, % integer()
+ cofactor % integer()
+ }.
+
+#'Curve'{
+ a, % octet_string()
+ b, % octet_string()
+ seed % bitstring() - optional
+
+ }.
+
+#'FieldID'{
+ fieldType, % oid()
+ parameters % Depending on fieldType
+ }.
+
+#'ECPoint'{
+ point % octet_string() - the public key
+ }.
+
+ </code>
+ </section>
+
</chapter>
diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl
index 460624163b..521a32189d 100644
--- a/lib/public_key/src/pubkey_pbe.erl
+++ b/lib/public_key/src/pubkey_pbe.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -22,7 +22,7 @@
-include("public_key.hrl").
--export([encode/4, decode/4, decrypt_parameters/1]).
+-export([encode/4, decode/4, decrypt_parameters/1, encrypt_parameters/1]).
-export([pbdkdf1/4, pbdkdf2/7]).
-define(DEFAULT_SHA_MAC_KEYLEN, 20).
@@ -40,16 +40,16 @@
%%--------------------------------------------------------------------
encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(des_cbc, Key, IV, Data);
+ crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, KeyDevParams));
encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
<<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
- crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, Data);
+ crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data));
encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) ->
{Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams),
- crypto:block_encrypt(rc2_cbc, Key, IV, Data).
+ crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, KeyDevParams)).
%%--------------------------------------------------------------------
-spec decode(binary(), string(), string(), term()) -> binary().
%%
@@ -108,6 +108,15 @@ decrypt_parameters(#'EncryptedPrivateKeyInfo_encryptionAlgorithm'{
algorithm = Oid, parameters = Param}) ->
decrypt_parameters(Oid, Param).
+
+%%--------------------------------------------------------------------
+-spec encrypt_parameters({Cipher::string(), Params::term()}) ->
+ #'EncryptedPrivateKeyInfo_encryptionAlgorithm'{}.
+%%
+%% Description: Performs ANS1-decoding of encryption parameters.
+%%--------------------------------------------------------------------
+encrypt_parameters({Cipher, Params}) ->
+ encrypt_parameters(Cipher, Params).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -117,14 +126,18 @@ password_to_key_and_iv(Password, _, #'PBES2-params'{} = Params) ->
<<Key:KeyLen/binary, _/binary>> =
pbdkdf2(Password, Salt, ItrCount, KeyLen, PseudoRandomFunction, PseudoHash, PseudoOtputLen),
{Key, IV};
+password_to_key_and_iv(Password, _Cipher, {#'PBEParameter'{salt = Salt,
+ iterationCount = Count}, Hash}) ->
+ <<Key:8/binary, IV:8/binary, _/binary>>
+ = pbdkdf1(Password, erlang:iolist_to_binary(Salt), Count, Hash),
+ {Key, IV};
password_to_key_and_iv(Password, Cipher, Salt) ->
- KeyLen = derived_key_length(Cipher, undefined),
+ KeyLen = derived_key_length(Cipher, undefined),
<<Key:KeyLen/binary, _/binary>> =
pem_encrypt(<<>>, Password, Salt, ceiling(KeyLen div 16), <<>>, md5),
%% Old PEM encryption does not use standard encryption method
- %% pbdkdf1 and uses then salt as IV
+ %% pbdkdf1 and uses then salt as IV
{Key, Salt}.
-
pem_encrypt(_, _, _, 0, Acc, _) ->
Acc;
pem_encrypt(Prev, Password, Salt, Count, Acc, Hash) ->
@@ -169,7 +182,52 @@ do_xor_sum(Prf, PrfHash, PrfLen, Prev, Password, Count, Acc)->
decrypt_parameters(?'id-PBES2', DekParams) ->
{ok, Params} = 'PKCS-FRAME':decode('PBES2-params', DekParams),
- {cipher(Params#'PBES2-params'.encryptionScheme), Params}.
+ {cipher(Params#'PBES2-params'.encryptionScheme), Params};
+decrypt_parameters(?'pbeWithSHA1AndRC2-CBC', DekParams) ->
+ {ok, Params} = 'PKCS-FRAME':decode('PBEParameter', DekParams),
+ {"RC2-CBC", {Params, sha}};
+decrypt_parameters(?'pbeWithSHA1AndDES-CBC', DekParams) ->
+ {ok, Params} = 'PKCS-FRAME':decode('PBEParameter', DekParams),
+ {"DES-CBC", {Params, sha}};
+decrypt_parameters(?'pbeWithMD5AndRC2-CBC', DekParams) ->
+ {ok, Params} = 'PKCS-FRAME':decode('PBEParameter', DekParams),
+ {"RC2-CBC", {Params, md5}};
+decrypt_parameters(?'pbeWithMD5AndDES-CBC', DekParams) ->
+ {ok, Params} = 'PKCS-FRAME':decode('PBEParameter', DekParams),
+ {"DES-CBC", {Params, md5}}.
+
+encrypt_parameters(_Cipher, #'PBES2-params'{} = Params) ->
+ {ok, Der} ='PKCS-FRAME':encode('PBES2-params', Params),
+ #'EncryptedPrivateKeyInfo_encryptionAlgorithm'{
+ algorithm = ?'id-PBES2',
+ parameters = Der};
+
+encrypt_parameters(Cipher, {#'PBEParameter'{} = Params, Hash}) ->
+ {ok, Der} ='PKCS-FRAME':encode('PBEParameter', Params),
+ #'EncryptedPrivateKeyInfo_encryptionAlgorithm'{
+ algorithm = pbe1_oid(Cipher, Hash),
+ parameters = Der}.
+
+pbe1_oid("RC2-CBC", sha) ->
+ ?'pbeWithSHA1AndRC2-CBC';
+pbe1_oid("DES-CBC", sha) ->
+ ?'pbeWithSHA1AndDES-CBC';
+pbe1_oid("RC2-CBC", md5) ->
+ ?'pbeWithMD5AndRC2-CBC';
+pbe1_oid("DES-CBC", md5) ->
+ ?'pbeWithMD5AndDES-CBC'.
+
+pbe_pad(Data, {#'PBEParameter'{}, _}) ->
+ pbe_pad(Data);
+pbe_pad(Data, #'PBES2-params'{}) ->
+ pbe_pad(Data);
+pbe_pad(Data, _) ->
+ Data.
+
+pbe_pad(Data) ->
+ N = 8 - (erlang:byte_size(Data) rem 8),
+ Pad = list_to_binary(lists:duplicate(N, N)),
+ <<Data/binary, Pad/binary>>.
key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc,
encryptionScheme = EncScheme}) ->
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index 3a1653d989..98881c4a6a 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -68,7 +68,8 @@ encode(PemEntries) ->
%%--------------------------------------------------------------------
-spec decipher({public_key:pki_asn1_type(), DerEncrypted::binary(),
- {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}}},
+ {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}
+ | {#'PBEParameter'{}, atom()}}},
string()) -> Der::binary().
%%
%% Description: Deciphers a decrypted pem entry.
@@ -77,7 +78,8 @@ decipher({_, DecryptDer, {Cipher, KeyDevParams}}, Password) ->
pubkey_pbe:decode(DecryptDer, Password, Cipher, KeyDevParams).
%%--------------------------------------------------------------------
--spec cipher(Der::binary(), {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}} ,
+-spec cipher(Der::binary(), {Cipher :: string(), Salt :: iodata() | #'PBES2-params'{}
+ | {#'PBEParameter'{}, atom()}},
string()) -> binary().
%%
%% Description: Ciphers a PEM entry
@@ -94,6 +96,10 @@ encode_pem_entries(Entries) ->
encode_pem_entry({Type, Der, not_encrypted}) ->
StartStr = pem_start(Type),
[StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"];
+encode_pem_entry({'PrivateKeyInfo', Der, EncParams}) ->
+ EncDer = encode_encrypted_private_keyinfo(Der, EncParams),
+ StartStr = pem_start('EncryptedPrivateKeyInfo'),
+ [StartStr, "\n", b64encode_and_split(EncDer), "\n", pem_end(StartStr) ,"\n\n"];
encode_pem_entry({Type, Der, {Cipher, Salt}}) ->
StartStr = pem_start(Type),
[StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n",
@@ -139,6 +145,12 @@ decode_encrypted_private_keyinfo(Der) ->
DecryptParams = pubkey_pbe:decrypt_parameters(AlgorithmInfo),
{'PrivateKeyInfo', iolist_to_binary(Data), DecryptParams}.
+
+encode_encrypted_private_keyinfo(EncData, EncryptParmams) ->
+ AlgorithmInfo = pubkey_pbe:encrypt_parameters(EncryptParmams),
+ public_key:der_encode('EncryptedPrivateKeyInfo',
+ #'EncryptedPrivateKeyInfo'{encryptionAlgorithm = AlgorithmInfo,
+ encryptedData = EncData}).
split_bin(Bin) ->
split_bin(0, Bin).
@@ -197,13 +209,15 @@ pem_start('DSAPrivateKey') ->
<<"-----BEGIN DSA PRIVATE KEY-----">>;
pem_start('DHParameter') ->
<<"-----BEGIN DH PARAMETERS-----">>;
+pem_start('EncryptedPrivateKeyInfo') ->
+ <<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>;
pem_start('CertificationRequest') ->
<<"-----BEGIN CERTIFICATE REQUEST-----">>;
pem_start('ContentInfo') ->
<<"-----BEGIN PKCS7-----">>;
pem_start('CertificateList') ->
<<"-----BEGIN X509 CRL-----">>;
-pem_start('OTPEcpkParameters') ->
+pem_start('EcpkParameters') ->
<<"-----BEGIN EC PARAMETERS-----">>;
pem_start('ECPrivateKey') ->
<<"-----BEGIN EC PRIVATE KEY-----">>.
@@ -260,7 +274,7 @@ asn1_type(<<"-----BEGIN PKCS7-----">>) ->
asn1_type(<<"-----BEGIN X509 CRL-----">>) ->
'CertificateList';
asn1_type(<<"-----BEGIN EC PARAMETERS-----">>) ->
- 'OTPEcpkParameters';
+ 'EcpkParameters';
asn1_type(<<"-----BEGIN EC PRIVATE KEY-----">>) ->
'ECPrivateKey'.
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index a732455aa7..1bbf4ef416 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -64,9 +64,15 @@
-type der_encoded() :: binary().
-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
| 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
- | 'SubjectPublicKeyInfo' | 'CertificationRequest' | 'CertificateList'.
--type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
- not_encrypted | {Cipher :: string(), Salt :: binary()}}.
+ | 'SubjectPublicKeyInfo' | 'PrivateKeyInfo' |
+ 'CertificationRequest' | 'CertificateList' |
+ 'ECPrivateKey' | 'EcpkParameters'.
+-type pem_entry() :: {pki_asn1_type(),
+ binary(), %% DER or Encrypted DER
+ not_encrypted | {Cipher :: string(), Salt :: binary()} |
+ {Cipher :: string(), #'PBES2-params'{}} |
+ {Cipher :: string(), {#'PBEParameter'{}, atom()}} %% hash type
+ }.
-type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
auth_keys.
@@ -133,20 +139,19 @@ pem_entry_decode({Asn1Type, CryptDer, {Cipher, #'PBES2-params'{}}} = PemEntry,
is_binary(CryptDer) andalso
is_list(Cipher) ->
do_pem_entry_decode(PemEntry, Password);
+pem_entry_decode({Asn1Type, CryptDer, {Cipher, {#'PBEParameter'{},_}}} = PemEntry,
+ Password) when is_atom(Asn1Type) andalso
+ is_binary(CryptDer) andalso
+ is_list(Cipher) ->
+ do_pem_entry_decode(PemEntry, Password);
pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry,
Password) when is_atom(Asn1Type) andalso
is_binary(CryptDer) andalso
is_list(Cipher) andalso
is_binary(Salt) andalso
- erlang:byte_size(Salt) == 8 ->
- do_pem_entry_decode(PemEntry, Password);
-pem_entry_decode({Asn1Type, CryptDer, {"AES-128-CBC"=Cipher, IV}} = PemEntry,
- Password) when is_atom(Asn1Type) andalso
- is_binary(CryptDer) andalso
- is_list(Cipher) andalso
- is_binary(IV) andalso
- erlang:byte_size(IV) == 16 ->
- do_pem_entry_decode(PemEntry, Password).
+ ((erlang:byte_size(Salt) == 8) or (erlang:byte_size(Salt) == 16)) ->
+ do_pem_entry_decode(PemEntry, Password).
+
%%--------------------------------------------------------------------
-spec pem_entry_encode(pki_asn1_type(), term()) -> pem_entry().
@@ -174,13 +179,19 @@ pem_entry_encode(Asn1Type, Entity, {{Cipher, #'PBES2-params'{}} = CipherInfo,
is_list(Password) andalso
is_list(Cipher) ->
do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password);
-
+pem_entry_encode(Asn1Type, Entity, {{Cipher,
+ {#'PBEParameter'{}, _}} = CipherInfo,
+ Password}) when is_atom(Asn1Type) andalso
+ is_list(Password) andalso
+ is_list(Cipher) ->
+ do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password);
pem_entry_encode(Asn1Type, Entity, {{Cipher, Salt} = CipherInfo,
Password}) when is_atom(Asn1Type) andalso
is_list(Password) andalso
is_list(Cipher) andalso
is_binary(Salt) andalso
- erlang:byte_size(Salt) == 8 ->
+ ((erlang:byte_size(Salt) == 8) or
+ (erlang:byte_size(Salt) == 16)) ->
do_pem_entry_encode(Asn1Type, Entity, CipherInfo, Password).
%%--------------------------------------------------------------------
@@ -615,11 +626,11 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
%--------------------------------------------------------------------
-spec pkix_crls_validate(#'OTPCertificate'{},
- [{DP::#'DistributionPoint'{} ,CRL::#'CertificateList'{}}],
+ [{DP::#'DistributionPoint'{}, {DerCRL::binary(), CRL::#'CertificateList'{}}}],
Options :: proplists:proplist()) -> valid | {bad_cert, revocation_status_undetermined}
| {bad_cert, {revoked, crl_reason()}}.
-%% Description: Performs a basic path validation according to RFC 5280.
+%% Description: Performs a CRL validation according to RFC 5280.
%%--------------------------------------------------------------------
pkix_crls_validate(OtpCert, [{_,_,_} |_] = DPAndCRLs, Options) ->
pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs,
diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl
index b68ffbd5fd..aa2bbdd24b 100644
--- a/lib/public_key/test/pbe_SUITE.erl
+++ b/lib/public_key/test/pbe_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -35,7 +35,9 @@ all() ->
[
pbdkdf1,
pbdkdf2,
- encrypted_private_key_info].
+ old_enc,
+ pbes1,
+ pbes2].
groups() ->
[].
@@ -192,44 +194,48 @@ pbdkdf2(Config) when is_list(Config) ->
16#cc, 16#37, 16#d7, 16#f0, 16#34, 16#25, 16#e0, 16#c3>>
= pubkey_pbe:pbdkdf2("pass\0word",
"sa\0lt", 4096, 16, fun crypto:hmac/4, sha, 20).
-
-encrypted_private_key_info() ->
- [{doc,"Tests reading a EncryptedPrivateKeyInfo file encrypted with different ciphers"}].
-encrypted_private_key_info(Config) when is_list(Config) ->
+
+old_enc() ->
+ [{doc,"Tests encode/decode RSA key encrypted with different ciphers using old PEM encryption scheme"}].
+old_enc(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- {ok, PemDes} = file:read_file(filename:join(Datadir, "des_cbc_enc_key.pem")),
+ %% key generated with ssh-keygen -N hello_aes -f old_aes_128_cbc_enc_key.pem
+ {ok, PemAesCbc} = file:read_file(filename:join(Datadir, "old_aes_128_cbc_enc_key.pem")),
- PemDesEntry = public_key:pem_decode(PemDes),
- ct:print("Pem entry: ~p" , [PemDesEntry]),
- [{'PrivateKeyInfo', _, {"DES-CBC",_}} = PubEntry0] = PemDesEntry,
- KeyInfo = public_key:pem_entry_decode(PubEntry0, "password"),
-
- {ok, Pem3Des} = file:read_file(filename:join(Datadir, "des_ede3_cbc_enc_key.pem")),
-
- Pem3DesEntry = public_key:pem_decode(Pem3Des),
- ct:print("Pem entry: ~p" , [Pem3DesEntry]),
- [{'PrivateKeyInfo', _, {"DES-EDE3-CBC",_}} = PubEntry1] = Pem3DesEntry,
- KeyInfo = public_key:pem_entry_decode(PubEntry1, "password"),
-
- {ok, PemRc2} = file:read_file(filename:join(Datadir, "rc2_cbc_enc_key.pem")),
-
- PemRc2Entry = public_key:pem_decode(PemRc2),
- ct:print("Pem entry: ~p" , [PemRc2Entry]),
- [{'PrivateKeyInfo', _, {"RC2-CBC",_}} = PubEntry2] = PemRc2Entry,
- KeyInfo = public_key:pem_entry_decode(PubEntry2, "password"),
-
- %% key generated with ssh-keygen -N hello_aes -f aes_128_cbc_enc_key
- {ok, PemAesCbc} = file:read_file(filename:join(Datadir, "aes_128_cbc_enc_key")),
-
PemAesCbcEntry = public_key:pem_decode(PemAesCbc),
ct:print("Pem entry: ~p" , [PemAesCbcEntry]),
[{'RSAPrivateKey', _, {"AES-128-CBC",_}} = PubAesCbcEntry] = PemAesCbcEntry,
- #'RSAPrivateKey'{} = public_key:pem_entry_decode(PubAesCbcEntry, "hello_aes"),
-
- check_key_info(KeyInfo).
+ #'RSAPrivateKey'{} = public_key:pem_entry_decode(PubAesCbcEntry, "hello_aes").
+pbes1() ->
+ [{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES1"}].
+pbes1(Config) when is_list(Config) ->
+ decode_encode_key_file("pbes1_des_cbc_md5_enc_key.pem", "password", "DES-CBC", Config).
+
+pbes2() ->
+ [{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES2"}].
+pbes2(Config) when is_list(Config) ->
+ decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config),
+ decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config),
+ decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config).
check_key_info(#'PrivateKeyInfo'{privateKeyAlgorithm =
#'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?rsaEncryption},
privateKey = Key}) ->
#'RSAPrivateKey'{} = public_key:der_decode('RSAPrivateKey', iolist_to_binary(Key)).
+
+decode_encode_key_file(File, Password, Cipher, Config) ->
+ Datadir = ?config(data_dir, Config),
+ {ok, PemKey} = file:read_file(filename:join(Datadir, File)),
+
+ PemEntry = public_key:pem_decode(PemKey),
+ ct:print("Pem entry: ~p" , [PemEntry]),
+ [{Asn1Type, _, {Cipher,_} = CipherInfo} = PubEntry] = PemEntry,
+ KeyInfo = public_key:pem_entry_decode(PubEntry, Password),
+ PemKey1 = public_key:pem_encode([public_key:pem_entry_encode(Asn1Type, KeyInfo, {CipherInfo, Password})]),
+ Pem = strip_ending_newlines(PemKey),
+ Pem = strip_ending_newlines(PemKey1),
+ check_key_info(KeyInfo).
+
+strip_ending_newlines(Bin) ->
+ string:strip(binary_to_list(Bin), right, 10).
diff --git a/lib/public_key/test/pbe_SUITE_data/aes_128_cbc_enc_key b/lib/public_key/test/pbe_SUITE_data/old_aes_128_cbc_enc_key.pem
index 34c7543f30..34c7543f30 100644
--- a/lib/public_key/test/pbe_SUITE_data/aes_128_cbc_enc_key
+++ b/lib/public_key/test/pbe_SUITE_data/old_aes_128_cbc_enc_key.pem
diff --git a/lib/public_key/test/pbe_SUITE_data/pbes1_des_cbc_md5_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes1_des_cbc_md5_enc_key.pem
new file mode 100644
index 0000000000..12e860c7a7
--- /dev/null
+++ b/lib/public_key/test/pbe_SUITE_data/pbes1_des_cbc_md5_enc_key.pem
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICoTAbBgkqhkiG9w0BBQMwDgQIZmB6EGEnIOcCAggABIICgDaaKoCEUowjKt5M
+uwMAIf7uugy09OcqR8PcB9ioiuk5NQGXkIBxOOlOuFb6xrP+O2dSppr5k/ZU+NEX
+Lf18AdMld1nlE6lwjPytOIqt6Q3YMeny8un1/jopnkQZKthJ5moER5ohp/2osTDV
+4Ih8MtHTwE879SHAmj7Y3G7itKHQi17212DVmL+D+P7iRzTCKIyPj5KMXvXN+eor
+j0urZXVOeyRTABHQnf6xJn8K+dGowC/AJTQWOgFunlBKzecepqF22OQzIW2R60aM
+VgykSd8A5G8o1F+tO2Qrp6KM9Ak709dUX8qRb/C02w5rjg2g0frgFyEGX0pUJbno
+dJLKMOT1WvDnsXaS720beyzrOynWiAuaFZwb1/nPSQnzJ4t0mUvQQis5ph3eHSR/
+a9/PER81IDjPtjlTJjaOGuwhIRmGFsLUrQhOnVcI7Z5TCSj7EHdqK3xzjSVzu5DY
+BqE2rsigiIOszPdbK4tKCDheIwBhYdptDvG9c+j3Mj0YNOXJxsX0gVoMqtpwryNG
+OZy5fLujS4l+cPq64dOh/LE87mrM9St6M6gw2VRW7d0U18Muubp/MK8q9O2i80Nw
+ZFrHHE1N09x3aTnty4mwdCHl6w5aJMZg6WbUXJnf0zKa8ADv5wZmAvW3fO4G8434
+3FHj1hdyKPcoVjoFVawyRUflF/jYd1pLpV+iZwDDR4lacb4ay1Lut452ifZ8DqOq
+lWYL0uskCn1WI856vtlLV3gnV02xDjAilSY2hASOyoD1wypZefPn5S+U3vkLuzFZ
+ycbyIwGYTLWj71u8Vu3JceRI3OIPDuM7zcNHr71eQyiwLEA0iszQQA9xgqmeFtJO
+IkpUTAY=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_des_cbc_enc_key.pem
index eaa06145aa..eaa06145aa 100644
--- a/lib/public_key/test/pbe_SUITE_data/des_cbc_enc_key.pem
+++ b/lib/public_key/test/pbe_SUITE_data/pbes2_des_cbc_enc_key.pem
diff --git a/lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_des_ede3_cbc_enc_key.pem
index 22ea46d56f..22ea46d56f 100644
--- a/lib/public_key/test/pbe_SUITE_data/des_ede3_cbc_enc_key.pem
+++ b/lib/public_key/test/pbe_SUITE_data/pbes2_des_ede3_cbc_enc_key.pem
diff --git a/lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_rc2_cbc_enc_key.pem
index 618cddcfd7..618cddcfd7 100644
--- a/lib/public_key/test/pbe_SUITE_data/rc2_cbc_enc_key.pem
+++ b/lib/public_key/test/pbe_SUITE_data/pbes2_rc2_cbc_enc_key.pem
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index f0450918aa..2fa2d725c3 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 0.22
+PUBLIC_KEY_VSN = 0.22.1
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
index ab3041137e..e4def7c7f5 100644
--- a/lib/sasl/doc/src/alarm_handler.xml
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -87,7 +87,9 @@
<v>AlarmId = term()</v>
</type>
<desc>
- <p>Clears all alarms with id <c>AlarmId</c>.
+ <p>Sends the <c>clear_alarm</c> event to all event handlers.</p>
+ <p>When receiving this event, the default simple handler
+ clears the latest received alarm with id <c>AlarmId</c>.
</p>
</desc>
</func>
@@ -109,8 +111,10 @@
<v>AlarmDescription = term()</v>
</type>
<desc>
- <p>Sets an alarm with id <c>AlarmId</c>. This id is used at a
- later stage when the alarm is cleared.
+ <p>Sends the <c>set_alarm</c> event to all event handlers.</p>
+ <p>When receiving this event, the default simple handler
+ stores the alarm. The <c>AlarmId</c> identifies the alarm
+ and is used when the alarm is cleared.
</p>
</desc>
</func>
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index 2928a12d22..95d7c6fa50 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -30,6 +30,26 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 2.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The documentation erroneously specified that
+ <c>alarm_handler:clear_alarm/1</c> would clear
+ <em>all</em> alarms with id <c>AlarmId</c>. This is now
+ corrected according to the implementation - only the
+ latest received alarm with the given <c>AlarmId</c> is
+ cleared by the simple default handler.</p>
+ <p>
+ Own Id: OTP-12025</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 2.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl
index e91d220daf..d7b99d506e 100644
--- a/lib/sasl/test/sasl_SUITE.erl
+++ b/lib/sasl/test/sasl_SUITE.erl
@@ -57,17 +57,29 @@ appup_test(_Config) ->
appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-appup_tests(App,{OkVsns,NokVsns}) ->
+appup_tests(App,{OkVsns0,NokVsns}) ->
application:load(App),
{_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
AppupFileName = atom_to_list(App) ++ ".appup",
AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
- ct:log("Testing ok versions: ~p~n",[OkVsns]),
+ OkVsns =
+ case OkVsns0 -- [Vsn] of
+ OkVsns0 ->
+ OkVsns0;
+ Ok ->
+ ct:log("Current version, ~p, is same as in previous release.~n"
+ "Removing this from the list of ok versions.",
+ [Vsn]),
+ Ok
+ end,
+ ct:log("Testing that appup allows upgrade from these versions: ~p~n",
+ [OkVsns]),
check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
- ct:log("Testing not ok versions: ~p~n",[NokVsns]),
+ ct:log("Testing that appup does not allow upgrade from these versions: ~p~n",
+ [NokVsns]),
check_appup(NokVsns,UpFrom,error),
check_appup(NokVsns,DownTo,error),
ok.
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index da8cbc5130..4259a2d76c 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 2.4
+SASL_VSN = 2.4.1
diff --git a/lib/snmp/.gitignore b/lib/snmp/.gitignore
index 650c1d6865..aef73491a4 100644
--- a/lib/snmp/.gitignore
+++ b/lib/snmp/.gitignore
@@ -5,5 +5,4 @@
*.rej
doc/index.html
-
-
+mibs/.index
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 15efd47a1c..bbe6438f04 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -33,7 +33,24 @@
</header>
- <section><title>SNMP 5.0</title>
+ <section><title>SNMP 5.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The SNMP manager has been enhanced with dual stack
+ IPv4+IPv6, as the agent just was. The documentation is
+ also now updated for both the agent and the manager.</p>
+ <p>
+ Own Id: OTP-12108 Aux Id: OTP-12020 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.0</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/snmp/doc/src/snmp_agent_config_files.xml b/lib/snmp/doc/src/snmp_agent_config_files.xml
index 1e8e879814..1e938c0dc8 100644
--- a/lib/snmp/doc/src/snmp_agent_config_files.xml
+++ b/lib/snmp/doc/src/snmp_agent_config_files.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -102,20 +102,41 @@
<p><c>AgentVariable</c> is one of the variables is
SNMP-FRAMEWORK-MIB or one of the internal variables
<c>intAgentUDPPort</c>, which defines which UDP port the agent
- listens to, or <c>intAgentIpAddress</c>, which defines the IP
- address of the agent. </p>
+ listens to, or <c>intAgentTransports</c>, which defines the
+ transport domains and addresses of the agent. </p>
</item>
<item>
<p><c>Value</c> is the value for the variable.</p>
</item>
</list>
- <p>The following example shows a <c>agent.conf</c> file: </p>
+ <p>The following example shows an <c>agent.conf</c> file: </p>
<pre>
{intAgentUDPPort, 4000}.
-{intAgentIpAddress,[141,213,11,24]}.
+{intAgentTransports,
+ [{transportDomainUdpIpv4, {141,213,11,24}},
+ {transportDomainUdpIpv6, {0,0,0,0,0,0,0,1}}]}.
{snmpEngineID, "mbj's engine"}.
{snmpEngineMaxPacketSize, 484}.
</pre>
+ <p>The value of <c>intAgentTransports</c> is a list of
+ <c>{Domain, Addr}</c> tuples, where <c>Domain</c>
+ is either <c>transportDomainUdpIpv4</c> or <c>transportDomainUdpIpv6</c>,
+ and <c>Addr</c> is the address in the domain.
+ <c>Addr</c> can be specified either as an
+ <c>IpAddr</c> or as an <c>{IpAddr, IpPort}</c> tuple.
+ <c>IpAddr</c> is either a regular Erlang/OTP
+ <seealso marker="kernel:inet#type-ip_address"><c>ip_address()</c></seealso>
+ or a traditional SNMP integer list and <c>IpPort</c> is an integer.
+ </p>
+
+ <p>When the <c>Addr</c> value does not contain a port number,
+ the value of <c>intAgentUDPPort</c> is used.</p>
+
+ <p>The legacy and intermediate variables <c>intAgentIpAddress</c>
+ and <c>intAgentTransportDomain</c> are still supported so old
+ <c>agent.conf</c> files will work.
+ </p>
+
<p>The value of <c>snmpEngineID</c> is a string, which for a
deployed agent should have a very specific structure. See
RFC 2271/2571 for details.</p>
@@ -362,9 +383,9 @@
SNMP-TARGET-MIB and <c>snmpTargetAddrExtTable</c> in the
SNMP-COMMUNITY-MIB. </p>
<p>Each entry is a term: </p>
- <p><c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId}.</c> <br></br> or <br></br>
-<c>{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c> <br></br> or <br></br>
-<c>{TargetName, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c> </p>
+ <p><c>{TargetName, Domain, Addr, Timeout, RetryCount, TagList, ParamsName, EngineId}.</c>
+ <br></br> or <br></br>
+ <c>{TargetName, Domain, Addr, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.</c> </p>
<list type="bulleted">
<item>
<p><c>TargetName</c> is a unique non-empty string. </p>
@@ -374,11 +395,14 @@
<c>transportDomainUdpIpv4</c> | <c>transportDomainUdpIpv6</c>. </p>
</item>
<item>
- <p><c>Ip</c> is a list of four or eight integers. </p>
- </item>
- <item>
- <p><c>Udp</c> is an integer. </p>
+ <p><c>Addr</c> is either an <c>IpAddr</c> or
+ an <c>{IpAddr, IpPort}</c> tuple. <c>IpAddr</c> is either
+ a regular Erlang/OTP
+ <seealso marker="kernel:inet#type-ip_address"><c>ip_address()</c></seealso>
+ or a traditional SNMP integer list, and <c>IpPort</c> is an integer.</p>
+ <p>If <c>IpPort</c> is omitted <c>162</c> is used.</p>
</item>
+
<item>
<p><c>Timeout</c> is an integer. </p>
</item>
@@ -395,13 +419,17 @@
<p><c>EngineId</c> is a string or the atom <c>discovery</c>. </p>
</item>
<item>
- <p><c>TMask</c> is a list of integer() of size 0,
- size 6 or size 10 (default: []). </p>
+ <p><c>TMask</c> is specified just as <c>Addr</c> or as <c>[]</c>.
+ Note in particular that using a list of 6 bytes for IPv4
+ or 8 words plus 2 bytes for IPv6 are still valid address formats
+ so old configurations will work.</p>
</item>
<item>
<p><c>MaxMessageSize</c> is an integer (default: 2048). </p>
</item>
</list>
+ <p>The old tuple formats with <c>Ip</c> address and <c>Udp</c>
+ port number found in old configurations still work.</p>
<p>Note that if <c>EngineId</c> has the value <c>discovery</c>,
the agent cannot send
<c>inform</c> messages to that manager until it has performed the
diff --git a/lib/snmp/doc/src/snmp_agent_netif.xml b/lib/snmp/doc/src/snmp_agent_netif.xml
index fccfc8857a..a9ce05e757 100644
--- a/lib/snmp/doc/src/snmp_agent_netif.xml
+++ b/lib/snmp/doc/src/snmp_agent_netif.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2013</year>
+ <year>1997</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -51,7 +51,8 @@
</p>
<p>It is also possible to write your own Net if process. The default
Net if process is implemented in the module <c>snmpa_net_if</c> and
- it uses UDP as the transport protocol.
+ it uses UDP as the transport protocol i.e the transport domains
+ <c>transportDomainUdpIpv4</c> and/or <c>transportDomainUdpIpv6</c>.
</p>
<p>This section describes how to write a Net if process.
</p>
@@ -70,6 +71,12 @@
<p>The section <em>Messages</em> describes mandatory messages, which
Net if must send and be able to receive.
</p>
+ <p>In this section an <c>Address</c> field is a
+ <c>{Domain, Addr}</c> tuple where <c>Domain</c> is
+ <c>transportDomainUdpIpv4</c> or <c>transportDomainUdpIpv4</c>,
+ and <c>Addr</c> is an
+ <c>{<seealso marker="kernel:inet#type-ip_address">IpAddr</seealso>,
+ IpPort}</c> tuple.</p>
<section>
<marker id="outgoing_messages"></marker>
@@ -96,10 +103,7 @@ MasterAgent ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra}
in use. Normally this is returned from
<c>snmpa_mpd:process_packet</c> (see Reference Manual).
</item>
- <item><c>From</c> is the source address. If UDP over IP is
- used, this should be a 2-tuple <c>{IP, UDPport}</c>, where
- <c>IP</c> is a 4-tuple with the IP address, and <c>UDPport</c>
- is an integer.
+ <item><c>From</c> is the source <c>Address</c>.
</item>
<item><c>Extra</c> is any term the Net if process wishes to
send to the agent. This term can be retrieved by the
@@ -127,10 +131,7 @@ Pid ! {snmp_response_received, Vsn, Pdu, From}
</item>
<item><c>Pdu</c> is the SNMP Pdu received
</item>
- <item><c>From</c> is the source address. If UDP over IP is
- used, this should be a 2-tuple <c>{IP, UDPport}</c>, where
- <c>IP</c> is a 4-tuple with the IP address, and <c>UDPport</c>
- is an integer.
+ <item><c>From</c> is the source <c>Address</c>.
</item>
</list>
</section>
@@ -168,10 +169,9 @@ Pid ! {snmp_response_received, Vsn, Pdu, From}
(see Reference Manual). </p>
</item>
<item>
- <p><c>To</c> is the destination address. If UDP over IP
- is used, this should be a 2-tuple <c>{IP, UDPport}</c>,
- where <c>IP</c> is a 4-tuple with the IP address, and
- <c>UDPport</c> is an integer. </p>
+ <p><c>To</c> is the destination <c>Address</c> that comes
+ from the <c>From</c> field in the corresponding <c>snmp_pdu</c>
+ message previously sent to the MasterAgent.</p>
</item>
<item>
<p><c>Extra</c> is the term that the Net if process
@@ -230,7 +230,8 @@ Pid ! {snmp_response_received, Vsn, Pdu, From}
SNMPv3, it is the context information. </p>
</item>
<item>
- <p><c>To</c> is a list of the destination addresses and
+ <p><c>To</c> is a list of <c>{Address, SecData}</c>
+ tuples i.e the destination addresses and
their corresponding security parameters. This value is
normally sent to <c>snmpa_mpd:generate_message/4</c>. </p>
</item>
@@ -268,7 +269,8 @@ Pid ! {snmp_response_received, Vsn, Pdu, From}
SNMPv3, it is the context information. </p>
</item>
<item>
- <p><c>To</c> is a list of the destination addresses and
+ <p><c>To</c> is a list of <c>{Address, SecData}</c>
+ tuples i.e the destination addresses and
their corresponding security parameters. This value is
normally sent to <c>snmpa_mpd:generate_message/4</c>. </p>
</item>
diff --git a/lib/snmp/doc/src/snmp_manager_config_files.xml b/lib/snmp/doc/src/snmp_manager_config_files.xml
index 486ef7c170..d8bd4b0f3a 100644
--- a/lib/snmp/doc/src/snmp_manager_config_files.xml
+++ b/lib/snmp/doc/src/snmp_manager_config_files.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -64,13 +64,42 @@
<item>
<p><c>Variable</c> is one of the following:</p>
<list type="bulleted">
- <item>
- <p><c>address</c> - which defines the IP address of the
- manager. Default is local host.</p>
- </item>
+ <item>
+ <p><c>transports</c> - which defines the transport domains
+ and their addresses for the manager. <em>Mandatory</em>
+ </p>
+ <p><c>Value</c> is a list of <c>{Domain, Addr}</c> tuples
+ or <c>Domain</c> atoms.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>Domain</c> is one of <c>transportDomainUdpIpv4</c>
+ or <c>transportDomainUdpIpv6</c>.</p>
+ </item>
+ <item>
+ <p><c>Addr</c> is for the currently supported domains
+ either an <c>IpAddr</c> or an <c>{IpAddr, IpPort}</c>
+ tuple.<c>IpAddr</c> is either a regular Erlang/OTP
+ <seealso marker="kernel:inet#type-ip_address">
+ <c>ip_address()</c></seealso> or a traditional SNMP integer list
+ and <c>IpPort</c> is an integer.
+ </p>
+ <p>When <c>Addr</c> does not contain a port number,
+ the value of <c>port</c> is used.
+ </p>
+ <p>When a <c>Addr</c> is not specified i.e by
+ using only a <c>Domain</c> atom, the host's name
+ is resolved to find the IP address, and the value of
+ <c>port</c> is used.
+ </p>
+ </item>
+ </list>
+ </item>
<item>
<p><c>port</c> - which defines which UDP port the manager uses
- for communicating with agents. <em>Mandatory</em>.</p>
+ for communicating with agents.
+ <em>Mandatory</em> if <c>transports</c> does not define
+ a port number for every transport.</p>
</item>
<item>
<p><c>engine_id</c> - The <c>SnmpEngineID</c> as defined in
@@ -87,11 +116,13 @@
</p>
</item>
</list>
+ <p>The legacy and intermediate variables <c>address</c> and <c>domain</c>
+ are still supported so old configurations will work.</p>
<p>The following example shows a <c>manager.conf</c> file:
</p>
<pre>
-{address, [141,213,11,24]}.
-{port, 5000}.
+{transports, [{transportDomainUdpIpv4, {{141,213,11,24}, 5000}},
+ {transportDomainUdpIpv6, {{0,0,0,0,0,0,0,1}, 5000}}]}.
{engine_id, "mgrEngine"}.
{max_message_size, 484}.
</pre>
@@ -146,7 +177,7 @@
</p>
<p>Each entry is a tuple:
</p>
- <p><c>{UserId, TargetName, Comm, Ip, Port, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel}.</c></p>
+ <p><c>{UserId, TargetName, Comm, Domain, Addr, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel}.</c></p>
<list type="bulleted">
<item>
<p><c>UserId</c> is the identity of the <em>manager user</em>
@@ -160,10 +191,17 @@
<p><c>Comm</c> is the community string (string).</p>
</item>
<item>
- <p><c>Ip</c> is the ip address of the agent (a list of four integers).</p>
+ <p><c>Domain</c> is the transport domain, either
+ <c>transportDomainUdpIpv4</c> or <c>transportDomainUdpIpv6</c>.</p>
</item>
<item>
- <p><c>Port</c> is the port number of the agent (integer).</p>
+ <p><c>Addr</c> is the address in the transport domain,
+ either an <c>{IpAddr, IpPort}</c> tuple or a traditional SNMP
+ integer list containing port number. <c>IpAddr</c> is either
+ a regular Erlang/OTP
+ <seealso marker="kernel:inet#type-ip_address"><c>ip_address()</c></seealso>
+ or a traditional SNMP integer list not containing port number,
+ and <c>IpPort</c> is an integer.</p>
</item>
<item>
<p><c>EngineID</c> is the engine-id of the agent (string).</p>
@@ -190,6 +228,9 @@
authPriv).</p>
</item>
</list>
+ <p>Legacy configurations using tuples without <c>Domain</c> element,
+ as well as with all <c>TDomain</c>, <c>Ip</c> and <c>Port</c> elements
+ still work.</p>
</section>
<section>
diff --git a/lib/snmp/doc/src/snmp_manager_netif.xml b/lib/snmp/doc/src/snmp_manager_netif.xml
index 757ed32880..97cedf00c0 100644
--- a/lib/snmp/doc/src/snmp_manager_netif.xml
+++ b/lib/snmp/doc/src/snmp_manager_netif.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -50,13 +50,14 @@
<p>The snmp application provides two different modules,
<c>snmpm_net_if</c> (the default) and <c>snmpm_net_if_mt</c>,
- both uses the UDP as the transport protocol. The difference
- between the two modules is that the latter is "multi-threaded",
- i.e. for each message/request a new process is created that
- process the message/request and then exits. </p>
+ both uses UDP as the transport protocol i.e the transport domains
+ <c>transportDomainUdpIpv4</c> and/or <c>transportDomainUdpIpv6</c>.
+ The difference between the two modules is that the latter is
+ "multi-threaded", i.e. for each message/request a new process
+ is created that processes the message/request and then exits. </p>
- <p>It is also possible to write your own Net if process,
- this section describes how to write a Net if processdo that.</p>
+ <p>It is also possible to write your own Net if process and
+ this section describes how to do that.</p>
<section>
<marker id="mandatory_functions"></marker>
@@ -70,11 +71,17 @@
<p>The section <em>Messages</em> describes mandatory messages, which
Net if must send to the manager server process.
</p>
+ <p>In this section a <c>Domain</c> field is the transport domain i.e
+ one of <c>transportDomainUdpIpv4</c> or <c>transportDomainUdpIpv6</c>,
+ and an <c>Addr</c> field is an
+ <c>{<seealso marker="kernel:inet#type-ip_address">IpAddr</seealso>,
+ IpPort}</c> tuple.</p>
+
<p>Net if must send the following message when it receives an
SNMP PDU from the network that is aimed for the MasterAgent:
</p>
<pre>
-Server ! {snmp_pdu, Pdu, Addr, Port}
+Server ! {snmp_pdu, Pdu, Domain, Addr}
</pre>
<list type="bulleted">
<item>
@@ -82,14 +89,14 @@ Server ! {snmp_pdu, Pdu, Addr, Port}
<c>snmp_types.hrl</c>, with the SNMP request.</p>
</item>
<item>
- <p><c>Addr</c> is the source address. </p>
+ <p><c>Domain</c> is the source transport domain. </p>
</item>
<item>
- <p><c>Port</c> is port number of the sender.</p>
+ <p><c>Addr</c> is the source address. </p>
</item>
</list>
<pre>
-Server ! {snmp_trap, Trap, Addr, Port}
+Server ! {snmp_trap, Trap, Domain, Addr}
</pre>
<list type="bulleted">
<item>
@@ -97,14 +104,14 @@ Server ! {snmp_trap, Trap, Addr, Port}
as defined in <c>snmp_types.hrl</c>, with the SNMP request.</p>
</item>
<item>
- <p><c>Addr</c> is the source address. </p>
+ <p><c>Domain</c> is the source transport domain. </p>
</item>
<item>
- <p><c>Port</c> is port number of the sender.</p>
+ <p><c>Addr</c> is the source address. </p>
</item>
</list>
<pre>
-Server ! {snmp_inform, Ref, Pdu, PduMS, Addr, Port}
+Server ! {snmp_inform, Ref, Pdu, PduMS, Domain, Addr}
</pre>
<list type="bulleted">
<item>
@@ -123,14 +130,14 @@ Server ! {snmp_inform, Ref, Pdu, PduMS, Addr, Port}
<c>snmp_types.hrl</c>, with the SNMP request.</p>
</item>
<item>
- <p><c>Addr</c> is the source address. </p>
+ <p><c>Domain</c> is the source transport domain. </p>
</item>
<item>
- <p><c>Port</c> is port number of the sender.</p>
+ <p><c>Addr</c> is the source address. </p>
</item>
</list>
<pre>
-Server ! {snmp_report, Data, Addr, Port}
+Server ! {snmp_report, Data, Domain, Addr}
</pre>
<list type="bulleted">
<item>
@@ -152,10 +159,10 @@ Server ! {snmp_report, Data, Addr, Port}
<p><c>ReasonInfo</c> is a term().</p>
</item>
<item>
- <p><c>Addr</c> is the source address. </p>
+ <p><c>Domain</c> is the source transport domain. </p>
</item>
<item>
- <p><c>Port</c> is port number of the sender.</p>
+ <p><c>Addr</c> is the source address. </p>
</item>
</list>
diff --git a/lib/snmp/doc/src/snmp_target_mib.xml b/lib/snmp/doc/src/snmp_target_mib.xml
index be6fa15c73..a076ff2d8e 100644
--- a/lib/snmp/doc/src/snmp_target_mib.xml
+++ b/lib/snmp/doc/src/snmp_target_mib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1998</year><year>2013</year>
+ <year>1998</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,18 +38,18 @@
functions for the SNMP-TARGET-MIB,
and functions for configuring the database. </p>
<p>The configuration files are described in the SNMP User's Manual.</p>
+ <p>Legacy API functions <c>add_addr/10</c> that does not specify
+ transport domain, and <c>add_addr/11</c> that has got separate
+ <c>IpAddr</c> and <c>PortNumber</c> arguments still work as before
+ for backwards compatibility reasons.</p>
<marker id="types"></marker>
</description>
<section>
<title>DATA TYPES</title>
- <code type="none"><![CDATA[
-transportDomain() = transportDomainUdpIpv4 | transportDomainUdpIpv6
-transportAddressIPv4() = [integer()], length 4
-transportAddressIPv6() = [integer()], length 8
-transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6)
- ]]></code>
+ <p>See the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
<marker id="configure"></marker>
</section>
@@ -129,20 +129,18 @@ transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6)
</func>
<func>
- <name>add_addr(Name, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name>
- <name>add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name>
+ <name>add_addr(Name, Domain, Addr, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret</name>
<fsummary>Add one target address definition</fsummary>
<type>
<v>Name = string()</v>
<v>Domain = transportDomain()</v>
- <v>Ip = transportAddressIPv4() | transportAddressIPv6() (depends on the value of Domain)</v>
- <v>Port = integer()</v>
+ <v>Addr = transportAddress() % Default port is 162</v>
<v>Timeout = integer()</v>
<v>Retry = integer()</v>
<v>TagList = string()</v>
<v>ParamsName = string()</v>
<v>EngineId = string()</v>
- <v>TMask = transportAddressMask() (depends on Domain)</v>
+ <v>TMask = transportAddressMask() % Depends on Domain</v>
<v>MMS = integer()</v>
<v>Ret = {ok, Key} | {error, Reason}</v>
<v>Key = term()</v>
diff --git a/lib/snmp/doc/src/snmpa_conf.xml b/lib/snmp/doc/src/snmpa_conf.xml
index 99a56cd601..2780cec156 100644
--- a/lib/snmp/doc/src/snmpa_conf.xml
+++ b/lib/snmp/doc/src/snmpa_conf.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2013</year>
+ <year>2006</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -45,20 +45,56 @@
<title>DATA TYPES</title>
<code type="none"><![CDATA[
transportDomain() = transportDomainUdpIpv4 | transportDomainUdpIpv6
-transportAddressIPv4() = [integer()], length 4
-transportAddressIPv6() = [integer()], length 8
-transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6)
+
+transportAddress() =
+ transportAddressIPv4() | transportAddressIPv6()
+
+transportAddressWithPort() =
+ transportAddressIPv4WithPort() | transportAddressIPv6WithPort()
+
+transportAddressWithoutPort() =
+ transportAddressIPv4WithoutPort() | transportAddressIPv6WithoutPort()
+
+transportAddressIPv4() =
+ transportAddressIPv4WithPort() | transportAddressIPv4WithoutPort()
+transportAddressIPv4WithPort =
+ {transportAddressIPv4WithoutPort(), inet:port_number()} |
+ [byte() x 4, byte() x 2]
+transportAddressIPv4WithoutPort =
+ inet:ip4_address() | [byte() x 4]
+
+transportAddressIPv6() =
+ transportAddressIPv6WithPort() | transportAddressIPv6WithoutPort()
+transportAddressIPv6WithPort =
+ {transportAddressIPv6WithoutPort(), inet:port_number()} |
+ [word() x 8, inet:port_number()] |
+ [word() x 8, byte() x 2] |
+ {byte() x 16, byte() x 2]
+transportAddressIPv6WithoutPort =
+ inet:ip6_address() | [word() x 8] | [byte() x 16]
+
+transportAddressMask() =
+ [] | transportAddressWithPort()
+
+byte() = 0..255
+word() = 0..65535
]]></code>
+ <p>For <c>inet:ip4_address()</c>, <c>inet:ip6_address()</c>
+ and <c>inet:port_number()</c>, see also
+ <seealso marker="kernel:inet#type-ip_address">
+ <c>inet:ip_address()</c></seealso></p>
<marker id="agent_entry"></marker>
</section>
+
+
<funcs>
<func>
<name>agent_entry(Tag, Val) -> agent_entry()</name>
<fsummary>Create an agent entry</fsummary>
<type>
- <v>Tag = intAgentIpAddress | intAgentUDPPort | intAgentMaxPacketSize | snmpEngineMaxMessageSize | snmpEngineID</v>
+ <v>Tag = intAgentTransports | intAgentUDPPort | intAgentMaxPacketSize | snmpEngineMaxMessageSize | snmpEngineID</v>
<v>Val = term()</v>
<v>agent_entry() = term()</v>
</type>
@@ -390,17 +426,15 @@ transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6)
</func>
<func>
- <name>target_addr_entry(Name, Ip, TagList, ParamsName, EngineId) -> target_addr_entry()</name>
- <name>target_addr_entry(Name, Ip, TagList, ParamsName, EngineId, TMask) -> target_addr_entry()</name>
- <name>target_addr_entry(Name, Ip, Udp, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name>
- <name>target_addr_entry(Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name>
- <name>target_addr_entry(Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name>
+ <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId) -> target_addr_entry()</name>
+ <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask) -> target_addr_entry()</name>
+ <name>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name>
+ <name>target_addr_entry(Name, Domain, Addr, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry()</name>
<fsummary>Create an target_addr entry</fsummary>
<type>
<v>Name = string()</v>
<v>Domain = transportDomain()</v>
- <v>Ip = transportAddressIPv4() | transportAddressIPv6() (depends on Domain)</v>
- <v>Udp = integer()</v>
+ <v>Ip = transportAddress() (depends on Domain)</v>
<v>Timeout = integer()</v>
<v>RetryCount = integer()</v>
<v>TagList = string()</v>
@@ -414,12 +448,12 @@ transportAddressMask() = [integer()], length 0 (default), 6 (IPv4) or 10 (IPv6)
<p>Create an entry for the agent target_addr config file,
<c>target_addr.conf</c>. </p>
<p><c>Name</c> must be a <em>non-empty</em> string. </p>
- <p><c>target_addr_entry/5</c> translates to the following call:
- <c>target_addr_entry(Name, Ip, TagList, ParamsName, EngineId)</c>. </p>
<p><c>target_addr_entry/6</c> translates to the following call:
- <c>target_addr_entry(Name, Ip, 162, TagList, ParamsName, EngineId, TMask, 2048)</c>. </p>
+ <c>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, [])</c>. </p>
+ <p><c>target_addr_entry/7</c> translates to the following call:
+ <c>target_addr_entry(Name, Domain, Addr, TagList, ParamsName, EngineId, TMask, 2048)</c>. </p>
<p><c>target_addr_entry/8</c> translates to the following call:
- <c>target_addr_entry(Name, Ip, Udp, 1500, 3, TagList, ParamsName, EngineId, TMask, MaxMessageSize)</c>. </p>
+ <c>target_addr_entry(Name, Domain, Addr, 1500, 3, TagList, ParamsName, EngineId, TMask, MaxMessageSize)</c>. </p>
<p>See
<seealso marker="snmp_agent_config_files#target_addr">Target Address Definitions</seealso>
for more info. </p>
diff --git a/lib/snmp/doc/src/snmpa_mpd.xml b/lib/snmp/doc/src/snmpa_mpd.xml
index c5ab0a0520..518100d30c 100644
--- a/lib/snmp/doc/src/snmpa_mpd.xml
+++ b/lib/snmp/doc/src/snmpa_mpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2013</year>
+ <year>1999</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,6 +43,12 @@
<marker id="init"></marker>
</description>
+ <section>
+ <title>DATA TYPES</title>
+ <p>See the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
+ </section>
+
<funcs>
<func>
<name>init(Vsns) -> mpd_state()</name>
@@ -63,16 +69,17 @@
</func>
<func>
- <name>process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
- <name>process_packet(Packet, TDomain, TAddress, LocalEngineID, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
+ <name>process_packet(Packet, From, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
+ <name>process_packet(Packet, From, LocalEngineID, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
<fsummary>Process a packet received from the network</fsummary>
<type>
<v>Packet = binary()</v>
- <v>TDomain = snmpUDPDomain</v>
- <v>TAddress = {Ip, Udp}</v>
+ <v>From = {TDomain, TAddr}</v>
+ <v>TDomain = transportDomainUdpIpv4 | transportDomainUdpIpv6</v>
+ <v>TAddr = {IpAddr, IpPort}</v>
<v>LocalEngineID = string()</v>
- <v>Ip = {integer(), integer(), integer(), integer()}</v>
- <v>Udp = integer()</v>
+ <v>IpAddr = <seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso></v>
+ <v>IpPort = inet:port_number()</v>
<v>State = mpd_state()</v>
<v>NoteStore = pid()</v>
<v>Log = snmp_log()</v>
@@ -85,7 +92,7 @@
</type>
<desc>
<p>Processes an incoming packet. Performs authentication and
- decryption as necessary. The return values should be passed the
+ decryption as necessary. The return values should be passed to the
agent.</p>
<note>
@@ -150,14 +157,20 @@
network.
</p>
<p><c>MsgData</c> is the message specific data used in
- the SNMP message. This value is received in a <c>send_pdu</c>
- or <c>send_pdu_req</c> message from the agent. In SNMPv1 and
+ the SNMP message. This value is received in a
+ <seealso marker="snmp_agent_netif#im_send_pdu"><c>send_pdu</c></seealso>
+ or
+ <seealso marker="snmp_agent_netif#im_send_pdu_req">
+ <c>send_pdu_req</c></seealso>
+ message from the agent. In SNMPv1 and
SNMPv2c, this message data is the community string. In
- SNMPv3, it is the context information.
- <c>To</c> is a list of the destination addresses and
+ SNMPv3, it is the context information.</p>
+ <p>
+ <c>To</c> is a list of destination addresses and
their corresponding security parameters. This value is
- also received from the requests mentioned above.
- </p>
+ received in the same message from the agent and then transformed
+ trough <seealso marker="#process_taddrs"><c>process_taddrs</c></seealso>
+ before passed to this function.</p>
<note>
<p>Note that the use of the LocalEngineID argument is only intended
@@ -166,6 +179,32 @@
(see SNMP-FRAMEWORK-MIB). </p>
</note>
+ <marker id="process_taddrs"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>process_taddrs(TDests) -> Dests</name>
+ <fsummary>Transform addresses from internal MIB format to a less internal
+ </fsummary>
+ <type>
+ <v>TDests = [TDest]</v>
+ <v>TDest = {{TDomain, TAddr}, SecData} | {TDomain, TAddr}</v>
+ <v>TDomain = term() % Not at tuple</v>
+ <v>TAddr = term()</v>
+ <v>SecData = term()</v>
+ <v>Dests = [Dest]</v>
+ <v>Dest = {{Domain, Addr}, SecData} | {Domain, Addr}</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddress() % Depends on Domain</v>
+ </type>
+ <desc>
+ <p>Transforms addresses from internal MIB format to one
+ more useful to <seealso marker="snmp_agent_netif">Agent Net if</seealso>.
+ </p>
+ <p>See also <seealso marker="#generate_msg"><c>generate_msg</c>.</seealso>
+ </p>
+
<marker id="discarded_pdu"></marker>
</desc>
</func>
diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml
index e08a26ed92..eb640e1bc3 100644
--- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2013</year>
+ <year>2007</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -58,10 +58,10 @@
on two levels: </p>
<list type="bulleted">
<item>
- <p>The first level is at the UDP entry / exit point, i.e.
- immediately after the receipt of the message, before any message
+ <p>The first level is at the transport entry / exit point, i.e.
+ immediately after the receipt of the message before any message
processing is done (accept_recv) and
- immediately before sending the message, after all message
+ immediately before sending the message after all message
processing is done (accept_send).</p>
</item>
<item>
@@ -78,6 +78,12 @@
<seealso marker="snmp_app#configuration_params">req_limit</seealso> and
the function
<seealso marker="snmpa#register_notification_filter">register_notification_filter</seealso>. </p>
+ <p>Legacy network interface filter modules used arguments on the form
+ <c>(IpAddr, PortNumber,...)</c> instead of
+ <c>(Domain, Addr, ...)</c>, and if the SNMP agent is run without
+ changing the configuration to use transport domains
+ the network interface filter will still get
+ the old arguments and work as before.</p>
</description>
<section>
@@ -88,15 +94,17 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
'set-request' | trap | 'get-bulk-request' | 'inform-request' |
report
</code>
+ <p>See also the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
<marker id="accept_recv"></marker>
</section>
<funcs>
<func>
- <name>accept_recv(Ip, Port) -> boolean()</name>
+ <name>accept_recv(Domain, Addr) -> boolean()</name>
<fsummary>Shall the received message be accepted</fsummary>
<type>
- <v>Ip = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
</type>
<desc>
<p>Called at the reception of a message (before <em>any</em> processing
@@ -107,11 +115,11 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
</desc>
</func>
<func>
- <name>accept_send(Ip, Port) -> boolean()</name>
+ <name>accept_send(Domain, Addr) -> boolean()</name>
<fsummary>Shall the message be sent</fsummary>
<type>
- <v>Ip = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
</type>
<desc>
<p>Called before the sending of a message (after <em>all</em> processing
@@ -122,11 +130,11 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
</desc>
</func>
<func>
- <name>accept_recv_pdu(Ip, Port, PduType) -> boolean()</name>
+ <name>accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name>
<fsummary>Shall the received pdu be accepted</fsummary>
<type>
- <v>Ip = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
<v>PduType = pdu_type()</v>
</type>
<desc>
@@ -144,7 +152,9 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
<type>
<v>Targets = targets()</v>
<v>targets() = [target()]</v>
- <v>target() = {ip_address(), port()}</v>
+ <v>target() = {Domain, Addr}</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
<v>PduType = pdu_type() > 0</v>
<v>Reply = boolean() | NewTargets</v>
<v>NewTargets = targets()</v>
@@ -155,7 +165,7 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
<p>For the message to be discarded all together, the function
<em>must</em> return <em>false</em>. </p>
<p>Note that it is possible for this function to filter out targets
- (but <em>not</em> add its own) by returning an updated
+ (but <em>not</em> to add its own) by returning an updated
<c>Targets</c> list (<c>NewTargets</c>). </p>
</desc>
</func>
diff --git a/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml b/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml
index aff71688b6..814f02a14c 100644
--- a/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml
+++ b/lib/snmp/doc/src/snmpa_notification_delivery_info_receiver.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -45,22 +45,30 @@
must export the following functions: </p>
<list type="bulleted">
<item>
- <p><seealso marker="#delivery_targets">delivery_targets/3</seealso></p>
+ <p><seealso marker="#delivery_targets/3">delivery_targets/3</seealso></p>
</item>
<item>
- <p><seealso marker="#delivery_info">delivery_info/4</seealso></p>
+ <p><seealso marker="#delivery_info/4">delivery_info/4</seealso></p>
</item>
</list>
<p>The semantics of them and their exact signatures are explained
below. </p>
+ <p>Legacy notification delivery information receiver modules
+ used a target argument on the form
+ <c>{IpAddr, PortNumber}</c> instead of
+ <c>{Domain, Addr}</c>, and if the SNMP Agent is run without
+ changing the configuration to use transport domains
+ the notification delivery information receiver will still get
+ the old arguments and work as before.</p>
+
</description>
<section>
<title>DATA TYPES</title>
- <code type="none"><![CDATA[
-address() = A 4-tuple
- ]]></code>
+ <p>See the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
+ <marker id="accept_recv"></marker>
<marker id="delivery_targets"></marker>
</section>
@@ -71,10 +79,8 @@ address() = A 4-tuple
<fsummary>Inform about target addresses</fsummary>
<type>
<v>Tag = term()</v>
- <v>Targets = [target()]</v>
- <v>target() = {Address, Port}</v>
- <v>Address = address()</v>
- <v>Port = integer()</v>
+ <v>Targets = [Target]</v>
+ <v>Target = {transportDomain(), transportAddressWithPort()</v>
<v>Extra = term()</v>
</type>
<desc>
@@ -94,10 +100,8 @@ address() = A 4-tuple
<fsummary>Inform about delivery result</fsummary>
<type>
<v>Tag = term()</v>
- <v>Target = target()</v>
- <v>target() = {Address, Port}</v>
- <v>Address = address()</v>
- <v>Port = integer()</v>
+ <v>Targets = [Target]</v>
+ <v>Target = {transportDomain(), transportAddressWithPort()</v>
<v>DeliveryResult = delivery_result()</v>
<v>delivery_result() = no_response | got_response</v>
<v>Extra = term()</v>
diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml
index dc8226bb87..ff90e49968 100644
--- a/lib/snmp/doc/src/snmpm.xml
+++ b/lib/snmp/doc/src/snmpm.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -69,6 +69,9 @@ sec_name() = string()
sec_level() = noAuthNoPriv | authNoPriv | authPriv
]]></code>
+ <p>See also the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
+
<marker id="monitor"></marker>
</section>
<funcs>
@@ -300,9 +303,9 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv
<p>The type of <c>Val</c> depends on <c>Item</c>: </p>
<code type="none"><![CDATA[
[mandatory] engine_id = string()
-[mandatory] address = ip_address()
-[optional] port = integer()
-[optional] tdomain = transportDomainUdpIpv4 | transportDomainUdpIpv6
+[mandatory] tadress = transportAddress() % Depends on tdomain
+[optional] port = inet:port_number()
+[optional] tdomain = transportDomain()
[optional] community = string()
[optional] timeout = integer() | snmp_timer()
[optional] max_message_size = integer()
@@ -313,7 +316,8 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv
]]></code>
<p>Note that if no <c>tdomain</c> is given, the default value,
<c>transportDomainUdpIpv4</c>, is used.</p>
- <p>Note that if no <c>port</c> is given, the default value is used.</p>
+ <p>Note that if no <c>port</c> is given and if <c>taddress</c> does not
+ contain a port number, the default value is used.</p>
<marker id="unregister_agent"></marker>
</desc>
diff --git a/lib/snmp/doc/src/snmpm_conf.xml b/lib/snmp/doc/src/snmpm_conf.xml
index 0cc9ff3379..8635fb705b 100644
--- a/lib/snmp/doc/src/snmpm_conf.xml
+++ b/lib/snmp/doc/src/snmpm_conf.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2013</year>
+ <year>2006</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -195,14 +195,14 @@
</desc>
</func>
<func>
- <name>agents_entry(UserId, TargetName, Comm, Ip, Port, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel) -> agents_entry()</name>
+ <name>agents_entry(UserId, TargetName, Comm, Domain, Addr, EngineID, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel) -> agents_entry()</name>
<fsummary>Create an agents entry</fsummary>
<type>
<v>UserId = term()</v>
<v>TargetName = string()</v>
<v>Comm = string()</v>
- <v>Ip = string()</v>
- <v>Port = integer()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddress()</v>
<v>EngineID = string()</v>
<v>Timeout = integer()</v>
<v>MaxMessageSize = integer()</v>
diff --git a/lib/snmp/doc/src/snmpm_mpd.xml b/lib/snmp/doc/src/snmpm_mpd.xml
index ad72fd7bc0..c23b2b6833 100644
--- a/lib/snmp/doc/src/snmpm_mpd.xml
+++ b/lib/snmp/doc/src/snmpm_mpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -39,7 +39,12 @@
It is supposed to be used from a Network Interface process
(<seealso marker="snmp_manager_netif">Definition of Manager Net if</seealso>).
</p>
+
+ <p>Legacy API function <c>process_msg/7</c> that has got separate
+ <c>IpAddr</c> and <c>PortNumber</c> arguments still works as before
+ for backwards compatibility reasons.</p>
</description>
+
<funcs>
<func>
<name>init_mpd(Vsns) -> mpd_state()</name>
@@ -58,13 +63,12 @@
</desc>
</func>
<func>
- <name>process_msg(Msg, TDomain, Addr, Port, State, NoteStore, Logger) -> {ok, Vsn, Pdu, PduMS, MsgData} | {discarded, Reason}</name>
+ <name>process_msg(Msg, Domain, Addr, State, NoteStore, Logger) -> {ok, Vsn, Pdu, PduMS, MsgData} | {discarded, Reason}</name>
<fsummary>Process a message received from the network</fsummary>
<type>
<v>Msg = binary()</v>
- <v>TDomain = snmpUDPDomain</v>
- <v>Addr = {integer(), integer(), integer(), integer()}</v>
- <v>Port = integer()</v>
+ <v>Domain = transportDomainUdpIpv4 | transportDomainUdpIpv6</v>
+ <v>Addr = {<seealso marker="kernel:inet#type-ip_address">inet:ip_address(), inet:port_number()</seealso>} </v>
<v>State = mpd_state()</v>
<v>NoteStore = pid()</v>
<v>Logger = function()</v>
diff --git a/lib/snmp/doc/src/snmpm_network_interface.xml b/lib/snmp/doc/src/snmpm_network_interface.xml
index 6cf7bd6ed7..bea6b46dc7 100644
--- a/lib/snmp/doc/src/snmpm_network_interface.xml
+++ b/lib/snmp/doc/src/snmpm_network_interface.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -69,6 +69,10 @@
<p>The semantics of them and their exact signatures are explained
below. </p>
+ <p>Legacy API function <c>send_pdu/7</c> that has got separate
+ <c>IpAddr</c> and <c>PortNumber</c> arguments still works as before
+ for backwards compatibility reasons.</p>
+
<marker id="start_link"></marker>
</description>
@@ -103,15 +107,15 @@
</func>
<func>
- <name>send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo) -> void()</name>
+ <name>send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo) -> void()</name>
<fsummary>Request the network interface process to send this pdu</fsummary>
<type>
<v>Pid = pid()</v>
<v>Pdu = pdu()</v>
<v>Vsn = 'version-1' | 'version-2' | 'version-3'</v>
<v>MsgData = term()</v>
- <v>Addr = address()</v>
- <v>Port = integer()</v>
+ <v>Domain = transportDomainUdpIpv4 | transportDomainUdpIpv6</v>
+ <v>Addr = {<seealso marker="kernel:inet#type-ip_address">inet:ip_address(), inet:port_number()</seealso>} </v>
<v>ExtraInfo = term()</v>
</type>
<desc>
diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
index f0526269b3..1ef4f29c0f 100644
--- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml
+++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2007</year><year>2013</year>
+ <year>2007</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -76,6 +76,12 @@
The default filter accepts all messages.</p>
<p>A network interface filter can e.g. be used during testing or for load
regulation. </p>
+ <p>Legacy network interface filter modules used arguments on the form
+ <c>(IpAddr, PortNumber,...)</c> instead of
+ <c>(Domain, Addr, ...)</c>, and if the SNMP manager is run without
+ changing the configuration to use transport domains
+ the network interface filter will still get
+ the old arguments and work as before.</p>
</description>
<section>
@@ -86,16 +92,18 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
'set-request' | trap | 'get-bulk-request' | 'inform-request' |
report | trappdu
</code>
+ <p>See also the <seealso marker="snmpa_conf#types">
+ data types in <c>snmpa_conf</c></seealso>.</p>
<marker id="accept_recv"></marker>
</section>
<funcs>
<func>
- <name>accept_recv(Addr, Port) -> boolean()</name>
+ <name>accept_recv(Domain, Addr) -> boolean()</name>
<fsummary>Shall the received message be accepted</fsummary>
<type>
- <v>Addr = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
</type>
<desc>
<p>Called at the reception of a message (before <em>any</em> processing
@@ -107,11 +115,11 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
</func>
<func>
- <name>accept_send(Addr, Port) -> boolean()</name>
+ <name>accept_send(Domain, Addr) -> boolean()</name>
<fsummary>Shall the message be sent</fsummary>
<type>
- <v>Addr = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
</type>
<desc>
<p>Called before the sending of a message (after <em>all</em> processing
@@ -123,11 +131,11 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
</func>
<func>
- <name>accept_recv_pdu(Addr, Port, PduType) -> boolean()</name>
+ <name>accept_recv_pdu(Domain, Addr, PduType) -> boolean()</name>
<fsummary>Shall the received pdu be accepted</fsummary>
<type>
- <v>Addr = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
<v>PduType = pdu_type()</v>
</type>
<desc>
@@ -141,11 +149,11 @@ pdu_type() = 'get-request' | 'get-next-request' | 'get-response' |
</func>
<func>
- <name>accept_send_pdu(Addr, Port, PduType) -> boolean()</name>
+ <name>accept_send_pdu(Domain, Addr, PduType) -> boolean()</name>
<fsummary>Shall the pdu be sent</fsummary>
<type>
- <v>Addr = ip_address()</v>
- <v>Port = port()</v>
+ <v>Domain = transportDomain()</v>
+ <v>Addr = transportAddressWithPort()</v>
<v>PduType = pdu_type() > 0</v>
</type>
<desc>
diff --git a/lib/snmp/doc/src/snmpm_user.xml b/lib/snmp/doc/src/snmpm_user.xml
index 6f412d90f8..a4492839cd 100644
--- a/lib/snmp/doc/src/snmpm_user.xml
+++ b/lib/snmp/doc/src/snmpm_user.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2004</year><year>2013</year>
+ <year>2004</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -63,10 +63,15 @@
<p>The semantics of them and their exact signatures are explained
below. </p>
<p>Some of the function has no defined return value (<c>void()</c>),
- they can ofcourse return anythyng. But the functions that do have
+ they can of course return anything. But the functions that do have
specified return value(s) <em>must</em> adhere to this. None of the
functions can use exit of throw to return. </p>
+ <p>If the manager is not configured to use any particular
+ transport domain, the behaviour <c>handle_agent/4</c>
+ will for backwards copmpatibility reasons be called with the old
+ <c>IpAddr</c> and <c>PortNumber</c> arguments</p>
+
<marker id="types"></marker>
</description>
@@ -116,11 +121,11 @@ snmp_v1_trap_info() :: {Enteprise :: snmp:oid(),
</func>
<func>
- <name>handle_agent(Addr, Port, Type, SnmpInfo, UserData) -> Reply</name>
+ <name>handle_agent(Domain, Addr, Type, SnmpInfo, UserData) -> Reply</name>
<fsummary>Handle agent</fsummary>
<type>
- <v>Addr = ip_address()</v>
- <v>Port = integer()</v>
+ <v>Domain = transportDomainUdpIpv4 | transportDomainUdpIpv6</v>
+ <v>Addr = {<seealso marker="kernel:inet#type-ip_address">inet:ip_address(), inet:port_number()</seealso>} </v>
<v>Type = pdu | trap | report | inform</v>
<v>SnmpInfo = SnmpPduInfo | SnmpTrapInfo | SnmpReportInfo | SnmpInformInfo</v>
<v>SnmpPduInfo = snmp_gen_info()</v>
diff --git a/lib/snmp/src/agent/snmp_framework_mib.erl b/lib/snmp/src/agent/snmp_framework_mib.erl
index 9d3f7ef5e7..6ff9224d34 100644
--- a/lib/snmp/src/agent/snmp_framework_mib.erl
+++ b/lib/snmp/src/agent/snmp_framework_mib.erl
@@ -207,22 +207,28 @@ check_agent({intAgentIpAddress = Tag, Ip} = Entry, {Domain, Port} = State) ->
[{Tag, FixedIp},
{intAgentTransports, [{Domain, {FixedIp, Port}}]}]
end, State};
-check_agent({intAgentTransports = Tag, Transports}, {_, Port} = State) ->
+check_agent({intAgentTransports = Tag, Transports}, {_, Port} = State)
+ when is_list(Transports) ->
CheckedTransports =
- [case
- case Port of
- undefined ->
- snmp_conf:check_address(Domain, Address);
- _ ->
- snmp_conf:check_address(Domain, Address, Port)
- end
- of
- ok ->
- Transport;
- {ok, FixedAddress} ->
- {Domain, FixedAddress}
+ [case Transport of
+ {Domain, Address} ->
+ case
+ case Port of
+ undefined ->
+ snmp_conf:check_address(Domain, Address);
+ _ ->
+ snmp_conf:check_address(Domain, Address, Port)
+ end
+ of
+ ok ->
+ Transport;
+ {ok, FixedAddress} ->
+ {Domain, FixedAddress}
+ end;
+ _ ->
+ error({bad_transport, Transport})
end
- || {Domain, Address} = Transport <- Transports],
+ || Transport <- Transports],
{{ok, {Tag, CheckedTransports}}, State};
check_agent(Entry, State) ->
{check_agent(Entry), State}.
diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl
index e916f17d6a..ef9503cda8 100644
--- a/lib/snmp/src/agent/snmp_target_mib.erl
+++ b/lib/snmp/src/agent/snmp_target_mib.erl
@@ -182,6 +182,10 @@ check_target_addr(
Name, Domain, Address, Timeout, RetryCount, TagList, Params,
EngineId, TMask, MMS);
check_target_addr(
+ {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
+ _EngineId, _TMask, _MMS}) -> % Arity 10
+ error({bad_address, {Domain, Address}});
+check_target_addr(
{Name, Domain, Address, Timeout, RetryCount, TagList, Params,
EngineId}) % Arity 8
when is_atom(Domain) ->
@@ -197,6 +201,10 @@ check_target_addr(
check_target_addr(
Name, Domain, Address, Timeout, RetryCount, TagList, Params,
EngineId);
+check_target_addr(
+ {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
+ _EngineId}) ->% Arity 8
+ error({bad_address, {Domain, Address}});
%% Use dummy engine id if the old style is found
check_target_addr(
{Name, Domain, Address, Timeout, RetryCount, TagList, Params}) % Arity 7
@@ -210,6 +218,9 @@ check_target_addr(
Address = {Ip, Udp},
check_target_addr(
Name, Domain, Address, Timeout, RetryCount, TagList, Params);
+check_target_addr(
+ {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params}) -> % Arity 7
+ error({bad_address, {Domain, Address}});
%% Use dummy engine id if the old style is found
check_target_addr(
{Name, Domain, Address, Timeout, RetryCount, TagList, Params,
@@ -225,6 +236,10 @@ check_target_addr(
Address = {Ip, Udp},
check_target_addr(
Name, Domain, Address, Timeout, RetryCount, TagList, Params, TMask, MMS);
+check_target_addr(
+ {_Name, Domain, Address, _Timeout, _RetryCount, _TagList, _Params,
+ _TMask, _MMS}) -> % Arity 9
+ error({bad_address, {Domain, Address}});
check_target_addr(X) ->
error({invalid_target_addr, X}).
@@ -291,7 +306,7 @@ check_engine_id(EngineId) ->
snmp_conf:check_string(EngineId).
check_address(Domain, Address) ->
- case snmp_conf:check_address(Domain, Address) of
+ case snmp_conf:check_address(Domain, Address, 162) of
ok ->
Address;
{ok, NAddress} ->
@@ -301,7 +316,11 @@ check_address(Domain, Address) ->
check_mask(_Domain, [] = Mask) ->
Mask;
check_mask(Domain, Mask) ->
- try check_address(Domain, Mask)
+ try snmp_conf:check_address(Domain, Mask) of
+ ok ->
+ Mask;
+ {ok, NMask} ->
+ NMask
catch
{error, {bad_address, Info}} ->
error({bad_mask, Info})
@@ -365,16 +384,21 @@ table_del_row(Tab, Key) ->
snmpa_mib_lib:table_del_row(db(Tab), Key).
-add_addr(Name, Ip, Port, Timeout, Retry, TagList,
- Params, EngineId, TMask, MMS) ->
- Domain = default_domain(),
- add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList,
- Params, EngineId, TMask, MMS).
-
-add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList,
- Params, EngineId, TMask, MMS) ->
- Addr = {Name, Domain, Ip, Port, Timeout, Retry, TagList,
- Params, EngineId, TMask, MMS},
+add_addr(
+ Name, Domain_or_Ip, Addr_or_Port, Timeout, Retry, TagList, Params,
+ EngineId, TMask, MMS) ->
+ add_addr(
+ {Name, Domain_or_Ip, Addr_or_Port, Timeout, Retry, TagList, Params,
+ EngineId, TMask, MMS}).
+%%
+add_addr(
+ Name, Domain, Ip, Port, Timeout, Retry, TagList, Params,
+ EngineId, TMask, MMS) ->
+ add_addr(
+ {Name, Domain, Ip, Port, Timeout, Retry, TagList, Params,
+ EngineId, TMask, MMS}).
+%%
+add_addr(Addr) ->
case (catch check_target_addr(Addr)) of
{ok, Row} ->
Key = element(1, Row),
diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl
index b4d32dc928..534d0e447b 100644
--- a/lib/snmp/src/agent/snmpa_conf.erl
+++ b/lib/snmp/src/agent/snmpa_conf.erl
@@ -47,7 +47,7 @@
read_standard_config/1,
%% target_addr.conf
- target_addr_entry/5, target_addr_entry/6,
+ target_addr_entry/5, target_addr_entry/6, target_addr_entry/7,
target_addr_entry/8, target_addr_entry/10, target_addr_entry/11,
write_target_addr_config/2, write_target_addr_config/3,
append_target_addr_config/2,
@@ -386,6 +386,12 @@ target_addr_entry(
target_addr_entry(Name, Ip, TagList, ParamsName, EngineId, []).
target_addr_entry(
+ Name, Domain, Addr, TagList,
+ ParamsName, EngineId) when is_atom(Domain) ->
+ target_addr_entry(
+ Name, Domain, Addr, TagList,
+ ParamsName, EngineId, []);
+target_addr_entry(
Name, Ip, TagList, ParamsName,
EngineId, TMask) ->
target_addr_entry(
@@ -394,6 +400,13 @@ target_addr_entry(
target_addr_entry(
Name, Domain_or_Ip, Addr_or_Port, TagList,
+ ParamsName, EngineId, TMask) ->
+ target_addr_entry(
+ Name, Domain_or_Ip, Addr_or_Port, TagList,
+ ParamsName, EngineId, TMask, 2048).
+
+target_addr_entry(
+ Name, Domain_or_Ip, Addr_or_Port, TagList,
ParamsName, EngineId, TMask, MaxMessageSize) ->
target_addr_entry(
Name, Domain_or_Ip, Addr_or_Port, 1500, 3, TagList,
@@ -475,6 +488,16 @@ write_target_addr_conf(Fd, Conf) ->
do_write_target_addr_conf(
Fd,
+ {Name, Domain, Address, Timeout, RetryCount, TagList,
+ ParamsName, EngineId, TMask, MaxMessageSize})
+ when is_atom(Domain) ->
+ io:format(
+ Fd,
+ "{\"~s\", ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n",
+ [Name, Domain, Address, Timeout, RetryCount, TagList,
+ ParamsName, EngineId, TMask, MaxMessageSize]);
+do_write_target_addr_conf(
+ Fd,
{Name, Ip, Udp, Timeout, RetryCount, TagList,
ParamsName, EngineId, TMask, MaxMessageSize})
when is_integer(Udp) ->
@@ -485,15 +508,10 @@ do_write_target_addr_conf(
{Name, Domain, Address, Timeout, RetryCount, TagList,
ParamsName, EngineId, TMask, MaxMessageSize});
do_write_target_addr_conf(
- Fd,
- {Name, Domain, Address, Timeout, RetryCount, TagList,
- ParamsName, EngineId, TMask, MaxMessageSize})
- when is_atom(Domain) ->
- io:format(
- Fd,
- "{\"~s\", ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n",
- [Name, Domain, Address, Timeout, RetryCount, TagList,
- ParamsName, EngineId, TMask, MaxMessageSize]);
+ _Fd,
+ {_Name, Domain, Address, _Timeout, _RetryCount, _TagList,
+ _ParamsName, _EngineId, _TMask, _MaxMessageSize}) ->
+ error({bad_address, {Domain, Address}});
do_write_target_addr_conf(
Fd,
{Name, Domain, Ip, Udp, Timeout, RetryCount, TagList,
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index ae79e3c1d1..1cc1a17b1d 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -28,6 +28,7 @@
%% {update, snmpa_local_db, soft, soft_purge, soft_purge, []}
%% {add_module, snmpm_net_if_mt}
[
+ {"5.0", [{restart_application, snmp}]},
{"4.25.1", [{restart_application, snmp}]},
{"4.25.0.1", [{restart_application, snmp}]},
{"4.25", [{restart_application, snmp}]},
@@ -42,6 +43,7 @@
%% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}}
[
+ {"5.0", [{restart_application, snmp}]},
{"4.25.1", [{restart_application, snmp}]},
{"4.25.0.1", [{restart_application, snmp}]},
{"4.25", [{restart_application, snmp}]},
diff --git a/lib/snmp/src/manager/depend.mk b/lib/snmp/src/manager/depend.mk
index 2e7783c8ed..60f61b0d3b 100644
--- a/lib/snmp/src/manager/depend.mk
+++ b/lib/snmp/src/manager/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2012. All Rights Reserved.
+# Copyright Ericsson AB 2004-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
@@ -50,10 +50,11 @@ $(EBIN)/snmpm_net_if.$(EMULATOR): \
snmpm_network_interface.erl
$(EBIN)/snmpm_net_if_mt.$(EMULATOR): \
+ snmpm_net_if_mt.erl \
../../include/snmp_types.hrl \
../misc/snmp_debug.hrl \
../misc/snmp_verbosity.hrl \
- snmpm_net_if_mt.erl \
+ snmpm_net_if.erl \
snmpm_network_interface.erl
$(EBIN)/snmpm_server.$(EMULATOR): \
diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl
index 888f19aec6..087ef6c6ea 100644
--- a/lib/snmp/src/manager/snmpm_conf.erl
+++ b/lib/snmp/src/manager/snmpm_conf.erl
@@ -114,6 +114,7 @@ do_write_manager_conf(Fd, {Tag, Val})
when Tag =:= domain;
Tag =:= address;
Tag =:= port;
+ Tag =:= transports;
Tag =:= max_message_size ->
io:format(Fd, "{~w, ~w}.~n", [Tag, Val]);
do_write_manager_conf(Fd, {Tag, Val})
@@ -199,9 +200,10 @@ do_write_users_conf(_Fd, Crap) ->
%% ------ agents.conf ------
%%
-agents_entry(UserId, TargetName, Comm, Ip, Port, EngineID, Timeout,
- MaxMessageSize, Version, SecModel, SecName, SecLevel) ->
- {UserId, TargetName, Comm, Ip, Port, EngineID, Timeout,
+agents_entry(
+ UserId, TargetName, Comm, Domain_or_Ip, Addr_or_Port, EngineID, Timeout,
+ MaxMessageSize, Version, SecModel, SecName, SecLevel) ->
+ {UserId, TargetName, Comm, Domain_or_Ip, Addr_or_Port, EngineID, Timeout,
MaxMessageSize, Version, SecModel, SecName, SecLevel}.
diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl
index 013fefa4e2..5cab81baf6 100644
--- a/lib/snmp/src/manager/snmpm_config.erl
+++ b/lib/snmp/src/manager/snmpm_config.erl
@@ -442,8 +442,31 @@ agent_info(TargetName, all) ->
All ->
{ok, [{Item, Val} || {{_, Item}, Val} <- All]}
end;
+%% Begin backwards compatibility
+agent_info(TargetName, address) ->
+ case agent_info({TargetName, taddress}) of
+ {ok, Val} ->
+ {Addr, _} = Val,
+ {ok, Addr};
+ _ ->
+ %% This should be redundant since 'taddress' should exist
+ agent_info({TargetName, address})
+ end;
+agent_info(TargetName, port) ->
+ case agent_info({TargetName, taddress}) of
+ {ok, Val} ->
+ {_, Port} = Val,
+ {ok, Port};
+ _ ->
+ %% This should be redundant since 'taddress' should exist
+ agent_info({TargetName, port})
+ end;
+%% End backwards compatibility
agent_info(TargetName, Item) ->
- case ets:lookup(snmpm_agent_table, {TargetName, Item}) of
+ agent_info({TargetName, Item}).
+
+agent_info(Key) ->
+ case ets:lookup(snmpm_agent_table, Key) of
[{_, Val}] ->
{ok, Val};
[] ->
@@ -456,29 +479,29 @@ agent_info(Domain, Address, Item) when is_atom(Domain) ->
do_agent_info(Domain, NAddress, Item)
catch
_Thrown ->
- p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
- " ~p",
- [Domain, Address, Item, _Thrown, erlang:get_stacktrace()]),
+ %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
+ %% " ~p",
+ %% [Domain, Address, Item, _Thrown, erlang:get_stacktrace()]),
{error, not_found}
end;
-agent_info(Ip, Port, Item) ->
- p(?MODULE_STRING":agent_info(~p, ~p, ~p) entry~n",
- [Ip, Port, Item]),
+agent_info(Ip, Port, Item) when is_integer(Port) ->
+ %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) entry~n",
+ %% [Ip, Port, Item]),
Domain = default_transport_domain(),
try fix_address(Domain, {Ip, Port}) of
Address ->
do_agent_info(Domain, Address, Item)
catch
_Thrown ->
- p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
- " ~p",
- [Ip, Port, Item, _Thrown, erlang:get_stacktrace()]),
+ %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n"
+ %% " ~p",
+ %% [Ip, Port, Item, _Thrown, erlang:get_stacktrace()]),
{error, not_found}
end.
do_agent_info(Domain, Address, target_name = Item) ->
- p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
- [Domain, Address, Item]),
+ %% p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
+ %% [Domain, Address, Item]),
case ets:lookup(snmpm_agent_table, {Domain, Address, Item}) of
[{_, Val}] ->
{ok, Val};
@@ -486,8 +509,8 @@ do_agent_info(Domain, Address, target_name = Item) ->
{error, not_found}
end;
do_agent_info(Domain, Address, Item) ->
- p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
- [Domain, Address, Item]),
+ %% p(?MODULE_STRING":do_agent_info(~p, ~p, ~p) entry~n",
+ %% [Domain, Address, Item]),
case do_agent_info(Domain, Address, target_name) of
{ok, TargetName} ->
agent_info(TargetName, Item);
@@ -1287,36 +1310,6 @@ verify_options(Opts, Mandatory) ->
verify_mandatory_options(Opts, Mandatory),
verify_options(Opts).
-%% mandatory() -> [mand()]
-%% mand() -> atom() | {atom, [atom()]}
-verify_mandatory_options(_Opts, []) ->
- ok;
-verify_mandatory_options(Opts, [Mand|Mands]) ->
- verify_mandatory_option(Opts, Mand),
- verify_mandatory_options(Opts, Mands).
-
-verify_mandatory_option(Opts, {Mand, MandSubOpts}) ->
- ?d("verify_mandatory_option -> entry with"
- "~n Mand: ~p"
- "~n MandSubObjs: ~p", [Mand, MandSubOpts]),
- case lists:keysearch(Mand, 1, Opts) of
- {value, {Mand, SubOpts}} ->
- verify_mandatory_options(SubOpts, MandSubOpts);
- false ->
- ?d("missing mandatory option: ~w [~p]", [Mand, MandSubOpts]),
- error({missing_mandatory, Mand, MandSubOpts})
- end;
-verify_mandatory_option(Opts, Mand) ->
- ?d("verify_mandatory_option -> entry with"
- "~n Mand: ~p", [Mand]),
- case lists:keymember(Mand, 1, Opts) of
- true ->
- ok;
- false ->
- ?d("missing mandatory option: ~w", [Mand]),
- error({missing_mandatory, Mand})
- end.
-
verify_options([]) ->
?d("verify_options -> done", []),
ok;
@@ -1632,7 +1625,38 @@ verify_verbosity(Verbosity) ->
_ ->
error({invalid_verbosity, Verbosity})
end.
+
+%% mandatory() -> [mand()]
+%% mand() -> atom() | {atom, [atom()]}
+verify_mandatory_options(_Opts, []) ->
+ ok;
+verify_mandatory_options(Opts, [Mand|Mands]) ->
+ verify_mandatory_option(Opts, Mand),
+ verify_mandatory_options(Opts, Mands).
+
+verify_mandatory_option(Opts, {Mand, MandSubOpts}) ->
+ ?d("verify_mandatory_option -> entry with"
+ "~n Mand: ~p"
+ "~n MandSubObjs: ~p", [Mand, MandSubOpts]),
+ case lists:keysearch(Mand, 1, Opts) of
+ {value, {Mand, SubOpts}} ->
+ verify_mandatory_options(SubOpts, MandSubOpts);
+ false ->
+ ?d("missing mandatory option: ~w [~p]", [Mand, MandSubOpts]),
+ error({missing_mandatory, Mand, MandSubOpts})
+ end;
+verify_mandatory_option(Opts, Mand) ->
+ ?d("verify_mandatory_option -> entry with"
+ "~n Mand: ~p", [Mand]),
+ case lists:keymember(Mand, 1, Opts) of
+ true ->
+ ok;
+ false ->
+ ?d("missing mandatory option: ~w", [Mand]),
+ error({missing_mandatory, Mand})
+ end.
+
%% ------------------------------------------------------------------------
init_manager_config([]) ->
@@ -1654,69 +1678,10 @@ init_agent_default() ->
{version, v2}, % MPModel
{sec_model, v2c}, % SecModel
{sec_name, "initial"}, % SecName
- {sec_level, noAuthPriv}, % SecLevel
+ {sec_level, noAuthNoPriv}, % SecLevel
{community, "all-rights"}], % Community
do_update_agent_info(default_agent, AgentDefaultConfig).
-%% %% Port
-%% init_agent_default(port, ?DEFAULT_AGENT_PORT),
-
-%% %% Timeout
-%% init_agent_default(timeout, 10000),
-
-%% %% Max message (packet) size
-%% init_agent_default(max_message_size, 484),
-
-%% %% MPModel
-%% init_agent_default(version, v2),
-
-%% %% SecModel
-%% init_agent_default(sec_model, v2c),
-
-%% %% SecName
-%% init_agent_default(sec_name, "initial"),
-
-%% %% SecLevel
-%% init_agent_default(sec_level, noAuthNoPriv),
-
-%% %% Community
-%% init_agent_default(community, "all-rights"),
-%% ok.
-
-
-%% init_agent_default(Item, Val) when Item =/= user_id ->
-%% case do_update_agent_info(default_agent, Item, Val) of
-%% ok ->
-%% ok;
-%% {error, Reason} ->
-%% error(Reason)
-%% end.
-
-%% read_agents_config_file(Dir) ->
-%% Verify = fun check_agent_config2/1,
-%% case read_file(Dir, "agents.conf", Verify, []) of
-%% {ok, Conf} ->
-%% Conf;
-%% Error ->
-%% ?vlog("agent config error: ~p", [Error]),
-%% throw(Error)
-%% end.
-
-%% check_agent_config2(Agent) ->
-%% case (catch check_agent_config(Agent)) of
-%% {ok, {UserId, TargetName, Conf, Version}} ->
-%% {ok, Vsns} = system_info(versions),
-%% case lists:member(Version, Vsns) of
-%% true ->
-%% {ok, {UserId, TargetName, Conf}};
-%% false ->
-%% error({version_not_supported_by_manager,
-%% Version, Vsns})
-%% end;
-%% Err ->
-%% throw(Err)
-%% end.
-
read_agents_config_file(Dir) ->
Order = fun snmp_conf:no_order/2,
Check = fun check_agent_config/2,
@@ -1739,21 +1704,35 @@ check_agent_config(Agent, State) ->
%% For backward compatibility
check_agent_config(
+ {UserId, TargetName, Community, Domain, Addr,
+ EngineId, Timeout, MaxMessageSize,
+ Version, SecModel, SecName, SecLevel}) when is_atom(Domain) ->
+ check_agent_config(
+ UserId, TargetName, Community, Domain, Addr,
+ EngineId, Timeout, MaxMessageSize,
+ Version, SecModel, SecName, SecLevel);
+check_agent_config(
{UserId, TargetName, Community, Ip, Port,
EngineId, Timeout, MaxMessageSize,
- Version, SecModel, SecName, SecLevel}) ->
+ Version, SecModel, SecName, SecLevel}) when is_integer(Port) ->
Domain = default_transport_domain(),
- Addr = fix_address(Domain, {Ip, Port}),
+ Addr = {Ip, Port},
check_agent_config(
UserId, TargetName, Community, Domain, Addr,
EngineId, Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel);
check_agent_config(
+ {_UserId, _TargetName, _Community, Domain, Addr,
+ _EngineId, _Timeout, _MaxMessageSize,
+ _Version, _SecModel, _SecName, _SecLevel}) ->
+ error({bad_address, {Domain, Addr}});
+check_agent_config(
{UserId, TargetName, Community, Domain, Ip, Port,
EngineId, Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel}) ->
+ Addr = {Ip, Port},
check_agent_config(
- UserId, TargetName, Community, Domain, {Ip, Port},
+ UserId, TargetName, Community, Domain, Addr,
EngineId, Timeout, MaxMessageSize,
Version, SecModel, SecName, SecLevel);
check_agent_config(Agent) ->
@@ -1776,7 +1755,7 @@ check_agent_config(
Conf =
[{reg_type, target_name},
{tdomain, Domain},
- {taddress, Addr},
+ {taddress, fix_address(Domain, Addr)},
{community, Comm},
{engine_id, EngineId},
{timeout, Timeout},
@@ -1860,7 +1839,11 @@ verify_agent_config(
{TD, VerifiedConf};
_ ->
%% Insert tdomain since it is missing
- TD = default_transport_domain(),
+ %% Note: not default_transport_domain() since
+ %% taddress is the new format hence the application
+ %% should be tdomain aware and therefore addresses
+ %% on the Domain, Addr format should be used and understood.
+ TD = transportDomainUdpIpv4,
{TD, [{tdomain, TD}|VerifiedConf]}
end,
case snmp_conf:check_address(TDomain, Address, 0) of
@@ -1946,16 +1929,6 @@ verify_agent_entry(Item, _) ->
-%% read_users_config_file(Dir) ->
-%% Verify = fun check_user_config/1,
-%% case read_file(Dir, "users.conf", Verify, []) of
-%% {ok, Conf} ->
-%% Conf;
-%% Error ->
-%% ?vlog("failure reading users config file: ~n ~p", [Error]),
-%% throw(Error)
-%% end.
-
read_users_config_file(Dir) ->
Order = fun snmp_conf:no_order/2,
Check = fun (User, State) -> {check_user_config(User), State} end,
@@ -2074,14 +2047,6 @@ verify_default_agent_config(Conf) ->
error({bad_default_agent_config, Error})
end.
-%% read_usm_config_file(Dir) ->
-%% Verify = fun check_usm_user_config/1,
-%% case read_file(Dir, "usm.conf", Verify, []) of
-%% {ok, Conf} ->
-%% Conf;
-%% Error ->
-%% throw(Error)
-%% end.
read_usm_config_file(Dir) ->
Order = fun snmp_conf:no_order/2,
@@ -2268,24 +2233,6 @@ is_crypto_supported(Func) ->
snmp_misc:is_crypto_supported(Func).
-%% read_manager_config_file(Dir) ->
-%% Verify = fun check_manager_config/1,
-%% case read_file(Dir, "manager.conf", Verify) of
-%% {ok, Conf} ->
-%% ?d("read_manager_config_file -> ok: "
-%% "~n Conf: ~p", [Conf]),
-%% %% If the address is not specified, then we assume
-%% %% it should be the local host.
-%% %% If the address is not possible to determine
-%% %% that way, then we give up...
-%% verify_mandatory(Conf, [port,engine_id,max_message_size]),
-%% ensure_config(default_manager_config(), Conf);
-%% %% check_mandatory_manager_config(Conf),
-%% %% ensure_manager_config(Conf);
-%% Error ->
-%% throw(Error)
-%% end.
-
read_manager_config_file(Dir) ->
Order = fun order_manager_config/2,
Check = fun check_manager_config/2,
@@ -2296,13 +2243,15 @@ read_manager_config_file(Dir) ->
%% it should be the local host.
%% If the address is not possible to determine
%% that way, then we give up...
- verify_mandatory(Conf, [port,engine_id,max_message_size]),
+ verify_someof(Conf, [port, transports]),
+ verify_mandatory(Conf, [engine_id, max_message_size]),
default_manager_config(Conf).
default_manager_config(Conf) ->
- %% Ensure address of right family
- case lists:keyfind(address, 1, Conf) of
+ %% Ensure valid transports entry
+ case lists:keyfind(transports, 1, Conf) of
false ->
+ {port, Port} = lists:keyfind(port, 1, Conf),
Domain =
case lists:keyfind(domain, 1, Conf) of
false ->
@@ -2311,55 +2260,77 @@ default_manager_config(Conf) ->
D
end,
Family = snmp_conf:tdomain_to_family(Domain),
- {ok, HostName} = inet:gethostname(),
- case inet:getaddr(HostName, Family) of
+ {ok, Hostname} = inet:gethostname(),
+ case inet:getaddr(Hostname, Family) of
{ok, Address} ->
- [{address, Address} | Conf];
+ lists:sort(
+ fun order_manager_config/2,
+ [{transports, [{Domain, {Address, Port}}]} | Conf]);
{error, _Reason} ->
?d("default_manager_config -> "
"failed getting ~w address for ~s:~n"
- " _Reason: ~p", [Family, HostName, _Reason]),
+ " _Reason: ~p", [Family, Hostname, _Reason]),
Conf
end;
_ ->
Conf
end.
-default_manager_config() ->
- {ok, HostName} = inet:gethostname(),
- case inet:getaddr(HostName, inet) of
- {ok, A} ->
- [{address, tuple_to_list(A)}];
- {error, _Reason} ->
- ?d("default_manager_config -> failed getting address: "
- "~n _Reason: ~p", [_Reason]),
- []
- end.
-
order_manager_config(EntryA, EntryB) ->
- snmp_conf:keyorder(1, EntryA, EntryB, [domain]).
-
-check_manager_config({domain, D}, _Domain) ->
- {snmp_conf:check_domain(D), D};
-check_manager_config({address = Tag, Ip}, D) ->
- Domain =
- case D of
- undefined ->
- default_transport_domain();
- _ ->
- D
- end,
+ snmp_conf:keyorder(1, EntryA, EntryB, [domain, port]).
+
+check_manager_config(Entry, undefined) ->
+ check_manager_config(Entry, {default_transport_domain(), undefined});
+check_manager_config({domain, Domain}, {_, Port}) ->
+ {snmp_conf:check_domain(Domain), {Domain, Port}};
+check_manager_config({port, Port}, {Domain, _}) ->
+ {ok = snmp_conf:check_port(Port), {Domain, Port}};
+check_manager_config({address, _}, {_, undefined}) ->
+ error({missing_mandatory, port});
+check_manager_config({address = Tag, Ip} = Entry, {Domain, Port} = State) ->
{case snmp_conf:check_ip(Domain, Ip) of
ok ->
- ok;
+ [Entry,
+ {transports, [{Domain, {Ip, Port}}]}];
{ok, FixedIp} ->
- {ok, {Tag, FixedIp}}
- end, Domain};
-check_manager_config(Entry, Domain) ->
- {check_manager_config(Entry), Domain}.
+ [{Tag, FixedIp},
+ {transports, [{Domain, {FixedIp, Port}}]}]
+ end, State};
+check_manager_config({transports = Tag, Transports}, {_, Port} = State)
+ when is_list(Transports) ->
+ CheckedTransports =
+ [case Transport of
+ {Domain, Address} ->
+ case
+ case Port of
+ undefined ->
+ snmp_conf:check_address(Domain, Address);
+ _ ->
+ snmp_conf:check_address(Domain, Address, Port)
+ end
+ of
+ ok ->
+ Transport;
+ {ok, FixedAddress} ->
+ {Domain, FixedAddress}
+ end;
+ _Domain when Port =:= undefined->
+ error({missing_mandatory, port});
+ Domain ->
+ Family = snmp_conf:tdomain_to_family(Domain),
+ {ok, Hostname} = inet:gethostname(),
+ case inet:getaddr(Hostname, Family) of
+ {ok, IpAddr} ->
+ {Domain, {IpAddr, Port}};
+ {error, _} ->
+ error({bad_address, {Domain, Hostname}})
+ end
+ end
+ || Transport <- Transports],
+ {{ok, {Tag, CheckedTransports}}, State};
+check_manager_config(Entry, State) ->
+ {check_manager_config(Entry), State}.
-check_manager_config({port, Port}) ->
- snmp_conf:check_port(Port);
check_manager_config({engine_id, EngineID}) ->
snmp_conf:check_string(EngineID);
check_manager_config({max_message_size, Max}) ->
@@ -2368,45 +2339,6 @@ check_manager_config(Conf) ->
error({unknown_config, Conf}).
-%% check_mandatory_manager_config(Conf) ->
-%% Mand = [port, engine_id, max_message_size],
-%% check_mandatory_manager_config(Mand, Conf).
-
-%% check_mandatory_manager_config([], _Conf) ->
-%% ok;
-%% check_mandatory_manager_config([Item|Mand], Conf) ->
-%% case lists:keysearch(Item, 1, Conf) of
-%% false ->
-%% error({missing_mandatory_manager_config, Item});
-%% _ ->
-%% check_mandatory_manager_config(Mand, Conf)
-%% end.
-
-
-%% ensure_manager_config(Confs) ->
-%% ensure_manager_config(Confs, default_manager_config()).
-
-%% ensure_manager_config(Confs, []) ->
-%% Confs;
-%% ensure_manager_config(Confs, [{Key,_} = DefKeyVal|Defs]) ->
-%% case lists:keysearch(Key, 1, Confs) of
-%% false ->
-%% ensure_manager_config([DefKeyVal|Confs], Defs);
-%% {value, _Conf} ->
-%% ensure_manager_config(Confs, Defs)
-%% end.
-
-% ensure_manager_config([], Defs, Confs) ->
-% Confs ++ Defs;
-% ensure_manager_config(Confs0, [{Key, DefVal}|Defs], Acc) ->
-% case lists:keysearch(Key, 1, Confs0) of
-% false ->
-% ensure_manager_config(Confs0, Defs, [{Key, DefVal}|Acc]);
-% {value, Conf} ->
-% Confs = lists:keydelete(Key, 1, Confs0),
-% ensure_manager_config(Confs, Defs, [Conf|Acc])
-% end.
-
read_file(Dir, FileName, Order, Check, Default) ->
try snmp_conf:read(filename:join(Dir, FileName), Order, Check)
catch
@@ -2424,87 +2356,6 @@ read_file(Dir, FileName, Order, Check) ->
erlang:raise(throw, Error, erlang:get_stacktrace())
end.
-
-
-
-
-
-%% read_file(Dir, FileName, Verify, Default) ->
-%% File = filename:join(Dir, FileName),
-%% case file:read_file_info(File) of
-%% {ok, _} ->
-%% read_file(File, Verify);
-%% {error, Reason} ->
-%% ?vlog("failed reading config from ~s: ~p", [FileName, Reason]),
-%% {ok, Default}
-%% end.
-
-%% read_file(Dir, FileName, Verify) ->
-%% File = filename:join(Dir, FileName),
-%% case file:read_file_info(File) of
-%% {ok, _} ->
-%% read_file(File, Verify);
-%% {error, Reason} ->
-%% error_msg("failed reading config from ~s: ~p", [FileName, Reason]),
-%% {error, {failed_reading, FileName, Reason}}
-%% end.
-
-%% read_file(File, Verify) ->
-%% Check = fun (Config, State) -> {Verify(Config), State} end,
-%% try snmp_conf:read(File, Check) of
-%% Conf ->
-%% ?vtrace("read_file -> read ok"
-%% "~n Conf: ~p", [Conf]),
-%% {ok, Conf}
-%% catch
-%% Error ->
-%% ?vtrace("read_file -> read failed:"
-%% "~n Error: ~p", [Error]),
-%% Error
-%% end.
-
-%% XXX remove
-
-%% read_file(Dir, FileName, Check, Default) ->
-%% File = filename:join(Dir, FileName),
-%% case file:read_file_info(File) of
-%% {ok, _} ->
-%% case (catch do_read(File, Check)) of
-%% {ok, Conf} ->
-%% {ok, Conf};
-%% Error ->
-%% ?vtrace("read_file -> read failed:"
-%% "~n Error: ~p", [Error]),
-%% Error
-%% end;
-%% {error, Reason} ->
-%% ?vlog("failed reading config from ~s: ~p", [FileName, Reason]),
-%% {ok, Default}
-%% end.
-
-%% read_file(Dir, FileName, Check) ->
-%% File = filename:join(Dir, FileName),
-%% case file:read_file_info(File) of
-%% {ok, _} ->
-%% case (catch do_read(File, Check)) of
-%% {ok, Conf} ->
-%% ?vtrace("read_file -> read ok"
-%% "~n Conf: ~p", [Conf]),
-%% {ok, Conf};
-%% Error ->
-%% ?vtrace("read_file -> read failed:"
-%% "~n Error: ~p", [Error]),
-%% Error
-%% end;
-%% {error, Reason} ->
-%% error_msg("failed reading config from ~s: ~p", [FileName, Reason]),
-%% {error, {failed_reading, FileName, Reason}}
-%% end.
-
-%% do_read(File, Check) ->
-%% {ok, snmp_conf:read(File, Check)}.
-
-
%%--------------------------------------------------------------------
%% Func: handle_call/3
%% Returns: {reply, Reply, State} |
@@ -3580,6 +3431,5 @@ error_msg(F, A) ->
%% p(F) ->
%% p(F, []).
-p(F, A) ->
- io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
-
+%% p(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE | A]).
diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl
index 860b0b83dd..cb72871177 100644
--- a/lib/snmp/src/manager/snmpm_net_if.erl
+++ b/lib/snmp/src/manager/snmpm_net_if.erl
@@ -17,7 +17,9 @@
%% %CopyrightEnd%
%%
+-ifndef(snmpm_net_if_mt).
-module(snmpm_net_if).
+-endif.
-behaviour(gen_server).
-behaviour(snmpm_network_interface).
@@ -59,8 +61,7 @@
{
server,
note_store,
- domain,
- sock,
+ transports = [],
mpd_state,
log,
irb = auto, % auto | {user, integer()}
@@ -68,6 +69,9 @@
filter
}).
+-record(transport,
+ {socket,
+ domain = snmpUDPDomain}).
-define(DEFAULT_FILTER_MODULE, snmpm_net_if_filter).
-define(DEFAULT_FILTER_OPTS, [{module, ?DEFAULT_FILTER_MODULE}]).
@@ -147,6 +151,64 @@ filter_reset(Pid) ->
%%%-------------------------------------------------------------------
+%%% Multi-thread manager
+%%%-------------------------------------------------------------------
+
+-ifdef(snmpm_net_if_mt).
+
+%% This function is called through the macro below to
+%% (in the not multithreaded case) avoid creating the
+%% Failer/4 fun, and to avoid calling the Worker through a fun
+%% (now it shall not be a fun, just a code snippet).
+
+worker(Worker, Failer, #state{log = Log} = State) ->
+ Verbosity = get(verbosity),
+ spawn_opt(
+ fun () ->
+ try
+ put(sname, mnifw),
+ put(verbosity, Verbosity),
+ NewState =
+ case do_reopen_log(Log) of
+ Log ->
+ State;
+ NewLog ->
+ State#state{log = NewLog}
+ end,
+ Worker(NewState)
+ of
+ Result ->
+ %% Winds up in handle_info {'DOWN', ...}
+ erlang:exit({net_if_worker, Result})
+ catch
+ Class:Reason ->
+ %% Winds up in handle_info {'DOWN', ...}
+ erlang:exit(
+ {net_if_worker, Failer,
+ Class, Reason, erlang:get_stacktrace()})
+ end
+ end,
+ [monitor]).
+-define(
+ worker(S, Worker, Failer, State),
+ begin
+ worker(
+ fun (S) -> begin Worker end end,
+ begin Failer end,
+ (State))
+ end).
+
+-else.
+
+-define(
+ worker(S, Worker, _Failer, State),
+ begin (S) = (State), begin Worker end end).
+
+-endif.
+
+
+
+%%%-------------------------------------------------------------------
%%% Callback functions from gen_server
%%%-------------------------------------------------------------------
@@ -161,12 +223,19 @@ init([Server, NoteStore]) ->
?d("init -> entry with"
"~n Server: ~p"
"~n NoteStore: ~p", [Server, NoteStore]),
- case (catch do_init(Server, NoteStore)) of
+ try do_init(Server, NoteStore)
+ catch
{error, Reason} ->
- {stop, Reason};
- {ok, State} ->
- {ok, State}
+ {stop, Reason}
end.
+
+-ifdef(snmpm_net_if_mt).
+%% This should really be protected, but it also needs to
+%% be writable for the worker processes, so...
+-define(inform_table_opts, [set, public, named_table, {keypos, 1}]).
+-else.
+-define(inform_table_opts, [set, protected, named_table, {keypos, 1}]).
+-endif.
do_init(Server, NoteStore) ->
process_flag(trap_exit, true),
@@ -176,18 +245,18 @@ do_init(Server, NoteStore) ->
process_flag(priority, Prio),
%% -- Create inform request table --
- ets:new(snmpm_inform_request_table,
- [set, protected, named_table, {keypos, 1}]),
+ ets:new(snmpm_inform_request_table, ?inform_table_opts),
%% -- Verbosity --
{ok, Verbosity} = snmpm_config:system_info(net_if_verbosity),
- put(sname,mnif),
- put(verbosity,Verbosity),
+ put(sname, mnif),
+ put(verbosity, Verbosity),
?vlog("starting", []),
%% -- MPD --
{ok, Vsns} = snmpm_config:system_info(versions),
MpdState = snmpm_mpd:init(Vsns),
+ ?vdebug("MpdState: ~w", [MpdState]),
%% -- Module dependent options --
{ok, Opts} = snmpm_config:system_info(net_if_options),
@@ -196,21 +265,6 @@ do_init(Server, NoteStore) ->
{ok, IRB} = snmpm_config:system_info(net_if_irb),
IrGcRef = irgc_start(IRB),
- %% -- Socket --
- SndBuf = get_opt(Opts, sndbuf, default),
- RecBuf = get_opt(Opts, recbuf, default),
- BindTo = get_opt(Opts, bind_to, false),
- NoReuse = get_opt(Opts, no_reuse, false),
- {ok, Port} = snmpm_config:system_info(port),
- Domain =
- case snmpm_config:system_info(domain) of
- {ok, D} ->
- D;
- _ ->
- snmpm_config:default_transport_domain()
- end,
- {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, Domain, BindTo, NoReuse),
-
%% Flow control --
FilterOpts = get_opt(Opts, filter, []),
FilterMod = create_filter(FilterOpts),
@@ -219,83 +273,104 @@ do_init(Server, NoteStore) ->
%% -- Audit trail log ---
{ok, ATL} = snmpm_config:system_info(audit_trail_log),
Log = do_init_log(ATL),
+ ?vdebug("Log: ~w", [Log]),
+
+ {ok, DomainAddresses} = snmpm_config:system_info(transports),
+ ?vdebug("DomainAddresses: ~w",[DomainAddresses]),
+ CommonSocketOpts = common_socket_opts(Opts),
+ BindTo = get_opt(Opts, bind_to, false),
+ case
+ [begin
+ {IpPort, SocketOpts} =
+ socket_params(Domain, Address, BindTo, CommonSocketOpts),
+ Socket = socket_open(IpPort, SocketOpts),
+ #transport{socket = Socket, domain = Domain}
+ end || {Domain, Address} <- DomainAddresses]
+ of
+ [] ->
+ ?vinfo("No transports configured: ~p", [DomainAddresses]),
+ throw({error, {no_transports,DomainAddresses}});
+ Transports ->
+ %% -- Initiate counters ---
+ init_counters(),
+
+ %% -- We are done ---
+ State = #state{
+ server = Server,
+ note_store = NoteStore,
+ mpd_state = MpdState,
+ transports = Transports,
+ log = Log,
+ irb = IRB,
+ irgc = IrGcRef,
+ filter = FilterMod},
+ ?vdebug("started", []),
+ {ok, State}
+ end.
- %% -- Initiate counters ---
- init_counters(),
-
- %% -- We are done ---
- State = #state{server = Server,
- note_store = NoteStore,
- mpd_state = MpdState,
- domain = Domain,
- sock = Sock,
- log = Log,
- irb = IRB,
- irgc = IrGcRef,
- filter = FilterMod},
- ?vdebug("started", []),
- {ok, State}.
-
-
-%% Open port
-do_open_port(Port, SendSz, RecvSz, Domain, BindTo, NoReuse) ->
- ?vtrace("do_open_port -> entry with~n"
- " Port: ~p~n"
- " SendSz: ~p~n"
- " RecvSz: ~p~n"
- " Domain: ~p~n"
- " BindTo: ~p~n"
- " NoReuse: ~p",
- [Port, SendSz, RecvSz, Domain, BindTo, NoReuse]),
- IpOpts1 = bind_to(BindTo),
- IpOpts2 = no_reuse(NoReuse),
- IpOpts3 = recbuf(RecvSz),
- IpOpts4 = sndbuf(SendSz),
- IpOpts =
- [binary,
- snmp_conf:tdomain_to_family(Domain) |
- IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4],
- OpenRes =
- case init:get_argument(snmpm_fd) of
- {ok, [[FdStr]]} ->
- Fd = list_to_integer(FdStr),
- gen_udp:open(0, [{fd, Fd}|IpOpts]);
- error ->
- gen_udp:open(Port, IpOpts)
- end,
- case OpenRes of
+socket_open(IpPort, SocketOpts) ->
+ ?vtrace("socket_open -> entry with~n"
+ " IpPort: ~p~n"
+ " SocketOpts: ~p", [IpPort, SocketOpts]),
+ case gen_udp:open(IpPort, SocketOpts) of
{error, _} = Error ->
throw(Error);
- OK ->
- OK
+ {ok, Socket} ->
+ Socket
end.
-bind_to(true) ->
- case snmpm_config:system_info(address) of
- {ok, Addr} when is_list(Addr) ->
- [{ip, list_to_tuple(Addr)}];
- {ok, Addr} ->
- [{ip, Addr}];
+socket_params(Domain, {IpAddr, IpPort}, BindTo, CommonSocketOpts) ->
+ Family = snmp_conf:tdomain_to_family(Domain),
+ SocketOpts =
+ case Family of
+ inet6 ->
+ [Family, {ipv6_v6only, true} | CommonSocketOpts];
+ Family ->
+ [Family | CommonSocketOpts]
+ end,
+ case Family of
+ inet ->
+ case init:get_argument(snmp_fd) of
+ {ok, [[FdStr]]} ->
+ Fd = list_to_integer(FdStr),
+ case BindTo of
+ true ->
+ {IpPort, [{ip, IpAddr}, {fd, Fd} | SocketOpts]};
+ _ ->
+ {0, [{fd, Fd} | SocketOpts]}
+ end;
+ error ->
+ {IpPort, [{ip, IpAddr} | SocketOpts]}
+ end;
_ ->
- []
- end;
-bind_to(_) ->
- [].
-
-no_reuse(false) ->
- [{reuseaddr, true}];
-no_reuse(_) ->
- [].
-
-recbuf(default) ->
- [];
-recbuf(Sz) ->
- [{recbuf, Sz}].
+ case BindTo of
+ true ->
+ {IpPort, [{ip, IpAddr} | SocketOpts]};
+ _ ->
+ {IpPort, SocketOpts}
+ end
+ end.
-sndbuf(default) ->
- [];
-sndbuf(Sz) ->
- [{sndbuf, Sz}].
+common_socket_opts(Opts) ->
+ [binary
+ | case get_opt(Opts, sndbuf, default) of
+ default ->
+ [];
+ Sz ->
+ [{sndbuf, Sz}]
+ end ++
+ case get_opt(Opts, recbuf, default) of
+ default ->
+ [];
+ Sz ->
+ [{sndbuf, Sz}]
+ end ++
+ case get_opt(Opts, no_reuse, false) of
+ false ->
+ [{reuseaddr, true}];
+ _ ->
+ []
+ end].
create_filter(Opts) when is_list(Opts) ->
@@ -310,6 +385,10 @@ create_filter(BadOpts) ->
throw({error, {bad_filter_opts, BadOpts}}).
+%% ----------------------------------------------------------------------
+%% Audit Trail Logger
+%% ----------------------------------------------------------------------
+
%% Open log
do_init_log(false) ->
?vtrace("do_init_log(false) -> entry", []),
@@ -330,24 +409,69 @@ do_init_log(true) ->
Function = increment_counter,
Args = [atl_seqno, Initial, Max],
SeqNoGen = {Module, Function, Args},
- case snmp_log:create(Name, File,
- SeqNoGen, Size, Repair, true) of
+ case snmp_log:create(
+ Name, File, SeqNoGen, Size, Repair, true) of
{ok, Log} ->
?vdebug("log created: ~w", [Log]),
- {Log, Type};
+ {Name, Log, Type};
{error, Reason} ->
throw({error, {failed_create_audit_log, Reason}})
end;
_ ->
case snmp_log:create(Name, File, Size, Repair, true) of
{ok, Log} ->
- {Log, Type};
+ ?vdebug("log created: ~w", [Log]),
+ {Name, Log, Type};
{error, Reason} ->
throw({error, {failed_create_audit_log, Reason}})
end
end.
-
+-ifdef(snmpm_net_if_mt).
+do_reopen_log(undefined) ->
+ undefined;
+do_reopen_log({Name, Log, Type}) ->
+ case snmp_log:open(Name, Log) of
+ {ok, NewLog} ->
+ {Name, NewLog, Type};
+ {error, Reason} ->
+ warning_msg(
+ "NetIf worker ~p failed to open ATL:~n"
+ " ~p", [self(), Reason]),
+ undefined
+ end.
+-endif.
+
+%% Close log
+do_close_log(undefined) ->
+ ok;
+do_close_log({_Name, Log, _Type}) ->
+ (catch snmp_log:sync(Log)),
+ (catch snmp_log:close(Log)),
+ ok;
+do_close_log(_) ->
+ ok.
+
+%% Log
+logger(undefined, _Type, _Domain, _Addr) ->
+ fun(_) ->
+ ok
+ end;
+logger({_Name, Log, Types}, Type, Domain, Addr) ->
+ case lists:member(Type, Types) of
+ true ->
+ AddrString =
+ iolist_to_binary(snmp_conf:mk_addr_string({Domain, Addr})),
+ fun(Msg) ->
+ snmp_log:log(Log, Msg, AddrString)
+ end;
+ false ->
+ fun(_) ->
+ ok
+ end
+ end.
+
+
%%--------------------------------------------------------------------
%% Func: handle_call/3
%% Returns: {reply, Reply, State} |
@@ -409,7 +533,7 @@ handle_cast({send_pdu, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo},
" Vsn: ~p~n"
" MsgData: ~p~n"
" Domain: ~p~n"
- " Addr : ~p", [Pdu, Vsn, MsgData, Domain, Addr]),
+ " Addr: ~p", [Pdu, Vsn, MsgData, Domain, Addr]),
maybe_process_extra_info(ExtraInfo),
maybe_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State),
{noreply, State};
@@ -439,11 +563,20 @@ handle_cast(Msg, State) ->
%% {stop, Reason, State} (terminate/2 is called)
%%--------------------------------------------------------------------
handle_info(
- {udp, Sock, Ip, Port, Bytes},
- #state{sock = Sock, domain = Domain} = State) ->
- ?vlog("received ~w bytes from ~p:~p [~w]", [size(Bytes), Ip, Port, Sock]),
- maybe_handle_recv_msg(Domain, {Ip, Port}, Bytes, State),
- {noreply, State};
+ {udp, Socket, IpAddr, IpPort, Bytes},
+ #state{transports = Transports} = State) ->
+ Size = byte_size(Bytes),
+ case lists:keyfind(Socket, #transport.socket, Transports) of
+ #transport{socket = Socket, domain = Domain} ->
+ ?vlog("received ~w bytes from ~p:~p [~w]",
+ [Size, IpAddr, IpPort, Socket]),
+ maybe_handle_recv_msg(Domain, {IpAddr, IpPort}, Bytes, State),
+ {noreply, State};
+ false ->
+ warning_msg("Received ~w bytes on unknown port: ~p from ~s",
+ [Size, Socket, format_address({IpAddr, IpPort})]),
+ {noreply, State}
+ end;
handle_info(inform_response_gc, State) ->
?vlog("received inform_response_gc message", []),
@@ -456,11 +589,42 @@ handle_info({disk_log, _Node, Log, Info}, State) ->
State2 = handle_disk_log(Log, Info, State),
{noreply, State2};
+handle_info({'DOWN', _, _, _, _} = Info, State) ->
+ handle_info_down(Info, State);
+
handle_info(Info, State) ->
+ handle_info_unknown(Info, State).
+
+
+handle_info_unknown(Info, State) ->
warning_msg("received unknown info: ~n~p", [Info]),
{noreply, State}.
+-ifdef(snmpm_net_if_mt).
+handle_info_down(
+ {'DOWN', _MRef, process, _Pid,
+ {net_if_worker, _Result}},
+ State) ->
+ ?vdebug("received DOWN message from net_if worker [~w]: "
+ "~n Result: ~p", [_Pid, _Result]),
+ {noreply, State};
+handle_info_down(
+ {'DOWN', _MRef, process, Pid,
+ {net_if_worker, Failer, Class, Reason, Stacktrace} = _ExitStatus},
+ State) ->
+ ?vdebug("received DOWN message from net_if worker [~w]: "
+ "~n ExitStatus: ~p", [Pid, _ExitStatus]),
+ Failer(Pid, Class, Reason, Stacktrace),
+ {noreply, State};
+handle_info_down(Info, State) ->
+ handle_info_unknown(Info, State).
+-else.
+handle_info_down(Info, State) ->
+ handle_info_unknown(Info, State).
+-endif.
+
+
%%--------------------------------------------------------------------
%% Func: terminate/2
%% Purpose: Shutdown the server
@@ -474,68 +638,12 @@ terminate(Reason, #state{log = Log, irgc = IrGcRef}) ->
ok.
-do_close_log({Log, _Type}) ->
- (catch snmp_log:sync(Log)),
- (catch snmp_log:close(Log)),
- ok;
-do_close_log(_) ->
- ok.
-
-
%%----------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%%----------------------------------------------------------------------
-code_change({down, _Vsn}, OldState, downgrade_to_pre_4_14) ->
- ?d("code_change(down, downgrade_to_pre_4_14) -> entry with"
- "~n OldState: ~p", [OldState]),
- #state{server = Server,
- note_store = NoteStore,
- sock = Sock,
- mpd_state = MpdState,
- log = {OldLog, Type},
- irb = IRB,
- irgc = IRGC} = OldState,
- NewLog = snmp_log:downgrade(OldLog),
- State =
- {state, Server, NoteStore, Sock, MpdState, {NewLog, Type}, IRB, IRGC},
- {ok, State};
-
-code_change({down, _Vsn}, OldState, downgrade_to_pre_4_16) ->
- ?d("code_change(down, downgrade_to_pre_4_16) -> entry with"
- "~n OldState: ~p", [OldState]),
- {OldLog, Type} = OldState#state.log,
- NewLog = snmp_log:downgrade(OldLog),
- State = OldState#state{log = {NewLog, Type}},
- {ok, State};
-
-% upgrade
-code_change(_Vsn, OldState, upgrade_from_pre_4_14) ->
- ?d("code_change(up, upgrade_from_pre_4_14) -> entry with"
- "~n OldState: ~p", [OldState]),
- {state, Server, NoteStore, Sock, MpdState, {OldLog, Type}, IRB, IRGC} =
- OldState,
- NewLog = snmp_log:upgrade(OldLog),
- State = #state{server = Server,
- note_store = NoteStore,
- sock = Sock,
- mpd_state = MpdState,
- log = {NewLog, Type},
- irb = IRB,
- irgc = IRGC,
- filter = ?DEFAULT_FILTER_MODULE},
- {ok, State};
-
-code_change(_Vsn, OldState, upgrade_from_pre_4_16) ->
- ?d("code_change(up, upgrade_from_pre_4_16) -> entry with"
- "~n OldState: ~p", [OldState]),
- {OldLog, Type} = OldState#state.log,
- NewLog = snmp_log:upgrade(OldLog),
- State = OldState#state{log = {NewLog, Type}},
- {ok, State};
-
code_change(_Vsn, State, _Extra) ->
?d("code_change -> entry with"
"~n Vsn: ~p"
@@ -548,80 +656,88 @@ code_change(_Vsn, State, _Extra) ->
%%% Internal functions
%%%-------------------------------------------------------------------
-maybe_handle_recv_msg(
+maybe_handle_recv_msg(Domain, Addr, Bytes, State) ->
+ ?worker(
+ S, maybe_handle_recv_msg_mt(Domain, Addr, Bytes, S),
+ fun (Pid, Class, Reason, Stacktrace) ->
+ warning_msg(
+ "Worker process (~p) terminated "
+ "while processing (incomming) message from %s:~n"
+ "~w:~w at ~p",
+ [Pid, snmp_conf:mk_addr_string({Domain, Addr}),
+ Class, Reason, Stacktrace])
+ end,
+ State).
+
+maybe_handle_recv_msg_mt(
Domain, Addr, Bytes,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ #state{filter = FilterMod, transports = Transports} = State) ->
+ {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}),
case (catch FilterMod:accept_recv(Arg1, Arg2)) of
false ->
%% Drop the received packet
- inc(netIfMsgInDrops),
- ok;
+ inc(netIfMsgInDrops);
_ ->
handle_recv_msg(Domain, Addr, Bytes, State)
- end.
+ end,
+ ok.
handle_recv_msg(Domain, Addr, Bytes, #state{server = Pid})
when is_binary(Bytes) andalso (size(Bytes) =:= 0) ->
- Pid ! {snmp_error, {empty_message, Domain, Addr}, Domain, Addr},
- ok;
-
+ Pid ! {snmp_error, {empty_message, Domain, Addr}, Domain, Addr};
+%%
handle_recv_msg(
Domain, Addr, Bytes,
- #state{server = Pid,
- note_store = NoteStore,
- mpd_state = MpdState,
- log = Log} = State) ->
+ #state{
+ server = Pid,
+ note_store = NoteStore,
+ mpd_state = MpdState,
+ log = Log} = State) ->
Logger = logger(Log, read, Domain, Addr),
- case (catch snmpm_mpd:process_msg(Bytes, Domain, Addr,
- MpdState, NoteStore, Logger)) of
+ case (catch snmpm_mpd:process_msg(
+ Bytes, Domain, Addr, MpdState, NoteStore, Logger)) of
{ok, Vsn, Pdu, MS, ACM} ->
- maybe_handle_recv_pdu(Domain, Addr, Vsn, Pdu, MS, ACM,
- Logger, State);
+ maybe_handle_recv_pdu(
+ Domain, Addr, Vsn, Pdu, MS, ACM, Logger, State);
{discarded, Reason, Report} ->
?vdebug("discarded: ~p", [Reason]),
ErrorInfo = {failed_processing_message, Reason},
Pid ! {snmp_error, ErrorInfo, Domain, Addr},
- maybe_udp_send(Domain, Addr, Report, State),
- ok;
+ maybe_udp_send(Domain, Addr, Report, State);
{discarded, Reason} ->
?vdebug("discarded: ~p", [Reason]),
ErrorInfo = {failed_processing_message, Reason},
- Pid ! {snmp_error, ErrorInfo, Domain, Addr},
- ok;
+ Pid ! {snmp_error, ErrorInfo, Domain, Addr};
Error ->
error_msg("processing of received message failed: "
- "~n ~p", [Error]),
- ok
+ "~n ~p", [Error])
end.
maybe_handle_recv_pdu(
Domain, Addr, Vsn, #pdu{type = Type} = Pdu, PduMS, ACM, Logger,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ #state{filter = FilterMod, transports = Transports} = State) ->
+ {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}),
case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, Type)) of
false ->
- inc(netIfPduInDrops),
- ok;
+ inc(netIfPduInDrops);
_ ->
handle_recv_pdu(
Domain, Addr, Vsn, Pdu, PduMS, ACM, Logger, State)
end;
maybe_handle_recv_pdu(
Domain, Addr, Vsn, Trap, PduMS, ACM, Logger,
- #state{filter = FilterMod, domain = ManagerDomain} = State)
+ #state{filter = FilterMod, transports = Transports} = State)
when is_record(Trap, trappdu) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}),
case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, trappdu)) of
false ->
- inc(netIfPduInDrops),
- ok;
+ inc(netIfPduInDrops);
_ ->
handle_recv_pdu(
Domain, Addr, Vsn, Trap, PduMS, ACM, Logger, State)
@@ -703,6 +819,19 @@ handle_inform_request(
end.
handle_inform_response(Ref, Domain, Addr, State) ->
+ ?worker(
+ S, handle_inform_response_mt(Ref, Domain, Addr, S),
+ fun (Pid, Class, Reason, Stacktrace) ->
+ warning_msg(
+ "Worker process (~p) terminated "
+ "while processing (outgoing) inform response for %s:~n"
+ "~w:~w at ~p",
+ [Pid, snmp_conf:mk_addr_string({Domain, Addr}),
+ Class, Reason, Stacktrace])
+ end,
+ State).
+
+handle_inform_response_mt(Ref, Domain, Addr, State) ->
Key = {Ref, Domain, Addr},
case ets:lookup(snmpm_inform_request_table, Key) of
[{Key, _, {Vsn, ACM, RePdu}}] ->
@@ -718,10 +847,11 @@ handle_inform_response(Ref, Domain, Addr, State) ->
maybe_send_inform_response(
RePdu, Vsn, ACM, Domain, Addr, Logger,
- #state{server = Pid,
- filter = FilterMod,
- domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ #state{
+ server = Pid,
+ filter = FilterMod,
+ transports = Transports} = State) ->
+ {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}),
case (catch FilterMod:accept_send_pdu(
Arg1, Arg2, pdu_type_of(RePdu)))
of
@@ -737,8 +867,7 @@ maybe_send_inform_response(
"~n Reason: ~p", [Reason]),
ReqId = RePdu#pdu.request_id,
ErrorInfo = {failed_generating_response, {RePdu, Reason}},
- Pid ! {snmp_error, ReqId, ErrorInfo, Domain, Addr},
- ok
+ Pid ! {snmp_error, ReqId, ErrorInfo, Domain, Addr}
end
end.
@@ -771,24 +900,37 @@ irgc_stop(undefined) ->
irgc_stop(Ref) ->
(catch erlang:cancel_timer(Ref)).
-
-maybe_handle_send_pdu(
+maybe_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State) ->
+ ?worker(
+ S, maybe_handle_send_pdu_mt(Pdu, Vsn, MsgData, Domain, Addr, S),
+ fun (Pid, Class, Reason, Stacktrace) ->
+ warning_msg(
+ "Worker process (~p) terminated "
+ "while processing (outgoing) pdu for %s:~n"
+ "~w:~w at ~p",
+ [Pid, snmp_conf:mk_addr_string({Domain, Addr}),
+ Class, Reason, Stacktrace])
+ end,
+ State).
+
+maybe_handle_send_pdu_mt(
Pdu, Vsn, MsgData, Domain, Addr,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ #state{filter = FilterMod, transports = Transports} = State) ->
+ {Arg1, Arg2} = fix_filter_address(Transports, {Domain, Addr}),
case (catch FilterMod:accept_send_pdu(Arg1, Arg2, pdu_type_of(Pdu))) of
false ->
- inc(netIfPduOutDrops),
- ok;
+ inc(netIfPduOutDrops);
_ ->
handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State)
- end.
+ end,
+ ok.
handle_send_pdu(
Pdu, Vsn, MsgData, Domain, Addr,
- #state{server = Pid,
- note_store = NoteStore,
- log = Log} = State) ->
+ #state{
+ server = Pid,
+ note_store = NoteStore,
+ log = Log} = State) ->
Logger = logger(Log, write, Domain, Addr),
case (catch snmpm_mpd:generate_msg(
Vsn, NoteStore, Pdu, MsgData, Logger)) of
@@ -799,43 +941,58 @@ handle_send_pdu(
?vlog("PDU not sent: "
"~n PDU: ~p"
"~n Reason: ~p", [Pdu, Reason]),
- Pid ! {snmp_error, Pdu, Reason},
- ok
+ Pid ! {snmp_error, Pdu, Reason}
end.
maybe_udp_send(
Domain, Addr, Msg,
- #state{sock = Sock, filter = FilterMod, domain = ManagerDomain}) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
+ #state{filter = FilterMod, transports = Transports}) ->
+ To = {Domain, Addr},
+ {Arg1, Arg2} = fix_filter_address(Transports, To),
case (catch FilterMod:accept_send(Arg1, Arg2)) of
false ->
inc(netIfMsgOutDrops),
ok;
_ ->
- %% XXX There should be some kind of lookup of socket
- %% from transport domain here
- {Ip, Port} = Addr,
- udp_send(Sock, Ip, Port, Msg)
+ case select_transport_from_domain(Domain, Transports) of
+ false ->
+ error_msg(
+ "Can not find transport~n"
+ " size: ~p~n"
+ " to: ~s",
+ [sz(Msg), format_address(To)]);
+ #transport{socket = Socket} ->
+ udp_send(Socket, Addr, Msg)
+ end
end.
-
-
-udp_send(Sock, Ip, Port, Msg) ->
- case (catch gen_udp:send(Sock, Ip, Port, Msg)) of
+
+udp_send(Sock, To, Msg) ->
+ {IpAddr, IpPort} =
+ case To of
+ {Domain, Addr} when is_atom(Domain) ->
+ Addr;
+ {_, P} = Addr when is_integer(P) ->
+ Addr
+ end,
+ try gen_udp:send(Sock, IpAddr, IpPort, Msg) of
ok ->
?vdebug("sent ~w bytes to ~w:~w [~w]",
- [sz(Msg), Ip, Port, Sock]),
+ [sz(Msg), IpAddr, IpPort, Sock]),
ok;
{error, Reason} ->
- error_msg("failed sending message to ~p:~p: "
- "~n ~p",[Ip, Port, Reason]);
- Error ->
- error_msg("failed sending message to ~p:~p: "
- "~n ~p",[Ip, Port, Error])
+ error_msg("failed sending message to ~p:~p:~n"
+ " ~p",[IpAddr, IpPort, Reason])
+ catch
+ error:Error ->
+ error_msg("failed sending message to ~p:~p:~n"
+ " error:~p~n"
+ " ~p",
+ [IpAddr, IpPort, Error, erlang:get_stacktrace()])
end.
sz(B) when is_binary(B) ->
- size(B);
+ byte_size(B);
sz(L) when is_list(L) ->
length(L);
sz(_) ->
@@ -1025,26 +1182,45 @@ handle_set_log_type(State, _NewType) ->
{State, {error, not_enabled}}.
+select_transport_from_domain(Domain, Transports) when is_atom(Domain) ->
+ Pos = #transport.domain,
+ case lists:keyfind(Domain, Pos, Transports) of
+ #transport{domain = Domain} = Transport ->
+ Transport;
+ false when Domain == snmpUDPDomain ->
+ lists:keyfind(transportDomainUdpIpv4, Pos, Transports);
+ false when Domain == transportDomainUdpIpv4 ->
+ lists:keyfind(snmpUDPDomain, Pos, Transports);
+ false ->
+ false
+ end.
+
%% If the manager uses legacy snmpUDPDomain e.g has not set
%% {domain, _}, then make sure snmpm_network_interface_filter
%% gets legacy arguments to not break backwards compatibility.
%%
-fix_filter_address(snmpUDPDomain, {Domain, Addr})
- when Domain =:= snmpUDPDomain;
- Domain =:= transportDomainUdpIpv4 ->
- Addr;
-fix_filter_address(_ManagerDomain, {Domain, _} = Address)
- when is_atom(Domain) ->
- Address;
-fix_filter_address(snmpUDPDomain, {_, Port} = Addr)
- when is_integer(Port) ->
- Addr.
+fix_filter_address(Transports, Address) ->
+ DefaultDomain = snmpm_config:default_transport_domain(),
+ case Transports of
+ [#transport{domain = DefaultDomain}, DefaultDomain] ->
+ case Address of
+ {Domain, Addr} when is_atom(Domain) ->
+ Addr;
+ {_, IpPort} = Addr when is_integer(IpPort) ->
+ Addr
+ end;
+ _ ->
+ Address
+ end.
address(Domain, Addr) when is_atom(Domain) ->
{Domain, Addr};
address(Ip, Port) when is_integer(Port) ->
{snmpm_config:default_transport_domain(), {Ip, Port}}.
+format_address(Address) ->
+ iolist_to_binary(snmp_conf:mk_addr_string(Address)).
+
%% -------------------------------------------------------------------
make_response_pdu(#pdu{request_id = ReqId, varbinds = Vbs}) ->
@@ -1085,27 +1261,6 @@ t() ->
%% -------------------------------------------------------------------
-logger(undefined, _Type, _Domain, _Addr) ->
- fun(_) ->
- ok
- end;
-logger({Log, Types}, Type, Domain, Addr) ->
- case lists:member(Type, Types) of
- true ->
- AddrString =
- iolist_to_binary(snmp_conf:mk_addr_string({Domain, Addr})),
- fun(Msg) ->
- snmp_log:log(Log, Msg, AddrString)
- end;
- false ->
- fun(_) ->
- ok
- end
- end.
-
-
-%% -------------------------------------------------------------------
-
%% info_msg(F, A) ->
%% ?snmpm_info("NET-IF server: " ++ F, A).
@@ -1130,10 +1285,11 @@ get_opt(Opts, Key, Def) ->
%% -------------------------------------------------------------------
-get_info(#state{sock = Id}) ->
+get_info(#state{transports = Transports}) ->
ProcSize = proc_mem(self()),
- PortInfo = get_port_info(Id),
- [{process_memory, ProcSize}, {port_info, PortInfo}].
+ [{process_memory, ProcSize}
+ | [{port_info, get_port_info(Socket)}
+ || #transport{socket = Socket} <- Transports]].
proc_mem(P) when is_pid(P) ->
case (catch erlang:process_info(P, memory)) of
@@ -1248,4 +1404,3 @@ call(Pid, Req, Timeout) ->
cast(Pid, Msg) ->
gen_server:cast(Pid, Msg).
-
diff --git a/lib/snmp/src/manager/snmpm_net_if_mt.erl b/lib/snmp/src/manager/snmpm_net_if_mt.erl
index 2937f5cc87..62f6023657 100644
--- a/lib/snmp/src/manager/snmpm_net_if_mt.erl
+++ b/lib/snmp/src/manager/snmpm_net_if_mt.erl
@@ -17,1309 +17,7 @@
%% %CopyrightEnd%
%%
--module(snmpm_net_if_mt).
-
--behaviour(gen_server).
--behaviour(snmpm_network_interface).
-
-
-%% Network Interface callback functions
--export([
- start_link/2,
- stop/1,
- send_pdu/6, % Backward compatibility
- send_pdu/7, % Partly backward compatibility
- send_pdu/8, % Backward compatibility
-
- inform_response/4,
-
- note_store/2,
-
- info/1,
- verbosity/2,
- %% system_info_updated/2,
- get_log_type/1, set_log_type/2,
- filter_reset/1
- ]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- code_change/3, terminate/2]).
-
--define(SNMP_USE_V3, true).
--include("snmp_types.hrl").
--include("snmpm_internal.hrl").
--include("snmpm_atl.hrl").
--include("snmp_debug.hrl").
-
-%% -define(VMODULE,"NET_IF").
--include("snmp_verbosity.hrl").
-
--record(state,
- {
- server,
- note_store,
- domain,
- sock,
- mpd_state,
- log,
- irb = auto, % auto | {user, integer()}
- irgc,
- filter
- }).
-
-
--define(DEFAULT_FILTER_MODULE, snmpm_net_if_filter).
--define(DEFAULT_FILTER_OPTS, [{module, ?DEFAULT_FILTER_MODULE}]).
-
--ifdef(snmp_debug).
--define(GS_START_LINK(Args),
- gen_server:start_link(?MODULE, Args, [{debug,[trace]}])).
--else.
--define(GS_START_LINK(Args),
- gen_server:start_link(?MODULE, Args, [])).
--endif.
-
-
--define(IRGC_TIMEOUT, timer:minutes(5)).
-
--define(ATL_SEQNO_INITIAL, 1).
--define(ATL_SEQNO_MAX, 2147483647).
-
-
-%%%-------------------------------------------------------------------
-%%% API
-%%%-------------------------------------------------------------------
-start_link(Server, NoteStore) ->
- ?d("start_link -> entry with"
- "~n Server: ~p"
- "~n NoteStore: ~p", [Server, NoteStore]),
- Args = [Server, NoteStore],
- ?GS_START_LINK(Args).
-
-stop(Pid) ->
- call(Pid, stop).
-
-send_pdu(Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port) ->
- send_pdu(
- Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port, ?DEFAULT_EXTRA_INFO).
-
-send_pdu(Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port, ExtraInfo)
- when is_record(Pdu, pdu) ->
- ?d("send_pdu -> entry with~n"
- " Pid: ~p~n"
- " Pdu: ~p~n"
- " Vsn: ~p~n"
- " MsgData: ~p~n"
- " Domain/IP: ~p~n"
- " Addr/Port : ~p",
- [Pid, Pdu, Vsn, MsgData, Domain_or_Ip, Addr_or_Port]),
- {Domain, Addr} = address(Domain_or_Ip, Addr_or_Port),
- cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo}).
-
-send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Ip, Port, ExtraInfo) ->
- send_pdu(Pid, Pdu, Vsn, MsgData, Domain, {Ip, Port}, ExtraInfo).
-
-note_store(Pid, NoteStore) ->
- call(Pid, {note_store, NoteStore}).
-
-inform_response(Pid, Ref, Domain_or_Ip, Addr_or_Port) ->
- {Domain, Addr} = address(Domain_or_Ip, Addr_or_Port),
- cast(Pid, {inform_response, Ref, Domain, Addr}).
-
-info(Pid) ->
- call(Pid, info).
-
-verbosity(Pid, V) ->
- call(Pid, {verbosity, V}).
-
-%% system_info_updated(Pid, What) ->
-%% call(Pid, {system_info_updated, What}).
-
-get_log_type(Pid) ->
- call(Pid, get_log_type).
-
-set_log_type(Pid, NewType) ->
- call(Pid, {set_log_type, NewType}).
-
-filter_reset(Pid) ->
- cast(Pid, filter_reset).
-
-
-%%%-------------------------------------------------------------------
-%%% Callback functions from gen_server
-%%%-------------------------------------------------------------------
-
-%%--------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%%--------------------------------------------------------------------
-init([Server, NoteStore]) ->
- ?d("init -> entry with"
- "~n Server: ~p"
- "~n NoteStore: ~p", [Server, NoteStore]),
- case (catch do_init(Server, NoteStore)) of
- {error, Reason} ->
- {stop, Reason};
- {ok, State} ->
- {ok, State}
- end.
-
-do_init(Server, NoteStore) ->
- process_flag(trap_exit, true),
-
- %% -- Prio --
- {ok, Prio} = snmpm_config:system_info(prio),
- process_flag(priority, Prio),
-
- %% -- Create inform request table --
- %% This should really be protected, but it also needs to
- %% be writable for the worker processes, so...
- ets:new(snmpm_inform_request_table,
- [set, public, named_table, {keypos, 1}]),
-
- %% -- Verbosity --
- {ok, Verbosity} = snmpm_config:system_info(net_if_verbosity),
- put(sname, mnif),
- put(verbosity, Verbosity),
- ?vlog("starting", []),
-
- %% -- MPD --
- {ok, Vsns} = snmpm_config:system_info(versions),
- MpdState = snmpm_mpd:init(Vsns),
- ?vdebug("MpdState: ~w", [MpdState]),
-
- %% -- Module dependent options --
- {ok, Opts} = snmpm_config:system_info(net_if_options),
-
- %% -- Inform response behaviour --
- {ok, IRB} = snmpm_config:system_info(net_if_irb),
- IrGcRef = irgc_start(IRB),
-
- %% -- Socket --
- SndBuf = get_opt(Opts, sndbuf, default),
- RecBuf = get_opt(Opts, recbuf, default),
- BindTo = get_opt(Opts, bind_to, false),
- NoReuse = get_opt(Opts, no_reuse, false),
- {ok, Port} = snmpm_config:system_info(port),
- Domain =
- case snmpm_config:system_info(domain) of
- {ok, D} ->
- D;
- _ ->
- snmpm_config:default_transport_domain()
- end,
- {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, Domain, BindTo, NoReuse),
-
- %% Flow control --
- FilterOpts = get_opt(Opts, filter, []),
- FilterMod = create_filter(FilterOpts),
- ?vdebug("FilterMod: ~w", [FilterMod]),
-
- %% -- Audit trail log ---
- {ok, ATL} = snmpm_config:system_info(audit_trail_log),
- Log = do_init_log(ATL),
- ?vdebug("Log: ~w", [Log]),
-
- %% -- Initiate counters ---
- init_counters(),
-
- %% -- We are done ---
- State = #state{server = Server,
- note_store = NoteStore,
- mpd_state = MpdState,
- domain = Domain,
- sock = Sock,
- log = Log,
- irb = IRB,
- irgc = IrGcRef,
- filter = FilterMod},
- ?vdebug("started", []),
- {ok, State}.
-
-
-%% Open port
-do_open_port(Port, SendSz, RecvSz, Domain, BindTo, NoReuse) ->
- ?vtrace("do_open_port -> entry with~n"
- " Port: ~p~n"
- " SendSz: ~p~n"
- " RecvSz: ~p~n"
- " Domain: ~p~n"
- " BindTo: ~p~n"
- " NoReuse: ~p",
- [Port, SendSz, RecvSz, Domain, BindTo, NoReuse]),
- IpOpts1 = bind_to(BindTo),
- IpOpts2 = no_reuse(NoReuse),
- IpOpts3 = recbuf(RecvSz),
- IpOpts4 = sndbuf(SendSz),
- IpOpts =
- [binary,
- snmp_conf:tdomain_to_family(Domain) |
- IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4],
- OpenRes =
- case init:get_argument(snmpm_fd) of
- {ok, [[FdStr]]} ->
- Fd = list_to_integer(FdStr),
- gen_udp:open(0, [{fd, Fd}|IpOpts]);
- error ->
- gen_udp:open(Port, IpOpts)
- end,
- case OpenRes of
- {error, _} = Error ->
- throw(Error);
- OK ->
- OK
- end.
-
-bind_to(true) ->
- case snmpm_config:system_info(address) of
- {ok, Addr} when is_list(Addr) ->
- [{ip, list_to_tuple(Addr)}];
- {ok, Addr} ->
- [{ip, Addr}];
- _ ->
- []
- end;
-bind_to(_) ->
- [].
-
-no_reuse(false) ->
- [{reuseaddr, true}];
-no_reuse(_) ->
- [].
-
-recbuf(default) ->
- [];
-recbuf(Sz) ->
- [{recbuf, Sz}].
-
-sndbuf(default) ->
- [];
-sndbuf(Sz) ->
- [{sndbuf, Sz}].
-
-
-create_filter(Opts) when is_list(Opts) ->
- case get_opt(Opts, module, ?DEFAULT_FILTER_MODULE) of
- ?DEFAULT_FILTER_MODULE = Mod ->
- Mod;
- Module ->
- snmpm_network_interface_filter:verify(Module),
- Module
- end;
-create_filter(BadOpts) ->
- throw({error, {bad_filter_opts, BadOpts}}).
-
-
-%% ----------------------------------------------------------------------
-%% Audit Trail Logger
-%% ----------------------------------------------------------------------
-
-%% Open log
-do_init_log(false) ->
- ?vtrace("do_init_log(false) -> entry", []),
- undefined;
-do_init_log(true) ->
- ?vtrace("do_init_log(true) -> entry", []),
- {ok, Type} = snmpm_config:system_info(audit_trail_log_type),
- {ok, Dir} = snmpm_config:system_info(audit_trail_log_dir),
- {ok, Size} = snmpm_config:system_info(audit_trail_log_size),
- {ok, Repair} = snmpm_config:system_info(audit_trail_log_repair),
- Name = ?audit_trail_log_name,
- File = filename:absname(?audit_trail_log_file, Dir),
- case snmpm_config:system_info(audit_trail_log_seqno) of
- {ok, true} ->
- Initial = ?ATL_SEQNO_INITIAL,
- Max = ?ATL_SEQNO_MAX,
- Module = snmpm_config,
- Function = increment_counter,
- Args = [atl_seqno, Initial, Max],
- SeqNoGen = {Module, Function, Args},
- case snmp_log:create(Name, File, SeqNoGen, Size, Repair, true) of
- {ok, Log} ->
- ?vdebug("log created: ~w", [Log]),
- {Name, Log, Type};
- {error, Reason} ->
- throw({error, {failed_create_audit_log, Reason}})
- end;
- _ ->
- case snmp_log:create(Name, File, Size, Repair, true) of
- {ok, Log} ->
- ?vdebug("log created: ~w", [Log]),
- {Name, Log, Type};
- {error, Reason} ->
- throw({error, {failed_create_audit_log, Reason}})
- end
- end.
-
-
-%% ----------------------------------------------------------------------
-
-%%--------------------------------------------------------------------
-%% Func: handle_call/3
-%% Returns: {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} | (terminate/2 is called)
-%% {stop, Reason, State} (terminate/2 is called)
-%%--------------------------------------------------------------------
-handle_call({verbosity, Verbosity}, _From, State) ->
- ?vlog("received verbosity request", []),
- put(verbosity, Verbosity),
- {reply, ok, State};
-
-%% handle_call({system_info_updated, What}, _From, State) ->
-%% ?vlog("received system_info_updated request with What = ~p", [What]),
-%% {NewState, Reply} = handle_system_info_updated(State, What),
-%% {reply, Reply, NewState};
-
-handle_call(get_log_type, _From, State) ->
- ?vlog("received get-log-type request", []),
- Reply = (catch handle_get_log_type(State)),
- {reply, Reply, State};
-
-handle_call({set_log_type, NewType}, _From, State) ->
- ?vlog("received set-log-type request with NewType = ~p", [NewType]),
- {NewState, Reply} = (catch handle_set_log_type(State, NewType)),
- {reply, Reply, NewState};
-
-handle_call({note_store, Pid}, _From, State) ->
- ?vlog("received new note_store: ~w", [Pid]),
- {reply, ok, State#state{note_store = Pid}};
-
-handle_call(stop, _From, State) ->
- ?vlog("received stop request", []),
- Reply = ok,
- {stop, normal, Reply, State};
-
-handle_call(info, _From, State) ->
- ?vlog("received info request", []),
- Reply = get_info(State),
- {reply, Reply, State};
-
-handle_call(Req, From, State) ->
- warning_msg("received unknown request (from ~p): ~n~p", [Req, From]),
- {reply, {error, {invalid_request, Req}}, State}.
-
-
-%%--------------------------------------------------------------------
-%% Func: handle_cast/2
-%% Returns: {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State} (terminate/2 is called)
-%%--------------------------------------------------------------------
-handle_cast({send_pdu, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo},
- State) ->
- ?vlog("received send_pdu message with~n"
- " Pdu: ~p~n"
- " Vsn: ~p~n"
- " MsgData: ~p~n"
- " Domain: ~p~n"
- " Addr: ~p", [Pdu, Vsn, MsgData, Domain, Addr]),
- maybe_process_extra_info(ExtraInfo),
- handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State),
- {noreply, State};
-
-handle_cast({inform_response, Ref, Domain, Addr}, State) ->
- ?vlog("received inform_response message with~n"
- " Ref: ~p~n"
- " Domain: ~p~n"
- " Addr: ~p", [Ref, Domain, Addr]),
- handle_inform_response(Ref, Domain, Addr, State),
- {noreply, State};
-
-handle_cast(filter_reset, State) ->
- ?vlog("received filter_reset message", []),
- reset_counters(),
- {noreply, State};
-
-handle_cast(Msg, State) ->
- warning_msg("received unknown message: ~n~p", [Msg]),
- {noreply, State}.
-
-
-%%--------------------------------------------------------------------
-%% Func: handle_info/2
-%% Returns: {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State} (terminate/2 is called)
-%%--------------------------------------------------------------------
-handle_info(
- {udp, Sock, Ip, Port, Bytes},
- #state{sock = Sock, domain = Domain} = State) ->
- ?vlog("received ~w bytes from ~p:~p", [size(Bytes), Ip, Port]),
- handle_udp(Domain, {Ip, Port}, Bytes, State),
- {noreply, State};
-
-handle_info(inform_response_gc, State) ->
- ?vlog("received inform_response_gc message", []),
- State2 = handle_inform_response_gc(State),
- {noreply, State2};
-
-handle_info({disk_log, _Node, Log, Info}, State) ->
- ?vlog("received disk_log message: "
- "~n Info: ~p", [Info]),
- State2 = handle_disk_log(Log, Info, State),
- {noreply, State2};
-
-handle_info({'DOWN', _MRef, process, Pid, {net_if_worker, ExitStatus}},
- State) ->
- ?vdebug("received DOWN message from net_if-worker: "
- "~n ExitStatus: ~p", [ExitStatus]),
- handle_worker_exit(Pid, ExitStatus),
- {noreply, State};
-
-handle_info(Info, State) ->
- warning_msg("received unknown info: ~n~p", [Info]),
- {noreply, State}.
-
-
-%%--------------------------------------------------------------------
-%% Func: terminate/2
-%% Purpose: Shutdown the server
-%% Returns: any (ignored by gen_server)
-%%--------------------------------------------------------------------
-terminate(Reason, #state{log = Log, irgc = IrGcRef}) ->
- ?vdebug("terminate: ~p", [Reason]),
- irgc_stop(IrGcRef),
- %% Close logs
- do_close_log(Log),
- ok.
-
-
-do_close_log({Log, _Type}) ->
- (catch snmp_log:sync(Log)),
- (catch snmp_log:close(Log)),
- ok;
-do_close_log(_) ->
- ok.
-
-
-%%----------------------------------------------------------------------
-%% Func: code_change/3
-%% Purpose: Convert process state when code is changed
-%% Returns: {ok, NewState}
-%%----------------------------------------------------------------------
-
-code_change(_Vsn, State, _Extra) ->
- ?d("code_change -> entry with"
- "~n Vsn: ~p"
- "~n State: ~p"
- "~n Extra: ~p", [_Vsn, State, _Extra]),
- {ok, State}.
-
-
-%%%-------------------------------------------------------------------
-%%% Internal functions
-%%%-------------------------------------------------------------------
-
-handle_udp(Domain, Addr, Bytes, State) ->
- Verbosity = get(verbosity),
- spawn_opt(
- fun() ->
- Log = worker_init(State, Verbosity),
- Res =
- (catch maybe_handle_recv_msg(
- Domain, Addr, Bytes,
- State#state{log = Log})),
- worker_exit(udp, {Domain, Addr}, Res)
- end,
- [monitor]).
-
-
-maybe_handle_recv_msg(
- Domain, Addr, Bytes,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_recv(Arg1, Arg2)) of
- false ->
- %% Drop the received packet
- inc(netIfMsgInDrops),
- ok;
- _ ->
- handle_recv_msg(Domain, Addr, Bytes, State)
- end.
-
-
-handle_recv_msg(Domain, Addr, Bytes, #state{server = Pid})
- when is_binary(Bytes) andalso (size(Bytes) =:= 0) ->
- Pid ! {snmp_error, {empty_message, Domain, Addr}, Domain, Addr},
- ok;
-
-handle_recv_msg(
- Domain, Addr, Bytes,
- #state{server = Pid,
- note_store = NoteStore,
- mpd_state = MpdState,
- log = Log} = State) ->
- Logger = logger(Log, read, Domain, Addr),
- case (catch snmpm_mpd:process_msg(Bytes, Domain, Addr,
- MpdState, NoteStore, Logger)) of
-
- {ok, Vsn, Pdu, MS, ACM} ->
- maybe_handle_recv_pdu(Domain, Addr, Vsn, Pdu, MS, ACM,
- Logger, State);
-
- {discarded, Reason, Report} ->
- ?vdebug("discarded: ~p", [Reason]),
- ErrorInfo = {failed_processing_message, Reason},
- Pid ! {snmp_error, ErrorInfo, Domain, Addr},
- maybe_udp_send(Domain, Addr, Report, State),
- ok;
- {discarded, Reason} ->
- ?vdebug("discarded: ~p", [Reason]),
- ErrorInfo = {failed_processing_message, Reason},
- Pid ! {snmp_error, ErrorInfo, Domain, Addr},
- ok;
-
- Error ->
- error_msg("processing of received message failed: "
- "~n ~p", [Error]),
- ok
- end.
-
-
-maybe_handle_recv_pdu(
- Domain, Addr, Vsn, #pdu{type = Type} = Pdu, PduMS, ACM, Logger,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, Type)) of
- false ->
- inc(netIfPduInDrops),
- ok;
- _ ->
- handle_recv_pdu(
- Domain, Addr, Vsn, Pdu, PduMS, ACM, Logger, State)
- end;
-maybe_handle_recv_pdu(
- Domain, Addr, Vsn, Trap, PduMS, ACM, Logger,
- #state{filter = FilterMod, domain = ManagerDomain} = State)
- when is_record(Trap, trappdu) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_recv_pdu(Arg1, Arg2, trappdu)) of
- false ->
- inc(netIfPduInDrops),
- ok;
- _ ->
- handle_recv_pdu(
- Domain, Addr, Vsn, Trap, PduMS, ACM, Logger, State)
- end;
-maybe_handle_recv_pdu(
- Domain, Addr, Vsn, Pdu, PduMS, ACM, Logger, State) ->
- handle_recv_pdu(Domain, Addr, Vsn, Pdu, PduMS, ACM, Logger, State).
-
-
-handle_recv_pdu(
- Domain, Addr, Vsn,
- #pdu{type = 'inform-request'} = Pdu, _PduMS, ACM, Logger,
- #state{server = Pid, irb = IRB} = State) ->
- handle_inform_request(
- IRB, Pid, Vsn, Pdu, ACM, Domain, Addr, Logger, State);
-handle_recv_pdu(
- Domain, Addr, _Vsn,
- #pdu{type = report} = Pdu, _PduMS, ok, _Logger,
- #state{server = Pid} = _State) ->
- ?vtrace("received report - ok", []),
- Pid ! {snmp_report, {ok, Pdu}, Domain, Addr},
- ok;
-handle_recv_pdu(
- Domain, Addr, _Vsn,
- #pdu{type = report} = Pdu, _PduMS, {error, ReqId, Reason}, _Logger,
- #state{server = Pid} = _State) ->
- ?vtrace("received report - error", []),
- Pid ! {snmp_report, {error, ReqId, Reason, Pdu}, Domain, Addr},
- ok;
-handle_recv_pdu(
- Domain, Addr, _Vsn,
- #pdu{type = 'snmpv2-trap'} = Pdu, _PduMS, _ACM, _Logger,
- #state{server = Pid} = _State) ->
- ?vtrace("received snmpv2-trap", []),
- Pid ! {snmp_trap, Pdu, Domain, Addr},
- ok;
-handle_recv_pdu(
- Domain, Addr, _Vsn, Trap, _PduMS, _ACM, _Logger,
- #state{server = Pid} = _State) when is_record(Trap, trappdu) ->
- ?vtrace("received trappdu", []),
- Pid ! {snmp_trap, Trap, Domain, Addr},
- ok;
-handle_recv_pdu(
- Domain, Addr, _Vsn, Pdu, _PduMS, _ACM, _Logger,
- #state{server = Pid} = _State) when is_record(Pdu, pdu) ->
- ?vtrace("received pdu", []),
- Pid ! {snmp_pdu, Pdu, Domain, Addr},
- ok;
-handle_recv_pdu(
- _Domain, _Addr, _Vsn, Pdu, _PduMS, ACM, _Logger, _State) ->
- ?vlog("received unexpected pdu: "
- "~n Pdu: ~p"
- "~n ACM: ~p", [Pdu, ACM]),
- ok.
-
-
-handle_inform_request(
- auto, Pid, Vsn, Pdu, ACM, Domain, Addr, Logger, State) ->
- ?vtrace("received inform-request (true)", []),
- Pid ! {snmp_inform, ignore, Pdu, Domain, Addr},
- RePdu = make_response_pdu(Pdu),
- maybe_send_inform_response(RePdu, Vsn, ACM, Domain, Addr, Logger, State);
-handle_inform_request(
- {user, To}, Pid, Vsn, #pdu{request_id = ReqId} = Pdu,
- ACM, Domain, Addr, _Logger, _State) ->
- ?vtrace("received inform-request (false)", []),
-
- Pid ! {snmp_inform, ReqId, Pdu, Domain, Addr},
-
- %% Before we go any further, we need to check that we have not
- %% already received this message (possible resend).
-
- Key = {ReqId, Domain, Addr},
- case ets:lookup(snmpm_inform_request_table, Key) of
- [_] ->
- %% OK, we already know about this. We assume this
- %% is a resend. Either the agent is really eager or
- %% the user has not answered yet. Bad user!
- ok;
- [] ->
- RePdu = make_response_pdu(Pdu),
- Expire = t() + To,
- Rec = {Key, Expire, {Vsn, ACM, RePdu}},
- ets:insert(snmpm_inform_request_table, Rec)
- end,
- ok.
-
-handle_inform_response(Ref, Domain, Addr, State) ->
- Verbosity = get(verbosity),
- spawn_opt(
- fun() ->
- Log = worker_init(State, Verbosity),
- Res = (catch do_handle_inform_response(
- Ref, Domain, Addr, State#state{log = Log})),
- worker_exit(inform_response, {Domain, Addr}, Res)
- end,
- [monitor]).
-
-
-
-do_handle_inform_response(Ref, Domain, Addr, State) ->
- Key = {Ref, Domain, Addr},
- case ets:lookup(snmpm_inform_request_table, Key) of
- [{Key, _, {Vsn, ACM, RePdu}}] ->
- Logger = logger(State#state.log, read, Domain, Addr),
- ets:delete(snmpm_inform_request_table, Key),
- maybe_send_inform_response(
- RePdu, Vsn, ACM, Domain, Addr, Logger, State);
- [] ->
- %% Already acknowledged, or the user was to slow to reply...
- ok
- end,
- ok.
-
-maybe_send_inform_response(
- RePdu, Vsn, ACM, Domain, Addr, Logger,
- #state{server = Pid,
- sock = Sock,
- domain = ManagerDomain,
- filter = FilterMod}) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_send_pdu(
- Arg1, Arg2, pdu_type_of(RePdu)))
- of
- false ->
- inc(netIfPduOutDrops),
- ok;
- _ ->
- case snmpm_mpd:generate_response_msg(Vsn, RePdu, ACM, Logger) of
- {ok, Msg} ->
- maybe_udp_send(
- Domain, Addr, Msg, Sock, FilterMod, ManagerDomain);
- {discarded, Reason} ->
- ?vlog("failed generating response message:"
- "~n Reason: ~p", [Reason]),
- ReqId = RePdu#pdu.request_id,
- ErrorInfo = {failed_generating_response, {RePdu, Reason}},
- Pid ! {snmp_error, ReqId, ErrorInfo, Domain, Addr},
- ok
- end
- end.
-
-handle_inform_response_gc(#state{irb = IRB} = State) ->
- ets:safe_fixtable(snmpm_inform_request_table, true),
- do_irgc(ets:first(snmpm_inform_request_table), t()),
- ets:safe_fixtable(snmpm_inform_request_table, false),
- State#state{irgc = irgc_start(IRB)}.
-
-%% We are deleting at the same time as we are traversing the table!!!
-do_irgc('$end_of_table', _) ->
- ok;
-do_irgc(Key, Now) ->
- Next = ets:next(snmpm_inform_request_table, Key),
- case ets:lookup(snmpm_inform_request_table, Key) of
- [{Key, BestBefore, _}] when BestBefore < Now ->
- ets:delete(snmpm_inform_request_table, Key);
- _ ->
- ok
- end,
- do_irgc(Next, Now).
-
-irgc_start(auto) ->
- undefined;
-irgc_start(_) ->
- erlang:send_after(?IRGC_TIMEOUT, self(), inform_response_gc).
-
-irgc_stop(undefined) ->
- ok;
-irgc_stop(Ref) ->
- (catch erlang:cancel_timer(Ref)).
-
-
-handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State) ->
- Verbosity = get(verbosity),
- spawn_opt(
- fun() ->
- Log = worker_init(State, Verbosity),
- Res = (catch maybe_handle_send_pdu(
- Pdu, Vsn, MsgData,
- Domain, Addr,
- State#state{log = Log})),
- worker_exit(send_pdu, {Domain, Addr}, Res)
- end,
- [monitor]).
-
-maybe_handle_send_pdu(
- Pdu, Vsn, MsgData, Domain, Addr,
- #state{filter = FilterMod, domain = ManagerDomain} = State) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_send_pdu(Arg1, Arg2, pdu_type_of(Pdu))) of
- false ->
- inc(netIfPduOutDrops),
- ok;
- _ ->
- do_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, State)
- end.
-
-do_handle_send_pdu(
- Pdu, Vsn, MsgData, Domain, Addr,
- #state{server = Pid,
- note_store = NoteStore,
- log = Log} = State) ->
- Logger = logger(Log, write, Domain, Addr),
- case (catch snmpm_mpd:generate_msg(
- Vsn, NoteStore, Pdu, MsgData, Logger)) of
- {ok, Msg} ->
- ?vtrace("do_handle_send_pdu -> message generated", []),
- maybe_udp_send(Domain, Addr, Msg, State);
- {discarded, Reason} ->
- ?vlog("PDU not sent: "
- "~n PDU: ~p"
- "~n Reason: ~p", [Pdu, Reason]),
- Pid ! {snmp_error, Pdu, Reason},
- ok
- end.
-
-maybe_udp_send(
- Domain, Addr, Msg,
- #state{sock = Sock, filter = FilterMod, domain = ManagerDomain}) ->
- maybe_udp_send(Domain, Addr, Msg, Sock, FilterMod, ManagerDomain).
-
-maybe_udp_send(Domain, Addr, Msg, Sock, FilterMod, ManagerDomain) ->
- {Arg1, Arg2} = fix_filter_address(ManagerDomain, {Domain, Addr}),
- case (catch FilterMod:accept_send(Arg1, Arg2)) of
- false ->
- inc(netIfMsgOutDrops),
- ok;
- _ ->
- %% XXX There should be some kind of lookup of socket
- %% from transport domain here
- {Ip, Port} = Addr,
- udp_send(Sock, Ip, Port, Msg)
- end.
-
-
-udp_send(Sock, Ip, Port, Msg) ->
- case (catch gen_udp:send(Sock, Ip, Port, Msg)) of
- ok ->
- ?vdebug("sent ~w bytes to ~w:~w [~w]",
- [sz(Msg), Ip, Port, Sock]),
- ok;
- {error, Reason} ->
- error_msg("failed sending message to ~p:~p: "
- "~n ~p", [Ip, Port, Reason]),
- ok;
- Error ->
- error_msg("failed sending message to ~p:~p: "
- "~n ~p", [Ip, Port, Error]),
- ok
- end.
-
-sz(B) when is_binary(B) ->
- size(B);
-sz(L) when is_list(L) ->
- length(L);
-sz(_) ->
- undefined.
-
-
-handle_disk_log(_Log, {wrap, NoLostItems}, State) ->
- ?vlog("Audit Trail Log - wrapped: ~w previously logged items where lost",
- [NoLostItems]),
- State;
-handle_disk_log(_Log, {truncated, NoLostItems}, State) ->
- ?vlog("Audit Trail Log - truncated: ~w items where lost when truncating",
- [NoLostItems]),
- State;
-handle_disk_log(_Log, full, State) ->
- error_msg("Failed to write to Audit Trail Log (full)", []),
- State;
-handle_disk_log(_Log, {error_status, ok}, State) ->
- State;
-handle_disk_log(_Log, {error_status, {error, Reason}}, State) ->
- error_msg("Error status received from Audit Trail Log: "
- "~n~p", [Reason]),
- State;
-handle_disk_log(_Log, _Info, State) ->
- State.
-
-
-%% mk_discovery_msg('version-3', Pdu, _VsnHdr, UserName) ->
-%% ScopedPDU = #scopedPdu{contextEngineID = "",
-%% contextName = "",
-%% data = Pdu},
-%% Bytes = snmp_pdus:enc_scoped_pdu(ScopedPDU),
-%% MsgID = get(msg_id),
-%% put(msg_id,MsgID+1),
-%% UsmSecParams =
-%% #usmSecurityParameters{msgAuthoritativeEngineID = "",
-%% msgAuthoritativeEngineBoots = 0,
-%% msgAuthoritativeEngineTime = 0,
-%% msgUserName = UserName,
-%% msgPrivacyParameters = "",
-%% msgAuthenticationParameters = ""},
-%% SecBytes = snmp_pdus:enc_usm_security_parameters(UsmSecParams),
-%% PduType = Pdu#pdu.type,
-%% Hdr = #v3_hdr{msgID = MsgID,
-%% msgMaxSize = 1000,
-%% msgFlags = snmp_misc:mk_msg_flags(PduType, 0),
-%% msgSecurityModel = ?SEC_USM,
-%% msgSecurityParameters = SecBytes},
-%% Msg = #message{version = 'version-3', vsn_hdr = Hdr, data = Bytes},
-%% case (catch snmp_pdus:enc_message_only(Msg)) of
-%% {'EXIT', Reason} ->
-%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
-%% error;
-%% L when list(L) ->
-%% {Msg, L}
-%% end;
-%% mk_discovery_msg(Version, Pdu, {Com, _, _, _, _}, UserName) ->
-%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu},
-%% case catch snmp_pdus:enc_message(Msg) of
-%% {'EXIT', Reason} ->
-%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
-%% error;
-%% L when list(L) ->
-%% {Msg, L}
-%% end.
-
-
-%% mk_msg('version-3', Pdu, {Context, User, EngineID, CtxEngineId, SecLevel},
-%% MsgData) ->
-%% %% Code copied from snmp_mpd.erl
-%% {MsgId, SecName, SecData} =
-%% if
-%% tuple(MsgData), Pdu#pdu.type == 'get-response' ->
-%% MsgData;
-%% true ->
-%% Md = get(msg_id),
-%% put(msg_id, Md + 1),
-%% {Md, User, []}
-%% end,
-%% ScopedPDU = #scopedPdu{contextEngineID = CtxEngineId,
-%% contextName = Context,
-%% data = Pdu},
-%% ScopedPDUBytes = snmp_pdus:enc_scoped_pdu(ScopedPDU),
-
-%% PduType = Pdu#pdu.type,
-%% V3Hdr = #v3_hdr{msgID = MsgId,
-%% msgMaxSize = 1000,
-%% msgFlags = snmp_misc:mk_msg_flags(PduType, SecLevel),
-%% msgSecurityModel = ?SEC_USM},
-%% Message = #message{version = 'version-3', vsn_hdr = V3Hdr,
-%% data = ScopedPDUBytes},
-%% SecEngineID = case PduType of
-%% 'get-response' -> snmp_framework_mib:get_engine_id();
-%% _ -> EngineID
-%% end,
-%% case catch snmp_usm:generate_outgoing_msg(Message, SecEngineID,
-%% SecName, SecData, SecLevel) of
-%% {'EXIT', Reason} ->
-%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
-%% error;
-%% {error, Reason} ->
-%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
-%% error;
-%% Packet ->
-%% Packet
-%% end;
-%% mk_msg(Version, Pdu, {Com, _User, _EngineID, _Ctx, _SecLevel}, _SecData) ->
-%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu},
-%% case catch snmp_pdus:enc_message(Msg) of
-%% {'EXIT', Reason} ->
-%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]),
-%% error;
-%% B when list(B) ->
-%% B
-%% end.
-
-
-%% handle_system_info_updated(#state{log = {Log, _OldType}} = State,
-%% audit_trail_log_type = _What) ->
-%% %% Just to make sure, check that ATL is actually enabled
-%% case snmpm_config:system_info(audit_trail_log) of
-%% {ok, true} ->
-%% {ok, Type} = snmpm_config:system_info(audit_trail_log_type),
-%% NewState = State#state{log = {Log, Type}},
-%% {NewState, ok};
-%% _ ->
-%% {State, {error, {adt_not_enabled}}}
-%% end;
-%% handle_system_info_updated(_State, _What) ->
-%% ok.
-
-handle_get_log_type(#state{log = {_Log, Value}} = State) ->
- %% Just to make sure, check that ATL is actually enabled
- case snmpm_config:system_info(audit_trail_log) of
- {ok, true} ->
- Type =
- case {lists:member(read, Value), lists:member(write, Value)} of
- {true, true} ->
- read_write;
- {true, false} ->
- read;
- {false, true} ->
- write;
- {false, false} ->
- throw({State, {error, {bad_atl_type, Value}}})
- end,
- {ok, Type};
- _ ->
- {error, not_enabled}
- end;
-handle_get_log_type(_State) ->
- {error, not_enabled}.
-
-handle_set_log_type(#state{log = {Log, OldValue}} = State, NewType) ->
- %% Just to make sure, check that ATL is actually enabled
- case snmpm_config:system_info(audit_trail_log) of
- {ok, true} ->
- NewValue =
- case NewType of
- read ->
- [read];
- write ->
- [write];
- read_write ->
- [read,write];
- _ ->
- throw({State, {error, {bad_atl_type, NewType}}})
- end,
- NewState = State#state{log = {Log, NewValue}},
- OldType =
- case {lists:member(read, OldValue),
- lists:member(write, OldValue)} of
- {true, true} ->
- read_write;
- {true, false} ->
- read;
- {false, true} ->
- write;
- {false, false} ->
- throw({State, {error, {bad_atl_type, OldValue}}})
- end,
- {NewState, {ok, OldType}};
- _ ->
- {State, {error, not_enabled}}
- end;
-handle_set_log_type(State, _NewType) ->
- {State, {error, not_enabled}}.
-
-
-%% -------------------------------------------------------------------
-
-worker_init(#state{log = undefined = Log}, Verbosity) ->
- worker_init2(Log, Verbosity);
-worker_init(#state{log = {Name, Log, Type}}, Verbosity) ->
- case snmp_log:open(Name, Log) of
- {ok, NewLog} ->
- worker_init2({Name, NewLog, Type}, Verbosity);
- {error, Reason} ->
- warning_msg("NetIf worker ~p failed opening ATL: "
- "~n ~p", [self(), Reason]),
- NewLog = undefined,
- worker_init2({Name, NewLog, Type}, Verbosity)
- end;
-worker_init(State, Verbosity) ->
- ?vinfo("worker_init -> entry with invalid data: "
- "~n State: ~p"
- "~n Verbosity: ~p", [State, Verbosity]),
- exit({worker_init, State, Verbosity}).
-
-worker_init2(Log, Verbosity) ->
- put(sname, mnifw),
- put(verbosity, Verbosity),
- Log.
-
-
-worker_exit(Tag, Info, Result) ->
- exit({net_if_worker, {Tag, Info, Result}}).
-
-handle_worker_exit(_, {_, _, ok}) ->
- ok;
-handle_worker_exit(Pid, {udp, {Domain, Addr}, ExitStatus}) ->
- warning_msg(
- "Worker process (~p) terminated "
- "while processing (incomming) message from %s:~n"
- "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]),
- ok;
-handle_worker_exit(Pid, {send_pdu, {Domain, Addr}, ExitStatus}) ->
- warning_msg(
- "Worker process (~p) terminated "
- "while processing (outgoing) pdu for %s:~n"
- "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]),
- ok;
-handle_worker_exit(Pid, {inform_response, {Domain, Addr}, ExitStatus}) ->
- warning_msg(
- "Worker process (~p) terminated "
- "while processing (outgoing) inform response for %s:~n"
- "~p", [Pid, snmp_conf:mk_addr_string({Domain, Addr}), ExitStatus]),
- ok;
-handle_worker_exit(_, _) ->
- ok.
-
-
-%% If the manager uses legacy snmpUDPDomain e.g has not set
-%% {domain, _}, then make sure snmpm_network_interface_filter
-%% gets legacy arguments to not break backwards compatibility.
-%%
-fix_filter_address(snmpUDPDomain, {Domain, Addr})
- when Domain =:= snmpUDPDomain;
- Domain =:= transportDomainUdpIpv4 ->
- Addr;
-fix_filter_address(_ManagerDomain, {Domain, _} = Address)
- when is_atom(Domain) ->
- Address;
-fix_filter_address(snmpUDPDomain, {_, Port} = Addr)
- when is_integer(Port) ->
- Addr.
-
-address(Domain, Addr) when is_atom(Domain) ->
- {Domain, Addr};
-address(Ip, Port) when is_integer(Port) ->
- {snmpm_config:default_transport_domain(), {Ip, Port}}.
-
-%% -------------------------------------------------------------------
-
-make_response_pdu(#pdu{request_id = ReqId, varbinds = Vbs}) ->
- #pdu{type = 'get-response',
- request_id = ReqId,
- error_status = noError,
- error_index = 0,
- varbinds = Vbs}.
-
-
-%% ----------------------------------------------------------------
-
-pdu_type_of(#pdu{type = Type}) ->
- Type;
-pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) ->
- trap.
-
-
-%% -------------------------------------------------------------------
-
-%% At this point this function is used during testing
-maybe_process_extra_info(?DEFAULT_EXTRA_INFO) ->
- ok;
-maybe_process_extra_info({?SNMPM_EXTRA_INFO_TAG, Fun})
- when is_function(Fun, 0) ->
- (catch Fun()),
- ok;
-maybe_process_extra_info(_ExtraInfo) ->
- ok.
-
-
-%% -------------------------------------------------------------------
-
-t() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
-
-
-%% -------------------------------------------------------------------
-
-logger(undefined, _Type, _Domain, _Addr) ->
- fun(_) ->
- ok
- end;
-logger({_Name, Log, Types}, Type, Domain, Addr) ->
- case lists:member(Type, Types) of
- true ->
- AddrString =
- iolist_to_binary(snmp_conf:mk_addr_string({Domain, Addr})),
- fun(Msg) ->
- snmp_log:log(Log, Msg, Domain, AddrString)
- end;
- false ->
- fun(_) ->
- ok
- end
- end.
-
-
-%% -------------------------------------------------------------------
-
-%% info_msg(F, A) ->
-%% ?snmpm_info("NET-IF server: " ++ F, A).
-
-warning_msg(F, A) ->
- ?snmpm_warning("NET-IF server: " ++ F, A).
-
-error_msg(F, A) ->
- ?snmpm_error("NET-IF server: " ++ F, A).
-
-
-
-%%%-------------------------------------------------------------------
-
-% get_opt(Key, Opts) ->
-% ?vtrace("get option ~w", [Key]),
-% snmp_misc:get_option(Key, Opts).
-
-get_opt(Opts, Key, Def) ->
- ?vtrace("get option ~w with default ~p", [Key, Def]),
- snmp_misc:get_option(Key, Opts, Def).
-
-
-%% -------------------------------------------------------------------
-
-get_info(#state{sock = Id}) ->
- ProcSize = proc_mem(self()),
- PortInfo = get_port_info(Id),
- [{process_memory, ProcSize}, {port_info, PortInfo}].
-
-proc_mem(P) when is_pid(P) ->
- case (catch erlang:process_info(P, memory)) of
- {memory, Sz} when is_integer(Sz) ->
- Sz;
- _ ->
- undefined
- end.
-%% proc_mem(_) ->
-%% undefined.
-
-
-get_port_info(Id) ->
- PortInfo =
- case (catch erlang:port_info(Id)) of
- PI when is_list(PI) ->
- [{port_info, PI}];
- _ ->
- []
- end,
- PortStatus =
- case (catch prim_inet:getstatus(Id)) of
- {ok, PS} ->
- [{port_status, PS}];
- _ ->
- []
- end,
- PortAct =
- case (catch inet:getopts(Id, [active])) of
- {ok, PA} ->
- [{port_act, PA}];
- _ ->
- []
- end,
- PortStats =
- case (catch inet:getstat(Id)) of
- {ok, Stat} ->
- [{port_stats, Stat}];
- _ ->
- []
- end,
- IfList =
- case (catch inet:getif(Id)) of
- {ok, IFs} ->
- [{interfaces, IFs}];
- _ ->
- []
- end,
- BufSz =
- case (catch inet:getopts(Id, [recbuf, sndbuf, buffer])) of
- {ok, Sz} ->
- [{buffer_size, Sz}];
- _ ->
- []
- end,
- [{socket, Id}] ++
- IfList ++
- PortStats ++
- PortInfo ++
- PortStatus ++
- PortAct ++
- BufSz.
-
-
-%%-----------------------------------------------------------------
-%% Counter functions
-%%-----------------------------------------------------------------
-init_counters() ->
- F = fun(Counter) -> maybe_create_counter(Counter) end,
- lists:map(F, counters()).
-
-reset_counters() ->
- F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end,
- lists:map(F, counters()).
-
-maybe_create_counter(Counter) ->
- snmpm_config:maybe_cre_stats_counter(Counter, 0).
-
-counters() ->
- [
- netIfMsgOutDrops,
- netIfMsgInDrops,
- netIfPduOutDrops,
- netIfPduInDrops
- ].
-
-inc(Name) -> inc(Name, 1).
-inc(Name, N) -> snmpm_config:incr_stats_counter(Name, N).
-
-%% get_counters() ->
-%% Counters = counters(),
-%% get_counters(Counters, []).
-
-%% get_counters([], Acc) ->
-%% lists:reverse(Acc);
-%% get_counters([Counter|Counters], Acc) ->
-%% case snmpm_config:get_stats_counter(Counter) of
-%% {ok, CounterVal} ->
-%% get_counters(Counters, [{Counter, CounterVal}|Acc]);
-%% _ ->
-%% get_counters(Counters, Acc)
-%% end.
-
-
-%% ----------------------------------------------------------------
-
-call(Pid, Req) ->
- call(Pid, Req, infinity).
-
-call(Pid, Req, Timeout) ->
- gen_server:call(Pid, Req, Timeout).
-
-cast(Pid, Msg) ->
- gen_server:cast(Pid, Msg).
+-define(snmpm_net_if_mt, true).
+-module(snmpm_net_if_mt).
+-include("snmpm_net_if.erl").
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
index ece5dad082..a75122d0bb 100644
--- a/lib/snmp/src/manager/snmpm_server.erl
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -2079,7 +2079,16 @@ do_handle_agent(DefUserId, DefMod,
SnmpInfo, DefData, State) ->
?vdebug("do_handle_agent -> entry when"
"~n DefUserId: ~p", [DefUserId]),
- try DefMod:handle_agent(Domain, Addr, Type, SnmpInfo, DefData) of
+ {Domain_or_Ip, Addr_or_Port} =
+ case Domain of
+ snmpUDPDomain ->
+ Addr;
+ _ ->
+ {Domain, Addr}
+ end,
+ try DefMod:handle_agent(
+ Domain_or_Ip, Addr_or_Port, Type, SnmpInfo, DefData)
+ of
{register, UserId2, TargetName, Config} ->
?vtrace("do_handle_agent -> register: "
"~n UserId2: ~p"
diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl
index f4483995cb..153c8070c2 100644
--- a/lib/snmp/src/misc/snmp_conf.erl
+++ b/lib/snmp/src/misc/snmp_conf.erl
@@ -749,8 +749,6 @@ which_domain({A0, A1, A2, A3, A4, A5, A6, A7})
%% ---------
-mk_addr_string({_IP, Port} = Addr) when is_integer(Port) ->
- mk_addr_string({snmpUDPDomain, Addr});
mk_addr_string({Domain, Addr}) when is_atom(Domain) ->
%% XXX There is only code for IP domains here
case check_address_ip(Domain, Addr) of
@@ -768,7 +766,11 @@ mk_addr_string({Domain, Addr}) when is_atom(Domain) ->
mk_addr_string_ntoa(Domain, Addr);
IP ->
mk_addr_string_ntoa(Domain, IP)
- end.
+ end;
+mk_addr_string({_IP, Port} = Addr) when is_integer(Port) ->
+ mk_addr_string({snmpUDPDomain, Addr});
+mk_addr_string(Strange) ->
+ lists:flatten(io_lib:format("~w", [Strange])).
mk_addr_string_ntoa({_, _, _, _} = IP) ->
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
index 9e9865f3b5..17dfcd70b4 100644
--- a/lib/snmp/src/misc/snmp_config.erl
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -47,8 +47,8 @@
write_agent_snmp_vacm_conf/3,
write_manager_snmp_files/8,
- write_manager_snmp_conf/5,
- write_manager_snmp_users_conf/2,
+ write_manager_snmp_conf/4, write_manager_snmp_conf/5,
+ write_manager_snmp_users_conf/2,
write_manager_snmp_agents_conf/2,
write_manager_snmp_usm_conf/2
@@ -1676,7 +1676,9 @@ write_agent_snmp_conf(Dir, AgentIP, AgentUDP, EngineID, MMS)
{intAgentIpAddress, AgentIP},
{snmpEngineID, EngineID},
{snmpEngineMaxMessageSize, MMS}],
- do_write_agent_snmp_conf(Dir, Conf).
+ do_write_agent_snmp_conf(Dir, Conf);
+write_agent_snmp_conf(_Dir, Domain, AgentAddr, _EngineID, _MMS) ->
+ error({bad_address, {Domain, AgentAddr}}).
do_write_agent_snmp_conf(Dir, Conf) ->
Comment =
@@ -2116,7 +2118,24 @@ write_manager_snmp_files(Dir, IP, Port, MMS, EngineID,
%% ------ manager.conf ------
%%
-write_manager_snmp_conf(Dir, IP, Port, MMS, EngineID) ->
+write_manager_snmp_conf(Dir, Transports, MMS, EngineID) ->
+ Comment =
+"%% This file defines the Manager local configuration info\n"
+"%% Each row is a 2-tuple:\n"
+"%% {Variable, Value}.\n"
+"%% For example\n"
+"%% {transports, [{transportDomainUdpIpv4, {{127,42,17,5}, 5000}}]}.\n"
+"%% {engine_id, \"managerEngine\"}.\n"
+"%% {max_message_size, 484}.\n"
+"%%\n\n",
+ Hdr = header() ++ Comment,
+ Conf =
+ [{transports, Transports},
+ {engine_id, EngineID},
+ {max_message_size, MMS}],
+ write_manager_config(Dir, Hdr, Conf).
+
+write_manager_snmp_conf(Dir, Domain_or_IP, Addr_or_Port, MMS, EngineID) ->
Comment =
"%% This file defines the Manager local configuration info\n"
"%% Each row is a 2-tuple:\n"
@@ -2129,15 +2148,16 @@ write_manager_snmp_conf(Dir, IP, Port, MMS, EngineID) ->
"%%\n\n",
Hdr = header() ++ Comment,
Conf =
- case Port of
- {Addr, P} when is_integer(P), is_atom(IP) ->
- Domain = IP,
- [{domain, Domain},
- {port, P},
- {address, Addr}];
- _ when is_integer(Port) ->
- [{port, Port},
- {address, IP}]
+ case Addr_or_Port of
+ {IP, Port} when is_integer(Port), is_atom(Domain_or_IP) ->
+ [{domain, Domain_or_IP},
+ {port, Port},
+ {address, IP}];
+ _ when is_integer(Addr_or_Port) ->
+ [{port, Addr_or_Port},
+ {address, Domain_or_IP}];
+ _ ->
+ error({bad_address, {Domain_or_IP, Addr_or_Port}})
end ++
[{engine_id, EngineID},
{max_message_size, MMS}],
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index 9a9258aa91..b4770ad0a9 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -532,6 +532,7 @@ groups() ->
{v3_inform, [], v3_inform_cases()},
{v3_security, [], v3_security_cases()},
{standard_mibs, [], standard_mibs_cases()},
+ {standard_mibs_ipv6, [], standard_mibs_cases_ipv6()},
{standard_mibs_2, [], standard_mibs2_cases()},
{standard_mibs_3, [], standard_mibs3_cases()},
{reported_bugs, [], reported_bugs_cases()},
@@ -651,11 +652,18 @@ init_per_group(GroupName, Config) ->
init_per_group_ipv6(GroupName, Config, Init) ->
case ct:require(ipv6_hosts) of
ok ->
- Init(
- snmp_test_lib:init_group_top_dir(
- GroupName,
- [{ipfamily, inet6},
- {ip, ?LOCALHOST(inet6)} | lists:keydelete(ip, 1, Config)]));
+ case gen_udp:open(0, [inet6]) of
+ {ok, S} ->
+ ok = gen_udp:close(S),
+ Init(
+ snmp_test_lib:init_group_top_dir(
+ GroupName,
+ [{ipfamily, inet6},
+ {ip, ?LOCALHOST(inet6)}
+ | lists:keydelete(ip, 1, Config)]));
+ {error, _} ->
+ {skip, "Host seems to not support IPv6"}
+ end;
_ ->
{skip, "Host does not support IPV6"}
end.
@@ -1714,7 +1722,7 @@ v1_cases_ipv6() ->
next_across_sa,
undo,
%% {group, reported_bugs},
- {group, standard_mibs}, % snmp_standard_mib still failing, sends v1 trap
+ {group, standard_mibs_ipv6},
sparse_table,
%% cnt_64, % sends v1 trap
opaque
@@ -4836,6 +4844,15 @@ standard_mibs_cases() ->
snmp_view_based_acm_mib
].
+standard_mibs_cases_ipv6() ->
+ [
+ %% snmp_standard_mib, % Sending v1 traps does not work over IPv6
+ snmp_community_mib,
+ snmp_framework_mib,
+ snmp_target_mib,
+ snmp_notification_mib,
+ snmp_view_based_acm_mib
+ ].
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
diff --git a/lib/snmp/test/snmp_manager_config_test.erl b/lib/snmp/test/snmp_manager_config_test.erl
index 2f5c68d14d..f37e957dae 100644
--- a/lib/snmp/test/snmp_manager_config_test.erl
+++ b/lib/snmp/test/snmp_manager_config_test.erl
@@ -1048,7 +1048,7 @@ start_with_invalid_agents_conf_file1(Conf) when is_list(Conf) ->
case config_start(Opts) of
{error, Reason51} ->
p("start failed (as expected): ~p", [Reason51]),
- ?line {failed_check, _, _, _, {bad_address, _}} = Reason51,
+ ?line {failed_check, _, _, _, {bad_domain, _}} = Reason51,
await_config_not_running();
OK_51 ->
exit({error, {unexpected_success, "51", OK_51}})
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 78352e59cf..fa90872172 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -584,17 +584,30 @@ init_per_group(event_tests_mt = GroupName, Config) ->
init_per_group(ipv6_mt = GroupName, Config) ->
case ct:require(ipv6_hosts) of
ok ->
- ipv6_init(
- snmp_test_lib:init_group_top_dir(
- GroupName,
- [{manager_net_if_module, snmpm_net_if_mt} | Config]));
+ case gen_udp:open(0, [inet6]) of
+ {ok, S} ->
+ ok = gen_udp:close(S),
+ ipv6_init(
+ snmp_test_lib:init_group_top_dir(
+ GroupName,
+ [{manager_net_if_module, snmpm_net_if_mt}
+ | Config]));
+ {error, _} ->
+ {skip, "Host seems to not support IPv6"}
+ end;
_ ->
{skip, "Host does not support IPV6"}
end;
init_per_group(ipv6 = GroupName, Config) ->
case ct:require(ipv6_hosts) of
ok ->
- ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ case gen_udp:open(0, [inet6]) of
+ {ok, S} ->
+ ok = gen_udp:close(S),
+ ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config));
+ {error, _} ->
+ {skip, "Host seems to not support IPv6"}
+ end;
_ ->
{skip, "Host does not support IPV6"}
end;
diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl
index ddbe156130..46c2b316be 100644
--- a/lib/snmp/test/snmp_manager_user.erl
+++ b/lib/snmp/test/snmp_manager_user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -109,7 +109,18 @@ start_link(Parent, Id) ->
proc_lib:start_link(?MODULE, main, [true, Parent, self(), Id]).
stop() ->
- cast(stop).
+ MRef = erlang:monitor(process, ?SERVER),
+ cast(stop),
+ receive {'DOWN', MRef, _, _, Info} ->
+ case Info of
+ noproc ->
+ ok;
+ noconnection ->
+ ok;
+ normal ->
+ ok
+ end
+ end.
info() ->
call(info).
diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
index 6e9c57bce9..2f96493ac5 100644
--- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
+++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl
@@ -19,6 +19,7 @@
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test suite uses the following external programs:
%% snmpget From packet 'snmp' (in Ubuntu 12.04)
+%% snmpd From packet 'snmpd' (in Ubuntu 12.04)
%% snmptrapd From packet 'snmpd' (in Ubuntu 12.04)
%% They originate from the Net-SNMP applications, see:
%% http://net-snmp.sourceforge.net/
@@ -33,6 +34,7 @@
-include_lib("snmp/include/STANDARD-MIB.hrl").
-define(AGENT_ENGINE_ID, "ErlangSnmpAgent").
+-define(MANAGER_ENGINE_ID, "ErlangSnmpManager").
-define(AGENT_PORT, 4000).
-define(MANAGER_PORT, 8989).
-define(DEFAULT_MAX_MESSAGE_SIZE, 484).
@@ -56,22 +58,32 @@ all() ->
groups() ->
[{ipv4, [],
- [{group, get},
- {group, inform}
+ [{group, snmpget},
+ {group, snmptrapd},
+ {group, snmpd_mt},
+ {group, snmpd}
]},
{ipv6, [],
- [{group, get},
- {group, inform}
+ [{group, snmpget},
+ {group, snmptrapd},
+ {group, snmpd_mt},
+ {group, snmpd}
]},
{ipv4_ipv6, [],
- [{group, get},
- {group, inform}
+ [{group, snmpget},
+ {group, snmptrapd},
+ {group, snmpd_mt},
+ {group, snmpd}
]},
%%
- {get, [],
+ {snmpget, [],
[erlang_agent_netsnmp_get]},
- {inform, [],
- [erlang_agent_netsnmp_inform]}
+ {snmptrapd, [],
+ [erlang_agent_netsnmp_inform]},
+ {snmpd_mt, [],
+ [erlang_manager_netsnmp_get]},
+ {snmpd, [],
+ [erlang_manager_netsnmp_get]}
].
init_per_suite(Config) ->
@@ -87,12 +99,22 @@ init_per_group(ipv6, Config) ->
init_per_group(ipv4_ipv6, Config) ->
init_per_group_ipv6([inet, inet6], Config);
%%
-init_per_group(get, Config) ->
+init_per_group(snmpget = Exec, Config) ->
%% From Ubuntu package snmp
- find_executable(snmpget, Config);
-init_per_group(inform, Config) ->
+ init_per_group_agent(Exec, Config);
+init_per_group(snmptrapd = Exec, Config) ->
%% From Ubuntu package snmpd
- find_executable(snmptrapd, Config);
+ init_per_group_agent(Exec, Config);
+init_per_group(snmpd_mt, Config) ->
+ %% From Ubuntu package snmp
+ init_per_group_manager(
+ snmpd,
+ [{manager_net_if_module, snmpm_net_if_mt} | Config]);
+init_per_group(snmpd = Exec, Config) ->
+ %% From Ubuntu package snmp
+ init_per_group_manager(
+ Exec,
+ [{manager_net_if_module, snmpm_net_if} | Config]);
%%
init_per_group(_, Config) ->
Config.
@@ -100,16 +122,20 @@ init_per_group(_, Config) ->
init_per_group_ipv6(Families, Config) ->
case ct:require(ipv6_hosts) of
ok ->
- init_per_group_ip(Families, Config);
+ case gen_udp:open(0, [inet6]) of
+ {ok, S} ->
+ ok = gen_udp:close(S),
+ init_per_group_ip(Families, Config);
+ {error, _} ->
+ {skip, "Host seems to not support IPv6"}
+ end;
_ ->
- {skip, "Host does not support IPV6"}
+ {skip, "Test config ipv6_hosts is missing"}
end.
init_per_group_ip(Families, Config) ->
- Dir = ?config(priv_dir, Config),
AgentPort = ?config(agent_port, Config),
ManagerPort = ?config(manager_port, Config),
- Versions = [v2],
{ok, Host} = inet:gethostname(),
Transports =
[begin
@@ -121,10 +147,22 @@ init_per_group_ip(Families, Config) ->
{ok, Addr} = inet:getaddr(Host, Family),
{domain(Family), {Addr, ManagerPort}}
end || Family <- Families],
+ [{transports, Transports}, {targets, Targets} | Config].
+
+init_per_group_agent(Exec, Config) ->
+ Versions = [v2],
+ Dir = ?config(priv_dir, Config),
+ Transports = ?config(transports, Config),
+ Targets = ?config(targets, Config),
agent_config(Dir, Transports, Targets, Versions),
- [{port, ?AGENT_PORT}, {snmp_versions, Versions},
- {transports, Transports}, {targets, Targets}
- | Config].
+ find_executable(Exec, [{snmp_versions, Versions} | Config]).
+
+init_per_group_manager(Exec, Config) ->
+ Versions = [v2],
+ Dir = ?config(priv_dir, Config),
+ Targets = ?config(targets, Config),
+ manager_config(Dir, Targets),
+ find_executable(Exec, [{snmp_versions, Versions} | Config]).
@@ -132,7 +170,7 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_Case, Config) ->
- Dog = ct:timetrap(10000),
+ Dog = ct:timetrap(20000),
application:stop(snmp),
application:unload(snmp),
[{watchdog, Dog} | Config].
@@ -179,7 +217,12 @@ find_sys_executable(Exec, ExecStr, [Dir | Dirs], Config) ->
start_agent(Config) ->
ok = application:load(snmp),
- ok = application:set_env(snmp, agent, app_env(Config)),
+ ok = application:set_env(snmp, agent, agent_app_env(Config)),
+ ok = application:start(snmp).
+
+start_manager(Config) ->
+ ok = application:load(snmp),
+ ok = application:set_env(snmp, manager, manager_app_env(Config)),
ok = application:start(snmp).
%%--------------------------------------------------------------------
@@ -199,6 +242,39 @@ erlang_agent_netsnmp_get(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
+erlang_manager_netsnmp_get() ->
+ [{doc,"Test that the erlang snmp manager can access snmpnet agent"}].
+
+erlang_manager_netsnmp_get(Config) when is_list(Config) ->
+ Community = "happy-testing",
+ SysDescr = "Net-SNMP agent",
+ TargetName = "Target Net-SNMP agent",
+ Transports = ?config(transports, Config),
+ ProgHandle = start_snmpd(Community, SysDescr, Config),
+ start_manager(Config),
+ snmp_manager_user:start_link(self(), test_user),
+ [snmp_manager_user:register_agent(
+ TargetName++domain_suffix(Domain),
+ [{reg_type, target_name},
+ {tdomain, Domain}, {taddress, Addr},
+ {community, Community}, {engine_id, "EngineId"},
+ {version, v2}, {sec_model, v2c}, {sec_level, noAuthNoPriv}])
+ || {Domain, Addr} <- Transports],
+ Results =
+ [snmp_manager_user:sync_get(
+ TargetName++domain_suffix(Domain),
+ [?sysDescr_instance])
+ || {Domain, _} <- Transports],
+ ct:pal("sync_get -> ~p", [Results]),
+ snmp_manager_user:stop(),
+ stop_program(ProgHandle),
+ [{ok,
+ {noError, 0,
+ [{varbind, ?sysDescr_instance, 'OCTET STRING', SysDescr,1}] },
+ _} = R || R <- Results],
+ ok.
+
+%%--------------------------------------------------------------------
erlang_agent_netsnmp_inform(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Mib = "TestTrapv2",
@@ -267,7 +343,29 @@ start_snmptrapd(Mibs, Config) ->
{ok, StartCheckMP} = re:compile("NET-SNMP version ", [anchored]),
start_program(snmptrapd, SnmptrapdArgs, StartCheckMP, Config).
+start_snmpd(Community, SysDescr, Config) ->
+ DataDir = ?config(data_dir, Config),
+ Targets = ?config(targets, Config),
+ Transports = ?config(transports, Config),
+ Port = mk_port_number(),
+ CommunityArgs =
+ ["--rocommunity"++domain_suffix(Domain)++"="
+ ++Community++" "++inet_parse:ntoa(Ip)
+ || {Domain, {Ip, _}} <- Targets],
+ SnmpdArgs =
+ ["-f", "-r", %"-Dverbose",
+ "-c", filename:join(DataDir, "snmpd.conf"),
+ "-C", "-Lo",
+ "-m", "",
+ "--sysDescr="++SysDescr,
+ "--agentXSocket=tcp:localhost:"++integer_to_list(Port)]
+ ++ CommunityArgs
+ ++ [net_snmp_addr_str(Transports)],
+ {ok, StartCheckMP} = re:compile("NET-SNMP version ", [anchored]),
+ start_program(snmpd, SnmpdArgs, StartCheckMP, Config).
+
start_program(Prog, Args, StartCheckMP, Config) ->
+ ct:pal("Starting program: ~w ~p", [Prog, Args]),
Path = ?config(Prog, Config),
DataDir = ?config(data_dir, Config),
StartWrapper = filename:join(DataDir, "start_stop_wrapper"),
@@ -318,12 +416,13 @@ wait_program_stop({Pid, Mon}) ->
end.
run_program(Parent, StartWrapper, ProgAndArgs) ->
+ [Prog | _] = ProgAndArgs,
Port =
open_port(
{spawn_executable, StartWrapper},
[{args, ProgAndArgs}, binary, stderr_to_stdout, {line, 80},
exit_status]),
- ct:pal("Prog ~p started: ~p", [Port, ProgAndArgs]),
+ ct:pal("Prog ~p started: ~p", [Port, Prog]),
run_program_loop(Parent, Port, []).
run_program_loop(Parent, Port, Buf) ->
@@ -352,7 +451,7 @@ run_program_loop(Parent, Port, Buf) ->
end.
-app_env(Config) ->
+agent_app_env(Config) ->
Dir = ?config(priv_dir, Config),
Vsns = ?config(snmp_versions, Config),
[{versions, Vsns},
@@ -372,6 +471,20 @@ app_env(Config) ->
{note_store, [{verbosity, silence}]},
{net_if, [{verbosity, trace}]}].
+manager_app_env(Config) ->
+ Dir = ?config(priv_dir, Config),
+ Vsns = ?config(snmp_versions, Config),
+ NetIfModule = ?config(manager_net_if_module, Config),
+ [{versions, Vsns},
+ {audit_trail_log, [{type, read_write},
+ {dir, Dir},
+ {size, {10240, 10}}]},
+ {net_if, [{module, NetIfModule}]},
+ {config, [{dir, Dir},
+ {db_dir, Dir},
+ {verbosity, trace}]}
+ ].
+
oid_str([1 | Ints]) ->
"iso." ++ oid_str_tl(Ints);
oid_str(Ints) ->
@@ -384,9 +497,6 @@ oid_str_tl([Int]) ->
oid_str_tl([Int | Ints]) ->
integer_to_list(Int) ++ "." ++ oid_str_tl(Ints).
-agent_config(Dir, Transports, TargetDomain, TargetAddr, Versions) ->
- agent_config(Dir, Transports, [{TargetDomain, TargetAddr}], Versions).
-%%
agent_config(Dir, Transports, Targets, Versions) ->
EngineID = ?AGENT_ENGINE_ID,
MMS = ?DEFAULT_MAX_MESSAGE_SIZE,
@@ -403,6 +513,11 @@ agent_config(Dir, Transports, Targets, Versions) ->
ok = snmp_config:write_agent_snmp_notify_conf(Dir, inform),
ok = snmp_config:write_agent_snmp_vacm_conf(Dir, Versions, none).
+manager_config(Dir, Targets) ->
+ EngineID = ?MANAGER_ENGINE_ID,
+ MMS = ?DEFAULT_MAX_MESSAGE_SIZE,
+ ok = snmp_config:write_manager_snmp_conf(Dir, Targets, MMS, EngineID).
+
net_snmp_version([v3 | _]) ->
"-v3";
net_snmp_version([v2 | _]) ->
@@ -431,3 +546,14 @@ net_snmp_addr_str({transportDomainUdpIpv6, {Addr, Port}}) ->
"udp6:[" ++
inet_parse:ntoa(Addr) ++ "]:" ++
integer_to_list(Port).
+
+domain_suffix(transportDomainUdpIpv4) ->
+ "";
+domain_suffix(transportDomainUdpIpv6) ->
+ "6".
+
+mk_port_number() ->
+ {ok, Socket} = gen_udp:open(0, [{reuseaddr, true}]),
+ {ok, PortNum} = inet:port(Socket),
+ ok = gen_udp:close(Socket),
+ PortNum.
diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE_data/snmpd.conf b/lib/snmp/test/snmp_to_snmpnet_SUITE_data/snmpd.conf
new file mode 100644
index 0000000000..2a5f31680f
--- /dev/null
+++ b/lib/snmp/test/snmp_to_snmpnet_SUITE_data/snmpd.conf
@@ -0,0 +1,12 @@
+sysLocation On the lab network
+sysContact otptest <[email protected]>
+
+createUser myinternaluser SHA dropdead
+
+agentSecName myinternaluser
+
+master agentx
+
+[snmp]
+noPersistentLoad yes
+noPersistentSave yes
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index fd10244386..b436a79076 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -18,6 +18,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.0
+SNMP_VSN = 5.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 60440d3a80..467e2ab27e 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,106 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.0.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Gracefully handle bad data from the client when expecting
+ ssh version exchange.</p>
+ <p>
+ Own Id: OTP-12157 Aux Id: seq12706 </p>
+ </item>
+ <item>
+ <p>
+ When restarting an ssh daemon, that was stopped with
+ ssh:stop_listner/ [1,2] new options given shall replace
+ old ones.</p>
+ <p>
+ Own Id: OTP-12168 Aux Id: seq12711 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ssh now has a format_status function to avoid printing
+ sensitive information in error loggs.</p>
+ <p>
+ Own Id: OTP-12030</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ The option <c>parallel_login</c> didn't work with the
+ value <c>true</c>. All logins were serial.</p>
+ <p>
+ Own Id: OTP-12194</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 3.0.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When starting an ssh-daemon giving the option
+ {parallel_login, true}, the timeout for authentication
+ negotiation ({negotiation_timeout, integer()}) was never
+ removed.</p>
+ <p>
+ This caused the session to always be terminated after the
+ timeout if parallel_login was set.</p>
+ <p>
+ Own Id: OTP-12057 Aux Id: seq12663 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warning: this is experimental and may disappear or change
+ without previous warning.</p>
+ <p>
+ Experimental support for running Quickcheck and PropEr
+ tests from common_test suites is added to common_test.
+ See the reference manual for the new module
+ <c>ct_property_testing</c>.</p>
+ <p>
+ Experimental property tests are added under
+ <c>lib/{inet,ssh}/test/property_test</c>. They can be run
+ directly or from the commont_test suites
+ <c>inet/ftp_property_test_SUITE.erl</c> and
+ <c>ssh/test/ssh_property_test_SUITE.erl</c>.</p>
+ <p>
+ See the code in the <c>test</c> directories and the man
+ page for details.</p>
+ <p>
+ (Thanks to Tuncer Ayaz for a patch adding Triq)</p>
+ <p>
+ Own Id: OTP-12119</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 3.0.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 876eba598a..9f5d1c003d 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -36,8 +36,8 @@
<list type="bulleted">
<item>SSH requires the crypto and public_key applications.</item>
<item>Supported SSH version is 2.0 </item>
- <item>Supported MAC algorithms: hmac-sha1</item>
- <item>Supported encryption algorithms: aes128-cb and 3des-cbc</item>
+ <item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1</item>
+ <item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc</item>
<item>Supports unicode filenames if the emulator and the underlaying OS supports it. See the DESCRIPTION section in <seealso marker="kernel:file">file</seealso> for information about this subject</item>
<item>Supports unicode in shell and cli</item>
</list>
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 2ef2859fd7..53c755d3cb 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -115,7 +115,7 @@ $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 8269f89e40..1917c95f5a 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,25 +19,9 @@
{"%VSN%",
[
- {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
- {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
- {load_module, ssh_acceptor, soft_purge, soft_purge, []},
- {load_module, ssh_message, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
{<<".*">>, [{restart_application, ssh}]}
],
[
- {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
- {"3.0.1", [{load_module, ssh, soft_purge, soft_purge, []},
- {load_module, ssh_acceptor, soft_purge, soft_purge, []},
- {load_module, ssh_message, soft_purge, soft_purge, []},
- {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
{<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 743c01a42c..de047d3c83 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -234,22 +234,26 @@ do_start_daemon(Host, Port, Options, SocketOptions) ->
{port, Port}, {role, server},
{socket_opts, SocketOptions},
{ssh_opts, Options}]) of
- {ok, SysSup} ->
- {ok, SysSup};
{error, {already_started, _}} ->
{error, eaddrinuse};
- {error, R} ->
- {error, R}
+ Result = {Code, _} when (Code == ok) or (Code == error) ->
+ Result
catch
exit:{noproc, _} ->
{error, ssh_not_started}
end;
Sup ->
- case ssh_system_sup:restart_acceptor(Host, Port) of
+ AccPid = ssh_system_sup:acceptor_supervisor(Sup),
+ case ssh_acceptor_sup:start_child(AccPid, [{address, Host},
+ {port, Port}, {role, server},
+ {socket_opts, SocketOptions},
+ {ssh_opts, Options}]) of
+ {error, {already_started, _}} ->
+ {error, eaddrinuse};
{ok, _} ->
{ok, Sup};
- _ ->
- {error, eaddrinuse}
+ Other ->
+ Other
end
end.
@@ -392,7 +396,8 @@ handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
Opt;
handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
is_atom(Function) ->
-
+ Opt;
+handle_ssh_option({exec, Function} = Opt) when is_function(Function) ->
Opt;
handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) ->
Opt;
diff --git a/lib/ssh/src/ssh_acceptor_sup.erl b/lib/ssh/src/ssh_acceptor_sup.erl
index 2be729d305..46fdef07d0 100644
--- a/lib/ssh/src/ssh_acceptor_sup.erl
+++ b/lib/ssh/src/ssh_acceptor_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -26,7 +26,7 @@
-module(ssh_acceptor_sup).
-behaviour(supervisor).
--export([start_link/1, start_child/2, stop_child/2]).
+-export([start_link/1, start_child/2, stop_child/3]).
%% Supervisor callback
-export([init/1]).
@@ -45,18 +45,17 @@ start_child(AccSup, ServerOpts) ->
{error, already_present} ->
Address = proplists:get_value(address, ServerOpts),
Port = proplists:get_value(port, ServerOpts),
- Name = id(Address, Port),
- supervisor:delete_child(?MODULE, Name),
+ stop_child(AccSup, Address, Port),
supervisor:start_child(AccSup, Spec);
Reply ->
Reply
end.
-stop_child(Address, Port) ->
+stop_child(AccSup, Address, Port) ->
Name = id(Address, Port),
- case supervisor:terminate_child(?MODULE, Name) of
+ case supervisor:terminate_child(AccSup, Name) of
ok ->
- supervisor:delete_child(?MODULE, Name);
+ supervisor:delete_child(AccSup, Name);
Error ->
Error
end.
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 77453e8fd7..18841e3d2d 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -457,17 +457,17 @@ bin_to_list(I) when is_integer(I) ->
start_shell(ConnectionHandler, State) ->
Shell = State#state.shell,
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
ShellFun = case is_function(Shell) of
true ->
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
fun() -> Shell(User) end;
{arity, 2} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(User, PeerAddr) end;
_ ->
@@ -485,9 +485,9 @@ start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
State#state{group = Group, buf = empty_buf()};
start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
- ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
+ ConnectionInfo = ssh_connection_handler:connection_info(ConnectionHandler,
[peer, user]),
- {ok, User} =
+ User =
proplists:get_value(user, ConnectionInfo),
ShellFun =
case erlang:fun_info(Shell, arity) of
@@ -496,7 +496,7 @@ start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when is_function
{arity, 2} ->
fun() -> Shell(Cmd, User) end;
{arity, 3} ->
- [{_, PeerAddr}] =
+ {_, PeerAddr} =
proplists:get_value(peer, ConnectionInfo),
fun() -> Shell(Cmd, User, PeerAddr) end;
_ ->
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 8421b07167..9307dbbad0 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-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
@@ -26,6 +26,7 @@
-define(DEFAULT_PACKET_SIZE, 32768).
-define(DEFAULT_WINDOW_SIZE, 2*?DEFAULT_PACKET_SIZE).
-define(DEFAULT_TIMEOUT, 5000).
+-define(MAX_PROTO_VERSION, 255).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index b377614949..33849f4527 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -782,9 +782,8 @@ handle_cli_msg(#connection{channel_cache = Cache} = Connection,
erlang:monitor(process, Pid),
Channel = Channel0#channel{user = Pid},
ssh_channel:cache_update(Cache, Channel),
- Reply = {connection_reply,
- channel_success_msg(RemoteId)},
- {{replies, [{channel_data, Pid, Reply0}, Reply]}, Connection};
+ {Reply, Connection1} = reply_msg(Channel, Connection, Reply0),
+ {{replies, [Reply]}, Connection1};
_Other ->
Reply = {connection_reply,
channel_failure_msg(RemoteId)},
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 86804c4436..4fbc5d0ae2 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -48,7 +48,7 @@
userauth/2, connected/2]).
-export([init/1, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+ handle_sync_event/4, handle_info/3, terminate/3, format_status/2, code_change/4]).
-record(state, {
role,
@@ -71,7 +71,8 @@
connection_queue,
address,
port,
- opts
+ opts,
+ recbuf
}).
-type state_name() :: hello | kexinit | key_exchange | new_keys | userauth | connection.
@@ -103,12 +104,22 @@ start_connection(client = Role, Socket, Options, Timeout) ->
end;
start_connection(server = Role, Socket, Options, Timeout) ->
+ SSH_Opts = proplists:get_value(ssh_opts, Options, []),
try
- case proplists:get_value(parallel_login, Options, false) of
+ case proplists:get_value(parallel_login, SSH_Opts, false) of
true ->
- spawn(fun() -> start_server_connection(Role, Socket, Options, Timeout) end);
+ HandshakerPid =
+ spawn_link(fun() ->
+ receive
+ {do_handshake, Pid} ->
+ handshake(Pid, erlang:monitor(process,Pid), Timeout)
+ end
+ end),
+ ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
+ HandshakerPid ! {do_handshake, ChildPid};
false ->
- start_server_connection(Role, Socket, Options, Timeout)
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
end
catch
exit:{noproc, _} ->
@@ -117,16 +128,14 @@ start_connection(server = Role, Socket, Options, Timeout) ->
{error, Error}
end.
-
-start_server_connection(server = Role, Socket, Options, Timeout) ->
+start_the_connection_child(UserPid, Role, Socket, Options) ->
Sups = proplists:get_value(supervisors, Options),
ConnectionSup = proplists:get_value(connection_sup, Sups),
- Opts = [{supervisors, Sups}, {user_pid, self()} | proplists:get_value(ssh_opts, Options, [])],
+ Opts = [{supervisors, Sups}, {user_pid, UserPid} | proplists:get_value(ssh_opts, Options, [])],
{ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Opts]),
{_, Callback, _} = proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}),
socket_control(Socket, Pid, Callback),
- Ref = erlang:monitor(process, Pid),
- handshake(Pid, Ref, Timeout).
+ Pid.
start_link(Role, Socket, Options) ->
@@ -293,28 +302,39 @@ info(ConnectionHandler, ChannelProcess) ->
hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) ->
VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)),
send_msg(VsnMsg, State),
- inet:setopts(Socket, [{packet, line}, {active, once}]),
- {next_state, hello, State};
+ {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]),
+ inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]),
+ {next_state, hello, State#state{recbuf = Size}};
-hello({info_line, _Line},#state{socket = Socket} = State) ->
+hello({info_line, _Line},#state{role = client, socket = Socket} = State) ->
+ %% The server may send info lines before the version_exchange
inet:setopts(Socket, [{active, once}]),
{next_state, hello, State};
+hello({info_line, _Line},#state{role = server} = State) ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code =
+ ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Did not receive expected protocol version exchange",
+ language = "en"},
+ handle_disconnect(DisconnectMsg, State);
+
hello({version_exchange, Version}, #state{ssh_params = Ssh0,
- socket = Socket} = State) ->
+ socket = Socket,
+ recbuf = Size} = State) ->
{NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
case handle_version(NumVsn, StrVsn, Ssh0) of
{ok, Ssh1} ->
- inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}]),
+ inet:setopts(Socket, [{packet,0}, {mode,binary}, {active, once}, {recbuf, Size}]),
{KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
send_msg(SshPacket, State),
{next_state, kexinit, next_packet(State#state{ssh_params = Ssh,
key_exchange_init_msg =
KeyInitMsg})};
not_supported ->
- DisconnectMsg =
+ DisconnectMsg =
#ssh_msg_disconnect{code =
- ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ ?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
description = "Protocol version " ++ StrVsn
++ " not supported",
language = "en"},
@@ -951,6 +971,36 @@ terminate_subsytem(#connection{system_supervisor = SysSup,
terminate_subsytem(_) ->
ok.
+format_status(normal, [_, State]) ->
+ [{data, [{"StateData", State}]}];
+format_status(terminate, [_, State]) ->
+ SshParams0 = (State#state.ssh_params),
+ SshParams = SshParams0#ssh{c_keyinit = "***",
+ s_keyinit = "***",
+ send_mac_key = "***",
+ send_mac_size = "***",
+ recv_mac_key = "***",
+ recv_mac_size = "***",
+ encrypt_keys = "***",
+ encrypt_ctx = "***",
+ decrypt_keys = "***",
+ decrypt_ctx = "***",
+ compress_ctx = "***",
+ decompress_ctx = "***",
+ shared_secret = "***",
+ exchanged_hash = "***",
+ session_id = "***",
+ keyex_key = "***",
+ keyex_info = "***",
+ available_host_keys = "***"},
+ [{data, [{"StateData", State#state{decoded_data_buffer = "***",
+ encoded_data_buffer = "***",
+ key_exchange_init_msg = "***",
+ opts = "***",
+ recbuf = "***",
+ ssh_params = SshParams
+ }}]}].
+
%%--------------------------------------------------------------------
-spec code_change(OldVsn::term(), state_name(), Oldstate::term(), Extra::term()) ->
{ok, state_name(), #state{}}.
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 848133f838..660fe8bb65 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -173,8 +173,8 @@ ssh_acceptor_sup([_ | Rest]) ->
ssh_acceptor_sup(Rest).
stop_acceptor(Sup) ->
- [Name] =
- [SupName || {SupName, _, _, [ssh_acceptor_sup]} <-
+ [{Name, AcceptorSup}] =
+ [{SupName, ASup} || {SupName, ASup, _, [ssh_acceptor_sup]} <-
supervisor:which_children(Sup)],
- supervisor:terminate_child(Sup, Name).
+ supervisor:terminate_child(AcceptorSup, Name).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 27723dc870..76fa776113 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2004-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
@@ -82,16 +82,21 @@ format_version({Major,Minor}) ->
integer_to_list(Minor) ++ "-Erlang".
handle_hello_version(Version) ->
- StrVersion = trim_tail(Version),
- case string:tokens(Version, "-") of
- [_, "2.0" | _] ->
- {{2,0}, StrVersion};
- [_, "1.99" | _] ->
- {{2,0}, StrVersion};
- [_, "1.3" | _] ->
- {{1,3}, StrVersion};
- [_, "1.5" | _] ->
- {{1,5}, StrVersion}
+ try
+ StrVersion = trim_tail(Version),
+ case string:tokens(Version, "-") of
+ [_, "2.0" | _] ->
+ {{2,0}, StrVersion};
+ [_, "1.99" | _] ->
+ {{2,0}, StrVersion};
+ [_, "1.3" | _] ->
+ {{1,3}, StrVersion};
+ [_, "1.5" | _] ->
+ {{1,5}, StrVersion}
+ end
+ catch
+ error:_ ->
+ {undefined, "unknown version"}
end.
key_exchange_init_msg(Ssh0) ->
@@ -113,15 +118,28 @@ key_init(client, Ssh, Value) ->
key_init(server, Ssh, Value) ->
Ssh#ssh{s_keyinit = Value}.
+available_ssh_algos() ->
+ Supports = crypto:supports(),
+ CipherAlgos = [{aes_ctr, "aes128-ctr"}, {aes_cbc128, "aes128-cbc"}, {des3_cbc, "3des-cbc"}],
+ Ciphers = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- CipherAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))],
+ HashAlgos = [{sha256, "hmac-sha2-256"}, {sha, "hmac-sha1"}],
+ Hashs = [SshAlgo ||
+ {CryptoAlgo, SshAlgo} <- HashAlgos,
+ lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))],
+ {Ciphers, Hashs}.
+
kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -129,14 +147,15 @@ kexinit_messsage(client, Random, Compression, HostKeyAlgs) ->
};
kexinit_messsage(server, Random, Compression, HostKeyAlgs) ->
+ {CipherAlgs, HashAlgs} = available_ssh_algos(),
#ssh_msg_kexinit{
cookie = Random,
kex_algorithms = ["diffie-hellman-group1-sha1"],
server_host_key_algorithms = HostKeyAlgs,
- encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"],
- encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"],
- mac_algorithms_client_to_server = ["hmac-sha1"],
- mac_algorithms_server_to_client = ["hmac-sha1"],
+ encryption_algorithms_client_to_server = CipherAlgs,
+ encryption_algorithms_server_to_client = CipherAlgs,
+ mac_algorithms_client_to_server = HashAlgs,
+ mac_algorithms_server_to_client = HashAlgs,
compression_algorithms_client_to_server = Compression,
compression_algorithms_server_to_client = Compression,
languages_client_to_server = [],
@@ -636,7 +655,21 @@ encrypt_init(#ssh{encrypt = 'aes128-cbc', role = server} = Ssh) ->
<<K:16/binary>> = hash(Ssh, "D", 128),
{ok, Ssh#ssh{encrypt_keys = K,
encrypt_block_size = 16,
- encrypt_ctx = IV}}.
+ encrypt_ctx = IV}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}};
+encrypt_init(#ssh{encrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{encrypt_keys = K,
+ encrypt_block_size = 16,
+ encrypt_ctx = State}}.
encrypt_final(Ssh) ->
{ok, Ssh#ssh{encrypt = none,
@@ -658,7 +691,11 @@ encrypt(#ssh{encrypt = 'aes128-cbc',
encrypt_ctx = IV0} = Ssh, Data) ->
Enc = crypto:block_encrypt(aes_cbc128, K,IV0,Data),
IV = crypto:next_iv(aes_cbc, Enc),
- {Ssh#ssh{encrypt_ctx = IV}, Enc}.
+ {Ssh#ssh{encrypt_ctx = IV}, Enc};
+encrypt(#ssh{encrypt = 'aes128-ctr',
+ encrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_encrypt(State0,Data),
+ {Ssh#ssh{encrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -690,7 +727,21 @@ decrypt_init(#ssh{decrypt = 'aes128-cbc', role = server} = Ssh) ->
hash(Ssh, "C", 128)},
<<K:16/binary>> = KD,
{ok, Ssh#ssh{decrypt_keys = K, decrypt_ctx = IV,
- decrypt_block_size = 16}}.
+ decrypt_block_size = 16}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = client} = Ssh) ->
+ IV = hash(Ssh, "B", 128),
+ <<K:16/binary>> = hash(Ssh, "D", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}};
+decrypt_init(#ssh{decrypt = 'aes128-ctr', role = server} = Ssh) ->
+ IV = hash(Ssh, "A", 128),
+ <<K:16/binary>> = hash(Ssh, "C", 128),
+ State = crypto:stream_init(aes_ctr, K, IV),
+ {ok, Ssh#ssh{decrypt_keys = K,
+ decrypt_block_size = 16,
+ decrypt_ctx = State}}.
decrypt_final(Ssh) ->
@@ -711,7 +762,11 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key,
decrypt_ctx = IV0} = Ssh, Data) ->
Dec = crypto:block_decrypt(aes_cbc128, Key,IV0,Data),
IV = crypto:next_iv(aes_cbc, Data),
- {Ssh#ssh{decrypt_ctx = IV}, Dec}.
+ {Ssh#ssh{decrypt_ctx = IV}, Dec};
+decrypt(#ssh{decrypt = 'aes128-ctr',
+ decrypt_ctx = State0} = Ssh, Data) ->
+ {State, Enc} = crypto:stream_decrypt(State0,Data),
+ {Ssh#ssh{decrypt_ctx = State}, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
@@ -846,7 +901,9 @@ mac('hmac-sha1-96', Key, SeqNum, Data) ->
mac('hmac-md5', Key, SeqNum, Data) ->
crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data]);
mac('hmac-md5-96', Key, SeqNum, Data) ->
- crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96')).
+ crypto:hmac(md5, Key, [<<?UINT32(SeqNum)>>, Data], mac_digest_size('hmac-md5-96'));
+mac('hmac-sha2-256', Key, SeqNum, Data) ->
+ crypto:hmac(sha256, Key, [<<?UINT32(SeqNum)>>, Data]).
%% return N hash bytes (HASH)
hash(SSH, Char, Bits) ->
@@ -911,12 +968,14 @@ mac_key_size('hmac-sha1') -> 20*8;
mac_key_size('hmac-sha1-96') -> 20*8;
mac_key_size('hmac-md5') -> 16*8;
mac_key_size('hmac-md5-96') -> 16*8;
+mac_key_size('hmac-sha2-256')-> 32*8;
mac_key_size(none) -> 0.
mac_digest_size('hmac-sha1') -> 20;
mac_digest_size('hmac-sha1-96') -> 12;
mac_digest_size('hmac-md5') -> 20;
mac_digest_size('hmac-md5-96') -> 12;
+mac_digest_size('hmac-sha2-256') -> 32;
mac_digest_size(none) -> 0.
peer_name({Host, _}) ->
diff --git a/lib/ssh/test/property_test/README b/lib/ssh/test/property_test/README
new file mode 100644
index 0000000000..57602bf719
--- /dev/null
+++ b/lib/ssh/test/property_test/README
@@ -0,0 +1,12 @@
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+The test in this directory are written assuming that the user has a QuickCheck license. They are to be run manually. Some may be possible to be run with other tools, e.g. PropEr.
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server.erl b/lib/ssh/test/property_test/ssh_eqc_client_server.erl
new file mode 100644
index 0000000000..cf895ae85e
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server.erl
@@ -0,0 +1,607 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssh_eqc_client_server).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-ifdef(PROPER).
+%% Proper is not supported.
+-else.
+-ifdef(TRIQ).
+%% Proper is not supported.
+-else.
+
+
+-include_lib("eqc/include/eqc.hrl").
+-include_lib("eqc/include/eqc_statem.hrl").
+-eqc_group_commands(true).
+
+-define(SSH_DIR,"ssh_eqc_client_server_dirs").
+
+-define(sec, *1000).
+-define(min, *60?sec).
+
+-record(srvr,{ref,
+ address,
+ port
+ }).
+
+-record(conn,{ref,
+ srvr_ref
+ }).
+
+-record(chan, {ref,
+ conn_ref,
+ subsystem,
+ client_pid
+ }).
+
+-record(state,{
+ initialized = false,
+ servers = [], % [#srvr{}]
+ clients = [],
+ connections = [], % [#conn{}]
+ channels = [], % [#chan{}]
+ data_dir
+ }).
+
+%%%===============================================================
+%%%
+%%% Specification of addresses, subsystems and such.
+%%%
+
+-define(MAX_NUM_SERVERS, 3).
+-define(MAX_NUM_CLIENTS, 3).
+
+-define(SUBSYSTEMS, ["echo1", "echo2", "echo3", "echo4"]).
+
+-define(SERVER_ADDRESS, { {127,1,1,1}, inet_port({127,1,1,1}) }).
+
+-define(SERVER_EXTRA_OPTIONS, [{parallel_login,bool()}] ).
+
+
+%%%================================================================
+%%%
+%%% The properties - one sequantial and one parallel with the same model
+%%%
+%%% Run as
+%%%
+%%% $ (cd ..; make)
+%%% $ erl -pz ..
+%%%
+%%% eqc:quickcheck( ssh_eqc_client_server:prop_seq() ).
+%%% eqc:quickcheck( ssh_eqc_client_server:prop_parallel() ).
+%%% eqc:quickcheck( ssh_eqc_client_server:prop_parallel_multi() ).
+%%%
+
+
+%% To be called as eqc:quickcheck( ssh_eqc_client_server:prop_seq() ).
+prop_seq() ->
+ do_prop_seq(?SSH_DIR).
+
+%% To be called from a common_test test suite
+prop_seq(CT_Config) ->
+ do_prop_seq(full_path(?SSH_DIR, CT_Config)).
+
+
+do_prop_seq(DataDir) ->
+ ?FORALL(Cmds,commands(?MODULE, #state{data_dir=DataDir}),
+ begin
+ {H,Sf,Result} = run_commands(?MODULE,Cmds),
+ present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
+ end).
+
+full_path(SSHdir, CT_Config) ->
+ filename:join(proplists:get_value(property_dir, CT_Config),
+ SSHdir).
+%%%----
+prop_parallel() ->
+ do_prop_parallel(?SSH_DIR).
+
+%% To be called from a common_test test suite
+prop_parallel(CT_Config) ->
+ do_prop_parallel(full_path(?SSH_DIR, CT_Config)).
+
+do_prop_parallel(DataDir) ->
+ ?FORALL(Cmds,parallel_commands(?MODULE, #state{data_dir=DataDir}),
+ begin
+ {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds),
+ present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
+ end).
+
+%%%----
+prop_parallel_multi() ->
+ do_prop_parallel_multi(?SSH_DIR).
+
+%% To be called from a common_test test suite
+prop_parallel_multi(CT_Config) ->
+ do_prop_parallel_multi(full_path(?SSH_DIR, CT_Config)).
+
+do_prop_parallel_multi(DataDir) ->
+ ?FORALL(Repetitions,?SHRINK(1,[10]),
+ ?FORALL(Cmds,parallel_commands(?MODULE, #state{data_dir=DataDir}),
+ ?ALWAYS(Repetitions,
+ begin
+ {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds),
+ present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
+ end))).
+
+%%%================================================================
+%%% State machine spec
+
+%%% called when using commands/1
+initial_state() ->
+ S = initial_state(#state{}),
+ S#state{initialized=true}.
+
+%%% called when using commands/2
+initial_state(S) ->
+ application:stop(ssh),
+ ssh:start(),
+ setup_rsa(S#state.data_dir).
+
+%%%----------------
+weight(S, ssh_send) -> 5*length([C || C<-S#state.channels, has_subsyst(C)]);
+weight(S, ssh_start_subsyst) -> 3*length([C || C<-S#state.channels, no_subsyst(C)]);
+weight(S, ssh_close_channel) -> 2*length([C || C<-S#state.channels, has_subsyst(C)]);
+weight(S, ssh_open_channel) -> length(S#state.connections);
+weight(_S, _) -> 1.
+
+%%%----------------
+%%% Initialize
+
+initial_state_pre(S) -> not S#state.initialized.
+
+initial_state_args(S) -> [S].
+
+initial_state_next(S, _, _) -> S#state{initialized=true}.
+
+%%%----------------
+%%% Start a new daemon
+%%% Precondition: not more than ?MAX_NUM_SERVERS started
+
+ssh_server_pre(S) -> S#state.initialized andalso
+ length(S#state.servers) < ?MAX_NUM_SERVERS.
+
+ssh_server_args(S) -> [?SERVER_ADDRESS, S#state.data_dir, ?SERVER_EXTRA_OPTIONS].
+
+ssh_server({IP,Port}, DataDir, ExtraOptions) ->
+ ok(ssh:daemon(IP, Port,
+ [
+ {system_dir, system_dir(DataDir)},
+ {user_dir, user_dir(DataDir)},
+ {subsystems, [{SS, {ssh_eqc_subsys, [SS]}} || SS <- ?SUBSYSTEMS]}
+ | ExtraOptions
+ ])).
+
+ssh_server_post(_S, _Args, Result) -> is_ok(Result).
+
+ssh_server_next(S, Result, [{IP,Port},_,_]) ->
+ S#state{servers=[#srvr{ref = Result,
+ address = IP,
+ port = Port}
+ | S#state.servers]}.
+
+%%%----------------
+%%% Start a new client
+%%% Precondition: not more than ?MAX_NUM_CLIENTS started
+
+ssh_client_pre(S) -> S#state.initialized andalso
+ length(S#state.clients) < ?MAX_NUM_CLIENTS.
+
+ssh_client_args(_S) -> [].
+
+ssh_client() -> spawn(fun client_init/0).
+
+ssh_client_next(S, Pid, _) -> S#state{clients=[Pid|S#state.clients]}.
+
+
+client_init() -> client_loop().
+
+client_loop() ->
+ receive
+ {please_do,Fun,Ref,Pid} ->
+ Pid ! {my_pleasure, catch Fun(), Ref},
+ client_loop()
+ end.
+
+do(Pid, Fun) -> do(Pid, Fun, 30?sec).
+
+do(Pid, Fun, Timeout) when is_function(Fun,0) ->
+ Pid ! {please_do,Fun,Ref=make_ref(),self()},
+ receive
+ {my_pleasure, Result, Ref} -> Result
+ after
+ Timeout -> {error,do_timeout}
+ end.
+
+%%%----------------
+%%% Start a new connection
+%%% Precondition: deamon exists
+
+ssh_open_connection_pre(S) -> S#state.servers /= [].
+
+ssh_open_connection_args(S) -> [oneof(S#state.servers), S#state.data_dir].
+
+ssh_open_connection(#srvr{address=Ip, port=Port}, DataDir) ->
+ ok(ssh:connect(ensure_string(Ip), Port,
+ [
+ {silently_accept_hosts, true},
+ {user_dir, user_dir(DataDir)},
+ {user_interaction, false}
+ ])).
+
+ssh_open_connection_post(_S, _Args, Result) -> is_ok(Result).
+
+ssh_open_connection_next(S, ConnRef, [#srvr{ref=SrvrRef},_]) ->
+ S#state{connections=[#conn{ref=ConnRef, srvr_ref=SrvrRef}|S#state.connections]}.
+
+%%%----------------
+%%% Stop a new connection
+%%% Precondition: connection exists
+
+ssh_close_connection_pre(S) -> S#state.connections /= [].
+
+ssh_close_connection_args(S) -> [oneof(S#state.connections)].
+
+ssh_close_connection(#conn{ref=ConnectionRef}) -> ssh:close(ConnectionRef).
+
+ssh_close_connection_next(S, _, [Conn=#conn{ref=ConnRef}]) ->
+ S#state{connections = S#state.connections--[Conn],
+ channels = [C || C <- S#state.channels,
+ C#chan.conn_ref /= ConnRef]
+ }.
+
+%%%----------------
+%%% Start a new channel without a sub system
+%%% Precondition: connection exists
+
+ssh_open_channel_pre(S) -> S#state.connections /= [].
+
+ssh_open_channel_args(S) -> [oneof(S#state.connections)].
+
+%%% For re-arrangement in parallel tests.
+ssh_open_channel_pre(S,[C]) -> lists:member(C,S#state.connections).
+
+ssh_open_channel(#conn{ref=ConnectionRef}) ->
+ ok(ssh_connection:session_channel(ConnectionRef, 20?sec)).
+
+ssh_open_channel_post(_S, _Args, Result) -> is_ok(Result).
+
+ssh_open_channel_next(S, ChannelRef, [#conn{ref=ConnRef}]) ->
+ S#state{channels=[#chan{ref=ChannelRef,
+ conn_ref=ConnRef}
+ | S#state.channels]}.
+
+%%%----------------
+%%% Stop a channel
+%%% Precondition: a channel exists, with or without a subsystem
+
+ssh_close_channel_pre(S) -> S#state.channels /= [].
+
+ssh_close_channel_args(S) -> [oneof(S#state.channels)].
+
+ssh_close_channel(#chan{ref=ChannelRef, conn_ref=ConnectionRef}) ->
+ ssh_connection:close(ConnectionRef, ChannelRef).
+
+ssh_close_channel_next(S, _, [C]) ->
+ S#state{channels = [Ci || Ci <- S#state.channels,
+ sig(C) /= sig(Ci)]}.
+
+
+sig(C) -> {C#chan.ref, C#chan.conn_ref}.
+
+
+%%%----------------
+%%% Start a sub system on a channel
+%%% Precondition: A channel without subsystem exists
+
+ssh_start_subsyst_pre(S) -> lists:any(fun no_subsyst/1, S#state.channels) andalso
+ S#state.clients /= [].
+
+ssh_start_subsyst_args(S) -> [oneof(lists:filter(fun no_subsyst/1, S#state.channels)),
+ oneof(?SUBSYSTEMS),
+ oneof(S#state.clients)
+ ].
+
+%% For re-arrangement in parallel tests.
+ssh_start_subsyst_pre(S, [C|_]) -> lists:member(C,S#state.channels)
+ andalso no_subsyst(C).
+
+ssh_start_subsyst(#chan{ref=ChannelRef, conn_ref=ConnectionRef}, SubSystem, Pid) ->
+ do(Pid, fun()->ssh_connection:subsystem(ConnectionRef, ChannelRef, SubSystem, 120?sec) end).
+
+ssh_start_subsyst_post(_S, _Args, Result) -> Result==success.
+
+ssh_start_subsyst_next(S, _Result, [C,SS,Pid|_]) ->
+ S#state{channels = [C#chan{subsystem=SS,
+ client_pid=Pid}|(S#state.channels--[C])] }.
+
+%%%----------------
+%%% Send a message on a channel
+%%% Precondition: a channel exists with a subsystem connected
+
+ssh_send_pre(S) -> lists:any(fun has_subsyst/1, S#state.channels).
+
+ssh_send_args(S) -> [oneof(lists:filter(fun has_subsyst/1, S#state.channels)),
+ choose(0,1),
+ message()].
+
+%% For re-arrangement in parallel tests.
+ssh_send_pre(S, [C|_]) -> lists:member(C, S#state.channels).
+
+ssh_send(C=#chan{conn_ref=ConnectionRef, ref=ChannelRef, client_pid=Pid}, Type, Msg) ->
+ do(Pid,
+ fun() ->
+ case ssh_connection:send(ConnectionRef, ChannelRef, Type, modify_msg(C,Msg), 10?sec) of
+ ok ->
+ receive
+ {ssh_cm,ConnectionRef,{data,ChannelRef,Type,Answer}} -> Answer
+ after 15?sec ->
+ %% receive
+ %% Other -> {error,{unexpected,Other}}
+ %% after 0 ->
+ {error,receive_timeout}
+ %% end
+ end;
+ Other ->
+ Other
+ end
+ end).
+
+ssh_send_blocking(_S, _Args) ->
+ true.
+
+ssh_send_post(_S, [C,_,Msg], Response) when is_binary(Response) ->
+ Expected = ssh_eqc_subsys:response(modify_msg(C,Msg), C#chan.subsystem),
+ case Response of
+ Expected -> true;
+ _ -> {send_failed, size(Response), size(Expected)}
+ end;
+
+ssh_send_post(_S, _Args, Response) ->
+ {error,Response}.
+
+
+modify_msg(_, <<>>) -> <<>>;
+modify_msg(#chan{subsystem=SS}, Msg) -> <<(list_to_binary(SS))/binary,Msg/binary>>.
+
+%%%================================================================
+%%% Misc functions
+
+message() ->
+ resize(500, binary()).
+
+ %% binary().
+
+ %% oneof([binary(),
+ %% ?LET(Size, choose(0,10000), binary(Size))
+ %% ]).
+
+has_subsyst(C) -> C#chan.subsystem /= undefined.
+
+no_subsyst(C) -> not has_subsyst(C).
+
+
+ok({ok,X}) -> X;
+ok({error,Err}) -> {error,Err}.
+
+is_ok({error,_}) -> false;
+is_ok(_) -> true.
+
+ensure_string({A,B,C,D}) -> lists:flatten(io_lib:format("~w.~w.~w.~w",[A,B,C,D]));
+ensure_string(X) -> X.
+
+%%%----------------------------------------------------------------
+present_result(_Module, Cmds, _Triple, true) ->
+ aggregate(with_title("Distribution sequential/parallel"), sequential_parallel(Cmds),
+ aggregate(with_title("Function calls"), cmnd_names(Cmds),
+ aggregate(with_title("Message sizes"), empty_msgs(Cmds),
+ aggregate(print_frequencies(), message_sizes(Cmds),
+ aggregate(title("Length of command sequences",print_frequencies()), num_calls(Cmds),
+ true)))));
+
+present_result(Module, Cmds, Triple, false) ->
+ pretty_commands(Module, Cmds, Triple, [{show_states,true}], false).
+
+
+
+cmnd_names(Cs) -> traverse_commands(fun cmnd_name/1, Cs).
+cmnd_name(L) -> [F || {set,_Var,{call,_Mod,F,_As}} <- L].
+
+empty_msgs(Cs) -> traverse_commands(fun empty_msg/1, Cs).
+empty_msg(L) -> [empty || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L,
+ size(Msg)==0].
+
+message_sizes(Cs) -> traverse_commands(fun message_size/1, Cs).
+message_size(L) -> [size(Msg) || {set,_,{call,_,ssh_send,[_,_,Msg]}} <- L].
+
+num_calls(Cs) -> traverse_commands(fun num_call/1, Cs).
+num_call(L) -> [length(L)].
+
+sequential_parallel(Cs) ->
+ traverse_commands(fun(L) -> dup_module(L, sequential) end,
+ fun(L) -> [dup_module(L1, mkmod("parallel",num(L1,L))) || L1<-L] end,
+ Cs).
+dup_module(L, ModName) -> lists:duplicate(length(L), ModName).
+mkmod(PfxStr,N) -> list_to_atom(PfxStr++"_"++integer_to_list(N)).
+
+%% Meta functions for the aggregate functions
+traverse_commands(Fun, L) when is_list(L) -> Fun(L);
+traverse_commands(Fun, {Seq, ParLs}) -> Fun(lists:append([Seq|ParLs])).
+
+traverse_commands(Fseq, _Fpar, L) when is_list(L) -> Fseq(L);
+traverse_commands(Fseq, Fpar, {Seq, ParLs}) -> lists:append([Fseq(Seq)|Fpar(ParLs)]).
+
+%%%----------------
+%% PrintMethod([{term(), int()}]) -> any().
+print_frequencies() -> print_frequencies(10).
+
+print_frequencies(Ngroups) -> fun([]) -> io:format('Empty list!~n',[]);
+ (L ) -> print_frequencies(L,Ngroups,0,element(1,lists:last(L)))
+ end.
+
+print_frequencies(Ngroups, MaxValue) -> fun(L) -> print_frequencies(L,Ngroups,0,MaxValue) end.
+
+print_frequencies(L, N, Min, Max) when N>Max -> print_frequencies(L++[{N,0}], N, Min, N);
+print_frequencies(L, N, Min, Max) ->
+%%io:format('L=~p~n',[L]),
+ try
+ IntervalUpperLimits =
+ lists:reverse(
+ [Max | tl(lists:reverse(lists:seq(Min,Max,round((Max-Min)/N))))]
+ ),
+ {Acc0,_} = lists:mapfoldl(fun(Upper,Lower) ->
+ {{{Lower,Upper},0}, Upper+1}
+ end, hd(IntervalUpperLimits), tl(IntervalUpperLimits)),
+ Fs0 = get_frequencies(L, Acc0),
+ SumVal = lists:sum([V||{_,V}<-Fs0]),
+ Fs = with_percentage(Fs0, SumVal),
+ Mean = mean(L),
+ Median = median(L),
+ Npos_value = num_digits(SumVal),
+ Npos_range = num_digits(Max),
+ io:format("Range~*s: ~s~n",[2*Npos_range-2,"", "Number in range"]),
+ io:format("~*c:~*c~n",[2*Npos_range+3,$-, max(16,Npos_value+10),$- ]),
+ [begin
+ io:format("~*w - ~*w: ~*w ~5.1f%",[Npos_range,Rlow,
+ Npos_range,Rhigh,
+ Npos_value,Val,
+ Percent]),
+ [io:format(" <-- mean=~.1f",[Mean]) || in_interval(Mean, Interval)],
+ [io:format(" <-- median=" ++
+ if
+ is_float(Median) -> "~.1f";
+ true -> "~p"
+ end, [Median]) || in_interval(Median, Interval)],
+ io:nl()
+ end
+ || {Interval={Rlow,Rhigh},Val,Percent} <- Fs],
+ io:format('~*c ~*c~n',[2*Npos_range,32,Npos_value+2,$-]),
+ io:format('~*c ~*w~n',[2*Npos_range,32,Npos_value,SumVal])
+ %%,io:format('L=~p~n',[L])
+ catch
+ C:E ->
+ io:format('*** Faild printing (~p:~p) for~n~p~n',[C,E,L])
+ end.
+
+get_frequencies([{I,Num}|T], [{{Lower,Upper},Cnt}|Acc]) when Lower=<I,I=<Upper ->
+ get_frequencies(T, [{{Lower,Upper},Cnt+Num}|Acc]);
+get_frequencies(L=[{I,_Num}|_], [Ah={{_Lower,Upper},_Cnt}|Acc]) when I>Upper ->
+ [Ah | get_frequencies(L,Acc)];
+get_frequencies([], Acc) ->
+ Acc.
+
+with_percentage(Fs, Sum) ->
+ [{Rng,Val,100*Val/Sum} || {Rng,Val} <- Fs].
+
+
+title(Str, Fun) ->
+ fun(L) ->
+ io:format('~s~n',[Str]),
+ Fun(L)
+ end.
+
+num_digits(I) -> 1+trunc(math:log(I)/math:log(10)).
+
+num(Elem, List) -> length(lists:takewhile(fun(E) -> E /= Elem end, List)) + 1.
+
+%%%---- Just for naming an operation for readability
+is_odd(I) -> (I rem 2) == 1.
+
+in_interval(Value, {Rlow,Rhigh}) ->
+ try
+ Rlow=<round(Value) andalso round(Value)=<Rhigh
+ catch
+ _:_ -> false
+ end.
+
+%%%================================================================
+%%% Statistical functions
+
+%%%---- Mean value
+mean(L = [X|_]) when is_number(X) ->
+ lists:sum(L) / length(L);
+mean(L = [{_Value,_Weight}|_]) ->
+ SumOfWeights = lists:sum([W||{_,W}<-L]),
+ WeightedSum = lists:sum([W*V||{V,W}<-L]),
+ WeightedSum / SumOfWeights;
+mean(_) ->
+ undefined.
+
+%%%---- Median
+median(L = [X|_]) when is_number(X) ->
+ case is_odd(length(L)) of
+ true ->
+ hd(lists:nthtail(length(L) div 2, L));
+ false ->
+ %% 1) L has at least on element (the when test).
+ %% 2) Length is even.
+ %% => Length >= 2
+ [M1,M2|_] = lists:nthtail((length(L) div 2)-1, L),
+ (M1+M2) / 2
+ end;
+%% integer Weights...
+median(L = [{_Value,_Weight}|_]) ->
+ median( lists:append([lists:duplicate(W,V) || {V,W} <- L]) );
+median(_) ->
+ undefined.
+
+%%%================================================================
+%%% The rest is taken and modified from ssh_test_lib.erl
+inet_port(IpAddress)->
+ {ok, Socket} = gen_tcp:listen(0, [{ip,IpAddress},{reuseaddr,true}]),
+ {ok, Port} = inet:port(Socket),
+ gen_tcp:close(Socket),
+ Port.
+
+setup_rsa(Dir) ->
+ erase_dir(system_dir(Dir)),
+ erase_dir(user_dir(Dir)),
+ file:make_dir(system_dir(Dir)),
+ file:make_dir(user_dir(Dir)),
+
+ file:copy(data_dir(Dir,"id_rsa"), user_dir(Dir,"id_rsa")),
+ file:copy(data_dir(Dir,"ssh_host_rsa_key"), system_dir(Dir,"ssh_host_rsa_key")),
+ file:copy(data_dir(Dir,"ssh_host_rsa_key"), system_dir(Dir,"ssh_host_rsa_key.pub")),
+ ssh_test_lib:setup_rsa_known_host(data_dir(Dir), user_dir(Dir)),
+ ssh_test_lib:setup_rsa_auth_keys(data_dir(Dir), user_dir(Dir)).
+
+data_dir(Dir, File) -> filename:join(Dir, File).
+system_dir(Dir, File) -> filename:join([Dir, "system", File]).
+user_dir(Dir, File) -> filename:join([Dir, "user", File]).
+
+data_dir(Dir) -> Dir.
+system_dir(Dir) -> system_dir(Dir,"").
+user_dir(Dir) -> user_dir(Dir,"").
+
+erase_dir(Dir) ->
+ case file:list_dir(Dir) of
+ {ok,Files} -> lists:foreach(fun(F) -> file:delete(filename:join(Dir,F)) end,
+ Files);
+ _ -> ok
+ end,
+ file:del_dir(Dir).
+
+-endif.
+-endif.
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_dsa b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_dsa
new file mode 100644
index 0000000000..d306f8b26e
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_dsa
@@ -0,0 +1,13 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQDfi2flSTZZofwT4yQT0NikX/LGNT7UPeB/XEWe/xovEYCElfaQ
+APFixXvEgXwoojmZ5kiQRKzLM39wBP0jPERLbnZXfOOD0PDnw0haMh7dD7XKVMod
+/EigVgHf/qBdM2M8yz1s/rRF7n1UpLSypziKjkzCm7JoSQ2zbWIPdmBIXwIVAMgP
+kpr7Sq3O7sHdb8D601DRjoExAoGAMOQxDfB2Fd8ouz6G96f/UOzRMI/Kdv8kYYKW
+JIGY+pRYrLPyYzUeJznwZreOJgrczAX+luHnKFWJ2Dnk5CyeXk67Wsr7pJ/4MBMD
+OKeIS0S8qoSBN8+Krp79fgA+yS3IfqbkJLtLu4EBaCX4mKQIX4++k44d4U5lc8pt
++9hlEI8CgYEAznKxx9kyC6bVo7LUYKaGhofRFt0SYFc5PVmT2VUGRs1R6+6DPD+e
+uEO6IhFct7JFSRbP9p0JD4Uk+3zlZF+XX6b2PsZkeV8f/02xlNGUSmEzCSiNg1AX
+Cy/WusYhul0MncWCHMcOZB5rIvU/aP5EJJtn3xrRaz6u0SThF6AnT34CFQC63czE
+ZU8w8Q+H7z0j+a+70x2iAw==
+-----END DSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_rsa b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_rsa
new file mode 100644
index 0000000000..9d7e0dd5fb
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/id_rsa
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQD1OET+3O/Bvj/dtjxDTXmj1oiJt4sIph5kGy0RfjoPrZfaS+CU
+DhakCmS6t2ivxWFgtpKWaoGMZMJqWj6F6ZsumyFl3FPBtujwY/35cgifrI9Ns4Tl
+zR1uuengNBmV+WRQ5cd9F2qS6Z8aDQihzt0r8JUqLcK+VQbrmNzboCCQQwIDAQAB
+AoGAPQEyqPTt8JUT7mRXuaacjFXiweAXhp9NEDpyi9eLOjtFe9lElZCrsUOkq47V
+TGUeRKEm9qSodfTbKPoqc8YaBJGJPhUaTAcha+7QcDdfHBvIsgxvU7ePVnlpXRp3
+CCUEMPhlnx6xBoTYP+fRU0e3+xJIPVyVCqX1jAdUMkzfRoECQQD6ux7B1QJAIWyK
+SGkbDUbBilNmzCFNgIpOP6PA+bwfi5d16diTpra5AX09keQABAo/KaP1PdV8Vg0p
+z4P3A7G3AkEA+l+AKG6m0kQTTBMJDqOdVPYwe+5GxunMaqmhokpEbuGsrZBl5Dvd
+WpcBjR7jmenrhKZRIuA+Fz5HPo/UQJPl1QJBAKxstDkeED8j/S2XoFhPKAJ+6t39
+sUVICVTIZQeXdmzHJXCcUSkw8+WEhakqw/3SyW0oaK2FSWQJFWJUZ+8eJj8CQEh3
+xeduB5kKnS9CvzdeghZqX6QvVosSdtlUmfUYW/BgH5PpHKTP8wTaeld3XldZTpMJ
+dKiMkUw2+XYROVUrubUCQD+Na1LhULlpn4ISEtIEfqpdlUhxDgO15Wg8USmsng+x
+ICliVOSQtwaZjm8kwaFt0W7XnpnDxbRs37vIEbIMWak=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key
new file mode 100644
index 0000000000..51ab6fbd88
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key
@@ -0,0 +1,13 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCClaHzE2ul0gKSUxah5W0W8UiJLy4hXngKEqpaUq9SSdVdY2LK
+wVfKH1gt5iuaf1FfzOhsIC9G/GLnjYttXZc92cv/Gfe3gR+s0ni2++MX+T++mE/Q
+diltXv/Hp27PybS67SmiFW7I+RWnT2OKlMPtw2oUuKeztCe5UWjaj/y5FQIVAPLA
+l9RpiU30Z87NRAHY3NTRaqtrAoGANMRxw8UfdtNVR0CrQj3AgPaXOGE4d+G4Gp4X
+skvnCHycSVAjtYxebUkzUzt5Q6f/IabuLUdge3gXrc8BetvrcKbp+XZgM0/Vj2CF
+Ymmy3in6kzGZq7Fw1sZaku6AOU8vLa5woBT2vAcHLLT1bLAzj7viL048T6MfjrOP
+ef8nHvACgYBhDWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah
+/XcF3DeRF+eEoz48wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+U
+ykSTXYUbtsfTNRFQGBW2/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0CgIVAN4wtL5W
+Lv62jKcdskxNyz2NQoBx
+-----END DSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key.pub b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..4dbb1305b0
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_dsa_key.pub
@@ -0,0 +1,11 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1kc3MAAACBAIKVofMTa6XSApJTFqHlbRbxSIkvLiFeeAoSqlpSr1JJ1V1j
+YsrBV8ofWC3mK5p/UV/M6GwgL0b8YueNi21dlz3Zy/8Z97eBH6zSeLb74xf5P76YT9B2
+KW1e/8enbs/JtLrtKaIVbsj5FadPY4qUw+3DahS4p7O0J7lRaNqP/LkVAAAAFQDywJfU
+aYlN9GfOzUQB2NzU0WqrawAAAIA0xHHDxR9201VHQKtCPcCA9pc4YTh34bganheyS+cI
+fJxJUCO1jF5tSTNTO3lDp/8hpu4tR2B7eBetzwF62+twpun5dmAzT9WPYIViabLeKfqT
+MZmrsXDWxlqS7oA5Ty8trnCgFPa8BwcstPVssDOPu+IvTjxPox+Os495/yce8AAAAIBh
+DWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah/XcF3DeRF+eEoz48
+wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+UykSTXYUbtsfTNRFQGBW2
+/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0Cg==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key
new file mode 100644
index 0000000000..79968bdd7d
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
+zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
+6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
+AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
+NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
+udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
+WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
+n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
+sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
+64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
+m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
+tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key.pub b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..75d2025c71
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server_dirs/ssh_host_rsa_key.pub
@@ -0,0 +1,5 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
+semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
+RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
new file mode 100644
index 0000000000..34630bdc91
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -0,0 +1,397 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssh_eqc_encode_decode).
+
+-compile(export_all).
+
+-proptest(eqc).
+-proptest([triq,proper]).
+
+-include_lib("ct_property_test.hrl").
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-ifndef(TRIQ).
+-define(EQC,true).
+%%-define(PROPER,true).
+%%-define(TRIQ,true).
+-endif.
+-endif.
+-endif.
+
+-ifdef(EQC).
+-include_lib("eqc/include/eqc.hrl").
+-define(MOD_eqc,eqc).
+
+-else.
+-ifdef(PROPER).
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-else.
+-ifdef(TRIQ).
+-define(MOD_eqc,triq).
+-include_lib("triq/include/triq.hrl").
+
+-endif.
+-endif.
+-endif.
+
+
+%%% Properties:
+
+prop_ssh_decode() ->
+ ?FORALL(Msg, ssh_msg(),
+ try ssh_message:decode(Msg)
+ of
+ _ -> true
+ catch
+ C:E -> io:format('~p:~p~n',[C,E]),
+ false
+ end
+ ).
+
+
+%%% This fails because ssh_message is not symmetric in encode and decode regarding data types
+prop_ssh_decode_encode() ->
+ ?FORALL(Msg, ssh_msg(),
+ Msg == ssh_message:encode(ssh_message:decode(Msg))
+ ).
+
+
+%%%================================================================
+%%%
+%%% Scripts to generate message generators
+%%%
+
+%% awk '/^( |\t)+byte( |\t)+SSH/,/^( |\t)*$/{print}' rfc425?.txt | sed 's/^\( \|\\t\)*//' > msgs.txt
+
+%% awk '/^byte( |\t)+SSH/{print $2","}' < msgs.txt
+
+%% awk 'BEGIN{print "%%%---- BEGIN GENERATED";prev=0} END{print " >>.\n%%%---- END GENERATED"} /^byte( |\t)+SSH/{if (prev==1) print " >>.\n"; prev=1; printf "%c%s%c",39,$2,39; print "()->\n <<?"$2;next} /^string( |\t)+\"/{print " ,"$2;next} /^string( |\t)+.*address/{print " ,(ssh_string_address())/binary %%",$2,$3,$4,$5,$6;next}/^string( |\t)+.*US-ASCII/{print " ,(ssh_string_US_ASCII())/binary %%",$2,$3,$4,$5,$6;next} /^string( |\t)+.*UTF-8/{print " ,(ssh_string_UTF_8())/binary %% ",$2,$3,$4,$5,$6;next} /^[a-z0-9]+( |\t)/{print " ,(ssh_"$1"())/binary %%",$2,$3,$4,$5,$6;next} /^byte\[16\]( |\t)+/{print" ,(ssh_byte_16())/binary %%",$2,$3,$4,$5,$6;next} /^name-list( |\t)+/{print" ,(ssh_name_list())/binary %%",$2,$3,$4,$5,$6;next} /./{print "?? %%",$0}' < msgs.txt > gen.txt
+
+%%%================================================================
+%%%
+%%% Generators
+%%%
+
+ssh_msg() -> ?LET(M,oneof(
+[[msg_code('SSH_MSG_CHANNEL_CLOSE'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_DATA'),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_EOF'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_EXTENDED_DATA'),gen_uint32(),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_FAILURE'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("direct-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("forwarded-tcpip"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("session"),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string("x11"),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN'),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN_CONFIRMATION'),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_OPEN_FAILURE'),gen_uint32(),gen_uint32(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("env"),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exec"),gen_boolean(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-signal"),0,gen_string( ),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("exit-status"),0,gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("pty-req"),gen_boolean(),gen_string( ),gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("shell"),gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("signal"),0,gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("subsystem"),gen_boolean(),gen_string( )],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("window-change"),0,gen_uint32(),gen_uint32(),gen_uint32(),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("x11-req"),gen_boolean(),gen_boolean(),gen_string( ),gen_string( ),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string("xon-xoff"),0,gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_REQUEST'),gen_uint32(),gen_string( ),gen_boolean()],
+ [msg_code('SSH_MSG_CHANNEL_SUCCESS'),gen_uint32()],
+ [msg_code('SSH_MSG_CHANNEL_WINDOW_ADJUST'),gen_uint32(),gen_uint32()],
+%%Assym [msg_code('SSH_MSG_DEBUG'),gen_boolean(),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_DISCONNECT'),gen_uint32(),gen_string( ),gen_string( )],
+%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("cancel-tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
+%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string("tcpip-forward"),gen_boolean(),gen_string( ),gen_uint32()],
+%%Assym [msg_code('SSH_MSG_GLOBAL_REQUEST'),gen_string( ),gen_boolean()],
+ [msg_code('SSH_MSG_IGNORE'),gen_string( )],
+ %% [msg_code('SSH_MSG_KEXDH_INIT'),gen_mpint()],
+ %% [msg_code('SSH_MSG_KEXDH_REPLY'),gen_string( ),gen_mpint(),gen_string( )],
+ %% [msg_code('SSH_MSG_KEXINIT'),gen_byte(16),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_name_list(),gen_boolean(),gen_uint32()],
+ [msg_code('SSH_MSG_KEX_DH_GEX_GROUP'),gen_mpint(),gen_mpint()],
+ [msg_code('SSH_MSG_NEWKEYS')],
+ [msg_code('SSH_MSG_REQUEST_FAILURE')],
+ [msg_code('SSH_MSG_REQUEST_SUCCESS')],
+ [msg_code('SSH_MSG_REQUEST_SUCCESS'),gen_uint32()],
+ [msg_code('SSH_MSG_SERVICE_ACCEPT'),gen_string( )],
+ [msg_code('SSH_MSG_SERVICE_REQUEST'),gen_string( )],
+ [msg_code('SSH_MSG_UNIMPLEMENTED'),gen_uint32()],
+ [msg_code('SSH_MSG_USERAUTH_BANNER'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_FAILURE'),gen_name_list(),gen_boolean()],
+ [msg_code('SSH_MSG_USERAUTH_PASSWD_CHANGEREQ'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_PK_OK'),gen_string( ),gen_string( )],
+ [msg_code('SSH_MSG_USERAUTH_SUCCESS')]
+]
+
+), list_to_binary(M)).
+
+
+%%%================================================================
+%%%
+%%% Generator
+%%%
+
+do() ->
+ io_lib:format('[~s~n]',
+ [write_gen(
+ files(["rfc4254.txt",
+ "rfc4253.txt",
+ "rfc4419.txt",
+ "rfc4252.txt",
+ "rfc4256.txt"]))]).
+
+
+write_gen(L) when is_list(L) ->
+ string:join(lists:map(fun write_gen/1, L), ",\n ");
+write_gen({MsgName,Args}) ->
+ lists:flatten(["[",generate_args([MsgName|Args]),"]"]).
+
+generate_args(As) -> string:join([generate_arg(A) || A <- As], ",").
+
+generate_arg({<<"string">>, <<"\"",B/binary>>}) ->
+ S = get_string($",B),
+ ["gen_string(\"",S,"\")"];
+generate_arg({<<"string">>, _}) -> "gen_string( )";
+generate_arg({<<"byte[",B/binary>>, _}) ->
+ io_lib:format("gen_byte(~p)",[list_to_integer(get_string($],B))]);
+generate_arg({<<"byte">> ,_}) -> "gen_byte()";
+generate_arg({<<"uint16">>,_}) -> "gen_uint16()";
+generate_arg({<<"uint32">>,_}) -> "gen_uint32()";
+generate_arg({<<"uint64">>,_}) -> "gen_uint64()";
+generate_arg({<<"mpint">>,_}) -> "gen_mpint()";
+generate_arg({<<"name-list">>,_}) -> "gen_name_list()";
+generate_arg({<<"boolean">>,<<"FALSE">>}) -> "0";
+generate_arg({<<"boolean">>,<<"TRUE">>}) -> "1";
+generate_arg({<<"boolean">>,_}) -> "gen_boolean()";
+generate_arg({<<"....">>,_}) -> ""; %% FIXME
+generate_arg(Name) when is_binary(Name) ->
+ lists:flatten(["msg_code('",binary_to_list(Name),"')"]).
+
+
+gen_boolean() -> choose(0,1).
+
+gen_byte() -> choose(0,255).
+
+gen_uint16() -> gen_byte(2).
+
+gen_uint32() -> gen_byte(4).
+
+gen_uint64() -> gen_byte(8).
+
+gen_byte(N) when N>0 -> [gen_byte() || _ <- lists:seq(1,N)].
+
+gen_char() -> choose($a,$z).
+
+gen_mpint() -> ?LET(Size, choose(1,20),
+ ?LET(Str, vector(Size, gen_byte()),
+ gen_string( strip_0s(Str) )
+ )).
+
+strip_0s([0|T]) -> strip_0s(T);
+strip_0s(X) -> X.
+
+
+gen_string() ->
+ ?LET(Size, choose(0,10),
+ ?LET(Vector,vector(Size, gen_char()),
+ gen_string(Vector)
+ )).
+
+gen_string(S) when is_binary(S) -> gen_string(binary_to_list(S));
+gen_string(S) when is_list(S) -> uint32_to_list(length(S)) ++ S.
+
+gen_name_list() ->
+ ?LET(NumNames, choose(0,10),
+ ?LET(L, [gen_name() || _ <- lists:seq(1,NumNames)],
+ gen_string( string:join(L,"," ) )
+ )).
+
+gen_name() -> gen_string().
+
+uint32_to_list(I) -> binary_to_list(<<I:32/unsigned-big-integer>>).
+
+%%%----
+get_string(Delim, B) ->
+ binary_to_list( element(1, split_binary(B, count_string_chars(Delim,B,0))) ).
+
+count_string_chars(Delim, <<Delim,_/binary>>, Acc) -> Acc;
+count_string_chars(Delim, <<_,B/binary>>, Acc) -> count_string_chars(Delim, B, Acc+1).
+
+
+-define(MSG_CODE(Name,Num),
+msg_code(Name) -> Num;
+msg_code(Num) -> Name
+).
+
+?MSG_CODE('SSH_MSG_USERAUTH_REQUEST', 50);
+?MSG_CODE('SSH_MSG_USERAUTH_FAILURE', 51);
+?MSG_CODE('SSH_MSG_USERAUTH_SUCCESS', 52);
+?MSG_CODE('SSH_MSG_USERAUTH_BANNER', 53);
+?MSG_CODE('SSH_MSG_USERAUTH_PK_OK', 60);
+?MSG_CODE('SSH_MSG_USERAUTH_PASSWD_CHANGEREQ', 60);
+?MSG_CODE('SSH_MSG_DISCONNECT', 1);
+?MSG_CODE('SSH_MSG_IGNORE', 2);
+?MSG_CODE('SSH_MSG_UNIMPLEMENTED', 3);
+?MSG_CODE('SSH_MSG_DEBUG', 4);
+?MSG_CODE('SSH_MSG_SERVICE_REQUEST', 5);
+?MSG_CODE('SSH_MSG_SERVICE_ACCEPT', 6);
+?MSG_CODE('SSH_MSG_KEXINIT', 20);
+?MSG_CODE('SSH_MSG_NEWKEYS', 21);
+?MSG_CODE('SSH_MSG_GLOBAL_REQUEST', 80);
+?MSG_CODE('SSH_MSG_REQUEST_SUCCESS', 81);
+?MSG_CODE('SSH_MSG_REQUEST_FAILURE', 82);
+?MSG_CODE('SSH_MSG_CHANNEL_OPEN', 90);
+?MSG_CODE('SSH_MSG_CHANNEL_OPEN_CONFIRMATION', 91);
+?MSG_CODE('SSH_MSG_CHANNEL_OPEN_FAILURE', 92);
+?MSG_CODE('SSH_MSG_CHANNEL_WINDOW_ADJUST', 93);
+?MSG_CODE('SSH_MSG_CHANNEL_DATA', 94);
+?MSG_CODE('SSH_MSG_CHANNEL_EXTENDED_DATA', 95);
+?MSG_CODE('SSH_MSG_CHANNEL_EOF', 96);
+?MSG_CODE('SSH_MSG_CHANNEL_CLOSE', 97);
+?MSG_CODE('SSH_MSG_CHANNEL_REQUEST', 98);
+?MSG_CODE('SSH_MSG_CHANNEL_SUCCESS', 99);
+?MSG_CODE('SSH_MSG_CHANNEL_FAILURE', 100);
+?MSG_CODE('SSH_MSG_USERAUTH_INFO_REQUEST', 60);
+?MSG_CODE('SSH_MSG_USERAUTH_INFO_RESPONSE', 61);
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_REQUEST_OLD', 30);
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_REQUEST', 34);
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_GROUP', 31);
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_INIT', 32);
+?MSG_CODE('SSH_MSG_KEX_DH_GEX_REPLY', 33).
+
+%%%=============================================================================
+%%%=============================================================================
+%%%=============================================================================
+
+files(Fs) ->
+ Defs = lists:usort(lists:flatten(lists:map(fun file/1, Fs))),
+ DefinedIDs = lists:usort([binary_to_list(element(1,D)) || D <- Defs]),
+ WantedIDs = lists:usort(wanted_messages()),
+ Missing = WantedIDs -- DefinedIDs,
+ case Missing of
+ [] -> ok;
+ _ -> io:format('%% Warning: missing ~p~n', [Missing])
+ end,
+ Defs.
+
+
+file(F) ->
+ {ok,B} = file:read_file(F),
+ hunt_msg_def(B).
+
+
+hunt_msg_def(<<"\n",B/binary>>) -> some_hope(skip_blanks(B));
+hunt_msg_def(<<_, B/binary>>) -> hunt_msg_def(B);
+hunt_msg_def(<<>>) -> [].
+
+some_hope(<<"byte ", B/binary>>) -> try_message(skip_blanks(B));
+some_hope(B) -> hunt_msg_def(B).
+
+try_message(B = <<"SSH_MSG_",_/binary>>) ->
+ {ID,Rest} = get_id(B),
+ case lists:member(binary_to_list(ID), wanted_messages()) of
+ true ->
+ {Lines,More} = get_def_lines(skip_blanks(Rest), []),
+ [{ID,lists:reverse(Lines)} | hunt_msg_def(More)];
+ false ->
+ hunt_msg_def(Rest)
+ end;
+try_message(B) -> hunt_msg_def(B).
+
+
+skip_blanks(<<32, B/binary>>) -> skip_blanks(B);
+skip_blanks(<< 9, B/binary>>) -> skip_blanks(B);
+skip_blanks(B) -> B.
+
+get_def_lines(B0 = <<"\n",B/binary>>, Acc) ->
+ {ID,Rest} = get_id(skip_blanks(B)),
+ case {size(ID), skip_blanks(Rest)} of
+ {0,<<"....",More/binary>>} ->
+ {Text,LineEnd} = get_to_eol(skip_blanks(More)),
+ get_def_lines(LineEnd, [{<<"....">>,Text}|Acc]);
+ {0,_} ->
+ {Acc,B0};
+ {_,Rest1} ->
+ {Text,LineEnd} = get_to_eol(Rest1),
+ get_def_lines(LineEnd, [{ID,Text}|Acc])
+ end;
+get_def_lines(B, Acc) ->
+ {Acc,B}.
+
+
+get_to_eol(B) -> split_binary(B, count_to_eol(B,0)).
+
+count_to_eol(<<"\n",_/binary>>, Acc) -> Acc;
+count_to_eol(<<>>, Acc) -> Acc;
+count_to_eol(<<_,B/binary>>, Acc) -> count_to_eol(B,Acc+1).
+
+
+get_id(B) -> split_binary(B, count_id_chars(B,0)).
+
+count_id_chars(<<C,B/binary>>, Acc) when $A=<C,C=<$Z -> count_id_chars(B,Acc+1);
+count_id_chars(<<C,B/binary>>, Acc) when $a=<C,C=<$z -> count_id_chars(B,Acc+1);
+count_id_chars(<<C,B/binary>>, Acc) when $0=<C,C=<$9 -> count_id_chars(B,Acc+1);
+count_id_chars(<<"_",B/binary>>, Acc) -> count_id_chars(B,Acc+1);
+count_id_chars(<<"-",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g name-list
+count_id_chars(<<"[",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g byte[16]
+count_id_chars(<<"]",B/binary>>, Acc) -> count_id_chars(B,Acc+1); %% e.g byte[16]
+count_id_chars(_, Acc) -> Acc.
+
+wanted_messages() ->
+ ["SSH_MSG_CHANNEL_CLOSE",
+ "SSH_MSG_CHANNEL_DATA",
+ "SSH_MSG_CHANNEL_EOF",
+ "SSH_MSG_CHANNEL_EXTENDED_DATA",
+ "SSH_MSG_CHANNEL_FAILURE",
+ "SSH_MSG_CHANNEL_OPEN",
+ "SSH_MSG_CHANNEL_OPEN_CONFIRMATION",
+ "SSH_MSG_CHANNEL_OPEN_FAILURE",
+ "SSH_MSG_CHANNEL_REQUEST",
+ "SSH_MSG_CHANNEL_SUCCESS",
+ "SSH_MSG_CHANNEL_WINDOW_ADJUST",
+ "SSH_MSG_DEBUG",
+ "SSH_MSG_DISCONNECT",
+ "SSH_MSG_GLOBAL_REQUEST",
+ "SSH_MSG_IGNORE",
+ "SSH_MSG_KEXDH_INIT",
+ "SSH_MSG_KEXDH_REPLY",
+ "SSH_MSG_KEXINIT",
+ "SSH_MSG_KEX_DH_GEX_GROUP",
+ "SSH_MSG_KEX_DH_GEX_REQUEST",
+ "SSH_MSG_KEX_DH_GEX_REQUEST_OLD",
+ "SSH_MSG_NEWKEYS",
+ "SSH_MSG_REQUEST_FAILURE",
+ "SSH_MSG_REQUEST_SUCCESS",
+ "SSH_MSG_SERVICE_ACCEPT",
+ "SSH_MSG_SERVICE_REQUEST",
+ "SSH_MSG_UNIMPLEMENTED",
+ "SSH_MSG_USERAUTH_BANNER",
+ "SSH_MSG_USERAUTH_FAILURE",
+%% hard args "SSH_MSG_USERAUTH_INFO_REQUEST",
+%% "SSH_MSG_USERAUTH_INFO_RESPONSE",
+ "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ",
+ "SSH_MSG_USERAUTH_PK_OK",
+%%rfc4252 p12 error "SSH_MSG_USERAUTH_REQUEST",
+ "SSH_MSG_USERAUTH_SUCCESS"].
+
diff --git a/lib/ssh/test/property_test/ssh_eqc_subsys.erl b/lib/ssh/test/property_test/ssh_eqc_subsys.erl
new file mode 100644
index 0000000000..e4b6af166f
--- /dev/null
+++ b/lib/ssh/test/property_test/ssh_eqc_subsys.erl
@@ -0,0 +1,63 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(ssh_eqc_subsys).
+
+-behaviour(ssh_daemon_channel).
+
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+-export([response/2]).
+
+-record(state, {id,
+ cm,
+ subsyst
+ }).
+
+init([SS]) ->
+ {ok, #state{subsyst=SS}}.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) ->
+ {ok, State#state{id = ChannelId,
+ cm = ConnectionManager}}.
+
+handle_ssh_msg({ssh_cm, CM, {data, ChannelId, Type, Data}}, S) ->
+ ssh_connection:send(CM, ChannelId, Type, response(Data,S)),
+ {ok, S};
+
+handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) ->
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, State) ->
+ {stop, ChannelId, State};
+
+handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) ->
+ {stop, ChannelId, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+
+response(Msg, #state{subsyst=SS}) -> response(Msg, SS);
+response(Msg, SS) -> <<"Resp: ",Msg/binary,(list_to_binary(SS))/binary>>.
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 35fca21021..415cb9fc9c 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -53,7 +53,7 @@ all() ->
{group, hardening_tests}
].
-groups() ->
+groups() ->
[{dsa_key, [], basic_tests()},
{rsa_key, [], basic_tests()},
{dsa_pass_key, [], [pass_phrase]},
@@ -63,7 +63,11 @@ groups() ->
ssh_connect_nonegtimeout_connected_sequential,
ssh_connect_negtimeout_parallel,
ssh_connect_negtimeout_sequential,
- max_sessions]}
+ max_sessions_ssh_connect_parallel,
+ max_sessions_ssh_connect_sequential,
+ max_sessions_sftp_start_channel_parallel,
+ max_sessions_sftp_start_channel_sequential
+ ]}
].
@@ -794,12 +798,14 @@ ssh_connect_nonegtimeout_connected(Config, Parallel) ->
{parallel_login, Parallel},
{negotiation_timeout, NegTimeOut},
{failfun, fun ssh_test_lib:failfun/2}]),
+ ct:pal("~p Listen ~p:~p",[_Pid,_Host,Port]),
ct:sleep(500),
IO = ssh_test_lib:start_io_server(),
Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
receive
- {'EXIT', _, _} ->
+ Error = {'EXIT', _, _} ->
+ ct:pal("~p",[Error]),
ct:fail(no_ssh_connection);
ErlShellStart ->
ct:pal("---Erlang shell start: ~p~n", [ErlShellStart]),
@@ -859,40 +865,92 @@ openssh_zlib_basic_test(Config) ->
%%--------------------------------------------------------------------
-max_sessions(Config) ->
+max_sessions_ssh_connect_parallel(Config) ->
+ max_sessions(Config, true, connect_fun(ssh__connect,Config)).
+max_sessions_ssh_connect_sequential(Config) ->
+ max_sessions(Config, false, connect_fun(ssh__connect,Config)).
+
+max_sessions_sftp_start_channel_parallel(Config) ->
+ max_sessions(Config, true, connect_fun(ssh_sftp__start_channel, Config)).
+max_sessions_sftp_start_channel_sequential(Config) ->
+ max_sessions(Config, false, connect_fun(ssh_sftp__start_channel, Config)).
+
+
+%%%---- helpers:
+connect_fun(ssh__connect, Config) ->
+ fun(Host,Port) ->
+ ssh_test_lib:connect(Host, Port,
+ [{silently_accept_hosts, true},
+ {user_dir, ?config(priv_dir,Config)},
+ {user_interaction, false},
+ {user, "carni"},
+ {password, "meat"}
+ ])
+ %% ssh_test_lib returns R when ssh:connect returns {ok,R}
+ end;
+connect_fun(ssh_sftp__start_channel, _Config) ->
+ fun(Host,Port) ->
+ {ok,_Pid,ConnRef} =
+ ssh_sftp:start_channel(Host, Port,
+ [{silently_accept_hosts, true},
+ {user, "carni"},
+ {password, "meat"}
+ ]),
+ ConnRef
+ end.
+
+
+max_sessions(Config, ParallelLogin, Connect0) when is_function(Connect0,2) ->
+ Connect = fun(Host,Port) ->
+ R = Connect0(Host,Port),
+ ct:pal("Connect(~p,~p) -> ~p",[Host,Port,R]),
+ R
+ end,
SystemDir = filename:join(?config(priv_dir, Config), system),
UserDir = ?config(priv_dir, Config),
- MaxSessions = 2,
- {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ MaxSessions = 5,
+ {Pid, Host, Port} = ssh_test_lib:daemon([
+ {system_dir, SystemDir},
{user_dir, UserDir},
{user_passwords, [{"carni", "meat"}]},
- {parallel_login, true},
+ {parallel_login, ParallelLogin},
{max_sessions, MaxSessions}
]),
-
- Connect = fun() ->
- R=ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
- {user_dir, UserDir},
- {user_interaction, false},
- {user, "carni"},
- {password, "meat"}
- ]),
- ct:log("Connection ~p up",[R])
- end,
-
- try [Connect() || _ <- lists:seq(1,MaxSessions)]
+ ct:pal("~p Listen ~p:~p for max ~p sessions",[Pid,Host,Port,MaxSessions]),
+ try [Connect(Host,Port) || _ <- lists:seq(1,MaxSessions)]
of
- _ ->
- ct:pal("Expect Info Report:",[]),
- try Connect()
+ Connections ->
+ %% Step 1 ok: could set up max_sessions connections
+ ct:log("Connections up: ~p",[Connections]),
+ [_|_] = Connections,
+
+ %% Now try one more than alowed:
+ ct:pal("Info Report might come here...",[]),
+ try Connect(Host,Port)
of
- _ConnectionRef ->
+ _ConnectionRef1 ->
ssh:stop_daemon(Pid),
{fail,"Too many connections accepted"}
catch
error:{badmatch,{error,"Connection closed"}} ->
- ssh:stop_daemon(Pid),
- ok
+ %% Step 2 ok: could not set up max_sessions+1 connections
+ %% This is expected
+ %% Now stop one connection and try to open one more
+ ok = ssh:close(hd(Connections)),
+ try Connect(Host,Port)
+ of
+ _ConnectionRef1 ->
+ %% Step 3 ok: could set up one more connection after killing one
+ %% Thats good.
+ ssh:stop_daemon(Pid),
+ ok
+ catch
+ error:{badmatch,{error,"Connection closed"}} ->
+ %% Bad indeed. Could not set up one more connection even after killing
+ %% one existing. Very bad.
+ ssh:stop_daemon(Pid),
+ {fail,"Does not decrease # active sessions"}
+ end
end
catch
error:{badmatch,{error,"Connection closed"}} ->
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index f4f0682b40..d226e5ba03 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -31,13 +31,21 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() ->
- [{ct_hooks,[ts_install_cth]}].
+%% suite() ->
+%% [{ct_hooks,[ts_install_cth]}].
all() ->
[
{group, openssh_payload},
- interrupted_send
+ interrupted_send,
+ start_shell,
+ start_shell_exec,
+ start_shell_exec_fun,
+ gracefull_invalid_version,
+ gracefull_invalid_start,
+ gracefull_invalid_long_start,
+ gracefull_invalid_long_start_no_nl,
+ stop_listener
].
groups() ->
[{openssh_payload, [], [simple_exec,
@@ -64,7 +72,7 @@ init_per_group(openssh_payload, _Config) ->
{skip,"No openssh deamon"};
{ok, Socket} ->
gen_tcp:close(Socket)
- end;
+ end;
init_per_group(_, Config) ->
Config.
@@ -177,10 +185,10 @@ big_cat(Config) when is_list(Config) ->
case size(Data) =:= size(Other) of
true ->
ct:pal("received and sent data are same"
- "size but do not match~n",[]);
+ "size but do not match~n",[]);
false ->
ct:pal("sent ~p but only received ~p~n",
- [size(Data), size(Other)])
+ [size(Data), size(Other)])
end,
ct:fail(receive_data_mismatch);
Else ->
@@ -247,10 +255,10 @@ interrupted_send(Config) when is_list(Config) ->
{subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}]),
ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
@@ -276,6 +284,253 @@ interrupted_send(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+start_shell() ->
+ [{doc, "Start a shell"}].
+
+start_shell(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {shell, fun(U, H) -> start_our_shell(U, H) end} ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+ ok = ssh_connection:shell(ConnectionRef,ChannelId0),
+
+ receive
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"Enter command\r\n">>}} ->
+ ok
+ after 5000 ->
+ ct:fail("CLI Timeout")
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
+start_shell_exec() ->
+ [{doc, "start shell to exec command"}].
+
+start_shell_exec(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {exec, {?MODULE,ssh_exec,[]}} ]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "testing", infinity),
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ ok
+ after 5000 ->
+ ct:fail("Exec Timeout")
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+start_shell_exec_fun() ->
+ [{doc, "start shell to exec command"}].
+
+start_shell_exec_fun(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {exec, fun ssh_exec/1}]),
+
+ ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ success = ssh_connection:exec(ConnectionRef, ChannelId0,
+ "testing", infinity),
+
+ receive
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ ok
+ after 5000 ->
+ ct:fail("Exec Timeout")
+ end,
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+
+gracefull_invalid_version(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}]),
+
+ {ok, S} = gen_tcp:connect(Host, Port, []),
+ ok = gen_tcp:send(S, ["SSH-8.-1","\r\n"]),
+ receive
+ Verstring ->
+ ct:pal("Server version: ~p~n", [Verstring]),
+ receive
+ {tcp_closed, S} ->
+ ok
+ end
+ end.
+
+gracefull_invalid_start(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}]),
+
+ {ok, S} = gen_tcp:connect(Host, Port, []),
+ ok = gen_tcp:send(S, ["foobar","\r\n"]),
+ receive
+ Verstring ->
+ ct:pal("Server version: ~p~n", [Verstring]),
+ receive
+ {tcp_closed, S} ->
+ ok
+ end
+ end.
+
+gracefull_invalid_long_start(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}]),
+
+ {ok, S} = gen_tcp:connect(Host, Port, []),
+ ok = gen_tcp:send(S, [lists:duplicate(257, $a), "\r\n"]),
+ receive
+ Verstring ->
+ ct:pal("Server version: ~p~n", [Verstring]),
+ receive
+ {tcp_closed, S} ->
+ ok
+ end
+ end.
+
+
+gracefull_invalid_long_start_no_nl(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"}]),
+
+ {ok, S} = gen_tcp:connect(Host, Port, []),
+ ok = gen_tcp:send(S, [lists:duplicate(257, $a), "\r\n"]),
+ receive
+ Verstring ->
+ ct:pal("Server version: ~p~n", [Verstring]),
+ receive
+ {tcp_closed, S} ->
+ ok
+ end
+ end.
+
+stop_listener() ->
+ [{doc, "start ssh daemon, setup connections, stop listener, restart listner"}].
+
+stop_listener(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth
+ file:make_dir(UserDir),
+ SysDir = ?config(data_dir, Config),
+
+ {Pid0, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "morot"},
+ {exec, fun ssh_exec/1}]),
+
+ ConnectionRef0 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+
+ {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef0, infinity),
+
+ ssh:stop_listener(Host, Port),
+
+ {error, _} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+ success = ssh_connection:exec(ConnectionRef0, ChannelId0,
+ "testing", infinity),
+ receive
+ {ssh_cm, ConnectionRef0, {data, ChannelId0, 0, <<"testing\r\n">>}} ->
+ ok
+ after 5000 ->
+ ct:fail("Exec Timeout")
+ end,
+
+ {ok, HostAddr} = inet:getaddr(Host, inet),
+ case ssh_test_lib:daemon(HostAddr, Port, [{system_dir, SysDir},
+ {user_dir, UserDir},
+ {password, "potatis"},
+ {exec, fun ssh_exec/1}]) of
+ {Pid1, HostAddr, Port} ->
+ ConnectionRef1 = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "potatis"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+ {error, _} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, true},
+ {user_dir, UserDir}]),
+ ssh:close(ConnectionRef0),
+ ssh:close(ConnectionRef1),
+ ssh:stop_daemon(Pid0),
+ ssh:stop_daemon(Pid1);
+ Error ->
+ ct:fail({unexpected, Error})
+ end.
+
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
big_cat_rx(ConnectionRef, ChannelId) ->
@@ -308,3 +563,16 @@ collect_data(ConnectionRef, ChannelId, Acc) ->
after 5000 ->
timeout
end.
+
+%%%-------------------------------------------------------------------
+%% This is taken from the ssh example code.
+start_our_shell(_User, _Peer) ->
+ spawn(fun() ->
+ io:format("Enter command\n")
+ %% Don't actually loop, just exit
+ end).
+
+ssh_exec(Cmd) ->
+ spawn(fun() ->
+ io:format(Cmd ++ "\n")
+ end).
diff --git a/lib/ssh/test/ssh_property_test_SUITE.erl b/lib/ssh/test/ssh_property_test_SUITE.erl
new file mode 100644
index 0000000000..ffad8ebbb7
--- /dev/null
+++ b/lib/ssh/test/ssh_property_test_SUITE.erl
@@ -0,0 +1,107 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%% Run like this:
+%%% ct:run_test([{suite,"ssh_property_test_SUITE"}, {logdir,"/ldisk/OTP/LOG"}]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% %%%
+%%% WARNING %%%
+%%% %%%
+%%% This is experimental code which may be changed or removed %%%
+%%% anytime without any warning. %%%
+%%% %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(ssh_property_test_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+all() -> [{group, messages},
+ {group, client_server}
+ ].
+
+groups() ->
+ [{messages, [], [decode,
+ decode_encode]},
+ {client_server, [], [client_server_sequential,
+ client_server_parallel,
+ client_server_parallel_multi]}
+ ].
+
+
+%%% First prepare Config and compile the property tests for the found tool:
+init_per_suite(Config) ->
+ ct_property_test:init_per_suite(Config).
+
+%%% One group in this suite happens to support only QuickCheck, so skip it
+%%% if we run proper.
+init_per_group(client_server, Config) ->
+ case ?config(property_test_tool,Config) of
+ eqc -> Config;
+ X -> {skip, lists:concat([X," is not supported"])}
+ end;
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
+%%% Always skip the testcase that is not quite in phase with the
+%%% ssh_message.erl code
+init_per_testcase(decode_encode, _) -> {skip, "Fails - testcase is not ok"};
+init_per_testcase(_TestCase, Config) -> Config.
+
+end_per_testcase(_TestCase, Config) -> Config.
+
+%%%================================================================
+%%% Test suites
+%%%
+decode(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_encode_decode:prop_ssh_decode(),
+ Config
+ ).
+
+decode_encode(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_encode_decode:prop_ssh_decode_encode(),
+ Config
+ ).
+
+client_server_sequential(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_client_server:prop_seq(Config),
+ Config
+ ).
+
+client_server_parallel(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_client_server:prop_parallel(Config),
+ Config
+ ).
+
+client_server_parallel_multi(Config) ->
+ ct_property_test:quickcheck(
+ ssh_eqc_client_server:prop_parallel_multi(Config),
+ Config
+ ).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 00c25bf394..b8abf5e80e 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -113,6 +113,9 @@ io_request({put_chars, Chars}, TestCase, _, _, Buff) ->
io_request({put_chars, unicode, Chars}, TestCase, _, _, Buff) when is_binary(Chars) ->
reply(TestCase, Chars),
{ok, ok, Buff};
+io_request({put_chars, unicode, io_lib, format, [Fmt,Args]}, TestCase, _, _, Buff) ->
+ reply(TestCase, io_lib:format(Fmt,Args)),
+ {ok, ok, Buff};
io_request({put_chars, Enc, Chars}, TestCase, _, _, Buff) ->
reply(TestCase, unicode:characters_to_binary(Chars,Enc,latin1)),
{ok, ok, Buff};
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 8b5343cecc..41fbd324c4 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-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
@@ -54,7 +54,9 @@ groups() ->
]},
{erlang_server, [], [erlang_server_openssh_client_exec,
erlang_server_openssh_client_exec_compressed,
- erlang_server_openssh_client_pulic_key_dsa]}
+ erlang_server_openssh_client_pulic_key_dsa,
+ erlang_server_openssh_client_cipher_suites,
+ erlang_server_openssh_client_macs]}
].
init_per_suite(Config) ->
@@ -89,6 +91,12 @@ end_per_group(erlang_server, Config) ->
end_per_group(_, Config) ->
Config.
+init_per_testcase(erlang_server_openssh_client_cipher_suites, Config) ->
+ check_ssh_client_support(Config);
+
+init_per_testcase(erlang_server_openssh_client_macs, Config) ->
+ check_ssh_client_support(Config);
+
init_per_testcase(_TestCase, Config) ->
ssh:start(),
Config.
@@ -111,15 +119,7 @@ erlang_shell_client_openssh_server(Config) when is_list(Config) ->
IO ! {input, self(), "echo Hej\n"},
receive_hej(),
IO ! {input, self(), "exit\n"},
- receive
- <<"logout">> ->
- receive
- <<"Connection closed">> ->
- ok
- end;
- Other0 ->
- ct:fail({unexpected_msg, Other0})
- end,
+ receive_logout(),
receive
{'EXIT', Shell, normal} ->
ok;
@@ -221,6 +221,108 @@ erlang_server_openssh_client_exec(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+erlang_server_openssh_client_cipher_suites() ->
+ [{doc, "Test that we can connect with different cipher suites."}].
+
+erlang_server_openssh_client_cipher_suites(Config) when is_list(Config) ->
+ SystemDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+
+ ct:sleep(500),
+
+ Supports = crypto:supports(),
+ Ciphers = proplists:get_value(ciphers, Supports),
+ Tests = [
+ {"3des-cbc", lists:member(des3_cbc, Ciphers)},
+ {"aes128-cbc", lists:member(aes_cbc128, Ciphers)},
+ {"aes128-ctr", lists:member(aes_ctr, Ciphers)},
+ {"aes256-cbc", false}
+ ],
+ lists:foreach(fun({Cipher, Expect}) ->
+ Cmd = "ssh -p " ++ integer_to_list(Port) ++
+ " -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " " ++
+ " -c " ++ Cipher ++ " 1+1.",
+
+ ct:pal("Cmd: ~p~n", [Cmd]),
+
+ SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
+
+ case Expect of
+ true ->
+ receive
+ {SshPort,{data, <<"2\n">>}} ->
+ ok
+ after ?TIMEOUT ->
+ ct:fail("Did not receive answer")
+ end;
+ false ->
+ receive
+ {SshPort,{data, <<"no matching cipher found", _/binary>>}} ->
+ ok
+ after ?TIMEOUT ->
+ ct:fail("Did not receive no matching cipher message")
+ end
+ end
+ end, Tests),
+
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
+erlang_server_openssh_client_macs() ->
+ [{doc, "Test that we can connect with different MACs."}].
+
+erlang_server_openssh_client_macs(Config) when is_list(Config) ->
+ SystemDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ KnownHosts = filename:join(PrivDir, "known_hosts"),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+
+ ct:sleep(500),
+
+ Supports = crypto:supports(),
+ Hashs = proplists:get_value(hashs, Supports),
+ MACs = [{"hmac-sha1", lists:member(sha, Hashs)},
+ {"hmac-sha2-256", lists:member(sha256, Hashs)},
+ {"hmac-md5-96", false},
+ {"hmac-ripemd160", false}],
+ lists:foreach(fun({MAC, Expect}) ->
+ Cmd = "ssh -p " ++ integer_to_list(Port) ++
+ " -o UserKnownHostsFile=" ++ KnownHosts ++ " " ++ Host ++ " " ++
+ " -o MACs=" ++ MAC ++ " 1+1.",
+
+ ct:pal("Cmd: ~p~n", [Cmd]),
+
+ SshPort = open_port({spawn, Cmd}, [binary, stderr_to_stdout]),
+
+ case Expect of
+ true ->
+ receive
+ {SshPort,{data, <<"2\n">>}} ->
+ ok
+ after ?TIMEOUT ->
+ ct:fail("Did not receive answer")
+ end;
+ false ->
+ receive
+ {SshPort,{data, <<"no matching mac found", _/binary>>}} ->
+ ok
+ after ?TIMEOUT ->
+ ct:fail("Did not receive no matching mac message")
+ end
+ end
+ end, MACs),
+
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
erlang_server_openssh_client_exec_compressed() ->
[{doc, "Test that exec command works."}].
@@ -433,3 +535,43 @@ receive_hej() ->
ct:pal("Extra info: ~p~n", [Info]),
receive_hej()
end.
+
+receive_logout() ->
+ receive
+ <<"logout">> ->
+ receive
+ <<"Connection closed">> ->
+ ok
+ end;
+ <<"TERM environment variable not set.\n">> -> %% Windows work around
+ receive_logout();
+ Other0 ->
+ ct:fail({unexpected_msg, Other0})
+ end.
+
+
+
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% Check if we have a "newer" ssh client that supports these test cases
+%%--------------------------------------------------------------------
+check_ssh_client_support(Config) ->
+ Port = open_port({spawn, "ssh -Q cipher"}, [exit_status, stderr_to_stdout]),
+ case check_ssh_client_support2(Port) of
+ 0 -> % exit status from command (0 == ok)
+ ssh:start(),
+ Config;
+ _ ->
+ {skip, "test case not supported by ssh client"}
+ end.
+
+check_ssh_client_support2(P) ->
+ receive
+ {P, {data, _A}} ->
+ check_ssh_client_support2(P);
+ {P, {exit_status, E}} ->
+ E
+ after 5000 ->
+ ct:pal("Openssh command timed out ~n"),
+ -1
+ end.
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 9bef10a366..11f30e8d04 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 3.0.4
+SSH_VSN = 3.0.6
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 1b37a2baa2..8643cd3745 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,47 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.3.5</title>
+ <section><title>SSL 5.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Corrected handling of ECC certificates, there where
+ several small issues with the handling of such
+ certificates in the ssl and public_key application. Now
+ ECC signed ECC certificates shall work and not only RSA
+ signed ECC certificates.</p>
+ <p>
+ Own Id: OTP-12026</p>
+ </item>
+ <item>
+ <p>
+ Check that the certificate chain ends with a trusted ROOT
+ CA e.i. a self-signed certificate, but provide an option
+ partial_chain to enable the application to define an
+ intermediat CA as trusted.</p>
+ <p>
+ Own Id: OTP-12149</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add decode functions for SNI (Server Name Indication)</p>
+ <p>
+ Own Id: OTP-12048</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index ffee4bd1af..f14d0b8bb7 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -226,7 +226,7 @@
<p>The verification fun should be defined as:</p>
<code>
-fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
+fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revoked, atom()}} |
{extension, #'Extension'{}}, InitialUserState :: term()) ->
{valid, UserState :: term()} | {valid_peer, UserState :: term()} |
{fail, Reason :: term()} | {unknown, UserState :: term()}.
@@ -252,7 +252,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
always returns {valid, UserState}, the TLS/SSL handshake will
not be terminated with respect to verification failures and
the connection will be established. If called with an
- extension unknown to the user application the return value
+ extension unknown to the user application, the return value
{unknown, UserState} should be used.</p>
<p>The default verify_fun option in verify_peer mode:</p>
@@ -283,9 +283,29 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
end, []}
</code>
-<p>Possible path validation errors: </p>
+ <p>Possible path validation errors are given on the form {bad_cert, Reason} where Reason is:</p>
-<p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca},{bad_cert, selfsigned_peer}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p>
+ <taglist>
+ <tag>unknown_ca</tag>
+ <item>No trusted CA was found in the trusted store. The trusted CA is
+ normally a so called ROOT CA that is a self-signed cert. Trust may
+ be claimed for an intermediat CA (trusted anchor does not have to be self signed
+ according to X-509) by using the option <c>partial_chain</c></item>
+
+ <tag>selfsigned_peer</tag>
+ <item>The chain consisted only of one self-signed certificate.</item>
+
+ <tag>PKIX X-509-path validation error</tag>
+ <item> Possible such reasons see <seealso
+ marker="public_key#pkix_path_validation-3"> public_key:pkix_path_validation/3 </seealso></item>
+ </taglist>
+
+ </item>
+
+ <tag>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} | unknown_ca </tag>
+ <item>
+ Claim an intermediat CA in the chain as trusted. TLS will then perform the public_key:pkix_path_validation/3
+ with the selected CA as trusted anchor and the rest of the chain.
</item>
<tag>{versions, [protocol()]}</tag>
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
index cdfafe224b..80d9cc4ee8 100644
--- a/lib/ssl/doc/src/ssl_protocol.xml
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -83,7 +83,7 @@
<em>subject</em>. The certificate is signed
with the private key of the issuer of the certificate. A chain
of trust is build by having the issuer in its turn being
- certified by an other certificate and so on until you reach the
+ certified by another certificate and so on until you reach the
so called root certificate that is self signed i.e. issued
by itself.</p>
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 7c4c8ec2cc..0c00a650b9 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -118,7 +118,7 @@ $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index b713f86c1e..650901ef54 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,22 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"5.3.5", [{load_module, ssl, soft_purge, soft_purge, [ssl_connection]},
+ {load_module, ssl_handshake, soft_purge, soft_purge, [ssl_certificate]},
+ {load_module, ssl_certificate, soft_purge, soft_purge, []},
+ {load_module, ssl_connection, soft_purge, soft_purge, [tls_connection]},
+ {update, tls_connection, {advanced, {up, "5.3.5", "5.3.6"}}, [ssl_handshake]}]},
{<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
+ {"5.3.5", [{load_module, ssl, soft_purge, soft_purge,[ssl_certificate]},
+ {load_module, ssl_handshake, soft_purge, soft_purge,[ssl_certificate]},
+ {load_module, ssl_certificate, soft_purge, soft_purge,[]},
+ {load_module, ssl_connection, soft_purge, soft_purge,[tls_connection]},
+ {update, tls_connection, {advanced, {down, "5.3.6", "5.3.5"}}, [ssl_handshake]}]},
{<<"5\\.3\\.[1-4]($|\\..*)">>, [{restart_application, ssl}]},
{<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d741fa63fb..b4bea25942 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -569,21 +569,24 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
cacertfile = CaCertFile0} = InheritedSslOpts) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts0, CaCerts),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} = handle_verify_options(Opts0, CaCerts),
CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of
undefined ->
CaCertDefault;
CAFile ->
CAFile
end,
+
NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts,
cacertfile = CaCertFile,
verify = Verify,
verify_fun = VerifyFun,
+ partial_chain = PartialChainHanlder,
fail_if_no_peer_cert = FailIfNoPeerCert},
SslOpts1 = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
- end, Opts0, [cacerts, cacertfile, verify, verify_fun, fail_if_no_peer_cert]),
+ end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain,
+ fail_if_no_peer_cert]),
case handle_option(versions, SslOpts1, []) of
[] ->
new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB);
@@ -603,10 +606,10 @@ handle_options(Opts0) ->
ReuseSessionFun = fun(_, _, _, _) -> true end,
CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} = handle_verify_options(Opts, CaCerts),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} =
+ handle_verify_options(Opts, CaCerts),
CertFile = handle_option(certfile, Opts, <<>>),
-
RecordCb = record_cb(Opts),
Versions = case handle_option(versions, Opts, []) of
@@ -620,6 +623,7 @@ handle_options(Opts0) ->
versions = Versions,
verify = validate_option(verify, Verify),
verify_fun = VerifyFun,
+ partial_chain = PartialChainHanlder,
fail_if_no_peer_cert = FailIfNoPeerCert,
verify_client_once = handle_option(verify_client_once, Opts, false),
depth = handle_option(depth, Opts, 1),
@@ -656,7 +660,7 @@ handle_options(Opts0) ->
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
- SslOptions = [protocol, versions, verify, verify_fun,
+ SslOptions = [protocol, versions, verify, verify_fun, partial_chain,
fail_if_no_peer_cert, verify_client_once,
depth, cert, certfile, key, keyfile,
password, cacerts, cacertfile, dh, dhfile,
@@ -708,6 +712,8 @@ validate_option(verify_fun, Fun) when is_function(Fun) ->
end, Fun};
validate_option(verify_fun, {Fun, _} = Value) when is_function(Fun) ->
Value;
+validate_option(partial_chain, Value) when is_function(Value) ->
+ Value;
validate_option(fail_if_no_peer_cert, Value) when is_boolean(Value) ->
Value;
validate_option(verify_client_once, Value) when is_boolean(Value) ->
@@ -1147,25 +1153,32 @@ handle_verify_options(Opts, CaCerts) ->
UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
UserVerifyFun = handle_option(verify_fun, Opts, undefined),
-
+ PartialChainHanlder = handle_option(partial_chain, Opts,
+ fun(_) -> unknown_ca end),
+
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
0 ->
{verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
+ VerifyNoneFun, PartialChainHanlder};
1 ->
{verify_peer, false,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
+ UserVerifyFun, PartialChainHanlder};
2 ->
{verify_peer, true,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
- verify_none ->
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
+ UserVerifyFun, PartialChainHanlder};
+ verify_none ->
{verify_none, false,
- ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
+ VerifyNoneFun, PartialChainHanlder};
verify_peer ->
{verify_peer, UserFailIfNoPeerCert,
- ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
+ UserVerifyFun, PartialChainHanlder};
Value ->
throw({error, {options, {verify, Value}}})
end.
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index b186a1015a..9c0ed181fe 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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
@@ -30,7 +30,7 @@
-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([trusted_cert_and_path/3,
+-export([trusted_cert_and_path/4,
certificate_chain/3,
file_to_certificats/2,
validate_extension/3,
@@ -46,14 +46,14 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec trusted_cert_and_path([der_cert()], db_handle(), certdb_ref()) ->
+-spec trusted_cert_and_path([der_cert()], db_handle(), certdb_ref(), fun()) ->
{der_cert() | unknown_ca, [der_cert()]}.
%%
%% Description: Extracts the root cert (if not presents tries to
%% look it up, if not found {bad_cert, unknown_ca} will be added verification
%% errors. Returns {RootCert, Path, VerifyErrors}
%%--------------------------------------------------------------------
-trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef) ->
+trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) ->
Path = [Cert | _] = lists:reverse(CertChain),
OtpCert = public_key:pkix_decode_cert(Cert, otp),
SignedAndIssuerID =
@@ -62,32 +62,23 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef) ->
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
{self, IssuerId};
false ->
- case public_key:pkix_issuer_id(OtpCert, other) of
- {ok, IssuerId} ->
- {other, IssuerId};
- {error, issuer_not_found} ->
- case find_issuer(OtpCert, CertDbHandle) of
- {ok, IssuerId} ->
- {other, IssuerId};
- Other ->
- Other
- end
- end
+ other_issuer(OtpCert, CertDbHandle)
end,
case SignedAndIssuerID of
{error, issuer_not_found} ->
%% The root CA was not sent and can not be found.
- {unknown_ca, Path};
+ handle_incomplete_chain(Path, PartialChainHandler);
{self, _} when length(Path) == 1 ->
{selfsigned_peer, Path};
{_ ,{SerialNr, Issuer}} ->
case ssl_manager:lookup_trusted_cert(CertDbHandle, CertDbRef, SerialNr, Issuer) of
- {ok, {BinCert,_}} ->
- {BinCert, Path};
+ {ok, Trusted} ->
+ %% Trusted must be selfsigned or it is an incomplete chain
+ handle_path(Trusted, Path, PartialChainHandler);
_ ->
%% Root CA could not be verified
- {unknown_ca, Path}
+ handle_incomplete_chain(Path, PartialChainHandler)
end
end.
@@ -222,23 +213,27 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
_ ->
%% The trusted cert may be obmitted from the chain as the
%% counter part needs to have it anyway to be able to
- %% verify it. This will be the normal case for servers
- %% that does not verify the clients and hence have not
- %% specified the cacertfile.
+ %% verify it.
{ok, lists:reverse(Chain)}
end.
find_issuer(OtpCert, CertDbHandle) ->
- IsIssuerFun = fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
- case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
- true ->
- throw(public_key:pkix_issuer_id(ErlCertCandidate, self));
- false ->
- Acc
- end;
- (_, Acc) ->
- Acc
- end,
+ IsIssuerFun =
+ fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
+ case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
+ true ->
+ case verify_cert_signer(OtpCert, ErlCertCandidate#'OTPCertificate'.tbsCertificate) of
+ true ->
+ throw(public_key:pkix_issuer_id(ErlCertCandidate, self));
+ false ->
+ Acc
+ end;
+ false ->
+ Acc
+ end;
+ (_, Acc) ->
+ Acc
+ end,
try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of
issuer_not_found ->
@@ -254,3 +249,57 @@ is_valid_extkey_usage(KeyUse, client) ->
is_valid_extkey_usage(KeyUse, server) ->
%% Server wants to verify client
is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
+
+verify_cert_signer(OtpCert, SignerTBSCert) ->
+ PublicKey = public_key(SignerTBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo),
+ public_key:pkix_verify(public_key:pkix_encode('OTPCertificate', OtpCert, otp), PublicKey).
+
+public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-ecPublicKey',
+ parameters = Params},
+ subjectPublicKey = Point}) ->
+ {Point, Params};
+public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'rsaEncryption'},
+ subjectPublicKey = Key}) ->
+ Key;
+public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-dsa',
+ parameters = {params, Params}},
+ subjectPublicKey = Key}) ->
+ {Key, Params}.
+
+other_issuer(OtpCert, CertDbHandle) ->
+ case public_key:pkix_issuer_id(OtpCert, other) of
+ {ok, IssuerId} ->
+ {other, IssuerId};
+ {error, issuer_not_found} ->
+ case find_issuer(OtpCert, CertDbHandle) of
+ {ok, IssuerId} ->
+ {other, IssuerId};
+ Other ->
+ Other
+ end
+ end.
+
+handle_path({BinCert, OTPCert}, Path, PartialChainHandler) ->
+ case public_key:pkix_is_self_signed(OTPCert) of
+ true ->
+ {BinCert, Path};
+ false ->
+ handle_incomplete_chain(Path, PartialChainHandler)
+ end.
+
+handle_incomplete_chain(Chain, Fun) ->
+ case catch Fun(Chain) of
+ {trusted_ca, DerCert} ->
+ new_trusteded_chain(DerCert, Chain);
+ unknown_ca = Error ->
+ {Error, Chain};
+ _ ->
+ {unknown_ca, Chain}
+ end.
+
+new_trusteded_chain(DerCert, [DerCert | Chain]) ->
+ {DerCert, Chain};
+new_trusteded_chain(DerCert, [_ | Rest]) ->
+ new_trusteded_chain(DerCert, Rest);
+new_trusteded_chain(_, []) ->
+ unknown_ca.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 4ac4e81d9e..8ff9913cee 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -414,7 +414,9 @@ certify(#certificate{} = Cert,
ssl_options = Opts} = State, Connection) ->
case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
- Opts#ssl_options.verify_fun, Role) of
+ Opts#ssl_options.verify_fun,
+ Opts#ssl_options.partial_chain,
+ Role) of
{PeerCert, PublicKeyInfo} ->
handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false}, Connection);
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index fc67d2c28d..88ccb94e0b 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -49,7 +49,7 @@
finished/5, next_protocol/1]).
%% Handle handshake messages
--export([certify/7, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
+-export([certify/8, client_certificate_verify/6, certificate_verify/6, verify_signature/5,
master_secret/5, server_key_exchange_hash/2, verify_connection/6,
init_handshake_history/0, update_handshake_history/2, verify_server_key/5
]).
@@ -201,13 +201,13 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
end.
%%--------------------------------------------------------------------
--spec certificate_request(ssl_cipher:erl_cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) ->
+-spec certificate_request(ssl_cipher:cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) ->
#certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
%%--------------------------------------------------------------------
certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) ->
- Types = certificate_types(CipherSuite),
+ Types = certificate_types(ssl_cipher:suite_definition(CipherSuite), Version),
HashSigns = advertised_hash_signs(Version),
Authorities = certificate_authorities(CertDbHandle, CertDbRef),
#certificate_request{
@@ -383,13 +383,13 @@ verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature,
%%--------------------------------------------------------------------
-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit,
- verify_peer | verify_none, {fun(), term},
+ verify_peer | verify_none, {fun(), term}, fun(),
client | server) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- MaxPathLen, _Verify, VerifyFunAndState, Role) ->
+ MaxPathLen, _Verify, VerifyFunAndState, PartialChain, Role) ->
[PeerCert | _] = ASN1Certs,
ValidationFunAndState =
@@ -421,7 +421,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
try
{TrustedErlCert, CertPath} =
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef),
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, PartialChain),
case public_key:pkix_path_validation(TrustedErlCert,
CertPath,
[{max_path_length,
@@ -1098,19 +1098,31 @@ supported_ecc(_) ->
%%-------------certificate handling --------------------------------
-certificate_types({KeyExchange, _, _, _})
- when KeyExchange == rsa;
- KeyExchange == dhe_dss;
- KeyExchange == dhe_rsa;
- KeyExchange == ecdhe_rsa ->
- <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
+certificate_types(_, {N, M}) when N >= 3 andalso M >= 3 ->
+ case proplists:get_bool(ecdsa,
+ proplists:get_value(public_keys, crypto:supports())) of
+ true ->
+ <<?BYTE(?ECDSA_SIGN), ?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
+ false ->
+ <<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>
+ end;
+
+certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == rsa;
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa ->
+ <<?BYTE(?RSA_SIGN)>>;
-certificate_types({KeyExchange, _, _, _})
- when KeyExchange == dh_ecdsa;
- KeyExchange == dhe_ecdsa ->
+certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dhe_dss;
+ KeyExchange == srp_dss ->
+ <<?BYTE(?DSS_SIGN)>>;
+
+certificate_types({KeyExchange, _, _, _}, _) when KeyExchange == dh_ecdsa;
+ KeyExchange == dhe_ecdsa;
+ KeyExchange == ecdh_ecdsa;
+ KeyExchange == ecdhe_ecdsa ->
<<?BYTE(?ECDSA_SIGN)>>;
-certificate_types(_) ->
+certificate_types(_, _) ->
<<?BYTE(?RSA_SIGN)>>.
certificate_authorities(CertDbHandle, CertDbRef) ->
@@ -1719,6 +1731,14 @@ dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
dec_hello_extensions(Rest, Acc#hello_extensions{ec_point_formats =
#ec_point_formats{ec_point_format_list =
ECPointFormats}});
+
+dec_hello_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len), Rest/binary>>, Acc) when Len == 0 ->
+ dec_hello_extensions(Rest, Acc#hello_extensions{sni = ""}); %% Server may send an empy SNI
+
+dec_hello_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ <<?UINT16(_), NameList/binary>> = ExtData,
+ dec_hello_extensions(Rest, Acc#hello_extensions{sni = dec_sni(NameList)});
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
@@ -1731,6 +1751,13 @@ dec_hello_extensions(_, Acc) ->
dec_hashsign(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
{ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+%% Ignore unknown names (only host_name is supported)
+dec_sni(<<?BYTE(?SNI_NAMETYPE_HOST_NAME), ?UINT16(Len),
+ HostName:Len/binary, _/binary>>) ->
+ #sni{hostname = binary_to_list(HostName)};
+dec_sni(<<?BYTE(_), ?UINT16(Len), _:Len, Rest/binary>>) -> dec_sni(Rest);
+dec_sni(_) -> undefined.
+
decode_next_protocols({next_protocol_negotiation, Protocols}) ->
decode_next_protocols(Protocols, []).
decode_next_protocols(<<>>, Acc) ->
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index fd0d87bd5f..85724de4bd 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -74,6 +74,7 @@
versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API
verify :: verify_none | verify_peer,
verify_fun, %%:: fun(CertVerifyErrors::term()) -> boolean(),
+ partial_chain :: fun(),
fail_if_no_peer_cert :: boolean(),
verify_client_once :: boolean(),
%% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError}
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 26de51985a..7df73fb581 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -329,7 +329,10 @@ terminate(Reason, StateName, State) ->
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
-code_change(_OldVsn, StateName, State, _Extra) ->
+code_change(_OldVsn, StateName, State0, {Direction, From, To}) ->
+ State = convert_state(State0, Direction, From, To),
+ {ok, StateName, State};
+code_change(_OldVsn, StateName, State, _) ->
{ok, StateName, State}.
format_status(Type, Data) ->
@@ -958,3 +961,14 @@ workaround_transport_delivery_problems(Socket, gen_tcp = Transport) ->
Transport:recv(Socket, 0, 30000);
workaround_transport_delivery_problems(Socket, Transport) ->
Transport:close(Socket).
+
+convert_state(#state{ssl_options = Options} = State, up, "5.3.5", "5.3.6") ->
+ State#state{ssl_options = convert_options_partial_chain(Options, up)};
+convert_state(#state{ssl_options = Options} = State, down, "5.3.6", "5.3.5") ->
+ State#state{ssl_options = convert_options_partial_chain(Options, down)}.
+
+convert_options_partial_chain(Options, up) ->
+ {Head, Tail} = lists:split(5, tuple_to_list(Options)),
+ list_to_tuple(Head ++ [{partial_chain, fun(_) -> unknown_ca end}] ++ Tail);
+convert_options_partial_chain(Options, down) ->
+ list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))).
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index 22dc951ac1..daf4466f11 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -325,14 +325,14 @@ sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
{Type, 'NULL'};
sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
{?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}};
-sign_algorithm(#'ECPrivateKey'{}, Opts) ->
+sign_algorithm(#'ECPrivateKey'{parameters = Parms}, Opts) ->
Type = case proplists:get_value(digest, Opts, sha1) of
sha1 -> ?'ecdsa-with-SHA1';
sha512 -> ?'ecdsa-with-SHA512';
sha384 -> ?'ecdsa-with-SHA384';
sha256 -> ?'ecdsa-with-SHA256'
end,
- {Type, 'NULL'}.
+ {Type, Parms}.
make_key(rsa, _Opts) ->
%% (OBS: for testing only)
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index 608f2f11c3..3566a8a0a5 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2007-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
@@ -57,41 +57,51 @@ all_versions_groups ()->
].
key_cert_combinations() ->
- [client_ec_server_ec,
- client_rsa_server_ec,
- client_ec_server_rsa,
- client_rsa_server_rsa].
+ [client_ecdh_server_ecdh,
+ client_rsa_server_ecdh,
+ client_ecdh_server_rsa,
+ client_rsa_server_rsa,
+ client_ecdsa_server_ecdsa,
+ client_ecdsa_server_rsa,
+ client_rsa_server_ecdsa
+ ].
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- catch crypto:stop(),
+init_per_suite(Config0) ->
+ end_per_suite(Config0),
try crypto:start() of
ok ->
- ssl:start(),
- Config
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:log("Make certs ~p~n", [Result]),
+ Config1 = ssl_test_lib:make_ecdsa_cert(Config0),
+ Config2 = ssl_test_lib:make_ecdh_rsa_cert(Config1),
+ ssl_test_lib:cert_options(Config2)
catch _:_ ->
{skip, "Crypto did not start"}
end.
end_per_suite(_Config) ->
- ssl:stop(),
+ application:stop(ssl),
application:stop(crypto).
%%--------------------------------------------------------------------
-init_per_group(erlang_client, Config) ->
+init_per_group(erlang_client = Group, Config) ->
case ssl_test_lib:is_sane_ecc(openssl) of
true ->
- common_init_per_group(erlang_client, [{server_type, openssl},
- {client_type, erlang} | Config]);
+ common_init_per_group(Group, [{server_type, openssl},
+ {client_type, erlang} | Config]);
false ->
{skip, "Known ECC bug in openssl"}
end;
-init_per_group(erlang_server, Config) ->
+init_per_group(erlang_server = Group, Config) ->
case ssl_test_lib:is_sane_ecc(openssl) of
true ->
- common_init_per_group(erlang_client, [{server_type, erlang},
- {client_type, openssl} | Config]);
+ common_init_per_group(Group, [{server_type, erlang},
+ {client_type, openssl} | Config]);
false ->
{skip, "Known ECC bug in openssl"}
end;
@@ -99,11 +109,21 @@ init_per_group(erlang_server, Config) ->
init_per_group(erlang = Group, Config) ->
case ssl_test_lib:sufficient_crypto_support(Group) of
true ->
- common_init_per_group(erlang, [{server_type, erlang},
- {client_type, erlang} | Config]);
+ common_init_per_group(Group, [{server_type, erlang},
+ {client_type, erlang} | Config]);
+ false ->
+ {skip, "Crypto does not support ECC"}
+ end;
+
+init_per_group(openssl = Group, Config) ->
+ case ssl_test_lib:sufficient_crypto_support(Group) of
+ true ->
+ common_init_per_group(Group, [{server_type, openssl},
+ {client_type, openssl} | Config]);
false ->
{skip, "Crypto does not support ECC"}
- end;
+ end;
+
init_per_group(Group, Config) ->
common_init_per_group(Group, Config).
@@ -121,76 +141,118 @@ end_per_group(_GroupName, Config) ->
%%--------------------------------------------------------------------
-init_per_testcase(_TestCase, Config) ->
+init_per_testcase(TestCase, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
+ end_per_testcase(TestCase, Config),
+ ssl:start(),
Config.
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(_TestCase, Config) ->
+ application:stop(ssl),
Config.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-client_ec_server_ec(Config) when is_list(Config) ->
- basic_test("ec1.crt", "ec1.key", "ec2.crt", "ec2.key", Config).
-
-client_ec_server_rsa(Config) when is_list(Config) ->
- basic_test("ec1.crt", "ec1.key", "rsa1.crt", "rsa1.key", Config).
+client_ecdh_server_ecdh(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdh_rsa_opts, Config),
+ SOpts = ?config(server_ecdh_rsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
+
+client_ecdh_server_rsa(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdh_rsa_opts, Config),
+ SOpts = ?config(server_ecdh_rsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
+
+client_rsa_server_ecdh(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdh_rsa_opts, Config),
+ SOpts = ?config(server_ecdh_rsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
+
+client_rsa_server_rsa(Config) when is_list(Config) ->
+ COpts = ?config(client_verification_opts, Config),
+ SOpts = ?config(server_verification_opts, Config),
+ basic_test(COpts, SOpts, Config).
+
+client_ecdsa_server_ecdsa(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdsa_opts, Config),
+ SOpts = ?config(server_ecdsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
-client_rsa_server_ec(Config) when is_list(Config) ->
- basic_test("rsa1.crt", "rsa1.key", "ec2.crt", "ec2.key", Config).
+client_ecdsa_server_rsa(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdsa_opts, Config),
+ SOpts = ?config(server_ecdsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
-client_rsa_server_rsa(Config) when is_list(Config) ->
- basic_test("rsa1.crt", "rsa1.key", "rsa2.crt", "rsa2.key", Config).
+client_rsa_server_ecdsa(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdsa_opts, Config),
+ SOpts = ?config(server_ecdsa_verify_opts, Config),
+ basic_test(COpts, SOpts, Config).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-basic_test(ClientCert, ClientKey, ServerCert, ServerKey, Config) ->
- DataDir = ?config(data_dir, Config),
+basic_test(COpts, SOpts, Config) ->
+ basic_test(proplists:get_value(certfile, COpts),
+ proplists:get_value(keyfile, COpts),
+ proplists:get_value(cacertfile, COpts),
+ proplists:get_value(certfile, SOpts),
+ proplists:get_value(keyfile, SOpts),
+ proplists:get_value(cacertfile, SOpts),
+ Config).
+
+basic_test(ClientCert, ClientKey, ClientCA, ServerCert, ServerKey, ServerCA, Config) ->
SType = ?config(server_type, Config),
CType = ?config(client_type, Config),
{Server, Port} = start_server(SType,
- filename:join(DataDir, "CA.pem"),
- filename:join(DataDir, ServerCert),
- filename:join(DataDir, ServerKey),
+ ClientCA, ServerCA,
+ ServerCert,
+ ServerKey,
Config),
- Client = start_client(CType, Port, filename:join(DataDir, "CA.pem"),
- filename:join(DataDir, ClientCert),
- filename:join(DataDir, ClientKey), Config),
- check_result(Server, SType, Client, CType).
+ Client = start_client(CType, Port, ServerCA, ClientCA,
+ ClientCert,
+ ClientKey, Config),
+ check_result(Server, SType, Client, CType),
+ close(Server, Client).
-start_client(openssl, Port, CA, Cert, Key, _) ->
+start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ Cert ++ " -CAfile " ++ CA
- ++ " -key " ++ Key ++ " -host localhost -msg",
+ Cmd = "openssl s_client -verify 2 -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
+ " -cert " ++ Cert ++ " -CAfile " ++ NewCA
+ ++ " -key " ++ Key ++ " -host localhost -msg -debug",
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
true = port_command(OpenSslPort, "Hello world"),
OpenSslPort;
-start_client(erlang, Port, CA, Cert, Key, Config) ->
+start_client(erlang, Port, CA, _, Cert, Key, Config) ->
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{verify, verify_peer}, {cacertfile, CA},
+ {options, [{verify, verify_peer},
+ {cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]).
-start_server(openssl, CA, Cert, Key, _) ->
+start_server(openssl, CA, OwnCa, Cert, Key, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
+
Port = ssl_test_lib:inet_port(node()),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ Cert ++ " -CAfile " ++ CA
- ++ " -key " ++ Key ++ " -Verify 2 -msg",
+ " -verify 2 -cert " ++ Cert ++ " -CAfile " ++ NewCA
+ ++ " -key " ++ Key ++ " -msg -debug",
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
ssl_test_lib:wait_for_openssl_server(),
true = port_command(OpenSslPort, "Hello world"),
{OpenSslPort, Port};
-start_server(erlang, CA, Cert, Key, Config) ->
+start_server(erlang, CA, _, Cert, Key, Config) ->
+
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -217,9 +279,31 @@ openssl_check(_, Config) ->
TLSVersion = ?config(tls_version, Config),
case ssl_test_lib:check_sane_openssl_version(TLSVersion) of
true ->
- ssl:start(),
Config;
false ->
{skip, "TLS version not supported by openssl"}
end.
+close(Port1, Port2) when is_port(Port1), is_port(Port2) ->
+ ssl_test_lib:close_port(Port1),
+ ssl_test_lib:close_port(Port2);
+close(Port, Pid) when is_port(Port) ->
+ ssl_test_lib:close_port(Port),
+ ssl_test_lib:close(Pid);
+close(Pid, Port) when is_port(Port) ->
+ ssl_test_lib:close_port(Port),
+ ssl_test_lib:close(Pid);
+close(Client, Server) ->
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%% Work around OpenSSL bug, apparently the same bug as we had fixed in
+%% 11629690ba61f8e0c93ef9b2b6102fd279825977
+new_ca(FileName, CA, OwnCa) ->
+ {ok, P1} = file:read_file(CA),
+ E1 = public_key:pem_decode(P1),
+ {ok, P2} = file:read_file(OwnCa),
+ E2 = public_key:pem_decode(P2),
+ Pem = public_key:pem_encode(E2 ++E1),
+ file:write_file(FileName, Pem),
+ FileName.
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/CA.pem b/lib/ssl/test/ssl_ECC_SUITE_data/CA.pem
deleted file mode 100644
index f82efdefc5..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/CA.pem
+++ /dev/null
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICGjCCAYegAwIBAgIQZIIqq4RXfpBKJXV69Jc4BjAJBgUrDgMCHQUAMB0xGzAZ
-BgNVBAMTEklTQSBUZXN0IEF1dGhvcml0eTAeFw0xMjAzMjAxNzEzMjFaFw0zOTEy
-MzEyMzU5NTlaMB0xGzAZBgNVBAMTEklTQSBUZXN0IEF1dGhvcml0eTCBnzANBgkq
-hkiG9w0BAQEFAAOBjQAwgYkCgYEAqnt6FSyFQVSDyP7mY63IhCzgysTxBEg1qDb8
-nBHj9REReZA5UQ5iyEOdTbdLyOaSk2rJyA2wdTjYkNnLzK49nZFlpf89r3/bakAM
-wZv69S3FJi9W2z9m4JPv/5+QCYnFNRSnnHw3maNElwoQyknx96I3W7EuVOvKtKhh
-4DaD0WsCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zBOBgNVHQEERzBFgBBCHwn2
-8AmbN+cvJl1iJ1bsoR8wHTEbMBkGA1UEAxMSSVNBIFRlc3QgQXV0aG9yaXR5ghBk
-giqrhFd+kEoldXr0lzgGMAkGBSsOAwIdBQADgYEAIlVecua5Cr1z/cdwQ8znlgOU
-U+y/uzg0nupKkopzVnRYhwV4hxZt3izAz4C/SJZB7eL0bUKlg1ceGjbQsGEm0fzF
-LEV3vym4G51bxv03Iecwo96G4NgjJ7+9/7ciBVzfxZyfuCpYG1M2LyrbOyuevtTy
-2+vIueT0lv6UftgBfIE=
------END CERTIFICATE-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/ec1.crt b/lib/ssl/test/ssl_ECC_SUITE_data/ec1.crt
deleted file mode 100644
index 7d2b9cde9d..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/ec1.crt
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBhjCB8AIBBjANBgkqhkiG9w0BAQUFADAdMRswGQYDVQQDExJJU0EgVGVzdCBB
-dXRob3JpdHkwHhcNMTMwODA4MTAxNDI3WhcNMjMwODA2MTAxNDI3WjBFMQswCQYD
-VQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWExFTATBgNVBAcTDEZvcnQgQmVsdm9p
-cjEMMAoGA1UEAxMDZWMxMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEpiRIxUCESROR
-P8IByg+vBv1fDdAg7yXfAh95GxFtvhBqZs6ATwaRKyLmZYgUm/4NUAyUeqmTBb7s
-2msKo5mnNzANBgkqhkiG9w0BAQUFAAOBgQAmwzoB1DVO69FQOUdBVnyups4t0c1c
-8h+1z/5P4EtPltk4o3mRn0AZogqdXCpNbuSGbSJh+dep5xW30VLxNHdc+tZSLK6j
-pT7A3hymMk8qbi13hxeH/VpEP25y1EjHowow9Wmb6ebtT/v7qFQ9AAHD9ONcIM4I
-FCC8vdFo7M5GgQ==
------END CERTIFICATE-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/ec1.key b/lib/ssl/test/ssl_ECC_SUITE_data/ec1.key
deleted file mode 100644
index 2dc9508b3c..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/ec1.key
+++ /dev/null
@@ -1,8 +0,0 @@
------BEGIN EC PARAMETERS-----
-BgUrgQQACg==
------END EC PARAMETERS-----
------BEGIN EC PRIVATE KEY-----
-MHQCAQEEIOO0WK8znNzLyZIoGRIlaKnCNr2Wy8uk9i+GGFIhDGNAoAcGBSuBBAAK
-oUQDQgAEpiRIxUCESRORP8IByg+vBv1fDdAg7yXfAh95GxFtvhBqZs6ATwaRKyLm
-ZYgUm/4NUAyUeqmTBb7s2msKo5mnNw==
------END EC PRIVATE KEY-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/ec2.crt b/lib/ssl/test/ssl_ECC_SUITE_data/ec2.crt
deleted file mode 100644
index b0558a0ebc..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/ec2.crt
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBhjCB8AIBBzANBgkqhkiG9w0BAQUFADAdMRswGQYDVQQDExJJU0EgVGVzdCBB
-dXRob3JpdHkwHhcNMTMwODA4MTAxNDM0WhcNMjMwODA2MTAxNDM0WjBFMQswCQYD
-VQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWExFTATBgNVBAcTDEZvcnQgQmVsdm9p
-cjEMMAoGA1UEAxMDZWMyMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzXaYReUyvoYl
-FwGOe0MJEXWCUncMfr2xG4GMjGYlfZsvLGEokefsJIvW+I+9jgUT2UFjxFXYNAvm
-uD1A1iWVWjANBgkqhkiG9w0BAQUFAAOBgQBFa6iIlrT9DWptIdB8uSYvp7qwiHxN
-hiVH5YhGIHHqjGZqtRHrSxqNEYMXXrgH9Hxc6gDbk9PsHZyVVoh/HgVWddqW1inh
-tStZm420PAKCuH4T6Cfsk76GE2m7FRzJvw9TM1f2A5nIy9abyrpup8lZGcIL4Kmq
-1Fix1LRtrmLNTA==
------END CERTIFICATE-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/ec2.key b/lib/ssl/test/ssl_ECC_SUITE_data/ec2.key
deleted file mode 100644
index 366d13648b..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/ec2.key
+++ /dev/null
@@ -1,8 +0,0 @@
------BEGIN EC PARAMETERS-----
-BgUrgQQACg==
------END EC PARAMETERS-----
------BEGIN EC PRIVATE KEY-----
-MHQCAQEEIPR3ORUpAFMTQhUJ0jllN38LKWziG8yP2H54Y/9vh1PwoAcGBSuBBAAK
-oUQDQgAEzXaYReUyvoYlFwGOe0MJEXWCUncMfr2xG4GMjGYlfZsvLGEokefsJIvW
-+I+9jgUT2UFjxFXYNAvmuD1A1iWVWg==
------END EC PRIVATE KEY-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.crt b/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.crt
deleted file mode 100644
index ed9beacf68..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.crt
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDVjCCAr8CAQkwDQYJKoZIhvcNAQEFBQAwHTEbMBkGA1UEAxMSSVNBIFRlc3Qg
-QXV0aG9yaXR5MB4XDTEzMDgwODEwMTUzNFoXDTQwMTIyNDEwMTUzNFowRjELMAkG
-A1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRUwEwYDVQQHEwxGb3J0IEJlbHZv
-aXIxDTALBgNVBAMTBHJzYTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQC62v40w1AjV3oJuyYC2Fw6XhTOi1il6xZFnB9J1WhCmuxAB/VMhBcNypx38mNk
-eQ7a/ERQ5ddhZey29DYeFYU8oqfDURgWx5USHufb90xBen9KPmX3VNuQ8ZFP2q8Q
-b01/oRHBJQRBuaCtFHzpGIVBjC6dD5yeQgJsYaF4u+PBbonsIGROXMybcvUzXmjU
-dwpy2NhjGQL5sWcOdIeRP43APSyRYvq4tuBUZk2XxWfBcvA8LpcoYPMlRTf6jGL1
-/fAAcCYJ9lh3h92w0NZ/7ZRa/ebTplxK6yqCftuSKui1KdL69m0WZqHl79AUSfs9
-lsOwx9lHkyYvJeMofyeDbZ+3OYLmVqEBG1fza2aV2XVh9zJ8fAwmXy/c2IDhw/oD
-HAe/rSg/Sgt03ydIKqtZHbl3v0EexQQRlJRULIzdtON02dJMUd4EFUgQ9OUtEmC2
-Psj9Jdu1g5cevU7Mymu8Ot+fjHiGTcBUsXNuXFCbON3Gw7cIDl4+iv+cpDHHVC9L
-HK3PMEq3vu3qOGXSz+LDOoqkfROcLG7BclBuN2zoVSsMHFkB4aJhwy7eHhGz0z2W
-c6LTVd+GAApdY80kmjOjT//QxHEsX/n1useHza3OszQqZiArr4ub4rtq+l1DxAS/
-DWrZ/JGsbKL8cjWso6qBF94xTi8WhjkKuUYhsm+qLAbNOQIDAQABMA0GCSqGSIb3
-DQEBBQUAA4GBAIcuzqRkfypV/9Z85ZQCCoejPm5Urhv7dfg1/B3QtazogPBZLgL5
-e60fG1uAw5GmqTViHLvW06z73oQvJrFkrCLVvadDNtrKYKXnXqdkgVyk36F/B737
-A43HGnMfSxCfRhIOuKZB9clP5PiNlhw36yi3DratqT6TUvI69hg8a7jA
------END CERTIFICATE-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.key b/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.key
deleted file mode 100644
index 6e0d913d79..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/rsa1.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKAIBAAKCAgEAutr+NMNQI1d6CbsmAthcOl4UzotYpesWRZwfSdVoQprsQAf1
-TIQXDcqcd/JjZHkO2vxEUOXXYWXstvQ2HhWFPKKnw1EYFseVEh7n2/dMQXp/Sj5l
-91TbkPGRT9qvEG9Nf6ERwSUEQbmgrRR86RiFQYwunQ+cnkICbGGheLvjwW6J7CBk
-TlzMm3L1M15o1HcKctjYYxkC+bFnDnSHkT+NwD0skWL6uLbgVGZNl8VnwXLwPC6X
-KGDzJUU3+oxi9f3wAHAmCfZYd4fdsNDWf+2UWv3m06ZcSusqgn7bkirotSnS+vZt
-Fmah5e/QFEn7PZbDsMfZR5MmLyXjKH8ng22ftzmC5lahARtX82tmldl1YfcyfHwM
-Jl8v3NiA4cP6AxwHv60oP0oLdN8nSCqrWR25d79BHsUEEZSUVCyM3bTjdNnSTFHe
-BBVIEPTlLRJgtj7I/SXbtYOXHr1OzMprvDrfn4x4hk3AVLFzblxQmzjdxsO3CA5e
-Por/nKQxx1QvSxytzzBKt77t6jhl0s/iwzqKpH0TnCxuwXJQbjds6FUrDBxZAeGi
-YcMu3h4Rs9M9lnOi01XfhgAKXWPNJJozo0//0MRxLF/59brHh82tzrM0KmYgK6+L
-m+K7avpdQ8QEvw1q2fyRrGyi/HI1rKOqgRfeMU4vFoY5CrlGIbJvqiwGzTkCAwEA
-AQKCAgBkXyaWKSRvF5pSh9lPRfGk2MzMdkXUOofoNIkKHDy5KocljiDSTVIk8mVC
-eU2ytuSn9UKtQgmEJEAXtu8rEdxUSftcC7+o3OTSqw9ZNWoc8jRWKVaUmVyoa1rn
-Tk0jwuYaXOcwnTXAKHqK/qpqe+V45FhVvgEfcc3jcj5OoH8jdMFZubyn62ltRz83
-rMsa9icCskDqWpEil40IUshP2ZfHYBUEs+qCNpoiPCIKGNw3KgqqCUzhP9LcfmYn
-jCnMge/eDGAikdXLv4vyYvwWFATRK/pGTuLcy542IvbHeY0vY5wVezH2CoOFBGD9
-xQ/UcZwE5hVtQToNsYhoRIVxL/3Of0qDk1M6W2Plh2MAstyejIHE3ct0pPfW3rsu
-j/9Z/H0P9Q5ghSjarwOp2qGrrz6/4LVbbTDY8V1L928l4SqbUMtEQxcxTBN8YFoD
-mPV3Jc3zls9wiiEX53RcH8MK5tjrcRwWqurTZvi/pkLfXlGDgKGCOaa3HgWVQyU+
-L6jVZM+u1nwN+jNXQYGeLEro/6tvG8WQbRMHQoxLG+rm4V3/SwH0DcfrVFDTg+i6
-3wMU1GC/aQEdTFWXvHAkpwrf4M9QWvjtheiaSxtBUoAY6l+ixCVHKrIk6glKLEjx
-92HxmcJdopQScFETAyg8eVKV0kOGfVeFEpIqwq7hVedmTflpQQKCAQEA44h4dAta
-cYeBqBr8eljWcgs79gmgwBEQxQUnwE/zuzLKn5NxAW324Kh25V/n/MupUzBlLPWn
-91UHfw9PCXT8/HvgYQ4S5sXbKRbGmuPSsTmz4Rfe2ix6RggVNUOwORVNDyM7SQh7
-USdzZH5dMxKfF5L/b4Byx7eQZaoeKlfaXcqgikNZZ6pkhVCNxUKi9vvjS9r2wwCd
-xtgu5MfTpdEci0zH1+uuRisVRcEbcRX9umUTCiZrmEeddZXNiwTAS3FtX7qGzuq9
-LKIeETwcOZrWj0E48UvbSfK4Axn7sf5J0n7/Qo7I089S5QQEI6ZDP501i71dNFhn
-qfcY30c1k3TC7QKCAQEA0juuVHExKNLLNmQejNPfuHYoH0Uk2BH/8x96/Mkj6k6K
-SUCHDS3iWOljXGw8YtpS8v5mGBGgMhJ+s/vCRM6R9eXYTc8u2ktY/kjyW0PgW8/Z
-vb9VrQpn5svTNwj2Q8qYsTqXnQKO7YuL+hnQpQNAcID6FTeOASVLGObEf810qRfN
-4y3RqCWUnYXXTyXj+cJdbXTxfF7HVZPIAQKqE7J5Qo9ynYILY62oSmUGC6m8VKyE
-rrvDMK1IVi0X4w+Jx4HX0IC2+DBKxCaLWT69bE1IwjB06Q5zoTQPVi6c6qQp7K0H
-kqSyLJ/ctwcEubu0DPNmvMlgWtAbAsoESA5GbIit/QKCAQEAxRzp9OYNAUM6AK74
-QOmLRZsT4+6tUxa1p2jy6fiZlnfG731kra9c630mG0n9iJPK6aWIUO20CGGiL+HM
-P84YiIaseIgfucp4NV1kyrRJR31MptjuF6Xme5ru/IjaNmmMq2uDJZ7ybfi2T73k
-8aTVLDANl8P4K6qLrnc00MvxAcXTVFRKNLN5h8CkQNqcoUjPvVxA3+g9xxBrd4jh
-gsnoZ4kpq5WiEWmrcRV8t3gsqfh8CRQFrBOGhmIzgZapG/J0pTTLKqBTKEJ9t8KS
-VRkdfVcshGWJ4MMjxJQS5zz7KR8Z9cgKlOwLzRiwmU2k/owr4hY3k2xuyeClrHBd
-KpRBdQKCAQBvDk/dE55gbloi9WieBB6eluxC+IeqDHgkunCBsM9kKvEqGQg+kgqL
-5V4zqImNvr8q1fCgrk7tpI+CDHBnYKgCOdS15cheUIdGbMp6I7UVSws/DR/5NRIF
-/Y4p+HX/Abr/hHAq5PsTyS+8gn6RbNJRnBB/vMUrHcQ5902+JY6G9KgyZjXmmVOU
-kutWSDHR8jbgZ3JZvMeYEWUKA5pMpW8hFh35zoStt0K7afpzlsqCAFBm7ZEC2cbo
-nxGLRN4HojObVSNSoFAepi3eiyINYBYbXvWjV5sFgTbI0/7YhLgQ6qahdJcas6go
-l3CLnPhUDxAqkkZwMpbSNl1kowXYt6sRAoIBAAOWnXgf9Bdb9OWKGgt42gVfC4cz
-zj2JoLpbDTtbEdHNn8XQvPhGbpdtgnsuEMijIMy1UTlmv17jbFWdZTDeN31EUJrC
-smgKX0OlVFKD90AI0BiIREK0hJUBV0pV4JoUjwnQBHGvranD06/wAtHEqgqF1Ipp
-DCAKwxggM7qtB1R1vkrc/aLQej+mlwA8N6q92rnEsg+EnEbhtLDDZQcV/q5cSDCN
-MMcnM+QdyjKwEeCVXHaqNfeSqKg/Ab2eZbS9VxA+XZD73+eUY/JeJsg7LfZrRz0T
-ij5LCS7A+nVB5/B5tGkk4fcNhk2n356be6l46S98BEgtuwGLC9pqXf7zyp4=
------END RSA PRIVATE KEY-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.crt b/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.crt
deleted file mode 100644
index 06ca92dda3..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.crt
+++ /dev/null
@@ -1,20 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDVjCCAr8CAQowDQYJKoZIhvcNAQEFBQAwHTEbMBkGA1UEAxMSSVNBIFRlc3Qg
-QXV0aG9yaXR5MB4XDTEzMDgwODEwMTYwMloXDTQwMTIyNDEwMTYwMlowRjELMAkG
-A1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRUwEwYDVQQHEwxGb3J0IEJlbHZv
-aXIxDTALBgNVBAMTBHJzYTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
-AQCjQUe0BGOpULjOAmLbXM4SSQzJvxJbCFi3tryyd+OARq6Fdp6/fslVhsr0PhWE
-X8yRbAugIjseTpLwz+1OC6LavOGV1ixzGTI/9HDXGKbf8qoCrSdh28sqQJnmqGT4
-UCKLn6Rqjg2iyBBcSK3LrtKEPI4C7NaSOZUtANkppvziEMwm+0r16sgHh2Xx6mxd
-22q01kq1lJqwEnIDPMSz3+ESUVQQ4T3ka7yFIhc9PYmILIXkZi0x7AiDeRkIILul
-GQrduTWSPGY3prXeDAbmQNazxrHp8fcR2AfFSI6HYxMALq9jWxc4xDIkss6BO2Et
-riJOIgXFpbyVsYCbkI1kXhEWFDt3uJBIcmtJKGzro4xv+XLG6BbUeTJgSHXMc7Cb
-fX87+CBIFR5a/aqkEKh/mcvsDdaV+kpNKdr7q4wAuIQb8g7IyXEDuAm1VZjQs9WC
-KFRGSq9sergEw9gna0iThRZjD+dzNzB17XmlAK4wa98a7MntwqpAt/GsCFOiPM8E
-c+8gpuo8WqC0kP8OpImyw9cQhlZ3dca1qkr2cyKyAOGxUxyA67FgiHSsxJJ2Xhse
-o49qeKTjMZd8zhSokM2TH6qEf7YfOePU51YRfAHUhzRmE31N/MExqDjFjklksEtM
-iHhbPo+cOoxV8x1u13umdUvtTaAUSBA/DpvzWdnORvnaqQIDAQABMA0GCSqGSIb3
-DQEBBQUAA4GBAFD+O7h+5R5S1rIN9eC+oEGpvRhMG4v4G3pJp+c7bbtO7ifFx1WP
-bta1b5YtiQYcKP0ORABm/3Kcvsb3VbaMH/zkxWEbASZsmIcBY3ml4f2kkn6WT2hD
-Wc6VMIAR3N6Mj1b30yI1qYVIid+zIouiykMB+zqllm+Uar0SPNjKxDU/
------END CERTIFICATE-----
diff --git a/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.key b/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.key
deleted file mode 100644
index d415ef0391..0000000000
--- a/lib/ssl/test/ssl_ECC_SUITE_data/rsa2.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJJwIBAAKCAgEAo0FHtARjqVC4zgJi21zOEkkMyb8SWwhYt7a8snfjgEauhXae
-v37JVYbK9D4VhF/MkWwLoCI7Hk6S8M/tTgui2rzhldYscxkyP/Rw1xim3/KqAq0n
-YdvLKkCZ5qhk+FAii5+kao4NosgQXEity67ShDyOAuzWkjmVLQDZKab84hDMJvtK
-9erIB4dl8epsXdtqtNZKtZSasBJyAzzEs9/hElFUEOE95Gu8hSIXPT2JiCyF5GYt
-MewIg3kZCCC7pRkK3bk1kjxmN6a13gwG5kDWs8ax6fH3EdgHxUiOh2MTAC6vY1sX
-OMQyJLLOgTthLa4iTiIFxaW8lbGAm5CNZF4RFhQ7d7iQSHJrSShs66OMb/lyxugW
-1HkyYEh1zHOwm31/O/ggSBUeWv2qpBCof5nL7A3WlfpKTSna+6uMALiEG/IOyMlx
-A7gJtVWY0LPVgihURkqvbHq4BMPYJ2tIk4UWYw/nczcwde15pQCuMGvfGuzJ7cKq
-QLfxrAhTojzPBHPvIKbqPFqgtJD/DqSJssPXEIZWd3XGtapK9nMisgDhsVMcgOux
-YIh0rMSSdl4bHqOPanik4zGXfM4UqJDNkx+qhH+2Hznj1OdWEXwB1Ic0ZhN9TfzB
-Mag4xY5JZLBLTIh4Wz6PnDqMVfMdbtd7pnVL7U2gFEgQPw6b81nZzkb52qkCAwEA
-AQKCAgBORLHXwHL3bdfsDIDQooG5ioQzBQQL2MiP63A0L/5GNZzeJ6ycKnDkLCeJ
-SWqPeE5fOemo8EBfm1QfV9BxpmqBbCTK7U+KLv5EYzDmLs9ydqjDd7h11iZlL2uZ
-hgpCckjdn7/3xfsLm9ccJ0wLZtlOxKlhBaMpn6nBVbLHoWOEDoGR/tBFbjZQRb2+
-aaFirhtOb56Jx6ER4QYAP1Ye1qrVWWBwZ0yBApXzThDOL36MZqwagFISqRK71YcG
-uoq78HGhM3ZXkdV/wNFYj3OPWG6W6h/KBVNqnqO7FbofdoRZhghYHgfYE1fm+ELA
-+nLwr5eK1gzmYTs0mVELRBZFlEOkCfYNOnuRgysFezEklS+ICp3HzIhYXza3kyTf
-B2ZBwZZVCv/94MKyibyANErmv1a5ugY5Hsn9/WKC8qTto+qLYoyFCvBjzj0PSaVX
-/3cty2DY0SK16K1Y4AOPtJMYTXYB3tVX8Akgjz1F6REBtZSOXrSQ3Vhy1ORl3Hzf
-WCBYDqL8K0hJiBVgkvneIyIjmFHsdM60Nr7EldBEnJ/UrPzsl2VuWFPZlnasfUaW
-x+vq1H4Dfz+bHt8coBRHDjKgUvwkfFeBQOBR5DG3vMrxguVRA1EYYMRR5C3yxk2m
-ARAtdh4VxUQDQjjrmr7Dl/y1rU34aInXIrrFWpuvIhl8Ht09sQKCAQEA1pXKK5f0
-HkKfM/qk5xzF+WdHClBrPXi0XwLN6UQ+WWMMNhkGZ+FMPXl/6IJDT91s6DA3tPhr
-OZF64n9ZFaGgHNBXNiB+Txjv5vZeSBMFt3hSonqt42aijx6gXfmLnkA+TYpa6Wex
-YCeEgdH8LocJa7Gj2vzrYliPYk3deh6SnZZ6N8bI+ciwK3ZGF/pkWaTX83dIFq3w
-YyZ+0dEpNGbA9812wNVourPg3OfqG3/CdnTfvY1M9KCC3JalpyzQL4Zm5soXF0wj
-36C2yTxA02AyFz3TvUIBrvsN6i0gmGfE79+UIp29JYrFRsIgBDt+ze2vQWUz2MX5
-GeX6/yCBgiTXtwKCAQEAwsNf6k2m5Cw+WtuLzzUfBBJCN+t1lrnYJ6lF0HubW6TZ
-vX1kBWyc+Rpo4ljr/+f4R9aC/gTEQOmV/hNVZy1RU2dAI8cH+r6JWG9lgif+8h//
-5R81txE7gnuK1Na7PmvnQPPN661zsQZ5e1ENPXS3TJmUW/M01JxAMqEQjvAPa/II
-H2KjL5NX28k9Hiw9rP6n+qXAfG/LEwXgoVCcehPwfANqQ1l95UgOdKDmjG94dipI
-h2DEK70ZbrsgQbT60Wd8I5h0yhiQsik2/bVkqLmcG4SSg0/5cf2vZMApgoH/adUz
-rJFdthm7iGPLhwS6fbhXew17Af96FvzfkifUV+cgnwKCAQBNUlYyFSQKz1jMgxFu
-kciokNVhWw75bIgaAEmwNz38OZuJ1sSfI+iz8hbr8hxNJ+15UP6RwD3q1YghG2A/
-Uij+mPgD8ftxhvvTDo10jR4vOTUVhP0phq8mwRNqKWRs1ptcl3Egz5NzoWm22bJ0
-FYaIfs8bNq2el2i7NHGM8n1EOZe6h2+dyfno/0pMk5YbUzHZce7Q9UY8g/+InUSq
-tCfuYuPaokuFkxGAqDSMSiIJSx3gEI1dTIU69TGlppkxts1XdhSR+YanqyKSKpr1
-T6FdDJNCjAlNQvuFmVM4d5PYF4kqXApu/60MTSD6RXHwxCe1ecEP6G5VLbCew9jG
-y33LAoIBAGsWyC9pwQEm/qYwn4AwYjx32acrtX1J9HtiTLvkqzjJvNu/DXcaEHm7
-tr32TNVp9A9z+JS5hDt49Hs+oC/aMCRe2lqRvmZ1y8kvfy4A1eLGC4stDPj65bDK
-QzziURRyejYxmCElPz6wI63VlCUdfwgEThn88SiSPY5ZF2SwxJoC+8peDwJCzwVP
-cmabxtHPOAfOibciNRPhoHCyhUdunUVjD1O26k1ewGwKaJoBVMgMWdLuNw8hq9FB
-3OukGmF3uD9OPbE9rpn3pX/89Dr9y8MpsvG20J6H8Z/BNVHILus/SmlxiIhvP7kv
-viIgTHaCHL/RWrhvg+8N3dRcSBqJQFsCggEAFe2TMEq2AlnBn4gsuAOIuZPYKQCg
-2a+tl1grQzmNth6AGGQcIqShadICD6SnVMIS64HHV/m18Cuz7GhJ06ZVjXJsHueG
-UpTE9wAmI2LxnNkupkLJu+SVcW3N86PujWmQBFpHkd+IRPLS51xjD9W5zLJ7HL4/
-fnKO+B+ZK6Imxbe5C5vJezkGfeOSyQoVtt6MT/XtSKNEGPBX+M6fLKgUMMg2H2Mt
-/SsD7DkOzFteKXzaEg/K8oOTpsOPkVDwNl2KErlEqbJv0k7yEVw50mYmsn/OLjh8
-+9EibISwCODbPxB+PhV6u2ue1IvGLRqtsN60lFOvbGn+kSewy9EUVHHQDQ==
------END RSA PRIVATE KEY-----
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 14047c6e9c..b7864ba6e7 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-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
@@ -58,6 +58,10 @@ tests() ->
server_verify_none,
server_require_peer_cert_ok,
server_require_peer_cert_fail,
+ server_require_peer_cert_partial_chain,
+ server_require_peer_cert_allow_partial_chain,
+ server_require_peer_cert_do_not_allow_partial_chain,
+ server_require_peer_cert_partial_chain_fun_fail,
verify_fun_always_run_client,
verify_fun_always_run_server,
cert_expired,
@@ -143,8 +147,8 @@ server_verify_none() ->
[{doc,"Test server option verify_none"}].
server_verify_none(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
Active = ?config(active, Config),
ReceiveFunction = ?config(receive_function, Config),
@@ -261,6 +265,163 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+
+server_require_peer_cert_partial_chain() ->
+ [{doc, "Client sends an incompleate chain, by default not acceptable."}].
+
+server_require_peer_cert_partial_chain(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
+ [{_,RootCA,_}, {_, _, _}] = public_key:pem_decode(ClientCAs),
+
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false},
+ {cacerts, [RootCA]} |
+ proplists:delete(cacertfile, ClientOpts)]}]),
+ receive
+ {Server, {error, {tls_alert, "unknown ca"}}} ->
+ receive
+ {Client, {error, {tls_alert, "unknown ca"}}} ->
+ ok;
+ {Client, {error, closed}} ->
+ ok
+ end
+ end.
+%%--------------------------------------------------------------------
+server_require_peer_cert_allow_partial_chain() ->
+ [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}].
+
+server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
+
+ PartialChain = fun(CertChain) ->
+ case lists:member(IntermidiateCA, CertChain) of
+ true ->
+ {trusted_ca, IntermidiateCA};
+ false ->
+ unknown_ca
+ end
+ end,
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts)]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+ %%--------------------------------------------------------------------
+server_require_peer_cert_do_not_allow_partial_chain() ->
+ [{doc, "Server does not accept the chain sent by the client as ROOT CA is unkown, "
+ "and we do not choose to trust the intermediate CA. (partial_chain option)"}].
+
+server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
+
+ PartialChain = fun(_CertChain) ->
+ unknown_ca
+ end,
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts)]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ receive
+ {Server, {error, {tls_alert, "unknown ca"}}} ->
+ receive
+ {Client, {error, {tls_alert, "unknown ca"}}} ->
+ ok;
+ {Client, {error, closed}} ->
+ ok
+ end
+ end.
+
+ %%--------------------------------------------------------------------
+server_require_peer_cert_partial_chain_fun_fail() ->
+ [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}].
+
+server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _}] = public_key:pem_decode(ServerCAs),
+
+ PartialChain = fun(_CertChain) ->
+ ture = false %% crash on purpose
+ end,
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts)]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ receive
+ {Server, {error, {tls_alert, "unknown ca"}}} ->
+ receive
+ {Client, {error, {tls_alert, "unknown ca"}}} ->
+ ok;
+ {Client, {error, closed}} ->
+ ok
+ end
+ end.
+
+%%--------------------------------------------------------------------
verify_fun_always_run_client() ->
[{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
@@ -434,10 +595,16 @@ cert_expired(Config) when is_list(Config) ->
Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
- {options, [{verify, verify_peer} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}},
- Client, {error, {tls_alert, "certificate expired"}}).
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+ receive
+ {Client, {error, {tls_alert, "certificate expired"}}} ->
+ receive
+ {Server, {error, {tls_alert, "certificate expired"}}} ->
+ ok;
+ {Server, {error, closed}} ->
+ ok
+ end
+ end.
two_digits_str(N) when N < 10 ->
lists:flatten(io_lib:format("0~p", [N]));
@@ -632,7 +799,7 @@ no_authority_key_identifier() ->
no_authority_key_identifier(Config) when is_list(Config) ->
ClientOpts = ?config(client_verification_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
PrivDir = ?config(priv_dir, Config),
KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
@@ -804,7 +971,7 @@ unknown_server_ca_fail() ->
[{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}].
unknown_server_ca_fail(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -833,11 +1000,11 @@ unknown_server_ca_fail(Config) when is_list(Config) ->
{verify_fun, FunAndState}
| ClientOpts]}]),
receive
- {Server, {error, {tls_alert, "unknown ca"}}} ->
+ {Client, {error, {tls_alert, "unknown ca"}}} ->
receive
- {Client, {error, {tls_alert, "unknown ca"}}} ->
+ {Server, {error, {tls_alert, "unknown ca"}}} ->
ok;
- {Client, {error, closed}} ->
+ {Server, {error, closed}} ->
ok
end
end.
@@ -848,7 +1015,7 @@ unknown_server_ca_accept_verify_none() ->
[{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}].
unknown_server_ca_accept_verify_none(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -873,7 +1040,7 @@ unknown_server_ca_accept_verify_peer() ->
" with a verify_fun that accepts the unknown ca error"}].
unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -912,7 +1079,7 @@ unknown_server_ca_accept_backwardscompatibility() ->
[{doc,"Test that old style verify_funs will work"}].
unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 5f36842f9e..8dca733526 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -38,6 +38,8 @@ all() -> [decode_hello_handshake,
decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
encode_single_hello_sni_extension_correctly,
+ decode_single_hello_sni_extension_correctly,
+ decode_empty_server_sni_correctly,
select_proper_tls_1_2_rsa_default_hashsign].
%%--------------------------------------------------------------------
@@ -98,6 +100,20 @@ encode_single_hello_sni_extension_correctly(_Config) ->
Encoded = ssl_handshake:encode_hello_extensions(Exts),
HelloExt = Encoded.
+decode_single_hello_sni_extension_correctly(_Config) ->
+ Exts = #hello_extensions{sni = #sni{hostname = "test.com"}},
+ SNI = <<16#00, 16#00, 16#00, 16#0d, 16#00, 16#0b, 16#00, 16#00, 16#08,
+ $t, $e, $s, $t, $., $c, $o, $m>>,
+ Decoded = ssl_handshake:decode_hello_extensions(SNI),
+ Exts = Decoded.
+
+decode_empty_server_sni_correctly(_Config) ->
+ Exts = #hello_extensions{sni = ""},
+ SNI = <<?UINT16(?SNI_EXT),?UINT16(0)>>,
+ Decoded = ssl_handshake:decode_hello_extensions(SNI),
+ Exts = Decoded.
+
+
select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
% RFC 5246 section 7.4.1.4.1 tells to use {sha1,rsa} as default signature_algorithm for RSA key exchanges
{sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,3}),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 150b5037d7..74d71263de 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -450,7 +450,7 @@ make_ecdsa_cert(Config) ->
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ClientCaCertFile},
+ {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
{client_ecdsa_opts, [{ssl_imp, new},{reuseaddr, true},
@@ -475,7 +475,7 @@ make_ecdh_rsa_cert(Config) ->
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
{server_ecdh_rsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
- {cacertfile, ClientCaCertFile},
+ {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile},
{verify, verify_peer}]},
{client_ecdh_rsa_opts, [{ssl_imp, new},{reuseaddr, true},
@@ -1136,3 +1136,36 @@ filter_suites(Ciphers0) ->
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl:suite_definition(S) || S <- Supported1],
[Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
+
+-define(OPENSSL_QUIT, "Q\n").
+close_port(Port) ->
+ catch port_command(Port, ?OPENSSL_QUIT),
+ close_loop(Port, 500, false).
+
+close_loop(Port, Time, SentClose) ->
+ receive
+ {Port, {data,Debug}} when is_port(Port) ->
+ ct:log("openssl ~s~n",[Debug]),
+ close_loop(Port, Time, SentClose);
+ {ssl,_,Msg} ->
+ ct:log("ssl Msg ~s~n",[Msg]),
+ close_loop(Port, Time, SentClose);
+ {Port, closed} ->
+ ct:log("Port Closed~n",[]),
+ ok;
+ {'EXIT', Port, Reason} ->
+ ct:log("Port Closed ~p~n",[Reason]),
+ ok;
+ Msg ->
+ ct:log("Port Msg ~p~n",[Msg]),
+ close_loop(Port, Time, SentClose)
+ after Time ->
+ case SentClose of
+ false ->
+ ct:log("Closing port ~n",[]),
+ catch erlang:port_close(Port),
+ close_loop(Port, Time, true);
+ true ->
+ ct:log("Timeout~n",[])
+ end
+ end.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index d36e441c7a..942c446ec4 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -226,7 +226,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -259,7 +259,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
%%--------------------------------------------------------------------
@@ -298,7 +298,7 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -332,11 +332,9 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
-
erlang_client_openssl_server_dsa_cert() ->
[{doc,"Test erlang server with openssl client"}].
erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
@@ -376,7 +374,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
@@ -414,7 +412,7 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
@@ -450,7 +448,7 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false),
ok.
@@ -496,7 +494,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false),
ok.
@@ -542,7 +540,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
@@ -581,7 +579,7 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
%% Clean close down! Server needs to be closed first !!
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
@@ -624,7 +622,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -666,7 +664,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
ssl_test_lib:check_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -708,7 +706,7 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
ssl_test_lib:close(Server),
process_flag(trap_exit, false).
@@ -821,7 +819,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
[{versions, [Version]} | ClientOpts]}]),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client1),
process_flag(trap_exit, false),
ok.
@@ -878,7 +876,7 @@ expired_session(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client2),
process_flag(trap_exit, false).
@@ -1089,7 +1087,7 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
Result = ssl_test_lib:wait_for_result(Client, ok),
%% Clean close down! Server needs to be closed first !!
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
ssl_test_lib:close(Client),
Return = case Result of
@@ -1136,7 +1134,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens
Callback(Client, OpensslPort),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -1175,7 +1173,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
Callback(Client, OpensslPort),
%% Clean close down! Server needs to be closed first !!
- close_port(OpensslPort),
+ ssl_test_lib:close_port(OpensslPort),
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
@@ -1205,7 +1203,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
@@ -1234,7 +1232,7 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
ssl_test_lib:close(Server),
- close_port(OpenSslPort),
+ ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
@@ -1282,39 +1280,6 @@ delayed_send(Socket, [ErlData, OpenSslData]) ->
ssl:send(Socket, ErlData),
erlang_ssl_receive(Socket, OpenSslData).
-close_port(Port) ->
- catch port_command(Port, ?OPENSSL_QUIT),
- close_loop(Port, 500, false).
-
-close_loop(Port, Time, SentClose) ->
- receive
- {Port, {data,Debug}} when is_port(Port) ->
- ct:log("openssl ~s~n",[Debug]),
- close_loop(Port, Time, SentClose);
- {ssl,_,Msg} ->
- ct:log("ssl Msg ~s~n",[Msg]),
- close_loop(Port, Time, SentClose);
- {Port, closed} ->
- ct:log("Port Closed~n",[]),
- ok;
- {'EXIT', Port, Reason} ->
- ct:log("Port Closed ~p~n",[Reason]),
- ok;
- Msg ->
- ct:log("Port Msg ~p~n",[Msg]),
- close_loop(Port, Time, SentClose)
- after Time ->
- case SentClose of
- false ->
- ct:log("Closing port ~n",[]),
- catch erlang:port_close(Port),
- close_loop(Port, Time, true);
- true ->
- ct:log("Timeout~n",[])
- end
- end.
-
-
server_sent_garbage(Socket) ->
receive
server_sent_garbage ->
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 004cacf7fc..404b71374f 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.5
+SSL_VSN = 5.3.6
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index b37f7fd7fd..64229fa8d3 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -319,6 +319,23 @@ false</code>
</func>
<func>
+ <name name="with" arity="2"/>
+ <fsummary></fsummary>
+ <desc>
+ <p>
+ Returns a new map <c><anno>Map2</anno></c> with the keys <c>K1</c> through <c>Kn</c> and their associated values from map <c><anno>Map1</anno></c>.
+ Any key in <c><anno>Ks</anno></c> that does not exist in <c><anno>Map1</anno></c> are ignored.
+ </p>
+ <p>Example:</p>
+ <code type="none">
+> Map = #{42 => value_three,1337 => "value two","a" => 1},
+ Ks = ["a",42,"other key"],
+ maps:without(Ks,Map).
+#{42 => value_three,"a" => 1}</code>
+ </desc>
+ </func>
+
+ <func>
<name name="without" arity="2"/>
<fsummary></fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 5e74616099..ebc750a399 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -30,6 +30,79 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The type spec of the FormFunc argument to
+ sys:handle_debug/4 was erroneously pointing to dbg_fun().
+ This is now corrected and the new type is format_fun().</p>
+ <p>
+ Own Id: OTP-11800</p>
+ </item>
+ <item>
+ <p>
+ Behaviors such as gen_fsm and gen_server should always
+ invoke format_status/2 before printing the state to the
+ logs.</p>
+ <p>
+ Own Id: OTP-11967</p>
+ </item>
+ <item>
+ <p> The documentation of <c>dets:insert_new/2</c> has
+ been corrected. (Thanks to Alexei Sholik for reporting
+ the bug.) </p>
+ <p>
+ Own Id: OTP-12024</p>
+ </item>
+ <item>
+ <p>
+ Printing a term with io_lib:format and control sequence
+ w, precision P and field width F, where F&lt; P would
+ fail in one of the two following ways:</p>
+ <p>
+ 1) If P &lt; printed length of the term, an infinite loop
+ would be entered, consuming all available memory.</p>
+ <p>
+ 2) If P &gt;= printed length of the term, an exception
+ would be raised.</p>
+ <p>
+ These two problems are now corrected.</p>
+ <p>
+ Own Id: OTP-12041</p>
+ </item>
+ <item>
+ <p>
+ The documentation of <c>maps:values/1</c> has been
+ corrected.</p>
+ <p>
+ Own Id: OTP-12055</p>
+ </item>
+ <item>
+ <p>
+ Expand shell functions in map expressions.</p>
+ <p>
+ Own Id: OTP-12063</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add maps:with/2</p>
+ <p>
+ Own Id: OTP-12137</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 2.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml
index c96cc95a44..b05d5cbc08 100644
--- a/lib/stdlib/doc/src/string.xml
+++ b/lib/stdlib/doc/src/string.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -124,6 +124,10 @@
<code type="none">
> tokens("abc defxxghix jkl", "x ").
["abc", "def", "ghi", "jkl"] </code>
+ <p>Note that, as shown in the example above, two or more
+ adjacent separator characters in <c><anno>String</anno></c>
+ will be treated as one. That is, there will not be any empty
+ strings in the resulting list of tokens.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index a46fa1289f..19605f325b 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -115,6 +115,9 @@
<datatype>
<name name="dbg_fun"/>
</datatype>
+ <datatype>
+ <name name="format_fun"/>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index c32da1624f..76e03bbfaa 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -440,9 +440,10 @@ insert(Tab, Objs) when is_list(Objs) ->
insert(Tab, Obj) ->
badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]).
--spec insert_new(Name, Objects) -> boolean() when
+-spec insert_new(Name, Objects) -> boolean() | {'error', Reason} when
Name :: tab_name(),
- Objects :: object() | [object()].
+ Objects :: object() | [object()],
+ Reason :: term().
insert_new(Tab, Objs) when is_list(Objs) ->
badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]);
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 3cfedfee97..639ddfc214 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -77,7 +77,7 @@
%% Only exprs/2 checks the command by calling erl_lint. The reason is
%% that if there is a function handler present, then it is possible
%% that there are valid constructs in Expression to be taken care of
-%% by a function handler but considerad errors by erl_lint.
+%% by a function handler but considered errors by erl_lint.
-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when
Expressions :: expressions(),
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index e1ae3b7aea..1d4a2a1fef 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -753,6 +753,9 @@ attribute_farity({cons,L,H,T}) ->
attribute_farity({tuple,L,Args0}) ->
Args = attribute_farity_list(Args0),
{tuple,L,Args};
+attribute_farity({map,L,Args0}) ->
+ Args = attribute_farity_map(Args0),
+ {map,L,Args};
attribute_farity({op,L,'/',{atom,_,_}=Name,{integer,_,_}=Arity}) ->
{tuple,L,[Name,Arity]};
attribute_farity(Other) -> Other.
@@ -760,6 +763,10 @@ attribute_farity(Other) -> Other.
attribute_farity_list(Args) ->
[attribute_farity(A) || A <- Args].
+%% It is not meaningful to have farity keys.
+attribute_farity_map(Args) ->
+ [{Op,L,K,attribute_farity(V)} || {Op,L,K,V} <- Args].
+
-spec error_bad_decl(integer(), attributes()) -> no_return().
error_bad_decl(L, S) ->
@@ -954,7 +961,9 @@ abstract([H|T], L, none=E) ->
abstract(List, L, E) when is_list(List) ->
abstract_list(List, [], L, E);
abstract(Tuple, L, E) when is_tuple(Tuple) ->
- {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)}.
+ {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)};
+abstract(Map, L, E) when is_map(Map) ->
+ {map,L,abstract_map_fields(maps:to_list(Map),L,E)}.
abstract_list([H|T], String, L, E) ->
case is_integer(H) andalso H >= 0 andalso E(H) of
@@ -979,6 +988,9 @@ abstract_tuple_list([H|T], L, E) ->
abstract_tuple_list([], _L, _E) ->
[].
+abstract_map_fields(Fs,L,E) ->
+ [{map_field_assoc,L,abstract(K,L,E),abstract(V,L,E)}||{K,V}<-Fs].
+
abstract_byte(Byte, L) when is_integer(Byte) ->
{bin_element, L, {integer, L, Byte}, default, default};
abstract_byte(Bits, L) ->
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 82bc2c1460..1fd6d2a8df 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -300,7 +300,15 @@ map_pair_types(Fs) ->
tuple_type(Fs, fun map_pair_type/1).
map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) ->
- {seq,[],[]," =>",[ltype(Ktype),ltype(Vtype)]}.
+ map_assoc_typed(ltype(Ktype), Vtype).
+
+map_assoc_typed(B, {type,_,union,Ts}) ->
+ {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts)}};
+map_assoc_typed(B, Type) ->
+ {list,[{cstep,[B," =>"],ltype(Type)}]}.
+
+map_assoc_union_type([T|Ts]) ->
+ [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/1)].
record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index ae59d5f44f..6fd6bb888b 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1075,7 +1075,7 @@ scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0) ->
Ncs = lists:reverse(Ncs0),
case catch list_to_integer(Ncs) of
B when B >= 2, B =< 1+$Z-$A+10 ->
- Bcs = ?STR(St, Ncs++[$#]),
+ Bcs = Ncs++[$#],
scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs});
B ->
Len = length(Ncs),
@@ -1108,7 +1108,7 @@ scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->
Ncs = lists:reverse(Ncs0),
case catch erlang:list_to_integer(Ncs, B) of
N when is_integer(N) ->
- tok3(Cs, St, Line, Col, Toks, integer, ?STR(St, Bcs++Ncs), N);
+ tok3(Cs, St, Line, Col, Toks, integer, Bcs++Ncs, N);
_ ->
Len = length(Bcs)+length(Ncs),
Ncol = incr_column(Col, Len),
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index 9efbe8da20..daae1fd2d2 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -371,7 +371,7 @@ compile_wildcard(Pattern, Cwd0) ->
[Root|Rest] = filename:split(Pattern),
case filename:pathtype(Root) of
relative ->
- Cwd = filename:join([Cwd0]),
+ Cwd = prepare_base(Cwd0),
compile_wildcard_2([Root|Rest], {cwd,Cwd});
_ ->
compile_wildcard_2(Rest, {root,0,Root})
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index d39dd89d3a..469acdc37c 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.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
@@ -49,8 +49,6 @@
-import(error_logger, [error_msg/2]).
--define(reply(X), From ! {element(2,Tag), X}).
-
-record(handler, {module :: atom(),
id = false,
state,
@@ -249,49 +247,49 @@ handle_msg(Msg, Parent, ServerName, MSL, Debug) ->
{notify, Event} ->
{Hib,MSL1} = server_notify(Event, handle_event, MSL, ServerName),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, {sync_notify, Event}} ->
+ {_From, Tag, {sync_notify, Event}} ->
{Hib, MSL1} = server_notify(Event, handle_event, MSL, ServerName),
- ?reply(ok),
+ reply(Tag, ok),
loop(Parent, ServerName, MSL1, Debug, Hib);
{'EXIT', From, Reason} ->
MSL1 = handle_exit(From, Reason, MSL, ServerName),
loop(Parent, ServerName, MSL1, Debug, false);
- {From, Tag, {call, Handler, Query}} ->
+ {_From, Tag, {call, Handler, Query}} ->
{Hib, Reply, MSL1} = server_call(Handler, Query, MSL, ServerName),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, {add_handler, Handler, Args}} ->
+ {_From, Tag, {add_handler, Handler, Args}} ->
{Hib, Reply, MSL1} = server_add_handler(Handler, Args, MSL),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, {add_sup_handler, Handler, Args, SupP}} ->
+ {_From, Tag, {add_sup_handler, Handler, Args, SupP}} ->
{Hib, Reply, MSL1} = server_add_sup_handler(Handler, Args, MSL, SupP),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, {delete_handler, Handler, Args}} ->
+ {_From, Tag, {delete_handler, Handler, Args}} ->
{Reply, MSL1} = server_delete_handler(Handler, Args, MSL,
ServerName),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, false);
- {From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} ->
+ {_From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} ->
{Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
Args2, MSL, ServerName),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2,
+ {_From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2,
Sup}} ->
{Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,
Args2, MSL, Sup, ServerName),
- ?reply(Reply),
+ reply(Tag, Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, stop} ->
+ {_From, Tag, stop} ->
catch terminate_server(normal, Parent, MSL, ServerName),
- ?reply(ok);
- {From, Tag, which_handlers} ->
- ?reply(the_handlers(MSL)),
+ reply(Tag, ok);
+ {_From, Tag, which_handlers} ->
+ reply(Tag, the_handlers(MSL)),
loop(Parent, ServerName, MSL, Debug, false);
- {From, Tag, get_modules} ->
- ?reply(get_modules(MSL)),
+ {_From, Tag, get_modules} ->
+ reply(Tag, get_modules(MSL)),
loop(Parent, ServerName, MSL, Debug, false);
Other ->
{Hib, MSL1} = server_notify(Other, handle_info, MSL, ServerName),
@@ -303,6 +301,10 @@ terminate_server(Reason, Parent, MSL, ServerName) ->
do_unlink(Parent, MSL),
exit(Reason).
+reply({From, Ref}, Msg) ->
+ From ! {Ref, Msg},
+ ok.
+
%% unlink the supervisor process of all supervised handlers.
%% We do not want a handler supervisor to EXIT due to the
%% termination of the event manager (server).
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index e914f7d0b2..5afe3e8b09 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.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
@@ -594,7 +594,8 @@ reply(Name, {To, Tag}, Reply, Debug, StateName) ->
terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
case catch Mod:terminate(Reason, StateName, StateData) of
{'EXIT', R} ->
- error_info(R, Name, Msg, StateName, StateData, Debug),
+ FmtStateData = format_status(terminate, Mod, get(), StateData),
+ error_info(R, Name, Msg, StateName, FmtStateData, Debug),
exit(R);
_ ->
case Reason of
@@ -605,17 +606,7 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- FmtStateData =
- case erlang:function_exported(Mod, format_status, 2) of
- true ->
- Args = [get(), StateData],
- case catch Mod:format_status(terminate, Args) of
- {'EXIT', _} -> StateData;
- Else -> Else
- end;
- _ ->
- StateData
- end,
+ FmtStateData = format_status(terminate, Mod, get(), StateData),
error_info(Reason,Name,Msg,StateName,FmtStateData,Debug),
exit(Reason)
end
@@ -680,21 +671,29 @@ format_status(Opt, StatusData) ->
Header = gen:format_status_header("Status for state machine",
Name),
Log = sys:get_debug(log, Debug, []),
- DefaultStatus = [{data, [{"StateData", StateData}]}],
- Specfic =
- case erlang:function_exported(Mod, format_status, 2) of
- true ->
- case catch Mod:format_status(Opt,[PDict,StateData]) of
- {'EXIT', _} -> DefaultStatus;
- StatusList when is_list(StatusList) -> StatusList;
- Else -> [Else]
- end;
- _ ->
- DefaultStatus
- end,
+ Specfic = format_status(Opt, Mod, PDict, StateData),
+ Specfic = case format_status(Opt, Mod, PDict, StateData) of
+ S when is_list(S) -> S;
+ S -> [S]
+ end,
[{header, Header},
{data, [{"Status", SysState},
{"Parent", Parent},
{"Logged events", Log},
{"StateName", StateName}]} |
Specfic].
+
+format_status(Opt, Mod, PDict, State) ->
+ DefStatus = case Opt of
+ terminate -> State;
+ _ -> [{data, [{"StateData", State}]}]
+ end,
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ case catch Mod:format_status(Opt, [PDict, State]) of
+ {'EXIT', _} -> DefStatus;
+ Else -> Else
+ end;
+ _ ->
+ DefStatus
+ end.
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 202a931fae..dadfe56b3d 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.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
@@ -720,7 +720,8 @@ print_event(Dev, Event, Name) ->
terminate(Reason, Name, Msg, Mod, State, Debug) ->
case catch Mod:terminate(Reason, State) of
{'EXIT', R} ->
- error_info(R, Name, Msg, State, Debug),
+ FmtState = format_status(terminate, Mod, get(), State),
+ error_info(R, Name, Msg, FmtState, Debug),
exit(R);
_ ->
case Reason of
@@ -731,17 +732,7 @@ terminate(Reason, Name, Msg, Mod, State, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- FmtState =
- case erlang:function_exported(Mod, format_status, 2) of
- true ->
- Args = [get(), State],
- case catch Mod:format_status(terminate, Args) of
- {'EXIT', _} -> State;
- Else -> Else
- end;
- _ ->
- State
- end,
+ FmtState = format_status(terminate, Mod, get(), State),
error_info(Reason, Name, Msg, FmtState, Debug),
exit(Reason)
end
@@ -875,23 +866,29 @@ name_to_pid(Name) ->
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
- Header = gen:format_status_header("Status for generic server",
- Name),
+ Header = gen:format_status_header("Status for generic server", Name),
Log = sys:get_debug(log, Debug, []),
- DefaultStatus = [{data, [{"State", State}]}],
- Specfic =
- case erlang:function_exported(Mod, format_status, 2) of
- true ->
- case catch Mod:format_status(Opt, [PDict, State]) of
- {'EXIT', _} -> DefaultStatus;
- StatusList when is_list(StatusList) -> StatusList;
- Else -> [Else]
- end;
- _ ->
- DefaultStatus
- end,
+ Specfic = case format_status(Opt, Mod, PDict, State) of
+ S when is_list(S) -> S;
+ S -> [S]
+ end,
[{header, Header},
{data, [{"Status", SysState},
{"Parent", Parent},
{"Logged events", Log}]} |
Specfic].
+
+format_status(Opt, Mod, PDict, State) ->
+ DefStatus = case Opt of
+ terminate -> State;
+ _ -> [{data, [{"State", State}]}]
+ end,
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ case catch Mod:format_status(Opt, [PDict, State]) of
+ {'EXIT', _} -> DefStatus;
+ Else -> Else
+ end;
+ _ ->
+ DefStatus
+ end.
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 56e15a17ec..89ae6fb187 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -255,7 +255,7 @@ term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
L = lists:flatlength(T),
- P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
+ P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),
if
L > P ->
adjust(chars($*, P), chars(Pad, F-P), Adj);
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 4ef1638e6d..ba4d6a5c87 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -24,6 +24,7 @@
map/2,
size/1,
without/2,
+ with/2,
get/3
]).
@@ -133,10 +134,10 @@ to_list(_) -> erlang:nif_error(undef).
update(_,_,_) -> erlang:nif_error(undef).
--spec values(Map) -> Keys when
+-spec values(Map) -> Values when
Map :: map(),
- Keys :: [Key],
- Key :: term().
+ Values :: [Value],
+ Value :: term().
values(_) -> erlang:nif_error(undef).
@@ -201,3 +202,13 @@ size(Map) when is_map(Map) ->
without(Ks, M) when is_list(Ks), is_map(M) ->
maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]).
+
+
+-spec with(Ks, Map1) -> Map2 when
+ Ks :: [K],
+ Map1 :: map(),
+ Map2 :: map(),
+ K :: term().
+
+with(Ks, M) when is_list(Ks), is_map(M) ->
+ maps:from_list([{K,V}||{K,V} <- maps:to_list(M), lists:member(K, Ks)]).
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index c0ee8799c8..6c25beabe9 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -421,13 +421,13 @@ obsolete_1(ssh_cm, stop_listener, 1) ->
obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 ->
{removed,{ssh_connection,session_channel,A},"R14B"};
obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 ->
- {removed,{ssh_connection,direct_tcpip,A}};
+ {removed,{ssh_connection,direct_tcpip,A},"R14B"};
obsolete_1(ssh_cm, tcpip_forward, 3) ->
{removed,{ssh_connection,tcpip_forward,3},"R14B"};
obsolete_1(ssh_cm, cancel_tcpip_forward, 3) ->
{removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"};
obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 ->
- {removed,{ssh_connection,open_pty,A},"R14"};
+ {removed,{ssh_connection,open_pty,A},"R14B"};
obsolete_1(ssh_cm, setenv, 5) ->
{removed,{ssh_connection,setenv,5},"R14B"};
obsolete_1(ssh_cm, shell, 2) ->
@@ -441,11 +441,11 @@ obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 ->
obsolete_1(ssh_cm, signal, 3) ->
{removed,{ssh_connection,signal,3},"R14B"};
obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 ->
- {removed,{ssh,attach,A}};
+ {removed,"no longer useful; removed in R14B"};
obsolete_1(ssh_cm, detach, 2) ->
- {removed,"no longer useful; will be removed in R14B"};
+ {removed,"no longer useful; removed in R14B"};
obsolete_1(ssh_cm, set_user_ack, 4) ->
- {removed,"no longer useful; will be removed in R14B"};
+ {removed,"no longer useful; removed in R14B"};
obsolete_1(ssh_cm, adjust_window, 3) ->
{removed,{ssh_connection,adjust_window,3},"R14B"};
obsolete_1(ssh_cm, close, 2) ->
@@ -461,9 +461,9 @@ obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 ->
obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 ->
{removed,{ssh,shell,A},"R14B"};
obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 ->
- {removed,{ssh,daemon,[1,2,3]},"R14"};
+ {removed,{ssh,daemon,[1,2,3]},"R14B"};
obsolete_1(ssh_sshd, stop, 1) ->
- {removed,{ssh,stop_listener,1}};
+ {removed,{ssh,stop_listener,1},"R14B"};
%% Added in R13A.
obsolete_1(regexp, _, _) ->
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 1eb6fc2e86..bf2a4e7ac5 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -216,10 +216,8 @@ ensure_link(SpawnOpts) ->
init_p(Parent, Ancestors, Fun) when is_function(Fun) ->
put('$ancestors', [Parent|Ancestors]),
- {module,Mod} = erlang:fun_info(Fun, module),
- {name,Name} = erlang:fun_info(Fun, name),
- {arity,Arity} = erlang:fun_info(Fun, arity),
- put('$initial_call', {Mod,Name,Arity}),
+ Mfa = erlang:fun_info_mfa(Fun),
+ put('$initial_call', Mfa),
try
Fun()
catch
diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl
index 3b90542452..679c13f0cf 100644
--- a/lib/stdlib/src/shell.erl
+++ b/lib/stdlib/src/shell.erl
@@ -371,6 +371,14 @@ expand_expr({bc,L,E,Qs}, C) ->
{bc,L,expand_expr(E, C),expand_quals(Qs, C)};
expand_expr({tuple,L,Elts}, C) ->
{tuple,L,expand_exprs(Elts, C)};
+expand_expr({map,L,Es}, C) ->
+ {map,L,expand_exprs(Es, C)};
+expand_expr({map,L,Arg,Es}, C) ->
+ {map,L,expand_expr(Arg, C),expand_exprs(Es, C)};
+expand_expr({map_field_assoc,L,K,V}, C) ->
+ {map_field_assoc,L,expand_expr(K, C),expand_expr(V, C)};
+expand_expr({map_field_exact,L,K,V}, C) ->
+ {map_field_exact,L,expand_expr(K, C),expand_expr(V, C)};
expand_expr({record_index,L,Name,F}, C) ->
{record_index,L,Name,expand_expr(F, C)};
expand_expr({record,L,Name,Is}, C) ->
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 3585eec342..aa9899da3b 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -103,7 +103,7 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-6.1.2","crypto-3.3",
+ {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-6.2","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 99d9b8b431..7802ea884f 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -17,11 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1
+ [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16
%% Down to - max one major revision back
- [{<<"2\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1
+ [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3
{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index e25cc25f57..d3ba09ce82 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.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
@@ -46,7 +46,7 @@
{N :: non_neg_integer(),
[{Event :: system_event(),
FuncState :: _,
- FormFunc :: dbg_fun()}]}}
+ FormFunc :: format_fun()}]}}
| {'statistics', {file:date_time(),
{'reductions', non_neg_integer()},
MessagesIn :: non_neg_integer(),
@@ -57,6 +57,10 @@
Event :: system_event(),
ProcState :: _) -> 'done' | (NewFuncState :: _)).
+-type format_fun() :: fun((Device :: io:device() | file:io_device(),
+ Event :: system_event(),
+ Extra :: term()) -> any()).
+
%%-----------------------------------------------------------------
%% System messages
%%-----------------------------------------------------------------
@@ -346,7 +350,7 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
%%-----------------------------------------------------------------
-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when
Debug :: [dbg_opt()],
- FormFunc :: dbg_fun(),
+ FormFunc :: format_fun(),
Extra :: term(),
Event :: system_event().
handle_debug([{trace, true} | T], FormFunc, State, Event) ->
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 6be37cbecf..119b4dc7cb 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -2032,6 +2032,12 @@ match(Config, Version) ->
CrashPos = if Version =:= 8 -> 5; Version =:= 9 -> 1 end,
crash(Fname, ObjPos2+CrashPos),
{ok, _} = dets:open_file(T, Args),
+ case dets:insert_new(T, Obj) of % OTP-12024
+ ok ->
+ bad_object(dets:sync(T), Fname);
+ Else3 ->
+ bad_object(Else3, Fname)
+ end,
io:format("Expect corrupt table:~n"),
case ins(T, N) of
ok ->
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index babf3a49eb..927fe0b595 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -604,20 +604,20 @@ import_export(Config) when is_list(Config) ->
misc_attrs(suite) ->
[];
misc_attrs(Config) when is_list(Config) ->
- ?line ok = pp_forms(<<"-module(m). ">>),
- ?line ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk,"
- "Blsjfdlslfjsdf]). ">>),
- ?line ok = pp_forms(<<"-export([]). ">>),
- ?line ok = pp_forms(<<"-export([foo/2, bar/0]). ">>),
- ?line ok = pp_forms(<<"-export([bar/0]). ">>),
- ?line ok = pp_forms(<<"-import(lists, []). ">>),
- ?line ok = pp_forms(<<"-import(lists, [map/2]). ">>),
- ?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>),
- ?line ok = pp_forms(<<"-'wild '({attr2,3}). ">>),
- ?line ok = pp_forms(<<"-record(a, {b,c}). ">>),
- ?line ok = pp_forms(<<"-record(' a ', {}). ">>),
- ?line ok = pp_forms(<<"-record(' a ', {foo = foo:bar()}). ">>),
-
+ ok = pp_forms(<<"-module(m). ">>),
+ ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk,"
+ "Blsjfdlslfjsdf]). ">>),
+ ok = pp_forms(<<"-export([]). ">>),
+ ok = pp_forms(<<"-export([foo/2, bar/0]). ">>),
+ ok = pp_forms(<<"-export([bar/0]). ">>),
+ ok = pp_forms(<<"-import(lists, []). ">>),
+ ok = pp_forms(<<"-import(lists, [map/2]). ">>),
+ ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>),
+ ok = pp_forms(<<"-'wild '({attr2,3}). ">>),
+ ok = pp_forms(<<"-record(a, {b,c}). ">>),
+ ok = pp_forms(<<"-record(' a ', {}). ">>),
+ ok = pp_forms(<<"-record(' a ', {foo = foo:bar()}). ">>),
+ ok = pp_forms(<<"-custom1(#{test1 => init/2, test2 => [val/1, val/2]}). ">>),
ok.
dialyzer_attrs(suite) ->
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 35067e8116..9be9f641c8 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -204,20 +204,20 @@ reserved_words() ->
[begin
?line {RW, true} = {RW, erl_scan:reserved_word(RW)},
S = atom_to_list(RW),
- Ts = [{RW,1}],
+ Ts = [{RW,{1,1}}],
?line test_string(S, Ts)
end || RW <- L],
ok.
atoms() ->
- ?line test_string("a
- b", [{atom,1,a},{atom,2,b}]),
- ?line test_string("'a b'", [{atom,1,'a b'}]),
- ?line test_string("a", [{atom,1,a}]),
- ?line test_string("a@2", [{atom,1,a@2}]),
- ?line test_string([39,65,200,39], [{atom,1,'AÈ'}]),
- ?line test_string("ärlig östen", [{atom,1,ärlig},{atom,1,östen}]),
+ test_string("a
+ b", [{atom,{1,1},a},{atom,{2,18},b}]),
+ test_string("'a b'", [{atom,{1,1},'a b'}]),
+ test_string("a", [{atom,{1,1},a}]),
+ test_string("a@2", [{atom,{1,1},a@2}]),
+ test_string([39,65,200,39], [{atom,{1,1},'AÈ'}]),
+ test_string("ärlig östen", [{atom,{1,1},ärlig},{atom,{1,7},östen}]),
?line {ok,[{atom,_,'$a'}],{1,6}} =
erl_scan:string("'$\\a'", {1,1}),
?line test("'$\\a'"),
@@ -230,7 +230,7 @@ punctuations() ->
%% One token at a time:
[begin
W = list_to_atom(S),
- Ts = [{W,1}],
+ Ts = [{W,{1,1}}],
?line test_string(S, Ts)
end || S <- L],
Three = ["/=:=", "<=:=", "==:=", ">=:="], % three tokens...
@@ -246,53 +246,60 @@ punctuations() ->
[begin
W1 = list_to_atom(S1),
W2 = list_to_atom(S2),
- Ts = [{W1,1},{W2,1}],
+ Ts = [{W1,{1,1}},{W2,{1,-L2+1}}],
?line test_string(S, Ts)
- end || {S,[{_,S1,S2}|_]} <- SL],
+ end || {S,[{L2,S1,S2}|_]} <- SL],
- PTs1 = [{'!',1},{'(',1},{')',1},{',',1},{';',1},{'=',1},{'[',1},
- {']',1},{'{',1},{'|',1},{'}',1}],
+ PTs1 = [{'!',{1,1}},{'(',{1,2}},{')',{1,3}},{',',{1,4}},{';',{1,5}},
+ {'=',{1,6}},{'[',{1,7}},{']',{1,8}},{'{',{1,9}},{'|',{1,10}},
+ {'}',{1,11}}],
?line test_string("!(),;=[]{|}", PTs1),
- PTs2 = [{'#',1},{'&',1},{'*',1},{'+',1},{'/',1},
- {':',1},{'<',1},{'>',1},{'?',1},{'@',1},
- {'\\',1},{'^',1},{'`',1},{'~',1}],
+ PTs2 = [{'#',{1,1}},{'&',{1,2}},{'*',{1,3}},{'+',{1,4}},{'/',{1,5}},
+ {':',{1,6}},{'<',{1,7}},{'>',{1,8}},{'?',{1,9}},{'@',{1,10}},
+ {'\\',{1,11}},{'^',{1,12}},{'`',{1,13}},{'~',{1,14}}],
?line test_string("#&*+/:<>?@\\^`~", PTs2),
- ?line test_string(".. ", [{'..',1}]),
- ?line test("1 .. 2"),
- ?line test_string("...", [{'...',1}]),
+ test_string(".. ", [{'..',{1,1}}]),
+ test_string("1 .. 2",
+ [{integer,{1,1},1},{'..',{1,3}},{integer,{1,6},2}]),
+ test_string("...", [{'...',{1,1}}]),
ok.
comments() ->
?line test("a %%\n b"),
?line {ok,[],1} = erl_scan:string("%"),
?line test("a %%\n b"),
- ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} =
+ {ok,[{atom,{1,1},a},{atom,{2,2},b}],{2,3}} =
erl_scan:string("a %%\n b",{1,1}),
- ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} =
+ {ok,[{atom,{1,1},a},{comment,{1,3},"%%"},{atom,{2,2},b}],{2,3}} =
erl_scan:string("a %%\n b",{1,1}, [return_comments]),
- ?line {ok,[{atom,_,a},
- {white_space,_," "},
- {white_space,_,"\n "},
- {atom,_,b}],
- {2,3}} =
+ {ok,[{atom,{1,1},a},
+ {white_space,{1,2}," "},
+ {white_space,{1,5},"\n "},
+ {atom,{2,2},b}],
+ {2,3}} =
erl_scan:string("a %%\n b",{1,1},[return_white_spaces]),
- ?line {ok,[{atom,_,a},
- {white_space,_," "},
- {comment,_,"%%"},
- {white_space,_,"\n "},
- {atom,_,b}],
- {2,3}} = erl_scan:string("a %%\n b",{1,1},[return]),
+ {ok,[{atom,{1,1},a},
+ {white_space,{1,2}," "},
+ {comment,{1,3},"%%"},
+ {white_space,{1,5},"\n "},
+ {atom,{2,2},b}],
+ {2,3}} = erl_scan:string("a %%\n b",{1,1},[return]),
ok.
errors() ->
?line {error,{1,erl_scan,{string,$',"qa"}},1} = erl_scan:string("'qa"), %'
+ {error,{{1,1},erl_scan,{string,$',"qa"}},{1,4}} = %'
+ erl_scan:string("'qa", {1,1}, []), %'
?line {error,{1,erl_scan,{string,$","str"}},1} = %"
erl_scan:string("\"str"), %"
+ {error,{{1,1},erl_scan,{string,$","str"}},{1,5}} = %"
+ erl_scan:string("\"str", {1,1}, []), %"
?line {error,{1,erl_scan,char},1} = erl_scan:string("$"),
- ?line test_string([34,65,200,34], [{string,1,"AÈ"}]),
- ?line test_string("\\", [{'\\',1}]),
+ {error,{{1,1},erl_scan,char},{1,2}} = erl_scan:string("$", {1,1}, []),
+ test_string([34,65,200,34], [{string,{1,1},"AÈ"}]),
+ test_string("\\", [{'\\',{1,1}}]),
?line {'EXIT',_} =
(catch {foo, erl_scan:string('$\\a', {1,1})}), % type error
?line {'EXIT',_} =
@@ -304,7 +311,7 @@ errors() ->
integers() ->
[begin
I = list_to_integer(S),
- Ts = [{integer,1,I}],
+ Ts = [{integer,{1,1},I}],
?line test_string(S, Ts)
end || S <- [[N] || N <- lists:seq($0, $9)] ++ ["2323","000"] ],
ok.
@@ -313,14 +320,16 @@ base_integers() ->
[begin
B = list_to_integer(BS),
I = erlang:list_to_integer(S, B),
- Ts = [{integer,1,I}],
+ Ts = [{integer,{1,1},I}],
?line test_string(BS++"#"++S, Ts)
end || {BS,S} <- [{"2","11"}, {"5","23234"}, {"12","05a"},
{"16","abcdef"}, {"16","ABCDEF"}] ],
?line {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"),
+ {error,{{1,1},erl_scan,{base,1}},{1,2}} =
+ erl_scan:string("1#000", {1,1}, []),
- ?line test_string("12#bc", [{integer,1,11},{atom,1,c}]),
+ test_string("12#bc", [{integer,{1,1},11},{atom,{1,5},c}]),
[begin
Str = BS ++ "#" ++ S,
@@ -329,40 +338,53 @@ base_integers() ->
end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ],
?line {ok,[{integer,1,239},{'@',1}],1} = erl_scan:string("16#ef@"),
- ?line {ok,[{integer,1,14},{atom,1,g@}],1} = erl_scan:string("16#eg@"),
+ {ok,[{integer,{1,1},239},{'@',{1,6}}],{1,7}} =
+ erl_scan:string("16#ef@", {1,1}, []),
+ {ok,[{integer,{1,1},14},{atom,{1,5},g@}],{1,7}} =
+ erl_scan:string("16#eg@", {1,1}, []),
ok.
floats() ->
[begin
F = list_to_float(FS),
- Ts = [{float,1,F}],
+ Ts = [{float,{1,1},F}],
?line test_string(FS, Ts)
end || FS <- ["1.0","001.17","3.31200","1.0e0","1.0E17",
"34.21E-18", "17.0E+14"]],
- ?line test_string("1.e2", [{integer,1,1},{'.',1},{atom,1,e2}]),
+ test_string("1.e2", [{integer,{1,1},1},{'.',{1,2}},{atom,{1,3},e2}]),
?line {error,{1,erl_scan,{illegal,float}},1} =
erl_scan:string("1.0e400"),
+ {error,{{1,1},erl_scan,{illegal,float}},{1,8}} =
+ erl_scan:string("1.0e400", {1,1}, []),
[begin
- ?line {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S)
+ {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S),
+ {error,{{1,1},erl_scan,{illegal,float}},{1,_}} =
+ erl_scan:string(S, {1,1}, [])
end || S <- ["1.14Ea"]],
ok.
dots() ->
- Dot = [{".", {ok,[{dot,1}],1}},
- {". ", {ok,[{dot,1}],1}},
- {".\n", {ok,[{dot,1}],2}},
- {".%", {ok,[{dot,1}],1}},
- {".\210",{ok,[{dot,1}],1}},
- {".% öh",{ok,[{dot,1}],1}},
- {".%\n", {ok,[{dot,1}],2}},
- {".$", {error,{1,erl_scan,char},1}},
- {".$\\", {error,{1,erl_scan,char},1}},
- {".a", {ok,[{'.',1},{atom,1,a}],1}}
+ Dot = [{".", {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,2}}},
+ {". ", {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}},
+ {".\n", {ok,[{dot,1}],2}, {ok,[{dot,{1,1}}],{2,1}}},
+ {".%", {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}},
+ {".\210",{ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}},
+ {".% öh",{ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,6}}},
+ {".%\n", {ok,[{dot,1}],2}, {ok,[{dot,{1,1}}],{2,1}}},
+ {".$", {error,{1,erl_scan,char},1},
+ {error,{{1,2},erl_scan,char},{1,3}}},
+ {".$\\", {error,{1,erl_scan,char},1},
+ {error,{{1,2},erl_scan,char},{1,4}}},
+ {".a", {ok,[{'.',1},{atom,1,a}],1},
+ {ok,[{'.',{1,1}},{atom,{1,2},a}],{1,3}}}
],
- ?line [R = erl_scan:string(S) || {S, R} <- Dot],
+ [begin
+ R = erl_scan:string(S),
+ R2 = erl_scan:string(S, {1,1}, [])
+ end || {S, R, R2} <- Dot],
?line {ok,[{dot,_}=T1],{1,2}} = erl_scan:string(".", {1,1}, text),
?line [{column,1},{length,1},{line,1},{text,"."}] =
@@ -379,55 +401,55 @@ dots() ->
?line {error,{{1,2},erl_scan,char},{1,4}} =
erl_scan:string(".$\\", {1,1}),
- ?line test(". "),
- ?line test(". "),
- ?line test(".\n"),
- ?line test(".\n\n"),
- ?line test(".\n\r"),
- ?line test(".\n\n\n"),
- ?line test(".\210"),
- ?line test(".%\n"),
- ?line test(".a"),
-
- ?line test("%. \n. "),
+ test_string(". ", [{dot,{1,1}}]),
+ test_string(". ", [{dot,{1,1}}]),
+ test_string(".\n", [{dot,{1,1}}]),
+ test_string(".\n\n", [{dot,{1,1}}]),
+ test_string(".\n\r", [{dot,{1,1}}]),
+ test_string(".\n\n\n", [{dot,{1,1}}]),
+ test_string(".\210", [{dot,{1,1}}]),
+ test_string(".%\n", [{dot,{1,1}}]),
+ test_string(".a", [{'.',{1,1}},{atom,{1,2},a}]),
+
+ test_string("%. \n. ", [{dot,{2,1}}]),
?line {more,C} = erl_scan:tokens([], "%. ",{1,1}, return),
- ?line {done,{ok,[{comment,_,"%. "},
- {white_space,_,"\n"},
- {dot,_}],
- {2,3}}, ""} =
+ {done,{ok,[{comment,{1,1},"%. "},
+ {white_space,{1,4},"\n"},
+ {dot,{2,1}}],
+ {2,3}}, ""} =
erl_scan:tokens(C, "\n. ", {1,1}, return), % any loc, any options
?line [test_string(S, R) ||
- {S, R} <- [{".$\n", [{'.',1},{char,1,$\n}]},
- {"$\\\n", [{char,1,$\n}]},
- {"'\\\n'", [{atom,1,'\n'}]},
- {"$\n", [{char,1,$\n}]}] ],
+ {S, R} <- [{".$\n", [{'.',{1,1}},{char,{1,2},$\n}]},
+ {"$\\\n", [{char,{1,1},$\n}]},
+ {"'\\\n'", [{atom,{1,1},'\n'}]},
+ {"$\n", [{char,{1,1},$\n}]}] ],
ok.
chars() ->
[begin
L = lists:flatten(io_lib:format("$\\~.8b", [C])),
- Ts = [{char,1,C}],
+ Ts = [{char,{1,1},C}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255)],
%% Leading zeroes...
[begin
L = lists:flatten(io_lib:format("$\\~3.8.0b", [C])),
- Ts = [{char,1,C}],
+ Ts = [{char,{1,1},C}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255)],
%% $\^\n now increments the line...
[begin
L = "$\\^" ++ [C],
- Ts = [{char,1,C band 2#11111}],
+ Ts = [{char,{1,1},C band 2#11111}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255)],
[begin
L = "$\\" ++ [C],
- Ts = [{char,1,V}],
+ Ts = [{char,{1,1},V}],
?line test_string(L, Ts)
end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v},
{$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s},
@@ -440,45 +462,45 @@ chars() ->
No = EC ++ Ds ++ X ++ New,
[begin
L = "$\\" ++ [C],
- Ts = [{char,1,C}],
+ Ts = [{char,{1,1},C}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255) -- No],
[begin
L = "'$\\" ++ [C] ++ "'",
- Ts = [{atom,1,list_to_atom("$"++[C])}],
+ Ts = [{atom,{1,1},list_to_atom("$"++[C])}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255) -- No],
- ?line test_string("\"\\013a\\\n\"", [{string,1,"\va\n"}]),
+ test_string("\"\\013a\\\n\"", [{string,{1,1},"\va\n"}]),
- ?line test_string("'\n'", [{atom,1,'\n'}]),
- ?line test_string("\"\n\a\"", [{string,1,"\na"}]),
+ test_string("'\n'", [{atom,{1,1},'\n'}]),
+ test_string("\"\n\a\"", [{string,{1,1},"\na"}]),
%% No escape
[begin
L = "$" ++ [C],
- Ts = [{char,1,C}],
+ Ts = [{char,{1,1},C}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255) -- (No ++ [$\\])],
- ?line test_string("$\n", [{char,1,$\n}]),
+ test_string("$\n", [{char,{1,1},$\n}]),
?line {error,{{1,1},erl_scan,char},{1,4}} =
erl_scan:string("$\\^",{1,1}),
- ?line test_string("$\\\n", [{char,1,$\n}]),
+ test_string("$\\\n", [{char,{1,1},$\n}]),
%% Robert's scanner returns line 1:
- ?line test_string("$\\\n", [{char,1,$\n}]),
- ?line test_string("$\n\n", [{char,1,$\n}]),
+ test_string("$\\\n", [{char,{1,1},$\n}]),
+ test_string("$\n\n", [{char,{1,1},$\n}]),
?line test("$\n\n"),
ok.
variables() ->
- ?line test_string(" \237_Aouåeiyäö", [{var,1,'_Aouåeiyäö'}]),
- ?line test_string("A_b_c@", [{var,1,'A_b_c@'}]),
- ?line test_string("V@2", [{var,1,'V@2'}]),
- ?line test_string("ABDÀ", [{var,1,'ABDÀ'}]),
- ?line test_string("Ärlig Östen", [{var,1,'Ärlig'},{var,1,'Östen'}]),
+ test_string(" \237_Aouåeiyäö", [{var,{1,7},'_Aouåeiyäö'}]),
+ test_string("A_b_c@", [{var,{1,1},'A_b_c@'}]),
+ test_string("V@2", [{var,{1,1},'V@2'}]),
+ test_string("ABDÀ", [{var,{1,1},'ABDÀ'}]),
+ test_string("Ärlig Östen", [{var,{1,1},'Ärlig'},{var,{1,7},'Östen'}]),
ok.
eof() ->
@@ -508,11 +530,25 @@ eof() ->
?line {done,{ok,[{atom,1,a}],1},eof} =
erl_scan:tokens(C5,eof,1),
+ %% With column.
+ {more, C6} = erl_scan:tokens([], "a", {1,1}),
+ %% An error before R13A.
+ %% {done,{error,{1,erl_scan,scan},1},eof} =
+ {done,{ok,[{atom,{1,1},a}],{1,2}},eof} =
+ erl_scan:tokens(C6,eof,1),
+
%% A dot followed by eof is special:
?line {more, C} = erl_scan:tokens([], "a.", 1),
?line {done,{ok,[{atom,1,a},{dot,1}],1},eof} = erl_scan:tokens(C,eof,1),
?line {ok,[{atom,1,foo},{dot,1}],1} = erl_scan:string("foo."),
+ %% With column.
+ {more, CCol} = erl_scan:tokens([], "a.", {1,1}),
+ {done,{ok,[{atom,{1,1},a},{dot,{1,2}}],{1,3}},eof} =
+ erl_scan:tokens(CCol,eof,1),
+ {ok,[{atom,{1,1},foo},{dot,{1,4}}],{1,5}} =
+ erl_scan:string("foo.", {1,1}, []),
+
ok.
illegal() ->
@@ -816,34 +852,34 @@ unicode() ->
erl_scan:string([1089]),
?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
erl_scan:string([1089], {1,1}),
- ?line {error,{1,erl_scan,{illegal,atom}},1} =
+ {error,{1,erl_scan,{illegal,atom}},1} =
erl_scan:string("'a"++[1089]++"b'", 1),
- ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,6}} =
+ {error,{{1,1},erl_scan,{illegal,atom}},{1,6}} =
erl_scan:string("'a"++[1089]++"b'", {1,1}),
?line test("\"a"++[1089]++"b\""),
- ?line {ok,[{char,1,1}],1} =
+ {ok,[{char,1,1}],1} =
erl_scan:string([$$,$\\,$^,1089], 1),
- ?line {error,{1,erl_scan,Error},1} =
+ {error,{1,erl_scan,Error},1} =
erl_scan:string("\"qa\x{aaa}", 1),
- ?line "unterminated string starting with \"qa"++[2730]++"\"" =
+ "unterminated string starting with \"qa"++[2730]++"\"" =
erl_scan:format_error(Error),
?line {error,{{1,1},erl_scan,_},{1,11}} =
erl_scan:string("\"qa\\x{aaa}",{1,1}),
- ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =
+ {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =
erl_scan:string("'qa\\x{aaa}'",{1,1}),
- ?line {ok,[{char,1,1089}],1} =
+ {ok,[{char,1,1089}],1} =
erl_scan:string([$$,1089], 1),
- ?line {ok,[{char,1,1089}],1} =
+ {ok,[{char,1,1089}],1} =
erl_scan:string([$$,$\\,1089], 1),
Qs = "$\\x{aaa}",
- ?line {ok,[{char,1,$\x{aaa}}],1} =
+ {ok,[{char,1,$\x{aaa}}],1} =
erl_scan:string(Qs, 1),
- ?line {ok,[Q2],{1,9}} =
+ {ok,[Q2],{1,9}} =
erl_scan:string("$\\x{aaa}", {1,1}, [text]),
- ?line [{category,char},{column,1},{length,8},
+ [{category,char},{column,1},{length,8},
{line,1},{symbol,16#aaa},{text,Qs}] =
erl_scan:token_info(Q2),
@@ -1164,7 +1200,13 @@ otp_11807(Config) when is_list(Config) ->
(catch erl_parse:abstract("string", [{encoding,bad}])),
ok.
-test_string(String, Expected) ->
+test_string(String, ExpectedWithCol) ->
+ {ok, ExpectedWithCol, _EndWithCol} = erl_scan:string(String, {1, 1}, []),
+ Expected = [ begin
+ {L,_C} = element(2, T),
+ setelement(2, T, L)
+ end
+ || T <- ExpectedWithCol ],
{ok, Expected, _End} = erl_scan:string(String),
test(String).
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 040ae1effc..bd313390b3 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -88,6 +88,7 @@ wildcard_two(Config) when is_list(Config) ->
?line ok = file:make_dir(Dir),
?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X end),
?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end),
+ ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/.") end),
case os:type() of
{win32,_} ->
ok;
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 8aeec07ae8..336065b258 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.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
@@ -31,7 +31,9 @@
-export([shutdown/1]).
--export([ sys1/1, call_format_status/1, error_format_status/1, get_state/1, replace_state/1]).
+-export([ sys1/1,
+ call_format_status/1, error_format_status/1, terminate_crash_format/1,
+ get_state/1, replace_state/1]).
-export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
@@ -66,7 +68,8 @@ groups() ->
start8, start9, start10, start11, start12]},
{abnormal, [], [abnormal1, abnormal2]},
{sys, [],
- [sys1, call_format_status, error_format_status, get_state, replace_state]}].
+ [sys1, call_format_status, error_format_status, terminate_crash_format,
+ get_state, replace_state]}].
init_per_suite(Config) ->
Config.
@@ -403,7 +406,7 @@ error_format_status(Config) when is_list(Config) ->
receive
{error,_GroupLeader,{Pid,
"** State machine"++_,
- [Pid,{_,_,badreturn},idle,StateData,_]}} ->
+ [Pid,{_,_,badreturn},idle,{formatted,StateData},_]}} ->
ok;
Other ->
?line io:format("Unexpected: ~p", [Other]),
@@ -413,6 +416,29 @@ error_format_status(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+terminate_crash_format(Config) when is_list(Config) ->
+ error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ StateData = crash_terminate,
+ {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, {state_data, StateData}, []),
+ stop_it(Pid),
+ receive
+ {error,_GroupLeader,{Pid,
+ "** State machine"++_,
+ [Pid,{_,_,_},idle,{formatted, StateData},_]}} ->
+ ok;
+ Other ->
+ io:format("Unexpected: ~p", [Other]),
+ ?t:fail()
+ after 5000 ->
+ io:format("Timeout: expected error logger msg", []),
+ ?t:fail()
+ end,
+ [] = ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
+ ok.
+
+
get_state(Config) when is_list(Config) ->
State = self(),
{ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []),
@@ -867,7 +893,8 @@ init({state_data, StateData}) ->
init(_) ->
{ok, idle, state_data}.
-
+terminate(_, _State, crash_terminate) ->
+ exit({crash, terminate});
terminate({From, stopped}, State, _Data) ->
From ! {self(), {stopped, State}},
ok;
@@ -1005,6 +1032,6 @@ handle_sync_event({get, _Pid}, _From, State, Data) ->
{reply, {state, State, Data}, State, Data}.
format_status(terminate, [_Pdict, StateData]) ->
- StateData;
+ {formatted, StateData};
format_status(normal, [_Pdict, _StateData]) ->
[format_status_called].
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 960e7f60e7..42694d8b5d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.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
@@ -32,7 +32,8 @@
spec_init_local_registered_parent/1,
spec_init_global_registered_parent/1,
otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1,
- error_format_status/1, get_state/1, replace_state/1, call_with_huge_message_queue/1
+ error_format_status/1, terminate_crash_format/1,
+ get_state/1, replace_state/1, call_with_huge_message_queue/1
]).
% spawn export
@@ -56,7 +57,8 @@ all() ->
call_remote_n3, spec_init,
spec_init_local_registered_parent,
spec_init_global_registered_parent, otp_5854, hibernate,
- otp_7669, call_format_status, error_format_status,
+ otp_7669,
+ call_format_status, error_format_status, terminate_crash_format,
get_state, replace_state,
call_with_huge_message_queue].
@@ -273,7 +275,7 @@ crash(Config) when is_list(Config) ->
receive
{error,_GroupLeader4,{Pid4,
"** Generic server"++_,
- [Pid4,crash,state4,crashed]}} ->
+ [Pid4,crash,{formatted, state4},crashed]}} ->
ok;
Other4a ->
?line io:format("Unexpected: ~p", [Other4a]),
@@ -1024,7 +1026,7 @@ error_format_status(Config) when is_list(Config) ->
receive
{error,_GroupLeader,{Pid,
"** Generic server"++_,
- [Pid,crash,State,crashed]}} ->
+ [Pid,crash,{formatted, State},crashed]}} ->
ok;
Other ->
?line io:format("Unexpected: ~p", [Other]),
@@ -1034,6 +1036,31 @@ error_format_status(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+%% Verify that error when terminating correctly calls our format_status/2 fun
+%%
+terminate_crash_format(Config) when is_list(Config) ->
+ error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ State = crash_terminate,
+ {ok, Pid} = gen_server:start_link(?MODULE, {state, State}, []),
+ gen_server:call(Pid, stop),
+ receive {'EXIT', Pid, {crash, terminate}} -> ok end,
+ receive
+ {error,_GroupLeader,{Pid,
+ "** Generic server"++_,
+ [Pid,stop, {formatted, State},{crash, terminate}]}} ->
+ ok;
+ Other ->
+ io:format("Unexpected: ~p", [Other]),
+ ?t:fail()
+ after 5000 ->
+ io:format("Timeout: expected error logger msg", []),
+ ?t:fail()
+ end,
+ ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
+ ok.
+
%% Verify that sys:get_state correctly returns gen_server state
%%
get_state(suite) ->
@@ -1323,10 +1350,12 @@ terminate({From, stopped}, _State) ->
terminate({From, stopped_info}, _State) ->
From ! {self(), stopped_info},
ok;
+terminate(_, crash_terminate) ->
+ exit({crash, terminate});
terminate(_Reason, _State) ->
ok.
format_status(terminate, [_PDict, State]) ->
- State;
+ {formatted, State};
format_status(normal, [_PDict, _State]) ->
format_status_called.
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 5a8971c071..3a76275f31 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -30,7 +30,7 @@
io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1,
printable_range/1,
io_lib_print_binary_depth_one/1, otp_10302/1, otp_10755/1,
- otp_10836/1]).
+ otp_10836/1, io_lib_width_too_small/1]).
-export([pretty/2]).
@@ -69,7 +69,8 @@ all() ->
io_lib_collect_line_3_wb, cr_whitespace_in_string,
io_fread_newlines, otp_8989, io_lib_fread_literal,
printable_range,
- io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836].
+ io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
+ io_lib_width_too_small].
groups() ->
[].
@@ -2213,3 +2214,8 @@ compile_file(File, Text, Config) ->
try compile:file(Fname, [return])
after ok %file:delete(Fname)
end.
+
+io_lib_width_too_small(Config) ->
+ "**" = lists:flatten(io_lib:format("~2.3w", [3.14])),
+ "**" = lists:flatten(io_lib:format("~2.5w", [3.14])),
+ ok.
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index c826ee731a..dda20a615b 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -24,10 +24,7 @@
-include_lib("test_server/include/test_server.hrl").
-% Default timetrap timeout (set in init_per_testcase).
-% This should be set relatively high (10-15 times the expected
-% max testcasetime).
--define(default_timeout, ?t:minutes(4)).
+-define(default_timeout, ?t:minutes(1)).
% Test server specific exports
-export([all/0]).
@@ -37,13 +34,13 @@
-export([init_per_testcase/2]).
-export([end_per_testcase/2]).
--export([get3/1]).
+-export([t_get_3/1,t_with_2/1,t_without_2/1]).
suite() ->
[{ct_hooks, [ts_install_cth]}].
all() ->
- [get3].
+ [t_get_3,t_with_2,t_without_2].
init_per_suite(Config) ->
Config.
@@ -52,7 +49,7 @@ end_per_suite(_Config) ->
ok.
init_per_testcase(_Case, Config) ->
- ?line Dog=test_server:timetrap(?default_timeout),
+ Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
@@ -60,10 +57,24 @@ end_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-get3(Config) when is_list(Config) ->
+t_get_3(Config) when is_list(Config) ->
Map = #{ key1 => value1, key2 => value2 },
DefaultValue = "Default value",
- ?line value1 = maps:get(key1, Map, DefaultValue),
- ?line value2 = maps:get(key2, Map, DefaultValue),
- ?line DefaultValue = maps:get(key3, Map, DefaultValue),
+ value1 = maps:get(key1, Map, DefaultValue),
+ value2 = maps:get(key2, Map, DefaultValue),
+ DefaultValue = maps:get(key3, Map, DefaultValue),
+ ok.
+
+t_without_2(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]),
+ M1 = maps:without([{k,I}||I <- Ki],M0),
+ ok.
+
+t_with_2(_Config) ->
+ Ki = [11,22,33,44,55,66,77,88,99],
+ M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]),
+ M1 = maps:from_list([{{k,I},{v,I}}||I<-Ki]),
+ M1 = maps:with([{k,I}||I <- Ki],M0),
ok.
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index e016432f4d..f841e2c4a6 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -2532,6 +2532,11 @@ otp_6554(Config) when is_list(Config) ->
"\n end.\nok.\n" =
t(<<"begin F = fun() -> foo end, 1 end. B = F(). C = 17. b().">>),
+ ?line "3: command not found" = comm_err(<<"#{v(3) => v}.">>),
+ ?line "3: command not found" = comm_err(<<"#{k => v(3)}.">>),
+ ?line "3: command not found" = comm_err(<<"#{v(3) := v}.">>),
+ ?line "3: command not found" = comm_err(<<"#{k := v(3)}.">>),
+ ?line "3: command not found" = comm_err(<<"(v(3))#{}.">>),
%% Tests I'd like to do: (you should try them manually)
%% "catch spawn_link(fun() -> timer:sleep(1000), exit(foo) end)."
%% "** exception error: foo" should be output after 1 second
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 59821220b4..3d09bd27ff 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -78,17 +78,29 @@ appup_test(_Config) ->
appup_tests(_App,{[],[]}) ->
{skip,"no previous releases available"};
-appup_tests(App,{OkVsns,NokVsns}) ->
+appup_tests(App,{OkVsns0,NokVsns}) ->
application:load(App),
{_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),
AppupFileName = atom_to_list(App) ++ ".appup",
AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),
{ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
ct:log("~p~n",[AppupScript]),
- ct:log("Testing ok versions: ~p~n",[OkVsns]),
+ OkVsns =
+ case OkVsns0 -- [Vsn] of
+ OkVsns0 ->
+ OkVsns0;
+ Ok ->
+ ct:log("Current version, ~p, is same as in previous release.~n"
+ "Removing this from the list of ok versions.",
+ [Vsn]),
+ Ok
+ end,
+ ct:log("Testing that appup allows upgrade from these versions: ~p~n",
+ [OkVsns]),
check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
- ct:log("Testing not ok versions: ~p~n",[NokVsns]),
+ ct:log("Testing that appup does not allow upgrade from these versions: ~p~n",
+ [NokVsns]),
check_appup(NokVsns,UpFrom,error),
check_appup(NokVsns,DownTo,error),
ok.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 9881ab040e..b522c3ea3c 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 2.1.1
+STDLIB_VSN = 2.2
diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile
index ab4dd4d95d..35bbad3c22 100644
--- a/lib/test_server/src/Makefile
+++ b/lib/test_server/src/Makefile
@@ -124,7 +124,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) "$(RELSYSDIR)/src"
$(INSTALL_DATA) $(ERL_FILES) "$(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/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 1ba2514977..faee5efd43 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -30,6 +30,21 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add log2 histogram to lcnt for lock wait time</p>
+ <p>
+ Own Id: OTP-12059</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.15</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index 7379215d68..b37d08e767 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -577,7 +577,7 @@ Please see the function `tempo-define-template'.")
"-record(state, {})." n n
(erlang-skel-double-separator-start 3)
- "%%% gen_event callbacks" n
+ "%%% API" n
(erlang-skel-double-separator-end 3) n
(erlang-skel-separator-start 2)
"%% @doc" n
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 4e3c49c717..c56759ebb9 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -7,7 +7,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
@@ -886,6 +886,7 @@ resulting regexp is surrounded by \\_< and \\_>."
"flush_monitor_message"
"format_cpu_topology"
"fun_info"
+ "fun_info_mfa"
"fun_to_list"
"function_exported"
"garbage_collect_message_area"
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 20ee32c861..f1251fddab 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -61,6 +61,8 @@
locations/1,
inspect/1,
inspect/2,
+ histogram/1,
+ histogram/2,
information/0,
swap_pid_keys/0,
% set options
@@ -89,14 +91,14 @@
duration = 0
}).
-
-record(stats, {
- file,
- line,
- tries,
- colls,
- time, % us
- nt % #timings collected
+ file :: atom(),
+ line :: non_neg_integer(),
+ tries :: non_neg_integer(),
+ colls :: non_neg_integer(),
+ time :: non_neg_integer(), % us
+ nt :: non_neg_integer(), % #timings collected
+ hist :: tuple() % histogram
}).
-record(lock, {
@@ -115,7 +117,9 @@
colls,
cr, % collision ratio
time,
- dtr % time duration ratio
+ dtr, % time duration ratio
+ %% new
+ hist % log2 histogram of lock wait_time
}).
@@ -127,7 +131,7 @@
%% -------------------------------------------------------------------- %%
start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []).
-stop() -> gen_server:cast(?MODULE, stop).
+stop() -> gen_server:call(?MODULE, stop, infinity).
init([]) -> {ok, #state{ locks = [], duration = 0 } }.
%% -------------------------------------------------------------------- %%
@@ -171,6 +175,8 @@ conflicts() -> call({conflicts, []}).
conflicts(Opts) -> call({conflicts, Opts}).
inspect(Lock) -> call({inspect, Lock, []}).
inspect(Lock, Opts) -> call({inspect, Lock, Opts}).
+histogram(Lock) -> call({histogram, Lock, []}).
+histogram(Lock, Opts)-> call({histogram, Lock, Opts}).
information() -> call(information).
swap_pid_keys() -> call(swap_pid_keys).
raw() -> call(raw).
@@ -283,14 +289,14 @@ handle_call({locations, InOpts}, _From, #state{ locks = Locks } = State) when is
{reply, ok, State};
-handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, locks = Locks } = State) when is_list(InOpts) ->
+handle_call({inspect, Lockname, InOpts}, _From, #state{ duration=Duration, locks=Locks } = State) when is_list(InOpts) ->
Default = [
{sort, time},
{reverse, false},
- {print, [name,id,tries,colls,ratio,time,duration]},
+ {print, [name,id,tries,colls,ratio,time,duration,histogram]},
{max_locks, 20},
{combine, false},
- {thresholds, [] },
+ {thresholds, []},
{locations, false}],
Opts = options(InOpts, Default),
@@ -299,7 +305,7 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
{true, true} -> locks_ids(Filtered);
_ -> []
end,
- Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
+ Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
case proplists:get_value(locations, Opts) of
true ->
lists:foreach(fun
@@ -313,17 +319,14 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
[] ->
ok;
_ ->
- %io:format("Combined ~p~n", [Combined]),
print("lock: " ++ term2string(Name)),
print("id: " ++ IdString),
print("type: " ++ term2string(Type)),
Ps = stats2print(Combined, Duration),
- Opts1 = options([{print, [entry, tries,colls,ratio,time,duration]},
+ Opts1 = options([{print, [entry, tries,colls,ratio,time,duration,histogram]},
{thresholds, [{tries, -1}, {colls, -1}, {time, -1}]}], Opts),
print_lock_information(filter_print(Ps, Opts1), proplists:get_value(print, Opts1))
end
- % (#lock{ name = Name, id = Id}) ->
- % io:format("Empty lock ~p ~p~n", [Name, Id])
end, Combos);
_ ->
Print1 = locks2print(Combos, Duration),
@@ -332,6 +335,34 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
end,
{reply, ok, State};
+%% histogram
+
+handle_call({histogram, Lockname, InOpts}, _From, #state{ duration=Duration, locks=Locks} = State)->
+ Default = [
+ {sort, time},
+ {reverse, false},
+ {print, [name,id,tries,colls,ratio,time,duration,histogram]},
+ {max_locks, 20},
+ {combine, true},
+ {thresholds, []},
+ {locations, false}],
+
+ Opts = options(InOpts, Default),
+ Filtered = filter_locks(Locks, Lockname),
+ Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
+ lists:foreach(fun
+ (#lock{ stats = Stats }=L) ->
+ SumStats = summate_stats(Stats),
+ Opts1 = options([{print, [name,id,tries,colls,ratio,time,duration]},
+ {thresholds, [{tries, -1}, {colls, -1}, {time, -1}]}], Opts),
+ Prints = locks2print([L], Duration),
+ print_lock_information(Prints, proplists:get_value(print, Opts1)),
+ print_full_histogram(SumStats#stats.hist),
+ io:format("~n")
+ end, Combos),
+
+ {reply, ok, State};
+
handle_call(raw, _From, #state{ locks = Locks} = State)->
{reply, Locks, State};
@@ -347,7 +378,6 @@ handle_call(swap_pid_keys, _From, #state{ locks = Locks } = State)->
(L) ->
L
end, Locks),
-
{reply, ok, State#state{ locks = SwappedLocks}};
% settings
@@ -380,6 +410,8 @@ handle_call({save, Filename}, _From, State) ->
{reply, {error, Error}, State}
end;
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
handle_call(Command, _From, State) ->
{reply, {error, {undefined, Command}}, State}.
@@ -390,8 +422,6 @@ handle_call(Command, _From, State) ->
%%
%% -------------------------------------------------------------------- %%
-handle_cast(stop, State) ->
- {stop, normal, State};
handle_cast(_, State) ->
{noreply, State}.
@@ -432,15 +462,32 @@ code_change(_OldVsn, State, _Extra) ->
summate_locks(Locks) -> summate_locks(Locks, #stats{ tries = 0, colls = 0, time = 0, nt = 0}).
summate_locks([], Stats) -> Stats;
-summate_locks([L|Ls], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt}) ->
+summate_locks([L|Ls], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt, hist = Hist}) ->
S = summate_stats(L#lock.stats),
- summate_locks(Ls, #stats{ tries = Tries + S#stats.tries, colls = Colls + S#stats.colls, time = Time + S#stats.time, nt = Nt + S#stats.nt}).
+ summate_locks(Ls, #stats{
+ tries = Tries + S#stats.tries,
+ colls = Colls + S#stats.colls,
+ time = Time + S#stats.time,
+ nt = Nt + S#stats.nt,
+ hist = summate_histogram(Hist, S#stats.hist)
+ }).
summate_stats(Stats) -> summate_stats(Stats, #stats{ tries = 0, colls = 0, time = 0, nt = 0}).
summate_stats([], Stats) -> Stats;
-summate_stats([S|Ss], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt}) ->
- summate_stats(Ss, #stats{ tries = Tries + S#stats.tries, colls = Colls + S#stats.colls, time = Time + S#stats.time, nt = Nt + S#stats.nt}).
-
+summate_stats([S|Ss], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt, hist = Hist}) ->
+ summate_stats(Ss, #stats{
+ tries = Tries + S#stats.tries,
+ colls = Colls + S#stats.colls,
+ time = Time + S#stats.time,
+ nt = Nt + S#stats.nt,
+ hist = summate_histogram(Hist, S#stats.hist)
+ }).
+
+%% first call is undefined
+summate_histogram(Tup,undefined) when is_tuple(Tup) -> Tup;
+summate_histogram(undefined,Tup) when is_tuple(Tup) -> Tup;
+summate_histogram(Hs1,Hs2) ->
+ list_to_tuple([ A + B || {A,B} <- lists:zip(tuple_to_list(Hs1),tuple_to_list(Hs2))]).
%% manipulators
filter_locks_type(Locks, undefined) -> Locks;
@@ -465,17 +512,16 @@ filter_print(PLs, Opts) ->
TLs = threshold_locks(PLs, proplists:get_value(thresholds, Opts, [])),
SLs = sort_locks(TLs, proplists:get_value(sort, Opts, time)),
CLs = cut_locks(SLs, proplists:get_value(max_locks, Opts, none)),
- reverse_locks(CLs, proplists:get_value(reverse, Opts, false)).
-
-sort_locks(Locks, Type) -> lists:reverse(sort_locks0(Locks, Type)).
-sort_locks0(Locks, name) -> lists:keysort(#print.name, Locks);
-sort_locks0(Locks, id) -> lists:keysort(#print.id, Locks);
-sort_locks0(Locks, type) -> lists:keysort(#print.type, Locks);
-sort_locks0(Locks, tries) -> lists:keysort(#print.tries, Locks);
-sort_locks0(Locks, colls) -> lists:keysort(#print.colls, Locks);
-sort_locks0(Locks, ratio) -> lists:keysort(#print.cr, Locks);
-sort_locks0(Locks, time) -> lists:keysort(#print.time, Locks);
-sort_locks0(Locks, _) -> sort_locks0(Locks, time).
+ reverse_locks(CLs, not proplists:get_value(reverse,Opts, false)).
+
+sort_locks(Locks, name) -> lists:keysort(#print.name, Locks);
+sort_locks(Locks, id) -> lists:keysort(#print.id, Locks);
+sort_locks(Locks, type) -> lists:keysort(#print.type, Locks);
+sort_locks(Locks, tries) -> lists:keysort(#print.tries, Locks);
+sort_locks(Locks, colls) -> lists:keysort(#print.colls, Locks);
+sort_locks(Locks, ratio) -> lists:keysort(#print.cr, Locks);
+sort_locks(Locks, time) -> lists:keysort(#print.time, Locks);
+sort_locks(Locks, _) -> sort_locks(Locks, time).
% cut locks not above certain thresholds
threshold_locks(Locks, Thresholds) ->
@@ -556,45 +602,61 @@ locks_ids(Locks) -> locks_ids(Locks, []).
locks_ids([], Out) -> Out;
locks_ids([#lock{ name = Key } = L|Ls], Out) ->
case proplists:get_value(Key, Out) of
- undefined ->
- locks_ids(Ls, [{Key, [L#lock.id] } | Out]);
- Ids ->
- locks_ids(Ls, [{Key, [L#lock.id | Ids] } | proplists:delete(Key,Out)])
+ undefined -> locks_ids(Ls, [{Key, [L#lock.id]}|Out]);
+ Ids -> locks_ids(Ls, [{Key, [L#lock.id|Ids]}|proplists:delete(Key,Out)])
end.
stats2print(Stats, Duration) ->
lists:map(fun
(S) ->
- #print{
- entry = term2string("~tp:~p", [S#stats.file, S#stats.line]),
- colls = S#stats.colls,
- tries = S#stats.tries,
- cr = percent(S#stats.colls, S#stats.tries),
- time = S#stats.time,
- dtr = percent(S#stats.time, Duration)
- }
+ #print{entry = term2string("~tp:~p", [S#stats.file, S#stats.line]),
+ colls = S#stats.colls,
+ tries = S#stats.tries,
+ cr = percent(S#stats.colls, S#stats.tries),
+ time = S#stats.time,
+ dtr = percent(S#stats.time, Duration),
+ hist = format_histogram(S#stats.hist)}
end, Stats).
locks2print(Locks, Duration) ->
lists:map( fun
(L) ->
- Tries = lists:sum([T || #stats{ tries = T} <- L#lock.stats]),
- Colls = lists:sum([C || #stats{ colls = C} <- L#lock.stats]),
- Time = lists:sum([T || #stats{ time = T} <- L#lock.stats]),
- Cr = percent(Colls, Tries),
- Dtr = percent(Time, Duration),
- #print{
- name = L#lock.name,
- id = L#lock.id,
- type = L#lock.type,
- tries = Tries,
- colls = Colls,
- cr = Cr,
- time = Time,
- dtr = Dtr
- }
+ #stats{tries = Tries,
+ colls = Colls,
+ time = Time,
+ hist = Hist} = summate_stats(L#lock.stats),
+ Cr = percent(Colls, Tries),
+ Dtr = percent(Time, Duration),
+ #print{name = L#lock.name,
+ id = L#lock.id,
+ type = L#lock.type,
+ tries = Tries,
+ colls = Colls,
+ hist = format_histogram(Hist),
+ cr = Cr,
+ time = Time,
+ dtr = Dtr}
end, Locks).
+
+format_histogram(Tup) when is_tuple(Tup) ->
+ Vs = tuple_to_list(Tup),
+ Max = lists:max(Vs),
+ case Max of
+ 0 -> string_histogram(Vs);
+ _ -> string_histogram([case V of 0 -> 0; _ -> V/Max end || V <- Vs])
+ end.
+
+string_histogram([0|Vs]) ->
+ [$\s|string_histogram(Vs)];
+string_histogram([V|Vs]) when V > 0.66 ->
+ [$X|string_histogram(Vs)];
+string_histogram([V|Vs]) when V > 0.33 ->
+ [$x|string_histogram(Vs)];
+string_histogram([_|Vs]) ->
+ [$.|string_histogram(Vs)];
+string_histogram([]) -> [].
+
%% state making
data2state(Data, State) ->
@@ -606,22 +668,32 @@ data2state(Data, State) ->
locks = Locks
}.
-locks2records(Locks) -> locks2records(Locks, []).
-locks2records([], Out) -> Out;
-locks2records([{Name, Id, Type, Stats}|Locks], Out) ->
- Lock = #lock{
- name = Name,
- id = clean_id_creation(Id),
- type = Type,
- stats = [ #stats{
- file = File,
- line = Line,
- tries = Tries,
- colls = Colls,
- time = time2us({S, Ns}),
- nt = N
- } || {{File, Line}, {Tries, Colls, {S, Ns, N}}} <- Stats] },
- locks2records(Locks, [Lock|Out]).
+locks2records([{Name, Id, Type, Stats}|Locks]) ->
+ [#lock{name = Name,
+ id = clean_id_creation(Id),
+ type = Type,
+ stats = stats2record(Stats)}|locks2records(Locks)];
+locks2records([]) -> [].
+
+%% new stats with histogram
+stats2record([{{File,Line},{Tries,Colls,{S,Ns,N}},Hist}|Stats]) ->
+ [#stats{file = File,
+ line = Line,
+ hist = Hist,
+ tries = Tries,
+ colls = Colls,
+ time = time2us({S, Ns}),
+ nt = N} | stats2record(Stats)];
+%% old stats without histogram
+stats2record([{{File,Line},{Tries,Colls,{S,Ns,N}}}|Stats]) ->
+ [#stats{file = File,
+ line = Line,
+ hist = {},
+ tries = Tries,
+ colls = Colls,
+ time = time2us({S, Ns}),
+ nt = N} | stats2record(Stats)];
+stats2record([]) -> [].
clean_id_creation(Id) when is_pid(Id) ->
Bin = term_to_binary(Id),
@@ -647,22 +719,45 @@ state2list(State) ->
(X, Y) -> {X,Y}
end, record_info(fields, state), Values).
-list2state(List) -> list2state(record_info(fields, state), List, [state]).
-list2state([], _, Out) -> list_to_tuple(lists:reverse(Out));
-list2state([locks|Fs], List, Out) ->
- Locks = [ list2lock(Lock) || Lock <- proplists:get_value(locks, List, [])],
- list2state(Fs, List, [Locks|Out]);
-list2state([F|Fs], List, Out) -> list2state(Fs, List, [proplists:get_value(F, List, state_default(F))|Out]).
-
lock_default(Field) -> proplists:get_value(Field, lock2list(#lock{})).
lock2list(Lock) ->
[_|Values] = tuple_to_list(Lock),
lists:zip(record_info(fields, lock), Values).
-list2lock(List) -> list2lock(record_info(fields, lock), List, [lock]).
-list2lock([], _, Out) -> list_to_tuple(lists:reverse(Out));
-list2lock([F|Fs], List, Out) -> list2lock(Fs, List, [proplists:get_value(F, List, lock_default(F))|Out]).
+
+list2state(List) ->
+ list_to_tuple([state|list2state(record_info(fields, state), List)]).
+list2state([], _) -> [];
+list2state([locks|Fs], List) ->
+ Locks = [list2lock(Lock) || Lock <- proplists:get_value(locks, List, [])],
+ [Locks|list2state(Fs,List)];
+list2state([F|Fs], List) ->
+ [proplists:get_value(F, List, state_default(F))|list2state(Fs, List)].
+
+list2lock(Ls) ->
+ list_to_tuple([lock|list2lock(record_info(fields, lock), Ls)]).
+
+list2lock([],_) -> [];
+list2lock([stats=F|Fs], Ls) ->
+ Stats = stats2stats(proplists:get_value(F, Ls, lock_default(F))),
+ [Stats|list2lock(Fs, Ls)];
+list2lock([F|Fs], Ls) ->
+ [proplists:get_value(F, Ls, lock_default(F))|list2lock(Fs, Ls)].
+
+%% process old stats (hack)
+%% old stats had no histograms
+%% in future versions stats should be serialized as a list, not a record
+
+stats2stats([]) -> [];
+stats2stats([Stat|Stats]) ->
+ Sz = tuple_size(#stats{}),
+ [stat2stat(Stat,Sz)|stats2stats(Stats)].
+
+stat2stat(Stat,Sz) when tuple_size(Stat) =:= Sz -> Stat;
+stat2stat(Stat,_) ->
+ %% assume no histogram at the end
+ list_to_tuple(tuple_to_list(Stat) ++ [{0}]).
%% printing
@@ -683,7 +778,7 @@ auto_print_width(Locks, Print) ->
({print,print}, Out) -> [print|Out];
({Str, Len}, Out) -> [erlang:min(erlang:max(length(s(Str))+1,Len),80)|Out]
end, [], lists:zip(tuple_to_list(L), tuple_to_list(Max)))))
- end, #print{ id = 4, type = 5, entry = 5, name = 6, tries = 8, colls = 13, cr = 16, time = 11, dtr = 14 },
+ end, #print{ id = 4, type = 5, entry = 5, name = 6, tries = 8, colls = 13, cr = 16, time = 11, dtr = 14, hist=20 },
Locks),
% Setup the offsets for later pruning
Offsets = [
@@ -695,7 +790,9 @@ auto_print_width(Locks, Print) ->
{colls, R#print.colls},
{ratio, R#print.cr},
{time, R#print.time},
- {duration, R#print.dtr}],
+ {duration, R#print.dtr},
+ {histogram, R#print.hist}
+ ],
% Prune offsets to only allow specified print options
lists:foldr(fun
({Type, W}, Out) -> [{Type, W}|Out];
@@ -705,9 +802,7 @@ auto_print_width(Locks, Print) ->
print_lock_information(Locks, Print) ->
% remake Print to autosize entries
AutoPrint = auto_print_width(Locks, Print),
-
print_header(AutoPrint),
-
lists:foreach(fun
(L) ->
print_lock(L, AutoPrint)
@@ -724,7 +819,8 @@ print_header(Opts) ->
colls = "#collisions",
cr = "collisions [%]",
time = "time [us]",
- dtr = "duration [%]"
+ dtr = "duration [%]",
+ hist = "histogram"
},
Divider = #print{
name = lists:duplicate(1 + length(Header#print.name), 45),
@@ -735,39 +831,44 @@ print_header(Opts) ->
colls = lists:duplicate(1 + length(Header#print.colls), 45),
cr = lists:duplicate(1 + length(Header#print.cr), 45),
time = lists:duplicate(1 + length(Header#print.time), 45),
- dtr = lists:duplicate(1 + length(Header#print.dtr), 45)
+ dtr = lists:duplicate(1 + length(Header#print.dtr), 45),
+ hist = lists:duplicate(1 + length(Header#print.hist), 45)
},
print_lock(Header, Opts),
print_lock(Divider, Opts),
ok.
-print_lock(L, Opts) -> print_lock(L, Opts, []).
-print_lock(_, [], Formats) -> print(strings(lists:reverse(Formats)));
-print_lock(L, [Opt|Opts], Formats) ->
+print_lock(L, Opts) ->
+ print(strings(format_lock(L, Opts))).
+
+format_lock(_, []) -> [];
+format_lock(L, [Opt|Opts]) ->
case Opt of
- id -> print_lock(L, Opts, [{space, 25, s(L#print.id) } | Formats]);
- {id, W} -> print_lock(L, Opts, [{space, W, s(L#print.id) } | Formats]);
- type -> print_lock(L, Opts, [{space, 18, s(L#print.type) } | Formats]);
- {type, W} -> print_lock(L, Opts, [{space, W, s(L#print.type) } | Formats]);
- entry -> print_lock(L, Opts, [{space, 30, s(L#print.entry)} | Formats]);
- {entry, W} -> print_lock(L, Opts, [{space, W, s(L#print.entry)} | Formats]);
- name -> print_lock(L, Opts, [{space, 22, s(L#print.name) } | Formats]);
- {name, W} -> print_lock(L, Opts, [{space, W, s(L#print.name) } | Formats]);
- tries -> print_lock(L, Opts, [{space, 12, s(L#print.tries)} | Formats]);
- {tries, W} -> print_lock(L, Opts, [{space, W, s(L#print.tries)} | Formats]);
- colls -> print_lock(L, Opts, [{space, 14, s(L#print.colls)} | Formats]);
- {colls, W} -> print_lock(L, Opts, [{space, W, s(L#print.colls)} | Formats]);
- ratio -> print_lock(L, Opts, [{space, 20, s(L#print.cr) } | Formats]);
- {ratio, W} -> print_lock(L, Opts, [{space, W, s(L#print.cr) } | Formats]);
- time -> print_lock(L, Opts, [{space, 15, s(L#print.time) } | Formats]);
- {time, W} -> print_lock(L, Opts, [{space, W, s(L#print.time) } | Formats]);
- duration -> print_lock(L, Opts, [{space, 20, s(L#print.dtr) } | Formats]);
- {duration, W} -> print_lock(L, Opts, [{space, W, s(L#print.dtr) } | Formats]);
- _ -> print_lock(L, Opts, Formats)
+ id -> [{space, 25, s(L#print.id) } | format_lock(L, Opts)];
+ {id, W} -> [{space, W, s(L#print.id) } | format_lock(L, Opts)];
+ type -> [{space, 18, s(L#print.type) } | format_lock(L, Opts)];
+ {type, W} -> [{space, W, s(L#print.type) } | format_lock(L, Opts)];
+ entry -> [{space, 30, s(L#print.entry)} | format_lock(L, Opts)];
+ {entry, W} -> [{space, W, s(L#print.entry)} | format_lock(L, Opts)];
+ name -> [{space, 22, s(L#print.name) } | format_lock(L, Opts)];
+ {name, W} -> [{space, W, s(L#print.name) } | format_lock(L, Opts)];
+ tries -> [{space, 12, s(L#print.tries)} | format_lock(L, Opts)];
+ {tries, W} -> [{space, W, s(L#print.tries)} | format_lock(L, Opts)];
+ colls -> [{space, 14, s(L#print.colls)} | format_lock(L, Opts)];
+ {colls, W} -> [{space, W, s(L#print.colls)} | format_lock(L, Opts)];
+ ratio -> [{space, 20, s(L#print.cr) } | format_lock(L, Opts)];
+ {ratio, W} -> [{space, W, s(L#print.cr) } | format_lock(L, Opts)];
+ time -> [{space, 15, s(L#print.time) } | format_lock(L, Opts)];
+ {time, W} -> [{space, W, s(L#print.time) } | format_lock(L, Opts)];
+ duration -> [{space, 20, s(L#print.dtr) } | format_lock(L, Opts)];
+ {duration, W} -> [{space, W, s(L#print.dtr) } | format_lock(L, Opts)];
+ histogram -> [{space, 0, s(L#print.hist) } | format_lock(L, Opts)];
+ {histogram, W} -> [{space, W, s(L#print.hist) } | format_lock(L, Opts)];
+ _ -> format_lock(L, Opts)
end.
-print_state_information(#state{ locks = Locks} = State) ->
+print_state_information(#state{locks = Locks} = State) ->
Stats = summate_locks(Locks),
print("information:"),
print(kv("#locks", s(length(Locks)))),
@@ -779,9 +880,25 @@ print_state_information(#state{ locks = Locks} = State) ->
print(kv("percent of duration", s(Stats#stats.time/State#state.duration*100) ++ " %")),
ok.
+
+print_full_histogram(T) when is_tuple(T) ->
+ Vs = tuple_to_list(T),
+ Max = lists:max(Vs),
+ W = 60,
+ print_full_histogram(0,Vs,Max,W).
+
+print_full_histogram(_,[],_,_) -> ok;
+print_full_histogram(Ix,[V|Vs],0,W) ->
+ io:format("~2w = log2 : ~8w |~n", [Ix,V]),
+ print_full_histogram(Ix+1,Vs,0,W);
+print_full_histogram(Ix,[V|Vs],Max,W) ->
+ io:format("~2w = log2 : ~8w | ~s~n", [Ix,V,lists:duplicate(trunc(W*(V/Max)), $#)]),
+ print_full_histogram(Ix+1,Vs,Max,W).
+
+
%% AUX
-time2us({S, Ns}) -> round(S*1000000 + Ns/1000).
+time2us({S, Ns}) -> S*1000000 + (Ns div 1000).
percent(_,0) -> 0.0;
percent(T,N) -> T/N*100.
@@ -808,7 +925,7 @@ s(T) -> term2string(T).
strings(Strings) -> strings(Strings, []).
strings([], Out) -> Out;
-strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ps", [N]), [S]));
+strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ws", [N]), [S]));
strings([{format, Format, S} | Ss], Out) -> strings(Ss, Out ++ term2string(Format, [S]));
strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~ts", [S])).
@@ -825,7 +942,7 @@ term2string(Term) when is_pid(Term) ->
term2string(Term) -> term2string("~w", [Term]).
term2string(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
-%%% AUD id binary
+%%% AUX id binary
bytes16(Value) ->
B0 = Value band 255,
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 1bee6021ab..010dffe138 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -27,11 +27,11 @@
%% Test cases
-export([
- load_v1/1,
- conflicts/1,
- locations/1,
- swap_keys/1
- ]).
+ t_load/1,
+ t_conflicts/1,
+ t_locations/1,
+ t_swap_keys/1
+ ]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(4)).
@@ -54,48 +54,52 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [load_v1, conflicts, locations, swap_keys].
+all() -> [t_load, t_conflicts, t_locations, t_swap_keys].
-groups() ->
- [].
+groups() -> [].
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(_GroupName, Config) -> Config.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(_GroupName, Config) -> Config.
%%----------------------------------------------------------------------
%% Tests
%%----------------------------------------------------------------------
-load_v1(suite) ->
- [];
-load_v1(doc) ->
- ["Load data from file."];
-load_v1(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:stop(),
+t_load(suite) -> [];
+t_load(doc) -> ["Load data from file."];
+t_load(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_load_file(Files),
ok.
-conflicts(suite) ->
- [];
-conflicts(doc) ->
- ["API: conflicts"];
-conflicts(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:conflicts(),
- THs = [-1, 0, 100, 1000],
- Print = [name , id , type , entry , tries , colls , ratio , time , duration],
- Opts = [
+t_load_file([]) -> ok;
+t_load_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:stop(),
+ t_load_file(Files).
+
+t_conflicts(suite) -> [];
+t_conflicts(doc) -> ["API: conflicts"];
+t_conflicts(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_conflicts_file(Files),
+ ok.
+
+t_conflicts_file([]) -> ok;
+t_conflicts_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:conflicts(),
+ THs = [-1, 0, 100, 1000],
+ Print = [name , id , type , entry , tries , colls , ratio , time , duration],
+ Opts = [
[{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
ML <- [none, 1 , 32, 4096],
@@ -103,28 +107,33 @@ conflicts(Config) when is_list(Config) ->
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Rev <- [true, false]
],
- ?line ok = test_conflicts_opts(Opts),
- ?line ok = lcnt:stop(),
- ok.
+ ok = test_conflicts_opts(Opts),
+ ok = lcnt:stop(),
+ t_conflicts_file(Files).
+
test_conflicts_opts([]) -> ok;
test_conflicts_opts([Opt|Opts]) ->
- ?line ok = lcnt:conflicts(Opt),
+ ok = lcnt:conflicts(Opt),
test_conflicts_opts(Opts).
-locations(suite) ->
- [];
-locations(doc) ->
- ["API: locations"];
-locations(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:locations(),
- THs = [-1, 0, 100, 1000],
- Print = [name , id , type , entry , tries , colls , ratio , time , duration],
- Opts = [
+t_locations(suite) -> [];
+t_locations(doc) -> ["API: locations"];
+t_locations(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_locations_file(Files),
+ ok.
+
+t_locations_file([]) -> ok;
+t_locations_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:locations(),
+ THs = [-1, 0, 100, 1000],
+ Print = [name , id , type , entry , tries , colls , ratio , time , duration],
+ Opts = [
[{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
ML <- [none, 1 , 64],
@@ -132,30 +141,34 @@ locations(Config) when is_list(Config) ->
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Id <- [true, false]
],
- ?line ok = test_locations_opts(Opts),
- ?line ok = lcnt:stop(),
- ok.
+ ok = test_locations_opts(Opts),
+ ok = lcnt:stop(),
+ t_locations_file(Files).
test_locations_opts([]) -> ok;
test_locations_opts([Opt|Opts]) ->
- ?line ok = lcnt:locations(Opt),
+ ok = lcnt:locations(Opt),
test_locations_opts(Opts).
-swap_keys(suite) ->
- [];
-swap_keys(doc) ->
- ["Test interchanging port/process id with class"];
-swap_keys(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:conflicts(),
- ?line ok = lcnt:swap_pid_keys(),
- ?line ok = lcnt:conflicts(),
- ?line ok = lcnt:stop(),
+t_swap_keys(suite) -> [];
+t_swap_keys(doc) -> ["Test interchanging port/process id with class"];
+t_swap_keys(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_swap_keys_file(Files),
ok.
+t_swap_keys_file([]) -> ok;
+t_swap_keys_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:conflicts(),
+ ok = lcnt:swap_pid_keys(),
+ ok = lcnt:conflicts(),
+ ok = lcnt:stop(),
+ t_swap_keys_file(Files).
+
%%----------------------------------------------------------------------
%% Auxiliary tests
diff --git a/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt b/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt
new file mode 100644
index 0000000000..ff5bdcbdaa
--- /dev/null
+++ b/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt
Binary files differ
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 54dc4ec91d..3acb8d38e2 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 2.6.15
+TOOLS_VSN = 2.7
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index a96f1f2632..4c4d4f41a8 100644
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -677,6 +677,27 @@ if test "x$GCC" = xyes; then
LM_TRY_ENABLE_CFLAG([-Werror=return-type], [CXXFLAGS])
fi
+dnl ----------------------------------------------------------------------
+dnl Enable -fsanitize= flags.
+dnl ----------------------------------------------------------------------
+
+m4_define(DEFAULT_SANITIZERS, [address,undefined])
+AC_ARG_ENABLE(
+ sanitizers,
+ AS_HELP_STRING(
+ [--enable-sanitizers@<:@=comma-separated list of sanitizers@:>@],
+ [Default=DEFAULT_SANITIZERS]),
+[
+case "$enableval" in
+ no) sanitizers= ;;
+ yes) sanitizers="-fsanitize=DEFAULT_SANITIZERS" ;;
+ *) sanitizers="-fsanitize=$enableval" ;;
+esac
+CFLAGS="$CFLAGS $sanitizers"
+CXXFLAGS="$CXXFLAGS $sanitizers"
+LDFLAGS="$LDFLAGS $sanitizers"
+])
+
#############################################################################
dnl
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 63eb047caa..5a9c53e3b6 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -31,6 +31,23 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.3.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Implement --enable-sanitizers[=sanitizers]. Similar to
+ debugging with Valgrind, it's very useful to enable
+ -fsanitize= switches to catch bugs at runtime.</p>
+ <p>
+ Own Id: OTP-12153</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index ee3d247553..24e8c2ed11 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.3
+WX_VSN = 1.3.1
diff --git a/lib/xmerl/doc/src/motorcycles2html.erl b/lib/xmerl/doc/src/motorcycles2html.erl
index dfbd19e359..45c713e1ac 100644
--- a/lib/xmerl/doc/src/motorcycles2html.erl
+++ b/lib/xmerl/doc/src/motorcycles2html.erl
@@ -7,7 +7,7 @@
%%%-------------------------------------------------------------------
-module(motorcycles2html).
--include("xmerl.hrl").
+-include_lib("xmerl/include/xmerl.hrl").
-import(xmerl_xs,
[ xslapply/2, value_of/1, select/2, built_in_rules/2 ]).
@@ -57,12 +57,12 @@ template(E) -> built_in_rules(fun template/1, E).
%% sorts on the bike name element, unwraps the bike information and
%% inserts a line feed and indentation on each bike element.
sort_by_manufacturer(L) ->
- Tuples=[X1||X1={H,T} <- L],
+ Tuples=[X1||X1={_,_} <- L],
SortedTS = lists:keysort(1,Tuples),
InsertRefName_UnWrap=
fun([{[Name],V}|Rest],Name,F)->
[V|F(Rest,Name,F)];
- ([{[Name],V}|Rest],PreviousName,F) ->
+ ([{[Name],V}|Rest],_PreviousName,F) ->
[["<a name=\"",Name,"\"></>"],V|F(Rest,Name,F)];
([],_,_) -> []
end,
@@ -71,7 +71,7 @@ sort_by_manufacturer(L) ->
WS = "\n ",
Fun=fun([H|T],Acc,F)->
F(T,[H,WS|Acc],F);
- ([],Acc,F)->
+ ([],Acc,_F)->
lists:reverse([WS|Acc])
end,
if length(SortedRefed) > 0 ->
@@ -96,13 +96,12 @@ remove_duplicates([A|L],Acc) ->
end.
make_ref([]) -> [];
-make_ref([H]) when atom(H) ->
+make_ref([H]) when is_atom(H) ->
"<ul><a href=\"#"++atom_to_list(H)++"\">"++atom_to_list(H)++"</a></ul>";
-make_ref([H]) when list(H) ->
+make_ref([H]) when is_list(H) ->
"<ul><a href=\"#"++H++"\">\s"++H++"</a></ul>";
-make_ref([H|T]) when atom(H) ->
+make_ref([H|T]) when is_atom(H) ->
["<ul><a href=\"#"++atom_to_list(H)++"\">\s"++atom_to_list(H)++",\n</a></ul>"
|make_ref(T)];
-make_ref([H|T]) when list(H) ->
+make_ref([H|T]) when is_list(H) ->
["<ul><a href=\"#"++H++"\">\s"++H++",\n</a></ul>"|make_ref(T)].
-