aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--OTP_VERSION2
-rw-r--r--configure.in6
-rw-r--r--erts/configure.in43
-rw-r--r--erts/doc/src/crash_dump.xml5
-rw-r--r--erts/doc/src/erl.xml20
-rw-r--r--erts/doc/src/erl_driver.xml8
-rw-r--r--erts/doc/src/erl_nif.xml25
-rw-r--r--erts/doc/src/erlang.xml11
-rw-r--r--erts/doc/src/notes.xml170
-rw-r--r--erts/emulator/beam/beam_ranges.c2
-rw-r--r--erts/emulator/beam/erl_alloc.types4
-rw-r--r--erts/emulator/beam/erl_alloc_util.c12
-rw-r--r--erts/emulator/beam/erl_bif_info.c36
-rw-r--r--erts/emulator/beam/erl_bif_port.c15
-rw-r--r--erts/emulator/beam/erl_driver.h2
-rw-r--r--erts/emulator/beam/erl_drv_nif.h1
-rw-r--r--erts/emulator/beam/erl_init.c18
-rw-r--r--erts/emulator/beam/erl_nif.c10
-rw-r--r--erts/emulator/beam/erl_nif.h23
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h41
-rw-r--r--erts/emulator/beam/erl_port_task.c24
-rw-r--r--erts/emulator/beam/erl_port_task.h2
-rw-r--r--erts/emulator/beam/erl_process.c68
-rw-r--r--erts/emulator/beam/erl_process.h1
-rw-r--r--erts/emulator/beam/io.c12
-rw-r--r--erts/emulator/beam/sys.h14
-rw-r--r--erts/emulator/beam/utils.c1
-rw-r--r--erts/emulator/drivers/win32/win_efile.c4
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m45
-rw-r--r--erts/emulator/hipe/hipe_bif0.c2
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c20
-rw-r--r--erts/emulator/hipe/hipe_risc_glue.h8
-rw-r--r--erts/emulator/hipe/hipe_x86_glue.h8
-rw-r--r--erts/emulator/sys/common/erl_check_io.c694
-rw-r--r--erts/emulator/sys/common/erl_check_io.h45
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c8
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h16
-rw-r--r--erts/emulator/sys/unix/sys.c6
-rw-r--r--erts/emulator/sys/win32/erl_poll.c2
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h4
-rw-r--r--erts/emulator/test/a_SUITE.erl14
-rw-r--r--erts/emulator/test/driver_SUITE.erl100
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c9
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c11
-rw-r--r--erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c9
-rw-r--r--erts/emulator/test/float_SUITE_data/fp_drv.c17
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c12
-rw-r--r--erts/emulator/test/port_SUITE.erl51
-rw-r--r--erts/emulator/test/z_SUITE.erl28
-rw-r--r--erts/etc/common/erlexec.c1
-rw-r--r--erts/etc/unix/etp-commands.in66
-rw-r--r--erts/etc/win32/Install.c2
-rw-r--r--erts/etc/win32/erl.c2
-rw-r--r--erts/lib_src/common/erl_misc_utils.c6
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin56176 -> 56292 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin97900 -> 97972 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin4160 -> 4164 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48800 -> 48808 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1464 -> 1468 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1332 -> 1340 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44900 -> 44904 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin73128 -> 73128 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23432 -> 23440 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin13180 -> 13188 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl9
-rw-r--r--erts/preloaded/src/erlang.erl1
-rw-r--r--erts/preloaded/src/erts.app.src1
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c2
-rw-r--r--lib/asn1/doc/src/notes.xml24
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Constructed.asn6
-rw-r--r--lib/asn1/test/ber_decode_error.erl4
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/doc/src/notes.xml48
-rw-r--r--lib/common_test/src/ct_cover.erl18
-rw-r--r--lib/common_test/src/ct_logs.erl67
-rw-r--r--lib/common_test/src/ct_property_test.erl4
-rw-r--r--lib/common_test/test/ct_cover_SUITE.erl17
-rw-r--r--lib/common_test/test/ct_test_support.erl1
-rw-r--r--lib/compiler/doc/src/notes.xml16
-rw-r--r--lib/compiler/vsn.mk2
-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/debugger/src/dbg_icmd.erl6
-rw-r--r--lib/debugger/src/dbg_ieval.erl12
-rw-r--r--lib/debugger/src/int.erl5
-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_utils.erl29
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/doc/src/notes.xml59
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl33
-rw-r--r--lib/edoc/doc/src/notes.xml16
-rw-r--r--lib/edoc/src/edoc_layout.erl15
-rw-r--r--lib/edoc/src/edoc_types.erl2
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/eldap/doc/src/eldap.xml16
-rw-r--r--lib/eldap/doc/src/notes.xml31
-rw-r--r--lib/eldap/src/eldap.erl88
-rw-r--r--lib/eldap/test/Makefile1
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl10
-rw-r--r--lib/eldap/test/eldap_connections_SUITE.erl147
-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.erl17
-rw-r--r--lib/erl_docgen/vsn.mk2
-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/decode/decode_big.c11
-rw-r--r--lib/erl_interface/src/erl_interface.app.src32
-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/vsn.mk2
-rw-r--r--lib/hipe/doc/src/notes.xml22
-rw-r--r--lib/hipe/vsn.mk2
-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/notes.xml53
-rw-r--r--lib/inets/src/ftp/ftp.erl77
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl4
-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.java134
-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.java2
-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.java3
-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.java24
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java4
-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.java11
-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.java8
-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.java42
-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.java26
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java7
-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.java31
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/jinterface.app.src32
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/FunEquals.java8
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/GetNames.java6
-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/notes.xml15
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/application_master.erl6
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl3
-rw-r--r--lib/kernel/test/kernel_SUITE.erl18
-rw-r--r--lib/kernel/vsn.mk2
-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/Mnesia_chap3.xml2
-rw-r--r--lib/mnesia/doc/src/mnesia.xml2
-rw-r--r--lib/mnesia/doc/src/notes.xml21
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/doc/src/notes.xml15
-rw-r--r--lib/observer/doc/src/observer_ug.xml5
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/configure.in2
-rw-r--r--lib/odbc/doc/src/notes.xml25
-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/notes.xml17
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/ose/doc/src/notes.xml59
-rw-r--r--lib/ose/vsn.mk2
-rw-r--r--lib/otp_mibs/src/Makefile2
-rw-r--r--lib/parsetools/include/leexinc.hrl12
-rw-r--r--lib/parsetools/test/leex_SUITE.erl51
-rw-r--r--lib/percept/src/Makefile3
-rw-r--r--lib/public_key/doc/src/notes.xml16
-rw-r--r--lib/public_key/doc/src/public_key.xml49
-rw-r--r--lib/public_key/vsn.mk2
-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.xml147
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml59
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml13
-rw-r--r--lib/ssh/src/Makefile3
-rw-r--r--lib/ssh/src/ssh.app.src1
-rw-r--r--lib/ssh/src/ssh.appup.src48
-rw-r--r--lib/ssh/src/ssh.erl19
-rw-r--r--lib/ssh/src/ssh_acceptor.erl4
-rw-r--r--lib/ssh/src/ssh_acceptor_sup.erl13
-rw-r--r--lib/ssh/src/ssh_auth.erl110
-rw-r--r--lib/ssh/src/ssh_channel.erl14
-rw-r--r--lib/ssh/src/ssh_connect.hrl7
-rw-r--r--lib/ssh/src/ssh_connection.erl84
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl187
-rw-r--r--lib/ssh/src/ssh_info.erl193
-rw-r--r--lib/ssh/src/ssh_io.erl6
-rw-r--r--lib/ssh/src/ssh_message.erl18
-rw-r--r--lib/ssh/src/ssh_sftp.erl35
-rw-r--r--lib/ssh/src/ssh_system_sup.erl8
-rw-r--r--lib/ssh/src/ssh_transport.erl27
-rw-r--r--lib/ssh/src/ssh_xfer.erl8
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_client_server.erl63
-rw-r--r--lib/ssh/test/property_test/ssh_eqc_encode_decode.erl2
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl13
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl339
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl24
-rw-r--r--lib/ssh/test/ssh_test_lib.erl3
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl63
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml58
-rw-r--r--lib/ssl/doc/src/ssl.xml28
-rw-r--r--lib/ssl/src/Makefile2
-rw-r--r--lib/ssl/src/ssl.appup.src12
-rw-r--r--lib/ssl/src/ssl.erl37
-rw-r--r--lib/ssl/src/ssl_certificate.erl100
-rw-r--r--lib/ssl/src/ssl_connection.erl4
-rw-r--r--lib/ssl/src/ssl_handshake.erl11
-rw-r--r--lib/ssl/src/ssl_internal.hrl1
-rw-r--r--lib/ssl/src/tls_connection.erl16
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl8
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl197
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl8
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/doc/src/notes.xml73
-rw-r--r--lib/stdlib/doc/src/string.xml6
-rw-r--r--lib/stdlib/src/dets.erl25
-rw-r--r--lib/stdlib/src/dets_server.erl18
-rw-r--r--lib/stdlib/src/edlin.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/filelib.erl2
-rw-r--r--lib/stdlib/src/gen_event.erl48
-rw-r--r--lib/stdlib/src/otp_internal.erl14
-rw-r--r--lib/stdlib/src/stdlib.appup.src4
-rw-r--r--lib/stdlib/test/dets_SUITE.erl138
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl28
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl1
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl18
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl35
-rw-r--r--lib/syntax_tools/test/Makefile1
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl329
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/m1.erl22
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/m2.erl26
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl540
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_test.erl115
-rw-r--r--lib/test_server/src/Makefile2
-rw-r--r--lib/test_server/src/configure.in18
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/emacs/erlang-skels.el150
-rw-r--r--lib/tools/emacs/erlang.el3
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/doc/src/notes.xml17
-rw-r--r--lib/wx/examples/demo/ex_graphicsContext.erl2
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--otp_versions.table4
-rw-r--r--system/doc/efficiency_guide/processes.xml2
325 files changed, 7736 insertions, 3513 deletions
diff --git a/.gitignore b/.gitignore
index d40f49b56f..eb14036789 100644
--- a/.gitignore
+++ b/.gitignore
@@ -208,6 +208,7 @@ JAVADOC-GENERATED
# common_test
+/lib/common_test/doc/src/ct_property_test.xml
/lib/common_test/doc/src/ct_slave.xml
/lib/common_test/priv/install.sh
diff --git a/OTP_VERSION b/OTP_VERSION
index 6f1dcfcb03..9f09b58414 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-17.3-rc0
+17.4-rc0
diff --git a/configure.in b/configure.in
index 780e660f9d..008fa38632 100644
--- a/configure.in
+++ b/configure.in
@@ -262,6 +262,10 @@ AS_HELP_STRING([--with-ssl=PATH], [specify location of OpenSSL include and lib])
AS_HELP_STRING([--with-ssl], [use SSL (default)])
AS_HELP_STRING([--without-ssl], [don't use SSL]))
+AC_ARG_WITH(ssl-incl,
+AS_HELP_STRING([--with-ssl-incl=PATH],
+ [location of OpenSSL include dir, if different than specified by --with-ssl=PATH]))
+
AC_ARG_ENABLE(dynamic-ssl-lib,
AS_HELP_STRING([--disable-dynamic-ssl-lib],
[disable using dynamic openssl libraries]))
@@ -412,7 +416,7 @@ AC_SUBST(NATIVE_LIBS_ENABLED)
rm -f $ERL_TOP/lib/SKIP-APPLICATIONS
for app in `cd lib && ls -d *`; do
var=`eval echo \\$with_$app`
- if test X${var} == Xno; then
+ if test X${var} = Xno; then
echo "$app" >> $ERL_TOP/lib/SKIP-APPLICATIONS
fi
done
diff --git a/erts/configure.in b/erts/configure.in
index c8b96c50f0..9864d03cde 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2109,6 +2109,17 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite isnan isinf res_gethostbyname dlop
flockfile fstat strlcpy strlcat setsid posix2time time2posix \
setlocale nl_langinfo poll mlockall])
+AC_MSG_CHECKING([for isfinite])
+AC_TRY_LINK([#include <math.h>],
+ [isfinite(0);], have_isfinite=yes, have_isfinite=no),
+
+if test $have_isfinite = yes; then
+ AC_DEFINE(HAVE_ISFINITE,[1],
+ [Define to 1 if you have the `isfinite' function.])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
case X$erl_xcomp_posix_memalign in
Xno) ;;
@@ -3988,7 +3999,7 @@ dnl If set to --with-ssl=PATH we use that path as the prefix, i.e. we
dnl use "PATH/include" and "PATH/lib".
AC_SUBST(SSL_INCLUDE)
-AC_SUBST(SSL_ROOT)
+AC_SUBST(SSL_INCDIR)
AC_SUBST(SSL_LIBDIR)
AC_SUBST(SSL_CRYPTO_LIBNAME)
AC_SUBST(SSL_SSL_LIBNAME)
@@ -4082,6 +4093,15 @@ AS_HELP_STRING([--with-ssl=PATH], [specify location of OpenSSL include and lib])
AS_HELP_STRING([--with-ssl], [use SSL (default)])
AS_HELP_STRING([--without-ssl], [don't use SSL]))
+AC_ARG_WITH(ssl-incl,
+AS_HELP_STRING([--with-ssl-incl=PATH], [location of OpenSSL include dir, if different than specified by --with-ssl=PATH]),
+[
+case X$with_ssl in
+ X | Xyes | Xno) AC_MSG_ERROR([--with-ssl-incl=PATH set without --with-ssl=PATH]);;
+esac
+],
+[with_ssl_incl=$with_ssl]) #default
+
AC_ARG_ENABLE(dynamic-ssl-lib,
AS_HELP_STRING([--disable-dynamic-ssl-lib],
[disable using dynamic openssl libraries]),
@@ -4196,7 +4216,7 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
dir="$erl_xcomp_sysroot$rdir"
if test -f "$erl_xcomp_isysroot$rdir/include/openssl/opensslv.h"; then
is_real_ssl=yes
- SSL_ROOT="$dir"
+ SSL_INCDIR="$dir"
if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes"; then
if test -f "$dir/lib/VC/libeay32.lib"; then
SSL_RUNTIME_LIBDIR="$rdir/lib/VC"
@@ -4326,8 +4346,8 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in
# Trust OpenBSD to have everything the in the correct locations.
ssl_found=yes
ssl_linkable=yes
- SSL_ROOT="$erl_xcomp_sysroot/usr"
- AC_MSG_RESULT([$SSL_ROOT])
+ SSL_INCDIR="$erl_xcomp_sysroot/usr"
+ AC_MSG_RESULT([$SSL_INCDIR])
SSL_RUNTIME_LIB="/usr/lib"
SSL_LIB="$erl_xcomp_sysroot/usr/lib"
SSL_BINDIR="/usr/sbin"
@@ -4394,7 +4414,10 @@ dnl so it is - be adoptable
if test ! -d "$with_ssl" ; then
AC_MSG_ERROR(Invalid path to option --with-ssl=PATH)
fi
- SSL_ROOT="$with_ssl"
+ if test ! -d "$with_ssl_incl" ; then
+ AC_MSG_ERROR(Invalid path to option --with-ssl-incl=PATH)
+ fi
+ SSL_INCDIR="$with_ssl_incl"
SSL_CRYPTO_LIBNAME=crypto
SSL_SSL_LIBNAME=ssl
if test "x$MIXED_CYGWIN" = "xyes" -o "x$MIXED_MSYS" = "xyes" && test -d "$with_ssl/lib/VC"; then
@@ -4444,12 +4467,12 @@ dnl so it is - be adoptable
elif test '!' -f ${SSL_LIBDIR}/lib${SSL_CRYPTO_LIBNAME}.so -a '!' -f "$SSL_LIBDIR/lib${SSL_CRYPTO_LIBNAME}.dylib"; then
SSL_STATIC_ONLY=yes
fi
- SSL_INCLUDE="-I$with_ssl/include"
+ SSL_INCLUDE="-I$with_ssl_incl/include"
SSL_APP=ssl
CRYPTO_APP=crypto
SSH_APP=ssh
if test "$cross_compiling" = "yes"; then
- SSL_RUNTIME_LIBDIR=`echo "$SSL_LIBDIR" | sed -n "s|^$erl_xcomp_sysroot\(.*\)\$|\1|p"`
+ SSL_RUNTIME_LIBDIR=`echo "$SSL_LIBDIR" | sed -n "s|^$erl_xcomp_sysroot\(/*\)\(.*\)\$|/\2|p"`
else
SSL_RUNTIME_LIBDIR="$SSL_LIBDIR"
fi
@@ -4507,8 +4530,8 @@ if test "x$SSL_APP" != "x" ; then
SSL_KRB5_INCLUDE=
if test "x$ssl_krb5_enabled" = "xyes" ; then
AC_MSG_CHECKING(for krb5.h in standard locations)
- for dir in $extra_dir "$SSL_ROOT/include" "$SSL_ROOT/include/openssl" \
- "$SSL_ROOT/include/kerberos" \
+ for dir in $extra_dir "$SSL_INCDIR/include" "$SSL_INCDIR/include/openssl" \
+ "$SSL_INCDIR/include/kerberos" \
"$erl_xcomp_isysroot/cygdrive/c/kerberos/include" \
"$erl_xcomp_isysroot/usr/local/kerberos/include" \
"$erl_xcomp_isysroot/usr/kerberos/include" \
@@ -4805,7 +4828,7 @@ AH_BOTTOM([
#define HAVE_GETHRVTIME
#endif
-#ifndef HAVE_FINITE
+#if !defined(HAVE_ISFINITE) && !defined(HAVE_FINITE)
# if defined(HAVE_ISINF) && defined(HAVE_ISNAN)
# define USE_ISINF_ISNAN
# endif
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index d3de29b876..2b5fc877c3 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -115,8 +115,9 @@
sockets/pipes can be used simultaneously by Erlang (due to
limitations in the Unix <c><![CDATA[select]]></c> call). The number of
open regular files is not affected by this.</item>
- <item>"Received SIGUSR1" - The SIGUSR1 signal was sent to the
- Erlang machine (Unix only).</item>
+ <item>"Received SIGUSR1" - Sending the SIGUSR1 signal to a
+ Erlang machine (Unix only) forces a crash dump. This slogan reflects
+ that the Erlang machine crash-dumped due to receiving that signal.</item>
<item>"Kernel pid terminated (<em>Who</em>)
(<em>Exit-reason</em>)" - The kernel supervisor has detected
a failure, usually that the <c><![CDATA[application_controller]]></c>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index f856b9ab86..d11f6b0c6d 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -525,7 +525,8 @@
core dump and no crash dump if an internal error is detected.</p>
<p>Calling <c>erlang:halt/1</c> with a string argument will still
- produce a crash dump.</p>
+ produce a crash dump. On Unix systems, sending an emulator process
+ a SIGUSR1 signal will also force a crash dump.</p>
</item>
<tag><marker id="+e"><c><![CDATA[+e Number]]></c></marker></tag>
<item>
@@ -1141,6 +1142,23 @@
<p>For more information, see
<seealso marker="erlang#system_info_cpu_topology">erlang:system_info(cpu_topology)</seealso>.</p>
</item>
+ <tag><marker id="+secio"><c>+secio true|false</c></marker></tag>
+ <item>
+ <p>Enable or disable eager check I/O scheduling. The default
+ is currently <c>false</c>, but will most likely be changed
+ to <c>true</c> in OTP 18. The behaviour before this flag
+ was introduced corresponds to <c>+secio false</c>.</p>
+ <p>The flag effects when schedulers will check for I/O
+ operations possible to execute, and when such I/O operations
+ will execute. As the name of the parameter implies,
+ schedulers will be more eager to check for I/O when
+ <c>true</c> is passed. This however also implies that
+ execution of outstanding I/O operation will not be
+ prioritized to the same extent as when <c>false</c> is
+ passed.</p>
+ <p><seealso marker="erlang#system_info_eager_check_io"><c>erlang:system_info(eager_check_io)</c></seealso>
+ returns the value of this parameter used when starting the VM.</p>
+ </item>
<tag><marker id="+sfwi"><c>+sfwi Interval</c></marker></tag>
<item>
<p>Set scheduler forced wakeup interval. All run queues will
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index ad37813ac0..77fc906aca 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -546,6 +546,7 @@ typedef struct ErlDrvSysInfo {
int scheduler_threads;
int nif_major_version;
int nif_minor_version;
+ int dirty_scheduler_support;
} ErlDrvSysInfo;
</code>
@@ -610,6 +611,10 @@ typedef struct ErlDrvSysInfo {
<tag><c>nif_minor_version</c></tag>
<item>The value of <c>ERL_NIF_MINOR_VERSION</c> when the runtime system was compiled.
</item>
+ <tag><c>dirty_scheduler_support</c></tag>
+ <item>A value <c>!= 0</c> if the runtime system has support for dirty scheduler threads;
+ otherwise <c>0</c>.
+ </item>
</taglist>
</item>
<tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag>
@@ -2028,7 +2033,8 @@ ERL_DRV_MAP int sz
entry function is called. If <c>ready_async</c> is null in
the driver entry, the <c>async_free</c> function is called
instead.</p>
- <p>The return value is a handle to the asynchronous task.</p>
+ <p>The return value is -1 if the <c>driver_async</c> call
+ fails.</p>
<note>
<p>As of erts version 5.5.4.3 the default stack size for
threads in the async-thread pool is 16 kilowords,
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 1d33b334bb..3de94be9ff 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -374,9 +374,8 @@ ok
the dirty NIF API is available, native code can check to see if the C preprocessor macro
<c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined. Also, if the Erlang runtime was built
without threading support, dirty schedulers are disabled. To check at runtime for the presence
- of dirty scheduler threads, code can call the <seealso marker="#enif_have_dirty_schedulers"><c>
- enif_have_dirty_schedulers()</c></seealso> API function, which returns true if dirty
- scheduler threads are present, false otherwise.</p></note>
+ of dirty scheduler threads, code can use the <seealso marker="#enif_system_info"><c>
+ enif_system_info()</c></seealso> API function.</p></note>
</item>
</taglist>
</section>
@@ -807,22 +806,6 @@ typedef enum {
and return true, or return false if <c>term</c> is not an unsigned integer or is
outside the bounds of type <c>unsigned long</c>.</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_have_dirty_schedulers()</nametext></name>
- <fsummary>Runtime check for the presence of dirty scheduler threads</fsummary>
- <desc>
- <p>Check at runtime for the presence of dirty scheduler threads. If the emulator is
- built with threading support, dirty scheduler threads are available and
- <c>enif_have_dirty_schedulers()</c> returns true. If the emulator was built without
- threading support, <c>enif_have_dirty_schedulers()</c> returns false.</p>
- <p>If dirty scheduler threads are not available in the emulator, a call to
- <c>enif_schedule_nif</c> with its <c>flags</c> argument set to indicate that the specified
- NIF is to be executed on a dirty scheduler thread results in a <c>badarg</c> exception.</p>
- <note><p>This function is available only when the emulator is configured with dirty
- schedulers enabled. This feature is currently disabled by default. To determine whether
- the dirty NIF API is available, native code can check to see if the C preprocessor macro
- <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined.</p></note>
- </desc>
- </func>
<func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name>
<fsummary>Inspect the content of a binary</fsummary>
<desc><p>Initialize the structure pointed to by <c>bin</c> with
@@ -1261,7 +1244,9 @@ typedef enum {
<p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the
experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>
if the job is expected to be primarily CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
- be I/O-bound.</p>
+ be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job
+ will result in a <c>badarg</c> exception.</p>
+
<p>The <c>argc</c> and <c>argv</c> arguments can either be the originals passed into the calling NIF, or
they can be values created by the calling NIF.</p>
<p>The calling NIF must use the return value of <c>enif_schedule_nif</c> as its own return value.</p>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 84168397f6..111756407f 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -5775,6 +5775,7 @@ ok
<name name="system_info" arity="1" clause_i="52"/>
<name name="system_info" arity="1" clause_i="53"/>
<name name="system_info" arity="1" clause_i="54"/>
+ <name name="system_info" arity="1" clause_i="55"/>
<fsummary>Information about the system</fsummary>
<desc>
<p>Returns various information about the current system
@@ -5970,6 +5971,16 @@ ok
The return value will always be <c>false</c> since
the elib_malloc allocator has been removed.</p>
</item>
+ <tag><marker id="system_info_eager_check_io"><c>eager_check_io</c></marker></tag>
+ <item>
+ <p>
+ Returns the value of the <c>erl</c>
+ <seealso marker="erl#+secio">+secio</seealso> command line
+ flag which is either <c>true</c> or <c>false</c>. See the
+ documentation of the command line flag for information about
+ the different values.
+ </p>
+ </item>
<tag><c>ets_limit</c></tag>
<item>
<p>Returns the maximum number of ETS tables allowed. This limit
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 5c4bb3ed25..743369951f 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -30,6 +30,176 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ General documentation updates.</p>
+ <p>
+ Own Id: OTP-12052</p>
+ </item>
+ <item>
+ <p>A bug in the VM code implementing sending of signals
+ to ports could cause the receiving port queue to remain
+ in a busy state forever. When this state had been
+ reached, processes sending command signals to the port
+ either got suspended forever, or, if the <c>nosuspend</c>
+ feature was used, always failed to send to the port. This
+ bug was introduced in ERTS version 5.10.</p>
+ <p>In order for this bug to be triggered on a port, one
+ had to at least once utilize the <c>nosuspend</c>
+ functionality when passing a signal to the port. This by
+ either calling</p> <list> <item> <seealso
+ marker="erlang#port_command/3"><c>port_command(Port,
+ Data, [nosuspend | Options])</c></seealso>, </item>
+ <item> <seealso
+ marker="erlang#send/3"><c>erlang:send(Port, {PortOwner,
+ {command, Data}}, [nosuspend | Options])</c></seealso>,
+ </item> <item> <seealso
+ marker="erlang#send_nosuspend/2"><c>erlang:send_nosuspend(Port,
+ {PortOwner, {command, Data}})</c></seealso>, or </item>
+ <item> <seealso
+ marker="erlang#send_nosuspend/3"><c>erlang:send_nosuspend(Port,
+ {PortOwner, {command, Data}}, Options)</c></seealso>.
+ </item> </list>
+ <p>Thanks Vasily Demidenok for reporting the issue, and
+ Sergey Kudryashov for providing a testcase.</p>
+ <p>
+ Own Id: OTP-12082 Aux Id: OTP-10336 </p>
+ </item>
+ <item>
+ <p>
+ Fix size overflow bug at memory allocation. A memory
+ allocation call, with an insane size close to the entire
+ address space, could return successfully as if it had
+ allocated just a few bytes. (Thanks to Don A. Bailey for
+ reporting)</p>
+ <p>
+ Own Id: OTP-12091</p>
+ </item>
+ <item>
+ <p>
+ Fix various issues where negating a signed integer would
+ trigger undefined behaviour. This fixes issues in the
+ enif_make_int64 interface and some edge cases inside the
+ erlang runtime system.</p>
+ <p>
+ Own Id: OTP-12097</p>
+ </item>
+ <item>
+ <p>
+ The documentation erroneously listed the <seealso
+ marker="erl#+swct"><c>+swct</c></seealso> command line
+ argument under <c>+sws</c>.</p>
+ <p>
+ Own Id: OTP-12102 Aux Id: OTP-10994 </p>
+ </item>
+ <item>
+ <p>
+ Profiling messages could be delivered out of order when
+ profiling on <c>runnable_procs</c> and/or
+ <c>runnable_ports</c> using <seealso
+ marker="erlang#system_profile/2"><c>erlang:system_profile/2</c></seealso>.
+ This bug was introduced in ERTS version 5.10.</p>
+ <p>
+ Own Id: OTP-12105 Aux Id: OTP-10336 </p>
+ </item>
+ <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>
+ <item>
+ <p>
+ Fix compiler warnings reported by LLVM</p>
+ <p>
+ Own Id: OTP-12138</p>
+ </item>
+ <item>
+ <p>
+ Correct conversion of <c>MIN_SMALL</c> by
+ <c>list_to_integer/1</c> and <c>binary_to_integer/1</c>.
+ The bug produced an unnormalized bignum which can cause
+ strange behavior such as comparing different to a correct
+ <c>MIN_SMALL</c> integer. The value <c>MIN_SMALL</c> is
+ <c>-(1 bsl 27) = -134217728</c> on a 32-bit VM and <c>-(1
+ bsl 59) = -576460752303423488</c> on a 64-bit VM. (Thanks
+ to Jesper Louis Andersen, Mikael Pettersson and Anthony
+ Ramine for report, patch and optimization suggestion)</p>
+ <p>
+ Own Id: OTP-12140</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in <c>term_to_binary</c> that reallocates binary
+ with inconsistent size information. Bug has never been
+ confirmed to be the cause of any faulty behavior.</p>
+ <p>
+ Own Id: OTP-12141</p>
+ </item>
+ <item>
+ <p>
+ Real_path method used while prim loading archive files
+ was not taking into account the fact that windows
+ directory symlinks can be across different drives.</p>
+ <p>
+ Own Id: OTP-12155</p>
+ </item>
+ </list>
+ </section>
+
+
+ <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>
+ <item>
+ <p>
+ Introduced <seealso
+ marker="erl_nif#enif_schedule_nif"><c>enif_schedule_nif()</c></seealso>
+ to the NIF API.</p>
+ <p>
+ The <c>enif_schedule_nif()</c> function allows a
+ long-running NIF to be broken into separate NIF
+ invocations without the help of a wrapper function
+ written in Erlang. The NIF first executes part of the
+ long-running task, then calls <c>enif_schedule_nif()</c>
+ to schedule a NIF for later execution to continue the
+ task. Any number of NIFs can be scheduled in this manner,
+ one after another. Since the emulator regains control
+ between invocations, this helps avoid problems caused by
+ native code tying up scheduler threads for too long.</p>
+ <p>
+ The <c>enif_schedule_nif()</c> function also replaces the
+ <c>enif_schedule_dirty_nif()</c> in the experimental
+ dirty NIF API. Note that the only incompatible changes
+ made are in the experimental dirty NIF API.</p>
+ <p>
+ See the <seealso marker="erl_nif">NIF
+ documentation</seealso> for more information.</p>
+ <p>
+ Thanks to Steve Vinoski.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-12128</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 6.1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/beam_ranges.c b/erts/emulator/beam/beam_ranges.c
index 0f2d5d0c2a..cb6470638f 100644
--- a/erts/emulator/beam/beam_ranges.c
+++ b/erts/emulator/beam/beam_ranges.c
@@ -282,7 +282,7 @@ find_range(BeamInstr* pc)
while (low < high) {
if (pc < mid->start) {
high = mid;
- } else if (pc > RANGE_END(mid)) {
+ } else if (pc >= RANGE_END(mid)) {
low = mid + 1;
} else {
erts_smp_atomic_set_nob(&r[active].mid, (erts_aint_t) mid);
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 37354b7f8d..21434eb117 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -267,7 +267,6 @@ type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q
type PROC_INTERVAL LONG_LIVED SYSTEM process_interval
type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table
type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller
-type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap
type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task
type PROC_SYS_TSK_QS SHORT_LIVED PROCESSES proc_sys_task_queues
@@ -364,6 +363,7 @@ type NLINK_SH STANDARD_LOW PROCESSES nlink_sh
type AINFO_REQ STANDARD_LOW SYSTEM alloc_info_request
type SCHED_WTIME_REQ STANDARD_LOW SYSTEM sched_wall_time_request
type GC_INFO_REQ STANDARD_LOW SYSTEM gc_info_request
+type PORT_DATA_HEAP STANDARD_LOW SYSTEM port_data_heap
+else # "fullword"
@@ -383,6 +383,7 @@ type NLINK_SH FIXED_SIZE PROCESSES nlink_sh
type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request
type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request
type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request
+type PORT_DATA_HEAP STANDARD SYSTEM port_data_heap
+endif
@@ -397,6 +398,7 @@ type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state
type DRV_EV_D_STATE FIXED_SIZE SYSTEM driver_event_data_state
type DRV_SEL_D_STATE FIXED_SIZE SYSTEM driver_select_data_state
type FD_LIST SHORT_LIVED SYSTEM fd_list
+type ACTIVE_FD_ARR SHORT_LIVED SYSTEM active_fd_array
type POLLSET LONG_LIVED SYSTEM pollset
type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req
type POLL_FDS LONG_LIVED SYSTEM poll_fds
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index a4e164bf51..55052430e1 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -1775,6 +1775,18 @@ handle_delayed_dealloc(Allctr_t *allctr,
* data has been overwritten by the queue.
*/
Carrier_t *crr = FIRST_BLK_TO_MBC(allctr, blk);
+
+ /* Restore word overwritten by the dd-queue as it will be read
+ * if this carrier is pulled from dc_list by cpool_fetch()
+ */
+ ERTS_ALC_CPOOL_ASSERT(FBLK_TO_MBC(blk) != crr);
+ ERTS_ALC_CPOOL_ASSERT(sizeof(ErtsAllctrDDBlock_t) == sizeof(void*));
+#ifdef MBC_ABLK_OFFSET_BITS
+ blk->u.carrier = crr;
+#else
+ blk->carrier = crr;
+#endif
+
ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 6efe9d9550..61e4469600 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -2696,6 +2696,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
? am_disabled
: am_enabled);
}
+ else if (ERTS_IS_ATOM_STR("eager_check_io",BIF_ARG_1)) {
+ BIF_RET(erts_eager_check_io ? am_true : am_false);
+ }
BIF_ERROR(BIF_P, BADARG);
}
@@ -3304,17 +3307,38 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
BIF_RET(make_small((Uint) words));
}
else if (ERTS_IS_ATOM_STR("check_io_debug", BIF_ARG_1)) {
- /* Used by (emulator) */
- int res;
+ /* Used by driver_SUITE (emulator) */
+ Uint sz, *szp;
+ Eterm res, *hp, **hpp;
+ int no_errors;
+ ErtsCheckIoDebugInfo ciodi = {0};
#ifdef HAVE_ERTS_CHECK_IO_DEBUG
erts_smp_proc_unlock(BIF_P,ERTS_PROC_LOCK_MAIN);
- res = erts_check_io_debug();
+ no_errors = erts_check_io_debug(&ciodi);
erts_smp_proc_lock(BIF_P,ERTS_PROC_LOCK_MAIN);
#else
- res = 0;
+ no_errors = 0;
#endif
- ASSERT(res >= 0);
- BIF_RET(erts_make_integer((Uint) res, BIF_P));
+ sz = 0;
+ szp = &sz;
+ hpp = NULL;
+ while (1) {
+ res = erts_bld_tuple(hpp, szp, 4,
+ erts_bld_uint(hpp, szp,
+ (Uint) no_errors),
+ erts_bld_uint(hpp, szp,
+ (Uint) ciodi.no_used_fds),
+ erts_bld_uint(hpp, szp,
+ (Uint) ciodi.no_driver_select_structs),
+ erts_bld_uint(hpp, szp,
+ (Uint) ciodi.no_driver_event_structs));
+ if (hpp)
+ break;
+ hp = HAlloc(BIF_P, sz);
+ szp = NULL;
+ hpp = &hp;
+ }
+ BIF_RET(res);
}
else if (ERTS_IS_ATOM_STR("process_info_args", BIF_ARG_1)) {
/* Used by process_SUITE (emulator) */
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index afb33c1cdb..64bd598ba6 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -493,8 +493,8 @@ void
erts_cleanup_port_data(Port *prt)
{
ASSERT(erts_atomic32_read_nob(&prt->state) & ERTS_PORT_SFLGS_INVALID_LOOKUP);
- cleanup_old_port_data(erts_smp_atomic_read_nob(&prt->data));
- erts_smp_atomic_set_nob(&prt->data, (erts_aint_t) THE_NON_VALUE);
+ cleanup_old_port_data(erts_smp_atomic_xchg_nob(&prt->data,
+ (erts_aint_t) NULL));
}
Uint
@@ -554,6 +554,7 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2)
hp = &pdhp->heap[0];
pdhp->off_heap.first = NULL;
pdhp->off_heap.overhead = 0;
+ pdhp->hsize = hsize;
pdhp->data = copy_struct(BIF_ARG_2, hsize, &hp, &pdhp->off_heap);
data = (erts_aint_t) pdhp;
ASSERT((data & 0x3) == 0);
@@ -561,8 +562,14 @@ BIF_RETTYPE port_set_data_2(BIF_ALIST_2)
data = erts_smp_atomic_xchg_wb(&prt->data, data);
+ if (data == (erts_aint_t)NULL) {
+ /* Port terminated by racing thread */
+ data = erts_smp_atomic_xchg_wb(&prt->data, data);
+ ASSERT(data != (erts_aint_t)NULL);
+ cleanup_old_port_data(data);
+ BIF_ERROR(BIF_P, BADARG);
+ }
cleanup_old_port_data(data);
-
BIF_RET(am_true);
}
@@ -581,6 +588,8 @@ BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
data = erts_smp_atomic_read_ddrb(&prt->data);
+ if (data == (erts_aint_t)NULL)
+ BIF_ERROR(BIF_P, BADARG); /* Port terminated by racing thread */
if ((data & 0x3) != 0) {
res = (Eterm) (UWord) data;
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 5ced8c5ca0..f9938fc66c 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -133,7 +133,7 @@ typedef struct {
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 3
-#define ERL_DRV_EXTENDED_MINOR_VERSION 0
+#define ERL_DRV_EXTENDED_MINOR_VERSION 1
/*
* The emulator will refuse to load a driver with a major version
diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h
index 3f829ea7ea..4e8c6dc68b 100644
--- a/erts/emulator/beam/erl_drv_nif.h
+++ b/erts/emulator/beam/erl_drv_nif.h
@@ -35,6 +35,7 @@ typedef struct {
int scheduler_threads;
int nif_major_version;
int nif_minor_version;
+ int dirty_scheduler_support;
} ErlDrvSysInfo;
typedef struct {
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 88c4006934..61f8385efc 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -548,6 +548,8 @@ void erts_usage(void)
erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
erts_fprintf(stderr, "-sct cput set cpu topology,\n");
erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
+ erts_fprintf(stderr, "-secio bool enable/disable eager check I/O scheduling,\n");
+ erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
#if ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT
erts_fprintf(stderr, "-sub bool enable/disable scheduler utilization balancing,\n");
#else
@@ -1674,6 +1676,22 @@ erl_start(int argc, char **argv)
erts_usage();
}
}
+ else if (has_prefix("ecio", sub_param)) {
+ arg = get_arg(sub_param+4, argv[i+1], &i);
+#ifndef __OSE__
+ if (sys_strcmp("true", arg) == 0)
+ erts_eager_check_io = 1;
+ else
+#endif
+ if (sys_strcmp("false", arg) == 0)
+ erts_eager_check_io = 0;
+ else {
+ erts_fprintf(stderr,
+ "bad schedule eager check I/O value '%s'\n",
+ arg);
+ erts_usage();
+ }
+ }
else if (has_prefix("pp", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
if (sys_strcmp(arg, "true") == 0)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 44914d3681..ede5f335dc 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1901,16 +1901,6 @@ enif_is_on_dirty_scheduler(ErlNifEnv* env)
return ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data);
}
-int
-enif_have_dirty_schedulers()
-{
-#ifdef USE_THREADS
- return 1;
-#else
- return 0;
-#endif
-}
-
#endif /* ERL_NIF_DIRTY_SCHEDULER_SUPPORT */
/* Maps */
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index 226fc199a1..849024453c 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -241,21 +241,10 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
# else
# define ERL_NIF_INIT_DECL(MODNAME) __declspec(dllexport) ErlNifEntry* nif_init(TWinDynNifCallbacks* callbacks)
# endif
-# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-# define ERL_NIF_INIT_BODY do { \
- memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks)); \
- entry.options = ERL_NIF_DIRTY_NIF_OPTION; \
- } while(0)
-# else
-# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks))
-# endif
+# define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks))
#else
# define ERL_NIF_INIT_GLOB
-# ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-# define ERL_NIF_INIT_BODY entry.options = ERL_NIF_DIRTY_NIF_OPTION
-# else
-# define ERL_NIF_INIT_BODY
-# endif
+# define ERL_NIF_INIT_BODY
# ifdef STATIC_ERLANG_NIF
# define ERL_NIF_INIT_DECL(MODNAME) ErlNifEntry* MODNAME ## _nif_init(void)
# else
@@ -263,6 +252,11 @@ extern TWinDynNifCallbacks WinDynNifCallbacks;
# endif
#endif
+#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+# define ERL_NIF_ENTRY_OPTIONS ERL_NIF_DIRTY_NIF_OPTION
+#else
+# define ERL_NIF_ENTRY_OPTIONS 0
+#endif
#ifdef __cplusplus
}
@@ -288,7 +282,8 @@ ERL_NIF_INIT_DECL(NAME) \
sizeof(FUNCS) / sizeof(*FUNCS), \
FUNCS, \
LOAD, RELOAD, UPGRADE, UNLOAD, \
- ERL_NIF_VM_VARIANT \
+ ERL_NIF_VM_VARIANT, \
+ ERL_NIF_ENTRY_OPTIONS \
}; \
ERL_NIF_INIT_BODY; \
return &entry; \
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index be39816a64..630cefae93 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -22,7 +22,7 @@
#endif
/*
-** WARNING: add new ERL_NIF_API_FUNC_DECL entries at the bottom of the list
+** WARNING: Add new ERL_NIF_API_FUNC_DECL entries at the bottom of the list
** to keep compatibility on Windows!!!
**
** And don't forget to increase ERL_NIF_MINOR_VERSION in erl_nif.h
@@ -141,12 +141,6 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_number,(ErlNifEnv*, ERL_NIF_TERM term));
ERL_NIF_API_FUNC_DECL(void*,enif_dlopen,(const char* lib, void (*err_handler)(void*,const char*), void* err_arg));
ERL_NIF_API_FUNC_DECL(void*,enif_dlsym,(void* handle, const char* symbol, void (*err_handler)(void*,const char*), void* err_arg));
ERL_NIF_API_FUNC_DECL(int,enif_consume_timeslice,(ErlNifEnv*, int percent));
-ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[]));
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
-ERL_NIF_API_FUNC_DECL(int,enif_have_dirty_schedulers,(void));
-#endif
-
ERL_NIF_API_FUNC_DECL(int, enif_is_map, (ErlNifEnv* env, ERL_NIF_TERM term));
ERL_NIF_API_FUNC_DECL(int, enif_get_map_size, (ErlNifEnv* env, ERL_NIF_TERM term, size_t *size));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_make_new_map, (ErlNifEnv* env));
@@ -161,12 +155,22 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_is_tail, (ErlNifEnv *env, ErlNifMap
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIterator *iter));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value));
-
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[]));
/*
-** Add new entries here to keep compatibility on Windows!!!
+** ADD NEW ENTRIES HERE (before this comment) !!!
*/
+
+
+/*
+ * Conditional EXPERIMENTAL stuff always last.
+ * Must be moved up and made unconditional to support binary backward
+ * compatibility on Windows.
+ */
+#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
#endif
+#endif /* ERL_NIF_API_FUNC_DECL */
/*
** Please keep the ERL_NIF_API_FUNC_MACRO list below in the same order
@@ -280,19 +284,12 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa
# define enif_make_int64 ERL_NIF_API_FUNC_MACRO(enif_make_int64)
# define enif_make_uint64 ERL_NIF_API_FUNC_MACRO(enif_make_uint64)
#endif
-
# define enif_is_exception ERL_NIF_API_FUNC_MACRO(enif_is_exception)
# define enif_make_reverse_list ERL_NIF_API_FUNC_MACRO(enif_make_reverse_list)
# define enif_is_number ERL_NIF_API_FUNC_MACRO(enif_is_number)
# define enif_dlopen ERL_NIF_API_FUNC_MACRO(enif_dlopen)
# define enif_dlsym ERL_NIF_API_FUNC_MACRO(enif_dlsym)
# define enif_consume_timeslice ERL_NIF_API_FUNC_MACRO(enif_consume_timeslice)
-# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif)
-#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
-# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler)
-# define enif_have_dirty_schedulers ERL_NIF_API_FUNC_MACRO(enif_have_dirty_schedulers)
-#endif
-
# define enif_is_map ERL_NIF_API_FUNC_MACRO(enif_is_map)
# define enif_get_map_size ERL_NIF_API_FUNC_MACRO(enif_get_map_size)
# define enif_make_new_map ERL_NIF_API_FUNC_MACRO(enif_make_new_map)
@@ -307,11 +304,21 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMa
# define enif_map_iterator_next ERL_NIF_API_FUNC_MACRO(enif_map_iterator_next)
# define enif_map_iterator_prev ERL_NIF_API_FUNC_MACRO(enif_map_iterator_prev)
# define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair)
+# define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif)
/*
-** Add new entries here
+** ADD NEW ENTRIES HERE (before this comment)
*/
+
+/*
+ * Conditional EXPERIMENTAL stuff always last
+ * Must be moved up and made unconditional to support binary backward
+ * compatibility on Windows.
+ */
+#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler)
#endif
+#endif /* ERL_NIF_API_FUNC_MACRO */
#if defined(__GNUC__) && !(defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index 682f6f8f4b..2aa0a27197 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -32,6 +32,7 @@
#include "global.h"
#include "erl_port_task.h"
#include "dist.h"
+#include "erl_check_io.h"
#include "dtrace-wrapper.h"
#include <stdarg.h>
@@ -550,6 +551,16 @@ reset_handle(ErtsPortTask *ptp)
}
static ERTS_INLINE void
+reset_executed_io_task_handle(ErtsPortTask *ptp)
+{
+ if (ptp->u.alive.handle) {
+ ASSERT(ptp == handle2task(ptp->u.alive.handle));
+ erts_io_notify_port_task_executed(ptp->u.alive.handle);
+ reset_port_task_handle(ptp->u.alive.handle);
+ }
+}
+
+static ERTS_INLINE void
set_handle(ErtsPortTask *ptp, ErtsPortTaskHandle *pthp)
{
ptp->u.alive.handle = pthp;
@@ -1396,10 +1407,7 @@ erts_port_task_schedule(Eterm id,
erts_aint32_t act, add_flags;
unsigned int prof_runnable_ports;
- if (pthp && erts_port_task_is_scheduled(pthp)) {
- ASSERT(0);
- erts_port_task_abort(pthp);
- }
+ ERTS_LC_ASSERT(!pthp || !erts_port_task_is_scheduled(pthp));
ASSERT(is_internal_port(id));
@@ -1699,8 +1707,6 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
goto aborted_port_task;
}
- reset_handle(ptp);
-
if (erts_system_monitor_long_schedule != 0) {
start_time = erts_timestamp_millis();
}
@@ -1711,6 +1717,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
switch (ptp->type) {
case ERTS_PORT_TASK_TIMEOUT:
+ reset_handle(ptp);
reds = ERTS_PORT_REDS_TIMEOUT;
if (!(state & ERTS_PORT_SFLGS_DEAD)) {
DTRACE_DRIVER(driver_timeout, pp);
@@ -1725,6 +1732,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
for input and output */
(*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event);
+ reset_executed_io_task_handle(ptp);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_OUTPUT:
@@ -1733,6 +1741,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
DTRACE_DRIVER(driver_ready_output, pp);
(*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event);
+ reset_executed_io_task_handle(ptp);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_EVENT:
@@ -1742,10 +1751,12 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
(*pp->drv_ptr->event)((ErlDrvData) pp->drv_data,
ptp->u.alive.td.io.event,
ptp->u.alive.td.io.event_data);
+ reset_executed_io_task_handle(ptp);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_PROC_SIG: {
ErtsProc2PortSigData *sigdp = &ptp->u.alive.td.psig.data;
+ reset_handle(ptp);
ASSERT((state & ERTS_PORT_SFLGS_DEAD) == 0);
if (!pp->sched.taskq.bpq)
reds = ptp->u.alive.td.psig.callback(pp,
@@ -1763,6 +1774,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
break;
}
case ERTS_PORT_TASK_DIST_CMD:
+ reset_handle(ptp);
reds = erts_dist_command(pp, CONTEXT_REDS - pp->reds);
break;
default:
diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h
index 9ef0cfcedc..406cd3c492 100644
--- a/erts/emulator/beam/erl_port_task.h
+++ b/erts/emulator/beam/erl_port_task.h
@@ -156,7 +156,7 @@ erts_port_task_handle_init(ErtsPortTaskHandle *pthp)
ERTS_GLB_INLINE int
erts_port_task_is_scheduled(ErtsPortTaskHandle *pthp)
{
- return ((void *) erts_smp_atomic_read_nob(pthp)) != NULL;
+ return ((void *) erts_smp_atomic_read_acqb(pthp)) != NULL;
}
ERTS_GLB_INLINE void erts_port_task_pre_init_sched(ErtsPortTaskSched *ptsp,
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 20a88ec581..e5bb1203c8 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -148,6 +148,12 @@ extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
+#ifdef __OSE__
+/* Eager check I/O not supported on OSE yet. */
+int erts_eager_check_io = 0;
+#else
+int erts_eager_check_io = 0;
+#endif
int erts_sched_compact_load;
int erts_sched_balance_util = 0;
Uint erts_no_schedulers;
@@ -2381,29 +2387,47 @@ try_set_sys_scheduling(void)
#endif
static ERTS_INLINE int
-prepare_for_sys_schedule(ErtsSchedulerData *esdp)
+prepare_for_sys_schedule(ErtsSchedulerData *esdp, int non_blocking)
{
+ if (non_blocking && erts_eager_check_io) {
#ifdef ERTS_SMP
- while (!erts_port_task_have_outstanding_io_tasks()
- && try_set_sys_scheduling()) {
#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1
- if (esdp->no != 1) {
- /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used
- then we make sure to wake scheduler 1 */
- ErtsRunQueue *rq = ERTS_RUNQ_IX(0);
- clear_sys_scheduling();
- wake_scheduler(rq);
- return 0;
- }
+ if (esdp->no != 1) {
+ /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used
+ then we make sure to wake scheduler 1 */
+ ErtsRunQueue *rq = ERTS_RUNQ_IX(0);
+ wake_scheduler(rq);
+ return 0;
+ }
#endif
- if (!erts_port_task_have_outstanding_io_tasks())
+ return try_set_sys_scheduling();
+#else
return 1;
- clear_sys_scheduling();
+#endif
}
- return 0;
+ else {
+#ifdef ERTS_SMP
+ while (!erts_port_task_have_outstanding_io_tasks()
+ && try_set_sys_scheduling()) {
+#ifdef ERTS_SCHED_ONLY_POLL_SCHED_1
+ if (esdp->no != 1) {
+ /* If we are not scheduler 1 and ERTS_SCHED_ONLY_POLL_SCHED_1 is used
+ then we make sure to wake scheduler 1 */
+ ErtsRunQueue *rq = ERTS_RUNQ_IX(0);
+ clear_sys_scheduling();
+ wake_scheduler(rq);
+ return 0;
+ }
+#endif
+ if (!erts_port_task_have_outstanding_io_tasks())
+ return 1;
+ clear_sys_scheduling();
+ }
+ return 0;
#else
- return !erts_port_task_have_outstanding_io_tasks();
+ return !erts_port_task_have_outstanding_io_tasks();
#endif
+ }
}
#ifdef ERTS_SMP
@@ -2780,7 +2804,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* be waiting in erl_sys_schedule()
*/
- if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(esdp)) {
+ if (ERTS_SCHEDULER_IS_DIRTY(esdp) || !prepare_for_sys_schedule(esdp, 0)) {
sched_waiting(esdp->no, rq);
@@ -2944,7 +2968,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* Got to check that we still got I/O tasks; otherwise
* we have to continue checking for I/O...
*/
- if (!prepare_for_sys_schedule(esdp)) {
+ if (!prepare_for_sys_schedule(esdp, 0)) {
spincount *= ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT;
goto tse_wait;
}
@@ -2966,7 +2990,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq)
* Got to check that we still got I/O tasks; otherwise
* we have to wait in erl_sys_schedule() after all...
*/
- if (!prepare_for_sys_schedule(esdp)) {
+ if (!prepare_for_sys_schedule(esdp, 0)) {
/*
* Not allowed to wait in erl_sys_schedule;
* do tse wait instead...
@@ -9200,7 +9224,7 @@ Process *schedule(Process *p, int calls)
}
else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) &&
(fcalls > input_reductions &&
- prepare_for_sys_schedule(esdp))) {
+ prepare_for_sys_schedule(esdp, !0))) {
/*
* Schedule system-level activities.
*/
@@ -9208,8 +9232,6 @@ Process *schedule(Process *p, int calls)
erts_smp_atomic32_set_relb(&function_calls, 0);
fcalls = 0;
- ASSERT(!erts_port_task_have_outstanding_io_tasks());
-
#if 0 /* Not needed since we wont wait in sys schedule */
erts_sys_schedule_interrupt(0);
#endif
@@ -9241,7 +9263,9 @@ Process *schedule(Process *p, int calls)
if (RUNQ_READ_LEN(&rq->ports.info.len)) {
int have_outstanding_io;
have_outstanding_io = erts_port_task_execute(rq, &esdp->current_port);
- if ((have_outstanding_io && fcalls > 2*input_reductions)
+ if ((!erts_eager_check_io
+ && have_outstanding_io
+ && fcalls > 2*input_reductions)
|| rq->halt_in_progress) {
/*
* If we have performed more than 2*INPUT_REDUCTIONS since
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 3b0798207e..27a3a3553b 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -104,6 +104,7 @@ struct saved_calls {
};
extern Export exp_send, exp_receive, exp_timeout;
+extern int erts_eager_check_io;
extern int erts_sched_compact_load;
extern int erts_sched_balance_util;
extern Uint erts_no_schedulers;
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index ae053fc191..9ae973e108 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -7274,6 +7274,18 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size)
sip->nif_major_version = ERL_NIF_MAJOR_VERSION;
sip->nif_minor_version = ERL_NIF_MINOR_VERSION;
}
+ /*
+ * 'dirty_scheduler_support' is the last field in the 4th version
+ * (driver version 3.1, NIF version 2.7)
+ */
+ if (si_size >= ERL_DRV_SYS_INFO_SIZE(dirty_scheduler_support)) {
+#if defined(ERL_NIF_DIRTY_SCHEDULER_SUPPORT) && defined(USE_THREADS)
+ sip->dirty_scheduler_support = 1;
+#else
+ sip->dirty_scheduler_support = 0;
+#endif
+ }
+
}
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 3d8dd9c6d0..c29d4b3777 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -66,8 +66,12 @@
*/
#ifndef ERTS_SYS_FD_TYPE
+#define ERTS_SYS_FD_INVALID ((ErtsSysFdType) -1)
typedef int ErtsSysFdType;
#else
+#ifndef ERTS_SYS_FD_INVALID
+# error missing ERTS_SYS_FD_INVALID
+#endif
typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
#endif
@@ -501,7 +505,7 @@ extern volatile int erts_writing_erl_crash_dump;
# define NO_ERF
# define NO_ERFC
/* This definition doesn't take NaN into account, but matherr() gets those */
-# define finite(x) (fabs(x) != HUGE_VAL)
+# define isfinite(x) (fabs(x) != HUGE_VAL)
# define USE_MATHERR
# define HAVE_FINITE
#endif
@@ -744,6 +748,14 @@ void init_getenv_state(GETENV_STATE *);
char * getenv_string(GETENV_STATE *);
void fini_getenv_state(GETENV_STATE *);
+#define HAVE_ERTS_CHECK_IO_DEBUG
+typedef struct {
+ int no_used_fds;
+ int no_driver_select_structs;
+ int no_driver_event_structs;
+} ErtsCheckIoDebugInfo;
+int erts_check_io_debug(ErtsCheckIoDebugInfo *ip);
+
/* xxxP */
#define SYS_DEFAULT_FLOAT_DECIMALS 20
void init_sys_float(void);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 55f9e68e78..f20e6e5665 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -48,6 +48,7 @@
#include "erl_sched_spec_pre_alloc.h"
#include "beam_bp.h"
#include "erl_ptab.h"
+#include "erl_check_io.h"
#undef M_TRIM_THRESHOLD
#undef M_TOP_PAD
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index a321bb9641..7e4043fc1b 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -1288,6 +1288,10 @@ do_fileinfo(Efile_call_state* state, Efile_info* pInfo,
{
HANDLE handle; /* Handle returned by CreateFile() */
BY_HANDLE_FILE_INFORMATION fileInfo; /* from CreateFile() */
+
+ /* We initialise nNumberOfLinks as GetFileInformationByHandle
+ does not always initialise this field */
+ fileInfo.nNumberOfLinks = 1;
if (handle = CreateFileW(name, GENERIC_READ, FILE_SHARE_FLAGS, NULL,
OPEN_EXISTING, 0, NULL)) {
GetFileInformationByHandle(handle, &fileInfo);
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index 0de69a617f..a3219c7586 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -39,7 +39,10 @@ define(HANDLE_GOT_MBUF,`
jmp 2b')
`#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
-# define CALL_BIF(F) movq $CSYM(F), P_BIF_CALLEE(P); call CSYM(hipe_debug_bif_wrapper)
+# define CALL_BIF(F) \
+ movq CSYM(F)@GOTPCREL(%rip), %r11; \
+ movq %r11, P_BIF_CALLEE(P); \
+ call CSYM(hipe_debug_bif_wrapper)
#else
# define CALL_BIF(F) call CSYM(F)
#endif'
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 2497d51df1..c9eee2acf2 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -1022,7 +1022,7 @@ BIF_RETTYPE hipe_conv_big_to_float(BIF_ALIST_1)
*/
void hipe_emulate_fpe(Process* p)
{
- if (!finite(p->hipe.float_result)) {
+ if (!isfinite(p->hipe.float_result)) {
p->fp_exception = 1;
}
}
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 4ddc2790b1..1ae1d17b7f 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -2,7 +2,7 @@
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2013. All Rights Reserved.
+ * Copyright Ericsson AB 2001-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
@@ -187,6 +187,9 @@ void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure)
void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
{
+ if (!hipe_bifcall_from_native_is_recursive(p))
+ return;
+
/* ensure that at least 2 words are available on the BEAM stack */
if ((p->stop - 2) < p->htop) {
DPRINTF("calling gc to reserve BEAM stack size");
@@ -195,25 +198,26 @@ void hipe_reserve_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
}
p->stop -= 2;
p->stop[0] = NIL;
- p->stop[1] = NIL;
+ p->stop[1] = hipe_beam_catch_throw;
}
static __inline__ void
hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
{
- if (p->flags & F_DISABLE_GC) {
+ if (&p->stop[1] < p->hend && p->stop[1] == hipe_beam_catch_throw) {
/* Trap frame already reserved */
- ASSERT(p->stop[0] == NIL && p->stop[1] == NIL);
+ ASSERT(p->stop[0] == NIL);
}
else {
+ ASSERT(!(p->flags & F_DISABLE_GC));
if ((p->stop - 2) < p->htop) {
DPRINTF("calling gc to increase BEAM stack size");
p->fcalls -= erts_garbage_collect(p, 2, reg, arity);
ASSERT(!((p->stop - 2) < p->htop));
}
p->stop -= 2;
+ p->stop[1] = hipe_beam_catch_throw;
}
- p->stop[1] = hipe_beam_catch_throw;
p->stop[0] = make_cp(p->cp);
++p->catches;
p->cp = hipe_beam_pc_return;
@@ -221,12 +225,16 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
void hipe_unreserve_beam_trap_frame(Process *p)
{
- ASSERT(p->stop[0] == NIL && p->stop[1] == NIL);
+ if (!hipe_bifcall_from_native_is_recursive(p))
+ return;
+
+ ASSERT(p->stop[0] == NIL && p->stop[1] == hipe_beam_catch_throw);
p->stop += 2;
}
static __inline__ void hipe_pop_beam_trap_frame(Process *p)
{
+ ASSERT(p->stop[1] == hipe_beam_catch_throw);
p->cp = cp_val(p->stop[0]);
--p->catches;
p->stop += 2;
diff --git a/erts/emulator/hipe/hipe_risc_glue.h b/erts/emulator/hipe/hipe_risc_glue.h
index cc2671c016..dbb7086dae 100644
--- a/erts/emulator/hipe/hipe_risc_glue.h
+++ b/erts/emulator/hipe/hipe_risc_glue.h
@@ -214,6 +214,14 @@ hipe_trap_from_native_is_recursive(Process *p)
return 0;
}
+/* Native called BIF. Is it a recursive call?
+ i.e should we return back to native when BIF is done? */
+static __inline__ int
+hipe_bifcall_from_native_is_recursive(Process *p)
+{
+ return (p->hipe.nra != (void(*)(void))&nbif_return);
+}
+
/* Native makes a call which needs to unload the parameters.
This differs from hipe_call_from_native_is_recursive() in
diff --git a/erts/emulator/hipe/hipe_x86_glue.h b/erts/emulator/hipe/hipe_x86_glue.h
index 63ad250d60..4b6e495b9a 100644
--- a/erts/emulator/hipe/hipe_x86_glue.h
+++ b/erts/emulator/hipe/hipe_x86_glue.h
@@ -207,6 +207,14 @@ hipe_trap_from_native_is_recursive(Process *p)
return 0;
}
+/* Native called BIF. Is it a recursive call?
+ i.e should we return back to native when BIF is done? */
+static __inline__ int
+hipe_bifcall_from_native_is_recursive(Process *p)
+{
+ return (*p->hipe.nsp != (Eterm)nbif_return);
+}
+
/* Native makes a call which needs to unload the parameters.
This differs from hipe_call_from_native_is_recursive() in
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 1db673e7f3..81cb5dc4bb 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -52,8 +52,17 @@ typedef char EventStateType;
#define ERTS_EV_TYPE_STOP_USE ((EventStateType) 3) /* pending stop_select */
typedef char EventStateFlags;
-#define ERTS_EV_FLAG_USED ((EventStateFlags) 1) /* ERL_DRV_USE has been turned on */
+#define ERTS_EV_FLAG_USED ((EventStateFlags) 1) /* ERL_DRV_USE has been turned on */
+#define ERTS_EV_FLAG_DEFER_IN_EV ((EventStateFlags) 2)
+#define ERTS_EV_FLAG_DEFER_OUT_EV ((EventStateFlags) 4)
+#ifdef DEBUG
+# define ERTS_ACTIVE_FD_INC 2
+#else
+# define ERTS_ACTIVE_FD_INC 128
+#endif
+
+#define ERTS_CHECK_IO_POLL_RES_LEN 512
#if defined(ERTS_KERNEL_POLL_VERSION)
# define ERTS_CIO_EXPORT(FUNC) FUNC ## _kp
@@ -67,6 +76,7 @@ typedef char EventStateFlags;
(ERTS_POLL_USE_POLL && !ERTS_POLL_USE_KERNEL_POLL)
#define ERTS_CIO_POLL_CTL ERTS_POLL_EXPORT(erts_poll_control)
+#define ERTS_CIO_POLL_CTLV ERTS_POLL_EXPORT(erts_poll_controlv)
#define ERTS_CIO_POLL_WAIT ERTS_POLL_EXPORT(erts_poll_wait)
#ifdef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
#define ERTS_CIO_POLL_AS_INTR ERTS_POLL_EXPORT(erts_poll_async_sig_interrupt)
@@ -85,6 +95,13 @@ static struct pollset_info
{
ErtsPollSet ps;
erts_smp_atomic_t in_poll_wait; /* set while doing poll */
+ struct {
+ int six; /* start index */
+ int eix; /* end index */
+ erts_smp_atomic32_t no;
+ int size;
+ ErtsSysFdType *array;
+ } active_fd;
#ifdef ERTS_SMP
struct removed_fd* removed_list; /* list of deselected fd's*/
erts_smp_spinlock_t removed_list_lock;
@@ -97,9 +114,11 @@ typedef struct {
SafeHashBucket hb;
#endif
ErtsSysFdType fd;
- union {
- ErtsDrvEventDataState *event; /* ERTS_EV_TYPE_DRV_EV */
+ struct {
ErtsDrvSelectDataState *select; /* ERTS_EV_TYPE_DRV_SEL */
+#if ERTS_CIO_HAVE_DRV_EVENT
+ ErtsDrvEventDataState *event; /* ERTS_EV_TYPE_DRV_EV */
+#endif
erts_driver_t* drv_ptr; /* ERTS_EV_TYPE_STOP_USE */
} driver;
ErtsPollEvents events;
@@ -169,6 +188,10 @@ static ERTS_INLINE ErtsDrvEventState* hash_new_drv_ev_state(ErtsSysFdType fd)
ErtsDrvEventState tmpl;
tmpl.fd = fd;
tmpl.driver.select = NULL;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ tmpl.driver.event = NULL;
+#endif
+ tmpl.driver.drv_ptr = NULL;
tmpl.events = 0;
tmpl.remove_cnt = 0;
tmpl.type = ERTS_EV_TYPE_NONE;
@@ -209,6 +232,65 @@ ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(removed_fd, struct removed_fd, 64, ERTS_ALC_T_F
#endif
static ERTS_INLINE void
+init_iotask(ErtsIoTask *io_task)
+{
+ erts_port_task_handle_init(&io_task->task);
+ erts_smp_atomic_init_nob(&io_task->executed_time, ~((erts_aint_t) 0));
+}
+
+static ERTS_INLINE int
+is_iotask_active(ErtsIoTask *io_task, erts_aint_t current_cio_time)
+{
+ if (erts_port_task_is_scheduled(&io_task->task))
+ return 1;
+ if (erts_smp_atomic_read_nob(&io_task->executed_time) == current_cio_time)
+ return 1;
+ return 0;
+}
+
+static ERTS_INLINE ErtsDrvSelectDataState *
+alloc_drv_select_data(void)
+{
+ ErtsDrvSelectDataState *dsp = erts_alloc(ERTS_ALC_T_DRV_SEL_D_STATE,
+ sizeof(ErtsDrvSelectDataState));
+ dsp->inport = NIL;
+ dsp->outport = NIL;
+ init_iotask(&dsp->iniotask);
+ init_iotask(&dsp->outiotask);
+ return dsp;
+}
+
+static ERTS_INLINE void
+free_drv_select_data(ErtsDrvSelectDataState *dsp)
+{
+ ASSERT(!erts_port_task_is_scheduled(&dsp->iniotask.task));
+ ASSERT(!erts_port_task_is_scheduled(&dsp->outiotask.task));
+ erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, dsp);
+}
+
+static ERTS_INLINE ErtsDrvEventDataState *
+alloc_drv_event_data(void)
+{
+ ErtsDrvEventDataState *dep = erts_alloc(ERTS_ALC_T_DRV_EV_D_STATE,
+ sizeof(ErtsDrvEventDataState));
+ dep->port = NIL;
+ dep->data = NULL;
+ dep->removed_events = 0;
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ dep->deferred_events = 0;
+#endif
+ init_iotask(&dep->iotask);
+ return dep;
+}
+
+static ERTS_INLINE void
+free_drv_event_data(ErtsDrvEventDataState *dep)
+{
+ ASSERT(!erts_port_task_is_scheduled(&dep->iotask.task));
+ erts_free(ERTS_ALC_T_DRV_EV_D_STATE, dep);
+}
+
+static ERTS_INLINE void
remember_removed(ErtsDrvEventState *state, struct pollset_info* psi)
{
#ifdef ERTS_SMP
@@ -288,7 +370,7 @@ forget_removed(struct pollset_info* psi)
drv_ptr = state->driver.drv_ptr;
ASSERT(drv_ptr);
state->type = ERTS_EV_TYPE_NONE;
- state->flags = 0;
+ state->flags &= ~ERTS_EV_FLAG_USED;
state->driver.drv_ptr = NULL;
/* Fall through */
case ERTS_EV_TYPE_NONE:
@@ -345,6 +427,10 @@ grow_drv_ev_state(int min_ix)
for (i = erts_smp_atomic_read_nob(&drv_ev_state_len); i < new_len; i++) {
drv_ev_state[i].fd = (ErtsSysFdType) i;
drv_ev_state[i].driver.select = NULL;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ drv_ev_state[i].driver.event = NULL;
+#endif
+ drv_ev_state[i].driver.drv_ptr = NULL;
drv_ev_state[i].events = 0;
drv_ev_state[i].remove_cnt = 0;
drv_ev_state[i].type = ERTS_EV_TYPE_NONE;
@@ -365,11 +451,7 @@ grow_drv_ev_state(int min_ix)
static ERTS_INLINE void
abort_task(Eterm id, ErtsPortTaskHandle *pthp, EventStateType type)
{
- if (is_nil(id)) {
- ASSERT(type == ERTS_EV_TYPE_NONE
- || !erts_port_task_is_scheduled(pthp));
- }
- else if (erts_port_task_is_scheduled(pthp)) {
+ if (is_not_nil(id) && erts_port_task_is_scheduled(pthp)) {
erts_port_task_abort(pthp);
ASSERT(erts_is_port_alive(id));
}
@@ -384,7 +466,7 @@ abort_tasks(ErtsDrvEventState *state, int mode)
#if ERTS_CIO_HAVE_DRV_EVENT
case ERTS_EV_TYPE_DRV_EV:
abort_task(state->driver.event->port,
- &state->driver.event->task,
+ &state->driver.event->iotask.task,
ERTS_EV_TYPE_DRV_EV);
return;
#endif
@@ -398,14 +480,14 @@ abort_tasks(ErtsDrvEventState *state, int mode)
case ERL_DRV_WRITE:
ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL);
abort_task(state->driver.select->outport,
- &state->driver.select->outtask,
+ &state->driver.select->outiotask.task,
state->type);
if (mode == ERL_DRV_WRITE)
break;
case ERL_DRV_READ:
ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL);
abort_task(state->driver.select->inport,
- &state->driver.select->intask,
+ &state->driver.select->iniotask.task,
state->type);
break;
default:
@@ -443,16 +525,14 @@ deselect(ErtsDrvEventState *state, int mode)
if (!(state->events)) {
switch (state->type) {
case ERTS_EV_TYPE_DRV_SEL:
- ASSERT(!erts_port_task_is_scheduled(&state->driver.select->intask));
- ASSERT(!erts_port_task_is_scheduled(&state->driver.select->outtask));
- erts_free(ERTS_ALC_T_DRV_SEL_D_STATE,
- state->driver.select);
+ state->driver.select->inport = NIL;
+ state->driver.select->outport = NIL;
break;
#if ERTS_CIO_HAVE_DRV_EVENT
case ERTS_EV_TYPE_DRV_EV:
- ASSERT(!erts_port_task_is_scheduled(&state->driver.event->task));
- erts_free(ERTS_ALC_T_DRV_EV_D_STATE,
- state->driver.event);
+ state->driver.event->port = NIL;
+ state->driver.event->data = NULL;
+ state->driver.event->removed_events = (ErtsPollEvents) 0;
break;
#endif
case ERTS_EV_TYPE_NONE:
@@ -462,20 +542,297 @@ deselect(ErtsDrvEventState *state, int mode)
break;
}
- state->driver.select = NULL;
state->type = ERTS_EV_TYPE_NONE;
- state->flags = 0;
+ state->flags &= ~ERTS_EV_FLAG_USED;
remember_removed(state, &pollset);
}
}
-
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
# define IS_FD_UNKNOWN(state) ((state)->type == ERTS_EV_TYPE_NONE && (state)->remove_cnt == 0)
#else
# define IS_FD_UNKNOWN(state) ((state) == NULL)
#endif
+static ERTS_INLINE void
+check_fd_cleanup(ErtsDrvEventState *state,
+#if ERTS_CIO_HAVE_DRV_EVENT
+ ErtsDrvEventDataState **free_event,
+#endif
+ ErtsDrvSelectDataState **free_select)
+{
+ erts_aint_t current_cio_time;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_mtx_is_locked(fd_mtx(state->fd)));
+
+ current_cio_time = erts_smp_atomic_read_acqb(&erts_check_io_time);
+ *free_select = NULL;
+ if (state->driver.select
+ && (state->type != ERTS_EV_TYPE_DRV_SEL)
+ && !is_iotask_active(&state->driver.select->iniotask, current_cio_time)
+ && !is_iotask_active(&state->driver.select->outiotask, current_cio_time)) {
+
+ *free_select = state->driver.select;
+ state->driver.select = NULL;
+ }
+
+#if ERTS_CIO_HAVE_DRV_EVENT
+ *free_event = NULL;
+ if (state->driver.event
+ && (state->type != ERTS_EV_TYPE_DRV_EV)
+ && !is_iotask_active(&state->driver.event->iotask, current_cio_time)) {
+
+ *free_event = state->driver.event;
+ state->driver.event = NULL;
+ }
+#endif
+
+#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
+ if (((state->type != ERTS_EV_TYPE_NONE)
+ | state->remove_cnt
+#if ERTS_CIO_HAVE_DRV_EVENT
+ | (state->driver.event != NULL)
+#endif
+ | (state->driver.select != NULL)) == 0) {
+
+ hash_erase_drv_ev_state(state);
+
+ }
+#endif
+}
+
+static ERTS_INLINE int
+check_cleanup_active_fd(ErtsSysFdType fd,
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ ErtsPollControlEntry *pce,
+ int *pce_ix,
+#endif
+ erts_aint_t current_cio_time)
+{
+ ErtsDrvEventState *state;
+ int active = 0;
+ erts_smp_mtx_t *mtx = fd_mtx(fd);
+ void *free_select = NULL;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ void *free_event = NULL;
+#endif
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ ErtsPollEvents evon = 0, evoff = 0;
+#endif
+
+ erts_smp_mtx_lock(mtx);
+
+#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
+ state = &drv_ev_state[(int) fd];
+#else
+ state = hash_get_drv_ev_state(fd); /* may be NULL! */
+ if (state)
+#endif
+ {
+ if (state->driver.select) {
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ if (is_iotask_active(&state->driver.select->iniotask, current_cio_time)) {
+ active = 1;
+ if ((state->events & ERTS_POLL_EV_IN)
+ && !(state->flags & ERTS_EV_FLAG_DEFER_IN_EV)) {
+ evoff |= ERTS_POLL_EV_IN;
+ state->flags |= ERTS_EV_FLAG_DEFER_IN_EV;
+ }
+ }
+ else if (state->flags & ERTS_EV_FLAG_DEFER_IN_EV) {
+ if (state->events & ERTS_POLL_EV_IN)
+ evon |= ERTS_POLL_EV_IN;
+ state->flags &= ~ERTS_EV_FLAG_DEFER_IN_EV;
+ }
+ if (is_iotask_active(&state->driver.select->outiotask, current_cio_time)) {
+ active = 1;
+ if ((state->events & ERTS_POLL_EV_OUT)
+ && !(state->flags & ERTS_EV_FLAG_DEFER_OUT_EV)) {
+ evoff |= ERTS_POLL_EV_OUT;
+ state->flags |= ERTS_EV_FLAG_DEFER_OUT_EV;
+ }
+ }
+ else if (state->flags & ERTS_EV_FLAG_DEFER_OUT_EV) {
+ if (state->events & ERTS_POLL_EV_OUT)
+ evon |= ERTS_POLL_EV_OUT;
+ state->flags &= ~ERTS_EV_FLAG_DEFER_OUT_EV;
+ }
+ if (active)
+ (void) 0;
+ else
+#else
+ if (is_iotask_active(&state->driver.select->iniotask, current_cio_time)
+ || is_iotask_active(&state->driver.select->outiotask, current_cio_time))
+ active = 1;
+ else
+#endif
+ if (state->type != ERTS_EV_TYPE_DRV_SEL) {
+ free_select = state->driver.select;
+ state->driver.select = NULL;
+ }
+ }
+
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (state->driver.event) {
+ if (is_iotask_active(&state->driver.event->iotask, current_cio_time)) {
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ ErtsPollEvents evs = state->events & ~state->driver.event->deferred_events;
+ if (evs) {
+ evoff |= evs;
+ state->driver.event->deferred_events |= evs;
+ }
+#endif
+ active = 1;
+ }
+ else if (state->type != ERTS_EV_TYPE_DRV_EV) {
+ free_event = state->driver.event;
+ state->driver.event = NULL;
+ }
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ else {
+ ErtsPollEvents evs = state->events & state->driver.event->deferred_events;
+ if (evs) {
+ evon |= evs;
+ state->driver.event->deferred_events = 0;
+ }
+ }
+#endif
+
+ }
+#endif
+
+#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
+ if (((state->type != ERTS_EV_TYPE_NONE) | state->remove_cnt | active) == 0)
+ hash_erase_drv_ev_state(state);
+#endif
+
+ }
+
+ erts_smp_mtx_unlock(mtx);
+
+ if (free_select)
+ free_drv_select_data(free_select);
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (free_event)
+ free_drv_event_data(free_event);
+#endif
+
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ if (evoff) {
+ ErtsPollControlEntry *pcep = &pce[(*pce_ix)++];
+ pcep->fd = fd;
+ pcep->events = evoff;
+ pcep->on = 0;
+ }
+ if (evon) {
+ ErtsPollControlEntry *pcep = &pce[(*pce_ix)++];
+ pcep->fd = fd;
+ pcep->events = evon;
+ pcep->on = 1;
+ }
+#endif
+
+ return active;
+}
+
+static void
+check_cleanup_active_fds(erts_aint_t current_cio_time)
+{
+ int six = pollset.active_fd.six;
+ int eix = pollset.active_fd.eix;
+ erts_aint32_t no = erts_smp_atomic32_read_dirty(&pollset.active_fd.no);
+ int size = pollset.active_fd.size;
+ int ix = six;
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ /* every fd might add two entries */
+ Uint pce_sz = 2*sizeof(ErtsPollControlEntry)*no;
+ ErtsPollControlEntry *pctrl_entries = (pce_sz
+ ? erts_alloc(ERTS_ALC_T_TMP, pce_sz)
+ : NULL);
+ int pctrl_ix = 0;
+#endif
+
+ while (ix != eix) {
+ ErtsSysFdType fd = pollset.active_fd.array[ix];
+ int nix = ix + 1;
+ if (nix >= size)
+ nix = 0;
+ ASSERT(fd != ERTS_SYS_FD_INVALID);
+ if (!check_cleanup_active_fd(fd,
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ pctrl_entries,
+ &pctrl_ix,
+#endif
+ current_cio_time)) {
+ no--;
+ if (ix == six) {
+#ifdef DEBUG
+ pollset.active_fd.array[ix] = ERTS_SYS_FD_INVALID;
+#endif
+ six = nix;
+ }
+ else {
+ pollset.active_fd.array[ix] = pollset.active_fd.array[six];
+#ifdef DEBUG
+ pollset.active_fd.array[six] = ERTS_SYS_FD_INVALID;
+#endif
+ six++;
+ if (six >= size)
+ six = 0;
+ }
+ }
+ ix = nix;
+ }
+
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ ASSERT(pctrl_ix <= pce_sz/sizeof(ErtsPollControlEntry));
+ if (pctrl_ix)
+ ERTS_CIO_POLL_CTLV(pollset.ps, pctrl_entries, pctrl_ix);
+ if (pctrl_entries)
+ erts_free(ERTS_ALC_T_TMP, pctrl_entries);
+#endif
+
+ pollset.active_fd.six = six;
+ pollset.active_fd.eix = eix;
+ erts_smp_atomic32_set_relb(&pollset.active_fd.no, no);
+}
+
+static ERTS_INLINE void
+add_active_fd(ErtsSysFdType fd)
+{
+ int eix = pollset.active_fd.eix;
+ int size = pollset.active_fd.size;
+
+
+ pollset.active_fd.array[eix] = fd;
+
+ erts_smp_atomic32_set_relb(&pollset.active_fd.no,
+ (erts_smp_atomic32_read_dirty(&pollset.active_fd.no)
+ + 1));
+
+ eix++;
+ if (eix >= size)
+ eix = 0;
+ if (pollset.active_fd.six == eix) {
+ pollset.active_fd.six = 0;
+ eix = size;
+ size += ERTS_ACTIVE_FD_INC;
+ pollset.active_fd.array = erts_realloc(ERTS_ALC_T_ACTIVE_FD_ARR,
+ pollset.active_fd.array,
+ sizeof(ErtsSysFdType)*size);
+ pollset.active_fd.size = size;
+#ifdef DEBUG
+ {
+ int i;
+ for (i = eix + 1; i < size; i++)
+ pollset.active_fd.array[i] = ERTS_SYS_FD_INVALID;
+ }
+#endif
+
+ }
+
+ pollset.active_fd.eix = eix;
+}
int
ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
@@ -492,6 +849,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
ErtsDrvEventState *state;
int wake_poller;
int ret;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ ErtsDrvEventDataState *free_event = NULL;
+#endif
+ ErtsDrvSelectDataState *free_select = NULL;
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(name, 64);
#endif
@@ -593,9 +954,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) {
if (state->type == ERTS_EV_TYPE_DRV_SEL && !state->events) {
state->type = ERTS_EV_TYPE_NONE;
- state->flags = 0;
- erts_free(ERTS_ALC_T_DRV_SEL_D_STATE, state->driver.select);
- state->driver.select = NULL;
+ state->flags &= ~ERTS_EV_FLAG_USED;
+ state->driver.select->inport = NIL;
+ state->driver.select->outport = NIL;
}
ret = -1;
goto done;
@@ -613,18 +974,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
state->events = new_events;
if (ctl_events) {
if (on) {
- if (state->type == ERTS_EV_TYPE_NONE) {
- ErtsDrvSelectDataState *dsdsp
- = erts_alloc(ERTS_ALC_T_DRV_SEL_D_STATE,
- sizeof(ErtsDrvSelectDataState));
- dsdsp->inport = NIL;
- dsdsp->outport = NIL;
- erts_port_task_handle_init(&dsdsp->intask);
- erts_port_task_handle_init(&dsdsp->outtask);
- ASSERT(state->driver.select == NULL);
- state->driver.select = dsdsp;
+ if (!state->driver.select)
+ state->driver.select = alloc_drv_select_data();
+ if (state->type == ERTS_EV_TYPE_NONE)
state->type = ERTS_EV_TYPE_DRV_SEL;
- }
ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL);
if (ctl_events & ERTS_POLL_EV_IN)
state->driver.select->inport = id;
@@ -645,17 +998,12 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
state->driver.select->outport = NIL;
}
if (new_events == 0) {
- ASSERT(!erts_port_task_is_scheduled(&state->driver.select->intask));
- ASSERT(!erts_port_task_is_scheduled(&state->driver.select->outtask));
if (old_events != 0) {
remember_removed(state, &pollset);
}
if ((mode & ERL_DRV_USE) || !(state->flags & ERTS_EV_FLAG_USED)) {
state->type = ERTS_EV_TYPE_NONE;
- state->flags = 0;
- erts_free(ERTS_ALC_T_DRV_SEL_D_STATE,
- state->driver.select);
- state->driver.select = NULL;
+ state->flags &= ~ERTS_EV_FLAG_USED;
}
/*else keep it, as fd will probably be selected upon again */
}
@@ -686,13 +1034,15 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
ret = 0;
-done:;
-#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
- if (state->type == ERTS_EV_TYPE_NONE && state->remove_cnt == 0) {
- hash_erase_drv_ev_state(state);
- }
+done:
+
+ check_fd_cleanup(state,
+#if ERTS_CIO_HAVE_DRV_EVENT
+ &free_event,
#endif
-done_unknown:
+ &free_select);
+
+done_unknown:
erts_smp_mtx_unlock(fd_mtx(fd));
if (stop_select_fn) {
int was_unmasked = erts_block_fpe();
@@ -700,6 +1050,12 @@ done_unknown:
(*stop_select_fn)(e, NULL);
erts_unblock_fpe(was_unmasked);
}
+ if (free_select)
+ free_drv_select_data(free_select);
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (free_event)
+ free_drv_event_data(free_event);
+#endif
return ret;
}
@@ -719,6 +1075,10 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,
ErtsDrvEventState *state;
int do_wake = 0;
int ret;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ ErtsDrvEventDataState *free_event;
+#endif
+ ErtsDrvSelectDataState *free_select;
Port *prt = erts_drvport2port(ix);
if (prt == ERTS_INVALID_ERL_DRV_PORT)
@@ -799,10 +1159,8 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,
state->driver.event->removed_events |= remove_events;
}
else {
- state->driver.event
- = erts_alloc(ERTS_ALC_T_DRV_EV_D_STATE,
- sizeof(ErtsDrvEventDataState));
- erts_port_task_handle_init(&state->driver.event->task);
+ if (!state->driver.event)
+ state->driver.event = alloc_drv_event_data();
state->driver.event->port = id;
state->driver.event->removed_events = (ErtsPollEvents) 0;
state->type = ERTS_EV_TYPE_DRV_EV;
@@ -812,10 +1170,10 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,
else {
if (state->type == ERTS_EV_TYPE_DRV_EV) {
abort_tasks(state, 0);
- erts_free(ERTS_ALC_T_DRV_EV_D_STATE,
- state->driver.event);
+ state->driver.event->port = NIL;
+ state->driver.event->data = NULL;
+ state->driver.event->removed_events = (ErtsPollEvents) 0;
}
- state->driver.select = NULL;
state->type = ERTS_EV_TYPE_NONE;
remember_removed(state, &pollset);
}
@@ -825,12 +1183,22 @@ ERTS_CIO_EXPORT(driver_event)(ErlDrvPort ix,
ret = 0;
done:
-#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
- if (state->type == ERTS_EV_TYPE_NONE && state->remove_cnt == 0) {
- hash_erase_drv_ev_state(state);
- }
+
+ check_fd_cleanup(state,
+#if ERTS_CIO_HAVE_DRV_EVENT
+ &free_event,
#endif
+ &free_select);
+
erts_smp_mtx_unlock(fd_mtx(fd));
+
+ if (free_select)
+ free_drv_select_data(free_select);
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (free_event)
+ free_drv_event_data(free_event);
+#endif
+
return ret;
#endif
}
@@ -1027,7 +1395,7 @@ steal_pending_stop_select(erts_dsprintf_buf_t *dsbufp, ErlDrvPort ix,
* In either case stop_select should not be called.
*/
state->type = ERTS_EV_TYPE_NONE;
- state->flags = 0;
+ state->flags &= ~ERTS_EV_FLAG_USED;
if (state->driver.drv_ptr->handle) {
erts_ddll_dereference_driver(state->driver.drv_ptr->handle);
}
@@ -1099,38 +1467,103 @@ event_large_fd_error(ErlDrvPort ix, ErtsSysFdType fd, ErlDrvEventData event_data
#endif
#endif
+static ERTS_INLINE int
+io_task_schedule_allowed(ErtsDrvEventState *state,
+ ErtsPortTaskType type,
+ erts_aint_t current_cio_time)
+{
+ ErtsIoTask *io_task;
+
+ switch (type) {
+ case ERTS_PORT_TASK_INPUT:
+ if (!state->driver.select)
+ return 0;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (state->driver.event)
+ return 0;
+#endif
+ io_task = &state->driver.select->iniotask;
+ break;
+ case ERTS_PORT_TASK_OUTPUT:
+ if (!state->driver.select)
+ return 0;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (state->driver.event)
+ return 0;
+#endif
+ io_task = &state->driver.select->outiotask;
+ break;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ case ERTS_PORT_TASK_EVENT:
+ if (!state->driver.event)
+ return 0;
+ if (state->driver.select)
+ return 0;
+ io_task = &state->driver.event->iotask;
+ break;
+#endif
+ default:
+ ERTS_INTERNAL_ERROR("Invalid I/O-task type");
+ return 0;
+ }
+
+ return !is_iotask_active(io_task, current_cio_time);
+}
+
static ERTS_INLINE void
-iready(Eterm id, ErtsDrvEventState *state)
+iready(Eterm id, ErtsDrvEventState *state, erts_aint_t current_cio_time)
{
- if (erts_port_task_schedule(id,
- &state->driver.select->intask,
- ERTS_PORT_TASK_INPUT,
- (ErlDrvEvent) state->fd) != 0) {
- stale_drv_select(id, state, ERL_DRV_READ);
+ if (io_task_schedule_allowed(state,
+ ERTS_PORT_TASK_INPUT,
+ current_cio_time)) {
+ ErtsIoTask *iotask = &state->driver.select->iniotask;
+ erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time);
+ if (erts_port_task_schedule(id,
+ &iotask->task,
+ ERTS_PORT_TASK_INPUT,
+ (ErlDrvEvent) state->fd) != 0) {
+ stale_drv_select(id, state, ERL_DRV_READ);
+ }
+ add_active_fd(state->fd);
}
}
static ERTS_INLINE void
-oready(Eterm id, ErtsDrvEventState *state)
+oready(Eterm id, ErtsDrvEventState *state, erts_aint_t current_cio_time)
{
- if (erts_port_task_schedule(id,
- &state->driver.select->outtask,
- ERTS_PORT_TASK_OUTPUT,
- (ErlDrvEvent) state->fd) != 0) {
- stale_drv_select(id, state, ERL_DRV_WRITE);
+ if (io_task_schedule_allowed(state,
+ ERTS_PORT_TASK_OUTPUT,
+ current_cio_time)) {
+ ErtsIoTask *iotask = &state->driver.select->outiotask;
+ erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time);
+ if (erts_port_task_schedule(id,
+ &iotask->task,
+ ERTS_PORT_TASK_OUTPUT,
+ (ErlDrvEvent) state->fd) != 0) {
+ stale_drv_select(id, state, ERL_DRV_WRITE);
+ }
+ add_active_fd(state->fd);
}
}
#if ERTS_CIO_HAVE_DRV_EVENT
static ERTS_INLINE void
-eready(Eterm id, ErtsDrvEventState *state, ErlDrvEventData event_data)
+eready(Eterm id, ErtsDrvEventState *state, ErlDrvEventData event_data,
+ erts_aint_t current_cio_time)
{
- if (erts_port_task_schedule(id,
- &state->driver.event->task,
- ERTS_PORT_TASK_EVENT,
- (ErlDrvEvent) state->fd,
- event_data) != 0) {
- stale_drv_select(id, state, 0);
+ if (io_task_schedule_allowed(state,
+ ERTS_PORT_TASK_EVENT,
+ current_cio_time)) {
+ ErtsIoTask *iotask = &state->driver.event->iotask;
+ erts_smp_atomic_set_nob(&iotask->executed_time, current_cio_time);
+ if (erts_port_task_schedule(id,
+ &iotask->task,
+ ERTS_PORT_TASK_EVENT,
+ (ErlDrvEvent) state->fd,
+ event_data) != 0) {
+ stale_drv_select(id, state, 0);
+ }
+ add_active_fd(state->fd);
}
}
#endif
@@ -1161,10 +1594,11 @@ ERTS_CIO_EXPORT(erts_check_io_interrupt_timed)(int set,
void
ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
{
- ErtsPollResFd pollres[256];
+ ErtsPollResFd *pollres;
int pollres_len;
SysTimeval wait_time;
int poll_ret, i;
+ erts_aint_t current_cio_time;
restart:
@@ -1181,10 +1615,24 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
wait_time.tv_usec = 0;
}
+ /*
+ * No need for an atomic inc op when incrementing
+ * erts_check_io_time, since only one thread can
+ * check io at a time.
+ */
+ current_cio_time = erts_smp_atomic_read_dirty(&erts_check_io_time);
+ current_cio_time++;
+ erts_smp_atomic_set_relb(&erts_check_io_time, current_cio_time);
+
+ check_cleanup_active_fds(current_cio_time);
+
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_check_exact(NULL, 0); /* No locks should be locked */
#endif
- pollres_len = sizeof(pollres)/sizeof(ErtsPollResFd);
+
+ pollres_len = erts_smp_atomic32_read_dirty(&pollset.active_fd.no) + ERTS_CHECK_IO_POLL_RES_LEN;
+
+ pollres = erts_alloc(ERTS_ALC_T_TMP, sizeof(ErtsPollResFd)*pollres_len);
erts_smp_atomic_set_nob(&pollset.in_poll_wait, 1);
@@ -1204,6 +1652,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
if (poll_ret != 0) {
erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0);
forget_removed(&pollset);
+ erts_free(ERTS_ALC_T_TMP, pollres);
if (poll_ret == EAGAIN) {
goto restart;
}
@@ -1263,15 +1712,15 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
if ((revents & ERTS_POLL_EV_IN)
|| (!(revents & ERTS_POLL_EV_OUT)
&& state->events & ERTS_POLL_EV_IN)) {
- iready(state->driver.select->inport, state);
+ iready(state->driver.select->inport, state, current_cio_time);
}
else if (state->events & ERTS_POLL_EV_OUT) {
- oready(state->driver.select->outport, state);
+ oready(state->driver.select->outport, state, current_cio_time);
}
}
else if (revents & (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) {
if (revents & ERTS_POLL_EV_OUT) {
- oready(state->driver.select->outport, state);
+ oready(state->driver.select->outport, state, current_cio_time);
}
/* Someone might have deselected input since revents
was read (true also on the non-smp emulator since
@@ -1279,7 +1728,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
revents... */
revents &= ~(~state->events & ERTS_POLL_EV_IN);
if (revents & ERTS_POLL_EV_IN) {
- iready(state->driver.select->inport, state);
+ iready(state->driver.select->inport, state, current_cio_time);
}
}
else if (revents & ERTS_POLL_EV_NVAL) {
@@ -1287,6 +1736,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
state->driver.select->inport,
state->driver.select->outport,
state->events);
+ add_active_fd(state->fd);
}
break;
}
@@ -1304,8 +1754,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
if (revents) {
event_data->events = state->events;
event_data->revents = revents;
-
- eready(state->driver.event->port, state, event_data);
+ eready(state->driver.event->port, state, event_data, current_cio_time);
}
break;
}
@@ -1323,6 +1772,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
(int) state->type);
ASSERT(0);
deselect(state, 0);
+ add_active_fd(state->fd);
break;
}
}
@@ -1334,6 +1784,7 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait)
}
erts_smp_atomic_set_nob(&pollset.in_poll_wait, 0);
+ erts_free(ERTS_ALC_T_TMP, pollres);
forget_removed(&pollset);
}
@@ -1469,10 +1920,27 @@ static void drv_ev_state_free(void *des)
void
ERTS_CIO_EXPORT(erts_init_check_io)(void)
{
+ erts_smp_atomic_init_nob(&erts_check_io_time, 0);
erts_smp_atomic_init_nob(&pollset.in_poll_wait, 0);
+
ERTS_CIO_POLL_INIT();
pollset.ps = ERTS_CIO_NEW_POLLSET();
+ pollset.active_fd.six = 0;
+ pollset.active_fd.eix = 0;
+ erts_smp_atomic32_init_nob(&pollset.active_fd.no, 0);
+ pollset.active_fd.size = ERTS_ACTIVE_FD_INC;
+ pollset.active_fd.array = erts_alloc(ERTS_ALC_T_ACTIVE_FD_ARR,
+ sizeof(ErtsSysFdType)*ERTS_ACTIVE_FD_INC);
+#ifdef DEBUG
+ {
+ int i;
+ for (i = 0; i < ERTS_ACTIVE_FD_INC; i++)
+ pollset.active_fd.array[i] = ERTS_SYS_FD_INVALID;
+ }
+#endif
+
+
#ifdef ERTS_SMP
init_removed_fd_alloc();
pollset.removed_list = NULL;
@@ -1548,12 +2016,27 @@ Eterm
ERTS_CIO_EXPORT(erts_check_io_info)(void *proc)
{
Process *p = (Process *) proc;
- Eterm tags[15], values[15], res;
+ Eterm tags[16], values[16], res;
Uint sz, *szp, *hp, **hpp, memory_size;
Sint i;
ErtsPollInfo pi;
-
- ERTS_CIO_POLL_INFO(pollset.ps, &pi);
+ erts_aint_t cio_time = erts_smp_atomic_read_acqb(&erts_check_io_time);
+ int active_fds = (int) erts_smp_atomic32_read_acqb(&pollset.active_fd.no);
+
+ while (1) {
+ erts_aint_t post_cio_time;
+ int post_active_fds;
+
+ ERTS_CIO_POLL_INFO(pollset.ps, &pi);
+
+ post_cio_time = erts_smp_atomic_read_mb(&erts_check_io_time);
+ post_active_fds = (int) erts_smp_atomic32_read_acqb(&pollset.active_fd.no);
+ if (cio_time == post_cio_time && active_fds == post_active_fds)
+ break;
+ cio_time = post_cio_time;
+ active_fds = post_active_fds;
+ }
+
memory_size = pi.memory_size;
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
memory_size += sizeof(ErtsDrvEventState) * erts_smp_atomic_read_nob(&drv_ev_state_len);
@@ -1617,6 +2100,9 @@ ERTS_CIO_EXPORT(erts_check_io_info)(void *proc)
tags[i] = erts_bld_atom(hpp, szp, "max_fds");
values[i++] = erts_bld_uint(hpp, szp, (Uint) pi.max_fds);
+ tags[i] = erts_bld_atom(hpp, szp, "active_fds");
+ values[i++] = erts_bld_uint(hpp, szp, (Uint) active_fds);
+
#ifdef ERTS_POLL_COUNT_AVOIDED_WAKEUPS
tags[i] = erts_bld_atom(hpp, szp, "no_avoided_wakeups");
values[i++] = erts_bld_uint(hpp, szp, (Uint) pi.no_avoided_wakeups);
@@ -1671,6 +2157,8 @@ print_events(ErtsPollEvents ev)
typedef struct {
int used_fds;
int num_errors;
+ int no_driver_select_structs;
+ int no_driver_event_structs;
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
int internal_fds;
ErtsPollEvents *epep;
@@ -1693,6 +2181,13 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters)
struct stat stat_buf;
#endif
+ if (state->driver.select)
+ counters->no_driver_select_structs++;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ if (state->driver.event)
+ counters->no_driver_event_structs++;
+#endif
+
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if (state->events || ep_events) {
if (ep_events & ERTS_POLL_EV_NVAL) {
@@ -1831,6 +2326,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters)
}
}
}
+#if ERTS_CIO_HAVE_DRV_EVENT
else if (state->type == ERTS_EV_TYPE_DRV_EV) {
Eterm id;
erts_printf("driver_event ");
@@ -1866,6 +2362,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters)
erts_free_port_names(pnp);
}
}
+#endif
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
else if (internal) {
erts_printf("internal ");
@@ -1905,7 +2402,7 @@ static void doit_erts_check_io_debug(void *vstate, void *vcounters)
}
int
-ERTS_CIO_EXPORT(erts_check_io_debug)(void)
+ERTS_CIO_EXPORT(erts_check_io_debug)(ErtsCheckIoDebugInfo *ciodip)
{
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
int fd, len;
@@ -1915,6 +2412,10 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void)
ErtsDrvEventState null_des;
null_des.driver.select = NULL;
+#if ERTS_CIO_HAVE_DRV_EVENT
+ null_des.driver.event = NULL;
+#endif
+ null_des.driver.drv_ptr = NULL;
null_des.events = 0;
null_des.remove_cnt = 0;
null_des.type = ERTS_EV_TYPE_NONE;
@@ -1935,6 +2436,8 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void)
#endif
counters.used_fds = 0;
counters.num_errors = 0;
+ counters.no_driver_select_structs = 0;
+ counters.no_driver_event_structs = 0;
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
len = erts_smp_atomic_read_nob(&drv_ev_state_len);
@@ -1951,8 +2454,16 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void)
erts_smp_thr_progress_unblock();
+ ciodip->no_used_fds = counters.used_fds;
+ ciodip->no_driver_select_structs = counters.no_driver_select_structs;
+ ciodip->no_driver_event_structs = counters.no_driver_event_structs;
+
erts_printf("\n");
erts_printf("used fds=%d\n", counters.used_fds);
+ erts_printf("Number of driver_select() structures=%d\n", counters.no_driver_select_structs);
+#if ERTS_CIO_HAVE_DRV_EVENT
+ erts_printf("Number of driver_event() structures=%d\n", counters.no_driver_event_structs);
+#endif
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
erts_printf("internal fds=%d\n", counters.internal_fds);
#endif
@@ -1961,6 +2472,7 @@ ERTS_CIO_EXPORT(erts_check_io_debug)(void)
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
erts_free(ERTS_ALC_T_TMP, (void *) counters.epep);
#endif
+
return counters.num_errors;
}
diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h
index edab7947ba..d01297d55c 100644
--- a/erts/emulator/sys/common/erl_check_io.h
+++ b/erts/emulator/sys/common/erl_check_io.h
@@ -26,6 +26,7 @@
#ifndef ERL_CHECK_IO_H__
#define ERL_CHECK_IO_H__
+#include "sys.h"
#include "erl_sys_driver.h"
#ifdef ERTS_ENABLE_KERNEL_POLL
@@ -52,8 +53,8 @@ void erts_check_io_kp(int);
void erts_check_io_nkp(int);
void erts_init_check_io_kp(void);
void erts_init_check_io_nkp(void);
-int erts_check_io_debug_kp(void);
-int erts_check_io_debug_nkp(void);
+int erts_check_io_debug_kp(ErtsCheckIoDebugInfo *);
+int erts_check_io_debug_nkp(ErtsCheckIoDebugInfo *);
#else /* !ERTS_ENABLE_KERNEL_POLL */
@@ -70,6 +71,27 @@ void erts_init_check_io(void);
#endif
+extern erts_smp_atomic_t erts_check_io_time;
+
+typedef struct {
+ ErtsPortTaskHandle task;
+ erts_smp_atomic_t executed_time;
+} ErtsIoTask;
+
+ERTS_GLB_INLINE void erts_io_notify_port_task_executed(ErtsPortTaskHandle *pthp);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE void
+erts_io_notify_port_task_executed(ErtsPortTaskHandle *pthp)
+{
+ ErtsIoTask *itp = (ErtsIoTask *) (((char *) pthp) - offsetof(ErtsIoTask, task));
+ erts_aint_t ci_time = erts_smp_atomic_read_acqb(&erts_check_io_time);
+ erts_smp_atomic_set_relb(&itp->executed_time, ci_time);
+}
+
+#endif
+
#endif /* ERL_CHECK_IO_H__ */
#if !defined(ERL_CHECK_IO_C__) && !defined(ERTS_ALLOC_C__)
@@ -81,6 +103,16 @@ void erts_init_check_io(void);
#include "erl_poll.h"
#include "erl_port_task.h"
+#ifdef __WIN32__
+/*
+ * Current erts_poll implementation for Windows cannot handle
+ * active events in the set of events polled.
+ */
+# define ERTS_CIO_DEFER_ACTIVE_EVENTS 1
+#else
+# define ERTS_CIO_DEFER_ACTIVE_EVENTS 0
+#endif
+
/*
* ErtsDrvEventDataState is used by driver_event() which is almost never
* used. We allocate ErtsDrvEventDataState separate since we dont wan't
@@ -91,13 +123,16 @@ typedef struct {
Eterm port;
ErlDrvEventData data;
ErtsPollEvents removed_events;
- ErtsPortTaskHandle task;
+#if ERTS_CIO_DEFER_ACTIVE_EVENTS
+ ErtsPollEvents deferred_events;
+#endif
+ ErtsIoTask iotask;
} ErtsDrvEventDataState;
typedef struct {
Eterm inport;
Eterm outport;
- ErtsPortTaskHandle intask;
- ErtsPortTaskHandle outtask;
+ ErtsIoTask iniotask;
+ ErtsIoTask outiotask;
} ErtsDrvSelectDataState;
#endif /* #ifndef ERL_CHECK_IO_INTERNAL__ */
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index e3ba741058..e63f0bda54 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -44,6 +44,14 @@
#endif
#endif
+/*
+ * erts_check_io_time is used by the erl_check_io implementation. The
+ * global erts_check_io_time variable is declared here since there
+ * (often) exist two versions of erl_check_io (kernel-poll and
+ * non-kernel-poll), and we dont want two versions of this variable.
+ */
+erts_smp_atomic_t erts_check_io_time;
+
/* Written once and only once */
static int filename_encoding = ERL_FILENAME_UNKNOWN;
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 176fc049a7..c3dba69acb 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -135,9 +135,6 @@
/* File descriptors are numbers anc consecutively allocated on Unix */
#define ERTS_SYS_CONTINOUS_FD_NUMBERS
-#define HAVE_ERTS_CHECK_IO_DEBUG
-int erts_check_io_debug(void);
-
#ifndef ERTS_SMP
# undef ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
# define ERTS_POLL_NEED_ASYNC_INTERRUPT_SUPPORT
@@ -230,8 +227,13 @@ extern void sys_stop_cat(void);
*/
#ifdef USE_ISINF_ISNAN /* simulate finite() */
-# define finite(f) (!isinf(f) && !isnan(f))
-# define HAVE_FINITE
+# define isfinite(f) (!isinf(f) && !isnan(f))
+# define HAVE_ISFINITE
+#elif defined(isfinite) && !defined(HAVE_ISFINITE)
+# define HAVE_ISFINITE
+#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE)
+# define isfinite finite
+# define HAVE_ISFINITE
#endif
#ifdef NO_FPE_SIGNALS
@@ -241,7 +243,7 @@ extern void sys_stop_cat(void);
#define erts_thread_init_fp_exception() do{}while(0)
#endif
# define __ERTS_FP_CHECK_INIT(fpexnp) do {} while (0)
-# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!finite(f)) { Action; } else {}
+# define __ERTS_FP_ERROR(fpexnp, f, Action) if (!isfinite(f)) { Action; } else {}
# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) __ERTS_FP_ERROR(fpexnp, f, Action)
# define __ERTS_SAVE_FP_EXCEPTION(fpexnp)
# define __ERTS_RESTORE_FP_EXCEPTION(fpexnp)
@@ -305,7 +307,7 @@ static __inline__ void __ERTS_FP_CHECK_INIT(volatile unsigned long *fp_exception
code to always throw floating-point exceptions on errors. */
static __inline__ int erts_check_fpe_thorough(volatile unsigned long *fp_exception, double f)
{
- return erts_check_fpe(fp_exception, f) || !finite(f);
+ return erts_check_fpe(fp_exception, f) || !isfinite(f);
}
# define __ERTS_FP_ERROR_THOROUGH(fpexnp, f, Action) \
do { if (erts_check_fpe_thorough((fpexnp),(f))) { Action; } } while (0)
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index c3d7440409..0d677d5f34 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -284,7 +284,7 @@ struct {
void (*check_io)(int);
Uint (*size)(void);
Eterm (*info)(void *);
- int (*check_io_debug)(void);
+ int (*check_io_debug)(ErtsCheckIoDebugInfo *);
} io_func = {0};
@@ -306,9 +306,9 @@ Eterm erts_check_io_info(void *p)
}
int
-erts_check_io_debug(void)
+erts_check_io_debug(ErtsCheckIoDebugInfo *ip)
{
- return (*io_func.check_io_debug)();
+ return (*io_func.check_io_debug)(ip);
}
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
index 7a1d129cd5..972170d465 100644
--- a/erts/emulator/sys/win32/erl_poll.c
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -1085,7 +1085,7 @@ void erts_poll_controlv(ErtsPollSet ps,
pcev[i].events,
pcev[i].on);
}
- ERTS_POLLSET_LOCK(ps);
+ ERTS_POLLSET_UNLOCK(ps);
HARDTRACEF(("Out erts_poll_controlv"));
}
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index a78dbf64af..838f0c61eb 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -113,12 +113,10 @@
/*
* Our own type of "FD's"
*/
+#define ERTS_SYS_FD_INVALID INVALID_HANDLE_VALUE
#define ERTS_SYS_FD_TYPE HANDLE
#define NO_FSTAT_ON_SYS_FD_TYPE 1 /* They are events, not files */
-#define HAVE_ERTS_CHECK_IO_DEBUG
-int erts_check_io_debug(void);
-
/*
* For erl_time_sup
*/
diff --git a/erts/emulator/test/a_SUITE.erl b/erts/emulator/test/a_SUITE.erl
index 195c9c0a5f..17579be416 100644
--- a/erts/emulator/test/a_SUITE.erl
+++ b/erts/emulator/test/a_SUITE.erl
@@ -97,23 +97,13 @@ display_check_io(ChkIo) ->
catch erlang:display('--- CHECK IO INFO ---'),
catch erlang:display(ChkIo),
catch erts_debug:set_internal_state(available_internal_state, true),
- NoOfErrorFds = (catch erts_debug:get_internal_state(check_io_debug)),
+ NoOfErrorFds = (catch element(1, erts_debug:get_internal_state(check_io_debug))),
catch erlang:display({'NoOfErrorFds', NoOfErrorFds}),
catch erts_debug:set_internal_state(available_internal_state, false),
catch erlang:display('--- CHECK IO INFO ---'),
ok.
get_check_io_info() ->
- ChkIo = erlang:system_info(check_io),
- case lists:keysearch(pending_updates, 1, ChkIo) of
- {value, {pending_updates, 0}} ->
- display_check_io(ChkIo),
- ChkIo;
- false ->
- ChkIo;
- _ ->
- receive after 10 -> ok end,
- get_check_io_info()
- end.
+ z_SUITE:get_check_io_info().
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 344bde7c91..8d2c620be0 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -31,8 +31,9 @@
end_per_suite/1, init_per_group/2,end_per_group/2,
init_per_testcase/2,
end_per_testcase/2,
+
+ a_test/1,
outputv_echo/1,
-
timer_measure/1,
timer_cancel/1,
timer_change/1,
@@ -79,7 +80,8 @@
thr_free_drv/1,
async_blast/1,
thr_msg_blast/1,
- consume_timeslice/1]).
+ consume_timeslice/1,
+ z_test/1]).
-export([bin_prefix/2]).
@@ -122,19 +124,19 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
_ -> erts_debug:set_internal_state(available_internal_state, true)
end,
erlang:display({init_per_testcase, Case}),
- ?line 0 = erts_debug:get_internal_state(check_io_debug),
+ ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
[{watchdog, Dog},{testcase, Case}|Config].
end_per_testcase(Case, Config) ->
Dog = ?config(watchdog, Config),
erlang:display({end_per_testcase, Case}),
- ?line 0 = erts_debug:get_internal_state(check_io_debug),
+ ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
?t:timetrap_cancel(Dog).
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [outputv_errors, outputv_echo, queue_echo, {group, timer},
+all() -> %% Keep a_test first and z_test last...
+ [a_test, outputv_errors, outputv_echo, queue_echo, {group, timer},
driver_unloaded, io_ready_exit, use_fallback_pollset,
bad_fd_in_pollset, driver_event, fd_change,
steal_control, otp_6602, driver_system_info_base_ver,
@@ -151,7 +153,8 @@ all() ->
thr_free_drv,
async_blast,
thr_msg_blast,
- consume_timeslice].
+ consume_timeslice,
+ z_test].
groups() ->
[{timer, [],
@@ -917,8 +920,7 @@ steal_control_test(Hndl = {erts_poll_info, Before}) ->
end.
chkio_test_init(Config) when is_list(Config) ->
- ?line wait_until_no_pending_updates(),
- ?line ChkIo = erlang:system_info(check_io),
+ ?line ChkIo = get_stable_check_io_info(),
?line case catch lists:keysearch(name, 1, ChkIo) of
{value, {name, erts_poll}} ->
?line ?t:format("Before test: ~p~n", [ChkIo]),
@@ -937,8 +939,7 @@ chkio_test_fini({skipped, _} = Res) ->
chkio_test_fini({chkio_test_result, Res, Before}) ->
?line ok = erl_ddll:unload_driver('chkio_drv'),
?line ok = erl_ddll:stop(),
- ?line wait_until_no_pending_updates(),
- ?line After = erlang:system_info(check_io),
+ ?line After = get_stable_check_io_info(),
?line ?t:format("After test: ~p~n", [After]),
?line verify_chkio_state(Before, After),
?line Res.
@@ -985,7 +986,7 @@ chkio_test({erts_poll_info, Before},
?line Fun(),
?line During = erlang:system_info(check_io),
?line erlang:display(During),
- ?line 0 = erts_debug:get_internal_state(check_io_debug),
+ ?line 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
?line ?t:format("During test: ~p~n", [During]),
?line chk_chkio_port(Port),
?line case erlang:port_control(Port, ?CHKIO_STOP, "") of
@@ -1034,18 +1035,22 @@ verify_chkio_state(Before, After) ->
After)
end,
?line ok.
-
-
-wait_until_no_pending_updates() ->
- case lists:keysearch(pending_updates, 1, erlang:system_info(check_io)) of
- {value, {pending_updates, 0}} ->
- ok;
- false ->
- ok;
+get_stable_check_io_info() ->
+ ChkIo = erlang:system_info(check_io),
+ PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of
+ {value, {pending_updates, PendNo}} ->
+ PendNo;
+ false ->
+ 0
+ end,
+ {value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo),
+ case {PendUpdNo, ActFds} of
+ {0, 0} ->
+ ChkIo;
_ ->
receive after 10 -> ok end,
- wait_until_no_pending_updates()
+ get_stable_check_io_info()
end.
otp_6602(doc) -> ["Missed port lock when stealing control of fd from a "
@@ -1084,7 +1089,15 @@ otp_6602(Config) when is_list(Config) ->
["async_thrs",
"sched_thrs"])).
--define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES2).
+-define(EXPECTED_SYSTEM_INFO_NAMES3,
+ (?EXPECTED_SYSTEM_INFO_NAMES2 ++
+ ["emu_nif_vsn"])).
+
+-define(EXPECTED_SYSTEM_INFO_NAMES4,
+ (?EXPECTED_SYSTEM_INFO_NAMES3 ++
+ ["dirty_sched"])).
+
+-define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4).
'driver_system_info_base_ver'(doc) ->
[];
@@ -1132,16 +1145,18 @@ check_driver_system_info_result(Result) ->
drv_vsn_str2tup(erlang:system_info(driver_version))} of
{DDVSN, DDVSN} ->
?line [] = Ns;
- {{1, 0}, _} ->
- ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
- -- ?EXPECTED_SYSTEM_INFO_NAMES1),
- ?line ExpNs = lists:sort(Ns);
- {{1, 1}, _} ->
+ %% {{1, 0}, _} ->
+ %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
+ %% -- ?EXPECTED_SYSTEM_INFO_NAMES1),
+ %% ?line ExpNs = lists:sort(Ns);
+ %% {{1, 1}, _} ->
+ %% ?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
+ %% -- ?EXPECTED_SYSTEM_INFO_NAMES2),
+ %% ?line ExpNs = lists:sort(Ns);
+ {{3, 0}, _} ->
?line ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
- -- ?EXPECTED_SYSTEM_INFO_NAMES2),
- ?line ExpNs = lists:sort(Ns);
- {{2, 0}, _} ->
- ?line [] = Ns
+ -- ?EXPECTED_SYSTEM_INFO_NAMES3),
+ ?line ExpNs = lists:sort(Ns)
end.
chk_sis(SIs, Ns) ->
@@ -1188,6 +1203,14 @@ check_si_res(["async_thrs", Value]) ->
check_si_res(["sched_thrs", Value]) ->
?line Value = integer_to_list(erlang:system_info(schedulers));
+%% Data added in 3rd version of driver_system_info() (driver version 1.5)
+check_si_res(["emu_nif_vsn", _Value]) ->
+ true;
+
+%% Data added in 4th version of driver_system_info() (driver version 3.1)
+check_si_res(["dirty_sched", _Value]) ->
+ true;
+
check_si_res(Unexpected) ->
?line ?t:fail({unexpected_result, Unexpected}).
@@ -2369,10 +2392,25 @@ count_proc_sched(Ps, PNs) ->
PNs
end.
+a_test(Config) when is_list(Config) ->
+ check_io_debug().
+
+z_test(Config) when is_list(Config) ->
+ check_io_debug().
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Utilities
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+check_io_debug() ->
+ get_stable_check_io_info(),
+ {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs}
+ = erts_debug:get_internal_state(check_io_debug),
+ 0 = NoErrorFds,
+ NoUsedFds = NoDrvSelStructs,
+ 0 = NoDrvEvStructs,
+ ok.
+
%flush_msgs() ->
% receive
% M ->
diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
index e44c7dbd5e..964034f5a6 100644
--- a/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/sys_info_base_drv.c
@@ -41,7 +41,9 @@
"thread=%s " \
"smp=%s " \
"async_thrs=%d " \
- "sched_thrs=%d"
+ "sched_thrs=%d " \
+ "emu_nif_vsn=%d.%d"
+
static size_t
sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
@@ -55,6 +57,7 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
slen += 5; /* smp */
slen += 20; /* async_thrs */
slen += 20; /* sched_thrs */
+ slen += 2*20; /* emu_nif_vsn */
return slen;
}
@@ -72,7 +75,9 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str)
sip->thread_support ? "true" : "false",
sip->smp_support ? "true" : "false",
sip->async_threads,
- sip->scheduler_threads);
+ sip->scheduler_threads,
+ sip->nif_major_version,
+ sip->nif_minor_version);
}
#include "sys_info_drv_impl.c"
diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c
index 5bbc966932..6d2c47fdaf 100644
--- a/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/sys_info_curr_drv.c
@@ -40,7 +40,9 @@
"thread=%s " \
"smp=%s " \
"async_thrs=%d " \
- "sched_thrs=%d"
+ "sched_thrs=%d " \
+ "emu_nif_vsn=%d.%d " \
+ "dirty_sched=%s"
static size_t
sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
@@ -54,6 +56,8 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
slen += 5; /* smp */
slen += 20; /* async_thrs */
slen += 20; /* sched_thrs */
+ slen += 2*20; /* emu_nif_vsn */
+ slen += 5; /* dirty_sched */
return slen;
}
@@ -71,7 +75,10 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str)
sip->thread_support ? "true" : "false",
sip->smp_support ? "true" : "false",
sip->async_threads,
- sip->scheduler_threads);
+ sip->scheduler_threads,
+ sip->nif_major_version,
+ sip->nif_minor_version,
+ sip->dirty_scheduler_support ? "true" : "false");
}
#include "sys_info_drv_impl.c"
diff --git a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
index 63c69f751c..2271d7027b 100644
--- a/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/sys_info_prev_drv.c
@@ -41,7 +41,9 @@
"thread=%s " \
"smp=%s " \
"async_thrs=%d " \
- "sched_thrs=%d"
+ "sched_thrs=%d " \
+ "emu_nif_vsn=%d.%d"
+
static size_t
sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
@@ -55,6 +57,7 @@ sys_info_drv_max_res_len(ErlDrvSysInfo *sip)
slen += 5; /* smp */
slen += 20; /* async_thrs */
slen += 20; /* sched_thrs */
+ slen += 2*20; /* emu_nif_vsn */
return slen;
}
@@ -72,7 +75,9 @@ sys_info_drv_sprintf_sys_info(ErlDrvSysInfo *sip, char *str)
sip->thread_support ? "true" : "false",
sip->smp_support ? "true" : "false",
sip->async_threads,
- sip->scheduler_threads);
+ sip->scheduler_threads,
+ sip->nif_major_version,
+ sip->nif_minor_version);
}
#include "sys_info_drv_impl.c"
diff --git a/erts/emulator/test/float_SUITE_data/fp_drv.c b/erts/emulator/test/float_SUITE_data/fp_drv.c
index b80385c3f9..82d18d6440 100644
--- a/erts/emulator/test/float_SUITE_data/fp_drv.c
+++ b/erts/emulator/test/float_SUITE_data/fp_drv.c
@@ -29,9 +29,14 @@
#if defined (__GNUC__)
int _finite(double x);
#endif
-#ifndef finite
-#define finite _finite
+#ifndef isfinite
+#define isfinite _finite
#endif
+#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE)
+/* If not windows and we do not have isfinite */
+#define isfinite finite
+#elif !defined(HAVE_ISFINITE)
+# error "No finite function found!"
#endif
#include "erl_driver.h"
@@ -79,21 +84,21 @@ do_test(void *unused)
x = 3.23e133;
y = 3.57e257;
z = x*y;
- if (finite(z))
+ if (isfinite(z))
return "is finite (1)";
x = 5.0;
y = 0.0;
z = x/y;
- if (finite(z))
+ if (isfinite(z))
return "is finite (2)";
z = log(-1.0);
- if (finite(z))
+ if (isfinite(z))
return "is finite (3)";
z = log(0.0);
- if (finite(z))
+ if (isfinite(z))
return "is finite (4)";
return "ok";
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index ff5fb8c5af..291c903947 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1539,13 +1539,21 @@ static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TE
}
#ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT
+
+static int have_dirty_schedulers(void)
+{
+ ErlNifSysInfo si;
+ enif_system_info(&si, sizeof(si));
+ return si.dirty_scheduler_support;
+}
+
static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int n;
char s[10];
ErlNifBinary b;
ERL_NIF_TERM result;
- if (enif_have_dirty_schedulers()) {
+ if (have_dirty_schedulers()) {
assert(enif_is_on_dirty_scheduler(env));
}
assert(argc == 3);
@@ -1566,7 +1574,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
assert(!enif_is_on_dirty_scheduler(env));
if (argc != 3)
return enif_make_badarg(env);
- if (enif_have_dirty_schedulers()) {
+ if (have_dirty_schedulers()) {
if (enif_get_int(env, argv[0], &n) &&
enif_get_string(env, argv[1], s, sizeof s, ERL_NIF_LATIN1) &&
enif_inspect_binary(env, argv[2], &b))
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index e01b2f253b..8792f25d76 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -90,6 +90,7 @@
mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1,
exit_status_multi_scheduling_block/1, ports/1,
spawn_driver/1, spawn_executable/1, close_deaf_port/1,
+ port_setget_data/1,
unregister_name/1, parallelism_option/1]).
-export([do_iter_max_ports/2]).
@@ -115,6 +116,7 @@ all() ->
mix_up_ports, otp_5112, otp_5119,
exit_status_multi_scheduling_block, ports, spawn_driver,
spawn_executable, close_deaf_port, unregister_name,
+ port_setget_data,
parallelism_option].
groups() ->
@@ -2333,6 +2335,55 @@ close_deaf_port_1(N, Cmd) ->
{comment, "Could not spawn more than " ++ integer_to_list(N) ++ " OS processes."}
end.
+%% Test undocumented port_set_data/2 and port_get_data/1
+%% Hammer from multiple processes a while
+%% and then abrubtly close the port (OTP-12208).
+port_setget_data(Config) when is_list(Config) ->
+ ok = load_driver(?config(data_dir, Config), "echo_drv"),
+ Port = erlang:open_port({spawn_driver, "echo_drv"}, []),
+
+ NSched = erlang:system_info(schedulers_online),
+ PRs = lists:map(fun(I) ->
+ spawn_opt(fun() -> port_setget_data_hammer(Port,1) end,
+ [monitor, {scheduler, I rem NSched}])
+ end,
+ lists:seq(1,10)),
+ receive after 100 -> ok end,
+ Papa = self(),
+ lists:foreach(fun({Pid,_}) -> Pid ! {Papa,prepare_for_close} end, PRs),
+ lists:foreach(fun({Pid,_}) ->
+ receive {Pid,prepare_for_close} -> ok end
+ end,
+ PRs),
+ port_close(Port),
+ lists:foreach(fun({Pid,Ref}) ->
+ receive {'DOWN', Ref, process, Pid, normal} -> ok end
+ end,
+ PRs),
+ ok.
+
+port_setget_data_hammer(Port, N) ->
+ Rand = random:uniform(3),
+ try case Rand of
+ 1 -> true = erlang:port_set_data(Port, atom);
+ 2 -> true = erlang:port_set_data(Port, {1,2,3});
+ 3 -> erlang:port_get_data(Port)
+ end
+ catch
+ error:badarg ->
+ true = get(prepare_for_close),
+ io:format("~p did ~p rounds before port closed\n", [self(), N]),
+ exit(normal)
+ end,
+ receive {Papa, prepare_for_close} ->
+ put(prepare_for_close, true),
+ Papa ! {self(),prepare_for_close}
+ after 0 ->
+ ok
+ end,
+ port_setget_data_hammer(Port, N+1).
+
+
wait_until(Fun) ->
case catch Fun() of
true ->
diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl
index 4b3075a164..b0c6224dfe 100644
--- a/erts/emulator/test/z_SUITE.erl
+++ b/erts/emulator/test/z_SUITE.erl
@@ -38,7 +38,7 @@
-export([schedulers_alive/1, node_container_refc_check/1,
long_timers/1, pollset_size/1,
- check_io_debug/1]).
+ check_io_debug/1, get_check_io_info/0]).
-define(DEFAULT_TIMEOUT, ?t:minutes(5)).
@@ -288,11 +288,14 @@ check_io_debug(Config) when is_list(Config) ->
end.
check_io_debug_test() ->
+ ?line erlang:display(get_check_io_info()),
?line erts_debug:set_internal_state(available_internal_state, true),
- ?line erlang:display(erlang:system_info(check_io)),
- ?line NoOfErrorFds = erts_debug:get_internal_state(check_io_debug),
+ ?line {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoDrvEvStructs}
+ = erts_debug:get_internal_state(check_io_debug),
?line erts_debug:set_internal_state(available_internal_state, false),
- ?line 0 = NoOfErrorFds,
+ ?line 0 = NoErrorFds,
+ ?line NoUsedFds = NoDrvSelStructs,
+ ?line 0 = NoDrvEvStructs,
?line ok.
@@ -305,7 +308,7 @@ display_check_io(ChkIo) ->
catch erlang:display('--- CHECK IO INFO ---'),
catch erlang:display(ChkIo),
catch erts_debug:set_internal_state(available_internal_state, true),
- NoOfErrorFds = (catch erts_debug:get_internal_state(check_io_debug)),
+ NoOfErrorFds = (catch element(1, erts_debug:get_internal_state(check_io_debug))),
catch erlang:display({'NoOfErrorFds', NoOfErrorFds}),
catch erts_debug:set_internal_state(available_internal_state, false),
catch erlang:display('--- CHECK IO INFO ---'),
@@ -313,14 +316,19 @@ display_check_io(ChkIo) ->
get_check_io_info() ->
ChkIo = erlang:system_info(check_io),
- case lists:keysearch(pending_updates, 1, ChkIo) of
- {value, {pending_updates, 0}} ->
+ PendUpdNo = case lists:keysearch(pending_updates, 1, ChkIo) of
+ {value, {pending_updates, PendNo}} ->
+ PendNo;
+ false ->
+ 0
+ end,
+ {value, {active_fds, ActFds}} = lists:keysearch(active_fds, 1, ChkIo),
+ case {PendUpdNo, ActFds} of
+ {0, 0} ->
display_check_io(ChkIo),
ChkIo;
- false ->
- ChkIo;
_ ->
- receive after 10 -> ok end,
+ receive after 100 -> ok end,
get_check_io_info()
end.
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 709c6f02d1..5ebde8ca3c 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -128,6 +128,7 @@ static char *pluss_val_switches[] = {
"bwt",
"cl",
"ct",
+ "ecio",
"fwi",
"tbt",
"wct",
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index bf6eb00314..0190ea613e 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1130,6 +1130,39 @@ document etp-cp
%---------------------------------------------------------------------------
end
+define etp-check-beam-ranges
+ set $etp_ci = 0
+ while $etp_ci < 3
+ printf "Checking code index %i...\n", $etp_ci
+ set $etp_j = 0
+ while $etp_j < r[$etp_ci].n
+ set $etp_p = &r[$etp_ci].modules[$etp_j]
+ if $etp_j > 0 && $etp_p->start < (Range*)$etp_p[-1].end.counter
+ printf "r[%i].modules[%i]: ERROR start < previous\n", $etp_ci, $etp_j
+ end
+ if $etp_p->start > (Range*)$etp_p->end.counter
+ printf "r[%i].modules[%i]: ERROR start > end\n", $etp_ci, $etp_j
+ else
+ if $etp_p->start == (Range*)$etp_p->end.counter
+ printf "r[%i].modules[%i]: Purged\n", $etp_ci, $etp_j
+ end
+ end
+ set $etp_j = $etp_j + 1
+ end
+ set $etp_ci = $etp_ci + 1
+ end
+end
+
+document etp-check-beam-ranges
+%---------------------------------------------------------------------------
+% etp-check-beam-ranges
+%
+% Do consistency check of beam_ranges data structure
+% and print errors and empty slots from purged modules.
+%---------------------------------------------------------------------------
+end
+
+
############################################################################
# Commands for special term bunches.
#
@@ -3552,6 +3585,39 @@ document etp-carrier-blocks
%---------------------------------------------------------------------------
end
+define etp-address-to-beam-opcode
+ set $etp_i = 0
+ set $etp_min_diff = ((UWord)1 << (sizeof(UWord)*8 - 1))
+ set $etp_min_opcode = -1
+ set $etp_addr = (UWord) ($arg0)
+
+ while $etp_i < num_instructions && $etp_min_diff > 0
+ if ($etp_addr - (UWord)beam_ops[$etp_i]) < $etp_min_diff
+ set $etp_min_diff = $etp_addr - (UWord)beam_ops[$etp_i]
+ set $etp_min_opcode = $etp_i
+ end
+ set $etp_i = $etp_i + 1
+ end
+ if $etp_min_diff == 0
+ printf "Address %p is start of '%s'\n", $etp_addr, opc[$etp_min_opcode].name
+ else
+ if $etp_min_opcode >= 0
+ printf "Address is %ld bytes into opcode '%s' at %p\n", $etp_min_diff, opc[$etp_min_opcode].name, beam_ops[$etp_min_opcode]
+ else
+ printf "Invalid opcode address\n"
+ end
+ end
+end
+
+document etp-address-to-beam-opcode
+%---------------------------------------------------------------------------
+% Get beam opcode from a native instruction address (within process_main())
+% Arg: Instructon pointer value
+%
+% Does not work with NO_JUMP_TABLE
+%---------------------------------------------------------------------------
+end
+
############################################################################
# Toolbox parameter handling
diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c
index 500fd166f8..9d85d642ab 100644
--- a/erts/etc/win32/Install.c
+++ b/erts/etc/win32/Install.c
@@ -80,7 +80,7 @@ int wmain(int argc, wchar_t **argv)
}
}
if (root == NULL) {
- if (module = NULL) {
+ if (module == NULL) {
fprintf(stderr, "Cannot GetModuleHandle()\n");
exit(1);
}
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
index 1d116bf36e..772b668586 100644
--- a/erts/etc/win32/erl.c
+++ b/erts/etc/win32/erl.c
@@ -264,7 +264,7 @@ static void get_parameters(void)
int len;
- if (module = NULL) {
+ if (module == NULL) {
error("Cannot GetModuleHandle()");
}
diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c
index d58a28b5cb..7833dd8219 100644
--- a/erts/lib_src/common/erl_misc_utils.c
+++ b/erts/lib_src/common/erl_misc_utils.c
@@ -1515,7 +1515,7 @@ const char* parse_topology_spec_group(erts_cpu_info_t *cpuinfo, const char* xml,
if (is_thread_group) {
thread++;
} else {
- *core_p = (*core_p)++;
+ *core_p = (*core_p) + 1;
}
index_procs++;
}
@@ -1535,9 +1535,9 @@ const char* parse_topology_spec_group(erts_cpu_info_t *cpuinfo, const char* xml,
if (parentCacheLevel == 0) {
*core_p = 0;
- *processor_p = (*processor_p)++;
+ *processor_p = (*processor_p) + 1;
} else {
- *core_p = (*core_p)++;
+ *core_p = (*core_p) + 1;
}
if (error)
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 5f2b619322..001c9c76ed 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index cf3effc1e5..1893dd84ac 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 7dc7407a81..b467633a4d 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 5c139c4550..915a2d1aef 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index cf32b79e8d..df1bf932a3 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 37ed8d0365..95b4bbca2d 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index d49578abfa..5e4fc5ba84 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 8420052533..9d9a4886d9 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 8dc8cb961b..d98b0275f4 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 7507efb076..0744bdb21d 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 466e0b0020..6b86a427ba 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -1507,7 +1507,14 @@ real_path(Name,[Path|Paths],Acc,Links) ->
[""|_] = LinkPaths ->
real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
LinkPaths ->
- real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links])
+ % windows currently does not allow creation of relative symlinks
+ % across different drives
+ case erlang:system_info(os_type) of
+ {win32, _} ->
+ real_path(Name,LinkPaths++Paths,[],[ThisFile|Links]);
+ _ ->
+ real_path(Name,LinkPaths++Paths,Acc,[ThisFile|Links])
+ end
end;
_ ->
real_path(Name,Paths,This,Links)
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 98d7a942a6..4ba623d921 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -2233,6 +2233,7 @@ tuple_to_list(_Tuple) ->
(dynamic_trace) -> none | dtrace | systemtap;
(dynamic_trace_probes) -> boolean();
(elib_malloc) -> false;
+ (eager_check_io) -> boolean();
(ets_limit) -> pos_integer();
(fullsweep_after) -> {fullsweep_after, non_neg_integer()};
(garbage_collection) -> [{atom(), integer()}];
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index a15da3a421..345a6ae3be 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -35,7 +35,6 @@
{registered, []},
{applications, []},
{env, []},
- {mod, {erts, []}},
{runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]}
]}.
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 0db4370ea8..b6a38f9361 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -17,7 +17,7 @@
# %CopyrightEnd%
#
-VSN = 6.1.2
+VSN = 6.2
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index 53e3aa1678..317a464060 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -949,7 +949,7 @@ static int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *
} 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) {
+ if (*ib_index+1 >= in_buf_len || form == ASN1_PRIMITIVE) {
return ASN1_INDEF_LEN_ERROR;
}
while (!(in_buf[*ib_index] == 0 && in_buf[*ib_index + 1] == 0)) {
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/test/asn1_SUITE_data/Constructed.asn b/lib/asn1/test/asn1_SUITE_data/Constructed.asn
index 09a66d0c0d..bd49741726 100644
--- a/lib/asn1/test/asn1_SUITE_data/Constructed.asn
+++ b/lib/asn1/test/asn1_SUITE_data/Constructed.asn
@@ -1,6 +1,3 @@
-
-
-
Constructed DEFINITIONS ::=
BEGIN
@@ -20,4 +17,7 @@ C ::= CHOICE {
S3 ::= SEQUENCE {i INTEGER}
S3ext ::= SEQUENCE {i INTEGER, ...}
+
+OS ::= OCTET STRING
+
END
diff --git a/lib/asn1/test/ber_decode_error.erl b/lib/asn1/test/ber_decode_error.erl
index 6fd2450c62..ef11717c45 100644
--- a/lib/asn1/test/ber_decode_error.erl
+++ b/lib/asn1/test/ber_decode_error.erl
@@ -61,6 +61,10 @@ run([]) ->
(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))),
+
+ %% A primitive must not be encoded with an indefinite length.
+ {error,{asn1,{invalid_length,_}}} =
+ (catch 'Constructed':decode('OS', <<4,128,4,3,97,98,99,0,0>>)),
ok.
sub(Bin, Bytes) ->
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/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/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_logs.erl b/lib/common_test/src/ct_logs.erl
index 43eabb18d5..7037cdca73 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -129,7 +129,13 @@ datestr_from_dirname([]) ->
close(Info, StartDir) ->
%% close executes on the ct_util process, not on the logger process
%% so we need to use a local copy of the log cache data
- LogCacheBin = make_last_run_index(),
+ LogCacheBin =
+ case make_last_run_index() of
+ {error,_} -> % log server not responding
+ undefined;
+ LCB ->
+ LCB
+ end,
put(ct_log_cache,LogCacheBin),
Cache2File = fun() ->
case get(ct_log_cache) of
@@ -710,6 +716,7 @@ logger_loop(State) ->
end
end,
if Importance >= (100-VLvl) ->
+ CtLogFd = State#logger_state.ct_log_fd,
case get_groupleader(Pid, GL, State) of
{tc_log,TCGL,TCGLs} ->
case erlang:is_process_alive(TCGL) of
@@ -723,14 +730,15 @@ logger_loop(State) ->
%% Group leader is dead, so write to the
%% CtLog or unexpected_io log instead
unexpected_io(Pid,Category,Importance,
- List,State),
+ List,CtLogFd),
+
logger_loop(State)
end;
{ct_log,_Fd,TCGLs} ->
%% If category is ct_internal then write
%% to ct_log, else write to unexpected_io
%% log
- unexpected_io(Pid,Category,Importance,List,State),
+ unexpected_io(Pid,Category,Importance,List,CtLogFd),
logger_loop(State#logger_state{
tc_groupleaders = TCGLs})
end;
@@ -803,16 +811,15 @@ logger_loop(State) ->
ok
end.
-create_io_fun(FromPid, State) ->
+create_io_fun(FromPid, CtLogFd) ->
%% we have to build one io-list of all strings
%% before printing, or other io printouts (made in
%% parallel) may get printed between this header
%% and footer
- Fd = State#logger_state.ct_log_fd,
fun({Str,Args}, IoList) ->
case catch io_lib:format(Str,Args) of
{'EXIT',_Reason} ->
- io:format(Fd, "Logging fails! Str: ~p, Args: ~p~n",
+ io:format(CtLogFd, "Logging fails! Str: ~p, Args: ~p~n",
[Str,Args]),
%% stop the testcase, we need to see the fault
exit(FromPid, {log_printout_error,Str,Args}),
@@ -827,28 +834,53 @@ create_io_fun(FromPid, State) ->
print_to_log(sync, FromPid, Category, TCGL, List, State) ->
%% in some situations (exceptions), the printout is made from the
%% test server IO process and there's no valid group leader to send to
+ CtLogFd = State#logger_state.ct_log_fd,
if FromPid /= TCGL ->
- IoFun = create_io_fun(FromPid, State),
+ IoFun = create_io_fun(FromPid, CtLogFd),
io:format(TCGL,"~ts", [lists:foldl(IoFun, [], List)]);
true ->
- unexpected_io(FromPid,Category,?MAX_IMPORTANCE,List,State)
+ unexpected_io(FromPid,Category,?MAX_IMPORTANCE,List,CtLogFd)
end,
State;
print_to_log(async, FromPid, Category, TCGL, List, State) ->
%% in some situations (exceptions), the printout is made from the
%% test server IO process and there's no valid group leader to send to
+ CtLogFd = State#logger_state.ct_log_fd,
Printer =
if FromPid /= TCGL ->
- IoFun = create_io_fun(FromPid, State),
+ IoFun = create_io_fun(FromPid, CtLogFd),
fun() ->
test_server:permit_io(TCGL, self()),
- io:format(TCGL, "~ts", [lists:foldl(IoFun, [], List)])
+
+ %% Since asynchronous io gets can get buffered if
+ %% the file system is slow, there is also a risk that
+ %% the group leader has terminated before we get to
+ %% the io:format(GL, ...) call. We check this and
+ %% print "expired" messages to the unexpected io
+ %% log instead (best we can do).
+
+ case erlang:is_process_alive(TCGL) of
+ true ->
+ try io:format(TCGL, "~ts",
+ [lists:foldl(IoFun,[],List)]) of
+ _ -> ok
+ catch
+ _:terminated ->
+ unexpected_io(FromPid, Category,
+ ?MAX_IMPORTANCE,
+ List, CtLogFd)
+ end;
+ false ->
+ unexpected_io(FromPid, Category,
+ ?MAX_IMPORTANCE,
+ List, CtLogFd)
+ end
end;
true ->
fun() ->
- unexpected_io(FromPid,Category,?MAX_IMPORTANCE,
- List,State)
+ unexpected_io(FromPid, Category, ?MAX_IMPORTANCE,
+ List, CtLogFd)
end
end,
case State#logger_state.async_print_jobs of
@@ -3149,12 +3181,11 @@ html_encoding(latin1) ->
html_encoding(utf8) ->
"utf-8".
-unexpected_io(Pid,ct_internal,_Importance,List,State) ->
- IoFun = create_io_fun(Pid,State),
- io:format(State#logger_state.ct_log_fd, "~ts",
- [lists:foldl(IoFun, [], List)]);
-unexpected_io(Pid,_Category,_Importance,List,State) ->
- IoFun = create_io_fun(Pid,State),
+unexpected_io(Pid,ct_internal,_Importance,List,CtLogFd) ->
+ IoFun = create_io_fun(Pid,CtLogFd),
+ io:format(CtLogFd, "~ts", [lists:foldl(IoFun, [], List)]);
+unexpected_io(Pid,_Category,_Importance,List,CtLogFd) ->
+ IoFun = create_io_fun(Pid,CtLogFd),
Data = io_lib:format("~ts", [lists:foldl(IoFun, [], List)]),
test_server_io:print_unexpected(Data),
ok.
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index 39d089f04c..52acda5388 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -59,8 +59,10 @@
%%% '''
%%%
%%% <warning>
+%%% <p>
%%% This is experimental code which may be changed or removed
-%%% anytime without any warning.
+%%% anytime without any warning.
+%%% </p>
%%% </warning>
%%%
%%% @end
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/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 2e2b45d59f..746469584d 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -481,6 +481,7 @@ er_loop(Evs) ->
From ! {event_receiver,lists:reverse(Evs)},
er_loop(Evs);
stop ->
+ unregister(event_receiver),
ok
end.
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/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/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/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl
index b1bf4ebecc..ce12c1beb3 100644
--- a/lib/debugger/src/dbg_icmd.erl
+++ b/lib/debugger/src/dbg_icmd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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,10 +49,6 @@
%% specifies if the process should break.
%%--------------------------------------------------------------------
-%% Common Test adaptation
-cmd({call_remote,0,ct_line,line,_As}, Bs, _Ieval) ->
- Bs;
-
cmd(Expr, Bs, Ieval) ->
cmd(Expr, Bs, get(next_break), Ieval).
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index 77297de0f3..96f9f91808 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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
@@ -457,11 +457,6 @@ do_eval_function(Mod, Fun, As0, Bs0, _, Ieval0) when is_function(Fun);
exception(error, Reason, Bs0, Ieval0)
end;
-%% Common Test adaptation
-do_eval_function(ct_line, line, As, Bs, extern, #ieval{level=Le}=Ieval) ->
- debugged_cmd({apply,ct_line,line,As}, Bs, Ieval#ieval{level=Le+1}),
- {value, ignore, Bs};
-
do_eval_function(Mod, Name, As0, Bs0, Called, Ieval0) ->
#ieval{level=Le,line=Li,top=Top} = Ieval0,
trace(call, {Called, {Le,Li,Mod,Name,As0}}),
@@ -896,11 +891,6 @@ expr({make_ext_fun,Line,MFA0}, Bs0, Ieval0) ->
exception(error, badarg, Bs, Ieval, true)
end;
-%% Common test adaptation
-expr({call_remote,0,ct_line,line,As0,Lc}, Bs0, Ieval0) ->
- {As,_Bs} = eval_list(As0, Bs0, Ieval0),
- eval_function(ct_line, line, As, Bs0, extern, Ieval0, Lc);
-
%% Local function call
expr({local_call,Line,F,As0,Lc}, Bs0, #ieval{module=M} = Ieval0) ->
Ieval = Ieval0#ieval{line=Line},
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index 2755db64b8..908390ce50 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1998-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
@@ -265,9 +265,6 @@ first_lines(Clauses) ->
first_line({clause,_L,_Vars,_,Exprs}) ->
first_line(Exprs);
-%% Common Test adaptation
-first_line([{call_remote,0,ct_line,line,_As}|Exprs]) ->
- first_line(Exprs);
first_line([Expr|_Exprs]) -> % Expr = {Op, Line, ..varying no of args..}
element(2, Expr).
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_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 4e2ec67b35..5297a3a7b4 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -98,7 +98,7 @@ get_abstract_code_from_src(File) ->
{'ok', abstract_code()} | {'error', [string()]}.
get_abstract_code_from_src(File, Opts) ->
- case compile:file(File, [to_pp, binary|Opts]) of
+ case compile:noenv_file(File, [to_pp, binary|Opts]) of
error -> {error, []};
{error, Errors, _} -> {error, format_errors(Errors)};
{ok, _, AbstrCode} -> {ok, AbstrCode}
@@ -173,7 +173,7 @@ get_core_from_abstract_code(AbstrCode, Opts) ->
AbstrCode1 = cleanup_parse_transforms(AbstrCode),
%% Remove parse_transforms (and other options) from compile options.
Opts2 = cleanup_compile_options(Opts),
- try compile:forms(AbstrCode1, Opts2 ++ src_compiler_opts()) of
+ try compile:noenv_forms(AbstrCode1, Opts2 ++ src_compiler_opts()) of
{ok, _, Core} -> {ok, Core};
_What -> error
catch
@@ -444,21 +444,18 @@ cleanup_parse_transforms([]) ->
-spec cleanup_compile_options([compile:option()]) -> [compile:option()].
+cleanup_compile_options(Opts) ->
+ lists:filter(fun keep_compile_option/1, Opts).
+
%% 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([]) ->
- [].
+keep_compile_option(from_asm) -> false;
+keep_compile_option(asm) -> false;
+keep_compile_option(from_core) -> false;
+%% The parse transform will already have been applied, may cause
+%% problems if it is re-applied.
+keep_compile_option({parse_transform, _}) -> false;
+keep_compile_option(warnings_as_errors) -> false;
+keep_compile_option(_) -> true.
-spec format_errors([{module(), string()}]) -> [string()].
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/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/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 f4e78e8f3a..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,9 +880,10 @@ 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_map_field(#xmlElement{content = [K,V]}) ->
t_utype_elem(K) ++ [" => "] ++ t_utype_elem(V).
t_record(E, Es) ->
@@ -1084,8 +1083,6 @@ ot_type([#xmlElement{name = tuple, content = Es}]) ->
ot_tuple(Es);
ot_type([#xmlElement{name = map, content = Es}]) ->
ot_map(Es);
-ot_type([#xmlElement{name = map_field, content = Es}]) ->
- ot_map_field(Es);
ot_type([#xmlElement{name = 'fun', content = Es}]) ->
ot_fun(Es);
ot_type([#xmlElement{name = record, content = Es}]) ->
@@ -1143,10 +1140,10 @@ ot_tuple(Es) ->
{type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
ot_map(Es) ->
- {type,0,map,[ot_utype_elem(E) || E <- Es]}.
+ {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
-ot_map_field(Es) ->
- {type,0,map_field_assoc,[ot_utype_elem(E) || E <- 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)),
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/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..4417551aa8 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -48,7 +48,7 @@ scope() See baseObject/0, singleLevel/0, wholeSubtree/0
dereference() See neverDerefAliases/0, derefInSearching/0, derefFindingBaseObj/0, derefAlways/0
filter() See present/1, substrings/2,
equalityMatch/2, greaterOrEqual/2, lessOrEqual/2,
- approxMatch/2,
+ approxMatch/2, extensibleMatch/2,
'and'/1, 'or'/1, 'not'/1.
</pre>
<p></p>
@@ -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>
@@ -346,6 +348,16 @@ filter() See present/1, substrings/2,
<desc> <p>Create a approximation match filter.</p> </desc>
</func>
<func>
+ <name>extensibleMatch(MatchValue, OptionalAttrs) -> filter()</name>
+ <fsummary>Create search filter option.</fsummary>
+ <type>
+ <v>MatchValue = string()</v>
+ <v>OptionalAttrs = [Attr]</v>
+ <v>Attr = {matchingRule,string()} | {type,string()} | {dnAttributes,boolean()}</v>
+ </type>
+ <desc> <p>Creates an extensible match filter. For example, <c>eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseExactMatch"}]))</c> creates a filter which performs a <c>caseExactMatch</c> on the attribute <c>sn</c> and matches with the value <c>"Bar"</c>. The default value of <c>dnAttributes</c> is <c>false</c>.</p> </desc>
+ </func>
+ <func>
<name>'and'([Filter]) -> filter()</name>
<fsummary>Create search filter option.</fsummary>
<type>
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..3fa440d37d 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -12,8 +12,10 @@
-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,
+ extensibleMatch/2,
approxMatch/2,search/2,substrings/2,present/1,
'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
mod_replace/2, add/3, delete/2, modify_dn/5,parse_dn/1,
@@ -92,6 +94,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.
%%% --------------------------------------------------------------------
@@ -340,6 +351,27 @@ substrings(Type, SubStr) when is_list(Type), is_list(SubStr) ->
{substrings,#'SubstringFilter'{type = Type,
substrings = Ss}}.
+%%%
+%%% Filter for extensibleMatch
+%%%
+extensibleMatch(MatchValue, OptArgs) ->
+ MatchingRuleAssertion =
+ mra(OptArgs, #'MatchingRuleAssertion'{matchValue = MatchValue}),
+ {extensibleMatch, MatchingRuleAssertion}.
+
+mra([{matchingRule,Val}|T], Ack) when is_list(Val) ->
+ mra(T, Ack#'MatchingRuleAssertion'{matchingRule=Val});
+mra([{type,Val}|T], Ack) when is_list(Val) ->
+ mra(T, Ack#'MatchingRuleAssertion'{type=Val});
+mra([{dnAttributes,true}|T], Ack) ->
+ mra(T, Ack#'MatchingRuleAssertion'{dnAttributes="TRUE"});
+mra([{dnAttributes,false}|T], Ack) ->
+ mra(T, Ack#'MatchingRuleAssertion'{dnAttributes="FALSE"});
+mra([H|_], _) ->
+ throw({error,{extensibleMatch_arg,H}});
+mra([], Ack) ->
+ Ack.
+
%%% --------------------------------------------------------------------
%%% Worker process. We keep track of a controlling process to
%%% be able to terminate together with it.
@@ -374,24 +406,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 +509,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);
@@ -811,6 +884,7 @@ v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};
v_filter({approxMatch,AV}) -> {approxMatch,AV};
v_filter({present,A}) -> {present,A};
v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S};
+v_filter({extensibleMatch,S}) when is_record(S,'MatchingRuleAssertion') -> {extensibleMatch,S};
v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).
v_modifications(Mods) ->
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/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 6c3d303da0..d87f3ac4ac 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -106,7 +106,9 @@ api(doc) -> "Basic test that all api functions works as expected";
api(suite) -> [];
api(Config) ->
{Host,Port} = proplists:get_value(ldap_server, Config),
- {ok, H} = eldap:open([Host], [{port,Port}]),
+ {ok, H} = eldap:open([Host], [{port,Port}
+ ,{log,fun(Lvl,Fmt,Args)-> io:format("~p: ~s",[Lvl,io_lib:format(Fmt,Args)]) end}
+ ]),
%% {ok, H} = eldap:open([Host], [{port,Port+1}, {ssl, true}]),
do_api_checks(H, Config),
eldap:close(H),
@@ -232,6 +234,12 @@ chk_search(H, BasePath) ->
{ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(F_AND),
F_NOT = eldap:'and'([eldap:present("objectclass"), eldap:'not'(eldap:present("ou"))]),
{ok, #eldap_search_result{entries=[#eldap_entry{}, #eldap_entry{}]}} = Search(F_NOT),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseExactMatch"}])),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"2.5.13.5"}])),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("Bar",[{type,"sn"},{matchingRule,"caseIgnoreMatch"}])),
+ {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"caseIgnoreMatch"}])),
+ {ok, #eldap_search_result{entries=[]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"gluffgluff"}])),
+ {ok, #eldap_search_result{entries=[]}} = Search(eldap:extensibleMatch("bar",[{type,"sn"},{matchingRule,"caseExactMatch"}])),
{ok,FB}. %% FIXME
chk_modify(H, FB) ->
diff --git a/lib/eldap/test/eldap_connections_SUITE.erl b/lib/eldap/test/eldap_connections_SUITE.erl
new file mode 100644
index 0000000000..c5460fef09
--- /dev/null
+++ b/lib/eldap/test/eldap_connections_SUITE.erl
@@ -0,0 +1,147 @@
+%%
+%% %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() ->
+ [
+ {group, v4},
+ {group, v6}
+ ].
+
+
+init_per_group(v4, Config) ->
+ [{listen_opts, []},
+ {listen_host, "localhost"},
+ {connect_opts, []}
+ | Config];
+init_per_group(v6, Config) ->
+ {ok, Hostname} = inet:gethostname(),
+ case lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts,[])) of
+ true ->
+ [{listen_opts, [inet6]},
+ {listen_host, "::"},
+ {connect_opts, [{tcpopts,[inet6]}]}
+ | Config];
+ false ->
+ {skip, io_lib:format("~p is not an ipv6_host",[Hostname])}
+ end.
+
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+groups() ->
+ [{v4, [], [tcp_connection, tcp_connection_option]},
+ {v6, [], [tcp_connection, tcp_connection_option]}
+ ].
+
+
+init_per_suite(Config) -> Config.
+
+
+end_per_suite(_Config) -> ok.
+
+
+init_per_testcase(_TestCase, Config) ->
+ case gen_tcp:listen(0, proplists:get_value(listen_opts,Config)) of
+ {ok,LSock} ->
+ {ok,{_,Port}} = inet:sockname(LSock),
+ [{listen_socket,LSock},
+ {listen_port,Port}
+ | Config];
+ Other ->
+ {fail, Other}
+ end.
+
+
+end_per_testcase(_TestCase, Config) ->
+ catch gen_tcp:close( proplists:get_value(listen_socket, Config) ).
+
+%%%================================================================
+%%%
+%%% Test cases
+%%%
+%%%----------------------------------------------------------------
+tcp_connection(Config) ->
+ Host = proplists:get_value(listen_host, Config),
+ Port = proplists:get_value(listen_port, Config),
+ Opts = proplists:get_value(connect_opts, Config),
+ case eldap:open([Host], [{port,Port}|Opts]) of
+ {ok,_H} ->
+ Sl = proplists:get_value(listen_socket, Config),
+ 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) ->
+ Host = proplists:get_value(listen_host, Config),
+ Port = proplists:get_value(listen_port, Config),
+ Opts = proplists:get_value(connect_opts, Config),
+ Sl = proplists:get_value(listen_socket, Config),
+
+ %% 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..432ba2e742 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1,2 +1 @@
-ELDAP_VSN = 1.0.3
-
+ELDAP_VSN = 1.1
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 cbdbbbee80..1075c47801 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -390,8 +390,6 @@ t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(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 = 'fun', content = Es}]) ->
["fun("] ++ t_fun(Es) ++ [")"];
t_type([E = #xmlElement{name = record, content = Es}]) ->
@@ -435,9 +433,10 @@ t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, 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_map_field(#xmlElement{content = [K,V]}) ->
[t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
t_fun(Es) ->
@@ -557,14 +556,12 @@ ot_type([#xmlElement{name = tuple, content = Es}]) ->
ot_tuple(Es);
ot_type([#xmlElement{name = map, content = Es}]) ->
ot_map(Es);
-ot_type([#xmlElement{name = map_field, content = Es}]) ->
- ot_map_field(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).
@@ -616,10 +613,10 @@ ot_tuple(Es) ->
{type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
ot_map(Es) ->
- {type,0,map,[ot_utype_elem(E) || E <- Es]}.
+ {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
-ot_map_field(Es) ->
- {type,0,map_field_assoc,[ot_utype_elem(E) || E <- 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)),
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/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/decode/decode_big.c b/lib/erl_interface/src/decode/decode_big.c
index b54ac85be2..b87d97d634 100644
--- a/lib/erl_interface/src/decode/decode_big.c
+++ b/lib/erl_interface/src/decode/decode_big.c
@@ -151,13 +151,18 @@ int ei_big_comp(erlang_big *x, erlang_big *y)
#endif
#ifdef USE_ISINF_ISNAN /* simulate finite() */
-# define finite(f) (!isinf(f) && !isnan(f))
-# define HAVE_FINITE
+# define isfinite(f) (!isinf(f) && !isnan(f))
+# define HAVE_ISFINITE
+#elif defined(isfinite) && !defined(HAVE_ISFINITE)
+# define HAVE_ISFINITE
+#elif !defined(HAVE_ISFINITE) && defined(HAVE_FINITE)
+# define isfinite finite
+# define HAVE_ISFINITE
#endif
#ifdef NO_FPE_SIGNALS
# define ERTS_FP_CHECK_INIT() do {} while (0)
-# define ERTS_FP_ERROR(f, Action) if (!finite(f)) { Action; } else {}
+# define ERTS_FP_ERROR(f, Action) if (!isfinite(f)) { Action; } else {}
# define ERTS_SAVE_FP_EXCEPTION()
# define ERTS_RESTORE_FP_EXCEPTION()
#else
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/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/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/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/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 06cb035370..4178cb7d4c 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -302,7 +302,7 @@ filename() = string()
will be sent to that process: <c>{http, {RequestId,
stream_start, Headers}}, {http, {RequestId, stream,
BinBodyPart}}, {http, {RequestId, stream_end, Headers}}</c>. When
- streaming to to the calling processes using the option
+ streaming to the calling processes using the option
<c>{self, once}</c> the first message will have an additional
element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}}</c>,
this is the process id that should be used as an argument to
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/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 5674599ac5..8e51b1be5a 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -60,6 +60,7 @@
-define(DATA_ACCEPT_TIMEOUT, infinity).
-define(DEFAULT_MODE, passive).
-define(PROGRESS_DEFAULT, ignore).
+-define(FTP_EXT_DEFAULT, false).
%% Internal Constants
-define(FTP_PORT, 21).
@@ -94,7 +95,8 @@
ipfamily, % inet | inet6 | inet6fb4
progress = ignore, % ignore | pid()
dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity
- tls_upgrading_data_connection = false
+ tls_upgrading_data_connection = false,
+ ftp_extension = ?FTP_EXT_DEFAULT
}).
@@ -969,6 +971,8 @@ start_options(Options) ->
%% timeout
%% dtimeout
%% progress
+%% ftp_extension
+
open_options(Options) ->
?fcrt("open_options", [{options, Options}]),
ValidateMode =
@@ -1013,6 +1017,11 @@ open_options(Options) ->
(_) ->
false
end,
+ ValidateFtpExtension =
+ fun(true) -> true;
+ (false) -> true;
+ (_) -> false
+ end,
ValidOptions =
[{mode, ValidateMode, false, ?DEFAULT_MODE},
{host, ValidateHost, true, ehost},
@@ -1020,7 +1029,8 @@ open_options(Options) ->
{ipfamily, ValidateIpFamily, false, inet},
{timeout, ValidateTimeout, false, ?CONNECTION_TIMEOUT},
{dtimeout, ValidateDTimeout, false, ?DATA_ACCEPT_TIMEOUT},
- {progress, ValidateProgress, false, ?PROGRESS_DEFAULT}],
+ {progress, ValidateProgress, false, ?PROGRESS_DEFAULT},
+ {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}],
validate_options(Options, ValidOptions, []).
tls_options(Options) ->
@@ -1174,12 +1184,14 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) ->
DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
Progress = key_search(progress, Opts, ignore),
IpFamily = key_search(ipfamily, Opts, inet),
+ FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
State2 = State#state{client = From,
mode = Mode,
progress = progress(Progress),
ipfamily = IpFamily,
- dtimeout = DTimeout},
+ dtimeout = DTimeout,
+ ftp_extension = FtpExt},
?fcrd("handle_call(open) -> setup ctrl connection with",
[{host, Host}, {port, Port}, {timeout, Timeout}]),
@@ -1202,11 +1214,13 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT),
DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT),
Progress = key_search(progress, Opts, ignore),
+ FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT),
State2 = State#state{client = From,
mode = Mode,
progress = progress(Progress),
- dtimeout = DTimeout},
+ dtimeout = DTimeout,
+ ftp_extension = FtpExt},
case setup_ctrl_connection(Host, Port, Timeout, State2) of
{ok, State3, WaitTimeout} ->
@@ -1785,7 +1799,8 @@ handle_ctrl_result({pos_compl, Lines},
ipfamily = inet,
client = From,
caller = {setup_data_connection, Caller},
- timeout = Timeout} = State) ->
+ timeout = Timeout,
+ ftp_extension = false} = State) ->
{_, [?LEFT_PAREN | Rest]} =
lists:splitwith(fun(?LEFT_PAREN) -> false; (_) -> true end, Lines),
@@ -1806,6 +1821,28 @@ handle_ctrl_result({pos_compl, Lines},
{noreply,State#state{client = undefined, caller = undefined}}
end;
+handle_ctrl_result({pos_compl, Lines},
+ #state{mode = passive,
+ ipfamily = inet,
+ client = From,
+ caller = {setup_data_connection, Caller},
+ csock = CSock,
+ timeout = Timeout,
+ ftp_extension = true} = State) ->
+
+ [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")),
+ {ok, {IP, _}} = peername(CSock),
+
+ ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]),
+ case connect(IP, list_to_integer(PortStr), Timeout, State) of
+ {ok, _, Socket} ->
+ handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}});
+ {error, _Reason} = Error ->
+ gen_server:reply(From, Error),
+ {noreply, State#state{client = undefined, caller = undefined}}
+ end;
+
+
%% FTP server does not support passive mode: try to fallback on active mode
handle_ctrl_result(_,
#state{mode = passive,
@@ -2157,7 +2194,8 @@ setup_ctrl_connection(Host, Port, Timeout, State) ->
setup_data_connection(#state{mode = active,
caller = Caller,
- csock = CSock} = State) ->
+ csock = CSock,
+ ftp_extension = FtpExt} = State) ->
case (catch sockname(CSock)) of
{ok, {{_, _, _, _, _, _, _, _} = IP, _}} ->
{ok, LSock} =
@@ -2174,11 +2212,18 @@ setup_data_connection(#state{mode = active,
{ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false},
binary, {packet, 0}]),
{ok, Port} = inet:port(LSock),
- {IP1, IP2, IP3, IP4} = IP,
- {Port1, Port2} = {Port div 256, Port rem 256},
- send_ctrl_message(State,
- mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
- [IP1, IP2, IP3, IP4, Port1, Port2])),
+ case FtpExt of
+ false ->
+ {IP1, IP2, IP3, IP4} = IP,
+ {Port1, Port2} = {Port div 256, Port rem 256},
+ send_ctrl_message(State,
+ mk_cmd("PORT ~w,~w,~w,~w,~w,~w",
+ [IP1, IP2, IP3, IP4, Port1, Port2]));
+ true ->
+ IpAddress = inet_parse:ntoa(IP),
+ Cmd = mk_cmd("EPRT |1|~s|~p|", [IpAddress, Port]),
+ send_ctrl_message(State, Cmd)
+ end,
activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection,
{LSock, Caller}}}}
@@ -2191,9 +2236,17 @@ setup_data_connection(#state{mode = passive, ipfamily = inet6,
{noreply, State#state{caller = {setup_data_connection, Caller}}};
setup_data_connection(#state{mode = passive, ipfamily = inet,
- caller = Caller} = State) ->
+ caller = Caller,
+ ftp_extension = false} = State) ->
send_ctrl_message(State, mk_cmd("PASV", [])),
activate_ctrl_connection(State),
+ {noreply, State#state{caller = {setup_data_connection, Caller}}};
+
+setup_data_connection(#state{mode = passive, ipfamily = inet,
+ caller = Caller,
+ ftp_extension = true} = State) ->
+ send_ctrl_message(State, mk_cmd("EPSV", [])),
+ activate_ctrl_connection(State),
{noreply, State#state{caller = {setup_data_connection, Caller}}}.
connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index d152d9f0be..0bbd40d656 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -350,7 +350,7 @@ handle_call(#request{address = Addr} = Request, _,
{reply, ok, State0#state{keep_alive = NewKeepAlive,
session = NewSession}};
undefined ->
- %% Note: tcp-message reciving has already been
+ %% Note: tcp-message receiving has already been
%% activated by handle_pipeline/2.
?hcrd("no current request", []),
cancel_timer(Timers#timers.queue_timer,
@@ -632,7 +632,7 @@ handle_info({timeout, RequestId},
handle_info(timeout_queue, State = #state{request = undefined}) ->
{stop, normal, State};
-%% Timing was such as the pipeline_timout was not canceled!
+%% Timing was such as the queue_timeout was not canceled!
handle_info(timeout_queue, #state{timers = Timers} = State) ->
{noreply, State#state{timers =
Timers#timers{queue_timer = undefined}}};
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 85d303689f..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("");
}
@@ -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;
@@ -978,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);
@@ -1054,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());
@@ -1088,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);
}
}
@@ -1100,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];
@@ -1107,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");
}
@@ -1151,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);
}
}
@@ -1167,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) {
@@ -1199,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;
@@ -1208,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);
@@ -1218,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);
}
}
@@ -1241,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) {
@@ -1248,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.");
}
@@ -1259,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;
@@ -1267,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);
@@ -1276,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);
}
}
@@ -1286,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.");
}
@@ -1305,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);
@@ -1320,7 +1337,7 @@ public abstract class AbstractConnection extends Thread {
if (traceLevel >= handshakeThreshold) {
System.out.println("-> " + "HANDSHAKE sendStatus" + " status="
- + status + " local=" + self);
+ + status + " local=" + localNode);
}
}
@@ -1328,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) {
@@ -1346,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 e7a9d1092c..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
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 c52909acc5..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;
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 84b1355c54..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;
@@ -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 0254edd5da..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;
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 f75e4353d0..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;
@@ -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 a5e202c473..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;
@@ -138,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 f813594541..bab0629382 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -85,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;
}
@@ -108,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);
}
/**
@@ -121,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");
}
@@ -214,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);
}
@@ -233,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);
}
@@ -253,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);
}
@@ -272,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);
}
@@ -288,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;
}
@@ -321,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;
@@ -350,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;
@@ -382,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();
@@ -1055,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);
@@ -1149,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) {
@@ -1166,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 4a4a1e7f8f..fc592c222c 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
@@ -128,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);
}
/**
@@ -350,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
@@ -375,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) {
}
@@ -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 31a5d0fb8f..7c5bc69361 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
@@ -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 c98790bbd4..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)
@@ -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
@@ -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_data/FunEquals.java b/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java
index 14f884cee7..961e462cb3 100644
--- a/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java
+++ b/lib/jinterface/test/jinterface_SUITE_data/FunEquals.java
@@ -17,9 +17,11 @@
* %CopyrightEnd%
*/
-import java.util.Arrays;
-
-import com.ericsson.otp.erlang.*;
+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 {
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/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/notes.xml b/lib/kernel/doc/src/notes.xml
index f92770603a..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>
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/application_master.erl b/lib/kernel/src/application_master.erl
index bc15b5a7de..7cdbe31ab2 100644
--- a/lib/kernel/src/application_master.erl
+++ b/lib/kernel/src/application_master.erl
@@ -103,9 +103,9 @@ call(AppMaster, Req) ->
%%% The reason for not using the logical structrure is that
%%% the application start function is synchronous, and
%%% that the AM is GL. This means that if AM executed the start
-%%% function, and this function uses spawn_request/1
-%%% or io, deadlock would occur. Therefore, this function is
-%%% executed by the process X. Also, AM needs three loops;
+%%% function, and this function uses io, deadlock would occur.
+%%% Therefore, this function is executed by the process X.
+%%% Also, AM needs three loops;
%%% init_loop (waiting for the start function to return)
%%% main_loop
%%% terminate_loop (waiting for the process to die)
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index a375adceea..7f6024f642 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -296,6 +296,7 @@ ctrl_keys(doc) -> ["Tests various control keys"];
ctrl_keys(_Conf) when is_list(_Conf) ->
Cu=[$\^u],
Cw=[$\^w],
+ Cy=[$\^y],
Home=[27,$O,$H],
End=[27,$O,$F],
rtnode([{putline,""},
@@ -308,6 +309,8 @@ ctrl_keys(_Conf) when is_list(_Conf) ->
{putline,"world\"."++Home++"\"hello "}, % test <HOME>
{getline,"\"hello world\""},
{putline,"world"++Home++"\"hello "++End++"\"."}, % test <END>
+ {getline,"\"hello world\""},
+ {putline,"\"hello world\""++Cu++Cy++"."},
{getline,"\"hello world\""}]
++wordLeft()++wordRight(),[]).
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/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/Mnesia_chap3.xml b/lib/mnesia/doc/src/Mnesia_chap3.xml
index d6b4a1c6a1..ae704b4199 100644
--- a/lib/mnesia/doc/src/Mnesia_chap3.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap3.xml
@@ -152,7 +152,7 @@ Transformer =
<c>ignore</c>, it indicates that only the meta data about the table will
be updated. Usage of <c>ignore</c> is not recommended (since it creates
inconsistencies between the meta data and the actual data) but included
- as a possibility for the user do to his own (off-line) transform.</p>
+ as a possibility for the user to do his own (off-line) transform.</p>
</item>
<item><c>change_table_copy_type(Tab, Node, ToType)</c>. This
function changes the storage type of a table. For example, a
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index 72e9bd7e8f..268dc18e65 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -2766,7 +2766,7 @@ raise(Name, Amount) ->
new type. The <c>Fun</c> argument can also be the atom
<c>ignore</c>, it indicates that only the meta data about the table will
be updated. Usage of <c>ignore</c> is not recommended but included
- as a possibility for the user do to his own transform.
+ as a possibility for the user to do his own transform.
<c>NewAttributeList</c> and <c>NewRecordName</c>
specifies the attributes and the new record type of converted
table. Table name will always remain unchanged, if the
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index df2c27afba..e5c7d87f52 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -38,7 +38,26 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.12.2</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>
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index d16d501103..d5b96c5c76 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.12.2
+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/doc/src/observer_ug.xml b/lib/observer/doc/src/observer_ug.xml
index 3aeaf1997a..62f99c5210 100644
--- a/lib/observer/doc/src/observer_ug.xml
+++ b/lib/observer/doc/src/observer_ug.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2011</year><year>2013</year>
+ <year>2011</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -54,9 +54,6 @@
impact only the active viewer is updated and the other
views will be updated when activated.
</p>
- <note>
- <p>Only R15B nodes can be observed.</p>
- </note>
<p> In general the mouse buttons behaves as expected, use left click
to select objects, right click to pop up a menu with most used
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/configure.in b/lib/odbc/configure.in
index ea5c51965f..0cfcb9964b 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -136,7 +136,7 @@ AC_SUBST(THR_LIBS)
odbc_lib_link_success=no
AC_SUBST(TARGET_FLAGS)
case $host_os in
- darwin1[[0-2]].*|darwin[[0-9]].*)
+ darwin1[[0-4]].*|darwin[[0-9]].*)
TARGET_FLAGS="-DUNIX"
if test ! -d "$with_odbc" || test "$with_odbc" = "yes"; then
ODBC_LIB= -L"/usr/lib"
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/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/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/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/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/parsetools/include/leexinc.hrl b/lib/parsetools/include/leexinc.hrl
index dbbb688d2d..938aef58f9 100644
--- a/lib/parsetools/include/leexinc.hrl
+++ b/lib/parsetools/include/leexinc.hrl
@@ -36,8 +36,8 @@ string(Ics0, L0, Tcs, Ts) ->
string_cont(Ics1, L1, yyaction(A, Alen, Tcs, L0), Ts);
{reject,_Alen,Tlen,_Ics1,L1,_S1} -> % After a non-accepting state
{error,{L0,?MODULE,{illegal,yypre(Tcs, Tlen+1)}},L1};
- {A,Alen,_Tlen,_Ics1,L1,_S1} ->
- string_cont(yysuf(Tcs, Alen), L1, yyaction(A, Alen, Tcs, L0), Ts)
+ {A,Alen,_Tlen,_Ics1,_L1,_S1} ->
+ string_cont(yysuf(Tcs, Alen), L0, yyaction(A, Alen, Tcs, L0), Ts)
end.
%% string_cont(RestChars, Line, Token, Tokens)
@@ -105,8 +105,8 @@ token(S0, Ics0, L0, Tcs, Tlen0, Tline, A0, Alen0) ->
{reject,_Alen1,Tlen1,Ics1,L1,_S1} -> % No token match
Error = {Tline,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
{done,{error,Error,L1},Ics1};
- {A1,Alen1,_Tlen1,_Ics1,L1,_S1} -> % Use last accept match
- token_cont(yysuf(Tcs, Alen1), L1, yyaction(A1, Alen1, Tcs, Tline))
+ {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} -> % Use last accept match
+ token_cont(yysuf(Tcs, Alen1), L0, yyaction(A1, Alen1, Tcs, Tline))
end.
%% token_cont(RestChars, Line, Token)
@@ -177,9 +177,9 @@ tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Ts, A0, Alen0) ->
%% Skip rest of tokens.
Error = {L1,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}},
skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error);
- {A1,Alen1,_Tlen1,_Ics1,L1,_S1} ->
+ {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} ->
Token = yyaction(A1, Alen1, Tcs, Tline),
- tokens_cont(yysuf(Tcs, Alen1), L1, Token, Ts)
+ tokens_cont(yysuf(Tcs, Alen1), L0, Token, Ts)
end.
%% tokens_cont(RestChars, Line, Token, Tokens)
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index eb15bebf63..6d2afe061e 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -43,8 +43,8 @@
file/1, compile/1, syntax/1,
pt/1, man/1, ex/1, ex2/1, not_yet/1,
-
- otp_10302/1, otp_11286/1, unicode/1]).
+ line_wrap/1,
+ otp_10302/1, otp_11286/1, unicode/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -61,12 +61,13 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, checks}, {group, examples}].
+ [{group, checks}, {group, examples}, {group, bugs}].
groups() ->
[{checks, [], [file, compile, syntax]},
{examples, [], [pt, man, ex, ex2, not_yet, unicode]},
- {tickets, [], [otp_10302, otp_11286]}].
+ {tickets, [], [otp_10302, otp_11286]},
+ {bugs, [], [line_wrap]}].
init_per_suite(Config) ->
Config.
@@ -871,6 +872,48 @@ scan_token_1({more, Cont}, [C | Cs], Fun, Loc, Rs) ->
%% End of ex2
+line_wrap(doc) -> "Much more examples.";
+line_wrap(suite) -> [];
+line_wrap(Config) when is_list(Config) ->
+ Xrl =
+ <<"
+Definitions.
+Rules.
+[a]+[\\n]*= : {token, {first, TokenLine}}.
+[a]+ : {token, {second, TokenLine}}.
+[\\s\\r\\n\\t]+ : skip_token.
+Erlang code.
+ ">>,
+ Dir = ?privdir,
+ XrlFile = filename:join(Dir, "test_line_wrap.xrl"),
+ ?line ok = file:write_file(XrlFile, Xrl),
+ ErlFile = filename:join(Dir, "test_line_wrap.erl"),
+ {ok, _} = leex:file(XrlFile, []),
+ {ok, _} = compile:file(ErlFile, [{outdir,Dir}]),
+ code:purge(test_line_wrap),
+ AbsFile = filename:rootname(ErlFile, ".erl"),
+ code:load_abs(AbsFile, test_line_wrap),
+ fun() ->
+ S = "aaa\naaa",
+ {ok,[{second,1},{second,2}],2} = test_line_wrap:string(S)
+ end(),
+ fun() ->
+ S = "aaa\naaa",
+ {ok,[{second,3},{second,4}],4} = test_line_wrap:string(S, 3)
+ end(),
+ fun() ->
+ {done,{ok,{second,1},1},"\na"} = test_line_wrap:token([], "a\na"),
+ {more,Cont1} = test_line_wrap:token([], "\na"),
+ {done,{ok,{second,2},2},eof} = test_line_wrap:token(Cont1, eof)
+ end(),
+ fun() ->
+ {more,Cont1} = test_line_wrap:tokens([], "a\na"),
+ {done,{ok,[{second,1},{second,2}],2},eof} = test_line_wrap:tokens(Cont1, eof)
+ end(),
+ ok.
+
+%% End of line_wrap
+
not_yet(doc) ->
"Not yet implemented.";
not_yet(suite) -> [];
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/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 88b1a9248e..e3473f80d7 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -431,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>
@@ -442,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>
@@ -451,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>
@@ -464,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()} |
@@ -493,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>
@@ -508,7 +539,7 @@ fun(OtpCert :: #'OTPCertificate'{},
</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/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/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..f3db05192e 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -29,6 +29,153 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 3.0.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixes of login blocking after port scanning.</p>
+ <p>
+ Own Id: OTP-12247 Aux Id: seq12726 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 3.0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add option sftp_vsn to SFTP</p>
+ <p>
+ Own Id: OTP-12227</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix option user_interaction to work as expected. When
+ password authentication is implemented with ssh
+ keyboard-interactive method and the password is already
+ supplied, so that we do not need to query user, then
+ connections should succeed even though user_interaction
+ option is set to false.</p>
+ <p>
+ Own Id: OTP-11329 Aux Id: seq12420, seq12335 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 72e7252536..ff72cf7ee0 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.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>
@@ -137,7 +137,7 @@
<tag><c><![CDATA[{pty, ssh_channel_id(),
boolean() = WantReply, {string() = Terminal, integer() = CharWidth,
- integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight,
+ integer() = RowHeight, integer() = PixelWidth, integer() = PixelHeight,
[{atom() | integer() = Opcode,
integer() = Value}] = TerminalModes}}]]></c></tag>
<item>A pseudo-terminal has been requested for the
@@ -148,11 +148,11 @@
drawable area of the window. The <c>Opcode</c> in the
<c>TerminalModes</c> list is the mnemonic name, represented
as an lowercase erlang atom, defined in
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8,
- or the opcode if the mnemonic name is not listed in the
+ <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8.
+ It may also be an opcode if the mnemonic name is not listed in the
RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom:
- echo</c>. There is currently no API function to generate this
- event.</item>
+ echo</c>.This event is sent as result of calling <seealso
+ marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso></item>
<tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag>
<item> This message will request that the user's default shell
@@ -273,7 +273,52 @@
</desc>
</func>
- <func>
+ <func>
+ <name>ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> success | failure</name>
+ <fsummary>Send status replies to requests that want such replies. </fsummary>
+ <type>
+ <v> ConnectionRef = ssh_connection_ref() </v>
+ <v> ChannelId = ssh_channel_id()</v>
+ <v> Options = proplists:proplist()</v>
+ </type>
+ <desc>
+ <p> Sends a SSH Connection Protocol pty_req, to allocate a pseudo tty.
+ Should be called by a SSH client process.
+ Options are:
+ </p>
+
+ <taglist>
+ <tag>{term, string()}</tag>
+ <item>
+ Defaults to os:getenv("TERM") or "vt100" if it is undefined.
+ </item>
+ <tag>{width, integer()}</tag>
+ <item>
+ Defaults to 80 if pixel_width is not defined.
+ </item>
+ <tag>{height, integer()}</tag>
+ <item>
+ Defaults to 24 if pixel_height is not defined.
+ </item>
+ <tag>{pixel_width, integer()}</tag>
+ <item>
+ Is disregarded if width is defined.
+ </item>
+ <tag>{pixel_height, integer()}</tag>
+ <item>
+ Is disregarded if height is defined.
+ </item>
+ <tag>{pty_opts, [{posix_atom(), integer()}]}</tag>
+ <item>
+ Option may be an empty list, otherwise
+ see possible POSIX names in section 8 in <url href="http://www.ietf.org/rfc/rfc4254.txt"> RFC 4254</url>.
+ </item>
+ </taglist>
+
+ </desc>
+ </func>
+
+ <func>
<name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name>
<fsummary>Send status replies to requests that want such replies. </fsummary>
<type>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index e55d092fe2..f1091e9eca 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2005</year><year>2013</year>
+ <year>2005</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -81,6 +81,17 @@
<p>The timeout is passed to the ssh_channel start function,
and defaults to infinity.</p>
</item>
+ <tag>
+ <p><c><![CDATA[{sftp_vsn, integer()}]]></c></p>
+ </tag>
+ <item>
+ <p>
+ Desired SFTP protocol version.
+ The actual version will be the minimum of
+ the desired version and the maximum supported
+ versions by the SFTP server.
+ </p>
+ </item>
</taglist>
<p>All other options are directly passed to
<seealso marker="ssh">ssh:connect/3</seealso> or ignored if a
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 2ef2859fd7..90d71107ad 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -65,6 +65,7 @@ MODULES= \
ssh_cli \
ssh_file \
ssh_io \
+ ssh_info \
ssh_math \
ssh_message \
ssh_no_io \
@@ -115,7 +116,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.app.src b/lib/ssh/src/ssh.app.src
index e0a51b3574..4ad55b34ca 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -23,6 +23,7 @@
sshd_sup,
ssh_file,
ssh_io,
+ ssh_info,
ssh_math,
ssh_no_io,
ssh_server_key_api,
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 8269f89e40..600c01454c 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,25 +19,49 @@
{"%VSN%",
[
- {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
+ {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
{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_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
{<<".*">>, [{restart_application, ssh}]}
],
[
- {"3.0.2", [{load_module, ssh_message, soft_purge, soft_purge, []},
+ {"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
{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_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
+ {"3.0.6", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_connection_handler, soft_purge, soft_purge, []},
- {load_module, ssh_io, soft_purge, soft_purge, []}]},
+ {load_module, ssh_info, soft_purge, soft_purge, []},
+ {load_module, ssh_message, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_io, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, [ssh_connection_handler]}]},
{<<".*">>, [{restart_application, ssh}]}
]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 8a8d4bb89e..eae33e3683 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -194,6 +194,7 @@ shell(Host, Port, Options) ->
{ok, ConnectionRef} ->
case ssh_connection:session_channel(ConnectionRef, infinity) of
{ok,ChannelId} ->
+ success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []),
Args = [{channel_cb, ssh_shell},
{init_args,[ConnectionRef, ChannelId]},
{cm, ConnectionRef}, {channel_id, ChannelId}],
@@ -234,22 +235,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.
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 7302196674..6c443eeb9c 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -22,7 +22,8 @@
-module(ssh_acceptor).
%% Internal application API
--export([start_link/5]).
+-export([start_link/5,
+ number_of_connections/1]).
%% spawn export
-export([acceptor_init/6, acceptor_loop/6]).
@@ -140,5 +141,6 @@ handle_error(Reason) ->
number_of_connections(SystemSup) ->
length([X ||
{R,X,supervisor,[ssh_subsystem_sup]} <- supervisor:which_children(SystemSup),
+ is_pid(X),
is_reference(R)
]).
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_auth.erl b/lib/ssh/src/ssh_auth.erl
index 45fd907383..45c4d52d7e 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.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
@@ -119,8 +119,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
false ->
FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG),
SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg, SecondAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
@@ -130,15 +129,13 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
case length(Algs) =:= 2 of
true ->
SecondAlg = other_alg(FirstAlg),
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg, SecondAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
service = "ssh-connection"});
_ ->
- AllowUserInt = proplists:get_value(user_interaction, Opts, true),
- Prefs = method_preference(FirstAlg, AllowUserInt),
+ Prefs = method_preference(FirstAlg),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
userauth_methods = none,
@@ -187,9 +184,8 @@ handle_userauth_request(#ssh_msg_service_request{name =
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
method = "password",
- data = Data}, _,
+ data = <<?FALSE, ?UINT32(Sz), BinPwd:Sz/binary>>}, _,
#ssh{opts = Opts} = Ssh) ->
- <<_:8, ?UINT32(Sz), BinPwd:Sz/binary>> = Data,
Password = unicode:characters_to_list(BinPwd),
case check_password(User, Password, Opts) of
true ->
@@ -204,6 +200,27 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User,
handle_userauth_request(#ssh_msg_userauth_request{user = User,
service = "ssh-connection",
+ method = "password",
+ data = <<?TRUE,
+ _/binary
+ %% ?UINT32(Sz1), OldBinPwd:Sz1/binary,
+ %% ?UINT32(Sz2), NewBinPwd:Sz2/binary
+ >>
+ }, _,
+ Ssh) ->
+ %% Password change without us having sent SSH_MSG_USERAUTH_PASSWD_CHANGEREQ (because we never do)
+ %% RFC 4252 says:
+ %% SSH_MSG_USERAUTH_FAILURE without partial success - The password
+ %% has not been changed. Either password changing was not supported,
+ %% or the old password was bad.
+
+ {not_authorized, {User, {error,"Password change not supported"}},
+ ssh_transport:ssh_packet(#ssh_msg_userauth_failure{
+ authentications = "",
+ partial_success = false}, Ssh)};
+
+handle_userauth_request(#ssh_msg_userauth_request{user = User,
+ service = "ssh-connection",
method = "none"}, _,
#ssh{userauth_supported_methods = Methods} = Ssh) ->
{not_authorized, {User, undefined},
@@ -256,15 +273,12 @@ handle_userauth_info_request(
data = Data}, IoCb,
#ssh{opts = Opts} = Ssh) ->
PromptInfos = decode_keyboard_interactive_prompts(NumPrompts,Data),
- Resps = keyboard_interact_get_responses(IoCb, Opts,
+ Responses = keyboard_interact_get_responses(IoCb, Opts,
Name, Instr, PromptInfos),
- RespBin = list_to_binary(
- lists:map(fun(S) -> <<?STRING(list_to_binary(S))>> end,
- Resps)),
{ok,
ssh_transport:ssh_packet(
#ssh_msg_userauth_info_response{num_responses = NumPrompts,
- data = RespBin}, Ssh)}.
+ data = Responses}, Ssh)}.
handle_userauth_info_response(#ssh_msg_userauth_info_response{},
_Auth) ->
@@ -276,25 +290,16 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{},
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-method_preference(Alg1, Alg2, true) ->
+method_preference(Alg1, Alg2) ->
[{"publickey", ?MODULE, publickey_msg, [Alg1]},
{"publickey", ?MODULE, publickey_msg,[Alg2]},
{"password", ?MODULE, password_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ];
-method_preference(Alg1, Alg2, false) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"publickey", ?MODULE, publickey_msg,[Alg2]},
- {"password", ?MODULE, password_msg, []}
].
-method_preference(Alg1, true) ->
+method_preference(Alg1) ->
[{"publickey", ?MODULE, publickey_msg, [Alg1]},
{"password", ?MODULE, password_msg, []},
{"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []}
- ];
-method_preference(Alg1, false) ->
- [{"publickey", ?MODULE, publickey_msg, [Alg1]},
- {"password", ?MODULE, password_msg, []}
].
user_name(Opts) ->
@@ -362,35 +367,29 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
algorithm_string('ssh-rsa') ->
"ssh-rsa";
algorithm_string('ssh-dss') ->
- "ssh-dss".
+ "ssh-dss".
decode_keyboard_interactive_prompts(_NumPrompts, Data) ->
ssh_message:decode_keyboard_interactive_prompts(Data, []).
keyboard_interact_get_responses(IoCb, Opts, Name, Instr, PromptInfos) ->
NumPrompts = length(PromptInfos),
- case proplists:get_value(keyboard_interact_fun, Opts) of
- undefined when NumPrompts == 1 ->
- %% Special case/fallback for just one prompt
- %% (assumed to be the password prompt)
- case proplists:get_value(password, Opts) of
- undefined -> keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
- PW -> [PW]
- end;
- undefined ->
- keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
- KbdInteractFun ->
- Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
- PromptInfos),
- case KbdInteractFun(Name, Instr, Prompts) of
- Rs when length(Rs) == NumPrompts ->
- Rs;
- Rs ->
- erlang:error({mismatching_number_of_responses,
- {got,Rs},
- {expected,NumPrompts}})
- end
- end.
+ keyboard_interact_get_responses(proplists:get_value(user_interaction, Opts, true),
+ proplists:get_value(keyboard_interact_fun, Opts),
+ proplists:get_value(password, Opts, undefined), IoCb, Name,
+ Instr, PromptInfos, Opts, NumPrompts).
+
+keyboard_interact_get_responses(_, undefined, Password, _, _, _, _, _,
+ 1) when Password =/= undefined ->
+ [Password]; %% Password auth implemented with keyboard-interaction and passwd is known
+keyboard_interact_get_responses(_, _, _, _, _, _, _, _, 0) ->
+ [""];
+keyboard_interact_get_responses(false, undefined, undefined, _, _, _, [Prompt|_], Opts, _) ->
+ ssh_no_io:read_line(Prompt, Opts); %% Throws error as keyboard interaction is not allowed
+keyboard_interact_get_responses(true, undefined, _,IoCb, Name, Instr, PromptInfos, Opts, _) ->
+ keyboard_interact(IoCb, Name, Instr, PromptInfos, Opts);
+keyboard_interact_get_responses(true, Fun, _, Name, Instr, PromptInfos, _, _, NumPrompts) ->
+ keyboard_interact_fun(Fun, Name, Instr, PromptInfos, NumPrompts).
keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
if Name /= "" -> IoCb:format("~s", [Name]);
@@ -404,6 +403,21 @@ keyboard_interact(IoCb, Name, Instr, Prompts, Opts) ->
end,
Prompts).
+keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
+ Prompts = lists:map(fun({Prompt, _Echo}) -> Prompt end,
+ PromptInfos),
+ case KbdInteractFun(Name, Instr, Prompts) of
+ Rs when length(Rs) == NumPrompts ->
+ Rs;
+ Rs ->
+ throw({mismatching_number_of_responses,
+ {got,Rs},
+ {expected, NumPrompts},
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = "User interaction failed",
+ language = "en"}})
+ end.
+
other_alg('ssh-rsa') ->
'ssh-dss';
other_alg('ssh-dss') ->
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 508ae637cf..5c24f362b1 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -67,7 +67,8 @@
%% Internal application API
-export([cache_create/0, cache_lookup/2, cache_update/2,
cache_delete/1, cache_delete/2, cache_foldl/3,
- cache_find/2]).
+ cache_find/2,
+ get_print_info/1]).
-record(state, {
cm,
@@ -190,6 +191,14 @@ init([Options]) ->
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
+handle_call(get_print_info, _From, State) ->
+ Reply =
+ {{State#state.cm,
+ State#state.channel_id},
+ io_lib:format('CB=~p',[State#state.channel_cb])
+ },
+ {reply, Reply, State};
+
handle_call(Request, From, #state{channel_cb = Module,
channel_state = ChannelState} = State) ->
try Module:handle_call(Request, From, ChannelState) of
@@ -333,6 +342,9 @@ cache_find(ChannelPid, Cache) ->
Channel
end.
+get_print_info(Pid) ->
+ call(Pid, get_print_info, 1000).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 8421b07167..d14f7ce27d 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).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -164,6 +165,10 @@
recipient_channel
}).
+-define(TERMINAL_WIDTH, 80).
+-define(TERMINAL_HEIGHT, 24).
+-define(DEFAULT_TERMINAL, "vt100").
+
-define(TTY_OP_END,0). %% Indicates end of options.
-define(VINTR,1). %% Interrupt character; 255 if none. Similarly for the
%% other characters. Not all of these characters are
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 33849f4527..593443e11c 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.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
@@ -32,11 +32,11 @@
%% API
-export([session_channel/2, session_channel/4,
exec/4, shell/2, subsystem/4, send/3, send/4, send/5,
- send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4]).
+ send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4,
+ ptty_alloc/3, ptty_alloc/4]).
%% Potential API currently unsupported and not tested
--export([open_pty/3, open_pty/7,
- open_pty/9, window_change/4, window_change/6,
+-export([window_change/4, window_change/6,
direct_tcpip/6, direct_tcpip/8, tcpip_forward/3,
cancel_tcpip_forward/3, signal/3, exit_status/3]).
@@ -107,9 +107,15 @@ shell(ConnectionHandler, ChannelId) ->
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
- ssh_connection_handler:request(ConnectionHandler, self(),
- ChannelId, "subsystem",
- true, [?string(SubSystem)], TimeOut).
+ case ssh_connection_handler:request(ConnectionHandler, self(),
+ ChannelId, "subsystem",
+ true, [?string(SubSystem)], TimeOut) of
+ success -> success;
+ failure -> failure;
+ {error,timeout} -> {error,timeout};
+ _ -> failure
+ end.
+
%%--------------------------------------------------------------------
-spec send(pid(), channel_id(), iodata()) ->
ok | {error, closed}.
@@ -183,6 +189,25 @@ reply_request(_,false, _, _) ->
ok.
%%--------------------------------------------------------------------
+-spec ptty_alloc(pid(), channel_id(), proplists:proplist()) -> success | failiure.
+%%
+%%
+%% Description: Sends a ssh connection protocol pty_req.
+%%--------------------------------------------------------------------
+ptty_alloc(ConnectionHandler, Channel, Options) ->
+ ptty_alloc(ConnectionHandler, Channel, Options, infinity).
+ptty_alloc(ConnectionHandler, Channel, Options, TimeOut) ->
+ {Width, PixWidth} = pty_default_dimensions(width, Options),
+ {Hight, PixHight} = pty_default_dimensions(hight, Options),
+ pty_req(ConnectionHandler, Channel,
+ proplists:get_value(term, Options, default_term()),
+ proplists:get_value(width, Options, Width),
+ proplists:get_value(hight, Options, Hight),
+ proplists:get_value(pixel_widh, Options, PixWidth),
+ proplists:get_value(pixel_hight, Options, PixHight),
+ proplists:get_value(pty_opts, Options, []), TimeOut
+ ).
+%%--------------------------------------------------------------------
%% Not yet officialy supported! The following functions are part of the
%% initial contributed ssh application. They are untested. Do we want them?
%% Should they be documented and tested?
@@ -205,23 +230,6 @@ exit_status(ConnectionHandler, Channel, Status) ->
ssh_connection_handler:request(ConnectionHandler, Channel,
"exit-status", false, [?uint32(Status)], 0).
-open_pty(ConnectionHandler, Channel, TimeOut) ->
- open_pty(ConnectionHandler, Channel,
- os:getenv("TERM"), 80, 24, [], TimeOut).
-
-open_pty(ConnectionHandler, Channel, Term, Width, Height, PtyOpts, TimeOut) ->
- open_pty(ConnectionHandler, Channel, Term, Width,
- Height, 0, 0, PtyOpts, TimeOut).
-
-open_pty(ConnectionHandler, Channel, Term, Width, Height,
- PixWidth, PixHeight, PtyOpts, TimeOut) ->
- ssh_connection_handler:request(ConnectionHandler,
- Channel, "pty-req", true,
- [?string(Term),
- ?uint32(Width), ?uint32(Height),
- ?uint32(PixWidth),?uint32(PixHeight),
- encode_pty_opts(PtyOpts)], TimeOut).
-
direct_tcpip(ConnectionHandler, RemoteHost,
RemotePort, OrigIP, OrigPort, Timeout) ->
direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort,
@@ -1074,6 +1082,27 @@ flow_control([_|_], #channel{flow_control = From,
flow_control(_,_,_) ->
[].
+pty_req(ConnectionHandler, Channel, Term, Width, Height,
+ PixWidth, PixHeight, PtyOpts, TimeOut) ->
+ ssh_connection_handler:request(ConnectionHandler,
+ Channel, "pty-req", true,
+ [?string(Term),
+ ?uint32(Width), ?uint32(Height),
+ ?uint32(PixWidth),?uint32(PixHeight),
+ encode_pty_opts(PtyOpts)], TimeOut).
+
+pty_default_dimensions(Dimension, Options) ->
+ case proplists:get_value(Dimension, Options, 0) of
+ N when is_integer(N), N > 0 ->
+ {N, 0};
+ _ ->
+ case proplists:get_value(list_to_atom("pixel_" ++ atom_to_list(Dimension)), Options, 0) of
+ N when is_integer(N), N > 0 ->
+ {0, N};
+ _ ->
+ {?TERMINAL_WIDTH, 0}
+ end
+ end.
encode_pty_opts(Opts) ->
Bin = list_to_binary(encode_pty_opts2(Opts)),
@@ -1271,3 +1300,10 @@ decode_ip(Addr) when is_binary(Addr) ->
{ok,A} -> A
end.
+default_term() ->
+ case os:getenv("TERM") of
+ false ->
+ ?DEFAULT_TERMINAL;
+ Str when is_list(Str)->
+ Str
+ end.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 86804c4436..8b7c4a5f80 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
@@ -41,14 +41,16 @@
global_request/4, send/5, send_eof/2, info/1, info/2,
connection_info/2, channel_info/3,
adjust_window/3, close/2, stop/1, renegotiate/1, renegotiate_data/1,
- start_connection/4]).
+ start_connection/4,
+ get_print_info/1]).
%% gen_fsm callbacks
-export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
- userauth/2, connected/2]).
+ userauth/2, connected/2,
+ error/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 +73,8 @@
connection_queue,
address,
port,
- opts
+ opts,
+ recbuf
}).
-type state_name() :: hello | kexinit | key_exchange | new_keys | userauth | connection.
@@ -103,12 +106,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 +130,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) ->
@@ -162,9 +173,23 @@ init([Role, Socket, SshOpts]) ->
State#state{ssh_params = Ssh})
catch
_:Error ->
- gen_fsm:enter_loop(?MODULE, [], error, {Error, State0})
+ gen_fsm:enter_loop(?MODULE, [], error, {Error, State})
end.
+%% Temporary fix for the Nessus error. SYN-> <-SYNACK ACK-> RST-> ?
+error(_Event, {Error,State=#state{}}) ->
+ case Error of
+ {badmatch,{error,enotconn}} ->
+ %% {error,enotconn} probably from inet:peername in
+ %% init_ssh(server,..)/5 called from init/1
+ {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}, State};
+ _ ->
+ {stop, {shutdown,{init,Error}}, State}
+ end;
+error(Event, State) ->
+ %% State deliberately not checked beeing #state. This is a panic-clause...
+ {stop, {shutdown,{init,{spurious_error,Event}}}, State}.
+
%%--------------------------------------------------------------------
-spec open_channel(pid(), string(), iodata(), integer(), integer(),
timeout()) -> {open, channel_id()} | {error, term()}.
@@ -231,6 +256,9 @@ send_eof(ConnectionHandler, ChannelId) ->
%%--------------------------------------------------------------------
-spec connection_info(pid(), [atom()]) -> proplists:proplist().
%%--------------------------------------------------------------------
+get_print_info(ConnectionHandler) ->
+ sync_send_all_state_event(ConnectionHandler, get_print_info, 1000).
+
connection_info(ConnectionHandler, Options) ->
sync_send_all_state_event(ConnectionHandler, {connection_info, Options}).
@@ -293,28 +321,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"},
@@ -530,7 +569,7 @@ connected({#ssh_msg_kexinit{}, _Payload} = Event, State) ->
%%--------------------------------------------------------------------
handle_event(#ssh_msg_disconnect{description = Desc} = DisconnectMsg, _StateName, #state{} = State) ->
- handle_disconnect(DisconnectMsg, State),
+ handle_disconnect(peer, DisconnectMsg, State),
{stop, {shutdown, Desc}, State};
handle_event(#ssh_msg_ignore{}, StateName, State) ->
@@ -585,7 +624,7 @@ handle_event(renegotiate, connected, #state{ssh_params = Ssh0}
renegotiate = true})};
handle_event(renegotiate, StateName, State) ->
- timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiatie]),
+ timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
%% Allready in keyexcahange so ignore
{next_state, StateName, State};
@@ -738,6 +777,20 @@ handle_sync_event({recv_window, ChannelId}, _From, StateName,
end,
{reply, Reply, StateName, next_packet(State)};
+handle_sync_event(get_print_info, _From, StateName, State) ->
+ Reply =
+ try
+ {inet:sockname(State#state.socket),
+ inet:peername(State#state.socket)
+ }
+ of
+ {{ok,Local}, {ok,Remote}} -> {{Local,Remote},io_lib:format("statename=~p",[StateName])};
+ _ -> {{"-",0},"-"}
+ catch
+ _:_ -> {{"?",0},"?"}
+ end,
+ {reply, Reply, StateName, State};
+
handle_sync_event({connection_info, Options}, _From, StateName, State) ->
Info = ssh_info(Options, State, []),
{reply, Info, StateName, State};
@@ -916,6 +969,10 @@ terminate(normal, _, #state{transport_cb = Transport,
(catch Transport:close(Socket)),
ok;
+terminate({shutdown,{init,Reason}}, StateName, State) ->
+ error_logger:info_report(io_lib:format("Erlang ssh in connection handler init: ~p~n",[Reason])),
+ terminate(normal, StateName, State);
+
%% Terminated by supervisor
terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
DisconnectMsg =
@@ -931,8 +988,10 @@ terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName,
{SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh});
+
terminate({shutdown, _}, StateName, State) ->
terminate(normal, StateName, State);
+
terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
connection_state = Connection} = State) ->
terminate_subsytem(Connection),
@@ -945,12 +1004,43 @@ terminate(Reason, StateName, #state{ssh_params = Ssh0, starter = _Pid,
send_msg(SshPacket, State),
terminate(normal, StateName, State#state{ssh_params = Ssh}).
+
terminate_subsytem(#connection{system_supervisor = SysSup,
sub_system_supervisor = SubSysSup}) when is_pid(SubSysSup) ->
ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
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{}}.
@@ -1111,7 +1201,10 @@ send_all_state_event(FsmPid, Event) ->
gen_fsm:send_all_state_event(FsmPid, Event).
sync_send_all_state_event(FsmPid, Event) ->
- try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity)
+ sync_send_all_state_event(FsmPid, Event, infinity).
+
+sync_send_all_state_event(FsmPid, Event, Timeout) ->
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
catch
exit:{noproc, _} ->
{error, closed};
@@ -1208,13 +1301,23 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
generate_event(Msg, StateName, State0, EncData) ->
Event = ssh_message:decode(Msg),
State = generate_event_new_state(State0, EncData),
- case Event of
- #ssh_msg_kexinit{} ->
- %% We need payload for verification later.
- event({Event, Msg}, StateName, State);
- _ ->
- event(Event, StateName, State)
- end.
+ try
+ case Event of
+ #ssh_msg_kexinit{} ->
+ %% We need payload for verification later.
+ event({Event, Msg}, StateName, State);
+ _ ->
+ event(Event, StateName, State)
+ end
+ catch
+ _:_ ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Encountered unexpected input",
+ language = "en"},
+ handle_disconnect(DisconnectMsg, State)
+ end.
+
handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From,
@@ -1392,17 +1495,27 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
handle_disconnect(DisconnectMsg, State0)
end.
-handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
- role = Role} = State0) ->
+handle_disconnect(DisconnectMsg, State) ->
+ handle_disconnect(own, DisconnectMsg, State).
+
+handle_disconnect(#ssh_msg_disconnect{} = DisconnectMsg, State, Error) ->
+ handle_disconnect(own, DisconnectMsg, State, Error);
+handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0, role = Role} = State0) ->
{disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(Replies, State0),
+ State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
{stop, {shutdown, Desc}, State#state{connection_state = Connection}}.
-handle_disconnect(#ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
- role = Role} = State0, ErrorMsg) ->
+
+handle_disconnect(Type, #ssh_msg_disconnect{description = Desc} = Msg, #state{connection_state = Connection0,
+ role = Role} = State0, ErrorMsg) ->
{disconnect, _, {{replies, Replies}, Connection}} = ssh_connection:handle_msg(Msg, Connection0, Role),
- State = send_replies(Replies, State0),
+ State = send_replies(disconnect_replies(Type, Msg, Replies), State0),
{stop, {shutdown, {Desc, ErrorMsg}}, State#state{connection_state = Connection}}.
+disconnect_replies(own, Msg, Replies) ->
+ [{connection_reply, Msg} | Replies];
+disconnect_replies(peer, _, Replies) ->
+ Replies.
+
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
new file mode 100644
index 0000000000..9ed598b3ab
--- /dev/null
+++ b/lib/ssh/src/ssh_info.erl
@@ -0,0 +1,193 @@
+%%
+%% %CopyrightBegin%
+%%
+%% 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Print some info of a running ssh aplication.
+%%----------------------------------------------------------------------
+
+-module(ssh_info).
+
+-compile(export_all).
+
+print() ->
+ try supervisor:which_children(ssh_sup)
+ of
+ _ ->
+ io:nl(),
+ print_general(),
+ io:nl(),
+ underline("Client part", $=),
+ print_clients(),
+ io:nl(),
+ underline("Server part", $=),
+ print_servers(),
+ io:nl(),
+ %% case os:type() of
+ %% {unix,_} ->
+ %% io:nl(),
+ %% underline("Linux part", $=),
+ %% underline("Listening"),
+ %% catch io:format(os:cmd("netstat -tpln")),
+ %% io:nl(),
+ %% underline("Other"),
+ %% catch io:format(os:cmd("netstat -tpn"));
+ %% _ -> ok
+ %% end,
+ underline("Supervisors", $=),
+ walk_sups(ssh_sup),
+ io:nl()
+ catch
+ _:_ ->
+ io:format("Ssh not found~n",[])
+ end.
+
+%%%================================================================
+print_general() ->
+ {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()),
+ underline(io_lib:format("~s ~s", [Slogan, Ver]), $=),
+ io:format('This printout is generated ~s. ~n',[datetime()]).
+
+%%%================================================================
+print_clients() ->
+ try
+ lists:foreach(fun print_client/1, supervisor:which_children(sshc_sup))
+ catch
+ C:E ->
+ io:format('***FAILED: ~p:~p~n',[C,E])
+ end.
+
+print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
+ {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid),
+ io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+print_client(Other) ->
+ io:format(" [[Other 1: ~p]]~n",[Other]).
+
+
+%%%================================================================
+print_servers() ->
+ try
+ lists:foreach(fun print_server/1, supervisor:which_children(sshd_sup))
+ catch
+ C:E ->
+ io:format('***FAILED: ~p:~p~n',[C,E])
+ end.
+
+print_server({{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
+ io:format('Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}),
+ ssh_acceptor:number_of_connections(Pid)]),
+ lists:foreach(fun print_system_sup/1, supervisor:which_children(Pid));
+print_server(Other) ->
+ io:format(" [[Other 2: ~p]]~n",[Other]).
+
+print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref),
+ is_pid(Pid) ->
+ lists:foreach(fun print_channels/1, supervisor:which_children(Pid));
+print_system_sup({{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) ->
+ io:format(" [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]);
+print_system_sup(Other) ->
+ io:format(" [[Other 3: ~p]]~n",[Other]).
+
+print_channels({{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
+ lists:foreach(fun print_channel/1, supervisor:which_children(Pid));
+print_channels(Other) ->
+ io:format(" [[Other 4: ~p]]~n",[Other]).
+
+
+print_channel({Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref),
+ is_pid(Pid) ->
+ {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid),
+ {{Local,Remote},StrM} = ssh_connection_handler:get_print_info(ConnManager),
+ io:format(' ch ~p: ~s ~s',[ChannelID, StrM, Str]),
+ io:format(" Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]);
+print_channel(Other) ->
+ io:format(" [[Other 5: ~p]]~n",[Other]).
+
+%%%================================================================
+-define(inc(N), (N+4)).
+
+walk_sups(StartPid) ->
+ io:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]),
+ walk_sups(children(StartPid), _Indent=?inc(0)).
+
+walk_sups([H={_,Pid,SupOrWorker,_}|T], Indent) ->
+ indent(Indent), io:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]),
+ case SupOrWorker of
+ supervisor -> walk_sups(children(Pid), ?inc(Indent));
+ _ -> ok
+ end,
+ walk_sups(T, Indent);
+walk_sups([], _) ->
+ ok.
+
+dead_or_alive(Name) when is_atom(Name) ->
+ case whereis(Name) of
+ undefined ->
+ "**UNDEFINED**";
+ Pid ->
+ dead_or_alive(Pid)
+ end;
+dead_or_alive(Pid) when is_pid(Pid) ->
+ case process_info(Pid) of
+ undefined -> "**DEAD**";
+ _ -> "alive"
+ end.
+
+indent(I) -> io:format('~*c',[I,$ ]).
+
+children(Pid) ->
+ Parent = self(),
+ Helper = spawn(fun() ->
+ Parent ! {self(),supervisor:which_children(Pid)}
+ end),
+ receive
+ {Helper,L} when is_list(L) ->
+ L
+ after
+ 2000 ->
+ catch exit(Helper, kill),
+ []
+ end.
+
+%%%================================================================
+underline(Str) ->
+ underline(Str, $-).
+
+underline(Str, LineChar) ->
+ Len = lists:flatlength(Str),
+ io:format('~s~n',[Str]),
+ line(Len,LineChar).
+
+line(Len, Char) ->
+ io:format('~*c~n', [Len,Char]).
+
+
+datetime() ->
+ {{YYYY,MM,DD}, {H,M,S}} = calendar:now_to_universal_time(now()),
+ lists:flatten(io_lib:format('~4w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w UTC',[YYYY,MM,DD, H,M,S])).
+
+
+fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]);
+fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]).
+
+
+
+nyi() ->
+ io:format('Not yet implemented~n',[]),
+ nyi.
diff --git a/lib/ssh/src/ssh_io.erl b/lib/ssh/src/ssh_io.erl
index 35336bce8b..97e2dee27a 100644
--- a/lib/ssh/src/ssh_io.erl
+++ b/lib/ssh/src/ssh_io.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
@@ -73,7 +73,9 @@ read_password(Prompt, Ssh) ->
listify(A) when is_atom(A) ->
atom_to_list(A);
listify(L) when is_list(L) ->
- L.
+ L;
+listify(B) when is_binary(B) ->
+ binary_to_list(B).
format(Fmt, Args) ->
io:format(Fmt, Args).
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 76b57cb995..66e7717095 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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
@@ -162,8 +162,15 @@ encode(#ssh_msg_userauth_info_request{
encode(#ssh_msg_userauth_info_response{
num_responses = Num,
data = Data}) ->
- ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_RESPONSE, Num, Data],
- [byte, uint32, '...']);
+ Responses = lists:map(fun("") ->
+ <<>>;
+ (Response) ->
+ ssh_bits:encode([Response], [string])
+ end, Data),
+ Start = ssh_bits:encode([?SSH_MSG_USERAUTH_INFO_RESPONSE, Num],
+ [byte, uint32]),
+ iolist_to_binary([Start, Responses]);
+
encode(#ssh_msg_disconnect{
code = Code,
description = Desc,
@@ -498,6 +505,11 @@ erl_boolean(1) ->
decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) ->
list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
+decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) ->
+ %% The mandatory trailing UINT32 is missing. Assume the value it anyhow must have
+ %% See rfc 4253 7.1
+ X = 0,
+ list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) ->
Names = string:tokens(unicode:characters_to_list(Data), ","),
decode_kex_init(Rest, [Names | Acc], N -1).
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 0ea2366ac7..721146c509 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.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
@@ -57,7 +57,8 @@
rep_buf = <<>>,
req_id,
req_list = [], %% {ReqId, Fun}
- inf %% list of fileinf
+ inf, %% list of fileinf,
+ opts
}).
-record(fileinf,
@@ -85,10 +86,11 @@ start_channel(Host) when is_list(Host) ->
start_channel(Host, []).
start_channel(Cm, Opts) when is_pid(Cm) ->
Timeout = proplists:get_value(timeout, Opts, infinity),
+ {_, SftpOpts} = handle_options(Opts, [], []),
case ssh_xfer:attach(Cm, []) of
{ok, ChannelId, Cm} ->
case ssh_channel:start(Cm, ChannelId,
- ?MODULE, [Cm, ChannelId, Timeout]) of
+ ?MODULE, [Cm, ChannelId, SftpOpts]) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -108,11 +110,12 @@ start_channel(Cm, Opts) when is_pid(Cm) ->
start_channel(Host, Opts) ->
start_channel(Host, 22, Opts).
start_channel(Host, Port, Opts) ->
- Timeout = proplists:get_value(timeout, Opts, infinity),
- case ssh_xfer:connect(Host, Port, proplists:delete(timeout, Opts)) of
+ {SshOpts, SftpOpts} = handle_options(Opts, [], []),
+ Timeout = proplists:get_value(timeout, SftpOpts, infinity),
+ case ssh_xfer:connect(Host, Port, SshOpts) of
{ok, ChannelId, Cm} ->
case ssh_channel:start(Cm, ChannelId, ?MODULE, [Cm,
- ChannelId, Timeout]) of
+ ChannelId, SftpOpts]) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -392,7 +395,8 @@ write_file_loop(Pid, Handle, Pos, Bin, Remain, PacketSz, FileOpTimeout) ->
%%
%% Description:
%%--------------------------------------------------------------------
-init([Cm, ChannelId, Timeout]) ->
+init([Cm, ChannelId, Options]) ->
+ Timeout = proplists:get_value(timeout, Options, infinity),
erlang:monitor(process, Cm),
case ssh_connection:subsystem(Cm, ChannelId, "sftp", Timeout) of
success ->
@@ -401,7 +405,8 @@ init([Cm, ChannelId, Timeout]) ->
{ok, #state{xf = Xf,
req_id = 0,
rep_buf = <<>>,
- inf = new_inf()}};
+ inf = new_inf(),
+ opts = Options}};
failure ->
{stop, "server failed to start sftp subsystem"};
Error ->
@@ -707,8 +712,9 @@ handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State0) ->
%%
%% Description: Handles channel messages
%%--------------------------------------------------------------------
-handle_msg({ssh_channel_up, _, _}, #state{xf = Xf} = State) ->
- ssh_xfer:protocol_version_request(Xf),
+handle_msg({ssh_channel_up, _, _}, #state{opts = Options, xf = Xf} = State) ->
+ Version = proplists:get_value(sftp_vsn, Options, ?SSH_SFTP_PROTOCOL_VERSION),
+ ssh_xfer:protocol_version_request(Xf, Version),
{ok, State};
%% Version negotiation timed out
@@ -754,6 +760,15 @@ terminate(_Reason, State) ->
%%====================================================================
%% Internal functions
%%====================================================================
+handle_options([], Sftp, Ssh) ->
+ {Ssh, Sftp};
+handle_options([{timeout, _} = Opt | Rest], Sftp, Ssh) ->
+ handle_options(Rest, [Opt | Sftp], Ssh);
+handle_options([{sftp_vsn, _} = Opt| Rest], Sftp, Ssh) ->
+ handle_options(Rest, [Opt | Sftp], Ssh);
+handle_options([Opt | Rest], Sftp, Ssh) ->
+ handle_options(Rest, Sftp, [Opt | Ssh]).
+
call(Pid, Msg, TimeOut) ->
ssh_channel:call(Pid, {{timeout, TimeOut}, Msg}, infinity).
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 ea05c849b7..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) ->
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index 63d01fd9de..1881392db8 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.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
@@ -28,7 +28,7 @@
rename/5, remove/3, mkdir/4, rmdir/3, realpath/3, extended/4,
stat/4, fstat/4, lstat/4, setstat/4,
readlink/3, fsetstat/4, symlink/4,
- protocol_version_request/1,
+ protocol_version_request/2,
xf_reply/2,
xf_send_reply/3, xf_send_names/3, xf_send_name/4,
xf_send_status/3, xf_send_status/4, xf_send_status/5,
@@ -67,8 +67,8 @@ open_xfer(CM, Opts) ->
Error
end.
-protocol_version_request(XF) ->
- xf_request(XF, ?SSH_FXP_INIT, <<?UINT32(?SSH_SFTP_PROTOCOL_VERSION)>>).
+protocol_version_request(XF, Version) ->
+ xf_request(XF, ?SSH_FXP_INIT, <<?UINT32(Version)>>).
open(XF, ReqID, FileName, Access, Flags, Attrs) ->
Vsn = XF#ssh_xfer.vsn,
diff --git a/lib/ssh/test/property_test/ssh_eqc_client_server.erl b/lib/ssh/test/property_test/ssh_eqc_client_server.erl
index cf895ae85e..123b48412b 100644
--- a/lib/ssh/test/property_test/ssh_eqc_client_server.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_client_server.erl
@@ -32,6 +32,10 @@
-else.
+%% Limit the testing time on CI server... this needs to be improved in % from total budget.
+-define(TESTINGTIME(Prop), eqc:testing_time(30,Prop)).
+
+
-include_lib("eqc/include/eqc.hrl").
-include_lib("eqc/include/eqc_statem.hrl").
-eqc_group_commands(true).
@@ -75,7 +79,9 @@
-define(SUBSYSTEMS, ["echo1", "echo2", "echo3", "echo4"]).
--define(SERVER_ADDRESS, { {127,1,1,1}, inet_port({127,1,1,1}) }).
+-define(SERVER_ADDRESS, { {127,1,0,choose(1,254)}, % IP
+ choose(1024,65535) % Port
+ }).
-define(SERVER_EXTRA_OPTIONS, [{parallel_login,bool()}] ).
@@ -97,7 +103,7 @@
%% To be called as eqc:quickcheck( ssh_eqc_client_server:prop_seq() ).
prop_seq() ->
- do_prop_seq(?SSH_DIR).
+ ?TESTINGTIME(do_prop_seq(?SSH_DIR)).
%% To be called from a common_test test suite
prop_seq(CT_Config) ->
@@ -105,9 +111,10 @@ prop_seq(CT_Config) ->
do_prop_seq(DataDir) ->
- ?FORALL(Cmds,commands(?MODULE, #state{data_dir=DataDir}),
+ setup_rsa(DataDir),
+ ?FORALL(Cmds,commands(?MODULE),
begin
- {H,Sf,Result} = run_commands(?MODULE,Cmds),
+ {H,Sf,Result} = run_commands(?MODULE,Cmds,[{data_dir,DataDir}]),
present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
end).
@@ -116,33 +123,35 @@ full_path(SSHdir, CT_Config) ->
SSHdir).
%%%----
prop_parallel() ->
- do_prop_parallel(?SSH_DIR).
+ ?TESTINGTIME(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}),
+ setup_rsa(DataDir),
+ ?FORALL(Cmds,parallel_commands(?MODULE),
begin
- {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds),
+ {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]),
present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
end).
%%%----
prop_parallel_multi() ->
- do_prop_parallel_multi(?SSH_DIR).
+ ?TESTINGTIME(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) ->
+ setup_rsa(DataDir),
?FORALL(Repetitions,?SHRINK(1,[10]),
- ?FORALL(Cmds,parallel_commands(?MODULE, #state{data_dir=DataDir}),
+ ?FORALL(Cmds,parallel_commands(?MODULE),
?ALWAYS(Repetitions,
begin
- {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds),
+ {H,Sf,Result} = run_parallel_commands(?MODULE,Cmds,[{data_dir,DataDir}]),
present_result(?MODULE, Cmds, {H,Sf,Result}, Result==ok)
end))).
@@ -151,14 +160,12 @@ do_prop_parallel_multi(DataDir) ->
%%% called when using commands/1
initial_state() ->
- S = initial_state(#state{}),
- S#state{initialized=true}.
+ #state{}.
%%% called when using commands/2
-initial_state(S) ->
+initial_state(DataDir) ->
application:stop(ssh),
- ssh:start(),
- setup_rsa(S#state.data_dir).
+ ssh:start().
%%%----------------
weight(S, ssh_send) -> 5*length([C || C<-S#state.channels, has_subsyst(C)]);
@@ -172,7 +179,7 @@ weight(_S, _) -> 1.
initial_state_pre(S) -> not S#state.initialized.
-initial_state_args(S) -> [S].
+initial_state_args(_) -> [{var,data_dir}].
initial_state_next(S, _, _) -> S#state{initialized=true}.
@@ -180,10 +187,17 @@ initial_state_next(S, _, _) -> S#state{initialized=true}.
%%% Start a new daemon
%%% Precondition: not more than ?MAX_NUM_SERVERS started
+%%% This is a bit funny because we need to pick an IP address and Port to
+%%% run the server on, but there is no way to atomically select a free Port!
+%%%
+%%% Therefore we just grab one IP-Port pair randomly and try to start the ssh server
+%%% on that pair. If it fails, we just forget about it and goes on. Yes, it
+%%% is a waste of cpu cycles, but at least it works!
+
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_args(_) -> [?SERVER_ADDRESS, {var,data_dir}, ?SERVER_EXTRA_OPTIONS].
ssh_server({IP,Port}, DataDir, ExtraOptions) ->
ok(ssh:daemon(IP, Port,
@@ -194,8 +208,10 @@ ssh_server({IP,Port}, DataDir, ExtraOptions) ->
| ExtraOptions
])).
+ssh_server_post(_S, _Args, {error,eaddrinuse}) -> true;
ssh_server_post(_S, _Args, Result) -> is_ok(Result).
+ssh_server_next(S, {error,eaddrinuse}, _) -> S;
ssh_server_next(S, Result, [{IP,Port},_,_]) ->
S#state{servers=[#srvr{ref = Result,
address = IP,
@@ -241,15 +257,16 @@ do(Pid, Fun, Timeout) when is_function(Fun,0) ->
ssh_open_connection_pre(S) -> S#state.servers /= [].
-ssh_open_connection_args(S) -> [oneof(S#state.servers), S#state.data_dir].
+ssh_open_connection_args(S) -> [oneof(S#state.servers), {var,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}
- ])).
+ {user_interaction, false},
+ {connect_timeout, 2000}
+ ], 2000)).
ssh_open_connection_post(_S, _Args, Result) -> is_ok(Result).
@@ -569,12 +586,6 @@ median(_) ->
%%%================================================================
%%% 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)),
diff --git a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
index 34630bdc91..57ea2012c1 100644
--- a/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
+++ b/lib/ssh/test/property_test/ssh_eqc_encode_decode.erl
@@ -25,8 +25,6 @@
-proptest(eqc).
-proptest([triq,proper]).
--include_lib("ct_property_test.hrl").
-
-ifndef(EQC).
-ifndef(PROPER).
-ifndef(TRIQ).
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 9242731924..415cb9fc9c 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -798,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]),
@@ -898,7 +900,12 @@ connect_fun(ssh_sftp__start_channel, _Config) ->
end.
-max_sessions(Config, ParallelLogin, Connect) when is_function(Connect,2) ->
+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 = 5,
@@ -909,7 +916,7 @@ max_sessions(Config, ParallelLogin, Connect) when is_function(Connect,2) ->
{parallel_login, ParallelLogin},
{max_sessions, MaxSessions}
]),
-
+ ct:pal("~p Listen ~p:~p for max ~p sessions",[Pid,Host,Port,MaxSessions]),
try [Connect(Host,Port) || _ <- lists:seq(1,MaxSessions)]
of
Connections ->
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index c115ccee5f..553d0f5720 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,23 +31,37 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() ->
- [{ct_hooks,[ts_install_cth]}].
+%% suite() ->
+%% [{ct_hooks,[ts_install_cth]}].
all() ->
[
- {group, openssh_payload},
+ {group, openssh},
+ start_subsystem_on_closed_channel,
interrupted_send,
start_shell,
start_shell_exec,
- start_shell_exec_fun
+ 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,
- small_cat,
- big_cat,
- send_after_exit
- ]}].
+ [{openssh, [], payload() ++ ptty()}].
+
+payload() ->
+ [simple_exec,
+ small_cat,
+ big_cat,
+ send_after_exit].
+
+ptty() ->
+ [ptty_alloc_default,
+ ptty_alloc,
+ ptty_alloc_pixel].
+
%%--------------------------------------------------------------------
init_per_suite(Config) ->
case catch crypto:start() of
@@ -61,13 +75,13 @@ end_per_suite(_Config) ->
crypto:stop().
%%--------------------------------------------------------------------
-init_per_group(openssh_payload, _Config) ->
+init_per_group(openssh, _Config) ->
case gen_tcp:connect("localhost", 22, []) of
{error,econnrefused} ->
{skip,"No openssh deamon"};
{ok, Socket} ->
gen_tcp:close(Socket)
- end;
+ end;
init_per_group(_, Config) ->
Config.
@@ -180,10 +194,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 ->
@@ -236,6 +250,68 @@ send_after_exit(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
+ptty_alloc_default() ->
+ [{doc, "Test sending PTTY alloc message with only defaults."}].
+
+ptty_alloc_default(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []),
+ ssh:close(ConnectionRef).
+
+%%--------------------------------------------------------------------
+ptty_alloc() ->
+ [{doc, "Test sending PTTY alloc message with width,height options."}].
+
+ptty_alloc(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId,
+ [{term, default_term()}, {width, 70}, {high, 20}]),
+ ssh:close(ConnectionRef).
+
+
+%%--------------------------------------------------------------------
+ptty_alloc_pixel() ->
+ [{doc, "Test sending PTTY alloc message pixel options."}].
+
+ptty_alloc_pixel(Config) when is_list(Config) ->
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+ success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId,
+ [{term, default_term()}, {pixel_widh, 630}, {pixel_hight, 470}]),
+ ssh:close(ConnectionRef).
+
+%%--------------------------------------------------------------------
+start_subsystem_on_closed_channel(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"},
+ {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}]),
+
+ {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
+
+ ok = ssh_connection:close(ConnectionRef, ChannelId),
+
+ failure = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity),
+
+ ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
+%%--------------------------------------------------------------------
interrupted_send() ->
[{doc, "Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."}].
@@ -250,10 +326,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),
@@ -288,24 +364,24 @@ start_shell(Config) when is_list(Config) ->
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} ]),
+ {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}]),
+ {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, ChannelId, 0, <<"Enter command\r\n">>}} ->
- ok
+ {ssh_cm,ConnectionRef, {data, ChannelId0, 0, <<"Enter command\r\n">>}} ->
+ ok
after 5000 ->
- ct:fail("CLI Timeout")
+ ct:fail("CLI Timeout")
end,
ssh:close(ConnectionRef),
@@ -320,25 +396,25 @@ start_shell_exec(Config) when is_list(Config) ->
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,[]}} ]),
+ {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}]),
+ {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),
+ "testing", infinity),
receive
- {ssh_cm,ConnectionRef, {data, ChannelId, 0, <<"testing\r\n">>}} ->
- ok
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ ok
after 5000 ->
- ct:fail("Exec Timeout")
+ ct:fail("Exec Timeout")
end,
ssh:close(ConnectionRef),
@@ -354,30 +430,177 @@ start_shell_exec_fun(Config) when is_list(Config) ->
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}]),
+ {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}]),
+ {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),
+ "testing", infinity),
receive
- {ssh_cm,ConnectionRef, {data, ChannelId, 0, <<"testing\r\n">>}} ->
- ok
+ {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} ->
+ ok
after 5000 ->
- ct:fail("Exec Timeout")
+ 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 ------------------------------------------------
%%--------------------------------------------------------------------
@@ -413,14 +636,22 @@ collect_data(ConnectionRef, ChannelId, Acc) ->
end.
%%%-------------------------------------------------------------------
-% This is taken from the ssh example code.
+%% 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
+ io:format("Enter command\n")
+ %% Don't actually loop, just exit
end).
ssh_exec(Cmd) ->
spawn(fun() ->
- io:format(Cmd ++ "\n")
+ io:format(Cmd ++ "\n")
end).
+
+default_term() ->
+ case os:getenv("TERM") of
+ false ->
+ "vt100";
+ Str when is_list(Str)->
+ Str
+ end.
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 56b1363b7a..4c46a1b1a8 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.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
@@ -65,7 +65,7 @@ groups() ->
[{erlang_server, [], [open_close_file, open_close_dir, read_file, read_dir,
write_file, rename_file, mk_rm_dir, remove_file, links,
retrieve_attributes, set_attributes, async_read,
- async_write, position, pos_read, pos_write]},
+ async_write, position, pos_read, pos_write, version_option]},
{openssh_server, [], [open_close_file, open_close_dir, read_file, read_dir,
write_file, rename_file, mk_rm_dir, remove_file, links,
retrieve_attributes, set_attributes, async_read,
@@ -111,6 +111,21 @@ init_per_testcase(sftp_nonexistent_subsystem, Config) ->
]),
[{sftpd, Sftpd} | Config];
+init_per_testcase(version_option, Config) ->
+ prep(Config),
+ TmpConfig0 = lists:keydelete(watchdog, 1, Config),
+ TmpConfig = lists:keydelete(sftp, 1, TmpConfig0),
+ Dog = ct:timetrap(?default_timeout),
+ {_,Host, Port} = ?config(sftpd, Config),
+ {ok, ChannelPid, Connection} =
+ ssh_sftp:start_channel(Host, Port,
+ [{sftp_vsn, 3},
+ {user, ?USER},
+ {password, ?PASSWD},
+ {user_interaction, false},
+ {silently_accept_hosts, true}]),
+ Sftp = {ChannelPid, Connection},
+ [{sftp, Sftp}, {watchdog, Dog} | TmpConfig];
init_per_testcase(Case, Config) ->
prep(Config),
TmpConfig0 = lists:keydelete(watchdog, 1, Config),
@@ -447,6 +462,11 @@ sftp_nonexistent_subsystem(Config) when is_list(Config) ->
{silently_accept_hosts, true}]).
%%--------------------------------------------------------------------
+version_option() ->
+ [{doc, "Test API option sftp_vsn"}].
+version_option(Config) when is_list(Config) ->
+ open_close_dir(Config).
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
prep(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 3500bf012b..af70eeb46c 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
@@ -119,22 +119,9 @@ 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
- {'EXIT', Shell, normal} ->
- ok;
- Other1 ->
- ct:fail({unexpected_msg, Other1})
- end.
-
+ receive_logout(),
+ receive_normal_exit(Shell).
+
%--------------------------------------------------------------------
erlang_client_openssh_server_exec() ->
[{doc, "Test api function ssh_connection:exec"}].
@@ -537,11 +524,44 @@ erlang_client_openssh_server_nonexistent_subsystem(Config) when is_list(Config)
%%--------------------------------------------------------------------
receive_hej() ->
receive
- <<"Hej\n">> = Hej->
+ <<"Hej", _binary>> = Hej ->
+ ct:pal("Expected result: ~p~n", [Hej]);
+ <<"Hej\n", _binary>> = Hej ->
+ ct:pal("Expected result: ~p~n", [Hej]);
+ <<"Hej\r\n", _/binary>> = Hej ->
ct:pal("Expected result: ~p~n", [Hej]);
Info ->
- ct:pal("Extra info: ~p~n", [Info]),
- receive_hej()
+ Lines = binary:split(Info, [<<"\r\n">>], [global]),
+ case lists:member(<<"Hej">>, Lines) of
+ true ->
+ ct:pal("Expected result found in lines: ~p~n", [Lines]),
+ ok;
+ false ->
+ ct:pal("Extra info: ~p~n", [Info]),
+ receive_hej()
+ end
+ end.
+
+receive_logout() ->
+ receive
+ <<"logout">> ->
+ receive
+ <<"Connection closed">> ->
+ ok
+ end;
+ Info ->
+ ct:pal("Extra info when logging out: ~p~n", [Info]),
+ receive_logout()
+ end.
+
+receive_normal_exit(Shell) ->
+ receive
+ {'EXIT', Shell, normal} ->
+ ok;
+ <<"\r\n">> ->
+ receive_normal_exit(Shell);
+ Other ->
+ ct:fail({unexpected_msg, Other})
end.
%%--------------------------------------------------------------------
@@ -564,4 +584,7 @@ check_ssh_client_support2(P) ->
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 73bf73971f..68544c1d0e 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.5
+SSH_VSN = 3.0.8
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 1b37a2baa2..62e9bd0165 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,63 @@
<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.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handle the fact that servers may send an empty SNI
+ extension to the client.</p>
+ <p>
+ Own Id: OTP-12198</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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..b53344e381 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: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/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..9d692379b4 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,24 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {"5.3.6", [{load_module, ssl_handshake, soft_purge, soft_purge, [ssl_connection]}]},
+ {"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.6", [{load_module, ssl_handshake, soft_purge, soft_purge, [ssl_connection]}]},
+ {"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 53366b060c..9c0ed181fe 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -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,28 +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 ->
- 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,
+ 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 ->
@@ -275,3 +265,41 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith
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 94ffd180c5..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
]).
@@ -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,
@@ -1732,6 +1732,9 @@ dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
#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,
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/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index 77e4c80bbe..3566a8a0a5 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -163,11 +163,11 @@ client_ecdh_server_ecdh(Config) when is_list(Config) ->
client_ecdh_server_rsa(Config) when is_list(Config) ->
COpts = ?config(client_ecdh_rsa_opts, Config),
- SOpts = ?config(server_verification_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_verification_opts, Config),
+ COpts = ?config(client_ecdh_rsa_opts, Config),
SOpts = ?config(server_ecdh_rsa_verify_opts, Config),
basic_test(COpts, SOpts, Config).
@@ -183,11 +183,11 @@ client_ecdsa_server_ecdsa(Config) when is_list(Config) ->
client_ecdsa_server_rsa(Config) when is_list(Config) ->
COpts = ?config(client_ecdsa_opts, Config),
- SOpts = ?config(server_verification_opts, Config),
+ SOpts = ?config(server_ecdsa_verify_opts, Config),
basic_test(COpts, SOpts, Config).
client_rsa_server_ecdsa(Config) when is_list(Config) ->
- COpts = ?config(client_verification_opts, Config),
+ COpts = ?config(client_ecdsa_opts, Config),
SOpts = ?config(server_ecdsa_verify_opts, Config),
basic_test(COpts, SOpts, Config).
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 e5e942ce1b..8dca733526 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -39,6 +39,7 @@ all() -> [decode_hello_handshake,
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].
%%--------------------------------------------------------------------
@@ -106,6 +107,13 @@ decode_single_hello_sni_extension_correctly(_Config) ->
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/vsn.mk b/lib/ssl/vsn.mk
index 004cacf7fc..da20ed8593 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.3.5
+SSL_VSN = 5.3.7
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/src/dets.erl b/lib/stdlib/src/dets.erl
index 76e03bbfaa..a4bd45ea19 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -2839,17 +2839,22 @@ fsck_try(Fd, Tab, FH, Fname, SlotNumbers, Version) ->
tempfile(Fname) ->
Tmp = lists:concat([Fname, ".TMP"]),
- tempfile(Tmp, 10).
-
-tempfile(Tmp, 0) ->
- Tmp;
-tempfile(Tmp, N) ->
case file:delete(Tmp) of
- {error, eacces} -> % 'dets_process_died' happened anyway... (W-nd-ws)
- timer:sleep(1000),
- tempfile(Tmp, N-1);
- _ ->
- Tmp
+ {error, _Reason} -> % typically enoent
+ ok;
+ ok ->
+ assure_no_file(Tmp)
+ end,
+ Tmp.
+
+assure_no_file(File) ->
+ case file:read_file_info(File) of
+ {ok, _FileInfo} ->
+ %% Wait for some other process to close the file:
+ timer:sleep(100),
+ assure_no_file(File);
+ {error, _} ->
+ ok
end.
%% -> {ok, NewHead} | {try_again, integer()} | Error
diff --git a/lib/stdlib/src/dets_server.erl b/lib/stdlib/src/dets_server.erl
index 268c201047..3164d40f35 100644
--- a/lib/stdlib/src/dets_server.erl
+++ b/lib/stdlib/src/dets_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2001-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
@@ -171,9 +171,15 @@ handle_info({pending_reply, {Ref, Result0}}, State) ->
link(Pid),
do_link(Store, FromPid),
true = ets:insert(Store, {FromPid, Tab}),
- true = ets:insert(?REGISTRY, {Tab, 1, Pid}),
- true = ets:insert(?OWNERS, {Pid, Tab}),
+ %% do_internal_open() has already done the following:
+ %% true = ets:insert(?REGISTRY, {Tab, 1, Pid}),
+ %% true = ets:insert(?OWNERS, {Pid, Tab}),
{ok, Tab};
+ {Reply, internal_open} ->
+ %% Clean up what do_internal_open() did:
+ true = ets:delete(?REGISTRY, Tab),
+ true = ets:delete(?OWNERS, Pid),
+ Reply;
{Reply, _} -> % ok or Error
Reply
end,
@@ -309,6 +315,12 @@ do_internal_open(State, From, Args) ->
[T, _, _] -> T;
[_, _] -> Ref
end,
+ %% Pretend the table is open. If someone else tries to
+ %% open the file it will always become a pending
+ %% 'add_user' request. If someone tries to use the table
+ %% there will be a delay, but that is OK.
+ true = ets:insert(?REGISTRY, {Tab, 1, Pid}),
+ true = ets:insert(?OWNERS, {Pid, Tab}),
pending_call(Tab, Pid, Ref, From, Args, internal_open, State);
Error ->
{Error, State}
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index be9a4f5107..b3bc5f6d92 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -390,7 +390,7 @@ do_op(end_of_line, Bef, [C|Aft], Rs) ->
do_op(end_of_line, Bef, [], Rs) ->
{{Bef,[]},Rs};
do_op(ctlu, Bef, Aft, Rs) ->
- put(kill_buffer, Bef),
+ put(kill_buffer, reverse(Bef)),
{{[], Aft}, [{delete_chars, -length(Bef)} | Rs]};
do_op(beep, Bef, Aft, Rs) ->
{{Bef,Aft},[beep|Rs]};
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/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/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/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/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 119b4dc7cb..3b08ac165e 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -223,8 +223,7 @@ open(Config, Version) ->
?format("Crashing dets server \n", []),
process_flag(trap_exit, true),
- Procs = [whereis(?DETS_SERVER) | map(fun(Tab) -> dets:info(Tab, pid) end,
- Tabs)],
+ Procs = [whereis(?DETS_SERVER) | [dets:info(Tab, pid) || Tab <- Tabs]],
foreach(fun(Pid) -> exit(Pid, kill) end, Procs),
timer:sleep(100),
c:flush(), %% flush all the EXIT sigs
@@ -235,18 +234,32 @@ open(Config, Version) ->
open_files(1, All, Version),
?format("Checking contents of repaired files \n", []),
check(Tabs, Data),
-
- close_all(Tabs),
+ close_all(Tabs),
delete_files(All),
- P1 = pps(),
+
{Ports0, Procs0} = P0,
- {Ports1, Procs1} = P1,
- true = Ports1 =:= Ports0,
- %% The dets_server process has been restarted:
- [_] = Procs0 -- Procs1,
- [_] = Procs1 -- Procs0,
- ok.
+ Test = fun() ->
+ P1 = pps(),
+ {Ports1, Procs1} = P1,
+ show("Old port", Ports0 -- Ports1),
+ show("New port", Ports1 -- Ports0),
+ show("Old procs", Procs0 -- Procs1),
+ show("New procs", Procs1 -- Procs0),
+ io:format("Remaining Dets-pids (should be nil): ~p~n",
+ [find_dets_pids()]),
+ true = Ports1 =:= Ports0,
+ %% The dets_server process has been restarted:
+ [_] = Procs0 -- Procs1,
+ [_] = Procs1 -- Procs0,
+ ok
+ end,
+ case catch Test() of
+ ok -> ok;
+ _ ->
+ timer:sleep(500),
+ ok = Test()
+ end.
check(Tabs, Data) ->
foreach(fun(Tab) ->
@@ -3275,12 +3288,22 @@ simultaneous_open(Config) ->
File = filename(Tab, Config),
ok = monit(Tab, File),
- ok = kill_while_repairing(Tab, File),
- ok = kill_while_init(Tab, File),
- ok = open_ro(Tab, File),
- ok = open_w(Tab, File, 0, Config),
- ok = open_w(Tab, File, 100, Config),
- ok.
+ case feasible() of
+ false -> {comment, "OK, but did not run all of the test"};
+ true ->
+ ok = kill_while_repairing(Tab, File),
+ ok = kill_while_init(Tab, File),
+ ok = open_ro(Tab, File),
+ ok = open_w(Tab, File, 0, Config),
+ ok = open_w(Tab, File, 100, Config)
+ end.
+
+feasible() ->
+ LP = erlang:system_info(logical_processors),
+ (is_integer(LP)
+ andalso LP >= erlang:system_info(schedulers_online)
+ andalso not erlang:system_info(debug_compiled)
+ andalso not erlang:system_info(lock_checking)).
%% One process logs and another process closes the log. Before
%% monitors were used, this would make the client never return.
@@ -3307,7 +3330,6 @@ kill_while_repairing(Tab, File) ->
Delay = 1000,
dets:start(),
Parent = self(),
- Ps = processes(),
F = fun() ->
R = (catch dets:open_file(Tab, [{file,File}])),
timer:sleep(Delay),
@@ -3318,7 +3340,7 @@ kill_while_repairing(Tab, File) ->
P1 = spawn(F),
P2 = spawn(F),
P3 = spawn(F),
- DetsPid = find_dets_pid([P1, P2, P3 | Ps]),
+ DetsPid = find_dets_pid(),
exit(DetsPid, kill),
receive {P1,R1} -> R1 end,
@@ -3342,12 +3364,6 @@ kill_while_repairing(Tab, File) ->
file:delete(File),
ok.
-find_dets_pid(P0) ->
- case lists:sort(processes() -- P0) of
- [P, _] -> P;
- _ -> timer:sleep(100), find_dets_pid(P0)
- end.
-
find_dets_pid() ->
case find_dets_pids() of
[] ->
@@ -3421,6 +3437,13 @@ open_ro(Tab, File) ->
open_w(Tab, File, Delay, Config) ->
create_opened_log(File),
+
+ Tab2 = t2,
+ File2 = filename(Tab2, Config),
+ file:delete(File2),
+ {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]),
+ ok = dets:close(Tab2),
+
Parent = self(),
F = fun() ->
R = dets:open_file(Tab, [{file,File}]),
@@ -3430,16 +3453,16 @@ open_w(Tab, File, Delay, Config) ->
Pid1 = spawn(F),
Pid2 = spawn(F),
Pid3 = spawn(F),
- undefined = dets:info(Tab), % is repairing now
- 0 = qlen(),
- Tab2 = t2,
- File2 = filename(Tab2, Config),
- file:delete(File2),
+ ok = wait_for_repair_to_start(Tab),
+
+ %% It is assumed that it takes some time to repair the file.
{ok,Tab2} = dets:open_file(Tab2, [{file,File2}]),
+ %% The Dets server managed to handle to open_file request.
+ 0 = qlen(), % still repairing
+
ok = dets:close(Tab2),
file:delete(File2),
- 0 = qlen(), % still repairing
receive {Pid1,R1} -> {ok, Tab} = R1 end,
receive {Pid2,R2} -> {ok, Tab} = R2 end,
@@ -3456,6 +3479,15 @@ open_w(Tab, File, Delay, Config) ->
file:delete(File),
ok.
+wait_for_repair_to_start(Tab) ->
+ case catch dets_server:get_pid(Tab) of
+ {'EXIT', _} ->
+ timer:sleep(1),
+ wait_for_repair_to_start(Tab);
+ Pid when is_pid(Pid) ->
+ ok
+ end.
+
qlen() ->
{_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())),
N.
@@ -4350,6 +4382,7 @@ check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) ->
true = test_server:is_native(M) andalso length(Args) =:= A.
check_pps({Ports0,Procs0} = P0) ->
+ ok = check_dets_tables(),
case pps() of
P0 ->
ok;
@@ -4375,13 +4408,45 @@ check_pps({Ports0,Procs0} = P0) ->
end
end.
+%% Copied from dets_server.erl:
+-define(REGISTRY, dets_registry).
+-define(OWNERS, dets_owners).
+-define(STORE, dets).
+
+check_dets_tables() ->
+ Store = [T ||
+ T <- ets:all(),
+ ets:info(T, name) =:= ?STORE,
+ owner(T) =:= dets],
+ S = case Store of
+ [Tab] -> ets:tab2list(Tab);
+ [] -> []
+ end,
+ case {ets:tab2list(?REGISTRY), ets:tab2list(?OWNERS), S} of
+ {[], [], []} -> ok;
+ {R, O, _} ->
+ io:format("Registry: ~p~n", [R]),
+ io:format("Owners: ~p~n", [O]),
+ io:format("Store: ~p~n", [S]),
+ not_ok
+ end.
+
+owner(Tab) ->
+ Owner = ets:info(Tab, owner),
+ case process_info(Owner, registered_name) of
+ {registered_name, Name} -> Name;
+ _ -> Owner
+ end.
+
show(_S, []) ->
ok;
-show(S, [Pid|Pids]) when is_pid(Pid) ->
- io:format("~s: ~p~n", [S, erlang:process_info(Pid)]),
+show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) ->
+ io:format("~s: ~w (~w), ~w: ~p~n",
+ [S, Pid, proc_reg_name(Name), InitCall,
+ erlang:process_info(Pid)]),
show(S, Pids);
-show(S, [Port|Ports]) when is_port(Port)->
- io:format("~s: ~p~n", [S, erlang:port_info(Port)]),
+show(S, [{Port, _}|Ports]) when is_port(Port)->
+ io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]),
show(S, Ports).
pps() ->
@@ -4397,5 +4462,8 @@ process_list() ->
safe_second_element(process_info(P, initial_call))} ||
P <- processes()].
+proc_reg_name({registered_name, Name}) -> Name;
+proc_reg_name([]) -> no_reg_name.
+
safe_second_element({_,Info}) -> Info;
safe_second_element(Other) -> Other.
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/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/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/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 46a5ca48df..40372a2106 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -669,6 +669,9 @@ is_leaf(Node) ->
operator -> true; % nonstandard type
string -> true;
text -> true; % nonstandard type
+ map_expr ->
+ map_expr_fields(Node) =:= [] andalso
+ map_expr_argument(Node) =:= none;
tuple -> tuple_elements(Node) =:= [];
underscore -> true;
variable -> true;
@@ -6093,6 +6096,9 @@ abstract([]) ->
nil();
abstract(T) when is_tuple(T) ->
tuple(abstract_list(tuple_to_list(T)));
+abstract(T) when is_map(T) ->
+ map_expr([map_field_assoc(abstract(Key),abstract(Value))
+ || {Key,Value} <- maps:to_list(T)]);
abstract(T) when is_binary(T) ->
binary([binary_field(integer(B)) || B <- binary_to_list(T)]);
abstract(T) ->
@@ -6154,6 +6160,14 @@ concrete(Node) ->
| concrete(list_tail(Node))];
tuple ->
list_to_tuple(concrete_list(tuple_elements(Node)));
+ map_expr ->
+ As = [tuple([map_field_assoc_name(F),
+ map_field_assoc_value(F)]) || F <- map_expr_fields(Node)],
+ M0 = maps:from_list(concrete_list(As)),
+ case map_expr_argument(Node) of
+ none -> M0;
+ Node0 -> maps:merge(concrete(Node0),M0)
+ end;
binary ->
Fs = [revert_binary_field(
binary_field(binary_field_body(F),
@@ -6209,10 +6223,31 @@ is_literal(T) ->
is_literal(list_head(T)) andalso is_literal(list_tail(T));
tuple ->
lists:all(fun is_literal/1, tuple_elements(T));
+ map_expr ->
+ case map_expr_argument(T) of
+ none -> true;
+ Arg -> is_literal(Arg)
+ end andalso lists:all(fun is_literal_map_field/1, map_expr_fields(T));
+ binary ->
+ lists:all(fun is_literal_binary_field/1, binary_fields(T));
_ ->
false
end.
+is_literal_binary_field(F) ->
+ case binary_field_types(F) of
+ [] -> is_literal(binary_field_body(F));
+ _ -> false
+ end.
+
+is_literal_map_field(F) ->
+ case type(F) of
+ map_field_assoc ->
+ is_literal(map_field_assoc_name(F)) andalso
+ is_literal(map_field_assoc_value(F));
+ map_field_exact ->
+ false
+ end.
%% =====================================================================
%% @doc Returns an `erl_parse'-compatible representation of a
diff --git a/lib/syntax_tools/test/Makefile b/lib/syntax_tools/test/Makefile
index d4733b9a42..f67e3f8984 100644
--- a/lib/syntax_tools/test/Makefile
+++ b/lib/syntax_tools/test/Makefile
@@ -61,5 +61,6 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) syntax_tools.spec syntax_tools.cover "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
+ @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index 6fb3e5ccfb..3c6b33f459 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -24,12 +24,16 @@
init_per_group/2,end_per_group/2]).
%% Test cases
--export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1]).
+-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1,
+ t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1,
+ t_comment_scan/1,t_igor/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test,appup_test,smoke_test,revert,revert_map].
+ [app_test,appup_test,smoke_test,revert,revert_map,
+ t_abstract_type,t_erl_parse_type,t_epp_dodger,
+ t_comment_scan,t_igor].
groups() ->
[].
@@ -54,15 +58,15 @@ appup_test(Config) when is_list(Config) ->
%% Read and parse all source in the OTP release.
smoke_test(Config) when is_list(Config) ->
- ?line Dog = ?t:timetrap(?t:minutes(12)),
- ?line Wc = filename:join([code:lib_dir(),"*","src","*.erl"]),
- ?line Fs = filelib:wildcard(Wc),
- ?line io:format("~p files\n", [length(Fs)]),
- ?line case p_run(fun smoke_test_file/1, Fs) of
- 0 -> ok;
- N -> ?line ?t:fail({N,errors})
- end,
- ?line ?t:timetrap_cancel(Dog).
+ Dog = ?t:timetrap(?t:minutes(12)),
+ Wc = filename:join([code:lib_dir(),"*","src","*.erl"]),
+ Fs = filelib:wildcard(Wc),
+ io:format("~p files\n", [length(Fs)]),
+ case p_run(fun smoke_test_file/1, Fs) of
+ 0 -> ok;
+ N -> ?t:fail({N,errors})
+ end,
+ ?t:timetrap_cancel(Dog).
smoke_test_file(File) ->
case epp_dodger:parse_file(File) of
@@ -94,9 +98,9 @@ revert(Config) when is_list(Config) ->
io:format("~p files\n", [length(Fs)]),
case p_run(fun (File) -> revert_file(File, Path) end, Fs) of
0 -> ok;
- N -> ?line ?t:fail({N,errors})
+ N -> ?t:fail({N,errors})
end,
- ?line ?t:timetrap_cancel(Dog).
+ ?t:timetrap_cancel(Dog).
revert_file(File, Path) ->
case epp:parse_file(File, Path, []) of
@@ -110,14 +114,298 @@ revert_file(File, Path) ->
end.
%% Testing bug fix for reverting map_field_assoc
-revert_map(Config) ->
+revert_map(Config) when is_list(Config) ->
Dog = ?t:timetrap(?t:minutes(1)),
- ?line [{map_field_assoc,16,{atom,17,name},{var,18,'Value'}}] =
- erl_syntax:revert_forms([{tree,map_field_assoc,
- {attr,16,[],none},
- {map_field_assoc,
- {atom,17,name},{var,18,'Value'}}}]),
- ?line ?t:timetrap_cancel(Dog).
+ [{map_field_assoc,16,{atom,17,name},{var,18,'Value'}}] =
+ erl_syntax:revert_forms([{tree,map_field_assoc,
+ {attr,16,[],none},
+ {map_field_assoc,{atom,17,name},{var,18,'Value'}}}]),
+ ?t:timetrap_cancel(Dog).
+
+
+
+%% api tests
+
+t_abstract_type(Config) when is_list(Config) ->
+ F = fun validate_abstract_type/1,
+ ok = validate(F,[{hi,atom},
+ {1,integer},
+ {1.0,float},
+ {$a,integer},
+ {[],nil},
+ {[<<1,2>>,a,b],list},
+ {[2,3,<<1,2>>,a,b],list},
+ {[$a,$b,$c],string},
+ {"hello world",string},
+ {<<1,2,3>>,binary},
+ {#{a=>1,"b"=>2},map_expr},
+ {#{#{i=>1}=>1,"b"=>#{v=>2}},map_expr},
+ {{a,b,c},tuple}]),
+ ok.
+
+t_erl_parse_type(Config) when is_list(Config) ->
+ F = fun validate_erl_parse_type/1,
+ %% leaf types
+ ok = validate(F,[{"1",integer,true},
+ {"123456789",integer,true},
+ {"$h", char,true},
+ {"3.1415", float,true},
+ {"1.33e36", float,true},
+ {"\"1.33e36: hello\"", string,true},
+ {"Var1", variable,true},
+ {"_", underscore,true},
+ {"[]", nil,true},
+ {"{}", tuple,true},
+ {"#{}",map_expr,true},
+ {"'some atom'", atom, true}]),
+ %% composite types
+ ok = validate(F,[{"case X of t -> t; f -> f end", case_expr,false},
+ {"try X of t -> t catch C:R -> error end", try_expr,false},
+ {"receive X -> X end", receive_expr,false},
+ {"receive M -> X1 after T -> X2 end", receive_expr,false},
+ {"catch (X)", catch_expr,false},
+ {"fun(X) -> X end", fun_expr,false},
+ {"fun Foo(X) -> X end", named_fun_expr,false},
+ {"fun foo/2", implicit_fun,false},
+ {"fun bar:foo/2", implicit_fun,false},
+ {"if X -> t; true -> f end", if_expr,false},
+ {"<<1,2,3,4>>", binary,false},
+ {"<<1,2,3,4:5>>", binary,false},
+ {"<<V1:63,V2:22/binary, V3/bits>>", binary,false},
+ {"begin X end", block_expr,false},
+ {"foo(X1,X2)", application,false},
+ {"bar:foo(X1,X2)", application,false},
+ {"[1,2,3,4]", list,false},
+ {"[1|4]", list, false},
+ {"[<<1>>,<<2>>,-2,<<>>,[more,list]]", list,false},
+ {"[1|[2|[3|[4|[]]]]]", list,false},
+ {"#{ a=>1, b=>2 }", map_expr,false},
+ {"#{3=>3}#{ a=>1, b=>2 }", map_expr,false},
+ {"#{ a:=1, b:=2 }", map_expr,false},
+ {"M#{ a=>1, b=>2 }", map_expr,false},
+ {"[V||V <- Vs]", list_comp,false},
+ {"<< <<B>> || <<B>> <= Bs>>", binary_comp,false},
+ {"#state{ a = A, b = B}", record_expr,false},
+ {"#state{}", record_expr,false},
+ {"#s{ a = #def{ a=A }, b = B}", record_expr,false},
+ {"State#state{ a = A, b = B}", record_expr,false},
+ {"State#state.a", record_access,false},
+ {"#state.a", record_index_expr,false},
+ {"-X", prefix_expr,false},
+ {"X1 + X2", infix_expr,false},
+ {"(X1 + X2) * X3", infix_expr,false},
+ {"X1 = X2", match_expr,false},
+ {"{a,b,c}", tuple,false}]),
+ ok.
+
+%% the macro ?MODULE seems faulty
+t_epp_dodger(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Filenames = ["syntax_tools_SUITE_test_module.erl",
+ "syntax_tools_test.erl"],
+ ok = test_epp_dodger(Filenames,DataDir,PrivDir),
+ ok.
+
+t_comment_scan(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Filenames = ["syntax_tools_SUITE_test_module.erl",
+ "syntax_tools_test.erl"],
+ ok = test_comment_scan(Filenames,DataDir),
+ ok.
+
+t_igor(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ FileM1 = filename:join(DataDir,"m1.erl"),
+ FileM2 = filename:join(DataDir,"m2.erl"),
+ ["m.erl",_]=R = igor:merge(m,[FileM1,FileM2],[{outdir,PrivDir}]),
+ io:format("igor:merge/3 = ~p~n", [R]),
+ ok.
+
+test_comment_scan([],_) -> ok;
+test_comment_scan([File|Files],DataDir) ->
+ Filename = filename:join(DataDir,File),
+ {ok, Fs0} = epp:parse_file(Filename, [], []),
+ Comments = erl_comment_scan:file(Filename),
+ Fun = fun(Node) ->
+ case erl_syntax:is_form(Node) of
+ true ->
+ C1 = erl_syntax:comment(2,[" This is a form."]),
+ Node1 = erl_syntax:add_precomments([C1],Node),
+ Node1;
+ false ->
+ Node
+ end
+ end,
+ Fs1 = erl_recomment:recomment_forms(Fs0, Comments),
+ Fs2 = erl_syntax_lib:map(Fun, Fs1),
+ io:format("File: ~s~n", [Filename]),
+ io:put_chars(erl_prettypr:format(Fs2, [{paper, 120},
+ {ribbon, 110}])),
+ test_comment_scan(Files,DataDir).
+
+
+test_epp_dodger([], _, _) -> ok;
+test_epp_dodger([Filename|Files],DataDir,PrivDir) ->
+ io:format("Parsing ~p~n", [Filename]),
+ InFile = filename:join(DataDir, Filename),
+ Parsers = [{fun epp_dodger:parse_file/1,parse_file},
+ {fun epp_dodger:quick_parse_file/1,quick_parse_file},
+ {fun (File) ->
+ {ok,Dev} = file:open(File,[read]),
+ Res = epp_dodger:parse(Dev),
+ file:close(File),
+ Res
+ end, parse},
+ {fun (File) ->
+ {ok,Dev} = file:open(File,[read]),
+ Res = epp_dodger:quick_parse(Dev),
+ file:close(File),
+ Res
+ end, quick_parse}],
+ FsForms = parse_with(Parsers, InFile),
+ ok = pretty_print_parse_forms(FsForms,PrivDir,Filename),
+ test_epp_dodger(Files,DataDir,PrivDir).
+
+parse_with([],_) -> [];
+parse_with([{Fun,ParserType}|Funs],File) ->
+ {ok, Fs} = Fun(File),
+ [{Fs,ParserType}|parse_with(Funs,File)].
+
+pretty_print_parse_forms([],_,_) -> ok;
+pretty_print_parse_forms([{Fs0,Type}|FsForms],PrivDir,Filename) ->
+ Parser = atom_to_list(Type),
+ OutFile = filename:join(PrivDir, Parser ++"_" ++ Filename),
+ io:format("Pretty print ~p (~w) to ~p~n", [Filename,Type,OutFile]),
+ Comment = fun (Node,{CntCase,CntTry}=Cnt) ->
+ case erl_syntax:type(Node) of
+ case_expr ->
+ C1 = erl_syntax:comment(2,["Before a case expression"]),
+ Node1 = erl_syntax:add_precomments([C1],Node),
+ C2 = erl_syntax:comment(2,["After a case expression"]),
+ Node2 = erl_syntax:add_postcomments([C2],Node1),
+ {Node2,{CntCase+1,CntTry}};
+ try_expr ->
+ C1 = erl_syntax:comment(2,["Before a try expression"]),
+ Node1 = erl_syntax:set_precomments(Node,
+ erl_syntax:get_precomments(Node) ++ [C1]),
+ C2 = erl_syntax:comment(2,["After a try expression"]),
+ Node2 = erl_syntax:set_postcomments(Node1,
+ erl_syntax:get_postcomments(Node1) ++ [C2]),
+ {Node2,{CntCase,CntTry+1}};
+ _ ->
+ {Node,Cnt}
+ end
+ end,
+ Fs1 = erl_syntax:form_list(Fs0),
+ {Fs2,{CC,CT}} = erl_syntax_lib:mapfold(Comment,{0,0}, Fs1),
+ io:format("Commented on ~w cases and ~w tries~n", [CC,CT]),
+ PP = erl_prettypr:format(Fs2),
+ ok = file:write_file(OutFile,iolist_to_binary(PP)),
+ pretty_print_parse_forms(FsForms,PrivDir,Filename).
+
+
+validate(_,[]) -> ok;
+validate(F,[V|Vs]) ->
+ ok = F(V),
+ validate(F,Vs).
+
+
+validate_abstract_type({Lit,Type}) ->
+ Tree = erl_syntax:abstract(Lit),
+ ok = validate_special_type(Type,Tree),
+ Type = erl_syntax:type(Tree),
+ true = erl_syntax:is_literal(Tree),
+ ErlT = erl_syntax:revert(Tree),
+ Type = erl_syntax:type(ErlT),
+ ok = validate_special_type(Type,ErlT),
+ Conc = erl_syntax:concrete(Tree),
+ Lit = Conc,
+ ok.
+
+validate_erl_parse_type({String,Type,Leaf}) ->
+ ErlT = string_to_expr(String),
+ ok = validate_special_type(Type,ErlT),
+ Type = erl_syntax:type(ErlT),
+ Leaf = erl_syntax:is_leaf(ErlT),
+ Tree = erl_syntax_lib:map(fun(Node) -> Node end, ErlT),
+ Type = erl_syntax:type(Tree),
+ _ = erl_syntax:meta(Tree),
+ ok = validate_special_type(Type,Tree),
+ RevT = erl_syntax:revert(Tree),
+ ok = validate_special_type(Type,RevT),
+ Type = erl_syntax:type(RevT),
+ ok.
+
+validate_special_type(string,Node) ->
+ Val = erl_syntax:string_value(Node),
+ true = erl_syntax:is_string(Node,Val),
+ _ = erl_syntax:string_literal(Node),
+ ok;
+validate_special_type(variable,Node) ->
+ _ = erl_syntax:variable_literal(Node),
+ ok;
+validate_special_type(fun_expr,Node) ->
+ A = erl_syntax:fun_expr_arity(Node),
+ true = is_integer(A),
+ ok;
+validate_special_type(named_fun_expr,Node) ->
+ A = erl_syntax:named_fun_expr_arity(Node),
+ true = is_integer(A),
+ ok;
+validate_special_type(tuple,Node) ->
+ Size = erl_syntax:tuple_size(Node),
+ true = is_integer(Size),
+ ok;
+validate_special_type(float,Node) ->
+ Str = erl_syntax:float_literal(Node),
+ Val = list_to_float(Str),
+ Val = erl_syntax:float_value(Node),
+ false = erl_syntax:is_proper_list(Node),
+ false = erl_syntax:is_list_skeleton(Node),
+ ok;
+validate_special_type(integer,Node) ->
+ Str = erl_syntax:integer_literal(Node),
+ Val = list_to_integer(Str),
+ true = erl_syntax:is_integer(Node,Val),
+ Val = erl_syntax:integer_value(Node),
+ false = erl_syntax:is_proper_list(Node),
+ ok;
+validate_special_type(nil,Node) ->
+ true = erl_syntax:is_proper_list(Node),
+ ok;
+validate_special_type(list,Node) ->
+ true = erl_syntax:is_list_skeleton(Node),
+ _ = erl_syntax:list_tail(Node),
+ ErrV = erl_syntax:list_head(Node),
+ false = erl_syntax:is_string(Node,ErrV),
+ Norm = erl_syntax:normalize_list(Node),
+ list = erl_syntax:type(Norm),
+ case erl_syntax:is_proper_list(Node) of
+ true ->
+ true = erl_syntax:is_list_skeleton(Node),
+ Compact = erl_syntax:compact_list(Node),
+ list = erl_syntax:type(Compact),
+ [_|_] = erl_syntax:list_elements(Node),
+ _ = erl_syntax:list_elements(Node),
+ N = erl_syntax:list_length(Node),
+ true = N > 0,
+ ok;
+ false ->
+ ok
+ end;
+validate_special_type(_,_) ->
+ ok.
+
+%%% scan_and_parse
+
+string_to_expr(String) ->
+ io:format("Str: ~p~n", [String]),
+ {ok, Ts, _} = erl_scan:string(String++"."),
+ {ok,[Expr]} = erl_parse:parse_exprs(Ts),
+ Expr.
+
p_run(Test, List) ->
N = erlang:system_info(schedulers),
@@ -138,4 +426,3 @@ p_run_loop(Test, List, N, Refs0, Errors0) ->
Refs = Refs0 -- [Ref],
p_run_loop(Test, List, N, Refs, Errors)
end.
-
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/m1.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/m1.erl
new file mode 100644
index 0000000000..d0d1911199
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/m1.erl
@@ -0,0 +1,22 @@
+%%
+%% File: m1.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2014-10-24
+%%
+
+-module(m1).
+
+-export([foo/0,bar/1,baz/2]).
+
+foo() ->
+ [m2:foo(),
+ m2:bar()].
+
+bar(A) ->
+ [m2:foo(A),
+ m2:bar(A),
+ m2:record_update(3,m2:record())].
+
+baz(A,B) ->
+ [m2:foo(A,B),
+ m2:bar(A,B)].
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/m2.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/m2.erl
new file mode 100644
index 0000000000..781139317d
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/m2.erl
@@ -0,0 +1,26 @@
+%%
+%% File: m2.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2014-10-24
+%%
+
+-module(m2).
+
+
+-export([foo/0,foo/1,foo/2,
+ bar/0,bar/1,bar/2,
+ record_update/2, record/0]).
+
+foo() -> ok.
+foo(A) -> [item,A].
+foo(A,B) -> A + B.
+
+bar() -> true.
+bar(A) -> {element,A}.
+bar(A,B) -> A*B.
+
+-record(rec, {a,b}).
+
+record() -> #rec{a=3,b=0}.
+record_update(V,#rec{a=V0}=R) ->
+ R#rec{a=V0+V,b=V0}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl
new file mode 100644
index 0000000000..07c419b4b7
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_SUITE_test_module.erl
@@ -0,0 +1,540 @@
+-module(syntax_tools_SUITE_test_module).
+
+-export([foo1/1,foo2/3,start_child/2]).
+
+-export([len/1,equal/2,concat/2,chr/2,rchr/2,str/2,rstr/2,
+ span/2,cspan/2,substr/2,substr/3,tokens/2,chars/2,chars/3]).
+-export([copies/2,words/1,words/2,strip/1,strip/2,strip/3,
+ sub_word/2,sub_word/3,left/2,left/3,right/2,right/3,
+ sub_string/2,sub_string/3,centre/2,centre/3, join/2]).
+-export([to_upper/1, to_lower/1]).
+
+-import(lists,[reverse/1,member/2]).
+
+
+%% @type some_type() = map()
+%% @type some_other_type() = {a, #{ list() => term()}}
+
+-type some_type() :: map().
+-type some_other_type() :: {'a', #{ list() => term()} }.
+
+-spec foo1(Map :: #{ 'a' => integer(), 'b' => term()}) -> term().
+
+%% @doc Gets value from map.
+
+foo1(#{ a:= 1, b := V}) -> V.
+
+%% @spec foo2(some_type(), Type2 :: some_other_type(), map()) -> Value
+%% @doc Gets value from map.
+
+-spec foo2(
+ Type1 :: some_type(),
+ Type2 :: some_other_type(),
+ 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}.
+
+
+%% From string.erl
+%% Robert's bit
+
+%% len(String)
+%% Return the length of a string.
+
+-spec len(String) -> Length when
+ String :: string(),
+ Length :: non_neg_integer().
+
+len(S) -> length(S).
+
+%% equal(String1, String2)
+%% Test if 2 strings are equal.
+
+-spec equal(String1, String2) -> boolean() when
+ String1 :: string(),
+ String2 :: string().
+
+equal(S, S) -> true;
+equal(_, _) -> false.
+
+%% concat(String1, String2)
+%% Concatenate 2 strings.
+
+-spec concat(String1, String2) -> String3 when
+ String1 :: string(),
+ String2 :: string(),
+ String3 :: string().
+
+concat(S1, S2) -> S1 ++ S2.
+
+%% chr(String, Char)
+%% rchr(String, Char)
+%% Return the first/last index of the character in a string.
+
+-spec chr(String, Character) -> Index when
+ String :: string(),
+ Character :: char(),
+ Index :: non_neg_integer().
+
+chr(S, C) when is_integer(C) -> chr(S, C, 1).
+
+chr([C|_Cs], C, I) -> I;
+chr([_|Cs], C, I) -> chr(Cs, C, I+1);
+chr([], _C, _I) -> 0.
+
+-spec rchr(String, Character) -> Index when
+ String :: string(),
+ Character :: char(),
+ Index :: non_neg_integer().
+
+rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0).
+
+rchr([C|Cs], C, I, _L) -> %Found one, now find next!
+ rchr(Cs, C, I+1, I);
+rchr([_|Cs], C, I, L) ->
+ rchr(Cs, C, I+1, L);
+rchr([], _C, _I, L) -> L.
+
+%% str(String, SubString)
+%% rstr(String, SubString)
+%% index(String, SubString)
+%% Return the first/last index of the sub-string in a string.
+%% index/2 is kept for backwards compatibility.
+
+-spec str(String, SubString) -> Index when
+ String :: string(),
+ SubString :: string(),
+ Index :: non_neg_integer().
+
+str(S, Sub) when is_list(Sub) -> str(S, Sub, 1).
+
+str([C|S], [C|Sub], I) ->
+ case prefix(Sub, S) of
+ true -> I;
+ false -> str(S, [C|Sub], I+1)
+ end;
+str([_|S], Sub, I) -> str(S, Sub, I+1);
+str([], _Sub, _I) -> 0.
+
+-spec rstr(String, SubString) -> Index when
+ String :: string(),
+ SubString :: string(),
+ Index :: non_neg_integer().
+
+rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0).
+
+rstr([C|S], [C|Sub], I, L) ->
+ case prefix(Sub, S) of
+ true -> rstr(S, [C|Sub], I+1, I);
+ false -> rstr(S, [C|Sub], I+1, L)
+ end;
+rstr([_|S], Sub, I, L) -> rstr(S, Sub, I+1, L);
+rstr([], _Sub, _I, L) -> L.
+
+prefix([C|Pre], [C|String]) -> prefix(Pre, String);
+prefix([], String) when is_list(String) -> true;
+prefix(Pre, String) when is_list(Pre), is_list(String) -> false.
+
+%% span(String, Chars) -> Length.
+%% cspan(String, Chars) -> Length.
+
+-spec span(String, Chars) -> Length when
+ String :: string(),
+ Chars :: string(),
+ Length :: non_neg_integer().
+
+span(S, Cs) when is_list(Cs) -> span(S, Cs, 0).
+
+span([C|S], Cs, I) ->
+ case member(C, Cs) of
+ true -> span(S, Cs, I+1);
+ false -> I
+ end;
+span([], _Cs, I) -> I.
+
+-spec cspan(String, Chars) -> Length when
+ String :: string(),
+ Chars :: string(),
+ Length :: non_neg_integer().
+
+cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0).
+
+cspan([C|S], Cs, I) ->
+ case member(C, Cs) of
+ true -> I;
+ false -> cspan(S, Cs, I+1)
+ end;
+cspan([], _Cs, I) -> I.
+
+%% substr(String, Start)
+%% substr(String, Start, Length)
+%% Extract a sub-string from String.
+
+-spec substr(String, Start) -> SubString when
+ String :: string(),
+ SubString :: string(),
+ Start :: pos_integer().
+
+substr(String, 1) when is_list(String) ->
+ String;
+substr(String, S) when is_integer(S), S > 1 ->
+ substr2(String, S).
+
+-spec substr(String, Start, Length) -> SubString when
+ String :: string(),
+ SubString :: string(),
+ Start :: pos_integer(),
+ Length :: non_neg_integer().
+
+substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 ->
+ substr1(substr2(String, S), L).
+
+substr1([C|String], L) when L > 0 -> [C|substr1(String, L-1)];
+substr1(String, _L) when is_list(String) -> []. %Be nice!
+
+substr2(String, 1) when is_list(String) -> String;
+substr2([_|String], S) -> substr2(String, S-1).
+
+%% tokens(String, Seperators).
+%% Return a list of tokens seperated by characters in Seperators.
+
+-spec tokens(String, SeparatorList) -> Tokens when
+ String :: string(),
+ SeparatorList :: string(),
+ Tokens :: [Token :: nonempty_string()].
+
+tokens(S, Seps) ->
+ tokens1(S, Seps, []).
+
+tokens1([C|S], Seps, Toks) ->
+ case member(C, Seps) of
+ true -> tokens1(S, Seps, Toks);
+ false -> tokens2(S, Seps, Toks, [C])
+ end;
+tokens1([], _Seps, Toks) ->
+ reverse(Toks).
+
+tokens2([C|S], Seps, Toks, Cs) ->
+ case member(C, Seps) of
+ true -> tokens1(S, Seps, [reverse(Cs)|Toks]);
+ false -> tokens2(S, Seps, Toks, [C|Cs])
+ end;
+tokens2([], _Seps, Toks, Cs) ->
+ reverse([reverse(Cs)|Toks]).
+
+-spec chars(Character, Number) -> String when
+ Character :: char(),
+ Number :: non_neg_integer(),
+ String :: string().
+
+chars(C, N) -> chars(C, N, []).
+
+-spec chars(Character, Number, Tail) -> String when
+ Character :: char(),
+ Number :: non_neg_integer(),
+ Tail :: string(),
+ String :: string().
+
+chars(C, N, Tail) when N > 0 ->
+ chars(C, N-1, [C|Tail]);
+chars(C, 0, Tail) when is_integer(C) ->
+ Tail.
+
+%% Torbjörn's bit.
+
+%%% COPIES %%%
+
+-spec copies(String, Number) -> Copies when
+ String :: string(),
+ Copies :: string(),
+ Number :: non_neg_integer().
+
+copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 ->
+ copies(CharList, Num, []).
+
+copies(_CharList, 0, R) ->
+ R;
+copies(CharList, Num, R) ->
+ copies(CharList, Num-1, CharList++R).
+
+%%% WORDS %%%
+
+-spec words(String) -> Count when
+ String :: string(),
+ Count :: pos_integer().
+
+words(String) -> words(String, $\s).
+
+-spec words(String, Character) -> Count when
+ String :: string(),
+ Character :: char(),
+ Count :: pos_integer().
+
+words(String, Char) when is_integer(Char) ->
+ w_count(strip(String, both, Char), Char, 0).
+
+w_count([], _, Num) -> Num+1;
+w_count([H|T], H, Num) -> w_count(strip(T, left, H), H, Num+1);
+w_count([_H|T], Char, Num) -> w_count(T, Char, Num).
+
+%%% SUB_WORDS %%%
+
+-spec sub_word(String, Number) -> Word when
+ String :: string(),
+ Word :: string(),
+ Number :: integer().
+
+sub_word(String, Index) -> sub_word(String, Index, $\s).
+
+-spec sub_word(String, Number, Character) -> Word when
+ String :: string(),
+ Word :: string(),
+ Number :: integer(),
+ Character :: char().
+
+sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) ->
+ case words(String, Char) of
+ Num when Num < Index ->
+ [];
+ _Num ->
+ s_word(strip(String, left, Char), Index, Char, 1, [])
+ end.
+
+s_word([], _, _, _,Res) -> reverse(Res);
+s_word([Char|_],Index,Char,Index,Res) -> reverse(Res);
+s_word([H|T],Index,Char,Index,Res) -> s_word(T,Index,Char,Index,[H|Res]);
+s_word([Char|T],Stop,Char,Index,Res) when Index < Stop ->
+ s_word(strip(T,left,Char),Stop,Char,Index+1,Res);
+s_word([_|T],Stop,Char,Index,Res) when Index < Stop ->
+ s_word(T,Stop,Char,Index,Res).
+
+%%% STRIP %%%
+
+-spec strip(string()) -> string().
+
+strip(String) -> strip(String, both).
+
+-spec strip(String, Direction) -> Stripped when
+ String :: string(),
+ Stripped :: string(),
+ Direction :: left | right | both.
+
+strip(String, left) -> strip_left(String, $\s);
+strip(String, right) -> strip_right(String, $\s);
+strip(String, both) ->
+ strip_right(strip_left(String, $\s), $\s).
+
+-spec strip(String, Direction, Character) -> Stripped when
+ String :: string(),
+ Stripped :: string(),
+ Direction :: left | right | both,
+ Character :: char().
+
+strip(String, right, Char) -> strip_right(String, Char);
+strip(String, left, Char) -> strip_left(String, Char);
+strip(String, both, Char) ->
+ strip_right(strip_left(String, Char), Char).
+
+strip_left([Sc|S], Sc) ->
+ strip_left(S, Sc);
+strip_left([_|_]=S, Sc) when is_integer(Sc) -> S;
+strip_left([], Sc) when is_integer(Sc) -> [].
+
+strip_right([Sc|S], Sc) ->
+ case strip_right(S, Sc) of
+ [] -> [];
+ T -> [Sc|T]
+ end;
+strip_right([C|S], Sc) ->
+ [C|strip_right(S, Sc)];
+strip_right([], Sc) when is_integer(Sc) ->
+ [].
+
+%%% LEFT %%%
+
+-spec left(String, Number) -> Left when
+ String :: string(),
+ Left :: string(),
+ Number :: non_neg_integer().
+
+left(String, Len) when is_integer(Len) -> left(String, Len, $\s).
+
+-spec left(String, Number, Character) -> Left when
+ String :: string(),
+ Left :: string(),
+ Number :: non_neg_integer(),
+ Character :: char().
+
+left(String, Len, Char) when is_integer(Char) ->
+ Slen = length(String),
+ if
+ Slen > Len -> substr(String, 1, Len);
+ Slen < Len -> l_pad(String, Len-Slen, Char);
+ Slen =:= Len -> String
+ end.
+
+l_pad(String, Num, Char) -> String ++ chars(Char, Num).
+
+%%% RIGHT %%%
+
+-spec right(String, Number) -> Right when
+ String :: string(),
+ Right :: string(),
+ Number :: non_neg_integer().
+
+right(String, Len) when is_integer(Len) -> right(String, Len, $\s).
+
+-spec right(String, Number, Character) -> Right when
+ String :: string(),
+ Right :: string(),
+ Number :: non_neg_integer(),
+ Character :: char().
+
+right(String, Len, Char) when is_integer(Char) ->
+ Slen = length(String),
+ if
+ Slen > Len -> substr(String, Slen-Len+1);
+ Slen < Len -> r_pad(String, Len-Slen, Char);
+ Slen =:= Len -> String
+ end.
+
+r_pad(String, Num, Char) -> chars(Char, Num, String).
+
+%%% CENTRE %%%
+
+-spec centre(String, Number) -> Centered when
+ String :: string(),
+ Centered :: string(),
+ Number :: non_neg_integer().
+
+centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s).
+
+-spec centre(String, Number, Character) -> Centered when
+ String :: string(),
+ Centered :: string(),
+ Number :: non_neg_integer(),
+ Character :: char().
+
+centre(String, 0, Char) when is_list(String), is_integer(Char) ->
+ []; % Strange cases to centre string
+centre(String, Len, Char) when is_integer(Char) ->
+ Slen = length(String),
+ if
+ Slen > Len -> substr(String, (Slen-Len) div 2 + 1, Len);
+ Slen < Len ->
+ N = (Len-Slen) div 2,
+ r_pad(l_pad(String, Len-(Slen+N), Char), N, Char);
+ Slen =:= Len -> String
+ end.
+
+%%% SUB_STRING %%%
+
+-spec sub_string(String, Start) -> SubString when
+ String :: string(),
+ SubString :: string(),
+ Start :: pos_integer().
+
+sub_string(String, Start) -> substr(String, Start).
+
+-spec sub_string(String, Start, Stop) -> SubString when
+ String :: string(),
+ SubString :: string(),
+ Start :: pos_integer(),
+ Stop :: pos_integer().
+
+sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1).
+
+%% ISO/IEC 8859-1 (latin1) letters are converted, others are ignored
+%%
+
+to_lower_char(C) when is_integer(C), $A =< C, C =< $Z ->
+ C + 32;
+to_lower_char(C) when is_integer(C), 16#C0 =< C, C =< 16#D6 ->
+ C + 32;
+to_lower_char(C) when is_integer(C), 16#D8 =< C, C =< 16#DE ->
+ C + 32;
+to_lower_char(C) ->
+ C.
+
+to_upper_char(C) when is_integer(C), $a =< C, C =< $z ->
+ C - 32;
+to_upper_char(C) when is_integer(C), 16#E0 =< C, C =< 16#F6 ->
+ C - 32;
+to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE ->
+ C - 32;
+to_upper_char(C) ->
+ C.
+
+-spec to_lower(String) -> Result when
+ String :: io_lib:latin1_string(),
+ Result :: io_lib:latin1_string()
+ ; (Char) -> CharResult when
+ Char :: char(),
+ CharResult :: char().
+
+to_lower(S) when is_list(S) ->
+ [to_lower_char(C) || C <- S];
+to_lower(C) when is_integer(C) ->
+ to_lower_char(C).
+
+-spec to_upper(String) -> Result when
+ String :: io_lib:latin1_string(),
+ Result :: io_lib:latin1_string()
+ ; (Char) -> CharResult when
+ Char :: char(),
+ CharResult :: char().
+
+to_upper(S) when is_list(S) ->
+ [to_upper_char(C) || C <- S];
+to_upper(C) when is_integer(C) ->
+ to_upper_char(C).
+
+-spec join(StringList, Separator) -> String when
+ StringList :: [string()],
+ Separator :: string(),
+ String :: string().
+
+join([], Sep) when is_list(Sep) ->
+ [];
+join([H|T], Sep) ->
+ H ++ lists:append([Sep ++ X || X <- T]).
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_test.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_test.erl
new file mode 100644
index 0000000000..dd3f88d7a8
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/syntax_tools_test.erl
@@ -0,0 +1,115 @@
+%%
+%% File: syntax_tools_test.erl
+%% Author: Björn-Egil Dahlberg
+%% Created: 2014-10-23
+%%
+
+-module(syntax_tools_test).
+
+-export([foo1/0,foo2/2,foo3/0,foo4/3,foo5/1]).
+
+-include_lib("kernel/include/file.hrl").
+-record(state, { a, b, c, d}).
+-attribute([foo/0]).
+
+-define(attrib, some_attrib).
+
+-?attrib([foo2/2]).
+
+-define(macro_simple1, ok).
+-define(MACRO_SIMPLE2, (other)).
+-define(macro_simple3, ?MODULE).
+-define(macro_simple4, [?macro_simple3,?MODULE,?MACRO_SIMPLE2]).
+-define(macro_simple5, (process_info)).
+-define(macro_string, "hello world").
+-define(macro_argument1(X), (X + 3)).
+-define(macro_argument2(X,Y), (X + 3 * Y)).
+-define(macro_block(X), begin X end).
+-define(macro_if(X1,X2), if X1 -> X2; true -> none end).
+
+
+-ifdef(macro_def1).
+-define(macro_cond1, yep).
+-else.
+-define(macro_cond1, nope).
+-endif.
+-ifndef(macro_def2).
+-define(macro_cond2, nope).
+-else.
+-define(macro_cond2, yep).
+-endif.
+-undef(macro_def1).
+-undef(macro_def2).
+
+%% basic test
+foo1() ->
+ ok.
+
+%% macro test
+foo2(A,B) ->
+ % string combining ?
+ [?macro_string, ?macro_string
+ ?macro_string,
+ "hello world "
+ "more hello",
+ [?macro_simple1,
+ ?MACRO_SIMPLE2,
+ ?macro_simple3,
+ ?macro_simple4,
+ ?macro_simple5,
+ ?macro_string,
+ ?macro_cond1,
+ ?macro_cond2,
+ ?macro_block(A),
+ ?macro_if(A,B),
+ ?macro_argument1(A),
+ ?macro_argument1(begin A end),
+ ?macro_block(<<"hello">>),
+ ?macro_block("hello"),
+ ?macro_block([$h,$e,$l,$l,$0]),
+ ?macro_argument1(id(<<"hello">>)),
+ ?macro_argument1(if A -> B; true -> 3.14 end),
+ ?macro_argument1(case A of ok -> B; C -> C end),
+ ?macro_argument1(receive M -> M after 100 -> 3 end),
+ ?macro_argument1(try foo5(A) catch C:?macro_simple5 -> {C,B} end),
+ ?macro_argument2(A,B)],
+ A,B,ok].
+
+id(I) -> I.
+%% basic terms
+
+foo3() ->
+ [atom,
+ 'some other atom',
+ {tuple,1,2,3},
+ 1,2,3,3333,
+ 3,3333,2,1,
+ [$a,$b,$c],
+ "hello world",
+ <<"hello world">>,
+ <<1,2,3,4,5:6>>,
+ 3.1415,
+ 1.03e33].
+
+%% application and records
+
+foo4(A,B,#state{c = C}=S) ->
+ Ls = foo3(),
+ S1 = #state{ a = 1, b = 2 },
+ [foo2(A,Ls),B,C,
+ B(3,C),
+ erlang:process_info(self()),
+ erlang:?macro_simple5(self()),
+ A:?MACRO_SIMPLE2(),
+ A:?macro_simple1(),
+ A:process_info(self()),
+ A:B(3),
+ S#state{ a = 2, b = B, d = S1 }].
+
+foo5(A) ->
+ try foo2(A,A) of
+ R -> R
+ catch
+ error:?macro_simple5 ->
+ nope
+ end.
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/test_server/src/configure.in b/lib/test_server/src/configure.in
index cd723bcd4d..8398825d95 100644
--- a/lib/test_server/src/configure.in
+++ b/lib/test_server/src/configure.in
@@ -357,7 +357,23 @@ AC_CHECK_FUNCS(usleep)
# First check if the library is available, then if we can choose between
# two versions of gethostbyname
AC_HAVE_LIBRARY(resolv)
-AC_CHECK_LIB(resolv, res_gethostbyname,[DEFS="$DEFS -DHAVE_RES_GETHOSTBYNAME=1"])
+AC_CHECK_LIB(resolv, res_gethostbyname,[AC_DEFINE(HAVE_RES_GETHOSTBYNAME,1)])
+
+#--------------------------------------------------------------------
+# Check for isfinite
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([for isfinite])
+AC_TRY_LINK([#include <math.h>],
+ [isfinite(0);], have_isfinite=yes, have_isfinite=no)
+
+if test $have_isfinite = yes; then
+ AC_DEFINE(HAVE_ISFINITE,1)
+ AC_MSG_RESULT(yes)
+else
+ AC_DEFINE(HAVE_FINITE,1)
+ AC_MSG_RESULT(no)
+fi
#--------------------------------------------------------------------
# Emulator compatible flags (for drivers)
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..78929ac510 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -31,6 +31,7 @@
("Module" "module" erlang-skel-module)
("Author" "author" erlang-skel-author)
("Function" "function" erlang-skel-function)
+ ("Spec" "spec" erlang-skel-spec)
()
("Small Header" "small-header"
erlang-skel-small-header erlang-skel-header)
@@ -54,6 +55,8 @@
erlang-skel-gen-event erlang-skel-header)
("gen_fsm" "gen-fsm"
erlang-skel-gen-fsm erlang-skel-header)
+ ("wx_object" "wx-object"
+ erlang-skel-wx-object erlang-skel-header)
("Library module" "gen-lib"
erlang-skel-lib erlang-skel-header)
("Corba callback" "gen-corba-cb"
@@ -147,6 +150,10 @@ Please see the function `tempo-define-template'.")
"*The template of a function skeleton.
Please see the function `tempo-define-template'.")
+(defvar erlang-skel-spec
+ '("-spec " (erlang-skel-get-function-name) "(" (erlang-skel-get-function-args) ") -> undefined.")
+ "*The template of a -spec for the function following point.
+Please see the function `tempo-define-template'.")
;; Attribute templates
@@ -577,7 +584,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
@@ -851,6 +858,137 @@ Please see the function `tempo-define-template'.")
"*The template of a gen_fsm.
Please see the function `tempo-define-template'.")
+(defvar erlang-skel-wx-object
+ '((erlang-skel-include erlang-skel-large-header)
+ "-behaviour(wx_object)." n n
+
+ "-include_lib(\"wx/include/wx.hrl\")." n n
+
+ "%% API" n
+ "-export([start_link/0])." n n
+
+ "%% wx_object callbacks" n
+ "-export([init/1, handle_call/3, handle_cast/2, "
+ "handle_info/2," n>
+ "handle_event/2, terminate/2, code_change/3])." n n
+
+ "-record(state, {})." n n
+
+ (erlang-skel-double-separator-start 3)
+ "%%% API" n
+ (erlang-skel-double-separator-end 3) n
+ (erlang-skel-separator-start 2)
+ "%% @doc" n
+ "%% Starts the server" n
+ "%%" n
+ "%% @spec start_link() -> wxWindow()" n
+ (erlang-skel-separator-end 2)
+ "start_link() ->" n>
+ "wx_object:start_link(?MODULE, [], [])." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% wx_object callbacks" n
+ (erlang-skel-double-separator-end 3)
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Initializes the server" n
+ "%%" n
+ "%% @spec init(Args) -> {wxWindow(), State} |" n
+ "%% {wxWindow(), State, Timeout} |" n
+ "%% ignore |" n
+ "%% {stop, Reason}" n
+ (erlang-skel-separator-end 2)
+ "init([]) ->" n>
+ "wx:new()," n>
+ "Frame = wxFrame:new()," n>
+ "{Frame, #state{}}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Handling events" n
+ "%%" n
+ "%% @spec handle_event(wx{}, State) ->" n
+ "%% {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State}" n
+ (erlang-skel-separator-end 2)
+ "handle_event(#wx{}, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Handling call messages" n
+ "%%" n
+ "%% @spec handle_call(Request, From, State) ->" n
+ "%% {reply, Reply, State} |" n
+ "%% {reply, Reply, State, Timeout} |" n
+ "%% {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, Reply, State} |" n
+ "%% {stop, Reason, State}" n
+ (erlang-skel-separator-end 2)
+ "handle_call(_Request, _From, State) ->" n>
+ "Reply = ok," n>
+ "{reply, Reply, State}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Handling cast messages" n
+ "%%" n
+ "%% @spec handle_cast(Msg, State) -> {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State}" n
+ (erlang-skel-separator-end 2)
+ "handle_cast(_Msg, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Handling all non call/cast messages" n
+ "%%" n
+ "%% @spec handle_info(Info, State) -> {noreply, State} |" n
+ "%% {noreply, State, Timeout} |" n
+ "%% {stop, Reason, State}" n
+ (erlang-skel-separator-end 2)
+ "handle_info(_Info, State) ->" n>
+ "{noreply, State}." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% This function is called by a wx_object when it is about to" n
+ "%% terminate. It should be the opposite of Module:init/1 and do any" n
+ "%% necessary cleaning up. When it returns, the wx_object terminates" n
+ "%% with Reason. The return value is ignored." n
+ "%%" n
+ "%% @spec terminate(Reason, State) -> void()" n
+ (erlang-skel-separator-end 2)
+ "terminate(_Reason, _State) ->" n>
+ "ok." n
+ n
+ (erlang-skel-separator-start 2)
+ "%% @private" n
+ "%% @doc" n
+ "%% Convert process state when code is changed" n
+ "%%" n
+ "%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}" n
+ (erlang-skel-separator-end 2)
+ "code_change(_OldVsn, State, _Extra) ->" n>
+ "{ok, State}." n
+ n
+ (erlang-skel-double-separator-start 3)
+ "%%% Internal functions" n
+ (erlang-skel-double-separator-end 3)
+ )
+ "*The template of a generic server.
+Please see the function `tempo-define-template'.")
+
(defvar erlang-skel-lib
'((erlang-skel-include erlang-skel-large-header)
@@ -1546,6 +1684,16 @@ The first character of DD is space if the value is less than 10."
(substring date 4 7)
(substring date -4))))
+(defun erlang-skel-get-function-name ()
+ (save-excursion
+ (erlang-beginning-of-function -1)
+ (erlang-get-function-name)))
+
+(defun erlang-skel-get-function-args ()
+ (save-excursion
+ (erlang-beginning-of-function -1)
+ (erlang-get-function-arguments)))
+
;; Local variables:
;; coding: iso-8859-1
;; End:
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/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/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/examples/demo/ex_graphicsContext.erl b/lib/wx/examples/demo/ex_graphicsContext.erl
index 59bfe7ff64..9047f1d135 100644
--- a/lib/wx/examples/demo/ex_graphicsContext.erl
+++ b/lib/wx/examples/demo/ex_graphicsContext.erl
@@ -54,7 +54,7 @@ do_init(Config) ->
%% Setup sizers
MainSizer = wxBoxSizer:new(?wxVERTICAL),
Sizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel,
- [{label, "wxGrapicsContext"}]),
+ [{label, "wxGraphicsContext"}]),
Win = wxPanel:new(Panel, []),
Pen = ?wxBLACK_PEN,
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/otp_versions.table b/otp_versions.table
index e628c65444..4da3e13559 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,7 @@
+OTP-17.3.3 : ssh-3.0.8 # asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.1 debugger-4.0.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 eldap-1.0.4 erl_docgen-0.3.6 erl_interface-3.7.19 erts-6.2 et-1.5 eunit-2.2.8 gs-1.5.16 hipe-3.11.1 ic-4.3.6 inets-5.10.3 jinterface-1.5.11 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4.1 snmp-5.1 ssl-5.3.7 stdlib-2.2 syntax_tools-1.6.16 test_server-3.7.1 tools-2.7 typer-0.9.8 webtool-0.8.10 wx-1.3.1 xmerl-1.3.7 :
+OTP-17.3.2 : ssh-3.0.7 ssl-5.3.7 # asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.1 debugger-4.0.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 eldap-1.0.4 erl_docgen-0.3.6 erl_interface-3.7.19 erts-6.2 et-1.5 eunit-2.2.8 gs-1.5.16 hipe-3.11.1 ic-4.3.6 inets-5.10.3 jinterface-1.5.11 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 orber-3.7.1 os_mon-2.3 ose-1.0.2 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4.1 snmp-5.1 stdlib-2.2 syntax_tools-1.6.16 test_server-3.7.1 tools-2.7 typer-0.9.8 webtool-0.8.10 wx-1.3.1 xmerl-1.3.7 :
+OTP-17.3.1 : eldap-1.0.4 erl_interface-3.7.19 jinterface-1.5.11 orber-3.7.1 ose-1.0.2 ssh-3.0.6 # asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4.1 debugger-4.0.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 erl_docgen-0.3.6 erts-6.2 et-1.5 eunit-2.2.8 gs-1.5.16 hipe-3.11.1 ic-4.3.6 inets-5.10.3 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 os_mon-2.3 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22.1 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4.1 snmp-5.1 ssl-5.3.6 stdlib-2.2 syntax_tools-1.6.16 test_server-3.7.1 tools-2.7 typer-0.9.8 webtool-0.8.10 wx-1.3.1 xmerl-1.3.7 :
+OTP-17.3 : asn1-3.0.2 common_test-1.8.2 compiler-5.0.2 crypto-3.4.1 dialyzer-2.7.2 diameter-1.7.1 edoc-0.7.15 erl_docgen-0.3.6 erl_interface-3.7.18 erts-6.2 eunit-2.2.8 hipe-3.11.1 ic-4.3.6 inets-5.10.3 jinterface-1.5.10 kernel-3.0.3 megaco-3.17.2 mnesia-4.12.3 observer-2.0.2 odbc-2.10.21 os_mon-2.3 ose-1.0.1 public_key-0.22.1 sasl-2.4.1 snmp-5.1 ssh-3.0.5 ssl-5.3.6 stdlib-2.2 tools-2.7 wx-1.3.1 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 debugger-4.0.1 eldap-1.0.3 et-1.5 gs-1.5.16 orber-3.7 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 reltool-0.6.6 runtime_tools-1.8.14 syntax_tools-1.6.16 test_server-3.7.1 typer-0.9.8 webtool-0.8.10 xmerl-1.3.7 :
OTP-17.2.2 : mnesia-4.12.2 # asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 edoc-0.7.14 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.17 erts-6.1.2 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.11 ic-4.3.5 inets-5.10.2 jinterface-1.5.9 kernel-3.0.2 megaco-3.17.1 observer-2.0.1 odbc-2.10.20 orber-3.7 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4 snmp-5.0 ssh-3.0.4 ssl-5.3.5 stdlib-2.1.1 syntax_tools-1.6.16 test_server-3.7.1 tools-2.6.15 typer-0.9.8 webtool-0.8.10 wx-1.3 xmerl-1.3.7 :
OTP-17.2.1 : ssh-3.0.4 # asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 edoc-0.7.14 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.17 erts-6.1.2 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.11 ic-4.3.5 inets-5.10.2 jinterface-1.5.9 kernel-3.0.2 megaco-3.17.1 mnesia-4.12.1 observer-2.0.1 odbc-2.10.20 orber-3.7 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4 snmp-5.0 ssl-5.3.5 stdlib-2.1.1 syntax_tools-1.6.16 test_server-3.7.1 tools-2.6.15 typer-0.9.8 webtool-0.8.10 wx-1.3 xmerl-1.3.7 :
OTP-17.2 : orber-3.7 snmp-5.0 # asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 edoc-0.7.14 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.17 erts-6.1.2 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.11 ic-4.3.5 inets-5.10.2 jinterface-1.5.9 kernel-3.0.2 megaco-3.17.1 mnesia-4.12.1 observer-2.0.1 odbc-2.10.20 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4 ssh-3.0.3 ssl-5.3.5 stdlib-2.1.1 syntax_tools-1.6.16 test_server-3.7.1 tools-2.6.15 typer-0.9.8 webtool-0.8.10 wx-1.3 xmerl-1.3.7 :
diff --git a/system/doc/efficiency_guide/processes.xml b/system/doc/efficiency_guide/processes.xml
index 6f85b029eb..86951e2dcc 100644
--- a/system/doc/efficiency_guide/processes.xml
+++ b/system/doc/efficiency_guide/processes.xml
@@ -186,7 +186,7 @@ kilo_byte(0, Acc) ->
kilo_byte(N, Acc) ->
kilo_byte(N-1, [Acc|Acc]).</code>
- <p><c>kilo_byte/1</c> creates a deep list. If we call
+ <p><c>kilo_byte/0</c> creates a deep list. If we call
<c>list_to_binary/1</c>, we can convert the deep list to a binary
of 1024 bytes:</p>